create-cartbase 0.1.16 → 0.1.18

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 (34) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +25 -25
  3. package/dist/index.js +20 -20
  4. package/package.json +1 -1
  5. package/template/app/docs/auth.md +105 -105
  6. package/template/app/docs/carts.md +376 -376
  7. package/template/app/docs/categories.md +194 -194
  8. package/template/app/docs/checkout.md +714 -714
  9. package/template/app/docs/components.md +55 -11
  10. package/template/app/docs/consent.md +91 -91
  11. package/template/app/docs/deploy.md +197 -197
  12. package/template/app/docs/gift-cards.md +153 -153
  13. package/template/app/docs/metaobjects.md +126 -126
  14. package/template/app/docs/orders.md +221 -221
  15. package/template/app/docs/products.md +51 -2
  16. package/template/app/docs/regions.md +269 -269
  17. package/template/app/docs/reviews.md +223 -223
  18. package/template/app/docs/search.md +227 -227
  19. package/template/app/docs/store.md +47 -47
  20. package/template/app/docs/subscriptions.md +148 -148
  21. package/template/app/docs/variables.md +315 -315
  22. package/template/app/package.json +1 -1
  23. package/template/app/postcss.config.cjs +11 -11
  24. package/template/app/src/app/checkout/checkout-page-client.tsx +73 -73
  25. package/template/app/src/app/checkout/page.tsx +48 -48
  26. package/template/app/src/app/globals.css +26 -26
  27. package/template/app/src/app/page.tsx +28 -28
  28. package/template/app/src/app/products/[handle]/page.tsx +87 -87
  29. package/template/app/src/app/providers.tsx +68 -64
  30. package/template/app/src/app/search/page.tsx +23 -23
  31. package/template/app/src/lib/browser-client.ts +35 -35
  32. package/template/app/src/lib/config.ts +41 -41
  33. package/template/app/src/lib/server-client.ts +25 -25
  34. package/template/app/src/lib/cart-actions.ts +0 -47
@@ -1,64 +1,68 @@
1
- "use client"
2
-
3
- import type { Cart } from "@cartbase/storefront/api/carts"
4
- import { CartDrawerProvider } from "@cartbase/storefront/cart-drawer/context"
5
- import { CartDrawerTemplate } from "@cartbase/storefront/cart-drawer/template"
6
- import { ConsentBanner } from "@cartbase/storefront/tracking/consent-banner"
7
- import {
8
- pickConsentCopy,
9
- shouldRenderBanner,
10
- type ConsentSettings,
11
- } from "@cartbase/storefront/tracking/consent"
12
- import { StorefrontLocaleProvider } from "@cartbase/storefront/locales"
13
- import { CART_COOKIE, CART_COOKIE_MAX_AGE, STORE_LOCALE } from "@/lib/config"
14
- import { browserClient } from "@/lib/browser-client"
15
-
16
- /**
17
- * Client-side shell: the store's language, ONE CartDrawerProvider per app
18
- * (cart-drawer family mount rule) + the consent banner (consent.md: render
19
- * the builtin banner only when `enabled && mode === "builtin"`; ConsentInit
20
- * stays in the server layout as the first child of <body>).
21
- *
22
- * The language is mounted once, here. Every client component inside reads
23
- * it, the cart drawer included, so nothing is handed over below. The
24
- * server-rendered templates cannot read it, so each page hands them their
25
- * area (`labels={STORE_LOCALE.store}`), and those props are required.
26
- */
27
- export function Providers({
28
- cart,
29
- consent,
30
- children,
31
- }: {
32
- cart: Cart | null
33
- consent: ConsentSettings
34
- children: React.ReactNode
35
- }) {
36
- // The store's own consent copy, in the store's language where it has it;
37
- // pickConsentCopy falls back to English and then to whatever exists.
38
- const copy = pickConsentCopy(consent.copy, STORE_LOCALE.code)
39
- return (
40
- <StorefrontLocaleProvider locale={STORE_LOCALE}>
41
- <CartDrawerProvider
42
- cart={cart}
43
- client={browserClient}
44
- onCartChange={(next) => {
45
- // Persist the cart id (carts.md: the app owns the cart cookie).
46
- document.cookie = `${CART_COOKIE}=${encodeURIComponent(
47
- next.id
48
- )};path=/;max-age=${CART_COOKIE_MAX_AGE};samesite=lax`
49
- }}
50
- >
51
- {children}
52
- <CartDrawerTemplate />
53
- {shouldRenderBanner(consent) && copy && (
54
- <ConsentBanner
55
- copy={copy}
56
- layout={consent.layout}
57
- privacyHref={consent.privacy_href}
58
- rejectOnFirstLayer={consent.reject_on_first_layer}
59
- />
60
- )}
61
- </CartDrawerProvider>
62
- </StorefrontLocaleProvider>
63
- )
64
- }
1
+ "use client"
2
+
3
+ import type { Cart } from "@cartbase/storefront/api/carts"
4
+ import { CartDrawerProvider } from "@cartbase/storefront/cart-drawer/context"
5
+ import { CartDrawerTemplate } from "@cartbase/storefront/cart-drawer/template"
6
+ import { ConsentBanner } from "@cartbase/storefront/tracking/consent-banner"
7
+ import {
8
+ pickConsentCopy,
9
+ shouldRenderBanner,
10
+ type ConsentSettings,
11
+ } from "@cartbase/storefront/tracking/consent"
12
+ import { StorefrontLocaleProvider } from "@cartbase/storefront/locales"
13
+ import { clearCartCookie } from "@cartbase/storefront/lib/cookie-names"
14
+ import { CART_COOKIE, CART_COOKIE_MAX_AGE, STORE_LOCALE } from "@/lib/config"
15
+ import { browserClient } from "@/lib/browser-client"
16
+
17
+ /**
18
+ * Client-side shell: the store's language, ONE CartDrawerProvider per app
19
+ * (cart-drawer family mount rule) + the consent banner (consent.md: render
20
+ * the builtin banner only when `enabled && mode === "builtin"`; ConsentInit
21
+ * stays in the server layout as the first child of <body>).
22
+ *
23
+ * The language is mounted once, here. Every client component inside reads
24
+ * it, the cart drawer included, so nothing is handed over below. The
25
+ * server-rendered templates cannot read it, so each page hands them their
26
+ * area (`labels={STORE_LOCALE.store}`), and those props are required.
27
+ */
28
+ export function Providers({
29
+ cart,
30
+ consent,
31
+ children,
32
+ }: {
33
+ cart: Cart | null
34
+ consent: ConsentSettings
35
+ children: React.ReactNode
36
+ }) {
37
+ // The store's own consent copy, in the store's language where it has it;
38
+ // pickConsentCopy falls back to English and then to whatever exists.
39
+ const copy = pickConsentCopy(consent.copy, STORE_LOCALE.code)
40
+ return (
41
+ <StorefrontLocaleProvider locale={STORE_LOCALE}>
42
+ <CartDrawerProvider
43
+ cart={cart}
44
+ client={browserClient}
45
+ onCartChange={(next) => {
46
+ // Persist the cart id (carts.md: the app owns the cart cookie).
47
+ document.cookie = `${CART_COOKIE}=${encodeURIComponent(
48
+ next.id
49
+ )};path=/;max-age=${CART_COOKIE_MAX_AGE};samesite=lax`
50
+ }}
51
+ // The cart is over (ordered, or gone): forget the stored id, so the
52
+ // next add starts a new cart.
53
+ onCartEnd={clearCartCookie}
54
+ >
55
+ {children}
56
+ <CartDrawerTemplate />
57
+ {shouldRenderBanner(consent) && copy && (
58
+ <ConsentBanner
59
+ copy={copy}
60
+ layout={consent.layout}
61
+ privacyHref={consent.privacy_href}
62
+ rejectOnFirstLayer={consent.reject_on_first_layer}
63
+ />
64
+ )}
65
+ </CartDrawerProvider>
66
+ </StorefrontLocaleProvider>
67
+ )
68
+ }
@@ -1,23 +1,23 @@
1
- import { SearchTemplate } from "@cartbase/storefront/store/search-template"
2
- import { PRICING_CONTEXT, STORE_LOCALE } from "@/lib/config"
3
- import { getServerClient } from "@/lib/server-client"
4
-
5
- /** Search page (runbook step 5) — fully URL-state driven search-template. */
6
- export default async function SearchPage({
7
- searchParams,
8
- }: {
9
- searchParams: Promise<Record<string, string | string[] | undefined>>
10
- }) {
11
- const params = await searchParams
12
- const client = await getServerClient()
13
- return (
14
- <SearchTemplate
15
- client={client}
16
- searchParams={params}
17
- pricingContext={PRICING_CONTEXT}
18
- // Server-rendered: the locale provider cannot reach it, so the pack
19
- // comes as a prop, and the prop is required.
20
- labels={STORE_LOCALE.store}
21
- />
22
- )
23
- }
1
+ import { SearchTemplate } from "@cartbase/storefront/store/search-template"
2
+ import { PRICING_CONTEXT, STORE_LOCALE } from "@/lib/config"
3
+ import { getServerClient } from "@/lib/server-client"
4
+
5
+ /** Search page (runbook step 5) — fully URL-state driven search-template. */
6
+ export default async function SearchPage({
7
+ searchParams,
8
+ }: {
9
+ searchParams: Promise<Record<string, string | string[] | undefined>>
10
+ }) {
11
+ const params = await searchParams
12
+ const client = await getServerClient()
13
+ return (
14
+ <SearchTemplate
15
+ client={client}
16
+ searchParams={params}
17
+ pricingContext={PRICING_CONTEXT}
18
+ // Server-rendered: the locale provider cannot reach it, so the pack
19
+ // comes as a prop, and the prop is required.
20
+ labels={STORE_LOCALE.store}
21
+ />
22
+ )
23
+ }
@@ -1,35 +1,35 @@
1
- "use client"
2
-
3
- import { StorefrontClient } from "@cartbase/storefront/api/http"
4
- import {
5
- BARTER_CLIENT_ID,
6
- BARTER_PUBLISHABLE_KEY,
7
- BARTER_URL,
8
- LOCALE_COOKIE,
9
- } from "./config"
10
-
11
- function readCookie(name: string): string | null {
12
- if (typeof document === "undefined") return null
13
- const match = document.cookie
14
- .split("; ")
15
- .find((row) => row.startsWith(`${name}=`))
16
- return match ? decodeURIComponent(match.slice(name.length + 1)) : null
17
- }
18
-
19
- /**
20
- * Browser-scope StorefrontClient (runbook step 2): constructed once per
21
- * app. Guest-only reference app — no auth token store.
22
- *
23
- * baseUrl is the APP origin, not the API origin: the store API ships no
24
- * CORS headers, so browser calls ride the same-origin `/api/store/*`
25
- * rewrite (next.config.ts). During SSR of client components no fetches
26
- * run — the API origin stands in only to satisfy the constructor.
27
- */
28
- export const browserClient = new StorefrontClient({
29
- baseUrl:
30
- typeof window !== "undefined" ? window.location.origin : BARTER_URL,
31
- clientId: BARTER_CLIENT_ID,
32
- publishableKey: BARTER_PUBLISHABLE_KEY,
33
- getAuthToken: () => null,
34
- getLocale: () => readCookie(LOCALE_COOKIE),
35
- })
1
+ "use client"
2
+
3
+ import { StorefrontClient } from "@cartbase/storefront/api/http"
4
+ import {
5
+ BARTER_CLIENT_ID,
6
+ BARTER_PUBLISHABLE_KEY,
7
+ BARTER_URL,
8
+ LOCALE_COOKIE,
9
+ } from "./config"
10
+
11
+ function readCookie(name: string): string | null {
12
+ if (typeof document === "undefined") return null
13
+ const match = document.cookie
14
+ .split("; ")
15
+ .find((row) => row.startsWith(`${name}=`))
16
+ return match ? decodeURIComponent(match.slice(name.length + 1)) : null
17
+ }
18
+
19
+ /**
20
+ * Browser-scope StorefrontClient (runbook step 2): constructed once per
21
+ * app. Guest-only reference app — no auth token store.
22
+ *
23
+ * baseUrl is the APP origin, not the API origin: the store API ships no
24
+ * CORS headers, so browser calls ride the same-origin `/api/store/*`
25
+ * rewrite (next.config.ts). During SSR of client components no fetches
26
+ * run — the API origin stands in only to satisfy the constructor.
27
+ */
28
+ export const browserClient = new StorefrontClient({
29
+ baseUrl:
30
+ typeof window !== "undefined" ? window.location.origin : BARTER_URL,
31
+ clientId: BARTER_CLIENT_ID,
32
+ publishableKey: BARTER_PUBLISHABLE_KEY,
33
+ getAuthToken: () => null,
34
+ getLocale: () => readCookie(LOCALE_COOKIE),
35
+ })
@@ -1,41 +1,41 @@
1
- /** Shared storefront configuration (BUILD-A-STOREFRONT.md — the one input). */
2
-
3
- import { cartbaseApiOrigin } from "@cartbase/storefront/lib/platform"
4
-
5
- /**
6
- * The store's publishable key names the store and scopes its catalog: the
7
- * ONE value a storefront needs (one key, 2026-09-07). The origin is the
8
- * platform's, overridable for a local or staging platform; the client id is
9
- * the platform's own door (hosted builds carry it) and never something to
10
- * copy by hand.
11
- */
12
- export const BARTER_PUBLISHABLE_KEY =
13
- process.env.NEXT_PUBLIC_CARTBASE_PUBLISHABLE_KEY || undefined
14
- export const BARTER_URL = cartbaseApiOrigin(process.env.NEXT_PUBLIC_CARTBASE_URL)
15
- export const BARTER_CLIENT_ID = process.env.NEXT_PUBLIC_CARTBASE_CLIENT_ID || undefined
16
-
17
- /**
18
- * Cart-id cookie — owned by the app (the SDK never persists the cart);
19
- * the NAME is owned by the library so every consumer emits the same wire
20
- * fingerprint (platform-fingerprints card). `readCartCookie` is the
21
- * backward-compat reader for the legacy `_barter_cart_id` name.
22
- */
23
- export { CART_COOKIE, readCartCookie } from "@cartbase/storefront/lib/cookie-names"
24
- export const CART_COOKIE_MAX_AGE = 60 * 60 * 24 * 30 // 30 days
25
-
26
- /** Locale cookie read by the clients' `getLocale`. */
27
- export const LOCALE_COOKIE = "_barter_locale"
28
-
29
- /**
30
- * THE STORE'S LANGUAGE, declared once. The library speaks international
31
- * English and knows no other language; a language is a typed pack the store
32
- * imports. Swap `en` for `bg` or `es` here (`@cartbase/storefront/locales/bg`)
33
- * and every screen follows: providers.tsx mounts it for the client
34
- * components, and each page hands the server-rendered templates their area
35
- * from it (`labels={STORE_LOCALE.store}`), because a server component cannot
36
- * read a client context. Those props are required, so a page cannot forget.
37
- */
38
- export { en as STORE_LOCALE } from "@cartbase/storefront/locales/en"
39
-
40
- /** Pricing context for every catalog surface (runbook step 5). EUR only. */
41
- export const PRICING_CONTEXT = { currency_code: "eur" } as const
1
+ /** Shared storefront configuration (BUILD-A-STOREFRONT.md — the one input). */
2
+
3
+ import { cartbaseApiOrigin } from "@cartbase/storefront/lib/platform"
4
+
5
+ /**
6
+ * The store's publishable key names the store and scopes its catalog: the
7
+ * ONE value a storefront needs (one key, 2026-09-07). The origin is the
8
+ * platform's, overridable for a local or staging platform; the client id is
9
+ * the platform's own door (hosted builds carry it) and never something to
10
+ * copy by hand.
11
+ */
12
+ export const BARTER_PUBLISHABLE_KEY =
13
+ process.env.NEXT_PUBLIC_CARTBASE_PUBLISHABLE_KEY || undefined
14
+ export const BARTER_URL = cartbaseApiOrigin(process.env.NEXT_PUBLIC_CARTBASE_URL)
15
+ export const BARTER_CLIENT_ID = process.env.NEXT_PUBLIC_CARTBASE_CLIENT_ID || undefined
16
+
17
+ /**
18
+ * Cart-id cookie — owned by the app (the SDK never persists the cart);
19
+ * the NAME is owned by the library so every consumer emits the same wire
20
+ * fingerprint (platform-fingerprints card). `readCartCookie` is the
21
+ * backward-compat reader for the legacy `_barter_cart_id` name.
22
+ */
23
+ export { CART_COOKIE, readCartCookie } from "@cartbase/storefront/lib/cookie-names"
24
+ export const CART_COOKIE_MAX_AGE = 60 * 60 * 24 * 30 // 30 days
25
+
26
+ /** Locale cookie read by the clients' `getLocale`. */
27
+ export const LOCALE_COOKIE = "_barter_locale"
28
+
29
+ /**
30
+ * THE STORE'S LANGUAGE, declared once. The library speaks international
31
+ * English and knows no other language; a language is a typed pack the store
32
+ * imports. Swap `en` for `bg` or `es` here (`@cartbase/storefront/locales/bg`)
33
+ * and every screen follows: providers.tsx mounts it for the client
34
+ * components, and each page hands the server-rendered templates their area
35
+ * from it (`labels={STORE_LOCALE.store}`), because a server component cannot
36
+ * read a client context. Those props are required, so a page cannot forget.
37
+ */
38
+ export { en as STORE_LOCALE } from "@cartbase/storefront/locales/en"
39
+
40
+ /** Pricing context for every catalog surface (runbook step 5). EUR only. */
41
+ export const PRICING_CONTEXT = { currency_code: "eur" } as const
@@ -1,25 +1,25 @@
1
- import { cookies } from "next/headers"
2
- import { StorefrontClient } from "@cartbase/storefront/api/http"
3
- import {
4
- BARTER_CLIENT_ID,
5
- BARTER_PUBLISHABLE_KEY,
6
- BARTER_URL,
7
- LOCALE_COOKIE,
8
- } from "./config"
9
-
10
- /**
11
- * Server-scope StorefrontClient (runbook step 2): construct per request;
12
- * `getLocale` reads the locale cookie. No customer accounts in this
13
- * reference app, so `getAuthToken` always answers null (guest).
14
- */
15
- export async function getServerClient(): Promise<StorefrontClient> {
16
- const jar = await cookies()
17
- const locale = jar.get(LOCALE_COOKIE)?.value ?? null
18
- return new StorefrontClient({
19
- baseUrl: BARTER_URL,
20
- clientId: BARTER_CLIENT_ID,
21
- publishableKey: BARTER_PUBLISHABLE_KEY,
22
- getAuthToken: () => null,
23
- getLocale: () => locale,
24
- })
25
- }
1
+ import { cookies } from "next/headers"
2
+ import { StorefrontClient } from "@cartbase/storefront/api/http"
3
+ import {
4
+ BARTER_CLIENT_ID,
5
+ BARTER_PUBLISHABLE_KEY,
6
+ BARTER_URL,
7
+ LOCALE_COOKIE,
8
+ } from "./config"
9
+
10
+ /**
11
+ * Server-scope StorefrontClient (runbook step 2): construct per request;
12
+ * `getLocale` reads the locale cookie. No customer accounts in this
13
+ * reference app, so `getAuthToken` always answers null (guest).
14
+ */
15
+ export async function getServerClient(): Promise<StorefrontClient> {
16
+ const jar = await cookies()
17
+ const locale = jar.get(LOCALE_COOKIE)?.value ?? null
18
+ return new StorefrontClient({
19
+ baseUrl: BARTER_URL,
20
+ clientId: BARTER_CLIENT_ID,
21
+ publishableKey: BARTER_PUBLISHABLE_KEY,
22
+ getAuthToken: () => null,
23
+ getLocale: () => locale,
24
+ })
25
+ }
@@ -1,47 +0,0 @@
1
- "use server"
2
-
3
- import { revalidatePath } from "next/cache"
4
- import { cookies } from "next/headers"
5
- import { addLineItem, createCart } from "@cartbase/storefront/api/carts"
6
- import { CART_COOKIE, CART_COOKIE_MAX_AGE, readCartCookie } from "./config"
7
- import { getServerClient } from "./server-client"
8
-
9
- /**
10
- * PDP add-to-cart server action (carts.md: create the cart lazily on the
11
- * first add — region falls back to the store default; persist `cart.id`
12
- * in a cookie). Passed into `ProductTemplate`'s `addToCart` seam.
13
- *
14
- * Reads via `readCartCookie` (new `_cartbase_cart` name, falls back to the
15
- * legacy `_barter_cart_id` — platform-fingerprints card); every WRITE uses
16
- * the new name only, so a visitor's cart survives the rename.
17
- */
18
- export async function addToCartAction(input: {
19
- variantId: string
20
- quantity: number
21
- }): Promise<void> {
22
- const client = await getServerClient()
23
- const jar = await cookies()
24
- const cartId = readCartCookie((name) => jar.get(name)?.value)
25
-
26
- if (cartId) {
27
- await addLineItem(client, cartId, {
28
- variant_id: input.variantId,
29
- quantity: input.quantity,
30
- })
31
- } else {
32
- const { cart } = await createCart(client, {
33
- currency_code: "eur",
34
- items: [{ variant_id: input.variantId, quantity: input.quantity }],
35
- })
36
- jar.set(CART_COOKIE, cart.id, {
37
- path: "/",
38
- sameSite: "lax",
39
- httpOnly: false, // the browser-side drawer persists the same cookie
40
- maxAge: CART_COOKIE_MAX_AGE,
41
- })
42
- }
43
-
44
- // Layout refetches the cart snapshot → CartDrawerProvider sees the new
45
- // product-line count and auto-opens the drawer.
46
- revalidatePath("/", "layout")
47
- }