@nextblock-cms/ecom 0.21.2 → 0.22.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.md CHANGED
@@ -1,43 +1,43 @@
1
- # @nextblock-cms/ecom
2
-
3
- The premium commerce module of [NextBlock](https://github.com/nextblock-cms/nextblock), the
4
- open-core CMS for Next.js and Supabase: products and variants, cart and checkout, orders,
5
- coupons, currencies, shipping zones, tax rates and invoices. Physical products are paid through
6
- Stripe and digital products through Freemius.
7
-
8
- NextBlock projects install it under the alias `@nextblock-cms/ecommerce`, and every import uses
9
- that name. Projects created with `npm create nextblock@latest` already have it:
10
-
11
- ```json
12
- "@nextblock-cms/ecommerce": "npm:@nextblock-cms/ecom@latest"
13
- ```
14
-
15
- It needs Next.js 16 and React 19 (peer dependencies), plus the NextBlock database schema that
16
- `@nextblock-cms/db` ships.
17
-
18
- ## Licensing
19
-
20
- The source is public under AGPL-3.0-or-later, and the features are license-gated. A NextBlock
21
- project turns on its checkout API and commerce CMS screens only after an ecommerce license is
22
- active: start a trial or buy one in the CMS under **Settings → Packages**. The app checks it
23
- with `verifyPackageOnline('ecommerce')` from `@nextblock-cms/db/server`.
24
-
25
- ## Entry points
26
-
27
- | Import | Use it from | What it holds |
28
- | :-- | :-- | :-- |
29
- | `@nextblock-cms/ecommerce` | client or server | Storefront and account components (`Cart`, `CartDrawer`, `Checkout`, `ProductGrid`, `ProductCard`, `FeaturedProduct`, `AddToCartButton`, `CurrencySwitcher`, …), the cart store, `CurrencyProvider`, and the CMS `ProductForm` |
30
- | `@nextblock-cms/ecommerce/server` | server only | Stripe checkout, webhooks and order sync, payment configuration, inventory, invoices, tax details and coupons |
31
- | `@nextblock-cms/ecommerce/<module>` | depends on the module | One compiled module, for example `@nextblock-cms/ecommerce/cart-store`, `/components/Cart` or `/server-actions/product-actions` |
32
-
33
- The package keeps each module's `'use client'` or `'use server'` directive, so the consuming
34
- Next.js build splits client and server code correctly.
35
-
36
- ## Documentation
37
-
38
- - [Ecommerce capabilities](https://github.com/nextblock-cms/nextblock/blob/master/docs/02-ECOMMERCE-CAPABILITIES.md)
39
- - [Project overview](https://github.com/nextblock-cms/nextblock/blob/master/docs/01-PROJECT-OVERVIEW.md)
40
-
41
- ## License
42
-
43
- AGPL-3.0-or-later.
1
+ # @nextblock-cms/ecom
2
+
3
+ The premium commerce module of [NextBlock](https://github.com/nextblock-cms/nextblock), the
4
+ open-core CMS for Next.js and Supabase: products and variants, cart and checkout, orders,
5
+ coupons, currencies, shipping zones, tax rates and invoices. Physical products are paid through
6
+ Stripe and digital products through Freemius.
7
+
8
+ NextBlock projects install it under the alias `@nextblock-cms/ecommerce`, and every import uses
9
+ that name. Projects created with `npm create nextblock@latest` already have it:
10
+
11
+ ```json
12
+ "@nextblock-cms/ecommerce": "npm:@nextblock-cms/ecom@latest"
13
+ ```
14
+
15
+ It needs Next.js 16 and React 19 (peer dependencies), plus the NextBlock database schema that
16
+ `@nextblock-cms/db` ships.
17
+
18
+ ## Licensing
19
+
20
+ The source is public under AGPL-3.0-or-later, and the features are license-gated. A NextBlock
21
+ project turns on its checkout API and commerce CMS screens only after an ecommerce license is
22
+ active: start a trial or buy one in the CMS under **Settings → Packages**. The app checks it
23
+ with `verifyPackageOnline('ecommerce')` from `@nextblock-cms/db/server`.
24
+
25
+ ## Entry points
26
+
27
+ | Import | Use it from | What it holds |
28
+ | :-- | :-- | :-- |
29
+ | `@nextblock-cms/ecommerce` | client or server | Storefront and account components (`Cart`, `CartDrawer`, `Checkout`, `ProductGrid`, `ProductCard`, `FeaturedProduct`, `AddToCartButton`, `CurrencySwitcher`, …), the cart store, `CurrencyProvider`, and the CMS `ProductForm` |
30
+ | `@nextblock-cms/ecommerce/server` | server only | Stripe checkout, webhooks and order sync, payment configuration, inventory, invoices, tax details and coupons |
31
+ | `@nextblock-cms/ecommerce/<module>` | depends on the module | One compiled module, for example `@nextblock-cms/ecommerce/cart-store`, `/components/Cart` or `/server-actions/product-actions` |
32
+
33
+ The package keeps each module's `'use client'` or `'use server'` directive, so the consuming
34
+ Next.js build splits client and server code correctly.
35
+
36
+ ## Documentation
37
+
38
+ - [Ecommerce capabilities](https://github.com/nextblock-cms/nextblock/blob/master/docs/02-ECOMMERCE-CAPABILITIES.md)
39
+ - [Project overview](https://github.com/nextblock-cms/nextblock/blob/master/docs/01-PROJECT-OVERVIEW.md)
40
+
41
+ ## License
42
+
43
+ AGPL-3.0-or-later.
@@ -7,7 +7,7 @@ const e=require("./customer.cjs"),t=require("./invoice.cjs"),n=require("./order-
7
7
  width,
8
8
  height
9
9
  )
10
- `;async function l(e){let{data:t}=await e.from(`site_settings`).select(`value`).eq(`key`,s).maybeSingle(),n=t?.value,r=typeof n==`string`&&n.length>0?n:null;if(r){let{data:t}=await e.from(`logos`).select(c).eq(`id`,r).maybeSingle();if(t)return t}let{data:i}=await e.from(`logos`).select(c).order(`created_at`,{ascending:!1}).limit(1).maybeSingle();return i??null}async function u(e){let n=a(e),[r,i]=await Promise.all([n.from(`site_settings`).select(`value`).eq(`key`,t.INVOICE_SETTINGS_KEY).maybeSingle(),l(n)]);return{settings:t.normalizeInvoiceSettings(r.data?.value??t.DEFAULT_INVOICE_SETTINGS),logo:o(i)}}async function d(e){let{data:t,error:n}=await a(e.client).rpc(`assign_order_invoice_metadata`,{p_order_id:e.orderId,p_paid_at:e.paidAt??null}).single();if(n)throw Error(n.message);return{invoiceNumber:t?.invoice_number??null,paidAt:t?.paid_at??null}}async function f(t,r){let i=a(r),{data:o,error:s}=await i.from(`orders`).select(`
10
+ `;async function l(e){let{data:t}=await e.from(`site_settings`).select(`value`).eq(`key`,s).maybeSingle(),n=t?.value,r=typeof n==`string`&&n.length>0?n:null;if(r){let{data:t}=await e.from(`logos`).select(c).eq(`id`,r).maybeSingle();if(t)return t}let{data:i}=await e.from(`logos`).select(c).order(`created_at`,{ascending:!1}).limit(1).maybeSingle();return i??null}async function u(e){let n=a(e),[r,i]=await Promise.all([n.from(`site_settings`).select(`value`).eq(`key`,t.INVOICE_SETTINGS_KEY).maybeSingle(),l(n)]);return{settings:t.normalizeInvoiceSettings(r.data?.value??t.DEFAULT_INVOICE_SETTINGS),logo:o(i)}}async function d(e){let{data:t,error:n}=await a(e.client).rpc(`assign_order_invoice_metadata`,{p_order_id:e.orderId,p_paid_at:e.paidAt??null}).single();if(n)throw Error(n.message);return{invoiceNumber:t?.invoice_number??null,paidAt:t?.paid_at??null}}async function f(t,i){let{data:o,error:s}=await a(i).from(`orders`).select(`
11
11
  id,
12
12
  invoice_number,
13
13
  paid_at,
@@ -30,4 +30,4 @@ const e=require("./customer.cjs"),t=require("./invoice.cjs"),n=require("./order-
30
30
  quantity,
31
31
  price_at_purchase
32
32
  )
33
- `).eq(`id`,t).single();if(s||!o)throw Error(s?.message||`Order not found`);let c=(o.order_items||[]).map(e=>e.product_id).filter(Boolean),l=(o.order_items||[]).map(e=>e.variant_id).filter(Boolean),[u,d]=await Promise.all([c.length?i.from(`products`).select(`id, title, sku`).in(`id`,c):Promise.resolve({data:[],error:null}),l.length?i.from(`product_variants`).select(`id, sku`).in(`id`,l):Promise.resolve({data:[],error:null})]),f=new Map((u.data||[]).map(e=>[e.id,e])),p=new Map((d.data||[]).map(e=>[e.id,e])),m=(o.order_items||[]).map(e=>{let t=e.product_id?f.get(e.product_id):null,n=e.variant_id?p.get(e.variant_id):null,r=t?.title||`Product`,i=n?.sku?`SKU: ${n.sku}`:t?.sku?`SKU: ${t.sku}`:null;return{id:e.id,product_id:e.product_id??null,variant_id:e.variant_id??null,title:r,description:i,quantity:e.quantity,unit_amount:e.price_at_purchase,total_amount:e.price_at_purchase*e.quantity,sku:n?.sku??t?.sku??null}});return{id:o.id,invoice_number:o.invoice_number??null,paid_at:o.paid_at??null,created_at:o.created_at??null,currency:o.currency||`usd`,status:o.status,provider:o.provider??null,subtotal:typeof o.subtotal==`number`?o.subtotal:m.reduce((e,t)=>e+t.total_amount,0),shipping_total:typeof o.shipping_total==`number`?o.shipping_total:0,discount_total:typeof o.discount_total==`number`?o.discount_total:0,coupon_code:o.coupon_code??null,tax_total:typeof o.tax_total==`number`?o.tax_total:0,total:o.total,customer_details:e.normalizeOrderCustomerDetails(o.customer_details??{}),tax_details:n.normalizeOrderTaxDetails(o.tax_details),items:m}}async function p(e,t){let n=a(t),[r,i]=await Promise.all([f(e,n),u(n)]);return{order:r,settings:i.settings,logo:i.logo}}exports.assignInvoiceMetadata=d,exports.getInvoiceBrandingData=u,exports.getInvoiceOrder=f,exports.getInvoicePresentationData=p;
33
+ `).eq(`id`,t).single();if(s||!o)throw Error(s?.message||`Order not found`);let c=(o.order_items||[]).map(e=>e.product_id).filter(Boolean),l=(o.order_items||[]).map(e=>e.variant_id).filter(Boolean),u=(0,r.getServiceRoleSupabaseClient)(),[d,f]=await Promise.all([c.length?u.from(`products`).select(`id, title, sku`).in(`id`,c):Promise.resolve({data:[],error:null}),l.length?u.from(`product_variants`).select(`id, sku`).in(`id`,l):Promise.resolve({data:[],error:null})]),p=new Map((d.data||[]).map(e=>[e.id,e])),m=new Map((f.data||[]).map(e=>[e.id,e])),h=(o.order_items||[]).map(e=>{let t=e.product_id?p.get(e.product_id):null,n=e.variant_id?m.get(e.variant_id):null,r=t?.title||`Product`,i=n?.sku?`SKU: ${n.sku}`:t?.sku?`SKU: ${t.sku}`:null;return{id:e.id,product_id:e.product_id??null,variant_id:e.variant_id??null,title:r,description:i,quantity:e.quantity,unit_amount:e.price_at_purchase,total_amount:e.price_at_purchase*e.quantity,sku:n?.sku??t?.sku??null}});return{id:o.id,invoice_number:o.invoice_number??null,paid_at:o.paid_at??null,created_at:o.created_at??null,currency:o.currency||`usd`,status:o.status,provider:o.provider??null,subtotal:typeof o.subtotal==`number`?o.subtotal:h.reduce((e,t)=>e+t.total_amount,0),shipping_total:typeof o.shipping_total==`number`?o.shipping_total:0,discount_total:typeof o.discount_total==`number`?o.discount_total:0,coupon_code:o.coupon_code??null,tax_total:typeof o.tax_total==`number`?o.tax_total:0,total:o.total,customer_details:e.normalizeOrderCustomerDetails(o.customer_details??{}),tax_details:n.normalizeOrderTaxDetails(o.tax_details),items:h}}async function p(e,t){let n=a(t),[r,i]=await Promise.all([f(e,n),u(n)]);return{order:r,settings:i.settings,logo:i.logo}}exports.assignInvoiceMetadata=d,exports.getInvoiceBrandingData=u,exports.getInvoiceOrder=f,exports.getInvoicePresentationData=p;
@@ -49,16 +49,16 @@ async function p(e) {
49
49
  };
50
50
  }
51
51
  async function m(t, n) {
52
- let r = s(n), { data: a, error: o } = await r.from("orders").select("\n id,\n invoice_number,\n paid_at,\n created_at,\n currency,\n status,\n provider,\n subtotal,\n shipping_total,\n discount_total,\n coupon_code,\n tax_total,\n total,\n customer_details,\n tax_details,\n order_items (\n id,\n product_id,\n variant_id,\n quantity,\n price_at_purchase\n )\n ").eq("id", t).single();
53
- if (o || !a) throw Error(o?.message || "Order not found");
54
- let c = (a.order_items || []).map((e) => e.product_id).filter(Boolean), l = (a.order_items || []).map((e) => e.variant_id).filter(Boolean), [u, d] = await Promise.all([c.length ? r.from("products").select("id, title, sku").in("id", c) : Promise.resolve({
52
+ let { data: r, error: o } = await s(n).from("orders").select("\n id,\n invoice_number,\n paid_at,\n created_at,\n currency,\n status,\n provider,\n subtotal,\n shipping_total,\n discount_total,\n coupon_code,\n tax_total,\n total,\n customer_details,\n tax_details,\n order_items (\n id,\n product_id,\n variant_id,\n quantity,\n price_at_purchase\n )\n ").eq("id", t).single();
53
+ if (o || !r) throw Error(o?.message || "Order not found");
54
+ let c = (r.order_items || []).map((e) => e.product_id).filter(Boolean), l = (r.order_items || []).map((e) => e.variant_id).filter(Boolean), u = a(), [d, f] = await Promise.all([c.length ? u.from("products").select("id, title, sku").in("id", c) : Promise.resolve({
55
55
  data: [],
56
56
  error: null
57
- }), l.length ? r.from("product_variants").select("id, sku").in("id", l) : Promise.resolve({
57
+ }), l.length ? u.from("product_variants").select("id, sku").in("id", l) : Promise.resolve({
58
58
  data: [],
59
59
  error: null
60
- })]), f = new Map((u.data || []).map((e) => [e.id, e])), p = new Map((d.data || []).map((e) => [e.id, e])), m = (a.order_items || []).map((e) => {
61
- let t = e.product_id ? f.get(e.product_id) : null, n = e.variant_id ? p.get(e.variant_id) : null, r = t?.title || "Product", i = n?.sku ? `SKU: ${n.sku}` : t?.sku ? `SKU: ${t.sku}` : null;
60
+ })]), p = new Map((d.data || []).map((e) => [e.id, e])), m = new Map((f.data || []).map((e) => [e.id, e])), h = (r.order_items || []).map((e) => {
61
+ let t = e.product_id ? p.get(e.product_id) : null, n = e.variant_id ? m.get(e.variant_id) : null, r = t?.title || "Product", i = n?.sku ? `SKU: ${n.sku}` : t?.sku ? `SKU: ${t.sku}` : null;
62
62
  return {
63
63
  id: e.id,
64
64
  product_id: e.product_id ?? null,
@@ -72,22 +72,22 @@ async function m(t, n) {
72
72
  };
73
73
  });
74
74
  return {
75
- id: a.id,
76
- invoice_number: a.invoice_number ?? null,
77
- paid_at: a.paid_at ?? null,
78
- created_at: a.created_at ?? null,
79
- currency: a.currency || "usd",
80
- status: a.status,
81
- provider: a.provider ?? null,
82
- subtotal: typeof a.subtotal == "number" ? a.subtotal : m.reduce((e, t) => e + t.total_amount, 0),
83
- shipping_total: typeof a.shipping_total == "number" ? a.shipping_total : 0,
84
- discount_total: typeof a.discount_total == "number" ? a.discount_total : 0,
85
- coupon_code: a.coupon_code ?? null,
86
- tax_total: typeof a.tax_total == "number" ? a.tax_total : 0,
87
- total: a.total,
88
- customer_details: e(a.customer_details ?? {}),
89
- tax_details: i(a.tax_details),
90
- items: m
75
+ id: r.id,
76
+ invoice_number: r.invoice_number ?? null,
77
+ paid_at: r.paid_at ?? null,
78
+ created_at: r.created_at ?? null,
79
+ currency: r.currency || "usd",
80
+ status: r.status,
81
+ provider: r.provider ?? null,
82
+ subtotal: typeof r.subtotal == "number" ? r.subtotal : h.reduce((e, t) => e + t.total_amount, 0),
83
+ shipping_total: typeof r.shipping_total == "number" ? r.shipping_total : 0,
84
+ discount_total: typeof r.discount_total == "number" ? r.discount_total : 0,
85
+ coupon_code: r.coupon_code ?? null,
86
+ tax_total: typeof r.tax_total == "number" ? r.tax_total : 0,
87
+ total: r.total,
88
+ customer_details: e(r.customer_details ?? {}),
89
+ tax_details: i(r.tax_details),
90
+ items: h
91
91
  };
92
92
  }
93
93
  async function h(e, t) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextblock-cms/ecom",
3
- "version": "0.21.2",
3
+ "version": "0.22.0",
4
4
  "license": "AGPL-3.0-or-later",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -12,9 +12,9 @@
12
12
  },
13
13
  "type": "module",
14
14
  "dependencies": {
15
- "@nextblock-cms/db": "^0.21.2",
16
- "@nextblock-cms/ui": "^0.21.2",
17
- "@nextblock-cms/utils": "^0.21.2",
15
+ "@nextblock-cms/db": "^0.22.0",
16
+ "@nextblock-cms/ui": "^0.22.0",
17
+ "@nextblock-cms/utils": "^0.22.0",
18
18
  "@freemius/checkout": "^1.5.1",
19
19
  "@freemius/sdk": "^0.5.0",
20
20
  "@hookform/resolvers": "^5.9.1",