@sfmc-bds/cli 0.2.0-beta.0 → 0.2.0-beta.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"world-packs.d.ts","sourceRoot":"","sources":["../src/world-packs.ts"],"names":[],"mappings":"AAQA,OAAO,EAeL,KAAK,kBAAkB,EAExB,MAAM,iCAAiC,CAAC;AAmBzC,eAAO,MAAM,eAAe,YAAI,OAAO,EAAE,OAAO,CAAU,CAAC;AAC3D,eAAO,MAAM,iBAAiB,YAC5B,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,WAAW,EACX,MAAM,EACN,SAAS,EACT,MAAM,EACN,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,SAAS,EACT,OAAO,EACP,QAAQ,CACA,CAAC;AASX,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,GAAG,IAAI,MAAM,CAErE;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AA4ED,gDAAgD;AAChD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,YAAY,CAAC,GAAG,OAAO,CAGzF;AAuRD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,CAAC,EAAE;IAC/C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA2JlE;AA6HD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA8QnG;AAED,wBAAgB,UAAU,IAAI,MAAM,CAuBnC"}
1
+ {"version":3,"file":"world-packs.d.ts","sourceRoot":"","sources":["../src/world-packs.ts"],"names":[],"mappings":"AAKA,OAAO,EAcL,KAAK,kBAAkB,EAExB,MAAM,iCAAiC,CAAC;AAoBzC,eAAO,MAAM,eAAe,YAAI,OAAO,EAAE,OAAO,CAAU,CAAC;AAC3D,eAAO,MAAM,iBAAiB,YAC5B,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,WAAW,EACX,MAAM,EACN,SAAS,EACT,MAAM,EACN,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,SAAS,EACT,OAAO,EACP,QAAQ,CACA,CAAC;AAcX,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,GAAG,IAAI,MAAM,CAErE;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AA4ED,gDAAgD;AAChD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,YAAY,CAAC,GAAG,OAAO,CAGzF;AAqED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,kBAAkB,EAAE,EAC3B,GAAG,EAAE,MAAM,EAAE,GAEX;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,kBAAkB,EAAE,CAAA;CAAE,GAChD;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAC1C;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAgB1C;AAmND;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,CAAC,EAAE;IAC/C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAiJlE;AAqHD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA8PnG;AAED,wBAAgB,UAAU,IAAI,MAAM,CAuBnC"}
@@ -1,13 +1,8 @@
1
1
  import { confirm, isCancel, multiselect } from "@clack/prompts";
2
- import crypto from "node:crypto";
3
- import fs from "node:fs";
4
- import path from "node:path";
5
2
  import {
6
3
  bumpPackPatchVersion,
7
- discoverPackRoots,
8
4
  disableInstalledPack,
9
5
  enableInstalledPack,
10
- extractArchiveToTemp,
11
6
  findInstalledPackById,
12
7
  installPackDirectory,
13
8
  isPackArchive,
@@ -15,11 +10,18 @@ import {
15
10
  listWorldEnableListResult,
16
11
  readPackDependencyUuids,
17
12
  readPackManifestInfo,
13
+ resolvePackRoots,
18
14
  uninstallInstalledPack,
19
15
  worldPackParentDir
20
16
  } from "@sfmc-bds/bds-tools/world-packs";
17
+ import crypto from "node:crypto";
18
+ import fs from "node:fs";
19
+ import path from "node:path";
21
20
  import { t } from "./i18n/index.js";
21
+ import { pushLog } from "./logs.js";
22
22
  import { BP_NAME, RP_NAME, resolveBdsContext } from "./pack-lifecycle.js";
23
+ import { getBinding, packSourcesPath, removeBinding } from "./pack-update/bindings.js";
24
+ import { resolvePackTrashDir, resolveUninstallTrashDir } from "./pack-update/config.js";
23
25
  import {
24
26
  bindPackSource,
25
27
  bindingLabelForUuid,
@@ -28,11 +30,6 @@ import {
28
30
  probeSourceAfterInstall,
29
31
  searchRemote
30
32
  } from "./pack-update/service.js";
31
- import { getBinding, removeBinding, packSourcesPath } from "./pack-update/bindings.js";
32
- import {
33
- resolvePackTrashDir,
34
- resolveUninstallTrashDir
35
- } from "./pack-update/config.js";
36
33
  import { ROOT } from "./runtime.js";
37
34
  import { c } from "./theme.js";
38
35
  const PACKS_CMD_NAMES = ["packs", "addon"];
@@ -59,6 +56,9 @@ function restartHint() {
59
56
  function rejoinHint() {
60
57
  return t("packs.rejoinHint");
61
58
  }
59
+ function logPack(text, level = "info") {
60
+ pushLog(text, "pack", level);
61
+ }
62
62
  function isPacksCommand(cmd) {
63
63
  return !!cmd && PACKS_CMD_NAMES.includes(cmd);
64
64
  }
@@ -172,6 +172,23 @@ function formatUninstallPickLabel(pack) {
172
172
  const en = pack.enabled ? t("packs.list.on") : t("packs.list.off");
173
173
  return `[${kind}] ${pack.name} ${pack.folderName} ${en.trim()} ${pack.uuid.slice(0, 8)}`;
174
174
  }
175
+ function resolveUninstallTargets(packs, ids) {
176
+ const missing = [];
177
+ const byUuid = /* @__PURE__ */ new Map();
178
+ for (const id of ids) {
179
+ const pack = findInstalledPackById(packs, id);
180
+ if (!pack) {
181
+ missing.push(id);
182
+ continue;
183
+ }
184
+ byUuid.set(pack.uuid.toLowerCase(), pack);
185
+ }
186
+ if (missing.length) return { status: "not_found", missing };
187
+ const selected = [...byUuid.values()];
188
+ const blocked = selected.find((p) => isProtectedSfmcPack(p));
189
+ if (blocked) return { status: "protected", folder: blocked.folderName };
190
+ return { status: "ok", selected };
191
+ }
175
192
  async function pickPacksForUninstall(packs) {
176
193
  const candidates = packs.filter((p) => !isProtectedSfmcPack(p));
177
194
  if (candidates.length === 0) return [];
@@ -215,8 +232,8 @@ async function uninstallPacksBatch(selected, allPacks, opts) {
215
232
  const extra = q.pairedOf ? ` ${c.dim(t("packs.uninstall.pairedOf", { folder: q.pairedOf }))}` : "";
216
233
  return ` [${kind}] ${q.pack.folderName}${extra}`;
217
234
  }).join("\n");
218
- console.log(t("packs.uninstall.summary", { count: queue.length }));
219
- console.log(preview);
235
+ logPack(t("packs.uninstall.summary", { count: queue.length }), "info");
236
+ logPack(preview, "info");
220
237
  const ans = await confirm({
221
238
  message: t("packs.uninstall.confirm", { count: String(queue.length) }),
222
239
  initialValue: false
@@ -238,21 +255,23 @@ async function uninstallPacksBatch(selected, allPacks, opts) {
238
255
  return lines.join("\n");
239
256
  }
240
257
  function printConflict(existing, incoming) {
241
- console.log(c.yellow(t("packs.conflict")));
242
- console.log(
258
+ logPack(c.yellow(t("packs.conflict")), "warn");
259
+ logPack(
243
260
  t("packs.conflict.existing", {
244
261
  name: existing.name,
245
262
  version: fmtVer(existing.version),
246
263
  uuid: existing.uuid,
247
264
  dir: existing.dir
248
- })
265
+ }),
266
+ "info"
249
267
  );
250
- console.log(
268
+ logPack(
251
269
  t("packs.conflict.incoming", {
252
270
  name: incoming.name,
253
271
  version: fmtVer(incoming.version),
254
272
  uuid: incoming.uuid
255
- })
273
+ }),
274
+ "info"
256
275
  );
257
276
  }
258
277
  async function askOverwrite() {
@@ -265,7 +284,12 @@ async function askOverwrite() {
265
284
  return !!ans;
266
285
  }
267
286
  async function installOnePackRoot(opts) {
268
- const info = readPackManifestInfo(opts.srcDir);
287
+ let info;
288
+ try {
289
+ info = readPackManifestInfo(opts.srcDir);
290
+ } catch {
291
+ return { ok: false, reason: t("packs.badManifest") };
292
+ }
269
293
  if (!info) {
270
294
  return { ok: false, reason: t("packs.badManifest") };
271
295
  }
@@ -283,18 +307,18 @@ async function installOnePackRoot(opts) {
283
307
  } else if (opts.interactive && process.stdin.isTTY) {
284
308
  force = await askOverwrite();
285
309
  if (!force) {
286
- console.log(c.dim(t("packs.skippedNoOverwrite")));
310
+ logPack(c.dim(t("packs.skippedNoOverwrite")), "info");
287
311
  return { ok: false, skipped: true, reason: "conflict:skipped", info };
288
312
  }
289
313
  } else {
290
- console.log(c.yellow(t("packs.conflictSkip", { name: info.name })));
314
+ logPack(c.yellow(t("packs.conflictSkip", { name: info.name })), "warn");
291
315
  return { ok: false, skipped: true, reason: "conflict:noninteractive", info };
292
316
  }
293
317
  result = await installPackDirectory({
294
318
  srcDir: opts.srcDir,
295
319
  destParent,
296
- force: true,
297
- ...opts.folderHint ? { folderName: opts.folderHint } : {}
320
+ force: true
321
+ /* 路径由同 uuid 决策锁定,不依赖 folderHint,避免旁路目录 */
298
322
  });
299
323
  }
300
324
  if (!result.ok || !result.info || !result.destDir) {
@@ -313,7 +337,7 @@ async function installOnePackRoot(opts) {
313
337
  ...opts.interactive !== void 0 ? { interactive: opts.interactive } : {}
314
338
  });
315
339
  } catch (e) {
316
- console.log(c.yellow(`[packs] probe: ${e.message}`));
340
+ logPack(c.yellow(`probe: ${e.message}`), "warn");
317
341
  }
318
342
  return { ok: true, info: result.info };
319
343
  }
@@ -340,7 +364,7 @@ async function scanAndInstallInbox(opts) {
340
364
  try {
341
365
  ({ bdsRoot, levelName } = resolveBdsContext());
342
366
  } catch (e) {
343
- console.log(c.yellow(t("packs.inboxSkip", { message: e.message })));
367
+ logPack(c.yellow(t("packs.inboxSkip", { message: e.message })), "warn");
344
368
  return { installed: 0, skipped: 0, failed: candidates.length };
345
369
  }
346
370
  const interactive = opts?.interactive ?? !!process.stdin.isTTY;
@@ -350,12 +374,12 @@ async function scanAndInstallInbox(opts) {
350
374
  let installed = 0;
351
375
  let skipped = 0;
352
376
  let failed = 0;
353
- console.log(c.dim(t("packs.inboxFound", { count: candidates.length, level: levelName })));
377
+ logPack(c.dim(t("packs.inboxFound", { count: candidates.length, level: levelName })));
354
378
  for (const src of candidates) {
355
379
  const base = path.basename(src);
356
380
  const fp = sourceFingerprint(src);
357
381
  if (state.installed[fp] && !force) {
358
- console.log(c.dim(t("packs.alreadyDone", { name: base })));
382
+ logPack(c.dim(t("packs.alreadyDone", { name: base })));
359
383
  skipped++;
360
384
  if (!dryRun) {
361
385
  try {
@@ -365,38 +389,37 @@ async function scanAndInstallInbox(opts) {
365
389
  }
366
390
  continue;
367
391
  }
368
- let tempDir = null;
392
+ let disposeRoots = null;
369
393
  try {
370
394
  let roots = [];
371
- if (fs.statSync(src).isDirectory()) {
372
- roots = discoverPackRoots(src, { maxDepth: 2 });
373
- } else if (isPackArchive(src)) {
374
- if (dryRun) {
375
- console.log(c.dim(t("packs.dryRunExtract", { name: base })));
395
+ if (fs.statSync(src).isDirectory() || isPackArchive(src)) {
396
+ if (dryRun && isPackArchive(src) && fs.statSync(src).isFile()) {
397
+ logPack(c.dim(t("packs.dryRunExtract", { name: base })));
376
398
  skipped++;
377
399
  continue;
378
400
  }
379
- tempDir = await extractArchiveToTemp(src);
380
- roots = discoverPackRoots(tempDir, { maxDepth: 2 });
401
+ const resolved = await resolvePackRoots(src);
402
+ disposeRoots = resolved.dispose;
403
+ roots = resolved.roots;
381
404
  } else {
382
405
  failed++;
383
406
  if (!dryRun) moveTo(src, failedDir(), base);
384
407
  continue;
385
408
  }
386
409
  if (roots.length === 0) {
387
- console.log(c.red(t("packs.noPackRoot", { name: base })));
410
+ logPack(c.red(t("packs.noPackRoot", { name: base })), "error");
388
411
  failed++;
389
- if (!dryRun && !tempDir) moveTo(src, failedDir(), `${base}-no-root`);
412
+ if (!dryRun && fs.statSync(src).isDirectory()) moveTo(src, failedDir(), `${base}-no-root`);
390
413
  continue;
391
414
  }
392
415
  if (dryRun) {
393
416
  for (const r of roots) {
394
- const info = readPackManifestInfo(r);
395
- console.log(
396
- c.dim(
397
- `[packs] dry-run: ${base} \u2192 ${info?.kind ?? "?"} ${info?.name ?? path.basename(r)}`
398
- )
399
- );
417
+ try {
418
+ const info = readPackManifestInfo(r);
419
+ logPack(c.dim(`dry-run: ${base} \u2192 ${info?.kind ?? "?"} ${info?.name ?? path.basename(r)}`), "info");
420
+ } catch {
421
+ logPack(c.red(t("packs.badManifest")), "error");
422
+ }
400
423
  }
401
424
  skipped++;
402
425
  continue;
@@ -418,22 +441,21 @@ async function scanAndInstallInbox(opts) {
418
441
  if (r.ok && r.info) {
419
442
  okCount++;
420
443
  lastUuid = r.info.uuid;
421
- console.log(
444
+ logPack(
422
445
  c.green(
423
446
  t("packs.installedEnabled", {
424
447
  kind: r.info.kind === "resource" ? "RP" : "BP",
425
448
  name: r.info.name,
426
449
  version: fmtVer(r.info.version)
427
450
  })
428
- )
451
+ ),
452
+ "success"
429
453
  );
430
454
  } else if (r.skipped) {
431
455
  skipCount++;
432
456
  } else {
433
457
  failCount++;
434
- console.log(
435
- c.red(t("packs.installFailed", { name: path.basename(root), reason: r.reason ?? "?" }))
436
- );
458
+ logPack(c.red(t("packs.installFailed", { name: path.basename(root), reason: r.reason ?? "?" })), "error");
437
459
  }
438
460
  }
439
461
  installed += okCount > 0 ? 1 : 0;
@@ -443,12 +465,12 @@ async function scanAndInstallInbox(opts) {
443
465
  state.installed[fp] = { uuid: lastUuid, at: (/* @__PURE__ */ new Date()).toISOString(), folderName: base };
444
466
  writeState(state);
445
467
  moveTo(src, doneDir(), base);
446
- console.log(c.yellow(`[packs] ${restartHint()}`));
468
+ logPack(c.yellow(`${restartHint()}`), "warn");
447
469
  } else if (failCount > 0 && skipCount === 0) {
448
470
  moveTo(src, failedDir(), base);
449
471
  }
450
472
  } catch (e) {
451
- console.log(c.red(`[packs] ${base}: ${e.message}`));
473
+ logPack(c.red(`${base}: ${e.message}`), "error");
452
474
  failed++;
453
475
  if (!dryRun && fs.existsSync(src)) {
454
476
  try {
@@ -457,12 +479,7 @@ async function scanAndInstallInbox(opts) {
457
479
  }
458
480
  }
459
481
  } finally {
460
- if (tempDir) {
461
- try {
462
- fs.rmSync(tempDir, { recursive: true, force: true });
463
- } catch {
464
- }
465
- }
482
+ if (disposeRoots) disposeRoots();
466
483
  }
467
484
  }
468
485
  return { installed, skipped, failed };
@@ -489,9 +506,7 @@ function formatPackList(packs) {
489
506
  for (const p of bp) {
490
507
  const en = p.enabled ? c.green(t("packs.list.on")) : c.dim(t("packs.list.off"));
491
508
  const src = bindingLabelForUuid(p.uuid);
492
- lines.push(
493
- ` [${en}] ${p.folderName} ${p.name} v${fmtVer(p.version)} ${c.dim(p.uuid)} ${c.dim(src)}`
494
- );
509
+ lines.push(` [${en}] ${p.folderName} ${p.name} v${fmtVer(p.version)} ${c.dim(p.uuid)} ${c.dim(src)}`);
495
510
  }
496
511
  }
497
512
  if (rp.length) {
@@ -662,28 +677,15 @@ async function dispatchPacksCommand(sub, args) {
662
677
  if (picked === null) return c.dim(t("packs.uninstall.cancelled"));
663
678
  if (picked.length === 0) return c.yellow(t("packs.uninstall.none"));
664
679
  selected = picked;
665
- } else if (ids.length === 1) {
666
- const pack = findInstalledPackById(packs, ids[0]);
667
- if (!pack) return c.red(t("packs.notFound", { id: ids[0] }));
668
- if (isProtectedSfmcPack(pack)) {
669
- return c.red(t("packs.uninstall.protected", { folder: pack.folderName }));
670
- }
671
- selected = [pack];
672
680
  } else {
673
- const missing = [];
674
- const byUuid = /* @__PURE__ */ new Map();
675
- for (const id of ids) {
676
- const pack = findInstalledPackById(packs, id);
677
- if (!pack) {
678
- missing.push(id);
679
- continue;
680
- }
681
- byUuid.set(pack.uuid.toLowerCase(), pack);
681
+ const resolved = resolveUninstallTargets(packs, ids);
682
+ if (resolved.status === "not_found") {
683
+ return c.red(t("packs.notFound", { id: resolved.missing.join(", ") }));
682
684
  }
683
- if (missing.length) {
684
- return c.red(t("packs.notFound", { id: missing.join(", ") }));
685
+ if (resolved.status === "protected") {
686
+ return c.red(t("packs.uninstall.protected", { folder: resolved.folder }));
685
687
  }
686
- selected = [...byUuid.values()];
688
+ selected = resolved.selected;
687
689
  }
688
690
  return await uninstallPacksBatch(selected, packs, {
689
691
  bdsRoot,
@@ -744,17 +746,14 @@ async function dispatchPacksCommand(sub, args) {
744
746
  const abs = path.resolve(target);
745
747
  if (!fs.existsSync(abs)) return c.red(t("packs.pathMissing", { path: abs }));
746
748
  const { bdsRoot, levelName } = resolveBdsContext();
747
- let tempDir = null;
749
+ let disposeRoots = null;
748
750
  try {
749
- let roots;
750
- if (fs.statSync(abs).isDirectory()) {
751
- roots = discoverPackRoots(abs, { maxDepth: 2 });
752
- } else if (isPackArchive(abs)) {
753
- tempDir = await extractArchiveToTemp(abs);
754
- roots = discoverPackRoots(tempDir, { maxDepth: 2 });
755
- } else {
751
+ if (!fs.statSync(abs).isDirectory() && !isPackArchive(abs)) {
756
752
  return c.red(t("packs.notArchive"));
757
753
  }
754
+ const resolved = await resolvePackRoots(abs);
755
+ disposeRoots = resolved.dispose;
756
+ const roots = resolved.roots;
758
757
  if (roots.length === 0) return c.red(t("packs.noManifestRoot"));
759
758
  const lines = [];
760
759
  for (const root of roots) {
@@ -790,7 +789,7 @@ async function dispatchPacksCommand(sub, args) {
790
789
  lines.push(c.yellow(restartHint()));
791
790
  return lines.join("\n");
792
791
  } finally {
793
- if (tempDir) fs.rmSync(tempDir, { recursive: true, force: true });
792
+ if (disposeRoots) disposeRoots();
794
793
  }
795
794
  }
796
795
  case "scan": {
@@ -867,6 +866,7 @@ export {
867
866
  isProtectedSfmcPack,
868
867
  packsInboxDir,
869
868
  packsUsage,
869
+ resolveUninstallTargets,
870
870
  scanAndInstallInbox
871
871
  };
872
872
  //# sourceMappingURL=world-packs.js.map