@withl5e/l5e 0.3.2 → 0.3.3-alpha.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.
@@ -10,6 +10,7 @@ import {
10
10
  } from 'fs';
11
11
  import { dirname, join, relative, resolve } from 'path';
12
12
  import type { Plugin, UserConfig } from 'vite';
13
+ import { findGlobalStyleInput } from './global-style';
13
14
 
14
15
  const VIRTUAL_L5E_VIEWS = 'virtual:l5e-views';
15
16
  const VIRTUAL_L5E_ROUTE = 'virtual:l5e-route';
@@ -246,6 +247,12 @@ function discoverRollupInput(rootDir: string): {
246
247
  console.log('[l5e] Detected src/client.global.ts and added as entry');
247
248
  }
248
249
 
250
+ const globalStyleInput = findGlobalStyleInput(rootDir);
251
+ if (globalStyleInput) {
252
+ input['global-style'] = globalStyleInput;
253
+ console.log('[l5e] Detected src/global.css and added as global stylesheet');
254
+ }
255
+
249
256
  // Scan all .tsx and .ts files
250
257
  const tsFiles = scanTsFiles(srcDir);
251
258
 
@@ -297,6 +304,12 @@ function discoverRollupInput(rootDir: string): {
297
304
 
298
305
  const absolutePath = resolve(rootDir, relativePath);
299
306
 
307
+ // src/global.css is already a dedicated convention entry. Keeping one
308
+ // Rollup input avoids collisions if a shared layout also calls useCss().
309
+ if (globalStyleInput && resolve(absolutePath) === resolve(globalStyleInput)) {
310
+ continue;
311
+ }
312
+
300
313
  // Only add if file exists
301
314
  if (!existsSync(absolutePath)) {
302
315
  console.warn(`[l5e] CSS file not found: ${absolutePath} (from ${cssPath})`);
@@ -725,8 +738,30 @@ export const viewComponents = import.meta.glob('/src/views/*/index.tsx');
725
738
 
726
739
  // Virtual module: l5e-route
727
740
  if (id === '\0' + VIRTUAL_L5E_ROUTE) {
728
- // Always use TypeScript
729
- return `export { default } from '/src/route.ts';`;
741
+ // Load /src/route.ts LAZILY (dynamic import inside the handler) rather
742
+ // than via a static `export { default } from '/src/route.ts'`.
743
+ //
744
+ // A static re-export drags the user's route module — and everything it
745
+ // imports — into entry-server's *static* SSR graph. Route files commonly
746
+ // import the `@withl5e/l5e` barrel (for RedirectException, types, ...),
747
+ // and that barrel re-exports `render` from entry-server. That closes a
748
+ // static import cycle:
749
+ // entry-server → virtual:l5e-route → /src/route.ts → @withl5e/l5e → entry-server
750
+ // On the first dev request Vite can begin evaluating entry-server while
751
+ // it is still suspended awaiting this very import, so `render()` runs
752
+ // before `const __vite_ssr_import_N__ = await import('virtual:l5e-route')`
753
+ // has been assigned — throwing "Cannot access '__vite_ssr_import_N__'
754
+ // before initialization". A page reload "fixes" it only because the graph
755
+ // is fully warm the second time.
756
+ //
757
+ // Importing lazily removes /src/route.ts from the static graph: entry-server
758
+ // finishes initializing first, and the route module (with its barrel import
759
+ // back to a now-complete entry-server) is pulled only on the first render()
760
+ // call. No manual caching — Vite/ESM caches the evaluated module and later
761
+ // imports still observe HMR invalidations of route.ts.
762
+ return `export default function routeHandler(requestInfo) {
763
+ return import('/src/route.ts').then((mod) => mod.default(requestInfo));
764
+ }`;
730
765
  }
731
766
 
732
767
  // Virtual module: l5e-ssr-entry
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  jsxFactory,
3
+ isIslandPropsExternalized,
4
+ pushIslandProps,
3
5
  registerIsland,
4
6
  registerSsrIsland,
5
7
  type JSXChild,
@@ -67,10 +69,31 @@ export function ClientIsland(attrs: ClientIslandProps): JSXNode {
67
69
  const dataAttrs: Record<string, string> = {
68
70
  'data-island': __key || 'unresolved',
69
71
  'data-island-name': componentName,
70
- 'data-island-props': JSON.stringify(props),
71
72
  'data-island-mount': mount,
72
73
  };
73
74
 
75
+ // Props delivery. `mount="none"` never mounts/hydrates on the client, so its
76
+ // props are dead weight → omit them entirely in the externalized mode.
77
+ //
78
+ // Externalized (default): props go into the per-request store and the element
79
+ // just carries a `data-island-idx` pointer; a single `_l5e_data_` script at the
80
+ // end of the document holds every island's props, keeping the SSR body lean.
81
+ // Falls back to inlining when there's no render context (idx === -1).
82
+ //
83
+ // Legacy inline mode is kept byte-for-byte (props inlined even for mount="none").
84
+ if (isIslandPropsExternalized()) {
85
+ if (mount !== 'none') {
86
+ const idx = pushIslandProps(props);
87
+ if (idx >= 0) {
88
+ dataAttrs['data-island-idx'] = String(idx);
89
+ } else {
90
+ dataAttrs['data-island-props'] = JSON.stringify(props);
91
+ }
92
+ }
93
+ } else {
94
+ dataAttrs['data-island-props'] = JSON.stringify(props);
95
+ }
96
+
74
97
  if (mountOpts) {
75
98
  dataAttrs['data-island-opts'] = mountOpts;
76
99
  }
@@ -69,12 +69,38 @@ import 'virtual:l5e-island-strategies';
69
69
  // Format: { "Counter_a3f2": "/assets/Counter-Abc123.js" }
70
70
  const islandRegistry: Record<string, string> = (window as any).__L5E_ISLANDS__ || {};
71
71
 
72
+ // Props for islands rendered in externalized mode live in a single
73
+ // `<script type="application/json" id="_l5e_data_">` at the end of the document
74
+ // (keeps the SSR body lean for crawlers). Parsed once, lazily.
75
+ let islandDataCache: unknown[] | null = null;
76
+ function getIslandData(): unknown[] {
77
+ if (islandDataCache) return islandDataCache;
78
+ const el = document.getElementById('_l5e_data_');
79
+ if (!el || !el.textContent) return (islandDataCache = []);
80
+ try {
81
+ const parsed = JSON.parse(el.textContent);
82
+ islandDataCache = Array.isArray(parsed) ? parsed : [];
83
+ } catch (err) {
84
+ console.error('[l5e-island] Failed to parse _l5e_data_ props store:', err);
85
+ islandDataCache = [];
86
+ }
87
+ return islandDataCache;
88
+ }
89
+
90
+ function readProps(el: Element): Record<string, unknown> {
91
+ const idx = el.getAttribute('data-island-idx');
92
+ if (idx !== null) {
93
+ return (getIslandData()[Number(idx)] as Record<string, unknown>) || {};
94
+ }
95
+ return JSON.parse(el.getAttribute('data-island-props') || '{}');
96
+ }
97
+
72
98
  function discoverIslands(): IslandMeta[] {
73
99
  return Array.from(document.querySelectorAll('[data-island]')).map((el) => ({
74
100
  element: el as HTMLElement,
75
101
  registryKey: el.getAttribute('data-island')!,
76
102
  exportName: el.getAttribute('data-island-name')!,
77
- props: JSON.parse(el.getAttribute('data-island-props') || '{}'),
103
+ props: readProps(el),
78
104
  mount: el.getAttribute('data-island-mount') || 'load',
79
105
  mountOpts: el.getAttribute('data-island-opts') || undefined,
80
106
  ssr: el.hasAttribute('data-island-ssr'),
@@ -1 +0,0 @@
1
- {"version":3,"file":"jsx-runtime-5m2ZiNtU.js","sources":["../src/core/head-priority.ts","../src/seo/mergeMetadata.ts","../src/core/const.ts","../src/core/jsx-runtime.ts"],"sourcesContent":["/**\n * Priority constants for Head component rendering order\n * Lower numbers render first (higher priority)\n */\nexport const HEAD_PRIORITY = {\n CRITICAL: 0, // charset, viewport - must be first\n HIGH: 10, // title, description, canonical\n MEDIUM: 50, // meta tags, robots\n SEO: 80, // OpenGraph, Twitter\n LOW: 100, // Custom head elements\n SCRIPTS: 200, // Scripts\n STYLES: 300, // Styles\n} as const;\n\nexport type HeadPriority = (typeof HEAD_PRIORITY)[keyof typeof HEAD_PRIORITY] | number;\n","import type { Metadata } from './types';\n\n/**\n * Merges parent and child metadata objects\n * Follows Next.js shallow merge pattern with deep merge for nested objects\n *\n * @param parent - Parent metadata (from layout/global loader)\n * @param child - Child metadata (from page/view loader)\n * @returns Merged metadata object\n */\nexport function mergeMetadata(\n parent: Metadata | null | undefined,\n child: Metadata | null | undefined,\n): Metadata {\n // If no parent, return child (or empty object)\n if (!parent) {\n return child || {};\n }\n\n // If no child, return parent\n if (!child) {\n return parent;\n }\n\n // Shallow merge base properties (child overrides parent)\n const merged: Metadata = {\n ...parent,\n ...child,\n };\n\n // Deep merge for nested objects\n // OpenGraph: merge nested properties\n if (child.openGraph || parent.openGraph) {\n if (child.openGraph && parent.openGraph) {\n merged.openGraph = {\n ...parent.openGraph,\n ...child.openGraph,\n // Deep merge for nested arrays/objects in openGraph\n images: child.openGraph.images ?? parent.openGraph.images,\n videos: child.openGraph.videos ?? parent.openGraph.videos,\n audio: child.openGraph.audio ?? parent.openGraph.audio,\n alternateLocale: child.openGraph.alternateLocale ?? parent.openGraph.alternateLocale,\n authors: child.openGraph.authors ?? parent.openGraph.authors,\n tags: child.openGraph.tags ?? parent.openGraph.tags,\n };\n } else {\n merged.openGraph = child.openGraph || parent.openGraph;\n }\n }\n\n // Twitter: merge nested properties\n if (child.twitter || parent.twitter) {\n if (child.twitter && parent.twitter) {\n merged.twitter = {\n ...parent.twitter,\n ...child.twitter,\n // Deep merge for nested objects\n images: child.twitter.images ?? parent.twitter.images,\n app: child.twitter.app\n ? {\n ...parent.twitter.app,\n ...child.twitter.app,\n id: {\n ...parent.twitter.app?.id,\n ...child.twitter.app.id,\n },\n url: {\n ...parent.twitter.app?.url,\n ...child.twitter.app.url,\n },\n }\n : parent.twitter.app,\n };\n } else {\n merged.twitter = child.twitter || parent.twitter;\n }\n }\n\n // Icons: merge nested properties\n if (child.icons || parent.icons) {\n if (child.icons && parent.icons) {\n merged.icons = {\n icon: child.icons.icon ?? parent.icons.icon,\n shortcut: child.icons.shortcut ?? parent.icons.shortcut,\n apple: child.icons.apple ?? parent.icons.apple,\n other: child.icons.other ?? parent.icons.other,\n };\n } else {\n merged.icons = child.icons || parent.icons;\n }\n }\n\n // Verification: merge nested properties\n if (child.verification || parent.verification) {\n if (child.verification && parent.verification) {\n merged.verification = {\n ...parent.verification,\n ...child.verification,\n // Deep merge for other verification tags\n other: child.verification.other\n ? {\n ...parent.verification.other,\n ...child.verification.other,\n }\n : parent.verification.other,\n me: child.verification.me ?? parent.verification.me,\n };\n } else {\n merged.verification = child.verification || parent.verification;\n }\n }\n\n // AppLinks: merge nested properties\n if (child.appLinks || parent.appLinks) {\n if (child.appLinks && parent.appLinks) {\n merged.appLinks = {\n ios: child.appLinks.ios\n ? {\n ...parent.appLinks.ios,\n ...child.appLinks.ios,\n }\n : parent.appLinks.ios,\n android: child.appLinks.android\n ? {\n ...parent.appLinks.android,\n ...child.appLinks.android,\n }\n : parent.appLinks.android,\n web: child.appLinks.web\n ? {\n ...parent.appLinks.web,\n ...child.appLinks.web,\n }\n : parent.appLinks.web,\n };\n } else {\n merged.appLinks = child.appLinks || parent.appLinks;\n }\n }\n\n // FormatDetection: merge nested properties\n if (child.formatDetection || parent.formatDetection) {\n if (child.formatDetection && parent.formatDetection) {\n merged.formatDetection = {\n ...parent.formatDetection,\n ...child.formatDetection,\n };\n } else {\n merged.formatDetection = child.formatDetection || parent.formatDetection;\n }\n }\n\n // Viewport: merge if both are objects\n if (child.viewport && parent.viewport) {\n if (typeof child.viewport === 'object' && typeof parent.viewport === 'object') {\n merged.viewport = {\n ...parent.viewport,\n ...child.viewport,\n } as Metadata['viewport'];\n } else {\n // If either is string, child overrides\n merged.viewport = child.viewport;\n }\n }\n\n // Robots: merge if both are objects\n if (child.robots && parent.robots) {\n if (typeof child.robots === 'object' && typeof parent.robots === 'object') {\n merged.robots = {\n ...parent.robots,\n ...child.robots,\n } as Metadata['robots'];\n } else {\n // If either is string, child overrides\n merged.robots = child.robots;\n }\n }\n\n // Array fields: child overrides parent (shallow merge behavior)\n // These are already handled by spread operator above\n // But we explicitly handle them for clarity:\n merged.keywords = child.keywords ?? parent.keywords;\n merged.themeColor = child.themeColor ?? parent.themeColor;\n merged.archives = child.archives ?? parent.archives;\n merged.assets = child.assets ?? parent.assets;\n\n // Other: merge objects\n if (child.other || parent.other) {\n if (child.other && parent.other) {\n merged.other = {\n ...parent.other,\n ...child.other,\n };\n } else {\n merged.other = child.other || parent.other;\n }\n }\n\n return merged;\n}\n","// Marker object để đánh dấu raw HTML không escape\nexport const RAW_HTML_MARKER = Symbol('rawHtml');\n","import { mergeMetadata } from '../seo/mergeMetadata';\nimport type { Metadata } from '../seo/types';\nimport { RAW_HTML_MARKER } from './const';\nimport { RequestInfo } from './entry-server';\nimport { HEAD_PRIORITY, type HeadPriority } from './head-priority';\n\nexport type JSXChild =\n | string\n | number\n | boolean\n | null\n | undefined\n | JSXNode\n | JSXChild[]\n | RawHtmlObject\n | HtmlContentObject;\n\nexport type RawHtmlObject = {\n [RAW_HTML_MARKER]: true;\n content: string;\n};\n\nexport type HtmlContentObject = {\n htmlContent: string;\n};\n\nexport type RenderedNode = {\n string: string;\n};\n\nexport type JSXNode = {\n type: string | ((props: any) => JSXChild);\n props: Record<string, any>;\n children: JSXChild[];\n};\n\nexport function jsxFactory(type: any, props: any, ...children: any): JSXNode {\n return { type, props: props || {}, children: children.flat() };\n}\n\nexport function Fragment({\n children,\n ...props\n}: { children?: JSXChild; setHtml?: unknown } & Record<string, any>): JSXChild {\n // Hỗ trợ setHtml cho Fragment\n if (props.setHtml !== undefined) {\n // Trả về object đặc biệt để không bị escape\n return { [RAW_HTML_MARKER]: true, content: props.setHtml?.toString() || '' };\n }\n return children;\n}\n\n// AsyncLocalStorage for render context (request-level)\nimport { AsyncLocalStorage } from 'async_hooks';\n\ninterface HeadEntry {\n content: JSXChild;\n priority: number; // Số càng nhỏ, render càng sớm\n source?: string; // Để debug (ví dụ: 'layout', 'page', 'seo')\n}\n\ninterface IslandEntry {\n key: string; // \"Counter_a3f2\" — registry key\n src: string; // \"src/views/.../Counter.tsx\" — manifest-compatible path\n name: string; // \"Counter\" — export name\n}\n\n/**\n * A pending server-side render request for an `ssr` island.\n * Collected during the synchronous render pass and filled in afterwards by\n * entry-server (which can `await import()` the component + call renderToString).\n */\ninterface SsrIslandEntry {\n token: string; // unique placeholder token embedded in the HTML body\n src: string; // \"src/views/.../Counter.tsx\" — manifest-compatible path (no leading slash)\n name: string; // \"Counter\" — export name\n props: Record<string, any>;\n}\n\ninterface RenderContext {\n clientJsRegistry: Array<{ path: string; from: string }>;\n cssRegistry: Array<{ path: string; from: string }>;\n islandRegistry: IslandEntry[];\n ssrIslands: SsrIslandEntry[];\n cacheTags: Set<string>;\n headRegistry: HeadEntry[]; // Thay vì JSXChild[]\n metadataStack: Metadata[]; // Stack để track metadata hierarchy\n schemaRegistry: Array<Record<string, any>>; // Schema.org structured data từ loaders\n request: RequestInfo;\n viewName?: string; // View name from route handler\n}\n\nconst renderStore = new AsyncLocalStorage<RenderContext>();\n\n// Create context for each request\nfunction createRequestContext(requestInfo: RequestInfo): RenderContext {\n return {\n clientJsRegistry: [],\n cssRegistry: [],\n islandRegistry: [],\n ssrIslands: [],\n cacheTags: new Set(),\n headRegistry: [],\n metadataStack: [],\n schemaRegistry: [],\n request: requestInfo,\n };\n}\n\n/**\n * Chuẩn hoá path asset về dạng web path có leading `/`.\n * Dev sinh thẻ thẳng từ path này, còn prod strip leading `/` để tra manifest —\n * không normalize thì `'src/a.css'` và `'/src/a.css'` là hai entry khác nhau ở\n * dev nhưng lại trỏ cùng một manifest key ở prod.\n */\nfunction normalizeAssetPath(path: string): string {\n const normalized = path.trim().replace(/\\\\/g, '/');\n return normalized.startsWith('/') ? normalized : `/${normalized}`;\n}\n\nexport function useClientJs(path: string): string {\n if (typeof path === 'string' && path.trim().length > 0) {\n const renderContext = renderStore.getStore();\n if (renderContext) {\n const normalized = normalizeAssetPath(path);\n // Dedupe theo path, giữ thứ tự lần gọi đầu tiên\n if (!renderContext.clientJsRegistry.some((entry) => entry.path === normalized)) {\n renderContext.clientJsRegistry.push({ path: normalized, from: 'Unknown' });\n }\n }\n }\n return '';\n}\n\nexport function registerIsland(key: string, src: string, name: string): void {\n const renderContext = renderStore.getStore();\n if (renderContext) {\n // Dedupe by key\n if (!renderContext.islandRegistry.some((e) => e.key === key)) {\n renderContext.islandRegistry.push({ key, src, name });\n }\n }\n}\n\nexport function getIslandEntries(): IslandEntry[] {\n const context = renderStore.getStore();\n if (!context) return [];\n return context.islandRegistry.slice();\n}\n\n/**\n * Register a pending SSR island render. Returns a unique placeholder token that\n * the caller embeds (as raw HTML) in the island's body. entry-server replaces\n * the token with the server-rendered component HTML after the sync render pass.\n */\nexport function registerSsrIsland(src: string, name: string, props: Record<string, any>): string {\n const renderContext = renderStore.getStore();\n if (!renderContext) return '';\n const token = `__L5E_SSR_${renderContext.ssrIslands.length}__`;\n renderContext.ssrIslands.push({ token, src, name, props });\n return token;\n}\n\nexport function getSsrIslands(): SsrIslandEntry[] {\n const context = renderStore.getStore();\n if (!context) return [];\n return context.ssrIslands.slice();\n}\n\nexport function useCss(path: string): string {\n if (typeof path === 'string' && path.trim().length > 0) {\n const renderContext = renderStore.getStore();\n if (renderContext) {\n const normalized = normalizeAssetPath(path);\n // Dedupe theo path — thứ tự lần gọi đầu tiên quyết định thứ tự cascade\n if (!renderContext.cssRegistry.some((entry) => entry.path === normalized)) {\n renderContext.cssRegistry.push({ path: normalized, from: 'Unknown' });\n }\n }\n }\n return '';\n}\n\n// Wrapper to run render in async context\nexport function runInRenderContext<T>(\n renderFn: () => T | Promise<T>,\n requestInfo: RequestInfo,\n viewName?: string,\n): Promise<T> {\n const context = createRequestContext(requestInfo);\n if (viewName) {\n context.viewName = viewName;\n }\n return renderStore.run(context, () => Promise.resolve(renderFn()));\n}\n\n// Set view name in current render context\nexport function setViewName(viewName: string): void {\n const context = renderStore.getStore();\n if (context) {\n context.viewName = viewName;\n }\n}\n\n// Get entries from current context\nexport function getClientJsEntries(): Array<{ path: string; from: string }> {\n const context = renderStore.getStore();\n if (!context) return [];\n return context.clientJsRegistry.slice();\n}\n\n// Get cache tags from current context\nexport function getCacheTags(): string[] {\n const context = renderStore.getStore();\n if (!context) return [];\n return Array.from(context.cacheTags);\n}\n\n// Add cache tags to current context\nexport function addCacheTag(tag: string | string[] | Record<string, boolean>): void {\n const context = renderStore.getStore();\n if (!context) return;\n\n if (Array.isArray(tag)) {\n tag.forEach((t) => {\n if (typeof t === 'string' && t.trim()) {\n context.cacheTags.add(t.trim());\n }\n });\n } else if (typeof tag === 'string' && tag.trim()) {\n context.cacheTags.add(tag.trim());\n } else if (typeof tag === 'object' && tag !== null) {\n Object.entries(tag).forEach(([key, value]) => {\n if (value && typeof key === 'string' && key.trim()) {\n context.cacheTags.add(key.trim());\n }\n });\n }\n}\n\n// Get CSS entries from current context\nexport function getCssEntries(): Array<{ path: string; from: string }> {\n const context = renderStore.getStore();\n if (!context) return [];\n return context.cssRegistry.slice();\n}\n\n// Head component to collect head elements with priority support\nexport function Head({\n children,\n priority = HEAD_PRIORITY.LOW, // Default priority\n}: {\n children?: JSXChild;\n priority?: HeadPriority;\n}): null {\n const renderContext = renderStore.getStore();\n if (renderContext && children) {\n renderContext.headRegistry.push({\n content: children,\n priority: typeof priority === 'number' ? priority : HEAD_PRIORITY.LOW,\n source: 'manual',\n });\n\n // Sort theo priority sau mỗi lần push để đảm bảo thứ tự đúng\n renderContext.headRegistry.sort((a, b) => a.priority - b.priority);\n }\n return null;\n}\n\n// Get head content from current context (already sorted by priority)\nexport function getHeadContent(): JSXChild[] {\n const context = renderStore.getStore();\n if (!context) return [];\n // Đã được sort trong Head component, chỉ cần map để lấy content\n return context.headRegistry.map((entry) => entry.content);\n}\n\n// Push metadata to stack (for hierarchical metadata support)\nexport function pushMetadata(metadata: Metadata): void {\n const context = renderStore.getStore();\n if (context && metadata) {\n context.metadataStack.push(metadata);\n }\n}\n\n// Resolve and merge all metadata from stack (root → leaf)\nexport function resolveMetadata(): Metadata | null {\n const context = renderStore.getStore();\n if (!context || context.metadataStack.length === 0) {\n return null;\n }\n\n // Merge from root → leaf (reduce left to right)\n return context.metadataStack.reduce(\n (acc, current) => mergeMetadata(acc, current),\n null as Metadata | null,\n );\n}\n\n// Push schema to registry (for schema markup from loaders)\n// Accepts schema-dts types (WithContext<T> or array of schemas)\nexport function pushSchema(schema: any | Array<any>): void {\n const context = renderStore.getStore();\n if (!context) return;\n\n if (Array.isArray(schema)) {\n context.schemaRegistry.push(...schema);\n } else {\n context.schemaRegistry.push(schema);\n }\n}\n\n// Get all schemas from registry\nexport function getSchemas(): Array<Record<string, any>> {\n const context = renderStore.getStore();\n if (!context) return [];\n return context.schemaRegistry.slice();\n}\n\n// Hook to get render request context\nexport function useRequest() {\n const context = renderStore.getStore();\n\n if (!context) {\n throw new Error('useRequest called outside of render context');\n }\n\n return {\n request: context.request,\n view: context.viewName,\n locals: (context.request.locals ?? {}) as Record<string, unknown>,\n params: (context.request.params ?? {}) as Record<string, any>,\n\n // Add cache tags\n addCacheTag: (tag: string | string[] | Record<string, boolean>) => {\n addCacheTag(tag);\n },\n\n // Get all cache tags\n getCacheTags: () => {\n return Array.from(context.cacheTags);\n },\n };\n}\n\n/**\n * Checks if a value is a valid JSX element (JSXNode)\n * Similar to React.isValidElement\n */\nexport function isValidElement(value: any): value is JSXNode {\n return (\n value !== null &&\n typeof value === 'object' &&\n 'type' in value &&\n 'props' in value &&\n 'children' in value &&\n (typeof value.type === 'string' || typeof value.type === 'function')\n );\n}\n\n/**\n * Clones a JSX element with new props and/or children\n * Similar to React.cloneElement\n */\nexport function cloneElement(\n element: JSXNode,\n props?: Record<string, any>,\n ...children: JSXChild[]\n): JSXNode {\n const newProps = { ...element.props, ...props };\n const newChildren = children.length > 0 ? children.flat() : element.children;\n\n return {\n type: element.type,\n props: newProps,\n children: newChildren,\n };\n}\n"],"names":["HEAD_PRIORITY","mergeMetadata","parent","child","merged","RAW_HTML_MARKER","jsxFactory","type","props","children","Fragment","renderStore","AsyncLocalStorage","createRequestContext","requestInfo","normalizeAssetPath","path","normalized","useClientJs","renderContext","entry","registerIsland","key","src","name","e","getIslandEntries","context","registerSsrIsland","token","getSsrIslands","useCss","runInRenderContext","renderFn","viewName","setViewName","getClientJsEntries","getCacheTags","addCacheTag","tag","t","value","getCssEntries","Head","priority","a","b","getHeadContent","pushMetadata","metadata","resolveMetadata","acc","current","pushSchema","schema","getSchemas","useRequest","isValidElement","cloneElement","element","newProps","newChildren"],"mappings":"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"}