@skuio/storefront 0.6.1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/store.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/store/cursor.ts","../src/store/catalog.ts","../src/store/customers.ts","../src/store/rows.ts","../src/store/checkout.ts","../src/store/partners.ts","../src/store/events.ts","../src/store/inventory.ts","../src/store/orders.ts","../src/store/password.ts","../src/store/owners.ts","../src/store/sessions.ts","../src/db/driver.ts","../src/store/index.ts"],"sourcesContent":["/**\n * Opaque keyset cursors for the catalogue list endpoints.\n *\n * A cursor is base64 of a JSON tuple of the sort keys of the last row on the\n * page. It is opaque by contract: callers echo it back and never parse it.\n * Orders keep their own older two-part encoding — cursors already issued to\n * SKU.io are live watermarks, so that format must not change.\n */\n\nconst toBase64 = (raw: string): string => {\n const bytes = new TextEncoder().encode(raw)\n let binary = ''\n for (const b of bytes) binary += String.fromCharCode(b)\n return btoa(binary).replace(/=+$/, '')\n}\n\nconst fromBase64 = (encoded: string): string => {\n const binary = atob(encoded)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n return new TextDecoder().decode(bytes)\n}\n\nexport const encodeKeysetCursor = (parts: (string | number)[]): string => toBase64(JSON.stringify(parts))\n\n/** Null when the cursor was not issued by this store, so the caller can 422 rather than guess. */\nexport function decodeKeysetCursor(cursor: string, length: number): (string | number)[] | null {\n try {\n const parts: unknown = JSON.parse(fromBase64(cursor))\n if (!Array.isArray(parts) || parts.length !== length) return null\n if (!parts.every((p) => typeof p === 'string' || typeof p === 'number')) return null\n return parts as (string | number)[]\n } catch {\n return null\n }\n}\n","import { Catalog } from '../catalog'\nimport type { Database, DbStatement } from '../db/driver'\nimport { slugify } from '../slug'\nimport type { Availability, Category, Money, Product, ProductImage } from '../types'\nimport { chunk, parseJson, placeholders, type StoreContext } from './context'\nimport { decodeKeysetCursor, encodeKeysetCursor } from './cursor'\nimport { invalid, notFound } from './errors'\n\n/** Product payload as the Store API and the seed script send it. */\nexport interface ProductInput {\n sku: string\n name: string\n slug?: string | null\n description_html?: string | null\n short_description_html?: string | null\n price: Money\n compare_at_price?: Money | null\n images?: { url: string; alt?: string | null }[]\n category_ids?: string[]\n attributes?: Record<string, string>\n active?: boolean\n position?: number\n /** Not part of the Store API; a migrated store's seed sets it. */\n legacy_path?: string | null\n}\n\nexport interface CategoryInput {\n id: string\n name: string\n slug?: string | null\n parent_id?: string | null\n description_html?: string | null\n image?: { url: string; alt?: string | null } | null\n position?: number\n}\n\ninterface ProductRow {\n sku: string\n name: string\n slug: string\n description_html: string | null\n short_description_html: string | null\n price_amount: number\n currency: string\n compare_at_amount: number | null\n attributes_json: string\n active: number\n position: number\n legacy_path: string | null\n created_at: string\n updated_at: string\n}\n\ninterface CategoryRow {\n id: string\n name: string\n slug: string\n parent_id: string | null\n description_html: string | null\n image_url: string | null\n image_alt: string | null\n position: number\n created_at: string\n updated_at: string\n}\n\n/** One product as the Store API reads it back — the whole row, not the shopper-facing projection. */\nexport interface ProductRecord {\n sku: string\n name: string\n slug: string\n descriptionHtml: string | null\n shortDescriptionHtml: string | null\n price: Money\n compareAtPrice: Money | null\n attributes: Record<string, string>\n active: boolean\n position: number\n legacyPath: string | null\n /** In the product's own order. `slug`/`name` are null for a link to a category the store never received. */\n categories: { id: string; slug: string | null; name: string | null }[]\n images: { url: string; alt: string | null; position: number }[]\n inventory: { available: number; allowBackorder: boolean; updatedAt: string } | null\n createdAt: string\n updatedAt: string\n}\n\nexport interface CategoryRecord {\n id: string\n parentId: string | null\n slug: string\n name: string\n descriptionHtml: string | null\n position: number\n productCount: number\n createdAt: string\n updatedAt: string\n}\n\nexport interface ProductPage {\n products: ProductRecord[]\n nextCursor: string | null\n}\n\nexport interface CategoryPage {\n categories: CategoryRecord[]\n nextCursor: string | null\n}\n\nexport interface ProductListQuery {\n updatedSince?: string | null\n cursor?: string | null\n limit?: number\n /** Omit for every product; true/false to filter on the storefront flag. */\n active?: boolean | null\n}\n\nexport interface CategoryListQuery {\n cursor?: string | null\n limit?: number\n}\n\ninterface InventoryRow {\n sku: string\n available: number\n allow_backorder: number\n updated_at: string\n}\n\ninterface CacheEntry {\n expires: number\n catalog: Promise<Catalog>\n}\n\n// One snapshot per database binding per isolate, refreshed on a short TTL and\n// dropped by every write this isolate makes.\nconst cache = new WeakMap<Database, CacheEntry>()\n\nexport function deriveAvailability(inv: { available: number; allow_backorder: number; updated_at?: string } | undefined, lowStockThreshold: number): Availability | undefined {\n if (!inv) return undefined\n const status =\n inv.available <= 0 ? (inv.allow_backorder ? 'backorder' : 'out_of_stock') : inv.available <= lowStockThreshold ? 'low_stock' : 'in_stock'\n return { status, quantity: inv.available, ...(inv.updated_at ? { updatedAt: inv.updated_at } : {}) }\n}\n\nexport class CatalogService {\n constructor(private readonly ctx: StoreContext, private readonly ttlMs: number) {}\n\n /** The catalogue snapshot: active products with availability, and the category tree. */\n load(): Promise<Catalog> {\n const hit = cache.get(this.ctx.db)\n if (hit && hit.expires > Date.now()) return hit.catalog\n const catalog = this.build()\n if (this.ttlMs > 0) cache.set(this.ctx.db, { expires: Date.now() + this.ttlMs, catalog })\n catalog.catch(() => cache.delete(this.ctx.db))\n return catalog\n }\n\n invalidate(): void {\n cache.delete(this.ctx.db)\n }\n\n private async build(): Promise<Catalog> {\n const db = this.ctx.db\n const [products, categories, links, images, inventory] = await Promise.all([\n db.prepare(`SELECT * FROM products WHERE active = 1 ORDER BY position, name`).all<ProductRow>(),\n db.prepare(`SELECT * FROM categories ORDER BY position, name`).all<CategoryRow>(),\n db.prepare(`SELECT pc.sku, pc.category_id FROM product_categories pc JOIN products p ON p.sku = pc.sku WHERE p.active = 1 ORDER BY pc.position`).all<{ sku: string; category_id: string }>(),\n db.prepare(`SELECT pi.sku, pi.url, pi.alt FROM product_images pi JOIN products p ON p.sku = pi.sku WHERE p.active = 1 ORDER BY pi.position, pi.id`).all<{ sku: string; url: string; alt: string | null }>(),\n db.prepare(`SELECT * FROM inventory`).all<InventoryRow>(),\n ])\n const categoryIds = new Map<string, string[]>()\n for (const l of links.results) (categoryIds.get(l.sku) ?? categoryIds.set(l.sku, []).get(l.sku)!).push(l.category_id)\n const imagesBySku = new Map<string, ProductImage[]>()\n for (const i of images.results) (imagesBySku.get(i.sku) ?? imagesBySku.set(i.sku, []).get(i.sku)!).push({ src: i.url, ...(i.alt ? { alt: i.alt } : {}) })\n const inv = new Map(inventory.results.map((r) => [r.sku, r]))\n const threshold = this.ctx.config.inventory.lowStockThreshold\n\n const list: Product[] = products.results.map((r) => {\n const availability = deriveAvailability(inv.get(r.sku), threshold)\n return {\n id: r.sku,\n sku: r.sku,\n name: r.name,\n slug: r.slug,\n ...(r.description_html ? { description: r.description_html } : {}),\n ...(r.short_description_html ? { shortDescription: r.short_description_html } : {}),\n price: { amount: r.price_amount, currency: r.currency },\n ...(r.compare_at_amount != null ? { compareAtPrice: { amount: r.compare_at_amount, currency: r.currency } } : {}),\n categoryIds: categoryIds.get(r.sku) ?? [],\n images: imagesBySku.get(r.sku) ?? [],\n available: !availability || availability.status !== 'out_of_stock',\n ...(availability ? { availability } : {}),\n attributes: parseJson<Record<string, string>>(r.attributes_json, {}),\n position: r.position,\n ...(r.legacy_path ? { legacyPath: r.legacy_path } : {}),\n }\n })\n const cats: Category[] = categories.results.map((r) => ({\n id: r.id,\n name: r.name,\n slug: r.slug,\n parentId: r.parent_id,\n ...(r.description_html ? { description: r.description_html } : {}),\n ...(r.image_url ? { image: { src: r.image_url, ...(r.image_alt ? { alt: r.image_alt } : {}) } } : {}),\n position: r.position,\n }))\n return Catalog.fromData(list, cats)\n }\n\n async counts(): Promise<{ products: number; categories: number }> {\n const [p, c] = await Promise.all([\n this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM products WHERE active = 1`).first<{ n: number }>(),\n this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM categories`).first<{ n: number }>(),\n ])\n return { products: p?.n ?? 0, categories: c?.n ?? 0 }\n }\n\n /** Every product row including inactive ones — the Store API's view for `unknown_skus` checks. */\n async existingSkus(skus: string[]): Promise<Set<string>> {\n const found = new Set<string>()\n for (const part of chunk([...new Set(skus)])) {\n const { results } = await this.ctx.db\n .prepare(`SELECT sku FROM products WHERE sku IN (${placeholders(part.length)})`)\n .bind(...part)\n .all<{ sku: string }>()\n for (const r of results) found.add(r.sku)\n }\n return found\n }\n\n /**\n * Products in keyset order, for an operations system importing the catalogue.\n * Ordered by `(updated_at, sku)` so a cursor is strictly monotonic and a\n * re-run from a watermark never repeats or skips a row.\n */\n async listProducts(query: ProductListQuery = {}): Promise<ProductPage> {\n const size = Math.min(Math.max(Math.trunc(query.limit ?? 50) || 50, 1), 200)\n const where: string[] = []\n const args: (string | number)[] = []\n if (query.cursor) {\n const parts = decodeKeysetCursor(query.cursor, 2)\n if (!parts) throw invalid('Invalid cursor', { cursor: 'is not a cursor this store issued' })\n where.push(`(updated_at, sku) > (?, ?)`)\n args.push(String(parts[0]), String(parts[1]))\n } else if (query.updatedSince) {\n where.push(`updated_at >= ?`)\n args.push(query.updatedSince)\n }\n if (query.active != null) {\n where.push(`active = ?`)\n args.push(query.active ? 1 : 0)\n }\n const sql = `SELECT * FROM products${where.length ? ` WHERE ${where.join(' AND ')}` : ''} ORDER BY updated_at ASC, sku ASC LIMIT ?`\n const { results } = await this.ctx.db.prepare(sql).bind(...args, size + 1).all<ProductRow>()\n const rows = results.slice(0, size)\n const last = rows[rows.length - 1]\n return {\n products: await this.hydrateProducts(rows),\n nextCursor: results.length > size && last ? encodeKeysetCursor([last.updated_at, last.sku]) : null,\n }\n }\n\n /** One product by SKU, active or not. Throws `not_found` when the store has never held it. */\n async requireProduct(sku: string): Promise<ProductRecord> {\n const row = await this.ctx.db.prepare(`SELECT * FROM products WHERE sku = ?`).bind(sku).first<ProductRow>()\n if (!row) throw notFound('Product')\n const [product] = await this.hydrateProducts([row])\n return product!\n }\n\n /**\n * The category tree as a flat list, parents before children within a branch.\n * `COALESCE(parent_id, '')` keeps every sort key non-null so the keyset\n * comparison can never drop a root category.\n */\n async listCategories(query: CategoryListQuery = {}): Promise<CategoryPage> {\n const size = Math.min(Math.max(Math.trunc(query.limit ?? 500) || 500, 1), 1000)\n const where: string[] = []\n const args: (string | number)[] = []\n if (query.cursor) {\n const parts = decodeKeysetCursor(query.cursor, 3)\n if (!parts) throw invalid('Invalid cursor', { cursor: 'is not a cursor this store issued' })\n where.push(`(COALESCE(c.parent_id, ''), c.position, c.id) > (?, ?, ?)`)\n args.push(String(parts[0]), Number(parts[1]), String(parts[2]))\n }\n const sql = `SELECT c.*, (SELECT COUNT(*) FROM product_categories pc WHERE pc.category_id = c.id) AS product_count\n FROM categories c${where.length ? ` WHERE ${where.join(' AND ')}` : ''}\n ORDER BY COALESCE(c.parent_id, '') ASC, c.position ASC, c.id ASC LIMIT ?`\n const { results } = await this.ctx.db.prepare(sql).bind(...args, size + 1).all<CategoryRow & { product_count: number }>()\n const rows = results.slice(0, size)\n const last = rows[rows.length - 1]\n return {\n categories: rows.map((r) => ({\n id: r.id,\n parentId: r.parent_id,\n slug: r.slug,\n name: r.name,\n descriptionHtml: r.description_html,\n position: r.position,\n productCount: r.product_count,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n })),\n nextCursor: results.length > size && last ? encodeKeysetCursor([last.parent_id ?? '', last.position, last.id]) : null,\n }\n }\n\n /** Attach categories, images and stock to a page of product rows. */\n private async hydrateProducts(rows: ProductRow[]): Promise<ProductRecord[]> {\n if (rows.length === 0) return []\n const skus = rows.map((r) => r.sku)\n const links = new Map<string, { id: string; slug: string | null; name: string | null }[]>()\n const images = new Map<string, { url: string; alt: string | null; position: number }[]>()\n const inventory = new Map<string, InventoryRow>()\n for (const part of chunk(skus)) {\n const ph = placeholders(part.length)\n const [l, i, inv] = await Promise.all([\n // LEFT JOIN: a link to a category the store never received still reports its id.\n this.ctx.db\n .prepare(`SELECT pc.sku, pc.category_id, c.slug, c.name FROM product_categories pc LEFT JOIN categories c ON c.id = pc.category_id WHERE pc.sku IN (${ph}) ORDER BY pc.position, pc.category_id`)\n .bind(...part)\n .all<{ sku: string; category_id: string; slug: string | null; name: string | null }>(),\n this.ctx.db\n .prepare(`SELECT sku, url, alt, position FROM product_images WHERE sku IN (${ph}) ORDER BY position, id`)\n .bind(...part)\n .all<{ sku: string; url: string; alt: string | null; position: number }>(),\n this.ctx.db.prepare(`SELECT * FROM inventory WHERE sku IN (${ph})`).bind(...part).all<InventoryRow>(),\n ])\n for (const r of l.results) (links.get(r.sku) ?? links.set(r.sku, []).get(r.sku)!).push({ id: r.category_id, slug: r.slug, name: r.name })\n for (const r of i.results) (images.get(r.sku) ?? images.set(r.sku, []).get(r.sku)!).push({ url: r.url, alt: r.alt, position: r.position })\n for (const r of inv.results) inventory.set(r.sku, r)\n }\n return rows.map((r) => {\n const stock = inventory.get(r.sku)\n return {\n sku: r.sku,\n name: r.name,\n slug: r.slug,\n descriptionHtml: r.description_html,\n shortDescriptionHtml: r.short_description_html,\n price: { amount: r.price_amount, currency: r.currency },\n compareAtPrice: r.compare_at_amount != null ? { amount: r.compare_at_amount, currency: r.currency } : null,\n attributes: parseJson<Record<string, string>>(r.attributes_json, {}),\n active: r.active === 1,\n position: r.position,\n legacyPath: r.legacy_path,\n categories: links.get(r.sku) ?? [],\n images: images.get(r.sku) ?? [],\n inventory: stock ? { available: stock.available, allowBackorder: stock.allow_backorder === 1, updatedAt: stock.updated_at } : null,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n }\n })\n }\n\n /**\n * Upsert products. Slugs: an explicit slug wins; an existing product keeps\n * its slug; a new one gets a slugified name, suffixed with the SKU when taken.\n */\n async upsertProducts(inputs: ProductInput[]): Promise<number> {\n if (inputs.length === 0) return 0\n const db = this.ctx.db\n const now = this.ctx.now()\n const skus = inputs.map((p) => p.sku)\n const existing = new Map<string, string>()\n for (const part of chunk(skus)) {\n const { results } = await db.prepare(`SELECT sku, slug FROM products WHERE sku IN (${placeholders(part.length)})`).bind(...part).all<{ sku: string; slug: string }>()\n for (const r of results) existing.set(r.sku, r.slug)\n }\n // Slug ownership across the whole table, so a new product never steals a URL.\n const wanted = inputs.map((p) => (p.slug?.trim() || existing.get(p.sku) || slugify(p.name)))\n const owners = new Map<string, string>()\n for (const part of chunk([...new Set(wanted)])) {\n const { results } = await db.prepare(`SELECT sku, slug FROM products WHERE slug IN (${placeholders(part.length)})`).bind(...part).all<{ sku: string; slug: string }>()\n for (const r of results) owners.set(r.slug, r.sku)\n }\n const taken = new Set<string>()\n const statements: DbStatement[] = []\n inputs.forEach((p, i) => {\n let slug = wanted[i]!\n const owner = owners.get(slug)\n if ((owner && owner !== p.sku) || taken.has(slug)) slug = `${slug}-${slugify(p.sku)}`\n taken.add(slug)\n if (p.price.currency.toLowerCase() !== this.ctx.config.currency.toLowerCase()) {\n throw invalid(`Product ${p.sku} is priced in ${p.price.currency}; this store sells in ${this.ctx.config.currency}`, { [`products.${i}.price.currency`]: 'must match the store currency' })\n }\n statements.push(\n db\n .prepare(\n `INSERT INTO products (sku, name, slug, description_html, short_description_html, price_amount, currency, compare_at_amount, attributes_json, active, position, legacy_path, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (sku) DO UPDATE SET\n name = excluded.name, slug = excluded.slug, description_html = excluded.description_html,\n short_description_html = excluded.short_description_html, price_amount = excluded.price_amount,\n currency = excluded.currency, compare_at_amount = excluded.compare_at_amount, attributes_json = excluded.attributes_json,\n active = excluded.active, position = excluded.position,\n legacy_path = COALESCE(excluded.legacy_path, products.legacy_path), updated_at = excluded.updated_at`\n )\n .bind(\n p.sku,\n p.name,\n slug,\n p.description_html ?? null,\n p.short_description_html ?? null,\n p.price.amount,\n p.price.currency.toLowerCase(),\n p.compare_at_price?.amount ?? null,\n JSON.stringify(p.attributes ?? {}),\n p.active === false ? 0 : 1,\n p.position ?? i,\n p.legacy_path ?? null,\n now,\n now\n )\n )\n statements.push(db.prepare(`DELETE FROM product_categories WHERE sku = ?`).bind(p.sku))\n ;[...new Set(p.category_ids ?? [])].forEach((categoryId, pos) => {\n statements.push(db.prepare(`INSERT INTO product_categories (sku, category_id, position) VALUES (?, ?, ?)`).bind(p.sku, categoryId, pos))\n })\n statements.push(db.prepare(`DELETE FROM product_images WHERE sku = ?`).bind(p.sku))\n ;(p.images ?? []).forEach((img, pos) => {\n statements.push(db.prepare(`INSERT INTO product_images (sku, url, alt, position) VALUES (?, ?, ?, ?)`).bind(p.sku, img.url, img.alt ?? null, pos))\n })\n })\n for (const part of chunk(statements, 250)) await db.batch(part)\n this.invalidate()\n return inputs.length\n }\n\n /**\n * Upsert categories, migration-safe. A category is identified by its id;\n * when the id is unknown but a category with the same slug under the same\n * parent already exists, that row is ADOPTED (re-keyed to the incoming id,\n * with its products and children moved along) instead of a suffixed\n * duplicate being created. When the id is known and a same-parent row owns\n * the natural slug, that duplicate is merged into it. A slug owned by a\n * category under a different parent, or claimed twice in one batch, gets a\n * suffix. Parents in the batch are resolved first, so a whole tree pushed\n * with new ids over a seeded tree lands on the existing nodes.\n */\n async upsertCategories(inputs: CategoryInput[]): Promise<number> {\n if (inputs.length === 0) return 0\n const db = this.ctx.db\n const now = this.ctx.now()\n\n // The table is small; resolve everything against one snapshot kept in step with the statements.\n const { results } = await db.prepare(`SELECT id, slug, parent_id FROM categories`).all<{ id: string; slug: string; parent_id: string | null }>()\n const rows = new Map(results.map((r) => [r.id, { id: r.id, slug: r.slug, parentId: r.parent_id }]))\n const bySlug = new Map(results.map((r) => [r.slug, r.id]))\n const batchIds = new Set(inputs.map((c) => c.id))\n const statements: DbStatement[] = []\n\n const reparent = (from: string, to: string) => {\n for (const r of rows.values()) if (r.parentId === from) r.parentId = to\n }\n /** Move the products and children of `from` onto `to`. */\n const moveRefs = (from: string, to: string) => {\n statements.push(db.prepare(`UPDATE OR IGNORE product_categories SET category_id = ? WHERE category_id = ?`).bind(to, from))\n statements.push(db.prepare(`DELETE FROM product_categories WHERE category_id = ?`).bind(from))\n statements.push(db.prepare(`UPDATE categories SET parent_id = ? WHERE parent_id = ?`).bind(to, from))\n reparent(from, to)\n }\n /** Unknown incoming id: the existing same-parent row becomes the incoming id. */\n const adopt = (existingId: string, incomingId: string) => {\n const row = rows.get(existingId)!\n statements.push(db.prepare(`UPDATE categories SET id = ? WHERE id = ?`).bind(incomingId, existingId))\n moveRefs(existingId, incomingId)\n rows.delete(existingId)\n rows.set(incomingId, { ...row, id: incomingId })\n bySlug.set(row.slug, incomingId)\n }\n /** Known incoming id: the same-parent duplicate is absorbed into it and deleted. */\n const merge = (duplicateId: string, intoId: string) => {\n const dup = rows.get(duplicateId)!\n moveRefs(duplicateId, intoId)\n statements.push(db.prepare(`DELETE FROM categories WHERE id = ?`).bind(duplicateId))\n rows.delete(duplicateId)\n bySlug.delete(dup.slug)\n }\n const sameParent = (ownerId: string, parentId: string | null) => (rows.get(ownerId)?.parentId ?? null) === parentId\n const freeSuffixed = (natural: string, id: string) => {\n let candidate = `${natural}-${slugify(id)}`\n for (let n = 2; bySlug.has(candidate); n++) candidate = `${natural}-${slugify(id)}-${n}`\n return candidate\n }\n\n for (const c of orderParentsFirst(inputs)) {\n const parentId = c.parent_id ?? null\n const existing = rows.get(c.id)\n const explicit = c.slug?.trim() || null\n const natural = explicit ?? slugify(c.name)\n let slug: string\n if (existing && !explicit) {\n // Slugs stay stable once set — unless a same-parent duplicate owns the natural one, which is healed by merging.\n const owner = bySlug.get(natural)\n if (owner && owner !== c.id && sameParent(owner, parentId) && !batchIds.has(owner)) {\n merge(owner, c.id)\n bySlug.delete(existing.slug)\n slug = natural\n } else {\n slug = existing.slug\n }\n } else {\n const owner = bySlug.get(natural)\n if (!owner || owner === c.id) {\n slug = natural\n } else if (sameParent(owner, parentId) && !batchIds.has(owner)) {\n if (existing) {\n merge(owner, c.id)\n bySlug.delete(existing.slug)\n } else {\n adopt(owner, c.id)\n }\n slug = natural\n } else {\n slug = existing?.slug === natural ? natural : freeSuffixed(natural, c.id)\n if (existing) bySlug.delete(existing.slug)\n }\n }\n statements.push(\n db\n .prepare(\n `INSERT INTO categories (id, name, slug, parent_id, description_html, image_url, image_alt, position, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n name = excluded.name, slug = excluded.slug, parent_id = excluded.parent_id, description_html = excluded.description_html,\n image_url = excluded.image_url, image_alt = excluded.image_alt, position = excluded.position, updated_at = excluded.updated_at`\n )\n .bind(c.id, c.name, slug, parentId, c.description_html ?? null, c.image?.url ?? null, c.image?.alt ?? null, c.position ?? inputs.indexOf(c), now, now)\n )\n rows.set(c.id, { id: c.id, slug, parentId })\n bySlug.set(slug, c.id)\n }\n for (const part of chunk(statements, 250)) await db.batch(part)\n this.invalidate()\n return inputs.length\n }\n\n /** Deactivate: the product disappears from the store but stays for order history. */\n async deactivateProduct(sku: string): Promise<boolean> {\n const result = await this.ctx.db.prepare(`UPDATE products SET active = 0, updated_at = ? WHERE sku = ?`).bind(this.ctx.now(), sku).run()\n this.invalidate()\n return (result.meta?.changes ?? 0) > 0\n }\n}\n\n/** Parents before children, so an adopted parent is keyed to its incoming id before its children look it up. */\nfunction orderParentsFirst(inputs: CategoryInput[]): CategoryInput[] {\n const pending = new Map(inputs.map((c) => [c.id, c]))\n const done = new Set<string>()\n const out: CategoryInput[] = []\n let progressed = true\n while (pending.size && progressed) {\n progressed = false\n for (const [id, c] of pending) {\n const parent = c.parent_id ?? null\n if (parent === null || !pending.has(parent) || done.has(parent)) {\n out.push(c)\n done.add(id)\n pending.delete(id)\n progressed = true\n }\n }\n }\n // A cycle is an inconsistent payload; take the rest in the order given.\n out.push(...pending.values())\n return out\n}\n","import type { Customer } from '../types'\nimport type { StoreContext } from './context'\n\ninterface CustomerRow {\n id: string\n email: string\n name: string | null\n phone: string | null\n created_at: string\n updated_at: string\n}\n\nconst toCustomer = (r: CustomerRow): Customer => ({\n id: r.id,\n email: r.email,\n name: r.name,\n phone: r.phone,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n})\n\nexport const normalizeEmail = (email: string) => email.trim().toLowerCase()\n\nexport class CustomersService {\n constructor(private readonly ctx: StoreContext) {}\n\n async byId(id: string): Promise<Customer | null> {\n const row = await this.ctx.db.prepare(`SELECT * FROM customers WHERE id = ?`).bind(id).first<CustomerRow>()\n return row ? toCustomer(row) : null\n }\n\n async byEmail(email: string): Promise<Customer | null> {\n const row = await this.ctx.db.prepare(`SELECT * FROM customers WHERE email = ?`).bind(normalizeEmail(email)).first<CustomerRow>()\n return row ? toCustomer(row) : null\n }\n\n async count(): Promise<number> {\n const row = await this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM customers`).first<{ n: number }>()\n return row?.n ?? 0\n }\n}\n","import type { Address, Fulfillment, Order, OrderLine, OrderStatus, PaymentStatus } from '../types'\nimport { parseJson } from './context'\n\nexport interface OrderRow {\n id: string\n number: number\n status: string\n payment_status: string\n payment_provider: string | null\n payment_reference: string | null\n payment_method_label: string | null\n paid_at: string | null\n currency: string\n subtotal: number\n shipping: number\n tax: number\n discount: number\n total: number\n customer_id: string\n email: string\n customer_name: string | null\n customer_phone: string | null\n shipping_address_json: string\n billing_address_json: string | null\n shipping_method_code: string | null\n shipping_method_name: string | null\n external_reference: string | null\n notes: string | null\n metadata_json: string\n access_key: string\n created_at: string\n updated_at: string\n cancelled_at: string | null\n discount_code: string | null\n discount_partner: string | null\n partner_commission: number\n}\n\nexport interface OrderLineRow {\n id: string\n order_id: string\n sku: string\n name: string\n quantity: number\n unit_amount: number\n total_amount: number\n fulfilled_quantity: number\n image_url: string | null\n position: number\n}\n\nexport interface FulfillmentRow {\n id: string\n order_id: string\n external_id: string\n carrier: string | null\n service: string | null\n tracking_number: string | null\n tracking_url: string | null\n shipped_at: string | null\n notified_at: string | null\n created_at: string\n}\n\nexport interface FulfillmentLineRow {\n fulfillment_id: string\n sku: string\n quantity: number\n}\n\nexport const ORDER_SELECT = `SELECT o.*, c.name AS customer_name, c.phone AS customer_phone FROM orders o JOIN customers c ON c.id = o.customer_id`\n\n/** Strip undefined so an address stored as JSON round-trips cleanly. */\nexport function cleanAddress(a: Address | null | undefined): Address | null {\n if (!a) return null\n const out: Address = {}\n for (const key of ['name', 'line1', 'line2', 'city', 'state', 'postalCode', 'country'] as const) {\n const v = a[key]\n if (typeof v === 'string' && v.trim()) out[key] = v.trim()\n }\n return out\n}\n\nexport function rowToLine(r: OrderLineRow, currency: string): OrderLine {\n return {\n id: r.id,\n sku: r.sku,\n name: r.name,\n quantity: r.quantity,\n unitAmount: { amount: r.unit_amount, currency },\n totalAmount: { amount: r.total_amount, currency },\n fulfilledQuantity: r.fulfilled_quantity,\n ...(r.image_url ? { image: r.image_url } : {}),\n }\n}\n\nexport function rowToFulfillment(r: FulfillmentRow, lines: FulfillmentLineRow[]): Fulfillment {\n return {\n id: r.id,\n externalId: r.external_id,\n carrier: r.carrier,\n service: r.service,\n trackingNumber: r.tracking_number,\n trackingUrl: r.tracking_url,\n shippedAt: r.shipped_at,\n lines: lines.map((l) => ({ sku: l.sku, quantity: l.quantity })),\n createdAt: r.created_at,\n }\n}\n\nexport function rowToOrder(r: OrderRow, lines: OrderLineRow[], fulfillments: Fulfillment[]): Order {\n const money = (amount: number) => ({ amount, currency: r.currency })\n return {\n id: r.id,\n number: String(r.number),\n status: r.status as OrderStatus,\n payment: {\n status: r.payment_status as PaymentStatus,\n provider: r.payment_provider,\n reference: r.payment_reference,\n methodLabel: r.payment_method_label,\n paidAt: r.paid_at,\n },\n currency: r.currency,\n subtotal: money(r.subtotal),\n shipping: money(r.shipping),\n tax: money(r.tax),\n discount: money(r.discount),\n discountCode: r.discount_code ?? null,\n partner: r.discount_partner ? { id: r.discount_partner, commission: money(r.partner_commission ?? 0) } : null,\n total: money(r.total),\n customer: { id: r.customer_id, email: r.email, name: r.customer_name, phone: r.customer_phone },\n shippingAddress: parseJson<Address>(r.shipping_address_json, {}),\n billingAddress: r.billing_address_json ? parseJson<Address | null>(r.billing_address_json, null) : null,\n shippingMethod: r.shipping_method_code && r.shipping_method_name ? { code: r.shipping_method_code, name: r.shipping_method_name } : null,\n lines: lines.map((l) => rowToLine(l, r.currency)),\n fulfillments,\n externalReference: r.external_reference,\n notes: r.notes,\n metadata: parseJson<Record<string, string>>(r.metadata_json, {}),\n accessKey: r.access_key,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n paidAt: r.paid_at,\n cancelledAt: r.cancelled_at,\n }\n}\n","import { checkAddressFormat } from '../address'\nimport { MAX_CART_LINES } from '../cart'\nimport { commissionAmount, discountAmount, findDiscount, normalizeDiscountCode, quantityTier, type DiscountCode, type QuantityDiscount } from '../discounts'\nimport type { DbStatement } from '../db/driver'\nimport { regionForCountry, shippingForRegion, type ShippingOption } from '../shipping'\nimport type { Address, Order } from '../types'\nimport type { CatalogService } from './catalog'\nimport type { StoreContext } from './context'\nimport { newId, randomToken } from './crypto'\nimport { normalizeEmail } from './customers'\nimport { invalid } from './errors'\nimport type { EventsService } from './events'\nimport type { OrdersService } from './orders'\nimport { cleanAddress } from './rows'\n\nexport interface CheckoutItem {\n sku: string\n quantity: number\n}\n\nexport interface CheckoutInput {\n items: CheckoutItem[]\n email: string\n name?: string | null\n phone?: string | null\n shippingAddress: Address\n billingAddress?: Address | null\n shippingOptionId: string\n /** A discount code the buyer entered or arrived with; validated against the store's configuration. */\n discountCode?: string | null\n notes?: string | null\n metadata?: Record<string, string>\n /**\n * The contact and address came from a wallet (PayPal express): the buyer typed nothing, so a phone\n * the store would normally require is optional — wallets share one only if the buyer allows it.\n */\n walletContact?: boolean\n}\n\nexport interface PricedLine {\n sku: string\n name: string\n quantity: number\n unitAmount: number\n totalAmount: number\n image: string | null\n}\n\nexport interface Quote {\n lines: PricedLine[]\n subtotal: number\n /** Minor units off the merchandise subtotal; 0 without a code. */\n discount: number\n /** The code the buyer used, or null. Kept for attribution even when a quantity tier saved more. */\n discountCode: DiscountCode | null\n /** The quantity tier that priced `discount` instead of the code, or null. */\n quantityTier: QuantityDiscount | null\n /** What the code's partner is owed for this order, minor units; 0 without a partner. */\n commission: number\n shipping: number\n total: number\n currency: string\n option: ShippingOption\n}\n\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport class CheckoutService {\n constructor(\n private readonly ctx: StoreContext,\n private readonly catalog: CatalogService,\n private readonly orders: OrdersService,\n private readonly events: EventsService\n ) {}\n\n /** Shipping options a destination may choose from. */\n optionsForCountry(country: string): ShippingOption[] {\n const region = regionForCountry(this.ctx.config.shipping, this.ctx.config.shipToCountries, country)\n return region ? shippingForRegion(this.ctx.config.shipping, this.ctx.config.shipToCountries, region).options : []\n }\n\n /**\n * Price a set of lines from the catalogue. Quantities are clamped to the\n * store's per-line maximum; unknown or unavailable SKUs fail validation.\n */\n async quote(items: CheckoutItem[], country: string, shippingOptionId: string, discountCode?: string | null): Promise<Quote> {\n const details: Record<string, string> = {}\n if (!items.length) details.items = 'is empty'\n if (items.length > MAX_CART_LINES) details.items = `has more than ${MAX_CART_LINES} lines`\n const catalog = await this.catalog.load()\n const merged = new Map<string, number>()\n for (const item of items) merged.set(item.sku, (merged.get(item.sku) ?? 0) + clampQuantity(item.quantity, this.ctx.config.checkout.maxQuantity))\n const lines: PricedLine[] = []\n for (const [sku, quantity] of merged) {\n const product = catalog.product(sku)\n if (!product) {\n details[`items.${sku}`] = 'is not a product in this store'\n continue\n }\n if (!product.available) {\n details[`items.${sku}`] = 'is sold out'\n continue\n }\n if (product.price.currency.toLowerCase() !== this.ctx.config.currency.toLowerCase()) {\n details[`items.${sku}`] = 'is priced in another currency'\n continue\n }\n const q = clampQuantity(quantity, this.ctx.config.checkout.maxQuantity)\n lines.push({ sku, name: product.name, quantity: q, unitAmount: product.price.amount, totalAmount: product.price.amount * q, image: product.images[0]?.src ?? null })\n }\n const options = this.optionsForCountry(country)\n if (!options.length) details['shipping_address.country'] = 'is not a country this store ships to'\n const option = options.find((o) => o.id === shippingOptionId)\n if (options.length && !option) details.shipping_option = 'is not available for this destination'\n // A code the buyer sees as applied must still be valid when the order is priced; never drop it silently.\n const code = discountCode && normalizeDiscountCode(discountCode) ? findDiscount(this.ctx.config.discounts, discountCode, new Date(this.ctx.now())) : null\n if (discountCode && normalizeDiscountCode(discountCode) && !code) details.discount_code = 'is not a valid code'\n if (Object.keys(details).length || !option) throw invalid('Checkout could not be priced', details)\n const subtotal = lines.reduce((sum, l) => sum + l.totalAmount, 0)\n const codeOff = code ? discountAmount(code, subtotal) : 0\n const tier = quantityTier(this.ctx.config.quantityDiscounts, lines.reduce((n, l) => n + l.quantity, 0))\n const tierOff = tier ? Math.min(subtotal, Math.round((subtotal * tier.percentOff) / 100)) : 0\n // Never stacked: the larger saving wins. The code still attributes the order either way.\n const discount = Math.max(codeOff, tierOff)\n const commission = code ? commissionAmount(code, subtotal, discount) : 0\n return {\n lines,\n subtotal,\n discount,\n discountCode: code,\n quantityTier: tierOff > codeOff ? tier : null,\n commission,\n shipping: option.price.amount,\n total: subtotal - discount + option.price.amount,\n currency: this.ctx.config.currency,\n option,\n }\n }\n\n /**\n * Price items for a destination using its first shipping option, for flows where the buyer never\n * picks one (express checkout: the address arrives from PayPal). Same rules and errors as quote().\n */\n async quoteDefault(items: CheckoutItem[], country: string, discountCode?: string | null): Promise<Quote> {\n return this.quote(items, country, this.optionsForCountry(country)[0]?.id ?? '', discountCode)\n }\n\n /** Create a pending order the payment adapter can take from here. */\n async createOrder(input: CheckoutInput): Promise<Order> {\n const details: Record<string, string> = {}\n const email = normalizeEmail(input.email ?? '')\n if (!EMAIL.test(email)) details.email = 'must be a valid email address'\n const shipTo = cleanAddress(input.shippingAddress) ?? {}\n if (!shipTo.name) details['shipping_address.name'] = 'is required'\n if (!shipTo.line1) details['shipping_address.line1'] = 'is required'\n if (!shipTo.city) details['shipping_address.city'] = 'is required'\n if (!shipTo.country || shipTo.country.length !== 2) details['shipping_address.country'] = 'is required'\n else shipTo.country = shipTo.country.toUpperCase()\n if (shipTo.country) Object.assign(details, checkAddressFormat(shipTo))\n if (this.ctx.config.checkout.collectPhone && !input.walletContact && !input.phone?.trim()) details.phone = 'is required'\n let quote: Quote | null = null\n try {\n quote = await this.quote(input.items, shipTo.country ?? '', input.shippingOptionId, input.discountCode)\n } catch (error) {\n if (error instanceof Error && 'details' in error) Object.assign(details, (error as { details: Record<string, string> | null }).details ?? {})\n else throw error\n }\n if (Object.keys(details).length || !quote) throw invalid('Checkout is incomplete', details)\n\n const db = this.ctx.db\n const now = this.ctx.now()\n const orderId = newId('ord')\n const name = input.name?.trim() || shipTo.name || null\n const phone = input.phone?.trim() || null\n const metadata = Object.fromEntries(Object.entries(input.metadata ?? {}).filter(([, v]) => typeof v === 'string').slice(0, 20))\n const statements: DbStatement[] = [\n db\n .prepare(\n `INSERT INTO customers (id, email, name, phone, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT (email) DO UPDATE SET name = COALESCE(excluded.name, customers.name), phone = COALESCE(excluded.phone, customers.phone), updated_at = excluded.updated_at`\n )\n .bind(newId('cus'), email, name, phone, now, now),\n db\n .prepare(\n `INSERT INTO orders (id, number, status, payment_status, currency, subtotal, shipping, tax, discount, total, customer_id, email,\n shipping_address_json, billing_address_json, shipping_method_code, shipping_method_name, notes, metadata_json, access_key, created_at, updated_at,\n discount_code, discount_partner, partner_commission)\n VALUES (?, (SELECT MAX(COALESCE(MAX(number), 0) + 1, ?) FROM orders), 'pending_payment', 'unpaid', ?, ?, ?, 0, ?, ?, (SELECT id FROM customers WHERE email = ?), ?,\n ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .bind(\n orderId,\n this.ctx.config.orders.numberStart,\n quote.currency,\n quote.subtotal,\n quote.shipping,\n quote.discount,\n quote.total,\n email,\n email,\n JSON.stringify(shipTo),\n input.billingAddress ? JSON.stringify(cleanAddress(input.billingAddress)) : null,\n quote.option.id,\n quote.option.label,\n input.notes?.trim().slice(0, 2000) || null,\n JSON.stringify(metadata),\n randomToken(16),\n now,\n now,\n quote.discountCode ? normalizeDiscountCode(quote.discountCode.code) : null,\n quote.discountCode?.partner ?? null,\n quote.commission\n ),\n ...quote.lines.map((l, i) =>\n db\n .prepare(`INSERT INTO order_lines (id, order_id, sku, name, quantity, unit_amount, total_amount, fulfilled_quantity, image_url, position) VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?)`)\n .bind(newId('lin'), orderId, l.sku, l.name, l.quantity, l.unitAmount, l.totalAmount, l.image, i)\n ),\n this.events.statement(orderId, 'created', {\n lines: quote.lines.length,\n total: quote.total,\n ...(quote.discountCode ? { discount_code: normalizeDiscountCode(quote.discountCode.code) } : {}),\n ...(quote.quantityTier ? { quantity_tier: quote.quantityTier.minQuantity } : {}),\n ...(quote.discount ? { discount: quote.discount } : {}),\n }),\n ]\n await db.batch(statements)\n return this.orders.require(orderId)\n }\n}\n\nfunction clampQuantity(n: number, max: number): number {\n return Number.isFinite(n) ? Math.min(Math.max(Math.trunc(n), 1), max) : 1\n}\n","import { findDiscount, normalizeDiscountCode, publicDiscount, type PartnerProfile, type PublicDiscount } from '../discounts'\nimport { partnerSignInMail } from '../mail/templates'\nimport type { Money } from '../types'\nimport { parseJson, type StoreContext } from './context'\nimport { hashToken, randomToken, secretsEqual, sha256Hex } from './crypto'\nimport { StoreError } from './errors'\n\nexport const PARTNER_LINK_MINUTES = 15\nexport const PARTNER_SESSION_DAYS = 30\n/** A second visit from the same visitor within this window is the same visit. */\nconst VISIT_DEDUPE_MINUTES = 30\n/**\n * Fetchers that open a link without a person behind them: link previews (a link pasted in\n * WhatsApp, X, Slack, Discord, Telegram or iMessage is fetched once to draw its card), search\n * crawlers and scripts. Counting them would credit a partner with a \"visit\" for every chat the\n * link is pasted into. In-app browsers (Instagram, Facebook's FBAN) are people and do not match.\n */\nconst NOT_A_PERSON = /bot\\b|bot\\/|crawl|spider|slurp|preview|facebookexternalhit|meta-externalagent|whatsapp|telegram|slack|discord|skypeuripreview|embedly|vkshare|pinterest\\/|bitlybot|headless|curl\\/|wget|python-requests|go-http-client|node-fetch|axios/i\n\n/** A user agent that belongs to a link preview, crawler or script rather than a person. */\nexport function isAutomatedVisit(userAgent: string | null | undefined): boolean {\n return !userAgent || NOT_A_PERSON.test(userAgent)\n}\n\nexport interface VisitInput {\n partner: string\n code: string\n /** Used only to derive the daily visitor hash; never stored. */\n ip: string | null\n userAgent: string | null\n source?: string | null\n medium?: string | null\n campaign?: string | null\n landingPath?: string | null\n country?: string | null\n}\n\nexport interface TrafficDay {\n day: string\n visits: number\n visitors: number\n checkouts: number\n orders: number\n}\n\nexport interface TrafficSource {\n source: string\n visits: number\n visitors: number\n orders: number\n sales: Money\n}\n\nexport interface PartnerTraffic {\n days: number\n totals: { visits: number; visitors: number; checkouts: number; orders: number; conversion: number; sales: Money; commission: Money }\n byDay: TrafficDay[]\n bySource: TrafficSource[]\n byCountry: { country: string; visits: number }[]\n}\n\nexport interface PartnerReportOptions {\n /** ISO lower bound on when the order was placed. */\n since?: string\n /** Days after payment before a commission counts as payable (the refund window). Default 30. */\n holdDays?: number\n}\n\n/** One attributed order as a partner may see it: no buyer name, email or address beyond the country. */\nexport interface PartnerReportLine {\n number: string\n placedAt: string\n paidAt: string | null\n country: string | null\n units: number\n /** Merchandise after the discount; shipping and tax excluded. */\n merchandise: Money\n commission: Money\n state: 'payable' | 'held' | 'void'\n}\n\nexport interface PartnerReport {\n partner: string\n codes: PublicDiscount[]\n lines: PartnerReportLine[]\n totals: { orders: number; units: number; merchandise: Money; payable: Money; held: Money }\n}\n\ninterface Row {\n number: number\n created_at: string\n paid_at: string | null\n status: string\n payment_status: string\n currency: string\n subtotal: number\n discount: number\n partner_commission: number\n shipping_address_json: string\n units: number | null\n}\n\n/**\n * Who sold what with which code. A partner's report reads the store's own\n * orders, so it covers every payment adapter; it is shown to the partner\n * through a link keyed by an HMAC of their id, never by a password.\n */\nexport class PartnersService {\n constructor(private readonly ctx: StoreContext) {}\n\n /** A partner's public profile; falls back to the id as the name when none is configured. */\n profile(id: string): PartnerProfile {\n return this.ctx.config.partners.find((p) => p.id === id) ?? { id, name: id }\n }\n\n /** The partner a code attributes to, with its public discount — for the welcome banner. */\n referral(code: string | null | undefined): { partner: PartnerProfile; discount: PublicDiscount } | null {\n const d = findDiscount(this.ctx.config.discounts, code, new Date(this.ctx.now()))\n if (!d?.partner) return null\n // Public fields only: this feeds a page. The contact's name is public (the banner greets\n // visitors as their friend); their email is not.\n const { id, name, contact, welcome, avatar } = this.profile(d.partner)\n return { partner: { id, name, ...(contact ? { contact } : {}), ...(welcome ? { welcome } : {}), ...(avatar ? { avatar } : {}) }, discount: publicDiscount(d, this.ctx.config.currency, this.ctx.config.locale) }\n }\n\n // ── Traffic ─────────────────────────────────────────────────────────────\n\n /** Record a visit through a partner link. Same visitor within 30 minutes counts once. Returns whether it was recorded. */\n async recordVisit(v: VisitInput): Promise<boolean> {\n if (!this.partners().includes(v.partner) || isAutomatedVisit(v.userAgent)) return false\n const now = this.ctx.now()\n const visitor = (await sha256Hex(`${this.ctx.env.SESSION_SECRET ?? ''}|${now.slice(0, 10)}|${v.ip ?? ''}|${v.userAgent ?? ''}`)).slice(0, 20)\n const since = new Date(Date.parse(now) - VISIT_DEDUPE_MINUTES * 60_000).toISOString()\n const seen = await this.ctx.db\n .prepare(`SELECT 1 AS x FROM referral_visits WHERE partner = ? AND visitor = ? AND created_at >= ? LIMIT 1`)\n .bind(v.partner, visitor, since)\n .first<{ x: number }>()\n if (seen) return false\n const clip = (x: string | null | undefined, n: number) => (x ? x.slice(0, n) : null)\n await this.ctx.db\n .prepare(`INSERT INTO referral_visits (partner, code, visitor, source, medium, campaign, landing_path, country, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)\n .bind(v.partner, normalizeDiscountCode(v.code), visitor, clip(v.source?.toLowerCase(), 60) ?? 'direct', clip(v.medium?.toLowerCase(), 60), clip(v.campaign, 80), clip(v.landingPath, 200), clip(v.country?.toUpperCase(), 2), now)\n .run()\n return true\n }\n\n /**\n * The funnel for one partner over the last `days`: visits and unique visitors through their\n * links, checkouts started with their code, paid orders, conversion (paid ÷ unique visitors),\n * by day, by source and by country. Orders carry the source of the visit that brought them\n * (metadata `ref_source`) so a channel's sales can be told apart; a spoken code shows as 'code'.\n */\n async traffic(partner: string, opts: { days?: number } = {}): Promise<PartnerTraffic> {\n const days = Math.min(Math.max(opts.days ?? 30, 1), 365)\n const currency = this.ctx.config.currency\n const now = Date.parse(this.ctx.now())\n const since = new Date(now - days * 86_400_000).toISOString()\n const db = this.ctx.db\n const visitsByDay = (\n await db\n .prepare(`SELECT substr(created_at, 1, 10) AS day, COUNT(*) AS visits, COUNT(DISTINCT visitor) AS visitors FROM referral_visits WHERE partner = ? AND created_at >= ? GROUP BY day`)\n .bind(partner, since)\n .all<{ day: string; visits: number; visitors: number }>()\n ).results\n const ordersByDay = (\n await db\n .prepare(\n `SELECT substr(created_at, 1, 10) AS day, COUNT(*) AS checkouts, SUM(CASE WHEN payment_status = 'paid' THEN 1 ELSE 0 END) AS orders\n FROM orders WHERE discount_partner = ? AND created_at >= ? GROUP BY day`\n )\n .bind(partner, since)\n .all<{ day: string; checkouts: number; orders: number }>()\n ).results\n const byDayMap = new Map<string, TrafficDay>()\n for (let i = days - 1; i >= 0; i--) {\n const day = new Date(now - i * 86_400_000).toISOString().slice(0, 10)\n byDayMap.set(day, { day, visits: 0, visitors: 0, checkouts: 0, orders: 0 })\n }\n for (const r of visitsByDay) Object.assign(byDayMap.get(r.day) ?? {}, { visits: r.visits, visitors: r.visitors })\n for (const r of ordersByDay) Object.assign(byDayMap.get(r.day) ?? {}, { checkouts: r.checkouts, orders: r.orders ?? 0 })\n\n const totalsRow = await db\n .prepare(`SELECT COUNT(*) AS visits, COUNT(DISTINCT visitor || substr(created_at, 1, 10)) AS visitors FROM referral_visits WHERE partner = ? AND created_at >= ?`)\n .bind(partner, since)\n .first<{ visits: number; visitors: number }>()\n const orderTotals = await db\n .prepare(\n `SELECT COUNT(*) AS checkouts,\n SUM(CASE WHEN payment_status = 'paid' THEN 1 ELSE 0 END) AS orders,\n SUM(CASE WHEN payment_status = 'paid' THEN subtotal - discount ELSE 0 END) AS sales,\n SUM(CASE WHEN payment_status = 'paid' THEN partner_commission ELSE 0 END) AS commission\n FROM orders WHERE discount_partner = ? AND created_at >= ?`\n )\n .bind(partner, since)\n .first<{ checkouts: number; orders: number | null; sales: number | null; commission: number | null }>()\n\n const sources = (\n await db\n .prepare(`SELECT COALESCE(source, 'direct') AS source, COUNT(*) AS visits, COUNT(DISTINCT visitor) AS visitors FROM referral_visits WHERE partner = ? AND created_at >= ? GROUP BY source ORDER BY visits DESC LIMIT 20`)\n .bind(partner, since)\n .all<{ source: string; visits: number; visitors: number }>()\n ).results\n const orderSources = (\n await db\n .prepare(\n `SELECT COALESCE(json_extract(metadata_json, '$.ref_source'), 'code') AS source, COUNT(*) AS orders, SUM(subtotal - discount) AS sales\n FROM orders WHERE discount_partner = ? AND created_at >= ? AND payment_status = 'paid' GROUP BY source`\n )\n .bind(partner, since)\n .all<{ source: string; orders: number; sales: number }>()\n ).results\n const bySourceMap = new Map<string, TrafficSource>()\n for (const r of sources) bySourceMap.set(r.source, { source: r.source, visits: r.visits, visitors: r.visitors, orders: 0, sales: { amount: 0, currency } })\n for (const r of orderSources) {\n const row = bySourceMap.get(r.source) ?? { source: r.source, visits: 0, visitors: 0, orders: 0, sales: { amount: 0, currency } }\n row.orders = r.orders\n row.sales = { amount: r.sales ?? 0, currency }\n bySourceMap.set(r.source, row)\n }\n const byCountry = (\n await db\n .prepare(`SELECT COALESCE(country, '??') AS country, COUNT(*) AS visits FROM referral_visits WHERE partner = ? AND created_at >= ? GROUP BY country ORDER BY visits DESC LIMIT 10`)\n .bind(partner, since)\n .all<{ country: string; visits: number }>()\n ).results\n\n const visitors = totalsRow?.visitors ?? 0\n const orders = orderTotals?.orders ?? 0\n return {\n days,\n totals: {\n visits: totalsRow?.visits ?? 0,\n visitors,\n checkouts: orderTotals?.checkouts ?? 0,\n orders,\n conversion: visitors ? orders / visitors : 0,\n sales: { amount: orderTotals?.sales ?? 0, currency },\n commission: { amount: orderTotals?.commission ?? 0, currency },\n },\n byDay: [...byDayMap.values()],\n bySource: [...bySourceMap.values()].sort((a, b) => b.visits - a.visits || b.orders - a.orders),\n byCountry,\n }\n }\n\n // ── Sign-in ─────────────────────────────────────────────────────────────\n\n private tokenHash(token: string) {\n return hashToken(`partner:${token}`, this.ctx.env.SESSION_SECRET)\n }\n\n /** Email a sign-in link when the address belongs to a partner. Answers the same either way. */\n async sendSignInLink(email: string): Promise<boolean> {\n const wanted = email.trim().toLowerCase()\n const partner = this.ctx.config.partners.find((p) => p.email?.toLowerCase() === wanted)\n if (!partner?.email) return false\n const token = randomToken(32)\n const now = this.ctx.now()\n const expiresAt = new Date(Date.parse(now) + PARTNER_LINK_MINUTES * 60_000).toISOString()\n await this.ctx.db\n .prepare(`INSERT INTO partner_magic_links (token_hash, partner, expires_at, created_at) VALUES (?, ?, ?, ?)`)\n .bind(await this.tokenHash(token), partner.id, expiresAt, now)\n .run()\n const url = `${this.ctx.siteUrl}/partners/verify/?token=${token}`\n const mail = partnerSignInMail({ url, name: (partner.contact ?? partner.name).split(/\\s+/)[0]!, expiresMinutes: PARTNER_LINK_MINUTES }, { config: this.ctx.config, siteUrl: this.ctx.siteUrl })\n await this.ctx.mail.send({ to: partner.email, ...mail })\n return true\n }\n\n /** Exchange a sign-in link for a session. Throws `not_found` when the link is unknown, used or expired. */\n async exchange(token: string): Promise<{ token: string; expiresAt: string; partner: string }> {\n const now = this.ctx.now()\n const hash = await this.tokenHash(token)\n const link = await this.ctx.db\n .prepare(`SELECT partner, expires_at, used_at FROM partner_magic_links WHERE token_hash = ?`)\n .bind(hash)\n .first<{ partner: string; expires_at: string; used_at: string | null }>()\n if (!link || link.used_at || link.expires_at <= now || !this.partners().includes(link.partner)) {\n throw new StoreError('not_found', 'This sign-in link is invalid, used or expired')\n }\n const session = randomToken(32)\n const expiresAt = new Date(Date.parse(now) + PARTNER_SESSION_DAYS * 86_400_000).toISOString()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE partner_magic_links SET used_at = ? WHERE token_hash = ? AND used_at IS NULL`).bind(now, hash),\n this.ctx.db\n .prepare(`INSERT INTO partner_sessions (token_hash, partner, expires_at, created_at, last_seen_at) VALUES (?, ?, ?, ?, ?)`)\n .bind(await this.tokenHash(session), link.partner, expiresAt, now, now),\n ])\n return { token: session, expiresAt, partner: link.partner }\n }\n\n /** The partner behind a session token, or null. */\n async resolveSession(token: string | null | undefined): Promise<string | null> {\n if (!token) return null\n const row = await this.ctx.db\n .prepare(`SELECT partner, expires_at FROM partner_sessions WHERE token_hash = ?`)\n .bind(await this.tokenHash(token))\n .first<{ partner: string; expires_at: string }>()\n if (!row || row.expires_at <= this.ctx.now() || !this.partners().includes(row.partner)) return null\n return row.partner\n }\n\n async signOut(token: string | null | undefined): Promise<void> {\n if (!token) return\n await this.ctx.db.prepare(`DELETE FROM partner_sessions WHERE token_hash = ?`).bind(await this.tokenHash(token)).run()\n }\n\n /** A code as the buyer sees it, if it is valid now. */\n lookupCode(input: string | null | undefined): PublicDiscount | null {\n const d = findDiscount(this.ctx.config.discounts, input, new Date(this.ctx.now()))\n return d ? publicDiscount(d, this.ctx.config.currency, this.ctx.config.locale) : null\n }\n\n /** Partners named by the configured codes. */\n partners(): string[] {\n return [...new Set(this.ctx.config.discounts.map((d) => d.partner).filter((p): p is string => !!p))]\n }\n\n /** The secret in a partner's report link. Needs SESSION_SECRET; rotating it revokes every link. */\n async reportKey(partner: string): Promise<string> {\n const secret = this.ctx.env.SESSION_SECRET\n if (!secret) throw new Error('SESSION_SECRET is required for partner report links')\n return (await hashToken(`partner-report:${partner}`, secret)).slice(0, 32)\n }\n\n async verifyReportKey(partner: string, key: string | null | undefined): Promise<boolean> {\n if (!key || !this.partners().includes(partner) || !this.ctx.env.SESSION_SECRET) return false\n return secretsEqual(key, await this.reportKey(partner))\n }\n\n async report(partner: string, options: PartnerReportOptions = {}): Promise<PartnerReport> {\n const holdMs = (options.holdDays ?? 30) * 86_400_000\n const now = Date.parse(this.ctx.now())\n const currency = this.ctx.config.currency\n const { results } = await this.ctx.db\n .prepare(\n `SELECT o.number, o.created_at, o.paid_at, o.status, o.payment_status, o.currency, o.subtotal, o.discount, o.partner_commission,\n o.shipping_address_json, (SELECT SUM(quantity) FROM order_lines l WHERE l.order_id = o.id) AS units\n FROM orders o\n WHERE o.discount_partner = ? AND o.payment_status IN ('paid', 'refunded') AND o.created_at >= ?\n ORDER BY o.created_at DESC`\n )\n .bind(partner, options.since ?? '0000')\n .all<Row>()\n const lines: PartnerReportLine[] = results.map((r) => {\n const voided = r.payment_status === 'refunded' || r.status === 'cancelled' || r.status === 'refunded'\n const settled = r.paid_at ? now - Date.parse(r.paid_at) >= holdMs : false\n return {\n number: `${this.ctx.config.orders.numberPrefix}${r.number}`,\n placedAt: r.created_at,\n paidAt: r.paid_at,\n country: parseJson<{ country?: string }>(r.shipping_address_json, {}).country ?? null,\n units: r.units ?? 0,\n merchandise: { amount: r.subtotal - r.discount, currency: r.currency },\n commission: { amount: voided ? 0 : r.partner_commission, currency: r.currency },\n state: voided ? 'void' : settled ? 'payable' : 'held',\n }\n })\n const sum = (pick: (l: PartnerReportLine) => number) => lines.reduce((n, l) => n + pick(l), 0)\n const live = lines.filter((l) => l.state !== 'void')\n return {\n partner,\n codes: this.ctx.config.discounts.filter((d) => d.partner === partner).map((d) => this.lookupCode(d.code) ?? { ...publicDiscount(d, currency, this.ctx.config.locale), label: 'expired' }),\n lines,\n totals: {\n orders: live.length,\n units: live.reduce((n, l) => n + l.units, 0),\n merchandise: { amount: live.reduce((n, l) => n + l.merchandise.amount, 0), currency },\n payable: { amount: sum((l) => (l.state === 'payable' ? l.commission.amount : 0)), currency },\n held: { amount: sum((l) => (l.state === 'held' ? l.commission.amount : 0)), currency },\n },\n }\n }\n}\n","import type { DbStatement } from '../db/driver'\nimport type { OrderEvent } from '../types'\nimport { parseJson, type StoreContext } from './context'\n\ninterface EventRow {\n id: number\n order_id: string\n type: string\n data_json: string | null\n created_at: string\n}\n\n/** The audit trail of an order: created, paid, cancelled, fulfilled, emails sent, … */\nexport class EventsService {\n constructor(private readonly ctx: StoreContext) {}\n\n /** A statement to include in a batch, so the event lands with the change it describes. */\n statement(orderId: string, type: string, data: Record<string, unknown> | null = null): DbStatement {\n return this.ctx.db\n .prepare(`INSERT INTO order_events (order_id, type, data_json, created_at) VALUES (?, ?, ?, ?)`)\n .bind(orderId, type, data ? JSON.stringify(data) : null, this.ctx.now())\n }\n\n async record(orderId: string, type: string, data: Record<string, unknown> | null = null): Promise<void> {\n await this.statement(orderId, type, data).run()\n }\n\n async list(orderId: string): Promise<OrderEvent[]> {\n const { results } = await this.ctx.db\n .prepare(`SELECT * FROM order_events WHERE order_id = ? ORDER BY id ASC`)\n .bind(orderId)\n .all<EventRow>()\n return results.map((r) => ({\n id: r.id,\n orderId: r.order_id,\n type: r.type,\n data: parseJson<Record<string, unknown> | null>(r.data_json, null),\n createdAt: r.created_at,\n }))\n }\n}\n","import type { DbStatement } from '../db/driver'\nimport type { Availability } from '../types'\nimport { deriveAvailability } from './catalog'\nimport { chunk, placeholders, type StoreContext } from './context'\n\nexport interface InventoryItemInput {\n sku: string\n available: number\n allow_backorder?: boolean\n}\n\nexport interface InventoryResult {\n updated: number\n unknown_skus: string[]\n}\n\nexport class InventoryService {\n constructor(private readonly ctx: StoreContext, private readonly existingSkus: (skus: string[]) => Promise<Set<string>>, private readonly invalidate: () => void) {}\n\n /** Set absolute available quantities. Unknown SKUs are reported, not created. */\n async set(items: InventoryItemInput[]): Promise<InventoryResult> {\n if (items.length === 0) return { updated: 0, unknown_skus: [] }\n const known = await this.existingSkus(items.map((i) => i.sku))\n const now = this.ctx.now()\n const unknown: string[] = []\n const statements: DbStatement[] = []\n for (const item of items) {\n if (!known.has(item.sku)) {\n unknown.push(item.sku)\n continue\n }\n statements.push(\n this.ctx.db\n .prepare(\n `INSERT INTO inventory (sku, available, allow_backorder, updated_at) VALUES (?, ?, ?, ?)\n ON CONFLICT (sku) DO UPDATE SET available = excluded.available, allow_backorder = excluded.allow_backorder, updated_at = excluded.updated_at`\n )\n .bind(item.sku, Math.trunc(item.available), item.allow_backorder ? 1 : 0, now)\n )\n }\n for (const part of chunk(statements, 250)) await this.ctx.db.batch(part)\n if (statements.length) this.invalidate()\n return { updated: statements.length, unknown_skus: [...new Set(unknown)] }\n }\n\n /** Availability for a set of SKUs; SKUs without a row are untracked (undefined). */\n async availability(skus: string[]): Promise<Map<string, Availability | undefined>> {\n const out = new Map<string, Availability | undefined>()\n for (const sku of skus) out.set(sku, undefined)\n for (const part of chunk([...new Set(skus)])) {\n const { results } = await this.ctx.db\n .prepare(`SELECT sku, available, allow_backorder, updated_at FROM inventory WHERE sku IN (${placeholders(part.length)})`)\n .bind(...part)\n .all<{ sku: string; available: number; allow_backorder: number; updated_at: string }>()\n for (const r of results) out.set(r.sku, deriveAvailability(r, this.ctx.config.inventory.lowStockThreshold))\n }\n return out\n }\n\n /** Statements that move tracked stock by `delta` per SKU (negative to consume). Untracked SKUs are untouched. */\n adjustStatements(lines: { sku: string; quantity: number }[], direction: 1 | -1): DbStatement[] {\n const now = this.ctx.now()\n return lines.map((l) =>\n this.ctx.db\n .prepare(`UPDATE inventory SET available = available + ?, updated_at = ? WHERE sku = ?`)\n .bind(direction * l.quantity, now, l.sku)\n )\n }\n}\n","import type { DbStatement } from '../db/driver'\nimport { merchantNewOrderMail, orderConfirmationMail, shippingConfirmationMail } from '../mail/templates'\nimport type { Fulfillment, Money, Order, OrderStatus } from '../types'\nimport { chunk, placeholders, type StoreContext } from './context'\nimport { newId } from './crypto'\nimport { normalizeEmail } from './customers'\nimport { conflict, invalid, notFound } from './errors'\nimport type { EventsService } from './events'\nimport type { InventoryService } from './inventory'\nimport { ORDER_SELECT, rowToFulfillment, rowToOrder, type FulfillmentLineRow, type FulfillmentRow, type OrderLineRow, type OrderRow } from './rows'\n\nexport interface PaymentRecord {\n provider: string\n reference?: string | null\n methodLabel?: string | null\n paidAt?: string | null\n /** What the processor actually charged, when it may differ from the order (promotion codes). */\n amount?: Money\n adjustments?: { discount?: Money; tax?: Money; total?: Money }\n}\n\nexport interface FulfillmentInput {\n external_id: string\n carrier?: string | null\n service?: string | null\n tracking_number?: string | null\n tracking_url?: string | null\n shipped_at?: string | null\n lines: { sku: string; quantity: number }[]\n notify_customer?: boolean\n}\n\n/** Processor checkouts still unpaid after this long are treated as abandoned. */\nexport const ABANDONED_AFTER_MINUTES = 1440\n\nexport interface ChangedPage {\n orders: Order[]\n nextCursor: string | null\n}\n\nconst encodeCursor = (updatedAt: string, id: string) => btoa(`${updatedAt}|${id}`).replace(/=+$/, '')\nconst decodeCursor = (cursor: string): { updatedAt: string; id: string } | null => {\n try {\n const raw = atob(cursor)\n const i = raw.lastIndexOf('|')\n if (i < 0) return null\n return { updatedAt: raw.slice(0, i), id: raw.slice(i + 1) }\n } catch {\n return null\n }\n}\n\nexport class OrdersService {\n constructor(\n private readonly ctx: StoreContext,\n private readonly events: EventsService,\n private readonly inventory: InventoryService\n ) {}\n\n // ---- reads -------------------------------------------------------------\n\n async get(id: string): Promise<Order | null> {\n const [order] = await this.hydrate(`${ORDER_SELECT} WHERE o.id = ?`, [id])\n return order ?? null\n }\n\n async require(id: string): Promise<Order> {\n const order = await this.get(id)\n if (!order) throw notFound('Order')\n return order\n }\n\n /** The confirmation page's lookup: id plus the access key issued at checkout. */\n async getWithKey(id: string, key: string): Promise<Order | null> {\n const order = await this.get(id)\n return order && key && order.accessKey === key ? order : null\n }\n\n /** Guest order status: number plus the email it was placed with. */\n async byNumberAndEmail(number: string, email: string): Promise<Order | null> {\n const digits = number.replace(/^\\D+/, '')\n if (!/^\\d+$/.test(digits)) return null\n const [order] = await this.hydrate(`${ORDER_SELECT} WHERE o.number = ? AND o.email = ?`, [Number(digits), normalizeEmail(email)])\n return order ?? null\n }\n\n async forCustomer(customerId: string): Promise<Order[]> {\n return this.hydrate(`${ORDER_SELECT} WHERE o.customer_id = ? ORDER BY o.created_at DESC, o.number DESC`, [customerId])\n }\n\n async getForCustomer(id: string, customerId: string): Promise<Order | null> {\n const [order] = await this.hydrate(`${ORDER_SELECT} WHERE o.id = ? AND o.customer_id = ?`, [id, customerId])\n return order ?? null\n }\n\n async byPaymentReference(provider: string, reference: string): Promise<Order | null> {\n const [order] = await this.hydrate(`${ORDER_SELECT} WHERE o.payment_provider = ? AND o.payment_reference = ?`, [provider, reference])\n return order ?? null\n }\n\n /** Newest first, for the owner's order list. Unpaid processor checkouts are left out unless asked for. */\n async recent(opts: { limit?: number; includeUnpaid?: boolean } = {}): Promise<Order[]> {\n const limit = Math.min(Math.max(opts.limit ?? 100, 1), 500)\n const where = opts.includeUnpaid ? '' : ` WHERE o.payment_status != 'unpaid' OR COALESCE(o.payment_provider, 'manual') = 'manual'`\n return this.hydrate(`${ORDER_SELECT}${where} ORDER BY o.created_at DESC, o.number DESC LIMIT ?`, [limit])\n }\n\n /**\n * The owner's order list: newest first, filtered and paged, with the total for paging. `query` matches the\n * buyer's email or name, the order number with or without the store's prefix (\"VLD4200\", \"4200\", \"#4200\"), or an\n * imported order's original number. Orders copied from an earlier platform carry `metadata.imported_from` (e.g.\n * \"woocommerce\") and `metadata.original_number`; `source` separates them from the store's own. Unpaid processor\n * checkouts (abandoned) are left out unless `includeUnpaid`.\n */\n async search(\n opts: { query?: string; status?: OrderStatus; source?: 'store' | 'imported'; includeUnpaid?: boolean; limit?: number; offset?: number } = {}\n ): Promise<{ orders: Order[]; total: number }> {\n const where: string[] = []\n const args: (string | number)[] = []\n const query = opts.query?.trim()\n if (query) {\n const prefix = this.ctx.config.orders.numberPrefix.toLowerCase()\n let number = query.replace(/^#/, '').trim()\n if (prefix && number.toLowerCase().startsWith(prefix)) number = number.slice(prefix.length)\n where.push(`(o.email LIKE ? OR c.name LIKE ? OR CAST(o.number AS TEXT) = ? OR json_extract(o.metadata_json, '$.original_number') LIKE ?)`)\n args.push(`%${query.toLowerCase()}%`, `%${query}%`, number.trim(), `%${query}%`)\n }\n if (opts.status) {\n where.push('o.status = ?')\n args.push(opts.status)\n }\n if (opts.source === 'imported') where.push(`json_extract(o.metadata_json, '$.imported_from') IS NOT NULL`)\n if (opts.source === 'store') where.push(`json_extract(o.metadata_json, '$.imported_from') IS NULL`)\n if (!opts.includeUnpaid) where.push(`(o.payment_status != 'unpaid' OR COALESCE(o.payment_provider, 'manual') = 'manual')`)\n const clause = where.length ? ` WHERE ${where.join(' AND ')}` : ''\n const limit = Math.min(Math.max(Math.trunc(opts.limit ?? 50), 1), 200)\n const offset = Math.max(Math.trunc(opts.offset ?? 0), 0)\n const counted = await this.ctx.db\n .prepare(`SELECT COUNT(*) AS n FROM orders o JOIN customers c ON c.id = o.customer_id${clause}`)\n .bind(...args)\n .first<{ n: number }>()\n const orders = await this.hydrate(`${ORDER_SELECT}${clause} ORDER BY o.created_at DESC, o.number DESC LIMIT ? OFFSET ?`, [...args, limit, offset])\n return { orders, total: counted?.n ?? 0 }\n }\n\n async count(): Promise<number> {\n const row = await this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM orders`).first<{ n: number }>()\n return row?.n ?? 0\n }\n\n /**\n * Orders changed since a watermark, oldest first, keyset-paged on\n * (updated_at, id). `updatedSince` is inclusive so a consumer can overlap.\n */\n async changedSince(updatedSince: string | null, cursor: string | null, limit: number): Promise<ChangedPage> {\n const size = Math.min(Math.max(Math.trunc(limit) || 50, 1), 200)\n const where: string[] = []\n const args: (string | number)[] = []\n if (cursor) {\n const c = decodeCursor(cursor)\n if (!c) throw invalid('Invalid cursor', { cursor: 'is not a cursor this store issued' })\n where.push(`(o.updated_at, o.id) > (?, ?)`)\n args.push(c.updatedAt, c.id)\n } else if (updatedSince) {\n where.push(`o.updated_at >= ?`)\n args.push(updatedSince)\n }\n const sql = `${ORDER_SELECT}${where.length ? ` WHERE ${where.join(' AND ')}` : ''} ORDER BY o.updated_at ASC, o.id ASC LIMIT ?`\n const orders = await this.hydrate(sql, [...args, size + 1])\n const page = orders.slice(0, size)\n const last = page[page.length - 1]\n return { orders: page, nextCursor: orders.length > size && last ? encodeCursor(last.updatedAt, last.id) : null }\n }\n\n private async hydrate(sql: string, args: (string | number)[]): Promise<Order[]> {\n const { results: rows } = await this.ctx.db.prepare(sql).bind(...args).all<OrderRow>()\n if (rows.length === 0) return []\n const ids = rows.map((r) => r.id)\n const lines = new Map<string, OrderLineRow[]>()\n const fulfillments = new Map<string, FulfillmentRow[]>()\n const fulfillmentLines = new Map<string, FulfillmentLineRow[]>()\n for (const part of chunk(ids)) {\n const ph = placeholders(part.length)\n const [l, f, fl] = await Promise.all([\n this.ctx.db.prepare(`SELECT * FROM order_lines WHERE order_id IN (${ph}) ORDER BY position, id`).bind(...part).all<OrderLineRow>(),\n this.ctx.db.prepare(`SELECT * FROM fulfillments WHERE order_id IN (${ph}) ORDER BY created_at, id`).bind(...part).all<FulfillmentRow>(),\n this.ctx.db\n .prepare(`SELECT fl.* FROM fulfillment_lines fl JOIN fulfillments f ON f.id = fl.fulfillment_id WHERE f.order_id IN (${ph}) ORDER BY fl.sku`)\n .bind(...part)\n .all<FulfillmentLineRow>(),\n ])\n for (const r of l.results) (lines.get(r.order_id) ?? lines.set(r.order_id, []).get(r.order_id)!).push(r)\n for (const r of f.results) (fulfillments.get(r.order_id) ?? fulfillments.set(r.order_id, []).get(r.order_id)!).push(r)\n for (const r of fl.results) (fulfillmentLines.get(r.fulfillment_id) ?? fulfillmentLines.set(r.fulfillment_id, []).get(r.fulfillment_id)!).push(r)\n }\n return rows.map((r) =>\n rowToOrder(\n r,\n lines.get(r.id) ?? [],\n (fulfillments.get(r.id) ?? []).map((f) => rowToFulfillment(f, fulfillmentLines.get(f.id) ?? []))\n )\n )\n }\n\n // ---- writes ------------------------------------------------------------\n\n /**\n * Record which adapter took the order to payment. `provider` is what\n * `expireAbandoned` uses to tell a processor checkout (which can be\n * abandoned) from a `manual` order (which waits as long as it takes).\n */\n async startPayment(id: string, input: { provider: string; kind: 'redirect' | 'instructions' }): Promise<void> {\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE orders SET payment_provider = ?, updated_at = ? WHERE id = ? AND payment_status = 'unpaid'`).bind(input.provider, now, id),\n this.events.statement(id, 'payment_started', { provider: input.provider, kind: input.kind }),\n ])\n }\n\n /**\n * Cancel processor checkouts the buyer never finished: every\n * `pending_payment` order older than the window whose adapter is not\n * `manual`. Called lazily (Store API order list, checkout endpoint), so no\n * cron is needed; a payment that arrives later reopens the order (markPaid).\n */\n async expireAbandoned(opts: { olderThanMinutes?: number; limit?: number } = {}): Promise<number> {\n const minutes = opts.olderThanMinutes ?? ABANDONED_AFTER_MINUTES\n const limit = Math.min(Math.max(opts.limit ?? 200, 1), 500)\n const now = this.ctx.now()\n const cutoff = new Date(new Date(now).getTime() - minutes * 60_000).toISOString()\n const { results } = await this.ctx.db\n .prepare(`SELECT id FROM orders WHERE status = 'pending_payment' AND COALESCE(payment_provider, '') != 'manual' AND created_at < ? ORDER BY created_at LIMIT ?`)\n .bind(cutoff, limit)\n .all<{ id: string }>()\n if (results.length === 0) return 0\n const statements = results.flatMap((r) => [\n this.ctx.db.prepare(`UPDATE orders SET status = 'cancelled', cancelled_at = ?, updated_at = ? WHERE id = ? AND status = 'pending_payment'`).bind(now, now, r.id),\n this.events.statement(r.id, 'abandoned', { after_minutes: minutes }),\n ])\n for (const part of chunk(statements, 250)) await this.ctx.db.batch(part)\n return results.length\n }\n\n /**\n * Record a successful payment. Idempotent: a second call for an order that\n * is already paid returns it unchanged, so webhook redelivery is harmless.\n * A cancelled order (abandoned and expired, or cancelled by operations) that\n * the processor reports paid is reopened as `paid` — money arrived, and a\n * paid order is never lost.\n */\n async markPaid(id: string, payment: PaymentRecord, opts: { notify?: boolean } = {}): Promise<Order> {\n const order = await this.require(id)\n if (order.payment.status === 'paid') return order\n if (order.payment.status === 'refunded') {\n this.ctx.log('orders: ignoring a payment for a refunded order', { id, provider: payment.provider, reference: payment.reference })\n return order\n }\n const reopened = order.status === 'cancelled'\n if (reopened) this.ctx.log('orders: payment arrived for a cancelled order; reopening it', { id, provider: payment.provider, reference: payment.reference })\n const now = this.ctx.now()\n const paidAt = payment.paidAt ?? now\n const discount = payment.adjustments?.discount?.amount ?? order.discount.amount\n const tax = payment.adjustments?.tax?.amount ?? order.tax.amount\n const total = payment.adjustments?.total?.amount ?? payment.amount?.amount ?? order.subtotal.amount + order.shipping.amount + tax - discount\n const nextStatus: OrderStatus = order.status === 'pending_payment' || reopened ? 'paid' : order.status\n const statements: DbStatement[] = [\n this.ctx.db\n .prepare(\n `UPDATE orders SET status = ?, payment_status = 'paid', payment_provider = ?, payment_reference = ?, payment_method_label = ?, paid_at = ?,\n discount = ?, tax = ?, total = ?, cancelled_at = ?, updated_at = ? WHERE id = ?`\n )\n .bind(nextStatus, payment.provider, payment.reference ?? null, payment.methodLabel ?? null, paidAt, discount, tax, total, reopened ? null : order.cancelledAt, now, id),\n this.ctx.db\n .prepare(`INSERT INTO payments (id, order_id, provider, reference, status, amount, currency, method_label, created_at) VALUES (?, ?, ?, ?, 'paid', ?, ?, ?, ?)`)\n .bind(newId('pay'), id, payment.provider, payment.reference ?? null, total, order.currency, payment.methodLabel ?? null, now),\n ...this.inventory.adjustStatements(order.lines, -1),\n ...(reopened ? [this.events.statement(id, 'uncancelled', { reason: 'payment received', provider: payment.provider })] : []),\n this.events.statement(id, 'paid', { provider: payment.provider, reference: payment.reference ?? null, total }),\n ]\n await this.ctx.db.batch(statements)\n const updated = await this.require(id)\n if (opts.notify !== false) await this.notifyPaid(updated)\n return updated\n }\n\n async setPaymentStatus(id: string, input: { status: 'paid' | 'refunded' | 'failed'; provider?: string | null; reference?: string | null; paid_at?: string | null }): Promise<Order> {\n if (input.status === 'paid') {\n return this.markPaid(id, { provider: input.provider ?? 'manual', reference: input.reference ?? null, paidAt: input.paid_at ?? null })\n }\n const order = await this.require(id)\n const now = this.ctx.now()\n if (input.status === 'refunded') {\n if (order.payment.status === 'refunded') return order\n if (order.payment.status !== 'paid') throw conflict('Only a paid order can be refunded')\n await this.ctx.db.batch([\n this.ctx.db\n .prepare(`UPDATE orders SET status = 'refunded', payment_status = 'refunded', payment_provider = COALESCE(?, payment_provider), payment_reference = COALESCE(?, payment_reference), updated_at = ? WHERE id = ?`)\n .bind(input.provider ?? null, input.reference ?? null, now, id),\n this.ctx.db\n .prepare(`INSERT INTO payments (id, order_id, provider, reference, status, amount, currency, method_label, created_at) VALUES (?, ?, ?, ?, 'refunded', ?, ?, ?, ?)`)\n .bind(newId('pay'), id, input.provider ?? order.payment.provider ?? 'manual', input.reference ?? order.payment.reference, -order.total.amount, order.currency, order.payment.methodLabel, now),\n this.events.statement(id, 'refunded', { provider: input.provider ?? null, reference: input.reference ?? null }),\n ])\n return this.require(id)\n }\n // failed: the order stays open for another attempt.\n if (order.payment.status === 'paid') throw conflict('A paid order cannot be marked failed')\n await this.ctx.db.batch([\n this.ctx.db\n .prepare(`UPDATE orders SET payment_status = 'failed', payment_provider = COALESCE(?, payment_provider), payment_reference = COALESCE(?, payment_reference), updated_at = ? WHERE id = ?`)\n .bind(input.provider ?? null, input.reference ?? null, now, id),\n this.events.statement(id, 'payment_failed', { provider: input.provider ?? null, reference: input.reference ?? null }),\n ])\n return this.require(id)\n }\n\n async cancel(id: string, reason: string | null = null): Promise<Order> {\n const order = await this.require(id)\n if (order.status === 'cancelled') return order\n if (order.status === 'fulfilled' || order.status === 'partially_fulfilled') throw conflict('An order that has shipped cannot be cancelled')\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE orders SET status = 'cancelled', cancelled_at = ?, updated_at = ? WHERE id = ?`).bind(now, now, id),\n ...(order.payment.status === 'paid' ? this.inventory.adjustStatements(order.lines, 1) : []),\n this.events.statement(id, 'cancelled', reason ? { reason } : null),\n ])\n return this.require(id)\n }\n\n async setExternalReference(id: string, reference: string | null): Promise<Order> {\n const order = await this.require(id)\n if (order.externalReference === reference) return order\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE orders SET external_reference = ?, updated_at = ? WHERE id = ?`).bind(reference, now, id),\n this.events.statement(id, 'external_reference_set', { reference }),\n ])\n return this.require(id)\n }\n\n /**\n * Record a shipment. Idempotent per (order, external_id): a redelivery\n * returns the existing fulfilment with `created: false` and sends nothing.\n */\n async addFulfillment(id: string, input: FulfillmentInput): Promise<{ order: Order; fulfillment: Fulfillment; created: boolean }> {\n const order = await this.require(id)\n const existing = order.fulfillments.find((f) => f.externalId === input.external_id)\n if (existing) return { order, fulfillment: existing, created: false }\n if (order.status === 'cancelled') throw conflict('A cancelled order cannot be fulfilled')\n\n const details: Record<string, string> = {}\n const merged = new Map<string, number>()\n input.lines.forEach((l, i) => {\n const line = order.lines.find((ol) => ol.sku === l.sku)\n if (!line) {\n details[`lines.${i}.sku`] = `is not on this order`\n return\n }\n merged.set(l.sku, (merged.get(l.sku) ?? 0) + l.quantity)\n const remaining = line.quantity - line.fulfilledQuantity\n if (merged.get(l.sku)! > remaining) details[`lines.${i}.quantity`] = `exceeds the ${remaining} unfulfilled unit(s) of ${l.sku}`\n })\n if (Object.keys(details).length) throw invalid('Fulfilment lines do not match the order', details)\n\n const now = this.ctx.now()\n const fulfillmentId = newId('ful')\n const statements: DbStatement[] = [\n this.ctx.db\n .prepare(`INSERT INTO fulfillments (id, order_id, external_id, carrier, service, tracking_number, tracking_url, shipped_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)\n .bind(fulfillmentId, id, input.external_id, input.carrier ?? null, input.service ?? null, input.tracking_number ?? null, input.tracking_url ?? null, input.shipped_at ?? null, now),\n ]\n for (const [sku, quantity] of merged) {\n statements.push(this.ctx.db.prepare(`INSERT INTO fulfillment_lines (fulfillment_id, sku, quantity) VALUES (?, ?, ?)`).bind(fulfillmentId, sku, quantity))\n statements.push(this.ctx.db.prepare(`UPDATE order_lines SET fulfilled_quantity = fulfilled_quantity + ? WHERE order_id = ? AND sku = ?`).bind(quantity, id, sku))\n }\n const allDone = order.lines.every((l) => l.fulfilledQuantity + (merged.get(l.sku) ?? 0) >= l.quantity)\n const nextStatus: OrderStatus = allDone ? 'fulfilled' : 'partially_fulfilled'\n statements.push(this.ctx.db.prepare(`UPDATE orders SET status = ?, updated_at = ? WHERE id = ?`).bind(nextStatus, now, id))\n statements.push(this.events.statement(id, 'fulfillment_added', { fulfillment: fulfillmentId, external_id: input.external_id, tracking_number: input.tracking_number ?? null }))\n await this.ctx.db.batch(statements)\n\n const updated = await this.require(id)\n const fulfillment = updated.fulfillments.find((f) => f.id === fulfillmentId)!\n if (input.notify_customer !== false) await this.notifyShipped(updated, fulfillment)\n return { order: updated, fulfillment, created: true }\n }\n\n // ---- notifications -----------------------------------------------------\n\n private mailContext() {\n return { config: this.ctx.config, siteUrl: this.ctx.siteUrl }\n }\n\n /** Buyer confirmation plus the merchant notification. Failures are logged, never thrown. */\n async notifyPaid(order: Order): Promise<void> {\n const buyer = orderConfirmationMail(order, this.mailContext())\n await this.send(order.id, 'order_confirmation', order.customer.email, buyer)\n const merchant = this.ctx.env.ORDER_NOTIFICATION_EMAIL\n if (merchant) await this.send(order.id, 'merchant_notification', merchant, merchantNewOrderMail(order, this.mailContext()), order.customer.email)\n }\n\n /** Order received but not yet paid (manual payment). */\n async notifyReceived(order: Order): Promise<void> {\n await this.send(order.id, 'order_received', order.customer.email, orderConfirmationMail(order, this.mailContext()))\n }\n\n async notifyShipped(order: Order, fulfillment: Fulfillment): Promise<void> {\n const sent = await this.send(order.id, 'shipping_confirmation', order.customer.email, shippingConfirmationMail(order, fulfillment, this.mailContext()))\n if (sent) await this.ctx.db.prepare(`UPDATE fulfillments SET notified_at = ? WHERE id = ?`).bind(this.ctx.now(), fulfillment.id).run()\n }\n\n private async send(orderId: string, kind: string, to: string, mail: { subject: string; html: string; text: string }, replyTo?: string): Promise<boolean> {\n try {\n await this.ctx.mail.send({ to, ...mail, ...(replyTo ? { replyTo } : {}) })\n await this.events.record(orderId, 'email_sent', { kind, to })\n return true\n } catch (error) {\n this.ctx.log(`orders: ${kind} email failed`, { orderId, to, error: error instanceof Error ? error.message : String(error) })\n await this.events.record(orderId, 'email_failed', { kind, to, error: error instanceof Error ? error.message : String(error) }).catch(() => {})\n return false\n }\n }\n}\n","/**\n * Password hashing on WebCrypto PBKDF2-HMAC-SHA256, so the same code runs on\n * Workers and in Node. The stored string carries every parameter:\n *\n * $pbkdf2-sha256$i=<iterations>$<salt base64url>$<hash base64url>\n *\n * so the cost can be raised later and old hashes keep verifying (and can be\n * re-hashed on the next successful sign-in).\n */\nconst encoder = new TextEncoder()\n\n/**\n * Cloudflare Workers cap a single PBKDF2 call at 100_000 iterations\n * (`NotSupportedError: iteration counts above 100000 are not supported`), which\n * Node does not — a gap that only shows up on the deployed Worker. To keep an\n * effective work factor above the OWASP-era 200k floor while staying under that\n * ceiling, the derivation is CHAINED: each 100k-iteration round feeds the next,\n * so a stored count of 300_000 is three rounds. The count must be a multiple of\n * the per-call cap.\n */\nexport const PBKDF2_MAX_PER_CALL = 100_000\nexport const PASSWORD_ITERATIONS = 300_000\nconst SALT_BYTES = 16\nconst HASH_BYTES = 32\nexport const PASSWORD_MIN_LENGTH = 12\n\nexport function toBase64Url(bytes: Uint8Array): string {\n let bin = ''\n for (const b of bytes) bin += String.fromCharCode(b)\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nexport function fromBase64Url(s: string): Uint8Array {\n const padded = s.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (s.length % 4)) % 4)\n const bin = atob(padded)\n const out = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i)\n return out\n}\n\nasync function pbkdf2(material: BufferSource, salt: Uint8Array, iterations: number): Promise<ArrayBuffer> {\n const key = await crypto.subtle.importKey('raw', material, 'PBKDF2', false, ['deriveBits'])\n return crypto.subtle.deriveBits({ name: 'PBKDF2', hash: 'SHA-256', salt: salt as BufferSource, iterations }, key, HASH_BYTES * 8)\n}\n\n/**\n * PBKDF2 chained in <=100k-iteration rounds so the total honours `iterations`\n * without any single call exceeding the Workers cap. Each round's output is the\n * next round's input; the salt is constant. Node and Workers agree round for round.\n */\nasync function derive(password: string, salt: Uint8Array, iterations: number): Promise<Uint8Array> {\n let material: BufferSource = encoder.encode(password.normalize('NFKC'))\n let remaining = iterations\n while (remaining > 0) {\n const step = Math.min(remaining, PBKDF2_MAX_PER_CALL)\n material = await pbkdf2(material, salt, step)\n remaining -= step\n }\n return new Uint8Array(material as ArrayBuffer)\n}\n\nexport async function hashPassword(password: string, iterations = PASSWORD_ITERATIONS): Promise<string> {\n const salt = new Uint8Array(SALT_BYTES)\n crypto.getRandomValues(salt)\n const hash = await derive(password, salt, iterations)\n return `$pbkdf2-sha256$i=${iterations}$${toBase64Url(salt)}$${toBase64Url(hash)}`\n}\n\nexport interface PasswordCheck {\n ok: boolean\n /** True when the stored hash uses fewer iterations than the current default. */\n needsRehash: boolean\n}\n\n/** Verify against a stored hash. Never throws on a malformed hash — it simply fails. */\nexport async function verifyPassword(password: string, stored: string | null | undefined): Promise<PasswordCheck> {\n const match = /^\\$pbkdf2-sha256\\$i=(\\d+)\\$([A-Za-z0-9_-]+)\\$([A-Za-z0-9_-]+)$/.exec(stored ?? '')\n if (!match) return { ok: false, needsRehash: false }\n const iterations = Number(match[1])\n if (!Number.isInteger(iterations) || iterations < 1_000 || iterations > 10_000_000) return { ok: false, needsRehash: false }\n const salt = fromBase64Url(match[2]!)\n const expected = fromBase64Url(match[3]!)\n const actual = await derive(password, salt, iterations)\n if (actual.length !== expected.length) return { ok: false, needsRehash: false }\n let diff = 0\n for (let i = 0; i < actual.length; i++) diff |= actual[i]! ^ expected[i]!\n return { ok: diff === 0, needsRehash: iterations < PASSWORD_ITERATIONS }\n}\n\n/** The one rule the store enforces on a new password. Length, not composition. */\nexport function passwordProblem(password: string): string | null {\n if (typeof password !== 'string' || password.length < PASSWORD_MIN_LENGTH) return `must be at least ${PASSWORD_MIN_LENGTH} characters`\n if (password.length > 256) return 'must be at most 256 characters'\n return null\n}\n","import { ownerPasswordResetMail } from '../mail/templates'\nimport type { StoreContext } from './context'\nimport { hashToken, newId, randomToken } from './crypto'\nimport { StoreError } from './errors'\nimport { hashPassword, passwordProblem, verifyPassword } from './password'\nimport { RateLimiter } from './ratelimit'\n\nexport const OWNER_SESSION_DAYS = 14\nexport const OWNER_RESET_MINUTES = 30\n\nexport interface Owner {\n id: string\n email: string\n name: string | null\n createdAt: string\n lastSignedInAt: string | null\n}\n\nexport interface OwnerSession {\n token: string\n expiresAt: string\n owner: Owner\n}\n\nexport interface OwnerResetLink {\n token: string\n url: string\n expiresAt: string\n owner: Owner\n}\n\ninterface OwnerRow {\n id: string\n email: string\n name: string | null\n password_hash: string\n created_at: string\n last_signed_in_at: string | null\n}\n\nconst toOwner = (r: OwnerRow): Owner => ({ id: r.id, email: r.email, name: r.name, createdAt: r.created_at, lastSignedInAt: r.last_signed_in_at })\n\nexport const normalizeOwnerEmail = (email: string) => email.trim().toLowerCase()\n\n/**\n * The people who run the store. Password sign-in with a 14-day session, an\n * emailed reset link, and a failure counter per email + address so a guessed\n * password costs the guesser more than it costs the store.\n */\nexport class OwnersService {\n private readonly limiter: RateLimiter\n\n constructor(private readonly ctx: StoreContext) {\n this.limiter = new RateLimiter(ctx, { maxFailures: 10, windowMinutes: 15, lockMinutes: 15 })\n }\n\n private hash(token: string) {\n return hashToken(token, this.ctx.env.SESSION_SECRET)\n }\n\n async count(): Promise<number> {\n const row = await this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM owners`).first<{ n: number }>()\n return row?.n ?? 0\n }\n\n async byId(id: string): Promise<Owner | null> {\n const row = await this.ctx.db.prepare(`SELECT * FROM owners WHERE id = ?`).bind(id).first<OwnerRow>()\n return row ? toOwner(row) : null\n }\n\n async byEmail(email: string): Promise<Owner | null> {\n const row = await this.ctx.db.prepare(`SELECT * FROM owners WHERE email = ?`).bind(normalizeOwnerEmail(email)).first<OwnerRow>()\n return row ? toOwner(row) : null\n }\n\n async list(): Promise<Owner[]> {\n const { results } = await this.ctx.db.prepare(`SELECT * FROM owners ORDER BY created_at ASC`).all<OwnerRow>()\n return results.map(toOwner)\n }\n\n /** Create or, when the email exists, reset that owner's name and password. */\n async upsert(input: { email: string; name?: string | null; password: string }): Promise<Owner> {\n const email = normalizeOwnerEmail(input.email)\n if (!email.includes('@')) throw new StoreError('validation_failed', 'Invalid owner', { email: 'must be an email address' })\n const problem = passwordProblem(input.password)\n if (problem) throw new StoreError('validation_failed', 'Invalid owner', { password: problem })\n const now = this.ctx.now()\n const id = newId('own')\n await this.ctx.db\n .prepare(\n `INSERT INTO owners (id, email, name, password_hash, created_at) VALUES (?, ?, ?, ?, ?)\n ON CONFLICT (email) DO UPDATE SET name = COALESCE(excluded.name, owners.name), password_hash = excluded.password_hash`\n )\n .bind(id, email, input.name ?? null, await hashPassword(input.password), now)\n .run()\n return (await this.byEmail(email))!\n }\n\n /**\n * Password sign-in. Null on a wrong email or password — the caller shows one\n * generic message either way. Throws `rate_limited` while the email+address\n * pair is locked out.\n */\n async signIn(email: string, password: string, address: string): Promise<OwnerSession | null> {\n const normalized = normalizeOwnerEmail(email)\n const limitKey = `signin:${normalized}|${address}`\n await this.limiter.assertAllowed(limitKey, 'Too many sign-in attempts. Try again in a few minutes.')\n const row = await this.ctx.db.prepare(`SELECT * FROM owners WHERE email = ?`).bind(normalized).first<OwnerRow>()\n // Verify against a dummy hash when the email is unknown so timing does not reveal which emails exist.\n const check = await verifyPassword(password, row?.password_hash ?? DUMMY_HASH)\n if (!row || !check.ok) {\n await this.limiter.recordFailure(limitKey)\n return null\n }\n const now = this.ctx.now()\n const token = randomToken(32)\n const expiresAt = new Date(new Date(now).getTime() + OWNER_SESSION_DAYS * 86_400_000).toISOString()\n const statements = [\n this.ctx.db\n .prepare(`INSERT INTO owner_sessions (token_hash, owner_id, expires_at, created_at, last_seen_at) VALUES (?, ?, ?, ?, ?)`)\n .bind(await this.hash(token), row.id, expiresAt, now, now),\n this.ctx.db.prepare(`UPDATE owners SET last_signed_in_at = ? WHERE id = ?`).bind(now, row.id),\n this.limiter.clearStatement(limitKey),\n ]\n if (check.needsRehash) statements.push(this.ctx.db.prepare(`UPDATE owners SET password_hash = ? WHERE id = ?`).bind(await hashPassword(password), row.id))\n await this.ctx.db.batch(statements)\n return { token, expiresAt, owner: { ...toOwner(row), lastSignedInAt: now } }\n }\n\n /** The owner behind a session token, or null when it is unknown or expired. */\n async resolve(token: string | null | undefined): Promise<Owner | null> {\n if (!token) return null\n const now = this.ctx.now()\n const hash = await this.hash(token)\n const row = await this.ctx.db\n .prepare(`SELECT owner_id, expires_at, last_seen_at FROM owner_sessions WHERE token_hash = ?`)\n .bind(hash)\n .first<{ owner_id: string; expires_at: string; last_seen_at: string }>()\n if (!row || row.expires_at <= now) return null\n if (new Date(now).getTime() - new Date(row.last_seen_at).getTime() > 3_600_000) {\n await this.ctx.db.prepare(`UPDATE owner_sessions SET last_seen_at = ? WHERE token_hash = ?`).bind(now, hash).run()\n }\n return this.byId(row.owner_id)\n }\n\n async signOut(token: string | null | undefined): Promise<void> {\n if (!token) return\n await this.ctx.db.prepare(`DELETE FROM owner_sessions WHERE token_hash = ?`).bind(await this.hash(token)).run()\n }\n\n /** End every session of an owner (after a password reset). */\n async signOutEverywhere(ownerId: string): Promise<void> {\n await this.ctx.db.prepare(`DELETE FROM owner_sessions WHERE owner_id = ?`).bind(ownerId).run()\n }\n\n /** Create a reset link for a known owner; null for an unknown email (the caller answers the same either way). */\n async createResetLink(email: string): Promise<OwnerResetLink | null> {\n const owner = await this.byEmail(email)\n if (!owner) return null\n const token = randomToken(32)\n const now = this.ctx.now()\n const expiresAt = new Date(new Date(now).getTime() + OWNER_RESET_MINUTES * 60_000).toISOString()\n await this.ctx.db\n .prepare(`INSERT INTO owner_reset_links (token_hash, owner_id, expires_at, created_at) VALUES (?, ?, ?, ?)`)\n .bind(await this.hash(token), owner.id, expiresAt, now)\n .run()\n return { token, url: `${this.ctx.siteUrl}/admin/reset-password/?token=${token}`, expiresAt, owner }\n }\n\n /** Email a reset link when the address belongs to an owner. Returns whether one was sent. */\n async sendResetLink(email: string): Promise<boolean> {\n const link = await this.createResetLink(email)\n if (!link) return false\n const mail = ownerPasswordResetMail({ url: link.url, expiresMinutes: OWNER_RESET_MINUTES }, { config: this.ctx.config, siteUrl: this.ctx.siteUrl })\n await this.ctx.mail.send({ to: link.owner.email, ...mail })\n return true\n }\n\n /** Exchange a reset token for a new password. Every existing session of that owner ends. */\n async resetPassword(token: string, password: string): Promise<Owner> {\n const problem = passwordProblem(password)\n if (problem) throw new StoreError('validation_failed', 'Invalid password', { password: problem })\n const now = this.ctx.now()\n const hash = await this.hash(token)\n const link = await this.ctx.db\n .prepare(`SELECT owner_id, expires_at, used_at FROM owner_reset_links WHERE token_hash = ?`)\n .bind(hash)\n .first<{ owner_id: string; expires_at: string; used_at: string | null }>()\n if (!link || link.used_at || link.expires_at <= now) throw new StoreError('not_found', 'This reset link is invalid, used or expired')\n const owner = await this.byId(link.owner_id)\n if (!owner) throw new StoreError('not_found', 'This reset link is invalid, used or expired')\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE owner_reset_links SET used_at = ? WHERE token_hash = ? AND used_at IS NULL`).bind(now, hash),\n this.ctx.db.prepare(`UPDATE owners SET password_hash = ? WHERE id = ?`).bind(await hashPassword(password), owner.id),\n this.ctx.db.prepare(`DELETE FROM owner_sessions WHERE owner_id = ?`).bind(owner.id),\n ])\n return owner\n }\n\n /** Housekeeping: drop expired sessions, used or expired reset links, and stale limiter rows. */\n async prune(): Promise<void> {\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`DELETE FROM owner_sessions WHERE expires_at <= ?`).bind(now),\n this.ctx.db.prepare(`DELETE FROM owner_reset_links WHERE expires_at <= ? OR used_at IS NOT NULL`).bind(now),\n this.limiter.pruneStatement(),\n ])\n }\n}\n\n// A real PBKDF2 hash of a random password, so unknown-email sign-ins cost the same time as known ones.\nconst DUMMY_HASH = '$pbkdf2-sha256$i=210000$AAAAAAAAAAAAAAAAAAAAAA$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'\n","import { magicLinkMail } from '../mail/templates'\nimport type { Customer } from '../types'\nimport type { StoreContext } from './context'\nimport { hashToken, randomToken } from './crypto'\nimport type { CustomersService } from './customers'\nimport { StoreError } from './errors'\n\nexport const MAGIC_LINK_MINUTES = 15\nexport const SESSION_DAYS = 30\n\nexport interface MagicLink {\n token: string\n url: string\n expiresAt: string\n customer: Customer\n}\n\nexport interface Session {\n token: string\n expiresAt: string\n customer: Customer\n}\n\n/**\n * Passwordless sign-in. A magic link is a single-use token emailed to a known\n * customer; exchanging it creates a 30-day session. Only hashes are stored.\n */\nexport class SessionsService {\n constructor(private readonly ctx: StoreContext, private readonly customers: CustomersService) {}\n\n private hash(token: string) {\n return hashToken(token, this.ctx.env.SESSION_SECRET)\n }\n\n /** Create a link for a known customer; null for an unknown email (the caller answers the same either way). */\n async createMagicLink(email: string): Promise<MagicLink | null> {\n const customer = await this.customers.byEmail(email)\n if (!customer) return null\n const token = randomToken(32)\n const now = this.ctx.now()\n const expiresAt = new Date(new Date(now).getTime() + MAGIC_LINK_MINUTES * 60_000).toISOString()\n await this.ctx.db\n .prepare(`INSERT INTO magic_links (token_hash, customer_id, expires_at, created_at) VALUES (?, ?, ?, ?)`)\n .bind(await this.hash(token), customer.id, expiresAt, now)\n .run()\n return { token, url: `${this.ctx.siteUrl}/account/verify/?token=${token}`, expiresAt, customer }\n }\n\n /** Email a sign-in link when the address belongs to a customer. Returns whether one was sent. */\n async sendMagicLink(email: string): Promise<boolean> {\n const link = await this.createMagicLink(email)\n if (!link) return false\n const mail = magicLinkMail({ url: link.url, expiresMinutes: MAGIC_LINK_MINUTES }, { config: this.ctx.config, siteUrl: this.ctx.siteUrl })\n await this.ctx.mail.send({ to: link.customer.email, ...mail })\n return true\n }\n\n /** Exchange a magic-link token for a session. Throws `not_found` when the link is unknown, used or expired. */\n async exchange(token: string): Promise<Session> {\n const now = this.ctx.now()\n const hash = await this.hash(token)\n const link = await this.ctx.db\n .prepare(`SELECT customer_id, expires_at, used_at FROM magic_links WHERE token_hash = ?`)\n .bind(hash)\n .first<{ customer_id: string; expires_at: string; used_at: string | null }>()\n if (!link || link.used_at || link.expires_at <= now) throw new StoreError('not_found', 'This sign-in link is invalid, used or expired')\n const customer = await this.customers.byId(link.customer_id)\n if (!customer) throw new StoreError('not_found', 'This sign-in link is invalid, used or expired')\n const sessionToken = randomToken(32)\n const expiresAt = new Date(new Date(now).getTime() + SESSION_DAYS * 86_400_000).toISOString()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE magic_links SET used_at = ? WHERE token_hash = ? AND used_at IS NULL`).bind(now, hash),\n this.ctx.db\n .prepare(`INSERT INTO customer_sessions (token_hash, customer_id, expires_at, created_at, last_seen_at) VALUES (?, ?, ?, ?, ?)`)\n .bind(await this.hash(sessionToken), customer.id, expiresAt, now, now),\n ])\n return { token: sessionToken, expiresAt, customer }\n }\n\n /** The customer behind a session token, or null when it is unknown or expired. */\n async resolve(token: string | null | undefined): Promise<Customer | null> {\n if (!token) return null\n const now = this.ctx.now()\n const hash = await this.hash(token)\n const row = await this.ctx.db\n .prepare(`SELECT customer_id, expires_at, last_seen_at FROM customer_sessions WHERE token_hash = ?`)\n .bind(hash)\n .first<{ customer_id: string; expires_at: string; last_seen_at: string }>()\n if (!row || row.expires_at <= now) return null\n if (new Date(now).getTime() - new Date(row.last_seen_at).getTime() > 3_600_000) {\n await this.ctx.db.prepare(`UPDATE customer_sessions SET last_seen_at = ? WHERE token_hash = ?`).bind(now, hash).run()\n }\n return this.customers.byId(row.customer_id)\n }\n\n async signOut(token: string | null | undefined): Promise<void> {\n if (!token) return\n await this.ctx.db.prepare(`DELETE FROM customer_sessions WHERE token_hash = ?`).bind(await this.hash(token)).run()\n }\n\n /** Housekeeping: drop expired links and sessions. Safe to call from any request. */\n async prune(): Promise<void> {\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`DELETE FROM magic_links WHERE expires_at <= ? OR used_at IS NOT NULL`).bind(now),\n this.ctx.db.prepare(`DELETE FROM customer_sessions WHERE expires_at <= ?`).bind(now),\n ])\n }\n}\n","/**\n * The slice of Cloudflare D1's API the store uses. A `D1Database` binding\n * satisfies it as-is; tests satisfy it with better-sqlite3 (see test/sqlite.ts).\n *\n * Rules the store code follows so both back ends behave the same:\n * - bind values are string | number | null | ArrayBuffer — never undefined or boolean\n * - `first()`/`all()` only on statements that return rows\n * - anything that must be atomic goes through `batch()`\n */\nexport interface DbStatement {\n bind(...values: DbValue[]): DbStatement\n first<T = Record<string, DbValue>>(): Promise<T | null>\n all<T = Record<string, DbValue>>(): Promise<{ results: T[] }>\n run(): Promise<{ meta?: { changes?: number; last_row_id?: number } }>\n}\n\nexport type DbValue = string | number | null | ArrayBuffer\n\nexport interface Database {\n prepare(sql: string): DbStatement\n batch<T = unknown>(statements: DbStatement[]): Promise<{ results: T[] }[]>\n}\n\n/** Coerce a value into something D1 accepts as a bind parameter. */\nexport function bindable(value: unknown): DbValue {\n if (value === undefined || value === null) return null\n if (typeof value === 'boolean') return value ? 1 : 0\n if (typeof value === 'number' || typeof value === 'string') return value\n if (value instanceof ArrayBuffer) return value\n return JSON.stringify(value)\n}\n","import type { StorefrontConfig } from '../config'\nimport type { Database } from '../db/driver'\nimport { consoleMail } from '../mail/console'\nimport type { MailAdapter } from '../mail/types'\nimport { CatalogService } from './catalog'\nimport { CheckoutService } from './checkout'\nimport { ConnectionsService } from './connections'\nimport { PartnersService } from './partners'\nimport type { StoreContext, StoreEnv } from './context'\nimport { CustomersService } from './customers'\nimport { EventsService } from './events'\nimport { InventoryService } from './inventory'\nimport { OrdersService } from './orders'\nimport { OwnersService } from './owners'\nimport { SessionsService } from './sessions'\n\nexport const STORE_PLATFORM_VERSION = '2.1.0'\n\nexport interface StoreOptions {\n db: Database\n config: StorefrontConfig\n env?: StoreEnv\n mail?: MailAdapter\n /** Injectable clock for tests. */\n clock?: () => Date\n /** How long an isolate keeps a catalogue snapshot. 0 disables the cache. */\n catalogCacheTtlMs?: number\n log?: (message: string, data?: unknown) => void\n}\n\nexport interface StoreHealth {\n name: string\n url: string\n currency: string\n locale: string\n version: string\n counts: { products: number; categories: number; orders: number; customers: number }\n /** True once an owner exists, so a consumer can tell the merchant to run `owner:create` first. */\n owner_configured: boolean\n time: string\n}\n\nexport interface Store {\n readonly db: Database\n readonly config: StorefrontConfig\n readonly env: StoreEnv\n readonly mail: MailAdapter\n readonly siteUrl: string\n now(): string\n readonly catalog: CatalogService\n readonly inventory: InventoryService\n readonly customers: CustomersService\n readonly orders: OrdersService\n readonly checkout: CheckoutService\n readonly sessions: SessionsService\n readonly events: EventsService\n readonly owners: OwnersService\n readonly connections: ConnectionsService\n readonly partners: PartnersService\n health(): Promise<StoreHealth>\n}\n\n/**\n * The store: every service over one database. Cheap to create, so make one\n * per request (the Astro convention is `store(Astro)` in src/lib/store.ts).\n */\nexport function createStore(options: StoreOptions): Store {\n const env = options.env ?? {}\n const clock = options.clock ?? (() => new Date())\n const ctx: StoreContext = {\n db: options.db,\n config: options.config,\n env,\n mail: options.mail ?? consoleMail(),\n now: () => clock().toISOString(),\n siteUrl: (env.PUBLIC_SITE_URL || options.config.url).replace(/\\/+$/, ''),\n log: options.log ?? ((message, data) => console.warn(message, data === undefined ? '' : JSON.stringify(data))),\n }\n const catalog = new CatalogService(ctx, options.catalogCacheTtlMs ?? 30_000)\n const inventory = new InventoryService(ctx, (skus) => catalog.existingSkus(skus), () => catalog.invalidate())\n const events = new EventsService(ctx)\n const customers = new CustomersService(ctx)\n const orders = new OrdersService(ctx, events, inventory)\n const checkout = new CheckoutService(ctx, catalog, orders, events)\n const sessions = new SessionsService(ctx, customers)\n const owners = new OwnersService(ctx)\n const connections = new ConnectionsService(ctx)\n const partners = new PartnersService(ctx)\n\n return {\n db: ctx.db,\n config: ctx.config,\n env,\n mail: ctx.mail,\n siteUrl: ctx.siteUrl,\n now: ctx.now,\n catalog,\n inventory,\n customers,\n orders,\n checkout,\n sessions,\n events,\n owners,\n connections,\n partners,\n async health() {\n const [counts, orderCount, customerCount, ownerCount] = await Promise.all([catalog.counts(), orders.count(), customers.count(), owners.count()])\n return {\n name: ctx.config.name,\n url: ctx.config.url,\n currency: ctx.config.currency.toUpperCase(),\n locale: ctx.config.locale,\n version: STORE_PLATFORM_VERSION,\n counts: { ...counts, orders: orderCount, customers: customerCount },\n owner_configured: ownerCount > 0,\n time: ctx.now(),\n }\n },\n }\n}\n\nexport type { StoreContext, StoreEnv } from './context'\nexport type { Database, DbStatement, DbValue } from '../db/driver'\nexport { bindable } from '../db/driver'\nexport { StoreError, type StoreErrorCode } from './errors'\nexport {\n CatalogService, type ProductInput, type CategoryInput, type ProductRecord, type CategoryRecord,\n type ProductPage, type CategoryPage, type ProductListQuery, type CategoryListQuery, deriveAvailability,\n} from './catalog'\nexport { InventoryService, type InventoryItemInput, type InventoryResult } from './inventory'\nexport { CustomersService, normalizeEmail } from './customers'\nexport { OrdersService, ABANDONED_AFTER_MINUTES, type PaymentRecord, type FulfillmentInput, type ChangedPage } from './orders'\nexport { CheckoutService, type CheckoutInput, type CheckoutItem, type Quote, type PricedLine } from './checkout'\nexport { SessionsService, type MagicLink, type Session, MAGIC_LINK_MINUTES, SESSION_DAYS } from './sessions'\nexport { EventsService } from './events'\nexport { newId, randomToken, randomBase64Url, hashToken, sha256Hex, secretsEqual } from './crypto'\nexport { OwnersService, normalizeOwnerEmail, type Owner, type OwnerSession, type OwnerResetLink, OWNER_SESSION_DAYS, OWNER_RESET_MINUTES } from './owners'\nexport {\n ConnectionsService, isLocalHost, type Connection, type AuthorizeParams, type AuthorizeRequest, type IssuedKey, type ConnectScope,\n CONNECT_CODE_MINUTES, CONNECT_SCOPES, SCOPE_DESCRIPTIONS, API_KEY_PREFIX,\n} from './connections'\nexport { hashPassword, verifyPassword, passwordProblem, PASSWORD_MIN_LENGTH, PASSWORD_ITERATIONS } from './password'\nexport { RateLimiter, type RateLimitPolicy } from './ratelimit'\nexport { PartnersService, PARTNER_LINK_MINUTES, PARTNER_SESSION_DAYS, type PartnerReport, type PartnerReportLine, type PartnerReportOptions, type PartnerTraffic, type TrafficDay, type TrafficSource, type VisitInput } from './partners'\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/store/cursor.ts","../src/store/catalog.ts","../src/store/customers.ts","../src/store/rows.ts","../src/store/checkout.ts","../src/store/partners.ts","../src/store/events.ts","../src/store/inventory.ts","../src/store/orders.ts","../src/store/password.ts","../src/store/owners.ts","../src/store/quotes.ts","../src/store/sessions.ts","../src/db/driver.ts","../src/store/index.ts"],"sourcesContent":["/**\n * Opaque keyset cursors for the catalog list endpoints.\n *\n * A cursor is base64 of a JSON tuple of the sort keys of the last row on the\n * page. It is opaque by contract: callers echo it back and never parse it.\n * Orders keep their own older two-part encoding — cursors already issued to\n * SKU.io are live watermarks, so that format must not change.\n */\n\nconst toBase64 = (raw: string): string => {\n const bytes = new TextEncoder().encode(raw)\n let binary = ''\n for (const b of bytes) binary += String.fromCharCode(b)\n return btoa(binary).replace(/=+$/, '')\n}\n\nconst fromBase64 = (encoded: string): string => {\n const binary = atob(encoded)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n return new TextDecoder().decode(bytes)\n}\n\nexport const encodeKeysetCursor = (parts: (string | number)[]): string => toBase64(JSON.stringify(parts))\n\n/** Null when the cursor was not issued by this store, so the caller can 422 rather than guess. */\nexport function decodeKeysetCursor(cursor: string, length: number): (string | number)[] | null {\n try {\n const parts: unknown = JSON.parse(fromBase64(cursor))\n if (!Array.isArray(parts) || parts.length !== length) return null\n if (!parts.every((p) => typeof p === 'string' || typeof p === 'number')) return null\n return parts as (string | number)[]\n } catch {\n return null\n }\n}\n","import { Catalog } from '../catalog'\nimport type { Database, DbStatement } from '../db/driver'\nimport { slugify } from '../slug'\nimport type { Availability, Category, Money, Product, ProductImage } from '../types'\nimport { chunk, parseJson, placeholders, type StoreContext } from './context'\nimport { decodeKeysetCursor, encodeKeysetCursor } from './cursor'\nimport { invalid, notFound } from './errors'\n\n/** Product payload as the Store API and the seed script send it. */\nexport interface ProductInput {\n sku: string\n name: string\n slug?: string | null\n description_html?: string | null\n short_description_html?: string | null\n price: Money\n compare_at_price?: Money | null\n images?: { url: string; alt?: string | null }[]\n category_ids?: string[]\n attributes?: Record<string, string>\n active?: boolean\n position?: number\n /** Not part of the Store API; a migrated store's seed sets it. */\n legacy_path?: string | null\n}\n\nexport interface CategoryInput {\n id: string\n name: string\n slug?: string | null\n parent_id?: string | null\n description_html?: string | null\n image?: { url: string; alt?: string | null } | null\n position?: number\n}\n\ninterface ProductRow {\n sku: string\n name: string\n slug: string\n description_html: string | null\n short_description_html: string | null\n price_amount: number\n currency: string\n compare_at_amount: number | null\n attributes_json: string\n active: number\n position: number\n legacy_path: string | null\n created_at: string\n updated_at: string\n}\n\ninterface CategoryRow {\n id: string\n name: string\n slug: string\n parent_id: string | null\n description_html: string | null\n image_url: string | null\n image_alt: string | null\n position: number\n created_at: string\n updated_at: string\n}\n\n/** One product as the Store API reads it back — the whole row, not the shopper-facing projection. */\nexport interface ProductRecord {\n sku: string\n name: string\n slug: string\n descriptionHtml: string | null\n shortDescriptionHtml: string | null\n price: Money\n compareAtPrice: Money | null\n attributes: Record<string, string>\n active: boolean\n position: number\n legacyPath: string | null\n /** In the product's own order. `slug`/`name` are null for a link to a category the store never received. */\n categories: { id: string; slug: string | null; name: string | null }[]\n images: { url: string; alt: string | null; position: number }[]\n inventory: { available: number; allowBackorder: boolean; updatedAt: string } | null\n createdAt: string\n updatedAt: string\n}\n\nexport interface CategoryRecord {\n id: string\n parentId: string | null\n slug: string\n name: string\n descriptionHtml: string | null\n position: number\n productCount: number\n createdAt: string\n updatedAt: string\n}\n\nexport interface ProductPage {\n products: ProductRecord[]\n nextCursor: string | null\n}\n\nexport interface CategoryPage {\n categories: CategoryRecord[]\n nextCursor: string | null\n}\n\nexport interface ProductListQuery {\n updatedSince?: string | null\n cursor?: string | null\n limit?: number\n /** Omit for every product; true/false to filter on the storefront flag. */\n active?: boolean | null\n}\n\nexport interface CategoryListQuery {\n cursor?: string | null\n limit?: number\n}\n\ninterface InventoryRow {\n sku: string\n available: number\n allow_backorder: number\n updated_at: string\n}\n\ninterface CacheEntry {\n expires: number\n catalog: Promise<Catalog>\n}\n\n// One snapshot per database binding per isolate, refreshed on a short TTL and\n// dropped by every write this isolate makes.\nconst cache = new WeakMap<Database, CacheEntry>()\n\nexport function deriveAvailability(inv: { available: number; allow_backorder: number; updated_at?: string } | undefined, lowStockThreshold: number): Availability | undefined {\n if (!inv) return undefined\n const status =\n inv.available <= 0 ? (inv.allow_backorder ? 'backorder' : 'out_of_stock') : inv.available <= lowStockThreshold ? 'low_stock' : 'in_stock'\n return { status, quantity: inv.available, ...(inv.updated_at ? { updatedAt: inv.updated_at } : {}) }\n}\n\nexport class CatalogService {\n constructor(private readonly ctx: StoreContext, private readonly ttlMs: number) {}\n\n /** The catalog snapshot: active products with availability, and the category tree. */\n load(): Promise<Catalog> {\n const hit = cache.get(this.ctx.db)\n if (hit && hit.expires > Date.now()) return hit.catalog\n const catalog = this.build()\n if (this.ttlMs > 0) cache.set(this.ctx.db, { expires: Date.now() + this.ttlMs, catalog })\n catalog.catch(() => cache.delete(this.ctx.db))\n return catalog\n }\n\n invalidate(): void {\n cache.delete(this.ctx.db)\n }\n\n private async build(): Promise<Catalog> {\n const db = this.ctx.db\n const [products, categories, links, images, inventory] = await Promise.all([\n db.prepare(`SELECT * FROM products WHERE active = 1 ORDER BY position, name`).all<ProductRow>(),\n db.prepare(`SELECT * FROM categories ORDER BY position, name`).all<CategoryRow>(),\n db.prepare(`SELECT pc.sku, pc.category_id FROM product_categories pc JOIN products p ON p.sku = pc.sku WHERE p.active = 1 ORDER BY pc.position`).all<{ sku: string; category_id: string }>(),\n db.prepare(`SELECT pi.sku, pi.url, pi.alt FROM product_images pi JOIN products p ON p.sku = pi.sku WHERE p.active = 1 ORDER BY pi.position, pi.id`).all<{ sku: string; url: string; alt: string | null }>(),\n db.prepare(`SELECT * FROM inventory`).all<InventoryRow>(),\n ])\n const categoryIds = new Map<string, string[]>()\n for (const l of links.results) (categoryIds.get(l.sku) ?? categoryIds.set(l.sku, []).get(l.sku)!).push(l.category_id)\n const imagesBySku = new Map<string, ProductImage[]>()\n for (const i of images.results) (imagesBySku.get(i.sku) ?? imagesBySku.set(i.sku, []).get(i.sku)!).push({ src: i.url, ...(i.alt ? { alt: i.alt } : {}) })\n const inv = new Map(inventory.results.map((r) => [r.sku, r]))\n const threshold = this.ctx.config.inventory.lowStockThreshold\n\n const list: Product[] = products.results.map((r) => {\n const availability = deriveAvailability(inv.get(r.sku), threshold)\n return {\n id: r.sku,\n sku: r.sku,\n name: r.name,\n slug: r.slug,\n ...(r.description_html ? { description: r.description_html } : {}),\n ...(r.short_description_html ? { shortDescription: r.short_description_html } : {}),\n price: { amount: r.price_amount, currency: r.currency },\n ...(r.compare_at_amount != null ? { compareAtPrice: { amount: r.compare_at_amount, currency: r.currency } } : {}),\n categoryIds: categoryIds.get(r.sku) ?? [],\n images: imagesBySku.get(r.sku) ?? [],\n available: !availability || availability.status !== 'out_of_stock',\n ...(availability ? { availability } : {}),\n attributes: parseJson<Record<string, string>>(r.attributes_json, {}),\n position: r.position,\n ...(r.legacy_path ? { legacyPath: r.legacy_path } : {}),\n }\n })\n const cats: Category[] = categories.results.map((r) => ({\n id: r.id,\n name: r.name,\n slug: r.slug,\n parentId: r.parent_id,\n ...(r.description_html ? { description: r.description_html } : {}),\n ...(r.image_url ? { image: { src: r.image_url, ...(r.image_alt ? { alt: r.image_alt } : {}) } } : {}),\n position: r.position,\n }))\n return Catalog.fromData(list, cats)\n }\n\n async counts(): Promise<{ products: number; categories: number }> {\n const [p, c] = await Promise.all([\n this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM products WHERE active = 1`).first<{ n: number }>(),\n this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM categories`).first<{ n: number }>(),\n ])\n return { products: p?.n ?? 0, categories: c?.n ?? 0 }\n }\n\n /** Every product row including inactive ones — the Store API's view for `unknown_skus` checks. */\n async existingSkus(skus: string[]): Promise<Set<string>> {\n const found = new Set<string>()\n for (const part of chunk([...new Set(skus)])) {\n const { results } = await this.ctx.db\n .prepare(`SELECT sku FROM products WHERE sku IN (${placeholders(part.length)})`)\n .bind(...part)\n .all<{ sku: string }>()\n for (const r of results) found.add(r.sku)\n }\n return found\n }\n\n /**\n * Products in keyset order, for an operations system importing the catalog.\n * Ordered by `(updated_at, sku)` so a cursor is strictly monotonic and a\n * re-run from a watermark never repeats or skips a row.\n */\n async listProducts(query: ProductListQuery = {}): Promise<ProductPage> {\n const size = Math.min(Math.max(Math.trunc(query.limit ?? 50) || 50, 1), 200)\n const where: string[] = []\n const args: (string | number)[] = []\n if (query.cursor) {\n const parts = decodeKeysetCursor(query.cursor, 2)\n if (!parts) throw invalid('Invalid cursor', { cursor: 'is not a cursor this store issued' })\n where.push(`(updated_at, sku) > (?, ?)`)\n args.push(String(parts[0]), String(parts[1]))\n } else if (query.updatedSince) {\n where.push(`updated_at >= ?`)\n args.push(query.updatedSince)\n }\n if (query.active != null) {\n where.push(`active = ?`)\n args.push(query.active ? 1 : 0)\n }\n const sql = `SELECT * FROM products${where.length ? ` WHERE ${where.join(' AND ')}` : ''} ORDER BY updated_at ASC, sku ASC LIMIT ?`\n const { results } = await this.ctx.db.prepare(sql).bind(...args, size + 1).all<ProductRow>()\n const rows = results.slice(0, size)\n const last = rows[rows.length - 1]\n return {\n products: await this.hydrateProducts(rows),\n nextCursor: results.length > size && last ? encodeKeysetCursor([last.updated_at, last.sku]) : null,\n }\n }\n\n /** One product by SKU, active or not. Throws `not_found` when the store has never held it. */\n async requireProduct(sku: string): Promise<ProductRecord> {\n const row = await this.ctx.db.prepare(`SELECT * FROM products WHERE sku = ?`).bind(sku).first<ProductRow>()\n if (!row) throw notFound('Product')\n const [product] = await this.hydrateProducts([row])\n return product!\n }\n\n /**\n * The category tree as a flat list, parents before children within a branch.\n * `COALESCE(parent_id, '')` keeps every sort key non-null so the keyset\n * comparison can never drop a root category.\n */\n async listCategories(query: CategoryListQuery = {}): Promise<CategoryPage> {\n const size = Math.min(Math.max(Math.trunc(query.limit ?? 500) || 500, 1), 1000)\n const where: string[] = []\n const args: (string | number)[] = []\n if (query.cursor) {\n const parts = decodeKeysetCursor(query.cursor, 3)\n if (!parts) throw invalid('Invalid cursor', { cursor: 'is not a cursor this store issued' })\n where.push(`(COALESCE(c.parent_id, ''), c.position, c.id) > (?, ?, ?)`)\n args.push(String(parts[0]), Number(parts[1]), String(parts[2]))\n }\n const sql = `SELECT c.*, (SELECT COUNT(*) FROM product_categories pc WHERE pc.category_id = c.id) AS product_count\n FROM categories c${where.length ? ` WHERE ${where.join(' AND ')}` : ''}\n ORDER BY COALESCE(c.parent_id, '') ASC, c.position ASC, c.id ASC LIMIT ?`\n const { results } = await this.ctx.db.prepare(sql).bind(...args, size + 1).all<CategoryRow & { product_count: number }>()\n const rows = results.slice(0, size)\n const last = rows[rows.length - 1]\n return {\n categories: rows.map((r) => ({\n id: r.id,\n parentId: r.parent_id,\n slug: r.slug,\n name: r.name,\n descriptionHtml: r.description_html,\n position: r.position,\n productCount: r.product_count,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n })),\n nextCursor: results.length > size && last ? encodeKeysetCursor([last.parent_id ?? '', last.position, last.id]) : null,\n }\n }\n\n /** Attach categories, images and stock to a page of product rows. */\n private async hydrateProducts(rows: ProductRow[]): Promise<ProductRecord[]> {\n if (rows.length === 0) return []\n const skus = rows.map((r) => r.sku)\n const links = new Map<string, { id: string; slug: string | null; name: string | null }[]>()\n const images = new Map<string, { url: string; alt: string | null; position: number }[]>()\n const inventory = new Map<string, InventoryRow>()\n for (const part of chunk(skus)) {\n const ph = placeholders(part.length)\n const [l, i, inv] = await Promise.all([\n // LEFT JOIN: a link to a category the store never received still reports its id.\n this.ctx.db\n .prepare(`SELECT pc.sku, pc.category_id, c.slug, c.name FROM product_categories pc LEFT JOIN categories c ON c.id = pc.category_id WHERE pc.sku IN (${ph}) ORDER BY pc.position, pc.category_id`)\n .bind(...part)\n .all<{ sku: string; category_id: string; slug: string | null; name: string | null }>(),\n this.ctx.db\n .prepare(`SELECT sku, url, alt, position FROM product_images WHERE sku IN (${ph}) ORDER BY position, id`)\n .bind(...part)\n .all<{ sku: string; url: string; alt: string | null; position: number }>(),\n this.ctx.db.prepare(`SELECT * FROM inventory WHERE sku IN (${ph})`).bind(...part).all<InventoryRow>(),\n ])\n for (const r of l.results) (links.get(r.sku) ?? links.set(r.sku, []).get(r.sku)!).push({ id: r.category_id, slug: r.slug, name: r.name })\n for (const r of i.results) (images.get(r.sku) ?? images.set(r.sku, []).get(r.sku)!).push({ url: r.url, alt: r.alt, position: r.position })\n for (const r of inv.results) inventory.set(r.sku, r)\n }\n return rows.map((r) => {\n const stock = inventory.get(r.sku)\n return {\n sku: r.sku,\n name: r.name,\n slug: r.slug,\n descriptionHtml: r.description_html,\n shortDescriptionHtml: r.short_description_html,\n price: { amount: r.price_amount, currency: r.currency },\n compareAtPrice: r.compare_at_amount != null ? { amount: r.compare_at_amount, currency: r.currency } : null,\n attributes: parseJson<Record<string, string>>(r.attributes_json, {}),\n active: r.active === 1,\n position: r.position,\n legacyPath: r.legacy_path,\n categories: links.get(r.sku) ?? [],\n images: images.get(r.sku) ?? [],\n inventory: stock ? { available: stock.available, allowBackorder: stock.allow_backorder === 1, updatedAt: stock.updated_at } : null,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n }\n })\n }\n\n /**\n * Upsert products. Slugs: an explicit slug wins; an existing product keeps\n * its slug; a new one gets a slugified name, suffixed with the SKU when taken.\n */\n async upsertProducts(inputs: ProductInput[]): Promise<number> {\n if (inputs.length === 0) return 0\n const db = this.ctx.db\n const now = this.ctx.now()\n const skus = inputs.map((p) => p.sku)\n const existing = new Map<string, string>()\n for (const part of chunk(skus)) {\n const { results } = await db.prepare(`SELECT sku, slug FROM products WHERE sku IN (${placeholders(part.length)})`).bind(...part).all<{ sku: string; slug: string }>()\n for (const r of results) existing.set(r.sku, r.slug)\n }\n // Slug ownership across the whole table, so a new product never steals a URL.\n const wanted = inputs.map((p) => (p.slug?.trim() || existing.get(p.sku) || slugify(p.name)))\n const owners = new Map<string, string>()\n for (const part of chunk([...new Set(wanted)])) {\n const { results } = await db.prepare(`SELECT sku, slug FROM products WHERE slug IN (${placeholders(part.length)})`).bind(...part).all<{ sku: string; slug: string }>()\n for (const r of results) owners.set(r.slug, r.sku)\n }\n const taken = new Set<string>()\n const statements: DbStatement[] = []\n inputs.forEach((p, i) => {\n let slug = wanted[i]!\n const owner = owners.get(slug)\n if ((owner && owner !== p.sku) || taken.has(slug)) slug = `${slug}-${slugify(p.sku)}`\n taken.add(slug)\n if (p.price.currency.toLowerCase() !== this.ctx.config.currency.toLowerCase()) {\n throw invalid(`Product ${p.sku} is priced in ${p.price.currency}; this store sells in ${this.ctx.config.currency}`, { [`products.${i}.price.currency`]: 'must match the store currency' })\n }\n statements.push(\n db\n .prepare(\n `INSERT INTO products (sku, name, slug, description_html, short_description_html, price_amount, currency, compare_at_amount, attributes_json, active, position, legacy_path, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (sku) DO UPDATE SET\n name = excluded.name, slug = excluded.slug, description_html = excluded.description_html,\n short_description_html = excluded.short_description_html, price_amount = excluded.price_amount,\n currency = excluded.currency, compare_at_amount = excluded.compare_at_amount, attributes_json = excluded.attributes_json,\n active = excluded.active, position = excluded.position,\n legacy_path = COALESCE(excluded.legacy_path, products.legacy_path), updated_at = excluded.updated_at`\n )\n .bind(\n p.sku,\n p.name,\n slug,\n p.description_html ?? null,\n p.short_description_html ?? null,\n p.price.amount,\n p.price.currency.toLowerCase(),\n p.compare_at_price?.amount ?? null,\n JSON.stringify(p.attributes ?? {}),\n p.active === false ? 0 : 1,\n p.position ?? i,\n p.legacy_path ?? null,\n now,\n now\n )\n )\n statements.push(db.prepare(`DELETE FROM product_categories WHERE sku = ?`).bind(p.sku))\n ;[...new Set(p.category_ids ?? [])].forEach((categoryId, pos) => {\n statements.push(db.prepare(`INSERT INTO product_categories (sku, category_id, position) VALUES (?, ?, ?)`).bind(p.sku, categoryId, pos))\n })\n statements.push(db.prepare(`DELETE FROM product_images WHERE sku = ?`).bind(p.sku))\n ;(p.images ?? []).forEach((img, pos) => {\n statements.push(db.prepare(`INSERT INTO product_images (sku, url, alt, position) VALUES (?, ?, ?, ?)`).bind(p.sku, img.url, img.alt ?? null, pos))\n })\n })\n for (const part of chunk(statements, 250)) await db.batch(part)\n this.invalidate()\n return inputs.length\n }\n\n /**\n * Upsert categories, migration-safe. A category is identified by its id;\n * when the id is unknown but a category with the same slug under the same\n * parent already exists, that row is ADOPTED (re-keyed to the incoming id,\n * with its products and children moved along) instead of a suffixed\n * duplicate being created. When the id is known and a same-parent row owns\n * the natural slug, that duplicate is merged into it. A slug owned by a\n * category under a different parent, or claimed twice in one batch, gets a\n * suffix. Parents in the batch are resolved first, so a whole tree pushed\n * with new ids over a seeded tree lands on the existing nodes.\n */\n async upsertCategories(inputs: CategoryInput[]): Promise<number> {\n if (inputs.length === 0) return 0\n const db = this.ctx.db\n const now = this.ctx.now()\n\n // The table is small; resolve everything against one snapshot kept in step with the statements.\n const { results } = await db.prepare(`SELECT id, slug, parent_id FROM categories`).all<{ id: string; slug: string; parent_id: string | null }>()\n const rows = new Map(results.map((r) => [r.id, { id: r.id, slug: r.slug, parentId: r.parent_id }]))\n const bySlug = new Map(results.map((r) => [r.slug, r.id]))\n const batchIds = new Set(inputs.map((c) => c.id))\n const statements: DbStatement[] = []\n\n const reparent = (from: string, to: string) => {\n for (const r of rows.values()) if (r.parentId === from) r.parentId = to\n }\n /** Move the products and children of `from` onto `to`. */\n const moveRefs = (from: string, to: string) => {\n statements.push(db.prepare(`UPDATE OR IGNORE product_categories SET category_id = ? WHERE category_id = ?`).bind(to, from))\n statements.push(db.prepare(`DELETE FROM product_categories WHERE category_id = ?`).bind(from))\n statements.push(db.prepare(`UPDATE categories SET parent_id = ? WHERE parent_id = ?`).bind(to, from))\n reparent(from, to)\n }\n /** Unknown incoming id: the existing same-parent row becomes the incoming id. */\n const adopt = (existingId: string, incomingId: string) => {\n const row = rows.get(existingId)!\n statements.push(db.prepare(`UPDATE categories SET id = ? WHERE id = ?`).bind(incomingId, existingId))\n moveRefs(existingId, incomingId)\n rows.delete(existingId)\n rows.set(incomingId, { ...row, id: incomingId })\n bySlug.set(row.slug, incomingId)\n }\n /** Known incoming id: the same-parent duplicate is absorbed into it and deleted. */\n const merge = (duplicateId: string, intoId: string) => {\n const dup = rows.get(duplicateId)!\n moveRefs(duplicateId, intoId)\n statements.push(db.prepare(`DELETE FROM categories WHERE id = ?`).bind(duplicateId))\n rows.delete(duplicateId)\n bySlug.delete(dup.slug)\n }\n const sameParent = (ownerId: string, parentId: string | null) => (rows.get(ownerId)?.parentId ?? null) === parentId\n const freeSuffixed = (natural: string, id: string) => {\n let candidate = `${natural}-${slugify(id)}`\n for (let n = 2; bySlug.has(candidate); n++) candidate = `${natural}-${slugify(id)}-${n}`\n return candidate\n }\n\n for (const c of orderParentsFirst(inputs)) {\n const parentId = c.parent_id ?? null\n const existing = rows.get(c.id)\n const explicit = c.slug?.trim() || null\n const natural = explicit ?? slugify(c.name)\n let slug: string\n if (existing && !explicit) {\n // Slugs stay stable once set — unless a same-parent duplicate owns the natural one, which is healed by merging.\n const owner = bySlug.get(natural)\n if (owner && owner !== c.id && sameParent(owner, parentId) && !batchIds.has(owner)) {\n merge(owner, c.id)\n bySlug.delete(existing.slug)\n slug = natural\n } else {\n slug = existing.slug\n }\n } else {\n const owner = bySlug.get(natural)\n if (!owner || owner === c.id) {\n slug = natural\n } else if (sameParent(owner, parentId) && !batchIds.has(owner)) {\n if (existing) {\n merge(owner, c.id)\n bySlug.delete(existing.slug)\n } else {\n adopt(owner, c.id)\n }\n slug = natural\n } else {\n slug = existing?.slug === natural ? natural : freeSuffixed(natural, c.id)\n if (existing) bySlug.delete(existing.slug)\n }\n }\n statements.push(\n db\n .prepare(\n `INSERT INTO categories (id, name, slug, parent_id, description_html, image_url, image_alt, position, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n name = excluded.name, slug = excluded.slug, parent_id = excluded.parent_id, description_html = excluded.description_html,\n image_url = excluded.image_url, image_alt = excluded.image_alt, position = excluded.position, updated_at = excluded.updated_at`\n )\n .bind(c.id, c.name, slug, parentId, c.description_html ?? null, c.image?.url ?? null, c.image?.alt ?? null, c.position ?? inputs.indexOf(c), now, now)\n )\n rows.set(c.id, { id: c.id, slug, parentId })\n bySlug.set(slug, c.id)\n }\n for (const part of chunk(statements, 250)) await db.batch(part)\n this.invalidate()\n return inputs.length\n }\n\n /** Deactivate: the product disappears from the store but stays for order history. */\n async deactivateProduct(sku: string): Promise<boolean> {\n const result = await this.ctx.db.prepare(`UPDATE products SET active = 0, updated_at = ? WHERE sku = ?`).bind(this.ctx.now(), sku).run()\n this.invalidate()\n return (result.meta?.changes ?? 0) > 0\n }\n}\n\n/** Parents before children, so an adopted parent is keyed to its incoming id before its children look it up. */\nfunction orderParentsFirst(inputs: CategoryInput[]): CategoryInput[] {\n const pending = new Map(inputs.map((c) => [c.id, c]))\n const done = new Set<string>()\n const out: CategoryInput[] = []\n let progressed = true\n while (pending.size && progressed) {\n progressed = false\n for (const [id, c] of pending) {\n const parent = c.parent_id ?? null\n if (parent === null || !pending.has(parent) || done.has(parent)) {\n out.push(c)\n done.add(id)\n pending.delete(id)\n progressed = true\n }\n }\n }\n // A cycle is an inconsistent payload; take the rest in the order given.\n out.push(...pending.values())\n return out\n}\n","import type { Customer } from '../types'\nimport type { StoreContext } from './context'\n\ninterface CustomerRow {\n id: string\n email: string\n name: string | null\n phone: string | null\n created_at: string\n updated_at: string\n}\n\nconst toCustomer = (r: CustomerRow): Customer => ({\n id: r.id,\n email: r.email,\n name: r.name,\n phone: r.phone,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n})\n\nexport const normalizeEmail = (email: string) => email.trim().toLowerCase()\n\nexport class CustomersService {\n constructor(private readonly ctx: StoreContext) {}\n\n async byId(id: string): Promise<Customer | null> {\n const row = await this.ctx.db.prepare(`SELECT * FROM customers WHERE id = ?`).bind(id).first<CustomerRow>()\n return row ? toCustomer(row) : null\n }\n\n async byEmail(email: string): Promise<Customer | null> {\n const row = await this.ctx.db.prepare(`SELECT * FROM customers WHERE email = ?`).bind(normalizeEmail(email)).first<CustomerRow>()\n return row ? toCustomer(row) : null\n }\n\n async count(): Promise<number> {\n const row = await this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM customers`).first<{ n: number }>()\n return row?.n ?? 0\n }\n}\n","import type { Address, Fulfillment, Order, OrderLine, OrderStatus, PaymentStatus } from '../types'\nimport { parseJson } from './context'\n\nexport interface OrderRow {\n id: string\n number: number\n status: string\n payment_status: string\n payment_provider: string | null\n payment_reference: string | null\n payment_method_label: string | null\n paid_at: string | null\n currency: string\n subtotal: number\n shipping: number\n tax: number\n discount: number\n total: number\n customer_id: string\n email: string\n customer_name: string | null\n customer_phone: string | null\n shipping_address_json: string\n billing_address_json: string | null\n shipping_method_code: string | null\n shipping_method_name: string | null\n external_reference: string | null\n notes: string | null\n metadata_json: string\n access_key: string\n created_at: string\n updated_at: string\n cancelled_at: string | null\n discount_code: string | null\n discount_partner: string | null\n partner_commission: number\n}\n\nexport interface OrderLineRow {\n id: string\n order_id: string\n sku: string\n name: string\n quantity: number\n unit_amount: number\n total_amount: number\n fulfilled_quantity: number\n image_url: string | null\n position: number\n}\n\nexport interface FulfillmentRow {\n id: string\n order_id: string\n external_id: string\n carrier: string | null\n service: string | null\n tracking_number: string | null\n tracking_url: string | null\n shipped_at: string | null\n notified_at: string | null\n created_at: string\n}\n\nexport interface FulfillmentLineRow {\n fulfillment_id: string\n sku: string\n quantity: number\n}\n\nexport const ORDER_SELECT = `SELECT o.*, c.name AS customer_name, c.phone AS customer_phone FROM orders o JOIN customers c ON c.id = o.customer_id`\n\n/** Strip undefined so an address stored as JSON round-trips cleanly. */\nexport function cleanAddress(a: Address | null | undefined): Address | null {\n if (!a) return null\n const out: Address = {}\n for (const key of ['name', 'line1', 'line2', 'city', 'state', 'postalCode', 'country'] as const) {\n const v = a[key]\n if (typeof v === 'string' && v.trim()) out[key] = v.trim()\n }\n return out\n}\n\nexport function rowToLine(r: OrderLineRow, currency: string): OrderLine {\n return {\n id: r.id,\n sku: r.sku,\n name: r.name,\n quantity: r.quantity,\n unitAmount: { amount: r.unit_amount, currency },\n totalAmount: { amount: r.total_amount, currency },\n fulfilledQuantity: r.fulfilled_quantity,\n ...(r.image_url ? { image: r.image_url } : {}),\n }\n}\n\nexport function rowToFulfillment(r: FulfillmentRow, lines: FulfillmentLineRow[]): Fulfillment {\n return {\n id: r.id,\n externalId: r.external_id,\n carrier: r.carrier,\n service: r.service,\n trackingNumber: r.tracking_number,\n trackingUrl: r.tracking_url,\n shippedAt: r.shipped_at,\n lines: lines.map((l) => ({ sku: l.sku, quantity: l.quantity })),\n createdAt: r.created_at,\n }\n}\n\nexport function rowToOrder(r: OrderRow, lines: OrderLineRow[], fulfillments: Fulfillment[]): Order {\n const money = (amount: number) => ({ amount, currency: r.currency })\n return {\n id: r.id,\n number: String(r.number),\n status: r.status as OrderStatus,\n payment: {\n status: r.payment_status as PaymentStatus,\n provider: r.payment_provider,\n reference: r.payment_reference,\n methodLabel: r.payment_method_label,\n paidAt: r.paid_at,\n },\n currency: r.currency,\n subtotal: money(r.subtotal),\n shipping: money(r.shipping),\n tax: money(r.tax),\n discount: money(r.discount),\n discountCode: r.discount_code ?? null,\n partner: r.discount_partner ? { id: r.discount_partner, commission: money(r.partner_commission ?? 0) } : null,\n total: money(r.total),\n customer: { id: r.customer_id, email: r.email, name: r.customer_name, phone: r.customer_phone },\n shippingAddress: parseJson<Address>(r.shipping_address_json, {}),\n billingAddress: r.billing_address_json ? parseJson<Address | null>(r.billing_address_json, null) : null,\n shippingMethod: r.shipping_method_code && r.shipping_method_name ? { code: r.shipping_method_code, name: r.shipping_method_name } : null,\n lines: lines.map((l) => rowToLine(l, r.currency)),\n fulfillments,\n externalReference: r.external_reference,\n notes: r.notes,\n metadata: parseJson<Record<string, string>>(r.metadata_json, {}),\n accessKey: r.access_key,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n paidAt: r.paid_at,\n cancelledAt: r.cancelled_at,\n }\n}\n","import { checkAddressFormat } from '../address'\nimport { MAX_CART_LINES } from '../cart'\nimport { commissionAmount, discountAmount, findDiscount, normalizeDiscountCode, quantityTier, type DiscountCode, type QuantityDiscount } from '../discounts'\nimport type { DbStatement } from '../db/driver'\nimport { regionForCountry, shippingForRegion, type ShippingOption } from '../shipping'\nimport type { Address, Order } from '../types'\nimport type { CatalogService } from './catalog'\nimport type { StoreContext } from './context'\nimport { newId, randomToken } from './crypto'\nimport { normalizeEmail } from './customers'\nimport { invalid } from './errors'\nimport type { EventsService } from './events'\nimport type { OrdersService } from './orders'\nimport { cleanAddress } from './rows'\n\nexport interface CheckoutItem {\n sku: string\n quantity: number\n}\n\nexport interface CheckoutInput {\n items: CheckoutItem[]\n email: string\n name?: string | null\n phone?: string | null\n shippingAddress: Address\n billingAddress?: Address | null\n shippingOptionId: string\n /** A discount code the buyer entered or arrived with; validated against the store's configuration. */\n discountCode?: string | null\n notes?: string | null\n metadata?: Record<string, string>\n /**\n * The contact and address came from a wallet (PayPal express): the buyer typed nothing, so a phone\n * the store would normally require is optional — wallets share one only if the buyer allows it.\n */\n walletContact?: boolean\n}\n\nexport interface PricedLine {\n sku: string\n name: string\n quantity: number\n unitAmount: number\n totalAmount: number\n image: string | null\n}\n\nexport interface Quote {\n lines: PricedLine[]\n subtotal: number\n /** Minor units off the merchandise subtotal; 0 without a code. */\n discount: number\n /** The code the buyer used, or null. Kept for attribution even when a quantity tier saved more. */\n discountCode: DiscountCode | null\n /** The quantity tier that priced `discount` instead of the code, or null. */\n quantityTier: QuantityDiscount | null\n /** What the code's partner is owed for this order, minor units; 0 without a partner. */\n commission: number\n shipping: number\n total: number\n currency: string\n option: ShippingOption\n}\n\n/** An order with every amount already decided, ready to insert. Amounts are minor units in `currency`. */\nexport interface PricedOrderSpec {\n email: string\n name: string | null\n phone: string | null\n currency: string\n lines: PricedLine[]\n subtotal: number\n shipping: number\n tax: number\n discount: number\n total: number\n shippingAddress: Address\n billingAddress: Address | null\n shippingMethod: { code: string; name: string } | null\n notes: string | null\n metadata: Record<string, string>\n discountCode: string | null\n discountPartner: string | null\n commission: number\n /** Extra fields for the order's `created` event. */\n createdEvent: Record<string, unknown>\n}\n\nexport const EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport class CheckoutService {\n constructor(\n private readonly ctx: StoreContext,\n private readonly catalog: CatalogService,\n private readonly orders: OrdersService,\n private readonly events: EventsService\n ) {}\n\n /** Shipping options a destination may choose from. */\n optionsForCountry(country: string): ShippingOption[] {\n const region = regionForCountry(this.ctx.config.shipping, this.ctx.config.shipToCountries, country)\n return region ? shippingForRegion(this.ctx.config.shipping, this.ctx.config.shipToCountries, region).options : []\n }\n\n /**\n * Price a set of lines from the catalog. Quantities are clamped to the\n * store's per-line maximum; unknown or unavailable SKUs fail validation.\n */\n async quote(items: CheckoutItem[], country: string, shippingOptionId: string, discountCode?: string | null): Promise<Quote> {\n const details: Record<string, string> = {}\n if (!items.length) details.items = 'is empty'\n if (items.length > MAX_CART_LINES) details.items = `has more than ${MAX_CART_LINES} lines`\n const catalog = await this.catalog.load()\n const merged = new Map<string, number>()\n for (const item of items) merged.set(item.sku, (merged.get(item.sku) ?? 0) + clampQuantity(item.quantity, this.ctx.config.checkout.maxQuantity))\n const lines: PricedLine[] = []\n for (const [sku, quantity] of merged) {\n const product = catalog.product(sku)\n if (!product) {\n details[`items.${sku}`] = 'is not a product in this store'\n continue\n }\n if (!product.available) {\n details[`items.${sku}`] = 'is sold out'\n continue\n }\n if (product.price.currency.toLowerCase() !== this.ctx.config.currency.toLowerCase()) {\n details[`items.${sku}`] = 'is priced in another currency'\n continue\n }\n const q = clampQuantity(quantity, this.ctx.config.checkout.maxQuantity)\n lines.push({ sku, name: product.name, quantity: q, unitAmount: product.price.amount, totalAmount: product.price.amount * q, image: product.images[0]?.src ?? null })\n }\n const options = this.optionsForCountry(country)\n if (!options.length) details['shipping_address.country'] = 'is not a country this store ships to'\n const option = options.find((o) => o.id === shippingOptionId)\n if (options.length && !option) details.shipping_option = 'is not available for this destination'\n // A code the buyer sees as applied must still be valid when the order is priced; never drop it silently.\n const code = discountCode && normalizeDiscountCode(discountCode) ? findDiscount(this.ctx.config.discounts, discountCode, new Date(this.ctx.now())) : null\n if (discountCode && normalizeDiscountCode(discountCode) && !code) details.discount_code = 'is not a valid code'\n if (Object.keys(details).length || !option) throw invalid('Checkout could not be priced', details)\n const subtotal = lines.reduce((sum, l) => sum + l.totalAmount, 0)\n const codeOff = code ? discountAmount(code, subtotal) : 0\n const tier = quantityTier(this.ctx.config.quantityDiscounts, lines.reduce((n, l) => n + l.quantity, 0))\n const tierOff = tier ? Math.min(subtotal, Math.round((subtotal * tier.percentOff) / 100)) : 0\n // Never stacked: the larger saving wins. The code still attributes the order either way.\n const discount = Math.max(codeOff, tierOff)\n const commission = code ? commissionAmount(code, subtotal, discount) : 0\n return {\n lines,\n subtotal,\n discount,\n discountCode: code,\n quantityTier: tierOff > codeOff ? tier : null,\n commission,\n shipping: option.price.amount,\n total: subtotal - discount + option.price.amount,\n currency: this.ctx.config.currency,\n option,\n }\n }\n\n /**\n * Price items for a destination using its first shipping option, for flows where the buyer never\n * picks one (express checkout: the address arrives from PayPal). Same rules and errors as quote().\n */\n async quoteDefault(items: CheckoutItem[], country: string, discountCode?: string | null): Promise<Quote> {\n return this.quote(items, country, this.optionsForCountry(country)[0]?.id ?? '', discountCode)\n }\n\n /** Create a pending order the payment adapter can take from here. */\n async createOrder(input: CheckoutInput): Promise<Order> {\n const details: Record<string, string> = {}\n const email = normalizeEmail(input.email ?? '')\n if (!EMAIL.test(email)) details.email = 'must be a valid email address'\n const shipTo = checkShippingAddress(input.shippingAddress, details)\n if (this.ctx.config.checkout.collectPhone && !input.walletContact && !input.phone?.trim()) details.phone = 'is required'\n let quote: Quote | null = null\n try {\n quote = await this.quote(input.items, shipTo.country ?? '', input.shippingOptionId, input.discountCode)\n } catch (error) {\n if (error instanceof Error && 'details' in error) Object.assign(details, (error as { details: Record<string, string> | null }).details ?? {})\n else throw error\n }\n if (Object.keys(details).length || !quote) throw invalid('Checkout is incomplete', details)\n\n const metadata = Object.fromEntries(Object.entries(input.metadata ?? {}).filter(([, v]) => typeof v === 'string').slice(0, 20))\n return this.placePricedOrder({\n email,\n name: input.name?.trim() || shipTo.name || null,\n phone: input.phone?.trim() || null,\n currency: quote.currency,\n lines: quote.lines,\n subtotal: quote.subtotal,\n shipping: quote.shipping,\n tax: 0,\n discount: quote.discount,\n total: quote.total,\n shippingAddress: shipTo,\n billingAddress: input.billingAddress ? cleanAddress(input.billingAddress) : null,\n shippingMethod: { code: quote.option.id, name: quote.option.label },\n notes: input.notes ?? null,\n metadata,\n discountCode: quote.discountCode ? normalizeDiscountCode(quote.discountCode.code) : null,\n discountPartner: quote.discountCode?.partner ?? null,\n commission: quote.commission,\n createdEvent: {\n ...(quote.discountCode ? { discount_code: normalizeDiscountCode(quote.discountCode.code) } : {}),\n ...(quote.quantityTier ? { quantity_tier: quote.quantityTier.minQuantity } : {}),\n ...(quote.discount ? { discount: quote.discount } : {}),\n },\n })\n }\n\n /**\n * Insert an order whose every amount is already decided. The checkout prices from the catalog first; an accepted\n * store quote (QuotesService.accept) passes the offered prices. Nothing on the Store API reaches this: an order is\n * only ever placed by a buyer.\n */\n async placePricedOrder(spec: PricedOrderSpec): Promise<Order> {\n const db = this.ctx.db\n const now = this.ctx.now()\n const orderId = newId('ord')\n const statements: DbStatement[] = [\n db\n .prepare(\n `INSERT INTO customers (id, email, name, phone, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT (email) DO UPDATE SET name = COALESCE(excluded.name, customers.name), phone = COALESCE(excluded.phone, customers.phone), updated_at = excluded.updated_at`\n )\n .bind(newId('cus'), spec.email, spec.name, spec.phone, now, now),\n db\n .prepare(\n `INSERT INTO orders (id, number, status, payment_status, currency, subtotal, shipping, tax, discount, total, customer_id, email,\n shipping_address_json, billing_address_json, shipping_method_code, shipping_method_name, notes, metadata_json, access_key, created_at, updated_at,\n discount_code, discount_partner, partner_commission)\n VALUES (?, (SELECT MAX(COALESCE(MAX(number), 0) + 1, ?) FROM orders), 'pending_payment', 'unpaid', ?, ?, ?, ?, ?, ?, (SELECT id FROM customers WHERE email = ?), ?,\n ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .bind(\n orderId,\n this.ctx.config.orders.numberStart,\n spec.currency,\n spec.subtotal,\n spec.shipping,\n spec.tax,\n spec.discount,\n spec.total,\n spec.email,\n spec.email,\n JSON.stringify(spec.shippingAddress),\n spec.billingAddress ? JSON.stringify(spec.billingAddress) : null,\n spec.shippingMethod?.code ?? null,\n spec.shippingMethod?.name ?? null,\n spec.notes?.trim().slice(0, 2000) || null,\n JSON.stringify(spec.metadata),\n randomToken(16),\n now,\n now,\n spec.discountCode,\n spec.discountPartner,\n spec.commission\n ),\n ...spec.lines.map((l, i) =>\n db\n .prepare(`INSERT INTO order_lines (id, order_id, sku, name, quantity, unit_amount, total_amount, fulfilled_quantity, image_url, position) VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?)`)\n .bind(newId('lin'), orderId, l.sku, l.name, l.quantity, l.unitAmount, l.totalAmount, l.image, i)\n ),\n this.events.statement(orderId, 'created', { lines: spec.lines.length, total: spec.total, ...spec.createdEvent }),\n ]\n await db.batch(statements)\n return this.orders.require(orderId)\n }\n}\n\n/**\n * Clean a shipping address and report what it is missing into `details` (keys `shipping_address.*`). The country is\n * upper-cased and the country's own format (state, postal code) is checked.\n */\nexport function checkShippingAddress(address: Address | null | undefined, details: Record<string, string>): Address {\n const shipTo = cleanAddress(address) ?? {}\n if (!shipTo.name) details['shipping_address.name'] = 'is required'\n if (!shipTo.line1) details['shipping_address.line1'] = 'is required'\n if (!shipTo.city) details['shipping_address.city'] = 'is required'\n if (!shipTo.country || shipTo.country.length !== 2) details['shipping_address.country'] = 'is required'\n else shipTo.country = shipTo.country.toUpperCase()\n if (shipTo.country) Object.assign(details, checkAddressFormat(shipTo))\n return shipTo\n}\n\nfunction clampQuantity(n: number, max: number): number {\n return Number.isFinite(n) ? Math.min(Math.max(Math.trunc(n), 1), max) : 1\n}\n","import { findDiscount, normalizeDiscountCode, publicDiscount, type PartnerProfile, type PublicDiscount } from '../discounts'\nimport { partnerSignInMail } from '../mail/templates'\nimport type { Money } from '../types'\nimport { parseJson, type StoreContext } from './context'\nimport { hashToken, randomToken, secretsEqual, sha256Hex } from './crypto'\nimport { StoreError } from './errors'\n\nexport const PARTNER_LINK_MINUTES = 15\nexport const PARTNER_SESSION_DAYS = 30\n/** A second visit from the same visitor within this window is the same visit. */\nconst VISIT_DEDUPE_MINUTES = 30\n/**\n * Fetchers that open a link without a person behind them: link previews (a link pasted in\n * WhatsApp, X, Slack, Discord, Telegram or iMessage is fetched once to draw its card), search\n * crawlers and scripts. Counting them would credit a partner with a \"visit\" for every chat the\n * link is pasted into. In-app browsers (Instagram, Facebook's FBAN) are people and do not match.\n */\nconst NOT_A_PERSON = /bot\\b|bot\\/|crawl|spider|slurp|preview|facebookexternalhit|meta-externalagent|whatsapp|telegram|slack|discord|skypeuripreview|embedly|vkshare|pinterest\\/|bitlybot|headless|curl\\/|wget|python-requests|go-http-client|node-fetch|axios/i\n\n/** A user agent that belongs to a link preview, crawler or script rather than a person. */\nexport function isAutomatedVisit(userAgent: string | null | undefined): boolean {\n return !userAgent || NOT_A_PERSON.test(userAgent)\n}\n\nexport interface VisitInput {\n partner: string\n code: string\n /** Used only to derive the daily visitor hash; never stored. */\n ip: string | null\n userAgent: string | null\n source?: string | null\n medium?: string | null\n campaign?: string | null\n landingPath?: string | null\n country?: string | null\n}\n\nexport interface TrafficDay {\n day: string\n visits: number\n visitors: number\n checkouts: number\n orders: number\n}\n\nexport interface TrafficSource {\n source: string\n visits: number\n visitors: number\n orders: number\n sales: Money\n}\n\nexport interface PartnerTraffic {\n days: number\n totals: { visits: number; visitors: number; checkouts: number; orders: number; conversion: number; sales: Money; commission: Money }\n byDay: TrafficDay[]\n bySource: TrafficSource[]\n byCountry: { country: string; visits: number }[]\n}\n\nexport interface PartnerReportOptions {\n /** ISO lower bound on when the order was placed. */\n since?: string\n /** Days after payment before a commission counts as payable (the refund window). Default 30. */\n holdDays?: number\n}\n\n/** One attributed order as a partner may see it: no buyer name, email or address beyond the country. */\nexport interface PartnerReportLine {\n number: string\n placedAt: string\n paidAt: string | null\n country: string | null\n units: number\n /** Merchandise after the discount; shipping and tax excluded. */\n merchandise: Money\n commission: Money\n state: 'payable' | 'held' | 'void'\n}\n\nexport interface PartnerReport {\n partner: string\n codes: PublicDiscount[]\n lines: PartnerReportLine[]\n totals: { orders: number; units: number; merchandise: Money; payable: Money; held: Money }\n}\n\ninterface Row {\n number: number\n created_at: string\n paid_at: string | null\n status: string\n payment_status: string\n currency: string\n subtotal: number\n discount: number\n partner_commission: number\n shipping_address_json: string\n units: number | null\n}\n\n/**\n * Who sold what with which code. A partner's report reads the store's own\n * orders, so it covers every payment adapter; it is shown to the partner\n * through a link keyed by an HMAC of their id, never by a password.\n */\nexport class PartnersService {\n constructor(private readonly ctx: StoreContext) {}\n\n /** A partner's public profile; falls back to the id as the name when none is configured. */\n profile(id: string): PartnerProfile {\n return this.ctx.config.partners.find((p) => p.id === id) ?? { id, name: id }\n }\n\n /** The partner a code attributes to, with its public discount — for the welcome banner. */\n referral(code: string | null | undefined): { partner: PartnerProfile; discount: PublicDiscount } | null {\n const d = findDiscount(this.ctx.config.discounts, code, new Date(this.ctx.now()))\n if (!d?.partner) return null\n // Public fields only: this feeds a page. The contact's name is public (the banner greets\n // visitors as their friend); their email is not.\n const { id, name, contact, welcome, avatar } = this.profile(d.partner)\n return { partner: { id, name, ...(contact ? { contact } : {}), ...(welcome ? { welcome } : {}), ...(avatar ? { avatar } : {}) }, discount: publicDiscount(d, this.ctx.config.currency, this.ctx.config.locale) }\n }\n\n // ── Traffic ─────────────────────────────────────────────────────────────\n\n /** Record a visit through a partner link. Same visitor within 30 minutes counts once. Returns whether it was recorded. */\n async recordVisit(v: VisitInput): Promise<boolean> {\n if (!this.partners().includes(v.partner) || isAutomatedVisit(v.userAgent)) return false\n const now = this.ctx.now()\n const visitor = (await sha256Hex(`${this.ctx.env.SESSION_SECRET ?? ''}|${now.slice(0, 10)}|${v.ip ?? ''}|${v.userAgent ?? ''}`)).slice(0, 20)\n const since = new Date(Date.parse(now) - VISIT_DEDUPE_MINUTES * 60_000).toISOString()\n const seen = await this.ctx.db\n .prepare(`SELECT 1 AS x FROM referral_visits WHERE partner = ? AND visitor = ? AND created_at >= ? LIMIT 1`)\n .bind(v.partner, visitor, since)\n .first<{ x: number }>()\n if (seen) return false\n const clip = (x: string | null | undefined, n: number) => (x ? x.slice(0, n) : null)\n await this.ctx.db\n .prepare(`INSERT INTO referral_visits (partner, code, visitor, source, medium, campaign, landing_path, country, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)\n .bind(v.partner, normalizeDiscountCode(v.code), visitor, clip(v.source?.toLowerCase(), 60) ?? 'direct', clip(v.medium?.toLowerCase(), 60), clip(v.campaign, 80), clip(v.landingPath, 200), clip(v.country?.toUpperCase(), 2), now)\n .run()\n return true\n }\n\n /**\n * The funnel for one partner over the last `days`: visits and unique visitors through their\n * links, checkouts started with their code, paid orders, conversion (paid ÷ unique visitors),\n * by day, by source and by country. Orders carry the source of the visit that brought them\n * (metadata `ref_source`) so a channel's sales can be told apart; a spoken code shows as 'code'.\n */\n async traffic(partner: string, opts: { days?: number } = {}): Promise<PartnerTraffic> {\n const days = Math.min(Math.max(opts.days ?? 30, 1), 365)\n const currency = this.ctx.config.currency\n const now = Date.parse(this.ctx.now())\n const since = new Date(now - days * 86_400_000).toISOString()\n const db = this.ctx.db\n const visitsByDay = (\n await db\n .prepare(`SELECT substr(created_at, 1, 10) AS day, COUNT(*) AS visits, COUNT(DISTINCT visitor) AS visitors FROM referral_visits WHERE partner = ? AND created_at >= ? GROUP BY day`)\n .bind(partner, since)\n .all<{ day: string; visits: number; visitors: number }>()\n ).results\n const ordersByDay = (\n await db\n .prepare(\n `SELECT substr(created_at, 1, 10) AS day, COUNT(*) AS checkouts, SUM(CASE WHEN payment_status = 'paid' THEN 1 ELSE 0 END) AS orders\n FROM orders WHERE discount_partner = ? AND created_at >= ? GROUP BY day`\n )\n .bind(partner, since)\n .all<{ day: string; checkouts: number; orders: number }>()\n ).results\n const byDayMap = new Map<string, TrafficDay>()\n for (let i = days - 1; i >= 0; i--) {\n const day = new Date(now - i * 86_400_000).toISOString().slice(0, 10)\n byDayMap.set(day, { day, visits: 0, visitors: 0, checkouts: 0, orders: 0 })\n }\n for (const r of visitsByDay) Object.assign(byDayMap.get(r.day) ?? {}, { visits: r.visits, visitors: r.visitors })\n for (const r of ordersByDay) Object.assign(byDayMap.get(r.day) ?? {}, { checkouts: r.checkouts, orders: r.orders ?? 0 })\n\n const totalsRow = await db\n .prepare(`SELECT COUNT(*) AS visits, COUNT(DISTINCT visitor || substr(created_at, 1, 10)) AS visitors FROM referral_visits WHERE partner = ? AND created_at >= ?`)\n .bind(partner, since)\n .first<{ visits: number; visitors: number }>()\n const orderTotals = await db\n .prepare(\n `SELECT COUNT(*) AS checkouts,\n SUM(CASE WHEN payment_status = 'paid' THEN 1 ELSE 0 END) AS orders,\n SUM(CASE WHEN payment_status = 'paid' THEN subtotal - discount ELSE 0 END) AS sales,\n SUM(CASE WHEN payment_status = 'paid' THEN partner_commission ELSE 0 END) AS commission\n FROM orders WHERE discount_partner = ? AND created_at >= ?`\n )\n .bind(partner, since)\n .first<{ checkouts: number; orders: number | null; sales: number | null; commission: number | null }>()\n\n const sources = (\n await db\n .prepare(`SELECT COALESCE(source, 'direct') AS source, COUNT(*) AS visits, COUNT(DISTINCT visitor) AS visitors FROM referral_visits WHERE partner = ? AND created_at >= ? GROUP BY source ORDER BY visits DESC LIMIT 20`)\n .bind(partner, since)\n .all<{ source: string; visits: number; visitors: number }>()\n ).results\n const orderSources = (\n await db\n .prepare(\n `SELECT COALESCE(json_extract(metadata_json, '$.ref_source'), 'code') AS source, COUNT(*) AS orders, SUM(subtotal - discount) AS sales\n FROM orders WHERE discount_partner = ? AND created_at >= ? AND payment_status = 'paid' GROUP BY source`\n )\n .bind(partner, since)\n .all<{ source: string; orders: number; sales: number }>()\n ).results\n const bySourceMap = new Map<string, TrafficSource>()\n for (const r of sources) bySourceMap.set(r.source, { source: r.source, visits: r.visits, visitors: r.visitors, orders: 0, sales: { amount: 0, currency } })\n for (const r of orderSources) {\n const row = bySourceMap.get(r.source) ?? { source: r.source, visits: 0, visitors: 0, orders: 0, sales: { amount: 0, currency } }\n row.orders = r.orders\n row.sales = { amount: r.sales ?? 0, currency }\n bySourceMap.set(r.source, row)\n }\n const byCountry = (\n await db\n .prepare(`SELECT COALESCE(country, '??') AS country, COUNT(*) AS visits FROM referral_visits WHERE partner = ? AND created_at >= ? GROUP BY country ORDER BY visits DESC LIMIT 10`)\n .bind(partner, since)\n .all<{ country: string; visits: number }>()\n ).results\n\n const visitors = totalsRow?.visitors ?? 0\n const orders = orderTotals?.orders ?? 0\n return {\n days,\n totals: {\n visits: totalsRow?.visits ?? 0,\n visitors,\n checkouts: orderTotals?.checkouts ?? 0,\n orders,\n conversion: visitors ? orders / visitors : 0,\n sales: { amount: orderTotals?.sales ?? 0, currency },\n commission: { amount: orderTotals?.commission ?? 0, currency },\n },\n byDay: [...byDayMap.values()],\n bySource: [...bySourceMap.values()].sort((a, b) => b.visits - a.visits || b.orders - a.orders),\n byCountry,\n }\n }\n\n // ── Sign-in ─────────────────────────────────────────────────────────────\n\n private tokenHash(token: string) {\n return hashToken(`partner:${token}`, this.ctx.env.SESSION_SECRET)\n }\n\n /** Email a sign-in link when the address belongs to a partner. Answers the same either way. */\n async sendSignInLink(email: string): Promise<boolean> {\n const wanted = email.trim().toLowerCase()\n const partner = this.ctx.config.partners.find((p) => p.email?.toLowerCase() === wanted)\n if (!partner?.email) return false\n const token = randomToken(32)\n const now = this.ctx.now()\n const expiresAt = new Date(Date.parse(now) + PARTNER_LINK_MINUTES * 60_000).toISOString()\n await this.ctx.db\n .prepare(`INSERT INTO partner_magic_links (token_hash, partner, expires_at, created_at) VALUES (?, ?, ?, ?)`)\n .bind(await this.tokenHash(token), partner.id, expiresAt, now)\n .run()\n const url = `${this.ctx.siteUrl}/partners/verify/?token=${token}`\n const mail = partnerSignInMail({ url, name: (partner.contact ?? partner.name).split(/\\s+/)[0]!, expiresMinutes: PARTNER_LINK_MINUTES }, { config: this.ctx.config, siteUrl: this.ctx.siteUrl })\n await this.ctx.mail.send({ to: partner.email, ...mail })\n return true\n }\n\n /** Exchange a sign-in link for a session. Throws `not_found` when the link is unknown, used or expired. */\n async exchange(token: string): Promise<{ token: string; expiresAt: string; partner: string }> {\n const now = this.ctx.now()\n const hash = await this.tokenHash(token)\n const link = await this.ctx.db\n .prepare(`SELECT partner, expires_at, used_at FROM partner_magic_links WHERE token_hash = ?`)\n .bind(hash)\n .first<{ partner: string; expires_at: string; used_at: string | null }>()\n if (!link || link.used_at || link.expires_at <= now || !this.partners().includes(link.partner)) {\n throw new StoreError('not_found', 'This sign-in link is invalid, used or expired')\n }\n const session = randomToken(32)\n const expiresAt = new Date(Date.parse(now) + PARTNER_SESSION_DAYS * 86_400_000).toISOString()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE partner_magic_links SET used_at = ? WHERE token_hash = ? AND used_at IS NULL`).bind(now, hash),\n this.ctx.db\n .prepare(`INSERT INTO partner_sessions (token_hash, partner, expires_at, created_at, last_seen_at) VALUES (?, ?, ?, ?, ?)`)\n .bind(await this.tokenHash(session), link.partner, expiresAt, now, now),\n ])\n return { token: session, expiresAt, partner: link.partner }\n }\n\n /** The partner behind a session token, or null. */\n async resolveSession(token: string | null | undefined): Promise<string | null> {\n if (!token) return null\n const row = await this.ctx.db\n .prepare(`SELECT partner, expires_at FROM partner_sessions WHERE token_hash = ?`)\n .bind(await this.tokenHash(token))\n .first<{ partner: string; expires_at: string }>()\n if (!row || row.expires_at <= this.ctx.now() || !this.partners().includes(row.partner)) return null\n return row.partner\n }\n\n async signOut(token: string | null | undefined): Promise<void> {\n if (!token) return\n await this.ctx.db.prepare(`DELETE FROM partner_sessions WHERE token_hash = ?`).bind(await this.tokenHash(token)).run()\n }\n\n /** A code as the buyer sees it, if it is valid now. */\n lookupCode(input: string | null | undefined): PublicDiscount | null {\n const d = findDiscount(this.ctx.config.discounts, input, new Date(this.ctx.now()))\n return d ? publicDiscount(d, this.ctx.config.currency, this.ctx.config.locale) : null\n }\n\n /** Partners named by the configured codes. */\n partners(): string[] {\n return [...new Set(this.ctx.config.discounts.map((d) => d.partner).filter((p): p is string => !!p))]\n }\n\n /** The secret in a partner's report link. Needs SESSION_SECRET; rotating it revokes every link. */\n async reportKey(partner: string): Promise<string> {\n const secret = this.ctx.env.SESSION_SECRET\n if (!secret) throw new Error('SESSION_SECRET is required for partner report links')\n return (await hashToken(`partner-report:${partner}`, secret)).slice(0, 32)\n }\n\n async verifyReportKey(partner: string, key: string | null | undefined): Promise<boolean> {\n if (!key || !this.partners().includes(partner) || !this.ctx.env.SESSION_SECRET) return false\n return secretsEqual(key, await this.reportKey(partner))\n }\n\n async report(partner: string, options: PartnerReportOptions = {}): Promise<PartnerReport> {\n const holdMs = (options.holdDays ?? 30) * 86_400_000\n const now = Date.parse(this.ctx.now())\n const currency = this.ctx.config.currency\n const { results } = await this.ctx.db\n .prepare(\n `SELECT o.number, o.created_at, o.paid_at, o.status, o.payment_status, o.currency, o.subtotal, o.discount, o.partner_commission,\n o.shipping_address_json, (SELECT SUM(quantity) FROM order_lines l WHERE l.order_id = o.id) AS units\n FROM orders o\n WHERE o.discount_partner = ? AND o.payment_status IN ('paid', 'refunded') AND o.created_at >= ?\n ORDER BY o.created_at DESC`\n )\n .bind(partner, options.since ?? '0000')\n .all<Row>()\n const lines: PartnerReportLine[] = results.map((r) => {\n const voided = r.payment_status === 'refunded' || r.status === 'cancelled' || r.status === 'refunded'\n const settled = r.paid_at ? now - Date.parse(r.paid_at) >= holdMs : false\n return {\n number: `${this.ctx.config.orders.numberPrefix}${r.number}`,\n placedAt: r.created_at,\n paidAt: r.paid_at,\n country: parseJson<{ country?: string }>(r.shipping_address_json, {}).country ?? null,\n units: r.units ?? 0,\n merchandise: { amount: r.subtotal - r.discount, currency: r.currency },\n commission: { amount: voided ? 0 : r.partner_commission, currency: r.currency },\n state: voided ? 'void' : settled ? 'payable' : 'held',\n }\n })\n const sum = (pick: (l: PartnerReportLine) => number) => lines.reduce((n, l) => n + pick(l), 0)\n const live = lines.filter((l) => l.state !== 'void')\n return {\n partner,\n codes: this.ctx.config.discounts.filter((d) => d.partner === partner).map((d) => this.lookupCode(d.code) ?? { ...publicDiscount(d, currency, this.ctx.config.locale), label: 'expired' }),\n lines,\n totals: {\n orders: live.length,\n units: live.reduce((n, l) => n + l.units, 0),\n merchandise: { amount: live.reduce((n, l) => n + l.merchandise.amount, 0), currency },\n payable: { amount: sum((l) => (l.state === 'payable' ? l.commission.amount : 0)), currency },\n held: { amount: sum((l) => (l.state === 'held' ? l.commission.amount : 0)), currency },\n },\n }\n }\n}\n","import type { DbStatement } from '../db/driver'\nimport type { OrderEvent } from '../types'\nimport { parseJson, type StoreContext } from './context'\n\ninterface EventRow {\n id: number\n order_id: string\n type: string\n data_json: string | null\n created_at: string\n}\n\n/** The audit trail of an order: created, paid, cancelled, fulfilled, emails sent, … */\nexport class EventsService {\n constructor(private readonly ctx: StoreContext) {}\n\n /** A statement to include in a batch, so the event lands with the change it describes. */\n statement(orderId: string, type: string, data: Record<string, unknown> | null = null): DbStatement {\n return this.ctx.db\n .prepare(`INSERT INTO order_events (order_id, type, data_json, created_at) VALUES (?, ?, ?, ?)`)\n .bind(orderId, type, data ? JSON.stringify(data) : null, this.ctx.now())\n }\n\n async record(orderId: string, type: string, data: Record<string, unknown> | null = null): Promise<void> {\n await this.statement(orderId, type, data).run()\n }\n\n async list(orderId: string): Promise<OrderEvent[]> {\n const { results } = await this.ctx.db\n .prepare(`SELECT * FROM order_events WHERE order_id = ? ORDER BY id ASC`)\n .bind(orderId)\n .all<EventRow>()\n return results.map((r) => ({\n id: r.id,\n orderId: r.order_id,\n type: r.type,\n data: parseJson<Record<string, unknown> | null>(r.data_json, null),\n createdAt: r.created_at,\n }))\n }\n}\n","import type { DbStatement } from '../db/driver'\nimport type { Availability } from '../types'\nimport { deriveAvailability } from './catalog'\nimport { chunk, placeholders, type StoreContext } from './context'\n\nexport interface InventoryItemInput {\n sku: string\n available: number\n allow_backorder?: boolean\n}\n\nexport interface InventoryResult {\n updated: number\n unknown_skus: string[]\n}\n\nexport class InventoryService {\n constructor(private readonly ctx: StoreContext, private readonly existingSkus: (skus: string[]) => Promise<Set<string>>, private readonly invalidate: () => void) {}\n\n /** Set absolute available quantities. Unknown SKUs are reported, not created. */\n async set(items: InventoryItemInput[]): Promise<InventoryResult> {\n if (items.length === 0) return { updated: 0, unknown_skus: [] }\n const known = await this.existingSkus(items.map((i) => i.sku))\n const now = this.ctx.now()\n const unknown: string[] = []\n const statements: DbStatement[] = []\n let updated = 0\n for (const item of items) {\n if (!known.has(item.sku)) {\n unknown.push(item.sku)\n continue\n }\n // Stock is part of the product resource GET /products returns, so a change to it is a change\n // to the product: bump products.updated_at (before the upsert, and only when the values\n // differ) so a consumer polling `updated_since` sees the new quantity without a full sweep.\n updated++\n const available = Math.trunc(item.available)\n const backorder = item.allow_backorder ? 1 : 0\n statements.push(\n this.ctx.db\n .prepare(\n `UPDATE products SET updated_at = ? WHERE sku = ?\n AND NOT EXISTS (SELECT 1 FROM inventory WHERE sku = ? AND available = ? AND allow_backorder = ?)`\n )\n .bind(now, item.sku, item.sku, available, backorder)\n )\n statements.push(\n this.ctx.db\n .prepare(\n `INSERT INTO inventory (sku, available, allow_backorder, updated_at) VALUES (?, ?, ?, ?)\n ON CONFLICT (sku) DO UPDATE SET available = excluded.available, allow_backorder = excluded.allow_backorder, updated_at = excluded.updated_at`\n )\n .bind(item.sku, available, backorder, now)\n )\n }\n for (const part of chunk(statements, 250)) await this.ctx.db.batch(part)\n if (statements.length) this.invalidate()\n return { updated, unknown_skus: [...new Set(unknown)] }\n }\n\n /** Availability for a set of SKUs; SKUs without a row are untracked (undefined). */\n async availability(skus: string[]): Promise<Map<string, Availability | undefined>> {\n const out = new Map<string, Availability | undefined>()\n for (const sku of skus) out.set(sku, undefined)\n for (const part of chunk([...new Set(skus)])) {\n const { results } = await this.ctx.db\n .prepare(`SELECT sku, available, allow_backorder, updated_at FROM inventory WHERE sku IN (${placeholders(part.length)})`)\n .bind(...part)\n .all<{ sku: string; available: number; allow_backorder: number; updated_at: string }>()\n for (const r of results) out.set(r.sku, deriveAvailability(r, this.ctx.config.inventory.lowStockThreshold))\n }\n return out\n }\n\n /** Statements that move tracked stock by `delta` per SKU (negative to consume). Untracked SKUs are untouched. */\n adjustStatements(lines: { sku: string; quantity: number }[], direction: 1 | -1): DbStatement[] {\n const now = this.ctx.now()\n // A tracked SKU's product is bumped too, for the same reason as in set().\n return lines.flatMap((l) => [\n this.ctx.db\n .prepare(`UPDATE products SET updated_at = ? WHERE sku = ? AND EXISTS (SELECT 1 FROM inventory WHERE sku = ?)`)\n .bind(now, l.sku, l.sku),\n this.ctx.db\n .prepare(`UPDATE inventory SET available = available + ?, updated_at = ? WHERE sku = ?`)\n .bind(direction * l.quantity, now, l.sku),\n ])\n }\n}\n","import type { DbStatement } from '../db/driver'\nimport { merchantNewOrderMail, orderConfirmationMail, shippingConfirmationMail } from '../mail/templates'\nimport type { Fulfillment, Money, Order, OrderStatus } from '../types'\nimport { chunk, placeholders, type StoreContext } from './context'\nimport { newId } from './crypto'\nimport { normalizeEmail } from './customers'\nimport { conflict, invalid, notFound } from './errors'\nimport type { EventsService } from './events'\nimport type { InventoryService } from './inventory'\nimport { ORDER_SELECT, rowToFulfillment, rowToOrder, type FulfillmentLineRow, type FulfillmentRow, type OrderLineRow, type OrderRow } from './rows'\n\nexport interface PaymentRecord {\n provider: string\n reference?: string | null\n methodLabel?: string | null\n paidAt?: string | null\n /** What the processor actually charged, when it may differ from the order (promotion codes). */\n amount?: Money\n adjustments?: { discount?: Money; tax?: Money; total?: Money }\n}\n\nexport interface FulfillmentInput {\n external_id: string\n carrier?: string | null\n service?: string | null\n tracking_number?: string | null\n tracking_url?: string | null\n shipped_at?: string | null\n lines: { sku: string; quantity: number }[]\n notify_customer?: boolean\n}\n\n/** Processor checkouts still unpaid after this long are treated as abandoned. */\nexport const ABANDONED_AFTER_MINUTES = 1440\n\nexport interface ChangedPage {\n orders: Order[]\n nextCursor: string | null\n}\n\nconst encodeCursor = (updatedAt: string, id: string) => btoa(`${updatedAt}|${id}`).replace(/=+$/, '')\nconst decodeCursor = (cursor: string): { updatedAt: string; id: string } | null => {\n try {\n const raw = atob(cursor)\n const i = raw.lastIndexOf('|')\n if (i < 0) return null\n return { updatedAt: raw.slice(0, i), id: raw.slice(i + 1) }\n } catch {\n return null\n }\n}\n\nexport class OrdersService {\n constructor(\n private readonly ctx: StoreContext,\n private readonly events: EventsService,\n private readonly inventory: InventoryService\n ) {}\n\n // ---- reads -------------------------------------------------------------\n\n async get(id: string): Promise<Order | null> {\n const [order] = await this.hydrate(`${ORDER_SELECT} WHERE o.id = ?`, [id])\n return order ?? null\n }\n\n async require(id: string): Promise<Order> {\n const order = await this.get(id)\n if (!order) throw notFound('Order')\n return order\n }\n\n /** The confirmation page's lookup: id plus the access key issued at checkout. */\n async getWithKey(id: string, key: string): Promise<Order | null> {\n const order = await this.get(id)\n return order && key && order.accessKey === key ? order : null\n }\n\n /** Guest order status: number plus the email it was placed with. */\n async byNumberAndEmail(number: string, email: string): Promise<Order | null> {\n const digits = number.replace(/^\\D+/, '')\n if (!/^\\d+$/.test(digits)) return null\n const [order] = await this.hydrate(`${ORDER_SELECT} WHERE o.number = ? AND o.email = ?`, [Number(digits), normalizeEmail(email)])\n return order ?? null\n }\n\n async forCustomer(customerId: string): Promise<Order[]> {\n return this.hydrate(`${ORDER_SELECT} WHERE o.customer_id = ? ORDER BY o.created_at DESC, o.number DESC`, [customerId])\n }\n\n async getForCustomer(id: string, customerId: string): Promise<Order | null> {\n const [order] = await this.hydrate(`${ORDER_SELECT} WHERE o.id = ? AND o.customer_id = ?`, [id, customerId])\n return order ?? null\n }\n\n async byPaymentReference(provider: string, reference: string): Promise<Order | null> {\n const [order] = await this.hydrate(`${ORDER_SELECT} WHERE o.payment_provider = ? AND o.payment_reference = ?`, [provider, reference])\n return order ?? null\n }\n\n /** Newest first, for the owner's order list. Unpaid processor checkouts are left out unless asked for. */\n async recent(opts: { limit?: number; includeUnpaid?: boolean } = {}): Promise<Order[]> {\n const limit = Math.min(Math.max(opts.limit ?? 100, 1), 500)\n const where = opts.includeUnpaid ? '' : ` WHERE o.payment_status != 'unpaid' OR COALESCE(o.payment_provider, 'manual') = 'manual'`\n return this.hydrate(`${ORDER_SELECT}${where} ORDER BY o.created_at DESC, o.number DESC LIMIT ?`, [limit])\n }\n\n /**\n * The owner's order list: newest first, filtered and paged, with the total for paging. `query` matches the\n * buyer's email or name, the order number with or without the store's prefix (\"VLD4200\", \"4200\", \"#4200\"), or an\n * imported order's original number. Orders copied from an earlier platform carry `metadata.imported_from` (e.g.\n * \"woocommerce\") and `metadata.original_number`; `source` separates them from the store's own. Unpaid processor\n * checkouts (abandoned) are left out unless `includeUnpaid`.\n */\n async search(\n opts: { query?: string; status?: OrderStatus; source?: 'store' | 'imported'; includeUnpaid?: boolean; limit?: number; offset?: number } = {}\n ): Promise<{ orders: Order[]; total: number }> {\n const where: string[] = []\n const args: (string | number)[] = []\n const query = opts.query?.trim()\n if (query) {\n const prefix = this.ctx.config.orders.numberPrefix.toLowerCase()\n let number = query.replace(/^#/, '').trim()\n if (prefix && number.toLowerCase().startsWith(prefix)) number = number.slice(prefix.length)\n where.push(`(o.email LIKE ? OR c.name LIKE ? OR CAST(o.number AS TEXT) = ? OR json_extract(o.metadata_json, '$.original_number') LIKE ?)`)\n args.push(`%${query.toLowerCase()}%`, `%${query}%`, number.trim(), `%${query}%`)\n }\n if (opts.status) {\n where.push('o.status = ?')\n args.push(opts.status)\n }\n if (opts.source === 'imported') where.push(`json_extract(o.metadata_json, '$.imported_from') IS NOT NULL`)\n if (opts.source === 'store') where.push(`json_extract(o.metadata_json, '$.imported_from') IS NULL`)\n if (!opts.includeUnpaid) where.push(`(o.payment_status != 'unpaid' OR COALESCE(o.payment_provider, 'manual') = 'manual')`)\n const clause = where.length ? ` WHERE ${where.join(' AND ')}` : ''\n const limit = Math.min(Math.max(Math.trunc(opts.limit ?? 50), 1), 200)\n const offset = Math.max(Math.trunc(opts.offset ?? 0), 0)\n const counted = await this.ctx.db\n .prepare(`SELECT COUNT(*) AS n FROM orders o JOIN customers c ON c.id = o.customer_id${clause}`)\n .bind(...args)\n .first<{ n: number }>()\n const orders = await this.hydrate(`${ORDER_SELECT}${clause} ORDER BY o.created_at DESC, o.number DESC LIMIT ? OFFSET ?`, [...args, limit, offset])\n return { orders, total: counted?.n ?? 0 }\n }\n\n async count(): Promise<number> {\n const row = await this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM orders`).first<{ n: number }>()\n return row?.n ?? 0\n }\n\n /**\n * Orders changed since a watermark, oldest first, keyset-paged on\n * (updated_at, id). `updatedSince` is inclusive so a consumer can overlap.\n */\n async changedSince(updatedSince: string | null, cursor: string | null, limit: number): Promise<ChangedPage> {\n const size = Math.min(Math.max(Math.trunc(limit) || 50, 1), 200)\n const where: string[] = []\n const args: (string | number)[] = []\n if (cursor) {\n const c = decodeCursor(cursor)\n if (!c) throw invalid('Invalid cursor', { cursor: 'is not a cursor this store issued' })\n where.push(`(o.updated_at, o.id) > (?, ?)`)\n args.push(c.updatedAt, c.id)\n } else if (updatedSince) {\n where.push(`o.updated_at >= ?`)\n args.push(updatedSince)\n }\n const sql = `${ORDER_SELECT}${where.length ? ` WHERE ${where.join(' AND ')}` : ''} ORDER BY o.updated_at ASC, o.id ASC LIMIT ?`\n const orders = await this.hydrate(sql, [...args, size + 1])\n const page = orders.slice(0, size)\n const last = page[page.length - 1]\n return { orders: page, nextCursor: orders.length > size && last ? encodeCursor(last.updatedAt, last.id) : null }\n }\n\n private async hydrate(sql: string, args: (string | number)[]): Promise<Order[]> {\n const { results: rows } = await this.ctx.db.prepare(sql).bind(...args).all<OrderRow>()\n if (rows.length === 0) return []\n const ids = rows.map((r) => r.id)\n const lines = new Map<string, OrderLineRow[]>()\n const fulfillments = new Map<string, FulfillmentRow[]>()\n const fulfillmentLines = new Map<string, FulfillmentLineRow[]>()\n for (const part of chunk(ids)) {\n const ph = placeholders(part.length)\n const [l, f, fl] = await Promise.all([\n this.ctx.db.prepare(`SELECT * FROM order_lines WHERE order_id IN (${ph}) ORDER BY position, id`).bind(...part).all<OrderLineRow>(),\n this.ctx.db.prepare(`SELECT * FROM fulfillments WHERE order_id IN (${ph}) ORDER BY created_at, id`).bind(...part).all<FulfillmentRow>(),\n this.ctx.db\n .prepare(`SELECT fl.* FROM fulfillment_lines fl JOIN fulfillments f ON f.id = fl.fulfillment_id WHERE f.order_id IN (${ph}) ORDER BY fl.sku`)\n .bind(...part)\n .all<FulfillmentLineRow>(),\n ])\n for (const r of l.results) (lines.get(r.order_id) ?? lines.set(r.order_id, []).get(r.order_id)!).push(r)\n for (const r of f.results) (fulfillments.get(r.order_id) ?? fulfillments.set(r.order_id, []).get(r.order_id)!).push(r)\n for (const r of fl.results) (fulfillmentLines.get(r.fulfillment_id) ?? fulfillmentLines.set(r.fulfillment_id, []).get(r.fulfillment_id)!).push(r)\n }\n return rows.map((r) =>\n rowToOrder(\n r,\n lines.get(r.id) ?? [],\n (fulfillments.get(r.id) ?? []).map((f) => rowToFulfillment(f, fulfillmentLines.get(f.id) ?? []))\n )\n )\n }\n\n // ---- writes ------------------------------------------------------------\n\n /**\n * Record which adapter took the order to payment. `provider` is what\n * `expireAbandoned` uses to tell a processor checkout (which can be\n * abandoned) from a `manual` order (which waits as long as it takes).\n */\n async startPayment(id: string, input: { provider: string; kind: 'redirect' | 'instructions' }): Promise<void> {\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE orders SET payment_provider = ?, updated_at = ? WHERE id = ? AND payment_status = 'unpaid'`).bind(input.provider, now, id),\n this.events.statement(id, 'payment_started', { provider: input.provider, kind: input.kind }),\n ])\n }\n\n /**\n * Cancel processor checkouts the buyer never finished: every\n * `pending_payment` order older than the window whose adapter is not\n * `manual`. Called lazily (Store API order list, checkout endpoint), so no\n * cron is needed; a payment that arrives later reopens the order (markPaid).\n */\n async expireAbandoned(opts: { olderThanMinutes?: number; limit?: number } = {}): Promise<number> {\n const minutes = opts.olderThanMinutes ?? ABANDONED_AFTER_MINUTES\n const limit = Math.min(Math.max(opts.limit ?? 200, 1), 500)\n const now = this.ctx.now()\n const cutoff = new Date(new Date(now).getTime() - minutes * 60_000).toISOString()\n const { results } = await this.ctx.db\n .prepare(`SELECT id FROM orders WHERE status = 'pending_payment' AND COALESCE(payment_provider, '') != 'manual' AND created_at < ? ORDER BY created_at LIMIT ?`)\n .bind(cutoff, limit)\n .all<{ id: string }>()\n if (results.length === 0) return 0\n const statements = results.flatMap((r) => [\n this.ctx.db.prepare(`UPDATE orders SET status = 'cancelled', cancelled_at = ?, updated_at = ? WHERE id = ? AND status = 'pending_payment'`).bind(now, now, r.id),\n this.events.statement(r.id, 'abandoned', { after_minutes: minutes }),\n ])\n for (const part of chunk(statements, 250)) await this.ctx.db.batch(part)\n return results.length\n }\n\n /**\n * Record a successful payment. Idempotent: a second call for an order that\n * is already paid returns it unchanged, so webhook redelivery is harmless.\n * A cancelled order (abandoned and expired, or cancelled by operations) that\n * the processor reports paid is reopened as `paid` — money arrived, and a\n * paid order is never lost.\n */\n async markPaid(id: string, payment: PaymentRecord, opts: { notify?: boolean } = {}): Promise<Order> {\n const order = await this.require(id)\n if (order.payment.status === 'paid') return order\n if (order.payment.status === 'refunded') {\n this.ctx.log('orders: ignoring a payment for a refunded order', { id, provider: payment.provider, reference: payment.reference })\n return order\n }\n const reopened = order.status === 'cancelled'\n if (reopened) this.ctx.log('orders: payment arrived for a cancelled order; reopening it', { id, provider: payment.provider, reference: payment.reference })\n const now = this.ctx.now()\n const paidAt = payment.paidAt ?? now\n const discount = payment.adjustments?.discount?.amount ?? order.discount.amount\n const tax = payment.adjustments?.tax?.amount ?? order.tax.amount\n const total = payment.adjustments?.total?.amount ?? payment.amount?.amount ?? order.subtotal.amount + order.shipping.amount + tax - discount\n const nextStatus: OrderStatus = order.status === 'pending_payment' || reopened ? 'paid' : order.status\n const statements: DbStatement[] = [\n this.ctx.db\n .prepare(\n `UPDATE orders SET status = ?, payment_status = 'paid', payment_provider = ?, payment_reference = ?, payment_method_label = ?, paid_at = ?,\n discount = ?, tax = ?, total = ?, cancelled_at = ?, updated_at = ? WHERE id = ?`\n )\n .bind(nextStatus, payment.provider, payment.reference ?? null, payment.methodLabel ?? null, paidAt, discount, tax, total, reopened ? null : order.cancelledAt, now, id),\n this.ctx.db\n .prepare(`INSERT INTO payments (id, order_id, provider, reference, status, amount, currency, method_label, created_at) VALUES (?, ?, ?, ?, 'paid', ?, ?, ?, ?)`)\n .bind(newId('pay'), id, payment.provider, payment.reference ?? null, total, order.currency, payment.methodLabel ?? null, now),\n ...this.inventory.adjustStatements(order.lines, -1),\n ...(reopened ? [this.events.statement(id, 'uncancelled', { reason: 'payment received', provider: payment.provider })] : []),\n this.events.statement(id, 'paid', { provider: payment.provider, reference: payment.reference ?? null, total }),\n ]\n await this.ctx.db.batch(statements)\n const updated = await this.require(id)\n if (opts.notify !== false) await this.notifyPaid(updated)\n return updated\n }\n\n async setPaymentStatus(id: string, input: { status: 'paid' | 'refunded' | 'failed'; provider?: string | null; reference?: string | null; paid_at?: string | null }): Promise<Order> {\n if (input.status === 'paid') {\n return this.markPaid(id, { provider: input.provider ?? 'manual', reference: input.reference ?? null, paidAt: input.paid_at ?? null })\n }\n const order = await this.require(id)\n const now = this.ctx.now()\n if (input.status === 'refunded') {\n if (order.payment.status === 'refunded') return order\n if (order.payment.status !== 'paid') throw conflict('Only a paid order can be refunded')\n await this.ctx.db.batch([\n this.ctx.db\n .prepare(`UPDATE orders SET status = 'refunded', payment_status = 'refunded', payment_provider = COALESCE(?, payment_provider), payment_reference = COALESCE(?, payment_reference), updated_at = ? WHERE id = ?`)\n .bind(input.provider ?? null, input.reference ?? null, now, id),\n this.ctx.db\n .prepare(`INSERT INTO payments (id, order_id, provider, reference, status, amount, currency, method_label, created_at) VALUES (?, ?, ?, ?, 'refunded', ?, ?, ?, ?)`)\n .bind(newId('pay'), id, input.provider ?? order.payment.provider ?? 'manual', input.reference ?? order.payment.reference, -order.total.amount, order.currency, order.payment.methodLabel, now),\n this.events.statement(id, 'refunded', { provider: input.provider ?? null, reference: input.reference ?? null }),\n ])\n return this.require(id)\n }\n // failed: the order stays open for another attempt.\n if (order.payment.status === 'paid') throw conflict('A paid order cannot be marked failed')\n await this.ctx.db.batch([\n this.ctx.db\n .prepare(`UPDATE orders SET payment_status = 'failed', payment_provider = COALESCE(?, payment_provider), payment_reference = COALESCE(?, payment_reference), updated_at = ? WHERE id = ?`)\n .bind(input.provider ?? null, input.reference ?? null, now, id),\n this.events.statement(id, 'payment_failed', { provider: input.provider ?? null, reference: input.reference ?? null }),\n ])\n return this.require(id)\n }\n\n async cancel(id: string, reason: string | null = null): Promise<Order> {\n const order = await this.require(id)\n if (order.status === 'cancelled') return order\n if (order.status === 'fulfilled' || order.status === 'partially_fulfilled') throw conflict('An order that has shipped cannot be cancelled')\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE orders SET status = 'cancelled', cancelled_at = ?, updated_at = ? WHERE id = ?`).bind(now, now, id),\n ...(order.payment.status === 'paid' ? this.inventory.adjustStatements(order.lines, 1) : []),\n this.events.statement(id, 'cancelled', reason ? { reason } : null),\n ])\n return this.require(id)\n }\n\n async setExternalReference(id: string, reference: string | null): Promise<Order> {\n const order = await this.require(id)\n if (order.externalReference === reference) return order\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE orders SET external_reference = ?, updated_at = ? WHERE id = ?`).bind(reference, now, id),\n this.events.statement(id, 'external_reference_set', { reference }),\n ])\n return this.require(id)\n }\n\n /**\n * Record a shipment. Idempotent per (order, external_id): a redelivery\n * returns the existing fulfilment with `created: false` and sends nothing.\n */\n async addFulfillment(id: string, input: FulfillmentInput): Promise<{ order: Order; fulfillment: Fulfillment; created: boolean }> {\n const order = await this.require(id)\n const existing = order.fulfillments.find((f) => f.externalId === input.external_id)\n if (existing) return { order, fulfillment: existing, created: false }\n if (order.status === 'cancelled') throw conflict('A cancelled order cannot be fulfilled')\n\n const details: Record<string, string> = {}\n const merged = new Map<string, number>()\n input.lines.forEach((l, i) => {\n const line = order.lines.find((ol) => ol.sku === l.sku)\n if (!line) {\n details[`lines.${i}.sku`] = `is not on this order`\n return\n }\n merged.set(l.sku, (merged.get(l.sku) ?? 0) + l.quantity)\n const remaining = line.quantity - line.fulfilledQuantity\n if (merged.get(l.sku)! > remaining) details[`lines.${i}.quantity`] = `exceeds the ${remaining} unfulfilled unit(s) of ${l.sku}`\n })\n if (Object.keys(details).length) throw invalid('Fulfilment lines do not match the order', details)\n\n const now = this.ctx.now()\n const fulfillmentId = newId('ful')\n const statements: DbStatement[] = [\n this.ctx.db\n .prepare(`INSERT INTO fulfillments (id, order_id, external_id, carrier, service, tracking_number, tracking_url, shipped_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)\n .bind(fulfillmentId, id, input.external_id, input.carrier ?? null, input.service ?? null, input.tracking_number ?? null, input.tracking_url ?? null, input.shipped_at ?? null, now),\n ]\n for (const [sku, quantity] of merged) {\n statements.push(this.ctx.db.prepare(`INSERT INTO fulfillment_lines (fulfillment_id, sku, quantity) VALUES (?, ?, ?)`).bind(fulfillmentId, sku, quantity))\n statements.push(this.ctx.db.prepare(`UPDATE order_lines SET fulfilled_quantity = fulfilled_quantity + ? WHERE order_id = ? AND sku = ?`).bind(quantity, id, sku))\n }\n const allDone = order.lines.every((l) => l.fulfilledQuantity + (merged.get(l.sku) ?? 0) >= l.quantity)\n const nextStatus: OrderStatus = allDone ? 'fulfilled' : 'partially_fulfilled'\n statements.push(this.ctx.db.prepare(`UPDATE orders SET status = ?, updated_at = ? WHERE id = ?`).bind(nextStatus, now, id))\n statements.push(this.events.statement(id, 'fulfillment_added', { fulfillment: fulfillmentId, external_id: input.external_id, tracking_number: input.tracking_number ?? null }))\n await this.ctx.db.batch(statements)\n\n const updated = await this.require(id)\n const fulfillment = updated.fulfillments.find((f) => f.id === fulfillmentId)!\n if (input.notify_customer !== false) await this.notifyShipped(updated, fulfillment)\n return { order: updated, fulfillment, created: true }\n }\n\n // ---- notifications -----------------------------------------------------\n\n private mailContext() {\n return { config: this.ctx.config, siteUrl: this.ctx.siteUrl }\n }\n\n /** Buyer confirmation plus the merchant notification. Failures are logged, never thrown. */\n async notifyPaid(order: Order): Promise<void> {\n const buyer = orderConfirmationMail(order, this.mailContext())\n await this.send(order.id, 'order_confirmation', order.customer.email, buyer)\n const merchant = this.ctx.env.ORDER_NOTIFICATION_EMAIL\n if (merchant) await this.send(order.id, 'merchant_notification', merchant, merchantNewOrderMail(order, this.mailContext()), order.customer.email)\n }\n\n /** Order received but not yet paid (manual payment). */\n async notifyReceived(order: Order): Promise<void> {\n await this.send(order.id, 'order_received', order.customer.email, orderConfirmationMail(order, this.mailContext()))\n }\n\n async notifyShipped(order: Order, fulfillment: Fulfillment): Promise<void> {\n const sent = await this.send(order.id, 'shipping_confirmation', order.customer.email, shippingConfirmationMail(order, fulfillment, this.mailContext()))\n if (sent) await this.ctx.db.prepare(`UPDATE fulfillments SET notified_at = ? WHERE id = ?`).bind(this.ctx.now(), fulfillment.id).run()\n }\n\n private async send(orderId: string, kind: string, to: string, mail: { subject: string; html: string; text: string }, replyTo?: string): Promise<boolean> {\n try {\n await this.ctx.mail.send({ to, ...mail, ...(replyTo ? { replyTo } : {}) })\n await this.events.record(orderId, 'email_sent', { kind, to })\n return true\n } catch (error) {\n this.ctx.log(`orders: ${kind} email failed`, { orderId, to, error: error instanceof Error ? error.message : String(error) })\n await this.events.record(orderId, 'email_failed', { kind, to, error: error instanceof Error ? error.message : String(error) }).catch(() => {})\n return false\n }\n }\n}\n","/**\n * Password hashing on WebCrypto PBKDF2-HMAC-SHA256, so the same code runs on\n * Workers and in Node. The stored string carries every parameter:\n *\n * $pbkdf2-sha256$i=<iterations>$<salt base64url>$<hash base64url>\n *\n * so the cost can be raised later and old hashes keep verifying (and can be\n * re-hashed on the next successful sign-in).\n */\nconst encoder = new TextEncoder()\n\n/**\n * Cloudflare Workers cap a single PBKDF2 call at 100_000 iterations\n * (`NotSupportedError: iteration counts above 100000 are not supported`), which\n * Node does not — a gap that only shows up on the deployed Worker. To keep an\n * effective work factor above the OWASP-era 200k floor while staying under that\n * ceiling, the derivation is CHAINED: each 100k-iteration round feeds the next,\n * so a stored count of 300_000 is three rounds. The count must be a multiple of\n * the per-call cap.\n */\nexport const PBKDF2_MAX_PER_CALL = 100_000\nexport const PASSWORD_ITERATIONS = 300_000\nconst SALT_BYTES = 16\nconst HASH_BYTES = 32\nexport const PASSWORD_MIN_LENGTH = 12\n\nexport function toBase64Url(bytes: Uint8Array): string {\n let bin = ''\n for (const b of bytes) bin += String.fromCharCode(b)\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nexport function fromBase64Url(s: string): Uint8Array {\n const padded = s.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (s.length % 4)) % 4)\n const bin = atob(padded)\n const out = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i)\n return out\n}\n\nasync function pbkdf2(material: BufferSource, salt: Uint8Array, iterations: number): Promise<ArrayBuffer> {\n const key = await crypto.subtle.importKey('raw', material, 'PBKDF2', false, ['deriveBits'])\n return crypto.subtle.deriveBits({ name: 'PBKDF2', hash: 'SHA-256', salt: salt as BufferSource, iterations }, key, HASH_BYTES * 8)\n}\n\n/**\n * PBKDF2 chained in <=100k-iteration rounds so the total honours `iterations`\n * without any single call exceeding the Workers cap. Each round's output is the\n * next round's input; the salt is constant. Node and Workers agree round for round.\n */\nasync function derive(password: string, salt: Uint8Array, iterations: number): Promise<Uint8Array> {\n let material: BufferSource = encoder.encode(password.normalize('NFKC'))\n let remaining = iterations\n while (remaining > 0) {\n const step = Math.min(remaining, PBKDF2_MAX_PER_CALL)\n material = await pbkdf2(material, salt, step)\n remaining -= step\n }\n return new Uint8Array(material as ArrayBuffer)\n}\n\nexport async function hashPassword(password: string, iterations = PASSWORD_ITERATIONS): Promise<string> {\n const salt = new Uint8Array(SALT_BYTES)\n crypto.getRandomValues(salt)\n const hash = await derive(password, salt, iterations)\n return `$pbkdf2-sha256$i=${iterations}$${toBase64Url(salt)}$${toBase64Url(hash)}`\n}\n\nexport interface PasswordCheck {\n ok: boolean\n /** True when the stored hash uses fewer iterations than the current default. */\n needsRehash: boolean\n}\n\n/** Verify against a stored hash. Never throws on a malformed hash — it simply fails. */\nexport async function verifyPassword(password: string, stored: string | null | undefined): Promise<PasswordCheck> {\n const match = /^\\$pbkdf2-sha256\\$i=(\\d+)\\$([A-Za-z0-9_-]+)\\$([A-Za-z0-9_-]+)$/.exec(stored ?? '')\n if (!match) return { ok: false, needsRehash: false }\n const iterations = Number(match[1])\n if (!Number.isInteger(iterations) || iterations < 1_000 || iterations > 10_000_000) return { ok: false, needsRehash: false }\n const salt = fromBase64Url(match[2]!)\n const expected = fromBase64Url(match[3]!)\n const actual = await derive(password, salt, iterations)\n if (actual.length !== expected.length) return { ok: false, needsRehash: false }\n let diff = 0\n for (let i = 0; i < actual.length; i++) diff |= actual[i]! ^ expected[i]!\n return { ok: diff === 0, needsRehash: iterations < PASSWORD_ITERATIONS }\n}\n\n/** The one rule the store enforces on a new password. Length, not composition. */\nexport function passwordProblem(password: string): string | null {\n if (typeof password !== 'string' || password.length < PASSWORD_MIN_LENGTH) return `must be at least ${PASSWORD_MIN_LENGTH} characters`\n if (password.length > 256) return 'must be at most 256 characters'\n return null\n}\n","import { ownerPasswordResetMail } from '../mail/templates'\nimport type { StoreContext } from './context'\nimport { hashToken, newId, randomToken } from './crypto'\nimport { StoreError } from './errors'\nimport { hashPassword, passwordProblem, verifyPassword } from './password'\nimport { RateLimiter } from './ratelimit'\n\nexport const OWNER_SESSION_DAYS = 14\nexport const OWNER_RESET_MINUTES = 30\n\nexport interface Owner {\n id: string\n email: string\n name: string | null\n createdAt: string\n lastSignedInAt: string | null\n}\n\nexport interface OwnerSession {\n token: string\n expiresAt: string\n owner: Owner\n}\n\nexport interface OwnerResetLink {\n token: string\n url: string\n expiresAt: string\n owner: Owner\n}\n\ninterface OwnerRow {\n id: string\n email: string\n name: string | null\n password_hash: string\n created_at: string\n last_signed_in_at: string | null\n}\n\nconst toOwner = (r: OwnerRow): Owner => ({ id: r.id, email: r.email, name: r.name, createdAt: r.created_at, lastSignedInAt: r.last_signed_in_at })\n\nexport const normalizeOwnerEmail = (email: string) => email.trim().toLowerCase()\n\n/**\n * The people who run the store. Password sign-in with a 14-day session, an\n * emailed reset link, and a failure counter per email + address so a guessed\n * password costs the guesser more than it costs the store.\n */\nexport class OwnersService {\n private readonly limiter: RateLimiter\n\n constructor(private readonly ctx: StoreContext) {\n this.limiter = new RateLimiter(ctx, { maxFailures: 10, windowMinutes: 15, lockMinutes: 15 })\n }\n\n private hash(token: string) {\n return hashToken(token, this.ctx.env.SESSION_SECRET)\n }\n\n async count(): Promise<number> {\n const row = await this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM owners`).first<{ n: number }>()\n return row?.n ?? 0\n }\n\n async byId(id: string): Promise<Owner | null> {\n const row = await this.ctx.db.prepare(`SELECT * FROM owners WHERE id = ?`).bind(id).first<OwnerRow>()\n return row ? toOwner(row) : null\n }\n\n async byEmail(email: string): Promise<Owner | null> {\n const row = await this.ctx.db.prepare(`SELECT * FROM owners WHERE email = ?`).bind(normalizeOwnerEmail(email)).first<OwnerRow>()\n return row ? toOwner(row) : null\n }\n\n async list(): Promise<Owner[]> {\n const { results } = await this.ctx.db.prepare(`SELECT * FROM owners ORDER BY created_at ASC`).all<OwnerRow>()\n return results.map(toOwner)\n }\n\n /** Create or, when the email exists, reset that owner's name and password. */\n async upsert(input: { email: string; name?: string | null; password: string }): Promise<Owner> {\n const email = normalizeOwnerEmail(input.email)\n if (!email.includes('@')) throw new StoreError('validation_failed', 'Invalid owner', { email: 'must be an email address' })\n const problem = passwordProblem(input.password)\n if (problem) throw new StoreError('validation_failed', 'Invalid owner', { password: problem })\n const now = this.ctx.now()\n const id = newId('own')\n await this.ctx.db\n .prepare(\n `INSERT INTO owners (id, email, name, password_hash, created_at) VALUES (?, ?, ?, ?, ?)\n ON CONFLICT (email) DO UPDATE SET name = COALESCE(excluded.name, owners.name), password_hash = excluded.password_hash`\n )\n .bind(id, email, input.name ?? null, await hashPassword(input.password), now)\n .run()\n return (await this.byEmail(email))!\n }\n\n /**\n * Password sign-in. Null on a wrong email or password — the caller shows one\n * generic message either way. Throws `rate_limited` while the email+address\n * pair is locked out.\n */\n async signIn(email: string, password: string, address: string): Promise<OwnerSession | null> {\n const normalized = normalizeOwnerEmail(email)\n const limitKey = `signin:${normalized}|${address}`\n await this.limiter.assertAllowed(limitKey, 'Too many sign-in attempts. Try again in a few minutes.')\n const row = await this.ctx.db.prepare(`SELECT * FROM owners WHERE email = ?`).bind(normalized).first<OwnerRow>()\n // Verify against a dummy hash when the email is unknown so timing does not reveal which emails exist.\n const check = await verifyPassword(password, row?.password_hash ?? DUMMY_HASH)\n if (!row || !check.ok) {\n await this.limiter.recordFailure(limitKey)\n return null\n }\n const now = this.ctx.now()\n const token = randomToken(32)\n const expiresAt = new Date(new Date(now).getTime() + OWNER_SESSION_DAYS * 86_400_000).toISOString()\n const statements = [\n this.ctx.db\n .prepare(`INSERT INTO owner_sessions (token_hash, owner_id, expires_at, created_at, last_seen_at) VALUES (?, ?, ?, ?, ?)`)\n .bind(await this.hash(token), row.id, expiresAt, now, now),\n this.ctx.db.prepare(`UPDATE owners SET last_signed_in_at = ? WHERE id = ?`).bind(now, row.id),\n this.limiter.clearStatement(limitKey),\n ]\n if (check.needsRehash) statements.push(this.ctx.db.prepare(`UPDATE owners SET password_hash = ? WHERE id = ?`).bind(await hashPassword(password), row.id))\n await this.ctx.db.batch(statements)\n return { token, expiresAt, owner: { ...toOwner(row), lastSignedInAt: now } }\n }\n\n /** The owner behind a session token, or null when it is unknown or expired. */\n async resolve(token: string | null | undefined): Promise<Owner | null> {\n if (!token) return null\n const now = this.ctx.now()\n const hash = await this.hash(token)\n const row = await this.ctx.db\n .prepare(`SELECT owner_id, expires_at, last_seen_at FROM owner_sessions WHERE token_hash = ?`)\n .bind(hash)\n .first<{ owner_id: string; expires_at: string; last_seen_at: string }>()\n if (!row || row.expires_at <= now) return null\n if (new Date(now).getTime() - new Date(row.last_seen_at).getTime() > 3_600_000) {\n await this.ctx.db.prepare(`UPDATE owner_sessions SET last_seen_at = ? WHERE token_hash = ?`).bind(now, hash).run()\n }\n return this.byId(row.owner_id)\n }\n\n async signOut(token: string | null | undefined): Promise<void> {\n if (!token) return\n await this.ctx.db.prepare(`DELETE FROM owner_sessions WHERE token_hash = ?`).bind(await this.hash(token)).run()\n }\n\n /** End every session of an owner (after a password reset). */\n async signOutEverywhere(ownerId: string): Promise<void> {\n await this.ctx.db.prepare(`DELETE FROM owner_sessions WHERE owner_id = ?`).bind(ownerId).run()\n }\n\n /** Create a reset link for a known owner; null for an unknown email (the caller answers the same either way). */\n async createResetLink(email: string): Promise<OwnerResetLink | null> {\n const owner = await this.byEmail(email)\n if (!owner) return null\n const token = randomToken(32)\n const now = this.ctx.now()\n const expiresAt = new Date(new Date(now).getTime() + OWNER_RESET_MINUTES * 60_000).toISOString()\n await this.ctx.db\n .prepare(`INSERT INTO owner_reset_links (token_hash, owner_id, expires_at, created_at) VALUES (?, ?, ?, ?)`)\n .bind(await this.hash(token), owner.id, expiresAt, now)\n .run()\n return { token, url: `${this.ctx.siteUrl}/admin/reset-password/?token=${token}`, expiresAt, owner }\n }\n\n /** Email a reset link when the address belongs to an owner. Returns whether one was sent. */\n async sendResetLink(email: string): Promise<boolean> {\n const link = await this.createResetLink(email)\n if (!link) return false\n const mail = ownerPasswordResetMail({ url: link.url, expiresMinutes: OWNER_RESET_MINUTES }, { config: this.ctx.config, siteUrl: this.ctx.siteUrl })\n await this.ctx.mail.send({ to: link.owner.email, ...mail })\n return true\n }\n\n /** Exchange a reset token for a new password. Every existing session of that owner ends. */\n async resetPassword(token: string, password: string): Promise<Owner> {\n const problem = passwordProblem(password)\n if (problem) throw new StoreError('validation_failed', 'Invalid password', { password: problem })\n const now = this.ctx.now()\n const hash = await this.hash(token)\n const link = await this.ctx.db\n .prepare(`SELECT owner_id, expires_at, used_at FROM owner_reset_links WHERE token_hash = ?`)\n .bind(hash)\n .first<{ owner_id: string; expires_at: string; used_at: string | null }>()\n if (!link || link.used_at || link.expires_at <= now) throw new StoreError('not_found', 'This reset link is invalid, used or expired')\n const owner = await this.byId(link.owner_id)\n if (!owner) throw new StoreError('not_found', 'This reset link is invalid, used or expired')\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE owner_reset_links SET used_at = ? WHERE token_hash = ? AND used_at IS NULL`).bind(now, hash),\n this.ctx.db.prepare(`UPDATE owners SET password_hash = ? WHERE id = ?`).bind(await hashPassword(password), owner.id),\n this.ctx.db.prepare(`DELETE FROM owner_sessions WHERE owner_id = ?`).bind(owner.id),\n ])\n return owner\n }\n\n /** Housekeeping: drop expired sessions, used or expired reset links, and stale limiter rows. */\n async prune(): Promise<void> {\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`DELETE FROM owner_sessions WHERE expires_at <= ?`).bind(now),\n this.ctx.db.prepare(`DELETE FROM owner_reset_links WHERE expires_at <= ? OR used_at IS NOT NULL`).bind(now),\n this.limiter.pruneStatement(),\n ])\n }\n}\n\n// A real PBKDF2 hash of a random password, so unknown-email sign-ins cost the same time as known ones.\nconst DUMMY_HASH = '$pbkdf2-sha256$i=210000$AAAAAAAAAAAAAAAAAAAAAA$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'\n","import { MAX_CART_LINES } from '../cart'\nimport type { DbStatement } from '../db/driver'\nimport { merchantQuoteMail, quoteReadyMail, quoteReceivedMail, type RenderedMail } from '../mail/templates'\nimport type { Address, Money, Order, QuoteLine, QuoteStatus, StoreQuote } from '../types'\nimport type { CatalogService } from './catalog'\nimport { checkShippingAddress, EMAIL, type CheckoutService, type PricedLine } from './checkout'\nimport { chunk, parseJson, placeholders, type StoreContext } from './context'\nimport { newId, randomToken } from './crypto'\nimport { normalizeEmail } from './customers'\nimport { conflict, invalid, notFound, StoreError } from './errors'\nimport type { OrdersService } from './orders'\nimport { cleanAddress } from './rows'\n\nexport interface QuoteRequestItem {\n sku: string\n quantity: number\n}\n\n/** What a buyer sends to ask for a quote. No account is needed: the email is the identity. */\nexport interface QuoteRequestInput {\n items: QuoteRequestItem[]\n email: string\n name: string\n phone?: string | null\n company?: string | null\n /** Lead time, intended use, delivery constraints — whatever the buyer wants priced in. */\n message?: string | null\n /** Optional at request time; required by the time the quote is accepted. */\n shippingAddress?: Address | null\n metadata?: Record<string, string>\n}\n\n/** The operations system's price for a quote. Replaces the lines: it may change quantities, drop or add SKUs. */\nexport interface QuoteOfferInput {\n lines: { sku: string; name?: string | null; quantity: number; unit_amount: Money }[]\n shipping?: Money | null\n tax?: Money | null\n /** ISO-8601; the offer lapses after it. Null or absent: open-ended. */\n expires_at?: string | null\n note?: string | null\n /** Email the buyer that the quote is ready (default true). */\n notify_customer?: boolean\n}\n\nexport interface QuoteAcceptInput {\n /** Falls back to the address given with the request. One of the two must be complete. */\n shippingAddress?: Address | null\n phone?: string | null\n /** The buyer's purchase-order number, kept on the order's notes and metadata. */\n poNumber?: string | null\n notes?: string | null\n}\n\nexport interface QuoteChangedPage {\n quotes: StoreQuote[]\n nextCursor: string | null\n}\n\ninterface QuoteRow {\n id: string\n number: number\n status: string\n customer_id: string\n email: string\n customer_name: string | null\n customer_phone: string | null\n company: string | null\n message: string | null\n shipping_address_json: string | null\n currency: string\n subtotal: number | null\n shipping: number | null\n tax: number | null\n total: number | null\n offer_note: string | null\n expires_at: string | null\n external_reference: string | null\n order_id: string | null\n decline_reason: string | null\n metadata_json: string\n access_key: string\n created_at: string\n updated_at: string\n quoted_at: string | null\n accepted_at: string | null\n declined_at: string | null\n cancelled_at: string | null\n}\n\ninterface QuoteLineRow {\n id: string\n quote_id: string\n sku: string\n name: string\n quantity: number\n list_amount: number | null\n unit_amount: number | null\n total_amount: number | null\n image_url: string | null\n position: number\n}\n\nconst QUOTE_SELECT = `SELECT q.*, c.name AS customer_name, c.phone AS customer_phone FROM quotes q JOIN customers c ON c.id = q.customer_id`\n\n/** Statuses an offer may be (re)made from: a fresh request, a live offer being revised, or a lapsed one being renewed. */\nconst OFFERABLE: QuoteStatus[] = ['requested', 'quoted', 'expired']\n\nconst encodeCursor = (updatedAt: string, id: string) => btoa(`${updatedAt}|${id}`).replace(/=+$/, '')\nconst decodeCursor = (cursor: string): { updatedAt: string; id: string } | null => {\n try {\n const raw = atob(cursor)\n const i = raw.lastIndexOf('|')\n return i < 0 ? null : { updatedAt: raw.slice(0, i), id: raw.slice(i + 1) }\n } catch {\n return null\n }\n}\n\nconst clean = (v: string | null | undefined, max: number) => (typeof v === 'string' && v.trim() ? v.trim().slice(0, max) : null)\n\n/**\n * Request for quote. A buyer asks for a price on products — signed in or as a guest who never makes an account —\n * the operations system prices it over the Store API (`PATCH /quotes/{id}` with an `offer`), and the buyer accepts\n * it, which places an order at exactly the offered prices, or declines it.\n *\n * The store never invents a price: a quote is either unpriced or carries the operations system's offer, and an\n * order only comes from the buyer accepting one.\n */\nexport class QuotesService {\n constructor(\n private readonly ctx: StoreContext,\n private readonly catalog: CatalogService,\n private readonly checkout: CheckoutService,\n private readonly orders: OrdersService\n ) {}\n\n // ---- reads -------------------------------------------------------------\n\n async get(id: string): Promise<StoreQuote | null> {\n const [quote] = await this.hydrate(`${QUOTE_SELECT} WHERE q.id = ?`, [id])\n return quote ?? null\n }\n\n async require(id: string): Promise<StoreQuote> {\n const quote = await this.get(id)\n if (!quote) throw notFound('Quote')\n return quote\n }\n\n /** The emailed link's lookup: id plus the access key. This is how a guest gets back to their quote. */\n async getWithKey(id: string, key: string): Promise<StoreQuote | null> {\n const quote = await this.get(id)\n return quote && key && quote.accessKey === key ? quote : null\n }\n\n async forCustomer(customerId: string): Promise<StoreQuote[]> {\n return this.hydrate(`${QUOTE_SELECT} WHERE q.customer_id = ? ORDER BY q.created_at DESC, q.number DESC`, [customerId])\n }\n\n async getForCustomer(id: string, customerId: string): Promise<StoreQuote | null> {\n const [quote] = await this.hydrate(`${QUOTE_SELECT} WHERE q.id = ? AND q.customer_id = ?`, [id, customerId])\n return quote ?? null\n }\n\n async count(): Promise<number> {\n const row = await this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM quotes`).first<{ n: number }>()\n return row?.n ?? 0\n }\n\n /** Quotes changed since a watermark, oldest first, keyset-paged on (updated_at, id); `updatedSince` is inclusive. */\n async changedSince(updatedSince: string | null, cursor: string | null, limit: number): Promise<QuoteChangedPage> {\n const size = Math.min(Math.max(Math.trunc(limit) || 50, 1), 200)\n const where: string[] = []\n const args: (string | number)[] = []\n if (cursor) {\n const c = decodeCursor(cursor)\n if (!c) throw invalid('Invalid cursor', { cursor: 'is not a cursor this store issued' })\n where.push(`(q.updated_at, q.id) > (?, ?)`)\n args.push(c.updatedAt, c.id)\n } else if (updatedSince) {\n where.push(`q.updated_at >= ?`)\n args.push(updatedSince)\n }\n const quotes = await this.hydrate(`${QUOTE_SELECT}${where.length ? ` WHERE ${where.join(' AND ')}` : ''} ORDER BY q.updated_at ASC, q.id ASC LIMIT ?`, [...args, size + 1])\n const page = quotes.slice(0, size)\n const last = page[page.length - 1]\n return { quotes: page, nextCursor: quotes.length > size && last ? encodeCursor(last.updatedAt, last.id) : null }\n }\n\n async events(id: string): Promise<{ id: number; type: string; data: Record<string, unknown> | null; createdAt: string }[]> {\n const { results } = await this.ctx.db\n .prepare(`SELECT * FROM quote_events WHERE quote_id = ? ORDER BY id ASC`)\n .bind(id)\n .all<{ id: number; type: string; data_json: string | null; created_at: string }>()\n return results.map((r) => ({ id: r.id, type: r.type, data: parseJson<Record<string, unknown> | null>(r.data_json, null), createdAt: r.created_at }))\n }\n\n // ---- buyer -------------------------------------------------------------\n\n /**\n * Record a request for quote. The buyer needs no account and no password: the customer row is keyed by email,\n * exactly as a guest checkout's is, and the emailed access-key link is how they come back. Sold-out products may\n * be quoted (lead time is part of the answer); unknown or withdrawn ones may not. Throttled per email address.\n */\n async request(input: QuoteRequestInput): Promise<StoreQuote> {\n const details: Record<string, string> = {}\n const email = normalizeEmail(input.email ?? '')\n if (!EMAIL.test(email)) details.email = 'must be a valid email address'\n const name = clean(input.name, 200)\n if (!name) details.name = 'is required'\n const items = input.items ?? []\n if (!items.length) details.items = 'is empty'\n if (items.length > MAX_CART_LINES) details.items = `has more than ${MAX_CART_LINES} lines`\n const max = this.ctx.config.quotes.maxQuantity\n const catalog = await this.catalog.load()\n const merged = new Map<string, number>()\n items.forEach((item, i) => {\n const quantity = Number(item.quantity)\n if (!Number.isInteger(quantity) || quantity < 1 || quantity > max) {\n details[`items.${i}.quantity`] = `must be a whole number from 1 to ${max}`\n return\n }\n if (!catalog.product(item.sku)) {\n details[`items.${i}.sku`] = 'is not a product in this store'\n return\n }\n merged.set(item.sku, Math.min((merged.get(item.sku) ?? 0) + quantity, max))\n })\n if (Object.keys(details).length) throw invalid('Quote request is incomplete', details)\n await this.assertNotFlooding(email)\n\n const db = this.ctx.db\n const now = this.ctx.now()\n const id = newId('quo')\n const address = cleanAddress(input.shippingAddress)\n if (address?.country) address.country = address.country.toUpperCase()\n const metadata = Object.fromEntries(Object.entries(input.metadata ?? {}).filter(([, v]) => typeof v === 'string').slice(0, 20))\n const lines = [...merged].map(([sku, quantity], position) => {\n const product = catalog.product(sku)!\n return { sku, quantity, position, name: product.name, list: product.price.amount, image: product.images[0]?.src ?? null }\n })\n await db.batch([\n db\n .prepare(\n `INSERT INTO customers (id, email, name, phone, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT (email) DO UPDATE SET name = COALESCE(excluded.name, customers.name), phone = COALESCE(excluded.phone, customers.phone), updated_at = excluded.updated_at`\n )\n .bind(newId('cus'), email, name, clean(input.phone, 50), now, now),\n db\n .prepare(\n `INSERT INTO quotes (id, number, status, customer_id, email, company, message, shipping_address_json, currency, metadata_json, access_key, created_at, updated_at)\n VALUES (?, (SELECT MAX(COALESCE(MAX(number), 0) + 1, ?) FROM quotes), 'requested', (SELECT id FROM customers WHERE email = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .bind(\n id,\n this.ctx.config.quotes.numberStart,\n email,\n email,\n clean(input.company, 200),\n clean(input.message, 4000),\n address && Object.keys(address).length ? JSON.stringify(address) : null,\n this.ctx.config.currency.toUpperCase(),\n JSON.stringify(metadata),\n randomToken(16),\n now,\n now\n ),\n ...lines.map((l) =>\n db\n .prepare(`INSERT INTO quote_lines (id, quote_id, sku, name, quantity, list_amount, image_url, position) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)\n .bind(newId('qln'), id, l.sku, l.name, l.quantity, l.list, l.image, l.position)\n ),\n this.event(id, 'requested', { lines: lines.map((l) => ({ sku: l.sku, quantity: l.quantity })) }),\n ])\n const quote = await this.require(id)\n await this.send(id, 'quote_received', quote.customer.email, quoteReceivedMail(quote, this.mailContext()))\n await this.notifyMerchant(quote, 'requested')\n return quote\n }\n\n /**\n * Accept a priced quote: places an order at exactly the offered prices (lines, shipping, tax) and links it. The\n * order is pay-later (`payment_provider: manual`, never expires) — the operations system invoices and marks it\n * paid, as B2B terms usually go; a store that wants card payment can start an adapter on the returned order.\n * A second accept returns the same order.\n */\n async accept(id: string, input: QuoteAcceptInput = {}): Promise<{ quote: StoreQuote; order: Order }> {\n await this.expireStale()\n const quote = await this.require(id)\n if (quote.status === 'accepted' && quote.orderId) return { quote, order: await this.orders.require(quote.orderId) }\n if (quote.status === 'expired') throw conflict('This quote has expired; ask for it to be renewed')\n if (quote.status !== 'quoted' || !quote.total) throw conflict(quote.status === 'requested' ? 'This quote has not been priced yet' : `A ${quote.status} quote cannot be accepted`)\n\n const details: Record<string, string> = {}\n const shipTo = checkShippingAddress(input.shippingAddress ?? quote.shippingAddress, details)\n if (Object.keys(details).length) throw invalid('A shipping address is needed to accept this quote', details)\n\n const now = this.ctx.now()\n // Claim the quote first so a double submit cannot place two orders.\n const claimed = await this.ctx.db\n .prepare(`UPDATE quotes SET status = 'accepted', accepted_at = ?, updated_at = ? WHERE id = ? AND status = 'quoted' AND (expires_at IS NULL OR expires_at > ?)`)\n .bind(now, now, id, now)\n .run()\n if ((claimed.meta?.changes ?? 0) === 0) {\n const current = await this.require(id)\n if (current.status === 'accepted' && current.orderId) return { quote: current, order: await this.orders.require(current.orderId) }\n throw conflict('This quote changed while you were accepting it; reload it and try again')\n }\n\n const po = clean(input.poNumber, 100)\n const lines: PricedLine[] = quote.lines.map((l) => ({\n sku: l.sku,\n name: l.name,\n quantity: l.quantity,\n unitAmount: l.unitAmount!.amount,\n totalAmount: l.totalAmount!.amount,\n image: l.image ?? null,\n }))\n let order: Order\n try {\n order = await this.checkout.placePricedOrder({\n email: quote.customer.email,\n name: quote.customer.name ?? shipTo.name ?? null,\n phone: clean(input.phone, 50) ?? quote.customer.phone,\n currency: quote.currency,\n lines,\n subtotal: quote.subtotal!.amount,\n shipping: quote.shipping?.amount ?? 0,\n tax: quote.tax?.amount ?? 0,\n discount: 0,\n total: quote.total.amount,\n shippingAddress: shipTo,\n billingAddress: null,\n shippingMethod: { code: 'quote', name: `As quoted (${this.ctx.config.quotes.numberPrefix}${quote.number})` },\n notes: [po ? `PO ${po}` : null, clean(input.notes, 1800)].filter(Boolean).join('\\n') || null,\n metadata: {\n quote_id: quote.id,\n quote_number: `${this.ctx.config.quotes.numberPrefix}${quote.number}`,\n ...(po ? { po_number: po } : {}),\n ...(quote.externalReference ? { quote_external_reference: quote.externalReference } : {}),\n },\n discountCode: null,\n discountPartner: null,\n commission: 0,\n createdEvent: { quote_id: quote.id },\n })\n } catch (error) {\n await this.ctx.db.prepare(`UPDATE quotes SET status = 'quoted', accepted_at = NULL, updated_at = ? WHERE id = ? AND status = 'accepted' AND order_id IS NULL`).bind(this.ctx.now(), id).run()\n throw error\n }\n await this.orders.startPayment(order.id, { provider: 'manual', kind: 'instructions' })\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE quotes SET order_id = ?, shipping_address_json = ?, updated_at = ? WHERE id = ?`).bind(order.id, JSON.stringify(shipTo), this.ctx.now(), id),\n this.event(id, 'accepted', { order_id: order.id, order_number: order.number, ...(po ? { po_number: po } : {}) }),\n ])\n const placed = await this.orders.require(order.id)\n await this.orders.notifyReceived(placed)\n const accepted = await this.require(id)\n await this.notifyMerchant(accepted, 'accepted', placed.number)\n return { quote: accepted, order: placed }\n }\n\n /** The buyer turns a quote down — before or after it is priced. */\n async decline(id: string, reason: string | null = null): Promise<StoreQuote> {\n const quote = await this.require(id)\n if (quote.status === 'declined') return quote\n if (quote.status !== 'requested' && quote.status !== 'quoted' && quote.status !== 'expired') throw conflict(`A ${quote.status} quote cannot be declined`)\n const now = this.ctx.now()\n const why = clean(reason, 1000)\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE quotes SET status = 'declined', declined_at = ?, decline_reason = ?, updated_at = ? WHERE id = ?`).bind(now, why, now, id),\n this.event(id, 'declined', why ? { reason: why } : null),\n ])\n const declined = await this.require(id)\n await this.notifyMerchant(declined, 'declined')\n return declined\n }\n\n // ---- operations system (Store API) ------------------------------------\n\n /**\n * Price a quote. The lines replace the request's: the operations system may change a quantity, drop a line or add\n * an alternative (a SKU the store does not list needs a `name`). Re-offering a live or lapsed quote revises it.\n */\n async offer(id: string, input: QuoteOfferInput): Promise<StoreQuote> {\n const quote = await this.require(id)\n if (!OFFERABLE.includes(quote.status)) throw conflict(`A ${quote.status} quote cannot be re-priced`)\n const currency = quote.currency.toUpperCase()\n const details: Record<string, string> = {}\n if (!input.lines.length) details['offer.lines'] = 'must have at least one line'\n if (input.lines.length > MAX_CART_LINES) details['offer.lines'] = `has more than ${MAX_CART_LINES} lines`\n const catalog = await this.catalog.load()\n const max = this.ctx.config.quotes.maxQuantity\n const requested = new Map(quote.lines.map((l) => [l.sku, l]))\n const seen = new Set<string>()\n const lines = input.lines.map((l, i) => {\n const product = catalog.product(l.sku)\n const name = clean(l.name, 300) ?? product?.name ?? requested.get(l.sku)?.name ?? null\n if (!name) details[`offer.lines.${i}.name`] = 'is required for a SKU this store does not list'\n if (seen.has(l.sku)) details[`offer.lines.${i}.sku`] = 'appears twice'\n seen.add(l.sku)\n if (l.quantity > max) details[`offer.lines.${i}.quantity`] = `must be at most ${max}`\n if (l.unit_amount.currency.toUpperCase() !== currency) details[`offer.lines.${i}.unit_amount.currency`] = `must be ${currency}`\n return {\n sku: l.sku,\n name: name ?? l.sku,\n quantity: l.quantity,\n unit: l.unit_amount.amount,\n total: l.unit_amount.amount * l.quantity,\n list: product?.price.amount ?? requested.get(l.sku)?.listAmount?.amount ?? null,\n image: product?.images[0]?.src ?? requested.get(l.sku)?.image ?? null,\n }\n })\n for (const [key, m] of [['shipping', input.shipping], ['tax', input.tax]] as const) {\n if (m && m.currency.toUpperCase() !== currency) details[`offer.${key}.currency`] = `must be ${currency}`\n }\n const now = this.ctx.now()\n if (input.expires_at && input.expires_at <= now) details['offer.expires_at'] = 'must be in the future'\n if (Object.keys(details).length) throw invalid('Offer is invalid', details)\n\n const subtotal = lines.reduce((n, l) => n + l.total, 0)\n const shipping = input.shipping?.amount ?? 0\n const tax = input.tax?.amount ?? 0\n const total = subtotal + shipping + tax\n const db = this.ctx.db\n const statements: DbStatement[] = [\n db.prepare(`DELETE FROM quote_lines WHERE quote_id = ?`).bind(id),\n ...lines.map((l, position) =>\n db\n .prepare(`INSERT INTO quote_lines (id, quote_id, sku, name, quantity, list_amount, unit_amount, total_amount, image_url, position) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)\n .bind(newId('qln'), id, l.sku, l.name, l.quantity, l.list, l.unit, l.total, l.image, position)\n ),\n db\n .prepare(\n `UPDATE quotes SET status = 'quoted', subtotal = ?, shipping = ?, tax = ?, total = ?, offer_note = ?, expires_at = ?, quoted_at = ?, updated_at = ?\n WHERE id = ? AND status IN ('requested', 'quoted', 'expired')`\n )\n .bind(subtotal, shipping, tax, total, clean(input.note, 4000), input.expires_at ?? null, now, now, id),\n this.event(id, quote.status === 'requested' ? 'quoted' : 'requoted', { total, lines: lines.length, expires_at: input.expires_at ?? null }),\n ]\n await db.batch(statements)\n const priced = await this.require(id)\n if (input.notify_customer !== false) await this.send(id, 'quote_ready', priced.customer.email, quoteReadyMail(priced, this.mailContext()))\n return priced\n }\n\n async setExternalReference(id: string, reference: string | null): Promise<StoreQuote> {\n const quote = await this.require(id)\n if (quote.externalReference === reference) return quote\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE quotes SET external_reference = ?, updated_at = ? WHERE id = ?`).bind(reference, now, id),\n this.event(id, 'external_reference_set', { reference }),\n ])\n return this.require(id)\n }\n\n /** The merchant withdraws a quote. An accepted one has an order: cancel that instead. */\n async cancel(id: string, reason: string | null = null): Promise<StoreQuote> {\n const quote = await this.require(id)\n if (quote.status === 'cancelled') return quote\n if (quote.status === 'accepted') throw conflict('An accepted quote has become an order; cancel the order instead')\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE quotes SET status = 'cancelled', cancelled_at = ?, updated_at = ? WHERE id = ?`).bind(now, now, id),\n this.event(id, 'cancelled', reason ? { reason } : null),\n ])\n return this.require(id)\n }\n\n /**\n * Lapse offers past their `expires_at`. Lazy, like abandoned checkouts: it runs at the start of the Store API quote\n * list and of an accept, so no cron is needed and a consumer never sees a lapsed offer as live.\n */\n async expireStale(limit = 200): Promise<number> {\n const now = this.ctx.now()\n const { results } = await this.ctx.db\n .prepare(`SELECT id FROM quotes WHERE status = 'quoted' AND expires_at IS NOT NULL AND expires_at <= ? ORDER BY expires_at LIMIT ?`)\n .bind(now, Math.min(Math.max(limit, 1), 500))\n .all<{ id: string }>()\n if (!results.length) return 0\n const statements = results.flatMap((r) => [\n this.ctx.db.prepare(`UPDATE quotes SET status = 'expired', updated_at = ? WHERE id = ? AND status = 'quoted'`).bind(now, r.id),\n this.event(r.id, 'expired'),\n ])\n for (const part of chunk(statements, 250)) await this.ctx.db.batch(part)\n return results.length\n }\n\n // ---- internals ---------------------------------------------------------\n\n private async assertNotFlooding(email: string): Promise<void> {\n const since = new Date(new Date(this.ctx.now()).getTime() - 3_600_000).toISOString()\n const row = await this.ctx.db.prepare(`SELECT COUNT(*) AS n FROM quotes WHERE email = ? AND created_at > ?`).bind(email, since).first<{ n: number }>()\n if ((row?.n ?? 0) >= this.ctx.config.quotes.maxRequestsPerHour) {\n throw new StoreError('rate_limited', 'Too many quote requests from this address. Please try again in an hour, or contact us directly.')\n }\n }\n\n private event(quoteId: string, type: string, data: Record<string, unknown> | null = null): DbStatement {\n return this.ctx.db.prepare(`INSERT INTO quote_events (quote_id, type, data_json, created_at) VALUES (?, ?, ?, ?)`).bind(quoteId, type, data ? JSON.stringify(data) : null, this.ctx.now())\n }\n\n private mailContext() {\n return { config: this.ctx.config, siteUrl: this.ctx.siteUrl }\n }\n\n /** The merchant hears about requests and answers at QUOTE_NOTIFICATION_EMAIL, else ORDER_NOTIFICATION_EMAIL. */\n private async notifyMerchant(quote: StoreQuote, event: 'requested' | 'accepted' | 'declined', orderNumber?: string): Promise<void> {\n const to = this.ctx.env.QUOTE_NOTIFICATION_EMAIL || this.ctx.env.ORDER_NOTIFICATION_EMAIL\n if (to) await this.send(quote.id, `merchant_quote_${event}`, to, merchantQuoteMail(quote, this.mailContext(), event, orderNumber), quote.customer.email)\n }\n\n private async send(quoteId: string, kind: string, to: string, mail: RenderedMail, replyTo?: string): Promise<void> {\n try {\n await this.ctx.mail.send({ to, ...mail, ...(replyTo ? { replyTo } : {}) })\n await this.event(quoteId, 'email_sent', { kind, to }).run()\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n this.ctx.log(`quotes: ${kind} email failed`, { quoteId, to, error: message })\n await this.event(quoteId, 'email_failed', { kind, to, error: message }).run().catch(() => {})\n }\n }\n\n private async hydrate(sql: string, args: (string | number)[]): Promise<StoreQuote[]> {\n const { results: rows } = await this.ctx.db.prepare(sql).bind(...args).all<QuoteRow>()\n if (!rows.length) return []\n const lines = new Map<string, QuoteLineRow[]>()\n for (const part of chunk(rows.map((r) => r.id))) {\n const { results } = await this.ctx.db.prepare(`SELECT * FROM quote_lines WHERE quote_id IN (${placeholders(part.length)}) ORDER BY position, id`).bind(...part).all<QuoteLineRow>()\n for (const r of results) (lines.get(r.quote_id) ?? lines.set(r.quote_id, []).get(r.quote_id)!).push(r)\n }\n return rows.map((r) => rowToQuote(r, lines.get(r.id) ?? []))\n }\n}\n\nfunction rowToQuote(r: QuoteRow, lines: QuoteLineRow[]): StoreQuote {\n const money = (amount: number | null): Money | null => (amount === null || amount === undefined ? null : { amount, currency: r.currency })\n return {\n id: r.id,\n number: String(r.number),\n status: r.status as QuoteStatus,\n currency: r.currency,\n customer: { id: r.customer_id, email: r.email, name: r.customer_name, phone: r.customer_phone },\n company: r.company,\n message: r.message,\n shippingAddress: r.shipping_address_json ? parseJson<Address | null>(r.shipping_address_json, null) : null,\n lines: lines.map(\n (l): QuoteLine => ({\n id: l.id,\n sku: l.sku,\n name: l.name,\n quantity: l.quantity,\n listAmount: money(l.list_amount),\n unitAmount: money(l.unit_amount),\n totalAmount: money(l.total_amount),\n ...(l.image_url ? { image: l.image_url } : {}),\n })\n ),\n subtotal: money(r.subtotal),\n shipping: money(r.shipping),\n tax: money(r.tax),\n total: money(r.total),\n offerNote: r.offer_note,\n expiresAt: r.expires_at,\n externalReference: r.external_reference,\n orderId: r.order_id,\n declineReason: r.decline_reason,\n metadata: parseJson<Record<string, string>>(r.metadata_json, {}),\n accessKey: r.access_key,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n quotedAt: r.quoted_at,\n acceptedAt: r.accepted_at,\n declinedAt: r.declined_at,\n cancelledAt: r.cancelled_at,\n }\n}\n","import { magicLinkMail } from '../mail/templates'\nimport type { Customer } from '../types'\nimport type { StoreContext } from './context'\nimport { hashToken, randomToken } from './crypto'\nimport type { CustomersService } from './customers'\nimport { StoreError } from './errors'\n\nexport const MAGIC_LINK_MINUTES = 15\nexport const SESSION_DAYS = 30\n\nexport interface MagicLink {\n token: string\n url: string\n expiresAt: string\n customer: Customer\n}\n\nexport interface Session {\n token: string\n expiresAt: string\n customer: Customer\n}\n\n/**\n * Passwordless sign-in. A magic link is a single-use token emailed to a known\n * customer; exchanging it creates a 30-day session. Only hashes are stored.\n */\nexport class SessionsService {\n constructor(private readonly ctx: StoreContext, private readonly customers: CustomersService) {}\n\n private hash(token: string) {\n return hashToken(token, this.ctx.env.SESSION_SECRET)\n }\n\n /** Create a link for a known customer; null for an unknown email (the caller answers the same either way). */\n async createMagicLink(email: string): Promise<MagicLink | null> {\n const customer = await this.customers.byEmail(email)\n if (!customer) return null\n const token = randomToken(32)\n const now = this.ctx.now()\n const expiresAt = new Date(new Date(now).getTime() + MAGIC_LINK_MINUTES * 60_000).toISOString()\n await this.ctx.db\n .prepare(`INSERT INTO magic_links (token_hash, customer_id, expires_at, created_at) VALUES (?, ?, ?, ?)`)\n .bind(await this.hash(token), customer.id, expiresAt, now)\n .run()\n return { token, url: `${this.ctx.siteUrl}/account/verify/?token=${token}`, expiresAt, customer }\n }\n\n /** Email a sign-in link when the address belongs to a customer. Returns whether one was sent. */\n async sendMagicLink(email: string): Promise<boolean> {\n const link = await this.createMagicLink(email)\n if (!link) return false\n const mail = magicLinkMail({ url: link.url, expiresMinutes: MAGIC_LINK_MINUTES }, { config: this.ctx.config, siteUrl: this.ctx.siteUrl })\n await this.ctx.mail.send({ to: link.customer.email, ...mail })\n return true\n }\n\n /** Exchange a magic-link token for a session. Throws `not_found` when the link is unknown, used or expired. */\n async exchange(token: string): Promise<Session> {\n const now = this.ctx.now()\n const hash = await this.hash(token)\n const link = await this.ctx.db\n .prepare(`SELECT customer_id, expires_at, used_at FROM magic_links WHERE token_hash = ?`)\n .bind(hash)\n .first<{ customer_id: string; expires_at: string; used_at: string | null }>()\n if (!link || link.used_at || link.expires_at <= now) throw new StoreError('not_found', 'This sign-in link is invalid, used or expired')\n const customer = await this.customers.byId(link.customer_id)\n if (!customer) throw new StoreError('not_found', 'This sign-in link is invalid, used or expired')\n const sessionToken = randomToken(32)\n const expiresAt = new Date(new Date(now).getTime() + SESSION_DAYS * 86_400_000).toISOString()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`UPDATE magic_links SET used_at = ? WHERE token_hash = ? AND used_at IS NULL`).bind(now, hash),\n this.ctx.db\n .prepare(`INSERT INTO customer_sessions (token_hash, customer_id, expires_at, created_at, last_seen_at) VALUES (?, ?, ?, ?, ?)`)\n .bind(await this.hash(sessionToken), customer.id, expiresAt, now, now),\n ])\n return { token: sessionToken, expiresAt, customer }\n }\n\n /** The customer behind a session token, or null when it is unknown or expired. */\n async resolve(token: string | null | undefined): Promise<Customer | null> {\n if (!token) return null\n const now = this.ctx.now()\n const hash = await this.hash(token)\n const row = await this.ctx.db\n .prepare(`SELECT customer_id, expires_at, last_seen_at FROM customer_sessions WHERE token_hash = ?`)\n .bind(hash)\n .first<{ customer_id: string; expires_at: string; last_seen_at: string }>()\n if (!row || row.expires_at <= now) return null\n if (new Date(now).getTime() - new Date(row.last_seen_at).getTime() > 3_600_000) {\n await this.ctx.db.prepare(`UPDATE customer_sessions SET last_seen_at = ? WHERE token_hash = ?`).bind(now, hash).run()\n }\n return this.customers.byId(row.customer_id)\n }\n\n async signOut(token: string | null | undefined): Promise<void> {\n if (!token) return\n await this.ctx.db.prepare(`DELETE FROM customer_sessions WHERE token_hash = ?`).bind(await this.hash(token)).run()\n }\n\n /** Housekeeping: drop expired links and sessions. Safe to call from any request. */\n async prune(): Promise<void> {\n const now = this.ctx.now()\n await this.ctx.db.batch([\n this.ctx.db.prepare(`DELETE FROM magic_links WHERE expires_at <= ? OR used_at IS NOT NULL`).bind(now),\n this.ctx.db.prepare(`DELETE FROM customer_sessions WHERE expires_at <= ?`).bind(now),\n ])\n }\n}\n","/**\n * The slice of Cloudflare D1's API the store uses. A `D1Database` binding\n * satisfies it as-is; tests satisfy it with better-sqlite3 (see test/sqlite.ts).\n *\n * Rules the store code follows so both back ends behave the same:\n * - bind values are string | number | null | ArrayBuffer — never undefined or boolean\n * - `first()`/`all()` only on statements that return rows\n * - anything that must be atomic goes through `batch()`\n */\nexport interface DbStatement {\n bind(...values: DbValue[]): DbStatement\n first<T = Record<string, DbValue>>(): Promise<T | null>\n all<T = Record<string, DbValue>>(): Promise<{ results: T[] }>\n run(): Promise<{ meta?: { changes?: number; last_row_id?: number } }>\n}\n\nexport type DbValue = string | number | null | ArrayBuffer\n\nexport interface Database {\n prepare(sql: string): DbStatement\n batch<T = unknown>(statements: DbStatement[]): Promise<{ results: T[] }[]>\n}\n\n/** Coerce a value into something D1 accepts as a bind parameter. */\nexport function bindable(value: unknown): DbValue {\n if (value === undefined || value === null) return null\n if (typeof value === 'boolean') return value ? 1 : 0\n if (typeof value === 'number' || typeof value === 'string') return value\n if (value instanceof ArrayBuffer) return value\n return JSON.stringify(value)\n}\n","import type { StorefrontConfig } from '../config'\nimport type { Database } from '../db/driver'\nimport { consoleMail } from '../mail/console'\nimport type { MailAdapter } from '../mail/types'\nimport { CatalogService } from './catalog'\nimport { CheckoutService } from './checkout'\nimport { ConnectionsService } from './connections'\nimport { PartnersService } from './partners'\nimport type { StoreContext, StoreEnv } from './context'\nimport { CustomersService } from './customers'\nimport { EventsService } from './events'\nimport { InventoryService } from './inventory'\nimport { OrdersService } from './orders'\nimport { OwnersService } from './owners'\nimport { QuotesService } from './quotes'\nimport { SessionsService } from './sessions'\n\nexport const STORE_PLATFORM_VERSION = '2.2.0'\n\nexport interface StoreOptions {\n db: Database\n config: StorefrontConfig\n env?: StoreEnv\n mail?: MailAdapter\n /** Injectable clock for tests. */\n clock?: () => Date\n /** How long an isolate keeps a catalog snapshot. 0 disables the cache. */\n catalogCacheTtlMs?: number\n log?: (message: string, data?: unknown) => void\n}\n\nexport interface StoreHealth {\n name: string\n url: string\n currency: string\n locale: string\n version: string\n counts: { products: number; categories: number; orders: number; customers: number; quotes: number }\n /** True once an owner exists, so a consumer can tell the merchant to run `owner:create` first. */\n owner_configured: boolean\n time: string\n}\n\nexport interface Store {\n readonly db: Database\n readonly config: StorefrontConfig\n readonly env: StoreEnv\n readonly mail: MailAdapter\n readonly siteUrl: string\n now(): string\n readonly catalog: CatalogService\n readonly inventory: InventoryService\n readonly customers: CustomersService\n readonly orders: OrdersService\n readonly checkout: CheckoutService\n readonly quotes: QuotesService\n readonly sessions: SessionsService\n readonly events: EventsService\n readonly owners: OwnersService\n readonly connections: ConnectionsService\n readonly partners: PartnersService\n health(): Promise<StoreHealth>\n}\n\n/**\n * The store: every service over one database. Cheap to create, so make one\n * per request (the Astro convention is `store(Astro)` in src/lib/store.ts).\n */\nexport function createStore(options: StoreOptions): Store {\n const env = options.env ?? {}\n const clock = options.clock ?? (() => new Date())\n const ctx: StoreContext = {\n db: options.db,\n config: options.config,\n env,\n mail: options.mail ?? consoleMail(),\n now: () => clock().toISOString(),\n siteUrl: (env.PUBLIC_SITE_URL || options.config.url).replace(/\\/+$/, ''),\n log: options.log ?? ((message, data) => console.warn(message, data === undefined ? '' : JSON.stringify(data))),\n }\n const catalog = new CatalogService(ctx, options.catalogCacheTtlMs ?? 30_000)\n const inventory = new InventoryService(ctx, (skus) => catalog.existingSkus(skus), () => catalog.invalidate())\n const events = new EventsService(ctx)\n const customers = new CustomersService(ctx)\n const orders = new OrdersService(ctx, events, inventory)\n const checkout = new CheckoutService(ctx, catalog, orders, events)\n const quotes = new QuotesService(ctx, catalog, checkout, orders)\n const sessions = new SessionsService(ctx, customers)\n const owners = new OwnersService(ctx)\n const connections = new ConnectionsService(ctx)\n const partners = new PartnersService(ctx)\n\n return {\n db: ctx.db,\n config: ctx.config,\n env,\n mail: ctx.mail,\n siteUrl: ctx.siteUrl,\n now: ctx.now,\n catalog,\n inventory,\n customers,\n orders,\n checkout,\n quotes,\n sessions,\n events,\n owners,\n connections,\n partners,\n async health() {\n const [counts, orderCount, customerCount, ownerCount, quoteCount] = await Promise.all([catalog.counts(), orders.count(), customers.count(), owners.count(), quotes.count()])\n return {\n name: ctx.config.name,\n url: ctx.config.url,\n currency: ctx.config.currency.toUpperCase(),\n locale: ctx.config.locale,\n version: STORE_PLATFORM_VERSION,\n counts: { ...counts, orders: orderCount, customers: customerCount, quotes: quoteCount },\n owner_configured: ownerCount > 0,\n time: ctx.now(),\n }\n },\n }\n}\n\nexport type { StoreContext, StoreEnv } from './context'\nexport type { Database, DbStatement, DbValue } from '../db/driver'\nexport { bindable } from '../db/driver'\nexport { StoreError, type StoreErrorCode } from './errors'\nexport {\n CatalogService, type ProductInput, type CategoryInput, type ProductRecord, type CategoryRecord,\n type ProductPage, type CategoryPage, type ProductListQuery, type CategoryListQuery, deriveAvailability,\n} from './catalog'\nexport { InventoryService, type InventoryItemInput, type InventoryResult } from './inventory'\nexport { CustomersService, normalizeEmail } from './customers'\nexport { OrdersService, ABANDONED_AFTER_MINUTES, type PaymentRecord, type FulfillmentInput, type ChangedPage } from './orders'\nexport { CheckoutService, checkShippingAddress, type CheckoutInput, type CheckoutItem, type Quote, type PricedLine, type PricedOrderSpec } from './checkout'\nexport { QuotesService, type QuoteRequestInput, type QuoteRequestItem, type QuoteOfferInput, type QuoteAcceptInput, type QuoteChangedPage } from './quotes'\nexport { SessionsService, type MagicLink, type Session, MAGIC_LINK_MINUTES, SESSION_DAYS } from './sessions'\nexport { EventsService } from './events'\nexport { newId, randomToken, randomBase64Url, hashToken, sha256Hex, secretsEqual } from './crypto'\nexport { OwnersService, normalizeOwnerEmail, type Owner, type OwnerSession, type OwnerResetLink, OWNER_SESSION_DAYS, OWNER_RESET_MINUTES } from './owners'\nexport {\n ConnectionsService, isLocalHost, type Connection, type AuthorizeParams, type AuthorizeRequest, type IssuedKey, type ConnectScope,\n CONNECT_CODE_MINUTES, CONNECT_SCOPES, SCOPE_DESCRIPTIONS, API_KEY_PREFIX,\n} from './connections'\nexport { hashPassword, verifyPassword, passwordProblem, PASSWORD_MIN_LENGTH, PASSWORD_ITERATIONS } from './password'\nexport { RateLimiter, type RateLimitPolicy } from './ratelimit'\nexport { PartnersService, PARTNER_LINK_MINUTES, PARTNER_SESSION_DAYS, type PartnerReport, type PartnerReportLine, type PartnerReportOptions, type PartnerTraffic, type TrafficDay, type TrafficSource, type VisitInput } from './partners'\n"],"mappings":"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","names":["encodeCursor","decodeCursor"]}