fresh-squeezy 0.1.20 → 0.2.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.
package/dist/cli.js CHANGED
@@ -2477,6 +2477,10 @@ var pickLauncherAction = async () => {
2477
2477
  name: "Run doctor now \u2014 pick stores interactively when needed",
2478
2478
  value: "doctor"
2479
2479
  },
2480
+ {
2481
+ name: "Manage resources \u2014 bulk delete / cancel / refund",
2482
+ value: "manage"
2483
+ },
2480
2484
  {
2481
2485
  name: "Show command examples \u2014 copy/paste friendly",
2482
2486
  value: "examples"
@@ -2712,6 +2716,29 @@ var isPromptCancel = (error) => {
2712
2716
  if (!(error instanceof Error)) return false;
2713
2717
  return error.name === "ExitPromptError" || error.message.includes("User force closed");
2714
2718
  };
2719
+ var pickManageOperation = async (ops) => {
2720
+ return select({
2721
+ message: "Which operation?",
2722
+ theme: PROMPT_THEME,
2723
+ choices: ops.map((op) => ({ name: `${op.verb} ${op.resource}`, value: op }))
2724
+ });
2725
+ };
2726
+ var askParentId = async (resource) => {
2727
+ const value = await input({
2728
+ message: `Parent id to list ${resource} items from:`,
2729
+ theme: PROMPT_THEME,
2730
+ validate: (entry) => entry.trim().length > 0 ? true : "A parent id is required."
2731
+ });
2732
+ return value.trim();
2733
+ };
2734
+ var pickBulkTargets = async (verb, items) => {
2735
+ return checkbox({
2736
+ message: `Pick items to ${verb} (space toggles, a selects all, enter confirms):`,
2737
+ theme: PROMPT_THEME,
2738
+ choices: items.map((item) => ({ name: `${item.label} \u2014 id ${item.id}`, value: item.id })),
2739
+ validate: (selected) => selected.length > 0 ? true : "Pick at least one item."
2740
+ });
2741
+ };
2715
2742
 
2716
2743
  // src/cli/resolveStores.ts
2717
2744
  var resolveStores = async (client, input2) => {
@@ -3295,81 +3322,6 @@ var pathExists = async (filePath) => {
3295
3322
  }
3296
3323
  };
3297
3324
 
3298
- // src/cli/commands/launcher.ts
3299
- var runLauncherCommand = async (options) => {
3300
- if (!options.isInteractive) return 2;
3301
- try {
3302
- process.stdout.write(
3303
- renderBrandHeader(
3304
- "Project bootstrap",
3305
- "Install the billing doctor locally, then verify Lemon Squeezy in one guided run."
3306
- )
3307
- );
3308
- process.stdout.write(renderStep(1, 2, "Project dependency", "add fresh-squeezy when missing"));
3309
- const installResult = await ensureFreshSqueezyDevDependency({
3310
- skipInstall: options.install === false,
3311
- packageVersion: options.packageVersion
3312
- });
3313
- process.stdout.write(renderProjectInstallResult(installResult));
3314
- if (!process.env[ENV_KEYS.apiKey]?.trim()) {
3315
- return startGuidedSetup();
3316
- }
3317
- return routeLauncherAction(await pickLauncherAction());
3318
- } catch (err) {
3319
- if (isPromptCancel(err)) {
3320
- process.stderr.write(renderCancelMessage());
3321
- return 130;
3322
- }
3323
- const message = err instanceof Error ? err.message : String(err);
3324
- process.stderr.write(`${chalk4.red("fresh-squeezy bootstrap failed:")} ${message}
3325
-
3326
- `);
3327
- process.stderr.write(renderCommandExamples());
3328
- return 1;
3329
- }
3330
- };
3331
- var routeLauncherAction = async (action) => {
3332
- switch (action) {
3333
- case "init":
3334
- return startGuidedSetup();
3335
- case "doctor":
3336
- return runDoctorCommand({ isInteractive: true });
3337
- case "examples":
3338
- process.stdout.write(renderCommandExamples());
3339
- return 0;
3340
- case "exit":
3341
- return 0;
3342
- }
3343
- };
3344
- var startGuidedSetup = () => {
3345
- process.stdout.write(
3346
- renderStep(2, 2, "Guided setup", "detect credentials, stores, and resources")
3347
- );
3348
- return runInitCommand({ isInteractive: true });
3349
- };
3350
- var renderProjectInstallResult = (result) => {
3351
- if (result.status === "installed") {
3352
- return renderDetected(
3353
- "Dev dependency",
3354
- "fresh-squeezy",
3355
- `${result.packageManager ?? "npm"} install`
3356
- );
3357
- }
3358
- if (result.status === "already-present") {
3359
- return renderDetected("Dev dependency", "fresh-squeezy", "package.json");
3360
- }
3361
- if (result.status === "self-package") {
3362
- return renderDetected("Project", "fresh-squeezy source repo", "package.json");
3363
- }
3364
- if (result.status === "skipped") {
3365
- return chalk4.dim(" Skipped dev dependency install (--no-install).\n");
3366
- }
3367
- return chalk4.yellow(" No package.json found; running without installing a dev dependency.\n");
3368
- };
3369
-
3370
- // src/cli/commands/resourceOps.ts
3371
- import { readFileSync } from "fs";
3372
-
3373
3325
  // src/cli/commands/bulkOps.ts
3374
3326
  var supportsBulk = (spec) => Boolean(spec.destructive) && spec.idRole === "id";
3375
3327
  var describeBulkTarget = (item) => {
@@ -3456,7 +3408,166 @@ ${tail}
3456
3408
  `;
3457
3409
  };
3458
3410
 
3411
+ // src/cli/commands/manage.ts
3412
+ var listManageOperations = () => resourceRegistry.filter((spec) => supportsBulk(spec) && findResourceVerb(spec.resource, "list")).map((spec) => ({ resource: spec.resource, verb: spec.verb }));
3413
+ var renderEquivalentCommand = (input2) => {
3414
+ const parts = ["fresh-squeezy", input2.verb, input2.resource];
3415
+ if (input2.picked.length === input2.total) {
3416
+ parts.push("--all");
3417
+ if (input2.storeId) parts.push("--store-ids", input2.storeId);
3418
+ if (input2.parentId) parts.push("--parent-id", input2.parentId);
3419
+ } else {
3420
+ parts.push("--ids", input2.picked.join(","));
3421
+ }
3422
+ parts.push("--yes");
3423
+ if (input2.mode === "live") parts.push("--mode", "live");
3424
+ return parts.join(" ");
3425
+ };
3426
+ var runManageCommand = async () => {
3427
+ try {
3428
+ const client = createFreshSqueezy();
3429
+ const http = new HttpClient(resolveConfig());
3430
+ const op = await pickManageOperation(listManageOperations());
3431
+ const spec = findResourceVerb(op.resource, op.verb);
3432
+ const listSpec = findResourceVerb(op.resource, "list");
3433
+ if (!spec || !listSpec) {
3434
+ throw new FreshSqueezyError({
3435
+ code: "UNKNOWN_OP",
3436
+ message: `${op.verb} ${op.resource} is not available.`
3437
+ });
3438
+ }
3439
+ process.stdout.write(`
3440
+ ${op.verb} ${op.resource} \u2014 mode=${client.mode}
3441
+ `);
3442
+ let scopes = [{}];
3443
+ if (listSpec.idRole === "store") {
3444
+ const { storeIds } = await resolveStores(client, { isInteractive: true });
3445
+ scopes = storeIds.map((storeId) => ({ storeId }));
3446
+ } else if (listSpec.idRole === "parent") {
3447
+ scopes = [{ parentId: await askParentId(op.resource) }];
3448
+ }
3449
+ let exitCode = 0;
3450
+ for (const scope of scopes) {
3451
+ const targets = await collectBulkTargets(http, spec, { all: true, ...scope });
3452
+ if (targets.length === 0) {
3453
+ process.stdout.write(
3454
+ `No ${op.resource} items found${scope.storeId ? ` in store ${scope.storeId}` : ""}.
3455
+ `
3456
+ );
3457
+ continue;
3458
+ }
3459
+ const pickedIds = await pickBulkTargets(op.verb, targets);
3460
+ const picked = targets.filter((t) => pickedIds.includes(t.id));
3461
+ const mark = client.mode === "live" ? "LIVE mode: " : "";
3462
+ const go = await confirmResourceOp(
3463
+ `${mark}${op.verb} ${picked.length} ${op.resource} item(s) (${previewTargets(picked)})?`
3464
+ );
3465
+ if (!go) {
3466
+ process.stdout.write("Skipped \u2014 nothing changed.\n");
3467
+ continue;
3468
+ }
3469
+ const summary = await executeBulk(http, spec, picked);
3470
+ process.stdout.write(renderBulkSummary(spec, summary));
3471
+ process.stdout.write(
3472
+ `
3473
+ Next time:
3474
+ ${renderEquivalentCommand({
3475
+ verb: op.verb,
3476
+ resource: op.resource,
3477
+ mode: client.mode,
3478
+ ...scope,
3479
+ picked: pickedIds,
3480
+ total: targets.length
3481
+ })}
3482
+ `
3483
+ );
3484
+ if (summary.failed > 0) exitCode = 1;
3485
+ }
3486
+ return exitCode;
3487
+ } catch (err) {
3488
+ if (isPromptCancel(err)) throw err;
3489
+ const message = err instanceof Error ? err.message : String(err);
3490
+ process.stderr.write(renderCliError(message, ["fresh-squeezy ops --list"]));
3491
+ return 2;
3492
+ }
3493
+ };
3494
+
3495
+ // src/cli/commands/launcher.ts
3496
+ var runLauncherCommand = async (options) => {
3497
+ if (!options.isInteractive) return 2;
3498
+ try {
3499
+ process.stdout.write(
3500
+ renderBrandHeader(
3501
+ "Project bootstrap",
3502
+ "Install the billing doctor locally, then verify Lemon Squeezy in one guided run."
3503
+ )
3504
+ );
3505
+ process.stdout.write(renderStep(1, 2, "Project dependency", "add fresh-squeezy when missing"));
3506
+ const installResult = await ensureFreshSqueezyDevDependency({
3507
+ skipInstall: options.install === false,
3508
+ packageVersion: options.packageVersion
3509
+ });
3510
+ process.stdout.write(renderProjectInstallResult(installResult));
3511
+ if (!process.env[ENV_KEYS.apiKey]?.trim()) {
3512
+ return startGuidedSetup();
3513
+ }
3514
+ return routeLauncherAction(await pickLauncherAction());
3515
+ } catch (err) {
3516
+ if (isPromptCancel(err)) {
3517
+ process.stderr.write(renderCancelMessage());
3518
+ return 130;
3519
+ }
3520
+ const message = err instanceof Error ? err.message : String(err);
3521
+ process.stderr.write(`${chalk4.red("fresh-squeezy bootstrap failed:")} ${message}
3522
+
3523
+ `);
3524
+ process.stderr.write(renderCommandExamples());
3525
+ return 1;
3526
+ }
3527
+ };
3528
+ var routeLauncherAction = async (action) => {
3529
+ switch (action) {
3530
+ case "init":
3531
+ return startGuidedSetup();
3532
+ case "doctor":
3533
+ return runDoctorCommand({ isInteractive: true });
3534
+ case "manage":
3535
+ return runManageCommand();
3536
+ case "examples":
3537
+ process.stdout.write(renderCommandExamples());
3538
+ return 0;
3539
+ case "exit":
3540
+ return 0;
3541
+ }
3542
+ };
3543
+ var startGuidedSetup = () => {
3544
+ process.stdout.write(
3545
+ renderStep(2, 2, "Guided setup", "detect credentials, stores, and resources")
3546
+ );
3547
+ return runInitCommand({ isInteractive: true });
3548
+ };
3549
+ var renderProjectInstallResult = (result) => {
3550
+ if (result.status === "installed") {
3551
+ return renderDetected(
3552
+ "Dev dependency",
3553
+ "fresh-squeezy",
3554
+ `${result.packageManager ?? "npm"} install`
3555
+ );
3556
+ }
3557
+ if (result.status === "already-present") {
3558
+ return renderDetected("Dev dependency", "fresh-squeezy", "package.json");
3559
+ }
3560
+ if (result.status === "self-package") {
3561
+ return renderDetected("Project", "fresh-squeezy source repo", "package.json");
3562
+ }
3563
+ if (result.status === "skipped") {
3564
+ return chalk4.dim(" Skipped dev dependency install (--no-install).\n");
3565
+ }
3566
+ return chalk4.yellow(" No package.json found; running without installing a dev dependency.\n");
3567
+ };
3568
+
3459
3569
  // src/cli/commands/resourceOps.ts
3570
+ import { readFileSync } from "fs";
3460
3571
  var runResourceOpCommand = async (options) => {
3461
3572
  if (options.listCatalog) {
3462
3573
  writeCatalog(options.json ?? false);