fresh-squeezy 0.1.20 → 0.3.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/README.es.md +1 -1
- package/README.ja.md +1 -1
- package/README.md +15 -1
- package/README.pt-BR.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/cli.js +882 -1745
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +194 -1048
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +558 -1302
- package/dist/index.d.ts +558 -1302
- package/dist/index.js +194 -991
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,21 @@ import { readFileSync as readFileSync2 } from "fs";
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import dotenv from "dotenv";
|
|
7
7
|
|
|
8
|
+
// src/core/errors.ts
|
|
9
|
+
var FreshSqueezyError = class _FreshSqueezyError extends Error {
|
|
10
|
+
code;
|
|
11
|
+
status;
|
|
12
|
+
detail;
|
|
13
|
+
constructor(opts) {
|
|
14
|
+
super(opts.message);
|
|
15
|
+
this.name = "FreshSqueezyError";
|
|
16
|
+
this.code = opts.code;
|
|
17
|
+
this.status = opts.status;
|
|
18
|
+
this.detail = opts.detail;
|
|
19
|
+
Object.setPrototypeOf(this, _FreshSqueezyError.prototype);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
8
23
|
// src/cli/commands/augment.ts
|
|
9
24
|
import { existsSync } from "fs";
|
|
10
25
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
@@ -217,21 +232,6 @@ export type {
|
|
|
217
232
|
`;
|
|
218
233
|
};
|
|
219
234
|
|
|
220
|
-
// src/core/errors.ts
|
|
221
|
-
var FreshSqueezyError = class _FreshSqueezyError extends Error {
|
|
222
|
-
code;
|
|
223
|
-
status;
|
|
224
|
-
detail;
|
|
225
|
-
constructor(opts) {
|
|
226
|
-
super(opts.message);
|
|
227
|
-
this.name = "FreshSqueezyError";
|
|
228
|
-
this.code = opts.code;
|
|
229
|
-
this.status = opts.status;
|
|
230
|
-
this.detail = opts.detail;
|
|
231
|
-
Object.setPrototypeOf(this, _FreshSqueezyError.prototype);
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
|
|
235
235
|
// src/core/config.ts
|
|
236
236
|
var DEFAULT_BASE_URL = "https://api.lemonsqueezy.com";
|
|
237
237
|
var ENV_KEYS = {
|
|
@@ -314,46 +314,6 @@ var HttpClient = class {
|
|
|
314
314
|
const doc = await this.request({ path: path4 });
|
|
315
315
|
return doc.data;
|
|
316
316
|
}
|
|
317
|
-
/**
|
|
318
|
-
* POST a JSON:API document and return the created `data` resource.
|
|
319
|
-
*
|
|
320
|
-
* @param path - API path starting with `/v1/...`.
|
|
321
|
-
* @param body - JSON:API create document.
|
|
322
|
-
* @returns The created resource.
|
|
323
|
-
*/
|
|
324
|
-
async postResource(path4, body) {
|
|
325
|
-
const doc = await this.request({ method: "POST", path: path4, body });
|
|
326
|
-
return doc.data;
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* PATCH a JSON:API document and return the updated `data` resource.
|
|
330
|
-
*
|
|
331
|
-
* @param path - API path starting with `/v1/...`.
|
|
332
|
-
* @param body - JSON:API update document.
|
|
333
|
-
* @returns The updated resource.
|
|
334
|
-
*/
|
|
335
|
-
async patchResource(path4, body) {
|
|
336
|
-
const doc = await this.request({ method: "PATCH", path: path4, body });
|
|
337
|
-
return doc.data;
|
|
338
|
-
}
|
|
339
|
-
/**
|
|
340
|
-
* DELETE a resource. Lemon Squeezy often returns an empty body on success.
|
|
341
|
-
*
|
|
342
|
-
* @param path - API path starting with `/v1/...`.
|
|
343
|
-
* @returns Nothing — single void return, not a multi-field bag.
|
|
344
|
-
*/
|
|
345
|
-
async deleteResource(path4) {
|
|
346
|
-
await this.request({ method: "DELETE", path: path4 });
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* Fetch a single page of a JSON:API collection. Most callers want
|
|
350
|
-
* `paginate()` instead — this method is kept for one-shot lookups where
|
|
351
|
-
* the caller knows the result fits in a single page.
|
|
352
|
-
*/
|
|
353
|
-
async getCollection(path4, query) {
|
|
354
|
-
const doc = await this.request({ path: path4, query });
|
|
355
|
-
return doc.data;
|
|
356
|
-
}
|
|
357
317
|
/**
|
|
358
318
|
* Walk a JSON:API collection across every page and concatenate the
|
|
359
319
|
* results. Lemon Squeezy returns 25 items per page by default; without
|
|
@@ -416,859 +376,101 @@ var extractJsonApiErrors = (responseBody) => {
|
|
|
416
376
|
);
|
|
417
377
|
};
|
|
418
378
|
|
|
419
|
-
// src/resources/affiliates.ts
|
|
420
|
-
var getAffiliate = async (http, affiliateId) => {
|
|
421
|
-
return http.getResource(`/v1/affiliates/${affiliateId}`);
|
|
422
|
-
};
|
|
423
|
-
var listAffiliatesForStore = async (http, storeId) => {
|
|
424
|
-
return http.paginate("/v1/affiliates", {
|
|
425
|
-
"filter[store_id]": String(storeId)
|
|
426
|
-
});
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
// src/resources/checkouts.ts
|
|
430
|
-
var getCheckout = async (http, checkoutId) => {
|
|
431
|
-
return http.getResource(`/v1/checkouts/${checkoutId}`);
|
|
432
|
-
};
|
|
433
|
-
var listCheckoutsForStore = async (http, storeId) => {
|
|
434
|
-
return http.paginate("/v1/checkouts", {
|
|
435
|
-
"filter[store_id]": String(storeId)
|
|
436
|
-
});
|
|
437
|
-
};
|
|
438
|
-
var createCheckout = async (http, body) => {
|
|
439
|
-
return http.postResource("/v1/checkouts", body);
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
// src/resources/customers.ts
|
|
443
|
-
var getCustomer = async (http, customerId) => {
|
|
444
|
-
return http.getResource(`/v1/customers/${customerId}`);
|
|
445
|
-
};
|
|
446
|
-
var listCustomersForStore = async (http, storeId) => {
|
|
447
|
-
return http.paginate("/v1/customers", {
|
|
448
|
-
"filter[store_id]": String(storeId)
|
|
449
|
-
});
|
|
450
|
-
};
|
|
451
|
-
var createCustomer = async (http, body) => {
|
|
452
|
-
return http.postResource("/v1/customers", body);
|
|
453
|
-
};
|
|
454
|
-
var updateCustomer = async (http, customerId, body) => {
|
|
455
|
-
return http.patchResource(`/v1/customers/${customerId}`, body);
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
// src/resources/discountRedemptions.ts
|
|
459
|
-
var getDiscountRedemption = async (http, discountRedemptionId) => {
|
|
460
|
-
return http.getResource(
|
|
461
|
-
`/v1/discount-redemptions/${discountRedemptionId}`
|
|
462
|
-
);
|
|
463
|
-
};
|
|
464
|
-
var listDiscountRedemptionsForDiscount = async (http, discountId) => {
|
|
465
|
-
return http.paginate("/v1/discount-redemptions", {
|
|
466
|
-
"filter[discount_id]": String(discountId)
|
|
467
|
-
});
|
|
468
|
-
};
|
|
469
|
-
|
|
470
|
-
// src/resources/discounts.ts
|
|
471
|
-
var getDiscount = async (http, discountId) => {
|
|
472
|
-
return http.getResource(`/v1/discounts/${discountId}`);
|
|
473
|
-
};
|
|
474
|
-
var listDiscountsForStore = async (http, storeId) => {
|
|
475
|
-
return http.paginate("/v1/discounts", {
|
|
476
|
-
"filter[store_id]": String(storeId)
|
|
477
|
-
});
|
|
478
|
-
};
|
|
479
|
-
var createDiscount = async (http, body) => {
|
|
480
|
-
return http.postResource("/v1/discounts", body);
|
|
481
|
-
};
|
|
482
|
-
var deleteDiscount = async (http, discountId) => {
|
|
483
|
-
await http.deleteResource(`/v1/discounts/${discountId}`);
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
// src/resources/files.ts
|
|
487
|
-
var getFile = async (http, fileId) => {
|
|
488
|
-
return http.getResource(`/v1/files/${fileId}`);
|
|
489
|
-
};
|
|
490
|
-
var listFilesForVariant = async (http, variantId) => {
|
|
491
|
-
return http.paginate("/v1/files", {
|
|
492
|
-
"filter[variant_id]": String(variantId)
|
|
493
|
-
});
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
// src/resources/licenseKeyInstances.ts
|
|
497
|
-
var getLicenseKeyInstance = async (http, licenseKeyInstanceId) => {
|
|
498
|
-
return http.getResource(
|
|
499
|
-
`/v1/license-key-instances/${licenseKeyInstanceId}`
|
|
500
|
-
);
|
|
501
|
-
};
|
|
502
|
-
var listLicenseKeyInstancesForLicenseKey = async (http, licenseKeyId) => {
|
|
503
|
-
return http.paginate("/v1/license-key-instances", {
|
|
504
|
-
"filter[license_key_id]": String(licenseKeyId)
|
|
505
|
-
});
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
// src/resources/licenseKeys.ts
|
|
509
|
-
var getLicenseKey = async (http, licenseKeyId) => {
|
|
510
|
-
return http.getResource(`/v1/license-keys/${licenseKeyId}`);
|
|
511
|
-
};
|
|
512
|
-
var listLicenseKeysForStore = async (http, storeId) => {
|
|
513
|
-
return http.paginate("/v1/license-keys", {
|
|
514
|
-
"filter[store_id]": String(storeId)
|
|
515
|
-
});
|
|
516
|
-
};
|
|
517
|
-
var updateLicenseKey = async (http, licenseKeyId, body) => {
|
|
518
|
-
return http.patchResource(`/v1/license-keys/${licenseKeyId}`, body);
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
// src/resources/orderItems.ts
|
|
522
|
-
var getOrderItem = async (http, orderItemId) => {
|
|
523
|
-
return http.getResource(`/v1/order-items/${orderItemId}`);
|
|
524
|
-
};
|
|
525
|
-
var listOrderItemsForOrder = async (http, orderId) => {
|
|
526
|
-
return http.paginate("/v1/order-items", {
|
|
527
|
-
"filter[order_id]": String(orderId)
|
|
528
|
-
});
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
// src/resources/orders.ts
|
|
532
|
-
var getOrder = async (http, orderId) => {
|
|
533
|
-
return http.getResource(`/v1/orders/${orderId}`);
|
|
534
|
-
};
|
|
535
|
-
var listOrdersForStore = async (http, storeId) => {
|
|
536
|
-
return http.paginate("/v1/orders", {
|
|
537
|
-
"filter[store_id]": String(storeId)
|
|
538
|
-
});
|
|
539
|
-
};
|
|
540
|
-
var refundOrder = async (http, orderId, body) => {
|
|
541
|
-
return http.postResource(`/v1/orders/${orderId}/refund`, body ?? {});
|
|
542
|
-
};
|
|
543
|
-
var generateOrderInvoice = async (http, orderId, body) => {
|
|
544
|
-
return http.request({
|
|
545
|
-
method: "POST",
|
|
546
|
-
path: `/v1/orders/${orderId}/generate-invoice`,
|
|
547
|
-
body: body ?? {}
|
|
548
|
-
});
|
|
549
|
-
};
|
|
550
|
-
|
|
551
|
-
// src/resources/prices.ts
|
|
552
|
-
var getPrice = async (http, priceId) => {
|
|
553
|
-
return http.getResource(`/v1/prices/${priceId}`);
|
|
554
|
-
};
|
|
555
|
-
var listPricesForVariant = async (http, variantId) => {
|
|
556
|
-
return http.paginate("/v1/prices", {
|
|
557
|
-
"filter[variant_id]": String(variantId)
|
|
558
|
-
});
|
|
559
|
-
};
|
|
560
|
-
|
|
561
|
-
// src/resources/products.ts
|
|
562
|
-
var getProduct = async (http, productId) => {
|
|
563
|
-
return http.getResource(`/v1/products/${productId}`);
|
|
564
|
-
};
|
|
565
|
-
var listProducts = async (http, storeId) => {
|
|
566
|
-
return http.paginate("/v1/products", {
|
|
567
|
-
"filter[store_id]": String(storeId)
|
|
568
|
-
});
|
|
569
|
-
};
|
|
570
|
-
|
|
571
379
|
// src/resources/registry.ts
|
|
572
|
-
var
|
|
573
|
-
|
|
574
|
-
{
|
|
575
|
-
|
|
576
|
-
{
|
|
577
|
-
{
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
},
|
|
584
|
-
// products (read-only)
|
|
585
|
-
{
|
|
586
|
-
resource: "product",
|
|
587
|
-
verb: "get",
|
|
588
|
-
docsPath: "products/retrieve-product",
|
|
589
|
-
body: "none",
|
|
590
|
-
idRole: "id"
|
|
591
|
-
},
|
|
592
|
-
{
|
|
593
|
-
resource: "product",
|
|
594
|
-
verb: "list",
|
|
595
|
-
docsPath: "products/list-all-products",
|
|
596
|
-
body: "none",
|
|
597
|
-
idRole: "store"
|
|
598
|
-
},
|
|
599
|
-
// variants (read-only)
|
|
600
|
-
{
|
|
601
|
-
resource: "variant",
|
|
602
|
-
verb: "get",
|
|
603
|
-
docsPath: "variants/retrieve-variant",
|
|
604
|
-
body: "none",
|
|
605
|
-
idRole: "id"
|
|
606
|
-
},
|
|
607
|
-
{
|
|
608
|
-
resource: "variant",
|
|
609
|
-
verb: "list",
|
|
610
|
-
docsPath: "variants/list-all-variants",
|
|
611
|
-
body: "none",
|
|
612
|
-
idRole: "parent"
|
|
613
|
-
},
|
|
614
|
-
// prices (read-only)
|
|
615
|
-
{ resource: "price", verb: "get", docsPath: "prices/retrieve-price", body: "none", idRole: "id" },
|
|
616
|
-
{
|
|
617
|
-
resource: "price",
|
|
618
|
-
verb: "list",
|
|
619
|
-
docsPath: "prices/list-all-prices",
|
|
620
|
-
body: "none",
|
|
621
|
-
idRole: "parent"
|
|
622
|
-
},
|
|
623
|
-
// files (read-only)
|
|
624
|
-
{ resource: "file", verb: "get", docsPath: "files/retrieve-file", body: "none", idRole: "id" },
|
|
625
|
-
{
|
|
626
|
-
resource: "file",
|
|
627
|
-
verb: "list",
|
|
628
|
-
docsPath: "files/list-all-files",
|
|
629
|
-
body: "none",
|
|
630
|
-
idRole: "parent"
|
|
631
|
-
},
|
|
632
|
-
// customers
|
|
633
|
-
{
|
|
634
|
-
resource: "customer",
|
|
635
|
-
verb: "get",
|
|
636
|
-
docsPath: "customers/retrieve-customer",
|
|
637
|
-
body: "none",
|
|
638
|
-
idRole: "id"
|
|
639
|
-
},
|
|
640
|
-
{
|
|
641
|
-
resource: "customer",
|
|
642
|
-
verb: "list",
|
|
643
|
-
docsPath: "customers/list-all-customers",
|
|
644
|
-
body: "none",
|
|
645
|
-
idRole: "store"
|
|
646
|
-
},
|
|
647
|
-
{
|
|
648
|
-
resource: "customer",
|
|
649
|
-
verb: "create",
|
|
650
|
-
docsPath: "customers/create-customer",
|
|
651
|
-
body: "required",
|
|
652
|
-
idRole: "none"
|
|
653
|
-
},
|
|
654
|
-
{
|
|
655
|
-
resource: "customer",
|
|
656
|
-
verb: "update",
|
|
657
|
-
docsPath: "customers/update-customer",
|
|
658
|
-
body: "required",
|
|
659
|
-
idRole: "id"
|
|
660
|
-
},
|
|
661
|
-
// orders
|
|
662
|
-
{ resource: "order", verb: "get", docsPath: "orders/retrieve-order", body: "none", idRole: "id" },
|
|
663
|
-
{
|
|
664
|
-
resource: "order",
|
|
665
|
-
verb: "list",
|
|
666
|
-
docsPath: "orders/list-all-orders",
|
|
667
|
-
body: "none",
|
|
668
|
-
idRole: "store"
|
|
669
|
-
},
|
|
670
|
-
{
|
|
671
|
-
resource: "order",
|
|
672
|
-
verb: "refund",
|
|
673
|
-
docsPath: "orders/issue-refund",
|
|
674
|
-
body: "optional",
|
|
675
|
-
destructive: true,
|
|
676
|
-
idRole: "id"
|
|
677
|
-
},
|
|
678
|
-
{
|
|
679
|
-
resource: "order",
|
|
680
|
-
verb: "generate-invoice",
|
|
681
|
-
docsPath: "orders/generate-order-invoice",
|
|
682
|
-
body: "optional",
|
|
683
|
-
idRole: "id"
|
|
684
|
-
},
|
|
685
|
-
// order-items (read-only)
|
|
686
|
-
{
|
|
687
|
-
resource: "order-item",
|
|
688
|
-
verb: "get",
|
|
689
|
-
docsPath: "order-items/retrieve-order-item",
|
|
690
|
-
body: "none",
|
|
691
|
-
idRole: "id"
|
|
692
|
-
},
|
|
693
|
-
{
|
|
694
|
-
resource: "order-item",
|
|
695
|
-
verb: "list",
|
|
696
|
-
docsPath: "order-items/list-all-order-items",
|
|
697
|
-
body: "none",
|
|
698
|
-
idRole: "parent"
|
|
699
|
-
},
|
|
700
|
-
// subscriptions
|
|
701
|
-
{
|
|
702
|
-
resource: "subscription",
|
|
703
|
-
verb: "get",
|
|
704
|
-
docsPath: "subscriptions/retrieve-subscription",
|
|
705
|
-
body: "none",
|
|
706
|
-
idRole: "id"
|
|
707
|
-
},
|
|
708
|
-
{
|
|
709
|
-
resource: "subscription",
|
|
710
|
-
verb: "list",
|
|
711
|
-
docsPath: "subscriptions/list-all-subscriptions",
|
|
712
|
-
body: "none",
|
|
713
|
-
idRole: "store"
|
|
714
|
-
},
|
|
715
|
-
{
|
|
716
|
-
resource: "subscription",
|
|
717
|
-
verb: "update",
|
|
718
|
-
docsPath: "subscriptions/update-subscription",
|
|
719
|
-
body: "required",
|
|
720
|
-
idRole: "id"
|
|
721
|
-
},
|
|
722
|
-
{
|
|
723
|
-
resource: "subscription",
|
|
724
|
-
verb: "cancel",
|
|
725
|
-
docsPath: "subscriptions/cancel-subscription",
|
|
726
|
-
body: "none",
|
|
727
|
-
destructive: true,
|
|
728
|
-
idRole: "id"
|
|
729
|
-
},
|
|
730
|
-
// subscription-items
|
|
731
|
-
{
|
|
732
|
-
resource: "subscription-item",
|
|
733
|
-
verb: "get",
|
|
734
|
-
docsPath: "subscription-items/retrieve-subscription-item",
|
|
735
|
-
body: "none",
|
|
736
|
-
idRole: "id"
|
|
737
|
-
},
|
|
738
|
-
{
|
|
739
|
-
resource: "subscription-item",
|
|
740
|
-
verb: "list",
|
|
741
|
-
docsPath: "subscription-items/list-all-subscription-items",
|
|
742
|
-
body: "none",
|
|
743
|
-
idRole: "parent"
|
|
744
|
-
},
|
|
745
|
-
{
|
|
746
|
-
resource: "subscription-item",
|
|
747
|
-
verb: "update",
|
|
748
|
-
docsPath: "subscription-items/update-subscription-item",
|
|
749
|
-
body: "required",
|
|
750
|
-
idRole: "id"
|
|
751
|
-
},
|
|
752
|
-
{
|
|
753
|
-
resource: "subscription-item",
|
|
754
|
-
verb: "current-usage",
|
|
755
|
-
docsPath: "subscription-items/retrieve-subscription-item-current-usage",
|
|
756
|
-
body: "none",
|
|
757
|
-
idRole: "id"
|
|
758
|
-
},
|
|
759
|
-
// subscription-invoices
|
|
760
|
-
{
|
|
761
|
-
resource: "subscription-invoice",
|
|
762
|
-
verb: "get",
|
|
763
|
-
docsPath: "subscription-invoices/retrieve-subscription-invoice",
|
|
764
|
-
body: "none",
|
|
765
|
-
idRole: "id"
|
|
766
|
-
},
|
|
767
|
-
{
|
|
768
|
-
resource: "subscription-invoice",
|
|
769
|
-
verb: "list",
|
|
770
|
-
docsPath: "subscription-invoices/list-all-subscription-invoices",
|
|
771
|
-
body: "none",
|
|
772
|
-
idRole: "parent"
|
|
773
|
-
},
|
|
774
|
-
{
|
|
775
|
-
resource: "subscription-invoice",
|
|
776
|
-
verb: "refund",
|
|
777
|
-
docsPath: "subscription-invoices/issue-refund",
|
|
778
|
-
body: "optional",
|
|
779
|
-
destructive: true,
|
|
780
|
-
idRole: "id"
|
|
781
|
-
},
|
|
782
|
-
{
|
|
783
|
-
resource: "subscription-invoice",
|
|
784
|
-
verb: "generate-invoice",
|
|
785
|
-
docsPath: "subscription-invoices/generate-subscription-invoice",
|
|
380
|
+
var VERB_RULES = {
|
|
381
|
+
get: { method: "GET", path: "/{id}", docs: "retrieve-{r}", body: "none" },
|
|
382
|
+
list: { method: "GET", path: "", docs: "list-all-{r}s", body: "none" },
|
|
383
|
+
create: { method: "POST", path: "", docs: "create-{r}", body: "required" },
|
|
384
|
+
update: { method: "PATCH", path: "/{id}", docs: "update-{r}", body: "required" },
|
|
385
|
+
delete: { method: "DELETE", path: "/{id}", docs: "delete-{r}", body: "none", destructive: true },
|
|
386
|
+
cancel: { method: "DELETE", path: "/{id}", docs: "cancel-{r}", body: "none", destructive: true },
|
|
387
|
+
refund: {
|
|
388
|
+
method: "POST",
|
|
389
|
+
path: "/{id}/refund",
|
|
390
|
+
docs: "issue-refund",
|
|
786
391
|
body: "optional",
|
|
787
|
-
|
|
788
|
-
},
|
|
789
|
-
// usage-records
|
|
790
|
-
{
|
|
791
|
-
resource: "usage-record",
|
|
792
|
-
verb: "get",
|
|
793
|
-
docsPath: "usage-records/retrieve-usage-record",
|
|
794
|
-
body: "none",
|
|
795
|
-
idRole: "id"
|
|
796
|
-
},
|
|
797
|
-
{
|
|
798
|
-
resource: "usage-record",
|
|
799
|
-
verb: "list",
|
|
800
|
-
docsPath: "usage-records/list-all-usage-records",
|
|
801
|
-
body: "none",
|
|
802
|
-
idRole: "parent"
|
|
803
|
-
},
|
|
804
|
-
{
|
|
805
|
-
resource: "usage-record",
|
|
806
|
-
verb: "create",
|
|
807
|
-
docsPath: "usage-records/create-usage-record",
|
|
808
|
-
body: "required",
|
|
809
|
-
idRole: "none"
|
|
810
|
-
},
|
|
811
|
-
// discounts
|
|
812
|
-
{
|
|
813
|
-
resource: "discount",
|
|
814
|
-
verb: "get",
|
|
815
|
-
docsPath: "discounts/retrieve-discount",
|
|
816
|
-
body: "none",
|
|
817
|
-
idRole: "id"
|
|
818
|
-
},
|
|
819
|
-
{
|
|
820
|
-
resource: "discount",
|
|
821
|
-
verb: "list",
|
|
822
|
-
docsPath: "discounts/list-all-discounts",
|
|
823
|
-
body: "none",
|
|
824
|
-
idRole: "store"
|
|
825
|
-
},
|
|
826
|
-
{
|
|
827
|
-
resource: "discount",
|
|
828
|
-
verb: "create",
|
|
829
|
-
docsPath: "discounts/create-discount",
|
|
830
|
-
body: "required",
|
|
831
|
-
idRole: "none"
|
|
832
|
-
},
|
|
833
|
-
{
|
|
834
|
-
resource: "discount",
|
|
835
|
-
verb: "delete",
|
|
836
|
-
docsPath: "discounts/delete-discount",
|
|
837
|
-
body: "none",
|
|
838
|
-
destructive: true,
|
|
839
|
-
idRole: "id"
|
|
840
|
-
},
|
|
841
|
-
// discount-redemptions (read-only)
|
|
842
|
-
{
|
|
843
|
-
resource: "discount-redemption",
|
|
844
|
-
verb: "get",
|
|
845
|
-
docsPath: "discount-redemptions/retrieve-discount-redemption",
|
|
846
|
-
body: "none",
|
|
847
|
-
idRole: "id"
|
|
848
|
-
},
|
|
849
|
-
{
|
|
850
|
-
resource: "discount-redemption",
|
|
851
|
-
verb: "list",
|
|
852
|
-
docsPath: "discount-redemptions/list-all-discount-redemptions",
|
|
853
|
-
body: "none",
|
|
854
|
-
idRole: "parent"
|
|
855
|
-
},
|
|
856
|
-
// license-keys
|
|
857
|
-
{
|
|
858
|
-
resource: "license-key",
|
|
859
|
-
verb: "get",
|
|
860
|
-
docsPath: "license-keys/retrieve-license-key",
|
|
861
|
-
body: "none",
|
|
862
|
-
idRole: "id"
|
|
863
|
-
},
|
|
864
|
-
{
|
|
865
|
-
resource: "license-key",
|
|
866
|
-
verb: "list",
|
|
867
|
-
docsPath: "license-keys/list-all-license-keys",
|
|
868
|
-
body: "none",
|
|
869
|
-
idRole: "store"
|
|
870
|
-
},
|
|
871
|
-
{
|
|
872
|
-
resource: "license-key",
|
|
873
|
-
verb: "update",
|
|
874
|
-
docsPath: "license-keys/update-license-key",
|
|
875
|
-
body: "required",
|
|
876
|
-
idRole: "id"
|
|
877
|
-
},
|
|
878
|
-
// license-key-instances (read-only)
|
|
879
|
-
{
|
|
880
|
-
resource: "license-key-instance",
|
|
881
|
-
verb: "get",
|
|
882
|
-
docsPath: "license-key-instances/retrieve-license-key-instance",
|
|
883
|
-
body: "none",
|
|
884
|
-
idRole: "id"
|
|
885
|
-
},
|
|
886
|
-
{
|
|
887
|
-
resource: "license-key-instance",
|
|
888
|
-
verb: "list",
|
|
889
|
-
docsPath: "license-key-instances/list-all-license-key-instances",
|
|
890
|
-
body: "none",
|
|
891
|
-
idRole: "parent"
|
|
892
|
-
},
|
|
893
|
-
// checkouts
|
|
894
|
-
{
|
|
895
|
-
resource: "checkout",
|
|
896
|
-
verb: "get",
|
|
897
|
-
docsPath: "checkouts/retrieve-checkout",
|
|
898
|
-
body: "none",
|
|
899
|
-
idRole: "id"
|
|
900
|
-
},
|
|
901
|
-
{
|
|
902
|
-
resource: "checkout",
|
|
903
|
-
verb: "list",
|
|
904
|
-
docsPath: "checkouts/list-all-checkouts",
|
|
905
|
-
body: "none",
|
|
906
|
-
idRole: "store"
|
|
907
|
-
},
|
|
908
|
-
{
|
|
909
|
-
resource: "checkout",
|
|
910
|
-
verb: "create",
|
|
911
|
-
docsPath: "checkouts/create-checkout",
|
|
912
|
-
body: "required",
|
|
913
|
-
idRole: "none"
|
|
914
|
-
},
|
|
915
|
-
// webhooks
|
|
916
|
-
{
|
|
917
|
-
resource: "webhook",
|
|
918
|
-
verb: "get",
|
|
919
|
-
docsPath: "webhooks/retrieve-webhook",
|
|
920
|
-
body: "none",
|
|
921
|
-
idRole: "id"
|
|
922
|
-
},
|
|
923
|
-
{
|
|
924
|
-
resource: "webhook",
|
|
925
|
-
verb: "list",
|
|
926
|
-
docsPath: "webhooks/list-all-webhooks",
|
|
927
|
-
body: "none",
|
|
928
|
-
idRole: "store"
|
|
929
|
-
},
|
|
930
|
-
{
|
|
931
|
-
resource: "webhook",
|
|
932
|
-
verb: "create",
|
|
933
|
-
docsPath: "webhooks/create-webhook",
|
|
934
|
-
body: "required",
|
|
935
|
-
idRole: "none"
|
|
936
|
-
},
|
|
937
|
-
{
|
|
938
|
-
resource: "webhook",
|
|
939
|
-
verb: "update",
|
|
940
|
-
docsPath: "webhooks/update-webhook",
|
|
941
|
-
body: "required",
|
|
942
|
-
idRole: "id"
|
|
392
|
+
destructive: true
|
|
943
393
|
},
|
|
944
|
-
{
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
body: "
|
|
949
|
-
destructive: true,
|
|
950
|
-
idRole: "id"
|
|
951
|
-
},
|
|
952
|
-
// affiliates (read-only)
|
|
953
|
-
{
|
|
954
|
-
resource: "affiliate",
|
|
955
|
-
verb: "get",
|
|
956
|
-
docsPath: "affiliates/retrieve-affiliate",
|
|
957
|
-
body: "none",
|
|
958
|
-
idRole: "id"
|
|
394
|
+
"generate-invoice": {
|
|
395
|
+
method: "POST",
|
|
396
|
+
path: "/{id}/generate-invoice",
|
|
397
|
+
docs: "generate-{r}-invoice",
|
|
398
|
+
body: "optional"
|
|
959
399
|
},
|
|
960
|
-
{
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
body: "none"
|
|
965
|
-
|
|
966
|
-
|
|
400
|
+
"current-usage": {
|
|
401
|
+
method: "GET",
|
|
402
|
+
path: "/{id}/current-usage",
|
|
403
|
+
docs: "retrieve-{r}-current-usage",
|
|
404
|
+
body: "none"
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
var RESOURCES = [
|
|
408
|
+
["user", ["get"]],
|
|
409
|
+
["store", ["get", "list"]],
|
|
410
|
+
["product", ["get", "list"], "store"],
|
|
411
|
+
["variant", ["get", "list"], "product"],
|
|
412
|
+
["price", ["get", "list"], "variant"],
|
|
413
|
+
["file", ["get", "list"], "variant"],
|
|
414
|
+
["customer", ["get", "list", "create", "update"], "store"],
|
|
415
|
+
["order", ["get", "list", "refund", "generate-invoice"], "store"],
|
|
416
|
+
["order-item", ["get", "list"], "order"],
|
|
417
|
+
["subscription", ["get", "list", "update", "cancel"], "store"],
|
|
418
|
+
["subscription-item", ["get", "list", "update", "current-usage"], "subscription"],
|
|
419
|
+
["subscription-invoice", ["get", "list", "refund", "generate-invoice"], "subscription"],
|
|
420
|
+
["usage-record", ["get", "list", "create"], "subscription-item"],
|
|
421
|
+
["discount", ["get", "list", "create", "delete"], "store"],
|
|
422
|
+
["discount-redemption", ["get", "list"], "discount"],
|
|
423
|
+
["license-key", ["get", "list", "update"], "store"],
|
|
424
|
+
["license-key-instance", ["get", "list"], "license-key"],
|
|
425
|
+
["checkout", ["get", "list", "create"], "store"],
|
|
426
|
+
["webhook", ["get", "list", "create", "update", "delete"], "store"],
|
|
427
|
+
["affiliate", ["get", "list"], "store"]
|
|
967
428
|
];
|
|
968
|
-
var
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
"
|
|
980
|
-
"
|
|
981
|
-
"
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
429
|
+
var DOCS_PATH_EXCEPTIONS = {
|
|
430
|
+
"subscription-invoice:generate-invoice": "subscription-invoices/generate-subscription-invoice"
|
|
431
|
+
};
|
|
432
|
+
var USER_ME_PATH = "/v1/users/me";
|
|
433
|
+
var listIdRole = (listScope) => {
|
|
434
|
+
if (!listScope) return "none";
|
|
435
|
+
return listScope === "store" ? "store" : "parent";
|
|
436
|
+
};
|
|
437
|
+
var toSpec = (resource, verb, listScope) => {
|
|
438
|
+
const rule = VERB_RULES[verb];
|
|
439
|
+
const docsPath = DOCS_PATH_EXCEPTIONS[`${resource}:${verb}`] ?? `${resource}s/${rule.docs.replaceAll("{r}", resource)}`;
|
|
440
|
+
let idRole = rule.path.includes("{id}") ? "id" : "none";
|
|
441
|
+
if (verb === "list") idRole = listIdRole(listScope);
|
|
442
|
+
if (resource === "user") idRole = "none";
|
|
443
|
+
return {
|
|
444
|
+
resource,
|
|
445
|
+
verb,
|
|
446
|
+
docsPath,
|
|
447
|
+
body: rule.body,
|
|
448
|
+
...rule.destructive ? { destructive: true } : {},
|
|
449
|
+
idRole
|
|
450
|
+
};
|
|
989
451
|
};
|
|
452
|
+
var resourceRegistry = RESOURCES.flatMap(
|
|
453
|
+
([resource, verbs, listScope]) => verbs.map((verb) => toSpec(resource, verb, listScope))
|
|
454
|
+
);
|
|
990
455
|
var normalizeResourceName = (resource) => {
|
|
991
456
|
const normalized = resource.replace(/_/g, "-").toLowerCase();
|
|
992
|
-
if (
|
|
993
|
-
return
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
return normalized.slice(0, -1);
|
|
997
|
-
}
|
|
998
|
-
return normalized;
|
|
999
|
-
};
|
|
1000
|
-
var findResourceVerb = (resource, verb) => {
|
|
1001
|
-
const key = normalizeResourceName(resource);
|
|
1002
|
-
return resourceRegistry.find((entry) => entry.resource === key && entry.verb === verb);
|
|
1003
|
-
};
|
|
1004
|
-
var listRegisteredResources = () => {
|
|
1005
|
-
return [...new Set(resourceRegistry.map((entry) => entry.resource))].sort();
|
|
1006
|
-
};
|
|
1007
|
-
|
|
1008
|
-
// src/resources/stores.ts
|
|
1009
|
-
var getStore = async (http, storeId) => {
|
|
1010
|
-
return http.getResource(`/v1/stores/${storeId}`);
|
|
1011
|
-
};
|
|
1012
|
-
var listStores = async (http) => {
|
|
1013
|
-
return http.paginate("/v1/stores");
|
|
1014
|
-
};
|
|
1015
|
-
|
|
1016
|
-
// src/resources/subscriptionInvoices.ts
|
|
1017
|
-
var getSubscriptionInvoice = async (http, subscriptionInvoiceId) => {
|
|
1018
|
-
return http.getResource(
|
|
1019
|
-
`/v1/subscription-invoices/${subscriptionInvoiceId}`
|
|
1020
|
-
);
|
|
1021
|
-
};
|
|
1022
|
-
var listSubscriptionInvoicesForSubscription = async (http, subscriptionId) => {
|
|
1023
|
-
return http.paginate("/v1/subscription-invoices", {
|
|
1024
|
-
"filter[subscription_id]": String(subscriptionId)
|
|
1025
|
-
});
|
|
1026
|
-
};
|
|
1027
|
-
var refundSubscriptionInvoice = async (http, subscriptionInvoiceId, body) => {
|
|
1028
|
-
return http.postResource(
|
|
1029
|
-
`/v1/subscription-invoices/${subscriptionInvoiceId}/refund`,
|
|
1030
|
-
body ?? {}
|
|
1031
|
-
);
|
|
1032
|
-
};
|
|
1033
|
-
var generateSubscriptionInvoice = async (http, subscriptionInvoiceId, body) => {
|
|
1034
|
-
return http.request({
|
|
1035
|
-
method: "POST",
|
|
1036
|
-
path: `/v1/subscription-invoices/${subscriptionInvoiceId}/generate-invoice`,
|
|
1037
|
-
body: body ?? {}
|
|
1038
|
-
});
|
|
1039
|
-
};
|
|
1040
|
-
|
|
1041
|
-
// src/resources/subscriptionItems.ts
|
|
1042
|
-
var getSubscriptionItem = async (http, subscriptionItemId) => {
|
|
1043
|
-
return http.getResource(
|
|
1044
|
-
`/v1/subscription-items/${subscriptionItemId}`
|
|
1045
|
-
);
|
|
1046
|
-
};
|
|
1047
|
-
var listSubscriptionItemsForSubscription = async (http, subscriptionId) => {
|
|
1048
|
-
return http.paginate("/v1/subscription-items", {
|
|
1049
|
-
"filter[subscription_id]": String(subscriptionId)
|
|
1050
|
-
});
|
|
1051
|
-
};
|
|
1052
|
-
var updateSubscriptionItem = async (http, subscriptionItemId, body) => {
|
|
1053
|
-
return http.patchResource(
|
|
1054
|
-
`/v1/subscription-items/${subscriptionItemId}`,
|
|
1055
|
-
body
|
|
1056
|
-
);
|
|
1057
|
-
};
|
|
1058
|
-
var getSubscriptionItemCurrentUsage = async (http, subscriptionItemId) => {
|
|
1059
|
-
return http.request({
|
|
1060
|
-
path: `/v1/subscription-items/${subscriptionItemId}/current-usage`
|
|
1061
|
-
});
|
|
1062
|
-
};
|
|
1063
|
-
|
|
1064
|
-
// src/resources/subscriptions.ts
|
|
1065
|
-
var getSubscription = async (http, subscriptionId) => {
|
|
1066
|
-
return http.getResource(`/v1/subscriptions/${subscriptionId}`);
|
|
1067
|
-
};
|
|
1068
|
-
var listSubscriptionsForStore = async (http, storeId) => {
|
|
1069
|
-
return http.paginate("/v1/subscriptions", {
|
|
1070
|
-
"filter[store_id]": String(storeId)
|
|
1071
|
-
});
|
|
1072
|
-
};
|
|
1073
|
-
var updateSubscription = async (http, subscriptionId, body) => {
|
|
1074
|
-
return http.patchResource(`/v1/subscriptions/${subscriptionId}`, body);
|
|
1075
|
-
};
|
|
1076
|
-
var cancelSubscription = async (http, subscriptionId) => {
|
|
1077
|
-
const doc = await http.request({
|
|
1078
|
-
method: "DELETE",
|
|
1079
|
-
path: `/v1/subscriptions/${subscriptionId}`
|
|
1080
|
-
});
|
|
1081
|
-
return doc?.data;
|
|
1082
|
-
};
|
|
1083
|
-
|
|
1084
|
-
// src/resources/usageRecords.ts
|
|
1085
|
-
var getUsageRecord = async (http, usageRecordId) => {
|
|
1086
|
-
return http.getResource(`/v1/usage-records/${usageRecordId}`);
|
|
1087
|
-
};
|
|
1088
|
-
var listUsageRecordsForSubscriptionItem = async (http, subscriptionItemId) => {
|
|
1089
|
-
return http.paginate("/v1/usage-records", {
|
|
1090
|
-
"filter[subscription_item_id]": String(subscriptionItemId)
|
|
1091
|
-
});
|
|
1092
|
-
};
|
|
1093
|
-
var createUsageRecord = async (http, body) => {
|
|
1094
|
-
return http.postResource("/v1/usage-records", body);
|
|
1095
|
-
};
|
|
1096
|
-
|
|
1097
|
-
// src/resources/users.ts
|
|
1098
|
-
var getAuthenticatedUser = async (http) => {
|
|
1099
|
-
return http.request({ path: "/v1/users/me" });
|
|
1100
|
-
};
|
|
1101
|
-
|
|
1102
|
-
// src/resources/variants.ts
|
|
1103
|
-
var getVariant = async (http, variantId) => {
|
|
1104
|
-
return http.getResource(`/v1/variants/${variantId}`);
|
|
1105
|
-
};
|
|
1106
|
-
var listVariantsForProduct = async (http, productId) => {
|
|
1107
|
-
return http.paginate("/v1/variants", {
|
|
1108
|
-
"filter[product_id]": String(productId)
|
|
1109
|
-
});
|
|
1110
|
-
};
|
|
1111
|
-
|
|
1112
|
-
// src/resources/webhooks.ts
|
|
1113
|
-
var getWebhook = async (http, webhookId) => {
|
|
1114
|
-
return http.getResource(`/v1/webhooks/${webhookId}`);
|
|
1115
|
-
};
|
|
1116
|
-
var listWebhooksForStore = async (http, storeId) => {
|
|
1117
|
-
return http.paginate("/v1/webhooks", {
|
|
1118
|
-
"filter[store_id]": String(storeId)
|
|
1119
|
-
});
|
|
1120
|
-
};
|
|
1121
|
-
var createWebhook = async (http, body) => {
|
|
1122
|
-
return http.postResource("/v1/webhooks", body);
|
|
1123
|
-
};
|
|
1124
|
-
var updateWebhook = async (http, webhookId, body) => {
|
|
1125
|
-
return http.patchResource(`/v1/webhooks/${webhookId}`, body);
|
|
457
|
+
if (normalized.endsWith("s") && normalized !== "status") {
|
|
458
|
+
return normalized.slice(0, -1);
|
|
459
|
+
}
|
|
460
|
+
return normalized;
|
|
1126
461
|
};
|
|
1127
|
-
var
|
|
1128
|
-
|
|
462
|
+
var findResourceVerb = (resource, verb) => {
|
|
463
|
+
const key = normalizeResourceName(resource);
|
|
464
|
+
return resourceRegistry.find((entry) => entry.resource === key && entry.verb === verb);
|
|
1129
465
|
};
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
var invokeOp = async (http, resource, verb, args = {}) => {
|
|
1133
|
-
const spec = findResourceVerb(resource, verb);
|
|
1134
|
-
if (!spec) {
|
|
1135
|
-
throw new FreshSqueezyError({
|
|
1136
|
-
code: "UNKNOWN_OP",
|
|
1137
|
-
message: `Unknown or unsupported op: ${verb} ${resource}. Run \`fresh-squeezy ops --list\`.`
|
|
1138
|
-
});
|
|
1139
|
-
}
|
|
1140
|
-
const key = `${spec.resource}:${spec.verb}`;
|
|
1141
|
-
switch (key) {
|
|
1142
|
-
case "user:get": {
|
|
1143
|
-
const doc = await getAuthenticatedUser(http);
|
|
1144
|
-
return doc.data;
|
|
1145
|
-
}
|
|
1146
|
-
case "store:get":
|
|
1147
|
-
return getStore(http, requireId(args, "store"));
|
|
1148
|
-
case "store:list":
|
|
1149
|
-
return listStores(http);
|
|
1150
|
-
case "product:get":
|
|
1151
|
-
return getProduct(http, requireId(args, "product"));
|
|
1152
|
-
case "product:list":
|
|
1153
|
-
return listProducts(http, requireStore(args));
|
|
1154
|
-
case "variant:get":
|
|
1155
|
-
return getVariant(http, requireId(args, "variant"));
|
|
1156
|
-
case "variant:list":
|
|
1157
|
-
return listVariantsForProduct(http, requireParent(args, "product"));
|
|
1158
|
-
case "price:get":
|
|
1159
|
-
return getPrice(http, requireId(args, "price"));
|
|
1160
|
-
case "price:list":
|
|
1161
|
-
return listPricesForVariant(http, requireParent(args, "variant"));
|
|
1162
|
-
case "file:get":
|
|
1163
|
-
return getFile(http, requireId(args, "file"));
|
|
1164
|
-
case "file:list":
|
|
1165
|
-
return listFilesForVariant(http, requireParent(args, "variant"));
|
|
1166
|
-
case "customer:get":
|
|
1167
|
-
return getCustomer(http, requireId(args, "customer"));
|
|
1168
|
-
case "customer:list":
|
|
1169
|
-
return listCustomersForStore(http, requireStore(args));
|
|
1170
|
-
case "customer:create":
|
|
1171
|
-
return createCustomer(http, requireBody(args));
|
|
1172
|
-
case "customer:update":
|
|
1173
|
-
return updateCustomer(http, requireId(args, "customer"), requireBody(args));
|
|
1174
|
-
case "order:get":
|
|
1175
|
-
return getOrder(http, requireId(args, "order"));
|
|
1176
|
-
case "order:list":
|
|
1177
|
-
return listOrdersForStore(http, requireStore(args));
|
|
1178
|
-
case "order:refund":
|
|
1179
|
-
return refundOrder(http, requireId(args, "order"), args.body);
|
|
1180
|
-
case "order:generate-invoice":
|
|
1181
|
-
return generateOrderInvoice(http, requireId(args, "order"), args.body);
|
|
1182
|
-
case "order-item:get":
|
|
1183
|
-
return getOrderItem(http, requireId(args, "order-item"));
|
|
1184
|
-
case "order-item:list":
|
|
1185
|
-
return listOrderItemsForOrder(http, requireParent(args, "order"));
|
|
1186
|
-
case "subscription:get":
|
|
1187
|
-
return getSubscription(http, requireId(args, "subscription"));
|
|
1188
|
-
case "subscription:list":
|
|
1189
|
-
return listSubscriptionsForStore(http, requireStore(args));
|
|
1190
|
-
case "subscription:update":
|
|
1191
|
-
return updateSubscription(http, requireId(args, "subscription"), requireBody(args));
|
|
1192
|
-
case "subscription:cancel":
|
|
1193
|
-
return cancelSubscription(http, requireId(args, "subscription"));
|
|
1194
|
-
case "subscription-item:get":
|
|
1195
|
-
return getSubscriptionItem(http, requireId(args, "subscription-item"));
|
|
1196
|
-
case "subscription-item:list":
|
|
1197
|
-
return listSubscriptionItemsForSubscription(http, requireParent(args, "subscription"));
|
|
1198
|
-
case "subscription-item:update":
|
|
1199
|
-
return updateSubscriptionItem(http, requireId(args, "subscription-item"), requireBody(args));
|
|
1200
|
-
case "subscription-item:current-usage":
|
|
1201
|
-
return getSubscriptionItemCurrentUsage(http, requireId(args, "subscription-item"));
|
|
1202
|
-
case "subscription-invoice:get":
|
|
1203
|
-
return getSubscriptionInvoice(http, requireId(args, "subscription-invoice"));
|
|
1204
|
-
case "subscription-invoice:list":
|
|
1205
|
-
return listSubscriptionInvoicesForSubscription(http, requireParent(args, "subscription"));
|
|
1206
|
-
case "subscription-invoice:refund":
|
|
1207
|
-
return refundSubscriptionInvoice(http, requireId(args, "subscription-invoice"), args.body);
|
|
1208
|
-
case "subscription-invoice:generate-invoice":
|
|
1209
|
-
return generateSubscriptionInvoice(http, requireId(args, "subscription-invoice"), args.body);
|
|
1210
|
-
case "usage-record:get":
|
|
1211
|
-
return getUsageRecord(http, requireId(args, "usage-record"));
|
|
1212
|
-
case "usage-record:list":
|
|
1213
|
-
return listUsageRecordsForSubscriptionItem(http, requireParent(args, "subscription-item"));
|
|
1214
|
-
case "usage-record:create":
|
|
1215
|
-
return createUsageRecord(http, requireBody(args));
|
|
1216
|
-
case "discount:get":
|
|
1217
|
-
return getDiscount(http, requireId(args, "discount"));
|
|
1218
|
-
case "discount:list":
|
|
1219
|
-
return listDiscountsForStore(http, requireStore(args));
|
|
1220
|
-
case "discount:create":
|
|
1221
|
-
return createDiscount(http, requireBody(args));
|
|
1222
|
-
case "discount:delete":
|
|
1223
|
-
await deleteDiscount(http, requireId(args, "discount"));
|
|
1224
|
-
return void 0;
|
|
1225
|
-
case "discount-redemption:get":
|
|
1226
|
-
return getDiscountRedemption(http, requireId(args, "discount-redemption"));
|
|
1227
|
-
case "discount-redemption:list":
|
|
1228
|
-
return listDiscountRedemptionsForDiscount(http, requireParent(args, "discount"));
|
|
1229
|
-
case "license-key:get":
|
|
1230
|
-
return getLicenseKey(http, requireId(args, "license-key"));
|
|
1231
|
-
case "license-key:list":
|
|
1232
|
-
return listLicenseKeysForStore(http, requireStore(args));
|
|
1233
|
-
case "license-key:update":
|
|
1234
|
-
return updateLicenseKey(http, requireId(args, "license-key"), requireBody(args));
|
|
1235
|
-
case "license-key-instance:get":
|
|
1236
|
-
return getLicenseKeyInstance(http, requireId(args, "license-key-instance"));
|
|
1237
|
-
case "license-key-instance:list":
|
|
1238
|
-
return listLicenseKeyInstancesForLicenseKey(http, requireParent(args, "license-key"));
|
|
1239
|
-
case "checkout:get":
|
|
1240
|
-
return getCheckout(http, requireId(args, "checkout"));
|
|
1241
|
-
case "checkout:list":
|
|
1242
|
-
return listCheckoutsForStore(http, requireStore(args));
|
|
1243
|
-
case "checkout:create":
|
|
1244
|
-
return createCheckout(http, requireBody(args));
|
|
1245
|
-
case "webhook:get":
|
|
1246
|
-
return getWebhook(http, requireId(args, "webhook"));
|
|
1247
|
-
case "webhook:list":
|
|
1248
|
-
return listWebhooksForStore(http, requireStore(args));
|
|
1249
|
-
case "webhook:create":
|
|
1250
|
-
return createWebhook(http, requireBody(args));
|
|
1251
|
-
case "webhook:update":
|
|
1252
|
-
return updateWebhook(http, requireId(args, "webhook"), requireBody(args));
|
|
1253
|
-
case "webhook:delete":
|
|
1254
|
-
await deleteWebhook(http, requireId(args, "webhook"));
|
|
1255
|
-
return void 0;
|
|
1256
|
-
case "affiliate:get":
|
|
1257
|
-
return getAffiliate(http, requireId(args, "affiliate"));
|
|
1258
|
-
case "affiliate:list":
|
|
1259
|
-
return listAffiliatesForStore(http, requireStore(args));
|
|
1260
|
-
default:
|
|
1261
|
-
throw new FreshSqueezyError({
|
|
1262
|
-
code: "UNKNOWN_OP",
|
|
1263
|
-
message: `Op registered but not wired: ${key}`
|
|
1264
|
-
});
|
|
1265
|
-
}
|
|
466
|
+
var listRegisteredResources = () => {
|
|
467
|
+
return [...new Set(resourceRegistry.map((entry) => entry.resource))].sort();
|
|
1266
468
|
};
|
|
1267
|
-
var requireId = (args,
|
|
469
|
+
var requireId = (args, spec) => {
|
|
1268
470
|
if (args.id === void 0 || args.id === "") {
|
|
1269
471
|
throw new FreshSqueezyError({
|
|
1270
472
|
code: "MISSING_ARG",
|
|
1271
|
-
message: `--id is required for this ${
|
|
473
|
+
message: `--id is required for this ${spec.resource} operation.`
|
|
1272
474
|
});
|
|
1273
475
|
}
|
|
1274
476
|
return args.id;
|
|
@@ -1282,11 +484,11 @@ var requireStore = (args) => {
|
|
|
1282
484
|
}
|
|
1283
485
|
return args.storeId;
|
|
1284
486
|
};
|
|
1285
|
-
var requireParent = (args,
|
|
487
|
+
var requireParent = (args, parent) => {
|
|
1286
488
|
if (args.parentId === void 0 || args.parentId === "") {
|
|
1287
489
|
throw new FreshSqueezyError({
|
|
1288
490
|
code: "MISSING_ARG",
|
|
1289
|
-
message: `--parent-id (${
|
|
491
|
+
message: `--parent-id (${parent} id) is required for this list operation.`
|
|
1290
492
|
});
|
|
1291
493
|
}
|
|
1292
494
|
return args.parentId;
|
|
@@ -1300,15 +502,44 @@ var requireBody = (args) => {
|
|
|
1300
502
|
}
|
|
1301
503
|
return args.body;
|
|
1302
504
|
};
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
505
|
+
var listFilter = (spec, args) => {
|
|
506
|
+
const listScope = RESOURCES.find(([resource]) => resource === spec.resource)?.[2];
|
|
507
|
+
if (!listScope) return void 0;
|
|
508
|
+
const scopeId = listScope === "store" ? requireStore(args) : requireParent(args, listScope);
|
|
509
|
+
return { [`filter[${listScope.replace(/-/g, "_")}_id]`]: String(scopeId) };
|
|
510
|
+
};
|
|
511
|
+
var requestBody = (rule, args) => {
|
|
512
|
+
if (rule.body === "required") return requireBody(args);
|
|
513
|
+
if (rule.body === "optional") return args.body ?? {};
|
|
1308
514
|
return void 0;
|
|
1309
515
|
};
|
|
516
|
+
var invokeOp = async (http, resource, verb, args = {}) => {
|
|
517
|
+
const spec = findResourceVerb(resource, verb);
|
|
518
|
+
if (!spec) {
|
|
519
|
+
throw new FreshSqueezyError({
|
|
520
|
+
code: "UNKNOWN_OP",
|
|
521
|
+
message: `Unknown or unsupported op: ${verb} ${resource}. Run \`fresh-squeezy ops --list\`.`
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
if (spec.resource === "user") {
|
|
525
|
+
const doc2 = await http.request({ path: USER_ME_PATH });
|
|
526
|
+
return doc2.data;
|
|
527
|
+
}
|
|
528
|
+
const collectionPath = `/v1/${spec.resource}s`;
|
|
529
|
+
if (spec.verb === "list") {
|
|
530
|
+
return http.paginate(collectionPath, listFilter(spec, args));
|
|
531
|
+
}
|
|
532
|
+
const rule = VERB_RULES[spec.verb];
|
|
533
|
+
const path4 = collectionPath + rule.path.replace("{id}", () => String(requireId(args, spec)));
|
|
534
|
+
const doc = await http.request({
|
|
535
|
+
method: rule.method,
|
|
536
|
+
path: path4,
|
|
537
|
+
body: requestBody(rule, args)
|
|
538
|
+
});
|
|
539
|
+
return doc?.data ?? doc;
|
|
540
|
+
};
|
|
1310
541
|
|
|
1311
|
-
// src/validate/
|
|
542
|
+
// src/validate/issues.ts
|
|
1312
543
|
var ISSUE_CODES = {
|
|
1313
544
|
AUTH_FAILED: "AUTH_FAILED",
|
|
1314
545
|
MODE_MISMATCH: "MODE_MISMATCH",
|
|
@@ -1432,11 +663,19 @@ var checkStoreOwnership = (check) => {
|
|
|
1432
663
|
};
|
|
1433
664
|
|
|
1434
665
|
// src/validate/connection.ts
|
|
666
|
+
var resolveActualMode = (testMode) => {
|
|
667
|
+
if (testMode === true) return "test";
|
|
668
|
+
if (testMode === false) return "live";
|
|
669
|
+
return void 0;
|
|
670
|
+
};
|
|
671
|
+
var fetchUserDocument = (http) => {
|
|
672
|
+
return http.request({ path: "/v1/users/me" });
|
|
673
|
+
};
|
|
1435
674
|
var validateConnection = async (http, mode) => {
|
|
1436
675
|
const fetched = await probeCollection(
|
|
1437
676
|
async () => {
|
|
1438
|
-
const userDoc2 = await
|
|
1439
|
-
const stores2 = await
|
|
677
|
+
const userDoc2 = await fetchUserDocument(http);
|
|
678
|
+
const stores2 = await http.paginate("/v1/stores");
|
|
1440
679
|
return { userDoc: userDoc2, stores: stores2 };
|
|
1441
680
|
},
|
|
1442
681
|
{
|
|
@@ -1492,12 +731,15 @@ var validateDiscount = async (http, mode, options) => {
|
|
|
1492
731
|
label: `discount ${options.discountId}`,
|
|
1493
732
|
id: String(options.discountId)
|
|
1494
733
|
};
|
|
1495
|
-
const fetched = await probeFetch(
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
734
|
+
const fetched = await probeFetch(
|
|
735
|
+
() => http.getResource(`/v1/discounts/${options.discountId}`),
|
|
736
|
+
{
|
|
737
|
+
notFoundCode: ISSUE_CODES.DISCOUNT_NOT_FOUND,
|
|
738
|
+
notFoundMessage: `Discount ${options.discountId} not found.`,
|
|
739
|
+
notFoundFix: "Verify the discount ID in the Lemon Squeezy dashboard.",
|
|
740
|
+
notFoundContext: { discountId: String(options.discountId) }
|
|
741
|
+
}
|
|
742
|
+
);
|
|
1501
743
|
if (!fetched.ok) {
|
|
1502
744
|
return buildResult("discount", mode, [fetched.issue], void 0, target);
|
|
1503
745
|
}
|
|
@@ -1604,12 +846,15 @@ var validateLicenseKey = async (http, mode, options) => {
|
|
|
1604
846
|
label: `license key ${options.licenseKeyId}`,
|
|
1605
847
|
id: String(options.licenseKeyId)
|
|
1606
848
|
};
|
|
1607
|
-
const fetched = await probeFetch(
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
849
|
+
const fetched = await probeFetch(
|
|
850
|
+
() => http.getResource(`/v1/license-keys/${options.licenseKeyId}`),
|
|
851
|
+
{
|
|
852
|
+
notFoundCode: ISSUE_CODES.LICENSE_KEY_NOT_FOUND,
|
|
853
|
+
notFoundMessage: `License key ${options.licenseKeyId} not found.`,
|
|
854
|
+
notFoundFix: "Verify the license key ID in the Lemon Squeezy dashboard.",
|
|
855
|
+
notFoundContext: { licenseKeyId: String(options.licenseKeyId) }
|
|
856
|
+
}
|
|
857
|
+
);
|
|
1613
858
|
if (!fetched.ok) {
|
|
1614
859
|
return buildResult(
|
|
1615
860
|
"licenseKey",
|
|
@@ -1691,19 +936,24 @@ var validateProduct = async (http, mode, options) => {
|
|
|
1691
936
|
label: `product ${options.productId}`,
|
|
1692
937
|
id: String(options.productId)
|
|
1693
938
|
};
|
|
1694
|
-
const fetched = await probeFetch(
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
939
|
+
const fetched = await probeFetch(
|
|
940
|
+
() => http.getResource(`/v1/products/${options.productId}`),
|
|
941
|
+
{
|
|
942
|
+
notFoundCode: ISSUE_CODES.PRODUCT_NOT_FOUND,
|
|
943
|
+
notFoundMessage: `Product ${options.productId} not found.`,
|
|
944
|
+
notFoundFix: "Verify the product ID in the Lemon Squeezy dashboard.",
|
|
945
|
+
notFoundContext: { productId: String(options.productId) }
|
|
946
|
+
}
|
|
947
|
+
);
|
|
1700
948
|
if (!fetched.ok) {
|
|
1701
949
|
return buildResult("product", mode, [fetched.issue], void 0, target);
|
|
1702
950
|
}
|
|
1703
951
|
const attrs = fetched.resource.attributes;
|
|
1704
952
|
issues.push(...checkProduct(attrs, { expectedStoreId: options.expectedStoreId }));
|
|
1705
953
|
const variantsProbed = await probeCollection(
|
|
1706
|
-
() =>
|
|
954
|
+
() => http.paginate("/v1/variants", {
|
|
955
|
+
"filter[product_id]": String(options.productId)
|
|
956
|
+
})
|
|
1707
957
|
);
|
|
1708
958
|
if (!variantsProbed.ok) {
|
|
1709
959
|
issues.push({ ...variantsProbed.issue, severity: "warning" });
|
|
@@ -1772,12 +1022,15 @@ var checkProduct = (attrs, options = {}) => {
|
|
|
1772
1022
|
// src/validate/store.ts
|
|
1773
1023
|
var validateStore = async (http, mode, storeId) => {
|
|
1774
1024
|
const target = { label: `store ${storeId}`, id: String(storeId) };
|
|
1775
|
-
const fetched = await probeFetch(
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1025
|
+
const fetched = await probeFetch(
|
|
1026
|
+
() => http.getResource(`/v1/stores/${storeId}`),
|
|
1027
|
+
{
|
|
1028
|
+
notFoundCode: ISSUE_CODES.STORE_NOT_FOUND,
|
|
1029
|
+
notFoundMessage: `Store ${storeId} not found with the current API key.`,
|
|
1030
|
+
notFoundFix: "Check the store ID and confirm the API key belongs to the account that owns it.",
|
|
1031
|
+
notFoundContext: { storeId: String(storeId) }
|
|
1032
|
+
}
|
|
1033
|
+
);
|
|
1781
1034
|
if (!fetched.ok) {
|
|
1782
1035
|
return buildResult("store", mode, [fetched.issue], void 0, target);
|
|
1783
1036
|
}
|
|
@@ -1795,7 +1048,7 @@ var validateSubscriptionPlan = async (http, mode, options) => {
|
|
|
1795
1048
|
id: String(options.variantId)
|
|
1796
1049
|
};
|
|
1797
1050
|
const fetched = await probeFetch(
|
|
1798
|
-
() =>
|
|
1051
|
+
() => http.getResource(`/v1/variants/${options.variantId}`),
|
|
1799
1052
|
{
|
|
1800
1053
|
notFoundCode: ISSUE_CODES.PLAN_VARIANT_NOT_FOUND,
|
|
1801
1054
|
notFoundMessage: `Variant ${options.variantId} not found.`,
|
|
@@ -1815,10 +1068,13 @@ var validateSubscriptionPlan = async (http, mode, options) => {
|
|
|
1815
1068
|
const variant = fetched.resource;
|
|
1816
1069
|
const attrs = variant.attributes;
|
|
1817
1070
|
const issues = checkSubscriptionPlan(attrs, { variantId: options.variantId });
|
|
1818
|
-
const productProbed = await probeFetch(
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1071
|
+
const productProbed = await probeFetch(
|
|
1072
|
+
() => http.getResource(`/v1/products/${attrs.product_id}`),
|
|
1073
|
+
{
|
|
1074
|
+
notFoundCode: ISSUE_CODES.PRODUCT_NOT_FOUND,
|
|
1075
|
+
notFoundMessage: `Product ${attrs.product_id} not found.`
|
|
1076
|
+
}
|
|
1077
|
+
);
|
|
1822
1078
|
if (productProbed.ok) {
|
|
1823
1079
|
const mismatch = checkStoreOwnership({
|
|
1824
1080
|
expectedStoreId: options.storeId,
|
|
@@ -1929,14 +1185,6 @@ var checkSubscriptionPlan = (attrs, options) => {
|
|
|
1929
1185
|
return issues;
|
|
1930
1186
|
};
|
|
1931
1187
|
|
|
1932
|
-
// src/core/equality.ts
|
|
1933
|
-
var sameWebhookUrl = (a, b) => {
|
|
1934
|
-
return normalizeWebhookUrl(a) === normalizeWebhookUrl(b);
|
|
1935
|
-
};
|
|
1936
|
-
var normalizeWebhookUrl = (raw) => {
|
|
1937
|
-
return raw.replace(/\/+$/, "").toLowerCase();
|
|
1938
|
-
};
|
|
1939
|
-
|
|
1940
1188
|
// src/support/manifest.ts
|
|
1941
1189
|
var RECOMMENDED_WEBHOOK_EVENTS = [
|
|
1942
1190
|
"order_created",
|
|
@@ -1957,9 +1205,19 @@ var OPTIONAL_WEBHOOK_EVENTS = [
|
|
|
1957
1205
|
];
|
|
1958
1206
|
|
|
1959
1207
|
// src/validate/webhook.ts
|
|
1208
|
+
var sameWebhookUrl = (a, b) => {
|
|
1209
|
+
return normalizeWebhookUrl(a) === normalizeWebhookUrl(b);
|
|
1210
|
+
};
|
|
1211
|
+
var normalizeWebhookUrl = (raw) => {
|
|
1212
|
+
return raw.replace(/\/+$/, "").toLowerCase();
|
|
1213
|
+
};
|
|
1960
1214
|
var validateWebhook = async (http, mode, options) => {
|
|
1961
1215
|
const target = { label: options.url, url: options.url };
|
|
1962
|
-
const fetched = await probeCollection(
|
|
1216
|
+
const fetched = await probeCollection(
|
|
1217
|
+
() => http.paginate("/v1/webhooks", {
|
|
1218
|
+
"filter[store_id]": String(options.storeId)
|
|
1219
|
+
})
|
|
1220
|
+
);
|
|
1963
1221
|
if (!fetched.ok) {
|
|
1964
1222
|
return buildResult("webhook", mode, [fetched.issue], void 0, target);
|
|
1965
1223
|
}
|
|
@@ -2214,80 +1472,196 @@ var createFreshSqueezy = (config = {}) => {
|
|
|
2214
1472
|
};
|
|
2215
1473
|
};
|
|
2216
1474
|
|
|
2217
|
-
// src/cli/
|
|
2218
|
-
var
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
}
|
|
1475
|
+
// src/cli/optionalValidators.ts
|
|
1476
|
+
var errorMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
1477
|
+
var listChoices = async (load, toChoice) => {
|
|
1478
|
+
try {
|
|
1479
|
+
const items = await load();
|
|
1480
|
+
return { choices: items.map(toChoice) };
|
|
1481
|
+
} catch (err) {
|
|
1482
|
+
return { choices: [], error: errorMessage(err) };
|
|
2225
1483
|
}
|
|
2226
|
-
return `${lines.join("\n")}
|
|
2227
|
-
`;
|
|
2228
1484
|
};
|
|
2229
|
-
var
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
1485
|
+
var discoverSubscriptionPlans = async ({
|
|
1486
|
+
client,
|
|
1487
|
+
listProducts
|
|
1488
|
+
}) => {
|
|
1489
|
+
const choices = [];
|
|
1490
|
+
try {
|
|
1491
|
+
for (const product of await listProducts()) {
|
|
1492
|
+
const variants = await client.variants.list(
|
|
1493
|
+
product.id
|
|
1494
|
+
);
|
|
1495
|
+
for (const variant of variants) {
|
|
1496
|
+
if (!variant.attributes.is_subscription) continue;
|
|
1497
|
+
const { name, interval, interval_count } = variant.attributes;
|
|
1498
|
+
const cadence = interval ? `${interval_count ?? 1}/${interval}` : "no interval";
|
|
1499
|
+
choices.push({
|
|
1500
|
+
value: variant.id,
|
|
1501
|
+
label: `${product.attributes.name} / ${name} (${cadence}) - id ${variant.id}`
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
return { choices };
|
|
1506
|
+
} catch (err) {
|
|
1507
|
+
return { choices, error: errorMessage(err) };
|
|
2239
1508
|
}
|
|
2240
|
-
return [];
|
|
2241
1509
|
};
|
|
2242
|
-
var
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
if (err.code === "INVALID_MODE") {
|
|
2248
|
-
return [
|
|
2249
|
-
`fresh-squeezy validate ${target} --mode test`,
|
|
2250
|
-
`fresh-squeezy validate ${target} --mode live`
|
|
2251
|
-
];
|
|
2252
|
-
}
|
|
2253
|
-
if (err.code === "MISSING_ARG") {
|
|
2254
|
-
return getMissingArgHints(target);
|
|
1510
|
+
var isHttpUrl = (value) => {
|
|
1511
|
+
try {
|
|
1512
|
+
return ["http:", "https:"].includes(new URL(value).protocol);
|
|
1513
|
+
} catch {
|
|
1514
|
+
return false;
|
|
2255
1515
|
}
|
|
2256
|
-
return [];
|
|
2257
1516
|
};
|
|
2258
|
-
var
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
1517
|
+
var validateWebhookUrls = (value) => {
|
|
1518
|
+
const urls = value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
1519
|
+
return urls.every(isHttpUrl) ? true : "Enter valid webhook URLs.";
|
|
1520
|
+
};
|
|
1521
|
+
var OPTIONAL_VALIDATORS = [
|
|
1522
|
+
{
|
|
1523
|
+
name: "product",
|
|
1524
|
+
field: "productIds",
|
|
1525
|
+
resultName: "product",
|
|
1526
|
+
label: "Products",
|
|
1527
|
+
menuLabel: "Product checkout",
|
|
1528
|
+
noun: "products",
|
|
1529
|
+
manualNoun: "Product IDs",
|
|
1530
|
+
discover: ({ listProducts }) => listChoices(listProducts, (product) => ({
|
|
1531
|
+
value: product.id,
|
|
1532
|
+
label: `${product.attributes.name} (${product.attributes.status}) - id ${product.id}`
|
|
1533
|
+
}))
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
name: "webhook",
|
|
1537
|
+
field: "webhookUrls",
|
|
1538
|
+
resultName: "webhook",
|
|
1539
|
+
label: "Webhooks",
|
|
1540
|
+
menuLabel: "Webhook registration",
|
|
1541
|
+
noun: "webhook URLs",
|
|
1542
|
+
manualNoun: "Webhook URLs",
|
|
1543
|
+
validate: validateWebhookUrls,
|
|
1544
|
+
discover: ({ client, storeId }) => listChoices(
|
|
1545
|
+
() => client.webhooks.list(storeId),
|
|
1546
|
+
(webhook) => ({
|
|
1547
|
+
value: webhook.attributes.url,
|
|
1548
|
+
label: `${webhook.attributes.url} - id ${webhook.id}`
|
|
1549
|
+
})
|
|
1550
|
+
)
|
|
1551
|
+
},
|
|
1552
|
+
{
|
|
1553
|
+
name: "discount",
|
|
1554
|
+
field: "discountIds",
|
|
1555
|
+
resultName: "discount",
|
|
1556
|
+
label: "Discounts",
|
|
1557
|
+
menuLabel: "Discount code",
|
|
1558
|
+
noun: "discounts",
|
|
1559
|
+
manualNoun: "Discount IDs",
|
|
1560
|
+
discover: ({ client, storeId }) => listChoices(
|
|
1561
|
+
() => client.discounts.list(storeId),
|
|
1562
|
+
(discount) => ({
|
|
1563
|
+
value: discount.id,
|
|
1564
|
+
label: `${discount.attributes.name} (${discount.attributes.code}) - id ${discount.id}`
|
|
1565
|
+
})
|
|
1566
|
+
)
|
|
1567
|
+
},
|
|
1568
|
+
{
|
|
1569
|
+
name: "license-key",
|
|
1570
|
+
field: "licenseKeyIds",
|
|
1571
|
+
resultName: "licenseKey",
|
|
1572
|
+
label: "License keys",
|
|
1573
|
+
menuLabel: "License key",
|
|
1574
|
+
noun: "license keys",
|
|
1575
|
+
manualNoun: "License key IDs",
|
|
1576
|
+
discover: ({ client, storeId }) => listChoices(
|
|
1577
|
+
() => client.licenseKeys.list(storeId),
|
|
1578
|
+
(key) => ({
|
|
1579
|
+
value: key.id,
|
|
1580
|
+
label: `${key.attributes.key_short} (${key.attributes.status}) - id ${key.id}`
|
|
1581
|
+
})
|
|
1582
|
+
)
|
|
1583
|
+
},
|
|
1584
|
+
{
|
|
1585
|
+
name: "subscription-plan",
|
|
1586
|
+
field: "variantIds",
|
|
1587
|
+
resultName: "subscriptionPlan",
|
|
1588
|
+
label: "Subscription plans",
|
|
1589
|
+
menuLabel: "Subscription plan",
|
|
1590
|
+
noun: "subscription plans",
|
|
1591
|
+
manualNoun: "Subscription plan variant IDs",
|
|
1592
|
+
discover: discoverSubscriptionPlans
|
|
2279
1593
|
}
|
|
2280
|
-
|
|
2281
|
-
|
|
1594
|
+
];
|
|
1595
|
+
var discoverChoices = async (client, storeId, names) => {
|
|
1596
|
+
let products;
|
|
1597
|
+
const context = {
|
|
1598
|
+
client,
|
|
1599
|
+
storeId,
|
|
1600
|
+
listProducts: () => {
|
|
1601
|
+
products ??= client.products.list(storeId);
|
|
1602
|
+
return products;
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
const choices = {};
|
|
1606
|
+
for (const row of OPTIONAL_VALIDATORS) {
|
|
1607
|
+
if (names.includes(row.name)) choices[row.name] = await row.discover(context);
|
|
2282
1608
|
}
|
|
2283
|
-
return
|
|
2284
|
-
};
|
|
2285
|
-
var isFreshSqueezyError = (err) => {
|
|
2286
|
-
return err instanceof FreshSqueezyError;
|
|
1609
|
+
return choices;
|
|
2287
1610
|
};
|
|
2288
1611
|
|
|
2289
1612
|
// src/cli/render.ts
|
|
2290
1613
|
import chalk from "chalk";
|
|
1614
|
+
var MISSING_API_KEY_HINTS = [
|
|
1615
|
+
"fresh-squeezy init",
|
|
1616
|
+
`export ${ENV_KEYS.apiKey}=ls_live_or_test_key`
|
|
1617
|
+
];
|
|
1618
|
+
var brand = chalk.green.bold("fresh-squeezy");
|
|
1619
|
+
var divider = chalk.dim("\u2022");
|
|
1620
|
+
var renderBrandHeader = (title, subtitle) => {
|
|
1621
|
+
const lines = [`${brand} ${divider} ${chalk.bold(title)}`];
|
|
1622
|
+
if (subtitle) lines.push(chalk.dim(subtitle));
|
|
1623
|
+
lines.push("");
|
|
1624
|
+
return lines.join("\n");
|
|
1625
|
+
};
|
|
1626
|
+
var renderStep = (index, total, title, detail) => {
|
|
1627
|
+
const suffix = detail ? ` ${chalk.dim(detail)}` : "";
|
|
1628
|
+
const prefix = index > 1 ? "\n" : "";
|
|
1629
|
+
return `${prefix}${chalk.yellow("\u25CF")} ${chalk.dim(`${index}/${total}`)} ${chalk.bold(title)}${suffix}
|
|
1630
|
+
`;
|
|
1631
|
+
};
|
|
1632
|
+
var renderDetected = (label, value, source) => {
|
|
1633
|
+
const suffix = source ? ` ${chalk.dim(`from ${source}`)}` : "";
|
|
1634
|
+
return ` ${chalk.green("\u2713")} ${chalk.bold(label)} ${value}${suffix}
|
|
1635
|
+
`;
|
|
1636
|
+
};
|
|
1637
|
+
var renderCommandExamples = () => {
|
|
1638
|
+
return [
|
|
1639
|
+
renderBrandHeader("Command examples", "Direct commands stay stable for scripts and CI."),
|
|
1640
|
+
`${chalk.bold("Guided setup")}`,
|
|
1641
|
+
" fresh-squeezy init",
|
|
1642
|
+
"",
|
|
1643
|
+
`${chalk.bold("Full doctor")}`,
|
|
1644
|
+
" fresh-squeezy doctor --all-stores",
|
|
1645
|
+
" fresh-squeezy doctor --store-ids 12 --product-id 987 --webhook-url https://app.example.com/api/webhooks/lemon-squeezy",
|
|
1646
|
+
"",
|
|
1647
|
+
`${chalk.bold("Single checks")}`,
|
|
1648
|
+
" fresh-squeezy validate connection",
|
|
1649
|
+
" fresh-squeezy validate webhook --store-ids 12 --webhook-url https://app.example.com/api/webhooks/lemon-squeezy",
|
|
1650
|
+
"",
|
|
1651
|
+
`${chalk.bold("Bulk ops (delete / cancel / refund)")}`,
|
|
1652
|
+
" fresh-squeezy delete discount --all --store-ids 12 --dry-run",
|
|
1653
|
+
" fresh-squeezy delete discount --all --store-ids 12 --match TEST --yes",
|
|
1654
|
+
" fresh-squeezy delete webhook --ids 1,2,3 --yes",
|
|
1655
|
+
"",
|
|
1656
|
+
`${chalk.bold("Automation")}`,
|
|
1657
|
+
" fresh-squeezy doctor --all-stores --json",
|
|
1658
|
+
""
|
|
1659
|
+
].join("\n");
|
|
1660
|
+
};
|
|
1661
|
+
var renderCancelMessage = () => {
|
|
1662
|
+
return `${chalk.dim("fresh-squeezy cancelled.")}
|
|
1663
|
+
`;
|
|
1664
|
+
};
|
|
2291
1665
|
var renderResult = (result) => {
|
|
2292
1666
|
const lines = [];
|
|
2293
1667
|
const badge = result.ok ? chalk.green("PASS") : chalk.red("FAIL");
|
|
@@ -2324,140 +1698,37 @@ var renderIssueLine = (issue2) => {
|
|
|
2324
1698
|
const label = issue2.severity === "error" ? chalk.red("\u2717 error") : issue2.severity === "warning" ? chalk.yellow("! warn ") : chalk.blue("i info ");
|
|
2325
1699
|
return `${label} ${chalk.gray(`[${issue2.code}]`)} ${issue2.message}`;
|
|
2326
1700
|
};
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
licenseKeys: { choices: [] },
|
|
2338
|
-
subscriptionPlans: { choices: [] }
|
|
2339
|
-
};
|
|
2340
|
-
var discoverInitResourceChoices = async (client, storeId, targets) => {
|
|
2341
|
-
const choices = {
|
|
2342
|
-
products: { choices: [] },
|
|
2343
|
-
webhooks: { choices: [] },
|
|
2344
|
-
discounts: { choices: [] },
|
|
2345
|
-
licenseKeys: { choices: [] },
|
|
2346
|
-
subscriptionPlans: { choices: [] }
|
|
2347
|
-
};
|
|
2348
|
-
let products;
|
|
2349
|
-
if (targets.includes("product") || targets.includes("subscription-plan")) {
|
|
2350
|
-
const result = await discover(
|
|
2351
|
-
() => paginate(client, "/v1/products", { "filter[store_id]": storeId })
|
|
2352
|
-
);
|
|
2353
|
-
if (result.ok) {
|
|
2354
|
-
products = result.value;
|
|
2355
|
-
choices.products = { choices: result.value.map(toProductChoice) };
|
|
2356
|
-
} else {
|
|
2357
|
-
choices.products = { choices: [], error: result.error };
|
|
2358
|
-
}
|
|
2359
|
-
}
|
|
2360
|
-
if (targets.includes("webhook")) {
|
|
2361
|
-
const result = await discover(
|
|
2362
|
-
() => paginate(client, "/v1/webhooks", { "filter[store_id]": storeId })
|
|
2363
|
-
);
|
|
2364
|
-
choices.webhooks = result.ok ? { choices: result.value.map(toWebhookChoice) } : { choices: [], error: result.error };
|
|
2365
|
-
}
|
|
2366
|
-
if (targets.includes("discount")) {
|
|
2367
|
-
const result = await discover(
|
|
2368
|
-
() => paginate(client, "/v1/discounts", { "filter[store_id]": storeId })
|
|
2369
|
-
);
|
|
2370
|
-
choices.discounts = result.ok ? { choices: result.value.map(toDiscountChoice) } : { choices: [], error: result.error };
|
|
2371
|
-
}
|
|
2372
|
-
if (targets.includes("license-key")) {
|
|
2373
|
-
const result = await discover(
|
|
2374
|
-
() => paginate(client, "/v1/license-keys", { "filter[store_id]": storeId })
|
|
2375
|
-
);
|
|
2376
|
-
choices.licenseKeys = result.ok ? { choices: result.value.map(toLicenseKeyChoice) } : { choices: [], error: result.error };
|
|
2377
|
-
}
|
|
2378
|
-
if (targets.includes("subscription-plan")) {
|
|
2379
|
-
const listedProducts = products ?? [];
|
|
2380
|
-
if (choices.products.error) {
|
|
2381
|
-
choices.subscriptionPlans = { choices: [], error: choices.products.error };
|
|
2382
|
-
} else {
|
|
2383
|
-
choices.subscriptionPlans = await discoverSubscriptionPlans(client, listedProducts);
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2386
|
-
return choices;
|
|
2387
|
-
};
|
|
2388
|
-
var discoverSubscriptionPlans = async (client, products) => {
|
|
2389
|
-
const discovered = [];
|
|
2390
|
-
for (const product of products) {
|
|
2391
|
-
const result = await discover(
|
|
2392
|
-
() => paginate(client, "/v1/variants", {
|
|
2393
|
-
"filter[product_id]": product.id
|
|
2394
|
-
})
|
|
2395
|
-
);
|
|
2396
|
-
if (!result.ok) {
|
|
2397
|
-
return { choices: discovered, error: result.error };
|
|
2398
|
-
}
|
|
2399
|
-
for (const variant of result.value) {
|
|
2400
|
-
if (variant.attributes.is_subscription) {
|
|
2401
|
-
discovered.push(toSubscriptionPlanChoice(variant, product.attributes.name));
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
}
|
|
2405
|
-
return { choices: discovered };
|
|
2406
|
-
};
|
|
2407
|
-
var paginate = async (client, path4, query) => {
|
|
2408
|
-
const all = [];
|
|
2409
|
-
let pageNumber = Number(query?.["page[number]"] ?? 1);
|
|
2410
|
-
while (true) {
|
|
2411
|
-
const doc = await client.request({
|
|
2412
|
-
path: path4,
|
|
2413
|
-
query: { ...query ?? {}, "page[number]": pageNumber }
|
|
2414
|
-
});
|
|
2415
|
-
all.push(...doc.data);
|
|
2416
|
-
const lastPage = doc.meta?.page?.lastPage;
|
|
2417
|
-
if (lastPage === void 0 || pageNumber >= lastPage) return all;
|
|
2418
|
-
pageNumber += 1;
|
|
2419
|
-
}
|
|
2420
|
-
};
|
|
2421
|
-
var discover = async (load) => {
|
|
2422
|
-
try {
|
|
2423
|
-
return { ok: true, value: await load() };
|
|
2424
|
-
} catch (err) {
|
|
2425
|
-
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
2426
|
-
}
|
|
2427
|
-
};
|
|
2428
|
-
var toProductChoice = (product) => {
|
|
2429
|
-
return {
|
|
2430
|
-
value: product.id,
|
|
2431
|
-
label: `${product.attributes.name} (${product.attributes.status}) - id ${product.id}`
|
|
2432
|
-
};
|
|
2433
|
-
};
|
|
2434
|
-
var toWebhookChoice = (webhook) => {
|
|
2435
|
-
return {
|
|
2436
|
-
value: webhook.attributes.url,
|
|
2437
|
-
label: `${webhook.attributes.url} - id ${webhook.id}`
|
|
2438
|
-
};
|
|
2439
|
-
};
|
|
2440
|
-
var toDiscountChoice = (discount) => {
|
|
2441
|
-
return {
|
|
2442
|
-
value: discount.id,
|
|
2443
|
-
label: `${discount.attributes.name} (${discount.attributes.code}) - id ${discount.id}`
|
|
2444
|
-
};
|
|
1701
|
+
var renderCliError = (message, hints = []) => {
|
|
1702
|
+
const lines = [`fresh-squeezy: ${message}`];
|
|
1703
|
+
if (hints.length > 0) {
|
|
1704
|
+
lines.push("", "Try:");
|
|
1705
|
+
for (const hint of hints) {
|
|
1706
|
+
lines.push(` ${hint}`);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
return `${lines.join("\n")}
|
|
1710
|
+
`;
|
|
2445
1711
|
};
|
|
2446
|
-
var
|
|
2447
|
-
return
|
|
2448
|
-
value: licenseKey.id,
|
|
2449
|
-
label: `${licenseKey.attributes.key_short} (${licenseKey.attributes.status}) - id ${licenseKey.id}`
|
|
2450
|
-
};
|
|
1712
|
+
var isFreshSqueezyError = (err) => {
|
|
1713
|
+
return err instanceof FreshSqueezyError;
|
|
2451
1714
|
};
|
|
2452
|
-
var
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
1715
|
+
var getDoctorHints = (err) => {
|
|
1716
|
+
if (!isFreshSqueezyError(err)) return [];
|
|
1717
|
+
if (err.code === "MISSING_API_KEY") {
|
|
1718
|
+
return MISSING_API_KEY_HINTS;
|
|
1719
|
+
}
|
|
1720
|
+
if (err.code === "INVALID_MODE") {
|
|
1721
|
+
return ["fresh-squeezy doctor --mode test", "fresh-squeezy doctor --mode live"];
|
|
1722
|
+
}
|
|
1723
|
+
if (err.code === "NO_STORES") {
|
|
1724
|
+
return ["Check that the API key belongs to an account with a Lemon Squeezy store."];
|
|
1725
|
+
}
|
|
1726
|
+
return [];
|
|
2458
1727
|
};
|
|
2459
1728
|
|
|
2460
1729
|
// src/cli/prompts.ts
|
|
1730
|
+
import path from "path";
|
|
1731
|
+
import { checkbox, confirm, input, password, select } from "@inquirer/prompts";
|
|
2461
1732
|
var PROMPT_THEME = {
|
|
2462
1733
|
prefix: {
|
|
2463
1734
|
idle: "\u203A",
|
|
@@ -2477,6 +1748,10 @@ var pickLauncherAction = async () => {
|
|
|
2477
1748
|
name: "Run doctor now \u2014 pick stores interactively when needed",
|
|
2478
1749
|
value: "doctor"
|
|
2479
1750
|
},
|
|
1751
|
+
{
|
|
1752
|
+
name: "Manage resources \u2014 bulk delete / cancel / refund",
|
|
1753
|
+
value: "manage"
|
|
1754
|
+
},
|
|
2480
1755
|
{
|
|
2481
1756
|
name: "Show command examples \u2014 copy/paste friendly",
|
|
2482
1757
|
value: "examples"
|
|
@@ -2519,128 +1794,60 @@ var pickStores = async (choices) => {
|
|
|
2519
1794
|
validate: (selected) => selected.length > 0 ? true : "Pick at least one store."
|
|
2520
1795
|
});
|
|
2521
1796
|
};
|
|
2522
|
-
var
|
|
1797
|
+
var selectOptionalValidators = async () => {
|
|
2523
1798
|
return checkbox({
|
|
2524
1799
|
message: "Add resource checks to this doctor run?",
|
|
2525
1800
|
theme: PROMPT_THEME,
|
|
2526
|
-
choices:
|
|
2527
|
-
{ name: "Product checkout", value: "product" },
|
|
2528
|
-
{ name: "Webhook registration", value: "webhook" },
|
|
2529
|
-
{ name: "Discount code", value: "discount" },
|
|
2530
|
-
{ name: "License key", value: "license-key" },
|
|
2531
|
-
{ name: "Subscription plan", value: "subscription-plan" }
|
|
2532
|
-
]
|
|
1801
|
+
choices: OPTIONAL_VALIDATORS.map((row) => ({ name: row.menuLabel, value: row.name }))
|
|
2533
1802
|
});
|
|
2534
1803
|
};
|
|
2535
1804
|
var splitManualValues = (value) => (value ?? "").split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
2536
|
-
var required = (label) => {
|
|
2537
|
-
return (value) => value.trim().length > 0 ? true : `${label} is required.`;
|
|
2538
|
-
};
|
|
2539
|
-
var optional = (label) => {
|
|
2540
|
-
return (value) => {
|
|
2541
|
-
const trimmed = value.trim();
|
|
2542
|
-
if (trimmed.length === 0) return true;
|
|
2543
|
-
return required(label)(trimmed);
|
|
2544
|
-
};
|
|
2545
|
-
};
|
|
2546
|
-
var validateWebhookUrl = (value) => {
|
|
2547
|
-
const trimmed = value.trim();
|
|
2548
|
-
if (trimmed.length === 0) return "Webhook URL is required.";
|
|
2549
|
-
try {
|
|
2550
|
-
const url = new URL(trimmed);
|
|
2551
|
-
return url.protocol === "http:" || url.protocol === "https:" ? true : "Webhook URL must start with http:// or https://.";
|
|
2552
|
-
} catch {
|
|
2553
|
-
return "Enter a valid webhook URL.";
|
|
2554
|
-
}
|
|
2555
|
-
};
|
|
2556
|
-
var optionalWebhookUrls = (value) => {
|
|
2557
|
-
const values2 = splitManualValues(value);
|
|
2558
|
-
if (values2.length === 0) return true;
|
|
2559
|
-
return values2.every((entry) => validateWebhookUrl(entry) === true) ? true : "Enter valid webhook URLs.";
|
|
2560
|
-
};
|
|
2561
1805
|
var cleanList = (value) => {
|
|
2562
1806
|
const values2 = Array.isArray(value) ? value : splitManualValues(value);
|
|
2563
1807
|
const cleaned = Array.from(new Set(values2.map((entry) => entry.trim()).filter(Boolean)));
|
|
2564
1808
|
return cleaned.length > 0 ? cleaned : void 0;
|
|
2565
1809
|
};
|
|
2566
|
-
var
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
}
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
pickMessage: "Pick discounts to validate:",
|
|
2590
|
-
emptyMessage: "No discounts selected. What now?",
|
|
2591
|
-
manualMessage: "Discount IDs to validate (comma-separated, leave empty to skip):",
|
|
2592
|
-
validate: optional("Discount ID")
|
|
2593
|
-
},
|
|
2594
|
-
{
|
|
2595
|
-
target: "license-key",
|
|
2596
|
-
field: "licenseKeyIds",
|
|
2597
|
-
group: "licenseKeys",
|
|
2598
|
-
pickMessage: "Pick license keys to validate:",
|
|
2599
|
-
emptyMessage: "No license keys selected. What now?",
|
|
2600
|
-
manualMessage: "License key IDs to validate (comma-separated, leave empty to skip):",
|
|
2601
|
-
validate: optional("License key ID")
|
|
2602
|
-
},
|
|
2603
|
-
{
|
|
2604
|
-
target: "subscription-plan",
|
|
2605
|
-
field: "variantIds",
|
|
2606
|
-
group: "subscriptionPlans",
|
|
2607
|
-
pickMessage: "Pick subscription plans to validate:",
|
|
2608
|
-
emptyMessage: "No subscription plans selected. What now?",
|
|
2609
|
-
manualMessage: "Subscription plan variant IDs to validate (comma-separated, leave empty to skip):",
|
|
2610
|
-
validate: optional("Variant ID")
|
|
2611
|
-
}
|
|
2612
|
-
];
|
|
2613
|
-
var askForDoctorTargetValues = async (targets, choices = EMPTY_INIT_RESOURCE_CHOICES) => {
|
|
2614
|
-
if (targets.length === 0) return {};
|
|
1810
|
+
var pickDiscoveredIds = async (row, group) => {
|
|
1811
|
+
const discovered = group?.choices ?? [];
|
|
1812
|
+
if (discovered.length === 0) return "manual";
|
|
1813
|
+
const values2 = await checkbox({
|
|
1814
|
+
message: `Pick ${row.noun} to validate:`,
|
|
1815
|
+
theme: PROMPT_THEME,
|
|
1816
|
+
choices: discovered.map((choice) => ({ name: choice.label, value: choice.value }))
|
|
1817
|
+
});
|
|
1818
|
+
if (values2.length > 0) return values2;
|
|
1819
|
+
return askEmptyTargetAction(`No ${row.noun} selected. What now?`);
|
|
1820
|
+
};
|
|
1821
|
+
var askEmptyTargetAction = async (message) => {
|
|
1822
|
+
return select({
|
|
1823
|
+
message,
|
|
1824
|
+
theme: PROMPT_THEME,
|
|
1825
|
+
choices: [
|
|
1826
|
+
{ name: "Enter manually", value: "manual" },
|
|
1827
|
+
{ name: "Skip this check", value: "skip" }
|
|
1828
|
+
]
|
|
1829
|
+
});
|
|
1830
|
+
};
|
|
1831
|
+
var askForValidatorIds = async (names, choices = {}) => {
|
|
1832
|
+
if (names.length === 0) return {};
|
|
2615
1833
|
const answers = {};
|
|
2616
|
-
const
|
|
2617
|
-
for (const
|
|
2618
|
-
if (!
|
|
2619
|
-
await
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
manualMessage: spec.manualMessage,
|
|
2627
|
-
validate: spec.validate
|
|
2628
|
-
});
|
|
2629
|
-
}
|
|
2630
|
-
for (const question of manualQuestions) {
|
|
2631
|
-
answers[question.field] = await input({
|
|
2632
|
-
message: question.message,
|
|
1834
|
+
const manualRows = [];
|
|
1835
|
+
for (const row of OPTIONAL_VALIDATORS) {
|
|
1836
|
+
if (!names.includes(row.name)) continue;
|
|
1837
|
+
const picked = await pickDiscoveredIds(row, choices[row.name]);
|
|
1838
|
+
if (picked === "manual") manualRows.push(row);
|
|
1839
|
+
else if (picked !== "skip") answers[row.field] = picked;
|
|
1840
|
+
}
|
|
1841
|
+
for (const row of manualRows) {
|
|
1842
|
+
answers[row.field] = await input({
|
|
1843
|
+
message: `${row.manualNoun} to validate (comma-separated, leave empty to skip):`,
|
|
2633
1844
|
theme: PROMPT_THEME,
|
|
2634
|
-
validate:
|
|
1845
|
+
validate: row.validate
|
|
2635
1846
|
});
|
|
2636
1847
|
}
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
discountIds: cleanList(answers.discountIds),
|
|
2641
|
-
licenseKeyIds: cleanList(answers.licenseKeyIds),
|
|
2642
|
-
variantIds: cleanList(answers.variantIds)
|
|
2643
|
-
};
|
|
1848
|
+
const targetValues = {};
|
|
1849
|
+
for (const row of OPTIONAL_VALIDATORS) targetValues[row.field] = cleanList(answers[row.field]);
|
|
1850
|
+
return targetValues;
|
|
2644
1851
|
};
|
|
2645
1852
|
var confirmLiveModeRun = async () => {
|
|
2646
1853
|
return confirm({
|
|
@@ -2663,46 +1870,6 @@ var confirmResourceOp = async (message) => {
|
|
|
2663
1870
|
theme: PROMPT_THEME
|
|
2664
1871
|
});
|
|
2665
1872
|
};
|
|
2666
|
-
var resolveTargetValue = async (input2) => {
|
|
2667
|
-
if (input2.group.choices.length === 0) {
|
|
2668
|
-
input2.manualQuestions.push({
|
|
2669
|
-
field: input2.field,
|
|
2670
|
-
message: input2.manualMessage,
|
|
2671
|
-
validate: input2.validate
|
|
2672
|
-
});
|
|
2673
|
-
return;
|
|
2674
|
-
}
|
|
2675
|
-
const values2 = await checkbox({
|
|
2676
|
-
message: input2.pickMessage,
|
|
2677
|
-
theme: PROMPT_THEME,
|
|
2678
|
-
choices: input2.group.choices.map((choice) => ({
|
|
2679
|
-
name: choice.label,
|
|
2680
|
-
value: choice.value
|
|
2681
|
-
}))
|
|
2682
|
-
});
|
|
2683
|
-
if (values2.length > 0) {
|
|
2684
|
-
input2.answers[input2.field] = values2;
|
|
2685
|
-
return;
|
|
2686
|
-
}
|
|
2687
|
-
const action = await askEmptyTargetAction(input2.emptyMessage);
|
|
2688
|
-
if (action === "manual") {
|
|
2689
|
-
input2.manualQuestions.push({
|
|
2690
|
-
field: input2.field,
|
|
2691
|
-
message: input2.manualMessage,
|
|
2692
|
-
validate: input2.validate
|
|
2693
|
-
});
|
|
2694
|
-
}
|
|
2695
|
-
};
|
|
2696
|
-
var askEmptyTargetAction = async (message) => {
|
|
2697
|
-
return select({
|
|
2698
|
-
message,
|
|
2699
|
-
theme: PROMPT_THEME,
|
|
2700
|
-
choices: [
|
|
2701
|
-
{ name: "Enter manually", value: "manual" },
|
|
2702
|
-
{ name: "Skip this check", value: "skip" }
|
|
2703
|
-
]
|
|
2704
|
-
});
|
|
2705
|
-
};
|
|
2706
1873
|
var formatPromptPath = (filePath) => {
|
|
2707
1874
|
const relative = path.relative(process.cwd(), filePath);
|
|
2708
1875
|
if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) return relative;
|
|
@@ -2710,10 +1877,41 @@ var formatPromptPath = (filePath) => {
|
|
|
2710
1877
|
};
|
|
2711
1878
|
var isPromptCancel = (error) => {
|
|
2712
1879
|
if (!(error instanceof Error)) return false;
|
|
2713
|
-
return error.name === "ExitPromptError"
|
|
1880
|
+
return error.name === "ExitPromptError";
|
|
1881
|
+
};
|
|
1882
|
+
var pickManageOperation = async (ops) => {
|
|
1883
|
+
return select({
|
|
1884
|
+
message: "Which operation?",
|
|
1885
|
+
theme: PROMPT_THEME,
|
|
1886
|
+
choices: ops.map((op) => ({ name: `${op.verb} ${op.resource}`, value: op }))
|
|
1887
|
+
});
|
|
1888
|
+
};
|
|
1889
|
+
var askParentId = async (resource) => {
|
|
1890
|
+
const value = await input({
|
|
1891
|
+
message: `Parent id to list ${resource} items from:`,
|
|
1892
|
+
theme: PROMPT_THEME,
|
|
1893
|
+
validate: (entry) => entry.trim().length > 0 ? true : "A parent id is required."
|
|
1894
|
+
});
|
|
1895
|
+
return value.trim();
|
|
1896
|
+
};
|
|
1897
|
+
var pickBulkTargets = async (verb, items) => {
|
|
1898
|
+
return checkbox({
|
|
1899
|
+
message: `Pick items to ${verb} (space toggles, a selects all, enter confirms):`,
|
|
1900
|
+
theme: PROMPT_THEME,
|
|
1901
|
+
choices: items.map((item) => ({ name: `${item.label} \u2014 id ${item.id}`, value: item.id })),
|
|
1902
|
+
validate: (selected) => selected.length > 0 ? true : "Pick at least one item."
|
|
1903
|
+
});
|
|
2714
1904
|
};
|
|
2715
1905
|
|
|
2716
1906
|
// src/cli/resolveStores.ts
|
|
1907
|
+
var listStoreChoices = async (client) => {
|
|
1908
|
+
const stores = await client.stores.list();
|
|
1909
|
+
return stores.map((store) => ({
|
|
1910
|
+
id: store.id,
|
|
1911
|
+
name: store.attributes.name,
|
|
1912
|
+
slug: store.attributes.slug
|
|
1913
|
+
}));
|
|
1914
|
+
};
|
|
2717
1915
|
var resolveStores = async (client, input2) => {
|
|
2718
1916
|
if (input2.storeIds && input2.storeIds.length > 0) {
|
|
2719
1917
|
return { storeIds: dedupe(input2.storeIds), skipped: false };
|
|
@@ -2741,17 +1939,7 @@ var resolveStores = async (client, input2) => {
|
|
|
2741
1939
|
if (ids.length === 1) {
|
|
2742
1940
|
return { storeIds: ids, skipped: false };
|
|
2743
1941
|
}
|
|
2744
|
-
const
|
|
2745
|
-
ids.map(async (id) => {
|
|
2746
|
-
const result = await client.validateStore(id);
|
|
2747
|
-
return {
|
|
2748
|
-
id,
|
|
2749
|
-
name: result.resource?.name ?? "(unnamed)",
|
|
2750
|
-
slug: result.resource?.slug ?? ""
|
|
2751
|
-
};
|
|
2752
|
-
})
|
|
2753
|
-
);
|
|
2754
|
-
const picked = await pickStores(detailed);
|
|
1942
|
+
const picked = await pickStores(await listStoreChoices(client));
|
|
2755
1943
|
if (picked.length === 0) {
|
|
2756
1944
|
throw new FreshSqueezyError({
|
|
2757
1945
|
code: "NO_SELECTION",
|
|
@@ -2778,13 +1966,6 @@ var dedupe = (values2) => {
|
|
|
2778
1966
|
};
|
|
2779
1967
|
|
|
2780
1968
|
// src/cli/commands/doctor.ts
|
|
2781
|
-
var ALL_RESOURCE_TARGETS = [
|
|
2782
|
-
"product",
|
|
2783
|
-
"webhook",
|
|
2784
|
-
"discount",
|
|
2785
|
-
"license-key",
|
|
2786
|
-
"subscription-plan"
|
|
2787
|
-
];
|
|
2788
1969
|
var runDoctorCommand = async (options) => {
|
|
2789
1970
|
try {
|
|
2790
1971
|
const client = createFreshSqueezy({ mode: options.mode });
|
|
@@ -2798,8 +1979,8 @@ var runDoctorCommand = async (options) => {
|
|
|
2798
1979
|
}
|
|
2799
1980
|
const reports = await Promise.all(
|
|
2800
1981
|
resolved.storeIds.map(async (storeId) => {
|
|
2801
|
-
const
|
|
2802
|
-
return client.doctor({ storeId, ...
|
|
1982
|
+
const validatorIds = await resolveValidatorIds(client, storeId, options);
|
|
1983
|
+
return client.doctor({ storeId, ...validatorIds });
|
|
2803
1984
|
})
|
|
2804
1985
|
);
|
|
2805
1986
|
const ok = reports.every((report) => report.ok);
|
|
@@ -2825,55 +2006,47 @@ var runDoctorCommand = async (options) => {
|
|
|
2825
2006
|
return 2;
|
|
2826
2007
|
}
|
|
2827
2008
|
};
|
|
2828
|
-
var
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2009
|
+
var one = (value) => value ? [value] : void 0;
|
|
2010
|
+
var explicitValidatorIds = (options) => ({
|
|
2011
|
+
productIds: one(options.productId),
|
|
2012
|
+
webhookUrls: one(options.webhookUrl),
|
|
2013
|
+
discountIds: one(options.discountId),
|
|
2014
|
+
licenseKeyIds: one(options.licenseKeyId),
|
|
2015
|
+
variantIds: one(options.variantId)
|
|
2016
|
+
});
|
|
2017
|
+
var values = (group) => group?.choices.length ? group.choices.map((choice) => choice.value) : void 0;
|
|
2018
|
+
var mergeValues = (explicit, discovered) => {
|
|
2019
|
+
const merged = Array.from(/* @__PURE__ */ new Set([...explicit ?? [], ...discovered ?? []]));
|
|
2020
|
+
return merged.length > 0 ? merged : void 0;
|
|
2021
|
+
};
|
|
2022
|
+
var resolveValidatorIds = async (client, storeId, options) => {
|
|
2023
|
+
const explicit = explicitValidatorIds(options);
|
|
2836
2024
|
if (!options.allResources) return explicit;
|
|
2837
|
-
const
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2025
|
+
const allNames = OPTIONAL_VALIDATORS.map((row) => row.name);
|
|
2026
|
+
const discovered = await discoverChoices(client, storeId, allNames);
|
|
2027
|
+
for (const row of OPTIONAL_VALIDATORS) {
|
|
2028
|
+
const error = discovered[row.name]?.error;
|
|
2029
|
+
if (error) {
|
|
2030
|
+
process.stderr.write(
|
|
2031
|
+
`fresh-squeezy: discovery skipped ${row.label.toLowerCase()}: ${error}
|
|
2842
2032
|
`
|
|
2843
|
-
|
|
2033
|
+
);
|
|
2034
|
+
}
|
|
2844
2035
|
}
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
variantIds: mergeValues(explicit.variantIds, values(discovered.subscriptionPlans))
|
|
2851
|
-
};
|
|
2852
|
-
};
|
|
2853
|
-
var DISCOVERY_LABELS = [
|
|
2854
|
-
["products", "products"],
|
|
2855
|
-
["webhooks", "webhooks"],
|
|
2856
|
-
["discounts", "discounts"],
|
|
2857
|
-
["licenseKeys", "license keys"],
|
|
2858
|
-
["subscriptionPlans", "subscription plans"]
|
|
2859
|
-
];
|
|
2860
|
-
var formatDiscoveryCounts = (choices) => DISCOVERY_LABELS.map(([key, label]) => `${label} ${choices[key].choices.length}`).join(", ");
|
|
2861
|
-
var reportDiscoveryErrors = (choices) => {
|
|
2862
|
-
for (const [key, label] of DISCOVERY_LABELS) {
|
|
2863
|
-
const error = choices[key].error;
|
|
2864
|
-
if (error) process.stderr.write(`fresh-squeezy: discovery skipped ${label}: ${error}
|
|
2036
|
+
if (!options.json) {
|
|
2037
|
+
const counts = OPTIONAL_VALIDATORS.map(
|
|
2038
|
+
(row) => `${row.label.toLowerCase()} ${discovered[row.name]?.choices.length ?? 0}`
|
|
2039
|
+
).join(", ");
|
|
2040
|
+
process.stderr.write(`fresh-squeezy: discovered store ${storeId} resources: ${counts}.
|
|
2865
2041
|
`);
|
|
2866
2042
|
}
|
|
2043
|
+
const merged = {};
|
|
2044
|
+
for (const row of OPTIONAL_VALIDATORS) {
|
|
2045
|
+
merged[row.field] = mergeValues(explicit[row.field], values(discovered[row.name]));
|
|
2046
|
+
}
|
|
2047
|
+
return merged;
|
|
2867
2048
|
};
|
|
2868
|
-
var
|
|
2869
|
-
var one = (value) => value ? [value] : void 0;
|
|
2870
|
-
var mergeValues = (explicit, discovered) => {
|
|
2871
|
-
const merged = Array.from(/* @__PURE__ */ new Set([...explicit ?? [], ...discovered ?? []]));
|
|
2872
|
-
return merged.length > 0 ? merged : void 0;
|
|
2873
|
-
};
|
|
2874
|
-
var hasExplicitResourceSelection = (options) => Boolean(
|
|
2875
|
-
options.productId || options.webhookUrl || options.discountId || options.licenseKeyId || options.variantId
|
|
2876
|
-
);
|
|
2049
|
+
var hasExplicitResourceSelection = (options) => Object.values(explicitValidatorIds(options)).some((ids) => ids !== void 0);
|
|
2877
2050
|
var runConnectionOnly = async (client, options) => {
|
|
2878
2051
|
const connection = await client.validateConnection();
|
|
2879
2052
|
const report = {
|
|
@@ -2910,59 +2083,7 @@ var writeFatal = (err, asJson) => {
|
|
|
2910
2083
|
// src/cli/commands/init.ts
|
|
2911
2084
|
import fs from "fs/promises";
|
|
2912
2085
|
import path2 from "path";
|
|
2913
|
-
import chalk3 from "chalk";
|
|
2914
|
-
|
|
2915
|
-
// src/cli/brand.ts
|
|
2916
2086
|
import chalk2 from "chalk";
|
|
2917
|
-
var brand = chalk2.green.bold("fresh-squeezy");
|
|
2918
|
-
var divider = chalk2.dim("\u2022");
|
|
2919
|
-
var renderBrandHeader = (title, subtitle) => {
|
|
2920
|
-
const lines = [`${brand} ${divider} ${chalk2.bold(title)}`];
|
|
2921
|
-
if (subtitle) lines.push(chalk2.dim(subtitle));
|
|
2922
|
-
lines.push("");
|
|
2923
|
-
return lines.join("\n");
|
|
2924
|
-
};
|
|
2925
|
-
var renderStep = (index, total, title, detail) => {
|
|
2926
|
-
const suffix = detail ? ` ${chalk2.dim(detail)}` : "";
|
|
2927
|
-
const prefix = index > 1 ? "\n" : "";
|
|
2928
|
-
return `${prefix}${chalk2.yellow("\u25CF")} ${chalk2.dim(`${index}/${total}`)} ${chalk2.bold(title)}${suffix}
|
|
2929
|
-
`;
|
|
2930
|
-
};
|
|
2931
|
-
var renderDetected = (label, value, source) => {
|
|
2932
|
-
const suffix = source ? ` ${chalk2.dim(`from ${source}`)}` : "";
|
|
2933
|
-
return ` ${chalk2.green("\u2713")} ${chalk2.bold(label)} ${value}${suffix}
|
|
2934
|
-
`;
|
|
2935
|
-
};
|
|
2936
|
-
var renderCommandExamples = () => {
|
|
2937
|
-
return [
|
|
2938
|
-
renderBrandHeader("Command examples", "Direct commands stay stable for scripts and CI."),
|
|
2939
|
-
`${chalk2.bold("Guided setup")}`,
|
|
2940
|
-
" fresh-squeezy init",
|
|
2941
|
-
"",
|
|
2942
|
-
`${chalk2.bold("Full doctor")}`,
|
|
2943
|
-
" fresh-squeezy doctor --all-stores",
|
|
2944
|
-
" fresh-squeezy doctor --store-ids 12 --product-id 987 --webhook-url https://app.example.com/api/webhooks/lemon-squeezy",
|
|
2945
|
-
"",
|
|
2946
|
-
`${chalk2.bold("Single checks")}`,
|
|
2947
|
-
" fresh-squeezy validate connection",
|
|
2948
|
-
" fresh-squeezy validate webhook --store-ids 12 --webhook-url https://app.example.com/api/webhooks/lemon-squeezy",
|
|
2949
|
-
"",
|
|
2950
|
-
`${chalk2.bold("Bulk ops (delete / cancel / refund)")}`,
|
|
2951
|
-
" fresh-squeezy delete discount --all --store-ids 12 --dry-run",
|
|
2952
|
-
" fresh-squeezy delete discount --all --store-ids 12 --match TEST --yes",
|
|
2953
|
-
" fresh-squeezy delete webhook --ids 1,2,3 --yes",
|
|
2954
|
-
"",
|
|
2955
|
-
`${chalk2.bold("Automation")}`,
|
|
2956
|
-
" fresh-squeezy doctor --all-stores --json",
|
|
2957
|
-
""
|
|
2958
|
-
].join("\n");
|
|
2959
|
-
};
|
|
2960
|
-
var renderCancelMessage = () => {
|
|
2961
|
-
return `${chalk2.dim("fresh-squeezy cancelled.")}
|
|
2962
|
-
`;
|
|
2963
|
-
};
|
|
2964
|
-
|
|
2965
|
-
// src/cli/commands/init.ts
|
|
2966
2087
|
var runInitCommand = async (options = {}) => {
|
|
2967
2088
|
try {
|
|
2968
2089
|
return await runInitFlow(options);
|
|
@@ -2993,7 +2114,8 @@ var runInitFlow = async (options) => {
|
|
|
2993
2114
|
process.stdout.write(renderStep(1, 5, "Credentials", "reuse env when available"));
|
|
2994
2115
|
const apiKey = await resolveApiKey();
|
|
2995
2116
|
process.stdout.write(renderStep(2, 5, "Account probe", "detect mode and reachable stores"));
|
|
2996
|
-
const { client,
|
|
2117
|
+
const { client, connection } = await connectWithDetectedMode(apiKey);
|
|
2118
|
+
const mode = client.mode;
|
|
2997
2119
|
if (!connection.ok) {
|
|
2998
2120
|
process.stdout.write(`${renderReport({ ok: false, mode, results: [connection] })}
|
|
2999
2121
|
`);
|
|
@@ -3003,81 +2125,60 @@ var runInitFlow = async (options) => {
|
|
|
3003
2125
|
process.stderr.write(renderCancelMessage());
|
|
3004
2126
|
return 130;
|
|
3005
2127
|
}
|
|
3006
|
-
const
|
|
3007
|
-
if (
|
|
2128
|
+
const stores = await listStoreChoices(client);
|
|
2129
|
+
if (stores.length === 0) {
|
|
3008
2130
|
process.stdout.write(
|
|
3009
|
-
|
|
2131
|
+
chalk2.yellow(
|
|
3010
2132
|
"No stores reachable with this key. Create a store in Lemon Squeezy and retry.\n"
|
|
3011
2133
|
)
|
|
3012
2134
|
);
|
|
3013
2135
|
return 1;
|
|
3014
2136
|
}
|
|
3015
|
-
process.stdout.write(renderDetected("Stores", String(
|
|
3016
|
-
const stores = await Promise.all(storeIds.map((id) => client.validateStore(id)));
|
|
3017
|
-
const pickable = stores.filter((entry) => entry.ok && entry.resource).map((entry, index) => ({
|
|
3018
|
-
id: storeIds[index] ?? "",
|
|
3019
|
-
name: entry.resource?.name ?? "(unnamed)",
|
|
3020
|
-
slug: entry.resource?.slug ?? ""
|
|
3021
|
-
})).filter((entry) => entry.id !== "");
|
|
3022
|
-
if (pickable.length === 0) {
|
|
3023
|
-
process.stdout.write(
|
|
3024
|
-
chalk3.yellow(
|
|
3025
|
-
"Stores were discovered, but none could be validated. Check account access and retry.\n"
|
|
3026
|
-
)
|
|
3027
|
-
);
|
|
3028
|
-
return 1;
|
|
3029
|
-
}
|
|
2137
|
+
process.stdout.write(renderDetected("Stores", String(stores.length), "Lemon Squeezy API"));
|
|
3030
2138
|
process.stdout.write(renderStep(3, 5, "Store selection", "auto-select when unambiguous"));
|
|
3031
|
-
const storeId = await resolveStoreSelection(
|
|
2139
|
+
const storeId = await resolveStoreSelection(stores);
|
|
3032
2140
|
process.stdout.write(renderStep(4, 5, "Optional checks", "pick resources before manual IDs"));
|
|
3033
|
-
const
|
|
3034
|
-
const resourceChoices = await discoverChoices(client, storeId,
|
|
3035
|
-
process.stdout.write(renderDiscoverySummary(resourceChoices,
|
|
3036
|
-
const
|
|
2141
|
+
const selectedValidators = await selectOptionalValidators();
|
|
2142
|
+
const resourceChoices = await discoverChoices(client, storeId, selectedValidators);
|
|
2143
|
+
process.stdout.write(renderDiscoverySummary(resourceChoices, selectedValidators));
|
|
2144
|
+
const validatorIds = await askForValidatorIds(selectedValidators, resourceChoices);
|
|
3037
2145
|
const envPath = path2.resolve(process.cwd(), options.envFile ?? ".env");
|
|
3038
|
-
const checkNames =
|
|
2146
|
+
const checkNames = listValidatorNames(validatorIds);
|
|
3039
2147
|
process.stdout.write(renderSetupSummary({ envPath, mode, storeId, checkNames }));
|
|
3040
2148
|
const shouldWrite = await confirmWriteEnvFile(envPath);
|
|
3041
2149
|
if (shouldWrite) {
|
|
3042
2150
|
await writeEnvFile(envPath, { apiKey, mode, storeId });
|
|
3043
|
-
process.stdout.write(
|
|
2151
|
+
process.stdout.write(chalk2.green(`Wrote ${envPath}
|
|
3044
2152
|
`));
|
|
3045
2153
|
}
|
|
3046
|
-
process.stdout.write(
|
|
2154
|
+
process.stdout.write(chalk2.dim(`
|
|
3047
2155
|
Running doctor (${checkNames.join(", ")})...
|
|
3048
2156
|
|
|
3049
2157
|
`));
|
|
3050
|
-
const report = await client.doctor({ storeId, ...
|
|
2158
|
+
const report = await client.doctor({ storeId, ...validatorIds });
|
|
3051
2159
|
process.stdout.write(`${renderReport(report)}
|
|
3052
2160
|
`);
|
|
3053
2161
|
return report.ok ? 0 : 1;
|
|
3054
2162
|
};
|
|
3055
|
-
var resolveStoreSelection = async (
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
renderDetected("Store", `${store.name} (${store.slug})`, `id ${store.id}`)
|
|
3061
|
-
);
|
|
3062
|
-
return store.id;
|
|
2163
|
+
var resolveStoreSelection = async (stores) => {
|
|
2164
|
+
const [only, ...others] = stores;
|
|
2165
|
+
if (only && others.length === 0) {
|
|
2166
|
+
process.stdout.write(renderDetected("Store", `${only.name} (${only.slug})`, `id ${only.id}`));
|
|
2167
|
+
return only.id;
|
|
3063
2168
|
}
|
|
3064
|
-
return pickStore(
|
|
3065
|
-
};
|
|
3066
|
-
var discoverChoices = async (client, storeId, selectedTargets) => {
|
|
3067
|
-
if (selectedTargets.length === 0) return EMPTY_INIT_RESOURCE_CHOICES;
|
|
3068
|
-
return discoverInitResourceChoices(client, storeId, selectedTargets);
|
|
2169
|
+
return pickStore(stores);
|
|
3069
2170
|
};
|
|
3070
2171
|
var resolveApiKey = async () => {
|
|
3071
2172
|
const envApiKey = process.env[ENV_KEYS.apiKey]?.trim();
|
|
3072
2173
|
if (envApiKey) {
|
|
3073
|
-
process.stdout.write(
|
|
2174
|
+
process.stdout.write(chalk2.dim(`Using ${ENV_KEYS.apiKey} from environment.
|
|
3074
2175
|
`));
|
|
3075
2176
|
return envApiKey;
|
|
3076
2177
|
}
|
|
3077
2178
|
const answers = await askForApiKey();
|
|
3078
2179
|
return answers.apiKey;
|
|
3079
2180
|
};
|
|
3080
|
-
var
|
|
2181
|
+
var connectWithDetectedMode = async (apiKey) => {
|
|
3081
2182
|
const envMode = parseEnvMode();
|
|
3082
2183
|
const initialMode = envMode ?? "test";
|
|
3083
2184
|
let client = createFreshSqueezy({ apiKey, mode: initialMode });
|
|
@@ -3085,58 +2186,53 @@ var createDetectedClient = async (apiKey) => {
|
|
|
3085
2186
|
const actualMode = connection.resource?.actualMode;
|
|
3086
2187
|
if (actualMode && actualMode !== initialMode) {
|
|
3087
2188
|
const message = envMode ? `Detected ${actualMode}-mode API key; ignoring ${ENV_KEYS.mode}=${initialMode} for this run.` : `Detected ${actualMode}-mode API key.`;
|
|
3088
|
-
process.stdout.write(
|
|
2189
|
+
process.stdout.write(chalk2.yellow(`${message}
|
|
3089
2190
|
`));
|
|
3090
2191
|
client = createFreshSqueezy({ apiKey, mode: actualMode });
|
|
3091
2192
|
connection = await client.validateConnection();
|
|
3092
|
-
return { client,
|
|
2193
|
+
return { client, connection };
|
|
3093
2194
|
}
|
|
3094
2195
|
if (actualMode) {
|
|
3095
|
-
process.stdout.write(
|
|
2196
|
+
process.stdout.write(chalk2.dim(`Detected ${actualMode}-mode API key.
|
|
3096
2197
|
`));
|
|
3097
|
-
return { client,
|
|
2198
|
+
return { client, connection };
|
|
3098
2199
|
}
|
|
3099
2200
|
if (envMode) {
|
|
3100
2201
|
process.stdout.write(
|
|
3101
|
-
|
|
2202
|
+
chalk2.dim(`Using ${ENV_KEYS.mode}=${envMode}; API mode was not exposed.
|
|
3102
2203
|
`)
|
|
3103
2204
|
);
|
|
3104
|
-
return { client,
|
|
2205
|
+
return { client, connection };
|
|
3105
2206
|
}
|
|
3106
|
-
process.stdout.write(
|
|
3107
|
-
return { client,
|
|
2207
|
+
process.stdout.write(chalk2.dim("Could not auto-detect key mode; using test mode.\n"));
|
|
2208
|
+
return { client, connection };
|
|
3108
2209
|
};
|
|
3109
2210
|
var parseEnvMode = () => {
|
|
3110
2211
|
const value = process.env[ENV_KEYS.mode]?.trim();
|
|
3111
2212
|
if (value === "test" || value === "live") return value;
|
|
3112
2213
|
if (value) {
|
|
3113
2214
|
process.stdout.write(
|
|
3114
|
-
|
|
2215
|
+
chalk2.yellow(`Ignoring invalid ${ENV_KEYS.mode}=${value}; expected test or live.
|
|
3115
2216
|
`)
|
|
3116
2217
|
);
|
|
3117
2218
|
}
|
|
3118
2219
|
return void 0;
|
|
3119
2220
|
};
|
|
3120
|
-
var
|
|
2221
|
+
var listValidatorNames = (validatorIds) => {
|
|
3121
2222
|
const names = ["connection", "store"];
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
pushCheckName(names, "subscriptionPlan", targets.variantIds);
|
|
2223
|
+
for (const row of OPTIONAL_VALIDATORS) {
|
|
2224
|
+
const count = validatorIds[row.field]?.length ?? 0;
|
|
2225
|
+
if (count > 0) names.push(count === 1 ? row.resultName : `${row.resultName} x${count}`);
|
|
2226
|
+
}
|
|
3127
2227
|
return names;
|
|
3128
2228
|
};
|
|
3129
|
-
var pushCheckName = (names, name, values2) => {
|
|
3130
|
-
if (!values2 || values2.length === 0) return;
|
|
3131
|
-
names.push(values2.length === 1 ? name : `${name} x${values2.length}`);
|
|
3132
|
-
};
|
|
3133
2229
|
var renderSetupSummary = (input2) => {
|
|
3134
2230
|
return [
|
|
3135
2231
|
renderStep(5, 5, "Ready to verify", "review before writing env"),
|
|
3136
|
-
` ${
|
|
3137
|
-
` ${
|
|
3138
|
-
` ${
|
|
3139
|
-
` ${
|
|
2232
|
+
` ${chalk2.dim("mode")} ${input2.mode}`,
|
|
2233
|
+
` ${chalk2.dim("store")} ${input2.storeId}`,
|
|
2234
|
+
` ${chalk2.dim("checks")} ${input2.checkNames.join(", ")}`,
|
|
2235
|
+
` ${chalk2.dim("env")} ${input2.envPath}`,
|
|
3140
2236
|
""
|
|
3141
2237
|
].join("\n");
|
|
3142
2238
|
};
|
|
@@ -3178,32 +2274,25 @@ var readEnvFile = async (envPath) => {
|
|
|
3178
2274
|
throw err;
|
|
3179
2275
|
}
|
|
3180
2276
|
};
|
|
3181
|
-
var renderDiscoverySummary = (choices,
|
|
3182
|
-
if (
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
if (!selectedTargets.includes(target)) continue;
|
|
3193
|
-
if (group.error) {
|
|
3194
|
-
lines.push(chalk3.yellow(` ! ${label} discovery failed; manual entry is available.`));
|
|
3195
|
-
} else {
|
|
3196
|
-
lines.push(
|
|
3197
|
-
renderDetected(label, String(group.choices.length), "Lemon Squeezy API").trimEnd()
|
|
3198
|
-
);
|
|
2277
|
+
var renderDiscoverySummary = (choices, selectedValidators) => {
|
|
2278
|
+
if (selectedValidators.length === 0)
|
|
2279
|
+
return chalk2.dim(" No optional resource checks selected.\n");
|
|
2280
|
+
const lines = OPTIONAL_VALIDATORS.filter((row) => selectedValidators.includes(row.name)).map(
|
|
2281
|
+
(row) => {
|
|
2282
|
+
const group = choices[row.name];
|
|
2283
|
+
if (group?.error) {
|
|
2284
|
+
return chalk2.yellow(` ! ${row.label} discovery failed; manual entry is available.`);
|
|
2285
|
+
}
|
|
2286
|
+
const count = String(group?.choices.length ?? 0);
|
|
2287
|
+
return renderDetected(row.label, count, "Lemon Squeezy API").trimEnd();
|
|
3199
2288
|
}
|
|
3200
|
-
|
|
2289
|
+
);
|
|
3201
2290
|
return `${lines.join("\n")}
|
|
3202
2291
|
`;
|
|
3203
2292
|
};
|
|
3204
2293
|
|
|
3205
2294
|
// src/cli/commands/launcher.ts
|
|
3206
|
-
import
|
|
2295
|
+
import chalk3 from "chalk";
|
|
3207
2296
|
|
|
3208
2297
|
// src/cli/projectInstall.ts
|
|
3209
2298
|
import { spawn } from "child_process";
|
|
@@ -3276,100 +2365,25 @@ var buildInstallCommand = (packageManager, packageSpec) => {
|
|
|
3276
2365
|
return ["npm", "install", "--save-dev", packageSpec];
|
|
3277
2366
|
};
|
|
3278
2367
|
var runPackageCommand = (command, args, options) => {
|
|
3279
|
-
return new Promise((resolve2, reject) => {
|
|
3280
|
-
const child = spawn(command, args, {
|
|
3281
|
-
cwd: options.cwd,
|
|
3282
|
-
stdio: "inherit",
|
|
3283
|
-
shell: false
|
|
3284
|
-
});
|
|
3285
|
-
child.on("error", reject);
|
|
3286
|
-
child.on("close", (code) => resolve2(code ?? 1));
|
|
3287
|
-
});
|
|
3288
|
-
};
|
|
3289
|
-
var pathExists = async (filePath) => {
|
|
3290
|
-
try {
|
|
3291
|
-
await fs2.access(filePath);
|
|
3292
|
-
return true;
|
|
3293
|
-
} catch {
|
|
3294
|
-
return false;
|
|
3295
|
-
}
|
|
3296
|
-
};
|
|
3297
|
-
|
|
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 });
|
|
2368
|
+
return new Promise((resolve2, reject) => {
|
|
2369
|
+
const child = spawn(command, args, {
|
|
2370
|
+
cwd: options.cwd,
|
|
2371
|
+
stdio: "inherit",
|
|
2372
|
+
shell: false
|
|
2373
|
+
});
|
|
2374
|
+
child.on("error", reject);
|
|
2375
|
+
child.on("close", (code) => resolve2(code ?? 1));
|
|
2376
|
+
});
|
|
3349
2377
|
};
|
|
3350
|
-
var
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
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");
|
|
2378
|
+
var pathExists = async (filePath) => {
|
|
2379
|
+
try {
|
|
2380
|
+
await fs2.access(filePath);
|
|
2381
|
+
return true;
|
|
2382
|
+
} catch {
|
|
2383
|
+
return false;
|
|
3366
2384
|
}
|
|
3367
|
-
return chalk4.yellow(" No package.json found; running without installing a dev dependency.\n");
|
|
3368
2385
|
};
|
|
3369
2386
|
|
|
3370
|
-
// src/cli/commands/resourceOps.ts
|
|
3371
|
-
import { readFileSync } from "fs";
|
|
3372
|
-
|
|
3373
2387
|
// src/cli/commands/bulkOps.ts
|
|
3374
2388
|
var supportsBulk = (spec) => Boolean(spec.destructive) && spec.idRole === "id";
|
|
3375
2389
|
var describeBulkTarget = (item) => {
|
|
@@ -3456,7 +2470,166 @@ ${tail}
|
|
|
3456
2470
|
`;
|
|
3457
2471
|
};
|
|
3458
2472
|
|
|
2473
|
+
// src/cli/commands/manage.ts
|
|
2474
|
+
var listManageOperations = () => resourceRegistry.filter((spec) => supportsBulk(spec) && findResourceVerb(spec.resource, "list")).map((spec) => ({ resource: spec.resource, verb: spec.verb }));
|
|
2475
|
+
var renderEquivalentCommand = (input2) => {
|
|
2476
|
+
const parts = ["fresh-squeezy", input2.verb, input2.resource];
|
|
2477
|
+
if (input2.picked.length === input2.total) {
|
|
2478
|
+
parts.push("--all");
|
|
2479
|
+
if (input2.storeId) parts.push("--store-ids", input2.storeId);
|
|
2480
|
+
if (input2.parentId) parts.push("--parent-id", input2.parentId);
|
|
2481
|
+
} else {
|
|
2482
|
+
parts.push("--ids", input2.picked.join(","));
|
|
2483
|
+
}
|
|
2484
|
+
parts.push("--yes");
|
|
2485
|
+
if (input2.mode === "live") parts.push("--mode", "live");
|
|
2486
|
+
return parts.join(" ");
|
|
2487
|
+
};
|
|
2488
|
+
var runManageCommand = async () => {
|
|
2489
|
+
try {
|
|
2490
|
+
const client = createFreshSqueezy();
|
|
2491
|
+
const http = new HttpClient(resolveConfig());
|
|
2492
|
+
const op = await pickManageOperation(listManageOperations());
|
|
2493
|
+
const spec = findResourceVerb(op.resource, op.verb);
|
|
2494
|
+
const listSpec = findResourceVerb(op.resource, "list");
|
|
2495
|
+
if (!spec || !listSpec) {
|
|
2496
|
+
throw new FreshSqueezyError({
|
|
2497
|
+
code: "UNKNOWN_OP",
|
|
2498
|
+
message: `${op.verb} ${op.resource} is not available.`
|
|
2499
|
+
});
|
|
2500
|
+
}
|
|
2501
|
+
process.stdout.write(`
|
|
2502
|
+
${op.verb} ${op.resource} \u2014 mode=${client.mode}
|
|
2503
|
+
`);
|
|
2504
|
+
let scopes = [{}];
|
|
2505
|
+
if (listSpec.idRole === "store") {
|
|
2506
|
+
const { storeIds } = await resolveStores(client, { isInteractive: true });
|
|
2507
|
+
scopes = storeIds.map((storeId) => ({ storeId }));
|
|
2508
|
+
} else if (listSpec.idRole === "parent") {
|
|
2509
|
+
scopes = [{ parentId: await askParentId(op.resource) }];
|
|
2510
|
+
}
|
|
2511
|
+
let exitCode = 0;
|
|
2512
|
+
for (const scope of scopes) {
|
|
2513
|
+
const targets = await collectBulkTargets(http, spec, { all: true, ...scope });
|
|
2514
|
+
if (targets.length === 0) {
|
|
2515
|
+
process.stdout.write(
|
|
2516
|
+
`No ${op.resource} items found${scope.storeId ? ` in store ${scope.storeId}` : ""}.
|
|
2517
|
+
`
|
|
2518
|
+
);
|
|
2519
|
+
continue;
|
|
2520
|
+
}
|
|
2521
|
+
const pickedIds = await pickBulkTargets(op.verb, targets);
|
|
2522
|
+
const picked = targets.filter((t) => pickedIds.includes(t.id));
|
|
2523
|
+
const mark = client.mode === "live" ? "LIVE mode: " : "";
|
|
2524
|
+
const go = await confirmResourceOp(
|
|
2525
|
+
`${mark}${op.verb} ${picked.length} ${op.resource} item(s) (${previewTargets(picked)})?`
|
|
2526
|
+
);
|
|
2527
|
+
if (!go) {
|
|
2528
|
+
process.stdout.write("Skipped \u2014 nothing changed.\n");
|
|
2529
|
+
continue;
|
|
2530
|
+
}
|
|
2531
|
+
const summary = await executeBulk(http, spec, picked);
|
|
2532
|
+
process.stdout.write(renderBulkSummary(spec, summary));
|
|
2533
|
+
process.stdout.write(
|
|
2534
|
+
`
|
|
2535
|
+
Next time:
|
|
2536
|
+
${renderEquivalentCommand({
|
|
2537
|
+
verb: op.verb,
|
|
2538
|
+
resource: op.resource,
|
|
2539
|
+
mode: client.mode,
|
|
2540
|
+
...scope,
|
|
2541
|
+
picked: pickedIds,
|
|
2542
|
+
total: targets.length
|
|
2543
|
+
})}
|
|
2544
|
+
`
|
|
2545
|
+
);
|
|
2546
|
+
if (summary.failed > 0) exitCode = 1;
|
|
2547
|
+
}
|
|
2548
|
+
return exitCode;
|
|
2549
|
+
} catch (err) {
|
|
2550
|
+
if (isPromptCancel(err)) throw err;
|
|
2551
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2552
|
+
process.stderr.write(renderCliError(message, ["fresh-squeezy ops --list"]));
|
|
2553
|
+
return 2;
|
|
2554
|
+
}
|
|
2555
|
+
};
|
|
2556
|
+
|
|
2557
|
+
// src/cli/commands/launcher.ts
|
|
2558
|
+
var runLauncherCommand = async (options) => {
|
|
2559
|
+
if (!options.isInteractive) return 2;
|
|
2560
|
+
try {
|
|
2561
|
+
process.stdout.write(
|
|
2562
|
+
renderBrandHeader(
|
|
2563
|
+
"Project bootstrap",
|
|
2564
|
+
"Install the billing doctor locally, then verify Lemon Squeezy in one guided run."
|
|
2565
|
+
)
|
|
2566
|
+
);
|
|
2567
|
+
process.stdout.write(renderStep(1, 2, "Project dependency", "add fresh-squeezy when missing"));
|
|
2568
|
+
const installResult = await ensureFreshSqueezyDevDependency({
|
|
2569
|
+
skipInstall: options.install === false,
|
|
2570
|
+
packageVersion: options.packageVersion
|
|
2571
|
+
});
|
|
2572
|
+
process.stdout.write(renderProjectInstallResult(installResult));
|
|
2573
|
+
if (!process.env[ENV_KEYS.apiKey]?.trim()) {
|
|
2574
|
+
return startGuidedSetup();
|
|
2575
|
+
}
|
|
2576
|
+
return routeLauncherAction(await pickLauncherAction());
|
|
2577
|
+
} catch (err) {
|
|
2578
|
+
if (isPromptCancel(err)) {
|
|
2579
|
+
process.stderr.write(renderCancelMessage());
|
|
2580
|
+
return 130;
|
|
2581
|
+
}
|
|
2582
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2583
|
+
process.stderr.write(`${chalk3.red("fresh-squeezy bootstrap failed:")} ${message}
|
|
2584
|
+
|
|
2585
|
+
`);
|
|
2586
|
+
process.stderr.write(renderCommandExamples());
|
|
2587
|
+
return 1;
|
|
2588
|
+
}
|
|
2589
|
+
};
|
|
2590
|
+
var routeLauncherAction = async (action) => {
|
|
2591
|
+
switch (action) {
|
|
2592
|
+
case "init":
|
|
2593
|
+
return startGuidedSetup();
|
|
2594
|
+
case "doctor":
|
|
2595
|
+
return runDoctorCommand({ isInteractive: true });
|
|
2596
|
+
case "manage":
|
|
2597
|
+
return runManageCommand();
|
|
2598
|
+
case "examples":
|
|
2599
|
+
process.stdout.write(renderCommandExamples());
|
|
2600
|
+
return 0;
|
|
2601
|
+
case "exit":
|
|
2602
|
+
return 0;
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
var startGuidedSetup = () => {
|
|
2606
|
+
process.stdout.write(
|
|
2607
|
+
renderStep(2, 2, "Guided setup", "detect credentials, stores, and resources")
|
|
2608
|
+
);
|
|
2609
|
+
return runInitCommand({ isInteractive: true });
|
|
2610
|
+
};
|
|
2611
|
+
var renderProjectInstallResult = (result) => {
|
|
2612
|
+
if (result.status === "installed") {
|
|
2613
|
+
return renderDetected(
|
|
2614
|
+
"Dev dependency",
|
|
2615
|
+
"fresh-squeezy",
|
|
2616
|
+
`${result.packageManager ?? "npm"} install`
|
|
2617
|
+
);
|
|
2618
|
+
}
|
|
2619
|
+
if (result.status === "already-present") {
|
|
2620
|
+
return renderDetected("Dev dependency", "fresh-squeezy", "package.json");
|
|
2621
|
+
}
|
|
2622
|
+
if (result.status === "self-package") {
|
|
2623
|
+
return renderDetected("Project", "fresh-squeezy source repo", "package.json");
|
|
2624
|
+
}
|
|
2625
|
+
if (result.status === "skipped") {
|
|
2626
|
+
return chalk3.dim(" Skipped dev dependency install (--no-install).\n");
|
|
2627
|
+
}
|
|
2628
|
+
return chalk3.yellow(" No package.json found; running without installing a dev dependency.\n");
|
|
2629
|
+
};
|
|
2630
|
+
|
|
3459
2631
|
// src/cli/commands/resourceOps.ts
|
|
2632
|
+
import { readFileSync } from "fs";
|
|
3460
2633
|
var runResourceOpCommand = async (options) => {
|
|
3461
2634
|
if (options.listCatalog) {
|
|
3462
2635
|
writeCatalog(options.json ?? false);
|
|
@@ -3489,14 +2662,13 @@ var runResourceOpCommand = async (options) => {
|
|
|
3489
2662
|
message: "This op requires a JSON body (--body, --body-file, or stdin)."
|
|
3490
2663
|
});
|
|
3491
2664
|
}
|
|
3492
|
-
const client = createFreshSqueezy({ mode: options.mode });
|
|
3493
2665
|
const config = resolveConfig({ mode: options.mode });
|
|
3494
2666
|
const http = new HttpClient(config);
|
|
3495
2667
|
const storeId = options.storeIds?.[0] ?? config.storeId;
|
|
3496
2668
|
if (options.all || options.ids) {
|
|
3497
|
-
return await runBulk(http, spec,
|
|
2669
|
+
return await runBulk(http, spec, config.mode, options, { storeId, body });
|
|
3498
2670
|
}
|
|
3499
|
-
const allowed = await assertWriteSafety(spec,
|
|
2671
|
+
const allowed = await assertWriteSafety(spec, config.mode, options);
|
|
3500
2672
|
if (!allowed) return 2;
|
|
3501
2673
|
const opBody = await invokeOp(http, options.resource, options.verb, {
|
|
3502
2674
|
id: options.id,
|
|
@@ -3506,7 +2678,7 @@ var runResourceOpCommand = async (options) => {
|
|
|
3506
2678
|
});
|
|
3507
2679
|
const envelope = {
|
|
3508
2680
|
ok: true,
|
|
3509
|
-
mode:
|
|
2681
|
+
mode: config.mode,
|
|
3510
2682
|
resource: spec.resource,
|
|
3511
2683
|
verb: spec.verb,
|
|
3512
2684
|
docs: `https://docs.lemonsqueezy.com/api/${spec.docsPath}`,
|
|
@@ -3517,7 +2689,7 @@ var runResourceOpCommand = async (options) => {
|
|
|
3517
2689
|
`);
|
|
3518
2690
|
} else {
|
|
3519
2691
|
process.stdout.write(
|
|
3520
|
-
`${spec.verb} ${spec.resource} \u2014 ok (mode=${
|
|
2692
|
+
`${spec.verb} ${spec.resource} \u2014 ok (mode=${config.mode})
|
|
3521
2693
|
${JSON.stringify(opBody ?? null, null, 2)}
|
|
3522
2694
|
`
|
|
3523
2695
|
);
|
|
@@ -3657,44 +2829,15 @@ var writeFatal2 = (err, json) => {
|
|
|
3657
2829
|
};
|
|
3658
2830
|
|
|
3659
2831
|
// src/cli/commands/validate.ts
|
|
3660
|
-
var
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
return emit(await client.validateConnection(), options);
|
|
3665
|
-
}
|
|
3666
|
-
if (target === "product") {
|
|
3667
|
-
return emit(await runProduct(client, options), options);
|
|
3668
|
-
}
|
|
3669
|
-
if (target === "discount") {
|
|
3670
|
-
return emit(await runDiscount(client, options), options);
|
|
3671
|
-
}
|
|
3672
|
-
if (target === "license-key") {
|
|
3673
|
-
return emit(await runLicenseKey(client, options), options);
|
|
3674
|
-
}
|
|
3675
|
-
if (target === "subscription-plan") {
|
|
3676
|
-
return emit(await runSubscriptionPlan(client, options), options);
|
|
3677
|
-
}
|
|
3678
|
-
const storeIds = await resolveStoresForTarget(client, target, options);
|
|
3679
|
-
const results = await runPerStore(client, target, storeIds, options);
|
|
3680
|
-
if (options.json) {
|
|
3681
|
-
process.stdout.write(`${JSON.stringify(results, null, 2)}
|
|
3682
|
-
`);
|
|
3683
|
-
} else {
|
|
3684
|
-
for (const result of results) {
|
|
3685
|
-
process.stdout.write(`${renderResult(result)}
|
|
3686
|
-
|
|
3687
|
-
`);
|
|
3688
|
-
}
|
|
3689
|
-
}
|
|
3690
|
-
return results.every((result) => result.ok) ? 0 : 1;
|
|
3691
|
-
} catch (err) {
|
|
3692
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3693
|
-
process.stderr.write(renderCliError(message, getValidateHints(err, target)));
|
|
3694
|
-
return 2;
|
|
2832
|
+
var EXAMPLE_WEBHOOK_URL = "https://app.example.com/api/webhooks/lemon-squeezy";
|
|
2833
|
+
var required = (value, message) => {
|
|
2834
|
+
if (value === void 0 || value === null || value === "") {
|
|
2835
|
+
throw new FreshSqueezyError({ code: "MISSING_ARG", message });
|
|
3695
2836
|
}
|
|
2837
|
+
return value;
|
|
3696
2838
|
};
|
|
3697
|
-
var
|
|
2839
|
+
var requiredStoreId = (options, target) => required(options.storeIds?.[0], `--store-ids is required for \`validate ${target}\`.`);
|
|
2840
|
+
var resolveStoresForSubcommand = async (client, target, options) => {
|
|
3698
2841
|
const resolved = await resolveStores(client, {
|
|
3699
2842
|
storeIds: options.storeIds,
|
|
3700
2843
|
allStores: options.allStores,
|
|
@@ -3708,72 +2851,127 @@ var resolveStoresForTarget = async (client, target, options) => {
|
|
|
3708
2851
|
}
|
|
3709
2852
|
return resolved.storeIds;
|
|
3710
2853
|
};
|
|
3711
|
-
var
|
|
3712
|
-
|
|
3713
|
-
|
|
2854
|
+
var VALIDATE_SUBCOMMANDS = {
|
|
2855
|
+
connection: {
|
|
2856
|
+
description: "Check that the API key authenticates",
|
|
2857
|
+
stores: "none",
|
|
2858
|
+
examples: ["fresh-squeezy validate connection"],
|
|
2859
|
+
run: (client) => client.validateConnection()
|
|
2860
|
+
},
|
|
2861
|
+
store: {
|
|
2862
|
+
description: "Check one or more stores are reachable",
|
|
2863
|
+
stores: "multi",
|
|
2864
|
+
storeIdsHelp: "Comma-separated store IDs",
|
|
2865
|
+
examples: [
|
|
2866
|
+
"fresh-squeezy validate store --store-ids 12",
|
|
2867
|
+
"fresh-squeezy validate store --all-stores"
|
|
2868
|
+
],
|
|
2869
|
+
run: async (client, options) => {
|
|
2870
|
+
const storeIds = await resolveStoresForSubcommand(client, "store", options);
|
|
2871
|
+
return Promise.all(storeIds.map((storeId) => client.validateStore(storeId)));
|
|
2872
|
+
}
|
|
2873
|
+
},
|
|
2874
|
+
product: {
|
|
2875
|
+
description: "Check a product is published with at least one variant",
|
|
2876
|
+
stores: "ownership",
|
|
2877
|
+
required: ["--product-id <id>", "Product ID to validate"],
|
|
2878
|
+
storeIdsHelp: "Expected owning store IDs (first is used for cross-check)",
|
|
2879
|
+
examples: ["fresh-squeezy validate product --product-id 987"],
|
|
2880
|
+
run: (client, options) => client.validateProduct({
|
|
2881
|
+
productId: required(options.productId, "--product-id is required for `validate product`."),
|
|
2882
|
+
expectedStoreId: options.storeIds?.[0]
|
|
2883
|
+
})
|
|
2884
|
+
},
|
|
2885
|
+
webhook: {
|
|
2886
|
+
description: "Check a webhook is registered with the recommended events",
|
|
2887
|
+
stores: "multi",
|
|
2888
|
+
required: ["--webhook-url <url>", "Public webhook URL"],
|
|
2889
|
+
storeIdsHelp: "Comma-separated store IDs",
|
|
2890
|
+
examples: [
|
|
2891
|
+
`fresh-squeezy validate webhook --store-ids 12 --webhook-url ${EXAMPLE_WEBHOOK_URL}`,
|
|
2892
|
+
`fresh-squeezy validate webhook --all-stores --webhook-url ${EXAMPLE_WEBHOOK_URL}`
|
|
2893
|
+
],
|
|
2894
|
+
run: async (client, options) => {
|
|
2895
|
+
const storeIds = await resolveStoresForSubcommand(client, "webhook", options);
|
|
2896
|
+
const url = required(options.webhookUrl, "--webhook-url is required for `validate webhook`.");
|
|
2897
|
+
return Promise.all(storeIds.map((storeId) => client.validateWebhook({ storeId, url })));
|
|
2898
|
+
}
|
|
2899
|
+
},
|
|
2900
|
+
discount: {
|
|
2901
|
+
description: "Check a discount code is valid and redeemable",
|
|
2902
|
+
stores: "ownership",
|
|
2903
|
+
required: ["--discount-id <id>", "Discount ID to validate"],
|
|
2904
|
+
storeIdsHelp: "Store ID for ownership check (first ID used)",
|
|
2905
|
+
examples: ["fresh-squeezy validate discount --store-ids 12 --discount-id 123"],
|
|
2906
|
+
run: (client, options) => client.validateDiscount({
|
|
2907
|
+
discountId: required(
|
|
2908
|
+
options.discountId,
|
|
2909
|
+
"--discount-id is required for `validate discount`."
|
|
2910
|
+
),
|
|
2911
|
+
storeId: requiredStoreId(options, "discount")
|
|
2912
|
+
})
|
|
2913
|
+
},
|
|
2914
|
+
"license-key": {
|
|
2915
|
+
description: "Check a license key is active and not at its activation limit",
|
|
2916
|
+
stores: "ownership",
|
|
2917
|
+
required: ["--license-key-id <id>", "License key ID to validate"],
|
|
2918
|
+
storeIdsHelp: "Store ID for ownership check (first ID used)",
|
|
2919
|
+
examples: ["fresh-squeezy validate license-key --store-ids 12 --license-key-id 123"],
|
|
2920
|
+
run: (client, options) => client.validateLicenseKey({
|
|
2921
|
+
licenseKeyId: required(
|
|
2922
|
+
options.licenseKeyId,
|
|
2923
|
+
"--license-key-id is required for `validate license-key`."
|
|
2924
|
+
),
|
|
2925
|
+
storeId: requiredStoreId(options, "license-key")
|
|
2926
|
+
})
|
|
2927
|
+
},
|
|
2928
|
+
"subscription-plan": {
|
|
2929
|
+
description: "Check a subscription plan variant has valid billing interval and trial config",
|
|
2930
|
+
stores: "ownership",
|
|
2931
|
+
required: ["--variant-id <id>", "Variant ID of the subscription plan"],
|
|
2932
|
+
storeIdsHelp: "Store ID for ownership check (first ID used)",
|
|
2933
|
+
examples: ["fresh-squeezy validate subscription-plan --store-ids 12 --variant-id 123"],
|
|
2934
|
+
run: (client, options) => client.validateSubscriptionPlan({
|
|
2935
|
+
variantId: required(
|
|
2936
|
+
options.variantId,
|
|
2937
|
+
"--variant-id is required for `validate subscription-plan`."
|
|
2938
|
+
),
|
|
2939
|
+
storeId: requiredStoreId(options, "subscription-plan")
|
|
2940
|
+
})
|
|
3714
2941
|
}
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
2942
|
+
};
|
|
2943
|
+
var errorHints = (err, target) => {
|
|
2944
|
+
if (!(err instanceof FreshSqueezyError)) return [];
|
|
2945
|
+
if (err.code === "MISSING_API_KEY") return MISSING_API_KEY_HINTS;
|
|
2946
|
+
if (err.code === "INVALID_MODE") {
|
|
2947
|
+
return [
|
|
2948
|
+
`fresh-squeezy validate ${target} --mode test`,
|
|
2949
|
+
`fresh-squeezy validate ${target} --mode live`
|
|
2950
|
+
];
|
|
3718
2951
|
}
|
|
2952
|
+
if (err.code === "MISSING_ARG") return VALIDATE_SUBCOMMANDS[target].examples;
|
|
3719
2953
|
return [];
|
|
3720
2954
|
};
|
|
3721
|
-
var
|
|
3722
|
-
const
|
|
3723
|
-
const expected = options.storeIds?.[0];
|
|
3724
|
-
return client.validateProduct({
|
|
3725
|
-
productId,
|
|
3726
|
-
expectedStoreId: expected
|
|
3727
|
-
});
|
|
3728
|
-
};
|
|
3729
|
-
var runDiscount = async (client, options) => {
|
|
3730
|
-
const discountId = required2(
|
|
3731
|
-
options.discountId,
|
|
3732
|
-
"--discount-id is required for `validate discount`."
|
|
3733
|
-
);
|
|
3734
|
-
const storeId = required2(
|
|
3735
|
-
options.storeIds?.[0],
|
|
3736
|
-
"--store-ids is required for `validate discount`."
|
|
3737
|
-
);
|
|
3738
|
-
return client.validateDiscount({ storeId, discountId });
|
|
3739
|
-
};
|
|
3740
|
-
var runLicenseKey = async (client, options) => {
|
|
3741
|
-
const licenseKeyId = required2(
|
|
3742
|
-
options.licenseKeyId,
|
|
3743
|
-
"--license-key-id is required for `validate license-key`."
|
|
3744
|
-
);
|
|
3745
|
-
const storeId = required2(
|
|
3746
|
-
options.storeIds?.[0],
|
|
3747
|
-
"--store-ids is required for `validate license-key`."
|
|
3748
|
-
);
|
|
3749
|
-
return client.validateLicenseKey({ storeId, licenseKeyId });
|
|
3750
|
-
};
|
|
3751
|
-
var runSubscriptionPlan = async (client, options) => {
|
|
3752
|
-
const variantId = required2(
|
|
3753
|
-
options.variantId,
|
|
3754
|
-
"--variant-id is required for `validate subscription-plan`."
|
|
3755
|
-
);
|
|
3756
|
-
const storeId = required2(
|
|
3757
|
-
options.storeIds?.[0],
|
|
3758
|
-
"--store-ids is required for `validate subscription-plan`."
|
|
3759
|
-
);
|
|
3760
|
-
return client.validateSubscriptionPlan({ storeId, variantId });
|
|
3761
|
-
};
|
|
3762
|
-
var emit = (result, options) => {
|
|
2955
|
+
var writeResults = (results, options) => {
|
|
2956
|
+
const list = Array.isArray(results) ? results : [results];
|
|
3763
2957
|
if (options.json) {
|
|
3764
|
-
process.stdout.write(`${JSON.stringify(
|
|
2958
|
+
process.stdout.write(`${JSON.stringify(results, null, 2)}
|
|
3765
2959
|
`);
|
|
3766
2960
|
} else {
|
|
3767
|
-
|
|
3768
|
-
`);
|
|
2961
|
+
const separator = Array.isArray(results) ? "\n\n" : "\n";
|
|
2962
|
+
for (const result of list) process.stdout.write(`${renderResult(result)}${separator}`);
|
|
3769
2963
|
}
|
|
3770
|
-
return result.ok ? 0 : 1;
|
|
2964
|
+
return list.every((result) => result.ok) ? 0 : 1;
|
|
3771
2965
|
};
|
|
3772
|
-
var
|
|
3773
|
-
|
|
3774
|
-
|
|
2966
|
+
var runValidateCommand = async (target, options) => {
|
|
2967
|
+
try {
|
|
2968
|
+
const client = createFreshSqueezy({ mode: options.mode });
|
|
2969
|
+
return writeResults(await VALIDATE_SUBCOMMANDS[target].run(client, options), options);
|
|
2970
|
+
} catch (err) {
|
|
2971
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2972
|
+
process.stderr.write(renderCliError(message, errorHints(err, target)));
|
|
2973
|
+
return 2;
|
|
3775
2974
|
}
|
|
3776
|
-
return value;
|
|
3777
2975
|
};
|
|
3778
2976
|
|
|
3779
2977
|
// src/cli/main.ts
|
|
@@ -3791,7 +2989,10 @@ var OP_VERBS = [
|
|
|
3791
2989
|
];
|
|
3792
2990
|
var parseMode = (value) => {
|
|
3793
2991
|
if (value === "test" || value === "live") return value;
|
|
3794
|
-
throw new
|
|
2992
|
+
throw new FreshSqueezyError({
|
|
2993
|
+
code: "INVALID_MODE",
|
|
2994
|
+
message: `Mode must be "test" or "live", got "${value}"`
|
|
2995
|
+
});
|
|
3795
2996
|
};
|
|
3796
2997
|
var parseCsv = (value) => value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
3797
2998
|
var readPackageVersion = () => {
|
|
@@ -3810,69 +3011,9 @@ var readPackageVersion = () => {
|
|
|
3810
3011
|
};
|
|
3811
3012
|
var isInteractive = Boolean(process.stdin.isTTY);
|
|
3812
3013
|
var packageVersion = readPackageVersion();
|
|
3813
|
-
var VALIDATE_COMMANDS = [
|
|
3814
|
-
{
|
|
3815
|
-
name: "connection",
|
|
3816
|
-
description: "Check that the API key authenticates",
|
|
3817
|
-
stores: "none"
|
|
3818
|
-
},
|
|
3819
|
-
{
|
|
3820
|
-
name: "store",
|
|
3821
|
-
description: "Check one or more stores are reachable",
|
|
3822
|
-
stores: "multi",
|
|
3823
|
-
storeIdsHelp: "Comma-separated store IDs"
|
|
3824
|
-
},
|
|
3825
|
-
{
|
|
3826
|
-
name: "product",
|
|
3827
|
-
description: "Check a product is published with at least one variant",
|
|
3828
|
-
stores: "ownership",
|
|
3829
|
-
required: ["--product-id <id>", "Product ID to validate"],
|
|
3830
|
-
storeIdsHelp: "Expected owning store IDs (first is used for cross-check)"
|
|
3831
|
-
},
|
|
3832
|
-
{
|
|
3833
|
-
name: "webhook",
|
|
3834
|
-
description: "Check a webhook is registered with the recommended events",
|
|
3835
|
-
stores: "multi",
|
|
3836
|
-
required: ["--webhook-url <url>", "Public webhook URL"],
|
|
3837
|
-
storeIdsHelp: "Comma-separated store IDs"
|
|
3838
|
-
},
|
|
3839
|
-
{
|
|
3840
|
-
name: "discount",
|
|
3841
|
-
description: "Check a discount code is valid and redeemable",
|
|
3842
|
-
stores: "ownership",
|
|
3843
|
-
required: ["--discount-id <id>", "Discount ID to validate"],
|
|
3844
|
-
storeIdsHelp: "Store ID for ownership check (first ID used)"
|
|
3845
|
-
},
|
|
3846
|
-
{
|
|
3847
|
-
name: "license-key",
|
|
3848
|
-
description: "Check a license key is active and not at its activation limit",
|
|
3849
|
-
stores: "ownership",
|
|
3850
|
-
required: ["--license-key-id <id>", "License key ID to validate"],
|
|
3851
|
-
storeIdsHelp: "Store ID for ownership check (first ID used)"
|
|
3852
|
-
},
|
|
3853
|
-
{
|
|
3854
|
-
name: "subscription-plan",
|
|
3855
|
-
description: "Check a subscription plan variant has valid billing interval and trial config",
|
|
3856
|
-
stores: "ownership",
|
|
3857
|
-
required: ["--variant-id <id>", "Variant ID of the subscription plan"],
|
|
3858
|
-
storeIdsHelp: "Store ID for ownership check (first ID used)"
|
|
3859
|
-
}
|
|
3860
|
-
];
|
|
3861
3014
|
var exitWith = async (code) => {
|
|
3862
|
-
process.
|
|
3863
|
-
};
|
|
3864
|
-
var toValidateOptions = (opts) => ({
|
|
3865
|
-
mode: opts.mode,
|
|
3866
|
-
storeIds: opts.storeIds,
|
|
3867
|
-
allStores: Boolean(opts.allStores),
|
|
3868
|
-
productId: opts.productId,
|
|
3869
|
-
webhookUrl: opts.webhookUrl,
|
|
3870
|
-
discountId: opts.discountId,
|
|
3871
|
-
licenseKeyId: opts.licenseKeyId,
|
|
3872
|
-
variantId: opts.variantId,
|
|
3873
|
-
json: Boolean(opts.json),
|
|
3874
|
-
isInteractive
|
|
3875
|
-
});
|
|
3015
|
+
process.exitCode = await code;
|
|
3016
|
+
};
|
|
3876
3017
|
var attachModeJson = (cmd) => cmd.option("-m, --mode <mode>", "test or live", parseMode).option("--json", "Emit machine-readable JSON");
|
|
3877
3018
|
var attachValidateFlags = (cmd, spec) => {
|
|
3878
3019
|
if (spec.required) cmd.requiredOption(spec.required[0], spec.required[1]);
|
|
@@ -3924,7 +3065,8 @@ program.action(async (opts) => {
|
|
|
3924
3065
|
"fresh-squeezy validate connection"
|
|
3925
3066
|
])
|
|
3926
3067
|
);
|
|
3927
|
-
process.
|
|
3068
|
+
process.exitCode = 2;
|
|
3069
|
+
return;
|
|
3928
3070
|
}
|
|
3929
3071
|
await exitWith(
|
|
3930
3072
|
runLauncherCommand({
|
|
@@ -3938,12 +3080,7 @@ var doctor2 = program.command("doctor").description("Run the full integration do
|
|
|
3938
3080
|
"--all-resources",
|
|
3939
3081
|
"Discover and validate every supported resource in each selected store"
|
|
3940
3082
|
).option("--product-id <id>", "Product to validate").option("--webhook-url <url>", "Webhook URL to validate").option("--discount-id <id>", "Discount to validate").option("--license-key-id <id>", "License key to validate").option("--variant-id <id>", "Subscription plan variant to validate").option("--json", "Emit machine-readable JSON").action(async (opts) => {
|
|
3941
|
-
await exitWith(
|
|
3942
|
-
runDoctorCommand({
|
|
3943
|
-
...toValidateOptions(opts),
|
|
3944
|
-
allResources: Boolean(opts.allResources)
|
|
3945
|
-
})
|
|
3946
|
-
);
|
|
3083
|
+
await exitWith(runDoctorCommand({ ...opts, isInteractive }));
|
|
3947
3084
|
});
|
|
3948
3085
|
doctor2.addHelpText(
|
|
3949
3086
|
"after",
|
|
@@ -3966,13 +3103,13 @@ Examples:
|
|
|
3966
3103
|
fresh-squeezy validate webhook --store-ids 12 --webhook-url https://app.example.com/api/webhooks/lemon-squeezy
|
|
3967
3104
|
|
|
3968
3105
|
Targets:
|
|
3969
|
-
|
|
3106
|
+
${Object.keys(VALIDATE_SUBCOMMANDS).join(", ")}
|
|
3970
3107
|
`
|
|
3971
3108
|
);
|
|
3972
|
-
for (const spec of
|
|
3973
|
-
attachValidateFlags(validate.command(
|
|
3109
|
+
for (const [name, spec] of Object.entries(VALIDATE_SUBCOMMANDS)) {
|
|
3110
|
+
attachValidateFlags(validate.command(name).description(spec.description), spec).action(
|
|
3974
3111
|
async (opts) => {
|
|
3975
|
-
await exitWith(runValidateCommand(
|
|
3112
|
+
await exitWith(runValidateCommand(name, { ...opts, isInteractive }));
|
|
3976
3113
|
}
|
|
3977
3114
|
);
|
|
3978
3115
|
}
|
|
@@ -4053,6 +3190,6 @@ program.parseAsync(process.argv).catch((err) => {
|
|
|
4053
3190
|
const message = err instanceof Error ? err.message : String(err);
|
|
4054
3191
|
const hints = message.includes("Mode must be") ? ["fresh-squeezy doctor --mode test", "fresh-squeezy doctor --mode live"] : ["fresh-squeezy --help"];
|
|
4055
3192
|
process.stderr.write(renderCliError(message, hints));
|
|
4056
|
-
process.
|
|
3193
|
+
process.exitCode = 2;
|
|
4057
3194
|
});
|
|
4058
3195
|
//# sourceMappingURL=cli.js.map
|