{"version":3,"sources":["../../src/context.ts","../../src/lib/internal/UnirendContext/context.ts","../../src/lib/internal/UnirendHead/context.ts"],"sourcesContent":["/**\n * Shared context objects for unirend.\n *\n * This entry point exists solely to guarantee that UnirendContext and\n * UnirendHeadContext are created exactly once at runtime. Both unirend/client\n * and unirend/server reference this subpath as an external import so that the\n * consuming bundler (Vite, webpack, etc.) deduplicates it — preventing the\n * duplicate createContext() calls that would break Provider/hook communication\n * in SSR bundles.\n *\n * Not intended for direct use by application code.\n */\nexport * from './lib/internal/UnirendContext/context';\nexport * from './lib/internal/UnirendHead/context';\n","import { createContext } from 'react';\nimport type { ReactNode } from 'react';\nimport type { DomainInfo } from '../domain-info';\n\n/**\n * Render mode type - SSR, SSG, or Client\n * - \"ssr\": Server-Side Rendering (runtime server rendering)\n * - \"ssg\": Static Site Generation (build-time server rendering)\n * - \"client\": Client-side execution (SPA or after a SSG build/SSR page hydration occurs)\n */\nexport type UnirendRenderMode = 'ssr' | 'ssg' | 'client';\n\n/**\n * Type guard to check if request has SSR helpers with request context\n */\nexport function hasSSRRequestContext(request: Request): request is Request & {\n  SSRHelpers: {\n    fastifyRequest: { requestContext: Record<string, unknown> };\n  };\n} {\n  if (!('SSRHelpers' in request)) {\n    return false;\n  }\n\n  const helpers = (request as Request & { SSRHelpers: unknown }).SSRHelpers;\n\n  if (typeof helpers !== 'object' || helpers === null) {\n    return false;\n  }\n\n  if (!('fastifyRequest' in helpers)) {\n    return false;\n  }\n\n  const fastifyReq = (helpers as { fastifyRequest: unknown }).fastifyRequest;\n\n  if (typeof fastifyReq !== 'object' || fastifyReq === null) {\n    return false;\n  }\n\n  if (!('requestContext' in fastifyReq)) {\n    return false;\n  }\n\n  const reqCtx = (fastifyReq as { requestContext: unknown }).requestContext;\n  return typeof reqCtx === 'object' && reqCtx !== null;\n}\n\n/**\n * Type guard to check if request has SSG helpers with request context\n */\nexport function hasSSGRequestContext(request: Request): request is Request & {\n  SSGHelpers: { requestContext: Record<string, unknown> };\n} {\n  if (!('SSGHelpers' in request)) {\n    return false;\n  }\n\n  const helpers = (request as Request & { SSGHelpers: unknown }).SSGHelpers;\n  if (typeof helpers !== 'object' || helpers === null) {\n    return false;\n  }\n\n  if (!('requestContext' in helpers)) {\n    return false;\n  }\n\n  const reqCtx = (helpers as { requestContext: unknown }).requestContext;\n  return typeof reqCtx === 'object' && reqCtx !== null;\n}\n\n/**\n * Type guard to check if window has request context\n */\nexport function hasWindowRequestContext(): boolean {\n  if (typeof window === 'undefined') {\n    return false;\n  }\n\n  // eslint-disable-next-line @typescript-eslint/naming-convention\n  const ctx = (window as Window & { __FRONTEND_REQUEST_CONTEXT__?: unknown })\n    .__FRONTEND_REQUEST_CONTEXT__;\n\n  return '__FRONTEND_REQUEST_CONTEXT__' in window && typeof ctx === 'object';\n}\n\n/**\n * Helper to get a value from request context storage\n * Works across SSR, SSG, and client environments\n */\nexport function getRequestContextValue(\n  context: UnirendContextValue,\n  key: string,\n): unknown {\n  if (context.fetchRequest && hasSSRRequestContext(context.fetchRequest)) {\n    // SSR: Read from fastify request context\n    return context.fetchRequest.SSRHelpers.fastifyRequest.requestContext[key];\n  } else if (\n    context.fetchRequest &&\n    hasSSGRequestContext(context.fetchRequest)\n  ) {\n    // SSG: Read from SSG request context\n    return context.fetchRequest.SSGHelpers.requestContext[key];\n  } else if (hasWindowRequestContext()) {\n    // Client: Read from window global\n    return (\n      window as unknown as {\n        // eslint-disable-next-line @typescript-eslint/naming-convention\n        __FRONTEND_REQUEST_CONTEXT__: Record<string, unknown>;\n      }\n    ).__FRONTEND_REQUEST_CONTEXT__[key];\n  } else {\n    // No context available\n    return undefined;\n  }\n}\n\n/**\n * Helper to set a value in request context storage\n * Works across SSR, SSG, and client environments\n * Automatically increments the revision counter to trigger reactivity\n */\nexport function setRequestContextValue(\n  context: UnirendContextValue,\n  key: string,\n  value: unknown,\n): void {\n  if (context.fetchRequest && hasSSRRequestContext(context.fetchRequest)) {\n    // SSR: Write to fastify request context\n    context.fetchRequest.SSRHelpers.fastifyRequest.requestContext[key] = value;\n    incrementContextRevision(context);\n  } else if (\n    context.fetchRequest &&\n    hasSSGRequestContext(context.fetchRequest)\n  ) {\n    // SSG: Write to SSG request context\n    context.fetchRequest.SSGHelpers.requestContext[key] = value;\n    incrementContextRevision(context);\n  } else if (hasWindowRequestContext()) {\n    // Client: Write to window global\n    (\n      window as unknown as {\n        // eslint-disable-next-line @typescript-eslint/naming-convention\n        __FRONTEND_REQUEST_CONTEXT__: Record<string, unknown>;\n      }\n    ).__FRONTEND_REQUEST_CONTEXT__[key] = value;\n    incrementContextRevision(context);\n  } else {\n    // No context available - create one on window for client-side\n    if (typeof window !== 'undefined') {\n      (\n        window as unknown as {\n          // eslint-disable-next-line @typescript-eslint/naming-convention\n          __FRONTEND_REQUEST_CONTEXT__?: Record<string, unknown>;\n        }\n      ).__FRONTEND_REQUEST_CONTEXT__ = { [key]: value };\n      incrementContextRevision(context);\n    } else {\n      // Server-side with no context - this shouldn't happen in normal usage\n      throw new TypeError(\n        'Cannot set request context: no context available (server-side without SSR/SSG helpers)',\n      );\n    }\n  }\n}\n\n/**\n * Helper to increment the request context revision counter\n * Reads the current revision from context, parses it, and generates a new unique revision\n * Format: `${timestamp}-${counter}` (e.g., \"1729123456789-0\", \"1729123456789-1\")\n */\nexport function incrementContextRevision(context: UnirendContextValue): void {\n  const currentRevision = context.requestContextRevision || '0-0';\n  const [timestampStr, counterStr] = currentRevision.split('-');\n  const lastTimestamp = parseInt(timestampStr, 10);\n  const lastCounter = parseInt(counterStr, 10);\n\n  const now = Date.now();\n\n  // If we're in a new millisecond, reset counter to 0\n  // Otherwise, increment the counter\n  if (now !== lastTimestamp) {\n    context.requestContextRevision = `${now}-0`;\n  } else {\n    context.requestContextRevision = `${now}-${lastCounter + 1}`;\n  }\n}\n\n/**\n * Unirend context value type\n */\nexport interface UnirendContextValue {\n  /**\n   * The render mode:\n   * - 'ssr': Server-Side Rendering\n   * - 'ssg': Static Site Generation\n   * - 'client': Client-side execution (SPA or after a SSG build/SSR page hydration occurs)\n   */\n  renderMode: UnirendRenderMode;\n\n  /**\n   * Whether the app is running in development mode\n   */\n  isDevelopment: boolean;\n\n  /**\n   * The Fetch API Request object (available during SSR/SSG rendering)\n   * Undefined on client-side after hydration\n   */\n  fetchRequest?: Request;\n\n  /**\n   * Public application configuration\n   * This is a frozen (immutable) copy of the config passed to the server\n   * Available on both server and client (injected into HTML during SSR/SSG)\n   */\n  publicAppConfig?: Record<string, unknown>;\n\n  /**\n   * CDN base URL for asset serving (e.g. 'https://cdn.example.com')\n   * Available on both server (from app config or per-request override) and client\n   * (read from window.__CDN_BASE_URL__ injected into HTML by the server)\n   * Empty string when no CDN is configured\n   */\n  cdnBaseURL?: string;\n\n  /**\n   * Domain information computed server-side from the request hostname.\n   * Available during SSR (computed per-request) and SSG when a `hostname` option is\n   * provided at build time. `null` when hostname is not known (SSG without hostname\n   * configured, or pure SPA — no server to compute it via the public suffix list).\n   */\n  domainInfo?: DomainInfo | null;\n\n  /**\n   * Request context revision counter for reactivity\n   * Format: `${timestamp}-${counter}` (e.g., \"1729123456789-0\", \"1729123456789-1\")\n   * Increments whenever request context is modified to trigger re-renders\n   * @internal\n   */\n  requestContextRevision?: string;\n}\n\n/**\n * Default context value (React requirement for createContext)\n *\n * In practice, this default is rarely used because:\n * - Server (SSR/SSG): Provides proper context values during rendering\n * - Client: mountApp() reads window.__PUBLIC_APP_CONFIG__ and provides proper values\n *\n * This default only applies if context is accessed outside of proper providers,\n * which shouldn't happen in normal usage.\n */\nconst defaultContextValue: UnirendContextValue = {\n  renderMode: 'client', // Default to client-only (SSR/SSG override this)\n  isDevelopment: false, // Default to production\n  fetchRequest: undefined,\n  publicAppConfig: undefined, // mountApp() reads from window.__PUBLIC_APP_CONFIG__\n  requestContextRevision: '0-0', // Initial revision\n};\n\n/**\n * React context for Unirend\n */\nexport const UnirendContext =\n  createContext<UnirendContextValue>(defaultContextValue);\n\n/**\n * Provider props\n */\nexport interface UnirendProviderProps {\n  children: ReactNode;\n  value: UnirendContextValue;\n}\n\n/**\n * Request context management interface\n */\nexport interface RequestContextManager {\n  /**\n   * Get a value from the request context\n   * @param key - The key to retrieve\n   * @returns The value associated with the key, or undefined if not found\n   */\n  get: (key: string) => unknown;\n\n  /**\n   * Set a value in the request context\n   * @param key - The key to set\n   * @param value - The value to associate with the key\n   */\n  set: (key: string, value: unknown) => void;\n\n  /**\n   * Check if a key exists in the request context\n   * @param key - The key to check\n   * @returns true if the key exists, false otherwise\n   */\n  has: (key: string) => boolean;\n\n  /**\n   * Delete a value from the request context\n   * @param key - The key to delete\n   * @returns true if the key existed and was deleted, false if it didn't exist\n   */\n  delete: (key: string) => boolean;\n\n  /**\n   * Clear all values from the request context\n   * @returns The number of keys that were cleared\n   */\n  clear: () => number;\n\n  /**\n   * Get all keys from the request context\n   * @returns An array of all keys\n   */\n  keys: () => string[];\n\n  /**\n   * Get the number of entries in the request context\n   * @returns The number of key-value pairs\n   */\n  size: () => number;\n}\n\n/**\n * Helper to get the entire request context object\n * Works across SSR, SSG, and client environments\n */\nexport function getRequestContextObject(\n  context: UnirendContextValue,\n): Record<string, unknown> | undefined {\n  if (context.fetchRequest && hasSSRRequestContext(context.fetchRequest)) {\n    // SSR: Read from fastify request context\n    return context.fetchRequest.SSRHelpers.fastifyRequest.requestContext;\n  } else if (\n    context.fetchRequest &&\n    hasSSGRequestContext(context.fetchRequest)\n  ) {\n    // SSG: Read from SSG request context\n    return context.fetchRequest.SSGHelpers.requestContext;\n  } else if (hasWindowRequestContext()) {\n    // Client: Read from window global\n    return (\n      window as unknown as {\n        // eslint-disable-next-line @typescript-eslint/naming-convention\n        __FRONTEND_REQUEST_CONTEXT__: Record<string, unknown>;\n      }\n    ).__FRONTEND_REQUEST_CONTEXT__;\n  } else {\n    // No context available\n    return undefined;\n  }\n}\n\nexport { type DomainInfo } from '../domain-info';\n","import { createContext } from 'react';\n\n/**\n * Collected head data built up during server-side renderToString.\n * Last write wins for title, meta and link entries accumulate.\n */\nexport interface HeadCollector {\n  title: string;\n  metas: Array<Record<string, string>>;\n  links: Array<Record<string, string>>;\n}\n\n/**\n * Context value is the collector on the server, null on the client.\n * null signals \"render JSX tags so React 19 can hoist them to <head>\".\n */\nexport const UnirendHeadContext = createContext<HeadCollector | null>(null);\n"],"mappings":"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","names":["import_react"]}