@zorgo/next 1.1.2 → 1.1.3

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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server.ts","../src/seo.ts"],"sourcesContent":["// Server-only entry (@zorgo/next/server). Node/Server-Component safe: no React,\n// no \"use client\", no client hooks — so scripts/prebuild.ts and the\n// app/api/zorgo-token route can import the token-exchange and build pipeline\n// from the package instead of scaffolding their own copies. Shipping these here\n// means a package update fixes every site, rather than each site freezing a\n// forked copy at scaffold time.\nimport { writeFileSync, mkdirSync } from \"fs\";\nimport path from \"path\";\nimport { loadEnvConfig } from \"@next/env\";\nimport { z } from \"zod\";\nimport type { Menu } from \"@zorgo/universal/utils/menuUtils\";\nimport { buildRestaurantJsonLd } from \"./seo\";\n\n// localhost fallback keeps a stray dev run from silently hitting production.\n// Read at call time, never at module load: prebuild imports this file before it\n// calls loadEnvConfig(), and ES imports are evaluated first — a module-level\n// read would freeze to the fallback before .env is loaded.\nfunction apiBaseUrl(): string {\n\treturn process.env.NEXT_PUBLIC_API_BASE_URL ?? \"http://localhost:3000\";\n}\n\n/** Why a site token could not be obtained — lets callers report precisely. */\nexport type SiteTokenFailure = \"missing-secret\" | \"rejected\" | \"unreachable\";\n\nexport class SiteTokenError extends Error {\n\treadonly reason: SiteTokenFailure;\n\tconstructor(reason: SiteTokenFailure, message: string, options?: { cause?: unknown }) {\n\t\tsuper(message, options);\n\t\tthis.name = \"SiteTokenError\";\n\t\tthis.reason = reason;\n\t}\n}\n\n/**\n * Exchange ZORGO_SITE_SECRET for a short-lived site token.\n *\n * Throws a {@link SiteTokenError} that distinguishes the three failure modes\n * (secret not set / endpoint rejected the secret / endpoint unreachable) rather\n * than collapsing them into one misleading \"missing secret\" message.\n */\nexport async function fetchSiteToken(): Promise<string> {\n\tconst secret = process.env.ZORGO_SITE_SECRET;\n\tif (!secret) {\n\t\tthrow new SiteTokenError(\n\t\t\t\"missing-secret\",\n\t\t\t\"ZORGO_SITE_SECRET is not set. Add it to this site's .env.\",\n\t\t);\n\t}\n\n\tconst url = `${apiBaseUrl()}/auth/site-token`;\n\tlet res: Response;\n\ttry {\n\t\tres = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"x-zorgo-site-secret\": secret },\n\t\t});\n\t} catch (cause) {\n\t\tthrow new SiteTokenError(\n\t\t\t\"unreachable\",\n\t\t\t`Could not reach the site-token endpoint at ${url}. Check NEXT_PUBLIC_API_BASE_URL and that the API is running.`,\n\t\t\t{ cause },\n\t\t);\n\t}\n\n\tif (!res.ok) {\n\t\tconst body = (await res.text().catch(() => \"\")).slice(0, 200);\n\t\tthrow new SiteTokenError(\n\t\t\t\"rejected\",\n\t\t\t`The site-token endpoint rejected the request (${res.status} ${res.statusText}) at ${url}. ` +\n\t\t\t\t`This usually means ZORGO_SITE_SECRET is wrong for this environment.${body ? ` Response: ${body}` : \"\"}`,\n\t\t);\n\t}\n\n\tconst { token } = (await res.json()) as { token?: string };\n\tif (!token) {\n\t\tthrow new SiteTokenError(\n\t\t\t\"rejected\",\n\t\t\t`The site-token endpoint returned 200 but no token at ${url}.`,\n\t\t);\n\t}\n\treturn token;\n}\n\n// ---------------------------------------------------------------------------\n// Prebuild pipeline\n// ---------------------------------------------------------------------------\n// Everything below runs at build time in a scaffolded site's cwd, fetching\n// franchise data and writing the generated modules/assets the site imports.\n\ninterface FetchArgs {\n\tfranchiseId: number;\n\ttoken: string;\n}\n\nconst BrandingSchema = z\n\t.object({\n\t\tbackgroundBase: z.string().default(\"#F5F5F5\"),\n\t\tprimary: z.string().default(\"#3264FF\"),\n\t\taccent: z.string().default(\"#9696FF\"),\n\t\tfontFamily: z.string().default(\"Inter\"), // Google Font family name\n\t})\n\t.prefault({});\ntype Branding = z.infer<typeof BrandingSchema>;\n\n// A freshly-created franchise commonly has null columns (no site_url set yet,\n// name not filled in). Coerce null|undefined to sensible values instead of\n// letting z.string() throw on null and blow up JSON-LD generation.\nconst FranchiseSchema = z\n\t.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.nullish()\n\t\t\t.transform((v) => v ?? process.env.ZORGO_PROJECT_NAME ?? \"Restaurant\"),\n\t\tsite_url: z\n\t\t\t.string()\n\t\t\t.nullish()\n\t\t\t.transform((v) => v ?? undefined),\n\t})\n\t.prefault({});\n\nconst HoursSchema = z.array(z.unknown()).prefault([]);\n\nconst MenuSchema = z.object({\n\titems: z.array(z.unknown()).default([]),\n\tcategories: z.array(z.unknown()).default([]),\n\tcomponents: z.array(z.unknown()).default([]),\n\trules: z.array(z.unknown()).default([]),\n});\n\nasync function fetchData<T>(\n\tendpoint: string,\n\ttoken: string,\n\tschema: z.ZodType<T>,\n): Promise<T> {\n\ttry {\n\t\tconst res = await fetch(`${apiBaseUrl()}${endpoint}`, {\n\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t});\n\t\tif (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);\n\t\tconst body = await res.json();\n\t\treturn schema.parse(body?.data ?? body);\n\t} catch (err) {\n\t\tconsole.error(`prebuild: ${endpoint} failed, using defaults:`, err);\n\t\treturn schema.parse(undefined);\n\t}\n}\n\n// style stuff\nconst FONT_ON_LIGHT = \"#0a0a0a\";\nconst FONT_ON_DARK = \"#f5f5f5\";\n/** Neutral channel delta toward white (elevated) / black (recessed). */\nconst BACKGROUND_SURFACE_DELTA = 15;\n/**\n * Max warm/cool bias via R↔B trade (full strength on light bases).\n * Scaled down by luminance so darker colors shift less.\n */\nconst BACKGROUND_TEMPERATURE_BIAS = 8;\n\nfunction parseHexRgb(background: string): { r: number; g: number; b: number } | null {\n\tconst hex = background.trim().replace(/^#/, \"\");\n\tconst full =\n\t\thex.length === 3\n\t\t\t? hex\n\t\t\t\t\t.split(\"\")\n\t\t\t\t\t.map((c) => c + c)\n\t\t\t\t\t.join(\"\")\n\t\t\t: hex;\n\tif (full.length !== 6 || Number.isNaN(Number.parseInt(full, 16))) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tr: Number.parseInt(full.slice(0, 2), 16),\n\t\tg: Number.parseInt(full.slice(2, 4), 16),\n\t\tb: Number.parseInt(full.slice(4, 6), 16),\n\t};\n}\n\nfunction toHex({ r, g, b }: { r: number; g: number; b: number }): string {\n\treturn `#${[r, g, b].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nfunction relativeLuminance({ r, g, b }: { r: number; g: number; b: number }): number {\n\treturn (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n}\n\n/** Temperature bias scaled by luminance; max on light bases, near-zero on dark. */\nfunction temperatureBiasFor({ r, g, b }: { r: number; g: number; b: number }): number {\n\treturn Math.round(BACKGROUND_TEMPERATURE_BIAS * relativeLuminance({ r, g, b }));\n}\n\n/** Off-black on light backgrounds, off-white on dark. */\nfunction deriveFontColor(background: string): string {\n\tconst rgb = parseHexRgb(background);\n\tif (!rgb) return FONT_ON_LIGHT;\n\treturn relativeLuminance(rgb) > 0.5 ? FONT_ON_LIGHT : FONT_ON_DARK;\n}\n\n/** Lighter, slightly warmer surface (cards, raised panels) from backgroundBase. */\nfunction deriveElevatedBackground(background: string): string {\n\tconst rgb = parseHexRgb(background);\n\tif (!rgb) return background;\n\tconst bias = temperatureBiasFor(rgb);\n\tconst warm = Math.round(bias / 4);\n\treturn toHex({\n\t\tr: Math.min(255, rgb.r + BACKGROUND_SURFACE_DELTA + warm),\n\t\tg: Math.min(255, rgb.g + BACKGROUND_SURFACE_DELTA),\n\t\tb: Math.min(255, rgb.b + BACKGROUND_SURFACE_DELTA - bias),\n\t});\n}\n\n/** Darker, slightly cooler surface (wells, inset areas) from backgroundBase. */\nfunction deriveRecessedBackground(background: string): string {\n\tconst rgb = parseHexRgb(background);\n\tif (!rgb) return background;\n\tconst bias = temperatureBiasFor(rgb);\n\tconst cool = Math.round(bias / 4);\n\treturn toHex({\n\t\tr: Math.max(0, rgb.r - BACKGROUND_SURFACE_DELTA - cool),\n\t\tg: Math.max(0, rgb.g - BACKGROUND_SURFACE_DELTA),\n\t\tb: Math.max(0, rgb.b - BACKGROUND_SURFACE_DELTA + bias),\n\t});\n}\n\nfunction writeWranglerToml() {\n\tconst name = process.env.ZORGO_PROJECT_NAME ?? \"zorgo-site\";\n\tconst siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? \"\";\n\tconst apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL ?? \"\";\n\tconst baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? siteUrl;\n\tconst supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL ?? \"\";\n\n\t// custom_domain routes need a bare hostname; skip them if the site URL isn't\n\t// a parseable absolute URL (e.g. a local/placeholder value).\n\tlet host: string | undefined;\n\ttry {\n\t\thost = new URL(siteUrl).hostname;\n\t} catch {\n\t\thost = undefined;\n\t}\n\tconst routes = host\n\t\t? `\n[[routes]]\npattern = \"${host}\"\ncustom_domain = true\n\n[[routes]]\npattern = \"www.${host}\"\ncustom_domain = true\n`\n\t\t: \"\";\n\n\t// Omit empty values: opennextjs-cloudflare injects [vars] into the build env, so\n\t// an empty NEXT_PUBLIC_SITE_URL (a site piloted before its URL is set) would land\n\t// as \"\" — a defined-but-empty value that slips past the `?? localhost` fallbacks in\n\t// layout/robots/sitemap and makes new URL(\"\") throw, failing the build/deploy.\n\t// Leaving the var out keeps it `undefined` so those fallbacks fire.\n\tconst vars: Record<string, string> = {\n\t\tNEXT_PUBLIC_SUPABASE_URL: supabaseUrl,\n\t\tNEXT_PUBLIC_API_BASE_URL: apiUrl,\n\t\tNEXT_PUBLIC_SITE_URL: siteUrl,\n\t\tNEXT_PUBLIC_BASE_URL: baseUrl,\n\t};\n\tconst varsBlock = Object.entries(vars)\n\t\t.filter(([, value]) => value !== \"\")\n\t\t.map(([key, value]) => `${key} = \"${value}\"`)\n\t\t.join(\"\\n\");\n\n\tconst toml = `# AUTO-GENERATED by scripts/prebuild.ts from .env — do not edit.\nname = \"${name}\"\nmain = \".open-next/worker.js\"\ncompatibility_date = \"2024-09-23\"\ncompatibility_flags = [\"nodejs_compat\"]\n\n# Bundles the Cloudflare worker as part of \\`wrangler deploy\\` (and \\`wrangler dev\\`),\n# so the deploy command stays a plain \\`wrangler deploy\\`. prebuild (which writes\n# this file + lib/generated) must still run first — on Cloudflare Workers Builds\n# that is the project's Build command; locally it is the npm scripts below.\n[build]\ncommand = \"npx opennextjs-cloudflare build\"\n\n[assets]\ndirectory = \".open-next/assets\"\nbinding = \"ASSETS\"\n\n[vars]\n${varsBlock}\n\n[observability]\nenabled = true\n\n[observability.logs]\nenabled = true\ninvocation_logs = true\n${routes}`;\n\n\twriteFileSync(path.join(process.cwd(), \"wrangler.toml\"), toml);\n}\n\nasync function logo({ franchiseId, token }: FetchArgs) {\n\tconst logoResponse = await fetch(\n\t\t`${apiBaseUrl()}/franchise-info/logo/${franchiseId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\t// 404 (no logo) returns a JSON body — don't persist it as logo.png, or the\n\t// icon/OG image would render an error blob instead of falling back.\n\tif (!logoResponse.ok) return;\n\n\tconst arrayBuffer = await logoResponse.arrayBuffer();\n\tconst buffer = Buffer.from(arrayBuffer);\n\n\tconst outDir = path.join(process.cwd(), `public/images/`);\n\tmkdirSync(outDir, { recursive: true });\n\twriteFileSync(path.join(outDir, \"logo.png\"), buffer);\n\twriteFileSync(path.join(outDir, \"logo.jpg\"), buffer);\n}\n\nfunction writeMenuModule(menu: Menu) {\n\tconst outDir = path.join(process.cwd(), `lib/generated/`);\n\tmkdirSync(outDir, { recursive: true });\n\t// typed module so consumers get Menu-checked imports instead of an\n\t// untyped runtime fetch of menu.json.\n\twriteFileSync(\n\t\tpath.join(outDir, \"menu.ts\"),\n\t\t`// AUTO-GENERATED by scripts/prebuild.ts — do not edit.\\n` +\n\t\t\t`import type { Menu } from \"@zorgo/next\";\\n\\n` +\n\t\t\t`export const menu: Menu = ${JSON.stringify(menu, null, 2)};\\n`,\n\t);\n}\n\nasync function menu({ franchiseId, token }: FetchArgs): Promise<Menu> {\n\tconst menuResponse = await fetch(\n\t\t`${apiBaseUrl()}/franchise-info/menu/${franchiseId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\t// A non-ok response is a real failure (auth, server error) — fail loudly.\n\tif (!menuResponse.ok) {\n\t\tthrow new Error(\n\t\t\t`menu fetch failed ${menuResponse.status}: ${await menuResponse.text()}`,\n\t\t);\n\t}\n\n\tconst body = await menuResponse.json();\n\tconst parsed = MenuSchema.parse(body?.data ?? {}) as Menu;\n\n\t// An empty menu is expected for a freshly-initialized franchise that hasn't\n\t// added items yet. Ship a valid empty menu.ts and keep building — the\n\t// storefront renders its empty state instead of failing the whole build.\n\tif (parsed.items.length === 0) {\n\t\tconsole.warn(\n\t\t\t\"prebuild: franchise has no menu items yet — generating an empty menu. \" +\n\t\t\t\t\"Add items in the dashboard and rebuild to populate the storefront.\",\n\t\t);\n\t}\n\n\twriteMenuModule(parsed);\n\treturn parsed;\n}\n\n// franchise row (name, site_url) -> Restaurant JSON-LD, written as a typed\n// module the layout imports and drops into <head>.\nasync function jsonLd({ franchiseId, token }: FetchArgs, menuData: Menu) {\n\tconst franchise = await fetchData(\n\t\t`/franchise-info/franchise/${franchiseId}`,\n\t\ttoken,\n\t\tFranchiseSchema,\n\t);\n\tconst url = franchise.site_url;\n\n\tconst data = buildRestaurantJsonLd(menuData, {\n\t\tname: franchise.name,\n\t\turl,\n\t\timage: url ? `${url}/images/logo.png` : undefined,\n\t});\n\n\tconst outDir = path.join(process.cwd(), `lib/generated/`);\n\tmkdirSync(outDir, { recursive: true });\n\twriteFileSync(\n\t\tpath.join(outDir, \"jsonld.ts\"),\n\t\t`// AUTO-GENERATED by scripts/prebuild.ts — do not edit.\\n` +\n\t\t\t`export const restaurantJsonLd = ${JSON.stringify(data, null, 2)};\\n`,\n\t);\n}\n\nasync function hours({ franchiseId, token }: FetchArgs) {\n\tawait fetchData(`/franchise-info/hours/${franchiseId}`, token, HoursSchema);\n}\n\nasync function styles({ franchiseId, token }: FetchArgs) {\n\tconst brand = await fetchData(\n\t\t`/franchise-info/styles/${franchiseId}`,\n\t\ttoken,\n\t\tBrandingSchema,\n\t);\n\n\t// Same derived palette globals.css uses, persisted as JSON so the build-time\n\t// icon/OG/manifest routes stay on brand (they readFileSync this).\n\tconst branding = {\n\t\t...brand,\n\t\tbackgroundElevated: deriveElevatedBackground(brand.backgroundBase),\n\t\tbackgroundRecessed: deriveRecessedBackground(brand.backgroundBase),\n\t\tforeground: deriveFontColor(brand.backgroundBase),\n\t\tprimaryForeground: deriveFontColor(brand.primary),\n\t\taccentForeground: deriveFontColor(brand.accent),\n\t};\n\n\tconst stylesDir = path.join(process.cwd(), `public/styles/`);\n\tmkdirSync(stylesDir, { recursive: true });\n\twriteFileSync(\n\t\tpath.join(stylesDir, \"styles.json\"),\n\t\tJSON.stringify(branding, null, 2),\n\t);\n\n\twriteFileSync(\n\t\tpath.join(process.cwd(), \"app\", \"globals.css\"),\n\t\tbuildGlobalsCss(brand),\n\t);\n}\n\nfunction buildGlobalsCss(branding: Branding): string {\n\tconst fontFamilyParam = branding.fontFamily.replace(/ /g, \"+\");\n\tconst foreground = deriveFontColor(branding.backgroundBase);\n\tconst primaryForeground = deriveFontColor(branding.primary);\n\tconst accentForeground = deriveFontColor(branding.accent);\n\tconst backgroundElevated = deriveElevatedBackground(branding.backgroundBase);\n\tconst backgroundRecessed = deriveRecessedBackground(branding.backgroundBase);\n\n\treturn `\n@import url(\"https://fonts.googleapis.com/css2?family=${fontFamilyParam}:wght@400;700&display=swap\");\n@import \"tailwindcss\";\n/* Tailwind v4 skips node_modules; @zorgo/next ships un-obfuscated components so\n their class names are scannable here. */\n@source \"../node_modules/@zorgo/next/dist/components.mjs\";\n\n:root {\n --background: ${branding.backgroundBase};\n --background-elevated: ${backgroundElevated};\n --background-recessed: ${backgroundRecessed};\n --foreground: ${foreground};\n --primary: ${branding.primary};\n --primary-foreground: ${primaryForeground};\n --accent: ${branding.accent};\n --accent-foreground: ${accentForeground};\n --font-sans: \"${branding.fontFamily}\", sans-serif;\n}\n\n@theme inline {\n --color-background: var(--background);\n --color-background-elevated: var(--background-elevated);\n --color-background-recessed: var(--background-recessed);\n --color-foreground: var(--foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --font-sans: var(--font-sans);\n}\n\n@layer base {\n button,\n [type=\"button\"],\n [type=\"submit\"],\n [type=\"reset\"] {\n cursor: pointer;\n }\n\n button:disabled,\n [type=\"button\"]:disabled,\n [type=\"submit\"]:disabled,\n [type=\"reset\"]:disabled {\n cursor: not-allowed;\n }\n}\n`;\n}\n\n/**\n * Full prebuild pipeline for a scaffolded site. Loads the site's .env, exchanges\n * the site secret for a token, then fetches franchise data and writes the\n * generated modules/assets (wrangler.toml, menu.ts, jsonld.ts, styles, logo).\n * Called from the site's thin scripts/prebuild.ts wrapper.\n */\nexport async function runPrebuild(): Promise<void> {\n\tconsole.log(\"running prebuild script...\");\n\n\t// Load .env here so the wrapper stays a one-liner and can't reintroduce the\n\t// import-order bug (env must be loaded before any env read below).\n\tloadEnvConfig(process.cwd());\n\n\t// regenerate deploy config from .env before any network work\n\twriteWranglerToml();\n\n\tconst token = await fetchSiteToken();\n\n\tconst encodedPayload = token.split(\".\")[1];\n\tif (!encodedPayload) {\n\t\tthrow new Error(\"site token is not a well-formed JWT (no payload segment)\");\n\t}\n\tconst payload = JSON.parse(Buffer.from(encodedPayload, \"base64url\").toString());\n\tconst franchiseId = payload.franchise_ids[0];\n\n\t// menu + franchise feed the JSON-LD; a menu *fetch* failure breaks the build\n\t// loudly, but an empty menu (new franchise) is tolerated inside menu().\n\tconst [menuData] = await Promise.all([\n\t\tmenu({ franchiseId, token }),\n\t\tlogo({ franchiseId, token }).catch((err) => console.error(\"logo failed:\", err)),\n\t\thours({ franchiseId, token }).catch((err) => console.error(\"hours failed:\", err)),\n\t\tstyles({ franchiseId, token }).catch((err) => console.error(\"styles failed:\", err)),\n\t]);\n\n\tawait jsonLd({ franchiseId, token }, menuData).catch((err) =>\n\t\tconsole.error(\"jsonLd failed:\", err),\n\t);\n\n\tconsole.log(\"prebuild script completed\");\n}\n","import type { Menu } from \"@zorgo/universal/utils/menuUtils\";\nimport type { ItemVariant, MenuItem } from \"@zorgo/universal/interfaces\";\n\n/** Franchise-level facts for the Restaurant node. Everything but `name` is\n * optional so the schema degrades gracefully when a field is missing. */\nexport interface RestaurantInfo {\n\tname: string;\n\turl?: string;\n\timage?: string;\n\tdescription?: string;\n\tservesCuisine?: string[];\n}\n\n// cents -> \"5.49\" (schema.org Offer.price is a string in major currency units)\nfunction priceString(cents: number): string {\n\treturn (cents / 100).toFixed(2);\n}\n\nfunction variantOffer(variant: ItemVariant) {\n\treturn {\n\t\t\"@type\": \"Offer\",\n\t\tprice: priceString(variant.display_price ?? variant.price),\n\t\tpriceCurrency: \"USD\",\n\t};\n}\n\nfunction menuItemNode(item: MenuItem) {\n\tconst node: Record<string, unknown> = {\n\t\t\"@type\": \"MenuItem\",\n\t\tname: item.name,\n\t};\n\tif (item.description) node.description = item.description;\n\t// one Offer for a single price, an array when the item has size/variant prices\n\tconst offers = item.variants.map(variantOffer);\n\tif (offers.length === 1) node.offers = offers[0];\n\telse if (offers.length > 1) node.offers = offers;\n\treturn node;\n}\n\n/**\n * Builds a standalone schema.org JSON-LD object for a single menu item, for\n * embedding in that item's `/menu/[slug]` page `<head>`. Reuses `menuItemNode`\n * (the same node shape used inside the Restaurant menu) and adds the\n * `@context` a top-level node needs. Pure — safe to run at build time.\n */\nexport function buildMenuItemJsonLd(item: MenuItem) {\n\treturn { \"@context\": \"https://schema.org\", ...menuItemNode(item) };\n}\n\n/**\n * Builds a schema.org Restaurant JSON-LD object from a prebuilt Menu and\n * franchise info. Categories become MenuSections; the items in each become\n * MenuItems. Pure — same inputs, same output — so it can run at prebuild time\n * and be embedded straight into the site's <head>.\n */\nexport function buildRestaurantJsonLd(menu: Menu, info: RestaurantInfo) {\n\tconst hasMenuSection = menu.categories.map((category) => {\n\t\tconst section: Record<string, unknown> = {\n\t\t\t\"@type\": \"MenuSection\",\n\t\t\tname: category.name,\n\t\t};\n\t\tconst items = menu.items\n\t\t\t.filter((item) => item.category_id === category.category_id)\n\t\t\t.map(menuItemNode);\n\t\tif (items.length) section.hasMenuItem = items;\n\t\treturn section;\n\t});\n\n\tconst restaurant: Record<string, unknown> = {\n\t\t\"@context\": \"https://schema.org\",\n\t\t\"@type\": \"Restaurant\",\n\t\tname: info.name,\n\t};\n\tif (info.url) restaurant.url = info.url;\n\tif (info.image) restaurant.image = info.image;\n\tif (info.description) restaurant.description = info.description;\n\tif (info.servesCuisine?.length) restaurant.servesCuisine = info.servesCuisine;\n\trestaurant.hasMenu = {\n\t\t\"@type\": \"Menu\",\n\t\tname: \"Menu\",\n\t\thasMenuSection,\n\t};\n\n\treturn restaurant;\n}\n"],"mappings":"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","names":["menu","path","menu"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server.ts","../src/seo.ts"],"sourcesContent":["// Server-only entry (@zorgo/next/server). Node/Server-Component safe: no React,\n// no \"use client\", no client hooks — so scripts/prebuild.ts and the\n// app/api/zorgo-token route can import the token-exchange and build pipeline\n// from the package instead of scaffolding their own copies. Shipping these here\n// means a package update fixes every site, rather than each site freezing a\n// forked copy at scaffold time.\nimport { writeFileSync, mkdirSync } from \"fs\";\nimport path from \"path\";\nimport { loadEnvConfig } from \"@next/env\";\nimport { z } from \"zod\";\nimport type { Menu } from \"@zorgo/universal/utils/menuUtils\";\nimport { buildRestaurantJsonLd } from \"./seo\";\n\n// localhost fallback keeps a stray dev run from silently hitting production.\n// Read at call time, never at module load: prebuild imports this file before it\n// calls loadEnvConfig(), and ES imports are evaluated first — a module-level\n// read would freeze to the fallback before .env is loaded.\nfunction apiBaseUrl(): string {\n\treturn process.env.NEXT_PUBLIC_API_BASE_URL ?? \"http://localhost:3000\";\n}\n\n/** Why a site token could not be obtained — lets callers report precisely. */\nexport type SiteTokenFailure = \"missing-secret\" | \"rejected\" | \"unreachable\";\n\nexport class SiteTokenError extends Error {\n\treadonly reason: SiteTokenFailure;\n\tconstructor(reason: SiteTokenFailure, message: string, options?: { cause?: unknown }) {\n\t\tsuper(message, options);\n\t\tthis.name = \"SiteTokenError\";\n\t\tthis.reason = reason;\n\t}\n}\n\n/**\n * Exchange ZORGO_SITE_SECRET for a short-lived site token.\n *\n * Throws a {@link SiteTokenError} that distinguishes the three failure modes\n * (secret not set / endpoint rejected the secret / endpoint unreachable) rather\n * than collapsing them into one misleading \"missing secret\" message.\n */\nexport async function fetchSiteToken(): Promise<string> {\n\tconst secret = process.env.ZORGO_SITE_SECRET;\n\tif (!secret) {\n\t\tthrow new SiteTokenError(\n\t\t\t\"missing-secret\",\n\t\t\t\"ZORGO_SITE_SECRET is not set. Add it to this site's .env.\",\n\t\t);\n\t}\n\n\tconst url = `${apiBaseUrl()}/auth/site-token`;\n\tlet res: Response;\n\ttry {\n\t\tres = await fetch(url, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"x-zorgo-site-secret\": secret },\n\t\t});\n\t} catch (cause) {\n\t\tthrow new SiteTokenError(\n\t\t\t\"unreachable\",\n\t\t\t`Could not reach the site-token endpoint at ${url}. Check NEXT_PUBLIC_API_BASE_URL and that the API is running.`,\n\t\t\t{ cause },\n\t\t);\n\t}\n\n\tif (!res.ok) {\n\t\tconst body = (await res.text().catch(() => \"\")).slice(0, 200);\n\t\tthrow new SiteTokenError(\n\t\t\t\"rejected\",\n\t\t\t`The site-token endpoint rejected the request (${res.status} ${res.statusText}) at ${url}. ` +\n\t\t\t\t`This usually means ZORGO_SITE_SECRET is wrong for this environment.${body ? ` Response: ${body}` : \"\"}`,\n\t\t);\n\t}\n\n\tconst { token } = (await res.json()) as { token?: string };\n\tif (!token) {\n\t\tthrow new SiteTokenError(\n\t\t\t\"rejected\",\n\t\t\t`The site-token endpoint returned 200 but no token at ${url}.`,\n\t\t);\n\t}\n\treturn token;\n}\n\n// ---------------------------------------------------------------------------\n// Prebuild pipeline\n// ---------------------------------------------------------------------------\n// Everything below runs at build time in a scaffolded site's cwd, fetching\n// franchise data and writing the generated modules/assets the site imports.\n\ninterface FetchArgs {\n\tfranchiseId: number;\n\ttoken: string;\n}\n\nconst BrandingSchema = z\n\t.object({\n\t\tbackgroundBase: z.string().default(\"#F5F5F5\"),\n\t\tprimary: z.string().default(\"#3264FF\"),\n\t\taccent: z.string().default(\"#9696FF\"),\n\t\tfontFamily: z.string().default(\"Inter\"), // Google Font family name\n\t})\n\t.prefault({});\ntype Branding = z.infer<typeof BrandingSchema>;\n\n// A freshly-created franchise commonly has null columns (no site_url set yet,\n// name not filled in). Coerce null|undefined to sensible values instead of\n// letting z.string() throw on null and blow up JSON-LD generation.\nconst FranchiseSchema = z\n\t.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.nullish()\n\t\t\t.transform((v) => v ?? process.env.ZORGO_PROJECT_NAME ?? \"Restaurant\"),\n\t\tsite_url: z\n\t\t\t.string()\n\t\t\t.nullish()\n\t\t\t.transform((v) => v ?? undefined),\n\t})\n\t.prefault({});\n\nconst HoursSchema = z.array(z.unknown()).prefault([]);\n\nconst MenuSchema = z.object({\n\titems: z.array(z.unknown()).default([]),\n\tcategories: z.array(z.unknown()).default([]),\n\tcomponents: z.array(z.unknown()).default([]),\n\trules: z.array(z.unknown()).default([]),\n});\n\nasync function fetchData<T>(\n\tendpoint: string,\n\ttoken: string,\n\tschema: z.ZodType<T>,\n): Promise<T> {\n\ttry {\n\t\tconst res = await fetch(`${apiBaseUrl()}${endpoint}`, {\n\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t});\n\t\tif (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);\n\t\tconst body = await res.json();\n\t\treturn schema.parse(body?.data ?? body);\n\t} catch (err) {\n\t\tconsole.error(`prebuild: ${endpoint} failed, using defaults:`, err);\n\t\treturn schema.parse(undefined);\n\t}\n}\n\n// style stuff\nconst FONT_ON_LIGHT = \"#0a0a0a\";\nconst FONT_ON_DARK = \"#f5f5f5\";\n/** Neutral channel delta toward white (elevated) / black (recessed). */\nconst BACKGROUND_SURFACE_DELTA = 15;\n/**\n * Max warm/cool bias via R↔B trade (full strength on light bases).\n * Scaled down by luminance so darker colors shift less.\n */\nconst BACKGROUND_TEMPERATURE_BIAS = 8;\n\nfunction parseHexRgb(background: string): { r: number; g: number; b: number } | null {\n\tconst hex = background.trim().replace(/^#/, \"\");\n\tconst full =\n\t\thex.length === 3\n\t\t\t? hex\n\t\t\t\t\t.split(\"\")\n\t\t\t\t\t.map((c) => c + c)\n\t\t\t\t\t.join(\"\")\n\t\t\t: hex;\n\tif (full.length !== 6 || Number.isNaN(Number.parseInt(full, 16))) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tr: Number.parseInt(full.slice(0, 2), 16),\n\t\tg: Number.parseInt(full.slice(2, 4), 16),\n\t\tb: Number.parseInt(full.slice(4, 6), 16),\n\t};\n}\n\nfunction toHex({ r, g, b }: { r: number; g: number; b: number }): string {\n\treturn `#${[r, g, b].map((c) => c.toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nfunction relativeLuminance({ r, g, b }: { r: number; g: number; b: number }): number {\n\treturn (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n}\n\n/** Temperature bias scaled by luminance; max on light bases, near-zero on dark. */\nfunction temperatureBiasFor({ r, g, b }: { r: number; g: number; b: number }): number {\n\treturn Math.round(BACKGROUND_TEMPERATURE_BIAS * relativeLuminance({ r, g, b }));\n}\n\n/** Off-black on light backgrounds, off-white on dark. */\nfunction deriveFontColor(background: string): string {\n\tconst rgb = parseHexRgb(background);\n\tif (!rgb) return FONT_ON_LIGHT;\n\treturn relativeLuminance(rgb) > 0.5 ? FONT_ON_LIGHT : FONT_ON_DARK;\n}\n\n/** Lighter, slightly warmer surface (cards, raised panels) from backgroundBase. */\nfunction deriveElevatedBackground(background: string): string {\n\tconst rgb = parseHexRgb(background);\n\tif (!rgb) return background;\n\tconst bias = temperatureBiasFor(rgb);\n\tconst warm = Math.round(bias / 4);\n\treturn toHex({\n\t\tr: Math.min(255, rgb.r + BACKGROUND_SURFACE_DELTA + warm),\n\t\tg: Math.min(255, rgb.g + BACKGROUND_SURFACE_DELTA),\n\t\tb: Math.min(255, rgb.b + BACKGROUND_SURFACE_DELTA - bias),\n\t});\n}\n\n/** Darker, slightly cooler surface (wells, inset areas) from backgroundBase. */\nfunction deriveRecessedBackground(background: string): string {\n\tconst rgb = parseHexRgb(background);\n\tif (!rgb) return background;\n\tconst bias = temperatureBiasFor(rgb);\n\tconst cool = Math.round(bias / 4);\n\treturn toHex({\n\t\tr: Math.max(0, rgb.r - BACKGROUND_SURFACE_DELTA - cool),\n\t\tg: Math.max(0, rgb.g - BACKGROUND_SURFACE_DELTA),\n\t\tb: Math.max(0, rgb.b - BACKGROUND_SURFACE_DELTA + bias),\n\t});\n}\n\nfunction writeWranglerToml() {\n\tconst name = process.env.ZORGO_PROJECT_NAME ?? \"zorgo-site\";\n\tconst siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? \"\";\n\tconst apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL ?? \"\";\n\tconst baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? siteUrl;\n\tconst supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL ?? \"\";\n\n\t// custom_domain routes need a bare hostname; skip them if the site URL isn't\n\t// a parseable absolute URL (e.g. a local/placeholder value).\n\tlet host: string | undefined;\n\ttry {\n\t\thost = new URL(siteUrl).hostname;\n\t} catch {\n\t\thost = undefined;\n\t}\n\tconst routes = host\n\t\t? `\n[[routes]]\npattern = \"${host}\"\ncustom_domain = true\n\n[[routes]]\npattern = \"www.${host}\"\ncustom_domain = true\n`\n\t\t: \"\";\n\n\t// Omit empty values: opennextjs-cloudflare injects [vars] into the build env, so\n\t// an empty NEXT_PUBLIC_SITE_URL (a site piloted before its URL is set) would land\n\t// as \"\" — a defined-but-empty value that slips past the `?? localhost` fallbacks in\n\t// layout/robots/sitemap and makes new URL(\"\") throw, failing the build/deploy.\n\t// Leaving the var out keeps it `undefined` so those fallbacks fire.\n\tconst vars: Record<string, string> = {\n\t\tNEXT_PUBLIC_SUPABASE_URL: supabaseUrl,\n\t\tNEXT_PUBLIC_API_BASE_URL: apiUrl,\n\t\tNEXT_PUBLIC_SITE_URL: siteUrl,\n\t\tNEXT_PUBLIC_BASE_URL: baseUrl,\n\t};\n\tconst varsBlock = Object.entries(vars)\n\t\t.filter(([, value]) => value !== \"\")\n\t\t.map(([key, value]) => `${key} = \"${value}\"`)\n\t\t.join(\"\\n\");\n\n\tconst toml = `# AUTO-GENERATED by scripts/prebuild.ts from .env — do not edit.\nname = \"${name}\"\nmain = \".open-next/worker.js\"\ncompatibility_date = \"2024-09-23\"\ncompatibility_flags = [\"nodejs_compat\"]\n\n# Bundles the Cloudflare worker as part of \\`wrangler deploy\\` (and \\`wrangler dev\\`),\n# so the deploy command stays a plain \\`wrangler deploy\\`. prebuild (which writes\n# this file + lib/generated) must still run first — on Cloudflare Workers Builds\n# that is the project's Build command; locally it is the npm scripts below.\n[build]\ncommand = \"npx opennextjs-cloudflare build\"\n\n[assets]\ndirectory = \".open-next/assets\"\nbinding = \"ASSETS\"\n\n[vars]\n${varsBlock}\n\n[observability]\nenabled = true\n\n[observability.logs]\nenabled = true\ninvocation_logs = true\n${routes}`;\n\n\twriteFileSync(path.join(process.cwd(), \"wrangler.toml\"), toml);\n}\n\nasync function logo({ franchiseId, token }: FetchArgs) {\n\tconst logoResponse = await fetch(\n\t\t`${apiBaseUrl()}/franchise-info/logo/${franchiseId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\t// 404 (no logo) returns a JSON body — don't persist it as logo.png, or the\n\t// icon/OG image would render an error blob instead of falling back.\n\tif (!logoResponse.ok) return;\n\n\tconst arrayBuffer = await logoResponse.arrayBuffer();\n\tconst buffer = Buffer.from(arrayBuffer);\n\n\tconst outDir = path.join(process.cwd(), `public/images/`);\n\tmkdirSync(outDir, { recursive: true });\n\twriteFileSync(path.join(outDir, \"logo.png\"), buffer);\n\twriteFileSync(path.join(outDir, \"logo.jpg\"), buffer);\n}\n\nfunction writeMenuModule(menu: Menu) {\n\tconst outDir = path.join(process.cwd(), `lib/generated/`);\n\tmkdirSync(outDir, { recursive: true });\n\t// typed module so consumers get Menu-checked imports instead of an\n\t// untyped runtime fetch of menu.json.\n\twriteFileSync(\n\t\tpath.join(outDir, \"menu.ts\"),\n\t\t`// AUTO-GENERATED by scripts/prebuild.ts — do not edit.\\n` +\n\t\t\t`import type { Menu } from \"@zorgo/next\";\\n\\n` +\n\t\t\t`export const menu: Menu = ${JSON.stringify(menu, null, 2)};\\n`,\n\t);\n}\n\nasync function menu({ franchiseId, token }: FetchArgs): Promise<Menu> {\n\tconst menuResponse = await fetch(\n\t\t`${apiBaseUrl()}/franchise-info/menu/${franchiseId}`,\n\t\t{ headers: { Authorization: `Bearer ${token}` } },\n\t);\n\n\t// A non-ok response is a real failure (auth, server error) — fail loudly.\n\tif (!menuResponse.ok) {\n\t\tthrow new Error(\n\t\t\t`menu fetch failed ${menuResponse.status}: ${await menuResponse.text()}`,\n\t\t);\n\t}\n\n\tconst body = await menuResponse.json();\n\tconst parsed = MenuSchema.parse(body?.data ?? {}) as Menu;\n\n\t// An empty menu is expected for a freshly-initialized franchise that hasn't\n\t// added items yet. Ship a valid empty menu.ts and keep building — the\n\t// storefront renders its empty state instead of failing the whole build.\n\tif (parsed.items.length === 0) {\n\t\tconsole.warn(\n\t\t\t\"prebuild: franchise has no menu items yet — generating an empty menu. \" +\n\t\t\t\t\"Add items in the dashboard and rebuild to populate the storefront.\",\n\t\t);\n\t}\n\n\twriteMenuModule(parsed);\n\treturn parsed;\n}\n\n// franchise row (name, site_url) -> Restaurant JSON-LD, written as a typed\n// module the layout imports and drops into <head>.\nasync function jsonLd({ franchiseId, token }: FetchArgs, menuData: Menu) {\n\tconst franchise = await fetchData(\n\t\t`/franchise-info/franchise/${franchiseId}`,\n\t\ttoken,\n\t\tFranchiseSchema,\n\t);\n\tconst url = franchise.site_url;\n\n\tconst data = buildRestaurantJsonLd(menuData, {\n\t\tname: franchise.name,\n\t\turl,\n\t\timage: url ? `${url}/images/logo.png` : undefined,\n\t});\n\n\tconst outDir = path.join(process.cwd(), `lib/generated/`);\n\tmkdirSync(outDir, { recursive: true });\n\twriteFileSync(\n\t\tpath.join(outDir, \"jsonld.ts\"),\n\t\t`// AUTO-GENERATED by scripts/prebuild.ts — do not edit.\\n` +\n\t\t\t`export const restaurantJsonLd = ${JSON.stringify(data, null, 2)};\\n`,\n\t);\n}\n\nasync function hours({ franchiseId, token }: FetchArgs) {\n\tawait fetchData(`/franchise-info/hours/${franchiseId}`, token, HoursSchema);\n}\n\nasync function styles({ franchiseId, token }: FetchArgs) {\n\tconst brand = await fetchData(\n\t\t`/franchise-info/styles/${franchiseId}`,\n\t\ttoken,\n\t\tBrandingSchema,\n\t);\n\n\t// Same derived palette globals.css uses, persisted as JSON so the build-time\n\t// icon/OG/manifest routes stay on brand (they readFileSync this).\n\tconst branding = {\n\t\t...brand,\n\t\tbackgroundElevated: deriveElevatedBackground(brand.backgroundBase),\n\t\tbackgroundRecessed: deriveRecessedBackground(brand.backgroundBase),\n\t\tforeground: deriveFontColor(brand.backgroundBase),\n\t\tprimaryForeground: deriveFontColor(brand.primary),\n\t\taccentForeground: deriveFontColor(brand.accent),\n\t};\n\n\tconst stylesDir = path.join(process.cwd(), `public/styles/`);\n\tmkdirSync(stylesDir, { recursive: true });\n\twriteFileSync(\n\t\tpath.join(stylesDir, \"styles.json\"),\n\t\tJSON.stringify(branding, null, 2),\n\t);\n\n\twriteFileSync(\n\t\tpath.join(process.cwd(), \"app\", \"globals.css\"),\n\t\tbuildGlobalsCss(brand),\n\t);\n}\n\nfunction buildGlobalsCss(branding: Branding): string {\n\tconst fontFamilyParam = branding.fontFamily.replace(/ /g, \"+\");\n\tconst foreground = deriveFontColor(branding.backgroundBase);\n\tconst primaryForeground = deriveFontColor(branding.primary);\n\tconst accentForeground = deriveFontColor(branding.accent);\n\tconst backgroundElevated = deriveElevatedBackground(branding.backgroundBase);\n\tconst backgroundRecessed = deriveRecessedBackground(branding.backgroundBase);\n\n\treturn `\n@import url(\"https://fonts.googleapis.com/css2?family=${fontFamilyParam}:wght@400;700&display=swap\");\n@import \"tailwindcss\";\n/* Tailwind v4 skips node_modules; @zorgo/next ships un-obfuscated components so\n their class names are scannable here. */\n@source \"../node_modules/@zorgo/next/dist/components.mjs\";\n\n:root {\n --background: ${branding.backgroundBase};\n --background-elevated: ${backgroundElevated};\n --background-recessed: ${backgroundRecessed};\n --foreground: ${foreground};\n --primary: ${branding.primary};\n --primary-foreground: ${primaryForeground};\n --accent: ${branding.accent};\n --accent-foreground: ${accentForeground};\n --font-sans: \"${branding.fontFamily}\", sans-serif;\n}\n\n@theme inline {\n --color-background: var(--background);\n --color-background-elevated: var(--background-elevated);\n --color-background-recessed: var(--background-recessed);\n --color-foreground: var(--foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --font-sans: var(--font-sans);\n}\n\n@layer base {\n button,\n [type=\"button\"],\n [type=\"submit\"],\n [type=\"reset\"] {\n cursor: pointer;\n }\n\n button:disabled,\n [type=\"button\"]:disabled,\n [type=\"submit\"]:disabled,\n [type=\"reset\"]:disabled {\n cursor: not-allowed;\n }\n}\n`;\n}\n\n/**\n * Full prebuild pipeline for a scaffolded site. Loads the site's .env, exchanges\n * the site secret for a token, then fetches franchise data and writes the\n * generated modules/assets (wrangler.toml, menu.ts, jsonld.ts, styles, logo).\n * Called from the site's thin scripts/prebuild.ts wrapper.\n */\nexport async function runPrebuild(): Promise<void> {\n\tconsole.log(\"running prebuild script...\");\n\n\t// Load .env here so the wrapper stays a one-liner and can't reintroduce the\n\t// import-order bug (env must be loaded before any env read below).\n\tloadEnvConfig(process.cwd());\n\n\t// regenerate deploy config from .env before any network work\n\twriteWranglerToml();\n\n\tconst token = await fetchSiteToken();\n\n\tconst encodedPayload = token.split(\".\")[1];\n\tif (!encodedPayload) {\n\t\tthrow new Error(\"site token is not a well-formed JWT (no payload segment)\");\n\t}\n\tconst payload = JSON.parse(Buffer.from(encodedPayload, \"base64url\").toString());\n\tconst franchiseId = payload.franchise_ids[0];\n\n\t// menu + franchise feed the JSON-LD; a menu *fetch* failure breaks the build\n\t// loudly, but an empty menu (new franchise) is tolerated inside menu().\n\tconst [menuData] = await Promise.all([\n\t\tmenu({ franchiseId, token }),\n\t\tlogo({ franchiseId, token }).catch((err) => console.error(\"logo failed:\", err)),\n\t\thours({ franchiseId, token }).catch((err) => console.error(\"hours failed:\", err)),\n\t\tstyles({ franchiseId, token }).catch((err) => console.error(\"styles failed:\", err)),\n\t]);\n\n\tawait jsonLd({ franchiseId, token }, menuData).catch((err) =>\n\t\tconsole.error(\"jsonLd failed:\", err),\n\t);\n\n\tconsole.log(\"prebuild script completed\");\n}\n","import type { Menu } from \"@zorgo/universal/utils/menuUtils\";\nimport type { ItemVariant, MenuItem } from \"@zorgo/universal/interfaces\";\n\n/** Franchise-level facts for the Restaurant node. Everything but `name` is\n * optional so the schema degrades gracefully when a field is missing. */\nexport interface RestaurantInfo {\n\tname: string;\n\turl?: string;\n\timage?: string;\n\tdescription?: string;\n\tservesCuisine?: string[];\n}\n\n// cents -> \"5.49\" (schema.org Offer.price is a string in major currency units)\nfunction priceString(cents: number): string {\n\treturn (cents / 100).toFixed(2);\n}\n\nfunction variantOffer(variant: ItemVariant) {\n\treturn {\n\t\t\"@type\": \"Offer\",\n\t\tprice: priceString(variant.display_price ?? variant.price),\n\t\tpriceCurrency: \"USD\",\n\t};\n}\n\nfunction menuItemNode(item: MenuItem) {\n\tconst node: Record<string, unknown> = {\n\t\t\"@type\": \"MenuItem\",\n\t\tname: item.name,\n\t};\n\tif (item.description) node.description = item.description;\n\t// one Offer for a single price, an array when the item has size/variant prices\n\tconst offers = item.variants.map(variantOffer);\n\tif (offers.length === 1) node.offers = offers[0];\n\telse if (offers.length > 1) node.offers = offers;\n\treturn node;\n}\n\n/**\n * Builds a standalone schema.org JSON-LD object for a single menu item, for\n * embedding in that item's `/menu/[slug]` page `<head>`. Reuses `menuItemNode`\n * (the same node shape used inside the Restaurant menu) and adds the\n * `@context` a top-level node needs. Pure — safe to run at build time.\n */\nexport function buildMenuItemJsonLd(item: MenuItem) {\n\treturn { \"@context\": \"https://schema.org\", ...menuItemNode(item) };\n}\n\n/**\n * Builds a schema.org Restaurant JSON-LD object from a prebuilt Menu and\n * franchise info. Categories become MenuSections; the items in each become\n * MenuItems. Pure — same inputs, same output — so it can run at prebuild time\n * and be embedded straight into the site's <head>.\n */\nexport function buildRestaurantJsonLd(menu: Menu, info: RestaurantInfo) {\n\tconst hasMenuSection = menu.categories.map((category) => {\n\t\tconst section: Record<string, unknown> = {\n\t\t\t\"@type\": \"MenuSection\",\n\t\t\tname: category.name,\n\t\t};\n\t\tconst items = menu.items\n\t\t\t.filter((item) => item.category_id === category.category_id)\n\t\t\t.map(menuItemNode);\n\t\tif (items.length) section.hasMenuItem = items;\n\t\treturn section;\n\t});\n\n\tconst restaurant: Record<string, unknown> = {\n\t\t\"@context\": \"https://schema.org\",\n\t\t\"@type\": \"Restaurant\",\n\t\tname: info.name,\n\t};\n\tif (info.url) restaurant.url = info.url;\n\tif (info.image) restaurant.image = info.image;\n\tif (info.description) restaurant.description = info.description;\n\tif (info.servesCuisine?.length) restaurant.servesCuisine = info.servesCuisine;\n\trestaurant.hasMenu = {\n\t\t\"@type\": \"Menu\",\n\t\tname: \"Menu\",\n\t\thasMenuSection,\n\t};\n\n\treturn restaurant;\n}\n"],"mappings":"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","names":["menu","menu"]}