{"version":3,"sources":["../src/index.ts","../src/collections/products.ts","../src/collections/categories.ts","../src/collections/orders.ts","../src/collections/customers.ts","../src/stripe/client.ts","../src/stripe/prices.ts","../src/cart/store.ts","../src/cart/cart.ts","../src/cart/handlers.ts","../src/checkout/checkout.ts","../src/checkout/handler.ts","../src/webhooks/orders.ts","../src/webhooks/payment-method.ts","../src/webhooks/handler.ts","../src/islands/format.ts","../src/storefront/render.ts","../src/storefront/address.ts"],"sourcesContent":["/**\n * @webhouse/cms-shop — F136 E-commerce module\n *\n * Phase 1: collection schemas + Stripe sync + cart engine + Checkout\n * + webhook handler + Interactive Islands. Sites import the collections\n * and mount route handlers/islands like any other CMS module.\n *\n * Subpath imports for tree-shaking:\n *   import { productsCollection } from '@webhouse/cms-shop/collections';\n *   import type { ShopProduct } from '@webhouse/cms-shop/types';\n *   import { syncProductToStripe } from '@webhouse/cms-shop/stripe';\n *   import { createCartHandlers } from '@webhouse/cms-shop/cart';\n *   import { createCheckoutHandler } from '@webhouse/cms-shop/checkout';\n *   import { createStripeWebhookHandler } from '@webhouse/cms-shop/webhooks';\n *   import { renderProductPage } from '@webhouse/cms-shop/storefront';\n *\n * The barrel below re-exports the runtime surface so a single\n * `from '@webhouse/cms-shop'` import is also valid.\n */\nexport * from './collections';\nexport * from './types';\nexport * from './stripe';\nexport * from './cart';\nexport * from './checkout';\nexport * from './webhooks';\nexport * from './storefront';\n","/**\n * F136 Phase 1 — Products collection schema.\n *\n * Imported into a site's cms.config.ts:\n *\n *   import { productsCollection } from '@webhouse/cms-shop/collections';\n *   export default defineConfig({\n *     collections: [productsCollection({ locales: ['da', 'en'] })],\n *     // …\n *   });\n *\n * Pass options to override defaults (locales, currency, extra fields).\n */\nimport { defineCollection, type CollectionConfig, type FieldConfig } from '@webhouse/cms';\n\nexport interface ProductsCollectionOptions {\n  /** Override the default name \"products\" if you need multiple product collections (e.g. per brand). */\n  name?: string;\n  label?: string;\n  /** Locales the product fields can be translated into. */\n  locales?: string[];\n  /** Source locale for translations. Defaults to the first entry in `locales`. */\n  sourceLocale?: string;\n  /** Extra fields to merge into the schema (e.g. site-specific metadata). */\n  extraFields?: FieldConfig[];\n}\n\nexport function productsCollection(opts: ProductsCollectionOptions = {}): CollectionConfig {\n  const sourceLocale = opts.sourceLocale ?? opts.locales?.[0];\n  return defineCollection({\n    name: opts.name ?? 'products',\n    label: opts.label ?? 'Products',\n    kind: 'page',\n    ...(sourceLocale ? { sourceLocale } : {}),\n    ...(opts.locales ? { locales: opts.locales } : {}),\n    description:\n      'Product catalog. Each product is a CMS document — same editor, same i18n, same AI integration. ' +\n      'Stripe sync is automatic on save (cms-shop module hook). Stockable, with variants, multi-currency pricing.',\n    fields: [\n      // Core identity\n      { name: 'title',            type: 'text',     label: 'Title', required: true },\n      { name: 'slug',             type: 'text',     label: 'Slug', required: true },\n      { name: 'description',      type: 'richtext', label: 'Description' },\n      { name: 'shortDescription', type: 'textarea', label: 'Short description (160 chars, used in cards + SEO)' },\n\n      // Type / classification\n      {\n        name: 'productType', type: 'select', label: 'Product type', required: true,\n        options: [\n          { value: 'physical',     label: 'Physical' },\n          { value: 'digital',      label: 'Digital' },\n          { value: 'subscription', label: 'Subscription' },\n          { value: 'course',       label: 'Course' },\n          { value: 'giftcard',     label: 'Gift card' },\n          { value: 'booking',      label: 'Booking' },\n        ],\n      },\n      {\n        name: 'deliveryType', type: 'select', label: 'Delivery type',\n        options: [\n          { value: 'shipping',         label: 'Shipping' },\n          { value: 'digital-download', label: 'Digital download' },\n          { value: 'pickup',           label: 'Pickup' },\n          { value: 'booking',          label: 'Booking' },\n        ],\n      },\n      { name: 'category', type: 'relation', collection: 'categories', label: 'Category', multiple: true },\n      { name: 'tags',     type: 'tags',     label: 'Tags' },\n      { name: 'brand',    type: 'text',     label: 'Brand' },\n      { name: 'sku',      type: 'text',     label: 'SKU' },\n\n      // Media\n      { name: 'images',       type: 'image-gallery', label: 'Images' },\n      { name: 'cardImageUrl', type: 'image',         label: 'Card image (used in lists, chat, recommendations)' },\n\n      // Pricing — stored as object so multi-currency is built-in.\n      // Editor sees one row per currency in a key-value editor.\n      {\n        name: 'priceByCurrency', type: 'object', label: 'Price by currency (in minor units, e.g. 12500 = 125,00 kr.)',\n        fields: [\n          { name: 'DKK', type: 'number', label: 'DKK (øre)' },\n          { name: 'EUR', type: 'number', label: 'EUR (cents)' },\n          { name: 'SEK', type: 'number', label: 'SEK (öre)' },\n          { name: 'NOK', type: 'number', label: 'NOK (øre)' },\n        ],\n      },\n      {\n        name: 'compareAtPriceByCurrency', type: 'object', label: 'Compare-at price (crossed-out reference)',\n        fields: [\n          { name: 'DKK', type: 'number', label: 'DKK (øre)' },\n          { name: 'EUR', type: 'number', label: 'EUR (cents)' },\n          { name: 'SEK', type: 'number', label: 'SEK (öre)' },\n          { name: 'NOK', type: 'number', label: 'NOK (øre)' },\n        ],\n      },\n\n      // Stock\n      { name: 'stockQuantity',     type: 'number', label: 'Stock quantity' },\n      { name: 'lowStockThreshold', type: 'number', label: 'Low-stock threshold (alert when below)' },\n      {\n        name: 'stockBehavior', type: 'select', label: 'When out of stock',\n        options: [\n          { value: 'hide',         label: 'Hide from listings' },\n          { value: 'out-of-stock', label: 'Show as out of stock' },\n          { value: 'backorder',    label: 'Allow backorder' },\n        ],\n      },\n\n      // Physical-only\n      {\n        name: 'dimensions', type: 'object', label: 'Dimensions (physical products only)',\n        fields: [\n          { name: 'lengthCm', type: 'number', label: 'Length (cm)' },\n          { name: 'widthCm',  type: 'number', label: 'Width (cm)' },\n          { name: 'heightCm', type: 'number', label: 'Height (cm)' },\n          { name: 'weightG',  type: 'number', label: 'Weight (g)' },\n        ],\n      },\n      { name: 'shippingClass', type: 'relation', collection: 'shipping-rates', label: 'Shipping class' },\n\n      // Status / scheduling\n      {\n        name: 'status', type: 'select', label: 'Status',\n        options: [\n          { value: 'draft',    label: 'Draft' },\n          { value: 'active',   label: 'Active' },\n          { value: 'archived', label: 'Archived' },\n        ],\n      },\n\n      // Stripe sync (read-only — populated by hook)\n      { name: 'stripeProductId', type: 'text', label: 'Stripe Product id (auto-synced)' },\n\n      ...(opts.extraFields ?? []),\n    ],\n  });\n}\n","/**\n * F136 Phase 1 — Categories collection schema.\n *\n * Hierarchical categories (via `parent` relation) so editors can build\n * trees like Clothing > Tops > T-shirts.\n */\nimport { defineCollection, type CollectionConfig, type FieldConfig } from '@webhouse/cms';\n\nexport interface CategoriesCollectionOptions {\n  name?: string;\n  label?: string;\n  locales?: string[];\n  sourceLocale?: string;\n  extraFields?: FieldConfig[];\n}\n\nexport function categoriesCollection(opts: CategoriesCollectionOptions = {}): CollectionConfig {\n  const sourceLocale = opts.sourceLocale ?? opts.locales?.[0];\n  const name = opts.name ?? 'categories';\n  return defineCollection({\n    name,\n    label: opts.label ?? 'Categories',\n    kind: 'data',\n    ...(sourceLocale ? { sourceLocale } : {}),\n    ...(opts.locales ? { locales: opts.locales } : {}),\n    description:\n      'Product categories. Editor-managed taxonomy — used for filtering on category pages, ' +\n      'breadcrumbs, faceted search. Self-referencing parent field allows nesting.',\n    fields: [\n      { name: 'name',            type: 'text',     label: 'Name', required: true },\n      { name: 'slug',            type: 'text',     label: 'Slug', required: true },\n      { name: 'description',     type: 'textarea', label: 'Description' },\n      { name: 'parent',          type: 'relation', collection: name, label: 'Parent category' },\n      { name: 'image',           type: 'image',    label: 'Image' },\n      { name: 'sortOrder',       type: 'number',   label: 'Sort order' },\n      { name: 'metaTitle',       type: 'text',     label: 'SEO title (optional)' },\n      { name: 'metaDescription', type: 'textarea', label: 'SEO description (optional)' },\n\n      ...(opts.extraFields ?? []),\n    ],\n  });\n}\n","/**\n * F136 Phase 1 — Orders collection schema.\n *\n * Orders are written by the checkout webhook handler — admins typically\n * READ + UPDATE (status, tracking, notes), not create. Set kind='form'\n * so AI agents don't try to generate orders.\n */\nimport { defineCollection, type CollectionConfig, type FieldConfig } from '@webhouse/cms';\n\nexport interface OrdersCollectionOptions {\n  name?: string;\n  label?: string;\n  extraFields?: FieldConfig[];\n}\n\nexport function ordersCollection(opts: OrdersCollectionOptions = {}): CollectionConfig {\n  return defineCollection({\n    name: opts.name ?? 'orders',\n    label: opts.label ?? 'Orders',\n    kind: 'form',  // read-only for AI; no SEO; no body remap\n    description:\n      'Customer orders. Created automatically by the Stripe checkout webhook. ' +\n      'Editors update status, tracking number, and notes. AI cannot create orders.',\n    fields: [\n      {\n        name: 'status', type: 'select', label: 'Status', required: true,\n        options: [\n          { value: 'pending',            label: 'Pending' },\n          { value: 'paid',               label: 'Paid' },\n          { value: 'fulfilled',          label: 'Fulfilled' },\n          { value: 'shipped',            label: 'Shipped' },\n          { value: 'delivered',          label: 'Delivered' },\n          { value: 'cancelled',          label: 'Cancelled' },\n          { value: 'refunded',           label: 'Refunded' },\n          { value: 'partially-refunded', label: 'Partially refunded' },\n          { value: 'failed',             label: 'Failed' },\n        ],\n      },\n      { name: 'email',         type: 'text',   label: 'Customer email', required: true },\n      { name: 'customerId',    type: 'relation', collection: 'customers', label: 'Customer' },\n      { name: 'currency',      type: 'text',   label: 'Currency (ISO)' },\n\n      // Items snapshot at purchase time\n      {\n        name: 'items', type: 'array', label: 'Line items',\n        fields: [\n          { name: 'productId',     type: 'text',   label: 'Product id' },\n          { name: 'variantId',     type: 'text',   label: 'Variant id' },\n          { name: 'titleSnapshot', type: 'text',   label: 'Title (at purchase)' },\n          { name: 'imageSnapshot', type: 'text',   label: 'Image (at purchase)' },\n          { name: 'unitPrice',     type: 'number', label: 'Unit price (minor units)' },\n          { name: 'quantity',      type: 'number', label: 'Quantity' },\n        ],\n      },\n\n      // Totals\n      { name: 'subtotal',       type: 'number', label: 'Subtotal' },\n      { name: 'discountTotal',  type: 'number', label: 'Discount total' },\n      { name: 'discountCode',   type: 'text',   label: 'Discount code applied' },\n      { name: 'shippingTotal',  type: 'number', label: 'Shipping total' },\n      { name: 'shippingMethod', type: 'text',   label: 'Shipping method' },\n      { name: 'taxTotal',       type: 'number', label: 'Tax total' },\n      { name: 'total',          type: 'number', label: 'Total' },\n\n      // Addresses\n      {\n        name: 'shippingAddress', type: 'object', label: 'Shipping address',\n        fields: [\n          { name: 'name',       type: 'text', label: 'Name' },\n          { name: 'line1',      type: 'text', label: 'Line 1' },\n          { name: 'line2',      type: 'text', label: 'Line 2' },\n          { name: 'postalCode', type: 'text', label: 'Postal code' },\n          { name: 'city',       type: 'text', label: 'City' },\n          { name: 'region',     type: 'text', label: 'Region/state' },\n          { name: 'country',    type: 'text', label: 'Country (ISO 3166-1 alpha-2)' },\n          { name: 'phone',      type: 'text', label: 'Phone' },\n        ],\n      },\n      {\n        name: 'billingAddress', type: 'object', label: 'Billing address',\n        fields: [\n          { name: 'name',       type: 'text', label: 'Name' },\n          { name: 'line1',      type: 'text', label: 'Line 1' },\n          { name: 'line2',      type: 'text', label: 'Line 2' },\n          { name: 'postalCode', type: 'text', label: 'Postal code' },\n          { name: 'city',       type: 'text', label: 'City' },\n          { name: 'region',     type: 'text', label: 'Region/state' },\n          { name: 'country',    type: 'text', label: 'Country (ISO 3166-1 alpha-2)' },\n          { name: 'phone',      type: 'text', label: 'Phone' },\n        ],\n      },\n\n      // Stripe / shipping ids\n      { name: 'stripeCheckoutSessionId', type: 'text', label: 'Stripe Checkout Session id' },\n      { name: 'stripePaymentIntentId',   type: 'text', label: 'Stripe Payment Intent id' },\n      { name: 'trackingNumber',          type: 'text', label: 'Tracking number' },\n      { name: 'trackingUrl',             type: 'text', label: 'Tracking URL' },\n      {\n        name: 'shippingProvider', type: 'select', label: 'Shipping provider',\n        options: [\n          { value: 'gls',      label: 'GLS' },\n          { value: 'dao',      label: 'DAO' },\n          { value: 'postnord', label: 'PostNord' },\n          { value: 'bring',    label: 'Bring' },\n          { value: 'manual',   label: 'Manual' },\n        ],\n      },\n\n      // Misc\n      { name: 'locale',       type: 'text',     label: 'Locale (drives receipt language)' },\n      { name: 'notes',        type: 'textarea', label: 'Internal notes' },\n      { name: 'placedAt',     type: 'date',     label: 'Placed at' },\n      { name: 'paidAt',       type: 'date',     label: 'Paid at' },\n      { name: 'fulfilledAt',  type: 'date',     label: 'Fulfilled at' },\n      { name: 'shippedAt',    type: 'date',     label: 'Shipped at' },\n      { name: 'deliveredAt',  type: 'date',     label: 'Delivered at' },\n      { name: 'cancelledAt',  type: 'date',     label: 'Cancelled at' },\n\n      ...(opts.extraFields ?? []),\n    ],\n  });\n}\n","/**\n * F136 Phase 1 — Customers collection schema.\n *\n * Customers are written by the checkout webhook (auto-created on first\n * order from a new email). Editors manage marketing consent, addresses,\n * notes. Lifetime value is recomputed by an order-paid hook.\n */\nimport { defineCollection, type CollectionConfig, type FieldConfig } from '@webhouse/cms';\n\nexport interface CustomersCollectionOptions {\n  name?: string;\n  label?: string;\n  extraFields?: FieldConfig[];\n}\n\nexport function customersCollection(opts: CustomersCollectionOptions = {}): CollectionConfig {\n  return defineCollection({\n    name: opts.name ?? 'customers',\n    label: opts.label ?? 'Customers',\n    kind: 'data',\n    description:\n      'Customer records. Auto-created on first order. Editors manage consent, addresses, notes. ' +\n      'Lifetime value is auto-computed on order paid.',\n    fields: [\n      { name: 'email', type: 'text',  label: 'Email', required: true },\n      { name: 'name',  type: 'text',  label: 'Name' },\n      { name: 'phone', type: 'text',  label: 'Phone' },\n\n      { name: 'stripeCustomerId',  type: 'text',    label: 'Stripe Customer id (auto-synced)' },\n      { name: 'preferredLocale',   type: 'text',    label: 'Preferred locale (drives email language)' },\n      { name: 'marketingConsent',  type: 'boolean', label: 'Marketing consent (GDPR opt-in)' },\n\n      {\n        name: 'addresses', type: 'array', label: 'Saved addresses',\n        fields: [\n          {\n            name: 'kind', type: 'select', label: 'Kind',\n            options: [\n              { value: 'shipping', label: 'Shipping' },\n              { value: 'billing',  label: 'Billing' },\n              { value: 'both',     label: 'Both' },\n            ],\n          },\n          { name: 'name',       type: 'text', label: 'Name' },\n          { name: 'line1',      type: 'text', label: 'Line 1' },\n          { name: 'line2',      type: 'text', label: 'Line 2' },\n          { name: 'postalCode', type: 'text', label: 'Postal code' },\n          { name: 'city',       type: 'text', label: 'City' },\n          { name: 'region',     type: 'text', label: 'Region/state' },\n          { name: 'country',    type: 'text', label: 'Country (ISO 3166-1 alpha-2)' },\n          { name: 'phone',      type: 'text', label: 'Phone' },\n        ],\n      },\n\n      {\n        name: 'lifetimeValue', type: 'object', label: 'Lifetime value (per currency, auto-computed)',\n        fields: [\n          { name: 'DKK', type: 'number', label: 'DKK (øre)' },\n          { name: 'EUR', type: 'number', label: 'EUR (cents)' },\n          { name: 'SEK', type: 'number', label: 'SEK (öre)' },\n          { name: 'NOK', type: 'number', label: 'NOK (øre)' },\n        ],\n      },\n      { name: 'createdAt', type: 'date', label: 'Created at' },\n\n      ...(opts.extraFields ?? []),\n    ],\n  });\n}\n","/**\n * F136 Phase 1 — Stripe SDK wrapper.\n *\n * Lazy-init: the Stripe client is constructed the first time it's needed,\n * not at import time. Two reasons:\n *   1. Tests can run without STRIPE_SECRET_KEY (calls are mocked elsewhere).\n *   2. Sites that don't actually use Stripe (free-tier marketing sites\n *      that only consume product collections) won't crash on missing env.\n *\n * Mode: per-site. Each site loads its own STRIPE_SECRET_KEY from env;\n * `getStripe(secretKey?)` accepts an explicit key for multi-tenant cms-admin\n * where the key lives in the site config rather than process.env.\n */\nimport Stripe from 'stripe';\n\nconst cache = new Map<string, Stripe>();\n\n/**\n * Get a memoized Stripe client.\n *\n * @param secretKey — explicit key (e.g. site.stripeSecretKey from cms-admin\n *                    site config). Falls back to process.env.STRIPE_SECRET_KEY.\n */\nexport function getStripe(secretKey?: string): Stripe {\n  const key = secretKey ?? process.env.STRIPE_SECRET_KEY;\n  if (!key) {\n    throw new Error(\n      '[cms-shop] STRIPE_SECRET_KEY is not set. ' +\n      'Set it as an env var on this site (or pass secretKey explicitly to getStripe).',\n    );\n  }\n  let client = cache.get(key);\n  if (!client) {\n    client = new Stripe(key, {\n      // Pin the API version so future Stripe upgrades don't silently\n      // change response shapes mid-deploy. We use the SDK's own\n      // pinned literal (`Stripe.API_VERSION`) so the value always\n      // matches the version the installed types were built against.\n      apiVersion: Stripe.API_VERSION,\n      typescript: true,\n      // Identify the integration in Stripe's logs — easier debugging\n      // when multiple webhouse sites share a Stripe account.\n      appInfo: {\n        name: '@webhouse/cms-shop',\n        version: '0.3.0',\n        url: 'https://docs.webhouse.app',\n      },\n    });\n    cache.set(key, client);\n  }\n  return client;\n}\n\n/** Test/cleanup hook — clears the memoized client cache. */\nexport function resetStripeClientCache(): void {\n  cache.clear();\n}\n","/**\n * F136 Phase 1 — Stripe product/price sync.\n *\n * Idempotent push from CMS document → Stripe Product + Prices.\n *   - First call creates the Stripe Product and one Price per currency.\n *   - Subsequent calls update the Product, archive stale Prices, and\n *     create new Prices for changed amounts (Stripe Prices are immutable\n *     once attached to a Subscription, so we never mutate; we archive +\n *     replace).\n *   - The CMS document's `stripeProductId` and `stripePriceIds` fields\n *     are populated and returned so the caller can persist them.\n *\n * Called from the cms-admin `content.afterCreate` and `afterUpdate`\n * hooks (wired up when @webhouse/cms-shop is registered as a module).\n */\nimport type Stripe from 'stripe';\nimport { getStripe } from './client';\nimport type { ShopProduct, MoneyAmountByCurrency } from '../types';\n\nexport interface SyncResult {\n  /** Stripe Product id (created or reused). */\n  stripeProductId: string;\n  /** Map of currency → Stripe Price id (active price for each currency). */\n  stripePriceIds: Record<string, string>;\n  /** Stripe Price ids that were archived in this sync (changed amounts). */\n  archivedPriceIds: string[];\n  /** Whether the Stripe Product was created (true) or updated (false). */\n  created: boolean;\n}\n\nexport interface SyncOptions {\n  /** Pass an explicit secret key (multi-tenant cms-admin). Falls back to env. */\n  secretKey?: string;\n  /** Override the active flag (e.g. archive on product deletion). */\n  active?: boolean;\n}\n\n/**\n * Push a CMS product document to Stripe.\n *\n * Behaviour:\n *   - status='active'  → Stripe Product is active, Prices are active\n *   - status='draft'   → Stripe Product is INactive (no checkout possible)\n *   - status='archived'→ Stripe Product is INactive, Prices archived too\n */\nexport async function syncProductToStripe(\n  product: ShopProduct,\n  opts: SyncOptions = {},\n): Promise<SyncResult> {\n  const stripe = getStripe(opts.secretKey);\n\n  const data = product.data;\n  const status = data.status ?? 'active';\n  const active = opts.active ?? status === 'active';\n\n  // ── 1. Upsert Product ────────────────────────────────────────────────\n  let stripeProductId = data.stripeProductId;\n  let created = false;\n  const productPayload: Stripe.ProductUpdateParams = {\n    name: data.title,\n    active,\n    metadata: {\n      cms_product_id: product.id,\n      cms_slug: product.slug,\n      cms_locale: product.locale ?? '',\n      cms_translation_group: product.translationGroup ?? '',\n      cms_product_type: data.productType,\n      cms_sku: data.sku ?? '',\n    },\n  };\n  if (data.shortDescription) productPayload.description = data.shortDescription;\n  if (data.images && data.images.length > 0) {\n    productPayload.images = data.images.slice(0, 8);  // Stripe caps at 8\n  }\n\n  if (stripeProductId) {\n    await stripe.products.update(stripeProductId, productPayload);\n  } else {\n    const newProduct = await stripe.products.create({\n      ...productPayload,\n      // `id` is optional — let Stripe generate one. We persist it on the doc.\n    } as Stripe.ProductCreateParams);\n    stripeProductId = newProduct.id;\n    created = true;\n  }\n\n  // ── 2. Sync Prices per currency ──────────────────────────────────────\n  // Strategy: list existing active prices for the product. For each\n  // currency in priceByCurrency: if amount matches an active price, keep.\n  // Otherwise archive the old price and create a new one. Currencies\n  // removed from the map get their prices archived.\n  const desired: MoneyAmountByCurrency = data.priceByCurrency ?? {};\n  const archivedPriceIds: string[] = [];\n  const stripePriceIds: Record<string, string> = {};\n\n  const existing = await stripe.prices.list({\n    product: stripeProductId,\n    active: true,\n    limit: 100,\n  });\n\n  // Index existing by currency for quick lookup\n  const existingByCurrency = new Map<string, Stripe.Price>();\n  for (const p of existing.data) {\n    existingByCurrency.set(p.currency.toUpperCase(), p);\n  }\n\n  for (const [currency, amount] of Object.entries(desired)) {\n    if (!Number.isFinite(amount) || amount <= 0) continue;\n    const cur = currency.toUpperCase();\n    const current = existingByCurrency.get(cur);\n\n    if (current && current.unit_amount === amount && current.active) {\n      // Amount unchanged — reuse.\n      stripePriceIds[cur] = current.id;\n      existingByCurrency.delete(cur);\n      continue;\n    }\n\n    if (current) {\n      // Archive the old one (Stripe forbids mutating prices already used\n      // in subscriptions; the safe pattern is archive + replace).\n      await stripe.prices.update(current.id, { active: false });\n      archivedPriceIds.push(current.id);\n      existingByCurrency.delete(cur);\n    }\n\n    if (active) {\n      const newPrice = await stripe.prices.create({\n        product: stripeProductId,\n        currency: cur.toLowerCase(),\n        unit_amount: amount,\n        // tax_behavior controls whether the amount above is inclusive or\n        // exclusive of tax. taxIncluded is the per-product override; if\n        // unset, Stripe uses the account default.\n        ...(typeof data.taxIncluded === 'boolean'\n          ? { tax_behavior: data.taxIncluded ? 'inclusive' as const : 'exclusive' as const }\n          : {}),\n      });\n      stripePriceIds[cur] = newPrice.id;\n    }\n  }\n\n  // Archive currencies removed from the desired map.\n  for (const stale of existingByCurrency.values()) {\n    await stripe.prices.update(stale.id, { active: false });\n    archivedPriceIds.push(stale.id);\n  }\n\n  return { stripeProductId, stripePriceIds, archivedPriceIds, created };\n}\n\n/**\n * Archive a product in Stripe (called when the CMS document is deleted\n * or its status changes to 'archived'). Stripe doesn't allow deleting\n * Products that have ever been associated with a Charge or Subscription,\n * so we always archive instead of delete.\n */\nexport async function archiveProductInStripe(\n  stripeProductId: string,\n  opts: SyncOptions = {},\n): Promise<void> {\n  const stripe = getStripe(opts.secretKey);\n  await stripe.products.update(stripeProductId, { active: false });\n\n  // Archive all active prices too — otherwise checkout sessions could\n  // still reference them and create orders for archived products.\n  const prices = await stripe.prices.list({\n    product: stripeProductId,\n    active: true,\n    limit: 100,\n  });\n  for (const p of prices.data) {\n    await stripe.prices.update(p.id, { active: false });\n  }\n}\n","/**\n * F136 Phase 1 — Cart storage adapter.\n *\n * Default implementation: in-process Map keyed by cartId. That's enough\n * for filesystem-adapter sites running on a single Node process and for\n * the unit tests.\n *\n * Sites running on multiple instances (Fly machine pairs, Vercel\n * serverless) should pass a `CartStore` backed by their shared cache —\n * Redis, Upstash KV, Cloudflare KV. The interface is small on purpose.\n *\n * Carts have a 48 h TTL. The in-memory store sweeps expired entries on\n * each `get()` so it doesn't grow unbounded.\n */\nimport type { ShopCart } from '../types';\n\nexport interface CartStore {\n  get(id: string): Promise<ShopCart | null>;\n  set(cart: ShopCart): Promise<void>;\n  delete(id: string): Promise<void>;\n}\n\nconst TTL_MS = 48 * 60 * 60 * 1000;\n\nexport function createInMemoryCartStore(): CartStore {\n  const data = new Map<string, ShopCart>();\n\n  function reap() {\n    const now = Date.now();\n    for (const [id, cart] of data) {\n      if (Date.parse(cart.expiresAt) <= now) data.delete(id);\n    }\n  }\n\n  return {\n    async get(id: string): Promise<ShopCart | null> {\n      reap();\n      return data.get(id) ?? null;\n    },\n    async set(cart: ShopCart): Promise<void> {\n      data.set(cart.id, cart);\n    },\n    async delete(id: string): Promise<void> {\n      data.delete(id);\n    },\n  };\n}\n\nexport const CART_TTL_MS = TTL_MS;\n","/**\n * F136 Phase 1 — Cart engine.\n *\n * Pure functions over the ShopCart data shape. The `CartStore` adapter\n * persists; this file does math + invariants.\n *\n * Currency rule: a cart is single-currency. The first item locks the\n * currency; adding an item priced in a different currency throws.\n * Sites that need multi-currency carts should split into separate carts\n * per currency (matches Stripe Checkout's own limitation).\n */\nimport { randomUUID } from 'crypto';\nimport type {\n  CartLineItem,\n  CurrencyCode,\n  MoneyAmount,\n  ShopAddress,\n  ShopCart,\n  ShopProduct,\n} from '../types';\nimport { CART_TTL_MS, type CartStore } from './store';\n\nexport interface CartContext {\n  store: CartStore;\n}\n\nexport interface AddItemInput {\n  product: ShopProduct;\n  variantId?: string;\n  quantity?: number;\n  /** Currency to price the item in. Must match an entry in priceByCurrency. */\n  currency: CurrencyCode;\n  /** Locale to lock onto the cart on first item add. */\n  locale?: string;\n}\n\nfunction newId(): string {\n  return `cart_${randomUUID().replace(/-/g, '').slice(0, 24)}`;\n}\n\nfunction nowIso(): string {\n  return new Date().toISOString();\n}\n\nfunction expiryIso(): string {\n  return new Date(Date.now() + CART_TTL_MS).toISOString();\n}\n\nfunction emptyCart(currency: CurrencyCode, locale?: string): ShopCart {\n  const now = nowIso();\n  return {\n    id: newId(),\n    currency: currency.toUpperCase(),\n    items: [],\n    subtotal: 0,\n    discountTotal: 0,\n    shippingTotal: 0,\n    taxTotal: 0,\n    total: 0,\n    createdAt: now,\n    updatedAt: now,\n    expiresAt: expiryIso(),\n    ...(locale ? { locale } : {}),\n  };\n}\n\nfunction lineKey(item: Pick<CartLineItem, 'productId' | 'variantId'>): string {\n  return `${item.productId}::${item.variantId ?? ''}`;\n}\n\nfunction recomputeTotals(cart: ShopCart): ShopCart {\n  const subtotal = cart.items.reduce(\n    (sum, l) => sum + l.unitPrice * l.quantity,\n    0,\n  );\n  const total =\n    subtotal - cart.discountTotal + cart.shippingTotal + cart.taxTotal;\n  return {\n    ...cart,\n    subtotal,\n    total: Math.max(0, total),\n    updatedAt: nowIso(),\n  };\n}\n\nfunction resolveUnitPrice(\n  product: ShopProduct,\n  variantId: string | undefined,\n  currency: string,\n): MoneyAmount {\n  const cur = currency.toUpperCase();\n  if (variantId) {\n    const variant = product.data.variants?.find((v) => v.id === variantId);\n    if (!variant) {\n      throw new Error(\n        `[cms-shop] variant ${variantId} not found on product ${product.id}`,\n      );\n    }\n    const variantPrice = variant.priceByCurrency?.[cur];\n    if (typeof variantPrice === 'number' && variantPrice > 0) {\n      return variantPrice;\n    }\n  }\n  const price = product.data.priceByCurrency?.[cur];\n  if (typeof price !== 'number' || price <= 0) {\n    throw new Error(\n      `[cms-shop] product ${product.id} is not priced in ${cur}`,\n    );\n  }\n  return price;\n}\n\nexport async function getOrCreateCart(\n  ctx: CartContext,\n  cartId: string | undefined,\n  fallbackCurrency: CurrencyCode,\n  locale?: string,\n): Promise<ShopCart> {\n  if (cartId) {\n    const existing = await ctx.store.get(cartId);\n    if (existing) return existing;\n  }\n  const cart = emptyCart(fallbackCurrency, locale);\n  await ctx.store.set(cart);\n  return cart;\n}\n\nexport async function getCart(\n  ctx: CartContext,\n  cartId: string,\n): Promise<ShopCart | null> {\n  return ctx.store.get(cartId);\n}\n\nexport async function addItem(\n  ctx: CartContext,\n  cartId: string | undefined,\n  input: AddItemInput,\n): Promise<ShopCart> {\n  const cart = await getOrCreateCart(\n    ctx,\n    cartId,\n    input.currency,\n    input.locale,\n  );\n  const cur = input.currency.toUpperCase();\n\n  if (cart.items.length > 0 && cart.currency !== cur) {\n    throw new Error(\n      `[cms-shop] cart is in ${cart.currency} — cannot add item in ${cur}`,\n    );\n  }\n  if (cart.items.length === 0 && cart.currency !== cur) {\n    cart.currency = cur;\n  }\n\n  const qty = Math.max(1, Math.floor(input.quantity ?? 1));\n  const unitPrice = resolveUnitPrice(input.product, input.variantId, cur);\n\n  const newLine: CartLineItem = {\n    productId: input.product.id,\n    ...(input.variantId ? { variantId: input.variantId } : {}),\n    unitPrice,\n    currency: cur,\n    quantity: qty,\n    titleSnapshot: input.product.data.title,\n    ...(input.product.data.cardImageUrl\n      ? { imageSnapshot: input.product.data.cardImageUrl }\n      : input.product.data.images?.[0]\n        ? { imageSnapshot: input.product.data.images[0] }\n        : {}),\n  };\n\n  const key = lineKey(newLine);\n  const existingIndex = cart.items.findIndex((l) => lineKey(l) === key);\n\n  if (existingIndex >= 0) {\n    const merged = [...cart.items];\n    const existing = merged[existingIndex]!;\n    merged[existingIndex] = {\n      ...existing,\n      quantity: existing.quantity + qty,\n    };\n    cart.items = merged;\n  } else {\n    cart.items = [...cart.items, newLine];\n  }\n\n  const next = recomputeTotals(cart);\n  await ctx.store.set(next);\n  return next;\n}\n\nexport async function updateItemQuantity(\n  ctx: CartContext,\n  cartId: string,\n  productId: string,\n  variantId: string | undefined,\n  quantity: number,\n): Promise<ShopCart> {\n  const cart = await ctx.store.get(cartId);\n  if (!cart) throw new Error(`[cms-shop] cart ${cartId} not found`);\n\n  const key = lineKey({ productId, ...(variantId ? { variantId } : {}) });\n  const items = cart.items\n    .map((l) =>\n      lineKey(l) === key\n        ? { ...l, quantity: Math.max(0, Math.floor(quantity)) }\n        : l,\n    )\n    .filter((l) => l.quantity > 0);\n\n  const next = recomputeTotals({ ...cart, items });\n  await ctx.store.set(next);\n  return next;\n}\n\nexport async function removeItem(\n  ctx: CartContext,\n  cartId: string,\n  productId: string,\n  variantId?: string,\n): Promise<ShopCart> {\n  return updateItemQuantity(ctx, cartId, productId, variantId, 0);\n}\n\nexport async function setShippingAddress(\n  ctx: CartContext,\n  cartId: string,\n  address: ShopAddress,\n): Promise<ShopCart> {\n  const cart = await ctx.store.get(cartId);\n  if (!cart) throw new Error(`[cms-shop] cart ${cartId} not found`);\n  const next = recomputeTotals({ ...cart, shippingAddress: address });\n  await ctx.store.set(next);\n  return next;\n}\n\nexport async function setEmail(\n  ctx: CartContext,\n  cartId: string,\n  email: string,\n): Promise<ShopCart> {\n  const cart = await ctx.store.get(cartId);\n  if (!cart) throw new Error(`[cms-shop] cart ${cartId} not found`);\n  const next = recomputeTotals({ ...cart, email });\n  await ctx.store.set(next);\n  return next;\n}\n\nexport async function clearCart(\n  ctx: CartContext,\n  cartId: string,\n): Promise<void> {\n  await ctx.store.delete(cartId);\n}\n","/**\n * F136 Phase 1 — Framework-agnostic cart HTTP handlers.\n *\n * Returns plain `Request → Response` functions so consumers can wire\n * them into Next.js Route Handlers, Hono, Fastify, or Bun.serve with\n * one line each.\n *\n * Cart id transport: `cms_shop_cart` cookie. The handler reads it on\n * every request, generates one on first add, and re-issues a Set-Cookie\n * so the client persists it. SameSite=Lax is the default — anything\n * stricter blocks Stripe Checkout's redirect-back flow.\n */\nimport type { ShopProduct } from '../types';\nimport {\n  addItem,\n  clearCart,\n  getCart,\n  removeItem,\n  setEmail,\n  setShippingAddress,\n  updateItemQuantity,\n  type CartContext,\n} from './cart';\nimport type { CartStore } from './store';\nimport { CART_TTL_MS } from './store';\n\nconst COOKIE_NAME = 'cms_shop_cart';\n\nexport interface CartHandlerOptions {\n  store: CartStore;\n  /**\n   * Resolve a product by id from the site's CMS. The shop module is\n   * data-source agnostic so we don't import filesystem readers here —\n   * the host wires this in.\n   */\n  loadProduct(id: string): Promise<ShopProduct | null>;\n  /** Called whenever a cart mutates — useful for analytics/abandonment. */\n  onCartChanged?(cartId: string): void;\n  /** Cookie domain (default: omit, browser scopes to current host). */\n  cookieDomain?: string;\n  /** Set Secure flag on the cookie (default: true in prod, false in dev). */\n  cookieSecure?: boolean;\n}\n\nexport interface CartHandlers {\n  /** GET — returns the current cart (creates one if cookie missing). */\n  get(req: Request): Promise<Response>;\n  /** POST { productId, variantId?, quantity?, currency, locale? }. */\n  add(req: Request): Promise<Response>;\n  /** PATCH { productId, variantId?, quantity }. */\n  update(req: Request): Promise<Response>;\n  /** DELETE { productId, variantId? }. */\n  remove(req: Request): Promise<Response>;\n  /** POST { email } — captures email for abandonment + receipt. */\n  setEmail(req: Request): Promise<Response>;\n  /** POST { address } — for shipping rate calc + checkout. */\n  setAddress(req: Request): Promise<Response>;\n  /** DELETE — wipes the current cart. */\n  clear(req: Request): Promise<Response>;\n}\n\nfunction readCookie(req: Request, name: string): string | undefined {\n  const header = req.headers.get('cookie');\n  if (!header) return undefined;\n  for (const part of header.split(';')) {\n    const [k, v] = part.trim().split('=');\n    if (k === name) return v;\n  }\n  return undefined;\n}\n\nfunction buildSetCookie(\n  cartId: string,\n  opts: { domain?: string; secure: boolean },\n): string {\n  const maxAge = Math.floor(CART_TTL_MS / 1000);\n  const parts = [\n    `${COOKIE_NAME}=${cartId}`,\n    'Path=/',\n    'HttpOnly',\n    'SameSite=Lax',\n    `Max-Age=${maxAge}`,\n  ];\n  if (opts.domain) parts.push(`Domain=${opts.domain}`);\n  if (opts.secure) parts.push('Secure');\n  return parts.join('; ');\n}\n\nfunction json(\n  body: unknown,\n  init: ResponseInit & { setCartCookie?: string } = {},\n): Response {\n  const headers = new Headers(init.headers);\n  headers.set('content-type', 'application/json; charset=utf-8');\n  if (init.setCartCookie) headers.set('set-cookie', init.setCartCookie);\n  return new Response(JSON.stringify(body), {\n    status: init.status ?? 200,\n    headers,\n  });\n}\n\nfunction badRequest(message: string): Response {\n  return json({ error: message }, { status: 400 });\n}\n\nasync function readJson(req: Request): Promise<Record<string, unknown>> {\n  try {\n    const body = (await req.json()) as Record<string, unknown>;\n    return body && typeof body === 'object' ? body : {};\n  } catch {\n    return {};\n  }\n}\n\nexport function createCartHandlers(opts: CartHandlerOptions): CartHandlers {\n  const ctx: CartContext = { store: opts.store };\n  const secure =\n    opts.cookieSecure ?? process.env.NODE_ENV === 'production';\n  const cookieOpts = {\n    secure,\n    ...(opts.cookieDomain ? { domain: opts.cookieDomain } : {}),\n  };\n\n  function setCookie(cartId: string): string {\n    return buildSetCookie(cartId, cookieOpts);\n  }\n\n  return {\n    async get(req: Request): Promise<Response> {\n      const cartId = readCookie(req, COOKIE_NAME);\n      if (!cartId) return json({ cart: null });\n      const cart = await getCart(ctx, cartId);\n      return json({ cart });\n    },\n\n    async add(req: Request): Promise<Response> {\n      const body = await readJson(req);\n      const productId = String(body.productId ?? '');\n      if (!productId) return badRequest('productId required');\n      const product = await opts.loadProduct(productId);\n      if (!product) return badRequest(`product ${productId} not found`);\n      const currency = String(body.currency ?? '').trim();\n      if (!currency) return badRequest('currency required');\n\n      const cartId = readCookie(req, COOKIE_NAME);\n      const cart = await addItem(ctx, cartId, {\n        product,\n        currency,\n        ...(body.variantId ? { variantId: String(body.variantId) } : {}),\n        ...(typeof body.quantity === 'number'\n          ? { quantity: body.quantity }\n          : {}),\n        ...(body.locale ? { locale: String(body.locale) } : {}),\n      });\n      opts.onCartChanged?.(cart.id);\n      return json({ cart }, { setCartCookie: setCookie(cart.id) });\n    },\n\n    async update(req: Request): Promise<Response> {\n      const cartId = readCookie(req, COOKIE_NAME);\n      if (!cartId) return badRequest('no cart');\n      const body = await readJson(req);\n      const productId = String(body.productId ?? '');\n      const quantity = Number(body.quantity);\n      if (!productId || !Number.isFinite(quantity)) {\n        return badRequest('productId + quantity required');\n      }\n      const cart = await updateItemQuantity(\n        ctx,\n        cartId,\n        productId,\n        body.variantId ? String(body.variantId) : undefined,\n        quantity,\n      );\n      opts.onCartChanged?.(cart.id);\n      return json({ cart });\n    },\n\n    async remove(req: Request): Promise<Response> {\n      const cartId = readCookie(req, COOKIE_NAME);\n      if (!cartId) return badRequest('no cart');\n      const body = await readJson(req);\n      const productId = String(body.productId ?? '');\n      if (!productId) return badRequest('productId required');\n      const cart = await removeItem(\n        ctx,\n        cartId,\n        productId,\n        body.variantId ? String(body.variantId) : undefined,\n      );\n      opts.onCartChanged?.(cart.id);\n      return json({ cart });\n    },\n\n    async setEmail(req: Request): Promise<Response> {\n      const cartId = readCookie(req, COOKIE_NAME);\n      if (!cartId) return badRequest('no cart');\n      const body = await readJson(req);\n      const email = String(body.email ?? '').trim();\n      if (!email || !email.includes('@')) return badRequest('email required');\n      const cart = await setEmail(ctx, cartId, email);\n      opts.onCartChanged?.(cart.id);\n      return json({ cart });\n    },\n\n    async setAddress(req: Request): Promise<Response> {\n      const cartId = readCookie(req, COOKIE_NAME);\n      if (!cartId) return badRequest('no cart');\n      const body = await readJson(req);\n      const address = body.address as\n        | Record<string, unknown>\n        | undefined;\n      if (!address || typeof address !== 'object') {\n        return badRequest('address required');\n      }\n      const required = ['name', 'line1', 'postalCode', 'city', 'country'];\n      for (const k of required) {\n        if (!address[k] || typeof address[k] !== 'string') {\n          return badRequest(`address.${k} required`);\n        }\n      }\n      const cart = await setShippingAddress(ctx, cartId, {\n        name: String(address.name),\n        line1: String(address.line1),\n        postalCode: String(address.postalCode),\n        city: String(address.city),\n        country: String(address.country),\n        ...(address.line2 ? { line2: String(address.line2) } : {}),\n        ...(address.region ? { region: String(address.region) } : {}),\n        ...(address.phone ? { phone: String(address.phone) } : {}),\n        ...(address.kind\n          ? { kind: address.kind as 'shipping' | 'billing' | 'both' }\n          : {}),\n      });\n      opts.onCartChanged?.(cart.id);\n      return json({ cart });\n    },\n\n    async clear(req: Request): Promise<Response> {\n      const cartId = readCookie(req, COOKIE_NAME);\n      if (cartId) await clearCart(ctx, cartId);\n      const expired = `${COOKIE_NAME}=; Path=/; Max-Age=0; HttpOnly; SameSite=Lax${secure ? '; Secure' : ''}`;\n      return json({ cleared: true }, { setCartCookie: expired });\n    },\n  };\n}\n\nexport const CART_COOKIE_NAME = COOKIE_NAME;\n","/**\n * F136 Phase 1 — Stripe Checkout Session creator.\n *\n * Translates a CMS cart into a Stripe Checkout Session and returns the\n * redirect URL. The actual order document is created later by the\n * webhook handler when Stripe confirms payment — never trust the\n * client to tell us \"the order is paid\".\n *\n * Two operating modes:\n *   1. Single-merchant: the site owns the Stripe account. Just pass\n *      `secretKey` (or set STRIPE_SECRET_KEY env). Money lands on that\n *      account directly.\n *   2. Marketplace (Stripe Connect): the platform (e.g. webhouse.app)\n *      owns the Stripe account, each merchant has a connected\n *      `acct_xxx`. Pass `connect: { destinationAccountId, applicationFeeAmount }`\n *      and money is transferred to the merchant minus the platform fee.\n *      Patterns lifted from the proven sanneandersen-site implementation\n *      (verified with real Stripe demo transactions, May 2026).\n *\n * Strategy: prefer existing `stripePriceIds` on each product (created\n * by syncProductToStripe). If a product hasn't been synced yet, fall\n * back to `price_data` inline — works but loses Stripe's reporting per\n * Price.\n */\nimport type Stripe from 'stripe';\nimport { getStripe } from '../stripe/client';\nimport type { ShopCart, ShopProduct } from '../types';\n\n// Stripe SDK declaration-merging on the Checkout namespace doesn't\n// surface SessionCreateParams reliably across module-resolution modes;\n// we infer the param type from the actual `create` call signature\n// instead — robust against namespace path quirks.\ntype CheckoutSessionCreateParams = NonNullable<\n  Parameters<Stripe['checkout']['sessions']['create']>[0]\n>;\ntype CheckoutLineItem = NonNullable<\n  CheckoutSessionCreateParams['line_items']\n>[number];\ntype AllowedCountry = NonNullable<\n  CheckoutSessionCreateParams['shipping_address_collection']\n>['allowed_countries'][number];\ntype CheckoutLocale = NonNullable<CheckoutSessionCreateParams['locale']>;\n\nexport interface ConnectOptions {\n  /** `acct_xxx` of the merchant's connected account. */\n  destinationAccountId: string;\n  /**\n   * Platform fee in minor units (øre/cents). Either supply this OR\n   * `applicationFeePercent` — not both. Wins if both are set.\n   */\n  applicationFeeAmount?: number;\n  /**\n   * Platform fee as a percentage of the cart total (0–100). Computed\n   * server-side at checkout time. Convenient for sites that price by\n   * percentage rather than fixed amount.\n   */\n  applicationFeePercent?: number;\n}\n\nexport interface CreateCheckoutOptions {\n  /** Stripe secret key (multi-tenant). Falls back to env. */\n  secretKey?: string;\n  /** Where Stripe redirects on success — `{CHECKOUT_SESSION_ID}` placeholder is allowed. */\n  successUrl: string;\n  /** Where Stripe redirects on cancel. */\n  cancelUrl: string;\n  /** Resolve a product id → ShopProduct so we can pull Stripe price ids. */\n  loadProduct(id: string): Promise<ShopProduct | null>;\n  /** Force Stripe to collect a shipping address (default: true if any cart line is physical). */\n  collectShippingAddress?: boolean;\n  /** Stripe shipping rate ids to offer at checkout. */\n  shippingRateIds?: string[];\n  /** Locale shown in Stripe Checkout UI (e.g. 'da', 'en'). Default: cart.locale. */\n  locale?: CheckoutLocale;\n  /** Allowed countries for shipping. ISO 3166-1 alpha-2. */\n  allowedShippingCountries?: string[];\n  /** Extra metadata to attach to the Stripe Session. */\n  metadata?: Record<string, string>;\n  /** Stripe Connect — turn this on for marketplace mode. */\n  connect?: ConnectOptions;\n  /**\n   * Force Stripe to create+attach a Customer object even for guest\n   * checkouts. Without this the Customer column in the Stripe dashboard\n   * is blank — which makes the merchant's life much harder.\n   * Default: true.\n   */\n  alwaysCreateCustomer?: boolean;\n  /**\n   * Short reference shown in the Stripe dashboard's payments table.\n   * The webhook handler uses this to find the matching CMS document\n   * by order/cart id without parsing metadata. Default: cart.id.\n   */\n  clientReferenceId?: string;\n  /**\n   * Human-friendly description shown in Stripe dashboard's payments\n   * table — what is this charge actually for? Default: short summary\n   * built from the first line item title + total.\n   */\n  description?: string;\n  /**\n   * Send Stripe-generated receipt email. Default: cart.email if present.\n   * Set to false to disable (e.g. if the site sends its own receipts).\n   */\n  receiptEmail?: string | false;\n  /**\n   * Discriminator written to `session.metadata.kind`. Lets the webhook\n   * handler dispatch one event type to multiple flows (e.g. 'order',\n   * 'subscription', 'donation'). Default: 'order'.\n   */\n  kind?: string;\n}\n\nexport interface CheckoutResult {\n  sessionId: string;\n  url: string;\n}\n\nfunction buildDefaultDescription(cart: ShopCart): string {\n  const first = cart.items[0];\n  if (!first) return `Order ${cart.id}`;\n  if (cart.items.length === 1) {\n    return `${first.titleSnapshot} (×${first.quantity})`;\n  }\n  const more = cart.items.length - 1;\n  return `${first.titleSnapshot} +${more} more`;\n}\n\nexport async function createCheckoutSession(\n  cart: ShopCart,\n  opts: CreateCheckoutOptions,\n): Promise<CheckoutResult> {\n  if (cart.items.length === 0) {\n    throw new Error('[cms-shop] cannot checkout an empty cart');\n  }\n\n  const stripe = getStripe(opts.secretKey);\n\n  const lineItems: CheckoutLineItem[] = [];\n  let needsShipping = false;\n\n  for (const line of cart.items) {\n    const product = await opts.loadProduct(line.productId);\n    if (!product) {\n      throw new Error(\n        `[cms-shop] cart references missing product ${line.productId}`,\n      );\n    }\n    if (\n      product.data.productType === 'physical' ||\n      product.data.deliveryType === 'shipping'\n    ) {\n      needsShipping = true;\n    }\n\n    const priceId =\n      product.data.stripePriceIds?.[cart.currency.toUpperCase()];\n\n    if (priceId) {\n      lineItems.push({ price: priceId, quantity: line.quantity });\n    } else {\n      lineItems.push({\n        quantity: line.quantity,\n        price_data: {\n          currency: cart.currency.toLowerCase(),\n          unit_amount: line.unitPrice,\n          product_data: {\n            name: line.titleSnapshot,\n            ...(line.imageSnapshot ? { images: [line.imageSnapshot] } : {}),\n            metadata: {\n              cms_product_id: line.productId,\n              ...(line.variantId ? { cms_variant_id: line.variantId } : {}),\n            },\n          },\n        },\n      });\n    }\n  }\n\n  const collectShipping = opts.collectShippingAddress ?? needsShipping;\n  const description = opts.description ?? buildDefaultDescription(cart);\n  const kind = opts.kind ?? 'order';\n\n  const params: CheckoutSessionCreateParams = {\n    mode: 'payment',\n    line_items: lineItems,\n    success_url: opts.successUrl,\n    cancel_url: opts.cancelUrl,\n    automatic_tax: { enabled: false },\n    client_reference_id: opts.clientReferenceId ?? cart.id,\n    metadata: {\n      cms_cart_id: cart.id,\n      kind,\n      ...(cart.locale ? { cms_locale: cart.locale } : {}),\n      ...(opts.metadata ?? {}),\n    },\n  };\n\n  if (opts.alwaysCreateCustomer !== false) {\n    params.customer_creation = 'always';\n  }\n\n  if (cart.email) params.customer_email = cart.email;\n  if (opts.locale) params.locale = opts.locale;\n\n  // ── Connect / marketplace mode ──────────────────────────────────────\n  // Pattern from sanneandersen-site (verified on real Stripe transactions).\n  // application_fee_amount + transfer_data routes money to the merchant\n  // minus platform fee; on_behalf_of makes the charge appear \"owned\" by\n  // the merchant so they see customer details on their dashboard.\n  if (opts.connect) {\n    const fee = computeApplicationFee(cart, opts.connect);\n    params.payment_intent_data = {\n      application_fee_amount: fee,\n      transfer_data: { destination: opts.connect.destinationAccountId },\n      on_behalf_of: opts.connect.destinationAccountId,\n      description,\n      metadata: {\n        cms_cart_id: cart.id,\n        kind,\n        ...(opts.metadata ?? {}),\n      },\n      ...(opts.receiptEmail !== false && (opts.receiptEmail || cart.email)\n        ? { receipt_email: (opts.receiptEmail || cart.email) as string }\n        : {}),\n    };\n  } else {\n    // Single-merchant mode still benefits from description + receipt_email\n    // for dashboard quality.\n    params.payment_intent_data = {\n      description,\n      metadata: {\n        cms_cart_id: cart.id,\n        kind,\n        ...(opts.metadata ?? {}),\n      },\n      ...(opts.receiptEmail !== false && (opts.receiptEmail || cart.email)\n        ? { receipt_email: (opts.receiptEmail || cart.email) as string }\n        : {}),\n    };\n  }\n\n  if (collectShipping) {\n    params.shipping_address_collection = {\n      allowed_countries:\n        (opts.allowedShippingCountries as AllowedCountry[]) ??\n        (['DK', 'SE', 'NO', 'DE'] as AllowedCountry[]),\n    };\n    if (opts.shippingRateIds && opts.shippingRateIds.length > 0) {\n      params.shipping_options = opts.shippingRateIds.map((id) => ({\n        shipping_rate: id,\n      }));\n    }\n  }\n\n  const session = await stripe.checkout.sessions.create(params);\n  if (!session.url) {\n    throw new Error('[cms-shop] Stripe returned a session without a url');\n  }\n  return { sessionId: session.id, url: session.url };\n}\n\n/**\n * Compute the platform fee in minor units. Exported so consumers (and\n * tests) can reason about the math without going through Stripe.\n */\nexport function computeApplicationFee(\n  cart: ShopCart,\n  connect: ConnectOptions,\n): number {\n  if (typeof connect.applicationFeeAmount === 'number') {\n    return Math.max(0, Math.floor(connect.applicationFeeAmount));\n  }\n  if (typeof connect.applicationFeePercent === 'number') {\n    const pct = Math.max(0, Math.min(100, connect.applicationFeePercent));\n    return Math.round((cart.total * pct) / 100);\n  }\n  return 0;\n}\n","/**\n * F136 Phase 1 — POST /api/shop/checkout HTTP handler.\n *\n * Reads the cart cookie, validates the cart is non-empty, creates a\n * Stripe Checkout Session, returns `{ url }` for the client to redirect\n * to. Returns 4xx with a clear message instead of a stack trace if the\n * cart is missing or empty (UX matters here).\n */\nimport { CART_COOKIE_NAME } from '../cart/handlers';\nimport type { CartStore } from '../cart/store';\nimport {\n  createCheckoutSession,\n  type CreateCheckoutOptions,\n} from './checkout';\n\nexport interface CheckoutHandlerOptions\n  extends Omit<CreateCheckoutOptions, 'successUrl' | 'cancelUrl'> {\n  store: CartStore;\n  /** Build success_url from cart id — typically `${siteOrigin}/checkout/success?session_id={CHECKOUT_SESSION_ID}`. */\n  successUrl: string | ((cartId: string) => string);\n  cancelUrl: string | ((cartId: string) => string);\n}\n\nfunction readCookie(req: Request, name: string): string | undefined {\n  const header = req.headers.get('cookie');\n  if (!header) return undefined;\n  for (const part of header.split(';')) {\n    const [k, v] = part.trim().split('=');\n    if (k === name) return v;\n  }\n  return undefined;\n}\n\nfunction json(body: unknown, status = 200): Response {\n  return new Response(JSON.stringify(body), {\n    status,\n    headers: { 'content-type': 'application/json; charset=utf-8' },\n  });\n}\n\nexport function createCheckoutHandler(opts: CheckoutHandlerOptions) {\n  return async function handle(req: Request): Promise<Response> {\n    const cartId = readCookie(req, CART_COOKIE_NAME);\n    if (!cartId) return json({ error: 'no cart' }, 400);\n    const cart = await opts.store.get(cartId);\n    if (!cart) return json({ error: 'cart expired' }, 404);\n    if (cart.items.length === 0) {\n      return json({ error: 'cart is empty' }, 400);\n    }\n\n    const successUrl =\n      typeof opts.successUrl === 'function'\n        ? opts.successUrl(cart.id)\n        : opts.successUrl;\n    const cancelUrl =\n      typeof opts.cancelUrl === 'function'\n        ? opts.cancelUrl(cart.id)\n        : opts.cancelUrl;\n\n    try {\n      const result = await createCheckoutSession(cart, {\n        ...opts,\n        successUrl,\n        cancelUrl,\n      });\n      return json(result);\n    } catch (err) {\n      const message = err instanceof Error ? err.message : 'checkout failed';\n      // eslint-disable-next-line no-console\n      console.error('[cms-shop] checkout failed', err);\n      return json({ error: message }, 500);\n    }\n  };\n}\n","/**\n * F136 Phase 1 — Order document factory.\n *\n * Pure function that takes a Stripe Checkout Session + the cart it\n * referenced and produces a `ShopOrder` data object ready for the host\n * to persist. The host owns \"where orders live\" (filesystem JSON,\n * GitHub adapter, external DB) so we don't write here — we return the\n * doc shape and let the host call its CMS write path.\n */\nimport type Stripe from 'stripe';\nimport type {\n  ShopAddress,\n  ShopCart,\n  ShopOrder,\n} from '../types';\n\nfunction nextOrderNumber(prefix = 'WH'): string {\n  const year = new Date().getUTCFullYear();\n  const rand = Math.floor(Math.random() * 0xfff_fff)\n    .toString(16)\n    .padStart(6, '0')\n    .toUpperCase();\n  return `${prefix}-${year}-${rand}`;\n}\n\nfunction stripeAddrToShop(\n  addr: Stripe.Address | null | undefined,\n  fallbackName: string,\n  phone?: string | null,\n): ShopAddress | undefined {\n  if (!addr || !addr.line1 || !addr.country) return undefined;\n  return {\n    name: fallbackName,\n    line1: addr.line1,\n    ...(addr.line2 ? { line2: addr.line2 } : {}),\n    postalCode: addr.postal_code ?? '',\n    city: addr.city ?? '',\n    country: addr.country,\n    ...(addr.state ? { region: addr.state } : {}),\n    ...(phone ? { phone } : {}),\n  };\n}\n\nexport interface BuildOrderInput {\n  session: Stripe.Checkout.Session;\n  cart: ShopCart;\n  /** Slug prefix for the order number — default \"WH\". */\n  orderNumberPrefix?: string;\n}\n\nexport function buildOrderFromCheckoutSession(\n  input: BuildOrderInput,\n): { id: string; slug: string; data: ShopOrder['data'] } {\n  const { session, cart } = input;\n  const slug = nextOrderNumber(input.orderNumberPrefix);\n  const placedAt = new Date().toISOString();\n  const paid = session.payment_status === 'paid';\n\n  const shipping = session.collected_information?.shipping_details;\n  const customerDetails = session.customer_details;\n\n  const shippingAddress = stripeAddrToShop(\n    shipping?.address ?? null,\n    shipping?.name ?? customerDetails?.name ?? cart.email ?? 'Customer',\n    customerDetails?.phone,\n  );\n  const billingAddress = stripeAddrToShop(\n    customerDetails?.address ?? null,\n    customerDetails?.name ?? cart.email ?? 'Customer',\n    customerDetails?.phone,\n  );\n\n  const data: ShopOrder['data'] = {\n    status: paid ? 'paid' : 'pending',\n    email:\n      session.customer_details?.email ??\n      session.customer_email ??\n      cart.email ??\n      '',\n    currency: cart.currency,\n    items: cart.items,\n    subtotal: cart.subtotal,\n    discountTotal: cart.discountTotal,\n    shippingTotal: cart.shippingTotal,\n    taxTotal: cart.taxTotal,\n    total: cart.total,\n    stripeCheckoutSessionId: session.id,\n    ...(typeof session.payment_intent === 'string'\n      ? { stripePaymentIntentId: session.payment_intent }\n      : session.payment_intent && 'id' in session.payment_intent\n        ? { stripePaymentIntentId: session.payment_intent.id }\n        : {}),\n    ...(shippingAddress ? { shippingAddress } : {}),\n    ...(billingAddress ? { billingAddress } : {}),\n    ...(cart.shippingRateId ? { shippingMethod: cart.shippingRateId } : {}),\n    ...(cart.locale ? { locale: cart.locale } : {}),\n    ...(cart.discountCode ? { discountCode: cart.discountCode } : {}),\n    placedAt,\n    ...(paid ? { paidAt: placedAt } : {}),\n  };\n\n  return { id: slug.toLowerCase(), slug, data };\n}\n","/**\n * F136 Phase 1 — Format Stripe payment_method_details into a human label.\n *\n * Lifted from the proven sanneandersen-site implementation\n * (verified on real Stripe transactions). Examples:\n *   - \"Visa •••• 4242\"\n *   - \"Mastercard •••• 1234\"\n *   - \"Apple Pay\"\n *   - \"Google Pay\"\n *   - \"MobilePay\"\n *   - \"Klarna\"\n */\nimport type Stripe from 'stripe';\n\nexport function formatPaymentMethod(\n  pm: Stripe.Charge.PaymentMethodDetails | null | undefined,\n): string | null {\n  if (!pm) return null;\n\n  // Apple Pay / Google Pay are reported as `card` with a wallet sub-type.\n  if (pm.card?.wallet?.type === 'apple_pay') return 'Apple Pay';\n  if (pm.card?.wallet?.type === 'google_pay') return 'Google Pay';\n\n  if (pm.card) {\n    const brand = pm.card.brand?.replace(/^\\w/, (c) => c.toUpperCase()) ?? 'Card';\n    const last4 = pm.card.last4 ? ` •••• ${pm.card.last4}` : '';\n    return `${brand}${last4}`;\n  }\n\n  if (pm.mobilepay) return 'MobilePay';\n\n  // Fallback: prettify the type string (sepa_debit → Sepa debit, klarna → Klarna).\n  return pm.type\n    ? pm.type.replace(/_/g, ' ').replace(/^\\w/, (c) => c.toUpperCase())\n    : null;\n}\n","/**\n * F136 Phase 1 — POST /api/shop/webhooks Stripe webhook handler.\n *\n * Verifies the Stripe signature header, dispatches events to the host's\n * `onOrderPaid` / `onOrderFailed` / `onOrderRefunded` callbacks. Order\n * persistence is the host's responsibility — this module only reshapes\n * Stripe payloads into CMS-friendly objects.\n *\n * Important: Stripe webhook signature verification needs the *raw*\n * request body. In Next.js Route Handlers and Hono, that means reading\n * `req.text()` directly. Frameworks that JSON-parse the body before our\n * handler runs will break signature verification.\n *\n * Connect mode: events about activity ON a connected account arrive\n * with `event.account = 'acct_xxx'` set. We pass that through to the\n * `onOrderPaid` callback so the host can route the order to the right\n * tenant. The pattern matches sanneandersen-site (proven on real\n * Stripe transactions).\n *\n * Idempotency: Stripe re-delivers the same event up to 10 times if the\n * receiver returns non-2xx. The host's `onOrderPaid` MUST be idempotent\n * — it is called per Stripe delivery, not per logical \"this charge\n * succeeded\" boundary. The recommended pattern: store the\n * `session.id` (or `event.id`) on first write and no-op on later\n * deliveries.\n */\nimport type Stripe from 'stripe';\nimport { getStripe } from '../stripe/client';\nimport type { CartStore } from '../cart/store';\nimport { buildOrderFromCheckoutSession } from './orders';\nimport { formatPaymentMethod } from './payment-method';\n\nexport interface OnOrderPaidArgs {\n  order: ReturnType<typeof buildOrderFromCheckoutSession>;\n  session: Stripe.Checkout.Session;\n  /** `acct_xxx` if this is a Connect event for a connected account, else null. */\n  connectedAccountId: string | null;\n  /** Discriminator from `session.metadata.kind` (default 'order'). */\n  kind: string;\n  /** Human label for the payment method (\"Visa •••• 4242\", \"MobilePay\"…) when expanded. */\n  paymentMethodLabel: string | null;\n  /** Stripe Event id — useful for the host's idempotency key. */\n  eventId: string;\n}\n\nexport interface WebhookHandlerOptions {\n  /** `whsec_…` from Stripe Dashboard. */\n  webhookSecret: string;\n  /** Stripe secret key — same one used elsewhere. */\n  secretKey?: string;\n  /** Cart store so we can look up the cart that produced the session. */\n  cartStore: CartStore;\n  /** Called when checkout.session.completed arrives with payment_status='paid'. */\n  onOrderPaid(args: OnOrderPaidArgs): Promise<void>;\n  /** Called on checkout.session.async_payment_failed or payment_intent.payment_failed. */\n  onOrderFailed?(args: {\n    sessionId?: string;\n    paymentIntentId?: string;\n    reason?: string;\n    connectedAccountId: string | null;\n    eventId: string;\n  }): Promise<void>;\n  /** Called on charge.refunded. */\n  onOrderRefunded?(args: {\n    paymentIntentId: string;\n    amountRefunded: number;\n    fullyRefunded: boolean;\n    connectedAccountId: string | null;\n    eventId: string;\n  }): Promise<void>;\n  /** Slug prefix for order numbers — default \"WH\". */\n  orderNumberPrefix?: string;\n  /** Called after a successful checkout to clear the cart. Default: true. */\n  clearCartOnPaid?: boolean;\n  /**\n   * Expand `payment_intent.latest_charge.payment_method_details` so we\n   * can label the payment method. Default: true. Disable for sites that\n   * don't need this and want to save one API call per webhook.\n   */\n  expandPaymentMethod?: boolean;\n}\n\nasync function expandPaymentMethodLabel(\n  stripe: Stripe,\n  session: Stripe.Checkout.Session,\n  connectedAccountId: string | null,\n): Promise<string | null> {\n  const paymentIntentId =\n    typeof session.payment_intent === 'string'\n      ? session.payment_intent\n      : session.payment_intent?.id;\n  if (!paymentIntentId) return null;\n  try {\n    const requestOpts = connectedAccountId\n      ? { stripeAccount: connectedAccountId }\n      : undefined;\n    const intent = await stripe.paymentIntents.retrieve(\n      paymentIntentId,\n      { expand: ['latest_charge.payment_method_details'] },\n      requestOpts,\n    );\n    const charge =\n      typeof intent.latest_charge === 'object' && intent.latest_charge\n        ? intent.latest_charge\n        : null;\n    return formatPaymentMethod(charge?.payment_method_details);\n  } catch {\n    return null;\n  }\n}\n\nexport function createStripeWebhookHandler(opts: WebhookHandlerOptions) {\n  const stripe = getStripe(opts.secretKey);\n  const clearCart = opts.clearCartOnPaid ?? true;\n  const expandPaymentMethod = opts.expandPaymentMethod ?? true;\n\n  return async function handle(req: Request): Promise<Response> {\n    const signature = req.headers.get('stripe-signature');\n    if (!signature) {\n      return new Response('missing stripe-signature', { status: 400 });\n    }\n\n    const rawBody = await req.text();\n\n    let event: Stripe.Event;\n    try {\n      event = stripe.webhooks.constructEvent(\n        rawBody,\n        signature,\n        opts.webhookSecret,\n      );\n    } catch (err) {\n      const message = err instanceof Error ? err.message : 'invalid signature';\n      // eslint-disable-next-line no-console\n      console.warn('[cms-shop] webhook signature failed:', message);\n      return new Response(`signature verification failed: ${message}`, {\n        status: 400,\n      });\n    }\n\n    const connectedAccountId = event.account ?? null;\n\n    try {\n      switch (event.type) {\n        case 'checkout.session.completed':\n        case 'checkout.session.async_payment_succeeded': {\n          const session = event.data.object as Stripe.Checkout.Session;\n          if (session.payment_status !== 'paid') {\n            // Async payment still pending — wait for the success event.\n            return new Response('pending', { status: 200 });\n          }\n          const cartId = session.metadata?.cms_cart_id;\n          if (!cartId) {\n            // eslint-disable-next-line no-console\n            console.warn(\n              '[cms-shop] checkout.session.completed missing cms_cart_id metadata',\n              session.id,\n            );\n            return new Response('ok', { status: 200 });\n          }\n          const cart = await opts.cartStore.get(cartId);\n          if (!cart) {\n            // eslint-disable-next-line no-console\n            console.warn(\n              `[cms-shop] cart ${cartId} not found for session ${session.id} — ` +\n                'cart may have been swept; the host should fall back to session line items',\n            );\n            return new Response('ok', { status: 200 });\n          }\n          const order = buildOrderFromCheckoutSession({\n            session,\n            cart,\n            ...(opts.orderNumberPrefix\n              ? { orderNumberPrefix: opts.orderNumberPrefix }\n              : {}),\n          });\n          const paymentMethodLabel = expandPaymentMethod\n            ? await expandPaymentMethodLabel(stripe, session, connectedAccountId)\n            : null;\n          await opts.onOrderPaid({\n            order,\n            session,\n            connectedAccountId,\n            kind: session.metadata?.kind ?? 'order',\n            paymentMethodLabel,\n            eventId: event.id,\n          });\n          if (clearCart) await opts.cartStore.delete(cartId);\n          return new Response('ok', { status: 200 });\n        }\n\n        case 'checkout.session.async_payment_failed':\n        case 'payment_intent.payment_failed': {\n          if (!opts.onOrderFailed) return new Response('ok', { status: 200 });\n          const obj = event.data.object as\n            | Stripe.Checkout.Session\n            | Stripe.PaymentIntent;\n          await opts.onOrderFailed({\n            ...('id' in obj && obj.object === 'checkout.session'\n              ? { sessionId: obj.id }\n              : {}),\n            ...('payment_intent' in obj && typeof obj.payment_intent === 'string'\n              ? { paymentIntentId: obj.payment_intent }\n              : 'object' in obj && obj.object === 'payment_intent'\n                ? { paymentIntentId: obj.id }\n                : {}),\n            ...('last_payment_error' in obj && obj.last_payment_error\n              ? { reason: obj.last_payment_error.message ?? 'failed' }\n              : {}),\n            connectedAccountId,\n            eventId: event.id,\n          });\n          return new Response('ok', { status: 200 });\n        }\n\n        case 'charge.refunded': {\n          if (!opts.onOrderRefunded) return new Response('ok', { status: 200 });\n          const charge = event.data.object as Stripe.Charge;\n          const paymentIntentId =\n            typeof charge.payment_intent === 'string'\n              ? charge.payment_intent\n              : charge.payment_intent?.id;\n          if (!paymentIntentId) return new Response('ok', { status: 200 });\n          await opts.onOrderRefunded({\n            paymentIntentId,\n            amountRefunded: charge.amount_refunded,\n            fullyRefunded: charge.amount_refunded >= charge.amount,\n            connectedAccountId,\n            eventId: event.id,\n          });\n          return new Response('ok', { status: 200 });\n        }\n\n        default:\n          return new Response('ignored', { status: 200 });\n      }\n    } catch (err) {\n      // eslint-disable-next-line no-console\n      console.error('[cms-shop] webhook handler error:', err);\n      // Return 500 so Stripe retries — better than swallowing failures.\n      return new Response('handler failed', { status: 500 });\n    }\n  };\n}\n","/**\n * F136 Phase 1 — Currency formatting helpers.\n *\n * Used by both server-side (storefront page renderer) and client-side\n * (cart island totals). Lives in /islands so islands can import without\n * pulling Stripe-SDK code into the browser bundle.\n */\nimport type { CurrencyCode, MoneyAmount } from '../types';\n\nconst FRACTIONLESS = new Set(['JPY', 'KRW', 'VND', 'IDR']);\n\nexport function formatMoney(\n  amount: MoneyAmount,\n  currency: CurrencyCode,\n  locale = 'da-DK',\n): string {\n  const cur = currency.toUpperCase();\n  const minor = FRACTIONLESS.has(cur) ? amount : amount / 100;\n  try {\n    return new Intl.NumberFormat(locale, {\n      style: 'currency',\n      currency: cur,\n      minimumFractionDigits: FRACTIONLESS.has(cur) ? 0 : 2,\n    }).format(minor);\n  } catch {\n    return `${minor.toFixed(2)} ${cur}`;\n  }\n}\n","/**\n * F136 Phase 1 — Server-side product page renderer.\n *\n * Returns an HTML fragment that includes the product card island mount\n * markup. Sites can plug this into their static page templates (Next.js\n * RSC, Astro, plain HTML build) without bringing in a UI framework.\n *\n * The returned HTML is intentionally minimal — we render the data, not\n * the design. Each block has a stable class prefix (`cms-shop-`) so\n * sites style with their own CSS.\n */\nimport { formatMoney } from '../islands/format';\nimport type { CurrencyCode, ShopProduct } from '../types';\n\nexport interface RenderProductOptions {\n  /** Currency to display the primary price in. Default: first key in priceByCurrency. */\n  currency?: CurrencyCode;\n  /** Locale for currency formatting. Default: 'da-DK'. */\n  locale?: string;\n  /** Optional translation map for UI strings. */\n  labels?: Partial<typeof DEFAULT_LABELS>;\n  /** Whether to include the `<script type=\"module\">` mount snippet. Default: false (host typically does this once). */\n  includeMountScript?: boolean;\n  /** Path to the islands JS bundle if includeMountScript is true. */\n  islandsScriptUrl?: string;\n}\n\nconst DEFAULT_LABELS = {\n  addToCart: 'Add to cart',\n  outOfStock: 'Out of stock',\n  inStock: 'In stock',\n  lowStock: 'Only a few left',\n};\n\nfunction escape(s: string): string {\n  return String(s)\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;');\n}\n\nfunction pickCurrency(\n  product: ShopProduct,\n  preferred?: CurrencyCode,\n): CurrencyCode | undefined {\n  const prices = product.data.priceByCurrency ?? {};\n  if (preferred && prices[preferred.toUpperCase()]) {\n    return preferred.toUpperCase();\n  }\n  const first = Object.keys(prices)[0];\n  return first ? first.toUpperCase() : undefined;\n}\n\nfunction stockBadge(product: ShopProduct, labels: typeof DEFAULT_LABELS): string {\n  const qty = product.data.stockQuantity;\n  const threshold = product.data.lowStockThreshold ?? 5;\n  if (typeof qty !== 'number') return '';\n  if (qty <= 0) {\n    return `<span class=\"cms-shop-stock cms-shop-stock-out\">${escape(labels.outOfStock)}</span>`;\n  }\n  if (qty <= threshold) {\n    return `<span class=\"cms-shop-stock cms-shop-stock-low\">${escape(labels.lowStock)}</span>`;\n  }\n  return `<span class=\"cms-shop-stock cms-shop-stock-ok\">${escape(labels.inStock)}</span>`;\n}\n\nexport function renderProductPage(\n  product: ShopProduct,\n  opts: RenderProductOptions = {},\n): string {\n  const labels = { ...DEFAULT_LABELS, ...(opts.labels ?? {}) };\n  const locale = opts.locale ?? 'da-DK';\n  const currency = pickCurrency(product, opts.currency);\n  const data = product.data;\n  const price =\n    currency !== undefined ? data.priceByCurrency?.[currency] : undefined;\n  const compareAt =\n    currency !== undefined\n      ? data.compareAtPriceByCurrency?.[currency]\n      : undefined;\n\n  const images = data.images ?? [];\n  const gallery = images\n    .map(\n      (src, i) =>\n        `<img class=\"cms-shop-product-img${i === 0 ? ' cms-shop-product-img-primary' : ''}\" src=\"${escape(src)}\" alt=\"${escape(data.title)}\">`,\n    )\n    .join('');\n\n  const priceHtml =\n    typeof price === 'number' && currency\n      ? `\n        <div class=\"cms-shop-product-price\">\n          <span class=\"cms-shop-price-current\">${escape(formatMoney(price, currency, locale))}</span>\n          ${\n            typeof compareAt === 'number' && compareAt > price\n              ? `<span class=\"cms-shop-price-compare\">${escape(formatMoney(compareAt, currency, locale))}</span>`\n              : ''\n          }\n        </div>`\n      : '';\n\n  const sellable =\n    currency &&\n    typeof price === 'number' &&\n    price > 0 &&\n    (data.stockQuantity ?? 1) > 0 &&\n    (data.status ?? 'active') === 'active';\n\n  const cardAttrs = [\n    `data-cms-shop=\"product-card\"`,\n    `data-product-id=\"${escape(product.id)}\"`,\n    currency ? `data-currency=\"${escape(currency)}\"` : '',\n  ]\n    .filter(Boolean)\n    .join(' ');\n\n  const buttonHtml = sellable\n    ? `<button type=\"button\" class=\"cms-shop-add\" data-cms-shop-add>${escape(labels.addToCart)}</button>`\n    : `<button type=\"button\" class=\"cms-shop-add\" disabled>${escape(labels.outOfStock)}</button>`;\n\n  const mountScript = opts.includeMountScript\n    ? `<script type=\"module\">\n  import { mountProductCards } from \"${escape(opts.islandsScriptUrl ?? '/_shop/islands.js')}\";\n  mountProductCards();\n</script>`\n    : '';\n\n  return `\n<article class=\"cms-shop-product\" ${cardAttrs}>\n  <div class=\"cms-shop-product-gallery\">${gallery}</div>\n  <div class=\"cms-shop-product-body\">\n    <h1 class=\"cms-shop-product-title\">${escape(data.title)}</h1>\n    ${data.shortDescription ? `<p class=\"cms-shop-product-short\">${escape(data.shortDescription)}</p>` : ''}\n    ${priceHtml}\n    ${stockBadge(product, labels)}\n    ${data.description ? `<div class=\"cms-shop-product-description\">${escape(data.description)}</div>` : ''}\n    ${buttonHtml}\n    <span class=\"cms-shop-product-status\" data-cms-shop-status></span>\n  </div>\n</article>\n${mountScript}\n`.trim();\n}\n\nexport interface RenderProductCardOptions extends RenderProductOptions {\n  /** URL for the product detail page. */\n  href?: string;\n}\n\n/** Compact card for listing pages. */\nexport function renderProductCard(\n  product: ShopProduct,\n  opts: RenderProductCardOptions = {},\n): string {\n  const labels = { ...DEFAULT_LABELS, ...(opts.labels ?? {}) };\n  const locale = opts.locale ?? 'da-DK';\n  const currency = pickCurrency(product, opts.currency);\n  const data = product.data;\n  const price =\n    currency !== undefined ? data.priceByCurrency?.[currency] : undefined;\n  const image = data.cardImageUrl ?? data.images?.[0];\n\n  const titleHtml = opts.href\n    ? `<a class=\"cms-shop-card-title\" href=\"${escape(opts.href)}\">${escape(data.title)}</a>`\n    : `<span class=\"cms-shop-card-title\">${escape(data.title)}</span>`;\n\n  return `\n<article class=\"cms-shop-card\" data-cms-shop=\"product-card\" data-product-id=\"${escape(product.id)}\" ${currency ? `data-currency=\"${escape(currency)}\"` : ''}>\n  ${image ? `<img class=\"cms-shop-card-img\" src=\"${escape(image)}\" alt=\"${escape(data.title)}\">` : ''}\n  ${titleHtml}\n  ${typeof price === 'number' && currency ? `<span class=\"cms-shop-card-price\">${escape(formatMoney(price, currency, locale))}</span>` : ''}\n  <button type=\"button\" class=\"cms-shop-card-add\" data-cms-shop-add>${escape(labels.addToCart)}</button>\n</article>\n`.trim();\n}\n","/**\n * F136 Phase 1 — Address validation (DK-first).\n *\n * Light validation that catches the obvious mistakes without becoming\n * a full address-resolution dependency. Country-specific patterns can\n * be added by later phases (or replaced with DAWA / PostNord lookup).\n */\nimport type { ShopAddress } from '../types';\n\nexport interface AddressValidationResult {\n  valid: boolean;\n  errors: Record<string, string>;\n}\n\nconst POSTAL_PATTERNS: Record<string, RegExp> = {\n  DK: /^\\d{4}$/,\n  SE: /^\\d{3}\\s?\\d{2}$/,\n  NO: /^\\d{4}$/,\n  DE: /^\\d{5}$/,\n  GB: /^[A-Z]{1,2}\\d[A-Z\\d]?\\s?\\d[A-Z]{2}$/i,\n  US: /^\\d{5}(-\\d{4})?$/,\n};\n\nexport function validateAddress(addr: Partial<ShopAddress>): AddressValidationResult {\n  const errors: Record<string, string> = {};\n  if (!addr.name?.trim()) errors['name'] = 'Name is required';\n  if (!addr.line1?.trim()) errors['line1'] = 'Address is required';\n  if (!addr.postalCode?.trim()) errors['postalCode'] = 'Postal code is required';\n  if (!addr.city?.trim()) errors['city'] = 'City is required';\n  if (!addr.country?.trim()) errors['country'] = 'Country is required';\n\n  if (addr.country && addr.postalCode) {\n    const pattern = POSTAL_PATTERNS[addr.country.toUpperCase()];\n    if (pattern && !pattern.test(addr.postalCode.trim())) {\n      errors['postalCode'] =\n        `Invalid postal code for ${addr.country.toUpperCase()}`;\n    }\n  }\n\n  return { valid: Object.keys(errors).length === 0, errors };\n}\n\n/**\n * Address autocomplete extension point. Phase 1 ships a no-op so the\n * import is stable; sites that need real lookup wire in DAWA (DK),\n * PostNord (SE/NO), or Google Places via this interface.\n */\nexport interface AddressAutocomplete {\n  /** Look up suggestions from a partial address string. */\n  suggest(query: string, country?: string): Promise<ShopAddress[]>;\n}\n\nexport const noopAutocomplete: AddressAutocomplete = {\n  async suggest() {\n    return [];\n  },\n};\n"],"mappings":"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","names":["import_cms","import_cms","import_cms","Stripe","readCookie","json","clearCart"]}