fluncle 0.169.0 → 0.171.0

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.
Files changed (2) hide show
  1. package/bin/fluncle.mjs +42 -7
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -561,7 +561,7 @@ function parseVersion(version) {
561
561
  var currentVersion;
562
562
  var init_version = __esm(() => {
563
563
  init_output();
564
- currentVersion = "0.169.0".trim() ? "0.169.0".trim() : "0.1.0";
564
+ currentVersion = "0.171.0".trim() ? "0.171.0".trim() : "0.1.0";
565
565
  });
566
566
 
567
567
  // src/update-notifier.ts
@@ -2259,7 +2259,7 @@ function parseVersion2(version) {
2259
2259
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2260
2260
  var init_version2 = __esm(() => {
2261
2261
  init_output();
2262
- currentVersion2 = "0.169.0".trim() ? "0.169.0".trim() : "0.1.0";
2262
+ currentVersion2 = "0.171.0".trim() ? "0.171.0".trim() : "0.1.0";
2263
2263
  });
2264
2264
 
2265
2265
  // ../../packages/registry/src/index.ts
@@ -6344,12 +6344,17 @@ var init_admin_artists2 = __esm(() => {
6344
6344
  // src/commands/admin-labels.ts
6345
6345
  var exports_admin_labels = {};
6346
6346
  __export(exports_admin_labels, {
6347
+ mergeLabelCommand: () => mergeLabelCommand,
6347
6348
  labelsBioQueueCommand: () => labelsBioQueueCommand,
6348
6349
  draftLabelBioCommand: () => draftLabelBioCommand,
6349
6350
  describeLabelCommand: () => describeLabelCommand,
6350
6351
  backfillLabelLineageCommand: () => backfillLabelLineageCommand,
6351
6352
  backfillLabelImagesCommand: () => backfillLabelImagesCommand
6352
6353
  });
6354
+ async function mergeLabelCommand(losingSlug, canonicalSlug) {
6355
+ const response = await adminApiPost(`/api/admin/labels/${encodeURIComponent(losingSlug)}/merge`, { canonicalSlug });
6356
+ return response.result;
6357
+ }
6353
6358
  async function describeLabelCommand(slug, options) {
6354
6359
  return adminApiPost(`/api/admin/labels/${encodeURIComponent(slug)}/bio`, buildBioBody2(options));
6355
6360
  }
@@ -6384,11 +6389,14 @@ var exports_admin_covers = {};
6384
6389
  __export(exports_admin_covers, {
6385
6390
  backfillCoverMastersCommand: () => backfillCoverMastersCommand
6386
6391
  });
6387
- async function backfillCoverMastersCommand(kind, limit, dryRun, cursor) {
6392
+ async function backfillCoverMastersCommand(kind, limit, dryRun, cursor, retryNone = false) {
6388
6393
  const params = new URLSearchParams({ dryRun: String(dryRun), kind, limit: String(limit) });
6389
6394
  if (cursor) {
6390
6395
  params.set("cursor", cursor);
6391
6396
  }
6397
+ if (retryNone) {
6398
+ params.set("retry", "none");
6399
+ }
6392
6400
  return adminApiPost(`/api/admin/backfill/cover-masters?${params.toString()}`);
6393
6401
  }
6394
6402
  var init_admin_covers = __esm(() => {
@@ -9627,7 +9635,7 @@ function addAdminCommands(program2) {
9627
9635
  const { backfillRecordingMbidsCommand: backfillRecordingMbidsCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
9628
9636
  await runBackfillRecordingMbids(options, backfillRecordingMbidsCommand2);
9629
9637
  });
9630
- backfill.command("cover-masters").description("Resolve owned \u22641200\xB2 cover masters (album/artist) into R2").option("--kind <kind>", "Which entity to drain: album | artist", "album").option("--dry-run", "Report the eligible worklist without any fetch or write", false).option("--limit <limit>", "Max entities to process", "50").option("--json", "Print JSON", false).action(async (options) => {
9638
+ backfill.command("cover-masters").description("Resolve owned \u22641200\xB2 cover masters (album/artist) into R2").option("--kind <kind>", "Which entity to drain: album | artist", "album").option("--dry-run", "Report the eligible worklist without any fetch or write", false).option("--retry-none", "First re-queue a bounded batch of terminal none rows to pending, then run the pass", false).option("--limit <limit>", "Max entities to process", "50").option("--json", "Print JSON", false).action(async (options) => {
9631
9639
  const { backfillCoverMastersCommand: backfillCoverMastersCommand2 } = await Promise.resolve().then(() => (init_admin_covers(), exports_admin_covers));
9632
9640
  await runBackfillCoverMasters(options, backfillCoverMastersCommand2);
9633
9641
  });
@@ -9653,7 +9661,7 @@ function addAdminCommands(program2) {
9653
9661
  const { draftArtistBioCommand: draftArtistBioCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
9654
9662
  await runEntityBioDraft("artist", slug, options, draftArtistBioCommand2);
9655
9663
  });
9656
- const labels = configureCommand(admin.command("labels").description("Label entity commands (the voiced bio)"));
9664
+ const labels = configureCommand(admin.command("labels").description("Label entity commands (voiced bio + slug-split merge)"));
9657
9665
  labels.action(() => {
9658
9666
  labels.outputHelp();
9659
9667
  });
@@ -9670,6 +9678,27 @@ function addAdminCommands(program2) {
9670
9678
  const { draftLabelBioCommand: draftLabelBioCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
9671
9679
  await runEntityBioDraft("label", slug, options, draftLabelBioCommand2);
9672
9680
  });
9681
+ labels.command("merge").description("Merge a slug-split label into its canonical row (operator; re-points + redirects)").argument("<losingSlug>", "The duplicate label to fold away").argument("<canonicalSlug>", "The label to keep \u2014 everything re-points onto it").option("--json", "Print JSON", false).action(async (losingSlug, canonicalSlug, options) => {
9682
+ const { mergeLabelCommand: mergeLabelCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
9683
+ const result = await mergeLabelCommand2(losingSlug, canonicalSlug);
9684
+ if (options.json) {
9685
+ printJson({ ok: true, result });
9686
+ return;
9687
+ }
9688
+ const {
9689
+ aliasWritten,
9690
+ canonicalSlug: canon,
9691
+ losingSlug: loser,
9692
+ reconciled,
9693
+ repointed
9694
+ } = result;
9695
+ console.log(`Merged ${loser} \u2192 ${canon}.`);
9696
+ console.log(` Re-pointed: ${repointed.tracks} track(s), ${repointed.childLabels} sublabel(s), ${repointed.aliases} alias(es).`);
9697
+ console.log(reconciled.length > 0 ? ` Filled onto ${canon} (was empty): ${reconciled.join(", ")}.` : ` Nothing to fill \u2014 ${canon} already carried every fact.`);
9698
+ console.log(` Seed state resolved to: ${result.seedState}.`);
9699
+ console.log(` Alias written: "${aliasWritten.alias}" (${aliasWritten.aliasSlug}) \u2192 confirmed, so it can never re-mint.`);
9700
+ console.log(` /label/${loser} now 301s to /label/${canon}.`);
9701
+ });
9673
9702
  const albums = configureCommand(admin.command("albums").description("Album entity commands (the voiced bio)"));
9674
9703
  albums.action(() => {
9675
9704
  albums.outputHelp();
@@ -10370,17 +10399,20 @@ async function runBackfillArtistImages(options, backfillArtistImagesCommand2) {
10370
10399
  async function runBackfillCoverMasters(options, backfillCoverMastersCommand2) {
10371
10400
  const limit = parseListLimit(options.limit);
10372
10401
  const kind = options.kind === "artist" ? "artist" : "album";
10402
+ const retryNone = options.retryNone ?? false;
10373
10403
  const resolved = [];
10374
10404
  const none = [];
10405
+ const requeued = [];
10375
10406
  const failed = [];
10376
10407
  let cursor;
10377
10408
  let dryRun = options.dryRun;
10378
10409
  while (resolved.length + none.length + failed.length < limit) {
10379
10410
  const remaining = limit - (resolved.length + none.length + failed.length);
10380
- const result = await backfillCoverMastersCommand2(kind, remaining, options.dryRun, cursor);
10411
+ const result = await backfillCoverMastersCommand2(kind, remaining, options.dryRun, cursor, retryNone && cursor === undefined);
10381
10412
  dryRun = result.dryRun;
10382
10413
  resolved.push(...result.resolved);
10383
10414
  none.push(...result.none);
10415
+ requeued.push(...result.requeued ?? []);
10384
10416
  failed.push(...result.failed);
10385
10417
  if (!options.json) {
10386
10418
  const verb2 = result.dryRun ? "would resolve" : "resolved";
@@ -10401,6 +10433,8 @@ async function runBackfillCoverMasters(options, backfillCoverMastersCommand2) {
10401
10433
  noneCount: none.length,
10402
10434
  ok: true,
10403
10435
  rateLimited: false,
10436
+ requeued,
10437
+ requeuedCount: requeued.length,
10404
10438
  resolved,
10405
10439
  resolvedCount: resolved.length
10406
10440
  });
@@ -10410,7 +10444,8 @@ async function runBackfillCoverMasters(options, backfillCoverMastersCommand2) {
10410
10444
  return;
10411
10445
  }
10412
10446
  const verb = dryRun ? "Would resolve" : "Resolved";
10413
- console.log(`${verb} ${resolved.length} ${kind} cover master(s); ${none.length} without a source; ${failed.length} failed.`);
10447
+ const requeuedNote = retryNone ? ` (${dryRun ? "would re-queue" : "re-queued"} ${requeued.length} from none)` : "";
10448
+ console.log(`${verb} ${resolved.length} ${kind} cover master(s); ${none.length} without a source; ${failed.length} failed${requeuedNote}.`);
10414
10449
  for (const slug of resolved) {
10415
10450
  console.log(` ${slug}`);
10416
10451
  }
package/package.json CHANGED
@@ -31,5 +31,5 @@
31
31
  "url": "git+https://github.com/mauricekleine/fluncle.git"
32
32
  },
33
33
  "type": "module",
34
- "version": "0.169.0"
34
+ "version": "0.171.0"
35
35
  }