{"version":3,"file":"required-context.cjs","names":["React"],"sources":["../../src/context/required-context.tsx"],"sourcesContent":["\"use client\";\n\nimport { isNonEmptyString } from \"@rzl-zone/utils-js/predicates\";\nimport React, { createContext, useContext, type ReactNode } from \"react\";\n\ntype UnwrapPromise<T> = ContextValue<T extends Promise<infer U> ? U : T>;\ntype ContextValue<T> = T extends undefined ? never : T;\n\ntype RequiredContextProviderProps<T> = {\n  readonly value: T;\n  readonly children: ReactNode;\n};\n\ntype RequiredContextProvider<T> = {\n  (props: RequiredContextProviderProps<T>): ReactNode;\n  displayName?: string;\n};\n\ntype RequiredContextReturn<T> = {\n  /** ------------------------------------------------------------------------\n   * * ***React Context instance (advanced usage).***\n   * ------------------------------------------------------------------------\n   *\n   * ⚠️ **Advanced usage only**\n   *\n   * - ***This context is exposed for:***\n   *    - Integration with third-party libraries.\n   *    - Adapter layers.\n   *    - Testing or devtools.\n   *\n   * - ***Important notes:***\n   *    - The value type is `T | undefined`.\n   *    - `undefined` is reserved internally to detect missing Providers.\n   *    - Consumers SHOULD prefer `use()` or `useSuspense()` instead of\n   *      calling `useContext(Context)` directly.\n   *\n   * @example\n   * **1. Adapter / integration layer.**\n   * ```tsx\n   * // Example: integrating with a third-party hook\n   * // that requires a raw React Context instance.\n   *\n   * import { useContext } from \"react\";\n   *\n   * type AuthState = { user: { name: string, email: string } };\n   *\n   * function useAuthFromAdapter(AuthContext: {\n   *   Context: React.Context<AuthState | undefined>;\n   * }) {\n   *   const value = useContext(AuthContext.Context);\n   *\n   *   // Adapter is responsible for handling `undefined`\n   *   if (value === undefined) {\n   *     throw new Error(\"AuthContext.Provider is missing\");\n   *   }\n   *\n   *   return value;\n   * }\n   * ```\n   *\n   * @example\n   * **2. Testing or devtools.**\n   * ```tsx\n   * import { render } from \"@testing-library/react\";\n   *\n   * render(\n   *   <AuthContext.Context.Provider value={{ user: { name: \"Rzl\" } }}>\n   *     <ComponentUnderTest />\n   *   </AuthContext.Context.Provider>\n   * );\n   * ```\n   */\n  Context: React.Context<T | undefined>;\n\n  /** ------------------------------------------------------------------------\n   * * ***Context Provider component.***\n   * ------------------------------------------------------------------------\n   *\n   * Provides the context value to the component subtree.\n   *\n   * - ⚠️ ***Important:***\n   *    - `undefined` is **reserved internally** to detect missing Providers.\n   *    - Do **NOT** pass `undefined` as a valid value.\n   *    - If you need to represent an “empty” or “not yet available” state,\n   *      use `null` instead and handle it explicitly in consumers.\n   *\n   * @example\n   * ```tsx\n   * type AuthState = { user: { name: string } } | null;\n   *\n   * const AuthContext =\n   *   createRequiredContext<AuthState>(\"AuthContext\", null);\n   *\n   * function App() {\n   *   return (\n   *     <AuthContext.Provider value={{ user: { name: \"Rzl\" } }}>\n   *       <UserComponent />\n   *     </AuthContext.Provider>\n   *   );\n   * }\n   *\n   * function UserComponent() {\n   *   const userContext = AuthContext.use();\n   *\n   *   if (userContext === null) throw new Error(\"UserContext is null\");\n   *\n   *   return <div>{userContext.user.name}</div>;\n   * }\n   * ```\n   */\n  Provider: RequiredContextProvider<T>;\n\n  /** ------------------------------------------------------------------------\n   * * ***Classic context consumer (non-Suspense).***\n   * ------------------------------------------------------------------------\n   *\n   * Reads the context value synchronously using `useContext`.\n   *\n   * - ***Behavior:***\n   *    - Throws if the Provider is missing.\n   *    - Filters **only `undefined`** (used internally to detect missing Provider).\n   *    - `null` is treated as a valid value and is **not** filtered.\n   *    - Never suspends.\n   *    - Does NOT require `<Suspense>`.\n   *\n   * @param missingProviderMessage - Optional custom error message when used outside its Provider.\n   *\n   * @throws {Error} Thrown when used outside its corresponding Provider.\n   *\n   * @returns The context value of type `T`.\n   *\n   * @example\n   * ```tsx\n   * type Theme = { theme: \"light\" | \"dark\" };\n   *\n   * const ThemeContext = createRequiredContext<Theme>(\"ThemeContext\");\n   *\n   * function App() {\n   *   return (\n   *     <ThemeContext.Provider value={{ theme: \"dark\" }}>\n   *       <ThemeToggle />\n   *     </ThemeContext.Provider>\n   *   );\n   * }\n   *\n   * function ThemeToggle() {\n   *   const { theme } = ThemeContext.use();\n   *   return <button data-theme={theme} />;\n   * }\n   * ```\n   */\n  use(missingProviderMessage?: string): ContextValue<T>;\n\n  /** ------------------------------------------------------------------------\n   * * ***React 19 `use()`-powered context consumer (Promise-aware).***\n   * ------------------------------------------------------------------------\n   *\n   * Reads the context value using React 19 `use(Context)`.\n   *\n   * - ***Behavior:***\n   *    - Throws if the Provider is missing.\n   *    - Filters **only `undefined`**.\n   *    - If the value is a Promise, rendering suspends until it resolves.\n   *    - If the value is synchronous, behaves like `useContext`.\n   *\n   * - ***⚠️ When the value is a Promise:***\n   *    - A `<Suspense>` boundary is ***REQUIRED***.\n   *    - The Promise must come from a Suspense-compatible source\n   *      (*not created during render*).\n   *\n   * ---\n   *\n   * ⚠️ This hook **requires** ***React 19*** or ***newer***, If used in an older React version, it will throw an error.\n   *\n   * ---\n   *\n   * @param missingProviderMessage - Optional custom error message when used outside its Provider.\n   * @param unsupportedReactErrorMessage  - Optional custom error message when `use()` is not supported (React < 19).\n   *\n   * @throws {Error}\n   * Thrown when:\n   * - Used outside its corresponding Provider.\n   * - React version is older than 19 and does not support `use`.\n   *\n   * @returns The resolved context value.\n   *\n   * @example\n   * ```tsx\n   * type User = { name: string };\n   *\n   * const userPromise: Promise<User> = fetch(\"/api/user\").then(r => r.json());\n   *\n   * const UserContext =\n   *   createRequiredContext<Promise<User>>(\"UserContext\");\n   *\n   * function App() {\n   *   return (\n   *     <UserContext.Provider value={userPromise}>\n   *       <Suspense fallback=\"Loading...\">\n   *         <Profile />\n   *       </Suspense>\n   *     </UserContext.Provider>\n   *   );\n   * }\n   *\n   * function Profile() {\n   *   const user = UserContext.useSuspense();\n   *   return <div>{user.name}</div>;\n   * }\n   * ```\n   */\n  useSuspense(\n    missingProviderMessage?: string,\n    unsupportedReactErrorMessage?: string\n  ): UnwrapPromise<T>;\n};\n\n/** --------------------------------------------------------------------------\n * * ***Create a React Context with a required Provider.***\n * --------------------------------------------------------------------------\n *\n * - ***This utility enforces that:***\n *    - The context **must** be wrapped with its Provider.\n *    - Using the context without a Provider throws a clear runtime error.\n *\n * - ⚠️ ***Important behavior:***\n *    - Only **`undefined`** is treated as an invalid value.\n *    - Passing `undefined` (explicitly or implicitly) will cause the consumer\n *      to throw a runtime error.\n *    - **`null` is allowed** and will be returned as-is.\n *\n * ---\n *\n * ℹ️ If you need an “empty” or “initial” state, prefer `null` instead of `undefined`.\n *\n * ---\n *\n * - ***Supports two consumption modes:***\n *    - `use()` ➔ classic `useContext` (non-suspending, filters `undefined`).\n *    - `useSuspense()` ➔ React 19 `use()` (Promise-aware, filters `undefined`).\n *\n * - ***Designed for:***\n *    - Application-level state.\n *    - Shared library contexts.\n *    - Fail-fast enforcement of required Providers.\n *\n * @template T - The context value type, `undefined` is reserved internally to detect missing Providers.\n *\n * @param name - Human-readable context name (used in error messages and React DevTools).\n *\n * @returns An object containing:\n * - `Context` — the raw React Context instance (advanced usage).\n * - `Provider` — the required Provider component.\n * - `use` — classic context consumer hook, throws if Provider is missing.\n * - `useSuspense` — React 19+ Suspense-aware consumer hook, throws if Provider is missing or React version is too old.\n *\n * @example\n * **1. Context with a default value.**\n * ```tsx\n * type AuthState = {\n *    user: {\n *      name: string\n *    }\n * } | null;\n *\n * const AuthContext =\n *   createRequiredContext<AuthState>(\"AuthContext\", null);\n *\n * function App() {\n *   return (\n *     <AuthContext.Provider value={{ user: { name: \"Rzl\" } }}>\n *       <UserComponent />\n *     </AuthContext.Provider>\n *   );\n * }\n *\n * function UserComponent() {\n *   const userContext = AuthContext.use();\n *\n *   if (userContext === null) throw new Error(\"UserContext is null\");\n *\n *   return <div>{userContext.user.name}</div>;\n * }\n * ```\n *\n * @example\n * **2. Context without a default value (Provider required).**\n * ```tsx\n * type Theme = { theme: \"light\" | \"dark\" };\n *\n * const ThemeContext = createRequiredContext<Theme>(\"ThemeContext\");\n *\n * function App() {\n *   return (\n *     <ThemeContext.Provider value={{ theme: \"dark\" }}>\n *       <ThemeToggle />\n *     </ThemeContext.Provider>\n *   );\n * }\n *\n * function ThemeToggle() {\n *   const { theme } = ThemeContext.use();\n *   return <button data-theme={theme} />;\n * }\n * ```\n *\n * @example\n * **3. Async (Promise) context with Suspense.**\n * ```tsx\n * type User = { name: string };\n *\n * const userPromise: Promise<User> = fetch(\"/api/user\").then(r => r.json());\n *\n * const UserContext =\n *   createRequiredContext<Promise<User>>(\"UserContext\");\n *\n * function App() {\n *   return (\n *     <UserContext.Provider value={userPromise}>\n *       <Suspense fallback=\"Loading...\">\n *         <Profile />\n *       </Suspense>\n *     </UserContext.Provider>\n *   );\n * }\n *\n * function Profile() {\n *   const user = UserContext.useSuspense();\n *   return <div>{user.name}</div>;\n * }\n * ```\n */\nexport function createRequiredContext<T>(\n  name: string,\n  defaultValue: T\n): RequiredContextReturn<T>;\nexport function createRequiredContext<T>(\n  name: string\n): RequiredContextReturn<T | undefined>;\nexport function createRequiredContext<T>(\n  name: string,\n  defaultValue?: T\n): RequiredContextReturn<T | undefined> {\n  if (!isNonEmptyString(name)) {\n    throw new Error(\n      \"Invalid `context` name passed to `createRequiredContext`: expected a non-empty string.\"\n    );\n  }\n\n  name = name.trim();\n\n  const { contextDisplayName, providerDisplayName } =\n    normalizeContextNames(name);\n\n  /** ------------------------------------------------------------------------\n   * * ***React Context instance (advanced usage).***\n   * ------------------------------------------------------------------------\n   */\n  const Context = createContext<T | undefined>(defaultValue);\n  Context.displayName = contextDisplayName;\n\n  /** ------------------------------------------------------------------------\n   * * ***Context Provider component.***\n   * ------------------------------------------------------------------------\n   */\n  function Provider(props: RequiredContextProviderProps<T | undefined>) {\n    return (\n      <Context.Provider value={props.value}>{props.children}</Context.Provider>\n    );\n  }\n\n  Provider.displayName = providerDisplayName;\n\n  /** ------------------------------------------------------------------------\n   * * ***Hook to consume the context value safely.***\n   * ------------------------------------------------------------------------\n   */\n  function useRequiredContext(\n    missingProviderMessage?: string\n  ): ContextValue<T> {\n    const value = useContext(Context);\n\n    if (value === undefined) {\n      throw new Error(\n        missingProviderMessage ??\n          `You are using \\`${contextDisplayName}\\`, but your component is not wrapped with <${providerDisplayName}>.\\n` +\n            `Did you forget to add <${providerDisplayName}> to your component tree?`\n      );\n    }\n\n    return value as ContextValue<T>;\n  }\n\n  /** ------------------------------------------------------------------------\n   * * ***Suspense consumer (React 19).***\n   * ----------------------------------------------------------------------\n   */\n  function useRequiredContextSuspense(\n    missingProviderMessage?: string,\n    unsupportedReactErrorMessage?: string\n  ): UnwrapPromise<T> {\n    //\n    // eslint-disable-next-line @rzl-zone/eslint/no-react19-api\n    if (typeof React?.use !== \"function\" || !(\"use\" in React)) {\n      throw new Error(\n        unsupportedReactErrorMessage ??\n          \"This feature requires `React 19` or `newer`, please upgrade your React version to use Suspense-enabled context.\"\n      );\n    }\n\n    // eslint-disable-next-line @rzl-zone/eslint/no-react19-api\n    const value = React.use(Context);\n\n    if (value === undefined) {\n      throw new Error(\n        missingProviderMessage ??\n          `You are using \\`${contextDisplayName}\\`, but your component is not wrapped with <${providerDisplayName}>.\\n` +\n            `Did you forget to add <${providerDisplayName}> to your component tree?`\n      );\n    }\n\n    return value as UnwrapPromise<T>;\n  }\n\n  return {\n    Context,\n    Provider,\n    use: useRequiredContext,\n    useSuspense: useRequiredContextSuspense\n  };\n}\n\nfunction normalizeContextNames(name: string) {\n  const trimmed = name.trim();\n\n  const baseName = /context$/i.test(trimmed)\n    ? trimmed.replace(/context$/i, \"\")\n    : trimmed;\n\n  const contextDisplayName = `${baseName}Context`;\n  const providerDisplayName = `${baseName}Provider`;\n\n  return {\n    baseName,\n    contextDisplayName,\n    providerDisplayName\n  };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmVA,SAAgB,sBACd,MACA,cACsC;CACtC,IAAI,qDAAkB,IAAI,GACxB,MAAM,IAAI,MACR,wFACF;CAGF,OAAO,KAAK,KAAK;CAEjB,MAAM,EAAE,oBAAoB,wBAC1B,sBAAsB,IAAI;;;;;CAM5B,MAAM,mCAAuC,YAAY;CACzD,QAAQ,cAAc;;;;;CAMtB,SAAS,SAAS,OAAoD;EACpE,OACE,CAAC,QAAQ,SAAS,OAAO,MAAM,QAAQ,MAAM,SAAS,EAAE,QAAQ;CAEpE;CAEA,SAAS,cAAc;;;;;CAMvB,SAAS,mBACP,wBACiB;EACjB,MAAM,8BAAmB,OAAO;EAEhC,IAAI,UAAU,QACZ,MAAM,IAAI,MACR,0BACE,mBAAmB,mBAAmB,8CAA8C,oBAAoB,6BAC5E,oBAAoB,0BACpD;EAGF,OAAO;CACT;;;;;CAMA,SAAS,2BACP,wBACA,8BACkB;EAGlB,IAAI,OAAOA,eAAO,QAAQ,cAAc,EAAE,SAASA,gBACjD,MAAM,IAAI,MACR,gCACE,iHACJ;EAIF,MAAM,QAAQA,cAAM,IAAI,OAAO;EAE/B,IAAI,UAAU,QACZ,MAAM,IAAI,MACR,0BACE,mBAAmB,mBAAmB,8CAA8C,oBAAoB,6BAC5E,oBAAoB,0BACpD;EAGF,OAAO;CACT;CAEA,OAAO;EACL;EACA;EACA,KAAK;EACL,aAAa;CACf;AACF;AAEA,SAAS,sBAAsB,MAAc;CAC3C,MAAM,UAAU,KAAK,KAAK;CAE1B,MAAM,WAAW,YAAY,KAAK,OAAO,IACrC,QAAQ,QAAQ,aAAa,EAAE,IAC/B;CAKJ,OAAO;EACL;EACA,uBAL4B,SAAS;EAMrC,wBAL6B,SAAS;CAMxC;AACF"}
