@gigamusic/checkout 4.8.0 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/checkout.ts","../src/fulfill.ts","../src/webhook.ts","../src/filenames.ts","../src/download.ts","../src/zip.ts","../src/zip-stream.ts","../src/sw-zip-fallback.ts"],"names":["session","Stripe","url"],"mappings":"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","file":"index.js","sourcesContent":["import type { NextRequest } from \"next/server\";\nimport Stripe from \"stripe\";\nimport type { ReleaseWithTracks, TrackWithFiles } from \"@gigamusic/db\";\nimport type { CheckoutCartItem, CheckoutDeps } from \"./types.js\";\n\n/**\n * Stripe rejects card charges below 50 cents (USD). A pay-what-you-want\n * override below this is clamped up rather than rejected, so a customer who\n * types a token amount still completes checkout instead of hitting an opaque\n * Stripe error. A `0` override never reaches here — the consumer routes free\n * downloads through its own gate, not the cart.\n */\nconst STRIPE_MIN_CHARGE_CENTS = 50;\n\n/**\n * Resolve the per-line `unit_amount`: a positive pay-what-you-want override\n * (clamped up to the Stripe minimum) when supplied, otherwise the catalog\n * price. Non-positive / non-finite overrides fall back to the catalog price.\n */\nfunction resolveUnitAmount(\n overrideCents: number | undefined,\n catalogCents: number,\n): number {\n if (\n typeof overrideCents === \"number\" &&\n Number.isFinite(overrideCents) &&\n overrideCents > 0\n ) {\n return Math.max(STRIPE_MIN_CHARGE_CENTS, Math.round(overrideCents));\n }\n return catalogCents;\n}\n\n/**\n * Split a bundle's charged total across its members, proportional to list\n * price, using largest-remainder allocation.\n *\n * Members are keyed by their `amounts`-metadata key (`r<id>` / `t<id>`) rather\n * than a bare numeric id, so a bundle holding tracks can't have a track id\n * collide with a release id.\n *\n * The returned values sum to `totalCents` exactly. That's load-bearing: they\n * become the `amounts` metadata `fulfillCheckoutSession` turns into\n * `order_items.price` rows, and `sum(order_items) === orders.amountTotal` is\n * what refunds and admin order views rely on. Ties break deterministically\n * (remainder desc, price desc, key asc) so a retry apportions identically.\n */\nfunction apportion(\n totalCents: number,\n members: { key: string; price: number }[],\n): Map<string, number> {\n const result = new Map<string, number>();\n if (members.length === 0) return result;\n\n const total = Math.max(0, Math.round(totalCents));\n const sum = members.reduce((s, m) => s + Math.max(0, m.price), 0);\n // All-free members carry no weight to divide by, so split evenly.\n const weights =\n sum > 0\n ? members.map((m) => Math.max(0, m.price) / sum)\n : members.map(() => 1 / members.length);\n\n const exact = weights.map((w) => total * w);\n const base = exact.map((v) => Math.floor(v));\n let remainder = total - base.reduce((s, v) => s + v, 0);\n\n const order = members\n .map((m, i) => ({ i, frac: exact[i]! - base[i]!, price: m.price, key: m.key }))\n .sort((a, b) => b.frac - a.frac || b.price - a.price || a.key.localeCompare(b.key));\n\n for (const { i } of order) {\n if (remainder <= 0) break;\n base[i]! += 1;\n remainder -= 1;\n }\n\n members.forEach((m, i) => result.set(m.key, base[i]!));\n return result;\n}\n\n/**\n * Build a POST handler that translates `{ items }` from the cart into a Stripe\n * Checkout Session and replies with `{ url }`. Release/track metadata is\n * stashed on the session so the webhook handler can later record the order\n * without re-deriving cart contents.\n *\n * Stripe is hard-wired (per project policy). The Stripe SDK instance is\n * created once per factory call and captured in the closure — no module\n * globals, no `process.env`.\n */\nexport function createCheckoutHandler(\n deps: CheckoutDeps,\n): (req: NextRequest) => Promise<Response> {\n const {\n stripeSecret,\n queries,\n site,\n baseUrl,\n successPath = \"/checkout/success\",\n cancelPath = \"/cart\",\n currency = \"usd\",\n catalogPurchase,\n bundlePurchase,\n } = deps;\n\n const stripe = new Stripe(stripeSecret, { typescript: true });\n\n return async (req: NextRequest): Promise<Response> => {\n let payload: { items?: CheckoutCartItem[] };\n try {\n payload = (await req.json()) as { items?: CheckoutCartItem[] };\n } catch {\n return jsonError(\"Invalid JSON body\", 400);\n }\n\n const items = payload.items;\n if (!items || items.length === 0) {\n return jsonError(\"No items provided\", 400);\n }\n\n const successUrl = `${baseUrl}${successPath}?session_id={CHECKOUT_SESSION_ID}`;\n const cancelUrl = `${baseUrl}${cancelPath}`;\n\n const isCatalogPurchase = items.some((i) => i.kind === \"catalog\");\n const bundleIds = [\n ...new Set(\n items\n .filter((i) => i.kind === \"bundle\" && typeof i.bundleId === \"string\")\n .map((i) => i.bundleId as string),\n ),\n ];\n\n // The catalog already contains every bundle, so charging for both would\n // bill the same releases twice.\n if (isCatalogPurchase && bundleIds.length > 0) {\n return jsonError(\"Cannot mix a catalog purchase with a bundle\", 400);\n }\n\n try {\n if (isCatalogPurchase) {\n const resolved =\n typeof catalogPurchase === \"function\"\n ? await catalogPurchase()\n : catalogPurchase;\n if (!resolved) {\n return jsonError(\"Catalog purchase not configured\", 400);\n }\n // Snapshot the current published-release ids so the webhook records\n // exactly what the customer bought, even if a release is unpublished\n // between session-create and webhook-delivery. The consumer-supplied\n // `totalCents` is what Stripe charges — pricing math lives in the\n // consumer (it owns whatever discount/rounding/tax rules apply).\n const releases = await queries.listPublishedReleases();\n\n const session = await stripe.checkout.sessions.create({\n mode: \"payment\",\n line_items: [\n {\n price_data: {\n currency,\n product_data: { name: resolved.productName },\n unit_amount: resolved.totalCents,\n },\n quantity: 1,\n },\n ],\n metadata: {\n site,\n catalog_purchase: \"true\",\n release_ids: JSON.stringify(releases.map((r) => r.id)),\n track_ids: \"[]\",\n },\n success_url: successUrl,\n cancel_url: cancelUrl,\n });\n\n return Response.json({ url: session.url });\n }\n\n const releaseIds = items\n .filter((i) => i.kind === \"release\" && i.id != null)\n .map((i) => i.id as number);\n const trackIds = items\n .filter((i) => i.kind === \"track\" && i.id != null)\n .map((i) => i.id as number);\n\n if (releaseIds.length === 0 && trackIds.length === 0 && bundleIds.length === 0) {\n return jsonError(\"No valid items found\", 400);\n }\n\n if (bundleIds.length > 0 && typeof bundlePurchase !== \"function\") {\n return jsonError(\"Bundle purchases are not configured\", 400);\n }\n\n // Pay-what-you-want overrides, keyed by id. Items without `priceCents`\n // (the fixed-price default — every thomas-adagio cart) leave the map\n // empty and fall back to the catalog price below.\n const releaseAmountOverride = new Map<number, number>(\n items\n .filter(\n (i) =>\n i.kind === \"release\" &&\n i.id != null &&\n typeof i.priceCents === \"number\",\n )\n .map((i) => [i.id as number, i.priceCents as number]),\n );\n const trackAmountOverride = new Map<number, number>(\n items\n .filter(\n (i) =>\n i.kind === \"track\" &&\n i.id != null &&\n typeof i.priceCents === \"number\",\n )\n .map((i) => [i.id as number, i.priceCents as number]),\n );\n\n // Charged `unit_amount` per line, keyed `r<id>` / `t<id>`. Stamped onto\n // the session so the webhook records the true price paid rather than\n // re-deriving the (suggested) catalog price.\n const chargedAmounts: Record<string, number> = {};\n\n // Single `listPublishedReleases` call sources both release and track\n // line items. Tracks live inside their release, so one fetch keeps the\n // ID/price/cover lookups consistent without a second round-trip.\n const allReleases = await queries.listPublishedReleases();\n const releaseById = new Map(allReleases.map((r) => [r.id, r]));\n const trackContext = new Map<\n number,\n { track: TrackWithFiles; release: ReleaseWithTracks }\n >();\n for (const release of allReleases) {\n for (const track of release.tracks) {\n trackContext.set(track.id, { track, release });\n }\n }\n\n const releaseLineItems = releaseIds\n .map((id) => releaseById.get(id))\n .filter((r): r is ReleaseWithTracks => r !== undefined)\n .map((r) => {\n const amount = resolveUnitAmount(releaseAmountOverride.get(r.id), r.price);\n // Accumulate: the same release can be charged by more than one line\n // (a bundle plus a loose copy in a stale cart), and the recorded\n // order item must carry the total actually paid for it.\n chargedAmounts[`r${r.id}`] = (chargedAmounts[`r${r.id}`] ?? 0) + amount;\n return {\n price_data: {\n currency,\n product_data: {\n name: r.name,\n ...(r.coverImageUrl\n ? { images: [toAbsoluteUrl(r.coverImageUrl, baseUrl)] }\n : {}),\n },\n unit_amount: amount,\n },\n quantity: 1 as const,\n };\n });\n\n const trackLineItems = trackIds\n .map((id) => trackContext.get(id))\n .filter(\n (c): c is { track: TrackWithFiles; release: ReleaseWithTracks } =>\n c !== undefined,\n )\n .map(({ track, release }) => {\n const amount = resolveUnitAmount(\n trackAmountOverride.get(track.id),\n track.price,\n );\n chargedAmounts[`t${track.id}`] = (chargedAmounts[`t${track.id}`] ?? 0) + amount;\n return {\n price_data: {\n currency,\n product_data: {\n name: `${release.name} — ${track.name}`,\n ...(release.coverImageUrl\n ? { images: [toAbsoluteUrl(release.coverImageUrl, baseUrl)] }\n : {}),\n },\n unit_amount: amount,\n },\n quantity: 1 as const,\n };\n });\n\n // Bundles resolve against the same published-release snapshot as the\n // loose lines, so member prices, names, and covers stay consistent. A\n // bundle may hold releases, tracks, or both — the package doesn't care\n // which; the consumer decides what a bundle is allowed to contain.\n const bundleLineItems: typeof releaseLineItems = [];\n const bundleReleaseMemberIds: number[] = [];\n const bundleTrackMemberIds: number[] = [];\n const unavailableBundleIds: string[] = [];\n\n for (const bundleId of bundleIds) {\n const resolved = await bundlePurchase!(bundleId);\n const releaseMembers = (resolved?.releaseIds ?? [])\n .map((id) => releaseById.get(id))\n .filter((r): r is ReleaseWithTracks => r !== undefined);\n const trackMembers = (resolved?.trackIds ?? [])\n .map((id) => trackContext.get(id))\n .filter(\n (c): c is { track: TrackWithFiles; release: ReleaseWithTracks } =>\n c !== undefined,\n );\n\n // No such bundle, or nothing in it is published any more. Charging for\n // the latter would produce a paid order with zero items.\n if (!resolved || releaseMembers.length + trackMembers.length === 0) {\n unavailableBundleIds.push(bundleId);\n continue;\n }\n\n const total = Math.max(\n STRIPE_MIN_CHARGE_CENTS,\n Math.round(resolved.totalCents),\n );\n // A track carries no cover of its own, so a track-only bundle falls\n // back to the cover of the release the first member belongs to.\n const cover =\n releaseMembers.find((m) => m.coverImageUrl)?.coverImageUrl ??\n trackMembers.find((c) => c.release.coverImageUrl)?.release.coverImageUrl;\n bundleLineItems.push({\n price_data: {\n currency,\n product_data: {\n name: resolved.productName,\n ...(cover ? { images: [toAbsoluteUrl(cover, baseUrl)] } : {}),\n },\n unit_amount: total,\n },\n quantity: 1 as const,\n });\n\n // Apportioned jointly across both member kinds — the total has to land\n // on the members as one set, not split per kind and rounded twice.\n for (const [key, cents] of apportion(total, [\n ...releaseMembers.map((m) => ({ key: `r${m.id}`, price: m.price })),\n ...trackMembers.map(({ track }) => ({ key: `t${track.id}`, price: track.price })),\n ])) {\n chargedAmounts[key] = (chargedAmounts[key] ?? 0) + cents;\n }\n bundleReleaseMemberIds.push(...releaseMembers.map((m) => m.id));\n bundleTrackMemberIds.push(...trackMembers.map(({ track }) => track.id));\n }\n\n if (unavailableBundleIds.length > 0) {\n return Response.json(\n { error: \"bundle-unavailable\", bundleIds: unavailableBundleIds },\n { status: 409 },\n );\n }\n\n const lineItems = [...bundleLineItems, ...releaseLineItems, ...trackLineItems];\n if (lineItems.length === 0) {\n return jsonError(\"No valid items found\", 400);\n }\n\n // Deduped: a release or track reachable from both a bundle and a loose\n // line still yields one order item, whose price is the summed `amounts`\n // entry.\n const resolvedReleaseIds = [\n ...new Set([\n ...(releaseLineItems.length ? releaseIds.filter((id) => releaseById.has(id)) : []),\n ...bundleReleaseMemberIds,\n ]),\n ];\n const resolvedTrackIds = [\n ...new Set([\n ...(trackLineItems.length ? trackIds.filter((id) => trackContext.has(id)) : []),\n ...bundleTrackMemberIds,\n ]),\n ];\n\n const session = await stripe.checkout.sessions.create({\n mode: \"payment\",\n line_items: lineItems,\n metadata: {\n site,\n release_ids: JSON.stringify(resolvedReleaseIds),\n track_ids: JSON.stringify(resolvedTrackIds),\n amounts: JSON.stringify(chargedAmounts),\n // Ignored by fulfillment — bundles decompose into per-release order\n // items — but invaluable when supporting \"what did I actually buy?\".\n ...(bundleIds.length > 0 ? { bundle_ids: JSON.stringify(bundleIds) } : {}),\n },\n success_url: successUrl,\n cancel_url: cancelUrl,\n });\n\n return Response.json({ url: session.url });\n } catch (err) {\n return jsonError(\"Failed to create checkout session\", 500, err);\n }\n };\n}\n\n/** Resolve a stored relative URL against `baseUrl` and percent-encode the path. Bare http(s) URLs pass through. */\nfunction toAbsoluteUrl(url: string, baseUrl: string): string {\n const raw = url.startsWith(\"http\")\n ? url\n : `${baseUrl}${url.startsWith(\"/\") ? \"\" : \"/\"}${url}`;\n try {\n const parsed = new URL(raw);\n parsed.pathname = encodeURI(decodeURI(parsed.pathname));\n return parsed.toString();\n } catch {\n return raw;\n }\n}\n\nfunction jsonError(message: string, status: number, cause?: unknown): Response {\n if (cause) {\n // Logged via console.error. Consumers can swap this for a structured\n // logger in a future iteration; the package contract doesn't expose a\n // logger slot yet.\n console.error(`[checkout] ${message}:`, cause);\n }\n return Response.json({ error: message }, { status });\n}\n","import type Stripe from \"stripe\";\nimport { signOrderToken } from \"@gigamusic/core\";\nimport {\n isUniqueConstraintError,\n type OrderWithItems,\n type ReleaseWithTracks,\n type TrackWithFiles,\n} from \"@gigamusic/db\";\nimport { renderPurchaseConfirmation } from \"@gigamusic/email\";\nimport type { FulfillSessionDeps, PurchaseConfirmationDeps } from \"./types.js\";\n\nexport type FulfillSessionResult =\n /** This call wrote the order row. The caller owns sending the confirmation email. */\n | {\n status: \"created\";\n order: OrderWithItems;\n itemNames: string[];\n customerEmail: string;\n totalCents: number;\n }\n /**\n * The order was already on disk — either the pre-flight lookup found it or a\n * concurrent writer won the unique-constraint race. `order` is null only if\n * the re-read after a lost race somehow came back empty.\n */\n | { status: \"already-recorded\"; order: OrderWithItems | null }\n /** Nothing was written and nothing is owed. */\n | { status: \"skipped\"; reason: \"site-mismatch\" | \"no-items\" | \"unpaid\" };\n\n/**\n * Turn a completed Stripe Checkout Session into a persisted order, idempotently.\n *\n * This is the single fulfillment path shared by the Stripe webhook and any\n * consumer that wants to fulfill synchronously — e.g. a post-checkout success\n * page that would otherwise have to wait on webhook delivery to show download\n * links. Whoever calls first writes the row; everyone after gets\n * `already-recorded`. That guarantee rests on the unique constraint over\n * `stripeSessionId` inside `recordCompletedOrder`, not on call ordering, so\n * concurrent callers are safe.\n *\n * Only `status: \"created\"` should trigger side effects (the confirmation\n * email) — that status is returned to exactly one caller per session.\n *\n * ### Published-only resolution (gotcha)\n *\n * Release/track IDs from `session.metadata` are resolved against\n * `queries.listPublishedReleases()`. Anything not in the published catalog at\n * fulfillment time is silently dropped from the persisted order's line items —\n * the customer is still charged, but the order row doesn't include the\n * unpublished item.\n *\n * In the canonical flow this can't happen: `createCheckoutHandler` filters on\n * the same `listPublishedReleases()` before it ever creates the Stripe\n * Session. The only ways an unpublished ID can reach here are:\n * - an admin unpublishes between session-create and fulfillment\n * (a several-second race window),\n * - a consumer bypasses `createCheckoutHandler` and creates Stripe sessions\n * directly with their own metadata.\n *\n * If either is a realistic concern, swap the resolution call here to\n * `listAllReleases()` (or wrap with a \"warn + fallback\" branch) — but be aware\n * that doing so will surface unpublished items in admin order views.\n */\nexport async function fulfillCheckoutSession(\n session: Stripe.Checkout.Session,\n deps: FulfillSessionDeps,\n): Promise<FulfillSessionResult> {\n const { queries, site } = deps;\n\n // One Stripe account fans every event out to every registered endpoint, and\n // a success URL can be hand-forged. Ignore sessions belonging to a different\n // site so we never record a foreign purchase (with zero matched line items)\n // under this one.\n if (session.metadata?.site !== site) {\n return { status: \"skipped\", reason: \"site-mismatch\" };\n }\n\n // Delayed-notification methods (bank debits, vouchers) complete checkout with\n // the money still in flight; the session only flips to `paid` later, via\n // `checkout.session.async_payment_succeeded`. Handing over downloads at that\n // point would be giving away unpaid goods. `no_payment_required` (fully\n // discounted) and legacy sessions with no `payment_status` are fulfillable.\n if (session.payment_status === \"unpaid\") {\n return { status: \"skipped\", reason: \"unpaid\" };\n }\n\n const existing = await queries.getOrderByStripeSessionId(session.id);\n if (existing) {\n return { status: \"already-recorded\", order: existing };\n }\n\n const releaseIds = safeJsonIdList(session.metadata?.release_ids);\n const trackIds = safeJsonIdList(session.metadata?.track_ids);\n // Per-line amounts the customer actually paid, keyed `r<id>` / `t<id>`,\n // stamped by `createCheckoutHandler`. Absent on pre-upgrade sessions and on\n // catalog purchases — those fall back to the catalog price below.\n const chargedAmounts = safeAmountMap(session.metadata?.amounts);\n\n if (releaseIds.length === 0 && trackIds.length === 0) {\n return { status: \"skipped\", reason: \"no-items\" };\n }\n\n // Snapshot the published catalog once so we can resolve both release and\n // track prices + names off a single in-memory lookup.\n const allReleases = await queries.listPublishedReleases();\n const releaseById = new Map(allReleases.map((r) => [r.id, r]));\n const trackContext = new Map<\n number,\n { track: TrackWithFiles; release: ReleaseWithTracks }\n >();\n for (const release of allReleases) {\n for (const track of release.tracks) {\n trackContext.set(track.id, { track, release });\n }\n }\n\n const matchedReleases = releaseIds\n .map((id) => releaseById.get(id))\n .filter((r): r is ReleaseWithTracks => r !== undefined);\n const matchedTracks = trackIds\n .map((id) => trackContext.get(id))\n .filter(\n (c): c is { track: TrackWithFiles; release: ReleaseWithTracks } => c !== undefined,\n );\n\n const orderItems = [\n ...matchedReleases.map((r) => ({\n releaseId: r.id,\n price: chargedAmounts[`r${r.id}`] ?? r.price,\n })),\n ...matchedTracks.map(({ track }) => ({\n trackId: track.id,\n price: chargedAmounts[`t${track.id}`] ?? track.price,\n })),\n ];\n\n const customerEmail = session.customer_details?.email ?? \"\";\n const totalCents = session.amount_total ?? 0;\n\n let order: OrderWithItems;\n try {\n order = await queries.recordCompletedOrder({\n stripeSessionId: session.id,\n stripePaymentId:\n typeof session.payment_intent === \"string\" ? session.payment_intent : null,\n email: customerEmail,\n amountTotal: totalCents,\n items: orderItems,\n });\n } catch (err) {\n // Two callers can both pass the pre-flight lookup and race here — a webhook\n // retry, or the success page rendering while the webhook is mid-flight. The\n // unique constraint on `stripeSessionId` guarantees only one row lands; the\n // loser surfaces as a 23505 unique-violation. Re-read so the loser still\n // gets the order to render, but without claiming the \"created\" side effects.\n if (isUniqueConstraintError(err)) {\n return {\n status: \"already-recorded\",\n order: (await queries.getOrderByStripeSessionId(session.id)) ?? null,\n };\n }\n throw err;\n }\n\n const itemNames = [\n ...matchedReleases.map((r) => r.name),\n ...matchedTracks.map(({ track, release }) => `${release.name} — ${track.name}`),\n ];\n\n return { status: \"created\", order, itemNames, customerEmail, totalCents };\n}\n\n/**\n * Render and send the post-purchase confirmation email carrying the durable\n * magic link back to the customer's downloads.\n *\n * Errors are logged, never thrown: the order is already recorded and paid for,\n * so a mail-provider hiccup must not fail the caller (a webhook returning 500\n * would make Stripe retry a fulfillment that already succeeded). Callers\n * typically wrap this in `after()` so the response isn't blocked on it.\n */\nexport async function sendPurchaseConfirmation(\n args: {\n orderId: number;\n customerEmail: string;\n itemNames: string[];\n totalCents: number;\n },\n deps: PurchaseConfirmationDeps,\n): Promise<void> {\n const {\n email,\n branding,\n emailFrom,\n baseUrl,\n orderTokenSecret,\n orderTokenExpiry = null,\n verifyPath = \"/orders/verify\",\n renderConfirmation = renderPurchaseConfirmation,\n } = deps;\n\n if (!args.customerEmail) {\n // Stripe almost always supplies an email but doesn't guarantee it. The\n // order is safely recorded; manual intervention required to deliver the\n // magic link.\n console.warn(\n `[gigamusic/checkout] order ${args.orderId} has no customer email — recorded, but the customer cannot retrieve downloads.`,\n );\n return;\n }\n\n try {\n const verifyToken = await signOrderToken({\n orderId: String(args.orderId),\n email: args.customerEmail,\n secret: orderTokenSecret,\n expiresIn: orderTokenExpiry,\n });\n const verifyUrl = `${baseUrl}${verifyPath}?token=${verifyToken}`;\n const { subject, html } = await renderConfirmation({\n branding,\n verifyUrl,\n itemNames: args.itemNames,\n totalCents: args.totalCents,\n });\n await email.send({ from: emailFrom, to: args.customerEmail, subject, html });\n } catch (err) {\n console.error(\n `[gigamusic/checkout] failed to send purchase confirmation to ${args.customerEmail} for order ${args.orderId}:`,\n err,\n );\n }\n}\n\n/**\n * Parse the JSON-stringified `{ \"r<id>\" | \"t<id>\": cents }` amount map from\n * Stripe metadata, returning `{}` on any failure. Non-finite values are\n * dropped so a malformed entry falls back to the catalog price at the call site.\n */\nfunction safeAmountMap(value: string | undefined | null): Record<string, number> {\n if (!value) return {};\n try {\n const parsed: unknown = JSON.parse(value);\n if (typeof parsed !== \"object\" || parsed === null) return {};\n const out: Record<string, number> = {};\n for (const [key, raw] of Object.entries(parsed as Record<string, unknown>)) {\n const n = Number(raw);\n if (Number.isFinite(n)) out[key] = n;\n }\n return out;\n } catch {\n return {};\n }\n}\n\n/** Parse a JSON-stringified id array from Stripe metadata, returning `[]` on any failure. NaN entries are dropped. */\nfunction safeJsonIdList(value: string | undefined | null): number[] {\n if (!value) return [];\n try {\n const parsed: unknown = JSON.parse(value);\n if (!Array.isArray(parsed)) return [];\n return parsed.map((v) => Number(v)).filter((n) => Number.isFinite(n));\n } catch {\n return [];\n }\n}\n","import { after, type NextRequest } from \"next/server\";\nimport Stripe from \"stripe\";\nimport { fulfillCheckoutSession, sendPurchaseConfirmation } from \"./fulfill.js\";\nimport type { WebhookDeps } from \"./types.js\";\n\n/**\n * Build the POST handler for Stripe's webhook endpoint. Verifies the\n * signature, records the order on `checkout.session.completed` via the shared\n * `fulfillCheckoutSession`, then defers the magic-link email via `after()` so\n * Stripe sees a fast 200.\n *\n * The handler is not the only fulfillment path — a consumer's success page can\n * call `fulfillCheckoutSession` directly rather than waiting on webhook\n * delivery. Whichever runs first writes the row; the other gets\n * `already-recorded` and skips the email, so the customer is mailed exactly\n * once no matter who wins. Every non-`created` outcome answers Stripe with\n * `{ received: true }` so it doesn't retry.\n */\nexport function createStripeWebhookHandler(\n deps: WebhookDeps,\n): (req: NextRequest) => Promise<Response> {\n const { stripe: stripeOverride, stripeSecret, webhookSecret, queries, site } = deps;\n\n if (!stripeOverride && !stripeSecret) {\n throw new Error(\n \"createStripeWebhookHandler: must supply either `stripe` or `stripeSecret`\",\n );\n }\n const stripe =\n stripeOverride ?? new Stripe(stripeSecret as string, { typescript: true });\n\n return async (req: NextRequest): Promise<Response> => {\n const body = await req.text();\n const sig = req.headers.get(\"stripe-signature\");\n\n if (!sig) {\n return Response.json({ error: \"Missing signature\" }, { status: 400 });\n }\n\n let event: Stripe.Event;\n try {\n event = stripe.webhooks.constructEvent(body, sig, webhookSecret);\n } catch {\n return Response.json({ error: \"Invalid signature\" }, { status: 400 });\n }\n\n if (event.type !== \"checkout.session.completed\") {\n return Response.json({ received: true });\n }\n\n const session = event.data.object as Stripe.Checkout.Session;\n\n const result = await fulfillCheckoutSession(session, { queries, site });\n if (result.status !== \"created\") {\n return Response.json({ received: true });\n }\n\n after(() =>\n sendPurchaseConfirmation(\n {\n orderId: result.order.id,\n customerEmail: result.customerEmail,\n itemNames: result.itemNames,\n totalCents: result.totalCents,\n },\n deps,\n ),\n );\n\n return Response.json({ received: true });\n };\n}\n","/**\n * Filename helpers shared by the single-file download route and the zip\n * routes. They live here rather than in either module because `download.ts`\n * now serves cover art (and so needs `coverArtFilename`) while `zip.ts` needs\n * `cleanDownloadFilename` — importing across the two directly would be a cycle.\n */\n\n/** Trim stray leading/trailing whitespace from a download filename while keeping its extension intact. */\nexport function cleanDownloadFilename(name: string): string {\n const dot = name.lastIndexOf(\".\");\n if (dot <= 0) return name.trim();\n return `${name.slice(0, dot).trim()}${name.slice(dot).trim()}`;\n}\n\n/** Strip path separators so a release/track name can't spawn unintended zip subfolders. */\nexport function sanitizeSegment(name: string): string {\n return name.replace(/[/\\\\]+/g, \"-\").trim();\n}\n\n/** Zip filename for a release's cover art. */\nexport function coverArtFilename(releaseName: string): string {\n return `${sanitizeSegment(releaseName)} - COVER ART.jpg`;\n}\n","import type { NextRequest } from \"next/server\";\nimport type { Queries } from \"@gigamusic/db\";\nimport type { DownloadDeps } from \"./types.js\";\nimport { cleanDownloadFilename, coverArtFilename } from \"./filenames.js\";\n\ninterface RouteContext {\n params: Promise<{ token: string }>;\n}\n\n/**\n * Build the GET handler for `/download/[token]?trackId=…&format=…`. The token\n * is looked up via `queries.getDownloadToken`; the track grant is checked via\n * `queries.tokenGrantsTrack`. On success the handler 302s to a presigned R2\n * URL — the storage provider has `Content-Disposition: attachment` baked in,\n * so the browser triggers a same-tab download.\n *\n * Order of checks: locate the file first (so a missing-file is 404), *then*\n * enforce the token-track grant (a wrong trackId for the given token is 403,\n * not 404).\n *\n * One extra job beyond serving the order page's per-track buttons, in service\n * of `createDownloadZipHandler`, whose manifest points every zip entry back\n * here so the R2 signature is minted when the service worker actually reaches\n * that file (see `zip.ts`): `?asset=cover&releaseId=…` serves a release's\n * cover art, which isn't track-keyed and so has no `trackId` to address it by.\n * That branch checks `queries.tokenGrantsRelease` instead.\n *\n * ### Lookups are keyed, not scanned\n *\n * Both branches resolve their target with a single keyed query\n * (`getTrackFile` / `getReleaseById`). They must not go back to\n * `listPublishedReleases()`: that pulls every published release with its\n * tracks and files, and because zip manifests fan every entry through this\n * route, one bulk download would issue that read once per file — dozens of\n * full-catalog scans to serve a single archive, against the most expensive\n * resource these sites have.\n *\n * Neither keyed query filters on `isPublished`, which is deliberate and is\n * also what makes them a complete replacement for the catalog walk rather\n * than a narrowing of it. Ownership is decided by the grant check below, not\n * by catalog membership, so a release unpublished after purchase and an\n * à-la-carte track belonging to no release both stay reachable to the customer\n * who bought them — exactly the cases the zip resolver lists, since it reads\n * the order rather than the catalog.\n */\nexport function createDownloadHandler(\n deps: DownloadDeps,\n): (req: NextRequest, ctx: RouteContext) => Promise<Response> {\n const { queries, storage } = deps;\n\n return async (req: NextRequest, ctx: RouteContext): Promise<Response> => {\n const { token } = await ctx.params;\n const requestUrl = new URL(req.url);\n const format = requestUrl.searchParams.get(\"format\") ?? \"mp3\";\n\n if (requestUrl.searchParams.get(\"asset\") === \"cover\") {\n return handleCoverArt(deps, token, requestUrl.searchParams.get(\"releaseId\"));\n }\n\n const trackIdRaw = requestUrl.searchParams.get(\"trackId\");\n if (!trackIdRaw) {\n return Response.json({ error: \"Missing trackId\" }, { status: 400 });\n }\n const trackId = Number(trackIdRaw);\n if (!Number.isFinite(trackId)) {\n return Response.json({ error: \"Invalid trackId\" }, { status: 400 });\n }\n\n const downloadToken = await queries.getDownloadToken(token);\n if (!downloadToken) {\n return Response.json({ error: \"Invalid download link\" }, { status: 404 });\n }\n\n const file = await queries.getTrackFile(trackId, format);\n if (!file) {\n return Response.json({ error: \"File not found\" }, { status: 404 });\n }\n\n const ok = await queries.tokenGrantsTrack(token, trackId);\n if (!ok) {\n return Response.json({ error: \"Track not in order\" }, { status: 403 });\n }\n\n const url = await storage.getPresignedDownloadUrl(file.storageKey, {\n filename: cleanDownloadFilename(file.fileName),\n contentType: format === \"wav\" ? \"audio/wav\" : \"audio/mpeg\",\n });\n return Response.redirect(url, 302);\n };\n}\n\n/** Serve `?asset=cover&releaseId=…` — the release-keyed twin of the track branch above. */\nasync function handleCoverArt(\n deps: DownloadDeps,\n token: string,\n releaseIdRaw: string | null,\n): Promise<Response> {\n const { queries, storage } = deps;\n\n if (!releaseIdRaw) {\n return Response.json({ error: \"Missing releaseId\" }, { status: 400 });\n }\n const releaseId = Number(releaseIdRaw);\n if (!Number.isFinite(releaseId)) {\n return Response.json({ error: \"Invalid releaseId\" }, { status: 400 });\n }\n\n const downloadToken = await queries.getDownloadToken(token);\n if (!downloadToken) {\n return Response.json({ error: \"Invalid download link\" }, { status: 404 });\n }\n\n const release = await queries.getReleaseById(releaseId);\n if (!release?.coverImageUrl) {\n return Response.json({ error: \"File not found\" }, { status: 404 });\n }\n\n const ok = await queries.tokenGrantsRelease(token, releaseId);\n if (!ok) {\n return Response.json({ error: \"Release not in order\" }, { status: 403 });\n }\n\n const url = await storage.getPresignedDownloadUrl(release.coverImageUrl, {\n filename: coverArtFilename(release.name),\n contentType: \"image/jpeg\",\n });\n return Response.redirect(url, 302);\n}\n\nexport { cleanDownloadFilename };\n\nexport type { Queries };\n","import type { NextRequest } from \"next/server\";\nimport type {\n OrderWithItems,\n Queries,\n ReleaseWithTracks,\n TrackWithFiles,\n} from \"@gigamusic/db\";\nimport type { StorageProvider } from \"@gigamusic/storage\";\nimport type { DownloadDeps } from \"./types.js\";\nimport { cleanDownloadFilename, coverArtFilename, sanitizeSegment } from \"./filenames.js\";\n\ninterface RouteContext {\n params: Promise<{ token: string }>;\n}\n\n/**\n * How a zip entry can be re-addressed through `createDownloadHandler`, so the\n * manifest can hand the service worker a same-origin URL that mints the R2\n * signature at fetch time instead of a signature minted up front. Entries\n * without a source fall back to a presigned URL (see `ZIP_MANIFEST_EXPIRES_IN_SECONDS`).\n */\nexport type ZipFileSource =\n | { kind: \"track\"; trackId: number; format: string }\n | { kind: \"cover\"; releaseId: number };\n\n/** A file destined for the zip, paired with the storage object it streams from. */\nexport interface ZipFile {\n fileName: string;\n storageKey: string;\n contentType: string;\n source?: ZipFileSource;\n}\n\ninterface ZipManifest {\n zipName: string;\n files: { fileName: string; url: string }[];\n}\n\n/**\n * Expiry for the presigned URLs the manifest falls back to when an entry has\n * no `source` to re-address it by.\n *\n * The storage default is 5 minutes, measured from when the manifest is built —\n * which is fatally short here. The service worker fetches manifest entries\n * strictly sequentially (a zip is a sequential format; `client-zip` drains one\n * body before opening the next), so a multi-gigabyte bundle is still working\n * through the list long after minute five. Worse, R2's signature-failure\n * responses carry no CORS headers, so the cross-origin fetch rejects with an\n * opaque `NetworkError` rather than a readable 403 — and the SW's per-file\n * catch turns each one into a `_FAILED_*.txt` placeholder, so the customer\n * gets a plausible-looking zip with most of their music missing.\n *\n * 12 hours comfortably outlasts any realistic download. These URLs are\n * shareable for that window, which is a small leak: the order link that\n * produced them grants the same files and doesn't expire at all.\n */\nexport const ZIP_MANIFEST_EXPIRES_IN_SECONDS = 12 * 60 * 60;\n\n/**\n * Discriminated result of `resolveZipBundle` — the SW-manifest handler and\n * the server-side streaming handler both call this so they produce\n * byte-identical archives. `ok: false` carries the HTTP status the caller\n * should return.\n */\nexport type ResolveZipResult =\n | { ok: true; zipName: string; files: ZipFile[] }\n | { ok: false; status: number; error: string };\n\nexport interface ResolveZipArgs {\n queries: Queries;\n token: string;\n /** Single-release download; falsy means \"whole order or selected tracks\". */\n releaseId?: number | string | null;\n /** Comma-separated track IDs for partial-order downloads. */\n trackIdsParam?: string | null;\n /** \"mp3\" or \"wav\". */\n format?: string;\n /** Optional artist-name prefix; see `DownloadDeps.zipNamePrefix`. */\n zipNamePrefix?: string;\n}\n\n/** Prepend the artist prefix when configured; otherwise return `base` untouched. */\nfunction applyZipNamePrefix(prefix: string | undefined, base: string): string {\n const trimmed = prefix?.trim();\n return trimmed ? `${trimmed} - ${base}` : base;\n}\n\n/**\n * Auth + file-list resolution for a customer's bulk download. The\n * `createDownloadZipHandler` (SW manifest path) and\n * `createDownloadZipStreamHandler` (server-side streaming path) both call\n * this so the two routes stay in lockstep on auth and on what ends up\n * in the archive.\n */\nexport async function resolveZipBundle(args: ResolveZipArgs): Promise<ResolveZipResult> {\n const { queries, token } = args;\n const format = args.format ?? \"mp3\";\n const audioContentType = format === \"wav\" ? \"audio/wav\" : \"audio/mpeg\";\n const releaseIdRaw = args.releaseId == null ? null : String(args.releaseId);\n const trackIdsParam = args.trackIdsParam ?? null;\n\n const downloadToken = await queries.getDownloadToken(token);\n if (!downloadToken) {\n return { ok: false, status: 404, error: \"Invalid download link\" };\n }\n\n const order = await queries.getOrderById(downloadToken.orderId);\n if (!order) {\n return { ok: false, status: 404, error: \"Invalid download link\" };\n }\n\n return resolveBundle({\n order,\n releaseId: releaseIdRaw,\n trackIdsParam,\n format,\n audioContentType,\n zipNamePrefix: args.zipNamePrefix,\n });\n}\n\n/**\n * Build the GET handler for the zip-manifest endpoint. Returns the JSON\n * manifest the consumer-shipped service worker (`public/sw-zip.js`)\n * consumes — the SW pipes each URL through `client-zip` and streams the\n * archive straight from storage to the browser, with the serverless function\n * never touching audio bytes.\n *\n * Manifest URLs point back at `createDownloadHandler` on this same origin\n * rather than directly at presigned R2 URLs. That route 302s to a *freshly*\n * signed URL, so the signature is minted at the moment the SW reaches that\n * entry — however many hours into the archive that is. Signing everything up\n * front meant every URL in a multi-gigabyte bundle died 5 minutes after the\n * click; see `ZIP_MANIFEST_EXPIRES_IN_SECONDS`. The redirect adds one cheap\n * function invocation per file and gives failures a server-side trace they\n * never had while the SW talked straight to R2.\n *\n * The manifest shape (`{ zipName, files: [{ fileName, url }] }`) is unchanged,\n * so an already-deployed service worker keeps working without modification.\n *\n * Query-param branches:\n * - `trackIds` set → curated track list, flat layout\n * - `releaseId` set → single-release bundle with cover art\n * - neither set → whole order (releases + à-la-carte tracks)\n */\nexport function createDownloadZipHandler(\n deps: DownloadDeps,\n): (req: NextRequest, ctx: RouteContext) => Promise<Response> {\n const { queries, storage } = deps;\n\n return async (req: NextRequest, ctx: RouteContext): Promise<Response> => {\n const { token } = await ctx.params;\n const url = new URL(req.url);\n const resolution = await resolveZipBundle({\n queries,\n token,\n releaseId: url.searchParams.get(\"releaseId\"),\n trackIdsParam: url.searchParams.get(\"trackIds\"),\n format: url.searchParams.get(\"format\") ?? \"mp3\",\n zipNamePrefix: deps.zipNamePrefix,\n });\n if (!resolution.ok) {\n return Response.json({ error: resolution.error }, { status: resolution.status });\n }\n\n const manifest: ZipManifest = {\n zipName: resolution.zipName,\n files: await buildManifestFiles(\n storage,\n resolution.files,\n downloadRouteUrl(url, token, deps.downloadPath),\n ),\n };\n return Response.json(manifest);\n };\n}\n\ntype Resolution =\n | { ok: true; zipName: string; files: ZipFile[] }\n | { ok: false; status: number; error: string };\n\ninterface ResolveArgs {\n order: OrderWithItems;\n releaseId: string | null;\n trackIdsParam: string | null;\n format: string;\n audioContentType: string;\n zipNamePrefix?: string;\n}\n\nfunction resolveBundle(args: ResolveArgs): Resolution {\n const { order, releaseId, trackIdsParam, format, audioContentType, zipNamePrefix } = args;\n const fmt = format.toUpperCase();\n\n if (trackIdsParam) {\n return resolveTrackList(order, trackIdsParam, format, audioContentType, fmt, zipNamePrefix);\n }\n if (releaseId) {\n const id = Number(releaseId);\n if (!Number.isFinite(id)) {\n return { ok: false, status: 404, error: \"Release not found\" };\n }\n return resolveSingleRelease(order, id, format, audioContentType, fmt, zipNamePrefix);\n }\n return resolveWholeOrder(order, format, audioContentType, fmt, zipNamePrefix);\n}\n\nfunction resolveTrackList(\n order: OrderWithItems,\n trackIdsParam: string,\n format: string,\n audioContentType: string,\n fmt: string,\n zipNamePrefix: string | undefined,\n): Resolution {\n const requestedTrackIds = trackIdsParam\n .split(\",\")\n .map((s) => Number(s.trim()))\n .filter((n) => Number.isFinite(n));\n const ownership = trackOwnership(order);\n const allOwned = requestedTrackIds.every((id) => ownership.owns(id));\n if (!allOwned) {\n return { ok: false, status: 403, error: \"Track not in order\" };\n }\n\n const files: ZipFile[] = [];\n for (const id of requestedTrackIds) {\n const ctx = ownership.locate(id);\n if (!ctx) continue;\n const file = ctx.track.files.find((f) => f.format === format);\n if (!file) continue;\n files.push({\n fileName: zipEntryPath(ctx.release?.name ?? null, file.fileName),\n storageKey: file.storageKey,\n contentType: audioContentType,\n source: { kind: \"track\", trackId: id, format },\n });\n }\n if (files.length === 0) {\n return {\n ok: false,\n status: 404,\n error: \"No audio files have been uploaded for these tracks yet.\",\n };\n }\n return {\n ok: true,\n zipName: applyZipNamePrefix(zipNamePrefix, `Tracks (${fmt}).zip`),\n files,\n };\n}\n\nfunction resolveSingleRelease(\n order: OrderWithItems,\n releaseId: number,\n format: string,\n audioContentType: string,\n fmt: string,\n zipNamePrefix: string | undefined,\n): Resolution {\n const orderHasRelease = order.items.some((item) => item.releaseId === releaseId);\n if (!orderHasRelease) {\n return { ok: false, status: 403, error: \"Release not in order\" };\n }\n\n const release = order.items.find((item) => item.release?.id === releaseId)?.release;\n if (!release) {\n return { ok: false, status: 404, error: \"Release not found\" };\n }\n\n const files: ZipFile[] = release.tracks\n .map((track): ZipFile | null => {\n const file = track.files.find((f) => f.format === format);\n if (!file) return null;\n return {\n fileName: zipEntryPath(null, file.fileName),\n storageKey: file.storageKey,\n contentType: audioContentType,\n source: { kind: \"track\", trackId: track.id, format },\n };\n })\n .filter((f): f is ZipFile => f !== null);\n\n if (files.length > 0 && release.coverImageUrl) {\n files.push(...coverArtEntries(release, release.coverImageUrl, \"\", files));\n }\n\n if (files.length === 0) {\n return {\n ok: false,\n status: 404,\n error: \"No audio files have been uploaded for these tracks yet.\",\n };\n }\n return {\n ok: true,\n zipName: applyZipNamePrefix(zipNamePrefix, `${release.name} (${fmt}).zip`),\n files,\n };\n}\n\nfunction resolveWholeOrder(\n order: OrderWithItems,\n format: string,\n audioContentType: string,\n fmt: string,\n zipNamePrefix: string | undefined,\n): Resolution {\n const releaseFiles: ZipFile[] = [];\n const aLaCarteFiles: ZipFile[] = [];\n\n for (const item of order.items) {\n if (item.release) {\n const release = item.release;\n const entries: ZipFile[] = [];\n for (const track of release.tracks) {\n const file = track.files.find((f) => f.format === format);\n if (!file) continue;\n entries.push({\n fileName: zipEntryPath(release.name, file.fileName),\n storageKey: file.storageKey,\n contentType: audioContentType,\n source: { kind: \"track\", trackId: track.id, format },\n });\n }\n if (entries.length > 0 && release.coverImageUrl) {\n entries.push(\n ...coverArtEntries(\n release,\n release.coverImageUrl,\n `${sanitizeSegment(release.name)}/`,\n entries,\n ),\n );\n }\n releaseFiles.push(...entries);\n } else if (item.track) {\n const track = item.track;\n const release = track.release ?? null;\n const file = track.files.find((f) => f.format === format);\n if (!file) continue;\n aLaCarteFiles.push({\n fileName: zipEntryPath(release?.name ?? null, file.fileName),\n storageKey: file.storageKey,\n contentType: audioContentType,\n source: { kind: \"track\", trackId: track.id, format },\n });\n }\n }\n\n const files = [...releaseFiles, ...aLaCarteFiles];\n if (files.length === 0) {\n return {\n ok: false,\n status: 404,\n error: \"No audio files have been uploaded for these tracks yet.\",\n };\n }\n return {\n ok: true,\n zipName: applyZipNamePrefix(zipNamePrefix, `Order ${order.id} (${fmt}).zip`),\n files,\n };\n}\n\ninterface TrackContext {\n track: TrackWithFiles;\n release?: ReleaseWithTracks | null;\n}\n\n/** Index every track an order grants access to so the manifest resolver can both check ownership and locate file rows. */\nfunction trackOwnership(order: OrderWithItems) {\n const byId = new Map<number, TrackContext>();\n for (const item of order.items) {\n if (item.release) {\n for (const t of item.release.tracks) {\n byId.set(t.id, { track: t, release: item.release });\n }\n }\n if (item.track) {\n byId.set(item.track.id, {\n track: item.track,\n release: (item.track.release as ReleaseWithTracks | null | undefined) ?? null,\n });\n }\n }\n return {\n owns: (id: number) => byId.has(id),\n locate: (id: number) => byId.get(id) ?? null,\n };\n}\n\n/**\n * Detects extended-mix tracks from their filename. The catalog marks them\n * inconsistently (\"Extended\", \"(Extended)\", \"[EXTENDED MIX]\"), so a loose\n * whole-word match on \"extended\" catches every variant.\n */\nexport function isExtendedMix(fileName: string): boolean {\n return /\\bextended\\b/i.test(fileName);\n}\n\n/**\n * Build a zip entry path: `[Release Name/][Extended/]filename`. Extended mixes\n * are nested in their own `Extended/` subfolder so they don't clutter the main\n * release listing. `releaseName` is null for single-release zips (which stay\n * flat apart from the `Extended/` split).\n */\nexport function zipEntryPath(releaseName: string | null, fileName: string): string {\n const clean = cleanDownloadFilename(fileName);\n const segments: string[] = [];\n if (releaseName) segments.push(sanitizeSegment(releaseName));\n if (isExtendedMix(clean)) segments.push(\"Extended\");\n segments.push(clean);\n return segments.join(\"/\");\n}\n\n/**\n * Cover-art zip entries for a release: the artwork alongside the tracks, plus\n * a duplicate inside the `Extended/` subfolder when any track entry uses one.\n * `folder` is the release's `Name/` prefix for multi-release zips, or \"\" for\n * flat single-release zips.\n *\n * Both entries carry the same `cover` source: cover art isn't track-keyed, so\n * `createDownloadHandler` addresses it by release id instead.\n */\nexport function coverArtEntries(\n release: Pick<ReleaseWithTracks, \"id\" | \"name\">,\n coverImageUrl: string,\n folder: string,\n trackEntries: ZipFile[],\n): ZipFile[] {\n const file = coverArtFilename(release.name);\n const source: ZipFileSource = { kind: \"cover\", releaseId: release.id };\n const entries: ZipFile[] = [\n {\n fileName: `${folder}${file}`,\n storageKey: coverImageUrl,\n contentType: \"image/jpeg\",\n source,\n },\n ];\n if (trackEntries.some((e) => e.fileName.split(\"/\").includes(\"Extended\"))) {\n entries.push({\n fileName: `${folder}Extended/${file}`,\n storageKey: coverImageUrl,\n contentType: \"image/jpeg\",\n source,\n });\n }\n return entries;\n}\n\nexport { coverArtFilename, sanitizeSegment };\n\n/**\n * Locate the single-file download route (`createDownloadHandler`) relative to\n * the manifest route that's currently serving this request.\n *\n * `docs/setup.md` mounts the manifest at `download/[token]/zip`, one segment\n * below `download/[token]`, so dropping the last path segment finds it. Doing\n * it this way rather than hardcoding `/download/{token}` keeps working under a\n * Next `basePath` or any other prefix; `downloadPath` is the escape hatch for\n * consumers who mount the two routes somewhere else entirely.\n */\nexport function downloadRouteUrl(\n manifestUrl: URL,\n token: string,\n downloadPath: string | undefined,\n): URL {\n if (downloadPath) {\n return new URL(downloadPath.replace(\"{token}\", encodeURIComponent(token)), manifestUrl);\n }\n const segments = manifestUrl.pathname.replace(/\\/+$/, \"\").split(\"/\");\n segments.pop();\n const url = new URL(manifestUrl.toString());\n url.search = \"\";\n url.hash = \"\";\n url.pathname = segments.join(\"/\") || \"/\";\n return url;\n}\n\n/**\n * Turn resolved zip entries into the `{ fileName, url }` manifest shape the\n * service worker consumes. Entries carrying a `source` become same-origin\n * `downloadUrl` links signed at fetch time; anything else falls back to a\n * long-lived presigned URL.\n */\nexport async function buildManifestFiles(\n storage: StorageProvider,\n files: ZipFile[],\n downloadUrl: URL,\n): Promise<ZipManifest[\"files\"]> {\n return Promise.all(\n files.map(async (file) => ({\n fileName: file.fileName,\n url: file.source\n ? sourceUrl(downloadUrl, file.source)\n : await storage.getPresignedDownloadUrl(file.storageKey, {\n filename: file.fileName.split(\"/\").pop() ?? file.fileName,\n contentType: file.contentType,\n expiresInSeconds: ZIP_MANIFEST_EXPIRES_IN_SECONDS,\n }),\n })),\n );\n}\n\n/** Address one zip entry through `createDownloadHandler`'s query-param contract. */\nfunction sourceUrl(downloadUrl: URL, source: ZipFileSource): string {\n const url = new URL(downloadUrl.toString());\n if (source.kind === \"track\") {\n url.searchParams.set(\"trackId\", String(source.trackId));\n url.searchParams.set(\"format\", source.format);\n } else {\n url.searchParams.set(\"asset\", \"cover\");\n url.searchParams.set(\"releaseId\", String(source.releaseId));\n }\n return url.toString();\n}\n\nexport type { Queries };\n","import type { NextRequest } from \"next/server\";\nimport { Readable, Transform } from \"node:stream\";\nimport { finished } from \"node:stream/promises\";\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\";\nimport archiver from \"archiver\";\nimport type { DownloadDeps, ZipEntryFailure } from \"./types.js\";\nimport { resolveZipBundle } from \"./zip.js\";\n\ninterface RouteContext {\n params: Promise<{ token: string }>;\n}\n\n/**\n * Server-side bulk-download fallback. Mirrors `createDownloadZipHandler`'s\n * auth + file-list logic via the shared `resolveZipBundle()`, but streams\n * a real zip back to the browser through `archiver` instead of returning\n * a manifest for a service worker to assemble.\n *\n * When to wire this up: in addition to `createDownloadZipHandler`. Browsers\n * that can't run the service worker reliably (WebKit — Safari desktop and\n * every iOS browser, plus private/incognito on most engines) get truncated\n * archives from the SW path. Detect those browsers at click time on the\n * client and hard-navigate here instead.\n *\n * Cost trade-off: audio bytes proxy through the serverless function and\n * cost egress, which is why the SW path stays the default for browsers\n * that handle it correctly.\n *\n * Per-file failure policy matches the SW: any failed storage fetch becomes\n * a `_FAILED_<name>.txt` placeholder so one bad object doesn't taint the\n * whole archive. Because that turns a broken purchase into an\n * apparently-successful download, every failure is reported through\n * `deps.onZipEntryFailure` and summarised in a single log line at the end of\n * the request.\n *\n * Unlike the manifest path, this handler presigns each file *inside* the loop,\n * immediately before fetching it, so signature expiry can't bite. Its own\n * ceiling is the platform's function timeout: audio bytes proxy through the\n * function, so the whole archive has to be produced within `maxDuration`\n * (Vercel: 300s by default, up to 800s on Fluid compute). Multi-gigabyte\n * bundles need that raised in the consuming route — `export const maxDuration`\n * — or they'll be cut off mid-stream.\n */\nexport function createDownloadZipStreamHandler(\n deps: DownloadDeps,\n): (req: NextRequest, ctx: RouteContext) => Promise<Response> {\n const { queries, storage } = deps;\n\n return async (req: NextRequest, ctx: RouteContext): Promise<Response> => {\n const { token } = await ctx.params;\n const url = new URL(req.url);\n\n const resolution = await resolveZipBundle({\n queries,\n token,\n releaseId: url.searchParams.get(\"releaseId\"),\n trackIdsParam: url.searchParams.get(\"trackIds\"),\n format: url.searchParams.get(\"format\") ?? \"mp3\",\n zipNamePrefix: deps.zipNamePrefix,\n });\n if (!resolution.ok) {\n return Response.json({ error: resolution.error }, { status: resolution.status });\n }\n\n // `store: true` = no DEFLATE compression. MP3/WAV barely compress, and\n // the SW path serves uncompressed entries — staying consistent means\n // identical byte counts across the two routes.\n const archive = archiver(\"zip\", { store: true });\n\n archive.on(\"error\", (err) => {\n console.error(\"[zip-stream] archiver error:\", err);\n });\n\n // Tear the pipeline down if the client disconnects mid-download.\n req.signal.addEventListener(\"abort\", () => archive.abort());\n\n const failures: ZipEntryFailure[] = [];\n /** Record one unusable entry and hand it to the consumer's reporter, never letting that reporter break the stream. */\n const reportFailure = (failure: ZipEntryFailure) => {\n failures.push(failure);\n try {\n deps.onZipEntryFailure?.(failure);\n } catch (err) {\n console.warn(\"[zip-stream] onZipEntryFailure threw:\", err);\n }\n };\n\n // Serialise storage fetches: only open file N's socket after archiver\n // has fully consumed file N-1. The naive parallel approach (open every\n // socket up front, let archiver drain them in order) leaves the later\n // sockets idle behind backpressure long enough for upstream R2/S3 to\n // time them out, surfacing as `TypeError: terminated` mid-stream. A\n // serial pipeline costs one TTFB per file transition — small compared\n // to the per-file streaming time — and archiver is the serial\n // bottleneck anyway, so end-to-end throughput is unchanged.\n (async () => {\n for (const file of resolution.files) {\n if (req.signal.aborted) return;\n const baseName = file.fileName.split(\"/\").pop() || file.fileName;\n try {\n const url = await storage.getPresignedDownloadUrl(file.storageKey, {\n filename: baseName,\n contentType: file.contentType,\n });\n const res = await fetch(url, { signal: req.signal });\n if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`);\n // DOM `ReadableStream` and Node's web `ReadableStream` are\n // runtime-compatible but typed separately — cast at the boundary.\n const body = Readable.fromWeb(\n res.body as unknown as NodeReadableStream<Uint8Array>,\n );\n // Pass-through counter for two reasons: we get a meaningful\n // \"bytesReceived\" in failure logs, and `archiver.append(counter)`\n // gives us a handle the `finished()` await below can resolve on\n // (archiver's internal entry isn't exposed).\n let bytesReceived = 0;\n const counter = new Transform({\n transform(chunk: Buffer, _enc, cb) {\n bytesReceived += chunk.length;\n cb(null, chunk);\n },\n });\n // Forward upstream errors onto the counter so the `finished`\n // await rejects and the loop can append a _FAILED_ placeholder.\n body.on(\"error\", (err) => counter.destroy(err));\n body.pipe(counter);\n archive.append(counter, { name: file.fileName });\n try {\n // Resolves once both sides of `counter` are done: body has\n // ended (upstream finished) AND archiver has drained the\n // readable side (entry fully written to the zip queue).\n await finished(counter);\n } catch (err) {\n if (req.signal.aborted) return;\n const detail = err instanceof Error ? err.message : String(err);\n console.error(\n `[zip-stream] body stream error for ${file.fileName} ` +\n `(storageKey=${file.storageKey}, bytesReceived=${bytesReceived}): ${detail}`,\n );\n reportFailure({\n token,\n fileName: file.fileName,\n storageKey: file.storageKey,\n reason: detail,\n bytesReceived,\n });\n try {\n archive.append(\n `Streaming \"${file.fileName}\" from storage failed after ${bytesReceived} bytes: ${detail}.\\n` +\n `Try downloading the track individually from your order page.\\n`,\n { name: `_FAILED_${baseName}.txt` },\n );\n } catch (appendErr) {\n console.warn(\n `[zip-stream] could not append failure placeholder for ${file.fileName}:`,\n appendErr,\n );\n }\n }\n } catch (err) {\n if (req.signal.aborted) return;\n const detail = err instanceof Error ? err.message : String(err);\n console.warn(`[zip-stream] fetch failed for ${file.fileName}:`, detail);\n reportFailure({\n token,\n fileName: file.fileName,\n storageKey: file.storageKey,\n reason: detail,\n bytesReceived: 0,\n });\n archive.append(\n `Failed to download \"${file.fileName}\" from storage: ${detail}.\\n` +\n `Try downloading the track individually from your order page.\\n`,\n { name: `_FAILED_${baseName}.txt` },\n );\n }\n }\n if (failures.length > 0) {\n console.error(\n `[zip-stream] ${failures.length}/${resolution.files.length} entries failed ` +\n `for token ${token}: ${failures.map((f) => f.fileName).join(\", \")}`,\n );\n }\n await archive.finalize();\n })().catch((err) => {\n console.error(\"[zip-stream] pipeline error:\", err);\n archive.abort();\n });\n\n return new Response(Readable.toWeb(archive) as ReadableStream<Uint8Array>, {\n headers: {\n \"Content-Type\": \"application/zip\",\n \"Content-Disposition\": `attachment; filename=\"${resolution.zipName.replace(/\"/g, \"\")}\"`,\n \"Cache-Control\": \"no-store\",\n },\n });\n };\n}\n","const ERROR_HTML = `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"color-scheme\" content=\"light dark\">\n <title>Bulk download couldn't start</title>\n <style>\n body { font-family: system-ui, -apple-system, sans-serif; max-width: 560px; margin: 3rem auto; padding: 0 1.25rem; line-height: 1.55; }\n h1 { font-size: 1.35rem; margin-bottom: 0.75rem; }\n ul { padding-left: 1.25rem; }\n li { margin: 0.35rem 0; }\n a { color: #6366f1; }\n </style>\n</head>\n<body>\n <h1>Bulk download couldn't start</h1>\n <p>Your browser couldn't generate the zip in the background. This usually happens in private/incognito mode, or after the order page has been open for a long time.</p>\n <p>To get your music:</p>\n <ul>\n <li>Go back to your order page and use the individual track download buttons.</li>\n <li>Or reopen the order link in a fresh tab and try the bulk download again.</li>\n </ul>\n <p><a href=\"javascript:history.back()\">&larr; Back to your order</a></p>\n</body>\n</html>`;\n\n/**\n * Fallback for `/sw-zip/[...path]` reached only when the consumer-shipped\n * service worker (`public/sw-zip.js`) fails to intercept the\n * navigation. Without this route the browser would save Next.js's 404\n * HTML under the URL's `.zip` filename — and macOS Archive Utility\n * surfaces that as the confusing \"Error 79 - Inappropriate file type or\n * format.\" Returning `text/html` (and no `Content-Disposition`) makes\n * the browser render the message instead of saving it.\n *\n * Wire up at `app/sw-zip/[...path]/route.ts`:\n *\n * ```ts\n * export const GET = createSwZipFallbackHandler();\n * ```\n */\nexport function createSwZipFallbackHandler(): () => Response {\n return () => {\n // Hitting this route is always a failure mode — the SW should have\n // intercepted. Log so the frequency shows up in serverless logs.\n console.warn(\"[sw-zip] fallback route hit — service worker did not intercept\");\n return new Response(ERROR_HTML, {\n status: 503,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"Cache-Control\": \"no-store\",\n },\n });\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/checkout.ts","../src/fulfill.ts","../src/webhook.ts","../src/filenames.ts","../src/download.ts","../src/zip.ts","../src/zip-length.ts","../src/zip-stream.ts","../src/sw-zip-fallback.ts"],"names":["session","Stripe","url"],"mappings":"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","file":"index.js","sourcesContent":["import type { NextRequest } from \"next/server\";\nimport Stripe from \"stripe\";\nimport type { ReleaseWithTracks, TrackWithFiles } from \"@gigamusic/db\";\nimport type { CheckoutCartItem, CheckoutDeps } from \"./types.js\";\n\n/**\n * Stripe rejects card charges below 50 cents (USD). A pay-what-you-want\n * override below this is clamped up rather than rejected, so a customer who\n * types a token amount still completes checkout instead of hitting an opaque\n * Stripe error. A `0` override never reaches here — the consumer routes free\n * downloads through its own gate, not the cart.\n */\nconst STRIPE_MIN_CHARGE_CENTS = 50;\n\n/**\n * Stripe turns Managed Payments — its merchant-of-record product — on by\n * default for newly created accounts. Left alone it rejects every line item\n * built here, because Managed Payments requires a `product_data.tax_code` we\n * don't set. Supplying one isn't the fix: the session then quietly comes back\n * with `managed_payments.enabled: true`, which sells through Link as merchant\n * of record, adds a per-transaction fee, replaces our purchase emails with\n * Link's, and lets Stripe refund customers on our behalf.\n *\n * Sites composing this package sell as their own merchant, so opt out per\n * session rather than depending on an account-level default that differs\n * between an artist's old and new Stripe accounts.\n */\nconst MANAGED_PAYMENTS_DISABLED = { enabled: false } as const;\n\n/**\n * Resolve the per-line `unit_amount`: a positive pay-what-you-want override\n * (clamped up to the Stripe minimum) when supplied, otherwise the catalog\n * price. Non-positive / non-finite overrides fall back to the catalog price.\n */\nfunction resolveUnitAmount(\n overrideCents: number | undefined,\n catalogCents: number,\n): number {\n if (\n typeof overrideCents === \"number\" &&\n Number.isFinite(overrideCents) &&\n overrideCents > 0\n ) {\n return Math.max(STRIPE_MIN_CHARGE_CENTS, Math.round(overrideCents));\n }\n return catalogCents;\n}\n\n/**\n * Split a bundle's charged total across its members, proportional to list\n * price, using largest-remainder allocation.\n *\n * Members are keyed by their `amounts`-metadata key (`r<id>` / `t<id>`) rather\n * than a bare numeric id, so a bundle holding tracks can't have a track id\n * collide with a release id.\n *\n * The returned values sum to `totalCents` exactly. That's load-bearing: they\n * become the `amounts` metadata `fulfillCheckoutSession` turns into\n * `order_items.price` rows, and `sum(order_items) === orders.amountTotal` is\n * what refunds and admin order views rely on. Ties break deterministically\n * (remainder desc, price desc, key asc) so a retry apportions identically.\n */\nfunction apportion(\n totalCents: number,\n members: { key: string; price: number }[],\n): Map<string, number> {\n const result = new Map<string, number>();\n if (members.length === 0) return result;\n\n const total = Math.max(0, Math.round(totalCents));\n const sum = members.reduce((s, m) => s + Math.max(0, m.price), 0);\n // All-free members carry no weight to divide by, so split evenly.\n const weights =\n sum > 0\n ? members.map((m) => Math.max(0, m.price) / sum)\n : members.map(() => 1 / members.length);\n\n const exact = weights.map((w) => total * w);\n const base = exact.map((v) => Math.floor(v));\n let remainder = total - base.reduce((s, v) => s + v, 0);\n\n const order = members\n .map((m, i) => ({ i, frac: exact[i]! - base[i]!, price: m.price, key: m.key }))\n .sort((a, b) => b.frac - a.frac || b.price - a.price || a.key.localeCompare(b.key));\n\n for (const { i } of order) {\n if (remainder <= 0) break;\n base[i]! += 1;\n remainder -= 1;\n }\n\n members.forEach((m, i) => result.set(m.key, base[i]!));\n return result;\n}\n\n/**\n * Build a POST handler that translates `{ items }` from the cart into a Stripe\n * Checkout Session and replies with `{ url }`. Release/track metadata is\n * stashed on the session so the webhook handler can later record the order\n * without re-deriving cart contents.\n *\n * Stripe is hard-wired (per project policy). The Stripe SDK instance is\n * created once per factory call and captured in the closure — no module\n * globals, no `process.env`.\n */\nexport function createCheckoutHandler(\n deps: CheckoutDeps,\n): (req: NextRequest) => Promise<Response> {\n const {\n stripeSecret,\n queries,\n site,\n baseUrl,\n successPath = \"/checkout/success\",\n cancelPath = \"/cart\",\n currency = \"usd\",\n catalogPurchase,\n bundlePurchase,\n } = deps;\n\n const stripe = new Stripe(stripeSecret, { typescript: true });\n\n return async (req: NextRequest): Promise<Response> => {\n let payload: { items?: CheckoutCartItem[] };\n try {\n payload = (await req.json()) as { items?: CheckoutCartItem[] };\n } catch {\n return jsonError(\"Invalid JSON body\", 400);\n }\n\n const items = payload.items;\n if (!items || items.length === 0) {\n return jsonError(\"No items provided\", 400);\n }\n\n const successUrl = `${baseUrl}${successPath}?session_id={CHECKOUT_SESSION_ID}`;\n const cancelUrl = `${baseUrl}${cancelPath}`;\n\n const isCatalogPurchase = items.some((i) => i.kind === \"catalog\");\n const bundleIds = [\n ...new Set(\n items\n .filter((i) => i.kind === \"bundle\" && typeof i.bundleId === \"string\")\n .map((i) => i.bundleId as string),\n ),\n ];\n\n // The catalog already contains every bundle, so charging for both would\n // bill the same releases twice.\n if (isCatalogPurchase && bundleIds.length > 0) {\n return jsonError(\"Cannot mix a catalog purchase with a bundle\", 400);\n }\n\n try {\n if (isCatalogPurchase) {\n const resolved =\n typeof catalogPurchase === \"function\"\n ? await catalogPurchase()\n : catalogPurchase;\n if (!resolved) {\n return jsonError(\"Catalog purchase not configured\", 400);\n }\n // Snapshot the current published-release ids so the webhook records\n // exactly what the customer bought, even if a release is unpublished\n // between session-create and webhook-delivery. The consumer-supplied\n // `totalCents` is what Stripe charges — pricing math lives in the\n // consumer (it owns whatever discount/rounding/tax rules apply).\n const releases = await queries.listPublishedReleases();\n\n const session = await stripe.checkout.sessions.create({\n mode: \"payment\",\n line_items: [\n {\n price_data: {\n currency,\n product_data: { name: resolved.productName },\n unit_amount: resolved.totalCents,\n },\n quantity: 1,\n },\n ],\n metadata: {\n site,\n catalog_purchase: \"true\",\n release_ids: JSON.stringify(releases.map((r) => r.id)),\n track_ids: \"[]\",\n },\n managed_payments: MANAGED_PAYMENTS_DISABLED,\n success_url: successUrl,\n cancel_url: cancelUrl,\n });\n\n return Response.json({ url: session.url });\n }\n\n const releaseIds = items\n .filter((i) => i.kind === \"release\" && i.id != null)\n .map((i) => i.id as number);\n const trackIds = items\n .filter((i) => i.kind === \"track\" && i.id != null)\n .map((i) => i.id as number);\n\n if (releaseIds.length === 0 && trackIds.length === 0 && bundleIds.length === 0) {\n return jsonError(\"No valid items found\", 400);\n }\n\n if (bundleIds.length > 0 && typeof bundlePurchase !== \"function\") {\n return jsonError(\"Bundle purchases are not configured\", 400);\n }\n\n // Pay-what-you-want overrides, keyed by id. Items without `priceCents`\n // (the fixed-price default — every thomas-adagio cart) leave the map\n // empty and fall back to the catalog price below.\n const releaseAmountOverride = new Map<number, number>(\n items\n .filter(\n (i) =>\n i.kind === \"release\" &&\n i.id != null &&\n typeof i.priceCents === \"number\",\n )\n .map((i) => [i.id as number, i.priceCents as number]),\n );\n const trackAmountOverride = new Map<number, number>(\n items\n .filter(\n (i) =>\n i.kind === \"track\" &&\n i.id != null &&\n typeof i.priceCents === \"number\",\n )\n .map((i) => [i.id as number, i.priceCents as number]),\n );\n\n // Charged `unit_amount` per line, keyed `r<id>` / `t<id>`. Stamped onto\n // the session so the webhook records the true price paid rather than\n // re-deriving the (suggested) catalog price.\n const chargedAmounts: Record<string, number> = {};\n\n // Single `listPublishedReleases` call sources both release and track\n // line items. Tracks live inside their release, so one fetch keeps the\n // ID/price/cover lookups consistent without a second round-trip.\n const allReleases = await queries.listPublishedReleases();\n const releaseById = new Map(allReleases.map((r) => [r.id, r]));\n const trackContext = new Map<\n number,\n { track: TrackWithFiles; release: ReleaseWithTracks }\n >();\n for (const release of allReleases) {\n for (const track of release.tracks) {\n trackContext.set(track.id, { track, release });\n }\n }\n\n const releaseLineItems = releaseIds\n .map((id) => releaseById.get(id))\n .filter((r): r is ReleaseWithTracks => r !== undefined)\n .map((r) => {\n const amount = resolveUnitAmount(releaseAmountOverride.get(r.id), r.price);\n // Accumulate: the same release can be charged by more than one line\n // (a bundle plus a loose copy in a stale cart), and the recorded\n // order item must carry the total actually paid for it.\n chargedAmounts[`r${r.id}`] = (chargedAmounts[`r${r.id}`] ?? 0) + amount;\n return {\n price_data: {\n currency,\n product_data: {\n name: r.name,\n ...(r.coverImageUrl\n ? { images: [toAbsoluteUrl(r.coverImageUrl, baseUrl)] }\n : {}),\n },\n unit_amount: amount,\n },\n quantity: 1 as const,\n };\n });\n\n const trackLineItems = trackIds\n .map((id) => trackContext.get(id))\n .filter(\n (c): c is { track: TrackWithFiles; release: ReleaseWithTracks } =>\n c !== undefined,\n )\n .map(({ track, release }) => {\n const amount = resolveUnitAmount(\n trackAmountOverride.get(track.id),\n track.price,\n );\n chargedAmounts[`t${track.id}`] = (chargedAmounts[`t${track.id}`] ?? 0) + amount;\n return {\n price_data: {\n currency,\n product_data: {\n name: `${release.name} — ${track.name}`,\n ...(release.coverImageUrl\n ? { images: [toAbsoluteUrl(release.coverImageUrl, baseUrl)] }\n : {}),\n },\n unit_amount: amount,\n },\n quantity: 1 as const,\n };\n });\n\n // Bundles resolve against the same published-release snapshot as the\n // loose lines, so member prices, names, and covers stay consistent. A\n // bundle may hold releases, tracks, or both — the package doesn't care\n // which; the consumer decides what a bundle is allowed to contain.\n const bundleLineItems: typeof releaseLineItems = [];\n const bundleReleaseMemberIds: number[] = [];\n const bundleTrackMemberIds: number[] = [];\n const unavailableBundleIds: string[] = [];\n\n for (const bundleId of bundleIds) {\n const resolved = await bundlePurchase!(bundleId);\n const releaseMembers = (resolved?.releaseIds ?? [])\n .map((id) => releaseById.get(id))\n .filter((r): r is ReleaseWithTracks => r !== undefined);\n const trackMembers = (resolved?.trackIds ?? [])\n .map((id) => trackContext.get(id))\n .filter(\n (c): c is { track: TrackWithFiles; release: ReleaseWithTracks } =>\n c !== undefined,\n );\n\n // No such bundle, or nothing in it is published any more. Charging for\n // the latter would produce a paid order with zero items.\n if (!resolved || releaseMembers.length + trackMembers.length === 0) {\n unavailableBundleIds.push(bundleId);\n continue;\n }\n\n const total = Math.max(\n STRIPE_MIN_CHARGE_CENTS,\n Math.round(resolved.totalCents),\n );\n // A track carries no cover of its own, so a track-only bundle falls\n // back to the cover of the release the first member belongs to.\n const cover =\n releaseMembers.find((m) => m.coverImageUrl)?.coverImageUrl ??\n trackMembers.find((c) => c.release.coverImageUrl)?.release.coverImageUrl;\n bundleLineItems.push({\n price_data: {\n currency,\n product_data: {\n name: resolved.productName,\n ...(cover ? { images: [toAbsoluteUrl(cover, baseUrl)] } : {}),\n },\n unit_amount: total,\n },\n quantity: 1 as const,\n });\n\n // Apportioned jointly across both member kinds — the total has to land\n // on the members as one set, not split per kind and rounded twice.\n for (const [key, cents] of apportion(total, [\n ...releaseMembers.map((m) => ({ key: `r${m.id}`, price: m.price })),\n ...trackMembers.map(({ track }) => ({ key: `t${track.id}`, price: track.price })),\n ])) {\n chargedAmounts[key] = (chargedAmounts[key] ?? 0) + cents;\n }\n bundleReleaseMemberIds.push(...releaseMembers.map((m) => m.id));\n bundleTrackMemberIds.push(...trackMembers.map(({ track }) => track.id));\n }\n\n if (unavailableBundleIds.length > 0) {\n return Response.json(\n { error: \"bundle-unavailable\", bundleIds: unavailableBundleIds },\n { status: 409 },\n );\n }\n\n const lineItems = [...bundleLineItems, ...releaseLineItems, ...trackLineItems];\n if (lineItems.length === 0) {\n return jsonError(\"No valid items found\", 400);\n }\n\n // Deduped: a release or track reachable from both a bundle and a loose\n // line still yields one order item, whose price is the summed `amounts`\n // entry.\n const resolvedReleaseIds = [\n ...new Set([\n ...(releaseLineItems.length ? releaseIds.filter((id) => releaseById.has(id)) : []),\n ...bundleReleaseMemberIds,\n ]),\n ];\n const resolvedTrackIds = [\n ...new Set([\n ...(trackLineItems.length ? trackIds.filter((id) => trackContext.has(id)) : []),\n ...bundleTrackMemberIds,\n ]),\n ];\n\n const session = await stripe.checkout.sessions.create({\n mode: \"payment\",\n line_items: lineItems,\n metadata: {\n site,\n release_ids: JSON.stringify(resolvedReleaseIds),\n track_ids: JSON.stringify(resolvedTrackIds),\n amounts: JSON.stringify(chargedAmounts),\n // Ignored by fulfillment — bundles decompose into per-release order\n // items — but invaluable when supporting \"what did I actually buy?\".\n ...(bundleIds.length > 0 ? { bundle_ids: JSON.stringify(bundleIds) } : {}),\n },\n managed_payments: MANAGED_PAYMENTS_DISABLED,\n success_url: successUrl,\n cancel_url: cancelUrl,\n });\n\n return Response.json({ url: session.url });\n } catch (err) {\n return jsonError(\"Failed to create checkout session\", 500, err);\n }\n };\n}\n\n/** Resolve a stored relative URL against `baseUrl` and percent-encode the path. Bare http(s) URLs pass through. */\nfunction toAbsoluteUrl(url: string, baseUrl: string): string {\n const raw = url.startsWith(\"http\")\n ? url\n : `${baseUrl}${url.startsWith(\"/\") ? \"\" : \"/\"}${url}`;\n try {\n const parsed = new URL(raw);\n parsed.pathname = encodeURI(decodeURI(parsed.pathname));\n return parsed.toString();\n } catch {\n return raw;\n }\n}\n\nfunction jsonError(message: string, status: number, cause?: unknown): Response {\n if (cause) {\n // Logged via console.error. Consumers can swap this for a structured\n // logger in a future iteration; the package contract doesn't expose a\n // logger slot yet.\n console.error(`[checkout] ${message}:`, cause);\n }\n return Response.json({ error: message }, { status });\n}\n","import type Stripe from \"stripe\";\nimport { signOrderToken } from \"@gigamusic/core\";\nimport {\n isUniqueConstraintError,\n type OrderWithItems,\n type ReleaseWithTracks,\n type TrackWithFiles,\n} from \"@gigamusic/db\";\nimport { renderPurchaseConfirmation } from \"@gigamusic/email\";\nimport type { FulfillSessionDeps, PurchaseConfirmationDeps } from \"./types.js\";\n\nexport type FulfillSessionResult =\n /** This call wrote the order row. The caller owns sending the confirmation email. */\n | {\n status: \"created\";\n order: OrderWithItems;\n itemNames: string[];\n customerEmail: string;\n totalCents: number;\n }\n /**\n * The order was already on disk — either the pre-flight lookup found it or a\n * concurrent writer won the unique-constraint race. `order` is null only if\n * the re-read after a lost race somehow came back empty.\n */\n | { status: \"already-recorded\"; order: OrderWithItems | null }\n /** Nothing was written and nothing is owed. */\n | { status: \"skipped\"; reason: \"site-mismatch\" | \"no-items\" | \"unpaid\" };\n\n/**\n * Turn a completed Stripe Checkout Session into a persisted order, idempotently.\n *\n * This is the single fulfillment path shared by the Stripe webhook and any\n * consumer that wants to fulfill synchronously — e.g. a post-checkout success\n * page that would otherwise have to wait on webhook delivery to show download\n * links. Whoever calls first writes the row; everyone after gets\n * `already-recorded`. That guarantee rests on the unique constraint over\n * `stripeSessionId` inside `recordCompletedOrder`, not on call ordering, so\n * concurrent callers are safe.\n *\n * Only `status: \"created\"` should trigger side effects (the confirmation\n * email) — that status is returned to exactly one caller per session.\n *\n * ### Published-only resolution (gotcha)\n *\n * Release/track IDs from `session.metadata` are resolved against\n * `queries.listPublishedReleases()`. Anything not in the published catalog at\n * fulfillment time is silently dropped from the persisted order's line items —\n * the customer is still charged, but the order row doesn't include the\n * unpublished item.\n *\n * In the canonical flow this can't happen: `createCheckoutHandler` filters on\n * the same `listPublishedReleases()` before it ever creates the Stripe\n * Session. The only ways an unpublished ID can reach here are:\n * - an admin unpublishes between session-create and fulfillment\n * (a several-second race window),\n * - a consumer bypasses `createCheckoutHandler` and creates Stripe sessions\n * directly with their own metadata.\n *\n * If either is a realistic concern, swap the resolution call here to\n * `listAllReleases()` (or wrap with a \"warn + fallback\" branch) — but be aware\n * that doing so will surface unpublished items in admin order views.\n */\nexport async function fulfillCheckoutSession(\n session: Stripe.Checkout.Session,\n deps: FulfillSessionDeps,\n): Promise<FulfillSessionResult> {\n const { queries, site } = deps;\n\n // One Stripe account fans every event out to every registered endpoint, and\n // a success URL can be hand-forged. Ignore sessions belonging to a different\n // site so we never record a foreign purchase (with zero matched line items)\n // under this one.\n if (session.metadata?.site !== site) {\n return { status: \"skipped\", reason: \"site-mismatch\" };\n }\n\n // Delayed-notification methods (bank debits, vouchers) complete checkout with\n // the money still in flight; the session only flips to `paid` later, via\n // `checkout.session.async_payment_succeeded`. Handing over downloads at that\n // point would be giving away unpaid goods. `no_payment_required` (fully\n // discounted) and legacy sessions with no `payment_status` are fulfillable.\n if (session.payment_status === \"unpaid\") {\n return { status: \"skipped\", reason: \"unpaid\" };\n }\n\n const existing = await queries.getOrderByStripeSessionId(session.id);\n if (existing) {\n return { status: \"already-recorded\", order: existing };\n }\n\n const releaseIds = safeJsonIdList(session.metadata?.release_ids);\n const trackIds = safeJsonIdList(session.metadata?.track_ids);\n // Per-line amounts the customer actually paid, keyed `r<id>` / `t<id>`,\n // stamped by `createCheckoutHandler`. Absent on pre-upgrade sessions and on\n // catalog purchases — those fall back to the catalog price below.\n const chargedAmounts = safeAmountMap(session.metadata?.amounts);\n\n if (releaseIds.length === 0 && trackIds.length === 0) {\n return { status: \"skipped\", reason: \"no-items\" };\n }\n\n // Snapshot the published catalog once so we can resolve both release and\n // track prices + names off a single in-memory lookup.\n const allReleases = await queries.listPublishedReleases();\n const releaseById = new Map(allReleases.map((r) => [r.id, r]));\n const trackContext = new Map<\n number,\n { track: TrackWithFiles; release: ReleaseWithTracks }\n >();\n for (const release of allReleases) {\n for (const track of release.tracks) {\n trackContext.set(track.id, { track, release });\n }\n }\n\n const matchedReleases = releaseIds\n .map((id) => releaseById.get(id))\n .filter((r): r is ReleaseWithTracks => r !== undefined);\n const matchedTracks = trackIds\n .map((id) => trackContext.get(id))\n .filter(\n (c): c is { track: TrackWithFiles; release: ReleaseWithTracks } => c !== undefined,\n );\n\n const orderItems = [\n ...matchedReleases.map((r) => ({\n releaseId: r.id,\n price: chargedAmounts[`r${r.id}`] ?? r.price,\n })),\n ...matchedTracks.map(({ track }) => ({\n trackId: track.id,\n price: chargedAmounts[`t${track.id}`] ?? track.price,\n })),\n ];\n\n const customerEmail = session.customer_details?.email ?? \"\";\n const totalCents = session.amount_total ?? 0;\n\n let order: OrderWithItems;\n try {\n order = await queries.recordCompletedOrder({\n stripeSessionId: session.id,\n stripePaymentId:\n typeof session.payment_intent === \"string\" ? session.payment_intent : null,\n email: customerEmail,\n amountTotal: totalCents,\n items: orderItems,\n });\n } catch (err) {\n // Two callers can both pass the pre-flight lookup and race here — a webhook\n // retry, or the success page rendering while the webhook is mid-flight. The\n // unique constraint on `stripeSessionId` guarantees only one row lands; the\n // loser surfaces as a 23505 unique-violation. Re-read so the loser still\n // gets the order to render, but without claiming the \"created\" side effects.\n if (isUniqueConstraintError(err)) {\n return {\n status: \"already-recorded\",\n order: (await queries.getOrderByStripeSessionId(session.id)) ?? null,\n };\n }\n throw err;\n }\n\n const itemNames = [\n ...matchedReleases.map((r) => r.name),\n ...matchedTracks.map(({ track, release }) => `${release.name} — ${track.name}`),\n ];\n\n return { status: \"created\", order, itemNames, customerEmail, totalCents };\n}\n\n/**\n * Render and send the post-purchase confirmation email carrying the durable\n * magic link back to the customer's downloads.\n *\n * Errors are logged, never thrown: the order is already recorded and paid for,\n * so a mail-provider hiccup must not fail the caller (a webhook returning 500\n * would make Stripe retry a fulfillment that already succeeded). Callers\n * typically wrap this in `after()` so the response isn't blocked on it.\n */\nexport async function sendPurchaseConfirmation(\n args: {\n orderId: number;\n customerEmail: string;\n itemNames: string[];\n totalCents: number;\n },\n deps: PurchaseConfirmationDeps,\n): Promise<void> {\n const {\n email,\n branding,\n emailFrom,\n baseUrl,\n orderTokenSecret,\n orderTokenExpiry = null,\n verifyPath = \"/orders/verify\",\n renderConfirmation = renderPurchaseConfirmation,\n } = deps;\n\n if (!args.customerEmail) {\n // Stripe almost always supplies an email but doesn't guarantee it. The\n // order is safely recorded; manual intervention required to deliver the\n // magic link.\n console.warn(\n `[gigamusic/checkout] order ${args.orderId} has no customer email — recorded, but the customer cannot retrieve downloads.`,\n );\n return;\n }\n\n try {\n const verifyToken = await signOrderToken({\n orderId: String(args.orderId),\n email: args.customerEmail,\n secret: orderTokenSecret,\n expiresIn: orderTokenExpiry,\n });\n const verifyUrl = `${baseUrl}${verifyPath}?token=${verifyToken}`;\n const { subject, html } = await renderConfirmation({\n branding,\n verifyUrl,\n itemNames: args.itemNames,\n totalCents: args.totalCents,\n });\n await email.send({ from: emailFrom, to: args.customerEmail, subject, html });\n } catch (err) {\n console.error(\n `[gigamusic/checkout] failed to send purchase confirmation to ${args.customerEmail} for order ${args.orderId}:`,\n err,\n );\n }\n}\n\n/**\n * Parse the JSON-stringified `{ \"r<id>\" | \"t<id>\": cents }` amount map from\n * Stripe metadata, returning `{}` on any failure. Non-finite values are\n * dropped so a malformed entry falls back to the catalog price at the call site.\n */\nfunction safeAmountMap(value: string | undefined | null): Record<string, number> {\n if (!value) return {};\n try {\n const parsed: unknown = JSON.parse(value);\n if (typeof parsed !== \"object\" || parsed === null) return {};\n const out: Record<string, number> = {};\n for (const [key, raw] of Object.entries(parsed as Record<string, unknown>)) {\n const n = Number(raw);\n if (Number.isFinite(n)) out[key] = n;\n }\n return out;\n } catch {\n return {};\n }\n}\n\n/** Parse a JSON-stringified id array from Stripe metadata, returning `[]` on any failure. NaN entries are dropped. */\nfunction safeJsonIdList(value: string | undefined | null): number[] {\n if (!value) return [];\n try {\n const parsed: unknown = JSON.parse(value);\n if (!Array.isArray(parsed)) return [];\n return parsed.map((v) => Number(v)).filter((n) => Number.isFinite(n));\n } catch {\n return [];\n }\n}\n","import { after, type NextRequest } from \"next/server\";\nimport Stripe from \"stripe\";\nimport { fulfillCheckoutSession, sendPurchaseConfirmation } from \"./fulfill.js\";\nimport type { WebhookDeps } from \"./types.js\";\n\n/**\n * Build the POST handler for Stripe's webhook endpoint. Verifies the\n * signature, records the order on `checkout.session.completed` via the shared\n * `fulfillCheckoutSession`, then defers the magic-link email via `after()` so\n * Stripe sees a fast 200.\n *\n * The handler is not the only fulfillment path — a consumer's success page can\n * call `fulfillCheckoutSession` directly rather than waiting on webhook\n * delivery. Whichever runs first writes the row; the other gets\n * `already-recorded` and skips the email, so the customer is mailed exactly\n * once no matter who wins. Every non-`created` outcome answers Stripe with\n * `{ received: true }` so it doesn't retry.\n */\nexport function createStripeWebhookHandler(\n deps: WebhookDeps,\n): (req: NextRequest) => Promise<Response> {\n const { stripe: stripeOverride, stripeSecret, webhookSecret, queries, site } = deps;\n\n if (!stripeOverride && !stripeSecret) {\n throw new Error(\n \"createStripeWebhookHandler: must supply either `stripe` or `stripeSecret`\",\n );\n }\n const stripe =\n stripeOverride ?? new Stripe(stripeSecret as string, { typescript: true });\n\n return async (req: NextRequest): Promise<Response> => {\n const body = await req.text();\n const sig = req.headers.get(\"stripe-signature\");\n\n if (!sig) {\n return Response.json({ error: \"Missing signature\" }, { status: 400 });\n }\n\n let event: Stripe.Event;\n try {\n event = stripe.webhooks.constructEvent(body, sig, webhookSecret);\n } catch {\n return Response.json({ error: \"Invalid signature\" }, { status: 400 });\n }\n\n if (event.type !== \"checkout.session.completed\") {\n return Response.json({ received: true });\n }\n\n const session = event.data.object as Stripe.Checkout.Session;\n\n const result = await fulfillCheckoutSession(session, { queries, site });\n if (result.status !== \"created\") {\n return Response.json({ received: true });\n }\n\n after(() =>\n sendPurchaseConfirmation(\n {\n orderId: result.order.id,\n customerEmail: result.customerEmail,\n itemNames: result.itemNames,\n totalCents: result.totalCents,\n },\n deps,\n ),\n );\n\n return Response.json({ received: true });\n };\n}\n","/**\n * Filename helpers shared by the single-file download route and the zip\n * routes. They live here rather than in either module because `download.ts`\n * now serves cover art (and so needs `coverArtFilename`) while `zip.ts` needs\n * `cleanDownloadFilename` — importing across the two directly would be a cycle.\n */\n\n/** Trim stray leading/trailing whitespace from a download filename while keeping its extension intact. */\nexport function cleanDownloadFilename(name: string): string {\n const dot = name.lastIndexOf(\".\");\n if (dot <= 0) return name.trim();\n return `${name.slice(0, dot).trim()}${name.slice(dot).trim()}`;\n}\n\n/** Strip path separators so a release/track name can't spawn unintended zip subfolders. */\nexport function sanitizeSegment(name: string): string {\n return name.replace(/[/\\\\]+/g, \"-\").trim();\n}\n\n/** Zip filename for a release's cover art. */\nexport function coverArtFilename(releaseName: string): string {\n return `${sanitizeSegment(releaseName)} - COVER ART.jpg`;\n}\n","import type { NextRequest } from \"next/server\";\nimport type { Queries } from \"@gigamusic/db\";\nimport type { DownloadDeps } from \"./types.js\";\nimport { cleanDownloadFilename, coverArtFilename } from \"./filenames.js\";\n\ninterface RouteContext {\n params: Promise<{ token: string }>;\n}\n\n/**\n * Build the GET handler for `/download/[token]?trackId=…&format=…`. The token\n * is looked up via `queries.getDownloadToken`; the track grant is checked via\n * `queries.tokenGrantsTrack`. On success the handler 302s to a presigned R2\n * URL — the storage provider has `Content-Disposition: attachment` baked in,\n * so the browser triggers a same-tab download.\n *\n * Order of checks: locate the file first (so a missing-file is 404), *then*\n * enforce the token-track grant (a wrong trackId for the given token is 403,\n * not 404).\n *\n * One extra job beyond serving the order page's per-track buttons, in service\n * of `createDownloadZipHandler`, whose manifest points every zip entry back\n * here so the R2 signature is minted when the service worker actually reaches\n * that file (see `zip.ts`): `?asset=cover&releaseId=…` serves a release's\n * cover art, which isn't track-keyed and so has no `trackId` to address it by.\n * That branch checks `queries.tokenGrantsRelease` instead.\n *\n * ### Lookups are keyed, not scanned\n *\n * Both branches resolve their target with a single keyed query\n * (`getTrackFile` / `getReleaseById`). They must not go back to\n * `listPublishedReleases()`: that pulls every published release with its\n * tracks and files, and because zip manifests fan every entry through this\n * route, one bulk download would issue that read once per file — dozens of\n * full-catalog scans to serve a single archive, against the most expensive\n * resource these sites have.\n *\n * Neither keyed query filters on `isPublished`, which is deliberate and is\n * also what makes them a complete replacement for the catalog walk rather\n * than a narrowing of it. Ownership is decided by the grant check below, not\n * by catalog membership, so a release unpublished after purchase and an\n * à-la-carte track belonging to no release both stay reachable to the customer\n * who bought them — exactly the cases the zip resolver lists, since it reads\n * the order rather than the catalog.\n */\nexport function createDownloadHandler(\n deps: DownloadDeps,\n): (req: NextRequest, ctx: RouteContext) => Promise<Response> {\n const { queries, storage } = deps;\n\n return async (req: NextRequest, ctx: RouteContext): Promise<Response> => {\n const { token } = await ctx.params;\n const requestUrl = new URL(req.url);\n const format = requestUrl.searchParams.get(\"format\") ?? \"mp3\";\n\n if (requestUrl.searchParams.get(\"asset\") === \"cover\") {\n return handleCoverArt(deps, token, requestUrl.searchParams.get(\"releaseId\"));\n }\n\n const trackIdRaw = requestUrl.searchParams.get(\"trackId\");\n if (!trackIdRaw) {\n return Response.json({ error: \"Missing trackId\" }, { status: 400 });\n }\n const trackId = Number(trackIdRaw);\n if (!Number.isFinite(trackId)) {\n return Response.json({ error: \"Invalid trackId\" }, { status: 400 });\n }\n\n const downloadToken = await queries.getDownloadToken(token);\n if (!downloadToken) {\n return Response.json({ error: \"Invalid download link\" }, { status: 404 });\n }\n\n const file = await queries.getTrackFile(trackId, format);\n if (!file) {\n return Response.json({ error: \"File not found\" }, { status: 404 });\n }\n\n const ok = await queries.tokenGrantsTrack(token, trackId);\n if (!ok) {\n return Response.json({ error: \"Track not in order\" }, { status: 403 });\n }\n\n const url = await storage.getPresignedDownloadUrl(file.storageKey, {\n filename: cleanDownloadFilename(file.fileName),\n contentType: format === \"wav\" ? \"audio/wav\" : \"audio/mpeg\",\n });\n return Response.redirect(url, 302);\n };\n}\n\n/** Serve `?asset=cover&releaseId=…` — the release-keyed twin of the track branch above. */\nasync function handleCoverArt(\n deps: DownloadDeps,\n token: string,\n releaseIdRaw: string | null,\n): Promise<Response> {\n const { queries, storage } = deps;\n\n if (!releaseIdRaw) {\n return Response.json({ error: \"Missing releaseId\" }, { status: 400 });\n }\n const releaseId = Number(releaseIdRaw);\n if (!Number.isFinite(releaseId)) {\n return Response.json({ error: \"Invalid releaseId\" }, { status: 400 });\n }\n\n const downloadToken = await queries.getDownloadToken(token);\n if (!downloadToken) {\n return Response.json({ error: \"Invalid download link\" }, { status: 404 });\n }\n\n const release = await queries.getReleaseById(releaseId);\n if (!release?.coverImageUrl) {\n return Response.json({ error: \"File not found\" }, { status: 404 });\n }\n\n const ok = await queries.tokenGrantsRelease(token, releaseId);\n if (!ok) {\n return Response.json({ error: \"Release not in order\" }, { status: 403 });\n }\n\n const url = await storage.getPresignedDownloadUrl(release.coverImageUrl, {\n filename: coverArtFilename(release.name),\n contentType: \"image/jpeg\",\n });\n return Response.redirect(url, 302);\n}\n\nexport { cleanDownloadFilename };\n\nexport type { Queries };\n","import type { NextRequest } from \"next/server\";\nimport type {\n OrderWithItems,\n Queries,\n ReleaseWithTracks,\n TrackWithFiles,\n} from \"@gigamusic/db\";\nimport type { StorageProvider } from \"@gigamusic/storage\";\nimport type { DownloadDeps } from \"./types.js\";\nimport { cleanDownloadFilename, coverArtFilename, sanitizeSegment } from \"./filenames.js\";\n\ninterface RouteContext {\n params: Promise<{ token: string }>;\n}\n\n/**\n * How a zip entry can be re-addressed through `createDownloadHandler`, so the\n * manifest can hand the service worker a same-origin URL that mints the R2\n * signature at fetch time instead of a signature minted up front. Entries\n * without a source fall back to a presigned URL (see `ZIP_MANIFEST_EXPIRES_IN_SECONDS`).\n */\nexport type ZipFileSource =\n | { kind: \"track\"; trackId: number; format: string }\n | { kind: \"cover\"; releaseId: number };\n\n/** A file destined for the zip, paired with the storage object it streams from. */\nexport interface ZipFile {\n fileName: string;\n storageKey: string;\n contentType: string;\n source?: ZipFileSource;\n /**\n * Exact byte length of the stored object, when the catalog knows it.\n * `track_files.fileSize` is nullable (and legacy rows carry `0`), and cover\n * art has no size row at all, so this is absent as often as not —\n * `createDownloadZipStreamHandler` measures the stragglers itself before\n * committing to a `Content-Length`.\n */\n byteSize?: number;\n}\n\n/** `track_files.fileSize` is nullable and legacy rows store `0`; only a positive integer is a real size. */\nfunction knownByteSize(fileSize: number | null | undefined): number | undefined {\n return typeof fileSize === \"number\" && Number.isSafeInteger(fileSize) && fileSize > 0\n ? fileSize\n : undefined;\n}\n\ninterface ZipManifest {\n zipName: string;\n files: { fileName: string; url: string }[];\n}\n\n/**\n * Expiry for the presigned URLs the manifest falls back to when an entry has\n * no `source` to re-address it by.\n *\n * The storage default is 5 minutes, measured from when the manifest is built —\n * which is fatally short here. The service worker fetches manifest entries\n * strictly sequentially (a zip is a sequential format; `client-zip` drains one\n * body before opening the next), so a multi-gigabyte bundle is still working\n * through the list long after minute five. Worse, R2's signature-failure\n * responses carry no CORS headers, so the cross-origin fetch rejects with an\n * opaque `NetworkError` rather than a readable 403 — and the SW's per-file\n * catch turns each one into a `_FAILED_*.txt` placeholder, so the customer\n * gets a plausible-looking zip with most of their music missing.\n *\n * 12 hours comfortably outlasts any realistic download. These URLs are\n * shareable for that window, which is a small leak: the order link that\n * produced them grants the same files and doesn't expire at all.\n */\nexport const ZIP_MANIFEST_EXPIRES_IN_SECONDS = 12 * 60 * 60;\n\n/**\n * Discriminated result of `resolveZipBundle` — the SW-manifest handler and\n * the server-side streaming handler both call this so they produce\n * byte-identical archives. `ok: false` carries the HTTP status the caller\n * should return.\n */\nexport type ResolveZipResult =\n | { ok: true; zipName: string; files: ZipFile[] }\n | { ok: false; status: number; error: string };\n\nexport interface ResolveZipArgs {\n queries: Queries;\n token: string;\n /** Single-release download; falsy means \"whole order or selected tracks\". */\n releaseId?: number | string | null;\n /** Comma-separated track IDs for partial-order downloads. */\n trackIdsParam?: string | null;\n /** \"mp3\" or \"wav\". */\n format?: string;\n /** Optional artist-name prefix; see `DownloadDeps.zipNamePrefix`. */\n zipNamePrefix?: string;\n}\n\n/** Prepend the artist prefix when configured; otherwise return `base` untouched. */\nfunction applyZipNamePrefix(prefix: string | undefined, base: string): string {\n const trimmed = prefix?.trim();\n return trimmed ? `${trimmed} - ${base}` : base;\n}\n\n/**\n * Auth + file-list resolution for a customer's bulk download. The\n * `createDownloadZipHandler` (SW manifest path) and\n * `createDownloadZipStreamHandler` (server-side streaming path) both call\n * this so the two routes stay in lockstep on auth and on what ends up\n * in the archive.\n */\nexport async function resolveZipBundle(args: ResolveZipArgs): Promise<ResolveZipResult> {\n const { queries, token } = args;\n const format = args.format ?? \"mp3\";\n const audioContentType = format === \"wav\" ? \"audio/wav\" : \"audio/mpeg\";\n const releaseIdRaw = args.releaseId == null ? null : String(args.releaseId);\n const trackIdsParam = args.trackIdsParam ?? null;\n\n const downloadToken = await queries.getDownloadToken(token);\n if (!downloadToken) {\n return { ok: false, status: 404, error: \"Invalid download link\" };\n }\n\n const order = await queries.getOrderById(downloadToken.orderId);\n if (!order) {\n return { ok: false, status: 404, error: \"Invalid download link\" };\n }\n\n return resolveBundle({\n order,\n releaseId: releaseIdRaw,\n trackIdsParam,\n format,\n audioContentType,\n zipNamePrefix: args.zipNamePrefix,\n });\n}\n\n/**\n * Build the GET handler for the zip-manifest endpoint. Returns the JSON\n * manifest the consumer-shipped service worker (`public/sw-zip.js`)\n * consumes — the SW pipes each URL through `client-zip` and streams the\n * archive straight from storage to the browser, with the serverless function\n * never touching audio bytes.\n *\n * Manifest URLs point back at `createDownloadHandler` on this same origin\n * rather than directly at presigned R2 URLs. That route 302s to a *freshly*\n * signed URL, so the signature is minted at the moment the SW reaches that\n * entry — however many hours into the archive that is. Signing everything up\n * front meant every URL in a multi-gigabyte bundle died 5 minutes after the\n * click; see `ZIP_MANIFEST_EXPIRES_IN_SECONDS`. The redirect adds one cheap\n * function invocation per file and gives failures a server-side trace they\n * never had while the SW talked straight to R2.\n *\n * The manifest shape (`{ zipName, files: [{ fileName, url }] }`) is unchanged,\n * so an already-deployed service worker keeps working without modification.\n *\n * Query-param branches:\n * - `trackIds` set → curated track list, flat layout\n * - `releaseId` set → single-release bundle with cover art\n * - neither set → whole order (releases + à-la-carte tracks)\n */\nexport function createDownloadZipHandler(\n deps: DownloadDeps,\n): (req: NextRequest, ctx: RouteContext) => Promise<Response> {\n const { queries, storage } = deps;\n\n return async (req: NextRequest, ctx: RouteContext): Promise<Response> => {\n const { token } = await ctx.params;\n const url = new URL(req.url);\n const resolution = await resolveZipBundle({\n queries,\n token,\n releaseId: url.searchParams.get(\"releaseId\"),\n trackIdsParam: url.searchParams.get(\"trackIds\"),\n format: url.searchParams.get(\"format\") ?? \"mp3\",\n zipNamePrefix: deps.zipNamePrefix,\n });\n if (!resolution.ok) {\n return Response.json({ error: resolution.error }, { status: resolution.status });\n }\n\n const manifest: ZipManifest = {\n zipName: resolution.zipName,\n files: await buildManifestFiles(\n storage,\n resolution.files,\n downloadRouteUrl(url, token, deps.downloadPath),\n ),\n };\n return Response.json(manifest);\n };\n}\n\ntype Resolution =\n | { ok: true; zipName: string; files: ZipFile[] }\n | { ok: false; status: number; error: string };\n\ninterface ResolveArgs {\n order: OrderWithItems;\n releaseId: string | null;\n trackIdsParam: string | null;\n format: string;\n audioContentType: string;\n zipNamePrefix?: string;\n}\n\nfunction resolveBundle(args: ResolveArgs): Resolution {\n const { order, releaseId, trackIdsParam, format, audioContentType, zipNamePrefix } = args;\n const fmt = format.toUpperCase();\n\n if (trackIdsParam) {\n return resolveTrackList(order, trackIdsParam, format, audioContentType, fmt, zipNamePrefix);\n }\n if (releaseId) {\n const id = Number(releaseId);\n if (!Number.isFinite(id)) {\n return { ok: false, status: 404, error: \"Release not found\" };\n }\n return resolveSingleRelease(order, id, format, audioContentType, fmt, zipNamePrefix);\n }\n return resolveWholeOrder(order, format, audioContentType, fmt, zipNamePrefix);\n}\n\nfunction resolveTrackList(\n order: OrderWithItems,\n trackIdsParam: string,\n format: string,\n audioContentType: string,\n fmt: string,\n zipNamePrefix: string | undefined,\n): Resolution {\n const requestedTrackIds = trackIdsParam\n .split(\",\")\n .map((s) => Number(s.trim()))\n .filter((n) => Number.isFinite(n));\n const ownership = trackOwnership(order);\n const allOwned = requestedTrackIds.every((id) => ownership.owns(id));\n if (!allOwned) {\n return { ok: false, status: 403, error: \"Track not in order\" };\n }\n\n const files: ZipFile[] = [];\n for (const id of requestedTrackIds) {\n const ctx = ownership.locate(id);\n if (!ctx) continue;\n const file = ctx.track.files.find((f) => f.format === format);\n if (!file) continue;\n files.push({\n fileName: zipEntryPath(ctx.release?.name ?? null, file.fileName),\n storageKey: file.storageKey,\n contentType: audioContentType,\n source: { kind: \"track\", trackId: id, format },\n byteSize: knownByteSize(file.fileSize),\n });\n }\n if (files.length === 0) {\n return {\n ok: false,\n status: 404,\n error: \"No audio files have been uploaded for these tracks yet.\",\n };\n }\n return {\n ok: true,\n zipName: applyZipNamePrefix(zipNamePrefix, `Tracks (${fmt}).zip`),\n files,\n };\n}\n\nfunction resolveSingleRelease(\n order: OrderWithItems,\n releaseId: number,\n format: string,\n audioContentType: string,\n fmt: string,\n zipNamePrefix: string | undefined,\n): Resolution {\n const orderHasRelease = order.items.some((item) => item.releaseId === releaseId);\n if (!orderHasRelease) {\n return { ok: false, status: 403, error: \"Release not in order\" };\n }\n\n const release = order.items.find((item) => item.release?.id === releaseId)?.release;\n if (!release) {\n return { ok: false, status: 404, error: \"Release not found\" };\n }\n\n const files: ZipFile[] = release.tracks\n .map((track): ZipFile | null => {\n const file = track.files.find((f) => f.format === format);\n if (!file) return null;\n return {\n fileName: zipEntryPath(null, file.fileName),\n storageKey: file.storageKey,\n contentType: audioContentType,\n source: { kind: \"track\", trackId: track.id, format },\n byteSize: knownByteSize(file.fileSize),\n };\n })\n .filter((f): f is ZipFile => f !== null);\n\n if (files.length > 0 && release.coverImageUrl) {\n files.push(...coverArtEntries(release, release.coverImageUrl, \"\", files));\n }\n\n if (files.length === 0) {\n return {\n ok: false,\n status: 404,\n error: \"No audio files have been uploaded for these tracks yet.\",\n };\n }\n return {\n ok: true,\n zipName: applyZipNamePrefix(zipNamePrefix, `${release.name} (${fmt}).zip`),\n files,\n };\n}\n\nfunction resolveWholeOrder(\n order: OrderWithItems,\n format: string,\n audioContentType: string,\n fmt: string,\n zipNamePrefix: string | undefined,\n): Resolution {\n const releaseFiles: ZipFile[] = [];\n const aLaCarteFiles: ZipFile[] = [];\n\n for (const item of order.items) {\n if (item.release) {\n const release = item.release;\n const entries: ZipFile[] = [];\n for (const track of release.tracks) {\n const file = track.files.find((f) => f.format === format);\n if (!file) continue;\n entries.push({\n fileName: zipEntryPath(release.name, file.fileName),\n storageKey: file.storageKey,\n contentType: audioContentType,\n source: { kind: \"track\", trackId: track.id, format },\n byteSize: knownByteSize(file.fileSize),\n });\n }\n if (entries.length > 0 && release.coverImageUrl) {\n entries.push(\n ...coverArtEntries(\n release,\n release.coverImageUrl,\n `${sanitizeSegment(release.name)}/`,\n entries,\n ),\n );\n }\n releaseFiles.push(...entries);\n } else if (item.track) {\n const track = item.track;\n const release = track.release ?? null;\n const file = track.files.find((f) => f.format === format);\n if (!file) continue;\n aLaCarteFiles.push({\n fileName: zipEntryPath(release?.name ?? null, file.fileName),\n storageKey: file.storageKey,\n contentType: audioContentType,\n source: { kind: \"track\", trackId: track.id, format },\n byteSize: knownByteSize(file.fileSize),\n });\n }\n }\n\n const files = [...releaseFiles, ...aLaCarteFiles];\n if (files.length === 0) {\n return {\n ok: false,\n status: 404,\n error: \"No audio files have been uploaded for these tracks yet.\",\n };\n }\n return {\n ok: true,\n zipName: applyZipNamePrefix(zipNamePrefix, `Order ${order.id} (${fmt}).zip`),\n files,\n };\n}\n\ninterface TrackContext {\n track: TrackWithFiles;\n release?: ReleaseWithTracks | null;\n}\n\n/** Index every track an order grants access to so the manifest resolver can both check ownership and locate file rows. */\nfunction trackOwnership(order: OrderWithItems) {\n const byId = new Map<number, TrackContext>();\n for (const item of order.items) {\n if (item.release) {\n for (const t of item.release.tracks) {\n byId.set(t.id, { track: t, release: item.release });\n }\n }\n if (item.track) {\n byId.set(item.track.id, {\n track: item.track,\n release: (item.track.release as ReleaseWithTracks | null | undefined) ?? null,\n });\n }\n }\n return {\n owns: (id: number) => byId.has(id),\n locate: (id: number) => byId.get(id) ?? null,\n };\n}\n\n/**\n * Detects extended-mix tracks from their filename. The catalog marks them\n * inconsistently (\"Extended\", \"(Extended)\", \"[EXTENDED MIX]\"), so a loose\n * whole-word match on \"extended\" catches every variant.\n */\nexport function isExtendedMix(fileName: string): boolean {\n return /\\bextended\\b/i.test(fileName);\n}\n\n/**\n * Build a zip entry path: `[Release Name/][Extended/]filename`. Extended mixes\n * are nested in their own `Extended/` subfolder so they don't clutter the main\n * release listing. `releaseName` is null for single-release zips (which stay\n * flat apart from the `Extended/` split).\n */\nexport function zipEntryPath(releaseName: string | null, fileName: string): string {\n const clean = cleanDownloadFilename(fileName);\n const segments: string[] = [];\n if (releaseName) segments.push(sanitizeSegment(releaseName));\n if (isExtendedMix(clean)) segments.push(\"Extended\");\n segments.push(clean);\n return segments.join(\"/\");\n}\n\n/**\n * Cover-art zip entries for a release: the artwork alongside the tracks, plus\n * a duplicate inside the `Extended/` subfolder when any track entry uses one.\n * `folder` is the release's `Name/` prefix for multi-release zips, or \"\" for\n * flat single-release zips.\n *\n * Both entries carry the same `cover` source: cover art isn't track-keyed, so\n * `createDownloadHandler` addresses it by release id instead.\n */\nexport function coverArtEntries(\n release: Pick<ReleaseWithTracks, \"id\" | \"name\">,\n coverImageUrl: string,\n folder: string,\n trackEntries: ZipFile[],\n): ZipFile[] {\n const file = coverArtFilename(release.name);\n const source: ZipFileSource = { kind: \"cover\", releaseId: release.id };\n const entries: ZipFile[] = [\n {\n fileName: `${folder}${file}`,\n storageKey: coverImageUrl,\n contentType: \"image/jpeg\",\n source,\n },\n ];\n if (trackEntries.some((e) => e.fileName.split(\"/\").includes(\"Extended\"))) {\n entries.push({\n fileName: `${folder}Extended/${file}`,\n storageKey: coverImageUrl,\n contentType: \"image/jpeg\",\n source,\n });\n }\n return entries;\n}\n\nexport { coverArtFilename, sanitizeSegment };\n\n/**\n * Locate the single-file download route (`createDownloadHandler`) relative to\n * the manifest route that's currently serving this request.\n *\n * `docs/setup.md` mounts the manifest at `download/[token]/zip`, one segment\n * below `download/[token]`, so dropping the last path segment finds it. Doing\n * it this way rather than hardcoding `/download/{token}` keeps working under a\n * Next `basePath` or any other prefix; `downloadPath` is the escape hatch for\n * consumers who mount the two routes somewhere else entirely.\n */\nexport function downloadRouteUrl(\n manifestUrl: URL,\n token: string,\n downloadPath: string | undefined,\n): URL {\n if (downloadPath) {\n return new URL(downloadPath.replace(\"{token}\", encodeURIComponent(token)), manifestUrl);\n }\n const segments = manifestUrl.pathname.replace(/\\/+$/, \"\").split(\"/\");\n segments.pop();\n const url = new URL(manifestUrl.toString());\n url.search = \"\";\n url.hash = \"\";\n url.pathname = segments.join(\"/\") || \"/\";\n return url;\n}\n\n/**\n * Turn resolved zip entries into the `{ fileName, url }` manifest shape the\n * service worker consumes. Entries carrying a `source` become same-origin\n * `downloadUrl` links signed at fetch time; anything else falls back to a\n * long-lived presigned URL.\n */\nexport async function buildManifestFiles(\n storage: StorageProvider,\n files: ZipFile[],\n downloadUrl: URL,\n): Promise<ZipManifest[\"files\"]> {\n return Promise.all(\n files.map(async (file) => ({\n fileName: file.fileName,\n url: file.source\n ? sourceUrl(downloadUrl, file.source)\n : await storage.getPresignedDownloadUrl(file.storageKey, {\n filename: file.fileName.split(\"/\").pop() ?? file.fileName,\n contentType: file.contentType,\n expiresInSeconds: ZIP_MANIFEST_EXPIRES_IN_SECONDS,\n }),\n })),\n );\n}\n\n/** Address one zip entry through `createDownloadHandler`'s query-param contract. */\nfunction sourceUrl(downloadUrl: URL, source: ZipFileSource): string {\n const url = new URL(downloadUrl.toString());\n if (source.kind === \"track\") {\n url.searchParams.set(\"trackId\", String(source.trackId));\n url.searchParams.set(\"format\", source.format);\n } else {\n url.searchParams.set(\"asset\", \"cover\");\n url.searchParams.set(\"releaseId\", String(source.releaseId));\n }\n return url.toString();\n}\n\nexport type { Queries };\n","/**\n * Byte-exact size prediction for the archives `createDownloadZipStreamHandler`\n * produces, so that route can send a real `Content-Length`.\n *\n * Why this matters: without `Content-Length` the response is chunked, and a\n * function that dies mid-body (timeout, OOM) looks *complete* to the browser.\n * The customer ends up with a file full of valid MP3 bytes and no\n * End-of-Central-Directory record — macOS Archive Utility calls that\n * \"Error 79 – Inappropriate file type or format\". Declaring the length up\n * front turns that silent corruption into a download the browser itself flags\n * as failed.\n *\n * The numbers below are not guesses about the zip spec in general; they are\n * the exact byte counts `compress-commons@6`'s `ZipArchiveOutputStream` emits\n * for the specific shape of archive this handler builds — every entry `store`d\n * (no DEFLATE) and appended from a *stream*, which is what forces a data\n * descriptor after each entry's payload. A buffer-sourced entry has a\n * different layout (real sizes in the local header, no data descriptor), which\n * is one reason the stream handler abandons its `Content-Length` the moment it\n * has to substitute a `_FAILED_*.txt` placeholder.\n */\n\n/** signature + version + flags + method + mtime + crc + csize + size + name len + extra len */\nconst LOCAL_FILE_HEADER_BYTES = 30;\n/** LFH fields + version made by + comment len + disk + attrs + LFH offset */\nconst CENTRAL_FILE_HEADER_BYTES = 46;\n/** signature + crc + csize + size, all 32-bit. */\nconst DATA_DESCRIPTOR_BYTES = 16;\n/** Same, with 64-bit sizes — used once an entry itself crosses 4 GiB. */\nconst ZIP64_DATA_DESCRIPTOR_BYTES = 24;\n/** header id + payload len + size + csize + LFH offset (three 64-bit fields). */\nconst ZIP64_EXTRA_FIELD_BYTES = 28;\n/** signature + disks + counts + CD size + CD offset + comment len, comment empty. */\nconst END_OF_CENTRAL_DIRECTORY_BYTES = 22;\n/** The zip64 EOCD record plus its locator, written as a pair or not at all. */\nconst ZIP64_END_OF_CENTRAL_DIRECTORY_BYTES = 56 + 20;\n\n/** Largest value a 32-bit zip field can hold; anything above forces zip64 encoding. */\nconst ZIP64_MAGIC = 0xffffffff;\n/** Largest entry count the classic 16-bit EOCD record can express. */\nconst ZIP64_MAGIC_SHORT = 0xffff;\n\n/** One entry as it will be appended: the in-archive path and the payload's exact byte count. */\nexport interface PredictedZipEntry {\n name: string;\n size: number;\n}\n\n/**\n * Reject any entry name whose bytes archiver would rewrite.\n *\n * `archiver` runs every entry name through `sanitizePath` (collapse separator\n * runs, `\\` → `/`, drop a `scheme:` prefix, drop leading `/` and `../`) before\n * writing it, and the name's byte length lands in three separate headers. If\n * our prediction is computed from a name archiver then shortens, the declared\n * `Content-Length` is wrong — which is worse than sending none at all. Rather\n * than reimplementing that normalisation and hoping the two stay in step, we\n * only predict for names that are already fixed points of it.\n */\nexport function isPredictableZipEntryName(name: string): boolean {\n if (name.length === 0) return false;\n if (name.includes(\"\\\\\")) return false;\n if (name.includes(\"//\")) return false;\n if (name.startsWith(\"/\")) return false;\n if (name.startsWith(\"../\")) return false;\n if (name.endsWith(\"/\")) return false;\n return !/^\\w+:/.test(name);\n}\n\n/**\n * Exact byte length of the stored, stream-appended zip built from `entries`,\n * or `null` when the layout can't be pinned down — an unusable entry name, a\n * non-integer size, or a total large enough to lose precision as a JS number.\n * Callers must treat `null` as \"omit the header and fall back to chunked\".\n *\n * Both zip64 escalations are modelled, because either one silently changes the\n * byte count: an individual entry past 4 GiB widens its data descriptor and\n * gains a central-directory extra field, and an archive whose central\n * directory starts (or ends up) past 4 GiB — or that holds more than 65535\n * entries — gains a zip64 EOCD record and locator ahead of the classic EOCD.\n */\nexport function predictStoredZipLength(entries: PredictedZipEntry[]): number | null {\n if (entries.length === 0) return null;\n\n let localBytes = 0;\n let centralBytes = 0;\n\n for (const entry of entries) {\n if (!Number.isSafeInteger(entry.size) || entry.size < 0) return null;\n if (!isPredictableZipEntryName(entry.name)) return null;\n\n const nameBytes = Buffer.byteLength(entry.name, \"utf8\");\n const entryIsZip64 = entry.size > ZIP64_MAGIC;\n // The entry's own local-header offset, captured before this entry moves it.\n const localHeaderOffset = localBytes;\n\n localBytes +=\n LOCAL_FILE_HEADER_BYTES +\n nameBytes +\n entry.size +\n (entryIsZip64 ? ZIP64_DATA_DESCRIPTOR_BYTES : DATA_DESCRIPTOR_BYTES);\n\n centralBytes +=\n CENTRAL_FILE_HEADER_BYTES +\n nameBytes +\n (entryIsZip64 || localHeaderOffset > ZIP64_MAGIC ? ZIP64_EXTRA_FIELD_BYTES : 0);\n }\n\n const archiveIsZip64 =\n entries.length > ZIP64_MAGIC_SHORT ||\n localBytes > ZIP64_MAGIC ||\n centralBytes > ZIP64_MAGIC;\n\n const total =\n localBytes +\n centralBytes +\n (archiveIsZip64 ? ZIP64_END_OF_CENTRAL_DIRECTORY_BYTES : 0) +\n END_OF_CENTRAL_DIRECTORY_BYTES;\n\n return Number.isSafeInteger(total) ? total : null;\n}\n\n/**\n * Byte cost of the zip64 EOCD record + locator pair, exported so the test\n * suite can assert it against a real `forceZip64` archive — the one zip64\n * branch that can be exercised without a multi-gigabyte fixture.\n */\nexport const ZIP64_TRAILER_BYTES = ZIP64_END_OF_CENTRAL_DIRECTORY_BYTES;\n","import type { NextRequest } from \"next/server\";\nimport { Readable, Transform } from \"node:stream\";\nimport { finished } from \"node:stream/promises\";\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\";\nimport archiver from \"archiver\";\nimport type { DownloadDeps, ZipEntryFailure } from \"./types.js\";\nimport { resolveZipBundle, type ZipFile } from \"./zip.js\";\nimport { predictStoredZipLength } from \"./zip-length.js\";\n\ninterface RouteContext {\n params: Promise<{ token: string }>;\n}\n\n/** How long the customer's browser may go without accepting a byte before we call the download abandoned. */\nconst DEFAULT_IDLE_TIMEOUT_MS = 30_000;\n/**\n * Bytes the response may sit on ahead of the consumer. The output is\n * demand-driven, so this is the whole of our buffering: nothing is pulled out\n * of archiver — and therefore nothing is pulled out of R2 — until the\n * customer's connection has taken what we already produced.\n */\nconst OUTPUT_HIGH_WATER_MARK_BYTES = 256 * 1024;\n/**\n * High-water mark handed to archiver, overriding its 1 MiB default.\n *\n * That default is not one buffer: archiver passes its options straight down to\n * `zip-stream`, so the `Archiver` transform and the `ZipArchiveOutputStream`\n * beneath it each get 1 MiB on *both* their readable and writable sides — a\n * measured ~4.6 MB resident per in-flight archive before the response has sent\n * a byte. That is invisible on a healthy download and lethal on a stalled one:\n * eight abandoned streams sharing a Fluid instance (which is exactly what the\n * retry storm in production produced) hold ~37 MB of zip buffers on their own,\n * on top of an open R2 socket and undici's own buffers each. 64 KiB per stage\n * costs a few more event-loop turns and nothing measurable in throughput,\n * since the network is the bottleneck either way.\n */\nconst ARCHIVER_HIGH_WATER_MARK_BYTES = 64 * 1024;\n/** Per-object timeout for the pre-flight size probe; a slow probe must not delay the archive. */\nconst SIZE_PROBE_TIMEOUT_MS = 8_000;\n/** Probes run in parallel, but not unboundedly — this is TTFB the customer waits through. */\nconst SIZE_PROBE_CONCURRENCY = 6;\n/**\n * Above this many unsized entries, skip the probe entirely and stream without\n * a `Content-Length`. A bundle that needs dozens of probes is one whose\n * catalog rows are broken; the pre-flight cost isn't worth paying on every\n * retry.\n */\nconst MAX_SIZE_PROBES = 32;\n\ninterface ActiveStream {\n startedAt: number;\n supersede: () => void;\n}\n\n/**\n * Zip streams currently in flight, keyed by download token.\n *\n * This is per-instance state and therefore *not* a global lock: two requests\n * that land on different Vercel instances will not see each other. That's\n * accepted deliberately. The failure this guards against — a customer whose\n * download appears stuck clicking again, and again — reuses one instance,\n * because Fluid compute packs concurrent requests for the same function onto\n * the same instance. Catching the common case is what keeps eight zombie\n * archives from stacking up and exhausting that instance's memory; a\n * cross-instance guard would need shared state (Redis, or the DB) and is not\n * worth the dependency for the remaining tail.\n */\nconst activeStreams = new Map<string, ActiveStream>();\n\n/**\n * Server-side bulk-download fallback. Mirrors `createDownloadZipHandler`'s\n * auth + file-list logic via the shared `resolveZipBundle()`, but streams\n * a real zip back to the browser through `archiver` instead of returning\n * a manifest for a service worker to assemble.\n *\n * When to wire this up: in addition to `createDownloadZipHandler`. Browsers\n * that can't run the service worker reliably (WebKit — Safari desktop and\n * every iOS browser, plus private/incognito on most engines) get truncated\n * archives from the SW path. Detect those browsers at click time on the\n * client and hard-navigate here instead.\n *\n * Cost trade-off: audio bytes proxy through the serverless function and\n * cost egress, which is why the SW path stays the default for browsers\n * that handle it correctly.\n *\n * Per-file failure policy matches the SW: any failed storage fetch becomes\n * a `_FAILED_<name>.txt` placeholder so one bad object doesn't taint the\n * whole archive. Because that turns a broken purchase into an\n * apparently-successful download, every failure is reported through\n * `deps.onZipEntryFailure` and summarised in a single log line at the end of\n * the request. The one exception is a failure that lands *after* a\n * `Content-Length` has been promised — see below.\n *\n * Unlike the manifest path, this handler presigns each file *inside* the loop,\n * immediately before fetching it, so signature expiry can't bite. Its own\n * ceiling is the platform's function timeout: audio bytes proxy through the\n * function, so the whole archive has to be produced within `maxDuration`\n * (Vercel: 300s by default, up to 800s on Fluid compute). Multi-gigabyte\n * bundles need that raised in the consuming route — `export const maxDuration`\n * — or they'll be cut off mid-stream.\n *\n * ## Surviving a stream that dies mid-body\n *\n * A function that hits its timeout or its memory ceiling after the response\n * headers are already out produces a *silent* truncation: the customer's\n * browser sees a connection close on a chunked response and marks the download\n * complete. The file is megabytes of perfectly good MP3 with no\n * End-of-Central-Directory record, which macOS Archive Utility reports as the\n * famously unhelpful \"Error 79 – Inappropriate file type or format\". Four\n * things here exist to keep that from happening again:\n *\n * 1. **`Content-Length`.** Entries are stored uncompressed, so the archive's\n * byte count is predictable from the entry sizes (`predictStoredZipLength`).\n * Sizes come from `track_files.fileSize`, with a ranged-GET probe covering\n * the rows where it's null and the cover art that has no row at all. If\n * any size stays unknown, the header is omitted rather than guessed — a\n * wrong `Content-Length` is worse than none. Once the header is out, a\n * failed entry can no longer be papered over with a placeholder (the byte\n * count would no longer match), so the transfer is torn down instead: the\n * browser reports a failed download, which is the honest outcome.\n * 2. **Demand-driven output, on small buffers.** The response body pulls from\n * archiver only when the customer's connection has drained what came\n * before, so a slow client throttles the R2 fetch rather than filling the\n * heap — and archiver's 1 MiB default high-water mark, which it silently\n * applies to four separate stream buffers, is cut to 64 KiB (see\n * `ARCHIVER_HIGH_WATER_MARK_BYTES`).\n * 3. **An idle watchdog.** `req.signal` is the documented disconnect signal\n * but doesn't reliably fire on Vercel, so an abandoned download is instead\n * detected by the consumer going quiet (`zipStreamIdleTimeoutMs`, 30s by\n * default) and torn down within seconds rather than burning the full\n * `maxDuration` of egress and memory.\n * 4. **One stream per token.** A customer retrying a stuck download would\n * otherwise stack zombie archives on a single instance; a second request\n * supersedes the first (see `activeStreams`).\n *\n * Every request ends with a one-line summary — outcome, entries, bytes sent\n * versus bytes promised, duration — so a future truncation shows up in the\n * logs instead of only in a customer's inbox.\n */\nexport function createDownloadZipStreamHandler(\n deps: DownloadDeps,\n): (req: NextRequest, ctx: RouteContext) => Promise<Response> {\n const { queries, storage } = deps;\n const idleTimeoutMs = deps.zipStreamIdleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;\n const contentLengthEnabled = deps.zipStreamContentLength !== false;\n\n return async (req: NextRequest, ctx: RouteContext): Promise<Response> => {\n const requestStartedAt = Date.now();\n const { token } = await ctx.params;\n const url = new URL(req.url);\n\n const resolution = await resolveZipBundle({\n queries,\n token,\n releaseId: url.searchParams.get(\"releaseId\"),\n trackIdsParam: url.searchParams.get(\"trackIds\"),\n format: url.searchParams.get(\"format\") ?? \"mp3\",\n zipNamePrefix: deps.zipNamePrefix,\n });\n if (!resolution.ok) {\n return Response.json({ error: resolution.error }, { status: resolution.status });\n }\n\n // `store: true` = no DEFLATE compression. MP3/WAV barely compress, and\n // the SW path serves uncompressed entries — staying consistent means\n // identical byte counts across the two routes. It's also what makes the\n // archive's final size predictable; see `predictStoredZipLength`.\n const archive = archiver(\"zip\", {\n store: true,\n highWaterMark: ARCHIVER_HIGH_WATER_MARK_BYTES,\n });\n\n archive.on(\"error\", (err) => {\n console.error(\"[zip-stream] archiver error:\", err);\n });\n\n // Every storage fetch runs on our own signal rather than `req.signal`\n // directly, so a disconnect, a supersede and the idle watchdog all have\n // the same power to stop pulling bytes out of R2. `archive.abort()` alone\n // doesn't: archiver kills its queue but never touches the source stream\n // it's already draining.\n const fetches = new AbortController();\n let abandonedFor: string | null = null;\n let currentSource: Transform | null = null;\n let finalized = false;\n\n /** Stop the whole pipeline — storage fetches, archiver, and the entry loop — recording why. */\n const abandon = (reason: string) => {\n if (abandonedFor) return;\n abandonedFor = reason;\n const err = new Error(`zip stream abandoned: ${reason}`);\n fetches.abort(err);\n currentSource?.destroy(err);\n // `abort()` stops archiver accepting work; `destroy()` is what actually\n // tears down the readable side, so a consumer still waiting on `pull`\n // gets an error rather than a stream that never ends.\n if (!finalized) archive.abort();\n archive.destroy(err);\n };\n\n const previous = activeStreams.get(token);\n if (previous) {\n console.warn(\n `[zip-stream] superseding an in-flight stream for token ${token} ` +\n `(started ${Date.now() - previous.startedAt}ms ago)`,\n );\n previous.supersede();\n }\n const registration: ActiveStream = {\n startedAt: requestStartedAt,\n supersede: () => abandon(\"superseded\"),\n };\n activeStreams.set(token, registration);\n const deregister = () => {\n if (activeStreams.get(token) === registration) activeStreams.delete(token);\n };\n\n req.signal.addEventListener(\"abort\", () => abandon(\"client-disconnect\"));\n\n const entrySizes = contentLengthEnabled\n ? await resolveEntrySizes(storage, resolution.files, fetches.signal)\n : null;\n const contentLength = entrySizes\n ? predictStoredZipLength(\n resolution.files.map((file, i) => ({ name: file.fileName, size: entrySizes[i]! })),\n )\n : null;\n\n const failures: ZipEntryFailure[] = [];\n /** Record one unusable entry and hand it to the consumer's reporter, never letting that reporter break the stream. */\n const reportFailure = (failure: ZipEntryFailure) => {\n failures.push(failure);\n try {\n deps.onZipEntryFailure?.(failure);\n } catch (err) {\n console.warn(\"[zip-stream] onZipEntryFailure threw:\", err);\n }\n };\n\n let sentBytes = () => 0;\n let summarised = false;\n /** One greppable line per request, so a truncated archive is visible in the logs rather than only in a support email. */\n const summarise = (outcome: string) => {\n if (summarised) return;\n summarised = true;\n deregister();\n const sent = sentBytes();\n const line =\n `[zip-stream] ${outcome} token=${token} entries=${resolution.files.length} ` +\n `failed=${failures.length} bytesSent=${sent} ` +\n `contentLength=${contentLength ?? \"none\"} durationMs=${Date.now() - requestStartedAt}`;\n if (\n outcome !== \"completed\" ||\n failures.length > 0 ||\n (contentLength !== null && sent !== contentLength)\n ) {\n console.error(line);\n } else {\n console.log(line);\n }\n };\n\n const output = createMeteredOutput(archive, {\n idleTimeoutMs,\n onIdle: () => {\n console.error(\n `[zip-stream] no progress for ${idleTimeoutMs}ms on token ${token}; ` +\n `treating the download as abandoned and releasing the storage stream`,\n );\n abandon(\"consumer-idle\");\n summarise(\"abandoned:consumer-idle\");\n },\n onCancel: () => {\n abandon(\"response-cancelled\");\n summarise(\"abandoned:response-cancelled\");\n },\n onClose: () => summarise(abandonedFor ? `abandoned:${abandonedFor}` : \"completed\"),\n onError: () => summarise(abandonedFor ? `abandoned:${abandonedFor}` : \"errored\"),\n });\n sentBytes = output.bytesSent;\n\n // Serialise storage fetches: only open file N's socket after archiver\n // has fully consumed file N-1. The naive parallel approach (open every\n // socket up front, let archiver drain them in order) leaves the later\n // sockets idle behind backpressure long enough for upstream R2/S3 to\n // time them out, surfacing as `TypeError: terminated` mid-stream. A\n // serial pipeline costs one TTFB per file transition — small compared\n // to the per-file streaming time — and archiver is the serial\n // bottleneck anyway, so end-to-end throughput is unchanged.\n (async () => {\n for (const [index, file] of resolution.files.entries()) {\n if (abandonedFor) return;\n const baseName = file.fileName.split(\"/\").pop() || file.fileName;\n // Only a length that actually went out on the wire binds us: sizes\n // resolved for an archive whose layout turned out unpredictable are\n // just unused numbers, and that request keeps the placeholder policy.\n const expectedSize = contentLength === null ? undefined : entrySizes?.[index];\n /**\n * Once a `Content-Length` is out on the wire the archive's byte count\n * is a promise we can't renegotiate, so a placeholder (which has a\n * different size) would itself produce the truncated download this\n * handler exists to prevent. Fail the transfer instead: the browser\n * flags it, the customer retries, and the operator gets a log line and\n * an `onZipEntryFailure` call either way.\n */\n const failFatally = (detail: string, bytesReceived: number) => {\n console.error(\n `[zip-stream] entry ${file.fileName} failed after Content-Length was committed ` +\n `(storageKey=${file.storageKey}, bytesReceived=${bytesReceived}, ` +\n `expected=${expectedSize}): ${detail} — aborting the response so the ` +\n `browser reports a failed download instead of a corrupt archive`,\n );\n reportFailure({\n token,\n fileName: file.fileName,\n storageKey: file.storageKey,\n reason: detail,\n bytesReceived,\n });\n abandon(\"entry-failed-after-content-length\");\n };\n\n try {\n const url = await storage.getPresignedDownloadUrl(file.storageKey, {\n filename: baseName,\n contentType: file.contentType,\n });\n const res = await fetch(url, { signal: fetches.signal });\n if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`);\n // DOM `ReadableStream` and Node's web `ReadableStream` are\n // runtime-compatible but typed separately — cast at the boundary.\n const body = Readable.fromWeb(\n res.body as unknown as NodeReadableStream<Uint8Array>,\n );\n // Pass-through counter for two reasons: we get a meaningful\n // \"bytesReceived\" in failure logs, and `archiver.append(counter)`\n // gives us a handle the `finished()` await below can resolve on\n // (archiver's internal entry isn't exposed).\n let bytesReceived = 0;\n const counter = new Transform({\n transform(chunk: Buffer, _enc, cb) {\n bytesReceived += chunk.length;\n cb(null, chunk);\n },\n });\n currentSource = counter;\n // Forward upstream errors onto the counter so the `finished`\n // await rejects and the loop can append a _FAILED_ placeholder.\n body.on(\"error\", (err) => counter.destroy(err));\n body.pipe(counter);\n archive.append(counter, { name: file.fileName });\n try {\n // Resolves once both sides of `counter` are done: body has\n // ended (upstream finished) AND archiver has drained the\n // readable side (entry fully written to the zip queue).\n await finished(counter);\n } catch (err) {\n if (abandonedFor) return;\n const detail = err instanceof Error ? err.message : String(err);\n if (expectedSize !== undefined) {\n failFatally(detail, bytesReceived);\n return;\n }\n console.error(\n `[zip-stream] body stream error for ${file.fileName} ` +\n `(storageKey=${file.storageKey}, bytesReceived=${bytesReceived}): ${detail}`,\n );\n reportFailure({\n token,\n fileName: file.fileName,\n storageKey: file.storageKey,\n reason: detail,\n bytesReceived,\n });\n try {\n archive.append(\n `Streaming \"${file.fileName}\" from storage failed after ${bytesReceived} bytes: ${detail}.\\n` +\n `Try downloading the track individually from your order page.\\n`,\n { name: `_FAILED_${baseName}.txt` },\n );\n } catch (appendErr) {\n console.warn(\n `[zip-stream] could not append failure placeholder for ${file.fileName}:`,\n appendErr,\n );\n }\n continue;\n }\n // The declared length was computed from the catalog (or from a\n // pre-flight probe); an object that turns out to be a different size\n // means the promise on the wire is already wrong, so stop rather\n // than ship a mismatched body.\n if (expectedSize !== undefined && bytesReceived !== expectedSize) {\n failFatally(\n `storage object is ${bytesReceived} bytes but the catalog says ${expectedSize}`,\n bytesReceived,\n );\n return;\n }\n } catch (err) {\n if (abandonedFor) return;\n const detail = err instanceof Error ? err.message : String(err);\n if (expectedSize !== undefined) {\n failFatally(detail, 0);\n return;\n }\n console.warn(`[zip-stream] fetch failed for ${file.fileName}:`, detail);\n reportFailure({\n token,\n fileName: file.fileName,\n storageKey: file.storageKey,\n reason: detail,\n bytesReceived: 0,\n });\n archive.append(\n `Failed to download \"${file.fileName}\" from storage: ${detail}.\\n` +\n `Try downloading the track individually from your order page.\\n`,\n { name: `_FAILED_${baseName}.txt` },\n );\n } finally {\n currentSource = null;\n }\n }\n if (abandonedFor) return;\n if (failures.length > 0) {\n console.error(\n `[zip-stream] ${failures.length}/${resolution.files.length} entries failed ` +\n `for token ${token}: ${failures.map((f) => f.fileName).join(\", \")}`,\n );\n }\n finalized = true;\n await archive.finalize();\n })().catch((err) => {\n console.error(\"[zip-stream] pipeline error:\", err);\n abandon(\"pipeline-error\");\n });\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/zip\",\n \"Content-Disposition\": `attachment; filename=\"${resolution.zipName.replace(/\"/g, \"\")}\"`,\n \"Cache-Control\": \"no-store\",\n };\n if (contentLength !== null) headers[\"Content-Length\"] = String(contentLength);\n\n return new Response(output.body, { headers });\n };\n}\n\ninterface MeteredOutput {\n body: ReadableStream<Uint8Array>;\n bytesSent: () => number;\n}\n\ninterface MeteredOutputHooks {\n idleTimeoutMs: number;\n /** The consumer stopped taking bytes for `idleTimeoutMs` — the download has been abandoned. */\n onIdle: () => void;\n /** The platform cancelled the response body (the usual shape of a client disconnect). */\n onCancel: () => void;\n onClose: () => void;\n onError: () => void;\n}\n\n/**\n * Wrap archiver's output in a strictly demand-driven `ReadableStream`.\n *\n * `Readable.toWeb()` — what this replaces — runs the Node stream in flowing\n * mode and pauses it once the web stream's queue is over its high-water mark.\n * That works, but it hands the platform's response writer an already-flowing\n * source and gives us no handle on whether the customer is actually taking\n * bytes. Pulling instead of pushing gives both: nothing leaves archiver until\n * the consumer asks, so a slow customer's connection is what paces the R2\n * fetch, and \"the consumer has not asked in a while\" becomes an observable\n * event.\n *\n * That second part is what makes an abandoned download detectable at all.\n * `req.signal` is supposed to fire on disconnect and does so locally, but the\n * production incident this was written for shows requests running the full\n * 300-second timeout after the customer had plainly given up — seven retries\n * from one order in 78 minutes, each of the earlier ones still burning R2\n * egress and heap. A consumer that has gone quiet for `idleTimeoutMs` is\n * treated as gone, whatever `req.signal` believes.\n *\n * The watchdog only counts time in which *we* are the ones waiting: while a\n * `pull` is outstanding the archive is the slow side (a large track can take\n * far longer than the idle timeout to fetch from R2), and that must never be\n * mistaken for an absent customer.\n */\nfunction createMeteredOutput(source: Readable, hooks: MeteredOutputHooks): MeteredOutput {\n let bytesSent = 0;\n let lastHandoffAt = Date.now();\n let awaitingConsumer = false;\n let watchdog: ReturnType<typeof setInterval> | undefined;\n\n const stopWatchdog = () => {\n if (watchdog !== undefined) {\n clearInterval(watchdog);\n watchdog = undefined;\n }\n };\n\n /** Resolve as soon as the archive has more bytes, has ended, or has failed. */\n const waitForSource = () =>\n new Promise<void>((resolve, reject) => {\n const cleanup = () => {\n source.off(\"readable\", onReadable);\n source.off(\"end\", onEnd);\n source.off(\"close\", onEnd);\n source.off(\"error\", onError);\n };\n const onReadable = () => {\n cleanup();\n resolve();\n };\n const onEnd = () => {\n cleanup();\n resolve();\n };\n const onError = (err: unknown) => {\n cleanup();\n reject(err instanceof Error ? err : new Error(String(err)));\n };\n source.on(\"readable\", onReadable);\n source.on(\"end\", onEnd);\n // A destroy() with no error emits neither `end` nor `error`; without\n // this the pull would wait forever on a stream that is already gone.\n source.on(\"close\", onEnd);\n source.on(\"error\", onError);\n });\n\n const body = new ReadableStream<Uint8Array>(\n {\n async pull(controller) {\n awaitingConsumer = false;\n try {\n for (;;) {\n const chunk = source.read() as Buffer | null;\n if (chunk !== null && chunk.length > 0) {\n bytesSent += chunk.length;\n lastHandoffAt = Date.now();\n awaitingConsumer = true;\n controller.enqueue(chunk);\n return;\n }\n if (source.errored) throw source.errored;\n if (source.readableEnded) {\n stopWatchdog();\n controller.close();\n hooks.onClose();\n return;\n }\n if (source.destroyed) {\n throw new Error(\"zip stream ended before the archive was finalized\");\n }\n await waitForSource();\n }\n } catch (err) {\n stopWatchdog();\n hooks.onError();\n throw err;\n }\n },\n cancel() {\n stopWatchdog();\n hooks.onCancel();\n },\n },\n // Byte-counted so the cap is a real memory bound rather than a chunk\n // count: at most this much archive output is ever queued ahead of the\n // customer's connection.\n new ByteLengthQueuingStrategy({ highWaterMark: OUTPUT_HIGH_WATER_MARK_BYTES }),\n );\n\n watchdog = setInterval(\n () => {\n if (!awaitingConsumer) return;\n if (Date.now() - lastHandoffAt < hooks.idleTimeoutMs) return;\n stopWatchdog();\n hooks.onIdle();\n },\n Math.max(100, Math.floor(hooks.idleTimeoutMs / 4)),\n );\n watchdog.unref?.();\n\n return { body, bytesSent: () => bytesSent };\n}\n\n/**\n * Exact byte length of every entry in the bundle, or `null` if even one is\n * unknown — the caller must then skip `Content-Length` entirely rather than\n * declare a number it can't keep.\n *\n * `track_files.fileSize` covers audio in the normal case, but the column is\n * nullable (legacy rows store `0`) and cover art has no row at all, so the\n * stragglers are measured directly against storage. The probe is a ranged GET\n * for a single byte rather than a HEAD: presigned URLs are signed per HTTP\n * method, so a `HEAD` against a GET signature is refused, while `Range` is an\n * unsigned header and rides along fine. The `Content-Range` response carries\n * the object's full length.\n */\nasync function resolveEntrySizes(\n storage: DownloadDeps[\"storage\"],\n files: ZipFile[],\n signal: AbortSignal,\n): Promise<number[] | null> {\n const sizes = files.map((file) => file.byteSize);\n const pending = sizes.flatMap((size, i) => (size === undefined ? [i] : []));\n if (pending.length === 0) return sizes as number[];\n if (pending.length > MAX_SIZE_PROBES) {\n console.warn(\n `[zip-stream] ${pending.length} entries have no recorded size; ` +\n `streaming without a Content-Length rather than probing them all`,\n );\n return null;\n }\n\n let cursor = 0;\n let failed = false;\n const workers = Array.from(\n { length: Math.min(SIZE_PROBE_CONCURRENCY, pending.length) },\n async () => {\n while (!failed) {\n const next = pending[cursor++];\n if (next === undefined) return;\n const size = await probeObjectSize(storage, files[next]!, signal);\n if (size === null) {\n failed = true;\n return;\n }\n sizes[next] = size;\n }\n },\n );\n await Promise.all(workers);\n\n return failed ? null : (sizes as number[]);\n}\n\n/** Measure one storage object with a single-byte ranged GET, or `null` if its size can't be established. */\nasync function probeObjectSize(\n storage: DownloadDeps[\"storage\"],\n file: ZipFile,\n signal: AbortSignal,\n): Promise<number | null> {\n let res: Response;\n try {\n const url = await storage.getPresignedDownloadUrl(file.storageKey, {\n filename: file.fileName.split(\"/\").pop() || file.fileName,\n contentType: file.contentType,\n });\n res = await fetch(url, {\n headers: { Range: \"bytes=0-0\" },\n signal: AbortSignal.any([signal, AbortSignal.timeout(SIZE_PROBE_TIMEOUT_MS)]),\n });\n } catch (err) {\n console.warn(`[zip-stream] size probe failed for ${file.fileName}:`, err);\n return null;\n }\n\n try {\n // 206: the object's total length is the tail of `bytes 0-0/<total>`.\n // 200: the range was ignored and `Content-Length` is the whole object.\n const header =\n res.status === 206\n ? /\\/(\\d+)$/.exec(res.headers.get(\"content-range\")?.trim() ?? \"\")?.[1]\n : res.ok\n ? res.headers.get(\"content-length\")\n : null;\n const size = header === null || header === undefined ? NaN : Number(header);\n if (!Number.isSafeInteger(size) || size <= 0) {\n console.warn(\n `[zip-stream] size probe for ${file.fileName} returned HTTP ${res.status} ` +\n `without a usable length`,\n );\n return null;\n }\n return size;\n } finally {\n await res.body?.cancel().catch(() => {});\n }\n}\n","const ERROR_HTML = `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"color-scheme\" content=\"light dark\">\n <title>Bulk download couldn't start</title>\n <style>\n body { font-family: system-ui, -apple-system, sans-serif; max-width: 560px; margin: 3rem auto; padding: 0 1.25rem; line-height: 1.55; }\n h1 { font-size: 1.35rem; margin-bottom: 0.75rem; }\n ul { padding-left: 1.25rem; }\n li { margin: 0.35rem 0; }\n a { color: #6366f1; }\n </style>\n</head>\n<body>\n <h1>Bulk download couldn't start</h1>\n <p>Your browser couldn't generate the zip in the background. This usually happens in private/incognito mode, or after the order page has been open for a long time.</p>\n <p>To get your music:</p>\n <ul>\n <li>Go back to your order page and use the individual track download buttons.</li>\n <li>Or reopen the order link in a fresh tab and try the bulk download again.</li>\n </ul>\n <p><a href=\"javascript:history.back()\">&larr; Back to your order</a></p>\n</body>\n</html>`;\n\n/**\n * Fallback for `/sw-zip/[...path]` reached only when the consumer-shipped\n * service worker (`public/sw-zip.js`) fails to intercept the\n * navigation. Without this route the browser would save Next.js's 404\n * HTML under the URL's `.zip` filename — and macOS Archive Utility\n * surfaces that as the confusing \"Error 79 - Inappropriate file type or\n * format.\" Returning `text/html` (and no `Content-Disposition`) makes\n * the browser render the message instead of saving it.\n *\n * Wire up at `app/sw-zip/[...path]/route.ts`:\n *\n * ```ts\n * export const GET = createSwZipFallbackHandler();\n * ```\n */\nexport function createSwZipFallbackHandler(): () => Response {\n return () => {\n // Hitting this route is always a failure mode — the SW should have\n // intercepted. Log so the frequency shows up in serverless logs.\n console.warn(\"[sw-zip] fallback route hit — service worker did not intercept\");\n return new Response(ERROR_HTML, {\n status: 503,\n headers: {\n \"Content-Type\": \"text/html; charset=utf-8\",\n \"Cache-Control\": \"no-store\",\n },\n });\n };\n}\n"]}