@elurjs/kit 2.4.10 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +313 -0
- package/README.md +248 -15
- package/dist/lib/action/index.cjs +1 -1
- package/dist/lib/action/index.js +1 -1
- package/dist/lib/{action-DZHihU3Z.cjs → action-BiE4tEty.cjs} +2 -2
- package/dist/lib/{action-DZHihU3Z.cjs.map → action-BiE4tEty.cjs.map} +1 -1
- package/dist/lib/{action-BB6N5yD-.js → action-CXVk8rWt.js} +3 -3
- package/dist/lib/{action-BB6N5yD-.js.map → action-CXVk8rWt.js.map} +1 -1
- package/dist/lib/adapter-DNsjXvq5.js +160 -0
- package/dist/lib/adapter-DNsjXvq5.js.map +1 -0
- package/dist/lib/adapter-Dz2hzR36.cjs +3 -0
- package/dist/lib/adapter-Dz2hzR36.cjs.map +1 -0
- package/dist/lib/adapters/bun.cjs +2 -2
- package/dist/lib/adapters/bun.cjs.map +1 -1
- package/dist/lib/adapters/bun.js +17 -9
- package/dist/lib/adapters/bun.js.map +1 -1
- package/dist/lib/adapters/index.d.cts +34 -0
- package/dist/lib/adapters/index.d.ts +34 -0
- package/dist/lib/adapters/netlify.cjs +1 -1
- package/dist/lib/adapters/netlify.js +7 -7
- package/dist/lib/adapters/node.cjs +5 -5
- package/dist/lib/adapters/node.cjs.map +1 -1
- package/dist/lib/adapters/node.js +20 -12
- package/dist/lib/adapters/node.js.map +1 -1
- package/dist/lib/adapters/vercel.cjs +1 -1
- package/dist/lib/adapters/vercel.js +8 -8
- package/dist/lib/build/build.d.cts +40 -0
- package/dist/lib/build/build.d.ts +40 -0
- package/dist/lib/build/document-shell.d.cts +47 -1
- package/dist/lib/build/document-shell.d.ts +47 -1
- package/dist/lib/build/vite-build.d.cts +25 -2
- package/dist/lib/build/vite-build.d.ts +25 -2
- package/dist/lib/cache/adapter.d.cts +41 -0
- package/dist/lib/cache/adapter.d.ts +41 -0
- package/dist/lib/cache/index.cjs +2 -0
- package/dist/lib/cache/index.cjs.map +1 -0
- package/dist/lib/cache/index.d.cts +5 -0
- package/dist/lib/cache/index.d.ts +5 -0
- package/dist/lib/cache/index.js +113 -0
- package/dist/lib/cache/index.js.map +1 -0
- package/dist/lib/cache/invalidation.d.cts +36 -0
- package/dist/lib/cache/invalidation.d.ts +36 -0
- package/dist/lib/cache/redis-adapter.d.cts +49 -0
- package/dist/lib/cache/redis-adapter.d.ts +49 -0
- package/dist/lib/cache-DGhTYyqh.js +50 -0
- package/dist/lib/cache-DGhTYyqh.js.map +1 -0
- package/dist/lib/cache-X1n4Ossu.cjs +2 -0
- package/dist/lib/cache-X1n4Ossu.cjs.map +1 -0
- package/dist/lib/cli/output.d.cts +58 -0
- package/dist/lib/cli/output.d.ts +58 -0
- package/dist/lib/cli/ports.d.cts +20 -0
- package/dist/lib/cli/ports.d.ts +20 -0
- package/dist/lib/cli.cjs +2559 -1488
- package/dist/lib/cli.cjs.map +1 -1
- package/dist/lib/cli.d.cts +21 -0
- package/dist/lib/cli.d.ts +21 -0
- package/dist/lib/cli.js +2558 -1488
- package/dist/lib/cli.js.map +1 -1
- package/dist/lib/config/index.cjs +1 -1
- package/dist/lib/config/index.cjs.map +1 -1
- package/dist/lib/config/index.d.cts +79 -0
- package/dist/lib/config/index.d.ts +79 -0
- package/dist/lib/config/index.js +21 -14
- package/dist/lib/config/index.js.map +1 -1
- package/dist/lib/{errors-ePYxDlhU.js → errors-j4cg9VN3.js} +24 -30
- package/dist/lib/{errors-ePYxDlhU.js.map → errors-j4cg9VN3.js.map} +1 -1
- package/dist/lib/errors-kSl9yLkg.cjs +2 -0
- package/dist/lib/{errors-0X7pCFNU.cjs.map → errors-kSl9yLkg.cjs.map} +1 -1
- package/dist/lib/hydrate-BSLuI0Nh.cjs +2 -0
- package/dist/lib/hydrate-BSLuI0Nh.cjs.map +1 -0
- package/dist/lib/hydrate-BaS7-d8C.js +133 -0
- package/dist/lib/hydrate-BaS7-d8C.js.map +1 -0
- package/dist/lib/idiomorph.esm-BOOTOg1r.cjs +2 -0
- package/dist/lib/idiomorph.esm-BOOTOg1r.cjs.map +1 -0
- package/dist/lib/idiomorph.esm-CXuY-EHB.js +371 -0
- package/dist/lib/idiomorph.esm-CXuY-EHB.js.map +1 -0
- package/dist/lib/index.cjs +1 -1
- package/dist/lib/index.cjs.map +1 -1
- package/dist/lib/index.d.cts +8 -6
- package/dist/lib/index.d.ts +8 -6
- package/dist/lib/index.js +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/interpolation-plugin-C0dTd69H.cjs +58 -0
- package/dist/lib/interpolation-plugin-C0dTd69H.cjs.map +1 -0
- package/dist/lib/{interpolation-plugin-Wgb1j4pT.js → interpolation-plugin-K2qE69tF.js} +95 -126
- package/dist/lib/interpolation-plugin-K2qE69tF.js.map +1 -0
- package/dist/lib/invalidation-CFpNQUv6.cjs +2 -0
- package/dist/lib/invalidation-CFpNQUv6.cjs.map +1 -0
- package/dist/lib/invalidation-DXhdE9wj.js +64 -0
- package/dist/lib/invalidation-DXhdE9wj.js.map +1 -0
- package/dist/lib/island/generate-entry.d.cts +40 -2
- package/dist/lib/island/generate-entry.d.ts +40 -2
- package/dist/lib/island/hydrate.d.cts +19 -6
- package/dist/lib/island/hydrate.d.ts +19 -6
- package/dist/lib/island/index.cjs +1 -1
- package/dist/lib/island/index.d.cts +2 -1
- package/dist/lib/island/index.d.ts +2 -1
- package/dist/lib/island/index.js +1 -1
- package/dist/lib/island/island.d.cts +15 -0
- package/dist/lib/island/island.d.ts +15 -0
- package/dist/lib/island-CTBnsOhI.js +59 -0
- package/dist/lib/island-CTBnsOhI.js.map +1 -0
- package/dist/lib/island-kfQEKRe_.cjs +2 -0
- package/dist/lib/island-kfQEKRe_.cjs.map +1 -0
- package/dist/lib/node-http-DVFcYeBS.js +723 -0
- package/dist/lib/node-http-DVFcYeBS.js.map +1 -0
- package/dist/lib/node-http-Uct7DhmN.cjs +13 -0
- package/dist/lib/node-http-Uct7DhmN.cjs.map +1 -0
- package/dist/lib/rolldown-runtime-BIInC43l.cjs +1 -0
- package/dist/lib/rolldown-runtime-Dy4uBu1J.js +11 -0
- package/dist/lib/router/client.cjs +1 -1
- package/dist/lib/router/client.cjs.map +1 -1
- package/dist/lib/router/client.d.cts +55 -8
- package/dist/lib/router/client.d.ts +55 -8
- package/dist/lib/router/client.js +298 -97
- package/dist/lib/router/client.js.map +1 -1
- package/dist/lib/router/redirects.d.cts +34 -0
- package/dist/lib/router/redirects.d.ts +34 -0
- package/dist/lib/runtime/handler.d.cts +59 -0
- package/dist/lib/runtime/handler.d.ts +59 -0
- package/dist/lib/runtime/index.cjs +1 -1
- package/dist/lib/runtime/index.d.cts +2 -1
- package/dist/lib/runtime/index.d.ts +2 -1
- package/dist/lib/runtime/index.js +4 -4
- package/dist/lib/runtime/logger.d.cts +47 -0
- package/dist/lib/runtime/logger.d.ts +47 -0
- package/dist/lib/runtime/node-http.d.cts +11 -1
- package/dist/lib/runtime/node-http.d.ts +11 -1
- package/dist/lib/runtime-ChE4flX3.cjs +2 -0
- package/dist/lib/runtime-ChE4flX3.cjs.map +1 -0
- package/dist/lib/runtime-OMyz5yE-.js +905 -0
- package/dist/lib/runtime-OMyz5yE-.js.map +1 -0
- package/dist/lib/seo/index.cjs +1 -10
- package/dist/lib/seo/index.d.cts +1 -0
- package/dist/lib/seo/index.d.ts +1 -0
- package/dist/lib/seo/index.js +2 -41
- package/dist/lib/seo/sitemap-from-routes.d.cts +24 -0
- package/dist/lib/seo/sitemap-from-routes.d.ts +24 -0
- package/dist/lib/{shared-CfqOLsKj.cjs → shared-CP0Dc3cp.cjs} +72 -47
- package/dist/lib/shared-CP0Dc3cp.cjs.map +1 -0
- package/dist/lib/{shared-DU3iQgr7.js → shared-D8w-neq2.js} +87 -58
- package/dist/lib/shared-D8w-neq2.js.map +1 -0
- package/dist/lib/sitemap-from-routes-CaBoiGvE.js +77 -0
- package/dist/lib/sitemap-from-routes-CaBoiGvE.js.map +1 -0
- package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs +13 -0
- package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs.map +1 -0
- package/dist/lib/ssr/render.d.cts +8 -2
- package/dist/lib/ssr/render.d.ts +8 -2
- package/dist/lib/ssr/server.d.cts +7 -0
- package/dist/lib/ssr/server.d.ts +7 -0
- package/dist/lib/ssr/stream-response.d.cts +30 -0
- package/dist/lib/ssr/stream-response.d.ts +30 -0
- package/dist/lib/ssr/stream.d.cts +16 -1
- package/dist/lib/ssr/stream.d.ts +16 -1
- package/dist/lib/{stream-DmZjYqHm.cjs → stream-Cg-xYG82.cjs} +3 -3
- package/dist/lib/stream-Cg-xYG82.cjs.map +1 -0
- package/dist/lib/stream-D-ytogdu.js +106 -0
- package/dist/lib/stream-D-ytogdu.js.map +1 -0
- package/dist/lib/vite/index.cjs +1 -1
- package/dist/lib/vite/index.cjs.map +1 -1
- package/dist/lib/vite/index.d.cts +11 -0
- package/dist/lib/vite/index.d.ts +11 -0
- package/dist/lib/vite/index.js +101 -94
- package/dist/lib/vite/index.js.map +1 -1
- package/package.json +25 -8
- package/dist/lib/errors-0X7pCFNU.cjs +0 -2
- package/dist/lib/hydrate-CJbUWdXu.js +0 -126
- package/dist/lib/hydrate-CJbUWdXu.js.map +0 -1
- package/dist/lib/hydrate-D6MR24mz.cjs +0 -2
- package/dist/lib/hydrate-D6MR24mz.cjs.map +0 -1
- package/dist/lib/interpolation-plugin-TWDch8nn.cjs +0 -39
- package/dist/lib/interpolation-plugin-TWDch8nn.cjs.map +0 -1
- package/dist/lib/interpolation-plugin-Wgb1j4pT.js.map +0 -1
- package/dist/lib/node-http-DRAUhO0c.js +0 -617
- package/dist/lib/node-http-DRAUhO0c.js.map +0 -1
- package/dist/lib/node-http-DhxguYyz.cjs +0 -13
- package/dist/lib/node-http-DhxguYyz.cjs.map +0 -1
- package/dist/lib/runtime-CrH5X-Es.cjs +0 -2
- package/dist/lib/runtime-CrH5X-Es.cjs.map +0 -1
- package/dist/lib/runtime-Ds2-wH6P.js +0 -520
- package/dist/lib/runtime-Ds2-wH6P.js.map +0 -1
- package/dist/lib/seo/index.cjs.map +0 -1
- package/dist/lib/seo/index.js.map +0 -1
- package/dist/lib/shared-CfqOLsKj.cjs.map +0 -1
- package/dist/lib/shared-DU3iQgr7.js.map +0 -1
- package/dist/lib/stream-CpOG052V.js +0 -104
- package/dist/lib/stream-CpOG052V.js.map +0 -1
- package/dist/lib/stream-DmZjYqHm.cjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/seo/index.ts"],"sourcesContent":["/**\n * SEO utilities — sitemap.xml and robots.txt generation.\n *\n * @module\n */\n\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\n\n// Types\n\nexport interface SitemapEntry {\n /** URL path, e.g. \"/docs/getting-started/introduction\". */\n url: string;\n /** Last modification date (ISO 8601 or YYYY-MM-DD). */\n lastmod?: string;\n /** Change frequency: always, hourly, daily, weekly, monthly, yearly, never. */\n changefreq?: \"always\" | \"hourly\" | \"daily\" | \"weekly\" | \"monthly\" | \"yearly\" | \"never\";\n /** Priority 0.0–1.0. */\n priority?: number;\n}\n\nexport interface SitemapConfig {\n /** Base URL of the site, e.g. \"https://example.com\". */\n siteUrl: string;\n /** List of URL entries to include in the sitemap. */\n urls: (SitemapEntry | string)[];\n /** Output directory where sitemap.xml will be written. */\n outDir: string;\n}\n\nexport interface RobotsConfig {\n /** Base URL of the site, e.g. \"https://example.com\". */\n siteUrl: string;\n /** Output directory where robots.txt will be written. */\n outDir: string;\n /** Rules for specific user agents. */\n rules?: RobotsRule[];\n /** Paths to disallow for all crawlers (shorthand for rules). */\n disallow?: string[];\n /** Sitemap URL override. If not set, defaults to `${siteUrl}/sitemap.xml`. */\n sitemapUrl?: string;\n}\n\nexport interface RobotsRule {\n /** User-agent, e.g. \"Googlebot\" or \"*\" for all. */\n userAgent: string;\n /** Paths to disallow. */\n disallow?: string[];\n /** Paths to allow. */\n allow?: string[];\n /** Crawl delay in seconds. */\n crawlDelay?: number;\n}\n\n// Sitemap generation\n\n/**\n * Generates a `sitemap.xml` file from a list of URLs.\n *\n * @example\n * ```ts\n * import { generateSitemap } from \"@elurjs/kit/seo\";\n *\n * await generateSitemap({\n * siteUrl: \"https://elur-kit.dev\",\n * outDir: \"./dist\",\n * urls: [\n * \"/\",\n * \"/docs/introduction\",\n * { url: \"/docs/routing\", changefreq: \"weekly\", priority: 0.8 },\n * ],\n * });\n * ```\n */\nexport async function generateSitemap(config: SitemapConfig): Promise<string> {\n const { siteUrl, urls, outDir } = config;\n const base = siteUrl.replace(/\\/$/, \"\");\n\n const entries: string[] = urls.map((entry) => {\n const e = typeof entry === \"string\" ? { url: entry } : entry;\n const loc = `${base}${e.url.startsWith(\"/\") ? \"\" : \"/\"}${e.url}`;\n const lines = [` <url>`, ` <loc>${escapeXml(loc)}</loc>`];\n if (e.lastmod) lines.push(` <lastmod>${e.lastmod}</lastmod>`);\n if (e.changefreq) lines.push(` <changefreq>${e.changefreq}</changefreq>`);\n if (e.priority !== undefined) lines.push(` <priority>${e.priority.toFixed(1)}</priority>`);\n lines.push(` </url>`);\n return lines.join(\"\\n\");\n });\n\n const xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${entries.join(\"\\n\")}\n</urlset>\n`;\n\n const filePath = join(outDir, \"sitemap.xml\");\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, xml, \"utf8\");\n return filePath;\n}\n\n// Robots.txt generation\n\n/**\n * Generates a `robots.txt` file.\n *\n * @example\n * ```ts\n * import { generateRobots } from \"@elurjs/kit/seo\";\n *\n * await generateRobots({\n * siteUrl: \"https://elur-kit.dev\",\n * outDir: \"./dist\",\n * disallow: [\"/api/\", \"/_elur/\"],\n * });\n * ```\n */\nexport async function generateRobots(config: RobotsConfig): Promise<string> {\n const { siteUrl, outDir, rules, disallow, sitemapUrl } = config;\n const base = siteUrl.replace(/\\/$/, \"\");\n const lines: string[] = [];\n\n if (rules && rules.length > 0) {\n for (const rule of rules) {\n lines.push(`User-agent: ${rule.userAgent}`);\n if (rule.allow) {\n for (const path of rule.allow) lines.push(`Allow: ${path}`);\n }\n if (rule.disallow) {\n for (const path of rule.disallow) lines.push(`Disallow: ${path}`);\n }\n if (rule.crawlDelay !== undefined) {\n lines.push(`Crawl-delay: ${rule.crawlDelay}`);\n }\n lines.push(\"\");\n }\n } else {\n lines.push(\"User-agent: *\");\n if (disallow && disallow.length > 0) {\n for (const path of disallow) lines.push(`Disallow: ${path}`);\n } else {\n lines.push(\"Disallow:\");\n }\n lines.push(\"\");\n }\n\n const sitemap = sitemapUrl ?? `${base}/sitemap.xml`;\n lines.push(`Sitemap: ${sitemap}`);\n\n const content = lines.join(\"\\n\") + \"\\n\";\n const filePath = join(outDir, \"robots.txt\");\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, content, \"utf8\");\n return filePath;\n}\n\n// JSON-LD / Structured data\n\nexport interface JsonLdSchema {\n [key: string]: unknown;\n}\n\n/**\n * Serializes a JSON-LD structured data object into a `<script type=\"application/ld+json\">` tag.\n *\n * @example\n * ```ts\n * import { jsonLd } from \"@elurjs/kit/seo\";\n *\n * const schema = jsonLd({\n * \"@context\": \"https://schema.org\",\n * \"@type\": \"TechArticle\",\n * headline: \"Routing\",\n * author: { \"@type\": \"Person\", name: \"Deiver Vasquez\" },\n * });\n * // Returns: <script type=\"application/ld+json\">{...}</script>\n * ```\n */\nexport function jsonLd(schema: JsonLdSchema | JsonLdSchema[]): string {\n const data = JSON.stringify(Array.isArray(schema) ? schema : schema);\n // Escape sequences that could close the <script> tag or introduce markup.\n // Per the HTML spec, inside a <script> block the only dangerous sequence\n // is \"</script\" (case-insensitive). We also escape \"<\" more broadly to\n // prevent any interpreter from seeing markup-like content, and escape\n // \"<!--\" to prevent HTML comment-based escapes.\n const safe = data\n .replace(/</g, \"\\\\u003c\")\n .replace(/>/g, \"\\\\u003e\")\n .replace(/&/g, \"\\\\u0026\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\n return `<script type=\"application/ld+json\">${safe}</script>`;\n}\n\n// Helpers\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n"],"mappings":";;;AA2EA,eAAsB,EAAgB,GAAwC;CAC5E,IAAM,EAAE,YAAS,SAAM,cAAW,GAC5B,IAAO,EAAQ,QAAQ,OAAO,EAAE,GAahC,IAAM;;EAXc,EAAK,KAAK,MAAU;EAC5C,IAAM,IAAI,OAAO,KAAU,WAAW,EAAE,KAAK,EAAM,IAAI,GAEjD,IAAQ,CAAC,WAAW,YAAY,EAAU,GADjC,IAAO,EAAE,IAAI,WAAW,GAAG,IAAI,KAAK,MAAM,EAAE,KACR,EAAE,OAAO;EAK5D,OAJI,EAAE,WAAS,EAAM,KAAK,gBAAgB,EAAE,QAAQ,WAAW,GAC3D,EAAE,cAAY,EAAM,KAAK,mBAAmB,EAAE,WAAW,cAAc,GACvE,EAAE,aAAa,KAAA,KAAW,EAAM,KAAK,iBAAiB,EAAE,SAAS,QAAQ,CAAC,EAAE,YAAY,GAC5F,EAAM,KAAK,UAAU,GACd,EAAM,KAAK,IAAI;CACxB,CAIA,CAAA,CAAQ,KAAK,IAAI,EAAE;;GAIb,IAAW,EAAK,GAAQ,aAAa;CAG3C,OAFA,MAAM,EAAM,EAAQ,CAAQ,GAAG,EAAE,WAAW,GAAK,CAAC,GAClD,MAAM,EAAU,GAAU,GAAK,MAAM,GAC9B;AACT;AAkBA,eAAsB,EAAe,GAAuC;CAC1E,IAAM,EAAE,YAAS,WAAQ,UAAO,aAAU,kBAAe,GACnD,IAAO,EAAQ,QAAQ,OAAO,EAAE,GAChC,IAAkB,CAAC;CAEzB,IAAI,KAAS,EAAM,SAAS,GAC1B,KAAK,IAAM,KAAQ,GAAO;EAExB,IADA,EAAM,KAAK,eAAe,EAAK,WAAW,GACtC,EAAK,OACP,KAAK,IAAM,KAAQ,EAAK,OAAO,EAAM,KAAK,UAAU,GAAM;EAE5D,IAAI,EAAK,UACP,KAAK,IAAM,KAAQ,EAAK,UAAU,EAAM,KAAK,aAAa,GAAM;EAKlE,AAHI,EAAK,eAAe,KAAA,KACtB,EAAM,KAAK,gBAAgB,EAAK,YAAY,GAE9C,EAAM,KAAK,EAAE;CACf;MACK;EAEL,IADA,EAAM,KAAK,eAAe,GACtB,KAAY,EAAS,SAAS,GAChC,KAAK,IAAM,KAAQ,GAAU,EAAM,KAAK,aAAa,GAAM;OAE3D,EAAM,KAAK,WAAW;EAExB,EAAM,KAAK,EAAE;CACf;CAEA,IAAM,IAAU,KAAc,GAAG,EAAK;CACtC,EAAM,KAAK,YAAY,GAAS;CAEhC,IAAM,IAAU,EAAM,KAAK,IAAI,IAAI,MAC7B,IAAW,EAAK,GAAQ,YAAY;CAG1C,OAFA,MAAM,EAAM,EAAQ,CAAQ,GAAG,EAAE,WAAW,GAAK,CAAC,GAClD,MAAM,EAAU,GAAU,GAAS,MAAM,GAClC;AACT;AAwBA,SAAgB,EAAO,GAA+C;CAapE,OAAO,sCAZM,KAAK,UAAkC,CAMvC,CAAA,CACV,QAAQ,MAAM,SAAS,CAAC,CACxB,QAAQ,MAAM,SAAS,CAAC,CACxB,QAAQ,MAAM,SAAS,CAAC,CACxB,QAAQ,WAAW,SAAS,CAAC,CAC7B,QAAQ,WAAW,SACuB,EAAK;AACpD;AAIA,SAAS,EAAU,GAAqB;CACtC,OAAO,EACJ,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,QAAQ,CAAC,CACvB,QAAQ,MAAM,QAAQ;AAC3B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"shared-CfqOLsKj.cjs","names":[],"sources":["../../src/adapters/shared.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, readdir, copyFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative } from \"node:path\";\nimport { scanRoutes } from \"../router/route-scanner.js\";\nimport type { AdapterOptions } from \"./index.js\";\nimport type { PageRoute } from \"../router/route-scanner.js\";\n\n/**\n * Shared helper: copy a directory recursively.\n */\nexport async function copyStatic(from: string, to: string): Promise<void> {\n await mkdir(to, { recursive: true });\n const entries = await readdir(from, { withFileTypes: true });\n for (const entry of entries) {\n const src = join(from, entry.name);\n const dest = join(to, entry.name);\n if (entry.isDirectory()) {\n await copyStatic(src, dest);\n } else {\n await copyFile(src, dest);\n }\n }\n}\n\n/** Adds every module the SSR runtime may import for a page to the registry. */\nfunction collectPageModules(\n page: PageRoute,\n moduleSet: Set<string>,\n actionPathsByPage: Map<string, Set<string>>,\n): void {\n moduleSet.add(page.pagePath);\n if (page.dataPath) moduleSet.add(page.dataPath);\n if (page.loadingPath) moduleSet.add(page.loadingPath);\n for (const layout of page.layouts) {\n moduleSet.add(layout);\n const layoutDataPath = layout.replace(/layout\\.ts$/, \"layout.data.ts\");\n if (layoutDataPath !== layout && existsSync(layoutDataPath)) {\n moduleSet.add(layoutDataPath);\n }\n }\n if (page.actionPath) {\n moduleSet.add(page.actionPath);\n let set = actionPathsByPage.get(page.path);\n if (!set) {\n set = new Set<string>();\n actionPathsByPage.set(page.path, set);\n }\n set.add(page.actionPath);\n }\n}\n\n/**\n * Build a self-contained SSR entry file for a platform adapter.\n * The generated module exports a default `handler(request: Request): Response`\n * and embeds the full route table plus a registry of all page/layout/data/\n * action modules so the runtime never touches the file system.\n */\nexport async function buildSsrEntry(\n routes: Awaited<ReturnType<typeof scanRoutes>>,\n options: AdapterOptions,\n entryDir: string,\n): Promise<string> {\n // Collect all module paths that the SSR runtime may need to import.\n const moduleSet = new Set<string>();\n const actionPathsByPage = new Map<string, Set<string>>();\n for (const page of routes.pages) {\n collectPageModules(page, moduleSet, actionPathsByPage);\n }\n if (routes.error404) collectPageModules(routes.error404, moduleSet, actionPathsByPage);\n if (routes.error500) collectPageModules(routes.error500, moduleSet, actionPathsByPage);\n for (const api of routes.api) {\n moduleSet.add(api.routePath);\n }\n const modules = Array.from(moduleSet);\n const moduleIndex = new Map(modules.map((path, index) => [path, index]));\n\n const imports = modules\n .map((path, index) => {\n const rel = relativeToPosix(entryDir, path);\n return `import * as m_${index} from ${JSON.stringify(rel)};`;\n })\n .join(\"\\n\");\n\n const renderPageRecord = (page: PageRoute): string => `{\n path: ${JSON.stringify(page.path)},\n pagePath: ${JSON.stringify(page.pagePath)},\n dataPath: ${JSON.stringify(page.dataPath ?? null)},\n actionPath: ${JSON.stringify(page.actionPath ?? null)},\n loadingPath: ${JSON.stringify(page.loadingPath ?? null)},\n layouts: ${JSON.stringify(page.layouts)},\n params: ${JSON.stringify(page.params)},\n }`;\n\n const pages = routes.pages.map(renderPageRecord).join(\",\\n\");\n\n const apiRoutes = routes.api\n .map((api) => {\n const index = moduleIndex.get(api.routePath);\n return ` { path: ${JSON.stringify(api.path)}, routePath: m_${index} },`;\n })\n .join(\"\\n\");\n\n const actionModules = Array.from(actionPathsByPage.entries())\n .map(([pagePath, paths]) => {\n const entries = Array.from(paths)\n .map((path) => {\n const index = moduleIndex.get(path);\n return ` [${JSON.stringify(path)}, m_${index}],`;\n })\n .join(\"\\n\");\n return ` [${JSON.stringify(pagePath)}, new Map([\\n${entries}\\n ])],`;\n })\n .join(\"\\n\");\n\n const actionsRegistry: Record<string, string[]> = {};\n for (const page of routes.pages) {\n if (!page.actionPath) continue;\n const mod = (await import(page.actionPath)) as Record<string, unknown>;\n const names: string[] = [];\n for (const [name, value] of Object.entries(mod)) {\n if (name === \"default\") continue;\n if (typeof value === \"function\") {\n names.push(name);\n }\n }\n if (names.length > 0) {\n actionsRegistry[page.path] = names;\n }\n }\n\n return `// AUTO-GENERATED by @elurjs/kit. Do not edit.\nimport { handleActionRequest, matchApiRoute, matchRoute, renderPage, renderErrorPage } from \"@elurjs/kit\";\n${imports}\n\nconst registry = new Map<string, unknown>([\n${modules.map((path, index) => ` [${JSON.stringify(path)}, m_${index}],`).join(\"\\n\")}\n]);\n\nconst pages = [\n${pages},\n];\n\nconst apiRoutes = [\n${apiRoutes}\n];\n\nconst actionModules = new Map<string, Map<string, unknown>>([\n${actionModules}\n]);\n\nconst actions = ${JSON.stringify(actionsRegistry)};\n\nconst routes = {\n pages,\n api: apiRoutes,\n error404: ${routes.error404 ? renderPageRecord(routes.error404) : \"undefined\"},\n error500: ${routes.error500 ? renderPageRecord(routes.error500) : \"undefined\"},\n};\n\nconst clientEntry = ${JSON.stringify(options.clientEntry)};\nconst lang = ${JSON.stringify(options.lang)};\n\nfunction loadModule(path: string) {\n const mod = registry.get(path);\n if (mod) return mod;\n throw new Error(\\`Module not found in registry: \\${path}\\`);\n}\n\nasync function resolveAction(name: string, page?: string) {\n // Match concrete page paths (e.g. /movies/inception) to their route pattern\n // (/movies/:slug) so actions on dynamic routes resolve by scope.\n let pageKey: string | undefined;\n if (page) {\n pageKey = routes.pages.some((route) => route.path === page)\n ? page\n : (matchRoute(page, routes.pages)?.route.path ?? page);\n }\n const pageModules = pageKey ? actionModules.get(pageKey) : undefined;\n const candidates = pageModules ? [...pageModules.values()] : [];\n if (!pageModules) {\n for (const mods of actionModules.values()) {\n for (const mod of mods.values()) {\n const action = (mod as Record<string, unknown>)[name];\n if (typeof action === \"function\") return action;\n }\n }\n }\n for (const mod of candidates) {\n const action = (mod as Record<string, unknown>)[name];\n if (typeof action === \"function\") {\n return action as (...args: unknown[]) => unknown;\n }\n }\n return undefined;\n}\n\nexport default async function handler(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n if (url.pathname === \"/__elur-js/actions\") {\n return handleActionRequest(request, resolveAction);\n }\n\n // Render endpoint used by the SPA router and streaming boundaries.\n if (url.pathname === \"/__elur-js/render\") {\n const page = url.searchParams.get(\"page\") ?? \"/\";\n const search = url.searchParams.get(\"search\") ?? \"\";\n const wantsJson = (request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n try {\n const match = matchRoute(page, routes.pages);\n if (!match) throw new Error(\\`No route found for \\${page}\\`);\n const { html } = await renderPage({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(search),\n config: { lang, clientEntry },\n importer: loadModule,\n actions,\n request,\n });\n const bodyStart = html.indexOf('<div id=\"app\">');\n const bodyEnd = html.lastIndexOf(\"</div>\");\n const body = bodyStart >= 0 && bodyEnd > bodyStart\n ? html.slice(bodyStart + 13, bodyEnd).trim()\n : html;\n const titleStart = html.indexOf(\"<title>\");\n const titleEnd = html.indexOf(\"</title>\");\n const title = titleStart >= 0 && titleEnd > titleStart\n ? html.slice(titleStart + 7, titleEnd)\n : \"\";\n if (wantsJson) {\n return new Response(JSON.stringify({ title, body }), {\n status: 200,\n headers: { \"Content-Type\": \"application/json; charset=utf-8\" },\n });\n }\n return new Response(body, {\n status: 200,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n } catch (err) {\n if ((err as { name?: string }).name === \"RouteNotFoundError\") {\n return new Response(\"Not Found\", {\n status: 404,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n console.error(\"[elur-kit] render endpoint error:\", err);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n }\n\n const apiMatch = matchApiRoute(url.pathname, apiRoutes);\n if (apiMatch) {\n const mod = apiMatch.route.routePath as Record<\n string,\n (request: Request, context?: { params: Record<string, string | string[]> }) => unknown\n >;\n const handler = mod[request.method ?? \"GET\"];\n if (typeof handler !== \"function\") {\n return new Response(\"Method not allowed: \" + request.method, { status: 405, headers: { \"Content-Type\": \"text/plain\" } });\n }\n return (await handler(request, { params: apiMatch.params })) as Response;\n }\n\n const match = matchRoute(url.pathname, routes.pages);\n if (!match) {\n const errorResult = await renderErrorPage({ routes, status: 404, config: { lang, clientEntry }, actions, importer: loadModule });\n if (errorResult) {\n return new Response(errorResult.html, { status: errorResult.status, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n }\n return new Response(\"Not Found\", { status: 404, headers: { \"Content-Type\": \"text/plain\" } });\n }\n\n try {\n const { html } = await renderPage({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(url.search),\n config: { lang, clientEntry },\n importer: loadModule,\n actions,\n request,\n });\n return new Response(html, { status: 200, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n } catch (err) {\n console.error(\"[elur-kit] SSR render error:\", err);\n const errorResult = await renderErrorPage({ routes, status: 500, error: err, config: { lang, clientEntry }, actions, importer: loadModule });\n if (errorResult) {\n return new Response(errorResult.html, { status: errorResult.status, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n }\n return new Response(\"Internal Server Error\", { status: 500, headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n}\n`;\n}\n\nfunction relativeToPosix(from: string, to: string): string {\n return relative(from, to).split(\"\\\\\").join(\"/\");\n}\n\n/**\n * Write a generated SSR entry file for an adapter.\n */\nexport async function writeSsrEntry(\n entryPath: string,\n routes: Awaited<ReturnType<typeof scanRoutes>>,\n options: AdapterOptions,\n): Promise<void> {\n await writeFile(\n entryPath,\n await buildSsrEntry(routes, options, dirname(entryPath)),\n \"utf8\",\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"shared-DU3iQgr7.js","names":[],"sources":["../../src/adapters/shared.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { mkdir, readdir, copyFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, relative } from \"node:path\";\nimport { scanRoutes } from \"../router/route-scanner.js\";\nimport type { AdapterOptions } from \"./index.js\";\nimport type { PageRoute } from \"../router/route-scanner.js\";\n\n/**\n * Shared helper: copy a directory recursively.\n */\nexport async function copyStatic(from: string, to: string): Promise<void> {\n await mkdir(to, { recursive: true });\n const entries = await readdir(from, { withFileTypes: true });\n for (const entry of entries) {\n const src = join(from, entry.name);\n const dest = join(to, entry.name);\n if (entry.isDirectory()) {\n await copyStatic(src, dest);\n } else {\n await copyFile(src, dest);\n }\n }\n}\n\n/** Adds every module the SSR runtime may import for a page to the registry. */\nfunction collectPageModules(\n page: PageRoute,\n moduleSet: Set<string>,\n actionPathsByPage: Map<string, Set<string>>,\n): void {\n moduleSet.add(page.pagePath);\n if (page.dataPath) moduleSet.add(page.dataPath);\n if (page.loadingPath) moduleSet.add(page.loadingPath);\n for (const layout of page.layouts) {\n moduleSet.add(layout);\n const layoutDataPath = layout.replace(/layout\\.ts$/, \"layout.data.ts\");\n if (layoutDataPath !== layout && existsSync(layoutDataPath)) {\n moduleSet.add(layoutDataPath);\n }\n }\n if (page.actionPath) {\n moduleSet.add(page.actionPath);\n let set = actionPathsByPage.get(page.path);\n if (!set) {\n set = new Set<string>();\n actionPathsByPage.set(page.path, set);\n }\n set.add(page.actionPath);\n }\n}\n\n/**\n * Build a self-contained SSR entry file for a platform adapter.\n * The generated module exports a default `handler(request: Request): Response`\n * and embeds the full route table plus a registry of all page/layout/data/\n * action modules so the runtime never touches the file system.\n */\nexport async function buildSsrEntry(\n routes: Awaited<ReturnType<typeof scanRoutes>>,\n options: AdapterOptions,\n entryDir: string,\n): Promise<string> {\n // Collect all module paths that the SSR runtime may need to import.\n const moduleSet = new Set<string>();\n const actionPathsByPage = new Map<string, Set<string>>();\n for (const page of routes.pages) {\n collectPageModules(page, moduleSet, actionPathsByPage);\n }\n if (routes.error404) collectPageModules(routes.error404, moduleSet, actionPathsByPage);\n if (routes.error500) collectPageModules(routes.error500, moduleSet, actionPathsByPage);\n for (const api of routes.api) {\n moduleSet.add(api.routePath);\n }\n const modules = Array.from(moduleSet);\n const moduleIndex = new Map(modules.map((path, index) => [path, index]));\n\n const imports = modules\n .map((path, index) => {\n const rel = relativeToPosix(entryDir, path);\n return `import * as m_${index} from ${JSON.stringify(rel)};`;\n })\n .join(\"\\n\");\n\n const renderPageRecord = (page: PageRoute): string => `{\n path: ${JSON.stringify(page.path)},\n pagePath: ${JSON.stringify(page.pagePath)},\n dataPath: ${JSON.stringify(page.dataPath ?? null)},\n actionPath: ${JSON.stringify(page.actionPath ?? null)},\n loadingPath: ${JSON.stringify(page.loadingPath ?? null)},\n layouts: ${JSON.stringify(page.layouts)},\n params: ${JSON.stringify(page.params)},\n }`;\n\n const pages = routes.pages.map(renderPageRecord).join(\",\\n\");\n\n const apiRoutes = routes.api\n .map((api) => {\n const index = moduleIndex.get(api.routePath);\n return ` { path: ${JSON.stringify(api.path)}, routePath: m_${index} },`;\n })\n .join(\"\\n\");\n\n const actionModules = Array.from(actionPathsByPage.entries())\n .map(([pagePath, paths]) => {\n const entries = Array.from(paths)\n .map((path) => {\n const index = moduleIndex.get(path);\n return ` [${JSON.stringify(path)}, m_${index}],`;\n })\n .join(\"\\n\");\n return ` [${JSON.stringify(pagePath)}, new Map([\\n${entries}\\n ])],`;\n })\n .join(\"\\n\");\n\n const actionsRegistry: Record<string, string[]> = {};\n for (const page of routes.pages) {\n if (!page.actionPath) continue;\n const mod = (await import(page.actionPath)) as Record<string, unknown>;\n const names: string[] = [];\n for (const [name, value] of Object.entries(mod)) {\n if (name === \"default\") continue;\n if (typeof value === \"function\") {\n names.push(name);\n }\n }\n if (names.length > 0) {\n actionsRegistry[page.path] = names;\n }\n }\n\n return `// AUTO-GENERATED by @elurjs/kit. Do not edit.\nimport { handleActionRequest, matchApiRoute, matchRoute, renderPage, renderErrorPage } from \"@elurjs/kit\";\n${imports}\n\nconst registry = new Map<string, unknown>([\n${modules.map((path, index) => ` [${JSON.stringify(path)}, m_${index}],`).join(\"\\n\")}\n]);\n\nconst pages = [\n${pages},\n];\n\nconst apiRoutes = [\n${apiRoutes}\n];\n\nconst actionModules = new Map<string, Map<string, unknown>>([\n${actionModules}\n]);\n\nconst actions = ${JSON.stringify(actionsRegistry)};\n\nconst routes = {\n pages,\n api: apiRoutes,\n error404: ${routes.error404 ? renderPageRecord(routes.error404) : \"undefined\"},\n error500: ${routes.error500 ? renderPageRecord(routes.error500) : \"undefined\"},\n};\n\nconst clientEntry = ${JSON.stringify(options.clientEntry)};\nconst lang = ${JSON.stringify(options.lang)};\n\nfunction loadModule(path: string) {\n const mod = registry.get(path);\n if (mod) return mod;\n throw new Error(\\`Module not found in registry: \\${path}\\`);\n}\n\nasync function resolveAction(name: string, page?: string) {\n // Match concrete page paths (e.g. /movies/inception) to their route pattern\n // (/movies/:slug) so actions on dynamic routes resolve by scope.\n let pageKey: string | undefined;\n if (page) {\n pageKey = routes.pages.some((route) => route.path === page)\n ? page\n : (matchRoute(page, routes.pages)?.route.path ?? page);\n }\n const pageModules = pageKey ? actionModules.get(pageKey) : undefined;\n const candidates = pageModules ? [...pageModules.values()] : [];\n if (!pageModules) {\n for (const mods of actionModules.values()) {\n for (const mod of mods.values()) {\n const action = (mod as Record<string, unknown>)[name];\n if (typeof action === \"function\") return action;\n }\n }\n }\n for (const mod of candidates) {\n const action = (mod as Record<string, unknown>)[name];\n if (typeof action === \"function\") {\n return action as (...args: unknown[]) => unknown;\n }\n }\n return undefined;\n}\n\nexport default async function handler(request: Request): Promise<Response> {\n const url = new URL(request.url);\n\n if (url.pathname === \"/__elur-js/actions\") {\n return handleActionRequest(request, resolveAction);\n }\n\n // Render endpoint used by the SPA router and streaming boundaries.\n if (url.pathname === \"/__elur-js/render\") {\n const page = url.searchParams.get(\"page\") ?? \"/\";\n const search = url.searchParams.get(\"search\") ?? \"\";\n const wantsJson = (request.headers.get(\"Accept\") ?? \"\").includes(\"application/json\");\n try {\n const match = matchRoute(page, routes.pages);\n if (!match) throw new Error(\\`No route found for \\${page}\\`);\n const { html } = await renderPage({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(search),\n config: { lang, clientEntry },\n importer: loadModule,\n actions,\n request,\n });\n const bodyStart = html.indexOf('<div id=\"app\">');\n const bodyEnd = html.lastIndexOf(\"</div>\");\n const body = bodyStart >= 0 && bodyEnd > bodyStart\n ? html.slice(bodyStart + 13, bodyEnd).trim()\n : html;\n const titleStart = html.indexOf(\"<title>\");\n const titleEnd = html.indexOf(\"</title>\");\n const title = titleStart >= 0 && titleEnd > titleStart\n ? html.slice(titleStart + 7, titleEnd)\n : \"\";\n if (wantsJson) {\n return new Response(JSON.stringify({ title, body }), {\n status: 200,\n headers: { \"Content-Type\": \"application/json; charset=utf-8\" },\n });\n }\n return new Response(body, {\n status: 200,\n headers: { \"Content-Type\": \"text/html; charset=utf-8\" },\n });\n } catch (err) {\n if ((err as { name?: string }).name === \"RouteNotFoundError\") {\n return new Response(\"Not Found\", {\n status: 404,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n console.error(\"[elur-kit] render endpoint error:\", err);\n return new Response(\"Internal Server Error\", {\n status: 500,\n headers: { \"Content-Type\": \"text/plain\" },\n });\n }\n }\n\n const apiMatch = matchApiRoute(url.pathname, apiRoutes);\n if (apiMatch) {\n const mod = apiMatch.route.routePath as Record<\n string,\n (request: Request, context?: { params: Record<string, string | string[]> }) => unknown\n >;\n const handler = mod[request.method ?? \"GET\"];\n if (typeof handler !== \"function\") {\n return new Response(\"Method not allowed: \" + request.method, { status: 405, headers: { \"Content-Type\": \"text/plain\" } });\n }\n return (await handler(request, { params: apiMatch.params })) as Response;\n }\n\n const match = matchRoute(url.pathname, routes.pages);\n if (!match) {\n const errorResult = await renderErrorPage({ routes, status: 404, config: { lang, clientEntry }, actions, importer: loadModule });\n if (errorResult) {\n return new Response(errorResult.html, { status: errorResult.status, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n }\n return new Response(\"Not Found\", { status: 404, headers: { \"Content-Type\": \"text/plain\" } });\n }\n\n try {\n const { html } = await renderPage({\n route: match.route,\n params: match.params,\n searchParams: new URLSearchParams(url.search),\n config: { lang, clientEntry },\n importer: loadModule,\n actions,\n request,\n });\n return new Response(html, { status: 200, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n } catch (err) {\n console.error(\"[elur-kit] SSR render error:\", err);\n const errorResult = await renderErrorPage({ routes, status: 500, error: err, config: { lang, clientEntry }, actions, importer: loadModule });\n if (errorResult) {\n return new Response(errorResult.html, { status: errorResult.status, headers: { \"Content-Type\": \"text/html; charset=utf-8\" } });\n }\n return new Response(\"Internal Server Error\", { status: 500, headers: { \"Content-Type\": \"text/plain; charset=utf-8\" } });\n }\n}\n`;\n}\n\nfunction relativeToPosix(from: string, to: string): string {\n return relative(from, to).split(\"\\\\\").join(\"/\");\n}\n\n/**\n * Write a generated SSR entry file for an adapter.\n */\nexport async function writeSsrEntry(\n entryPath: string,\n routes: Awaited<ReturnType<typeof scanRoutes>>,\n options: AdapterOptions,\n): Promise<void> {\n await writeFile(\n entryPath,\n await buildSsrEntry(routes, options, dirname(entryPath)),\n \"utf8\",\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { u as e } from "./errors-ePYxDlhU.js";
|
|
2
|
-
import { _ as t, l as n, o as r, v as i } from "./node-http-DRAUhO0c.js";
|
|
3
|
-
//#region src/ssr/stream.ts
|
|
4
|
-
var a = /* @__PURE__ */ e({
|
|
5
|
-
RouteNotFoundError: () => u,
|
|
6
|
-
renderPageBody: () => d,
|
|
7
|
-
renderStreamingPage: () => l
|
|
8
|
-
}), o = (e) => import(e);
|
|
9
|
-
function s(e, t) {
|
|
10
|
-
return e.replace(/:([a-zA-Z0-9_]+)(\*)?/g, (e, n, r) => {
|
|
11
|
-
let i = t[n];
|
|
12
|
-
return i == null ? "" : r && Array.isArray(i) ? i.join("/") : String(i);
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
function c(e, t) {
|
|
16
|
-
return `<script type="module">${`
|
|
17
|
-
async function __elurJsStreamRender() {
|
|
18
|
-
try {
|
|
19
|
-
const url = "/__elur-js/render?page=" + encodeURIComponent(${JSON.stringify(e)}) + "&search=" + encodeURIComponent(${JSON.stringify(t)});
|
|
20
|
-
const res = await fetch(url);
|
|
21
|
-
if (!res.ok) throw new Error("Streaming render failed: " + res.status);
|
|
22
|
-
const html = await res.text();
|
|
23
|
-
const app = document.getElementById("app");
|
|
24
|
-
if (app) app.innerHTML = html;
|
|
25
|
-
document.dispatchEvent(new CustomEvent("elur:rendered"));
|
|
26
|
-
} catch (err) {
|
|
27
|
-
console.error("[elur-kit] streaming render failed", err);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
__elurJsStreamRender();
|
|
31
|
-
`}<\/script>`;
|
|
32
|
-
}
|
|
33
|
-
async function l(e) {
|
|
34
|
-
let { route: n, params: r, searchParams: a, config: l, importer: u = o, actions: d } = e;
|
|
35
|
-
if (!n.loadingPath) throw Error("Cannot stream a page without a loading.ts boundary");
|
|
36
|
-
let { default: f } = await u(n.loadingPath), p = `<div id="elur-loading">${await i(() => f())}</div>${c(s(n.path, r), a.toString())}`, m = {}, h = [], g = [];
|
|
37
|
-
if (n.layouts.length > 0) {
|
|
38
|
-
let t = n.layouts[0], i = t.replace(/layout\.ts$/, "layout.data.ts");
|
|
39
|
-
if (i !== t) try {
|
|
40
|
-
let t = await u(i), n = t.load ? await t.load({
|
|
41
|
-
params: r,
|
|
42
|
-
searchParams: a,
|
|
43
|
-
request: e.request
|
|
44
|
-
}) : void 0;
|
|
45
|
-
if (n && typeof n == "object") {
|
|
46
|
-
let e = n.htmlAttributes;
|
|
47
|
-
e && Object.assign(m, e);
|
|
48
|
-
let t = n.headScripts;
|
|
49
|
-
Array.isArray(t) && h.push(...t);
|
|
50
|
-
let r = n.headLinks;
|
|
51
|
-
Array.isArray(r) && g.push(...r);
|
|
52
|
-
}
|
|
53
|
-
} catch {}
|
|
54
|
-
}
|
|
55
|
-
return t({
|
|
56
|
-
title: "Loading...",
|
|
57
|
-
lang: l.lang,
|
|
58
|
-
body: p,
|
|
59
|
-
data: {
|
|
60
|
-
__elur_js_streaming: !0,
|
|
61
|
-
page: n.path
|
|
62
|
-
},
|
|
63
|
-
actions: d,
|
|
64
|
-
htmlAttributes: m,
|
|
65
|
-
headScripts: h,
|
|
66
|
-
headLinks: g,
|
|
67
|
-
clientEntry: l.clientEntry
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
var u = class extends Error {
|
|
71
|
-
constructor(e) {
|
|
72
|
-
super(`No route found for ${e}`), this.name = "RouteNotFoundError";
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
async function d(e) {
|
|
76
|
-
let { routes: t, pathname: i, searchParams: a, config: s, actions: c, importer: l = o, request: d } = e, f = r(i, t.pages);
|
|
77
|
-
if (!f) throw new u(i);
|
|
78
|
-
let p = await n({
|
|
79
|
-
route: f.route,
|
|
80
|
-
params: f.params,
|
|
81
|
-
searchParams: a,
|
|
82
|
-
config: s,
|
|
83
|
-
actions: c,
|
|
84
|
-
importer: l,
|
|
85
|
-
request: d
|
|
86
|
-
});
|
|
87
|
-
if (p.response) return {
|
|
88
|
-
body: "",
|
|
89
|
-
title: "",
|
|
90
|
-
response: p.response
|
|
91
|
-
};
|
|
92
|
-
let m = p.html.match(/<div id="app">([\s\S]*)<\/div>\s*(<script|$)/), h = m ? m[1].trim() : p.html, g = p.html.match(/<title[^>]*>([^<]*)<\/title>/);
|
|
93
|
-
return {
|
|
94
|
-
body: h,
|
|
95
|
-
title: g ? g[1] : p.resolvedTitle ?? "",
|
|
96
|
-
fullHtml: p.html,
|
|
97
|
-
clearActionErrorCookie: p.clearActionErrorCookie,
|
|
98
|
-
head: p.head
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
//#endregion
|
|
102
|
-
export { a as i, d as n, l as r, u as t };
|
|
103
|
-
|
|
104
|
-
//# sourceMappingURL=stream-CpOG052V.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stream-CpOG052V.js","names":[],"sources":["../../src/ssr/stream.ts"],"sourcesContent":["import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString } from \"../render/render-to-string.js\";\nimport { documentShell } from \"../build/document-shell.js\";\nimport type { PageRoute, ScannedRoutes } from \"../router/route-scanner.js\";\nimport type { BuildConfig } from \"../build/build.js\";\nimport type { PageDataLoad } from \"../types.js\";\nimport { matchRoute } from \"./match.js\";\nimport { renderPage } from \"./render.js\";\n\nexport interface StreamingPageOptions {\n route: PageRoute;\n params: Record<string, string | string[]>;\n searchParams: URLSearchParams;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\">;\n importer?: (path: string) => Promise<unknown>;\n actions?: Record<string, string[]>;\n request?: Request;\n}\n\nconst defaultImport = (path: string) => import(path);\n\n/** Builds the concrete URL path for a route pattern given matched params. */\nfunction buildConcretePath(\n routePath: string,\n params: Record<string, string | string[]>,\n): string {\n return routePath.replace(/:([a-zA-Z0-9_]+)(\\*)?/g, (_m, name: string, catchAll?: string) => {\n const value = params[name];\n if (value === undefined || value === null) return \"\";\n return catchAll ? (Array.isArray(value) ? value.join(\"/\") : String(value)) : String(value);\n });\n}\n\nfunction streamingScript(page: string, search: string): string {\n const src = `\n async function __elurJsStreamRender() {\n try {\n const url = \"/__elur-js/render?page=\" + encodeURIComponent(${JSON.stringify(page)}) + \"&search=\" + encodeURIComponent(${JSON.stringify(search)});\n const res = await fetch(url);\n if (!res.ok) throw new Error(\"Streaming render failed: \" + res.status);\n const html = await res.text();\n const app = document.getElementById(\"app\");\n if (app) app.innerHTML = html;\n document.dispatchEvent(new CustomEvent(\"elur:rendered\"));\n } catch (err) {\n console.error(\"[elur-kit] streaming render failed\", err);\n }\n }\n __elurJsStreamRender();\n `;\n return `<script type=\"module\">${src}</script>`;\n}\n\n/**\n * Render a page shell that shows the loading boundary while the real content\n * is fetched and injected by the client.\n */\nexport async function renderStreamingPage(options: StreamingPageOptions): Promise<string> {\n const { route, params, searchParams, config, importer = defaultImport, actions } = options;\n if (!route.loadingPath) {\n throw new Error(\"Cannot stream a page without a loading.ts boundary\");\n }\n\n const { default: Loading } = (await importer(route.loadingPath)) as {\n default: () => ElurTemplate;\n };\n\n const loadingBody = await renderToString(() => Loading());\n const concretePath = buildConcretePath(route.path, params);\n const body = `<div id=\"elur-loading\">${loadingBody}</div>${streamingScript(concretePath, searchParams.toString())}`;\n\n // Apply <html> attributes and head scripts (e.g. data-theme and the no-flash\n // theme script) from the root layout loader so the shell paints correctly\n // before the real content arrives.\n const htmlAttributes: Record<string, string> = {};\n const headScripts: string[] = [];\n const headLinks: string[] = [];\n if (route.layouts.length > 0) {\n const rootLayout = route.layouts[0];\n const dataPath = rootLayout.replace(/layout\\.ts$/, \"layout.data.ts\");\n if (dataPath !== rootLayout) {\n try {\n const mod = (await importer(dataPath)) as { load?: PageDataLoad };\n const layoutData = mod.load ? await mod.load({ params, searchParams, request: options.request }) : undefined;\n if (layoutData && typeof layoutData === \"object\") {\n const attrs = (layoutData as { htmlAttributes?: Record<string, string> }).htmlAttributes;\n if (attrs) Object.assign(htmlAttributes, attrs);\n const scripts = (layoutData as { headScripts?: string[] }).headScripts;\n if (Array.isArray(scripts)) headScripts.push(...scripts);\n const links = (layoutData as { headLinks?: string[] }).headLinks;\n if (Array.isArray(links)) headLinks.push(...links);\n }\n } catch {\n // The root layout loader is optional; ignore failures here.\n }\n }\n }\n\n return documentShell({\n title: \"Loading...\",\n lang: config.lang,\n body,\n data: { __elur_js_streaming: true, page: route.path },\n actions,\n htmlAttributes,\n headScripts,\n headLinks,\n clientEntry: config.clientEntry,\n });\n}\n\nexport interface RenderPageBodyOptions {\n routes: ScannedRoutes;\n pathname: string;\n searchParams: URLSearchParams;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\">;\n actions?: Record<string, string[]>;\n importer?: (path: string) => Promise<unknown>;\n request?: Request;\n}\n\nexport interface RenderPageBodyResult {\n /** Inner HTML body for the page (without the document shell). */\n body: string;\n /** Page title extracted from the rendered shell. */\n title: string;\n /** Full rendered document shell (used for ISR caching). */\n fullHtml?: string;\n /** `Set-Cookie` value that clears a consumed action error cookie. */\n clearActionErrorCookie?: string;\n /** `<head>` tags (title, meta, OG, twitter) for the SPA router to merge. */\n head?: string;\n /** First-class Response when a loader threw one (A-22). */\n response?: Response;\n}\n\n/** Thrown by `renderPageBody` when the requested path has no matching route. */\nexport class RouteNotFoundError extends Error {\n constructor(pathname: string) {\n super(`No route found for ${pathname}`);\n this.name = \"RouteNotFoundError\";\n }\n}\n\n/**\n * Render only the inner HTML body for a page. Used by the streaming endpoint\n * to inject the real content into the shell.\n */\nexport async function renderPageBody(options: RenderPageBodyOptions): Promise<RenderPageBodyResult> {\n const { routes, pathname, searchParams, config, actions, importer = defaultImport, request } = options;\n const match = matchRoute(pathname, routes.pages);\n if (!match) {\n throw new RouteNotFoundError(pathname);\n }\n\n const result = await renderPage({\n route: match.route,\n params: match.params,\n searchParams,\n config,\n actions,\n importer,\n request,\n });\n\n // If a loader threw a Response (redirect, 404, etc.), propagate it (A-22).\n if (result.response) {\n return {\n body: \"\",\n title: \"\",\n response: result.response,\n };\n }\n\n const bodyMatch = result.html.match(/<div id=\"app\">([\\s\\S]*)<\\/div>\\s*(<script|$)/);\n const body = bodyMatch ? bodyMatch[1].trim() : result.html;\n const titleMatch = result.html.match(/<title[^>]*>([^<]*)<\\/title>/);\n return {\n body,\n title: titleMatch ? titleMatch[1] : result.resolvedTitle ?? \"\",\n fullHtml: result.html,\n clearActionErrorCookie: result.clearActionErrorCookie,\n head: result.head,\n };\n}\n"],"mappings":";;;;;;;IAmBM,KAAiB,MAAiB,OAAO;AAG/C,SAAS,EACP,GACA,GACQ;CACR,OAAO,EAAU,QAAQ,2BAA2B,GAAI,GAAc,MAAsB;EAC1F,IAAM,IAAQ,EAAO;EAErB,OADI,KAAiC,OAAa,KAC3C,KAAY,MAAM,QAAQ,CAAK,IAAI,EAAM,KAAK,GAAG,IAAI,OAAO,CAAK;CAC1E,CAAC;AACH;AAEA,SAAS,EAAgB,GAAc,GAAwB;CAiB7D,OAAO,yBAAyB;;;qEAbmC,KAAK,UAAU,CAAI,EAAE,sCAAsC,KAAK,UAAU,CAAM,EAAE;;;;;;;;;;;;IAajH;AACtC;AAMA,eAAsB,EAAoB,GAAgD;CACxF,IAAM,EAAE,UAAO,WAAQ,iBAAc,WAAQ,cAAW,GAAe,eAAY;CACnF,IAAI,CAAC,EAAM,aACT,MAAU,MAAM,oDAAoD;CAGtE,IAAM,EAAE,SAAS,MAAa,MAAM,EAAS,EAAM,WAAW,GAMxD,IAAO,0BAA0B,MAFb,QAAqB,EAAQ,CAAC,EAEL,QAAQ,EADtC,EAAkB,EAAM,MAAM,CACwB,GAAc,EAAa,SAAS,CAAC,KAK1G,IAAyC,CAAC,GAC1C,IAAwB,CAAC,GACzB,IAAsB,CAAC;CAC7B,IAAI,EAAM,QAAQ,SAAS,GAAG;EAC5B,IAAM,IAAa,EAAM,QAAQ,IAC3B,IAAW,EAAW,QAAQ,eAAe,gBAAgB;EACnE,IAAI,MAAa,GACf,IAAI;GACF,IAAM,IAAO,MAAM,EAAS,CAAQ,GAC9B,IAAa,EAAI,OAAO,MAAM,EAAI,KAAK;IAAE;IAAQ;IAAc,SAAS,EAAQ;GAAQ,CAAC,IAAI,KAAA;GACnG,IAAI,KAAc,OAAO,KAAe,UAAU;IAChD,IAAM,IAAS,EAA2D;IAC1E,AAAI,KAAO,OAAO,OAAO,GAAgB,CAAK;IAC9C,IAAM,IAAW,EAA0C;IAC3D,AAAI,MAAM,QAAQ,CAAO,KAAG,EAAY,KAAK,GAAG,CAAO;IACvD,IAAM,IAAS,EAAwC;IACvD,AAAI,MAAM,QAAQ,CAAK,KAAG,EAAU,KAAK,GAAG,CAAK;GACnD;EACF,QAAQ,CAER;CAEJ;CAEA,OAAO,EAAc;EACnB,OAAO;EACP,MAAM,EAAO;EACb;EACA,MAAM;GAAE,qBAAqB;GAAM,MAAM,EAAM;EAAK;EACpD;EACA;EACA;EACA;EACA,aAAa,EAAO;CACtB,CAAC;AACH;AA4BA,IAAa,IAAb,cAAwC,MAAM;CAC5C,YAAY,GAAkB;EAE5B,AADA,MAAM,sBAAsB,GAAU,GACtC,KAAK,OAAO;CACd;AACF;AAMA,eAAsB,EAAe,GAA+D;CAClG,IAAM,EAAE,WAAQ,aAAU,iBAAc,WAAQ,YAAS,cAAW,GAAe,eAAY,GACzF,IAAQ,EAAW,GAAU,EAAO,KAAK;CAC/C,IAAI,CAAC,GACH,MAAM,IAAI,EAAmB,CAAQ;CAGvC,IAAM,IAAS,MAAM,EAAW;EAC9B,OAAO,EAAM;EACb,QAAQ,EAAM;EACd;EACA;EACA;EACA;EACA;CACF,CAAC;CAGD,IAAI,EAAO,UACT,OAAO;EACL,MAAM;EACN,OAAO;EACP,UAAU,EAAO;CACnB;CAGF,IAAM,IAAY,EAAO,KAAK,MAAM,8CAA8C,GAC5E,IAAO,IAAY,EAAU,EAAE,CAAC,KAAK,IAAI,EAAO,MAChD,IAAa,EAAO,KAAK,MAAM,8BAA8B;CACnE,OAAO;EACL;EACA,OAAO,IAAa,EAAW,KAAK,EAAO,iBAAiB;EAC5D,UAAU,EAAO;EACjB,wBAAwB,EAAO;EAC/B,MAAM,EAAO;CACf;AACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stream-DmZjYqHm.cjs","names":[],"sources":["../../src/ssr/stream.ts"],"sourcesContent":["import type { ElurTemplate } from \"@elurjs/core\";\nimport { renderToString } from \"../render/render-to-string.js\";\nimport { documentShell } from \"../build/document-shell.js\";\nimport type { PageRoute, ScannedRoutes } from \"../router/route-scanner.js\";\nimport type { BuildConfig } from \"../build/build.js\";\nimport type { PageDataLoad } from \"../types.js\";\nimport { matchRoute } from \"./match.js\";\nimport { renderPage } from \"./render.js\";\n\nexport interface StreamingPageOptions {\n route: PageRoute;\n params: Record<string, string | string[]>;\n searchParams: URLSearchParams;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\">;\n importer?: (path: string) => Promise<unknown>;\n actions?: Record<string, string[]>;\n request?: Request;\n}\n\nconst defaultImport = (path: string) => import(path);\n\n/** Builds the concrete URL path for a route pattern given matched params. */\nfunction buildConcretePath(\n routePath: string,\n params: Record<string, string | string[]>,\n): string {\n return routePath.replace(/:([a-zA-Z0-9_]+)(\\*)?/g, (_m, name: string, catchAll?: string) => {\n const value = params[name];\n if (value === undefined || value === null) return \"\";\n return catchAll ? (Array.isArray(value) ? value.join(\"/\") : String(value)) : String(value);\n });\n}\n\nfunction streamingScript(page: string, search: string): string {\n const src = `\n async function __elurJsStreamRender() {\n try {\n const url = \"/__elur-js/render?page=\" + encodeURIComponent(${JSON.stringify(page)}) + \"&search=\" + encodeURIComponent(${JSON.stringify(search)});\n const res = await fetch(url);\n if (!res.ok) throw new Error(\"Streaming render failed: \" + res.status);\n const html = await res.text();\n const app = document.getElementById(\"app\");\n if (app) app.innerHTML = html;\n document.dispatchEvent(new CustomEvent(\"elur:rendered\"));\n } catch (err) {\n console.error(\"[elur-kit] streaming render failed\", err);\n }\n }\n __elurJsStreamRender();\n `;\n return `<script type=\"module\">${src}</script>`;\n}\n\n/**\n * Render a page shell that shows the loading boundary while the real content\n * is fetched and injected by the client.\n */\nexport async function renderStreamingPage(options: StreamingPageOptions): Promise<string> {\n const { route, params, searchParams, config, importer = defaultImport, actions } = options;\n if (!route.loadingPath) {\n throw new Error(\"Cannot stream a page without a loading.ts boundary\");\n }\n\n const { default: Loading } = (await importer(route.loadingPath)) as {\n default: () => ElurTemplate;\n };\n\n const loadingBody = await renderToString(() => Loading());\n const concretePath = buildConcretePath(route.path, params);\n const body = `<div id=\"elur-loading\">${loadingBody}</div>${streamingScript(concretePath, searchParams.toString())}`;\n\n // Apply <html> attributes and head scripts (e.g. data-theme and the no-flash\n // theme script) from the root layout loader so the shell paints correctly\n // before the real content arrives.\n const htmlAttributes: Record<string, string> = {};\n const headScripts: string[] = [];\n const headLinks: string[] = [];\n if (route.layouts.length > 0) {\n const rootLayout = route.layouts[0];\n const dataPath = rootLayout.replace(/layout\\.ts$/, \"layout.data.ts\");\n if (dataPath !== rootLayout) {\n try {\n const mod = (await importer(dataPath)) as { load?: PageDataLoad };\n const layoutData = mod.load ? await mod.load({ params, searchParams, request: options.request }) : undefined;\n if (layoutData && typeof layoutData === \"object\") {\n const attrs = (layoutData as { htmlAttributes?: Record<string, string> }).htmlAttributes;\n if (attrs) Object.assign(htmlAttributes, attrs);\n const scripts = (layoutData as { headScripts?: string[] }).headScripts;\n if (Array.isArray(scripts)) headScripts.push(...scripts);\n const links = (layoutData as { headLinks?: string[] }).headLinks;\n if (Array.isArray(links)) headLinks.push(...links);\n }\n } catch {\n // The root layout loader is optional; ignore failures here.\n }\n }\n }\n\n return documentShell({\n title: \"Loading...\",\n lang: config.lang,\n body,\n data: { __elur_js_streaming: true, page: route.path },\n actions,\n htmlAttributes,\n headScripts,\n headLinks,\n clientEntry: config.clientEntry,\n });\n}\n\nexport interface RenderPageBodyOptions {\n routes: ScannedRoutes;\n pathname: string;\n searchParams: URLSearchParams;\n config: Pick<BuildConfig, \"lang\" | \"clientEntry\">;\n actions?: Record<string, string[]>;\n importer?: (path: string) => Promise<unknown>;\n request?: Request;\n}\n\nexport interface RenderPageBodyResult {\n /** Inner HTML body for the page (without the document shell). */\n body: string;\n /** Page title extracted from the rendered shell. */\n title: string;\n /** Full rendered document shell (used for ISR caching). */\n fullHtml?: string;\n /** `Set-Cookie` value that clears a consumed action error cookie. */\n clearActionErrorCookie?: string;\n /** `<head>` tags (title, meta, OG, twitter) for the SPA router to merge. */\n head?: string;\n /** First-class Response when a loader threw one (A-22). */\n response?: Response;\n}\n\n/** Thrown by `renderPageBody` when the requested path has no matching route. */\nexport class RouteNotFoundError extends Error {\n constructor(pathname: string) {\n super(`No route found for ${pathname}`);\n this.name = \"RouteNotFoundError\";\n }\n}\n\n/**\n * Render only the inner HTML body for a page. Used by the streaming endpoint\n * to inject the real content into the shell.\n */\nexport async function renderPageBody(options: RenderPageBodyOptions): Promise<RenderPageBodyResult> {\n const { routes, pathname, searchParams, config, actions, importer = defaultImport, request } = options;\n const match = matchRoute(pathname, routes.pages);\n if (!match) {\n throw new RouteNotFoundError(pathname);\n }\n\n const result = await renderPage({\n route: match.route,\n params: match.params,\n searchParams,\n config,\n actions,\n importer,\n request,\n });\n\n // If a loader threw a Response (redirect, 404, etc.), propagate it (A-22).\n if (result.response) {\n return {\n body: \"\",\n title: \"\",\n response: result.response,\n };\n }\n\n const bodyMatch = result.html.match(/<div id=\"app\">([\\s\\S]*)<\\/div>\\s*(<script|$)/);\n const body = bodyMatch ? bodyMatch[1].trim() : result.html;\n const titleMatch = result.html.match(/<title[^>]*>([^<]*)<\\/title>/);\n return {\n body,\n title: titleMatch ? titleMatch[1] : result.resolvedTitle ?? \"\",\n fullHtml: result.html,\n clearActionErrorCookie: result.clearActionErrorCookie,\n head: result.head,\n };\n}\n"],"mappings":"oKAmBM,EAAiB,GAAiB,OAAO,GAG/C,SAAS,EACP,EACA,EACQ,CACR,OAAO,EAAU,QAAQ,0BAA2B,EAAI,EAAc,IAAsB,CAC1F,IAAM,EAAQ,EAAO,GAErB,OADI,GAAiC,KAAa,GAC3C,GAAY,MAAM,QAAQ,CAAK,EAAI,EAAM,KAAK,GAAG,EAAI,OAAO,CAAK,CAC1E,CAAC,CACH,CAEA,SAAS,EAAgB,EAAc,EAAwB,CAiB7D,MAAO,yBAAyB;;;qEAbmC,KAAK,UAAU,CAAI,EAAE,sCAAsC,KAAK,UAAU,CAAM,EAAE;;;;;;;;;;;;IAajH,WACtC,CAMA,eAAsB,EAAoB,EAAgD,CACxF,GAAM,CAAE,QAAO,SAAQ,eAAc,SAAQ,WAAW,EAAe,WAAY,EACnF,GAAI,CAAC,EAAM,YACT,MAAU,MAAM,oDAAoD,EAGtE,GAAM,CAAE,QAAS,GAAa,MAAM,EAAS,EAAM,WAAW,EAMxD,EAAO,0BAA0B,MAFb,EAAA,MAAqB,EAAQ,CAAC,EAEL,QAAQ,EADtC,EAAkB,EAAM,KAAM,CACwB,EAAc,EAAa,SAAS,CAAC,IAK1G,EAAyC,CAAC,EAC1C,EAAwB,CAAC,EACzB,EAAsB,CAAC,EAC7B,GAAI,EAAM,QAAQ,OAAS,EAAG,CAC5B,IAAM,EAAa,EAAM,QAAQ,GAC3B,EAAW,EAAW,QAAQ,cAAe,gBAAgB,EACnE,GAAI,IAAa,EACf,GAAI,CACF,IAAM,EAAO,MAAM,EAAS,CAAQ,EAC9B,EAAa,EAAI,KAAO,MAAM,EAAI,KAAK,CAAE,SAAQ,eAAc,QAAS,EAAQ,OAAQ,CAAC,EAAI,IAAA,GACnG,GAAI,GAAc,OAAO,GAAe,SAAU,CAChD,IAAM,EAAS,EAA2D,eACtE,GAAO,OAAO,OAAO,EAAgB,CAAK,EAC9C,IAAM,EAAW,EAA0C,YACvD,MAAM,QAAQ,CAAO,GAAG,EAAY,KAAK,GAAG,CAAO,EACvD,IAAM,EAAS,EAAwC,UACnD,MAAM,QAAQ,CAAK,GAAG,EAAU,KAAK,GAAG,CAAK,CACnD,CACF,MAAQ,CAER,CAEJ,CAEA,OAAO,EAAA,EAAc,CACnB,MAAO,aACP,KAAM,EAAO,KACb,OACA,KAAM,CAAE,oBAAqB,GAAM,KAAM,EAAM,IAAK,EACpD,UACA,iBACA,cACA,YACA,YAAa,EAAO,WACtB,CAAC,CACH,CA4BA,IAAa,EAAb,cAAwC,KAAM,CAC5C,YAAY,EAAkB,CAC5B,MAAM,sBAAsB,GAAU,EACtC,KAAK,KAAO,oBACd,CACF,EAMA,eAAsB,EAAe,EAA+D,CAClG,GAAM,CAAE,SAAQ,WAAU,eAAc,SAAQ,UAAS,WAAW,EAAe,WAAY,EACzF,EAAQ,EAAA,EAAW,EAAU,EAAO,KAAK,EAC/C,GAAI,CAAC,EACH,MAAM,IAAI,EAAmB,CAAQ,EAGvC,IAAM,EAAS,MAAM,EAAA,EAAW,CAC9B,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,eACA,SACA,UACA,WACA,SACF,CAAC,EAGD,GAAI,EAAO,SACT,MAAO,CACL,KAAM,GACN,MAAO,GACP,SAAU,EAAO,QACnB,EAGF,IAAM,EAAY,EAAO,KAAK,MAAM,8CAA8C,EAC5E,EAAO,EAAY,EAAU,EAAE,CAAC,KAAK,EAAI,EAAO,KAChD,EAAa,EAAO,KAAK,MAAM,8BAA8B,EACnE,MAAO,CACL,OACA,MAAO,EAAa,EAAW,GAAK,EAAO,eAAiB,GAC5D,SAAU,EAAO,KACjB,uBAAwB,EAAO,uBAC/B,KAAM,EAAO,IACf,CACF"}
|