@rovela-ai/sdk 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/admin/api/payment-methods.d.ts.map +1 -1
  2. package/dist/admin/api/payment-methods.js +8 -0
  3. package/dist/admin/api/payment-methods.js.map +1 -1
  4. package/dist/admin/api/shipping.d.ts.map +1 -1
  5. package/dist/admin/api/shipping.js +15 -0
  6. package/dist/admin/api/shipping.js.map +1 -1
  7. package/dist/admin/api/tax-zones.d.ts.map +1 -1
  8. package/dist/admin/api/tax-zones.js +17 -0
  9. package/dist/admin/api/tax-zones.js.map +1 -1
  10. package/dist/admin/components/GroupVisibilityPicker.d.ts +10 -0
  11. package/dist/admin/components/GroupVisibilityPicker.d.ts.map +1 -0
  12. package/dist/admin/components/GroupVisibilityPicker.js +30 -0
  13. package/dist/admin/components/GroupVisibilityPicker.js.map +1 -0
  14. package/dist/admin/components/PaymentMethodsManager.d.ts.map +1 -1
  15. package/dist/admin/components/PaymentMethodsManager.js +7 -3
  16. package/dist/admin/components/PaymentMethodsManager.js.map +1 -1
  17. package/dist/admin/components/ShippingSettings.d.ts.map +1 -1
  18. package/dist/admin/components/ShippingSettings.js +4 -1
  19. package/dist/admin/components/ShippingSettings.js.map +1 -1
  20. package/dist/admin/components/TaxSettings.d.ts.map +1 -1
  21. package/dist/admin/components/TaxSettings.js +5 -1
  22. package/dist/admin/components/TaxSettings.js.map +1 -1
  23. package/dist/admin/components/index.d.ts +2 -0
  24. package/dist/admin/components/index.d.ts.map +1 -1
  25. package/dist/admin/components/index.js +1 -0
  26. package/dist/admin/components/index.js.map +1 -1
  27. package/dist/admin/types.d.ts +11 -0
  28. package/dist/admin/types.d.ts.map +1 -1
  29. package/dist/checkout/api/manual.d.ts.map +1 -1
  30. package/dist/checkout/api/manual.js +8 -1
  31. package/dist/checkout/api/manual.js.map +1 -1
  32. package/dist/checkout/api/payment-methods.d.ts.map +1 -1
  33. package/dist/checkout/api/payment-methods.js +5 -1
  34. package/dist/checkout/api/payment-methods.js.map +1 -1
  35. package/dist/checkout/api/shipping.d.ts.map +1 -1
  36. package/dist/checkout/api/shipping.js +8 -1
  37. package/dist/checkout/api/shipping.js.map +1 -1
  38. package/dist/checkout/index.d.ts +1 -1
  39. package/dist/checkout/index.d.ts.map +1 -1
  40. package/dist/checkout/index.js.map +1 -1
  41. package/dist/checkout/server/create-checkout-session.d.ts.map +1 -1
  42. package/dist/checkout/server/create-checkout-session.js +2 -1
  43. package/dist/checkout/server/create-checkout-session.js.map +1 -1
  44. package/dist/checkout/server/handle-webhook.js +1 -0
  45. package/dist/checkout/server/handle-webhook.js.map +1 -1
  46. package/dist/checkout/server/pricing.d.ts +4 -0
  47. package/dist/checkout/server/pricing.d.ts.map +1 -1
  48. package/dist/checkout/server/pricing.js +7 -4
  49. package/dist/checkout/server/pricing.js.map +1 -1
  50. package/dist/core/db/index.d.ts +1 -1
  51. package/dist/core/db/index.d.ts.map +1 -1
  52. package/dist/core/db/index.js +2 -0
  53. package/dist/core/db/index.js.map +1 -1
  54. package/dist/core/db/queries.d.ts +36 -5
  55. package/dist/core/db/queries.d.ts.map +1 -1
  56. package/dist/core/db/queries.js +87 -7
  57. package/dist/core/db/queries.js.map +1 -1
  58. package/dist/core/db/schema.d.ts +57 -0
  59. package/dist/core/db/schema.d.ts.map +1 -1
  60. package/dist/core/db/schema.js +15 -0
  61. package/dist/core/db/schema.js.map +1 -1
  62. package/dist/core/server/index.d.ts +1 -1
  63. package/dist/core/server/index.d.ts.map +1 -1
  64. package/dist/core/server/index.js +2 -0
  65. package/dist/core/server/index.js.map +1 -1
  66. package/dist/emails/send/orders.d.ts +2 -0
  67. package/dist/emails/send/orders.d.ts.map +1 -1
  68. package/dist/emails/send/orders.js +18 -12
  69. package/dist/emails/send/orders.js.map +1 -1
  70. package/dist/emails/templates/order-confirmation.d.ts.map +1 -1
  71. package/dist/emails/templates/order-confirmation.js +4 -2
  72. package/dist/emails/templates/order-confirmation.js.map +1 -1
  73. package/dist/emails/types.d.ts +2 -0
  74. package/dist/emails/types.d.ts.map +1 -1
  75. package/package.json +1 -1
@@ -1644,6 +1644,51 @@ export async function setCustomerGroup(customerId, groupId) {
1644
1644
  // =============================================================================
1645
1645
  // Payment Methods (merchant-managed payment options beyond native Stripe)
1646
1646
  // =============================================================================
1647
+ // =============================================================================
1648
+ // Group gating (Phase 4) — visible_to_groups on payment methods / shipping
1649
+ // rates / tax zones. Empty array = visible to everyone (opt-in gating).
1650
+ // =============================================================================
1651
+ let visibilityColumnsEnsured = false;
1652
+ /**
1653
+ * Self-heal the `visible_to_groups` column on the three gateable tables.
1654
+ * Empty-array default keeps every existing row visible to everyone.
1655
+ * Idempotent + module-cached.
1656
+ */
1657
+ export async function ensureVisibilityColumns() {
1658
+ if (visibilityColumnsEnsured)
1659
+ return;
1660
+ const db = getDb();
1661
+ try {
1662
+ await db.execute(sql `ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS visible_to_groups jsonb NOT NULL DEFAULT '[]'::jsonb`);
1663
+ await db.execute(sql `ALTER TABLE shipping_rates ADD COLUMN IF NOT EXISTS visible_to_groups jsonb NOT NULL DEFAULT '[]'::jsonb`);
1664
+ await db.execute(sql `ALTER TABLE tax_zones ADD COLUMN IF NOT EXISTS visible_to_groups jsonb NOT NULL DEFAULT '[]'::jsonb`);
1665
+ visibilityColumnsEnsured = true;
1666
+ }
1667
+ catch (err) {
1668
+ console.error('[ensureVisibilityColumns] migration failed:', err);
1669
+ throw err;
1670
+ }
1671
+ }
1672
+ /** Best-effort variant for hot read paths. */
1673
+ async function ensureVisibilityColumnsSafe() {
1674
+ try {
1675
+ await ensureVisibilityColumns();
1676
+ }
1677
+ catch {
1678
+ // logged inside ensureVisibilityColumns
1679
+ }
1680
+ }
1681
+ /**
1682
+ * The single gating predicate: an entry with an empty `visibleToGroups` list
1683
+ * is visible to EVERYONE (guests included); a non-empty list requires the
1684
+ * trusted customer's group to be in it.
1685
+ */
1686
+ export function isVisibleToGroup(visibleToGroups, groupId) {
1687
+ const groups = Array.isArray(visibleToGroups) ? visibleToGroups : [];
1688
+ if (groups.length === 0)
1689
+ return true;
1690
+ return !!groupId && groups.includes(groupId);
1691
+ }
1647
1692
  let paymentMethodsSchemaEnsured = false;
1648
1693
  /**
1649
1694
  * Self-heal the payment-methods schema on stores provisioned before this
@@ -1682,6 +1727,8 @@ export async function ensurePaymentMethodsSchema() {
1682
1727
  // FK-free (soft reference, like orders.customer_id) so this ALTER never
1683
1728
  // depends on the store_locations table existing first.
1684
1729
  await db.execute(sql `ALTER TABLE orders ADD COLUMN IF NOT EXISTS pickup_location_id uuid`);
1730
+ // Group gating (Phase 4) — payment_methods reads all pass through here.
1731
+ await ensureVisibilityColumns();
1685
1732
  paymentMethodsSchemaEnsured = true;
1686
1733
  }
1687
1734
  catch (err) {
@@ -1707,15 +1754,20 @@ export async function findPaymentMethods() {
1707
1754
  .from(schema.paymentMethods)
1708
1755
  .orderBy(asc(schema.paymentMethods.sortOrder), asc(schema.paymentMethods.createdAt));
1709
1756
  }
1710
- /** Enabled methods only — what checkout offers alongside native Stripe. */
1711
- export async function findEnabledPaymentMethods() {
1757
+ /**
1758
+ * Enabled methods only — what checkout offers alongside native Stripe.
1759
+ * `groupId` is the TRUSTED customer group (resolveCheckoutCustomer);
1760
+ * group-gated methods are hidden from guests and non-members.
1761
+ */
1762
+ export async function findEnabledPaymentMethods(groupId) {
1712
1763
  await ensurePaymentMethodsSchema();
1713
1764
  const db = getDb();
1714
- return db
1765
+ const methods = await db
1715
1766
  .select()
1716
1767
  .from(schema.paymentMethods)
1717
1768
  .where(eq(schema.paymentMethods.enabled, true))
1718
1769
  .orderBy(asc(schema.paymentMethods.sortOrder), asc(schema.paymentMethods.createdAt));
1770
+ return methods.filter((m) => isVisibleToGroup(m.visibleToGroups, groupId));
1719
1771
  }
1720
1772
  export async function findPaymentMethodById(id) {
1721
1773
  await ensurePaymentMethodsSchema();
@@ -1739,6 +1791,7 @@ export async function createPaymentMethod(data) {
1739
1791
  enabled: data.enabled ?? true,
1740
1792
  sortOrder: data.sortOrder ?? 0,
1741
1793
  config: data.config ?? {},
1794
+ visibleToGroups: data.visibleToGroups ?? [],
1742
1795
  })
1743
1796
  .returning();
1744
1797
  return method;
@@ -1759,6 +1812,8 @@ export async function updatePaymentMethod(id, data) {
1759
1812
  set.sortOrder = data.sortOrder;
1760
1813
  if (data.config !== undefined)
1761
1814
  set.config = data.config;
1815
+ if (data.visibleToGroups !== undefined)
1816
+ set.visibleToGroups = data.visibleToGroups;
1762
1817
  const [method] = await db
1763
1818
  .update(schema.paymentMethods)
1764
1819
  .set(set)
@@ -2574,6 +2629,8 @@ export async function findCarriersWithZonesAndRates() {
2574
2629
  * Find all shipping zones with their rates and carrier info
2575
2630
  */
2576
2631
  export async function findShippingZones() {
2632
+ // Rate SELECTs enumerate visible_to_groups — self-heal older stores first.
2633
+ await ensureVisibilityColumnsSafe();
2577
2634
  const db = getDb();
2578
2635
  // Get all zones with carrier info via join
2579
2636
  const zonesWithCarriers = await db
@@ -2625,6 +2682,7 @@ export async function findShippingZones() {
2625
2682
  estimatedDays: rate.estimatedDays,
2626
2683
  isActive: rate.isActive,
2627
2684
  sortOrder: rate.sortOrder,
2685
+ visibleToGroups: Array.isArray(rate.visibleToGroups) ? rate.visibleToGroups : [],
2628
2686
  })),
2629
2687
  }));
2630
2688
  }
@@ -2686,6 +2744,7 @@ export async function deleteShippingZone(id) {
2686
2744
  * Create a shipping rate for a zone
2687
2745
  */
2688
2746
  export async function createShippingRate(data) {
2747
+ await ensureVisibilityColumnsSafe();
2689
2748
  const db = getDb();
2690
2749
  const [rate] = await db
2691
2750
  .insert(schema.shippingRates)
@@ -2699,6 +2758,7 @@ export async function createShippingRate(data) {
2699
2758
  estimatedDays: data.estimatedDays || null,
2700
2759
  isActive: data.isActive ?? true,
2701
2760
  sortOrder: data.sortOrder ?? 0,
2761
+ visibleToGroups: data.visibleToGroups ?? [],
2702
2762
  })
2703
2763
  .returning();
2704
2764
  return rate;
@@ -2707,6 +2767,7 @@ export async function createShippingRate(data) {
2707
2767
  * Update a shipping rate
2708
2768
  */
2709
2769
  export async function updateShippingRate(id, data) {
2770
+ await ensureVisibilityColumnsSafe();
2710
2771
  const db = getDb();
2711
2772
  const updateData = {};
2712
2773
  if (data.name !== undefined)
@@ -2725,6 +2786,8 @@ export async function updateShippingRate(id, data) {
2725
2786
  updateData.isActive = data.isActive;
2726
2787
  if (data.sortOrder !== undefined)
2727
2788
  updateData.sortOrder = data.sortOrder;
2789
+ if (data.visibleToGroups !== undefined)
2790
+ updateData.visibleToGroups = data.visibleToGroups;
2728
2791
  const [rate] = await db
2729
2792
  .update(schema.shippingRates)
2730
2793
  .set(updateData)
@@ -2751,7 +2814,7 @@ export async function deleteShippingRate(id) {
2751
2814
  * @param orderAmount - Order subtotal in dollars
2752
2815
  * @returns Array of applicable shipping rates with carrier info
2753
2816
  */
2754
- export async function findShippingRatesForCountry(country, orderAmount) {
2817
+ export async function findShippingRatesForCountry(country, orderAmount, groupId) {
2755
2818
  const zones = await findActiveShippingZones();
2756
2819
  const applicableRates = [];
2757
2820
  for (const zone of zones) {
@@ -2767,6 +2830,10 @@ export async function findShippingRatesForCountry(country, orderAmount) {
2767
2830
  for (const rate of zone.rates) {
2768
2831
  if (!rate.isActive)
2769
2832
  continue;
2833
+ // Group gating: a rate restricted to groups the customer isn't in is
2834
+ // simply not offered (guests see only unrestricted rates).
2835
+ if (!isVisibleToGroup(rate.visibleToGroups, groupId))
2836
+ continue;
2770
2837
  // Check min/max order amount constraints
2771
2838
  if (rate.minOrderAmount !== null && orderAmount < rate.minOrderAmount)
2772
2839
  continue;
@@ -2795,8 +2862,8 @@ export async function findShippingRatesForCountry(country, orderAmount) {
2795
2862
  * @param orderAmount - Order subtotal in dollars
2796
2863
  * @returns Shipping options grouped by carrier
2797
2864
  */
2798
- export async function findShippingOptionsForCountry(country, orderAmount) {
2799
- const rates = await findShippingRatesForCountry(country, orderAmount);
2865
+ export async function findShippingOptionsForCountry(country, orderAmount, groupId) {
2866
+ const rates = await findShippingRatesForCountry(country, orderAmount, groupId);
2800
2867
  // Group by carrier
2801
2868
  const carrierMap = new Map();
2802
2869
  for (const rate of rates) {
@@ -2831,6 +2898,8 @@ export async function findShippingOptionsForCountry(country, orderAmount) {
2831
2898
  * Find all tax zones ordered by priority (sortOrder ascending)
2832
2899
  */
2833
2900
  export async function findTaxZones() {
2901
+ // The SELECT enumerates visible_to_groups — self-heal older stores first.
2902
+ await ensureVisibilityColumnsSafe();
2834
2903
  const db = getDb();
2835
2904
  const zones = await db
2836
2905
  .select()
@@ -2844,6 +2913,7 @@ export async function findTaxZones() {
2844
2913
  taxRate: Number(zone.taxRate),
2845
2914
  isActive: zone.isActive,
2846
2915
  sortOrder: zone.sortOrder,
2916
+ visibleToGroups: Array.isArray(zone.visibleToGroups) ? zone.visibleToGroups : [],
2847
2917
  createdAt: zone.createdAt,
2848
2918
  updatedAt: zone.updatedAt,
2849
2919
  }));
@@ -2866,6 +2936,7 @@ export async function findTaxZoneById(id) {
2866
2936
  * Create a new tax zone
2867
2937
  */
2868
2938
  export async function createTaxZone(data) {
2939
+ await ensureVisibilityColumnsSafe();
2869
2940
  const db = getDb();
2870
2941
  const [zone] = await db
2871
2942
  .insert(schema.taxZones)
@@ -2876,6 +2947,7 @@ export async function createTaxZone(data) {
2876
2947
  taxRate: String(data.taxRate),
2877
2948
  isActive: data.isActive ?? true,
2878
2949
  sortOrder: data.sortOrder ?? 0,
2950
+ visibleToGroups: data.visibleToGroups ?? [],
2879
2951
  })
2880
2952
  .returning();
2881
2953
  return zone;
@@ -2884,6 +2956,7 @@ export async function createTaxZone(data) {
2884
2956
  * Update a tax zone
2885
2957
  */
2886
2958
  export async function updateTaxZone(id, data) {
2959
+ await ensureVisibilityColumnsSafe();
2887
2960
  const db = getDb();
2888
2961
  const updateData = { updatedAt: new Date() };
2889
2962
  if (data.name !== undefined)
@@ -2898,6 +2971,8 @@ export async function updateTaxZone(id, data) {
2898
2971
  updateData.isActive = data.isActive;
2899
2972
  if (data.sortOrder !== undefined)
2900
2973
  updateData.sortOrder = data.sortOrder;
2974
+ if (data.visibleToGroups !== undefined)
2975
+ updateData.visibleToGroups = data.visibleToGroups;
2901
2976
  const [zone] = await db
2902
2977
  .update(schema.taxZones)
2903
2978
  .set(updateData)
@@ -2929,11 +3004,16 @@ export async function deleteTaxZone(id) {
2929
3004
  * @param state - State/province code for US (e.g., 'CA', 'NY')
2930
3005
  * @returns Tax rate info or null if no matching zone
2931
3006
  */
2932
- export async function findTaxRateForAddress(country, state) {
3007
+ export async function findTaxRateForAddress(country, state, groupId) {
2933
3008
  const zones = await findActiveTaxZones();
2934
3009
  const upperCountry = country.toUpperCase();
2935
3010
  const upperState = state?.toUpperCase() || '';
2936
3011
  for (const zone of zones) {
3012
+ // Group gating: a gated zone is SKIPPED for non-members, who fall
3013
+ // through to the next matching zone by priority (e.g. a pro-only 0%
3014
+ // zone listed above the standard rate).
3015
+ if (!isVisibleToGroup(zone.visibleToGroups, groupId))
3016
+ continue;
2937
3017
  // Check if zone covers this country
2938
3018
  const hasWildcard = zone.countries.includes('*');
2939
3019
  const hasCountry = zone.countries.includes(upperCountry);