@powerportalspro/react 5.0.0-beta.3 → 5.0.0
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/dist/index.cjs +378 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +485 -310
- package/dist/index.d.ts +485 -310
- package/dist/index.js +370 -33
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts","../src/caches/in-memory-cache.ts","../src/use-power-portals-pro.ts","../src/localizer.tsx","../src/unsaved-changes-registry.tsx","../src/provider.tsx","../src/use-auth.ts","../src/authorized-view.tsx","../src/internal/use-async-resource.ts","../src/use-record.ts","../src/use-fetch-records.ts","../src/use-grid-data.ts","../src/use-view-data-source.ts","../src/use-table-metadata.ts","../src/use-table-permissions.ts","../src/use-view-metadata.ts","../src/use-views-for-table.ts","../src/use-environment-file-settings.ts","../src/use-supported-locales.ts","../src/use-query-param.ts","../src/internal/use-before-unload-when.ts","../src/main-context.tsx","../src/overlay-service.tsx","../src/validation-context.tsx","../src/record-context.tsx","../src/lookup-record-context.tsx","../src/use-column-metadata.ts","../src/dialog-service.tsx","../src/unsaved-changes-warning.ts","../src/index.ts"],"names":["createContext","useContext","useCallback","useMemo","useEffect","useSyncExternalStore","jsx","useState","useRef","prefixes","Transport","AuthClient","PowerPortalsProClient","LoginResult","SwitchIdentityResult","Fragment","resolvers","useReducer","useId","value","next"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\r\n AuthClient,\r\n CurrentUserInfo,\r\n LoginRequest,\r\n LoginResponse,\r\n PowerPortalsProClient,\r\n SwitchIdentityResponse,\r\n Transport,\r\n} from '@powerportalspro/core';\r\nimport { createContext } from 'react';\r\nimport type {\r\n EnvironmentFileSettingsCache,\r\n TableMetadataCache,\r\n ViewMetadataCache,\r\n ViewsForTableCache,\r\n} from './caches/types.js';\r\n\r\n/**\r\n * Status values for the {@link AuthState} state machine.\r\n *\r\n * Hybrid shape — bare string-literal union as the type (clean narrowing) plus a\r\n * const-object export for consumers who prefer refactor-safe constants. Both\r\n * compare against the same runtime string, so `auth.status === 'loading'` and\r\n * `auth.status === AuthStatus.Loading` are equivalent. Matches the\r\n * auto-generated `LoginResult`-style pattern from `@powerportalspro/core`.\r\n */\r\nexport const AuthStatus = {\r\n Loading: 'loading',\r\n Anonymous: 'anonymous',\r\n Authenticated: 'authenticated',\r\n} as const;\r\nexport type AuthStatus = (typeof AuthStatus)[keyof typeof AuthStatus];\r\n\r\n/**\r\n * Discriminated state union exposed by {@link useAuth}. `loading` is the initial state\r\n * during the first `/api/auth/me` call on mount; once that resolves, the state is\r\n * either `anonymous` or `authenticated` for the rest of the app's lifetime (until\r\n * `login()` / `logout()` actions transition it).\r\n *\r\n * The `authenticated` branch carries the {@link CurrentUserInfo} the server returned —\r\n * the auto-generated DTO from `@powerportalspro/core`, no narrowing applied. Fields\r\n * like `roles` arrive as `string[] | undefined` per the wire contract; consumers\r\n * coalesce to `[]` at the read site if they want a non-null array.\r\n */\r\nexport type AuthState =\r\n | { readonly status: 'loading' }\r\n | { readonly status: 'anonymous' }\r\n | { readonly status: 'authenticated'; readonly user: CurrentUserInfo };\r\n\r\nexport interface PowerPortalsProContextValue {\r\n /** The shared {@link Transport} both clients are constructed against. Exposed for advanced use. */\r\n readonly transport: Transport;\r\n /** {@link AuthClient} instance — same one returned by `usePowerPortalsPro().auth`. */\r\n readonly auth: AuthClient;\r\n /** {@link PowerPortalsProClient} instance — same one returned by `usePowerPortalsPro().ppp`. */\r\n readonly ppp: PowerPortalsProClient;\r\n /**\r\n * Client-side cache for Dataverse table metadata. Reads dedupe\r\n * concurrent fetches and persist resolved values for the lifetime of\r\n * the `PowerPortalsProClient`. See {@link TableMetadataCache}.\r\n */\r\n readonly tableMetadataCache: TableMetadataCache;\r\n /**\r\n * Client-side cache for Dataverse view metadata. Same shape as\r\n * {@link tableMetadataCache}, keyed by view id. See {@link ViewMetadataCache}.\r\n */\r\n readonly viewMetadataCache: ViewMetadataCache;\r\n /**\r\n * Client-side cache for the per-table views list (powers grid view-\r\n * pickers). See {@link ViewsForTableCache}.\r\n */\r\n readonly viewsForTableCache: ViewsForTableCache;\r\n /**\r\n * Client-side cache for the singleton environment file settings\r\n * (blocked extensions, max upload size). See\r\n * {@link EnvironmentFileSettingsCache}.\r\n */\r\n readonly environmentFileSettingsCache: EnvironmentFileSettingsCache;\r\n /** Current auth state. {@link useAuth} unpacks this directly. */\r\n readonly authState: AuthState;\r\n /** Refreshes the auth state by re-calling `/api/auth/me`. */\r\n readonly refreshAuth: () => Promise<void>;\r\n /** Submits credentials. On success the auth state transitions to `authenticated` automatically. */\r\n readonly login: (request: LoginRequest) => Promise<LoginResponse>;\r\n /** Clears the auth cookie and transitions the auth state to `anonymous`. */\r\n readonly logout: () => Promise<void>;\r\n /**\r\n * Swaps the auth cookie for the user's alt-identity sibling (the\r\n * contact↔systemuser pair stamped via `PortalClaimTypes.AltIdentity*`).\r\n * On `SwitchIdentityResult.Switched` the in-memory auth state is refreshed\r\n * from `/api/auth/me`; other outcomes leave it unchanged so the caller can\r\n * surface a message to the user. The endpoint takes no parameters — the\r\n * alt id is read from the cookie's claims server-side.\r\n */\r\n readonly switchIdentity: () => Promise<SwitchIdentityResponse>;\r\n}\r\n\r\nexport const PowerPortalsProContext = createContext<PowerPortalsProContextValue | null>(null);\r\n","import type {\r\n components,\r\n PowerPortalsProClient,\r\n TableMetadata,\r\n ViewMetadata,\r\n} from '@powerportalspro/core';\r\nimport type {\r\n EnvironmentFileSettingsCache,\r\n TableMetadataCache,\r\n ViewMetadataCache,\r\n ViewsForTableCache,\r\n} from './types.js';\r\n\r\ntype EnvironmentFileSettings = components['schemas']['EnvironmentFileSettings'];\r\n\r\n/**\r\n * Shared internal helper — single in-memory cache class behind both the\r\n * table and view metadata caches. Stores resolved values in a Map; tracks\r\n * in-flight promises in a sibling Map so concurrent calls for the same\r\n * key share a single underlying fetch. On fetch error the in-flight slot\r\n * is evicted so the next caller can retry rather than getting a stuck\r\n * rejection.\r\n *\r\n * Not exported directly — consumers see the {@link TableMetadataCache} /\r\n * {@link ViewMetadataCache} interfaces; this is the implementation\r\n * behind the default cache instances the provider mounts.\r\n */\r\nclass InMemoryCache<TValue> {\r\n private readonly resolved = new Map<string, TValue>();\r\n private readonly inFlight = new Map<string, Promise<TValue | null>>();\r\n\r\n constructor(private readonly fetcher: (key: string) => Promise<TValue | null>) {}\r\n\r\n async getAsync(key: string): Promise<TValue | null> {\r\n const cached = this.resolved.get(key);\r\n if (cached !== undefined) return cached;\r\n const pending = this.inFlight.get(key);\r\n if (pending) return pending;\r\n const promise = (async () => {\r\n try {\r\n const result = await this.fetcher(key);\r\n if (result !== null) this.resolved.set(key, result);\r\n return result;\r\n } finally {\r\n this.inFlight.delete(key);\r\n }\r\n })();\r\n this.inFlight.set(key, promise);\r\n return promise;\r\n }\r\n\r\n invalidate(key: string): void {\r\n this.resolved.delete(key);\r\n // In-flight calls are deliberately left alone — they'll resolve and\r\n // re-populate. `invalidate` semantics is \"drop the cached value\", not\r\n // \"abort the in-flight fetch.\"\r\n }\r\n\r\n clear(): void {\r\n this.resolved.clear();\r\n }\r\n}\r\n\r\n/**\r\n * Default in-memory implementation of {@link TableMetadataCache}. Wraps\r\n * a `PowerPortalsProClient.retrieveTableMetadataAsync` call with Map-based\r\n * dedup. One instance per `PowerPortalsProClient` — the\r\n * `PowerPortalsProProvider` constructs it.\r\n */\r\nexport class InMemoryTableMetadataCache implements TableMetadataCache {\r\n private readonly cache: InMemoryCache<TableMetadata>;\r\n\r\n constructor(ppp: PowerPortalsProClient) {\r\n this.cache = new InMemoryCache<TableMetadata>((tableLogicalName) =>\r\n ppp.retrieveTableMetadataAsync(tableLogicalName),\r\n );\r\n }\r\n\r\n getAsync(tableLogicalName: string): Promise<TableMetadata | null> {\r\n return this.cache.getAsync(tableLogicalName);\r\n }\r\n\r\n invalidate(tableLogicalName: string): void {\r\n this.cache.invalidate(tableLogicalName);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n}\r\n\r\n/**\r\n * Default in-memory implementation of {@link ViewMetadataCache}. Same\r\n * shape as {@link InMemoryTableMetadataCache}, keyed by view id.\r\n */\r\nexport class InMemoryViewMetadataCache implements ViewMetadataCache {\r\n private readonly cache: InMemoryCache<ViewMetadata>;\r\n\r\n constructor(ppp: PowerPortalsProClient) {\r\n this.cache = new InMemoryCache<ViewMetadata>((viewId) =>\r\n ppp.retrieveViewMetadataAsync(viewId),\r\n );\r\n }\r\n\r\n getAsync(viewId: string): Promise<ViewMetadata | null> {\r\n return this.cache.getAsync(viewId);\r\n }\r\n\r\n invalidate(viewId: string): void {\r\n this.cache.invalidate(viewId);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n}\r\n\r\n/**\r\n * Default in-memory implementation of {@link EnvironmentFileSettingsCache}.\r\n * Singleton-shaped: no key, just one stored value + one in-flight promise.\r\n *\r\n * Implemented as a thin wrapper around the keyed {@link InMemoryCache}\r\n * with a fixed sentinel key, so the dedup / fetch / error-eviction\r\n * behaviors stay identical to the metadata caches without re-implementing\r\n * any of that logic.\r\n */\r\nexport class InMemoryEnvironmentFileSettingsCache\r\n implements EnvironmentFileSettingsCache\r\n{\r\n private static readonly KEY = 'environment-file-settings';\r\n private readonly cache: InMemoryCache<EnvironmentFileSettings>;\r\n\r\n constructor(ppp: PowerPortalsProClient) {\r\n this.cache = new InMemoryCache<EnvironmentFileSettings>(() =>\r\n ppp.getEnvironmentFileSettingsAsync(),\r\n );\r\n }\r\n\r\n getAsync(): Promise<EnvironmentFileSettings | null> {\r\n return this.cache.getAsync(InMemoryEnvironmentFileSettingsCache.KEY);\r\n }\r\n\r\n invalidate(): void {\r\n this.cache.invalidate(InMemoryEnvironmentFileSettingsCache.KEY);\r\n }\r\n}\r\n\r\n/**\r\n * Default in-memory implementation of {@link ViewsForTableCache}. Wraps\r\n * the per-table views-list endpoint with the same in-memory + in-flight\r\n * dedup machinery the metadata caches use. Coerces a `null` fetch result\r\n * (table not found / 404) to an empty list so consumers can treat the\r\n * absence uniformly without branching on null.\r\n */\r\nexport class InMemoryViewsForTableCache implements ViewsForTableCache {\r\n private readonly cache: InMemoryCache<readonly ViewMetadata[]>;\r\n\r\n constructor(ppp: PowerPortalsProClient) {\r\n this.cache = new InMemoryCache<readonly ViewMetadata[]>(async (tableLogicalName) => {\r\n const result = await ppp.retrieveViewsForTableAsync(tableLogicalName);\r\n return result ?? [];\r\n });\r\n }\r\n\r\n async getAsync(tableLogicalName: string): Promise<readonly ViewMetadata[]> {\r\n const result = await this.cache.getAsync(tableLogicalName);\r\n return result ?? [];\r\n }\r\n\r\n invalidate(tableLogicalName: string): void {\r\n this.cache.invalidate(tableLogicalName);\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n}\r\n","import { useContext } from 'react';\r\nimport { PowerPortalsProContext, type PowerPortalsProContextValue } from './context.js';\r\n\r\n/**\r\n * Returns the {@link AuthClient}, {@link PowerPortalsProClient}, and underlying\r\n * {@link Transport} from the nearest {@link PowerPortalsProProvider}. Use this when\r\n * you need to call a method that doesn't have a dedicated hook (the higher-level\r\n * `useAuth` / `useRecord` / `useFetchRecords` etc. cover the common cases).\r\n *\r\n * Throws if there's no provider above the calling component.\r\n */\r\nexport function usePowerPortalsPro(): PowerPortalsProContextValue {\r\n const ctx = useContext(PowerPortalsProContext);\r\n if (!ctx) {\r\n throw new Error(\r\n 'usePowerPortalsPro must be used inside <PowerPortalsProProvider>. Wrap your app at or near the root.',\r\n );\r\n }\r\n return ctx;\r\n}\r\n","import type { PowerPortalsProClient } from '@powerportalspro/core';\r\nimport {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n useSyncExternalStore,\r\n type ReactNode,\r\n} from 'react';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\n/**\r\n * Headless localizer contract. The framework calls `t(key)` to fetch a\r\n * localized string; consumers can plug in any implementation that satisfies\r\n * this interface — the bundled default ({@link createLocalizer}) reads from a\r\n * flat key→string map, but an i18next-backed adapter, a hardcoded English\r\n * map for tests, or a server-driven RPC localizer all work the same way.\r\n *\r\n * Args use positional indexing (`args[0]` → `{0}` in the template) to match\r\n * the existing Blazor `IStringLocalizer` JSON format — `.NET String.Format`\r\n * placeholders. Consumers using a named-args lib (e.g. i18next's `{{name}}`)\r\n * adapt at the boundary.\r\n */\r\nexport interface Localizer {\r\n t(key: string, args?: readonly unknown[]): string;\r\n}\r\n\r\nconst LocalizerContext = createContext<Localizer | null>(null);\r\nLocalizerContext.displayName = 'Localizer';\r\n\r\n/**\r\n * Reads the nearest {@link Localizer}. Throws when called outside a provider —\r\n * mirrors `useDialogService`'s strict behavior. The framework auto-mounts a\r\n * default `<DefaultLocalizerProvider>` inside `<PowerPortalsProProvider>`, so\r\n * this only fails when neither is present (typically a misconfigured test).\r\n */\r\nexport function useLocalizer(): Localizer {\r\n const ctx = useContext(LocalizerContext);\r\n if (!ctx) {\r\n throw new Error(\r\n 'useLocalizer must be used inside a <LocalizerProvider>. ' +\r\n 'PowerPortalsProProvider auto-mounts a default localizer; ensure your tree is wrapped.',\r\n );\r\n }\r\n return ctx;\r\n}\r\n\r\n/**\r\n * Sugar over `useLocalizer().t` — returns the bound `t` function directly so\r\n * components can write `const t = useT();` and then `t('app.buttons.save.label')`.\r\n */\r\nexport function useT(): Localizer['t'] {\r\n return useLocalizer().t;\r\n}\r\n\r\n/**\r\n * Returns a {@link Localizer.t} function with every key automatically\r\n * prepended with `{prefix}.`. React analog of Blazor's\r\n * `IPrefixedStringLocalizer` / `IStringLocalizer<T>` — use when a\r\n * component consumes strings from a known namespace (typically another\r\n * framework's class-scoped keys) and you don't want to repeat the\r\n * prefix at every call site.\r\n *\r\n * Pure sugar over `useT()` — relies on the same auto-mounted\r\n * `<DefaultLocalizerProvider>` and inherits all of its behavior\r\n * (cache, locale tracking, missing-key warnings). The bundle still\r\n * needs the full unprefixed key; this just saves the caller from\r\n * writing it out each time.\r\n *\r\n * @example\r\n * ```tsx\r\n * const t = usePrefixedT(\r\n * 'components.PowerPortalsPro.Web.Blazor.FluentUI.Components.Theme.ThemeColorSelector',\r\n * );\r\n * t('OfficeColor.0');\r\n * // → looks up\r\n * // 'components.PowerPortalsPro.Web.Blazor.FluentUI.Components.Theme.ThemeColorSelector.OfficeColor.0'\r\n * ```\r\n *\r\n * The returned function is stable per `prefix` value (memoized via\r\n * `useCallback`), so it's safe to reference from effect dependency\r\n * arrays. Passing a fresh literal each render reuses the same identity\r\n * as long as the string is identical.\r\n */\r\nexport function usePrefixedT(prefix: string): Localizer['t'] {\r\n const t = useT();\r\n return useCallback(\r\n (key: string, args?: readonly unknown[]) => t(`${prefix}.${key}`, args),\r\n [t, prefix],\r\n );\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Prefetcher — declarative API for components / routes to declare which\r\n// per-resource localization bundles they need. The default localizer\r\n// provider auto-fetches on cache miss, but the miss happens DURING render,\r\n// which produces a brief flash of raw keys. The prefetcher API lets a\r\n// caller queue the fetch up front so the strings are in cache (or have\r\n// been attempted) before the consuming render runs.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Imperative contract for queueing per-resource localization bundles. The\r\n * default localizer provider sets one of these on the\r\n * {@link LocalizationPrefetcherContext}; custom adapters set via\r\n * `<LocalizerProvider value={…}>` don't have to provide one — the public\r\n * hooks degrade gracefully (treat everything as \"loaded\") when the\r\n * prefetcher is absent.\r\n */\r\nexport interface LocalizationPrefetcher {\r\n /**\r\n * Queue the supplied prefixes for fetch via the existing debounce / batch\r\n * mechanism. Prefixes already fetched (or in flight) are silently skipped\r\n * — repeated calls with the same prefixes don't generate redundant network\r\n * traffic. Returns immediately; the actual fetch happens after the next\r\n * flush tick.\r\n */\r\n prefetch(prefixes: readonly string[]): void;\r\n\r\n /**\r\n * Returns `true` once every supplied prefix has been *settled* — fetched\r\n * successfully OR a failed attempt has resolved. Failures count as settled\r\n * deliberately: a flaky bundle endpoint shouldn't strand consumers waiting\r\n * forever. Per-fetch errors are logged via `console.warn` by the\r\n * underlying flush so they remain debuggable.\r\n *\r\n * Prefixes not covered by a per-resource bundle (anything outside\r\n * `tables.{name}` / `views.{viewId}` shape) are treated as already loaded\r\n * — those keys live in the default bundle that loads on app boot.\r\n */\r\n isLoaded(prefixes: readonly string[]): boolean;\r\n\r\n /**\r\n * Subscribe to settled-set changes. The listener fires synchronously once\r\n * per flush, after the settled set is updated. Used by\r\n * {@link useLocalizationLoaded} to re-render subscribers; advanced\r\n * consumers can hook in directly. Returns an unsubscribe function.\r\n */\r\n subscribe(listener: () => void): () => void;\r\n}\r\n\r\nconst LocalizationPrefetcherContext = createContext<LocalizationPrefetcher | null>(null);\r\nLocalizationPrefetcherContext.displayName = 'LocalizationPrefetcher';\r\n\r\n/**\r\n * Reads the active {@link LocalizationPrefetcher}. Returns `null` when no\r\n * provider is mounted (custom-adapter setups). Most consumers should use\r\n * the higher-level {@link useLocalization} / {@link useLocalizationLoaded}\r\n * hooks; this is the raw escape hatch.\r\n */\r\nexport function useLocalizationPrefetcher(): LocalizationPrefetcher | null {\r\n return useContext(LocalizationPrefetcherContext);\r\n}\r\n\r\nconst NOOP_UNSUBSCRIBE = (): void => undefined;\r\n\r\n/**\r\n * Declares localization prefixes the calling component (or its subtree)\r\n * needs. The framework queues them via the existing debounced fetch\r\n * pipeline — by the time the user wanders into a component that calls\r\n * `t('tables.account.label')`, the fetch is either complete or in flight,\r\n * collapsing the brief flash of raw keys.\r\n *\r\n * Fire-and-forget — does not block render or expose loading state. Pair\r\n * with `<LocalizationBoundary>` (from `@powerportalspro/react-fluent`) or\r\n * {@link useLocalizationLoaded} when you need to hold off rendering until\r\n * the strings are ready.\r\n *\r\n * Accepts prefix or full-key shapes:\r\n * - `tables.{tablename}` (token) → loads `tables.<tablename>.*` strings\r\n * - `tables.account.label` (key) → coerced to `tables.account`\r\n * - `views.{viewId}` (token) → loads `views.<viewId>.*` strings\r\n * - `tables.account.views.{viewId}.foo` (key) → coerced to `views.<viewId>`\r\n * - Anything else (`app.*`, `components.*`) → silently dropped; covered\r\n * by the default bundle loaded on app boot.\r\n *\r\n * Re-fires only when `prefixes` changes by VALUE — passing a fresh array\r\n * with the same content each render doesn't requeue. Already-fetched\r\n * prefixes are deduped at the prefetcher layer regardless, so calling\r\n * `useLocalization(['tables.account'])` from two components on the same\r\n * page produces one network round-trip total.\r\n *\r\n * No-op when no `<DefaultLocalizerProvider>` ancestor is mounted (consumer\r\n * is on a custom localizer adapter).\r\n *\r\n * @example\r\n * ```tsx\r\n * function MyAccountPage() {\r\n * useLocalization(['tables.account', 'tables.contact']);\r\n * return <Form>...</Form>;\r\n * }\r\n * ```\r\n */\r\nexport function useLocalization(prefixes: readonly string[]): void {\r\n const prefetcher = useLocalizationPrefetcher();\r\n // Canonicalize the prefix list to a stable string key so an inline\r\n // `[…]` literal at the call site doesn't refire the effect every\r\n // render. Sorted + deduped so order-only differences also collapse.\r\n const prefixesKey = useMemo(() => {\r\n if (prefixes.length === 0) return '';\r\n const unique = Array.from(new Set(prefixes));\r\n unique.sort();\r\n return unique.join('\\n');\r\n }, [prefixes]);\r\n\r\n useEffect(() => {\r\n if (!prefetcher) return;\r\n if (prefixesKey === '') return;\r\n const tokens = prefixesKey.split('\\n');\r\n // Queue once on mount.\r\n prefetcher.prefetch(tokens);\r\n // Re-queue on every prefetcher notify. The notify fires whenever the\r\n // provider clears its settled set (e.g. locale change) — that's the\r\n // exact moment we need to re-declare these prefixes for the new\r\n // locale. It also fires after each per-resource flush completes;\r\n // those calls are dedup'd by the prefetcher (tokens already in\r\n // tried/pending/settled don't re-fetch), so the overhead is one\r\n // Set-membership check per token per notify. We can't rely on a\r\n // `locale` dep here because React fires child effects BEFORE the\r\n // provider's tier-1 effect clears its state, so a single re-queue\r\n // would still run against the OLD locale's tried-tokens and skip.\r\n // Subscribing routes the re-queue through the provider's notify\r\n // (called AFTER clearing) so we see the freshly-empty state.\r\n return prefetcher.subscribe(() => {\r\n prefetcher.prefetch(tokens);\r\n });\r\n }, [prefetcher, prefixesKey]);\r\n}\r\n\r\n/**\r\n * Reactive boolean — `true` once every prefix in the array has been settled\r\n * (fetched or failed). Re-renders the calling component when the state\r\n * flips. Treats a missing prefetcher (custom-adapter consumer) as \"always\r\n * loaded\" so dependent components degrade gracefully.\r\n *\r\n * Powers `<LocalizationBoundary>` and is exported for consumers that want\r\n * to drive their own gating UX — e.g. a loading row inside a grid rather\r\n * than holding off the whole subtree.\r\n *\r\n * @example\r\n * ```tsx\r\n * function MyForm() {\r\n * const ready = useLocalizationLoaded(['tables.account']);\r\n * if (!ready) return <Spinner />;\r\n * return <Form>...</Form>;\r\n * }\r\n * ```\r\n */\r\nexport function useLocalizationLoaded(prefixes: readonly string[]): boolean {\r\n const prefetcher = useLocalizationPrefetcher();\r\n // Stable subscribe — the prefetcher object identity is stable across\r\n // renders (built once on mount in DefaultLocalizerProvider), so this\r\n // closure stays stable for useSyncExternalStore's diffing.\r\n const subscribe = useCallback(\r\n (listener: () => void) => prefetcher?.subscribe(listener) ?? NOOP_UNSUBSCRIBE,\r\n [prefetcher],\r\n );\r\n const getSnapshot = useCallback(\r\n () => (prefetcher ? prefetcher.isLoaded(prefixes) : true),\r\n [prefetcher, prefixes],\r\n );\r\n // SSR snapshot — always \"loaded\" so server-rendered content renders\r\n // immediately and the client hydrates without a fallback flash.\r\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshotAlwaysLoaded);\r\n}\r\n\r\nconst getServerSnapshotAlwaysLoaded = (): boolean => true;\r\n\r\n/**\r\n * Provides an explicit {@link Localizer} to the subtree. Used internally by\r\n * {@link DefaultLocalizerProvider}; consumers wanting to swap in i18next or\r\n * any other implementation wrap their tree with this and pass their adapter\r\n * as `value`.\r\n */\r\nexport function LocalizerProvider({\r\n value,\r\n children,\r\n}: {\r\n value: Localizer;\r\n children: ReactNode;\r\n}) {\r\n return <LocalizerContext.Provider value={value}>{children}</LocalizerContext.Provider>;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Default implementation — flat-map lookup with positional `{0}` interpolation.\r\n// Matches the Blazor `IStringLocalizer` JSON format directly so the same\r\n// `ppp-server.defaults.en.json` file can drive both stacks.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Flattens a nested object (Blazor's localization JSON shape) into a flat\r\n * `key.path → value` map. Drops non-string leaves (no use for them in the\r\n * localizer's lookup).\r\n */\r\nexport function flattenStrings(\r\n obj: Record<string, unknown>,\r\n prefix = '',\r\n): Record<string, string> {\r\n const out: Record<string, string> = {};\r\n for (const [k, v] of Object.entries(obj)) {\r\n const key = prefix ? `${prefix}.${k}` : k;\r\n if (typeof v === 'string') {\r\n out[key] = v;\r\n } else if (v !== null && typeof v === 'object' && !Array.isArray(v)) {\r\n Object.assign(out, flattenStrings(v as Record<string, unknown>, key));\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n/**\r\n * Substitutes `{0}`, `{1}`, etc. with the corresponding positional arg.\r\n * Strips `.NET`-style format specifiers (`{0:N0}`, `{1:yyyy-MM-dd}`) since\r\n * those are .NET-only and don't translate to JS without a custom formatter\r\n * pipeline. Consumers needing locale-aware number / date display should\r\n * format their values via `Intl.NumberFormat` / `Intl.DateTimeFormat` before\r\n * passing them to `t()`.\r\n *\r\n * Out-of-range placeholders are left as-is (visible in the rendered string)\r\n * so the missing arg surfaces during dev.\r\n */\r\nexport function interpolate(template: string, args?: readonly unknown[]): string {\r\n if (!args || args.length === 0) return template;\r\n return template.replace(/\\{(\\d+)(?::[^}]*)?\\}/g, (match, indexStr) => {\r\n const idx = Number(indexStr);\r\n if (idx < 0 || idx >= args.length) return match;\r\n return String(args[idx]);\r\n });\r\n}\r\n\r\n/**\r\n * Optional behavior knobs for {@link createLocalizer}.\r\n */\r\nexport interface CreateLocalizerOptions {\r\n /**\r\n * Called once per missing key the first time it's looked up. Use to wire\r\n * dev-mode warnings, telemetry, or to fail loudly during tests. The hook\r\n * is invoked AFTER the localizer falls back to returning the raw key.\r\n *\r\n * Each key is reported at most once per localizer instance — repeat\r\n * lookups for the same missing key don't fire it again. This keeps a\r\n * deeply-rendered missing key from spamming the console.\r\n */\r\n onMissingKey?: (key: string) => void;\r\n}\r\n\r\n/**\r\n * Builds a {@link Localizer} backed by a flat string map. Missing keys\r\n * return the key itself (visible fallback for missing strings — easier to\r\n * spot than silently showing nothing). Pass `onMissingKey` to get a hook\r\n * that fires once per missing key.\r\n */\r\nexport function createLocalizer(\r\n strings: Record<string, string>,\r\n options?: CreateLocalizerOptions,\r\n): Localizer {\r\n // Track keys we've already warned about so each missing key fires\r\n // `onMissingKey` exactly once per localizer instance — keeps `t('foo')`\r\n // called from N rendered components from spamming the console N times.\r\n const warnedKeys = new Set<string>();\r\n return {\r\n t(key, args) {\r\n const template = strings[key];\r\n if (template === undefined) {\r\n if (options?.onMissingKey && !warnedKeys.has(key)) {\r\n warnedKeys.add(key);\r\n options.onMissingKey(key);\r\n }\r\n return key;\r\n }\r\n return interpolate(template, args);\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Fetches a JSON file and returns its parsed nested object. Throws on\r\n * non-2xx response. Internal helper for {@link DefaultLocalizerProvider}'s\r\n * multi-URL merge.\r\n */\r\nasync function fetchLocalizationJson(url: string): Promise<Record<string, unknown>> {\r\n const response = await fetch(url);\r\n if (!response.ok) throw new Error(`HTTP ${response.status} fetching ${url}`);\r\n return (await response.json()) as Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Substitutes the `{locale}` placeholder in a URL template. URLs without\r\n * the placeholder pass through unchanged — useful for static-locale\r\n * deployments where every locale uses the same URL.\r\n */\r\nexport function resolveLocalizationUrl(template: string, locale: string): string {\r\n return template.replace(/\\{locale\\}/g, locale);\r\n}\r\n\r\n/**\r\n * Cookie name ASP.NET Core's `CookieRequestCultureProvider` uses to persist\r\n * the active culture. Same cookie the Blazor stack sets via the\r\n * <c>/Culture/{culture}</c> endpoint, so reading/writing it here keeps the\r\n * React and Blazor stacks in sync when both are hosted side-by-side.\r\n */\r\nexport const ASP_NET_CULTURE_COOKIE = '.AspNetCore.Culture';\r\n\r\n/**\r\n * Reads the ASP.NET Core culture cookie. The cookie value follows the\r\n * `c={culture}|uic={uiCulture}` shape (URL-encoded) — we parse the `c`\r\n * segment. Returns `null` when the cookie isn't set or can't be parsed.\r\n *\r\n * Safe to call in non-browser environments (returns `null`); both `document`\r\n * and `document.cookie` are guarded.\r\n */\r\nexport function getAspNetCultureCookie(): string | null {\r\n if (typeof document === 'undefined' || !document.cookie) return null;\r\n for (const part of document.cookie.split('; ')) {\r\n const eq = part.indexOf('=');\r\n if (eq < 0) continue;\r\n const name = part.substring(0, eq);\r\n if (name !== ASP_NET_CULTURE_COOKIE) continue;\r\n const value = decodeURIComponent(part.substring(eq + 1));\r\n // Standard ASP.NET shape: `c=en|uic=en`. Either segment alone (`c=en`,\r\n // raw `en`) is also accepted as a fallback.\r\n const cMatch = value.match(/c=([^|]+)/);\r\n if (cMatch?.[1]) return cMatch[1];\r\n return value || null;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Writes the ASP.NET Core culture cookie with both `c` and `uic` segments\r\n * set to the same value. Mirrors what the server-side\r\n * <c>/Culture/{culture}</c> endpoint sets, so a React-side locale change\r\n * immediately affects subsequent server-rendered Blazor pages on the same\r\n * origin (and vice versa).\r\n *\r\n * Defaults to `path=/`, 1-year expiry, `SameSite=Lax`. Safe to call in\r\n * non-browser environments (no-op).\r\n */\r\nexport function setAspNetCultureCookie(\r\n culture: string,\r\n options?: { path?: string; maxAgeSeconds?: number },\r\n): void {\r\n if (typeof document === 'undefined') return;\r\n const path = options?.path ?? '/';\r\n const maxAge = options?.maxAgeSeconds ?? 31_536_000; // 1 year\r\n const value = encodeURIComponent(`c=${culture}|uic=${culture}`);\r\n document.cookie = `${ASP_NET_CULTURE_COOKIE}=${value}; path=${path}; max-age=${maxAge}; samesite=lax`;\r\n}\r\n\r\n/**\r\n * Default regex matching a locale code at the start of `window.location.pathname` —\r\n * shapes like `/en`, `/en/`, `/en-US/page`, `/fr-CA/path/x`. Captures the\r\n * locale code in group 1. Only 2-letter primary tags + optional 2–4-letter\r\n * subtag (script / region) are recognized; arbitrary path segments don't\r\n * trigger a false match.\r\n */\r\nexport const DEFAULT_URL_LOCALE_PATTERN = /^\\/([a-z]{2}(?:-[a-zA-Z]{2,4})?)(?:\\/|$)/;\r\n\r\n/**\r\n * Reads the locale from the URL path's first segment using\r\n * {@link DEFAULT_URL_LOCALE_PATTERN} (override via `pattern`). Returns the\r\n * matched locale or `null` when the URL doesn't carry one.\r\n *\r\n * Safe to call in non-browser environments (returns `null`).\r\n */\r\nexport function getLocaleFromUrlPath(pattern: RegExp = DEFAULT_URL_LOCALE_PATTERN): string | null {\r\n if (typeof window === 'undefined') return null;\r\n const match = window.location.pathname.match(pattern);\r\n return match?.[1] ?? null;\r\n}\r\n\r\n/**\r\n * Resolves the active locale from common sources, in order of precedence:\r\n *\r\n * 1. URL path segment (`/en/...`) — picked up via {@link getLocaleFromUrlPath}\r\n * when `matchUrl` is `true` (default).\r\n * 2. ASP.NET culture cookie (`.AspNetCore.Culture`) — picked up via\r\n * {@link getAspNetCultureCookie}.\r\n * 3. `navigator.language` — when `matchBrowser` is `true` (default), strips\r\n * the region (e.g. `en-US` → `en`) for compatibility with bundle locale\r\n * codes which are typically primary-tag only.\r\n * 4. The supplied `fallback` (default `'en'`).\r\n *\r\n * Used by `<LocaleProvider>` for auto-detection; also exported for consumer\r\n * code that wants the resolved locale without the provider context.\r\n */\r\nexport function detectLocale(options?: {\r\n matchUrl?: boolean;\r\n urlPattern?: RegExp;\r\n matchBrowser?: boolean;\r\n fallback?: string;\r\n}): string {\r\n const {\r\n matchUrl = true,\r\n urlPattern = DEFAULT_URL_LOCALE_PATTERN,\r\n matchBrowser = true,\r\n fallback = 'en',\r\n } = options ?? {};\r\n\r\n if (matchUrl) {\r\n const fromUrl = getLocaleFromUrlPath(urlPattern);\r\n if (fromUrl) return fromUrl;\r\n }\r\n const fromCookie = getAspNetCultureCookie();\r\n if (fromCookie) return fromCookie;\r\n if (matchBrowser && typeof navigator !== 'undefined' && navigator.language) {\r\n // Strip region: `en-US` → `en`. Bundles are typically published per\r\n // primary tag, so a hyphenated browser preference would 404 against\r\n // `supportedLocales`.\r\n const primary = navigator.language.split('-')[0];\r\n if (primary) return primary;\r\n }\r\n return fallback;\r\n}\r\n\r\n/**\r\n * Debounce window for batching cache-miss `t()` lookups before flushing\r\n * coerced tokens out to the per-table / per-view bundle endpoints. Long\r\n * enough to capture a full render pass's misses; short enough that the\r\n * user doesn't notice the delay.\r\n */\r\nconst MISSING_KEY_FLUSH_DELAY_MS = 75;\r\n\r\n/**\r\n * Sentinel token added to {@link DefaultLocalizerProvider}'s\r\n * `settledTokensRef` after the tier-1 default-bundle fetch resolves\r\n * (success or failure). Lets `isLoaded` track \"has the default\r\n * bundle been attempted?\" the same way it tracks per-resource bundle\r\n * attempts — one unified set membership check, no separate flag.\r\n *\r\n * Real prefix-derived tokens are always `tables.{name}` or\r\n * `views.{viewId}` (see {@link coercePrefixToToken}), so a sentinel\r\n * without a dot can't collide. Internal — not exported.\r\n */\r\nconst DEFAULT_BUNDLE_SENTINEL = 'defaultBundle';\r\n\r\n/**\r\n * Coerces an arbitrary localization key into the bundle token that covers\r\n * it — `tables.{name}` for any `tables.{name}.…` key, `views.{viewId}` for\r\n * any `tables.{name}.views.{viewId}.…` key — or returns `null` for keys no\r\n * per-resource bundle covers (the static default bundle handles them, or\r\n * they're genuinely unresolvable like raw `choices.*` references). Mirror\r\n * of the C# source generator's `CoerceToTableOrViewToken` so build-time\r\n * and runtime agree on which keys map to which bundle.\r\n */\r\nfunction coerceKeyToToken(key: string): string | null {\r\n if (!key) return null;\r\n const lower = key.toLowerCase();\r\n if (!lower.startsWith('tables.')) return null;\r\n\r\n const afterTables = lower.substring('tables.'.length);\r\n const firstDot = afterTables.indexOf('.');\r\n if (firstDot < 0) return `tables.${afterTables}`;\r\n\r\n const tableName = afterTables.substring(0, firstDot);\r\n const afterTable = afterTables.substring(firstDot + 1);\r\n if (afterTable.startsWith('views.')) {\r\n const afterViews = afterTable.substring('views.'.length);\r\n const nextDot = afterViews.indexOf('.');\r\n const viewId = nextDot < 0 ? afterViews : afterViews.substring(0, nextDot);\r\n if (!viewId) return null;\r\n return `views.${viewId}`;\r\n }\r\n return `tables.${tableName}`;\r\n}\r\n\r\n/**\r\n * Like {@link coerceKeyToToken} but additionally accepts inputs that\r\n * are already in TOKEN shape — `tables.{name}` and `views.{viewId}`.\r\n * Used by the prefetcher's public API where callers naturally pass\r\n * prefixes (\"the namespace they want loaded\") rather than full keys.\r\n *\r\n * `coerceKeyToToken` itself stays as-is because its comment pins it\r\n * as the byte-for-byte runtime mirror of the C# source generator's\r\n * `CoerceToTableOrViewToken` — that path only ever sees full keys\r\n * (from missed `t()` lookups), not bare tokens.\r\n */\r\nfunction coercePrefixToToken(prefix: string): string | null {\r\n if (!prefix) return null;\r\n const lower = prefix.toLowerCase();\r\n\r\n // Direct `views.*` shape — caller passed a view token (or a key\r\n // under one). Extract the viewId segment, ignoring anything after.\r\n if (lower.startsWith('views.')) {\r\n const afterViews = lower.substring('views.'.length);\r\n const nextDot = afterViews.indexOf('.');\r\n const viewId = nextDot < 0 ? afterViews : afterViews.substring(0, nextDot);\r\n return viewId ? `views.${viewId}` : null;\r\n }\r\n\r\n // `tables.*` shape — delegate to the existing key-coercion logic,\r\n // which already handles `tables.{name}` (token), `tables.{name}.{key}`\r\n // (key), and `tables.{name}.views.{viewId}.{key}` (nested-view key).\r\n return coerceKeyToToken(prefix);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Locale state — independent of the localizer itself. Consumer apps that\r\n// want a global \"current locale\" use the LocaleProvider; the\r\n// DefaultLocalizerProvider reads from it to drive URL substitution.\r\n// Apps with their own state management can skip the provider and pass\r\n// resolved URLs directly to DefaultLocalizerProvider.\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface LocaleState {\r\n /** Current locale code (e.g. `'en'`, `'es'`, `'fr-CA'`). */\r\n readonly locale: string;\r\n /** Replace the active locale. Triggers re-fetch of localization JSONs. */\r\n readonly setLocale: (locale: string) => void;\r\n}\r\n\r\nconst LocaleContext = createContext<LocaleState | null>(null);\r\nLocaleContext.displayName = 'Locale';\r\n\r\n/**\r\n * Manifest data populated by {@link DefaultLocalizerProvider} once its\r\n * tier-1 fetch completes. Exposed through a separate context (rather\r\n * than {@link LocaleContext}) because {@link LocaleProvider} doesn't\r\n * know about the manifest — only the localizer provider does — and\r\n * because the data is `undefined` until the network round-trip lands.\r\n *\r\n * Consumed by {@link useSupportedLocales} so callers like\r\n * `<LanguageDropdown>` can read the supported-locale list without\r\n * issuing their own redundant `/localizations/version` fetch on every\r\n * mount. Falls back to a direct fetch when no\r\n * {@link DefaultLocalizerProvider} is mounted (rare — consumers who\r\n * opted out of the framework localizer entirely).\r\n */\r\nexport interface LocalizationManifestState {\r\n readonly supportedLocales: readonly string[] | undefined;\r\n}\r\nexport const LocalizationManifestContext = createContext<LocalizationManifestState | null>(null);\r\nLocalizationManifestContext.displayName = 'LocalizationManifest';\r\n\r\n/**\r\n * Reads the active locale state, falling back to `{ locale: 'en' }` when no\r\n * `<LocaleProvider>` is mounted. Apps that don't need locale switching can\r\n * skip the provider — the framework treats the default locale as `'en'`.\r\n */\r\nexport function useLocale(): LocaleState {\r\n const ctx = useContext(LocaleContext);\r\n return ctx ?? { locale: 'en', setLocale: noopSetLocale };\r\n}\r\n\r\nconst noopSetLocale = (_: string): void => undefined;\r\n\r\nexport interface LocaleProviderProps {\r\n /**\r\n * Initial locale on mount. Used as the FALLBACK when auto-detection is\r\n * enabled (the default) and finds no URL / cookie / browser hint, OR as\r\n * the literal initial value when `autoDetect={false}`. Defaults to `'en'`.\r\n */\r\n initialLocale?: string;\r\n /**\r\n * Auto-detect the active locale via {@link detectLocale} on mount. Default\r\n * `true`. When enabled, the resolution order is:\r\n * URL path → ASP.NET culture cookie → `navigator.language` → `initialLocale`.\r\n * Pass `false` to start from `initialLocale` ignoring the environment.\r\n */\r\n autoDetect?: boolean;\r\n /**\r\n * Override the URL pattern used by `autoDetect`. Defaults to\r\n * {@link DEFAULT_URL_LOCALE_PATTERN}. Set to a custom RegExp when your\r\n * routes use a non-standard locale-segment shape, or pass `null` to\r\n * disable URL-based detection (only cookie + browser will be checked).\r\n */\r\n urlPattern?: RegExp | null;\r\n /**\r\n * Persist locale changes to the ASP.NET culture cookie (the same cookie\r\n * Blazor's locale switcher writes via `/Culture/{culture}`). Default\r\n * `true` — keeps the React + Blazor stacks in sync when both render\r\n * pages on the same origin. Pass `false` to manage persistence yourself\r\n * (e.g. via the `onLocaleChange` callback).\r\n */\r\n persistToCookie?: boolean;\r\n /**\r\n * Optional controlled locale. When provided, the component is controlled\r\n * and `setLocale` calls are forwarded via {@link onLocaleChange}. When\r\n * omitted, the component is uncontrolled with internal state.\r\n */\r\n locale?: string;\r\n /**\r\n * Called when `setLocale` is invoked. Required when {@link locale} is\r\n * provided (controlled mode); ignored otherwise.\r\n */\r\n onLocaleChange?: (locale: string) => void;\r\n children: ReactNode;\r\n}\r\n\r\n/**\r\n * Provides the active locale + a setter to the subtree. Descendants read via\r\n * {@link useLocale}; the {@link DefaultLocalizerProvider} subscribes\r\n * automatically and re-fetches its JSONs when the locale changes.\r\n *\r\n * Supports both controlled (`locale` + `onLocaleChange` props) and\r\n * uncontrolled (`initialLocale` only, internal state) modes — same pattern\r\n * as Fluent UI's controlled inputs.\r\n *\r\n * Example:\r\n * ```tsx\r\n * <LocaleProvider initialLocale=\"en\">\r\n * <PowerPortalsProProvider>\r\n * <App /> {/* useLocale() inside returns { locale: 'en', setLocale: ... } *\\/}\r\n * </PowerPortalsProProvider>\r\n * </LocaleProvider>\r\n * ```\r\n */\r\nexport function LocaleProvider({\r\n initialLocale = 'en',\r\n autoDetect = true,\r\n urlPattern,\r\n persistToCookie = true,\r\n locale: controlledLocale,\r\n onLocaleChange,\r\n children,\r\n}: LocaleProviderProps) {\r\n // Compute the initial locale ONCE on mount. `useState`'s lazy initializer\r\n // runs the detection without recomputing on subsequent renders — switching\r\n // back to the URL/cookie value mid-session would surprise consumers who\r\n // expect `setLocale` to be the only mechanism that changes the active locale.\r\n const [internalLocale, setInternalLocale] = useState(() => {\r\n if (!autoDetect) return initialLocale;\r\n return detectLocale({\r\n matchUrl: urlPattern !== null,\r\n ...(urlPattern instanceof RegExp && { urlPattern }),\r\n fallback: initialLocale,\r\n });\r\n });\r\n const isControlled = controlledLocale !== undefined;\r\n const locale = isControlled ? controlledLocale : internalLocale;\r\n\r\n const setLocale = useCallback(\r\n (next: string) => {\r\n if (!isControlled) setInternalLocale(next);\r\n // Persist to the ASP.NET culture cookie BEFORE invoking the consumer\r\n // callback, so a consumer that triggers a navigation in `onLocaleChange`\r\n // (e.g. routing to /es/...) sees the cookie already updated.\r\n if (persistToCookie) setAspNetCultureCookie(next);\r\n onLocaleChange?.(next);\r\n },\r\n [isControlled, persistToCookie, onLocaleChange],\r\n );\r\n\r\n const value = useMemo<LocaleState>(() => ({ locale, setLocale }), [locale, setLocale]);\r\n return <LocaleContext.Provider value={value}>{children}</LocaleContext.Provider>;\r\n}\r\n\r\nexport interface DefaultLocalizerProviderProps {\r\n /**\r\n * Optional additional URL templates to fetch + layer on top of the\r\n * server bundle. Each may contain `{locale}` substituted with the active\r\n * locale. Layered later-wins: bundle first, then each URL in order.\r\n *\r\n * Most apps leave this empty — the server bundle (framework defaults +\r\n * app overrides + Dataverse-metadata-derived strings outside `tables.*`)\r\n * already covers the common case. Use this for client-side overrides or\r\n * page-specific files the consumer doesn't want round-tripping through\r\n * the server.\r\n */\r\n urls?: readonly string[];\r\n /**\r\n * @deprecated Use {@link urls} instead. Kept for backwards compatibility —\r\n * when set, treated as `urls: [url]`.\r\n */\r\n url?: string;\r\n /**\r\n * Called once per missing key the first time it's looked up via `t(key)`.\r\n * Defaults to a `console.warn` so missing strings surface during dev.\r\n * Pass `() => undefined` to silence; pass a custom function to route the\r\n * warnings to telemetry.\r\n *\r\n * Fires whether or not the on-demand endpoint fetch succeeds — the hook is\r\n * about \"this key wasn't in the static cache when first seen\", not \"this\r\n * key is unresolvable globally\".\r\n */\r\n onMissingKey?: (key: string) => void;\r\n /**\r\n * Baseline localization prefixes the provider should eagerly load\r\n * alongside the default bundle on mount. Same shape as the prop on\r\n * {@link LocalizationBoundary} — `tables.{name}` / `views.{viewId}`\r\n * tokens (or full keys, coerced). The framework queues them through\r\n * the standard prefetch pipeline once the default bundle has\r\n * resolved; descendant `<LocalizationBoundary>` components\r\n * automatically wait for them as part of their readiness check, so\r\n * baseline strings the whole app needs (e.g. tables that appear in\r\n * the navigation chrome) flow into the cache before any boundary\r\n * mounts its children.\r\n *\r\n * Prefixes the default bundle already covers (`app.*`,\r\n * `components.*`) are silently dropped — they're loaded as part of\r\n * Tier 1 regardless.\r\n *\r\n * Changing this prop after mount is supported: new prefixes are\r\n * queued on the next render; removed prefixes stay cached (we don't\r\n * actively evict strings — they just won't be re-fetched on locale\r\n * change). Most apps set this once at the root and never change it.\r\n */\r\n prefixes?: readonly string[];\r\n children: ReactNode;\r\n}\r\n\r\n// Initial-state localizer used before the fetch resolves. Returns the raw\r\n// key for any lookup (visible fallback that surfaces missing strings).\r\n// Stable identity — created once at module load so React state init\r\n// doesn't allocate a new instance per provider mount. Uses no missing-key\r\n// warning so the brief pre-fetch interval doesn't spam the console.\r\nconst FALLBACK_LOCALIZER = createLocalizer({});\r\n\r\n/**\r\n * Default missing-key handler — warns once per key via `console.warn`.\r\n * Visible during dev so typos and missing JSON entries surface early.\r\n */\r\nfunction defaultMissingKeyWarning(key: string): void {\r\n // eslint-disable-next-line no-console\r\n console.warn(`PowerPortalsPro: missing localization key \"${key}\"`);\r\n}\r\n\r\n/**\r\n * Auto-loads localized strings and provides a {@link Localizer} to the\r\n * subtree using a two-tier strategy:\r\n *\r\n * **Tier 1 — manifest + default bundle (eager, on mount + locale change).**\r\n *\r\n * Fetches the manifest from `/localizations/version`, then the default\r\n * bundle from `/localizations/default/{locale}.{thumbprint}.json`. The\r\n * thumbprint segment makes each release's URL unique so the browser can\r\n * cache it `immutable`. The default bundle contains everything outside\r\n * `tables.*` / `choices.*`. Optional client-side overlay URLs (the `urls`\r\n * prop) are fetched in parallel and layered later-wins on top.\r\n *\r\n * **Tier 2 — per-resource bundle fetches (lazy, on cache miss).**\r\n *\r\n * When `t(key)` is called and the key isn't in the static cache, the key\r\n * is coerced to its owning bundle token (`tables.{name}` or `views.{viewId}`)\r\n * and queued for a debounced batched fetch (~75 ms). The flush fires one\r\n * GET per distinct token in parallel, each landing under a per-resource\r\n * thumbprinted URL (`/localizations/tables/{name}/{locale}.{thumb}.json`,\r\n * `/localizations/views/{viewId}/{locale}.{thumb}.json`). Subsequent\r\n * navigations to a page declaring the same token reuse the browser-cached\r\n * response with zero network traffic. Source-generator-emitted tokens (via\r\n * `LocalizationManifest`) flow through the same path — one fetch per\r\n * declared token, on first hit, then cached.\r\n *\r\n * **Render strategy — non-blocking.** Children render immediately with a\r\n * key-fallback localizer (every `t(key)` returns the key as plain text);\r\n * the provider swaps in real strings once the manifest + bundle resolve.\r\n * Cache misses appear as raw keys briefly until the per-resource fetch\r\n * resolves and triggers a re-render.\r\n *\r\n * **Locale changes** clear the cache, cancel any in-flight fetch, re-fetch\r\n * the manifest + bundle at the new locale, and start fresh.\r\n *\r\n * Auto-mounted by `<PowerPortalsProProvider>`. Wrap with a custom\r\n * `<LocalizerProvider>` and pass `localizationUrl={false}` on the provider\r\n * to opt out entirely (e.g. when adapting i18next).\r\n */\r\nexport function DefaultLocalizerProvider({\r\n urls,\r\n url,\r\n onMissingKey = defaultMissingKeyWarning,\r\n prefixes,\r\n children,\r\n}: DefaultLocalizerProviderProps) {\r\n const [cache, setCache] = useState<Record<string, string>>({});\r\n // Supported-locale list from the manifest fetch. `undefined` until\r\n // the tier-1 fetch lands; consumed by `useSupportedLocales` via the\r\n // `LocalizationManifestContext` below so descendants don't re-fetch\r\n // `/localizations/version` themselves.\r\n const [supportedLocales, setSupportedLocales] = useState<readonly string[] | undefined>(undefined);\r\n const { locale } = useLocale();\r\n const { ppp } = usePowerPortalsPro();\r\n\r\n // Per-render-stable refs: the latest values that flush callbacks need to\r\n // read at flush time (which can be ~75ms after the render that scheduled\r\n // the flush). Without these, the flush would close over stale state.\r\n const localeRef = useRef(locale);\r\n localeRef.current = locale;\r\n const pppRef = useRef(ppp);\r\n pppRef.current = ppp;\r\n\r\n // Manifest-derived state stashed in refs so the tier-2 flush can read it\r\n // without re-running a manifest fetch. Cleared on locale change.\r\n // - resolvedLocaleRef: the locale we actually fetch under (resolved\r\n // against manifest.supportedLocales — may differ from `locale` if the\r\n // user's preferred locale isn't supported).\r\n // - thumbprintsRef: per-resource thumbprints for that locale.\r\n const resolvedLocaleRef = useRef<string | null>(null);\r\n const thumbprintsRef = useRef<{\r\n bundle: string;\r\n tables: Readonly<Record<string, string>>;\r\n views: Readonly<Record<string, string>>;\r\n } | null>(null);\r\n\r\n // Pending cache-miss queue + dedup tracking. Refs (not state) — the queue\r\n // is consumed by a setTimeout, no need to re-render on push.\r\n // - pendingTokensRef: bundle tokens (`tables.{name}` / `views.{viewId}`)\r\n // coerced from cache-miss keys, awaiting a flush.\r\n // - triedTokensRef: tokens we've already attempted. Prevents re-queueing\r\n // the same token on every render, which would cause a flush storm.\r\n // - pendingTimerRef: handle of the scheduled flush, or null when none.\r\n const pendingTokensRef = useRef<Set<string>>(new Set());\r\n const triedTokensRef = useRef<Set<string>>(new Set());\r\n const pendingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\r\n\r\n // Settled-tokens tracking for the LocalizationPrefetcher API. Distinct\r\n // from `triedTokensRef` because tried tokens may still be in flight —\r\n // settled means \"the flush attempt has resolved, either to a cached\r\n // result or to a logged failure.\" `useLocalizationLoaded` reads this\r\n // to decide whether the boundary should mount its children.\r\n // Failures intentionally count as settled so a flaky bundle endpoint\r\n // can't permanently strand a <LocalizationBoundary>; per-fetch errors\r\n // are still surfaced via `console.warn` from `flushPending`.\r\n const settledTokensRef = useRef<Set<string>>(new Set());\r\n // Subscribers to settled-set changes. Each `useLocalizationLoaded`\r\n // call adds one via the prefetcher's `subscribe`; the flush fires them\r\n // all after updating the set. Set (not Array) so unsubscribe is O(1).\r\n const listenersRef = useRef<Set<() => void>>(new Set());\r\n\r\n // Baseline prefixes the provider was constructed with — the\r\n // `prefixes` prop. Stored as both the raw input (for re-queueing on\r\n // locale change) and pre-coerced tokens (for cheap `isLoaded`\r\n // checks that don't re-coerce on every render). Updated via the\r\n // useMemo below so prop changes flow through.\r\n const baselinePrefixesRef = useRef<readonly string[]>([]);\r\n const baselineTokensRef = useRef<readonly string[]>([]);\r\n // Stable serialization key — drives effect deps + lets baseline\r\n // tracking ignore order-only / reference-only prop churn.\r\n const baselinePrefixesKey = useMemo(() => {\r\n const input = prefixes ?? [];\r\n if (input.length === 0) {\r\n baselinePrefixesRef.current = [];\r\n baselineTokensRef.current = [];\r\n return '';\r\n }\r\n const unique = Array.from(new Set(input));\r\n unique.sort();\r\n baselinePrefixesRef.current = unique;\r\n // Coerce once per change. Null-coercing prefixes (covered by the\r\n // default bundle) are skipped — `isLoaded` doesn't need to check\r\n // them since Tier 1 already does.\r\n const tokens: string[] = [];\r\n for (const p of unique) {\r\n const t = coercePrefixToToken(p);\r\n if (t !== null) tokens.push(t);\r\n }\r\n baselineTokensRef.current = tokens;\r\n return unique.join('\\n');\r\n }, [prefixes]);\r\n\r\n // Stable notifier — used by both the flush completion and the\r\n // locale-change reset path. Snapshots the listener set before iterating\r\n // so a listener that unsubscribes itself during the callback doesn't\r\n // skip a sibling.\r\n const notifyListeners = useCallback(() => {\r\n const snapshot = Array.from(listenersRef.current);\r\n for (const listener of snapshot) listener();\r\n }, []);\r\n\r\n // Tracks the locale + overlay combo this provider was last run against.\r\n // Used by the tier-1 effect to distinguish \"initial mount\" from \"real\r\n // locale / url change\" — the cache + queue clearing at the top of the\r\n // tier-1 effect runs only on the latter. Without this, children's\r\n // useEffect-queued prefetches (which fire BEFORE the parent's effect)\r\n // get wiped immediately on mount.\r\n const previousRunRef = useRef<{ locale: string; overlayKey: string } | null>(null);\r\n\r\n // Forward-reference bridge for `flushPending` — the tier-1 effect's\r\n // async block needs to schedule a flush AFTER it completes (to drain\r\n // any baseline prefixes it queued), but `flushPending` is declared\r\n // below the effect. Storing the latest callback in a ref + reading it\r\n // through `flushPendingRef.current()` at call time gives the effect\r\n // access without reordering the function body.\r\n const flushPendingRef = useRef<() => void>(() => undefined);\r\n\r\n // Resolve any consumer-supplied overlay URL templates (rare). The bundle\r\n // endpoint provides the bulk of strings; these layer on top.\r\n const overlayTemplates = urls ?? (url !== undefined ? [url] : []);\r\n const resolvedOverlayUrls = overlayTemplates.map((t) => resolveLocalizationUrl(t, locale));\r\n const overlayUrlsKey = resolvedOverlayUrls.join('\\n');\r\n\r\n // Tier 1: manifest + default bundle + optional overlays on mount + locale change.\r\n useEffect(() => {\r\n let cancelled = false;\r\n // Locale change: clear the cache + queue + cancel any pending flush.\r\n // On the FIRST mount we skip this — children's useEffects run before\r\n // ours, and they may have already queued prefetches we'd otherwise\r\n // wipe out. Refs start fresh by JS semantics on first mount, so the\r\n // clearing would be redundant anyway.\r\n const previous = previousRunRef.current;\r\n const isLocaleOrUrlChange = previous !== null &&\r\n (previous.locale !== locale || previous.overlayKey !== overlayUrlsKey);\r\n previousRunRef.current = { locale, overlayKey: overlayUrlsKey };\r\n\r\n if (isLocaleOrUrlChange) {\r\n setCache({});\r\n // Clear the cached supported-locale list too — the new locale's\r\n // manifest fetch below repopulates it. Skipping this would leave\r\n // `useSupportedLocales` returning stale data during the\r\n // re-fetch window, which is a minor inconsistency that's easier\r\n // to avoid than to explain.\r\n setSupportedLocales(undefined);\r\n pendingTokensRef.current = new Set();\r\n triedTokensRef.current = new Set();\r\n if (pendingTimerRef.current !== null) {\r\n clearTimeout(pendingTimerRef.current);\r\n pendingTimerRef.current = null;\r\n }\r\n // Settled set clears wholesale — strings under the new locale\r\n // haven't been attempted yet. This also removes the\r\n // `DEFAULT_BUNDLE_SENTINEL` entry, so `isLoaded` flips back to\r\n // false until the new locale's default bundle resolves. Notify\r\n // subscribers so any mounted boundary flips back to its fallback\r\n // while the new-locale bundle loads.\r\n if (settledTokensRef.current.size > 0) {\r\n settledTokensRef.current = new Set();\r\n notifyListeners();\r\n }\r\n }\r\n resolvedLocaleRef.current = null;\r\n thumbprintsRef.current = null;\r\n\r\n const overlayList = overlayUrlsKey.split('\\n').filter((u) => u.length > 0);\r\n\r\n (async () => {\r\n try {\r\n // Step 1: version manifest — supported-locale codes only. Tiny.\r\n const manifest = await pppRef.current.retrieveLocalizationBundleManifestAsync();\r\n if (cancelled) return;\r\n\r\n // Stash the supported-locale list in state so descendants reading\r\n // it via `useSupportedLocales` see the manifest's data without\r\n // issuing their own `/localizations/version` fetch.\r\n setSupportedLocales(manifest.supportedLocales);\r\n\r\n // Resolve the requested locale to one the server actually has a\r\n // bundle for. Fallback chain: exact match → first supported.\r\n const resolvedLocale = manifest.supportedLocales.includes(locale)\r\n ? locale\r\n : manifest.supportedLocales[0] ?? locale;\r\n\r\n // Step 2: per-locale thumbprints. Returned from a separate endpoint\r\n // so the version response stays small regardless of how many\r\n // locales the server supports.\r\n const thumbs = await pppRef.current.retrieveLocalizationThumbprintsAsync(resolvedLocale);\r\n if (cancelled) return;\r\n\r\n resolvedLocaleRef.current = resolvedLocale;\r\n thumbprintsRef.current = thumbs;\r\n\r\n // Step 3: default bundle (via the typed client) + consumer overlays\r\n // (via window.fetch — they're arbitrary URLs, not framework\r\n // endpoints) in parallel.\r\n const [bundleData, ...overlayDatas] = await Promise.all([\r\n pppRef.current.retrieveLocalizationBundleAsync(resolvedLocale, thumbs.bundle),\r\n ...overlayList.map(fetchLocalizationJson),\r\n ]);\r\n if (cancelled) return;\r\n\r\n // Merge later-wins: bundle first, then each overlay file.\r\n const merged: Record<string, string> = {};\r\n Object.assign(merged, flattenStrings(bundleData));\r\n for (const data of overlayDatas) {\r\n Object.assign(merged, flattenStrings(data));\r\n }\r\n setCache(merged);\r\n } catch (e) {\r\n if (cancelled) return;\r\n // eslint-disable-next-line no-console\r\n console.error(\r\n 'PowerPortalsPro: failed to load localization bundle; staying on key-fallback localizer.',\r\n e,\r\n );\r\n }\r\n if (cancelled) return;\r\n // Tier-1 attempt is done (success or failure) — add the sentinel\r\n // to the settled set so `isLoaded`'s uniform membership check\r\n // reports the default bundle as loaded. The settled set is the\r\n // single source of truth for \"what's been attempted\" across both\r\n // tier-1 and per-resource bundles.\r\n settledTokensRef.current.add(DEFAULT_BUNDLE_SENTINEL);\r\n\r\n // Queue baseline prefixes through the existing pending-tokens\r\n // pipeline. We do this AFTER tier-1 settles (rather than in\r\n // parallel) so the per-resource fetches run against valid\r\n // thumbprints; if tier-1 failed, the thumbs ref stays null and\r\n // the next flushPending call early-returns + re-queues the\r\n // baseline tokens, which is fine. Coerced tokens were\r\n // pre-computed in `baselinePrefixesKey` so we just walk the\r\n // ref's current value.\r\n let queuedBaseline = false;\r\n for (const token of baselineTokensRef.current) {\r\n if (triedTokensRef.current.has(token)) continue;\r\n if (pendingTokensRef.current.has(token)) continue;\r\n pendingTokensRef.current.add(token);\r\n queuedBaseline = true;\r\n }\r\n if (\r\n pendingTokensRef.current.size > 0 &&\r\n pendingTimerRef.current === null\r\n ) {\r\n // Use the ref bridge — `flushPending` is declared below this\r\n // useEffect. The ref is updated on every render so by the time\r\n // this setTimeout fires, `flushPendingRef.current` is the\r\n // latest stable callback identity.\r\n pendingTimerRef.current = setTimeout(\r\n () => flushPendingRef.current(),\r\n MISSING_KEY_FLUSH_DELAY_MS,\r\n );\r\n }\r\n // Notify even if we didn't queue new baselines — the tier-1 flip\r\n // itself is observable via `isLoaded` and listeners must re-evaluate.\r\n notifyListeners();\r\n void queuedBaseline; // referenced for readability; no further action needed\r\n })();\r\n\r\n return () => {\r\n cancelled = true;\r\n if (pendingTimerRef.current !== null) {\r\n clearTimeout(pendingTimerRef.current);\r\n pendingTimerRef.current = null;\r\n }\r\n };\r\n }, [locale, overlayUrlsKey]);\r\n\r\n // Tier 2: drains pending tokens and fans out parallel GETs per token.\r\n // One distinct URL per token (table / view bundle), each cached\r\n // immutable per release, so a fetch-storm-of-one happens at most once\r\n // per token per session — and zero times across sessions for unchanged\r\n // tables/views.\r\n const flushPending = useCallback(async () => {\r\n pendingTimerRef.current = null;\r\n const tokensToFetch = Array.from(pendingTokensRef.current);\r\n if (tokensToFetch.length === 0) return;\r\n pendingTokensRef.current = new Set();\r\n // Mark queried tokens as tried up-front so concurrent renders don't\r\n // re-queue them while requests are in flight.\r\n for (const tok of tokensToFetch) {\r\n triedTokensRef.current.add(tok);\r\n }\r\n\r\n const thumbs = thumbprintsRef.current;\r\n const requestLocale = resolvedLocaleRef.current;\r\n if (!thumbs || !requestLocale) {\r\n // Manifest hasn't loaded yet (or failed). Re-queue the tokens for\r\n // the next flush — by then tier-1 should have either succeeded\r\n // (we'll have thumbprints) or failed permanently (we'll skip again).\r\n for (const tok of tokensToFetch) {\r\n triedTokensRef.current.delete(tok);\r\n pendingTokensRef.current.add(tok);\r\n }\r\n return;\r\n }\r\n\r\n const fetches = tokensToFetch.map(async (token) => {\r\n try {\r\n if (token.startsWith('tables.')) {\r\n const tableName = token.substring('tables.'.length);\r\n const thumb = thumbs.tables[tableName];\r\n if (!thumb) return null;\r\n return await pppRef.current.retrieveLocalizationTableBundleAsync(\r\n tableName,\r\n requestLocale,\r\n thumb,\r\n );\r\n }\r\n if (token.startsWith('views.')) {\r\n const viewId = token.substring('views.'.length);\r\n const thumb = thumbs.views[viewId];\r\n if (!thumb) return null;\r\n return await pppRef.current.retrieveLocalizationViewBundleAsync(\r\n viewId,\r\n requestLocale,\r\n thumb,\r\n );\r\n }\r\n return null;\r\n } catch (e) {\r\n // Per-token failures are isolated; one failed table fetch shouldn't\r\n // strand the rest of the batch. Logged at warn so it surfaces in\r\n // dev without crashing the render loop.\r\n // eslint-disable-next-line no-console\r\n console.warn(`PowerPortalsPro: localization fetch failed for token ${token}`, e);\r\n // Drop from triedTokensRef so a future render can retry.\r\n triedTokensRef.current.delete(token);\r\n return null;\r\n }\r\n });\r\n\r\n const responses = await Promise.all(fetches);\r\n\r\n // Discard if locale changed mid-flight — the cache was reset by the\r\n // locale-change effect; applying these strings would mix locales.\r\n if (requestLocale !== resolvedLocaleRef.current) return;\r\n\r\n // Every token in this batch is now settled (success or failure) —\r\n // record before merging strings so a boundary observing the\r\n // settled-set transition sees a consistent state when its\r\n // re-render runs (cache + settled set in agreement). Notify\r\n // subscribers AFTER updating the set; a listener that calls\r\n // isLoaded() during its callback should see the fresh values.\r\n let settledChanged = false;\r\n for (const tok of tokensToFetch) {\r\n if (!settledTokensRef.current.has(tok)) {\r\n settledTokensRef.current.add(tok);\r\n settledChanged = true;\r\n }\r\n }\r\n\r\n const merged: Record<string, string> = {};\r\n for (const data of responses) {\r\n if (data === null) continue;\r\n Object.assign(merged, flattenStrings(data));\r\n }\r\n if (Object.keys(merged).length > 0) {\r\n setCache((prev) => ({ ...prev, ...merged }));\r\n }\r\n if (settledChanged) {\r\n notifyListeners();\r\n }\r\n }, [notifyListeners]);\r\n\r\n // Bridge `flushPending` back to the tier-1 effect via the ref.\r\n // Identity is stable across renders (useCallback deps are stable), so\r\n // this assignment is a no-op after the first render — but keeping it\r\n // unconditional lets us drop the ref check if the dependency surface\r\n // ever expands.\r\n flushPendingRef.current = flushPending;\r\n\r\n // Runtime-baseline-change effect: when the `prefixes` prop changes\r\n // after tier-1 has already settled, queue the new tokens. On the\r\n // initial mount this is a no-op because tier-1 hasn't settled yet —\r\n // the tier-1 async block handles the first queueing after the\r\n // default bundle resolves. On a real prop change (rare; most apps\r\n // declare prefixes once at the root), this picks up any new tokens\r\n // and feeds them through the standard pipeline.\r\n useEffect(() => {\r\n // Skip until tier-1 has settled — gauged by sentinel presence in\r\n // the settled set. Without the sentinel we can't yet fetch\r\n // per-resource bundles (thumbprints aren't loaded).\r\n if (!settledTokensRef.current.has(DEFAULT_BUNDLE_SENTINEL)) return;\r\n let added = false;\r\n for (const token of baselineTokensRef.current) {\r\n if (triedTokensRef.current.has(token)) continue;\r\n if (pendingTokensRef.current.has(token)) continue;\r\n pendingTokensRef.current.add(token);\r\n added = true;\r\n }\r\n if (pendingTokensRef.current.size > 0 && pendingTimerRef.current === null) {\r\n pendingTimerRef.current = setTimeout(flushPending, MISSING_KEY_FLUSH_DELAY_MS);\r\n }\r\n // Notify subscribers so a boundary watching the baselines re-evaluates\r\n // `isLoaded` (new baselines may have flipped its check to false).\r\n if (added) notifyListeners();\r\n }, [baselinePrefixesKey, flushPending, notifyListeners]);\r\n\r\n // Stable callback that queues a cache-miss key for the next batched flush.\r\n // Coerces the key into its owning table/view token first; non-coerceable\r\n // keys (raw `app.*`, `components.*`, etc.) are silently dropped — those\r\n // are covered by the static default bundle, so a miss means the key\r\n // genuinely doesn't exist (typo, removed-but-still-referenced, etc.).\r\n // The `onMissingKey` warning still fires for those, surfacing the typo\r\n // during dev.\r\n const queueMissingKey = useCallback(\r\n (key: string) => {\r\n const token = coerceKeyToToken(key);\r\n if (token === null) return;\r\n if (triedTokensRef.current.has(token)) return;\r\n if (pendingTokensRef.current.has(token)) return;\r\n pendingTokensRef.current.add(token);\r\n if (pendingTimerRef.current === null) {\r\n pendingTimerRef.current = setTimeout(flushPending, MISSING_KEY_FLUSH_DELAY_MS);\r\n }\r\n },\r\n [flushPending],\r\n );\r\n\r\n // Combined missing-key handler — fires the consumer's `onMissingKey`\r\n // (typically a dev warning) AND queues the key for endpoint resolution.\r\n // `createLocalizer` already dedupes via its own warnedKeys Set, so each\r\n // missing key fires the consumer's hook at most once.\r\n const onMissingKeyCombined = useCallback(\r\n (key: string) => {\r\n onMissingKey(key);\r\n queueMissingKey(key);\r\n },\r\n [onMissingKey, queueMissingKey],\r\n );\r\n\r\n // The exposed Localizer is a fresh `createLocalizer` over the current\r\n // cache — re-built on cache changes so consumers re-render and pick up\r\n // newly-resolved strings. Identity-stable across renders within a single\r\n // cache state, so unaffected components don't re-render needlessly.\r\n const localizer = useMemo<Localizer>(\r\n () => createLocalizer(cache, { onMissingKey: onMissingKeyCombined }),\r\n [cache, onMissingKeyCombined],\r\n );\r\n\r\n // Imperative prefetcher exposed via context — `useLocalization` calls\r\n // `prefetch`, `<LocalizationBoundary>` calls both `prefetch` and\r\n // `isLoaded` (via `useLocalizationLoaded`'s `useSyncExternalStore`).\r\n //\r\n // Identity-stable across renders so the LocalizationPrefetcherContext\r\n // value doesn't churn — the inner methods close over refs (settled set,\r\n // pending queue, listeners) so they always see fresh state without\r\n // needing to be rebuilt on cache changes. `flushPending` is captured\r\n // because it's the actual fetch-driver, and its identity is stable\r\n // across renders (depends only on `notifyListeners`, which is stable).\r\n const prefetcher = useMemo<LocalizationPrefetcher>(\r\n () => ({\r\n prefetch(prefixes) {\r\n for (const prefix of prefixes) {\r\n const token = coercePrefixToToken(prefix);\r\n if (token === null) continue;\r\n if (triedTokensRef.current.has(token)) continue;\r\n if (pendingTokensRef.current.has(token)) continue;\r\n pendingTokensRef.current.add(token);\r\n }\r\n // Schedule a flush if there's queued work and no timer is in\r\n // flight. Checked AFTER the add loop (not gated on whether we\r\n // added anything new) so that a queue surviving a prior\r\n // cleared timer — e.g. StrictMode's double-effect-invocation\r\n // where the tier-1 cleanup clears the timer between runs —\r\n // gets re-armed on the second prefetch invocation.\r\n if (pendingTokensRef.current.size > 0 && pendingTimerRef.current === null) {\r\n pendingTimerRef.current = setTimeout(flushPending, MISSING_KEY_FLUSH_DELAY_MS);\r\n }\r\n },\r\n isLoaded(prefixes) {\r\n // Uniform membership check: every token we expect (the tier-1\r\n // sentinel, every baseline token, every coerced argument\r\n // token) must be in the settled set. Tier-1 isn't a special\r\n // case — it's just \"another thing we wait for,\" represented as\r\n // the `DEFAULT_BUNDLE_SENTINEL` token added to settled set\r\n // after the default bundle's fetch attempt resolves\r\n // (success OR failure — failures count as \"tried, move on\" so\r\n // a flaky endpoint can't strand a boundary).\r\n //\r\n // Without the sentinel check, a `<LocalizationBoundary>` whose\r\n // only declared prefixes are `app.*` / `components.*`\r\n // (default-bundle territory, coerce to null) would render\r\n // children before the default bundle resolves — undoing the\r\n // whole point of the boundary on cold-load first paint.\r\n if (!settledTokensRef.current.has(DEFAULT_BUNDLE_SENTINEL)) return false;\r\n\r\n // Baseline prefixes the provider declared — the boundary\r\n // inherits these for free. Pre-coerced once per\r\n // `baselinePrefixesKey` change, so this is a cheap walk.\r\n for (const token of baselineTokensRef.current) {\r\n if (!settledTokensRef.current.has(token)) return false;\r\n }\r\n\r\n // Caller-declared prefixes (`useLocalization` or\r\n // `<LocalizationBoundary prefixes={…}>`). Non-coerceable\r\n // prefixes (`app.*` / `components.*`) are treated as already\r\n // loaded because the default bundle covers them — and we've\r\n // already gated on the tier-1 sentinel, so the cache\r\n // definitely has them by this point (or the fetch failed and\r\n // we've accepted that).\r\n for (const prefix of prefixes) {\r\n const token = coercePrefixToToken(prefix);\r\n if (token === null) continue;\r\n if (!settledTokensRef.current.has(token)) return false;\r\n }\r\n return true;\r\n },\r\n subscribe(listener) {\r\n listenersRef.current.add(listener);\r\n return () => {\r\n listenersRef.current.delete(listener);\r\n };\r\n },\r\n }),\r\n [flushPending],\r\n );\r\n\r\n // Memoize the manifest context value so its identity doesn't churn\r\n // on every render — only when `supportedLocales` actually changes\r\n // (initial fetch lands, locale change resets to undefined and re-\r\n // fetches). Without the memo, every descendant consumer would re-\r\n // render on each parent render.\r\n const manifestContextValue = useMemo<LocalizationManifestState>(\r\n () => ({ supportedLocales }),\r\n [supportedLocales],\r\n );\r\n\r\n return (\r\n <LocalizationManifestContext.Provider value={manifestContextValue}>\r\n <LocalizationPrefetcherContext.Provider value={prefetcher}>\r\n <LocalizerProvider value={localizer}>{children}</LocalizerProvider>\r\n </LocalizationPrefetcherContext.Provider>\r\n </LocalizationManifestContext.Provider>\r\n );\r\n}\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useMemo,\r\n useRef,\r\n useSyncExternalStore,\r\n} from 'react';\r\nimport type { ReactNode } from 'react';\r\n\r\n/**\r\n * Source of unsaved-changes state on the page. Each topmost\r\n * {@link MainContext} / {@link RecordContext} registers one of these so a\r\n * page-wide guard (browser beforeunload, router-blocker) can answer \"is\r\n * anything on this page dirty?\" without having to walk every framework\r\n * subtree itself.\r\n *\r\n * Nested contexts don't register — their dirty state already bubbles up\r\n * to the topmost MainContext through the existing\r\n * {@link MainContextValue.registerDescendant} plumbing, so registering\r\n * nested contexts here would double-count.\r\n */\r\nexport interface UnsavedChangesReporter {\r\n /**\r\n * Snapshot getter — invoked synchronously by the registry on every\r\n * subscriber notification. Read-through to the reporter's live\r\n * `isDirty` so subscribers see the latest aggregate without needing to\r\n * re-register on every state flip.\r\n */\r\n readonly isDirty: () => boolean;\r\n}\r\n\r\n/**\r\n * App-wide registry of {@link UnsavedChangesReporter}s. Owned by\r\n * {@link PowerPortalsProProvider}; consumed by:\r\n *\r\n * - The auto-registration effect inside topmost\r\n * {@link MainContext} / {@link RecordContext}, which adds a reporter on\r\n * mount + removes it on unmount.\r\n * - `<RouterUnsavedChangesGuard>` (in `@powerportalspro/react-fluent`),\r\n * which subscribes via {@link useIsAnyDirty} and gates `useBlocker` on\r\n * the aggregate.\r\n *\r\n * Router-agnostic by design — the registry knows nothing about\r\n * react-router, history, or navigation events. That coupling lives in\r\n * the optional UI package so consumers using a different router (or\r\n * none at all) aren't forced into a dependency they don't need.\r\n */\r\nexport interface UnsavedChangesRegistry {\r\n /**\r\n * Add a reporter to the registry. Returns the deregister function;\r\n * call it in the same effect's cleanup so the reporter doesn't outlive\r\n * the component that owns it.\r\n */\r\n readonly register: (reporter: UnsavedChangesReporter) => () => void;\r\n /**\r\n * Subscribe to \"the set of dirty reporters might have changed\" events.\r\n * Fires when reporters are added or removed, AND when a registered\r\n * reporter's wrapping {@link MainContext}'s `isDirty` flips (the\r\n * reporter's `isDirty` getter closes over the live value).\r\n *\r\n * Shape compatible with {@link useSyncExternalStore} so the consuming\r\n * hook ({@link useIsAnyDirty}) can use it directly.\r\n */\r\n readonly subscribe: (listener: () => void) => () => void;\r\n /**\r\n * Walks every registered reporter and returns `true` as soon as one\r\n * reports dirty. Used as `getSnapshot` for {@link useSyncExternalStore}\r\n * and called imperatively from the beforeunload handler.\r\n */\r\n readonly isAnyDirty: () => boolean;\r\n /**\r\n * Forces a notification fire — used by topmost MainContexts after\r\n * their internal `isDirty` aggregate changes, so subscribers re-read\r\n * the registry's `isAnyDirty()` and pick up the new value. Without\r\n * this, the registry would only notify on register/deregister and\r\n * stay silent through the user's actual edits.\r\n */\r\n readonly notifyDirtyChanged: () => void;\r\n}\r\n\r\nconst UnsavedChangesRegistryContext = createContext<UnsavedChangesRegistry | null>(null);\r\nUnsavedChangesRegistryContext.displayName = 'UnsavedChangesRegistry';\r\n\r\nexport interface UnsavedChangesRegistryProviderProps {\r\n children: ReactNode;\r\n}\r\n\r\n/**\r\n * Mounts an {@link UnsavedChangesRegistry} for its subtree. Auto-mounted\r\n * by {@link PowerPortalsProProvider}; consumers don't normally render it\r\n * directly.\r\n */\r\nexport function UnsavedChangesRegistryProvider({ children }: UnsavedChangesRegistryProviderProps) {\r\n // Set keeps insertion order which doesn't matter for `isAnyDirty()` but\r\n // is useful for any future \"first dirty wins\" diagnostics. Stored in a\r\n // ref because mutations don't need to drive re-renders here — the\r\n // listener fan-out below handles that.\r\n const reportersRef = useRef<Set<UnsavedChangesReporter>>(new Set());\r\n const listenersRef = useRef<Set<() => void>>(new Set());\r\n\r\n const notify = useCallback(() => {\r\n for (const listener of listenersRef.current) listener();\r\n }, []);\r\n\r\n const register = useCallback(\r\n (reporter: UnsavedChangesReporter) => {\r\n reportersRef.current.add(reporter);\r\n notify();\r\n return () => {\r\n reportersRef.current.delete(reporter);\r\n notify();\r\n };\r\n },\r\n [notify],\r\n );\r\n\r\n const subscribe = useCallback((listener: () => void) => {\r\n listenersRef.current.add(listener);\r\n return () => {\r\n listenersRef.current.delete(listener);\r\n };\r\n }, []);\r\n\r\n const isAnyDirty = useCallback(() => {\r\n for (const reporter of reportersRef.current) {\r\n if (reporter.isDirty()) return true;\r\n }\r\n return false;\r\n }, []);\r\n\r\n const registry = useMemo<UnsavedChangesRegistry>(\r\n () => ({ register, subscribe, isAnyDirty, notifyDirtyChanged: notify }),\r\n [register, subscribe, isAnyDirty, notify],\r\n );\r\n\r\n return (\r\n <UnsavedChangesRegistryContext.Provider value={registry}>\r\n {children}\r\n </UnsavedChangesRegistryContext.Provider>\r\n );\r\n}\r\n\r\n/**\r\n * Returns the nearest ancestor {@link UnsavedChangesRegistry}, or `null`\r\n * when none is mounted. Framework components ({@link MainContext}) use\r\n * this to opt into reporting their dirty state; guard components\r\n * ({@code RouterUnsavedChangesGuard}) use {@link useIsAnyDirty} which\r\n * already null-checks internally.\r\n */\r\nexport function useUnsavedChangesRegistry(): UnsavedChangesRegistry | null {\r\n return useContext(UnsavedChangesRegistryContext);\r\n}\r\n\r\n/**\r\n * Reactive read of \"is anything on this page dirty?\" — wraps\r\n * {@link useSyncExternalStore} against the surrounding\r\n * {@link UnsavedChangesRegistry}. Returns `false` when no registry is\r\n * mounted (e.g. a tree that hasn't been wrapped in\r\n * {@link PowerPortalsProProvider} yet) so guard components can default\r\n * to \"don't block\" rather than throw.\r\n *\r\n * Use this from any component that wants to react to page-wide unsaved\r\n * state — typically the router-blocker guard, but also custom dirty\r\n * indicators in app chrome.\r\n */\r\nexport function useIsAnyDirty(): boolean {\r\n const registry = useUnsavedChangesRegistry();\r\n const subscribe = useCallback(\r\n (listener: () => void) => {\r\n if (!registry) return () => {};\r\n return registry.subscribe(listener);\r\n },\r\n [registry],\r\n );\r\n const getSnapshot = useCallback(() => {\r\n if (!registry) return false;\r\n return registry.isAnyDirty();\r\n }, [registry]);\r\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\r\n}\r\n","import { AuthClient, LoginResult, PowerPortalsProClient, SwitchIdentityResult, Transport } from '@powerportalspro/core';\r\nimport type { LoginRequest, SwitchIdentityResponse } from '@powerportalspro/core';\r\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\r\nimport type { ReactNode } from 'react';\r\nimport {\r\n InMemoryEnvironmentFileSettingsCache,\r\n InMemoryTableMetadataCache,\r\n InMemoryViewMetadataCache,\r\n InMemoryViewsForTableCache,\r\n} from './caches/in-memory-cache.js';\r\nimport type {\r\n EnvironmentFileSettingsCache,\r\n TableMetadataCache,\r\n ViewMetadataCache,\r\n ViewsForTableCache,\r\n} from './caches/types.js';\r\nimport {\r\n AuthStatus,\r\n PowerPortalsProContext,\r\n type AuthState,\r\n type PowerPortalsProContextValue,\r\n} from './context.js';\r\nimport { DefaultLocalizerProvider, LocaleProvider, useLocale } from './localizer.js';\r\nimport { UnsavedChangesRegistryProvider } from './unsaved-changes-registry.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\nexport interface PowerPortalsProProviderProps {\r\n children: ReactNode;\r\n /**\r\n * Optional pre-constructed {@link Transport} for the AuthClient and PowerPortalsProClient\r\n * to share. Pass one to set a non-default `baseUrl`, inject a custom `fetch`\r\n * implementation for tests, etc. When omitted, a default transport is constructed —\r\n * same-origin, browser fetch, cookie credentials included.\r\n *\r\n * The provider holds a stable reference to the transport for its lifetime — passing\r\n * a new instance on every render is wasteful, so memoize it at the call site or\r\n * construct it at module scope. Changes to this prop after first render are ignored.\r\n */\r\n transport?: Transport;\r\n /**\r\n * Single override JSON layered on top of the framework defaults. Use this\r\n * when the consumer app has one additional strings file (e.g.\r\n * `/localization/app.en.json`) with app-specific strings or English\r\n * overrides. The framework defaults remain the base.\r\n *\r\n * Pass `localizationUrl={false}` to opt out of the auto-mounted localizer\r\n * entirely (e.g. when adapting i18next via your own `<LocalizerProvider>`).\r\n */\r\n localizationUrl?: string | false;\r\n /**\r\n * Multiple override JSONs layered on top of the framework defaults, in\r\n * order. Later URLs override earlier ones for matching keys. Use this when\r\n * the consumer app has more than one strings file (e.g. one per page,\r\n * one per feature module).\r\n *\r\n * Combined with `localizationUrl`, both are appended to the framework\r\n * defaults — `localizationUrl` first, then `localizationUrls`.\r\n */\r\n localizationUrls?: readonly string[];\r\n /**\r\n * Baseline localization prefixes the auto-mounted\r\n * `<DefaultLocalizerProvider>` should eagerly load alongside the\r\n * default bundle. Forwards directly to that provider's `prefixes`\r\n * prop — see its doc for the full semantics. Ignored when\r\n * `localizationUrl={false}` (consumer is wiring their own\r\n * localizer adapter).\r\n *\r\n * Typical use: declare baseline tables / views the entire app\r\n * touches (header lookups, navigation chrome) so every\r\n * `<LocalizationBoundary>` inherits them for free.\r\n */\r\n localizationPrefixes?: readonly string[];\r\n /**\r\n * Custom {@link TableMetadataCache} implementation. When omitted, the\r\n * provider mounts an in-memory cache with in-flight-promise dedup.\r\n * Override with a localStorage-backed implementation for cross-session\r\n * persistence, or a mock for tests.\r\n */\r\n tableMetadataCache?: TableMetadataCache;\r\n /**\r\n * Custom {@link ViewMetadataCache} implementation. Same defaulting\r\n * behavior as {@link tableMetadataCache}.\r\n */\r\n viewMetadataCache?: ViewMetadataCache;\r\n /**\r\n * Custom {@link ViewsForTableCache} implementation. Same defaulting\r\n * behavior as {@link tableMetadataCache}.\r\n */\r\n viewsForTableCache?: ViewsForTableCache;\r\n /**\r\n * Custom {@link EnvironmentFileSettingsCache} implementation. Same\r\n * defaulting behavior as {@link tableMetadataCache}.\r\n */\r\n environmentFileSettingsCache?: EnvironmentFileSettingsCache;\r\n}\r\n\r\n/**\r\n * Top-level provider for `@powerportalspro/react`. Constructs an {@link AuthClient}\r\n * and a {@link PowerPortalsProClient} (sharing one {@link Transport}) and manages the\r\n * auth state machine — `loading` until the first `/api/auth/me` response, then\r\n * `anonymous` or `authenticated` for the rest of the session.\r\n *\r\n * Wrap the app once near the root:\r\n * ```tsx\r\n * <PowerPortalsProProvider>\r\n * <App />\r\n * </PowerPortalsProProvider>\r\n * ```\r\n *\r\n * Hooks ({@link useAuth}, {@link usePowerPortalsPro}, {@link useRecord}, …) read from\r\n * this provider's context.\r\n */\r\nexport function PowerPortalsProProvider({\r\n children,\r\n transport,\r\n localizationUrl,\r\n localizationUrls,\r\n localizationPrefixes,\r\n tableMetadataCache,\r\n viewMetadataCache,\r\n viewsForTableCache,\r\n environmentFileSettingsCache,\r\n}: PowerPortalsProProviderProps) {\r\n // Capture the transport at first render; changes after mount are ignored. This avoids\r\n // the dependency-loop trap where every parent render passes a new TransportOptions\r\n // object literal and we'd reconstruct AuthClient + PowerPortalsProClient + auth state\r\n // every time. Consumers who need to swap the transport should remount the provider.\r\n const transportRef = useRef<Transport | null>(null);\r\n if (transportRef.current === null) {\r\n transportRef.current = transport ?? new Transport();\r\n }\r\n const transportInstance = transportRef.current;\r\n\r\n const auth = useMemo(() => new AuthClient(transportInstance), [transportInstance]);\r\n const ppp = useMemo(() => new PowerPortalsProClient(transportInstance), [transportInstance]);\r\n // Metadata caches. Consumer can override (e.g. with a localStorage-\r\n // backed cache) via props; default to in-memory + in-flight-promise\r\n // dedup. One instance per `ppp` so two transports get two caches.\r\n const resolvedTableMetadataCache = useMemo(\r\n () => tableMetadataCache ?? new InMemoryTableMetadataCache(ppp),\r\n [ppp, tableMetadataCache],\r\n );\r\n const resolvedViewMetadataCache = useMemo(\r\n () => viewMetadataCache ?? new InMemoryViewMetadataCache(ppp),\r\n [ppp, viewMetadataCache],\r\n );\r\n const resolvedViewsForTableCache = useMemo(\r\n () => viewsForTableCache ?? new InMemoryViewsForTableCache(ppp),\r\n [ppp, viewsForTableCache],\r\n );\r\n const resolvedEnvironmentFileSettingsCache = useMemo(\r\n () => environmentFileSettingsCache ?? new InMemoryEnvironmentFileSettingsCache(ppp),\r\n [ppp, environmentFileSettingsCache],\r\n );\r\n\r\n const [authState, setAuthState] = useState<AuthState>({ status: AuthStatus.Loading });\r\n\r\n const refreshAuth = useCallback(async () => {\r\n try {\r\n const me = await auth.getCurrentUserAsync();\r\n if (me.isAuthenticated) {\r\n setAuthState({ status: AuthStatus.Authenticated, user: me });\r\n } else {\r\n setAuthState({ status: AuthStatus.Anonymous });\r\n }\r\n } catch {\r\n // Network failures, problem+json, anything else: treat as anonymous so the\r\n // app can render the sign-in flow rather than spinning forever.\r\n setAuthState({ status: AuthStatus.Anonymous });\r\n }\r\n }, [auth]);\r\n\r\n const login = useCallback(\r\n async (request: LoginRequest) => {\r\n const response = await auth.loginAsync(request);\r\n if (response.result === LoginResult.Success) {\r\n await refreshAuth();\r\n }\r\n return response;\r\n },\r\n [auth, refreshAuth],\r\n );\r\n\r\n const logout = useCallback(async () => {\r\n await auth.logoutAsync();\r\n setAuthState({ status: AuthStatus.Anonymous });\r\n }, [auth]);\r\n\r\n // Swaps the auth cookie for the user's alt-identity sibling. On a successful\r\n // swap the cookie is replaced server-side and we re-run `/api/auth/me` so the\r\n // in-memory `authState.user` reflects the new identity (table name, roles,\r\n // alt-identity hints, etc.). Other outcomes (`NoAltIdentity`, `AltIdentityNotFound`,\r\n // `NotAuthenticated`) leave the existing state untouched — the caller decides\r\n // what to surface to the user via the returned response.\r\n const switchIdentity = useCallback(async (): Promise<SwitchIdentityResponse> => {\r\n const response = await auth.switchIdentityAsync();\r\n if (response.result === SwitchIdentityResult.Switched) {\r\n await refreshAuth();\r\n }\r\n return response;\r\n }, [auth, refreshAuth]);\r\n\r\n useEffect(() => {\r\n refreshAuth();\r\n }, [refreshAuth]);\r\n\r\n const value = useMemo<PowerPortalsProContextValue>(\r\n () => ({\r\n transport: transportInstance,\r\n auth,\r\n ppp,\r\n tableMetadataCache: resolvedTableMetadataCache,\r\n viewMetadataCache: resolvedViewMetadataCache,\r\n viewsForTableCache: resolvedViewsForTableCache,\r\n environmentFileSettingsCache: resolvedEnvironmentFileSettingsCache,\r\n authState,\r\n refreshAuth,\r\n login,\r\n logout,\r\n switchIdentity,\r\n }),\r\n [\r\n transportInstance,\r\n auth,\r\n ppp,\r\n resolvedTableMetadataCache,\r\n resolvedViewMetadataCache,\r\n resolvedViewsForTableCache,\r\n resolvedEnvironmentFileSettingsCache,\r\n authState,\r\n refreshAuth,\r\n login,\r\n logout,\r\n switchIdentity,\r\n ],\r\n );\r\n\r\n // Auto-mount the default localizer unless the consumer opted out via\r\n // `localizationUrl={false}` (typically to wire i18next or another lib via\r\n // their own <LocalizerProvider>). The default provider renders children\r\n // immediately with a key-fallback localizer, then upgrades when the JSON\r\n // resolves — so the first paint isn't gated on a network round-trip.\r\n //\r\n // Build the URL chain: framework defaults always first; consumer's\r\n // `localizationUrl` (single) and `localizationUrls` (multi) layer on top\r\n // in that order. Later wins for matching keys.\r\n //\r\n // ALSO auto-mount `<LocaleProvider>` around the localizer so locale\r\n // switching via `<LanguageDropdown>` (or any other consumer using\r\n // `useLocale().setLocale`) actually works. Without a LocaleProvider,\r\n // `useLocale()` falls back to a no-op `setLocale` and selecting a\r\n // language in the dropdown silently does nothing — the locale-switch\r\n // path is dead, and DefaultLocalizerProvider stays pinned to 'en'.\r\n // Consumers managing locale themselves (e.g. wiring i18next or\r\n // providing their own LocaleProvider higher in the tree) opt out of\r\n // BOTH the localizer and the locale provider with\r\n // `localizationUrl={false}` — they're co-mounted because they're a\r\n // matched pair: the localizer subscribes to the locale provider for\r\n // re-fetches on change.\r\n let localizationContent: ReactNode;\r\n if (localizationUrl === false) {\r\n localizationContent = children;\r\n } else {\r\n // The default provider hits the server endpoint\r\n // (`ppp.retrieveLocalizedStringsAsync(locale)`) which returns the merged\r\n // metadata + framework JSON + server-side overrides. `localizationUrl` /\r\n // `localizationUrls` add extra client-side overlays on top, layered in\r\n // declaration order.\r\n const overlayUrls: string[] = [];\r\n if (typeof localizationUrl === 'string') overlayUrls.push(localizationUrl);\r\n if (localizationUrls) overlayUrls.push(...localizationUrls);\r\n localizationContent = (\r\n <LocaleProvider>\r\n <CacheLocaleSync />\r\n <DefaultLocalizerProvider\r\n {...(overlayUrls.length > 0 && { urls: overlayUrls })}\r\n {...(localizationPrefixes !== undefined && { prefixes: localizationPrefixes })}\r\n >\r\n {children}\r\n </DefaultLocalizerProvider>\r\n </LocaleProvider>\r\n );\r\n }\r\n\r\n return (\r\n <PowerPortalsProContext.Provider value={value}>\r\n <UnsavedChangesRegistryProvider>{localizationContent}</UnsavedChangesRegistryProvider>\r\n </PowerPortalsProContext.Provider>\r\n );\r\n}\r\n\r\n/**\r\n * Internal helper mounted inside `<LocaleProvider>` whenever the\r\n * provider auto-mounts the localization stack. Watches the active\r\n * locale and clears the metadata caches (table / view / views-for-\r\n * table / environment-file-settings) on every change.\r\n *\r\n * The caches store server responses whose payload includes localized\r\n * display names that were resolved against the cookie / request\r\n * culture at the time of the fetch. Keying by name alone — as the\r\n * default in-memory caches do — would surface stale display names\r\n * from the previous locale after a `<LanguageDropdown>` selection,\r\n * even though the cookie + server response are correct.\r\n *\r\n * \"Clear on change\" was chosen over \"key by `{name, locale}`\" because\r\n * the cache interface stays simple (no per-call locale plumbing) and\r\n * the trade-off — a re-fetch when the user toggles back to a prior\r\n * locale — is negligible for these endpoints. Consumers shipping\r\n * their own cache implementations via the provider's\r\n * `tableMetadataCache` / etc. props benefit from the same wiring:\r\n * the framework calls `clear()` on them on locale change, so the\r\n * custom cache just needs to implement that method per the\r\n * `TableMetadataCache` interface contract.\r\n */\r\nfunction CacheLocaleSync(): null {\r\n const { locale } = useLocale();\r\n const {\r\n tableMetadataCache,\r\n viewMetadataCache,\r\n viewsForTableCache,\r\n environmentFileSettingsCache,\r\n } = usePowerPortalsPro();\r\n // Skip the first render — the caches are empty and the initial\r\n // locale resolution doesn't constitute a \"change.\" Clearing here\r\n // would force every consumer that mounts a record / grid below to\r\n // re-fetch on a fresh-cache miss that was already going to fire.\r\n const isFirstRun = useRef(true);\r\n useEffect(() => {\r\n if (isFirstRun.current) {\r\n isFirstRun.current = false;\r\n return;\r\n }\r\n tableMetadataCache.clear();\r\n viewMetadataCache.clear();\r\n viewsForTableCache.clear();\r\n // EnvironmentFileSettings is a singleton-shaped cache (no key) —\r\n // `invalidate()` is the equivalent of `clear()` for it.\r\n environmentFileSettingsCache.invalidate();\r\n }, [\r\n locale,\r\n tableMetadataCache,\r\n viewMetadataCache,\r\n viewsForTableCache,\r\n environmentFileSettingsCache,\r\n ]);\r\n return null;\r\n}\r\n","import type { LoginRequest, LoginResponse, SwitchIdentityResponse } from '@powerportalspro/core';\r\nimport type { AuthState } from './context.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\n/**\r\n * Auth state + actions, surfaced as a discriminated union spread. Compare against\r\n * the bare string literals or against {@link AuthStatus} constants — both are\r\n * type-narrowed equivalently:\r\n *\r\n * ```tsx\r\n * import { AuthStatus, useAuth } from '@powerportalspro/react';\r\n *\r\n * const auth = useAuth();\r\n * if (auth.status === AuthStatus.Loading) return <Spinner />;\r\n * if (auth.status === AuthStatus.Anonymous) return <SignInButton onClick={() => auth.login(...)} />;\r\n * if (auth.status === AuthStatus.Authenticated) return <span>Hi, {auth.user.email}</span>;\r\n * ```\r\n *\r\n * Behind the scenes the state lives on the {@link PowerPortalsProProvider} and\r\n * propagates via context, so all `useAuth` calls in the tree see the same value\r\n * and re-render on transitions.\r\n *\r\n * `login` mirrors {@link AuthClient.loginAsync} — same request/response shapes —\r\n * but additionally re-fetches `/api/auth/me` on `Success` so the auth state\r\n * transitions to `authenticated` without an extra round-trip from the consumer.\r\n *\r\n * `logout` mirrors {@link AuthClient.logoutAsync} and transitions the state to\r\n * `anonymous` immediately on success.\r\n */\r\nexport type UseAuthResult = AuthState & {\r\n /** Re-runs `/api/auth/me` and updates the state. Call after side effects that may have changed the user (server-driven role updates, contact-record edits, etc.). */\r\n refresh: () => Promise<void>;\r\n /** Submits credentials. On `LoginResult.Success`, the state transitions to `authenticated`. */\r\n login: (request: LoginRequest) => Promise<LoginResponse>;\r\n /** Clears the auth cookie and transitions the state to `anonymous`. */\r\n logout: () => Promise<void>;\r\n /**\r\n * Swaps the auth cookie for the user's alt-identity sibling. Returns the\r\n * server's `SwitchIdentityResponse` so the caller can surface non-success\r\n * outcomes (`NoAltIdentity`, `AltIdentityNotFound`, `NotAuthenticated`).\r\n * On `Switched`, the in-memory auth state is refreshed automatically.\r\n */\r\n switchIdentity: () => Promise<SwitchIdentityResponse>;\r\n};\r\n\r\nexport function useAuth(): UseAuthResult {\r\n const { authState, refreshAuth, login, logout, switchIdentity } = usePowerPortalsPro();\r\n return {\r\n ...authState,\r\n refresh: refreshAuth,\r\n login,\r\n logout,\r\n switchIdentity,\r\n };\r\n}\r\n","import { type ReactNode } from 'react';\r\nimport { AuthStatus } from './context.js';\r\nimport { useAuth } from './use-auth.js';\r\n\r\nexport interface AuthorizedViewProps {\r\n /**\r\n * Children rendered when the principal is authenticated (and, if {@link roles}\r\n * is supplied, holds at least one of the listed roles).\r\n */\r\n children: ReactNode;\r\n /**\r\n * Optional role gate. When supplied, the principal must hold AT LEAST ONE of\r\n * the listed roles for the children to render — matching Blazor's\r\n * `<AuthorizeView Roles=\"Admin,Manager\">` (comma-separated = OR).\r\n *\r\n * Roles arrive on `auth.user.roles` from `/api/auth/me`. Per-role claims are\r\n * only emitted for systemuser-backed principals — contacts don't have roles —\r\n * so a role gate also implicitly limits the surface to staff users.\r\n */\r\n roles?: string | readonly string[];\r\n /**\r\n * Rendered when the principal is anonymous, the auth state is still loading,\r\n * or the role check fails. Defaults to `null` (renders nothing).\r\n */\r\n fallback?: ReactNode;\r\n}\r\n\r\n/**\r\n * Headless auth gate — React equivalent of Blazor's `<AuthorizeView>`. Renders\r\n * {@link AuthorizedViewProps.children} only when the principal is authenticated;\r\n * if {@link AuthorizedViewProps.roles} is supplied, the principal must hold at\r\n * least one of the listed roles. Otherwise renders {@link AuthorizedViewProps.fallback}\r\n * (default `null`).\r\n *\r\n * Loading is treated as \"not yet authorized\" — fallback renders during the\r\n * initial `/api/auth/me` round-trip so consumers don't briefly flash the\r\n * authorized UI before the cookie principal is hydrated.\r\n *\r\n * @example\r\n * ```tsx\r\n * <AuthorizedView>\r\n * <Link to=\"/Account/Manage\">Manage account</Link>\r\n * </AuthorizedView>\r\n *\r\n * <AuthorizedView roles=\"SystemAdmin\">\r\n * <Link to=\"/SiteAdmin\">Site admin</Link>\r\n * </AuthorizedView>\r\n *\r\n * <AuthorizedView roles={[\"Admin\", \"Manager\"]} fallback={<span>Restricted</span>}>\r\n * <SensitiveTools />\r\n * </AuthorizedView>\r\n * ```\r\n */\r\nexport function AuthorizedView({ children, roles, fallback = null }: AuthorizedViewProps) {\r\n const auth = useAuth();\r\n\r\n if (auth.status !== AuthStatus.Authenticated) return <>{fallback}</>;\r\n\r\n if (roles !== undefined) {\r\n const required = typeof roles === 'string' ? [roles] : roles;\r\n if (required.length > 0) {\r\n const userRoles = auth.user.roles ?? [];\r\n const hasAny = required.some((r) => userRoles.includes(r));\r\n if (!hasAny) return <>{fallback}</>;\r\n }\r\n }\r\n\r\n return <>{children}</>;\r\n}\r\n","import type { PowerPortalsProError } from '@powerportalspro/core';\r\nimport { useCallback, useEffect, useRef, useState } from 'react';\r\n\r\n/**\r\n * Status values for query hooks ({@link useRecord}, {@link useFetchRecords},\r\n * {@link useTableMetadata}, …) and {@link RecordContext}.\r\n *\r\n * Hybrid shape — bare string-literal union as the type for clean narrowing in\r\n * `if (q.status === 'success')`-style branching, plus a const-object export so\r\n * consumers who prefer the refactor-safe form can write `q.status === QueryStatus.Success`.\r\n * Both compare against the same runtime string. Matches the auto-generated\r\n * `LoginResult`-style pattern from `@powerportalspro/core`.\r\n */\r\nexport const QueryStatus = {\r\n Loading: 'loading',\r\n Success: 'success',\r\n Error: 'error',\r\n} as const;\r\nexport type QueryStatus = (typeof QueryStatus)[keyof typeof QueryStatus];\r\n\r\n/**\r\n * Discriminated state surface returned by every query hook. `data` and `error` are\r\n * always present as fields (so consumers can read them at any time) but only carry\r\n * meaningful values when `status` matches. `refetch()` re-runs the query with the\r\n * current dependency values, regardless of current state.\r\n *\r\n * Loosely mirrors react-query's API shape so consumers familiar with that library\r\n * find the contract familiar — but with no caching, no dedup, no shared cache between\r\n * components. Each hook call owns its own fetch lifecycle.\r\n *\r\n * **`status` vs. `isFetching`** — `status` describes the *outcome* (what's\r\n * in `data`): `'loading'` = no data yet, `'success'` = data is populated,\r\n * `'error'` = the most recent fetch threw. `isFetching` describes the\r\n * *current request* — true whenever a network call is in flight,\r\n * regardless of whether prior data is still being shown. With\r\n * `keepPreviousData` enabled (see {@link useAsyncResource}), a deps-change\r\n * triggered refetch keeps `status='success'` and the prior `data`\r\n * populated while `isFetching` flips to true, so consumers can render a\r\n * scrim overlay instead of wiping the UI.\r\n */\r\nexport interface QueryResult<T> {\r\n readonly status: QueryStatus;\r\n readonly data: T | undefined;\r\n readonly error: PowerPortalsProError | undefined;\r\n /**\r\n * True whenever a network request is currently in flight, including\r\n * refetches triggered by deps changes or explicit `refetch()` calls.\r\n * Independent of `status`: when `keepPreviousData` is enabled, the hook\r\n * can be `status='success'` (prior data still on screen) AND\r\n * `isFetching=true` (new request running) at the same time.\r\n */\r\n readonly isFetching: boolean;\r\n /**\r\n * Re-runs the query immediately, bypassing the deps comparison. Returns a\r\n * promise that resolves when the next fetch settles successfully (or\r\n * rejects if the fetch errors). Awaiting is optional — callers that just\r\n * want to trigger a refresh can fire-and-forget.\r\n *\r\n * If the request is superseded by another deps change or another\r\n * `refetch()` call before it settles, the returned promise resolves anyway\r\n * (with the stale-but-final state of this `refetch` call's outcome) — that\r\n * matches the \"kick + observe\" semantics callers want for a save-then-refresh\r\n * flow, where the new data landing is what matters, not which trigger\r\n * caused it.\r\n */\r\n readonly refetch: () => Promise<void>;\r\n}\r\n\r\n/**\r\n * Internal hook used by every public query hook (useRecord, useFetchRecords, …) to\r\n * implement the loading / error / data state machine on top of an async fetcher.\r\n *\r\n * - Reruns when any dep in `deps` changes (consumer-controlled — pass primitive\r\n * comparable values).\r\n * - Aborts the in-flight request via AbortController when deps change or the\r\n * component unmounts.\r\n * - `enabled: false` puts the hook in the \"success-with-undefined-data\" idle state\r\n * without firing a request — useful for conditional fetches (e.g. don't fetch\r\n * until a parent dependency is ready).\r\n *\r\n * @param fetcher Receives an AbortSignal and returns the data. Don't memoize this —\r\n * we capture it every render and the deps array drives reruns.\r\n * @param deps Stable values whose change should trigger a refetch. Treat as a\r\n * React `useEffect` dependency list.\r\n * @param enabled When false, the hook returns idle state and skips fetching.\r\n * @param keepPreviousData When true and a previous fetch succeeded, deps-change\r\n * refetches don't wipe `status`/`data` back to `'loading'`/`undefined`\r\n * — instead `status` stays `'success'`, `data` stays populated with\r\n * the prior result, and `isFetching` flips to true for the duration\r\n * of the new request. The grid (and any other consumer with already-\r\n * rendered content) can use this to scrim the UI rather than collapse\r\n * to a \"loading…\" placeholder. Defaults to false to preserve the\r\n * strict state-machine behavior every existing consumer relies on.\r\n */\r\nexport function useAsyncResource<T>(\r\n fetcher: (signal: AbortSignal) => Promise<T>,\r\n deps: readonly unknown[],\r\n enabled = true,\r\n keepPreviousData = false,\r\n): QueryResult<T> {\r\n type State = Pick<QueryResult<T>, 'status' | 'data' | 'error' | 'isFetching'>;\r\n const [state, setState] = useState<State>({\r\n status: enabled ? QueryStatus.Loading : QueryStatus.Success,\r\n data: undefined,\r\n error: undefined,\r\n isFetching: enabled,\r\n });\r\n const [refetchTick, setRefetchTick] = useState(0);\r\n\r\n // Pending resolvers/rejecters from `refetch()` calls. Drained when the next\r\n // fetch settles. Stored in a ref (not state) — they're imperative\r\n // continuations, not part of the rendered UI.\r\n type PendingResolver = { resolve: () => void; reject: (err: unknown) => void };\r\n const pendingResolversRef = useRef<PendingResolver[]>([]);\r\n\r\n useEffect(() => {\r\n if (!enabled) {\r\n setState({\r\n status: QueryStatus.Success,\r\n data: undefined,\r\n error: undefined,\r\n isFetching: false,\r\n });\r\n // Disabled state still resolves any pending refetch promises so awaiters\r\n // don't hang forever when a parent gates the query off mid-flight.\r\n const resolvers = pendingResolversRef.current;\r\n pendingResolversRef.current = [];\r\n for (const r of resolvers) r.resolve();\r\n return;\r\n }\r\n\r\n const controller = new AbortController();\r\n setState((prev) => {\r\n // `keepPreviousData` keeps the prior successful result on screen while a\r\n // deps-change refetch is in flight. Without it, every deps change wipes\r\n // back to loading + undefined — which is what existing consumers assume,\r\n // so this branch only applies when the caller opts in.\r\n if (keepPreviousData && prev.status === QueryStatus.Success && prev.data !== undefined) {\r\n // Already in keep-prior state? Skip the state update entirely — the\r\n // identity short-circuit avoids a wasted render on every re-run.\r\n if (prev.isFetching && prev.error === undefined) return prev;\r\n return { ...prev, error: undefined, isFetching: true };\r\n }\r\n // Same short-circuit on the fresh-loading path: if we're already in the\r\n // exact loading-with-fetching state the next branch produces, returning\r\n // prev avoids a needless render. (Original optimization in the pre-\r\n // isFetching version of this hook — kept to preserve perf invariants\r\n // every downstream test was depending on.)\r\n if (\r\n prev.status === QueryStatus.Loading &&\r\n prev.data === undefined &&\r\n prev.error === undefined &&\r\n prev.isFetching\r\n ) {\r\n return prev;\r\n }\r\n return {\r\n status: QueryStatus.Loading,\r\n data: undefined,\r\n error: undefined,\r\n isFetching: true,\r\n };\r\n });\r\n\r\n fetcher(controller.signal).then(\r\n (data) => {\r\n if (!controller.signal.aborted) {\r\n setState({\r\n status: QueryStatus.Success,\r\n data,\r\n error: undefined,\r\n isFetching: false,\r\n });\r\n }\r\n // Drain pending refetch resolvers whether or not this fetch\r\n // was superseded. The caller of `refetch()` wants to know the\r\n // operation FINISHED — they don't care whether a later\r\n // refetch happened to invalidate this one mid-flight. Without\r\n // this, an aborted-mid-flight refetch leaves its resolver\r\n // dangling forever and any `await refetch()` in\r\n // save-then-refresh code hangs the test.\r\n const resolvers = pendingResolversRef.current;\r\n pendingResolversRef.current = [];\r\n for (const r of resolvers) r.resolve();\r\n },\r\n (err: unknown) => {\r\n // Aborted requests aren't errors — they happen on every deps-change/unmount.\r\n // Drain resolvers (resolve, not reject) so awaiters don't hang\r\n // when the underlying fetch was just superseded.\r\n if (controller.signal.aborted || (err instanceof Error && err.name === 'AbortError')) {\r\n const resolvers = pendingResolversRef.current;\r\n pendingResolversRef.current = [];\r\n for (const r of resolvers) r.resolve();\r\n return;\r\n }\r\n // Errors wipe `data` regardless of keepPreviousData — a status='error'\r\n // state with stale data on screen is a confusing surface for consumers\r\n // (which one drives rendering?) and react-query handles it the same way.\r\n setState({\r\n status: QueryStatus.Error,\r\n data: undefined,\r\n error: err as PowerPortalsProError,\r\n isFetching: false,\r\n });\r\n const resolvers = pendingResolversRef.current;\r\n pendingResolversRef.current = [];\r\n for (const r of resolvers) r.reject(err);\r\n },\r\n );\r\n\r\n return () => {\r\n controller.abort();\r\n // Drain pending refetch resolvers on abort so callers awaiting\r\n // a `refetch()` promise don't hang when the request gets\r\n // cancelled by a deps change or unmount. Resolve (not reject) —\r\n // the typical caller is \"save then refresh\" code that doesn't\r\n // care whether the post-save fetch completed or was superseded;\r\n // they only care that the operation finished.\r\n const resolvers = pendingResolversRef.current;\r\n pendingResolversRef.current = [];\r\n for (const r of resolvers) r.resolve();\r\n };\r\n // We intentionally rely on the consumer-supplied deps array — fetcher is captured\r\n // each render but only the deps drive when we re-fire. ESLint can't see through\r\n // this to validate, so disabling exhaustive-deps for this effect is correct.\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [...deps, enabled, refetchTick]);\r\n\r\n const refetch = useCallback((): Promise<void> => {\r\n return new Promise<void>((resolve, reject) => {\r\n pendingResolversRef.current.push({ resolve, reject });\r\n setRefetchTick((n) => n + 1);\r\n });\r\n }, []);\r\n\r\n return { ...state, refetch };\r\n}\r\n","import type { TableRecord } from '@powerportalspro/core';\r\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\nexport interface UseRecordOptions {\r\n /** Column logical names to project. Omit for the table's full visible column set. */\r\n columns?: readonly string[];\r\n /** When false, the hook stays in the idle (success / data: undefined) state and skips fetching. Defaults to true. */\r\n enabled?: boolean;\r\n}\r\n\r\n/**\r\n * Fetches a single Dataverse record by table + id. Re-runs when `tableLogicalName`,\r\n * `recordId`, or the joined column list changes; aborts in-flight requests on unmount.\r\n *\r\n * The `data` field is the raw `TableRecord` envelope (id, tableName, properties,\r\n * permissions, formattedValues). Drill into `data.properties[columnName]` for typed\r\n * column values; `data.formattedValues[columnName]` for the display string Dataverse\r\n * supplied (already formatted in the user's locale).\r\n *\r\n * Permission enforcement happens server-side — an unauthorized read surfaces as a\r\n * `UnauthorizedAccessError` in `error`, not a partial record.\r\n */\r\nexport function useRecord(\r\n tableLogicalName: string,\r\n recordId: string,\r\n options?: UseRecordOptions,\r\n): QueryResult<TableRecord> {\r\n const { ppp } = usePowerPortalsPro();\r\n const columns = options?.columns;\r\n const enabled = options?.enabled ?? true;\r\n\r\n return useAsyncResource(\r\n (signal) => ppp.retrieveRecordAsync(tableLogicalName, recordId, columns, signal),\r\n // Joining the columns array is the cheapest way to get a stable dep without\r\n // forcing the consumer to memoize. Column lists are short (typically <20).\r\n [ppp, tableLogicalName, recordId, columns?.join(',') ?? ''],\r\n enabled,\r\n );\r\n}\r\n","import type { RetrieveRecordsResponse } from '@powerportalspro/core';\r\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\nexport interface UseFetchRecordsOptions {\r\n /** When false, the hook stays in the idle (success / data: undefined) state and skips fetching. Defaults to true. */\r\n enabled?: boolean;\r\n}\r\n\r\n/**\r\n * Runs a FetchXML query. Re-runs when the `fetchXml` string changes; aborts\r\n * in-flight requests on unmount. The query string is the FetchXML XML literal —\r\n * the same format Dataverse natively accepts.\r\n *\r\n * `data.records` is the matched record list; `data.pagingCookie` and friends drive\r\n * the standard FetchXML paging UX. Sorting, filtering, and link-entity joins are all\r\n * encoded in the FetchXML — for grid scenarios a higher-level hook (in\r\n * `@powerportalspro/react-fluent`'s eventual `MainGrid`) will manage the FetchXML\r\n * lifecycle around view changes.\r\n */\r\nexport function useFetchRecords(\r\n fetchXml: string,\r\n options?: UseFetchRecordsOptions,\r\n): QueryResult<RetrieveRecordsResponse> {\r\n const { ppp } = usePowerPortalsPro();\r\n const enabled = options?.enabled ?? true;\r\n\r\n return useAsyncResource(\r\n (signal) => ppp.retrieveRecordsAsync(fetchXml, signal),\r\n [ppp, fetchXml],\r\n enabled,\r\n );\r\n}\r\n","import type { GridDataRequest, GridDataResponse } from '@powerportalspro/core';\r\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\nexport interface UseGridDataOptions {\r\n /** When false, the hook stays in the idle (success / data: undefined) state and skips fetching. Defaults to true. */\r\n enabled?: boolean;\r\n /**\r\n * When true, deps-change refetches (new page, new sort, new search text)\r\n * hold the prior successful response on screen while the next request is\r\n * in flight — `status` stays `'success'`, `data` stays populated, and\r\n * `isFetching` flips to true so consumers can scrim the UI. Defaults to\r\n * false, matching the strict state-machine behavior the other hooks use.\r\n * Grids and other long-lived data surfaces will typically want this on.\r\n */\r\n keepPreviousData?: boolean;\r\n}\r\n\r\n/**\r\n * Grid-data counterpart to {@link useFetchRecords}. Calls the server's\r\n * `POST /api/grids/data` endpoint, which composes the final FetchXML via the\r\n * shared `IFetchXmlQueryComposer` — applying search across the view's\r\n * searchable columns, replacing sorts, AND-merging filters, etc. — so the\r\n * search / sort / paging semantics match the Blazor grid exactly.\r\n *\r\n * Response includes both the row data (`tableRecords`, `pagingInfo`) and a\r\n * server-resolved `columns` array — display names already localized, types\r\n * for cell dispatch, sortability flags, widths. Grid consumers render\r\n * straight from the response without a separate table-metadata round-trip.\r\n *\r\n * Re-runs whenever any field of the `request` changes (compared via JSON\r\n * serialization — cheap, and the request is a small flat object). Aborts\r\n * in-flight requests on unmount or when the request changes.\r\n *\r\n * Exactly one of `request.viewId` or `request.fetchXml` must be supplied;\r\n * sending both or neither produces a 400 from the server.\r\n */\r\nexport function useGridData(\r\n request: GridDataRequest,\r\n options?: UseGridDataOptions,\r\n): QueryResult<GridDataResponse> {\r\n const { ppp } = usePowerPortalsPro();\r\n const enabled = options?.enabled ?? true;\r\n const keepPreviousData = options?.keepPreviousData ?? false;\r\n // Stringify the request so the dep array is value-based, not identity-\r\n // based — consumers commonly construct the request inline on each\r\n // render, which would otherwise force a refetch every render.\r\n const requestKey = JSON.stringify(request);\r\n\r\n return useAsyncResource(\r\n (signal) => ppp.loadGridAsync(request, signal),\r\n [ppp, requestKey],\r\n enabled,\r\n keepPreviousData,\r\n );\r\n}\r\n","import type {\n ChartAggregation,\n ChartData,\n ColumnSort,\n GridFilterBase,\n GridDataRequest,\n PowerPortalsProError,\n ResolvedColumn,\n TableSecurityPermission,\n TableRecord,\n} from '@powerportalspro/core';\nimport { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\nimport { QueryStatus } from './internal/use-async-resource.js';\nimport { useGridData } from './use-grid-data.js';\n\n/**\n * Soft cross-filter — a chart slice the user clicked but didn't drill\n * through. Drives styling on consumers (chart: emphasize matching\n * slice + dim others; grid: dim non-matching rows) WITHOUT changing\n * the server-side query. Distinct from the hard filter\n * ({@link ViewDataSourceProps.filter}) which IS sent to the server\n * and re-fetches both row page and aggregations.\n */\nexport interface ViewDataSourceHighlight {\n /** Column the user clicked into — the slice's group-by column. */\n readonly column: string;\n /** Raw column value to match. Compared by strict equality. */\n readonly value: unknown;\n}\n\n/**\n * Options accepted by {@link useViewDataSource}. Split into two roles:\n *\n * - **Props** (forwarded straight into every request) — `viewId` /\n * `fetchXml` source, `staticFilters` (always-applied, never cleared\n * by mutators — the SubGrid relationship-filter case), `aggregations`\n * spec, behavior knobs (`enabled`, `keepPreviousData`, `debounceMs`).\n * - **Initial state** (seeds once on mount, mutators take over) —\n * `initialFilter`, `initialSort`, `initialSearch`, `initialPage`,\n * `initialPageSize`, `initialHighlight`.\n *\n * The same datasource instance feeds a `<MainGrid>` and a\n * `<DataverseChart>` (or two of either) — they all read the same row\n * page + aggregations, so filter changes from any view land\n * everywhere in one round-trip.\n */\nexport interface UseViewDataSourceOptions {\n /**\n * Stored Dataverse view to query. Mutually exclusive with\n * {@link fetchXml}. Reading the view's columns drives the\n * search-column-type dispatch.\n */\n viewId?: string;\n /**\n * Caller-supplied FetchXML to query. Mutually exclusive with\n * {@link viewId}. Useful for custom views.\n */\n fetchXml?: string;\n /**\n * Filters that the datasource never mutates — always sent verbatim\n * with every request. The primary use case is `<SubGrid>` passing a\n * `RelationshipFilter` to scope rows to a parent record; the\n * datasource's user-driven {@link ViewDataSource.filter} state\n * layers on top.\n */\n staticFilters?: readonly GridFilterBase[];\n /**\n * Optional aggregations to compute alongside the row page. Each\n * entry becomes one entry in `GridDataRequest.Aggregations`; the\n * server returns one `ChartData` per entry in\n * {@link ViewDataSource.aggregationResults}, same order.\n * <c>undefined</c> / empty → no chart pass; the response's\n * `aggregations` slot is `null`. Treated as a prop (changes\n * re-fetch) — if you need it to be mutable state inside the\n * datasource, lift it to the consumer.\n */\n aggregations?: readonly ChartAggregation[];\n\n /** Seed for {@link ViewDataSource.filter}. Defaults to empty. */\n initialFilter?: readonly GridFilterBase[];\n /** Seed for {@link ViewDataSource.sort}. Defaults to empty. */\n initialSort?: readonly ColumnSort[];\n /** Seed for {@link ViewDataSource.search}. Defaults to empty string. */\n initialSearch?: string;\n /** Seed for {@link ViewDataSource.page}. Defaults to 1. */\n initialPage?: number;\n /** Seed for {@link ViewDataSource.pageSize}. Defaults to 50. */\n initialPageSize?: number;\n /** Seed for {@link ViewDataSource.highlight}. Defaults to undefined. */\n initialHighlight?: ViewDataSourceHighlight;\n\n /**\n * Debounce window in ms for the actual server request. State setter\n * calls land synchronously (consumers reading `ds.search` see the\n * latest typed value), but the network fetch fires once the\n * debounce settles. Defaults to 300ms; pass 0 to disable.\n */\n debounceMs?: number;\n /** When false, no fetch fires. Defaults to true. */\n enabled?: boolean;\n /**\n * Keep the prior successful response on screen during deps-change\n * refetches. Defaults to true — grids and charts typically want\n * scrim-during-refetch over wiping the UI.\n */\n keepPreviousData?: boolean;\n}\n\n/**\n * The reactive view of one query, shared by every consumer (grid,\n * chart, side-panel, etc.) wired to the same {@link useViewDataSource}\n * call. All consumers see the same row page + aggregations because\n * the server returns them in a single response (atomically consistent\n * — no race where the chart shows aggregations for an old filter\n * while the grid shows rows for the new one).\n *\n * Reads (`filter` / `sort` / `search` / `rows` / `aggregationResults`)\n * are snapshots at the time the hook ran. Mutators\n * (`setFilter` / `setSearch` / `setHighlight` / etc.) trigger\n * re-renders of the hook owner, which propagates to all consumers.\n *\n * Stable identity per snapshot — the returned object only changes\n * reference when one of its fields actually changes. Safe to pass\n * through `React.memo`'d component trees.\n */\nexport interface ViewDataSource {\n // ---- Query state — owned, mutable via the setters below ----------------\n\n /**\n * User-driven filter applied on top of {@link UseViewDataSourceOptions.staticFilters}.\n * Always shipped as an AND-merge with the view's base filter.\n */\n readonly filter: readonly GridFilterBase[];\n readonly sort: readonly ColumnSort[];\n readonly search: string;\n readonly page: number;\n readonly pageSize: number;\n /**\n * Soft cross-filter for chart slice clicks — drives client-side\n * styling on consumers (chart slice emphasis, grid row dimming)\n * without changing the server query. Set to `undefined` to clear.\n * Distinct from {@link filter}, which IS a hard filter.\n */\n readonly highlight: ViewDataSourceHighlight | undefined;\n\n // ---- Forwarded from options (consumer-controlled) ----------------------\n\n readonly aggregations: readonly ChartAggregation[];\n\n // ---- Server response ---------------------------------------------------\n\n readonly status: QueryStatus;\n readonly rows: readonly TableRecord[];\n readonly totalCount: number | string | null | undefined;\n readonly columns: readonly ResolvedColumn[];\n /**\n * Current user's combined table-level `TableSecurityPermission` mask for\n * the view's table — comes from the server response's\n * `GridDataResponse.tablePermissions`. Mirrors the per-row\n * permissions but at table scope, so toolbar buttons (`<MainGrid>`'s\n * New / Delete and consumer-built equivalents) can gate off one\n * authoritative value instead of sampling row[0] as a proxy.\n * `undefined` until the first response lands.\n */\n readonly tablePermissions: TableSecurityPermission | undefined;\n /**\n * One {@link ChartData} per requested aggregation, in request order.\n * Empty when no aggregations were requested OR when the response\n * hasn't landed yet — consumers can render an empty / spinner state\n * uniformly.\n */\n readonly aggregationResults: readonly ChartData[];\n readonly error: PowerPortalsProError | undefined;\n /** True whenever a fetch is in flight (including debounced refetches). */\n readonly isFetching: boolean;\n /**\n * True only when the in-flight request actually carries the\n * `aggregations` spec — i.e., when the chart's data is about to\n * change. The skip-on-sort optimization above means that a column-\n * header click on a sibling grid fires a row-page-only refetch the\n * server processes without re-running aggregations, and this flag\n * stays false through that. Chart consumers ({@link DataverseChart})\n * gate their loading overlay on this so a sort-only refetch doesn't\n * flash a spinner over an unchanged chart.\n *\n * Always false when {@link aggregations} is empty (no chart data\n * being requested at all).\n */\n readonly isFetchingAggregations: boolean;\n\n // ---- Mutators ----------------------------------------------------------\n\n setFilter(filter: readonly GridFilterBase[]): void;\n setSort(sort: readonly ColumnSort[]): void;\n setSearch(search: string): void;\n setPage(page: number): void;\n setPageSize(pageSize: number): void;\n setHighlight(highlight: ViewDataSourceHighlight | undefined): void;\n /** Re-fires the current request immediately (skipping the debounce). */\n refresh(): Promise<void>;\n}\n\n/**\n * Internal reducer state. Mutators dispatch one of the action shapes\n * below; the request build pulls these values + the option props.\n */\ninterface State {\n readonly filter: readonly GridFilterBase[];\n readonly sort: readonly ColumnSort[];\n readonly search: string;\n readonly page: number;\n readonly pageSize: number;\n readonly highlight: ViewDataSourceHighlight | undefined;\n}\n\ntype Action =\n | { type: 'setFilter'; filter: readonly GridFilterBase[] }\n | { type: 'setSort'; sort: readonly ColumnSort[] }\n | { type: 'setSearch'; search: string }\n | { type: 'setPage'; page: number }\n | { type: 'setPageSize'; pageSize: number }\n | { type: 'setHighlight'; highlight: ViewDataSourceHighlight | undefined };\n\nfunction reduce(state: State, action: Action): State {\n switch (action.type) {\n // Filter / sort / search / pageSize changes invalidate the current\n // page — the new result set has different rows, the user expects to\n // land on page 1. Mirrors what `<MainGrid>` does for standalone\n // mode. Highlight changes DON'T reset the page (purely client-side).\n case 'setFilter':\n return { ...state, filter: action.filter, page: 1 };\n case 'setSort':\n return { ...state, sort: action.sort, page: 1 };\n case 'setSearch':\n return { ...state, search: action.search, page: 1 };\n case 'setPage':\n return { ...state, page: action.page };\n case 'setPageSize':\n return { ...state, pageSize: action.pageSize, page: 1 };\n case 'setHighlight':\n return { ...state, highlight: action.highlight };\n }\n}\n\n/**\n * Shared {@link GridDataRequest} + chart-spec state for grid+chart\n * scenarios. Hosts the filter / sort / search / page / aggregations\n * once for any combination of `<MainGrid dataSource={ds}>` and\n * `<DataverseChart dataSource={ds}>` consumers — one server request\n * keeps the row page and the chart aggregations atomically consistent.\n *\n * Standalone usage (no chart) still works: omit the `aggregations`\n * option and the datasource is just a hoisted-state grid hook.\n *\n * **Typical usage:**\n *\n * ```tsx\n * function OpportunitiesPage() {\n * const ds = useViewDataSource({\n * viewId: openOpportunitiesViewId,\n * aggregations: [\n * { groupBy: { column: 'stepname' },\n * aggregateColumn: 'estimatedvalue',\n * aggregate: AggregateType.Sum },\n * ],\n * });\n * return (\n * <>\n * <DataverseChart dataSource={ds} type=\"bar\" />\n * <MainGrid dataSource={ds} />\n * </>\n * );\n * }\n * ```\n */\nexport function useViewDataSource(options: UseViewDataSourceOptions): ViewDataSource {\n // Stable behavior knobs — captured once on mount so options changes\n // between renders don't churn timer setup. The deps that DO matter\n // (viewId, fetchXml, staticFilters, aggregations) drive the request\n // itself, picked up via the request-build memo.\n const debounceMs = options.debounceMs ?? 300;\n const enabled = options.enabled ?? true;\n const keepPreviousData = options.keepPreviousData ?? true;\n\n const [state, dispatch] = useReducer(reduce, undefined, () => ({\n filter: options.initialFilter ?? EMPTY_FILTERS,\n sort: options.initialSort ?? EMPTY_SORTS,\n search: options.initialSearch ?? '',\n page: options.initialPage ?? 1,\n pageSize: options.initialPageSize ?? 50,\n highlight: options.initialHighlight,\n }));\n\n // Aggregation-skip optimization. Aggregations are sort/page/pageSize-\n // independent server-side (the row page slices the result; the chart\n // sees the full filtered set), so re-running them when the user just\n // clicked a column header or paged is wasted work.\n //\n // Strategy:\n // 1. Fingerprint the aggregation-AFFECTING inputs (viewId / fetchXml\n // / staticFilters / state.filter / state.search / aggregations\n // spec).\n // 2. Track which fingerprint the LAST successful response was for.\n // 3. When building the next request, only include the `aggregations`\n // spec if the current fingerprint differs from the last one we\n // got back. Sort/page changes leave the fingerprint identical →\n // no spec sent → server's GridService skips the entire\n // aggregation pass.\n // 4. When the response comes back without aggregations (because we\n // skipped), the chart falls back to the cached results from the\n // last fetch that did include them — see `aggregationResults`\n // below.\n //\n // `null` fingerprint means \"no aggregations requested at all\" — the\n // optimization short-circuits and we never include them.\n const aggregationFingerprint = useMemo(() => {\n if (!options.aggregations || options.aggregations.length === 0) return null;\n return JSON.stringify({\n viewId: options.viewId,\n fetchXml: options.fetchXml,\n staticFilters: options.staticFilters,\n filter: state.filter,\n search: state.search,\n aggregations: options.aggregations,\n });\n }, [\n options.viewId,\n options.fetchXml,\n options.staticFilters,\n options.aggregations,\n state.filter,\n state.search,\n ]);\n\n // `null` until the first aggregation response lands. Updated by the\n // post-response effect below. Read inside `request` via ref so\n // changes between renders don't churn the memo identity (the memo's\n // own dep on `aggregationFingerprint` already handles re-evaluation\n // when fingerprint changes).\n const lastFetchedAggregationFingerprintRef = useRef<string | null>(null);\n const cachedAggregationResultsRef = useRef<readonly ChartData[]>(EMPTY_AGGREGATIONS);\n\n // Memoize the un-debounced request so its identity only changes when\n // a field actually changes. State changes flow in immediately;\n // debouncing happens at the level below. The `aggregations` slot is\n // included only when the fingerprint differs from what's cached —\n // sort/page changes leave it omitted.\n const request = useMemo<GridDataRequest>(() => {\n const filters = [\n ...(options.staticFilters ?? []),\n ...state.filter,\n ];\n const includeAggregations =\n aggregationFingerprint !== null\n && aggregationFingerprint !== lastFetchedAggregationFingerprintRef.current;\n return {\n ...(options.viewId !== undefined && { viewId: options.viewId }),\n ...(options.fetchXml !== undefined && { fetchXml: options.fetchXml }),\n sorts: state.sort as ColumnSort[],\n pageNumber: state.page,\n pageSize: state.pageSize,\n ...(state.search !== '' && { searchText: state.search }),\n ...(filters.length > 0 && { filters }),\n ...(includeAggregations && options.aggregations && {\n aggregations: options.aggregations as ChartAggregation[],\n }),\n };\n }, [\n options.viewId,\n options.fetchXml,\n options.staticFilters,\n options.aggregations,\n state.filter,\n state.sort,\n state.search,\n state.page,\n state.pageSize,\n aggregationFingerprint,\n ]);\n\n // Debounce the request that feeds useGridData — state updates\n // remain synchronous (so consumers see `ds.search` update on every\n // keystroke) but the network fetch only fires once typing settles.\n // `debounceMs={0}` disables the debounce entirely; useful for tests\n // and for programmatic (non-user-driven) flows.\n const debouncedRequest = useDebouncedValue(request, debounceMs);\n\n const query = useGridData(debouncedRequest, { enabled, keepPreviousData });\n\n // Mutators — stable callbacks so consumers can pass them through\n // `useCallback`/effect deps without churn.\n const setFilter = useCallback((filter: readonly GridFilterBase[]) => {\n dispatch({ type: 'setFilter', filter });\n }, []);\n const setSort = useCallback((sort: readonly ColumnSort[]) => {\n dispatch({ type: 'setSort', sort });\n }, []);\n const setSearch = useCallback((search: string) => {\n dispatch({ type: 'setSearch', search });\n }, []);\n const setPage = useCallback((page: number) => {\n dispatch({ type: 'setPage', page });\n }, []);\n const setPageSize = useCallback((pageSize: number) => {\n dispatch({ type: 'setPageSize', pageSize });\n }, []);\n const setHighlight = useCallback((highlight: ViewDataSourceHighlight | undefined) => {\n dispatch({ type: 'setHighlight', highlight });\n }, []);\n // `refresh()` bypasses the debounce — useGridData's refetch fires\n // immediately. The promise resolves when the next fetch settles\n // (success or error).\n const refresh = useCallback(async () => {\n await query.refetch();\n }, [query.refetch]);\n\n const data = query.data;\n const rows = data?.tableRecords ?? EMPTY_RECORDS;\n const columns = data?.columns ?? EMPTY_COLUMNS;\n const totalCount = data?.pagingInfo?.totalRecordCount;\n const tablePermissions = data?.tablePermissions;\n const aggregations = options.aggregations ?? EMPTY_AGGREGATION_SPEC;\n\n // True only when the in-flight (debounced) request actually carries\n // the aggregations spec — when the skip-on-sort optimization\n // omitted it, the chart's data won't change and consumers shouldn't\n // show a chart-loading spinner. The same in-flight request still\n // drives the grid's row-page scrim via the generic `isFetching`.\n const isFetchingAggregations =\n query.isFetching\n && debouncedRequest.aggregations !== undefined\n && debouncedRequest.aggregations !== null\n && debouncedRequest.aggregations.length > 0;\n\n // After a successful response that INCLUDED aggregations, cache them\n // + record the fingerprint they correspond to. The next request's\n // build decides whether to re-include aggregations by comparing\n // fingerprints — sort/page changes leave the fingerprint identical,\n // server skips the aggregation pass, and the cached value carries\n // forward.\n //\n // GATED ON DATA-INSTANCE CHANGE (via lastDataRef), not on the deps\n // array alone. Without this guard, a fingerprint change (typed\n // search) fires the effect BEFORE the new fetch lands — at that\n // moment `data` is still the prior response (kept on screen by\n // `keepPreviousData`), so `data.aggregations` carries the OLD\n // aggregations. The effect would then mislabel the OLD aggregations\n // with the NEW fingerprint, and a follow-up sort would pull stale\n // data out of the cache. Compare data by identity to update only\n // when a new response actually arrived.\n const lastSeenDataRef = useRef<typeof data>(undefined);\n useEffect(() => {\n if (data === lastSeenDataRef.current) return;\n lastSeenDataRef.current = data;\n if (data?.aggregations) {\n cachedAggregationResultsRef.current = data.aggregations;\n lastFetchedAggregationFingerprintRef.current = aggregationFingerprint;\n }\n }, [data, aggregationFingerprint]);\n\n // Effective aggregationResults: latest server response (if it\n // included aggregations) OR the cached value from the last fetch\n // that did. Falls through to empty when the spec is currently\n // empty, regardless of what's cached — consumer toggled\n // aggregations off, chart should show empty.\n const aggregationResults =\n (options.aggregations?.length ?? 0) === 0\n ? EMPTY_AGGREGATIONS\n : data?.aggregations ?? cachedAggregationResultsRef.current;\n\n return useMemo<ViewDataSource>(\n () => ({\n filter: state.filter,\n sort: state.sort,\n search: state.search,\n page: state.page,\n pageSize: state.pageSize,\n highlight: state.highlight,\n aggregations,\n status: query.status,\n rows,\n totalCount,\n columns,\n tablePermissions,\n aggregationResults,\n error: query.error,\n isFetching: query.isFetching,\n isFetchingAggregations,\n setFilter,\n setSort,\n setSearch,\n setPage,\n setPageSize,\n setHighlight,\n refresh,\n }),\n [\n state.filter,\n state.sort,\n state.search,\n state.page,\n state.pageSize,\n state.highlight,\n aggregations,\n query.status,\n rows,\n totalCount,\n columns,\n tablePermissions,\n aggregationResults,\n query.error,\n query.isFetching,\n isFetchingAggregations,\n setFilter,\n setSort,\n setSearch,\n setPage,\n setPageSize,\n setHighlight,\n refresh,\n ],\n );\n}\n\n// Stable module-level empties so the returned snapshot identity only\n// flips when something actually changes. Cheaper than recreating arrays\n// per render.\nconst EMPTY_FILTERS: readonly GridFilterBase[] = [];\nconst EMPTY_SORTS: readonly ColumnSort[] = [];\nconst EMPTY_RECORDS: readonly TableRecord[] = [];\nconst EMPTY_COLUMNS: readonly ResolvedColumn[] = [];\nconst EMPTY_AGGREGATIONS: readonly ChartData[] = [];\nconst EMPTY_AGGREGATION_SPEC: readonly ChartAggregation[] = [];\n\n/**\n * Light-weight value debouncer. Returns the latest value after no\n * change for {@code delayMs} ms. {@code delayMs=0} short-circuits\n * (returns the live value with no scheduling) — useful for tests and\n * programmatic flows that don't want debounce noise. Compared by\n * JSON-stringified identity so equivalent-but-fresh objects (e.g.\n * `request = useMemo(...)` producing structurally equal output) don't\n * spuriously fire the debounce.\n */\nfunction useDebouncedValue<T>(value: T, delayMs: number): T {\n const [debounced, setDebounced] = useState(value);\n // Avoid scheduling a no-op timer when nothing changed. JSON\n // stringify on a small request object is cheap and keeps the\n // useEffect-cleanup churn from cancelling a real in-flight debounce\n // (e.g. parent re-renders inline-rebuilding the request).\n const valueKey = JSON.stringify(value);\n const previousKeyRef = useRef(valueKey);\n\n useEffect(() => {\n if (delayMs === 0) {\n // No debounce — sync the latest value immediately.\n setDebounced(value);\n previousKeyRef.current = valueKey;\n return;\n }\n if (previousKeyRef.current === valueKey) return;\n const timer = setTimeout(() => {\n setDebounced(value);\n previousKeyRef.current = valueKey;\n }, delayMs);\n return () => clearTimeout(timer);\n // value is captured in the closure; valueKey is the change\n // signal. Including `value` in deps would churn identity-driven\n // restarts of the timer when the value's reference changes but\n // its content doesn't. eslint-disable-next-line\n // react-hooks/exhaustive-deps\n }, [valueKey, delayMs]);\n\n return debounced;\n}\n","import type { TableMetadata } from '@powerportalspro/core';\r\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\nexport interface UseTableMetadataOptions {\r\n enabled?: boolean;\r\n}\r\n\r\n/**\r\n * Fetches the column + relationship metadata for one Dataverse table.\r\n * Routed through the {@link TableMetadataCache} on the provider — first\r\n * call kicks off a server round-trip, concurrent calls dedupe onto the\r\n * same in-flight promise, and resolved values are reused for the\r\n * lifetime of the `PowerPortalsProClient`. Critical for the inline-\r\n * editable grid, where N visible rows each mount their own\r\n * `<RecordContext>` and would otherwise thunder N parallel\r\n * `retrieveTableMetadataAsync` requests at toggle time.\r\n *\r\n * Coerces the cache's `null` \"table not found\" return to `undefined` in\r\n * the query result so downstream consumers can treat metadata absence\r\n * uniformly via `data == null`.\r\n */\r\nexport function useTableMetadata(\r\n tableLogicalName: string,\r\n options?: UseTableMetadataOptions,\r\n): QueryResult<TableMetadata> {\r\n const { tableMetadataCache } = usePowerPortalsPro();\r\n const enabled = options?.enabled ?? true;\r\n\r\n return useAsyncResource(\r\n async () => {\r\n const result = await tableMetadataCache.getAsync(tableLogicalName);\r\n // The cache returns `null` only when the table doesn't exist. The\r\n // hook's `data` slot is typed as `TableMetadata | undefined`; map\r\n // null → undefined so downstream `data == null` checks work\r\n // uniformly. (`useAsyncResource` treats `undefined` data + status:\r\n // 'success' as \"no result.\")\r\n return (result ?? undefined) as TableMetadata;\r\n },\r\n [tableMetadataCache, tableLogicalName],\r\n enabled,\r\n );\r\n}\r\n","import type { TableSecurityPermission } from '@powerportalspro/core';\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\nexport interface UseTablePermissionsOptions {\n enabled?: boolean;\n}\n\n/**\n * Fetches the current user's combined table-level `TableSecurityPermission`\n * mask for one Dataverse table. Mirrors the server-side\n * `ITablePermissionCache.GetPermissionForUserAsync` lookup the Blazor\n * grid buttons already use directly via DI — exposed here so React\n * consumers can drive the same \"Can the user create / delete on this\n * table?\" gates without firing a grid query.\n *\n * When a `<MainGrid>` / `<SubGrid>` is on the page the grid response\n * already carries the same value as `GridDataResponse.tablePermissions`\n * and toolbar buttons read it from there; reach for this hook when\n * you're rendering UI outside a grid (a custom toolbar, conditional\n * navigation, a \"permission required\" tooltip) and don't want to fire\n * a grid query just to read the permission bits.\n *\n * Returns `data: TableSecurityPermission` once the fetch resolves. The\n * caller compares via bit math, e.g.\n * `(data & TableSecurityPermission.Create) === TableSecurityPermission.Create`.\n *\n * Empty `tableLogicalName` returns `data: TableSecurityPermission.None` (0)\n * without firing a server round-trip — matches the server-side cache\n * which returns `None` for null/empty table names.\n */\nexport function useTablePermissions(\n tableLogicalName: string,\n options?: UseTablePermissionsOptions,\n): QueryResult<TableSecurityPermission> {\n const { ppp } = usePowerPortalsPro();\n const enabled = (options?.enabled ?? true) && tableLogicalName.length > 0;\n\n return useAsyncResource(\n async () => ppp.retrieveTablePermissionsAsync(tableLogicalName),\n [ppp, tableLogicalName],\n enabled,\n );\n}\n","import type { ViewMetadata } from '@powerportalspro/core';\r\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\nexport interface UseViewMetadataOptions {\r\n enabled?: boolean;\r\n}\r\n\r\n/**\r\n * Fetches metadata for one Dataverse view by its id (a GUID string).\r\n * Routed through the {@link ViewMetadataCache} on the provider — first\r\n * call hits the server, concurrent calls dedupe, resolved values are\r\n * reused for the lifetime of the `PowerPortalsProClient`. Use\r\n * {@link useViewsForTable} when you want every view for a table instead.\r\n */\r\nexport function useViewMetadata(\r\n viewId: string,\r\n options?: UseViewMetadataOptions,\r\n): QueryResult<ViewMetadata> {\r\n const { viewMetadataCache } = usePowerPortalsPro();\r\n const enabled = options?.enabled ?? true;\r\n\r\n return useAsyncResource(\r\n async () => {\r\n const result = await viewMetadataCache.getAsync(viewId);\r\n return (result ?? undefined) as ViewMetadata;\r\n },\r\n [viewMetadataCache, viewId],\r\n enabled,\r\n );\r\n}\r\n","import type { ViewMetadata } from '@powerportalspro/core';\r\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\nexport interface UseViewsForTableOptions {\r\n enabled?: boolean;\r\n}\r\n\r\n/**\r\n * Lists every view for a given table. Drives view-pickers and default-view\r\n * resolution. Routed through {@link ViewsForTableCache} so two grids on\r\n * the same page targeting the same table share one fetch.\r\n */\r\nexport function useViewsForTable(\r\n tableLogicalName: string,\r\n options?: UseViewsForTableOptions,\r\n): QueryResult<readonly ViewMetadata[]> {\r\n const { viewsForTableCache } = usePowerPortalsPro();\r\n const enabled = options?.enabled ?? true;\r\n\r\n return useAsyncResource(\r\n () => viewsForTableCache.getAsync(tableLogicalName),\r\n [viewsForTableCache, tableLogicalName],\r\n enabled,\r\n );\r\n}\r\n","import type { components } from '@powerportalspro/core';\r\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\ntype EnvironmentFileSettings = components['schemas']['EnvironmentFileSettings'];\r\n\r\nexport interface UseEnvironmentFileSettingsOptions {\r\n enabled?: boolean;\r\n}\r\n\r\n/**\r\n * Fetches the environment-wide file upload settings (blocked extension\r\n * list, organization-level max upload size). Mirrors the Blazor\r\n * <c>IPowerPortalsProService.GetEnvironmentFileSettingsAsync</c> call.\r\n *\r\n * Routed through {@link EnvironmentFileSettingsCache} on the provider —\r\n * concurrent callers (e.g. multiple `<FileGrid>`s on the same page)\r\n * share a single fetch, and the resolved value is reused across re-\r\n * mounts for the lifetime of the `PowerPortalsProClient`. The cap\r\n * reported here is the org-wide ceiling — the effective per-upload cap\r\n * also has to account for per-column <c>maxSizeInKB</c> from\r\n * {@link useTableMetadata}.\r\n */\r\nexport function useEnvironmentFileSettings(\r\n options?: UseEnvironmentFileSettingsOptions,\r\n): QueryResult<EnvironmentFileSettings> {\r\n const { environmentFileSettingsCache } = usePowerPortalsPro();\r\n const enabled = options?.enabled ?? true;\r\n\r\n return useAsyncResource(\r\n async () => {\r\n const result = await environmentFileSettingsCache.getAsync();\r\n return (result ?? undefined) as EnvironmentFileSettings;\r\n },\r\n [environmentFileSettingsCache],\r\n enabled,\r\n );\r\n}\r\n","import { useContext } from 'react';\r\nimport { QueryStatus, useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\r\nimport { LocalizationManifestContext } from './localizer.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\nexport interface UseSupportedLocalesOptions {\r\n /**\r\n * Gates the fetch. Defaults to `true` — the manifest is tiny (a\r\n * handful of locale codes) and effectively free to fetch. Set\r\n * `false` only when the consumer wants to suppress the call until\r\n * some later condition (e.g. only fetch after auth resolves).\r\n */\r\n enabled?: boolean;\r\n}\r\n\r\n/**\r\n * Returns the framework's configured supported UI locales. Reads from\r\n * the {@link LocalizationManifestContext} populated by\r\n * `DefaultLocalizerProvider`'s tier-1 manifest fetch — so call sites\r\n * (typically `<LanguageDropdown>`) reuse the already-loaded data\r\n * instead of issuing their own `/localizations/version` round-trip\r\n * every time the hook mounts. The previous implementation re-fetched\r\n * on each mount, which surfaced as one extra `/localizations/version`\r\n * request on every page navigation in apps where the dropdown sits\r\n * inside route-level chrome.\r\n *\r\n * Fallback path: when no `DefaultLocalizerProvider` is mounted (rare —\r\n * consumers who wired their own i18next setup via\r\n * `localizationUrl={false}`), the hook still issues its own fetch so\r\n * the API works in isolation. The fallback uses the same\r\n * `useAsyncResource` shape it always did, so the {@link QueryResult}\r\n * surface is unchanged.\r\n *\r\n * The Blazor side gets this server-side via\r\n * `IOptions<RequestLocalizationOptions>.SupportedUICultures`; in\r\n * React the same configuration is exposed through the localization\r\n * manifest endpoint at `/localizations/version`. Order matches the\r\n * server's configured preference, default culture first.\r\n */\r\nexport function useSupportedLocales(\r\n options?: UseSupportedLocalesOptions,\r\n): QueryResult<readonly string[]> {\r\n const { ppp } = usePowerPortalsPro();\r\n const enabled = options?.enabled ?? true;\r\n const manifestState = useContext(LocalizationManifestContext);\r\n // Skip the local fetch when the provider's manifest is in scope —\r\n // the provider has either already fetched (data available) OR is\r\n // about to fetch (data undefined but will update soon). Either way,\r\n // we read from the provider's state instead of duplicating the\r\n // round-trip. `useAsyncResource` still runs (so the hook returns\r\n // the same shape), but with `enabled: false` it stays idle.\r\n const providerScoped = manifestState !== null;\r\n const fallback = useAsyncResource(\r\n async () => {\r\n const manifest = await ppp.retrieveLocalizationBundleManifestAsync();\r\n return manifest.supportedLocales;\r\n },\r\n [ppp],\r\n enabled && !providerScoped,\r\n );\r\n\r\n if (providerScoped) {\r\n // Mirror the QueryResult shape so consumers get the same fields\r\n // regardless of which code path served the data. `data` is undefined\r\n // while the provider's fetch is still in flight; `status` reflects\r\n // that as Loading. Once the provider populates the supported-locale\r\n // list, this hook re-evaluates and flips to Success.\r\n if (manifestState.supportedLocales === undefined) {\r\n return {\r\n status: enabled ? QueryStatus.Loading : QueryStatus.Success,\r\n data: undefined,\r\n error: undefined,\r\n isFetching: enabled,\r\n refetch: fallback.refetch,\r\n };\r\n }\r\n return {\r\n status: QueryStatus.Success,\r\n data: manifestState.supportedLocales,\r\n error: undefined,\r\n isFetching: false,\r\n refetch: fallback.refetch,\r\n };\r\n }\r\n\r\n return fallback;\r\n}\r\n","import { useEffect, useState } from 'react';\r\n\r\n/**\r\n * Reads the value of a URL query-string parameter and re-renders when\r\n * it changes (via browser back / forward, programmatic\r\n * `history.pushState` / `replaceState`, or hash-only navigation).\r\n * Router-agnostic — uses the platform `URLSearchParams` API directly\r\n * so consumers don't have to pull in a specific router's hook.\r\n *\r\n * Returns `null` when the parameter is absent. SSR-safe — returns\r\n * `null` when `window` is undefined.\r\n *\r\n * **Detecting in-app navigation:** `popstate` covers back / forward\r\n * but the History API has no built-in event for `pushState` /\r\n * `replaceState` calls. We patch both at module load to dispatch a\r\n * synthetic `pushstate` / `replacestate` event, so consumers using\r\n * any router (react-router, TanStack Router, Next.js, hand-rolled)\r\n * get correct re-renders without taking a router dependency.\r\n *\r\n * Multiple components subscribing to the same parameter all re-read\r\n * synchronously — each call sites runs its own `URLSearchParams`\r\n * parse, which is cheap.\r\n */\r\nexport function useQueryParam(name: string): string | null {\r\n const [value, setValue] = useState<string | null>(() => readParam(name));\r\n\r\n useEffect(() => {\r\n if (typeof window === 'undefined') return undefined;\r\n installHistoryPatch();\r\n setValue(readParam(name));\r\n const handler = () => setValue(readParam(name));\r\n window.addEventListener('popstate', handler);\r\n window.addEventListener(PUSH_EVENT, handler);\r\n window.addEventListener(REPLACE_EVENT, handler);\r\n return () => {\r\n window.removeEventListener('popstate', handler);\r\n window.removeEventListener(PUSH_EVENT, handler);\r\n window.removeEventListener(REPLACE_EVENT, handler);\r\n };\r\n }, [name]);\r\n\r\n return value;\r\n}\r\n\r\nconst PUSH_EVENT = 'ppp:pushstate';\r\nconst REPLACE_EVENT = 'ppp:replacestate';\r\n\r\nlet historyPatched = false;\r\nfunction installHistoryPatch(): void {\r\n if (historyPatched) return;\r\n if (typeof window === 'undefined' || typeof window.history === 'undefined') return;\r\n historyPatched = true;\r\n const origPush = window.history.pushState.bind(window.history);\r\n const origReplace = window.history.replaceState.bind(window.history);\r\n window.history.pushState = function (...args: Parameters<History['pushState']>) {\r\n origPush(...args);\r\n window.dispatchEvent(new Event(PUSH_EVENT));\r\n };\r\n window.history.replaceState = function (...args: Parameters<History['replaceState']>) {\r\n origReplace(...args);\r\n window.dispatchEvent(new Event(REPLACE_EVENT));\r\n };\r\n}\r\n\r\nfunction readParam(name: string): string | null {\r\n if (typeof window === 'undefined') return null;\r\n try {\r\n const params = new URLSearchParams(window.location.search);\r\n return params.get(name);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n","import { useEffect } from 'react';\r\n\r\n/**\r\n * Registers a `beforeunload` listener while `condition` is `true`. The\r\n * listener calls `preventDefault()` (and sets `returnValue = ''` for legacy\r\n * compatibility) so the browser shows its native \"Leave site?\" prompt.\r\n *\r\n * Internal helper — both `MainContext` and `RecordContext` use this to\r\n * implement the auto unsaved-changes warning at the top-most context level.\r\n * Public consumers should use {@link useUnsavedChangesWarning} which adds\r\n * the MainContext.isDirty wiring.\r\n */\r\nexport function useBeforeUnloadWhen(condition: boolean): void {\r\n useEffect(() => {\r\n if (!condition) return;\r\n const handler = (e: BeforeUnloadEvent) => {\r\n // Modern browsers ignore any custom message and show their own prompt;\r\n // setting `returnValue` is the legacy fallback that's still required by\r\n // some implementations to actually trigger the prompt.\r\n e.preventDefault();\r\n e.returnValue = '';\r\n };\r\n window.addEventListener('beforeunload', handler);\r\n return () => window.removeEventListener('beforeunload', handler);\r\n }, [condition]);\r\n}\r\n","import type { OrganizationRequest } from '@powerportalspro/core';\r\nimport {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useMemo,\r\n useRef,\r\n useState,\r\n} from 'react';\r\nimport type { ReactNode } from 'react';\r\nimport { useBeforeUnloadWhen } from './internal/use-before-unload-when.js';\r\nimport { useUnsavedChangesRegistry } from './unsaved-changes-registry.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\n\r\n/**\r\n * Per-descendant registration the {@link MainContext} keeps for save/refresh\r\n * coordination + dirty aggregation. Each child component (typically a\r\n * {@link RecordContext}, a `MainGrid`/`SubGrid`, or a many-to-many editor)\r\n * calls {@link MainContextValue.registerDescendant} on mount and the returned\r\n * unregister function on unmount.\r\n *\r\n * The parent uses the registry to orchestrate save / refresh / validate /\r\n * aggregate `IsDirty`. Save is transactional: the parent collects\r\n * {@link getRequests} across every descendant and ships them in a single\r\n * `executeMultiple` round-trip.\r\n *\r\n * `isDirty` is a **getter** (`() => boolean`) — the parent calls it every\r\n * time it recomputes the aggregate. Descendants should back it with a\r\n * ref so it reads the latest value without re-registering. When the\r\n * value flips, the descendant calls\r\n * {@link MainContextValue.notifyDescendantDirtyChanged} to prompt the\r\n * parent to re-read; this is materially cheaper than tearing down and\r\n * re-creating the registration on every transition, which used to\r\n * cascade re-renders through every {@link MainContext} consumer and\r\n * dropped focus on the first keystroke in inline-edit cells.\r\n */\r\nexport interface MainContextDescendant {\r\n /**\r\n * Returns all pending Dataverse operations for this descendant subtree.\r\n * Returns an empty array when nothing is dirty. The parent collects these\r\n * across every descendant and ships them in one transactional\r\n * `executeMultiple` batch. Descendants that themselves contain nested\r\n * descendants (e.g. a {@link RecordContext} hosting a `SubGrid`) should\r\n * include the nested requests in the returned array so the entire subtree\r\n * lands in a single batch.\r\n */\r\n readonly getRequests: () => OrganizationRequest[];\r\n /**\r\n * Returns `true` when the descendant subtree is in a state suitable for\r\n * save. The parent calls this before collecting {@link getRequests};\r\n * returning `false` aborts the save without firing any server-side\r\n * operations.\r\n *\r\n * For descendants whose validation lives in a parallel system (e.g.\r\n * {@link RecordContext} which routes per-column validation through\r\n * `ValidationContext`), this can simply return `true` and let the\r\n * dedicated validation surface drive the abort decision separately.\r\n */\r\n readonly validate: () => boolean | Promise<boolean>;\r\n /** Reloads this descendant subtree from the server. */\r\n readonly refresh: () => Promise<void>;\r\n /**\r\n * Discards every pending edit in this descendant subtree, reverting back\r\n * to the loaded baseline WITHOUT re-fetching from the server. The \"Cancel\"\r\n * / \"Discard\" primitive.\r\n *\r\n * Implementations should clear their own pending queue (`pendingChanges`\r\n * for `RecordContext`, the queued row updates / creates / associates /\r\n * disassociates for `MainGrid`, the pending adds / removes for\r\n * `<ManyToManyLookupEdit>`, etc.) AND cascade to any nested descendants\r\n * so the entire subtree is clean afterward.\r\n */\r\n readonly resetState: () => void;\r\n /**\r\n * Getter the parent calls when recomputing aggregate dirty state.\r\n * Back it with a ref so the latest value is visible without\r\n * re-registering; call\r\n * {@link MainContextValue.notifyDescendantDirtyChanged} whenever the\r\n * underlying value transitions so the parent knows to re-read.\r\n */\r\n readonly isDirty: () => boolean;\r\n}\r\n\r\nexport interface MainContextValue {\r\n /**\r\n * Transactionally saves the entire descendant subtree:\r\n *\r\n * 1. Calls {@link MainContextProps.onBeforeSave} (if wired). Throwing\r\n * aborts the save before any server call.\r\n * 2. Runs {@link validate} across descendants; returns early without\r\n * saving when any descendant reports invalid.\r\n * 3. Collects {@link MainContextDescendant.getRequests} from every\r\n * registered descendant.\r\n * 4. If non-empty, ships the combined array in one\r\n * `executeMultipleAsync` round-trip so the operations land\r\n * transactionally on the server.\r\n * 5. Calls {@link refreshAll} so the local state picks up server-applied\r\n * side-effects (computed columns, plugin defaults, the modifiedon\r\n * timestamp, etc.).\r\n *\r\n * Throws on the first server-side failure; later steps are skipped.\r\n */\r\n readonly saveAll: () => Promise<void>;\r\n\r\n /** Refreshes every registered descendant. */\r\n readonly refreshAll: () => Promise<void>;\r\n\r\n /**\r\n * Discards every pending edit in every registered descendant — the\r\n * \"Cancel\" / \"Discard\" primitive that returns the entire subtree to its\r\n * loaded baseline WITHOUT re-fetching from the server.\r\n *\r\n * Synchronous: descendants clear their local state in their own\r\n * `setState` calls. Doesn't itself await anything.\r\n */\r\n readonly resetState: () => void;\r\n\r\n /**\r\n * Runs {@link MainContextDescendant.validate} across every registered\r\n * descendant and returns `false` if any of them invalidates. Awaitable\r\n * so descendants can perform async validation (e.g. async column\r\n * validators routed through `ValidationContext.validateAll`).\r\n */\r\n readonly validate: () => Promise<boolean>;\r\n\r\n /**\r\n * Aggregates every pending Dataverse operation across the descendant\r\n * subtree without firing them. Returns an empty array when nothing is\r\n * dirty. Useful for previewing the payload {@link saveAll} would ship\r\n * (audit log, dry-run UI, custom transactional flow that wants to\r\n * append extra requests before calling\r\n * `IPowerPortalsPro.executeMultipleAsync` directly).\r\n *\r\n * Mirrors Blazor's public `MainContext.GetRequests()` — same walk over\r\n * registered grids / child contexts / M2M editors, same shape.\r\n * Consumers that just want to save should call {@link saveAll}; this\r\n * is the escape hatch for the rarer dry-run / merge-with-extras case.\r\n */\r\n readonly getRequests: () => OrganizationRequest[];\r\n\r\n /**\r\n * Adds `descendant` to the registry and returns an unregister function. Each\r\n * descendant should call this once on mount and call the returned function\r\n * on unmount, typically inside a single `useEffect` whose deps DON'T include\r\n * the descendant's local `isDirty` — instead, signal dirty transitions via\r\n * {@link notifyDescendantDirtyChanged}. The descendant's\r\n * {@link MainContextDescendant.isDirty} getter is what the parent reads.\r\n */\r\n readonly registerDescendant: (descendant: MainContextDescendant) => () => void;\r\n\r\n /**\r\n * Called by a descendant when its\r\n * {@link MainContextDescendant.isDirty} return value transitions.\r\n * Prompts the parent to recompute its aggregate without forcing the\r\n * descendant to unregister + re-register itself. The recommended\r\n * idiom is a `useEffect` keyed on the local `isDirty` value.\r\n */\r\n readonly notifyDescendantDirtyChanged: () => void;\r\n\r\n /**\r\n * Aggregated dirty state — `true` if any registered descendant currently\r\n * reports dirty. Flips reactively as descendants register/unregister/notify;\r\n * consumers subscribed via {@link useMainContext} re-render on transitions.\r\n */\r\n readonly isDirty: boolean;\r\n\r\n /**\r\n * Resolved value of {@link MainContextProps.warnOnUnsavedChanges} for this\r\n * context — `true` means the auto unsaved-changes warning is active; `false`\r\n * means it's suppressed. Children read this to inherit the effective value\r\n * when their own prop is omitted.\r\n */\r\n readonly warnOnUnsavedChanges: boolean;\r\n /**\r\n * Resolved value of {@link MainContextProps.forceSuccessfulValidationBeforeSave}.\r\n * When `true` (the default), save flows in this subtree gate on\r\n * validation passing — `SaveContextButton`, the dialog buttons inside\r\n * `<OpenRecordGridButton>` / `<NewRecordGridButton>`, and\r\n * {@link saveAll} all abort when any registered column validator reports\r\n * an error. When `false`, those gates are bypassed and the save proceeds\r\n * regardless. The server's own column validation (Dataverse required-on-\r\n * update / type checks) still applies.\r\n *\r\n * Children read this to inherit the effective value when their own prop\r\n * is omitted. Per-button overrides (e.g. on `<OpenRecordGridButton>`)\r\n * take precedence over the cascaded value for the scope of that button.\r\n */\r\n readonly forceSuccessfulValidationBeforeSave: boolean;\r\n}\r\n\r\nconst MainContextContext = createContext<MainContextValue | null>(null);\r\nMainContextContext.displayName = 'MainContext';\r\n\r\nexport interface MainContextProps {\r\n children: ReactNode;\r\n /**\r\n * Optional pre-save hook. Throw (or return a rejected promise) to cancel\r\n * the save — subsequent descendants are not saved.\r\n */\r\n onBeforeSave?: () => void | Promise<void>;\r\n /**\r\n * Auto-register a browser `beforeunload` warning when this context (or any\r\n * descendant) is dirty.\r\n *\r\n * Resolution rules:\r\n * - `true` / `false` — explicit override at this level.\r\n * - `undefined` (default) — inherit from the parent\r\n * {@link MainContext} / {@link RecordContext}, falling back to `true` at\r\n * the root. Set `false` on the outer context of e.g. a demo site to\r\n * suppress the prompt across the whole subtree without touching every\r\n * nested context.\r\n *\r\n * Only the **top-most** MainContext / RecordContext on the page actually\r\n * registers the listener — when contexts nest, the inner ones skip\r\n * registration to avoid duplicate `beforeunload` handlers (the browser would\r\n * still only prompt once, but the duplicates are wasteful). Soft navigation\r\n * (e.g. `react-router` `<Link>` clicks) is NOT covered — that's\r\n * router-specific; pair with the router's blocker hook plus\r\n * {@link useWarnAboutUnsavedChanges}.\r\n */\r\n warnOnUnsavedChanges?: boolean;\r\n /**\r\n * When `true` (default), descendants' save flows abort if any registered\r\n * column validator reports an error — covers `<SaveContextButton>`, the\r\n * dialog buttons on `<OpenRecordGridButton>` / `<NewRecordGridButton>`,\r\n * and {@link MainContextValue.saveAll}'s pre-flight. When `false`, those\r\n * gates are bypassed and the save proceeds regardless. Useful for draft-\r\n * style \"save what you have\" flows or when the consumer wants to defer\r\n * validation to a downstream review step.\r\n *\r\n * Resolution rules (same as {@link warnOnUnsavedChanges}):\r\n * - `true` / `false` — explicit override at this level.\r\n * - `undefined` (default) — inherit from the parent\r\n * {@link MainContext} / {@link RecordContext}, falling back to `true`\r\n * at the root.\r\n */\r\n forceSuccessfulValidationBeforeSave?: boolean;\r\n}\r\n\r\n/**\r\n * A context root that coordinates save and refresh across descendant components\r\n * ({@link RecordContext}, future SubGrid / many-to-many editors). Headless:\r\n * provides state and orchestration via React context, no rendering of its own\r\n * beyond `children`.\r\n *\r\n * Nesting is supported — a child MainContext doesn't replace its parent's\r\n * registry; it's just a separate root for its own subtree. Descendants always\r\n * register with their nearest ancestor.\r\n *\r\n * Deferred surface (and how to layer it on later):\r\n * - **Dirty tracking.** Blazor's MainContext aggregates `isDirty` across children\r\n * via `EditContext`. React doesn't have a built-in form layer; consumers bring\r\n * their own (react-hook-form, formik, vanilla state). When the dirty story\r\n * solidifies, an `isDirty: () => boolean` getter on `MainContextDescendant`\r\n * plus an aggregate getter on `MainContextValue` is the natural extension.\r\n * - **Unsaved-changes warning** dialog on navigation. Driven by dirty tracking.\r\n * - **Validation orchestration**. Same — needs the form layer.\r\n * - **Persistent state across server↔WASM**. N/A in pure React.\r\n */\r\nexport function MainContext({\r\n children,\r\n onBeforeSave,\r\n warnOnUnsavedChanges,\r\n forceSuccessfulValidationBeforeSave,\r\n}: MainContextProps) {\r\n // Read parent BEFORE we wrap children in our own provider — `useContext`\r\n // here returns whatever's above us, so `null` means \"we're the top-most\r\n // MainContext on the page\". Used to gate the beforeunload registration so\r\n // nested contexts don't double-register, AND to inherit unset prop values\r\n // from the parent's already-resolved state.\r\n const parentMainContext = useContext(MainContextContext);\r\n const isTopMost = parentMainContext === null;\r\n // Resolution: explicit prop > parent's resolved value > root default (true).\r\n // Mirrors Blazor's `DisableUnsavedChangesWarning ?? ParentContext?.IsUnsavedChangesWarningDisabled ?? false`.\r\n const resolvedWarnOnUnsavedChanges =\r\n warnOnUnsavedChanges ?? parentMainContext?.warnOnUnsavedChanges ?? true;\r\n // Same resolution pattern as `warnOnUnsavedChanges`. Default `true` matches\r\n // Blazor's `MainContext.ForceSuccessfulValidationBeforeSave = true`.\r\n const resolvedForceSuccessfulValidationBeforeSave =\r\n forceSuccessfulValidationBeforeSave ??\r\n parentMainContext?.forceSuccessfulValidationBeforeSave ??\r\n true;\r\n // Set keeps insertion order under a Set in modern JS, which matches the\r\n // first-registered-saves-first ordering Blazor uses. Stored in a ref (not\r\n // state) so re-registrations on every keystroke don't trigger a full\r\n // MainContext re-render — the `version` counter below is what notifies React\r\n // to recompute the aggregate.\r\n const descendantsRef = useRef<Set<MainContextDescendant>>(new Set());\r\n // Bumped on every register/unregister. The aggregate `isDirty` `useMemo`\r\n // depends on this counter, so React knows to re-evaluate when the descendant\r\n // set changes (without paying the cost of putting the Set itself in state).\r\n const [version, setVersion] = useState(0);\r\n\r\n const { ppp } = usePowerPortalsPro();\r\n\r\n const refreshAll = useCallback(async () => {\r\n for (const descendant of descendantsRef.current) {\r\n await descendant.refresh();\r\n }\r\n }, []);\r\n\r\n // Cascade resetState across every descendant. Synchronous — descendants\r\n // are expected to do their own `setState` calls, which React batches into\r\n // a single re-render at the end of the calling event.\r\n const resetState = useCallback(() => {\r\n for (const descendant of descendantsRef.current) {\r\n descendant.resetState();\r\n }\r\n }, []);\r\n\r\n const validate = useCallback(async () => {\r\n for (const descendant of descendantsRef.current) {\r\n const ok = await descendant.validate();\r\n if (!ok) return false;\r\n }\r\n return true;\r\n }, []);\r\n\r\n const saveAll = useCallback(async () => {\r\n if (onBeforeSave) await onBeforeSave();\r\n // Validate the whole subtree before touching the server. Aborting here\r\n // avoids partial writes when one descendant is invalid. Skipped when\r\n // `forceSuccessfulValidationBeforeSave` is `false` — consumer opted into\r\n // \"save what's there, server arbitrates the rest\" semantics.\r\n if (resolvedForceSuccessfulValidationBeforeSave) {\r\n const valid = await validate();\r\n if (!valid) return;\r\n }\r\n // Collect requests across descendants. Each descendant is responsible\r\n // for including its own nested descendants' requests in the returned\r\n // array — this MainContext doesn't recurse into descendant trees.\r\n const requests: OrganizationRequest[] = [];\r\n for (const descendant of descendantsRef.current) {\r\n requests.push(...descendant.getRequests());\r\n }\r\n // No work to do — nothing was dirty. Skip the round-trip and the\r\n // subsequent refresh; refreshing a clean subtree would be a wasted\r\n // re-fetch.\r\n if (requests.length === 0) return;\r\n // One transactional batch. We pass `returnResponses: false` for now\r\n // because the current React port doesn't support CreateRequests\r\n // (no new-record flow yet) — none of the response payloads carry\r\n // information the client needs that wouldn't already come back in\r\n // `refreshAll`. When CreateRequest support lands and the client\r\n // needs newly-assigned record ids, this flips to `true` and an\r\n // id-distribution pass walks the response array.\r\n await ppp.executeMultipleAsync(requests, { returnResponses: false });\r\n // Reload everything so server-side computed columns / plugin defaults\r\n // / formatted values / file ids land back on the local state. Matches\r\n // Blazor's `MainContext.SaveAsync` → `RefreshAsync(true)` step.\r\n await refreshAll();\r\n }, [onBeforeSave, validate, ppp, refreshAll, resolvedForceSuccessfulValidationBeforeSave]);\r\n\r\n const registerDescendant = useCallback((descendant: MainContextDescendant) => {\r\n descendantsRef.current.add(descendant);\r\n setVersion((v) => v + 1);\r\n return () => {\r\n descendantsRef.current.delete(descendant);\r\n setVersion((v) => v + 1);\r\n };\r\n }, []);\r\n\r\n // Bumps `version` so the aggregate `isDirty` memo below re-reads each\r\n // descendant's `isDirty()` getter. Descendants call this when their\r\n // local dirty state flips — no need to re-register the descendant\r\n // object itself, which avoids the cascade of context re-renders that\r\n // used to drop focus from the active inline editor on the first\r\n // keystroke (the registry teardown + re-add fired two version bumps\r\n // and forced the MainContext provider value identity to flip).\r\n const notifyDescendantDirtyChanged = useCallback(() => {\r\n setVersion((v) => v + 1);\r\n }, []);\r\n\r\n // Recomputes when `version` bumps (descendants joined / left / signalled\r\n // a dirty transition). The dep on `version` looks unused but is the\r\n // trigger — refs don't subscribe React to changes on their own.\r\n const isDirty = useMemo(() => {\r\n void version;\r\n for (const descendant of descendantsRef.current) {\r\n if (descendant.isDirty()) return true;\r\n }\r\n return false;\r\n }, [version]);\r\n\r\n // Aggregate getRequests across all descendants — used when this\r\n // MainContext is itself a descendant of an OUTER MainContext (nested\r\n // contexts case). The outer parent calls this descendant's getRequests\r\n // and expects all nested requests in the returned array.\r\n const getRequests = useCallback((): OrganizationRequest[] => {\r\n const all: OrganizationRequest[] = [];\r\n for (const descendant of descendantsRef.current) {\r\n all.push(...descendant.getRequests());\r\n }\r\n return all;\r\n }, []);\r\n\r\n const value = useMemo<MainContextValue>(\r\n () => ({\r\n saveAll,\r\n refreshAll,\r\n resetState,\r\n validate,\r\n getRequests,\r\n registerDescendant,\r\n notifyDescendantDirtyChanged,\r\n isDirty,\r\n warnOnUnsavedChanges: resolvedWarnOnUnsavedChanges,\r\n forceSuccessfulValidationBeforeSave: resolvedForceSuccessfulValidationBeforeSave,\r\n }),\r\n [\r\n saveAll,\r\n refreshAll,\r\n resetState,\r\n validate,\r\n getRequests,\r\n registerDescendant,\r\n notifyDescendantDirtyChanged,\r\n isDirty,\r\n resolvedWarnOnUnsavedChanges,\r\n resolvedForceSuccessfulValidationBeforeSave,\r\n ],\r\n );\r\n\r\n // Auto-register with the OUTER MainContext (if any) — same pattern\r\n // RecordContext uses. Means a nested MainContext bubbles its aggregated\r\n // save / refresh / isDirty up to its parent, so the top-most MainContext's\r\n // isDirty includes the entire subtree (matches Blazor's `MainContext.IsDirty`\r\n // walking `_childRecordContexts` recursively). We forward our own\r\n // aggregated getRequests / validate so the outer's transactional save\r\n // sweeps the full descendant tree.\r\n //\r\n // `isDirty` is passed as a ref-backed getter and dropped from the\r\n // dep array so the registration sticks across dirty-state transitions\r\n // — see the \"first-keystroke focus loss\" comment on\r\n // `notifyDescendantDirtyChanged` above. The `notify` effect below\r\n // bumps the outer when our local aggregate flips.\r\n useEffect(() => {\r\n if (!parentMainContext) return;\r\n return parentMainContext.registerDescendant({\r\n getRequests,\r\n validate,\r\n refresh: refreshAll,\r\n resetState,\r\n isDirty: () => isDirtyRef.current,\r\n });\r\n // Key on `registerDescendant` identity (stable per MainContext\r\n // instance) rather than the parent value object — see the same\r\n // pattern in `record-context.tsx` for the infinite-loop rationale.\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [parentMainContext?.registerDescendant, getRequests, validate, refreshAll, resetState]);\r\n\r\n // Tell the outer to re-read our aggregate when it flips. Read the\r\n // parent reference via a ref (declared below `isDirtyRef`) so the\r\n // effect doesn't re-fire when the outer's value identity changes —\r\n // which it does every time its own isDirty flips, creating an\r\n // infinite loop between the two layers if we depended on\r\n // parentMainContext directly.\r\n const parentMainContextRef = useRef(parentMainContext);\r\n parentMainContextRef.current = parentMainContext;\r\n useEffect(() => {\r\n parentMainContextRef.current?.notifyDescendantDirtyChanged();\r\n }, [isDirty]);\r\n\r\n // Auto unsaved-changes warning — only top-most fires (avoid duplicate\r\n // listeners when contexts nest). Browser-level only (covers tab close /\r\n // refresh / URL bar); soft nav is router-specific.\r\n useBeforeUnloadWhen(isTopMost && resolvedWarnOnUnsavedChanges && isDirty);\r\n\r\n // Register with the page-wide unsaved-changes registry — only the\r\n // topmost MainContext does so, since its `isDirty` aggregate already\r\n // covers every descendant via the existing registerDescendant chain.\r\n // Sibling top-level MainContexts on the same page each register\r\n // independently; the router-blocker guard reads `isAnyDirty()` so\r\n // ANY topmost dirty source blocks navigation with one prompt.\r\n // `warnOnUnsavedChanges=false` opts the whole subtree out of guarding\r\n // even when dirty — matches the beforeunload gate above.\r\n const unsavedChangesRegistry = useUnsavedChangesRegistry();\r\n const isDirtyRef = useRef(isDirty);\r\n isDirtyRef.current = isDirty;\r\n const resolvedWarnRef = useRef(resolvedWarnOnUnsavedChanges);\r\n resolvedWarnRef.current = resolvedWarnOnUnsavedChanges;\r\n useEffect(() => {\r\n if (!isTopMost) return;\r\n if (!unsavedChangesRegistry) return;\r\n // The reporter's `isDirty` reads through to the ref so subscribers\r\n // see the latest aggregate without us re-registering on every\r\n // edit (which would churn the registry's listener set every\r\n // keystroke). The notify-on-change effect below pokes the registry\r\n // when our local `isDirty` actually flips.\r\n return unsavedChangesRegistry.register({\r\n isDirty: () => resolvedWarnRef.current && isDirtyRef.current,\r\n });\r\n }, [isTopMost, unsavedChangesRegistry]);\r\n // Bump the registry's listeners whenever this MainContext's aggregate\r\n // changes so subscribers re-read. Otherwise the registry stays silent\r\n // through user edits since the reporter's identity doesn't change.\r\n useEffect(() => {\r\n if (!isTopMost) return;\r\n if (!unsavedChangesRegistry) return;\r\n unsavedChangesRegistry.notifyDirtyChanged();\r\n }, [isTopMost, unsavedChangesRegistry, isDirty, resolvedWarnOnUnsavedChanges]);\r\n\r\n return <MainContextContext.Provider value={value}>{children}</MainContextContext.Provider>;\r\n}\r\n\r\n/**\r\n * Returns the nearest ancestor {@link MainContext} value, or `null` if there is\r\n * none. Components like {@link RecordContext} use this to optionally register\r\n * themselves for save/refresh coordination — `null` is the standalone case where\r\n * the component manages its own save without a coordinator.\r\n */\r\nexport function useMainContext(): MainContextValue | null {\r\n return useContext(MainContextContext);\r\n}\r\n\r\n/**\r\n * Provides an existing {@link MainContextValue} to a subtree. Used by\r\n * {@link RecordContext} to expose itself as a `MainContext` for nested\r\n * descendants (mirroring Blazor's `RecordContext : MainContext` inheritance) —\r\n * keeps the raw `MainContextContext` internal while still letting framework\r\n * components compose multiple `MainContext` levels.\r\n *\r\n * Not generally needed by application code — use `<MainContext>` instead, which\r\n * builds the value internally.\r\n */\r\nexport function MainContextProvider({\r\n value,\r\n children,\r\n}: {\r\n value: MainContextValue;\r\n children: ReactNode;\r\n}) {\r\n return <MainContextContext.Provider value={value}>{children}</MainContextContext.Provider>;\r\n}\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type ReactNode,\r\n} from 'react';\r\n\r\n/** Options passed to {@link OverlayService.show}. */\r\nexport interface OverlayOptions {\r\n /**\r\n * Target id to scope the overlay. Undefined / empty = global (full-screen);\r\n * any other value routes to a matching scoped `<OverlayTarget>` registered\r\n * with the same id.\r\n */\r\n targetId?: string | undefined;\r\n /** Text to display alongside the spinner (e.g. `\"Loading...\"`, `\"Saving...\"`). */\r\n description?: string | undefined;\r\n /** Whether to show a spinner. Defaults to `true`. */\r\n showProgress?: boolean | undefined;\r\n}\r\n\r\n/**\r\n * A live overlay handle returned by {@link OverlayService.show}. Pass it to\r\n * {@link OverlayService.hide} to dismiss exactly that overlay (matters when\r\n * concurrent show calls are stacked on the same target).\r\n */\r\nexport interface OverlayInstance extends OverlayOptions {\r\n /** Stable id assigned by the service at show time. */\r\n readonly id: string;\r\n}\r\n\r\n/**\r\n * Headless overlay service contract — equivalent to Blazor's\r\n * `IOverlayService`. Maintains a per-target stack of overlay instances; the\r\n * topmost instance for a target is what renders. Concrete implementations\r\n * (e.g. `<FluentOverlayProvider>` from `@powerportalspro/react-fluent`)\r\n * provide the actual UI.\r\n *\r\n * Reads happen via the {@link useOverlayForTarget} hook — that's how\r\n * components subscribe to \"what overlay should I currently render\". The\r\n * service interface itself is write-only (show / hide / hideAll).\r\n */\r\nexport interface OverlayService {\r\n /** Push an overlay onto its target's stack. Returns the handle to dismiss it. */\r\n show(options?: OverlayOptions): OverlayInstance;\r\n /** Remove a specific overlay from its target's stack. No-op if already removed. */\r\n hide(overlay: OverlayInstance): void;\r\n /** Clear every overlay on every target. */\r\n hideAll(): void;\r\n}\r\n\r\ninterface OverlayContextValue {\r\n readonly service: OverlayService;\r\n /**\r\n * Per-target stacks. Keyed by `targetId ?? ''` (empty string = global).\r\n * Last item in each stack is the topmost / currently-rendered overlay.\r\n * Mutated only via the service; consumers read via {@link useOverlayForTarget}.\r\n */\r\n readonly overlaysByTarget: ReadonlyMap<string, readonly OverlayInstance[]>;\r\n}\r\n\r\nconst OverlayContext = createContext<OverlayContextValue | null>(null);\r\nOverlayContext.displayName = 'OverlayService';\r\n\r\n/** Returns the nearest {@link OverlayService}, or `null` when no provider is mounted. */\r\nexport function useOverlayService(): OverlayService | null {\r\n return useContext(OverlayContext)?.service ?? null;\r\n}\r\n\r\n/**\r\n * Returns the topmost overlay currently active for the given target id (or\r\n * `null` if there is none). Subscribers re-render when their target's stack\r\n * changes — this is how overlay-rendering components like\r\n * `<FluentOverlayProvider>` and `<FluentOverlayTarget>` know what to draw.\r\n */\r\nexport function useOverlayForTarget(targetId?: string): OverlayInstance | null {\r\n const ctx = useContext(OverlayContext);\r\n if (!ctx) return null;\r\n const stack = ctx.overlaysByTarget.get(targetId ?? '') ?? [];\r\n return stack[stack.length - 1] ?? null;\r\n}\r\n\r\n/**\r\n * Provides an externally-built {@link OverlayService} + state to the subtree.\r\n * Real implementations like `<FluentOverlayProvider>` use this internally;\r\n * tests can use it to inject a mock service.\r\n */\r\nexport function OverlayServiceProvider({\r\n value,\r\n children,\r\n}: {\r\n value: OverlayContextValue;\r\n children: ReactNode;\r\n}) {\r\n return <OverlayContext.Provider value={value}>{children}</OverlayContext.Provider>;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Implementation helper — creates an OverlayService backed by React state.\r\n// Lives in @powerportalspro/react so the FluentOverlayProvider (and any\r\n// future non-Fluent provider) can share the same logic; only the rendering\r\n// differs between implementations.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Hook that builds a state-backed {@link OverlayService} + the overlays-by-target\r\n * map needed for the {@link OverlayServiceProvider} value. Used by overlay\r\n * provider components — wraps children with `<OverlayServiceProvider value={...}>`\r\n * and renders the actual overlay UI based on the returned `overlaysByTarget`.\r\n */\r\nexport function useOverlayServiceState(): OverlayContextValue {\r\n const [overlaysByTarget, setOverlaysByTarget] = useState<\r\n ReadonlyMap<string, readonly OverlayInstance[]>\r\n >(() => new Map());\r\n // Stable id counter shared across all show() calls for this provider.\r\n const nextIdRef = useRef(1);\r\n\r\n const service = useMemo<OverlayService>(\r\n () => ({\r\n show(options) {\r\n const id = `overlay-${nextIdRef.current++}`;\r\n const instance: OverlayInstance = { id, ...options };\r\n setOverlaysByTarget((prev) => {\r\n const key = options?.targetId ?? '';\r\n const next = new Map(prev);\r\n next.set(key, [...(next.get(key) ?? []), instance]);\r\n return next;\r\n });\r\n return instance;\r\n },\r\n hide(overlay) {\r\n setOverlaysByTarget((prev) => {\r\n const key = overlay.targetId ?? '';\r\n const stack = prev.get(key) ?? [];\r\n const idx = stack.findIndex((o) => o.id === overlay.id);\r\n if (idx < 0) return prev;\r\n const next = new Map(prev);\r\n if (stack.length === 1) {\r\n next.delete(key);\r\n } else {\r\n next.set(key, [...stack.slice(0, idx), ...stack.slice(idx + 1)]);\r\n }\r\n return next;\r\n });\r\n },\r\n hideAll() {\r\n setOverlaysByTarget(new Map());\r\n },\r\n }),\r\n [],\r\n );\r\n\r\n return useMemo(() => ({ service, overlaysByTarget }), [service, overlaysByTarget]);\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Convenience helpers for \"show overlay during async op\". Works with no\r\n// service present (just runs the op without UI feedback) so consumers don't\r\n// have to null-check before every call.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Run an async operation while showing an overlay. Hides on success or\r\n * failure (the overlay is dismissed in `finally`, so failures don't leave a\r\n * stuck spinner). Mirrors the try/finally pattern Blazor's\r\n * `MainContext.SaveAsync` uses around `_overlayService.ShowOverlayAsync` /\r\n * `HideOverlayAsync`.\r\n */\r\nexport async function runWithOverlayAsync<T>(\r\n service: OverlayService | null,\r\n options: OverlayOptions,\r\n operation: () => Promise<T>,\r\n): Promise<T> {\r\n if (!service) return operation();\r\n const handle = service.show(options);\r\n try {\r\n return await operation();\r\n } finally {\r\n service.hide(handle);\r\n }\r\n}\r\n\r\n/**\r\n * Hook variant of {@link runWithOverlayAsync}. Returns a stable function bound\r\n * to the current overlay service. No-op (just runs the op) when no service is\r\n * present.\r\n */\r\nexport function useRunWithOverlay(): <T>(\r\n options: OverlayOptions,\r\n operation: () => Promise<T>,\r\n) => Promise<T> {\r\n const service = useOverlayService();\r\n return useCallback(\r\n <T,>(options: OverlayOptions, operation: () => Promise<T>) =>\r\n runWithOverlayAsync(service, options, operation),\r\n [service],\r\n );\r\n}\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useMemo,\r\n useRef,\r\n useState,\r\n type ReactNode,\r\n} from 'react';\r\n\r\n/**\r\n * A single column's validator. Returns the list of error messages produced by\r\n * the current value (empty list = valid). Sync or async — async validators\r\n * run sequentially during {@link ValidationContextValue.validateAll}.\r\n *\r\n * Implementations must read whatever state they need (current value, required\r\n * flag, metadata) at call time — typically from refs — because the validator\r\n * is invoked from outside the registering component's render cycle.\r\n */\r\nexport type ColumnValidator = () => string[] | Promise<string[]>;\r\n\r\n/**\r\n * Per-form-scope validation registry. One instance per logical form scope\r\n * (auto-mounted by `<RecordContext>`); edit components below it register\r\n * their validators and read back any errors.\r\n *\r\n * Reads happen via {@link useColumnValidationErrors} — that's how edit\r\n * components subscribe to \"do I have errors right now?\" without re-rendering\r\n * on every other column's transition.\r\n */\r\nexport interface ValidationContextValue {\r\n /**\r\n * Register a column's validator. Returns the unregister function. The\r\n * recommended idiom is a `useEffect` whose cleanup unregisters; on column\r\n * rename or unmount the registration is replaced cleanly.\r\n */\r\n readonly register: (columnName: string, validator: ColumnValidator) => () => void;\r\n /** Run a single column's validator and update its error state. */\r\n readonly validateColumn: (columnName: string) => Promise<void>;\r\n /**\r\n * Run every registered validator and update all error states. Returns\r\n * `true` when nothing failed. Used by save flows (e.g. `SaveContextButton`)\r\n * to gate a save on overall validity.\r\n */\r\n readonly validateAll: () => Promise<boolean>;\r\n /** Drop all errors. Useful when the underlying record reloads. */\r\n readonly clearErrors: () => void;\r\n /**\r\n * Aggregated validity — `false` if any registered column currently has\r\n * errors. Reactive; consumers subscribed via `useValidationContext` re-render\r\n * on transitions.\r\n */\r\n readonly isValid: boolean;\r\n /**\r\n * Current errors keyed by column logical name. Re-rendered subscribers see\r\n * the latest snapshot. Use {@link useColumnValidationErrors} for a\r\n * column-scoped read that doesn't churn on unrelated transitions.\r\n */\r\n readonly errors: ReadonlyMap<string, readonly string[]>;\r\n}\r\n\r\nconst ValidationContextContext = createContext<ValidationContextValue | null>(null);\r\nValidationContextContext.displayName = 'ValidationContext';\r\n\r\n/**\r\n * Returns the nearest {@link ValidationContextValue}, or `null` when no\r\n * provider is mounted. Validation is optional — a form rendered outside a\r\n * `<RecordContext>` (or anywhere without an explicit `<ValidationProvider>`)\r\n * just doesn't run validators.\r\n */\r\nexport function useValidationContext(): ValidationContextValue | null {\r\n return useContext(ValidationContextContext);\r\n}\r\n\r\n/**\r\n * Returns the current error messages for a single column (empty array if\r\n * valid; `undefined` if no validation provider is mounted). Edit components\r\n * call this to drive their inline error display.\r\n */\r\nexport function useColumnValidationErrors(\r\n columnName: string,\r\n): readonly string[] | undefined {\r\n const ctx = useValidationContext();\r\n if (!ctx) return undefined;\r\n return ctx.errors.get(columnName);\r\n}\r\n\r\n/**\r\n * Mounts a fresh validation scope for descendants. `<RecordContext>`\r\n * auto-mounts one — manual use is for edge cases (e.g. a non-record form).\r\n */\r\nexport function ValidationProvider({ children }: { children: ReactNode }) {\r\n const validatorsRef = useRef<Map<string, ColumnValidator>>(new Map());\r\n const [errors, setErrors] = useState<ReadonlyMap<string, readonly string[]>>(\r\n () => new Map(),\r\n );\r\n\r\n const register = useCallback(\r\n (columnName: string, validator: ColumnValidator) => {\r\n validatorsRef.current.set(columnName, validator);\r\n return () => {\r\n validatorsRef.current.delete(columnName);\r\n setErrors((prev) => {\r\n if (!prev.has(columnName)) return prev;\r\n const next = new Map(prev);\r\n next.delete(columnName);\r\n return next;\r\n });\r\n };\r\n },\r\n [],\r\n );\r\n\r\n const validateColumn = useCallback(async (columnName: string) => {\r\n const validator = validatorsRef.current.get(columnName);\r\n if (!validator) return;\r\n const fieldErrors = await validator();\r\n setErrors((prev) => {\r\n // Avoid creating a new Map if nothing changed — keeps subscribers from\r\n // re-rendering on every keystroke when the validator output is stable.\r\n const existing = prev.get(columnName) ?? [];\r\n if (\r\n existing.length === fieldErrors.length &&\r\n existing.every((e, i) => e === fieldErrors[i])\r\n ) {\r\n return prev;\r\n }\r\n const next = new Map(prev);\r\n if (fieldErrors.length === 0) {\r\n next.delete(columnName);\r\n } else {\r\n next.set(columnName, fieldErrors);\r\n }\r\n return next;\r\n });\r\n }, []);\r\n\r\n const validateAll = useCallback(async () => {\r\n const next = new Map<string, readonly string[]>();\r\n for (const [col, validator] of validatorsRef.current) {\r\n const fieldErrors = await validator();\r\n if (fieldErrors.length > 0) {\r\n next.set(col, fieldErrors);\r\n }\r\n }\r\n setErrors(next);\r\n return next.size === 0;\r\n }, []);\r\n\r\n const clearErrors = useCallback(() => {\r\n setErrors(new Map());\r\n }, []);\r\n\r\n const isValid = errors.size === 0;\r\n\r\n const value = useMemo<ValidationContextValue>(\r\n () => ({ register, validateColumn, validateAll, clearErrors, isValid, errors }),\r\n [register, validateColumn, validateAll, clearErrors, isValid, errors],\r\n );\r\n\r\n return (\r\n <ValidationContextContext.Provider value={value}>\r\n {children}\r\n </ValidationContextContext.Provider>\r\n );\r\n}\r\n","import type {\r\n components,\r\n CreateRequest,\r\n OrganizationRequest,\r\n PowerPortalsProError,\r\n TableRecord,\r\n UpdateRequest,\r\n} from '@powerportalspro/core';\r\nimport {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useId,\r\n useMemo,\r\n useRef,\r\n useState,\r\n} from 'react';\r\nimport type { QueryStatus } from './internal/use-async-resource.js';\r\nimport {\r\n MainContextProvider,\r\n useMainContext,\r\n type MainContextDescendant,\r\n type MainContextProps,\r\n type MainContextValue,\r\n} from './main-context.js';\r\nimport { useBeforeUnloadWhen } from './internal/use-before-unload-when.js';\r\nimport { useUnsavedChangesRegistry } from './unsaved-changes-registry.js';\r\nimport { useLocalizer } from './localizer.js';\r\nimport { useOverlayService, type OverlayInstance } from './overlay-service.js';\r\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\r\nimport { useRecord } from './use-record.js';\r\nimport { useQueryParam } from './use-query-param.js';\r\nimport { useTableMetadata } from './use-table-metadata.js';\r\nimport { ValidationProvider, useValidationContext } from './validation-context.js';\r\n\r\ntype ColumnValueBase = components['schemas']['ColumnValueBase'];\r\ntype TableMetadata = components['schemas']['TableMetadata'];\r\n\r\n/**\r\n * Structural equality for two column values, treating `null` and `undefined`\r\n * as equivalent (\"no value\"). Used by the change tracker to detect reverts —\r\n * if a user edits a column and then sets it back to the loaded value, the\r\n * column drops out of the dirty set rather than staying flagged.\r\n *\r\n * Mirrors Blazor's `_equalityComparer.Equals(_originalPropertyValues[name], value)`\r\n * branch in `TableRecord.cs`. Uses `JSON.stringify` for the structural compare;\r\n * fine for the wire-format shapes we deal with (consistent key order from both\r\n * the server and edit components), and avoids pulling in a deep-equal dep.\r\n */\r\nfunction valuesEqual(\r\n a: ColumnValueBase | null | undefined,\r\n b: ColumnValueBase | null | undefined,\r\n): boolean {\r\n const aEmpty = a === null || a === undefined;\r\n const bEmpty = b === null || b === undefined;\r\n if (aEmpty && bEmpty) return true;\r\n if (aEmpty !== bEmpty) return false;\r\n return JSON.stringify(a) === JSON.stringify(b);\r\n}\r\n\r\nconst GUID_PATTERN =\r\n /^\\{?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\}?$/i;\r\n\r\n/**\r\n * Parses a single URL query-string value into the `ColumnValueBase` shape\r\n * the wire expects, dispatched by the column's metadata `$type`. Returns\r\n * `null` when the value can't be parsed for the column kind, when the\r\n * column is a File / Image column (no sensible URL encoding), or when the\r\n * column type is unknown. Mirrors Blazor's `RecordContext.ParseQueryDefault`\r\n * arm-by-arm — same accepted formats so a URL constructed by either side\r\n * loads the same.\r\n *\r\n * Lookup values follow Blazor's JSON shape: `?customerid={\"tableName\":\"account\",\"value\":\"<guid>\",\"name\":\"Foo\"}`\r\n * with case-insensitive property names.\r\n */\r\nfunction parseQueryDefault(\r\n metadata: { $type?: number },\r\n rawValue: string,\r\n): ColumnValueBase | null {\r\n switch (metadata.$type) {\r\n // StringMetadata + MemoMetadata both ride on StringValue. In Blazor\r\n // MemoMetadata derives from StringMetadata so a single `StringMetadata`\r\n // pattern catches both; in TS the discriminators are distinct so we\r\n // list both arms.\r\n case 14:\r\n case 7:\r\n return { $type: 14, value: rawValue };\r\n case 5: {\r\n const n = Number(rawValue);\r\n return Number.isFinite(n) && Number.isInteger(n) ? { $type: 5, value: n } : null;\r\n }\r\n case 18: {\r\n const n = Number(rawValue);\r\n return Number.isFinite(n) && Number.isInteger(n) ? { $type: 18, value: n } : null;\r\n }\r\n case 3: {\r\n const n = Number(rawValue);\r\n return Number.isFinite(n) ? { $type: 3, value: n } : null;\r\n }\r\n case 4: {\r\n const n = Number(rawValue);\r\n return Number.isFinite(n) ? { $type: 4, value: n } : null;\r\n }\r\n case 8: {\r\n const n = Number(rawValue);\r\n return Number.isFinite(n) ? { $type: 8, value: n } : null;\r\n }\r\n case 0: {\r\n const lower = rawValue.trim().toLowerCase();\r\n if (lower === 'true') return { $type: 0, value: true };\r\n if (lower === 'false') return { $type: 0, value: false };\r\n return null;\r\n }\r\n case 2: {\r\n const ts = Date.parse(rawValue);\r\n if (Number.isNaN(ts)) return null;\r\n return { $type: 2, value: new Date(ts).toISOString() };\r\n }\r\n case 11: {\r\n const n = Number(rawValue);\r\n return Number.isFinite(n) && Number.isInteger(n) ? { $type: 11, value: n } : null;\r\n }\r\n case 40: {\r\n const parts = rawValue\r\n .split(',')\r\n .map((s) => s.trim())\r\n .filter(Boolean);\r\n const values: { value: number }[] = [];\r\n for (const p of parts) {\r\n const n = Number(p);\r\n if (Number.isFinite(n) && Number.isInteger(n)) values.push({ value: n });\r\n }\r\n return values.length > 0 ? { $type: 40, value: values } : null;\r\n }\r\n case 6: {\r\n try {\r\n const parsed: unknown = JSON.parse(rawValue);\r\n if (!parsed || typeof parsed !== 'object') return null;\r\n const findKey = (target: string): string | undefined =>\r\n Object.keys(parsed as object).find(\r\n (k) => k.toLowerCase() === target.toLowerCase(),\r\n );\r\n const obj = parsed as Record<string, unknown>;\r\n const tableNameKey = findKey('tableName');\r\n const valueKey = findKey('value');\r\n const nameKey = findKey('name');\r\n const tableName = tableNameKey ? obj[tableNameKey] : undefined;\r\n const value = valueKey ? obj[valueKey] : undefined;\r\n const name = nameKey ? obj[nameKey] : null;\r\n if (typeof tableName !== 'string' || tableName.length === 0) return null;\r\n if (typeof value !== 'string' || value.length === 0) return null;\r\n return {\r\n $type: 6,\r\n tableName,\r\n value,\r\n name: typeof name === 'string' ? name : null,\r\n };\r\n } catch {\r\n return null;\r\n }\r\n }\r\n case 15: {\r\n if (!GUID_PATTERN.test(rawValue)) return null;\r\n return { $type: 15, value: rawValue.replace(/[{}]/g, '') };\r\n }\r\n default:\r\n // FileMetadata (41), ImageMetadata (42), or unknown — no URL form.\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Walks the URL query string, looks up each parameter in the table's column\r\n * metadata, parses, and returns a `Record<string, ColumnValueBase>` of the\r\n * successfully-parsed values. Best-effort: malformed values for any single\r\n * column silently skip — a bad URL doesn't break new-record load. Mirrors\r\n * Blazor's create-mode `_navigationManager.QueryString()` loop in\r\n * `RecordContext.razor.cs:386-413`.\r\n */\r\nfunction parseQueryDefaultsFromUrl(\r\n search: string,\r\n tableMetadata: TableMetadata,\r\n): Record<string, ColumnValueBase> {\r\n const out: Record<string, ColumnValueBase> = {};\r\n if (!search) return out;\r\n const params = new URLSearchParams(search);\r\n for (const [columnName, rawValue] of params) {\r\n if (!columnName) continue;\r\n const meta = tableMetadata.columns.find(\r\n (c) => (c as { columnName?: string }).columnName === columnName,\r\n );\r\n if (!meta) continue;\r\n try {\r\n const parsed = parseQueryDefault(meta, rawValue);\r\n if (parsed) out[columnName] = parsed;\r\n } catch {\r\n // Per-column try/catch — see helper doc.\r\n }\r\n }\r\n return out;\r\n}\r\n\r\nexport interface RecordContextValue {\r\n /** Discriminator for the load lifecycle — same {@link QueryStatus} the query hooks use. */\r\n readonly status: QueryStatus;\r\n /** Most recent record from the server (or undefined while loading / on error). */\r\n readonly record: TableRecord | undefined;\r\n /** Last error thrown by the load. Undefined on success. */\r\n readonly error: PowerPortalsProError | undefined;\r\n /**\r\n * Re-fetches this record from the server (replacing any local mutations) and\r\n * cascades to every nested descendant's `refresh()`.\r\n */\r\n readonly reload: () => Promise<void>;\r\n /**\r\n * Persists this record AND any nested descendants (grids, M2M editors, child\r\n * `<RecordContext>`s) — `RecordContext` IS-A `MainContext`, so `save()` here\r\n * is the same as `MainContext.saveAll()` would be at this level.\r\n *\r\n * Order: this record's PATCH first, then each registered descendant's `save()`\r\n * in registration order. Each descendant decides its own dirty-skip; saving a\r\n * clean RecordContext is a no-op for the record itself but still cascades to\r\n * nested descendants in case they have their own pending changes.\r\n *\r\n * Calls {@link RecordContextProps.onBeforeSave} (when provided) before issuing\r\n * the PATCH; throwing from that callback cancels the entire save (no descendants\r\n * run either). When inside an outer `<MainContext>`, this is also what gets\r\n * wired into the outer `MainContext.saveAll()` for this subtree.\r\n */\r\n readonly save: () => Promise<void>;\r\n /**\r\n * Aggregates every pending Dataverse operation across this record AND\r\n * every nested descendant — the own `UpdateRequest` / `CreateRequest`\r\n * (when dirty) followed by each nested descendant's `getRequests()`, in\r\n * registration order. Returns an empty array when nothing is dirty.\r\n *\r\n * Mirrors Blazor's public `RecordContext.GetRequests()` — same shape, same\r\n * traversal. Use for previewing the payload {@link save} would ship,\r\n * for dry-run UIs, or to merge the records into a custom\r\n * `executeMultiple` batch alongside consumer-supplied requests.\r\n * Consumers that just want to persist should call {@link save}.\r\n */\r\n readonly getRequests: () => OrganizationRequest[];\r\n /**\r\n * Discards every pending edit on this record AND in every nested\r\n * descendant, reverting back to the loaded baseline WITHOUT re-fetching\r\n * from the server. The \"Cancel\" / \"Discard\" primitive — same surface\r\n * as `MainContextValue.resetState`, also drops the record's own\r\n * baseline so it re-loads clean.\r\n *\r\n * Validation errors tracked against the dropped pending values are\r\n * also cleared. Synchronous: descendants do their own `setState` calls.\r\n */\r\n readonly resetState: () => void;\r\n /**\r\n * Immutably updates one column on the in-memory record. Triggers a re-render of all\r\n * descendants reading the record (typed-column hooks bind to this from\r\n * `@powerportalspro/react-fluent`'s edit components and similar). Pass `null` or\r\n * `undefined` to clear the column. Marks the column dirty in {@link dirtyColumns}\r\n * and sets `record.isDirty = true`.\r\n *\r\n * No-op if no record has loaded yet — children should gate input on `status === 'success'`.\r\n */\r\n readonly setColumnValue: (\r\n columnName: string,\r\n value: ColumnValueBase | null | undefined,\r\n ) => void;\r\n /**\r\n * Patches the loaded baseline with a fresh value for one column WITHOUT\r\n * marking the column dirty. Use for late-arriving server-side data — file\r\n * binaries lazy-loaded by `<ImageEdit>`/`<FileEdit>`, async-resolved\r\n * formatted values, secondary fetches that fill in fields the initial\r\n * record query didn't populate. NOT for user edits — those go through\r\n * {@link setColumnValue}.\r\n *\r\n * If the column already has a pending edit, the hydration is skipped to\r\n * avoid silently clobbering the user's in-progress change. This\r\n * distinguishes \"load filled this in\" from \"user typed this\" — the\r\n * former sets the baseline, the latter sets a pending value compared\r\n * against the baseline for dirty calculation.\r\n *\r\n * No-op if no record has loaded yet.\r\n */\r\n readonly hydrateColumnValue: (\r\n columnName: string,\r\n value: ColumnValueBase | null,\r\n ) => void;\r\n /**\r\n * Table metadata for the parent record's table — column types, required levels,\r\n * isValidForCreate/Update flags, etc. Loaded in parallel with the record itself.\r\n *\r\n * Independent of `status`: this is `undefined` while metadata is loading, on\r\n * metadata fetch failure, or for tables that have no metadata. The record's own\r\n * `status` reflects only the record query — metadata is best-effort, edit\r\n * components fall back to consumer-passed props when it's absent. This keeps\r\n * forms rendering as soon as the record is ready instead of blocking on a\r\n * second round-trip.\r\n *\r\n * Use {@link useColumnMetadata} from `@powerportalspro/react` to look up a\r\n * single column's entry.\r\n */\r\n readonly tableMetadata: TableMetadata | undefined;\r\n /**\r\n * `true` when at least one column on this record has been modified, OR when\r\n * any nested descendant (child `<RecordContext>`, grid, M2M editor) reports\r\n * dirty.\r\n *\r\n * Use this for Save-button enablement, navigation-warning gates, or any UI\r\n * that should react to the existence of unsaved changes anywhere in the\r\n * record's subtree. For a more focused check (just this record's columns,\r\n * ignoring nested descendants), inspect {@link dirtyColumns} directly.\r\n */\r\n readonly isDirty: boolean;\r\n /**\r\n * The set of column logical names that have been modified since the record\r\n * loaded or was last saved. Cleared on successful {@link save} and when a\r\n * fresh record lands from {@link reload}.\r\n *\r\n * Useful for per-field dirty indicators (a small marker next to a label),\r\n * for save filtering (only ship changed columns), or for diff-style review\r\n * UIs that show what's about to be persisted.\r\n */\r\n readonly dirtyColumns: ReadonlySet<string>;\r\n /**\r\n * Stable id this RecordContext uses when calling {@link OverlayService.show}\r\n * during load. Pass to a scoped overlay container (e.g.\r\n * `<FluentOverlayTarget targetId={ctx.overlayTargetId}>`) inside the\r\n * RecordContext subtree to render the loading spinner localized to that\r\n * region rather than full-screen.\r\n *\r\n * Generated once per RecordContext instance via React's `useId`. If no\r\n * `<OverlayService>` is mounted, the show calls no-op silently.\r\n */\r\n readonly overlayTargetId: string;\r\n /**\r\n * Runs the context-level {@link RecordContextProps.onBeforeDelete}\r\n * callback (when configured), surfacing any thrown error to the\r\n * caller so the delete can be cancelled. No-op when no callback is\r\n * wired. Delete-style buttons should `await` this BEFORE issuing\r\n * their own pre-delete prompts or the actual `deleteRecordAsync`\r\n * call. Parallels how `MainContext.saveAll` invokes\r\n * {@link MainContextProps.onBeforeSave}.\r\n */\r\n readonly runBeforeDelete: () => Promise<void>;\r\n}\r\n\r\nconst RecordContextContext = createContext<RecordContextValue | null>(null);\r\nRecordContextContext.displayName = 'RecordContext';\r\n\r\n/**\r\n * `RecordContext` extends {@link MainContextProps} because every `RecordContext`\r\n * conceptually IS-A `MainContext` — Blazor's `RecordContext.razor.cs` literally\r\n * inherits from `MainContext.razor.cs` and `override`s `IsDirty` to add the\r\n * record's own dirty state on top of the base aggregation. Inheriting the props\r\n * means `onBeforeSave` (and any future cross-cutting `MainContextProps`) is\r\n * available on a standalone `<RecordContext>` without a wrapping `<MainContext>`.\r\n */\r\nexport interface RecordContextProps extends MainContextProps {\r\n /** Logical name of the Dataverse table (e.g. `\"account\"`, `\"contact\"`). */\r\n table: string;\r\n /**\r\n * Primary id of the record to edit. Omit (or pass empty / `Guid.Empty`)\r\n * to enter **create mode**: no fetch fires, the in-memory record starts\r\n * either empty or from {@link initialRecord}, and {@link save} emits a\r\n * `CreateRequest` instead of an `UpdateRequest` on commit.\r\n */\r\n id?: string;\r\n /**\r\n * Optional pre-populated record to start from in **create mode** (when\r\n * {@link id} is omitted). Use to seed default values — typical case is\r\n * a SubGrid-hosted new-record dialog pre-filling the parent foreign-key\r\n * column (`parentcustomerid`, `contact_customer_accounts` etc.). Ignored\r\n * in edit mode — when `id` is set the server-loaded record is the\r\n * baseline. The `tableName` on `initialRecord` should match {@link table};\r\n * `id` will be ignored either way (a create starts without one).\r\n */\r\n initialRecord?: TableRecord;\r\n /**\r\n * Pre-loaded record to use as the baseline instead of fetching from the\r\n * server. When provided in edit mode (with {@link id} set), the network\r\n * fetch is skipped entirely and the supplied record becomes the\r\n * `loadedRecord` baseline — `status` immediately reads `'success'`.\r\n *\r\n * The intended consumer is the inline-editable grid: each row already\r\n * has its record in hand from the grid's `useGridData` fetch, so per-\r\n * row RecordContexts would otherwise issue N redundant `retrieveRecord`\r\n * round-trips just to re-fetch what's already on screen. Passing\r\n * `loadedRecord={row}` short-circuits that.\r\n *\r\n * Identity change re-baselines: a fresh reference is treated like a\r\n * fresh load, which seeds `loadedRecord` and resets `pendingChanges`\r\n * (mirroring the success-effect's behavior on a real refetch). Pass a\r\n * stable / memoized reference if you want pending edits to persist\r\n * across re-renders.\r\n *\r\n * Ignored in create mode ({@link id} omitted) — {@link initialRecord}\r\n * is the create-mode baseline.\r\n */\r\n loadedRecord?: TableRecord;\r\n /**\r\n * Pending column changes to apply on top of the server-loaded record.\r\n * Used by the dialog-based Edit flow when reopening a row that already\r\n * has queued updates from a prior session — the queued values become\r\n * the dialog's initial pendingChanges, so reverting to the server value\r\n * cleanly drops the column from `pendingChanges` (matches the loaded\r\n * baseline → removed) and the eventual save can detect \"no net change\"\r\n * by inspecting `dirtyColumns`.\r\n *\r\n * Applied once when the load lands; further user edits drive the\r\n * pending map normally via `setColumnValue`. Identity change after the\r\n * initial apply re-applies (rare — typically the prop is stable for\r\n * the lifetime of a dialog instance).\r\n */\r\n initialPendingChanges?: ReadonlyMap<string, ColumnValueBase | null>;\r\n /** Optional column allow-list. Omit for the table's full visible column set. */\r\n columns?: readonly string[];\r\n /** Fired once after a successful initial load (and again after any subsequent reload). */\r\n onRecordLoaded?: (record: TableRecord) => void;\r\n /**\r\n * Name of the URL query-string parameter to read the record's id\r\n * from. When set, the parameter's current value overrides\r\n * {@link id} — useful for routes shaped like\r\n * `/accounts/edit?accountId=...` where the consumer would\r\n * otherwise wire `useSearchParams` + pass to `id` by hand.\r\n *\r\n * Router-agnostic — uses `useQueryParam` under the hood which\r\n * reads from `window.location.search` and subscribes to back /\r\n * forward + programmatic `pushState` / `replaceState` so any\r\n * router's navigation triggers an id re-read.\r\n *\r\n * When the parameter is absent (or empty / Guid.Empty), the\r\n * RecordContext enters create mode — same semantic as omitting\r\n * the `id` prop entirely.\r\n */\r\n queryParameterName?: string;\r\n /**\r\n * Cancellable hook fired before this record is deleted. Throw from\r\n * the callback to abort — the delete request never goes out and\r\n * subsequent `onDeleted` hooks don't run either.\r\n *\r\n * Surfaces as `useRecordContext().onBeforeDelete` for delete-style\r\n * buttons (the framework `<DeleteContextButton>`, or consumer-built\r\n * delete actions) to consult before issuing `deleteRecordAsync`.\r\n * Per-button `onBeforeDelete` props chain after this — context first\r\n * (record-scoped policy), button second (action-specific).\r\n *\r\n * Use this for context-level deletion gates: \"deleted records can't\r\n * have active child records\" / \"only owners can delete\" / etc.\r\n * Throwing with a meaningful error message is fine — the framework\r\n * delete button routes thrown errors through its `onError` path or\r\n * the dialog service's error display.\r\n */\r\n onBeforeDelete?: () => void | Promise<void>;\r\n}\r\n\r\n/**\r\n * Loads a single Dataverse record and provides it to descendants via React\r\n * context (the analog of Blazor's cascading value). Children call\r\n * {@link useRecordContext} to read the record + status.\r\n *\r\n * Also acts as a `MainContext` for its own subtree — nested grids, M2M editors,\r\n * and child `<RecordContext>`s register with this `RecordContext` instead of\r\n * skipping past it to a further-up `<MainContext>`. The aggregated\r\n * `isDirty`/`saveAll` exposed to a wrapping outer `<MainContext>` includes both\r\n * this record's own pending edits AND every nested descendant's state, so a\r\n * single outer `<SaveButton>` covers everything beneath it.\r\n *\r\n * Auto-registers with a parent {@link MainContext} (when present) so\r\n * {@link MainContext.saveAll} / {@link MainContext.refreshAll} include this record.\r\n * Without a parent, `save()` and `reload()` are still callable directly from\r\n * descendants via the context.\r\n *\r\n * What's deferred from Blazor's RecordContext (the full version is ~700 lines —\r\n * this is the headless coordination skeleton):\r\n * - **New-record creation** (id absent → empty TableRecord). Will need a\r\n * `mode: \"edit\" | \"create\"` discriminator and dispatching `createRecordAsync`\r\n * instead of `updateRecordAsync` from `save()`.\r\n * - **Dirty tracking + EditContext-style validation**. Tied to the form layer.\r\n * - **`OnBeforeSave` / `OnBeforeDelete`** cancellable callbacks at this level\r\n * (MainContext already has `onBeforeSave`).\r\n * - **Delete handler** with confirmation flow.\r\n * - **URL query-parameter reading** (Blazor's `QueryParameterName`). Consumers can\r\n * pull the id from `useSearchParams()` themselves and pass it as the `id` prop.\r\n * - **PersistentComponentState** for server→WASM bridging. N/A in pure React.\r\n */\r\n/**\r\n * Wrapper that auto-mounts a {@link ValidationProvider} around the inner\r\n * record-context logic. The split lets the inner component call\r\n * `useValidationContext()` (e.g. to clear errors on reload) — that hook\r\n * resolves the provider this wrapper just rendered.\r\n *\r\n * The two-component shape is invisible to consumers — `<RecordContext>` is\r\n * the only public surface.\r\n */\r\nexport function RecordContext(props: RecordContextProps) {\r\n return (\r\n <ValidationProvider>\r\n <RecordContextInner {...props} />\r\n </ValidationProvider>\r\n );\r\n}\r\n\r\nfunction RecordContextInner({\r\n children,\r\n table,\r\n id: idProp,\r\n queryParameterName,\r\n initialRecord,\r\n loadedRecord: loadedRecordProp,\r\n initialPendingChanges,\r\n columns,\r\n onRecordLoaded,\r\n onBeforeSave,\r\n onBeforeDelete,\r\n warnOnUnsavedChanges,\r\n forceSuccessfulValidationBeforeSave,\r\n}: RecordContextProps) {\r\n // Resolve the consumer-supplied id: when `queryParameterName` is set,\r\n // the URL's named parameter wins (matches Blazor's\r\n // `QueryParameterName` precedence). Empty / absent parameters fall\r\n // through to `idProp`, which itself may be undefined → create mode.\r\n // The hook is unconditional even when `queryParameterName` is\r\n // empty — React-rule-of-hooks demands a fixed call order — but\r\n // passing `''` reads nothing.\r\n const queryId = useQueryParam(queryParameterName ?? '');\r\n const resolvedId =\r\n queryParameterName && queryId !== null && queryId.length > 0\r\n ? queryId\r\n : idProp;\r\n const propIsEmptyId = !resolvedId || resolvedId === '00000000-0000-0000-0000-000000000000';\r\n\r\n // Effective id — starts from the resolved id (a non-Empty GUID puts\r\n // us in edit mode), and gets overwritten with the server-assigned id\r\n // after a successful create. Mirrors Blazor's auto-flip behavior on\r\n // `RecordContext` where the post-save state treats subsequent edits\r\n // as updates rather than creates. State (not derived) so the prop /\r\n // URL can flip create → edit without remounting; an explicit effect\r\n // below resyncs from the resolved id when it changes.\r\n const [effectiveId, setEffectiveId] = useState<string | undefined>(\r\n propIsEmptyId ? undefined : resolvedId,\r\n );\r\n // Re-sync when the resolved id changes (e.g. consumer navigates from\r\n // one record to another, or the URL's query parameter changes).\r\n // When the resolved id matches the auto-flipped value we just set,\r\n // this is a no-op — state equality short-circuits the re-render.\r\n useEffect(() => {\r\n setEffectiveId(propIsEmptyId ? undefined : resolvedId);\r\n }, [resolvedId, propIsEmptyId]);\r\n\r\n // Mode discrimination — derived from the effective id so post-save\r\n // the context behaves as edit mode without waiting for the consumer\r\n // / URL to update. The create-mode flag gates the entire fetch\r\n // path below and the save path's CreateRequest-vs-UpdateRequest\r\n // selection.\r\n const isCreateMode = effectiveId === undefined;\r\n // Skip-fetch mode: consumer supplied a pre-loaded record (typically the\r\n // inline-editable grid passing its already-fetched row), so the\r\n // server round-trip is unnecessary. Only meaningful in edit mode —\r\n // create mode uses `initialRecord` as its baseline regardless.\r\n const isSkipFetchMode = !isCreateMode && loadedRecordProp !== undefined;\r\n const { ppp } = usePowerPortalsPro();\r\n const mainContext = useMainContext();\r\n // The auto-mounted ValidationProvider is the wrapping component, so this\r\n // resolves to its state. Used to clear errors when fresh data lands and to\r\n // expose `validate()` on the RecordContextValue so save flows can gate.\r\n const validation = useValidationContext();\r\n // RecordContext IS-A MainContext. When it's the only one on the page (no\r\n // outer MainContext), it owns the top-most beforeunload warning.\r\n const isTopMost = mainContext === null;\r\n // Same resolution rule as MainContext: explicit prop > parent's resolved\r\n // value > root default (true). Lets a consumer disable the warning at any\r\n // level and have it propagate down without re-stating the prop.\r\n const resolvedWarnOnUnsavedChanges =\r\n warnOnUnsavedChanges ?? mainContext?.warnOnUnsavedChanges ?? true;\r\n // Same resolution pattern. Default `true` matches Blazor's\r\n // `MainContext.ForceSuccessfulValidationBeforeSave = true`.\r\n const resolvedForceSuccessfulValidationBeforeSave =\r\n forceSuccessfulValidationBeforeSave ??\r\n mainContext?.forceSuccessfulValidationBeforeSave ??\r\n true;\r\n const overlayService = useOverlayService();\r\n const localizer = useLocalizer();\r\n // Stable per-instance overlay target — use it from a scoped overlay\r\n // container if you want the loading spinner localized to this RecordContext.\r\n const overlayTargetId = useId();\r\n // Skip the server fetch entirely in create mode (no record yet) and in\r\n // skip-fetch mode (consumer supplied the record). `useRecord` still\r\n // mounts in both — hook order has to stay stable across renders — but\r\n // is disabled, which leaves it in `status='success'` with `data=undefined`\r\n // (see `useAsyncResource`). Pass a placeholder id to keep the hook's\r\n // type happy; it's never read when enabled=false.\r\n const query = useRecord(table, effectiveId ?? '', {\r\n ...(columns ? { columns } : {}),\r\n enabled: !isCreateMode && !isSkipFetchMode,\r\n });\r\n // Fire metadata fetch in parallel with the record fetch. Independent of the\r\n // record's status so descendants get richer behavior progressively as metadata\r\n // arrives, but the form doesn't block on it.\r\n const metadataQuery = useTableMetadata(table);\r\n\r\n // The clean baseline as last loaded from the server (or as last successfully\r\n // saved — see `save()`). The dirty tracker compares pending edits against\r\n // this snapshot, so a user edit-then-revert reads as clean instead of dirty.\r\n //\r\n // Lazy-init from `loadedRecord` prop (skip-fetch mode) so descendants see\r\n // the baseline on the very first render rather than after a follow-up\r\n // effect. Without this, edit components would mount against an\r\n // `undefined` record for one paint, fire their per-column validators\r\n // against missing values, and flash transient \"required\" errors.\r\n const [loadedRecord, setLoadedRecord] = useState<TableRecord | undefined>(\r\n () => (isSkipFetchMode ? loadedRecordProp : undefined),\r\n );\r\n // Per-column pending edits keyed by column name. `null` means \"explicitly\r\n // cleared\". Mirrors Blazor's `_originalPropertyValues` (size === 0 ⇒ clean).\r\n // `dirtyColumns`/`isDirty`/the exposed `record` are all derived from this +\r\n // `loadedRecord`, so there's a single source of truth for unsaved state.\r\n const [pendingChanges, setPendingChanges] = useState<ReadonlyMap<string, ColumnValueBase | null>>(\r\n () => new Map(),\r\n );\r\n\r\n // Capture the latest onRecordLoaded / onBeforeSave in refs so the save() and\r\n // load-effect closures don't have to depend on consumer callback identity.\r\n const onRecordLoadedRef = useRef(onRecordLoaded);\r\n onRecordLoadedRef.current = onRecordLoaded;\r\n const onBeforeSaveRef = useRef(onBeforeSave);\r\n onBeforeSaveRef.current = onBeforeSave;\r\n // Stash mutable state in refs so saveAll/reload close over them without\r\n // re-creating each render. Without this, `useRecord`'s query object (new\r\n // identity every render) plus React's strict no-stale-closure rules would\r\n // give us a fresh `reload` reference on every render → fresh\r\n // `innerMainContext` → nested descendants' useEffects re-run → infinite\r\n // register/version loop.\r\n const refetchRef = useRef(query.refetch);\r\n refetchRef.current = query.refetch;\r\n const recordRef = useRef<TableRecord | undefined>(undefined);\r\n const loadedRecordRef = useRef<TableRecord | undefined>(undefined);\r\n const ownDirtyRef = useRef(false);\r\n const pendingChangesRef = useRef<ReadonlyMap<string, ColumnValueBase | null>>(new Map());\r\n pendingChangesRef.current = pendingChanges;\r\n\r\n // Stash validation in a ref so the load effect doesn't have to depend on\r\n // its identity (which churns on every error transition).\r\n const validationRef = useRef(validation);\r\n validationRef.current = validation;\r\n\r\n // Create-mode seeding. Runs once when entering create mode so the\r\n // loadedRecord baseline reflects whatever defaults the caller wired up\r\n // via `initialRecord` — typically a parent-FK lookup pre-populated by\r\n // a SubGrid-hosted new-record dialog. The pending-changes map starts\r\n // empty (the user hasn't typed yet), so the record reads \"clean\" until\r\n // they make the first edit; isDirty drives the Save-button enable\r\n // gate. Mirrors Blazor's `RecordContext.razor.cs:378-432` create-new\r\n // branch — same intent, expressed inline as a one-shot effect rather\r\n // than as the `InitializeRecordCoreInnerAsync` else-branch.\r\n //\r\n // We also fall through to a bare-empty record when `initialRecord` is\r\n // omitted, so a consumer who just passes `<RecordContext table=\"contact\" />`\r\n // gets a blank-form dialog without having to construct a stub.\r\n const isCreateModeRef = useRef(isCreateMode);\r\n isCreateModeRef.current = isCreateMode;\r\n // Sync the resolved-flag ref so `saveAll` stays stable while still\r\n // honoring the latest prop / cascade resolution. Same pattern\r\n // `onBeforeSaveRef` and `isCreateModeRef` use.\r\n const forceSuccessfulValidationBeforeSaveRef = useRef(\r\n resolvedForceSuccessfulValidationBeforeSave,\r\n );\r\n forceSuccessfulValidationBeforeSaveRef.current =\r\n resolvedForceSuccessfulValidationBeforeSave;\r\n // Read through refs in `saveAll` so its identity stays stable across\r\n // `mainContext` re-mounts and stays in sync with the latest\r\n // `queryParameterName` prop without forcing the callback to re-create.\r\n const queryParameterNameRef = useRef(queryParameterName);\r\n queryParameterNameRef.current = queryParameterName;\r\n const isTopMostRef = useRef(mainContext === null);\r\n isTopMostRef.current = mainContext === null;\r\n useEffect(() => {\r\n if (!isCreateMode) return;\r\n // Pre-generate a GUID into `_idForCreate` (NOT `id`) so descendants\r\n // — `<ManyToManyLookupEdit>`, SubGrid parent-FK pre-fill, anything\r\n // emitting a cross-record wire reference — can resolve a stable\r\n // parent identity even before the record is saved, while\r\n // `record.id` remains undefined to flag \"not yet persisted.\" The\r\n // temp GUID flows through to the `CreateRequest` body in\r\n // `buildOwnSaveRequest`; the server's in-batch resolver\r\n // (`ResolveAssociateRecordAsync`) matches AssociateRequest /\r\n // child-FK lookups against either `id` or `_idForCreate`, so a\r\n // Create + Associate pair within one `ExecuteMultiple` resolves\r\n // correctly. Mirrors Blazor's `TableRecord._idForCreate` field +\r\n // the implicit `TableRecord -> TableRecordReference` conversion.\r\n //\r\n // An explicit `initialRecord.id` still wins (rare but supported\r\n // — useful for workflows that import data with pre-assigned ids);\r\n // otherwise we seed `_idForCreate` and leave `id` unset.\r\n const baseSeed = initialRecord ?? { tableName: table, properties: {} };\r\n const seeded: TableRecord =\r\n baseSeed.id !== undefined\r\n ? baseSeed\r\n : { ...baseSeed, _idForCreate: crypto.randomUUID() };\r\n setLoadedRecord(seeded);\r\n setPendingChanges(new Map());\r\n // Only re-seeds when entering create mode for the first time or when\r\n // the caller swaps `initialRecord` to a new identity. The pending\r\n // changes wipe is intentional — switching default values mid-edit\r\n // would otherwise leave the user's typed values dangling against a\r\n // baseline they don't match. A re-seed regenerates `_idForCreate` too,\r\n // matching the wipe's \"fresh form\" intent.\r\n }, [isCreateMode, initialRecord, table]);\r\n\r\n // URL query-string column defaults. Mirrors Blazor's create-mode\r\n // `_navigationManager.QueryString()` loop in `RecordContext.razor.cs:386-413`\r\n // — when a `queryParameterName` is set, every URL param whose name matches\r\n // a column on the table seeds that column's value on the new record. Gated\r\n // on:\r\n // - create mode (URL defaults only apply to fresh records)\r\n // - `queryParameterName` set (the opt-in marker — without it, Blazor\r\n // doesn't read the URL either)\r\n // - table metadata loaded (we need column $type discriminators to parse)\r\n // - the seeded baseline exists (so the prior effect has run)\r\n // - the user hasn't started typing (`pendingChanges` is empty) — if they\r\n // have, we'd clobber their edits\r\n //\r\n // Layered on top of `initialRecord`: URL wins for any column it specifies\r\n // (matches the explicit intent of constructing a `?col=val` link from e.g.\r\n // `<NavigateNewRecordGridButton>`). Applied as `loadedRecord` mutation, NOT\r\n // `pendingChanges`, so the record reads \"clean\" after the layering — the\r\n // SaveContextButton's enable gate only fires on the user's first edit, and\r\n // a `CreateRequest` ships the whole property set regardless of the\r\n // dirty-tracker's state. Matches Blazor's post-apply `Record.ResetState()`.\r\n useEffect(() => {\r\n if (!isCreateMode) return;\r\n if (!queryParameterName) return;\r\n if (!metadataQuery.data) return;\r\n if (!loadedRecord) return;\r\n if (pendingChangesRef.current.size > 0) return;\r\n const search = typeof window !== 'undefined' ? window.location.search : '';\r\n const defaults = parseQueryDefaultsFromUrl(search, metadataQuery.data);\r\n const cols = Object.keys(defaults);\r\n if (cols.length === 0) return;\r\n let changed = false;\r\n const nextProperties: Record<string, ColumnValueBase> = {\r\n ...(loadedRecord.properties ?? {}),\r\n };\r\n for (const col of cols) {\r\n const next = defaults[col]!;\r\n if (!valuesEqual(nextProperties[col], next)) {\r\n nextProperties[col] = next;\r\n changed = true;\r\n }\r\n }\r\n if (!changed) return;\r\n setLoadedRecord((current) =>\r\n current ? { ...current, properties: nextProperties } : current,\r\n );\r\n }, [isCreateMode, queryParameterName, metadataQuery.data, loadedRecord]);\r\n\r\n // Skip-fetch re-baseline. When the consumer-supplied `loadedRecord`\r\n // prop changes identity (typically after a grid refresh delivered fresh\r\n // row data), treat it as if a fetch had just completed: set the\r\n // baseline, re-seed pending changes from `initialPendingChanges` (or\r\n // empty), and clear validation errors. Pass a stable / memoized\r\n // reference if you want pending edits to persist across re-renders.\r\n useEffect(() => {\r\n if (!isSkipFetchMode || !loadedRecordProp) return;\r\n setLoadedRecord(loadedRecordProp);\r\n setPendingChanges(\r\n initialPendingChanges && initialPendingChanges.size > 0\r\n ? new Map(initialPendingChanges)\r\n : new Map(),\r\n );\r\n validationRef.current?.clearErrors();\r\n onRecordLoadedRef.current?.(loadedRecordProp);\r\n }, [isSkipFetchMode, loadedRecordProp, initialPendingChanges]);\r\n\r\n useEffect(() => {\r\n if (query.status === 'success' && query.data) {\r\n setLoadedRecord(query.data);\r\n // Fresh server data — seed pendingChanges from the caller-supplied\r\n // overlay (or empty when no overlay). Used by the dialog-based\r\n // Edit flow to apply previously-queued dirty columns onto the\r\n // freshly-loaded server record. Keeping these as pendingChanges\r\n // (rather than mutating loadedRecord) means a user revert-to-\r\n // server-value cleanly removes the column from the map — the\r\n // baseline-match check in `setColumnValue` sees the original\r\n // server value and the new typed value collide, and drops the\r\n // entry. (Reload semantics still apply otherwise: server wins\r\n // over local edits across reloads, matching Blazor's\r\n // RefreshAsync.)\r\n setPendingChanges(\r\n initialPendingChanges && initialPendingChanges.size > 0\r\n ? new Map(initialPendingChanges)\r\n : new Map(),\r\n );\r\n // Drop any validation errors that were tracked against the previous\r\n // record — fresh data is a clean slate.\r\n validationRef.current?.clearErrors();\r\n onRecordLoadedRef.current?.(query.data);\r\n }\r\n // Re-running on `initialPendingChanges` identity change is intentional\r\n // — a caller swapping the overlay between renders should re-seed.\r\n // In practice the overlay is stable per dialog session.\r\n }, [query.status, query.data, initialPendingChanges]);\r\n\r\n // Derived view of the record: loadedRecord with pendingChanges applied.\r\n // Consumers see one merged record; internally we keep the baseline + diff\r\n // separate so the dirty calculation can compare against original values.\r\n loadedRecordRef.current = loadedRecord;\r\n const record = useMemo<TableRecord | undefined>(() => {\r\n if (!loadedRecord) return undefined;\r\n if (pendingChanges.size === 0) return loadedRecord;\r\n const properties: Record<string, ColumnValueBase> = { ...(loadedRecord.properties ?? {}) };\r\n for (const [col, val] of pendingChanges) {\r\n if (val === null) {\r\n delete properties[col];\r\n } else {\r\n properties[col] = val;\r\n }\r\n }\r\n return { ...loadedRecord, properties, isDirty: true };\r\n }, [loadedRecord, pendingChanges]);\r\n\r\n const dirtyColumns = useMemo<ReadonlySet<string>>(\r\n () => new Set(pendingChanges.keys()),\r\n [pendingChanges],\r\n );\r\n const ownDirty = pendingChanges.size > 0;\r\n recordRef.current = record;\r\n ownDirtyRef.current = ownDirty;\r\n\r\n // Nested descendants — grids, M2M editors, child <RecordContext>s. RecordContext\r\n // serves as their MainContext. Same registry pattern as MainContext: stable\r\n // ref + version counter so re-registrations don't churn React.\r\n const nestedRef = useRef<Set<MainContextDescendant>>(new Set());\r\n const [nestedVersion, setNestedVersion] = useState(0);\r\n const nestedDirty = useMemo(() => {\r\n void nestedVersion;\r\n for (const d of nestedRef.current) {\r\n if (d.isDirty()) return true;\r\n }\r\n return false;\r\n }, [nestedVersion]);\r\n const isDirty = ownDirty || nestedDirty;\r\n\r\n const registerNested = useCallback((descendant: MainContextDescendant) => {\r\n nestedRef.current.add(descendant);\r\n setNestedVersion((v) => v + 1);\r\n return () => {\r\n nestedRef.current.delete(descendant);\r\n setNestedVersion((v) => v + 1);\r\n };\r\n }, []);\r\n\r\n // Counterpart to `MainContext`'s `notifyDescendantDirtyChanged` — see\r\n // its comment for the focus-loss rationale. Descendants whose `isDirty`\r\n // getter starts returning a different value call this so this\r\n // RecordContext re-aggregates without forcing them to unregister +\r\n // re-register their descendant object (which used to cascade context\r\n // re-renders down to every inline editor on the row).\r\n const notifyNestedDirtyChanged = useCallback(() => {\r\n setNestedVersion((v) => v + 1);\r\n }, []);\r\n\r\n // Build the per-RecordContext save request — UpdateRequest for an\r\n // existing record (id present), CreateRequest for a new record being\r\n // composed in create mode. Returns null when nothing is dirty (no work\r\n // to do). Pulled out so it can feed both the public saveAll AND the\r\n // outward {@link MainContextDescendant.getRequests} that registers\r\n // with a parent MainContext.\r\n //\r\n // **Update path** (id present, default): mirrors Blazor's\r\n // `RecordContext.GetRequests` returning an UpdateRequest for\r\n // `TableRecord.GetChangedRecord()`. Only the dirty columns ride along —\r\n // unchanged columns are excluded so server-side audit logs reflect\r\n // actual changes. `null` entries in pendingChanges are explicit-clear\r\n // signals (user cleared a lookup, removed a file, etc.); the server's\r\n // update handler interprets nulls and translates them into the right\r\n // operation.\r\n //\r\n // **Create path** (create mode): ships the FULL property set — both\r\n // the `initialRecord` defaults (parent FK pre-fills, etc.) AND the\r\n // user's edits, since the server has no baseline to merge against.\r\n // Mirrors Blazor's `MainContext.SaveAsync` ExecuteMultiple dispatch\r\n // where new TableRecords flow through `CreateRequest` rather than\r\n // `UpdateRequest`.\r\n //\r\n // The cast on `properties` keeps null-clear semantics flowing despite\r\n // the wire shape modeling it as no-null — same caveat the update path\r\n // already lives with.\r\n const buildOwnSaveRequest = useCallback(():\r\n | UpdateRequest\r\n | CreateRequest\r\n | null => {\r\n if (!recordRef.current || !ownDirtyRef.current) return null;\r\n const current = recordRef.current;\r\n if (isCreateModeRef.current) {\r\n // Take the merged record (loaded baseline + pending edits) verbatim —\r\n // `recordRef.current` is already the merged view that descendants\r\n // see, so every column with a value lands in the create payload.\r\n const allProperties: Record<string, ColumnValueBase | null> = {\r\n ...(current.properties as Record<string, ColumnValueBase | null>),\r\n };\r\n return {\r\n $type: 'CreateRequest',\r\n record: {\r\n tableName: current.tableName,\r\n // Include the client-pre-generated GUID. The seed effect writes\r\n // it into `_idForCreate` (Blazor parity); the server's in-batch\r\n // resolver matches AssociateRequest / child-FK references\r\n // against either `id` or `_idForCreate`, so a Create + Associate\r\n // pair within one `ExecuteMultiple` resolves to the same record.\r\n // `id` is also forwarded when the consumer supplied an explicit\r\n // one via `initialRecord` (rare — pre-assigned ids on data\r\n // import).\r\n ...(current.id !== undefined && { id: current.id }),\r\n ...(current._idForCreate !== undefined &&\r\n current._idForCreate !== null && {\r\n _idForCreate: current._idForCreate,\r\n }),\r\n properties: allProperties as Record<string, ColumnValueBase>,\r\n },\r\n };\r\n }\r\n const partialProperties: Record<string, ColumnValueBase | null> = {};\r\n for (const [col, val] of pendingChangesRef.current) {\r\n partialProperties[col] = val;\r\n }\r\n const partialRecord: TableRecord = {\r\n tableName: current.tableName,\r\n properties: partialProperties as Record<string, ColumnValueBase>,\r\n ...(current.id !== undefined && { id: current.id }),\r\n };\r\n return { $type: 'UpdateRequest', record: partialRecord };\r\n }, []);\r\n\r\n // Aggregated requests for THIS subtree: own UpdateRequest (if dirty)\r\n // followed by every nested descendant's getRequests, in registration\r\n // order. The outer MainContext.saveAll calls this on each direct\r\n // descendant and concatenates the results into a single\r\n // executeMultiple batch — same shape Blazor's\r\n // `MainContext.GetRequests` builds by walking _grids / _childContexts.\r\n const getRequests = useCallback((): OrganizationRequest[] => {\r\n const all: OrganizationRequest[] = [];\r\n const own = buildOwnSaveRequest();\r\n if (own) all.push(own);\r\n for (const descendant of nestedRef.current) {\r\n all.push(...descendant.getRequests());\r\n }\r\n return all;\r\n }, [buildOwnSaveRequest]);\r\n\r\n // Aggregate validate. Defaults to true at the RecordContext level —\r\n // per-column validation is already routed through the parallel\r\n // `ValidationContext` surface (driven by `SaveContextButton`), which is\r\n // a different concern than the structural-validation hook Blazor uses\r\n // for `IComponentContext.Validate`. Nested descendants drive their own\r\n // validate() (e.g. a future editable grid may invalidate when a queued\r\n // row fails its rules); this RecordContext just defers to them.\r\n const validate = useCallback(async (): Promise<boolean> => {\r\n for (const descendant of nestedRef.current) {\r\n if (!(await descendant.validate())) return false;\r\n }\r\n return true;\r\n }, []);\r\n\r\n // Public + inner saveAll. Uses the same transactional flow the outer\r\n // MainContext.saveAll uses, so calling this directly on a RecordContext\r\n // (standalone, no outer MainContext) produces identical server-side\r\n // behavior to wrapping it in a MainContext and calling that's saveAll.\r\n //\r\n // Stable across renders — reads state through refs so innerMainContext's\r\n // memo identity stays steady when the user types.\r\n const saveAll = useCallback(async () => {\r\n // Defensive guard against calling save before the record has loaded —\r\n // catches programmer errors (forgot to gate on `status === 'success'`).\r\n if (!loadedRecordRef.current) {\r\n throw new Error('RecordContext.save called before the record loaded.');\r\n }\r\n // onBeforeSave cancels the entire subtree, not just the own-record\r\n // PATCH — matches Blazor's behavior where throwing from OnBeforeSave\r\n // aborts before any child save runs.\r\n await onBeforeSaveRef.current?.();\r\n // Structural validation cascade. Per-column validation runs separately\r\n // through ValidationContext (driven by SaveContextButton) — keeps the\r\n // per-edit error UX intact when this RC is the top-most context.\r\n // Skipped when `forceSuccessfulValidationBeforeSave` is `false`.\r\n if (forceSuccessfulValidationBeforeSaveRef.current) {\r\n const valid = await validate();\r\n if (!valid) return;\r\n }\r\n // Re-build the own request inline (rather than via getRequests\r\n // which also concatenates descendants') so we keep a handle on\r\n // whether it was a CreateRequest — needed below to find the\r\n // matching CreateResponse and flip create → edit mode.\r\n const ownReq = buildOwnSaveRequest();\r\n const descendantReqs: OrganizationRequest[] = [];\r\n for (const descendant of nestedRef.current) {\r\n descendantReqs.push(...descendant.getRequests());\r\n }\r\n const requests = ownReq ? [ownReq, ...descendantReqs] : descendantReqs;\r\n if (requests.length > 0) {\r\n // Ask the server to return individual responses so the CreateResponse\r\n // for `ownReq` (when applicable) surfaces the new record id back to\r\n // us — without this, a create succeeds but the context stays stuck\r\n // in create mode forever.\r\n const responses = await ppp.executeMultipleAsync(requests, { returnResponses: true });\r\n // Auto-flip create → edit when own was a CreateRequest. Response\r\n // index 0 corresponds to ownReq (it was placed first above).\r\n // Setting `effectiveId` cascades into the `useRecord` query via\r\n // its deps, which auto-fires a fetch and clears pendingChanges\r\n // through the success effect; no explicit refetch needed for the\r\n // create case.\r\n if (ownReq?.$type === 'CreateRequest') {\r\n const ownResp = responses[0] as\r\n | { $type?: string; id?: string }\r\n | undefined;\r\n if (ownResp?.$type === 'CreateResponse' && ownResp.id) {\r\n setEffectiveId(ownResp.id);\r\n // Post-create URL navigation. Mirrors Blazor's\r\n // `RecordContext.SaveAsync` → `_navigationManager.NavigateTo`\r\n // branch: when this is the top-most context and the URL\r\n // doesn't already carry the new id under `queryParameterName`,\r\n // rewrite the URL to point at the freshly-created record.\r\n //\r\n // `replaceState` (not pushState) so the \"new record\" URL\r\n // doesn't hang around in history — pressing Back should\r\n // return to wherever the user came from, not back to the\r\n // create form for the now-existing record. Mirrors Blazor's\r\n // `NavigationHistory.PopCurrent()` + `NavigateTo` pair.\r\n const qpn = queryParameterNameRef.current;\r\n if (qpn && isTopMostRef.current && typeof window !== 'undefined') {\r\n const existing = new URLSearchParams(window.location.search).get(\r\n qpn,\r\n );\r\n if (!existing) {\r\n const newUrl = new URL(window.location.href);\r\n newUrl.searchParams.set(qpn, ownResp.id);\r\n window.history.replaceState(\r\n window.history.state,\r\n '',\r\n newUrl.toString(),\r\n );\r\n }\r\n }\r\n }\r\n }\r\n // Clear the local dirty markers after the round-trip succeeds. The\r\n // refetch below would also clear them via the success effect when its\r\n // fresh data lands, but clearing here gives the UI an immediate\r\n // \"saved\" state without waiting for the GET.\r\n setPendingChanges(new Map());\r\n }\r\n // Descendants with side-channel commits (file uploads, M2M\r\n // associations going through endpoints other than executeMultiple)\r\n // still need a refresh pass even when the batched request set is\r\n // empty — they may have committed their own work and need their\r\n // post-save state pulled in. Always run the cascade below\r\n // regardless of `requests.length`. Matches Blazor's\r\n // `MainContext.SaveAsync` which calls `RefreshAsync(true)` on\r\n // every descendant after a save attempt, batch-or-no-batch.\r\n // Refresh own record (skipped in create mode — the `effectiveId`\r\n // flip above already kicks off a fresh fetch via the query dep\r\n // change; the explicit refetch path is only meaningful for update\r\n // saves where the id hasn't changed) + cascade to nested\r\n // descendants regardless of mode so child grids / record contexts\r\n // pick up any changes.\r\n if (!isCreateModeRef.current) {\r\n // Fire-and-forget the post-save refetch. Awaiting it would let\r\n // the test framework's `act()` wait for the in-flight fetch to\r\n // settle — which never completes deterministically in a\r\n // unit-test runner, since the fakeFetch resolves on a microtask\r\n // and `act()` keeps scheduling renders for each setState in the\r\n // success chain. The local dirty state is already cleared\r\n // synchronously above; the refetch's data lands via the\r\n // success effect when it eventually completes, with no observer\r\n // depending on the await to finish. Mirrors how Blazor's\r\n // `MainContext.SaveAsync(refresh: true)` doesn't block on the\r\n // post-save refresh either.\r\n void refetchRef.current();\r\n }\r\n for (const descendant of nestedRef.current) {\r\n await descendant.refresh();\r\n }\r\n }, [ppp, validate, buildOwnSaveRequest]);\r\n\r\n const reload = useCallback(async () => {\r\n refetchRef.current();\r\n for (const descendant of nestedRef.current) {\r\n await descendant.refresh();\r\n }\r\n }, []);\r\n\r\n // Discard own pending edits + clear validation + cascade to nested\r\n // descendants. The \"Cancel\" / \"Discard\" primitive — mirrors Blazor's\r\n // `RecordContext.ResetState()` which overrides `MainContext.ResetState()`\r\n // to also wipe `_originalPropertyValues`. Synchronous; React batches all\r\n // the setState calls into one re-render.\r\n const resetState = useCallback(() => {\r\n setPendingChanges(new Map());\r\n validationRef.current?.clearErrors();\r\n for (const descendant of nestedRef.current) {\r\n descendant.resetState();\r\n }\r\n }, []);\r\n\r\n const setColumnValue = useCallback(\r\n (columnName: string, value: ColumnValueBase | null | undefined) => {\r\n setPendingChanges((prev) => {\r\n if (!loadedRecord) return prev;\r\n const original = loadedRecord.properties?.[columnName];\r\n const newValue = value ?? null; // normalize undefined → null\r\n\r\n // If the new value matches the loaded baseline, drop this column from\r\n // the pending set — the edit cancels itself out and the column reads\r\n // clean again. Mirrors Blazor's `_originalPropertyValues.Remove(...)`\r\n // branch in `TableRecord.cs`.\r\n if (valuesEqual(original, newValue)) {\r\n if (!prev.has(columnName)) return prev;\r\n const next = new Map(prev);\r\n next.delete(columnName);\r\n return next;\r\n }\r\n\r\n // Skip the state update if the pending value already matches what\r\n // we'd write — saves a render when consumers fire change events\r\n // repeatedly with the same value (e.g. text inputs on every keystroke\r\n // that lands the same character).\r\n const existing = prev.has(columnName) ? prev.get(columnName) ?? null : undefined;\r\n if (existing !== undefined && valuesEqual(existing, newValue)) return prev;\r\n\r\n const next = new Map(prev);\r\n next.set(columnName, newValue);\r\n return next;\r\n });\r\n },\r\n [loadedRecord],\r\n );\r\n\r\n // Updates the loaded baseline directly so the column's `record.properties[…]`\r\n // changes WITHOUT being marked dirty. The use case is late-arriving server\r\n // data — file binaries lazy-loaded by `<ImageEdit>`/`<FileEdit>` once the\r\n // record's metadata-only initial load surfaces the file id, async-resolved\r\n // formatted-value lookups, etc. Skips when the column has a pending edit so\r\n // the lazy fetch doesn't clobber what the user is currently typing.\r\n const hydrateColumnValue = useCallback(\r\n (columnName: string, value: ColumnValueBase | null) => {\r\n setLoadedRecord((current) => {\r\n if (!current) return current;\r\n // Pending-edit guard. Reading the latest `pendingChanges` via setState\r\n // would require an awkward double-setter pattern; instead we read the\r\n // closed-over `pendingChanges` and accept that a hydration arriving\r\n // simultaneously with an edit MAY land. In practice the lazy-load\r\n // path fires from `useEffect` AFTER the initial render — by then any\r\n // pre-existing pending change is already in `pendingChanges` and this\r\n // closure sees it. Edits that race in during the fetch are extremely\r\n // unlikely given the typical 100ms+ network round-trip.\r\n if (pendingChanges.has(columnName)) return current;\r\n\r\n const previous = current.properties?.[columnName];\r\n // Wire-shape `properties` is `{[k]: ColumnValueBase}` — no null\r\n // allowed. Clearing means deleting the key so the column reads as\r\n // \"absent\" rather than \"present with null value\", which matches\r\n // how Dataverse round-trips an unset column.\r\n if (value === null) {\r\n if (previous === undefined) return current;\r\n const nextProperties = { ...(current.properties ?? {}) };\r\n delete nextProperties[columnName];\r\n return { ...current, properties: nextProperties };\r\n }\r\n\r\n if (valuesEqual(previous, value)) return current;\r\n return {\r\n ...current,\r\n properties: { ...(current.properties ?? {}), [columnName]: value },\r\n };\r\n });\r\n },\r\n [pendingChanges],\r\n );\r\n\r\n // Auto-show a scoped loading overlay while the record is fetching. Hides\r\n // when the load completes (success or error) or when the RecordContext\r\n // unmounts. No-op when no <OverlayService> is mounted — overlays are\r\n // optional UX, not a hard dependency. Mirrors Blazor's per-RecordContext\r\n // overlay behavior in `InitializeRecordCoreAsync`.\r\n const loadingOverlayRef = useRef<OverlayInstance | null>(null);\r\n useEffect(() => {\r\n if (!overlayService) return;\r\n if (query.status === 'loading') {\r\n // Only one loading overlay at a time per RecordContext — don't stack\r\n // if React effects run a second time while already loading.\r\n if (!loadingOverlayRef.current) {\r\n loadingOverlayRef.current = overlayService.show({\r\n targetId: overlayTargetId,\r\n description: localizer.t('app.messages.record-loading'),\r\n });\r\n }\r\n } else if (loadingOverlayRef.current) {\r\n overlayService.hide(loadingOverlayRef.current);\r\n loadingOverlayRef.current = null;\r\n }\r\n return () => {\r\n if (loadingOverlayRef.current) {\r\n overlayService.hide(loadingOverlayRef.current);\r\n loadingOverlayRef.current = null;\r\n }\r\n };\r\n }, [overlayService, query.status, overlayTargetId, localizer]);\r\n\r\n // Mirror the current aggregate `isDirty` into a ref so the registered\r\n // descendant's getter (and the page-wide unsaved-changes registry's\r\n // reporter further down) can read the latest value without forcing\r\n // those registrations to re-run on every transition. Declared BEFORE\r\n // the registerDescendant effect because the effect's closure captures\r\n // `isDirtyRef` and runs on first mount.\r\n const isDirtyRef = useRef(isDirty);\r\n isDirtyRef.current = isDirty;\r\n // Same trick for the outer MainContext reference — the parent's value\r\n // identity churns every time ITS `isDirty` aggregate flips (because\r\n // `isDirty` lives in the value memo's deps). If our notify effect\r\n // depended on the live `mainContext` value, the cycle\r\n // child.isDirty flip → notify → parent.isDirty flip → parent value\r\n // identity flip → child notify effect re-fires → notify again\r\n // would loop until React detected an infinite update. Reading the\r\n // mainContext via a ref breaks the cycle: the notify effect only\r\n // re-fires on actual local `isDirty` transitions, and always uses\r\n // the latest mainContext snapshot when it does.\r\n const mainContextRef = useRef(mainContext);\r\n mainContextRef.current = mainContext;\r\n\r\n // Auto-register with the OUTER MainContext (if one wraps this RecordContext).\r\n // We register the AGGREGATED save/refresh/isDirty so the outer sees one\r\n // descendant whose state already includes nested children — outer.saveAll\r\n // ends up calling our saveAll which does own + nested in one shot.\r\n //\r\n // `isDirty` is a ref-backed getter and dropped from the dep array so\r\n // the registration sticks across dirty-state transitions. The first\r\n // keystroke in any inline-edit cell flips isDirty here → before this\r\n // change, the effect re-fired, tearing down + re-creating the\r\n // descendant on the outer, which cascaded context re-renders down\r\n // through the SubGrid / MainGrid tree and dropped focus from the\r\n // active input. The `notify` effect below pokes the outer when our\r\n // local aggregate flips so its `nestedDirty` still re-evaluates.\r\n useEffect(() => {\r\n if (!mainContext) return;\r\n return mainContext.registerDescendant({\r\n getRequests,\r\n validate,\r\n refresh: reload,\r\n resetState,\r\n isDirty: () => isDirtyRef.current,\r\n });\r\n // Key on `mainContext.registerDescendant` (stable per MainContext\r\n // instance via useCallback([])) rather than the whole `mainContext`\r\n // value object — the value identity churns every time the outer\r\n // RecordContext's own isDirty aggregate flips, and re-firing this\r\n // registration in response would tear the descendant out of the\r\n // registry (briefly flipping aggregate to false) and then re-add it\r\n // (flipping it back to true), which churns the value identity\r\n // again in an infinite loop. Reading via the function identity\r\n // means a re-register only happens when the consumer actually\r\n // mounts a different MainContext instance.\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [mainContext?.registerDescendant, getRequests, validate, reload, resetState]);\r\n\r\n // Notify the outer MainContext that our local aggregate flipped so it\r\n // re-reads `isDirty()` and updates its own aggregate. Read mainContext\r\n // via the ref so we don't re-fire when the parent's value identity\r\n // changes (which it does every time ITS isDirty flips, which is\r\n // exactly when WE'D be notifying it of OUR new dirty state — an\r\n // infinite loop without the ref indirection).\r\n useEffect(() => {\r\n mainContextRef.current?.notifyDescendantDirtyChanged();\r\n }, [isDirty]);\r\n\r\n // Auto unsaved-changes warning when this is the top-most context (no outer\r\n // MainContext wrapping us). Same pattern as MainContext — wired here too\r\n // because a standalone <RecordContext> with no outer wrapper IS the\r\n // top-most context on the page.\r\n useBeforeUnloadWhen(isTopMost && resolvedWarnOnUnsavedChanges && isDirty);\r\n\r\n // Same registry registration as `MainContext` — when this RecordContext\r\n // is itself topmost (no outer MainContext wraps it) it reports its\r\n // aggregate `isDirty` to the page-wide registry so the router-blocker\r\n // guard can prompt on navigation. Reading through refs (already wired\r\n // above) avoids re-registering on every keystroke;\r\n // `notifyDirtyChanged()` below fires on the actual aggregate flip.\r\n const unsavedChangesRegistry = useUnsavedChangesRegistry();\r\n const resolvedWarnRef = useRef(resolvedWarnOnUnsavedChanges);\r\n resolvedWarnRef.current = resolvedWarnOnUnsavedChanges;\r\n useEffect(() => {\r\n if (!isTopMost) return;\r\n if (!unsavedChangesRegistry) return;\r\n return unsavedChangesRegistry.register({\r\n isDirty: () => resolvedWarnRef.current && isDirtyRef.current,\r\n });\r\n }, [isTopMost, unsavedChangesRegistry]);\r\n useEffect(() => {\r\n if (!isTopMost) return;\r\n if (!unsavedChangesRegistry) return;\r\n unsavedChangesRegistry.notifyDirtyChanged();\r\n }, [isTopMost, unsavedChangesRegistry, isDirty, resolvedWarnOnUnsavedChanges]);\r\n\r\n // Inner MainContextValue this RecordContext provides for its subtree —\r\n // makes nested grids/M2M editors/child <RecordContext>s register with us\r\n // instead of skipping past to a further-up MainContext.\r\n const innerMainContext = useMemo<MainContextValue>(\r\n () => ({\r\n saveAll,\r\n refreshAll: reload,\r\n resetState,\r\n validate,\r\n getRequests,\r\n registerDescendant: registerNested,\r\n notifyDescendantDirtyChanged: notifyNestedDirtyChanged,\r\n isDirty,\r\n warnOnUnsavedChanges: resolvedWarnOnUnsavedChanges,\r\n forceSuccessfulValidationBeforeSave: resolvedForceSuccessfulValidationBeforeSave,\r\n }),\r\n [\r\n saveAll,\r\n reload,\r\n resetState,\r\n validate,\r\n getRequests,\r\n registerNested,\r\n notifyNestedDirtyChanged,\r\n isDirty,\r\n resolvedWarnOnUnsavedChanges,\r\n resolvedForceSuccessfulValidationBeforeSave,\r\n ],\r\n );\r\n\r\n // Stash `onBeforeDelete` in a ref so the callback identity can churn\r\n // (consumers often pass inline arrow functions) without invalidating\r\n // the context value or our `runBeforeDelete` callback. Each render\r\n // overwrites the ref with the latest closure; click-time invocation\r\n // reads `.current` and gets the up-to-date handler.\r\n const onBeforeDeleteRef = useRef<typeof onBeforeDelete>(onBeforeDelete);\r\n onBeforeDeleteRef.current = onBeforeDelete;\r\n const runBeforeDelete = useCallback(async () => {\r\n const handler = onBeforeDeleteRef.current;\r\n if (handler) await handler();\r\n }, []);\r\n\r\n const value = useMemo<RecordContextValue>(\r\n () => ({\r\n status: query.status,\r\n record,\r\n error: query.error,\r\n reload,\r\n save: saveAll,\r\n getRequests,\r\n resetState,\r\n setColumnValue,\r\n hydrateColumnValue,\r\n tableMetadata: metadataQuery.data,\r\n isDirty,\r\n dirtyColumns,\r\n overlayTargetId,\r\n runBeforeDelete,\r\n }),\r\n [\r\n query.status,\r\n query.error,\r\n record,\r\n reload,\r\n saveAll,\r\n getRequests,\r\n resetState,\r\n setColumnValue,\r\n hydrateColumnValue,\r\n metadataQuery.data,\r\n isDirty,\r\n dirtyColumns,\r\n overlayTargetId,\r\n runBeforeDelete,\r\n ],\r\n );\r\n\r\n return (\r\n <MainContextProvider value={innerMainContext}>\r\n <RecordContextContext.Provider value={value}>{children}</RecordContextContext.Provider>\r\n </MainContextProvider>\r\n );\r\n}\r\n\r\n/**\r\n * Reads the nearest ancestor {@link RecordContext}'s value. Throws when called\r\n * outside one — same diagnostic pattern as `useAuth`.\r\n *\r\n * @example\r\n * ```tsx\r\n * import { QueryStatus, useRecordContext } from '@powerportalspro/react';\r\n *\r\n * function ContactDetails() {\r\n * const { status, record } = useRecordContext();\r\n * if (status === QueryStatus.Loading) return <Spinner />;\r\n * if (status === QueryStatus.Error) return <ErrorBanner />;\r\n * return <h1>{record!.formattedValues?.name}</h1>;\r\n * }\r\n * ```\r\n */\r\nexport function useRecordContext(): RecordContextValue {\r\n const ctx = useContext(RecordContextContext);\r\n if (!ctx) {\r\n throw new Error(\r\n 'useRecordContext must be used inside <RecordContext>. Wrap the component tree where you need the record.',\r\n );\r\n }\r\n return ctx;\r\n}\r\n\r\n/**\r\n * Non-throwing variant of {@link useRecordContext} — returns the nearest\r\n * `<RecordContext>`'s value when one is present, otherwise `null`. Useful\r\n * for components that work either inside or outside a record context\r\n * (e.g. `<SubGrid>` picks up the cascading record when one is available\r\n * but accepts an explicit `record` prop as an alternative).\r\n *\r\n * Prefer {@link useRecordContext} when the component absolutely requires\r\n * the context — the loud throw catches wiring bugs early; this hook is\r\n * for the genuinely optional case.\r\n */\r\nexport function useRecordContextOptional(): RecordContextValue | null {\r\n return useContext(RecordContextContext);\r\n}\r\n","import type { components } from '@powerportalspro/core';\r\nimport type { ReactNode } from 'react';\r\nimport { RecordContext, useRecordContext } from './record-context.js';\r\n\r\ntype Schemas = components['schemas'];\r\n\r\n/**\r\n * The empty-GUID sentinel Dataverse round-trips for cleared lookups.\r\n * Treated as \"no value\" so an explicitly-cleared lookup short-circuits\r\n * the nested context (matches Blazor's `Guid.Empty` check).\r\n */\r\nconst EMPTY_GUID = '00000000-0000-0000-0000-000000000000';\r\n\r\nexport interface LookupRecordContextProps {\r\n /**\r\n * Logical name of the lookup column on the parent `<RecordContext>`'s\r\n * record. The column must resolve to a `LookupValue` (typed\r\n * `$type: 6`) — the component reads the lookup's `tableName` + `value`\r\n * and spawns a nested `<RecordContext>` for the referenced record.\r\n *\r\n * Required. Mirrors Blazor's `<LookupRecordContext ColumnName=\"...\">`\r\n * `ColumnName` parameter.\r\n */\r\n columnName: string;\r\n\r\n /**\r\n * Optional pre-save cancellable hook forwarded to the nested\r\n * `<RecordContext>`. Throwing (or returning a rejected promise) from\r\n * this callback aborts the save before any server call — the same\r\n * semantic as `<RecordContext onBeforeSave={…}>` directly. Mirrors\r\n * Blazor's `LookupRecordContext.OnBeforeSave` cancellable hook.\r\n */\r\n onBeforeSave?: () => void | Promise<void>;\r\n\r\n /**\r\n * Children rendered inside the nested context. Typical contents:\r\n * edit components (`<TextEdit>`, `<NumberEdit>`, etc.) bound to\r\n * columns on the LOOKED-UP record's table — e.g. an\r\n * `<TextEdit columnName=\"firstname\" />` inside a\r\n * `<LookupRecordContext columnName=\"primarycontactid\">` on an account\r\n * form edits the linked contact's first name.\r\n */\r\n children?: ReactNode;\r\n\r\n /**\r\n * When `false`, renders nothing. Defaults to `true`. Useful for\r\n * conditionally hiding the section without unmounting the\r\n * declaration. Mirrors `IsVisible` on the other framework\r\n * components.\r\n */\r\n isVisible?: boolean;\r\n}\r\n\r\n/**\r\n * Branches off into a nested `<RecordContext>` for the record referenced\r\n * by a lookup column on the surrounding `<RecordContext>`. Mirrors\r\n * Blazor's `<LookupRecordContext>` — a thin wrapper that resolves the\r\n * lookup value, gates on its presence, and spawns the nested context.\r\n *\r\n * Edit components inside the lookup context bind to columns on the\r\n * LOOKED-UP record's table, not the parent's. Changes flow through the\r\n * surrounding save cascade automatically via the descendant-registry\r\n * plumbing every `<RecordContext>` already wires up — the nested record\r\n * registers with the outermost `<MainContext>` (or\r\n * `<RecordContext>`-as-MainContext) and its pending edits join the\r\n * single transactional `executeMultiple` batch on save.\r\n *\r\n * **Conditional render (matches Blazor's lazy pattern):** the nested\r\n * `<RecordContext>` is only instantiated when the lookup actually has\r\n * a value (non-null + non-empty-GUID + has `tableName`). Empty lookups\r\n * render nothing — avoids spinning up nested contexts, EditContext\r\n * validators, descendant registrations, etc. for every empty lookup\r\n * column on a form. The Blazor source carries an explicit perf note on\r\n * this: ~10 lookups × per-render lifecycle overhead added up to a\r\n * measurable freeze before the conditional render was added.\r\n *\r\n * ```tsx\r\n * <RecordContext table=\"account\" id={accountId}>\r\n * <Section>\r\n * <SectionColumn header={<Text>Account</Text>}>\r\n * <TextEdit columnName=\"name\" />\r\n * <TextEdit columnName=\"accountnumber\" />\r\n * </SectionColumn>\r\n *\r\n * <SectionColumn header={<Text>Primary contact</Text>}>\r\n * <LookupRecordContext columnName=\"primarycontactid\">\r\n * {/* These edits bind to the linked CONTACT, not the account *\\/}\r\n * <TextEdit columnName=\"firstname\" />\r\n * <TextEdit columnName=\"lastname\" />\r\n * <TextEdit columnName=\"emailaddress1\" type=\"email\" />\r\n * </LookupRecordContext>\r\n * </SectionColumn>\r\n * </Section>\r\n * </RecordContext>\r\n * ```\r\n *\r\n * Renders `null` when:\r\n * - the surrounding `<RecordContext>` hasn't loaded yet,\r\n * - the parent column doesn't carry a `LookupValue`,\r\n * - the lookup is the empty GUID (Dataverse's \"no value\" sentinel), or\r\n * - the lookup is missing its `tableName`.\r\n *\r\n * `<LookupRecordContext>` deliberately does NOT mirror Blazor's\r\n * `BaseColumnEdit` inheritance — the parent's column read-write loop\r\n * is owned by `useLookupColumn(columnName)` for consumers who want to\r\n * change the lookup value programmatically; `<LookupRecordContext>` is\r\n * pure read-then-branch.\r\n */\r\nexport function LookupRecordContext({\r\n columnName,\r\n onBeforeSave,\r\n children,\r\n isVisible = true,\r\n}: LookupRecordContextProps) {\r\n // Hook order: useRecordContext FIRST so the rules-of-hooks invariant\r\n // holds across the early-return branches below. The hook throws if\r\n // mounted outside a parent <RecordContext> — the user-facing diagnostic\r\n // matches the pattern <SubGrid>/<TextEdit>/etc. follow.\r\n const parent = useRecordContext();\r\n if (!isVisible) return null;\r\n\r\n const raw = parent.record?.properties?.[columnName] as\r\n | Schemas['ColumnValueBaseLookupValue']\r\n | undefined;\r\n\r\n // Three short-circuit conditions, each preserving the \"no DOM, no\r\n // nested lifecycle\" guarantee that makes empty-lookup-heavy forms\r\n // cheap:\r\n // - no raw value (lookup never set, or parent record still loading)\r\n // - empty-GUID sentinel (Dataverse round-trips cleared lookups as\r\n // Guid.Empty — both shapes mean \"no record to load\")\r\n // - missing tableName (defensive — without a target table the\r\n // nested useRecord call would have nothing to query)\r\n if (!raw?.value || raw.value === EMPTY_GUID || !raw.tableName) {\r\n return null;\r\n }\r\n\r\n return (\r\n <RecordContext\r\n table={raw.tableName}\r\n id={raw.value}\r\n {...(onBeforeSave && { onBeforeSave })}\r\n >\r\n {children}\r\n </RecordContext>\r\n );\r\n}\r\n","import type { components } from '@powerportalspro/core';\r\nimport { useRecordContext } from './record-context.js';\r\n\r\ntype ColumnMetadataBase = components['schemas']['ColumnMetadataBase'];\r\n\r\n/**\r\n * Returns the column metadata for the given column name from the parent\r\n * {@link RecordContext}'s table metadata. Returns `undefined` when:\r\n * - the table metadata hasn't loaded yet (the request is still in flight),\r\n * - the table metadata fetch failed,\r\n * - the column doesn't exist on the table.\r\n *\r\n * Edit components use this to auto-resolve defaults (`maxLength`, `min`,\r\n * `max`, `precision`, `required`, `readOnly`) when consumer-passed props are\r\n * absent. Pass-through props always win — same convention as Blazor's\r\n * `BaseColumnEdit.OnParametersSetAsync` (which only fills metadata-derived\r\n * defaults when the consumer hasn't set them explicitly).\r\n *\r\n * Throws if not used inside a `<RecordContext>`, since column metadata only\r\n * makes sense relative to a record's table.\r\n */\r\nexport function useColumnMetadata(columnName: string): ColumnMetadataBase | undefined {\r\n const { tableMetadata } = useRecordContext();\r\n return tableMetadata?.columns.find((c) => c.columnName === columnName);\r\n}\r\n","import { createContext, useCallback, useContext } from 'react';\r\nimport type { ReactNode } from 'react';\r\nimport { useLocalizer, type Localizer } from './localizer.js';\r\n\r\n/**\r\n * Outcome of a dialog interaction. `cancelled` is `true` when the user\r\n * dismissed the dialog (clicked the secondary button on a confirmation,\r\n * pressed Escape, clicked the backdrop, or otherwise rejected the action).\r\n *\r\n * Mirrors Blazor's `DialogResult` shape — kept as an object rather than a\r\n * naked boolean so we can extend with `data?` etc. when custom-content\r\n * dialogs land without a wire-breaking change.\r\n */\r\nexport interface DialogResult {\r\n /** True when the user dismissed/cancelled, false when they confirmed. */\r\n readonly cancelled: boolean;\r\n}\r\n\r\n/** Common shape for all single-button dialogs (success / warning / error / info). */\r\nexport interface DialogOptions {\r\n /** Dialog title. When omitted, the implementation supplies a sensible default for the variant. */\r\n title?: string;\r\n /** Label for the primary (and only) action button. Defaults to `\"OK\"`. */\r\n primaryText?: string;\r\n}\r\n\r\n/** Options for a confirmation dialog (two action buttons). */\r\nexport interface ConfirmationDialogOptions extends DialogOptions {\r\n /** Label for the primary (affirmative) button. Defaults to `\"Yes\"`. */\r\n primaryText?: string;\r\n /** Label for the secondary (negative) button. Defaults to `\"No\"`. */\r\n secondaryText?: string;\r\n}\r\n\r\n/**\r\n * Headless dialog service contract — equivalent to Blazor's `IDialogService`.\r\n * The framework calls these methods to surface success/warning/error/info\r\n * messages and confirmation prompts; consumers provide the concrete\r\n * implementation via {@link DialogServiceProvider} (typically the Fluent UI\r\n * implementation that ships with `@powerportalspro/react-fluent`, but any\r\n * dialog UI works as long as it satisfies this contract).\r\n *\r\n * **Message format.** The `message` argument is treated as HTML markup —\r\n * matching Blazor's `Microsoft.FluentUI.AspNetCore.Components.IDialogService`,\r\n * which renders messages via `MarkupString`. Tags like `<br/>`, `<strong>`,\r\n * `<em>` work; framework localization strings (e.g.\r\n * `app.messages.unsaved-changes`) rely on this. Don't pass unsanitized user\r\n * input — same constraint as Blazor.\r\n *\r\n * Each method returns a {@link DialogResult} so the same shape covers both\r\n * single-button (always `{ cancelled: false }` once dismissed) and\r\n * confirmation dialogs.\r\n */\r\nexport interface DialogService {\r\n showSuccessAsync(message: string, options?: DialogOptions): Promise<DialogResult>;\r\n showWarningAsync(message: string, options?: DialogOptions): Promise<DialogResult>;\r\n showErrorAsync(message: string, options?: DialogOptions): Promise<DialogResult>;\r\n showInfoAsync(message: string, options?: DialogOptions): Promise<DialogResult>;\r\n showConfirmationAsync(\r\n message: string,\r\n options?: ConfirmationDialogOptions,\r\n ): Promise<DialogResult>;\r\n}\r\n\r\nconst DialogServiceContext = createContext<DialogService | null>(null);\r\nDialogServiceContext.displayName = 'DialogService';\r\n\r\n/**\r\n * Reads the nearest ancestor {@link DialogServiceProvider}. Throws when called\r\n * outside one — the framework relies on the dialog service for unsaved-changes\r\n * warnings, error reporting, etc.; throwing surfaces missing-provider bugs at\r\n * mount time instead of producing silent no-op dialogs.\r\n */\r\nexport function useDialogService(): DialogService {\r\n const ctx = useContext(DialogServiceContext);\r\n if (!ctx) {\r\n throw new Error(\r\n 'useDialogService must be used inside a <DialogServiceProvider>. ' +\r\n 'Wrap your app in <FluentDialogProvider> from @powerportalspro/react-fluent ' +\r\n '(or your own implementation) so framework dialogs have somewhere to render.',\r\n );\r\n }\r\n return ctx;\r\n}\r\n\r\n/**\r\n * Provides a {@link DialogService} implementation to the subtree. UI libraries\r\n * that ship a `DialogService` (e.g. `@powerportalspro/react-fluent`'s\r\n * `<FluentDialogProvider>`) wrap their tree with this internally.\r\n */\r\nexport function DialogServiceProvider({\r\n value,\r\n children,\r\n}: {\r\n value: DialogService;\r\n children: ReactNode;\r\n}) {\r\n return <DialogServiceContext.Provider value={value}>{children}</DialogServiceContext.Provider>;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Framework-specific helpers — equivalent to Blazor's\r\n// `IPowerPortalsProDialogService` methods, but exposed as standalone functions\r\n// that take a `DialogService` rather than a second interface. Keeps the API\r\n// surface flat (one service contract, one context) and lets consumers compose\r\n// these into their own helpers without inheritance plumbing.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Prompts the user about unsaved changes. Returns `true` if the user wants to\r\n * continue (discarding their edits), `false` if they want to cancel and keep\r\n * editing. Mirrors Blazor's `IPowerPortalsProDialogService.WarnAboutUnsavedChangesAsync`.\r\n *\r\n * Used by the framework before navigation, refresh, and any other operation\r\n * that would discard pending edits. Application code can also call it directly\r\n * for custom flows.\r\n *\r\n * Strings come from the supplied {@link Localizer} (default English from\r\n * the framework's bundled JSON). Keys: `app.messages.unsaved-changes` (body),\r\n * `app.messages.unsaved-changes-title` (title), `app.buttons.yes.label` /\r\n * `app.buttons.no.label` (button labels).\r\n */\r\nexport async function warnAboutUnsavedChangesAsync(\r\n dialog: DialogService,\r\n localizer: Localizer,\r\n): Promise<boolean> {\r\n const result = await dialog.showConfirmationAsync(localizer.t('app.messages.unsaved-changes'), {\r\n title: localizer.t('app.messages.unsaved-changes-title'),\r\n primaryText: localizer.t('app.buttons.yes.label'),\r\n secondaryText: localizer.t('app.buttons.no.label'),\r\n });\r\n return !result.cancelled;\r\n}\r\n\r\n/**\r\n * Hook variant of {@link warnAboutUnsavedChangesAsync}. Returns a stable\r\n * function bound to the current dialog service + localizer from context.\r\n */\r\nexport function useWarnAboutUnsavedChanges(): () => Promise<boolean> {\r\n const dialog = useDialogService();\r\n const localizer = useLocalizer();\r\n return useCallback(\r\n () => warnAboutUnsavedChangesAsync(dialog, localizer),\r\n [dialog, localizer],\r\n );\r\n}\r\n","import { useBeforeUnloadWhen } from './internal/use-before-unload-when.js';\r\nimport { useWarnAboutUnsavedChanges } from './dialog-service.js';\r\nimport { useMainContext } from './main-context.js';\r\nimport { useIsAnyDirty } from './unsaved-changes-registry.js';\r\n\r\n/**\r\n * Standalone hook that registers a `beforeunload` handler while the nearest\r\n * `MainContext` / `RecordContext` reports `isDirty`.\r\n *\r\n * **You usually don't need this.** Both `<MainContext>` and `<RecordContext>`\r\n * register the warning automatically when they're the top-most context on\r\n * the page (controlled via the `warnOnUnsavedChanges` prop, default `true`).\r\n * This hook is exported for cases where you want to wire the warning to a\r\n * dirty signal that doesn't come from a MainContext (e.g. a `react-hook-form`\r\n * `formState.isDirty`) or from a non-top-most context.\r\n *\r\n * **What this covers:** browser-level navigation — closing the tab, hitting\r\n * the back button, refreshing the page, typing a new URL. The browser's\r\n * native prompt fires; the message it shows is browser-controlled (you\r\n * cannot customize it — security restriction since Chrome 60+).\r\n *\r\n * **What this does NOT cover:** in-app route changes via `react-router`'s\r\n * `<Link>` or `navigate()`. Those don't trigger `beforeunload` because the\r\n * page never actually unloads. For soft-nav blocking, use\r\n * {@link useUnsavedChangesGuard} from any router's blocker callback (see\r\n * its JSDoc for canonical patterns for react-router, TanStack Router,\r\n * Wouter, etc.).\r\n */\r\nexport function useUnsavedChangesWarning(): void {\r\n const ctx = useMainContext();\r\n const isDirty = ctx?.isDirty ?? false;\r\n useBeforeUnloadWhen(isDirty);\r\n}\r\n\r\n/**\r\n * Headless controller for building a router blocker that prompts the user\r\n * before discarding unsaved edits. Bundles the two primitives any soft-nav\r\n * guard needs:\r\n *\r\n * - `isAnyDirty` — reactive boolean tracking whether any registered\r\n * top-most {@link MainContext} / {@link RecordContext} on the page is\r\n * dirty (read-through to {@link useIsAnyDirty}).\r\n * - `warn` — async function that opens the framework confirmation dialog\r\n * and resolves `true` if the user confirmed discarding the edits,\r\n * `false` if they cancelled (read-through to\r\n * {@link useWarnAboutUnsavedChanges}).\r\n *\r\n * Router-agnostic — this package has zero router dependencies. Pair it\r\n * with whichever router blocker your app uses:\r\n *\r\n * **react-router-dom (data router):** use the pre-built\r\n * `<RouterUnsavedChangesGuard>` from `@powerportalspro/react-fluent` — it\r\n * wraps this hook + react-router's `useBlocker`. Or roll your own:\r\n *\r\n * ```tsx\r\n * import { useBlocker } from 'react-router-dom';\r\n * function MyGuard() {\r\n * const guard = useUnsavedChangesGuard();\r\n * const blocker = useBlocker(\r\n * ({ currentLocation, nextLocation }) =>\r\n * guard.isAnyDirty &&\r\n * currentLocation.pathname !== nextLocation.pathname,\r\n * );\r\n * useEffect(() => {\r\n * if (blocker.state !== 'blocked') return;\r\n * let cancelled = false;\r\n * (async () => {\r\n * const proceed = await guard.warn();\r\n * if (cancelled) return;\r\n * if (proceed) blocker.proceed();\r\n * else blocker.reset();\r\n * })();\r\n * return () => { cancelled = true; };\r\n * }, [blocker, guard]);\r\n * return null;\r\n * }\r\n * ```\r\n *\r\n * **TanStack Router:** use `useBlocker({ blockerFn })`. The blocker fn\r\n * returns `true` to allow the navigation, `false` to block. Pump the\r\n * `warn` result through inverted:\r\n *\r\n * ```tsx\r\n * import { useBlocker } from '@tanstack/react-router';\r\n * function MyGuard() {\r\n * const guard = useUnsavedChangesGuard();\r\n * useBlocker({\r\n * blockerFn: async () => {\r\n * if (!guard.isAnyDirty) return true; // allow\r\n * return await guard.warn(); // user choice\r\n * },\r\n * });\r\n * return null;\r\n * }\r\n * ```\r\n *\r\n * **Wouter:** doesn't ship a blocker API. Hand-roll by intercepting\r\n * `<Link>` clicks at the app shell — call `event.preventDefault()` and\r\n * route through `guard.warn()` before calling `navigate(to)`.\r\n *\r\n * **Next.js App Router:** `router.events` is gone in the App Router; the\r\n * community pattern is to intercept `<Link>` clicks similar to Wouter.\r\n * Same flow with `guard.warn()`.\r\n *\r\n * Same-pathname navigations (search-param tweaks, `replace: true` to the\r\n * current URL) should not be blocked — the user isn't actually leaving\r\n * the page. The react-router-dom guard above filters those out via the\r\n * `currentLocation.pathname !== nextLocation.pathname` check; the TanStack\r\n * blocker fn can do the equivalent using its passed-in location args.\r\n */\r\nexport interface UnsavedChangesGuardController {\r\n /**\r\n * Reactive — `true` when at least one top-most\r\n * {@link MainContext} / {@link RecordContext} registered with the page's\r\n * {@link UnsavedChangesRegistry} reports dirty. Subscribes via\r\n * {@link useSyncExternalStore} so it re-renders the calling component\r\n * exactly when the aggregate flips.\r\n */\r\n readonly isAnyDirty: boolean;\r\n /**\r\n * Opens the framework's \"discard your changes?\" confirmation dialog\r\n * and resolves `true` if the user confirmed proceeding (discarding\r\n * pending edits), `false` if they cancelled (staying on the page).\r\n * Stable identity across renders when the underlying dialog service\r\n * / localizer haven't changed, so it's safe to put in effect deps.\r\n */\r\n readonly warn: () => Promise<boolean>;\r\n}\r\n\r\nexport function useUnsavedChangesGuard(): UnsavedChangesGuardController {\r\n const isAnyDirty = useIsAnyDirty();\r\n const warn = useWarnAboutUnsavedChanges();\r\n return { isAnyDirty, warn };\r\n}\r\n","export const VERSION = '0.1.0';\n\nexport {\n AuthStatus,\n PowerPortalsProContext,\n type AuthState,\n type PowerPortalsProContextValue,\n} from './context.js';\nexport { PowerPortalsProProvider, type PowerPortalsProProviderProps } from './provider.js';\n\nexport { usePowerPortalsPro } from './use-power-portals-pro.js';\nexport { useAuth, type UseAuthResult } from './use-auth.js';\nexport { AuthorizedView, type AuthorizedViewProps } from './authorized-view.js';\n\nexport { useRecord, type UseRecordOptions } from './use-record.js';\nexport { useFetchRecords, type UseFetchRecordsOptions } from './use-fetch-records.js';\nexport { useGridData, type UseGridDataOptions } from './use-grid-data.js';\nexport {\n useViewDataSource,\n type ViewDataSource,\n type ViewDataSourceHighlight,\n type UseViewDataSourceOptions,\n} from './use-view-data-source.js';\nexport { useTableMetadata, type UseTableMetadataOptions } from './use-table-metadata.js';\nexport { useTablePermissions, type UseTablePermissionsOptions } from './use-table-permissions.js';\nexport { useViewMetadata, type UseViewMetadataOptions } from './use-view-metadata.js';\nexport { useViewsForTable, type UseViewsForTableOptions } from './use-views-for-table.js';\nexport {\n useEnvironmentFileSettings,\n type UseEnvironmentFileSettingsOptions,\n} from './use-environment-file-settings.js';\nexport {\n useSupportedLocales,\n type UseSupportedLocalesOptions,\n} from './use-supported-locales.js';\nexport { useQueryParam } from './use-query-param.js';\n// Client-side metadata caches. Consumers can swap the default in-memory\n// implementations for custom ones (e.g. localStorage-backed for cross-\n// session persistence) via the provider's `tableMetadataCache` /\n// `viewMetadataCache` props. Mirrors Blazor's `ITableMetadataCache` /\n// `IViewMetadataCache` injection points.\nexport type {\n EnvironmentFileSettingsCache,\n TableMetadataCache,\n ViewMetadataCache,\n ViewsForTableCache,\n} from './caches/types.js';\nexport {\n InMemoryEnvironmentFileSettingsCache,\n InMemoryTableMetadataCache,\n InMemoryViewMetadataCache,\n InMemoryViewsForTableCache,\n} from './caches/in-memory-cache.js';\n\nexport { QueryStatus, type QueryResult } from './internal/use-async-resource.js';\n\nexport {\n MainContext,\n MainContextProvider,\n useMainContext,\n type MainContextProps,\n type MainContextValue,\n type MainContextDescendant,\n} from './main-context.js';\n\nexport {\n RecordContext,\n useRecordContext,\n useRecordContextOptional,\n type RecordContextProps,\n type RecordContextValue,\n} from './record-context.js';\n\n// `<LookupRecordContext>` — branches off into a nested `<RecordContext>`\n// for the record referenced by a lookup column on the parent context.\n// Lets consumers inline-edit the looked-up record's fields with changes\n// folded into the parent's transactional save. Mirrors Blazor's\n// `<LookupRecordContext>` and its lazy \"only spin up the nested context\n// when there's actually a target record\" perf pattern.\nexport {\n LookupRecordContext,\n type LookupRecordContextProps,\n} from './lookup-record-context.js';\n\nexport { useColumnMetadata } from './use-column-metadata.js';\n\nexport {\n DialogServiceProvider,\n useDialogService,\n warnAboutUnsavedChangesAsync,\n useWarnAboutUnsavedChanges,\n type DialogService,\n type DialogResult,\n type DialogOptions,\n type ConfirmationDialogOptions,\n} from './dialog-service.js';\n\nexport {\n OverlayServiceProvider,\n useOverlayService,\n useOverlayForTarget,\n useOverlayServiceState,\n useRunWithOverlay,\n runWithOverlayAsync,\n type OverlayService,\n type OverlayInstance,\n type OverlayOptions,\n} from './overlay-service.js';\n\nexport {\n useUnsavedChangesWarning,\n useUnsavedChangesGuard,\n type UnsavedChangesGuardController,\n} from './unsaved-changes-warning.js';\nexport {\n UnsavedChangesRegistryProvider,\n useUnsavedChangesRegistry,\n useIsAnyDirty,\n type UnsavedChangesRegistry,\n type UnsavedChangesReporter,\n type UnsavedChangesRegistryProviderProps,\n} from './unsaved-changes-registry.js';\n\nexport {\n ValidationProvider,\n useValidationContext,\n useColumnValidationErrors,\n type ValidationContextValue,\n type ColumnValidator,\n} from './validation-context.js';\n\nexport {\n LocalizerProvider,\n DefaultLocalizerProvider,\n LocaleProvider,\n useLocalizer,\n useT,\n usePrefixedT,\n useLocale,\n useLocalization,\n useLocalizationLoaded,\n useLocalizationPrefetcher,\n createLocalizer,\n flattenStrings,\n interpolate,\n resolveLocalizationUrl,\n detectLocale,\n getAspNetCultureCookie,\n setAspNetCultureCookie,\n getLocaleFromUrlPath,\n ASP_NET_CULTURE_COOKIE,\n DEFAULT_URL_LOCALE_PATTERN,\n type Localizer,\n type LocalizationPrefetcher,\n type LocaleState,\n type LocaleProviderProps,\n type CreateLocalizerOptions,\n type DefaultLocalizerProviderProps,\n} from './localizer.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/masking/reformat.ts","../src/masking/use-masked-text-field.ts","../src/context.ts","../src/caches/in-memory-cache.ts","../src/use-power-portals-pro.ts","../src/localizer.tsx","../src/unsaved-changes-registry.tsx","../src/provider.tsx","../src/use-auth.ts","../src/authorized-view.tsx","../src/internal/use-async-resource.ts","../src/use-record.ts","../src/use-fetch-records.ts","../src/use-grid-data.ts","../src/use-view-data-source.ts","../src/use-table-metadata.ts","../src/use-table-permissions.ts","../src/use-view-metadata.ts","../src/use-views-for-table.ts","../src/use-organization-settings.ts","../src/use-supported-locales.ts","../src/use-query-param.ts","../src/internal/use-before-unload-when.ts","../src/main-context.tsx","../src/overlay-service.tsx","../src/validation-context.tsx","../src/record-context.tsx","../src/lookup-record-context.tsx","../src/use-column-metadata.ts","../src/dialog-service.tsx","../src/unsaved-changes-warning.ts","../src/index.ts"],"names":["useRef","useState","useEffect","useLayoutEffect","useCallback","createContext","useContext","useMemo","useSyncExternalStore","jsx","prefixes","Transport","AuthClient","PowerPortalsProClient","LoginResult","SwitchIdentityResult","Fragment","resolvers","useReducer","useId","value","next"],"mappings":"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","file":"index.cjs","sourcesContent":["// Caret-safe masking primitives. These pure functions reformat an input value\n// while preserving the user's caret position — re-running the mask on already-\n// masked text is idempotent, and inserted literals / separators never trap the\n// cursor as it moves through the field. The DOM-attach lifecycle lives in the\n// React hook (`useMaskedTextField`), leaving this module pure and trivially\n// testable.\n\n/**\n * Masking strategy applied to a masked text field.\n *\n * Hybrid const-object + literal-union — same pattern other enums in\n * the package follow (e.g. `GridButtonBehavior`).\n */\nexport const MaskMode = {\n /** No masking — value passes through unchanged. */\n None: 'none',\n /**\n * Per-character allow filter. Characters that don't match\n * {@link MaskOptions.allowedPattern} are dropped as they're typed or pasted.\n */\n Regex: 'regex',\n /**\n * Slotted template (see {@link MaskOptions.pattern}) such as `(000) 000-0000`.\n * `0` = digit, `A` = letter, `*` = alphanumeric; every other character is a\n * literal inserted automatically.\n */\n Pattern: 'pattern',\n /**\n * Live numeric grouping driven by {@link MaskOptions.numeric} — thousands\n * separators, an optional decimal portion, and an optional leading sign.\n */\n Numeric: 'numeric',\n} as const;\nexport type MaskMode = (typeof MaskMode)[keyof typeof MaskMode];\n\n/**\n * Numeric masking options for {@link MaskMode.Numeric}. All separators are\n * supplied by the caller so the field stays culture-correct (a German consumer\n * gets `.` groups and `,` decimals).\n */\nexport interface NumericOptions {\n /** Character used to separate the whole and fractional parts (e.g. `.`). */\n decimalSeparator: string;\n /** Character inserted between thousands groups (e.g. `,`). Empty disables grouping. */\n groupSeparator: string;\n /** Whether a single leading minus sign is accepted. */\n allowNegative: boolean;\n /** Maximum fractional digits, or `null` for unlimited. */\n maxDecimals: number | null;\n /** Whether a decimal portion is allowed at all (integers set this false). */\n allowDecimal: boolean;\n}\n\n/** Configuration for a single reformat call. */\nexport interface MaskOptions {\n mode: MaskMode;\n /** Regex mode: a per-character allow test, e.g. `[0-9.]`. Chars failing it are dropped. */\n allowedPattern: string | null;\n /**\n * Pattern mode: a slotted template, e.g. `(000) 000-0000`. `0`=digit, `A`=letter,\n * `*`=alphanumeric; every other character is a literal inserted automatically.\n */\n pattern: string | null;\n /** Numeric mode: separators, sign, decimal options. */\n numeric: NumericOptions | null;\n /**\n * When true, the value reported by the consumer's `onValueChange` is the\n * masked display string (with literals/separators included); when false, the\n * unmasked logical value (e.g. `5551234567` rather than `(555) 123-4567`).\n * Round-trips correctly either way because reformat is idempotent.\n */\n valueIncludesMask: boolean;\n}\n\n/** Output of a single reformat call. */\nexport interface ReformatResult {\n /** The string to display in the input (masked / grouped). */\n display: string;\n /**\n * The logical value reported back to the consumer (group separators stripped\n * for numeric; mask literals stripped for pattern). Drives the bound model.\n */\n raw: string;\n /** Caret offset into `display` after reformatting. */\n caret: number;\n}\n\nconst DIGIT = /[0-9]/;\nconst LETTER = /[a-zA-Z]/;\nconst ALNUM = /[a-zA-Z0-9]/;\n\nfunction isPatternLiteral(token: string): boolean {\n return token !== '0' && token !== 'A' && token !== '*';\n}\n\nfunction patternTokenMatches(token: string, ch: string): boolean {\n if (token === '0') return DIGIT.test(ch);\n if (token === 'A') return LETTER.test(ch);\n if (token === '*') return ALNUM.test(ch);\n return false;\n}\n\n// Count the \"significant\" characters (those that survive formatting) appearing\n// strictly before `caret` in `source`. Used to re-anchor the caret after a\n// reformat: the cursor is placed just past the same number of significant chars\n// in the formatted output, so inserted separators/literals never trap it.\nfunction countSignificant(\n source: string,\n caret: number,\n isSignificant: (c: string) => boolean,\n): number {\n let n = 0;\n for (let i = 0; i < caret && i < source.length; i++) {\n // charAt() returns '' for out-of-bounds — fine; isSignificant('') is false.\n if (isSignificant(source.charAt(i))) n++;\n }\n return n;\n}\n\n// Place the caret just after the `target`-th significant char in `display`.\nfunction caretAfterSignificant(\n display: string,\n target: number,\n isSignificant: (c: string) => boolean,\n): number {\n if (target <= 0) {\n // Sit before any leading literals (e.g. a \"(\" in a phone mask) so the\n // first keystroke lands in the first real slot.\n let i = 0;\n while (i < display.length && !isSignificant(display.charAt(i))) i++;\n return i;\n }\n let seen = 0;\n for (let i = 0; i < display.length; i++) {\n if (isSignificant(display.charAt(i))) {\n seen++;\n if (seen === target) return i + 1;\n }\n }\n return display.length;\n}\n\nfunction reformatNumeric(value: string, caret: number, o: NumericOptions): ReformatResult {\n const sigChars = (c: string) =>\n DIGIT.test(c) || c === o.decimalSeparator || (o.allowNegative && c === '-');\n const sigBefore = countSignificant(value, caret, sigChars);\n\n // A single leading sign, only when negatives are permitted.\n const negative = o.allowNegative && value.trimStart().startsWith('-');\n\n // Keep digits and (at most) the first decimal separator.\n let intPart = '';\n let fracPart = '';\n let sawDecimal = false;\n for (const ch of value) {\n if (DIGIT.test(ch)) {\n if (sawDecimal) fracPart += ch;\n else intPart += ch;\n } else if (o.allowDecimal && ch === o.decimalSeparator && !sawDecimal) {\n sawDecimal = true;\n }\n }\n\n // Drop leading zeros on the integer part, but keep a lone \"0\".\n intPart = intPart.replace(/^0+(?=\\d)/, '');\n\n if (o.maxDecimals !== null && fracPart.length > o.maxDecimals) {\n fracPart = fracPart.slice(0, o.maxDecimals);\n }\n\n // Group the integer part every three digits.\n const grouped = o.groupSeparator\n ? intPart.replace(/\\B(?=(\\d{3})+(?!\\d))/g, o.groupSeparator)\n : intPart;\n\n const sign = negative ? '-' : '';\n let display = sign + grouped;\n let raw = sign + intPart;\n if (sawDecimal) {\n display += o.decimalSeparator + fracPart;\n raw += o.decimalSeparator + fracPart;\n }\n\n const newCaret = caretAfterSignificant(display, sigBefore, sigChars);\n return { display, raw, caret: newCaret };\n}\n\nfunction reformatPattern(value: string, caret: number, pattern: string): ReformatResult {\n // Significant input = chars that can fill a slot (drop existing literals so\n // re-running the mask on already-masked text is idempotent).\n const sourceChars: string[] = [];\n for (const ch of value) {\n if (DIGIT.test(ch) || LETTER.test(ch)) sourceChars.push(ch);\n }\n const sigBefore = countSignificant(value, caret, (c) => DIGIT.test(c) || LETTER.test(c));\n\n let display = '';\n let raw = '';\n let si = 0;\n for (let pi = 0; pi < pattern.length && si <= sourceChars.length; pi++) {\n // Both pattern[pi] and sourceChars[si] are bounds-checked by the loop /\n // while guards above; non-null assertions cover noUncheckedIndexedAccess.\n const token = pattern[pi]!;\n if (isPatternLiteral(token)) {\n // Only emit the literal once we have a source char to anchor it\n // (avoids dangling trailing literals while typing).\n if (si < sourceChars.length) display += token;\n continue;\n }\n // Advance through source chars until one matches this slot's token.\n while (si < sourceChars.length && !patternTokenMatches(token, sourceChars[si]!)) si++;\n if (si < sourceChars.length) {\n display += sourceChars[si]!;\n raw += sourceChars[si]!;\n si++;\n } else {\n break;\n }\n }\n\n const newCaret = caretAfterSignificant(display, sigBefore, (c) => DIGIT.test(c) || LETTER.test(c));\n return { display, raw, caret: newCaret };\n}\n\nfunction reformatRegex(value: string, caret: number, allowedPattern: string): ReformatResult {\n let re: RegExp;\n try {\n re = new RegExp(allowedPattern);\n } catch {\n // A bad pattern shouldn't brick typing — pass the value through.\n return { display: value, raw: value, caret };\n }\n const keep = (c: string) => re.test(c);\n const sigBefore = countSignificant(value, caret, keep);\n let display = '';\n for (const ch of value) {\n if (keep(ch)) display += ch;\n }\n const newCaret = caretAfterSignificant(display, sigBefore, keep);\n return { display, raw: display, caret: newCaret };\n}\n\n/**\n * Reformat `value` according to `options`. Pure — same input always yields the\n * same `{ display, raw, caret }`. Caller is responsible for actually writing\n * the new display string into the input and calling `setSelectionRange(caret,\n * caret)`. {@link useMaskedTextField} does that wiring for React consumers.\n */\nexport function reformat(value: string, caret: number, options: MaskOptions): ReformatResult {\n switch (options.mode) {\n case MaskMode.Numeric:\n return options.numeric\n ? reformatNumeric(value, caret, options.numeric)\n : { display: value, raw: value, caret };\n case MaskMode.Pattern:\n return options.pattern\n ? reformatPattern(value, caret, options.pattern)\n : { display: value, raw: value, caret };\n case MaskMode.Regex:\n return options.allowedPattern\n ? reformatRegex(value, caret, options.allowedPattern)\n : { display: value, raw: value, caret };\n default:\n return { display: value, raw: value, caret };\n }\n}\n","import {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type ChangeEvent,\n} from 'react';\nimport {\n MaskMode,\n type MaskOptions,\n type NumericOptions,\n reformat,\n} from './reformat.js';\n\n// **Architecture note — why \"controlled\" and not \"JS owns input.value\"**:\n// Fluent UI v9's `<Input>` uses `useControllableState` to track value, and\n// React's reconciler resets `input.value` from that state on every render.\n// A \"JS writes input.value + native input listener\" design fights with the\n// reconciler — the next React render clobbers the masked display back to\n// whatever the user last typed (the unmasked input), and the rendered\n// value disappears.\n//\n// So the hook is fully controlled: we return a `displayValue` (the masked\n// string) and an `onChange` handler the consumer wires onto Fluent's\n// `<Input value onChange>`. React re-renders with the new `displayValue`\n// after every keystroke, and a `useLayoutEffect` restores the caret\n// position before the browser paints — caret-safe under React's render\n// cycle.\n//\n// External value pushes (a sibling editor bound to the same column writing\n// the value) flow through the standard `value` prop — gated on `!focused`\n// so an in-progress edit isn't overwritten.\n\nexport interface UseMaskedTextFieldOptions {\n // Optional fields below explicitly include `| undefined` in their types so\n // consumers compiled with `exactOptionalPropertyTypes: true` can spread\n // their own optional props through without hitting a \"Type 'undefined' is\n // not assignable\" error. Functionally the same as `field?: T` — the field\n // may be absent OR explicitly undefined.\n\n /**\n * Current logical value (unmasked, the way it should be stored on the\n * bound model). The hook reformats this into the masked `displayValue` it\n * returns. Pass `null` / `undefined` for empty.\n */\n value: string | null | undefined;\n\n /**\n * Fires on every keystroke that changes the reported value. Receives the\n * unmasked logical value (e.g. `5551234567`) when {@link valueIncludesMask}\n * is `false`, the masked display string (e.g. `(555) 123-4567`) when `true`.\n */\n onValueChange: (raw: string | null) => void;\n\n /** Masking strategy. Defaults to {@link MaskMode.None}. */\n mode?: MaskMode | undefined;\n\n /** Slotted template for {@link MaskMode.Pattern} (e.g. `(000) 000-0000`). */\n pattern?: string | null | undefined;\n\n /** Per-character allow regex for {@link MaskMode.Regex} (e.g. `[A-Za-z]`). */\n allowedPattern?: string | null | undefined;\n\n /** Numeric options for {@link MaskMode.Numeric}. */\n numeric?: NumericOptions | null | undefined;\n\n /**\n * When `true`, the value reported by `onValueChange` is the masked display\n * string (with literals/separators); when `false` (default), the unmasked\n * logical value. Either form round-trips correctly because reformat is\n * idempotent.\n */\n valueIncludesMask?: boolean | undefined;\n}\n\nexport interface UseMaskedTextFieldReturn {\n /**\n * Masked display string. Pass this to the consumer input's `value` prop\n * the hook re-renders it via React state on every keystroke + external\n * value push.\n */\n displayValue: string;\n\n /**\n * Input change handler — matches Fluent v9 `<Input onChange>`'s\n * `(event, data: { value })` shape. Reformats the typed value, updates\n * `displayValue`, and reports the logical value via `onValueChange`.\n */\n onChange: (\n event: ChangeEvent<HTMLInputElement>,\n data: { value: string },\n ) => void;\n\n /**\n * Callback ref — attach to the underlying `<input>`. The hook needs the\n * element reference both for caret-position bookkeeping and for the\n * focus / blur listeners that gate external value pushes.\n */\n inputRef: (el: HTMLInputElement | null) => void;\n\n /**\n * Push a value into the field even while it has focus, parking the caret\n * at the end. Used by an owning editor's spinner / stepper controls.\n */\n forceValue: (value: string | null) => void;\n}\n\nfunction computeDisplay(value: string | null | undefined, options: MaskOptions): string {\n if (value === null || value === undefined || value === '') return '';\n return reformat(value, value.length, options).display;\n}\n\nfunction computeReported(value: string | null | undefined, options: MaskOptions): string {\n if (value === null || value === undefined || value === '') return '';\n const r = reformat(value, value.length, options);\n return options.valueIncludesMask ? r.display : r.raw;\n}\n\n/**\n * React hook that wires caret-safe masking onto a controlled input. Returns\n * `displayValue` + `onChange` to spread onto a Fluent v9 `<Input>` (or any\n * controlled `<input>`), plus an `inputRef` callback to attach to the\n * underlying element and an imperative `forceValue` helper.\n */\nexport function useMaskedTextField(opts: UseMaskedTextFieldOptions): UseMaskedTextFieldReturn {\n const {\n value,\n onValueChange,\n mode = MaskMode.None,\n pattern = null,\n allowedPattern = null,\n numeric = null,\n valueIncludesMask = false,\n } = opts;\n\n // Pin live state in refs so the long-lived input/change/forceValue\n // callbacks always read the freshest values without re-binding every render.\n const optionsRef = useRef<MaskOptions>({\n mode,\n pattern,\n allowedPattern,\n numeric,\n valueIncludesMask,\n });\n optionsRef.current = { mode, pattern, allowedPattern, numeric, valueIncludesMask };\n\n const onValueChangeRef = useRef(onValueChange);\n onValueChangeRef.current = onValueChange;\n\n const elementRef = useRef<HTMLInputElement | null>(null);\n const focusedRef = useRef(false);\n // Whether the user has typed since the most recent focus event. Reset on\n // focus, set on every keystroke. The value-sync effect uses this to allow\n // the parent's reformatted value to flow into the display ONCE on focus\n // (covers the \"field switches from a padded display to an editable display\n // when focused\" pattern NumberEdit/MoneyEdit use) without clobbering the\n // caret mid-edit on subsequent renders.\n const hasInputSinceFocusRef = useRef(false);\n // What we last reported via onValueChange — used to coalesce idempotent\n // reformats (re-running the mask on already-masked text yields the same\n // logical value, no need to fire the callback again).\n const lastReportedRef = useRef<string | null>(null);\n // Caret position the next render should restore. Set by `onChange` (the\n // user just typed something — caret was at `result.caret` in the new\n // display) and consumed by `useLayoutEffect` after React commits.\n const pendingCaretRef = useRef<number | null>(null);\n\n // Seed the display from the external value on first render. After that,\n // the value-sync effect below keeps it in step with `value` changes.\n const [displayValue, setDisplayValue] = useState(() =>\n computeDisplay(value, optionsRef.current),\n );\n\n // External value push: when `value` changes, mirror it into the display.\n // Two skip cases:\n // * Not focused → always update (standard sibling-component-write case).\n // * Focused but no input has happened since focus → also update. This is\n // the \"just got focus, parent switched its display formula\" case (e.g.\n // NumberEdit/MoneyEdit unpad on focus so the user can type past their\n // padded zeros). The single focus-transition update isn't an in-progress\n // edit so we can't clobber a caret that isn't yet in motion.\n // * Focused AND already received input → skip; user is mid-edit and we\n // can't safely overwrite the display under their cursor.\n useEffect(() => {\n if (focusedRef.current && hasInputSinceFocusRef.current) return;\n const next = computeDisplay(value, optionsRef.current);\n setDisplayValue(next);\n lastReportedRef.current = computeReported(value, optionsRef.current);\n }, [value]);\n\n // Re-apply when mask options change so the field picks up the new shape\n // (e.g. consumer flips from phone pattern to SSN). Same focus gating as\n // the value-sync effect above.\n useEffect(() => {\n if (focusedRef.current && hasInputSinceFocusRef.current) return;\n const next = computeDisplay(value, optionsRef.current);\n setDisplayValue(next);\n lastReportedRef.current = computeReported(value, optionsRef.current);\n // `value` is intentionally excluded from deps here — the value-sync\n // effect above handles `value` changes. This effect fires only when\n // mask options change, re-applying the (unchanged) value through the\n // (changed) options.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [mode, pattern, allowedPattern, numeric, valueIncludesMask]);\n\n // Restore caret position after every commit when the most recent\n // `onChange` recorded a target. useLayoutEffect runs synchronously\n // between the DOM update and the browser paint, so the user never sees\n // a momentary caret jump to the end before we restore it.\n useLayoutEffect(() => {\n const target = pendingCaretRef.current;\n if (target === null) return;\n const input = elementRef.current;\n if (!input) return;\n try {\n input.setSelectionRange(target, target);\n } catch {\n /* detached / unsupported input type — fine to ignore */\n }\n pendingCaretRef.current = null;\n });\n\n const onChange = useCallback(\n (_event: ChangeEvent<HTMLInputElement>, data: { value: string }): void => {\n // Flag that the user has begun editing — gates the value-sync effect\n // above so subsequent parent value updates don't clobber the caret.\n hasInputSinceFocusRef.current = true;\n const o = optionsRef.current;\n const input = elementRef.current;\n // Anchor the caret-tracking at the user's current cursor position. When\n // the input isn't yet in the DOM (StrictMode double-invoke quirks) fall\n // back to \"end of typed value\".\n const caret = input?.selectionStart ?? data.value.length;\n const result = reformat(data.value, caret, o);\n setDisplayValue(result.display);\n pendingCaretRef.current = result.caret;\n\n const reported = o.valueIncludesMask ? result.display : result.raw;\n const empty = reported === '';\n const next = empty ? null : reported;\n if (reported !== lastReportedRef.current) {\n lastReportedRef.current = reported;\n onValueChangeRef.current(next);\n }\n },\n [],\n );\n\n const inputRef = useCallback((el: HTMLInputElement | null) => {\n // Detach listeners from any prior element before reassigning the ref\n // mount / unmount cycles in StrictMode or when the component remounts\n // (e.g. demo's `key=` swap on preset change) need clean teardown.\n const prev = elementRef.current;\n if (prev && prev !== el) {\n prev.removeEventListener('focus', handleFocusRef.current);\n prev.removeEventListener('blur', handleBlurRef.current);\n }\n elementRef.current = el;\n if (el) {\n el.addEventListener('focus', handleFocusRef.current);\n el.addEventListener('blur', handleBlurRef.current);\n }\n }, []);\n\n // Focus / blur handlers pinned in refs so the callback ref above\n // (stable identity) can read the freshest closure.\n const handleFocusRef = useRef<() => void>(() => {});\n handleFocusRef.current = () => {\n focusedRef.current = true;\n // Reset the \"has user typed since focus\" flag so the value-sync effect\n // (above) can let one focus-transition value update through before\n // typing starts. Without this, NumberEdit/MoneyEdit's switch from\n // padded display to unpadded edit-form on focus would never reach the\n // input — focus would set `focusedRef=true`, the effect would always\n // skip, and the user would still see the padded value.\n hasInputSinceFocusRef.current = false;\n };\n const handleBlurRef = useRef<() => void>(() => {});\n handleBlurRef.current = () => {\n focusedRef.current = false;\n // Only re-sync display from the bound value when the user did NOT type\n // during this focus session.\n // * No typing → cover the \"sibling component wrote during the focus\"\n // case: the value-sync effect skipped while focused, so on blur we\n // pull the (possibly externally-updated) value through reformat.\n // * Did type → the user's display IS the source of truth, even when\n // it carries a trailing-zero shape the bound number doesn't (typing\n // \"5.50\" stores value=5.5 because numbers drop trailing zeros, but\n // re-reformatting 5.5 here would give \"5.5\" — clobbering the user's\n // \"5.50\". The value-sync effect on the next render — after the\n // parent re-renders with `isFocused=false` and passes a padded\n // value like \"5.50\" — reformats to the polished display.\n if (!hasInputSinceFocusRef.current) {\n const next = computeDisplay(value, optionsRef.current);\n if (next !== displayValue) {\n setDisplayValue(next);\n lastReportedRef.current = computeReported(value, optionsRef.current);\n }\n }\n };\n\n const forceValue = useCallback((newValue: string | null): void => {\n const o = optionsRef.current;\n if (newValue === null || newValue === '') {\n setDisplayValue('');\n lastReportedRef.current = '';\n pendingCaretRef.current = 0;\n onValueChangeRef.current(null);\n return;\n }\n const result = reformat(newValue, newValue.length, o);\n setDisplayValue(result.display);\n // Park caret at the end of the new display — spinner / stepper consumers\n // are pushing a discrete value, not preserving cursor context.\n pendingCaretRef.current = result.display.length;\n const reported = o.valueIncludesMask ? result.display : result.raw;\n lastReportedRef.current = reported;\n onValueChangeRef.current(reported === '' ? null : reported);\n }, []);\n\n return { displayValue, onChange, inputRef, forceValue };\n}\n","import type {\n AuthClient,\n CurrentUserInfo,\n LoginRequest,\n LoginResponse,\n PowerPortalsProClient,\n SwitchIdentityResponse,\n Transport,\n} from '@powerportalspro/core';\nimport { createContext } from 'react';\nimport type {\n OrganizationSettingsCache,\n TableMetadataCache,\n ViewMetadataCache,\n ViewsForTableCache,\n} from './caches/types.js';\n\n/**\n * Status values for the {@link AuthState} state machine.\n *\n * Hybrid shape — bare string-literal union as the type (clean narrowing) plus a\n * const-object export for consumers who prefer refactor-safe constants. Both\n * compare against the same runtime string, so `auth.status === 'loading'` and\n * `auth.status === AuthStatus.Loading` are equivalent. Matches the\n * auto-generated `LoginResult`-style pattern from `@powerportalspro/core`.\n */\nexport const AuthStatus = {\n Loading: 'loading',\n Anonymous: 'anonymous',\n Authenticated: 'authenticated',\n} as const;\nexport type AuthStatus = (typeof AuthStatus)[keyof typeof AuthStatus];\n\n/**\n * Discriminated state union exposed by {@link useAuth}. `loading` is the initial state\n * during the first `/api/auth/me` call on mount; once that resolves, the state is\n * either `anonymous` or `authenticated` for the rest of the app's lifetime (until\n * `login()` / `logout()` actions transition it).\n *\n * The `authenticated` branch carries the {@link CurrentUserInfo} the server returned\n * the auto-generated DTO from `@powerportalspro/core`, no narrowing applied. Fields\n * like `roles` arrive as `string[] | undefined` per the wire contract; consumers\n * coalesce to `[]` at the read site if they want a non-null array.\n */\nexport type AuthState =\n | { readonly status: 'loading' }\n | { readonly status: 'anonymous' }\n | { readonly status: 'authenticated'; readonly user: CurrentUserInfo };\n\nexport interface PowerPortalsProContextValue {\n /** The shared {@link Transport} both clients are constructed against. Exposed for advanced use. */\n readonly transport: Transport;\n /** {@link AuthClient} instance — same one returned by `usePowerPortalsPro().auth`. */\n readonly auth: AuthClient;\n /** {@link PowerPortalsProClient} instance — same one returned by `usePowerPortalsPro().ppp`. */\n readonly ppp: PowerPortalsProClient;\n /**\n * Client-side cache for Dataverse table metadata. Reads dedupe\n * concurrent fetches and persist resolved values for the lifetime of\n * the `PowerPortalsProClient`. See {@link TableMetadataCache}.\n */\n readonly tableMetadataCache: TableMetadataCache;\n /**\n * Client-side cache for Dataverse view metadata. Same shape as\n * {@link tableMetadataCache}, keyed by view id. See {@link ViewMetadataCache}.\n */\n readonly viewMetadataCache: ViewMetadataCache;\n /**\n * Client-side cache for the per-table views list (powers grid view-\n * pickers). See {@link ViewsForTableCache}.\n */\n readonly viewsForTableCache: ViewsForTableCache;\n /**\n * Client-side cache for the singleton organization settings (default\n * currency plus file-upload constraints). See {@link OrganizationSettingsCache}.\n */\n readonly organizationSettingsCache: OrganizationSettingsCache;\n /** Current auth state. {@link useAuth} unpacks this directly. */\n readonly authState: AuthState;\n /** Refreshes the auth state by re-calling `/api/auth/me`. */\n readonly refreshAuth: () => Promise<void>;\n /** Submits credentials. On success the auth state transitions to `authenticated` automatically. */\n readonly login: (request: LoginRequest) => Promise<LoginResponse>;\n /** Clears the auth cookie and transitions the auth state to `anonymous`. */\n readonly logout: () => Promise<void>;\n /**\n * Swaps the auth cookie for the user's alt-identity sibling (the\n * contact↔systemuser pair stamped via `PortalClaimTypes.AltIdentity*`).\n * On `SwitchIdentityResult.Switched` the in-memory auth state is refreshed\n * from `/api/auth/me`; other outcomes leave it unchanged so the caller can\n * surface a message to the user. The endpoint takes no parameters — the\n * alt id is read from the cookie's claims server-side.\n */\n readonly switchIdentity: () => Promise<SwitchIdentityResponse>;\n}\n\nexport const PowerPortalsProContext = createContext<PowerPortalsProContextValue | null>(null);\n","import type {\n components,\n PowerPortalsProClient,\n TableMetadata,\n ViewMetadata,\n} from '@powerportalspro/core';\nimport type {\n OrganizationSettingsCache,\n TableMetadataCache,\n ViewMetadataCache,\n ViewsForTableCache,\n} from './types.js';\n\ntype OrganizationSettings = components['schemas']['OrganizationSettings'];\n\n/**\n * Shared internal helper — single in-memory cache class behind both the\n * table and view metadata caches. Stores resolved values in a Map; tracks\n * in-flight promises in a sibling Map so concurrent calls for the same\n * key share a single underlying fetch. On fetch error the in-flight slot\n * is evicted so the next caller can retry rather than getting a stuck\n * rejection.\n *\n * Not exported directly — consumers see the {@link TableMetadataCache} /\n * {@link ViewMetadataCache} interfaces; this is the implementation\n * behind the default cache instances the provider mounts.\n */\nclass InMemoryCache<TValue> {\n private readonly resolved = new Map<string, TValue>();\n private readonly inFlight = new Map<string, Promise<TValue | null>>();\n\n constructor(private readonly fetcher: (key: string) => Promise<TValue | null>) {}\n\n async getAsync(key: string): Promise<TValue | null> {\n const cached = this.resolved.get(key);\n if (cached !== undefined) return cached;\n const pending = this.inFlight.get(key);\n if (pending) return pending;\n const promise = (async () => {\n try {\n const result = await this.fetcher(key);\n if (result !== null) this.resolved.set(key, result);\n return result;\n } finally {\n this.inFlight.delete(key);\n }\n })();\n this.inFlight.set(key, promise);\n return promise;\n }\n\n invalidate(key: string): void {\n this.resolved.delete(key);\n // In-flight calls are deliberately left alone — they'll resolve and\n // re-populate. `invalidate` semantics is \"drop the cached value\", not\n // \"abort the in-flight fetch.\"\n }\n\n clear(): void {\n this.resolved.clear();\n }\n}\n\n/**\n * Default in-memory implementation of {@link TableMetadataCache}. Wraps\n * a `PowerPortalsProClient.retrieveTableMetadataAsync` call with Map-based\n * dedup. One instance per `PowerPortalsProClient` — the\n * `PowerPortalsProProvider` constructs it.\n */\nexport class InMemoryTableMetadataCache implements TableMetadataCache {\n private readonly cache: InMemoryCache<TableMetadata>;\n\n constructor(ppp: PowerPortalsProClient) {\n this.cache = new InMemoryCache<TableMetadata>((tableLogicalName) =>\n ppp.retrieveTableMetadataAsync(tableLogicalName),\n );\n }\n\n getAsync(tableLogicalName: string): Promise<TableMetadata | null> {\n return this.cache.getAsync(tableLogicalName);\n }\n\n invalidate(tableLogicalName: string): void {\n this.cache.invalidate(tableLogicalName);\n }\n\n clear(): void {\n this.cache.clear();\n }\n}\n\n/**\n * Default in-memory implementation of {@link ViewMetadataCache}. Same\n * shape as {@link InMemoryTableMetadataCache}, keyed by view id.\n */\nexport class InMemoryViewMetadataCache implements ViewMetadataCache {\n private readonly cache: InMemoryCache<ViewMetadata>;\n\n constructor(ppp: PowerPortalsProClient) {\n this.cache = new InMemoryCache<ViewMetadata>((viewId) =>\n ppp.retrieveViewMetadataAsync(viewId),\n );\n }\n\n getAsync(viewId: string): Promise<ViewMetadata | null> {\n return this.cache.getAsync(viewId);\n }\n\n invalidate(viewId: string): void {\n this.cache.invalidate(viewId);\n }\n\n clear(): void {\n this.cache.clear();\n }\n}\n\n/**\n * Default in-memory implementation of {@link OrganizationSettingsCache}.\n * Singleton-shaped: no key, just one stored value + one in-flight promise.\n *\n * Implemented as a thin wrapper around the keyed {@link InMemoryCache} with\n * a fixed sentinel key, so the dedup / fetch / error-eviction behaviors stay\n * identical to the metadata caches without re-implementing any of that\n * logic.\n */\nexport class InMemoryOrganizationSettingsCache implements OrganizationSettingsCache {\n private static readonly KEY = 'organization-settings';\n private readonly cache: InMemoryCache<OrganizationSettings>;\n\n constructor(ppp: PowerPortalsProClient) {\n this.cache = new InMemoryCache<OrganizationSettings>(() =>\n ppp.getOrganizationSettingsAsync(),\n );\n }\n\n getAsync(): Promise<OrganizationSettings | null> {\n return this.cache.getAsync(InMemoryOrganizationSettingsCache.KEY);\n }\n\n invalidate(): void {\n this.cache.invalidate(InMemoryOrganizationSettingsCache.KEY);\n }\n}\n\n/**\n * Default in-memory implementation of {@link ViewsForTableCache}. Wraps\n * the per-table views-list endpoint with the same in-memory + in-flight\n * dedup machinery the metadata caches use. Coerces a `null` fetch result\n * (table not found / 404) to an empty list so consumers can treat the\n * absence uniformly without branching on null.\n */\nexport class InMemoryViewsForTableCache implements ViewsForTableCache {\n private readonly cache: InMemoryCache<readonly ViewMetadata[]>;\n\n constructor(ppp: PowerPortalsProClient) {\n this.cache = new InMemoryCache<readonly ViewMetadata[]>(async (tableLogicalName) => {\n const result = await ppp.retrieveViewsForTableAsync(tableLogicalName);\n return result ?? [];\n });\n }\n\n async getAsync(tableLogicalName: string): Promise<readonly ViewMetadata[]> {\n const result = await this.cache.getAsync(tableLogicalName);\n return result ?? [];\n }\n\n invalidate(tableLogicalName: string): void {\n this.cache.invalidate(tableLogicalName);\n }\n\n clear(): void {\n this.cache.clear();\n }\n}\n","import { useContext } from 'react';\nimport { PowerPortalsProContext, type PowerPortalsProContextValue } from './context.js';\n\n/**\n * Returns the {@link AuthClient}, {@link PowerPortalsProClient}, and underlying\n * {@link Transport} from the nearest {@link PowerPortalsProProvider}. Use this when\n * you need to call a method that doesn't have a dedicated hook (the higher-level\n * `useAuth` / `useRecord` / `useFetchRecords` etc. cover the common cases).\n *\n * Throws if there's no provider above the calling component.\n */\nexport function usePowerPortalsPro(): PowerPortalsProContextValue {\n const ctx = useContext(PowerPortalsProContext);\n if (!ctx) {\n throw new Error(\n 'usePowerPortalsPro must be used inside <PowerPortalsProProvider>. Wrap your app at or near the root.',\n );\n }\n return ctx;\n}\n","import type { PowerPortalsProClient } from '@powerportalspro/core';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from 'react';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\n/**\n * Headless localizer contract. The framework calls `t(key)` to fetch a\n * localized string; consumers can plug in any implementation that satisfies\n * this interface — the bundled default ({@link createLocalizer}) reads from a\n * flat key→string map, but an i18next-backed adapter, a hardcoded English\n * map for tests, or a server-driven RPC localizer all work the same way.\n *\n * Args use positional indexing (`args[0]` → `{0}` in the template) to match\n * the existing JSON format — `.NET String.Format`\n * placeholders. Consumers using a named-args lib (e.g. i18next's `{{name}}`)\n * adapt at the boundary.\n */\nexport interface Localizer {\n t(key: string, args?: readonly unknown[]): string;\n}\n\nconst LocalizerContext = createContext<Localizer | null>(null);\nLocalizerContext.displayName = 'Localizer';\n\n/**\n * Reads the nearest {@link Localizer}. Throws when called outside a provider\n * mirrors `useDialogService`'s strict behavior. The framework auto-mounts a\n * default `<DefaultLocalizerProvider>` inside `<PowerPortalsProProvider>`, so\n * this only fails when neither is present (typically a misconfigured test).\n */\nexport function useLocalizer(): Localizer {\n const ctx = useContext(LocalizerContext);\n if (!ctx) {\n throw new Error(\n 'useLocalizer must be used inside a <LocalizerProvider>. ' +\n 'PowerPortalsProProvider auto-mounts a default localizer; ensure your tree is wrapped.',\n );\n }\n return ctx;\n}\n\n/**\n * Sugar over `useLocalizer().t` — returns the bound `t` function directly so\n * components can write `const t = useT();` and then `t('app.buttons.save.label')`.\n */\nexport function useT(): Localizer['t'] {\n return useLocalizer().t;\n}\n\n/**\n * Returns a {@link Localizer.t} function with every key automatically\n * prepended with `{prefix}.`. Use when a component consumes strings from\n * a known namespace and you don't want to repeat the prefix at every\n * call site.\n *\n * Pure sugar over `useT()` — relies on the same auto-mounted\n * `<DefaultLocalizerProvider>` and inherits all of its behavior\n * (cache, locale tracking, missing-key warnings). The bundle still\n * needs the full unprefixed key; this just saves the caller from\n * writing it out each time.\n *\n * @example\n * ```tsx\n * const t = usePrefixedT('components.MyApp.Pages.Editors.TextEditDemoPage');\n * t('title');\n * // → looks up\n * // 'components.MyApp.Pages.Editors.TextEditDemoPage.title'\n * ```\n *\n * The returned function is stable per `prefix` value (memoized via\n * `useCallback`), so it's safe to reference from effect dependency\n * arrays. Passing a fresh literal each render reuses the same identity\n * as long as the string is identical.\n */\nexport function usePrefixedT(prefix: string): Localizer['t'] {\n const t = useT();\n return useCallback(\n (key: string, args?: readonly unknown[]) => t(`${prefix}.${key}`, args),\n [t, prefix],\n );\n}\n\n// ---------------------------------------------------------------------------\n// Prefetcher — declarative API for components / routes to declare which\n// per-resource localization bundles they need. The default localizer\n// provider auto-fetches on cache miss, but the miss happens DURING render,\n// which produces a brief flash of raw keys. The prefetcher API lets a\n// caller queue the fetch up front so the strings are in cache (or have\n// been attempted) before the consuming render runs.\n// ---------------------------------------------------------------------------\n\n/**\n * Imperative contract for queueing per-resource localization bundles. The\n * default localizer provider sets one of these on the\n * {@link LocalizationPrefetcherContext}; custom adapters set via\n * `<LocalizerProvider value={…}>` don't have to provide one — the public\n * hooks degrade gracefully (treat everything as \"loaded\") when the\n * prefetcher is absent.\n */\nexport interface LocalizationPrefetcher {\n /**\n * Queue the supplied prefixes for fetch via the existing debounce / batch\n * mechanism. Prefixes already fetched (or in flight) are silently skipped\n * — repeated calls with the same prefixes don't generate redundant network\n * traffic. Returns immediately; the actual fetch happens after the next\n * flush tick.\n */\n prefetch(prefixes: readonly string[]): void;\n\n /**\n * Returns `true` once every supplied prefix has been *settled* — fetched\n * successfully OR a failed attempt has resolved. Failures count as settled\n * deliberately: a flaky bundle endpoint shouldn't strand consumers waiting\n * forever. Per-fetch errors are logged via `console.warn` by the\n * underlying flush so they remain debuggable.\n *\n * Prefixes not covered by a per-resource bundle (anything outside\n * `tables.{name}` / `views.{viewId}` shape) are treated as already loaded\n * — those keys live in the default bundle that loads on app boot.\n */\n isLoaded(prefixes: readonly string[]): boolean;\n\n /**\n * Subscribe to settled-set changes. The listener fires synchronously once\n * per flush, after the settled set is updated. Used by\n * {@link useLocalizationLoaded} to re-render subscribers; advanced\n * consumers can hook in directly. Returns an unsubscribe function.\n */\n subscribe(listener: () => void): () => void;\n}\n\nconst LocalizationPrefetcherContext = createContext<LocalizationPrefetcher | null>(null);\nLocalizationPrefetcherContext.displayName = 'LocalizationPrefetcher';\n\n/**\n * Reads the active {@link LocalizationPrefetcher}. Returns `null` when no\n * provider is mounted (custom-adapter setups). Most consumers should use\n * the higher-level {@link useLocalization} / {@link useLocalizationLoaded}\n * hooks; this is the raw escape hatch.\n */\nexport function useLocalizationPrefetcher(): LocalizationPrefetcher | null {\n return useContext(LocalizationPrefetcherContext);\n}\n\nconst NOOP_UNSUBSCRIBE = (): void => undefined;\n\n/**\n * Declares localization prefixes the calling component (or its subtree)\n * needs. The framework queues them via the existing debounced fetch\n * pipeline — by the time the user wanders into a component that calls\n * `t('tables.account.label')`, the fetch is either complete or in flight,\n * collapsing the brief flash of raw keys.\n *\n * Fire-and-forget — does not block render or expose loading state. Pair\n * with `<LocalizationBoundary>` (from `@powerportalspro/react-fluent`) or\n * {@link useLocalizationLoaded} when you need to hold off rendering until\n * the strings are ready.\n *\n * Accepts prefix or full-key shapes:\n * - `tables.{tablename}` (token) → loads `tables.<tablename>.*` strings\n * - `tables.account.label` (key) → coerced to `tables.account`\n * - `views.{viewId}` (token) → loads `views.<viewId>.*` strings\n * - `tables.account.views.{viewId}.foo` (key) → coerced to `views.<viewId>`\n * - Anything else (`app.*`, `components.*`) → silently dropped; covered\n * by the default bundle loaded on app boot.\n *\n * Re-fires only when `prefixes` changes by VALUE — passing a fresh array\n * with the same content each render doesn't requeue. Already-fetched\n * prefixes are deduped at the prefetcher layer regardless, so calling\n * `useLocalization(['tables.account'])` from two components on the same\n * page produces one network round-trip total.\n *\n * No-op when no `<DefaultLocalizerProvider>` ancestor is mounted (consumer\n * is on a custom localizer adapter).\n *\n * @example\n * ```tsx\n * function MyAccountPage() {\n * useLocalization(['tables.account', 'tables.contact']);\n * return <Form>...</Form>;\n * }\n * ```\n */\nexport function useLocalization(prefixes: readonly string[]): void {\n const prefetcher = useLocalizationPrefetcher();\n // Canonicalize the prefix list to a stable string key so an inline\n // `[…]` literal at the call site doesn't refire the effect every\n // render. Sorted + deduped so order-only differences also collapse.\n const prefixesKey = useMemo(() => {\n if (prefixes.length === 0) return '';\n const unique = Array.from(new Set(prefixes));\n unique.sort();\n return unique.join('\\n');\n }, [prefixes]);\n\n useEffect(() => {\n if (!prefetcher) return;\n if (prefixesKey === '') return;\n const tokens = prefixesKey.split('\\n');\n // Queue once on mount.\n prefetcher.prefetch(tokens);\n // Re-queue on every prefetcher notify. The notify fires whenever the\n // provider clears its settled set (e.g. locale change) — that's the\n // exact moment we need to re-declare these prefixes for the new\n // locale. It also fires after each per-resource flush completes;\n // those calls are dedup'd by the prefetcher (tokens already in\n // tried/pending/settled don't re-fetch), so the overhead is one\n // Set-membership check per token per notify. We can't rely on a\n // `locale` dep here because React fires child effects BEFORE the\n // provider's tier-1 effect clears its state, so a single re-queue\n // would still run against the OLD locale's tried-tokens and skip.\n // Subscribing routes the re-queue through the provider's notify\n // (called AFTER clearing) so we see the freshly-empty state.\n return prefetcher.subscribe(() => {\n prefetcher.prefetch(tokens);\n });\n }, [prefetcher, prefixesKey]);\n}\n\n/**\n * Reactive boolean — `true` once every prefix in the array has been settled\n * (fetched or failed). Re-renders the calling component when the state\n * flips. Treats a missing prefetcher (custom-adapter consumer) as \"always\n * loaded\" so dependent components degrade gracefully.\n *\n * Powers `<LocalizationBoundary>` and is exported for consumers that want\n * to drive their own gating UX — e.g. a loading row inside a grid rather\n * than holding off the whole subtree.\n *\n * @example\n * ```tsx\n * function MyForm() {\n * const ready = useLocalizationLoaded(['tables.account']);\n * if (!ready) return <Spinner />;\n * return <Form>...</Form>;\n * }\n * ```\n */\nexport function useLocalizationLoaded(prefixes: readonly string[]): boolean {\n const prefetcher = useLocalizationPrefetcher();\n // Stable subscribe — the prefetcher object identity is stable across\n // renders (built once on mount in DefaultLocalizerProvider), so this\n // closure stays stable for useSyncExternalStore's diffing.\n const subscribe = useCallback(\n (listener: () => void) => prefetcher?.subscribe(listener) ?? NOOP_UNSUBSCRIBE,\n [prefetcher],\n );\n const getSnapshot = useCallback(\n () => (prefetcher ? prefetcher.isLoaded(prefixes) : true),\n [prefetcher, prefixes],\n );\n // SSR snapshot — always \"loaded\" so server-rendered content renders\n // immediately and the client hydrates without a fallback flash.\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshotAlwaysLoaded);\n}\n\nconst getServerSnapshotAlwaysLoaded = (): boolean => true;\n\n/**\n * Provides an explicit {@link Localizer} to the subtree. Used internally by\n * {@link DefaultLocalizerProvider}; consumers wanting to swap in i18next or\n * any other implementation wrap their tree with this and pass their adapter\n * as `value`.\n */\nexport function LocalizerProvider({\n value,\n children,\n}: {\n value: Localizer;\n children: ReactNode;\n}) {\n return <LocalizerContext.Provider value={value}>{children}</LocalizerContext.Provider>;\n}\n\n// ---------------------------------------------------------------------------\n// Default implementation — flat-map lookup with positional `{0}` interpolation.\n// Matches the JSON format directly so the same\n// `ppp-server.defaults.en.json` file can drive both stacks.\n// ---------------------------------------------------------------------------\n\n/**\n * Flattens a nested object into a flat `key.path → value` map. Drops\n * non-string leaves (no use for them in the localizer's lookup).\n */\nexport function flattenStrings(\n obj: Record<string, unknown>,\n prefix = '',\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(obj)) {\n const key = prefix ? `${prefix}.${k}` : k;\n if (typeof v === 'string') {\n out[key] = v;\n } else if (v !== null && typeof v === 'object' && !Array.isArray(v)) {\n Object.assign(out, flattenStrings(v as Record<string, unknown>, key));\n }\n }\n return out;\n}\n\n/**\n * Substitutes `{0}`, `{1}`, etc. with the corresponding positional arg.\n * Strips `.NET`-style format specifiers (`{0:N0}`, `{1:yyyy-MM-dd}`) since\n * those are .NET-only and don't translate to JS without a custom formatter\n * pipeline. Consumers needing locale-aware number / date display should\n * format their values via `Intl.NumberFormat` / `Intl.DateTimeFormat` before\n * passing them to `t()`.\n *\n * Out-of-range placeholders are left as-is (visible in the rendered string)\n * so the missing arg surfaces during dev.\n */\nexport function interpolate(template: string, args?: readonly unknown[]): string {\n if (!args || args.length === 0) return template;\n return template.replace(/\\{(\\d+)(?::[^}]*)?\\}/g, (match, indexStr) => {\n const idx = Number(indexStr);\n if (idx < 0 || idx >= args.length) return match;\n return String(args[idx]);\n });\n}\n\n/**\n * Optional behavior knobs for {@link createLocalizer}.\n */\nexport interface CreateLocalizerOptions {\n /**\n * Called once per missing key the first time it's looked up. Use to wire\n * dev-mode warnings, telemetry, or to fail loudly during tests. The hook\n * is invoked AFTER the localizer falls back to returning the raw key.\n *\n * Each key is reported at most once per localizer instance — repeat\n * lookups for the same missing key don't fire it again. This keeps a\n * deeply-rendered missing key from spamming the console.\n */\n onMissingKey?: (key: string) => void;\n}\n\n/**\n * Builds a {@link Localizer} backed by a flat string map. Missing keys\n * return the key itself (visible fallback for missing strings — easier to\n * spot than silently showing nothing). Pass `onMissingKey` to get a hook\n * that fires once per missing key.\n */\nexport function createLocalizer(\n strings: Record<string, string>,\n options?: CreateLocalizerOptions,\n): Localizer {\n // Track keys we've already warned about so each missing key fires\n // `onMissingKey` exactly once per localizer instance — keeps `t('foo')`\n // called from N rendered components from spamming the console N times.\n const warnedKeys = new Set<string>();\n return {\n t(key, args) {\n const template = strings[key];\n if (template === undefined) {\n if (options?.onMissingKey && !warnedKeys.has(key)) {\n warnedKeys.add(key);\n options.onMissingKey(key);\n }\n return key;\n }\n return interpolate(template, args);\n },\n };\n}\n\n/**\n * Fetches a JSON file and returns its parsed nested object. Throws on\n * non-2xx response. Internal helper for {@link DefaultLocalizerProvider}'s\n * multi-URL merge.\n */\nasync function fetchLocalizationJson(url: string): Promise<Record<string, unknown>> {\n const response = await fetch(url);\n if (!response.ok) throw new Error(`HTTP ${response.status} fetching ${url}`);\n return (await response.json()) as Record<string, unknown>;\n}\n\n/**\n * Substitutes the `{locale}` placeholder in a URL template. URLs without\n * the placeholder pass through unchanged — useful for static-locale\n * deployments where every locale uses the same URL.\n */\nexport function resolveLocalizationUrl(template: string, locale: string): string {\n return template.replace(/\\{locale\\}/g, locale);\n}\n\n/**\n * Cookie name ASP.NET Core's `CookieRequestCultureProvider` uses to persist\n * the active culture. Reading and writing this cookie keeps the React app\n * in sync with any same-origin server-rendered surface that reads its\n * culture from the same source.\n */\nexport const ASP_NET_CULTURE_COOKIE = '.AspNetCore.Culture';\n\n/**\n * Reads the ASP.NET Core culture cookie. The cookie value follows the\n * `c={culture}|uic={uiCulture}` shape (URL-encoded) — we parse the `c`\n * segment. Returns `null` when the cookie isn't set or can't be parsed.\n *\n * Safe to call in non-browser environments (returns `null`); both `document`\n * and `document.cookie` are guarded.\n */\nexport function getAspNetCultureCookie(): string | null {\n if (typeof document === 'undefined' || !document.cookie) return null;\n for (const part of document.cookie.split('; ')) {\n const eq = part.indexOf('=');\n if (eq < 0) continue;\n const name = part.substring(0, eq);\n if (name !== ASP_NET_CULTURE_COOKIE) continue;\n const value = decodeURIComponent(part.substring(eq + 1));\n // Standard ASP.NET shape: `c=en|uic=en`. Either segment alone (`c=en`,\n // raw `en`) is also accepted as a fallback.\n const cMatch = value.match(/c=([^|]+)/);\n if (cMatch?.[1]) return cMatch[1];\n return value || null;\n }\n return null;\n}\n\n/**\n * Writes the ASP.NET Core culture cookie with both `c` and `uic` segments\n * set to the same value. A React-side locale change immediately affects\n * subsequent same-origin server-rendered pages that read their culture\n * from the same cookie (and vice versa).\n *\n * Defaults to `path=/`, 1-year expiry, `SameSite=Lax`. Safe to call in\n * non-browser environments (no-op).\n */\nexport function setAspNetCultureCookie(\n culture: string,\n options?: { path?: string; maxAgeSeconds?: number },\n): void {\n if (typeof document === 'undefined') return;\n const path = options?.path ?? '/';\n const maxAge = options?.maxAgeSeconds ?? 31_536_000; // 1 year\n const value = encodeURIComponent(`c=${culture}|uic=${culture}`);\n document.cookie = `${ASP_NET_CULTURE_COOKIE}=${value}; path=${path}; max-age=${maxAge}; samesite=lax`;\n}\n\n/**\n * Default regex matching a locale code at the start of `window.location.pathname`\n * shapes like `/en`, `/en/`, `/en-US/page`, `/fr-CA/path/x`. Captures the\n * locale code in group 1. Only 2-letter primary tags + optional 2–4-letter\n * subtag (script / region) are recognized; arbitrary path segments don't\n * trigger a false match.\n */\nexport const DEFAULT_URL_LOCALE_PATTERN = /^\\/([a-z]{2}(?:-[a-zA-Z]{2,4})?)(?:\\/|$)/;\n\n/**\n * Reads the locale from the URL path's first segment using\n * {@link DEFAULT_URL_LOCALE_PATTERN} (override via `pattern`). Returns the\n * matched locale or `null` when the URL doesn't carry one.\n *\n * Safe to call in non-browser environments (returns `null`).\n */\nexport function getLocaleFromUrlPath(pattern: RegExp = DEFAULT_URL_LOCALE_PATTERN): string | null {\n if (typeof window === 'undefined') return null;\n const match = window.location.pathname.match(pattern);\n return match?.[1] ?? null;\n}\n\n/**\n * The server's configured URL-culture strategy, surfaced to the client through\n * the localization manifest (`urlCultureStrategy`). Mirrors the C#\n * `PowerPortalsPro.Web.Options.UrlCultureStrategy` enum names:\n *\n * - `PathPrefix` — default locale bare (`/contacts`), others prefixed (`/fr/contacts`).\n * - `PathPrefixAll` — every locale prefixed, including the default (`/en/contacts`).\n * - `CookieOnly` — the locale never appears in the URL; switching is in-place.\n */\nexport type UrlCultureStrategyName = 'PathPrefix' | 'PathPrefixAll' | 'CookieOnly';\n\n/**\n * Whether a URL for `locale` carries a `/{locale}` path prefix under `strategy`.\n * Runtime mirror of the server-side `CultureUrls.ShouldPrefix` so client-built\n * links match the shape the LocalizationMiddleware actually serves.\n */\nexport function shouldPrefixLocale(\n strategy: UrlCultureStrategyName,\n locale: string,\n defaultLocale: string,\n): boolean {\n switch (strategy) {\n case 'CookieOnly':\n return false;\n case 'PathPrefixAll':\n return true;\n default: // PathPrefix — default bare, everything else prefixed.\n return locale.toLowerCase() !== defaultLocale.toLowerCase();\n }\n}\n\n/**\n * Removes a leading `/{locale}` segment from `pathname` when that segment is one\n * of `supportedLocales`. Returns the \"cultureless\" path (always leading-slash,\n * `/` for root). Mirror of the server-side `StripCulturePrefix`.\n */\nexport function stripLocalePrefix(pathname: string, supportedLocales: readonly string[]): string {\n const trimmed = pathname.replace(/^\\/+/, '');\n if (trimmed.length === 0) return '/';\n\n const slash = trimmed.indexOf('/');\n const first = slash < 0 ? trimmed : trimmed.slice(0, slash);\n const isLocale = supportedLocales.some((l) => l.toLowerCase() === first.toLowerCase());\n if (!isLocale) return pathname.startsWith('/') ? pathname : `/${pathname}`;\n\n const rest = slash < 0 ? '' : trimmed.slice(slash);\n return rest.length === 0 ? '/' : rest;\n}\n\n/**\n * Builds the path (leading slash, no origin) at which `locale` serves the current\n * page under `strategy`, given the current `pathname` (which may already carry a\n * locale prefix) and the server's `supportedLocales`. Runtime mirror of the\n * server-side `CultureUrls.BuildPath`. Use it to compute the navigation target\n * when switching locale under a prefix strategy.\n */\nexport function buildLocalePath(options: {\n strategy: UrlCultureStrategyName;\n locale: string;\n defaultLocale: string;\n pathname: string;\n supportedLocales: readonly string[];\n}): string {\n const { strategy, locale, defaultLocale, pathname, supportedLocales } = options;\n const cultureless = stripLocalePrefix(pathname, supportedLocales);\n return shouldPrefixLocale(strategy, locale, defaultLocale) ? `/${locale}${cultureless}` : cultureless;\n}\n\n/**\n * Resolves the active locale from common sources, in order of precedence:\n *\n * 1. URL path segment (`/en/...`) — picked up via {@link getLocaleFromUrlPath}\n * when `matchUrl` is `true` (default).\n * 2. ASP.NET culture cookie (`.AspNetCore.Culture`) — picked up via\n * {@link getAspNetCultureCookie}.\n * 3. `navigator.language` — when `matchBrowser` is `true` (default), strips\n * the region (e.g. `en-US` → `en`) for compatibility with bundle locale\n * codes which are typically primary-tag only.\n * 4. The supplied `fallback` (default `'en'`).\n *\n * Used by `<LocaleProvider>` for auto-detection; also exported for consumer\n * code that wants the resolved locale without the provider context.\n */\nexport function detectLocale(options?: {\n matchUrl?: boolean;\n urlPattern?: RegExp;\n matchBrowser?: boolean;\n fallback?: string;\n}): string {\n const {\n matchUrl = true,\n urlPattern = DEFAULT_URL_LOCALE_PATTERN,\n matchBrowser = true,\n fallback = 'en',\n } = options ?? {};\n\n if (matchUrl) {\n const fromUrl = getLocaleFromUrlPath(urlPattern);\n if (fromUrl) return fromUrl;\n }\n const fromCookie = getAspNetCultureCookie();\n if (fromCookie) return fromCookie;\n if (matchBrowser && typeof navigator !== 'undefined' && navigator.language) {\n // Strip region: `en-US` → `en`. Bundles are typically published per\n // primary tag, so a hyphenated browser preference would 404 against\n // `supportedLocales`.\n const primary = navigator.language.split('-')[0];\n if (primary) return primary;\n }\n return fallback;\n}\n\n/**\n * Debounce window for batching cache-miss `t()` lookups before flushing\n * coerced tokens out to the per-table / per-view bundle endpoints. Long\n * enough to capture a full render pass's misses; short enough that the\n * user doesn't notice the delay.\n */\nconst MISSING_KEY_FLUSH_DELAY_MS = 75;\n\n/**\n * Sentinel token added to {@link DefaultLocalizerProvider}'s\n * `settledTokensRef` after the tier-1 default-bundle fetch resolves\n * (success or failure). Lets `isLoaded` track \"has the default\n * bundle been attempted?\" the same way it tracks per-resource bundle\n * attempts — one unified set membership check, no separate flag.\n *\n * Real prefix-derived tokens are always `tables.{name}` or\n * `views.{viewId}` (see {@link coercePrefixToToken}), so a sentinel\n * without a dot can't collide. Internal — not exported.\n */\nconst DEFAULT_BUNDLE_SENTINEL = 'defaultBundle';\n\n/**\n * Coerces an arbitrary localization key into the bundle token that covers\n * it — `tables.{name}` for any `tables.{name}.…` key, `views.{viewId}` for\n * any `tables.{name}.views.{viewId}.…` key — or returns `null` for keys no\n * per-resource bundle covers (the static default bundle handles them, or\n * they're genuinely unresolvable like raw `choices.*` references). Mirror\n * of the C# source generator's `CoerceToTableOrViewToken` so build-time\n * and runtime agree on which keys map to which bundle.\n */\nfunction coerceKeyToToken(key: string): string | null {\n if (!key) return null;\n const lower = key.toLowerCase();\n if (!lower.startsWith('tables.')) return null;\n\n const afterTables = lower.substring('tables.'.length);\n const firstDot = afterTables.indexOf('.');\n if (firstDot < 0) return `tables.${afterTables}`;\n\n const tableName = afterTables.substring(0, firstDot);\n const afterTable = afterTables.substring(firstDot + 1);\n if (afterTable.startsWith('views.')) {\n const afterViews = afterTable.substring('views.'.length);\n const nextDot = afterViews.indexOf('.');\n const viewId = nextDot < 0 ? afterViews : afterViews.substring(0, nextDot);\n if (!viewId) return null;\n return `views.${viewId}`;\n }\n return `tables.${tableName}`;\n}\n\n/**\n * Like {@link coerceKeyToToken} but additionally accepts inputs that\n * are already in TOKEN shape — `tables.{name}` and `views.{viewId}`.\n * Used by the prefetcher's public API where callers naturally pass\n * prefixes (\"the namespace they want loaded\") rather than full keys.\n *\n * `coerceKeyToToken` itself stays as-is because its comment pins it\n * as the byte-for-byte runtime mirror of the C# source generator's\n * `CoerceToTableOrViewToken` — that path only ever sees full keys\n * (from missed `t()` lookups), not bare tokens.\n */\nfunction coercePrefixToToken(prefix: string): string | null {\n if (!prefix) return null;\n const lower = prefix.toLowerCase();\n\n // Direct `views.*` shape — caller passed a view token (or a key\n // under one). Extract the viewId segment, ignoring anything after.\n if (lower.startsWith('views.')) {\n const afterViews = lower.substring('views.'.length);\n const nextDot = afterViews.indexOf('.');\n const viewId = nextDot < 0 ? afterViews : afterViews.substring(0, nextDot);\n return viewId ? `views.${viewId}` : null;\n }\n\n // `tables.*` shape — delegate to the existing key-coercion logic,\n // which already handles `tables.{name}` (token), `tables.{name}.{key}`\n // (key), and `tables.{name}.views.{viewId}.{key}` (nested-view key).\n return coerceKeyToToken(prefix);\n}\n\n// ---------------------------------------------------------------------------\n// Locale state — independent of the localizer itself. Consumer apps that\n// want a global \"current locale\" use the LocaleProvider; the\n// DefaultLocalizerProvider reads from it to drive URL substitution.\n// Apps with their own state management can skip the provider and pass\n// resolved URLs directly to DefaultLocalizerProvider.\n// ---------------------------------------------------------------------------\n\nexport interface LocaleState {\n /** Current locale code (e.g. `'en'`, `'es'`, `'fr-CA'`). */\n readonly locale: string;\n /** Replace the active locale. Triggers re-fetch of localization JSONs. */\n readonly setLocale: (locale: string) => void;\n}\n\nconst LocaleContext = createContext<LocaleState | null>(null);\nLocaleContext.displayName = 'Locale';\n\n/**\n * Manifest data populated by {@link DefaultLocalizerProvider} once its\n * tier-1 fetch completes. Exposed through a separate context (rather\n * than {@link LocaleContext}) because {@link LocaleProvider} doesn't\n * know about the manifest — only the localizer provider does — and\n * because the data is `undefined` until the network round-trip lands.\n *\n * Consumed by {@link useSupportedLocales} so callers like\n * `<LanguageDropdown>` can read the supported-locale list without\n * issuing their own redundant `/localizations/version` fetch on every\n * mount. Falls back to a direct fetch when no\n * {@link DefaultLocalizerProvider} is mounted (rare — consumers who\n * opted out of the framework localizer entirely).\n */\nexport interface LocalizationManifestState {\n readonly supportedLocales: readonly string[] | undefined;\n /**\n * The server's configured {@link UrlCultureStrategyName}, from the manifest's\n * `urlCultureStrategy`. `undefined` until the tier-1 manifest fetch lands.\n * Drives whether a locale switch navigates (prefix strategies) or swaps in\n * place (`CookieOnly`); read it via {@link useUrlCultureStrategy}.\n */\n readonly urlCultureStrategy: UrlCultureStrategyName | undefined;\n}\nexport const LocalizationManifestContext = createContext<LocalizationManifestState | null>(null);\nLocalizationManifestContext.displayName = 'LocalizationManifest';\n\n/**\n * Reads the server's configured {@link UrlCultureStrategyName} from the\n * {@link LocalizationManifestContext} (populated by\n * {@link DefaultLocalizerProvider}'s manifest fetch). Returns `undefined`\n * before the manifest lands, or when no provider is mounted. Consumers\n * building a custom locale switcher use this to decide whether to navigate\n * to a `/{locale}` URL or switch in place.\n */\nexport function useUrlCultureStrategy(): UrlCultureStrategyName | undefined {\n return useContext(LocalizationManifestContext)?.urlCultureStrategy;\n}\n\n/**\n * Reads the server's default locale — the first entry of the manifest's\n * supported-locale list (the server emits it default-culture-first). Returns\n * `undefined` before the manifest lands, or when no provider is mounted.\n */\nexport function useDefaultLocale(): string | undefined {\n return useContext(LocalizationManifestContext)?.supportedLocales?.[0];\n}\n\n/**\n * Reads the active locale state, falling back to `{ locale: 'en' }` when no\n * `<LocaleProvider>` is mounted. Apps that don't need locale switching can\n * skip the provider — the framework treats the default locale as `'en'`.\n */\nexport function useLocale(): LocaleState {\n const ctx = useContext(LocaleContext);\n return ctx ?? { locale: 'en', setLocale: noopSetLocale };\n}\n\nconst noopSetLocale = (_: string): void => undefined;\n\nexport interface LocaleProviderProps {\n /**\n * Initial locale on mount. Used as the FALLBACK when auto-detection is\n * enabled (the default) and finds no URL / cookie / browser hint, OR as\n * the literal initial value when `autoDetect={false}`. Defaults to `'en'`.\n */\n initialLocale?: string;\n /**\n * Auto-detect the active locale via {@link detectLocale} on mount. Default\n * `true`. When enabled, the resolution order is:\n * URL path → ASP.NET culture cookie → `navigator.language` → `initialLocale`.\n * Pass `false` to start from `initialLocale` ignoring the environment.\n */\n autoDetect?: boolean;\n /**\n * Override the URL pattern used by `autoDetect`. Defaults to\n * {@link DEFAULT_URL_LOCALE_PATTERN}. Set to a custom RegExp when your\n * routes use a non-standard locale-segment shape, or pass `null` to\n * disable URL-based detection (only cookie + browser will be checked).\n */\n urlPattern?: RegExp | null;\n /**\n * Persist locale changes to the ASP.NET culture cookie. Default `true` —\n * keeps any same-origin server-rendered page in sync with the React-side\n * locale. Pass `false` to manage persistence yourself (e.g. via the\n * `onLocaleChange` callback).\n */\n persistToCookie?: boolean;\n /**\n * Optional controlled locale. When provided, the component is controlled\n * and `setLocale` calls are forwarded via {@link onLocaleChange}. When\n * omitted, the component is uncontrolled with internal state.\n */\n locale?: string;\n /**\n * Called when `setLocale` is invoked. Required when {@link locale} is\n * provided (controlled mode); ignored otherwise.\n */\n onLocaleChange?: (locale: string) => void;\n children: ReactNode;\n}\n\n/**\n * Provides the active locale + a setter to the subtree. Descendants read via\n * {@link useLocale}; the {@link DefaultLocalizerProvider} subscribes\n * automatically and re-fetches its JSONs when the locale changes.\n *\n * Supports both controlled (`locale` + `onLocaleChange` props) and\n * uncontrolled (`initialLocale` only, internal state) modes — same pattern\n * as Fluent UI's controlled inputs.\n *\n * Example:\n * ```tsx\n * <LocaleProvider initialLocale=\"en\">\n * <PowerPortalsProProvider>\n * <App /> {/* useLocale() inside returns { locale: 'en', setLocale: ... } *\\/}\n * </PowerPortalsProProvider>\n * </LocaleProvider>\n * ```\n */\nexport function LocaleProvider({\n initialLocale = 'en',\n autoDetect = true,\n urlPattern,\n persistToCookie = true,\n locale: controlledLocale,\n onLocaleChange,\n children,\n}: LocaleProviderProps) {\n // Compute the initial locale ONCE on mount. `useState`'s lazy initializer\n // runs the detection without recomputing on subsequent renders — switching\n // back to the URL/cookie value mid-session would surprise consumers who\n // expect `setLocale` to be the only mechanism that changes the active locale.\n const [internalLocale, setInternalLocale] = useState(() => {\n if (!autoDetect) return initialLocale;\n return detectLocale({\n matchUrl: urlPattern !== null,\n ...(urlPattern instanceof RegExp && { urlPattern }),\n fallback: initialLocale,\n });\n });\n const isControlled = controlledLocale !== undefined;\n const locale = isControlled ? controlledLocale : internalLocale;\n\n const setLocale = useCallback(\n (next: string) => {\n if (!isControlled) setInternalLocale(next);\n // Persist to the ASP.NET culture cookie BEFORE invoking the consumer\n // callback, so a consumer that triggers a navigation in `onLocaleChange`\n // (e.g. routing to /es/...) sees the cookie already updated.\n if (persistToCookie) setAspNetCultureCookie(next);\n onLocaleChange?.(next);\n },\n [isControlled, persistToCookie, onLocaleChange],\n );\n\n const value = useMemo<LocaleState>(() => ({ locale, setLocale }), [locale, setLocale]);\n return <LocaleContext.Provider value={value}>{children}</LocaleContext.Provider>;\n}\n\nexport interface DefaultLocalizerProviderProps {\n /**\n * Optional additional URL templates to fetch + layer on top of the\n * server bundle. Each may contain `{locale}` substituted with the active\n * locale. Layered later-wins: bundle first, then each URL in order.\n *\n * Most apps leave this empty — the server bundle (framework defaults +\n * app overrides + Dataverse-metadata-derived strings outside `tables.*`)\n * already covers the common case. Use this for client-side overrides or\n * page-specific files the consumer doesn't want round-tripping through\n * the server.\n */\n urls?: readonly string[];\n /**\n * @deprecated Use {@link urls} instead. Kept for backwards compatibility\n * when set, treated as `urls: [url]`.\n */\n url?: string;\n /**\n * Called once per missing key the first time it's looked up via `t(key)`.\n * Defaults to a `console.warn` so missing strings surface during dev.\n * Pass `() => undefined` to silence; pass a custom function to route the\n * warnings to telemetry.\n *\n * Fires whether or not the on-demand endpoint fetch succeeds — the hook is\n * about \"this key wasn't in the static cache when first seen\", not \"this\n * key is unresolvable globally\".\n */\n onMissingKey?: (key: string) => void;\n /**\n * Baseline localization prefixes the provider should eagerly load\n * alongside the default bundle on mount. Same shape as the prop on\n * {@link LocalizationBoundary} — `tables.{name}` / `views.{viewId}`\n * tokens (or full keys, coerced). The framework queues them through\n * the standard prefetch pipeline once the default bundle has\n * resolved; descendant `<LocalizationBoundary>` components\n * automatically wait for them as part of their readiness check, so\n * baseline strings the whole app needs (e.g. tables that appear in\n * the navigation chrome) flow into the cache before any boundary\n * mounts its children.\n *\n * Prefixes the default bundle already covers (`app.*`,\n * `components.*`) are silently dropped — they're loaded as part of\n * Tier 1 regardless.\n *\n * Changing this prop after mount is supported: new prefixes are\n * queued on the next render; removed prefixes stay cached (we don't\n * actively evict strings — they just won't be re-fetched on locale\n * change). Most apps set this once at the root and never change it.\n */\n prefixes?: readonly string[];\n children: ReactNode;\n}\n\n// Initial-state localizer used before the fetch resolves. Returns the raw\n// key for any lookup (visible fallback that surfaces missing strings).\n// Stable identity — created once at module load so React state init\n// doesn't allocate a new instance per provider mount. Uses no missing-key\n// warning so the brief pre-fetch interval doesn't spam the console.\nconst FALLBACK_LOCALIZER = createLocalizer({});\n\n/**\n * Default missing-key handler — warns once per key via `console.warn`.\n * Visible during dev so typos and missing JSON entries surface early.\n */\nfunction defaultMissingKeyWarning(key: string): void {\n // eslint-disable-next-line no-console\n console.warn(`PowerPortalsPro: missing localization key \"${key}\"`);\n}\n\n/**\n * Auto-loads localized strings and provides a {@link Localizer} to the\n * subtree using a two-tier strategy:\n *\n * **Tier 1 — manifest + default bundle (eager, on mount + locale change).**\n *\n * Fetches the manifest from `/localizations/version`, then the default\n * bundle from `/localizations/default/{locale}.{thumbprint}.json`. The\n * thumbprint segment makes each release's URL unique so the browser can\n * cache it `immutable`. The default bundle contains everything outside\n * `tables.*` / `choices.*`. Optional client-side overlay URLs (the `urls`\n * prop) are fetched in parallel and layered later-wins on top.\n *\n * **Tier 2 — per-resource bundle fetches (lazy, on cache miss).**\n *\n * When `t(key)` is called and the key isn't in the static cache, the key\n * is coerced to its owning bundle token (`tables.{name}` or `views.{viewId}`)\n * and queued for a debounced batched fetch (~75 ms). The flush fires one\n * GET per distinct token in parallel, each landing under a per-resource\n * thumbprinted URL (`/localizations/tables/{name}/{locale}.{thumb}.json`,\n * `/localizations/views/{viewId}/{locale}.{thumb}.json`). Subsequent\n * navigations to a page declaring the same token reuse the browser-cached\n * response with zero network traffic. Source-generator-emitted tokens (via\n * `LocalizationManifest`) flow through the same path — one fetch per\n * declared token, on first hit, then cached.\n *\n * **Render strategy — non-blocking.** Children render immediately with a\n * key-fallback localizer (every `t(key)` returns the key as plain text);\n * the provider swaps in real strings once the manifest + bundle resolve.\n * Cache misses appear as raw keys briefly until the per-resource fetch\n * resolves and triggers a re-render.\n *\n * **Locale changes** clear the cache, cancel any in-flight fetch, re-fetch\n * the manifest + bundle at the new locale, and start fresh.\n *\n * Auto-mounted by `<PowerPortalsProProvider>`. Wrap with a custom\n * `<LocalizerProvider>` and pass `localizationUrl={false}` on the provider\n * to opt out entirely (e.g. when adapting i18next).\n */\nexport function DefaultLocalizerProvider({\n urls,\n url,\n onMissingKey = defaultMissingKeyWarning,\n prefixes,\n children,\n}: DefaultLocalizerProviderProps) {\n const [cache, setCache] = useState<Record<string, string>>({});\n // Supported-locale list from the manifest fetch. `undefined` until\n // the tier-1 fetch lands; consumed by `useSupportedLocales` via the\n // `LocalizationManifestContext` below so descendants don't re-fetch\n // `/localizations/version` themselves.\n const [supportedLocales, setSupportedLocales] = useState<readonly string[] | undefined>(undefined);\n // The server's configured URL-culture strategy from the manifest. `undefined`\n // until the tier-1 fetch lands; surfaced via `LocalizationManifestContext` so\n // `<LanguageDropdown>` (and custom switchers) can navigate-vs-swap correctly.\n const [urlCultureStrategy, setUrlCultureStrategy] = useState<UrlCultureStrategyName | undefined>(\n undefined,\n );\n const { locale } = useLocale();\n const { ppp } = usePowerPortalsPro();\n\n // Per-render-stable refs: the latest values that flush callbacks need to\n // read at flush time (which can be ~75ms after the render that scheduled\n // the flush). Without these, the flush would close over stale state.\n const localeRef = useRef(locale);\n localeRef.current = locale;\n const pppRef = useRef(ppp);\n pppRef.current = ppp;\n\n // Manifest-derived state stashed in refs so the tier-2 flush can read it\n // without re-running a manifest fetch. Cleared on locale change.\n // - resolvedLocaleRef: the locale we actually fetch under (resolved\n // against manifest.supportedLocales — may differ from `locale` if the\n // user's preferred locale isn't supported).\n // - thumbprintsRef: per-resource thumbprints for that locale.\n const resolvedLocaleRef = useRef<string | null>(null);\n const thumbprintsRef = useRef<{\n bundle: string;\n tables: Readonly<Record<string, string>>;\n views: Readonly<Record<string, string>>;\n } | null>(null);\n\n // Pending cache-miss queue + dedup tracking. Refs (not state) — the queue\n // is consumed by a setTimeout, no need to re-render on push.\n // - pendingTokensRef: bundle tokens (`tables.{name}` / `views.{viewId}`)\n // coerced from cache-miss keys, awaiting a flush.\n // - triedTokensRef: tokens we've already attempted. Prevents re-queueing\n // the same token on every render, which would cause a flush storm.\n // - pendingTimerRef: handle of the scheduled flush, or null when none.\n const pendingTokensRef = useRef<Set<string>>(new Set());\n const triedTokensRef = useRef<Set<string>>(new Set());\n const pendingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // Settled-tokens tracking for the LocalizationPrefetcher API. Distinct\n // from `triedTokensRef` because tried tokens may still be in flight\n // settled means \"the flush attempt has resolved, either to a cached\n // result or to a logged failure.\" `useLocalizationLoaded` reads this\n // to decide whether the boundary should mount its children.\n // Failures intentionally count as settled so a flaky bundle endpoint\n // can't permanently strand a <LocalizationBoundary>; per-fetch errors\n // are still surfaced via `console.warn` from `flushPending`.\n const settledTokensRef = useRef<Set<string>>(new Set());\n // Subscribers to settled-set changes. Each `useLocalizationLoaded`\n // call adds one via the prefetcher's `subscribe`; the flush fires them\n // all after updating the set. Set (not Array) so unsubscribe is O(1).\n const listenersRef = useRef<Set<() => void>>(new Set());\n\n // Baseline prefixes the provider was constructed with — the\n // `prefixes` prop. Stored as both the raw input (for re-queueing on\n // locale change) and pre-coerced tokens (for cheap `isLoaded`\n // checks that don't re-coerce on every render). Updated via the\n // useMemo below so prop changes flow through.\n const baselinePrefixesRef = useRef<readonly string[]>([]);\n const baselineTokensRef = useRef<readonly string[]>([]);\n // Stable serialization key — drives effect deps + lets baseline\n // tracking ignore order-only / reference-only prop churn.\n const baselinePrefixesKey = useMemo(() => {\n const input = prefixes ?? [];\n if (input.length === 0) {\n baselinePrefixesRef.current = [];\n baselineTokensRef.current = [];\n return '';\n }\n const unique = Array.from(new Set(input));\n unique.sort();\n baselinePrefixesRef.current = unique;\n // Coerce once per change. Null-coercing prefixes (covered by the\n // default bundle) are skipped — `isLoaded` doesn't need to check\n // them since Tier 1 already does.\n const tokens: string[] = [];\n for (const p of unique) {\n const t = coercePrefixToToken(p);\n if (t !== null) tokens.push(t);\n }\n baselineTokensRef.current = tokens;\n return unique.join('\\n');\n }, [prefixes]);\n\n // Stable notifier — used by both the flush completion and the\n // locale-change reset path. Snapshots the listener set before iterating\n // so a listener that unsubscribes itself during the callback doesn't\n // skip a sibling.\n const notifyListeners = useCallback(() => {\n const snapshot = Array.from(listenersRef.current);\n for (const listener of snapshot) listener();\n }, []);\n\n // Tracks the locale + overlay combo this provider was last run against.\n // Used by the tier-1 effect to distinguish \"initial mount\" from \"real\n // locale / url change\" — the cache + queue clearing at the top of the\n // tier-1 effect runs only on the latter. Without this, children's\n // useEffect-queued prefetches (which fire BEFORE the parent's effect)\n // get wiped immediately on mount.\n const previousRunRef = useRef<{ locale: string; overlayKey: string } | null>(null);\n\n // Forward-reference bridge for `flushPending` — the tier-1 effect's\n // async block needs to schedule a flush AFTER it completes (to drain\n // any baseline prefixes it queued), but `flushPending` is declared\n // below the effect. Storing the latest callback in a ref + reading it\n // through `flushPendingRef.current()` at call time gives the effect\n // access without reordering the function body.\n const flushPendingRef = useRef<() => void>(() => undefined);\n\n // Resolve any consumer-supplied overlay URL templates (rare). The bundle\n // endpoint provides the bulk of strings; these layer on top.\n const overlayTemplates = urls ?? (url !== undefined ? [url] : []);\n const resolvedOverlayUrls = overlayTemplates.map((t) => resolveLocalizationUrl(t, locale));\n const overlayUrlsKey = resolvedOverlayUrls.join('\\n');\n\n // Tier 1: manifest + default bundle + optional overlays on mount + locale change.\n useEffect(() => {\n let cancelled = false;\n // Locale change: clear the cache + queue + cancel any pending flush.\n // On the FIRST mount we skip this — children's useEffects run before\n // ours, and they may have already queued prefetches we'd otherwise\n // wipe out. Refs start fresh by JS semantics on first mount, so the\n // clearing would be redundant anyway.\n const previous = previousRunRef.current;\n const isLocaleOrUrlChange = previous !== null &&\n (previous.locale !== locale || previous.overlayKey !== overlayUrlsKey);\n previousRunRef.current = { locale, overlayKey: overlayUrlsKey };\n\n if (isLocaleOrUrlChange) {\n setCache({});\n // Clear the cached supported-locale list too — the new locale's\n // manifest fetch below repopulates it. Skipping this would leave\n // `useSupportedLocales` returning stale data during the\n // re-fetch window, which is a minor inconsistency that's easier\n // to avoid than to explain.\n setSupportedLocales(undefined);\n pendingTokensRef.current = new Set();\n triedTokensRef.current = new Set();\n if (pendingTimerRef.current !== null) {\n clearTimeout(pendingTimerRef.current);\n pendingTimerRef.current = null;\n }\n // Settled set clears wholesale — strings under the new locale\n // haven't been attempted yet. This also removes the\n // `DEFAULT_BUNDLE_SENTINEL` entry, so `isLoaded` flips back to\n // false until the new locale's default bundle resolves. Notify\n // subscribers so any mounted boundary flips back to its fallback\n // while the new-locale bundle loads.\n if (settledTokensRef.current.size > 0) {\n settledTokensRef.current = new Set();\n notifyListeners();\n }\n }\n resolvedLocaleRef.current = null;\n thumbprintsRef.current = null;\n\n const overlayList = overlayUrlsKey.split('\\n').filter((u) => u.length > 0);\n\n (async () => {\n try {\n // Step 1: version manifest — supported-locale codes only. Tiny.\n const manifest = await pppRef.current.retrieveLocalizationBundleManifestAsync();\n if (cancelled) return;\n\n // Stash the supported-locale list in state so descendants reading\n // it via `useSupportedLocales` see the manifest's data without\n // issuing their own `/localizations/version` fetch.\n setSupportedLocales(manifest.supportedLocales);\n // The URL-culture strategy is a server-wide constant (not locale-\n // dependent), so it isn't cleared on locale change above — just set\n // it here once the manifest lands. The cast narrows the wire `string`\n // to the known union; an unrecognized value falls through as-is and\n // the consumer's navigate-vs-swap logic treats it as a prefix strategy.\n setUrlCultureStrategy(manifest.urlCultureStrategy as UrlCultureStrategyName);\n\n // Resolve the requested locale to one the server actually has a\n // bundle for. Fallback chain: exact match → first supported.\n const resolvedLocale = manifest.supportedLocales.includes(locale)\n ? locale\n : manifest.supportedLocales[0] ?? locale;\n\n // Step 2: per-locale thumbprints. Returned from a separate endpoint\n // so the version response stays small regardless of how many\n // locales the server supports.\n const thumbs = await pppRef.current.retrieveLocalizationThumbprintsAsync(resolvedLocale);\n if (cancelled) return;\n\n resolvedLocaleRef.current = resolvedLocale;\n thumbprintsRef.current = thumbs;\n\n // Step 3: default bundle (via the typed client) + consumer overlays\n // (via window.fetch — they're arbitrary URLs, not framework\n // endpoints) in parallel.\n const [bundleData, ...overlayDatas] = await Promise.all([\n pppRef.current.retrieveLocalizationBundleAsync(resolvedLocale, thumbs.bundle),\n ...overlayList.map(fetchLocalizationJson),\n ]);\n if (cancelled) return;\n\n // Merge later-wins: bundle first, then each overlay file.\n const merged: Record<string, string> = {};\n Object.assign(merged, flattenStrings(bundleData));\n for (const data of overlayDatas) {\n Object.assign(merged, flattenStrings(data));\n }\n setCache(merged);\n } catch (e) {\n if (cancelled) return;\n // eslint-disable-next-line no-console\n console.error(\n 'PowerPortalsPro: failed to load localization bundle; staying on key-fallback localizer.',\n e,\n );\n }\n if (cancelled) return;\n // Tier-1 attempt is done (success or failure) — add the sentinel\n // to the settled set so `isLoaded`'s uniform membership check\n // reports the default bundle as loaded. The settled set is the\n // single source of truth for \"what's been attempted\" across both\n // tier-1 and per-resource bundles.\n settledTokensRef.current.add(DEFAULT_BUNDLE_SENTINEL);\n\n // Queue baseline prefixes through the existing pending-tokens\n // pipeline. We do this AFTER tier-1 settles (rather than in\n // parallel) so the per-resource fetches run against valid\n // thumbprints; if tier-1 failed, the thumbs ref stays null and\n // the next flushPending call early-returns + re-queues the\n // baseline tokens, which is fine. Coerced tokens were\n // pre-computed in `baselinePrefixesKey` so we just walk the\n // ref's current value.\n let queuedBaseline = false;\n for (const token of baselineTokensRef.current) {\n if (triedTokensRef.current.has(token)) continue;\n if (pendingTokensRef.current.has(token)) continue;\n pendingTokensRef.current.add(token);\n queuedBaseline = true;\n }\n if (\n pendingTokensRef.current.size > 0 &&\n pendingTimerRef.current === null\n ) {\n // Use the ref bridge — `flushPending` is declared below this\n // useEffect. The ref is updated on every render so by the time\n // this setTimeout fires, `flushPendingRef.current` is the\n // latest stable callback identity.\n pendingTimerRef.current = setTimeout(\n () => flushPendingRef.current(),\n MISSING_KEY_FLUSH_DELAY_MS,\n );\n }\n // Notify even if we didn't queue new baselines — the tier-1 flip\n // itself is observable via `isLoaded` and listeners must re-evaluate.\n notifyListeners();\n void queuedBaseline; // referenced for readability; no further action needed\n })();\n\n return () => {\n cancelled = true;\n if (pendingTimerRef.current !== null) {\n clearTimeout(pendingTimerRef.current);\n pendingTimerRef.current = null;\n }\n };\n }, [locale, overlayUrlsKey]);\n\n // Tier 2: drains pending tokens and fans out parallel GETs per token.\n // One distinct URL per token (table / view bundle), each cached\n // immutable per release, so a fetch-storm-of-one happens at most once\n // per token per session — and zero times across sessions for unchanged\n // tables/views.\n const flushPending = useCallback(async () => {\n pendingTimerRef.current = null;\n const tokensToFetch = Array.from(pendingTokensRef.current);\n if (tokensToFetch.length === 0) return;\n pendingTokensRef.current = new Set();\n // Mark queried tokens as tried up-front so concurrent renders don't\n // re-queue them while requests are in flight.\n for (const tok of tokensToFetch) {\n triedTokensRef.current.add(tok);\n }\n\n const thumbs = thumbprintsRef.current;\n const requestLocale = resolvedLocaleRef.current;\n if (!thumbs || !requestLocale) {\n // Manifest hasn't loaded yet (or failed). Re-queue the tokens for\n // the next flush — by then tier-1 should have either succeeded\n // (we'll have thumbprints) or failed permanently (we'll skip again).\n for (const tok of tokensToFetch) {\n triedTokensRef.current.delete(tok);\n pendingTokensRef.current.add(tok);\n }\n return;\n }\n\n const fetches = tokensToFetch.map(async (token) => {\n try {\n if (token.startsWith('tables.')) {\n const tableName = token.substring('tables.'.length);\n const thumb = thumbs.tables[tableName];\n if (!thumb) return null;\n return await pppRef.current.retrieveLocalizationTableBundleAsync(\n tableName,\n requestLocale,\n thumb,\n );\n }\n if (token.startsWith('views.')) {\n const viewId = token.substring('views.'.length);\n const thumb = thumbs.views[viewId];\n if (!thumb) return null;\n return await pppRef.current.retrieveLocalizationViewBundleAsync(\n viewId,\n requestLocale,\n thumb,\n );\n }\n return null;\n } catch (e) {\n // Per-token failures are isolated; one failed table fetch shouldn't\n // strand the rest of the batch. Logged at warn so it surfaces in\n // dev without crashing the render loop.\n // eslint-disable-next-line no-console\n console.warn(`PowerPortalsPro: localization fetch failed for token ${token}`, e);\n // Drop from triedTokensRef so a future render can retry.\n triedTokensRef.current.delete(token);\n return null;\n }\n });\n\n const responses = await Promise.all(fetches);\n\n // Discard if locale changed mid-flight — the cache was reset by the\n // locale-change effect; applying these strings would mix locales.\n if (requestLocale !== resolvedLocaleRef.current) return;\n\n // Every token in this batch is now settled (success or failure)\n // record before merging strings so a boundary observing the\n // settled-set transition sees a consistent state when its\n // re-render runs (cache + settled set in agreement). Notify\n // subscribers AFTER updating the set; a listener that calls\n // isLoaded() during its callback should see the fresh values.\n let settledChanged = false;\n for (const tok of tokensToFetch) {\n if (!settledTokensRef.current.has(tok)) {\n settledTokensRef.current.add(tok);\n settledChanged = true;\n }\n }\n\n const merged: Record<string, string> = {};\n for (const data of responses) {\n if (data === null) continue;\n Object.assign(merged, flattenStrings(data));\n }\n if (Object.keys(merged).length > 0) {\n setCache((prev) => ({ ...prev, ...merged }));\n }\n if (settledChanged) {\n notifyListeners();\n }\n }, [notifyListeners]);\n\n // Bridge `flushPending` back to the tier-1 effect via the ref.\n // Identity is stable across renders (useCallback deps are stable), so\n // this assignment is a no-op after the first render — but keeping it\n // unconditional lets us drop the ref check if the dependency surface\n // ever expands.\n flushPendingRef.current = flushPending;\n\n // Runtime-baseline-change effect: when the `prefixes` prop changes\n // after tier-1 has already settled, queue the new tokens. On the\n // initial mount this is a no-op because tier-1 hasn't settled yet\n // the tier-1 async block handles the first queueing after the\n // default bundle resolves. On a real prop change (rare; most apps\n // declare prefixes once at the root), this picks up any new tokens\n // and feeds them through the standard pipeline.\n useEffect(() => {\n // Skip until tier-1 has settled — gauged by sentinel presence in\n // the settled set. Without the sentinel we can't yet fetch\n // per-resource bundles (thumbprints aren't loaded).\n if (!settledTokensRef.current.has(DEFAULT_BUNDLE_SENTINEL)) return;\n let added = false;\n for (const token of baselineTokensRef.current) {\n if (triedTokensRef.current.has(token)) continue;\n if (pendingTokensRef.current.has(token)) continue;\n pendingTokensRef.current.add(token);\n added = true;\n }\n if (pendingTokensRef.current.size > 0 && pendingTimerRef.current === null) {\n pendingTimerRef.current = setTimeout(flushPending, MISSING_KEY_FLUSH_DELAY_MS);\n }\n // Notify subscribers so a boundary watching the baselines re-evaluates\n // `isLoaded` (new baselines may have flipped its check to false).\n if (added) notifyListeners();\n }, [baselinePrefixesKey, flushPending, notifyListeners]);\n\n // Stable callback that queues a cache-miss key for the next batched flush.\n // Coerces the key into its owning table/view token first; non-coerceable\n // keys (raw `app.*`, `components.*`, etc.) are silently dropped — those\n // are covered by the static default bundle, so a miss means the key\n // genuinely doesn't exist (typo, removed-but-still-referenced, etc.).\n // The `onMissingKey` warning still fires for those, surfacing the typo\n // during dev.\n const queueMissingKey = useCallback(\n (key: string) => {\n const token = coerceKeyToToken(key);\n if (token === null) return;\n if (triedTokensRef.current.has(token)) return;\n if (pendingTokensRef.current.has(token)) return;\n pendingTokensRef.current.add(token);\n if (pendingTimerRef.current === null) {\n pendingTimerRef.current = setTimeout(flushPending, MISSING_KEY_FLUSH_DELAY_MS);\n }\n },\n [flushPending],\n );\n\n // Combined missing-key handler — fires the consumer's `onMissingKey`\n // (typically a dev warning) AND queues the key for endpoint resolution.\n // `createLocalizer` already dedupes via its own warnedKeys Set, so each\n // missing key fires the consumer's hook at most once.\n const onMissingKeyCombined = useCallback(\n (key: string) => {\n onMissingKey(key);\n queueMissingKey(key);\n },\n [onMissingKey, queueMissingKey],\n );\n\n // The exposed Localizer is a fresh `createLocalizer` over the current\n // cache — re-built on cache changes so consumers re-render and pick up\n // newly-resolved strings. Identity-stable across renders within a single\n // cache state, so unaffected components don't re-render needlessly.\n const localizer = useMemo<Localizer>(\n () => createLocalizer(cache, { onMissingKey: onMissingKeyCombined }),\n [cache, onMissingKeyCombined],\n );\n\n // Imperative prefetcher exposed via context — `useLocalization` calls\n // `prefetch`, `<LocalizationBoundary>` calls both `prefetch` and\n // `isLoaded` (via `useLocalizationLoaded`'s `useSyncExternalStore`).\n // Identity-stable across renders so the LocalizationPrefetcherContext\n // value doesn't churn — the inner methods close over refs (settled set,\n // pending queue, listeners) so they always see fresh state without\n // needing to be rebuilt on cache changes. `flushPending` is captured\n // because it's the actual fetch-driver, and its identity is stable\n // across renders (depends only on `notifyListeners`, which is stable).\n const prefetcher = useMemo<LocalizationPrefetcher>(\n () => ({\n prefetch(prefixes) {\n for (const prefix of prefixes) {\n const token = coercePrefixToToken(prefix);\n if (token === null) continue;\n if (triedTokensRef.current.has(token)) continue;\n if (pendingTokensRef.current.has(token)) continue;\n pendingTokensRef.current.add(token);\n }\n // Schedule a flush if there's queued work and no timer is in\n // flight. Checked AFTER the add loop (not gated on whether we\n // added anything new) so that a queue surviving a prior\n // cleared timer — e.g. StrictMode's double-effect-invocation\n // where the tier-1 cleanup clears the timer between runs\n // gets re-armed on the second prefetch invocation.\n if (pendingTokensRef.current.size > 0 && pendingTimerRef.current === null) {\n pendingTimerRef.current = setTimeout(flushPending, MISSING_KEY_FLUSH_DELAY_MS);\n }\n },\n isLoaded(prefixes) {\n // Uniform membership check: every token we expect (the tier-1\n // sentinel, every baseline token, every coerced argument\n // token) must be in the settled set. Tier-1 isn't a special\n // case — it's just \"another thing we wait for,\" represented as\n // the `DEFAULT_BUNDLE_SENTINEL` token added to settled set\n // after the default bundle's fetch attempt resolves\n // (success OR failure — failures count as \"tried, move on\" so\n // a flaky endpoint can't strand a boundary).\n // Without the sentinel check, a `<LocalizationBoundary>` whose\n // only declared prefixes are `app.*` / `components.*`\n // (default-bundle territory, coerce to null) would render\n // children before the default bundle resolves — undoing the\n // whole point of the boundary on cold-load first paint.\n if (!settledTokensRef.current.has(DEFAULT_BUNDLE_SENTINEL)) return false;\n\n // Baseline prefixes the provider declared — the boundary\n // inherits these for free. Pre-coerced once per\n // `baselinePrefixesKey` change, so this is a cheap walk.\n for (const token of baselineTokensRef.current) {\n if (!settledTokensRef.current.has(token)) return false;\n }\n\n // Caller-declared prefixes (`useLocalization` or\n // `<LocalizationBoundary prefixes={…}>`). Non-coerceable\n // prefixes (`app.*` / `components.*`) are treated as already\n // loaded because the default bundle covers them — and we've\n // already gated on the tier-1 sentinel, so the cache\n // definitely has them by this point (or the fetch failed and\n // we've accepted that).\n for (const prefix of prefixes) {\n const token = coercePrefixToToken(prefix);\n if (token === null) continue;\n if (!settledTokensRef.current.has(token)) return false;\n }\n return true;\n },\n subscribe(listener) {\n listenersRef.current.add(listener);\n return () => {\n listenersRef.current.delete(listener);\n };\n },\n }),\n [flushPending],\n );\n\n // Memoize the manifest context value so its identity doesn't churn\n // on every render — only when `supportedLocales` actually changes\n // (initial fetch lands, locale change resets to undefined and re-\n // fetches). Without the memo, every descendant consumer would re-\n // render on each parent render.\n const manifestContextValue = useMemo<LocalizationManifestState>(\n () => ({ supportedLocales, urlCultureStrategy }),\n [supportedLocales, urlCultureStrategy],\n );\n\n return (\n <LocalizationManifestContext.Provider value={manifestContextValue}>\n <LocalizationPrefetcherContext.Provider value={prefetcher}>\n <LocalizerProvider value={localizer}>{children}</LocalizerProvider>\n </LocalizationPrefetcherContext.Provider>\n </LocalizationManifestContext.Provider>\n );\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from 'react';\nimport type { ReactNode } from 'react';\n\n/**\n * Source of unsaved-changes state on the page. Each topmost\n * {@link MainContext} / {@link RecordContext} registers one of these so a\n * page-wide guard (browser beforeunload, router-blocker) can answer \"is\n * anything on this page dirty?\" without having to walk every framework\n * subtree itself.\n *\n * Nested contexts don't register — their dirty state already bubbles up\n * to the topmost MainContext through the existing\n * {@link MainContextValue.registerDescendant} plumbing, so registering\n * nested contexts here would double-count.\n */\nexport interface UnsavedChangesReporter {\n /**\n * Snapshot getter — invoked synchronously by the registry on every\n * subscriber notification. Read-through to the reporter's live\n * `isDirty` so subscribers see the latest aggregate without needing to\n * re-register on every state flip.\n */\n readonly isDirty: () => boolean;\n}\n\n/**\n * App-wide registry of {@link UnsavedChangesReporter}s. Owned by\n * {@link PowerPortalsProProvider}; consumed by:\n *\n * - The auto-registration effect inside topmost\n * {@link MainContext} / {@link RecordContext}, which adds a reporter on\n * mount + removes it on unmount.\n * - `<RouterUnsavedChangesGuard>` (in `@powerportalspro/react-fluent`),\n * which subscribes via {@link useIsAnyDirty} and gates `useBlocker` on\n * the aggregate.\n *\n * Router-agnostic by design — the registry knows nothing about\n * react-router, history, or navigation events. That coupling lives in\n * the optional UI package so consumers using a different router (or\n * none at all) aren't forced into a dependency they don't need.\n */\nexport interface UnsavedChangesRegistry {\n /**\n * Add a reporter to the registry. Returns the deregister function;\n * call it in the same effect's cleanup so the reporter doesn't outlive\n * the component that owns it.\n */\n readonly register: (reporter: UnsavedChangesReporter) => () => void;\n /**\n * Subscribe to \"the set of dirty reporters might have changed\" events.\n * Fires when reporters are added or removed, AND when a registered\n * reporter's wrapping {@link MainContext}'s `isDirty` flips (the\n * reporter's `isDirty` getter closes over the live value).\n *\n * Shape compatible with {@link useSyncExternalStore} so the consuming\n * hook ({@link useIsAnyDirty}) can use it directly.\n */\n readonly subscribe: (listener: () => void) => () => void;\n /**\n * Walks every registered reporter and returns `true` as soon as one\n * reports dirty. Used as `getSnapshot` for {@link useSyncExternalStore}\n * and called imperatively from the beforeunload handler.\n */\n readonly isAnyDirty: () => boolean;\n /**\n * Forces a notification fire — used by topmost MainContexts after\n * their internal `isDirty` aggregate changes, so subscribers re-read\n * the registry's `isAnyDirty()` and pick up the new value. Without\n * this, the registry would only notify on register/deregister and\n * stay silent through the user's actual edits.\n */\n readonly notifyDirtyChanged: () => void;\n}\n\nconst UnsavedChangesRegistryContext = createContext<UnsavedChangesRegistry | null>(null);\nUnsavedChangesRegistryContext.displayName = 'UnsavedChangesRegistry';\n\nexport interface UnsavedChangesRegistryProviderProps {\n children: ReactNode;\n}\n\n/**\n * Mounts an {@link UnsavedChangesRegistry} for its subtree. Auto-mounted\n * by {@link PowerPortalsProProvider}; consumers don't normally render it\n * directly.\n */\nexport function UnsavedChangesRegistryProvider({ children }: UnsavedChangesRegistryProviderProps) {\n // Set keeps insertion order which doesn't matter for `isAnyDirty()` but\n // is useful for any future \"first dirty wins\" diagnostics. Stored in a\n // ref because mutations don't need to drive re-renders here — the\n // listener fan-out below handles that.\n const reportersRef = useRef<Set<UnsavedChangesReporter>>(new Set());\n const listenersRef = useRef<Set<() => void>>(new Set());\n\n const notify = useCallback(() => {\n for (const listener of listenersRef.current) listener();\n }, []);\n\n const register = useCallback(\n (reporter: UnsavedChangesReporter) => {\n reportersRef.current.add(reporter);\n notify();\n return () => {\n reportersRef.current.delete(reporter);\n notify();\n };\n },\n [notify],\n );\n\n const subscribe = useCallback((listener: () => void) => {\n listenersRef.current.add(listener);\n return () => {\n listenersRef.current.delete(listener);\n };\n }, []);\n\n const isAnyDirty = useCallback(() => {\n for (const reporter of reportersRef.current) {\n if (reporter.isDirty()) return true;\n }\n return false;\n }, []);\n\n const registry = useMemo<UnsavedChangesRegistry>(\n () => ({ register, subscribe, isAnyDirty, notifyDirtyChanged: notify }),\n [register, subscribe, isAnyDirty, notify],\n );\n\n return (\n <UnsavedChangesRegistryContext.Provider value={registry}>\n {children}\n </UnsavedChangesRegistryContext.Provider>\n );\n}\n\n/**\n * Returns the nearest ancestor {@link UnsavedChangesRegistry}, or `null`\n * when none is mounted. Framework components ({@link MainContext}) use\n * this to opt into reporting their dirty state; guard components\n * ({@code RouterUnsavedChangesGuard}) use {@link useIsAnyDirty} which\n * already null-checks internally.\n */\nexport function useUnsavedChangesRegistry(): UnsavedChangesRegistry | null {\n return useContext(UnsavedChangesRegistryContext);\n}\n\n/**\n * Reactive read of \"is anything on this page dirty?\" — wraps\n * {@link useSyncExternalStore} against the surrounding\n * {@link UnsavedChangesRegistry}. Returns `false` when no registry is\n * mounted (e.g. a tree that hasn't been wrapped in\n * {@link PowerPortalsProProvider} yet) so guard components can default\n * to \"don't block\" rather than throw.\n *\n * Use this from any component that wants to react to page-wide unsaved\n * state — typically the router-blocker guard, but also custom dirty\n * indicators in app chrome.\n */\nexport function useIsAnyDirty(): boolean {\n const registry = useUnsavedChangesRegistry();\n const subscribe = useCallback(\n (listener: () => void) => {\n if (!registry) return () => {};\n return registry.subscribe(listener);\n },\n [registry],\n );\n const getSnapshot = useCallback(() => {\n if (!registry) return false;\n return registry.isAnyDirty();\n }, [registry]);\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n","import { AuthClient, LoginResult, PowerPortalsProClient, SwitchIdentityResult, Transport } from '@powerportalspro/core';\nimport type { LoginRequest, SwitchIdentityResponse } from '@powerportalspro/core';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport {\n InMemoryOrganizationSettingsCache,\n InMemoryTableMetadataCache,\n InMemoryViewMetadataCache,\n InMemoryViewsForTableCache,\n} from './caches/in-memory-cache.js';\nimport type {\n OrganizationSettingsCache,\n TableMetadataCache,\n ViewMetadataCache,\n ViewsForTableCache,\n} from './caches/types.js';\nimport {\n AuthStatus,\n PowerPortalsProContext,\n type AuthState,\n type PowerPortalsProContextValue,\n} from './context.js';\nimport { DefaultLocalizerProvider, LocaleProvider, useLocale } from './localizer.js';\nimport { UnsavedChangesRegistryProvider } from './unsaved-changes-registry.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\nexport interface PowerPortalsProProviderProps {\n children: ReactNode;\n /**\n * Optional pre-constructed {@link Transport} for the AuthClient and PowerPortalsProClient\n * to share. Pass one to set a non-default `baseUrl`, inject a custom `fetch`\n * implementation for tests, etc. When omitted, a default transport is constructed\n * same-origin, browser fetch, cookie credentials included.\n *\n * The provider holds a stable reference to the transport for its lifetime — passing\n * a new instance on every render is wasteful, so memoize it at the call site or\n * construct it at module scope. Changes to this prop after first render are ignored.\n */\n transport?: Transport;\n /**\n * Single override JSON layered on top of the framework defaults. Use this\n * when the consumer app has one additional strings file (e.g.\n * `/localization/app.en.json`) with app-specific strings or English\n * overrides. The framework defaults remain the base.\n *\n * Pass `localizationUrl={false}` to opt out of the auto-mounted localizer\n * entirely (e.g. when adapting i18next via your own `<LocalizerProvider>`).\n */\n localizationUrl?: string | false;\n /**\n * Multiple override JSONs layered on top of the framework defaults, in\n * order. Later URLs override earlier ones for matching keys. Use this when\n * the consumer app has more than one strings file (e.g. one per page,\n * one per feature module).\n *\n * Combined with `localizationUrl`, both are appended to the framework\n * defaults — `localizationUrl` first, then `localizationUrls`.\n */\n localizationUrls?: readonly string[];\n /**\n * Baseline localization prefixes the auto-mounted\n * `<DefaultLocalizerProvider>` should eagerly load alongside the\n * default bundle. Forwards directly to that provider's `prefixes`\n * prop — see its doc for the full semantics. Ignored when\n * `localizationUrl={false}` (consumer is wiring their own\n * localizer adapter).\n *\n * Typical use: declare baseline tables / views the entire app\n * touches (header lookups, navigation chrome) so every\n * `<LocalizationBoundary>` inherits them for free.\n */\n localizationPrefixes?: readonly string[];\n /**\n * Custom {@link TableMetadataCache} implementation. When omitted, the\n * provider mounts an in-memory cache with in-flight-promise dedup.\n * Override with a localStorage-backed implementation for cross-session\n * persistence, or a mock for tests.\n */\n tableMetadataCache?: TableMetadataCache;\n /**\n * Custom {@link ViewMetadataCache} implementation. Same defaulting\n * behavior as {@link tableMetadataCache}.\n */\n viewMetadataCache?: ViewMetadataCache;\n /**\n * Custom {@link ViewsForTableCache} implementation. Same defaulting\n * behavior as {@link tableMetadataCache}.\n */\n viewsForTableCache?: ViewsForTableCache;\n /**\n * Custom {@link OrganizationSettingsCache} implementation. Same\n * defaulting behavior as {@link tableMetadataCache}.\n */\n organizationSettingsCache?: OrganizationSettingsCache;\n}\n\n/**\n * Top-level provider for `@powerportalspro/react`. Constructs an {@link AuthClient}\n * and a {@link PowerPortalsProClient} (sharing one {@link Transport}) and manages the\n * auth state machine — `loading` until the first `/api/auth/me` response, then\n * `anonymous` or `authenticated` for the rest of the session.\n *\n * Wrap the app once near the root:\n * ```tsx\n * <PowerPortalsProProvider>\n * <App />\n * </PowerPortalsProProvider>\n * ```\n *\n * Hooks ({@link useAuth}, {@link usePowerPortalsPro}, {@link useRecord}, …) read from\n * this provider's context.\n */\nexport function PowerPortalsProProvider({\n children,\n transport,\n localizationUrl,\n localizationUrls,\n localizationPrefixes,\n tableMetadataCache,\n viewMetadataCache,\n viewsForTableCache,\n organizationSettingsCache,\n}: PowerPortalsProProviderProps) {\n // Capture the transport at first render; changes after mount are ignored. This avoids\n // the dependency-loop trap where every parent render passes a new TransportOptions\n // object literal and we'd reconstruct AuthClient + PowerPortalsProClient + auth state\n // every time. Consumers who need to swap the transport should remount the provider.\n const transportRef = useRef<Transport | null>(null);\n if (transportRef.current === null) {\n transportRef.current = transport ?? new Transport();\n }\n const transportInstance = transportRef.current;\n\n const auth = useMemo(() => new AuthClient(transportInstance), [transportInstance]);\n const ppp = useMemo(() => new PowerPortalsProClient(transportInstance), [transportInstance]);\n // Metadata caches. Consumer can override (e.g. with a localStorage-\n // backed cache) via props; default to in-memory + in-flight-promise\n // dedup. One instance per `ppp` so two transports get two caches.\n const resolvedTableMetadataCache = useMemo(\n () => tableMetadataCache ?? new InMemoryTableMetadataCache(ppp),\n [ppp, tableMetadataCache],\n );\n const resolvedViewMetadataCache = useMemo(\n () => viewMetadataCache ?? new InMemoryViewMetadataCache(ppp),\n [ppp, viewMetadataCache],\n );\n const resolvedViewsForTableCache = useMemo(\n () => viewsForTableCache ?? new InMemoryViewsForTableCache(ppp),\n [ppp, viewsForTableCache],\n );\n const resolvedOrganizationSettingsCache = useMemo(\n () => organizationSettingsCache ?? new InMemoryOrganizationSettingsCache(ppp),\n [ppp, organizationSettingsCache],\n );\n\n const [authState, setAuthState] = useState<AuthState>({ status: AuthStatus.Loading });\n\n const refreshAuth = useCallback(async () => {\n try {\n const me = await auth.getCurrentUserAsync();\n if (me.isAuthenticated) {\n setAuthState({ status: AuthStatus.Authenticated, user: me });\n } else {\n setAuthState({ status: AuthStatus.Anonymous });\n }\n } catch {\n // Network failures, problem+json, anything else: treat as anonymous so the\n // app can render the sign-in flow rather than spinning forever.\n setAuthState({ status: AuthStatus.Anonymous });\n }\n }, [auth]);\n\n const login = useCallback(\n async (request: LoginRequest) => {\n const response = await auth.loginAsync(request);\n if (response.result === LoginResult.Success) {\n await refreshAuth();\n }\n return response;\n },\n [auth, refreshAuth],\n );\n\n const logout = useCallback(async () => {\n await auth.logoutAsync();\n setAuthState({ status: AuthStatus.Anonymous });\n }, [auth]);\n\n // Swaps the auth cookie for the user's alt-identity sibling. On a successful\n // swap the cookie is replaced server-side and we re-run `/api/auth/me` so the\n // in-memory `authState.user` reflects the new identity (table name, roles,\n // alt-identity hints, etc.). Other outcomes (`NoAltIdentity`, `AltIdentityNotFound`,\n // `NotAuthenticated`) leave the existing state untouched — the caller decides\n // what to surface to the user via the returned response.\n const switchIdentity = useCallback(async (): Promise<SwitchIdentityResponse> => {\n const response = await auth.switchIdentityAsync();\n if (response.result === SwitchIdentityResult.Switched) {\n await refreshAuth();\n }\n return response;\n }, [auth, refreshAuth]);\n\n useEffect(() => {\n refreshAuth();\n }, [refreshAuth]);\n\n const value = useMemo<PowerPortalsProContextValue>(\n () => ({\n transport: transportInstance,\n auth,\n ppp,\n tableMetadataCache: resolvedTableMetadataCache,\n viewMetadataCache: resolvedViewMetadataCache,\n viewsForTableCache: resolvedViewsForTableCache,\n organizationSettingsCache: resolvedOrganizationSettingsCache,\n authState,\n refreshAuth,\n login,\n logout,\n switchIdentity,\n }),\n [\n transportInstance,\n auth,\n ppp,\n resolvedTableMetadataCache,\n resolvedViewMetadataCache,\n resolvedViewsForTableCache,\n resolvedOrganizationSettingsCache,\n authState,\n refreshAuth,\n login,\n logout,\n switchIdentity,\n ],\n );\n\n // Auto-mount the default localizer unless the consumer opted out via\n // `localizationUrl={false}` (typically to wire i18next or another lib via\n // their own <LocalizerProvider>). The default provider renders children\n // immediately with a key-fallback localizer, then upgrades when the JSON\n // resolves — so the first paint isn't gated on a network round-trip.\n // Build the URL chain: framework defaults always first; consumer's\n // `localizationUrl` (single) and `localizationUrls` (multi) layer on top\n // in that order. Later wins for matching keys.\n // ALSO auto-mount `<LocaleProvider>` around the localizer so locale\n // switching via `<LanguageDropdown>` (or any other consumer using\n // `useLocale().setLocale`) actually works. Without a LocaleProvider,\n // `useLocale()` falls back to a no-op `setLocale` and selecting a\n // language in the dropdown silently does nothing — the locale-switch\n // path is dead, and DefaultLocalizerProvider stays pinned to 'en'.\n // Consumers managing locale themselves (e.g. wiring i18next or\n // providing their own LocaleProvider higher in the tree) opt out of\n // BOTH the localizer and the locale provider with\n // `localizationUrl={false}` — they're co-mounted because they're a\n // matched pair: the localizer subscribes to the locale provider for\n // re-fetches on change.\n let localizationContent: ReactNode;\n if (localizationUrl === false) {\n localizationContent = children;\n } else {\n // The default provider hits the server endpoint\n // (`ppp.retrieveLocalizedStringsAsync(locale)`) which returns the merged\n // metadata + framework JSON + server-side overrides. `localizationUrl` /\n // `localizationUrls` add extra client-side overlays on top, layered in\n // declaration order.\n const overlayUrls: string[] = [];\n if (typeof localizationUrl === 'string') overlayUrls.push(localizationUrl);\n if (localizationUrls) overlayUrls.push(...localizationUrls);\n localizationContent = (\n <LocaleProvider>\n <CacheLocaleSync />\n <DefaultLocalizerProvider\n {...(overlayUrls.length > 0 && { urls: overlayUrls })}\n {...(localizationPrefixes !== undefined && { prefixes: localizationPrefixes })}\n >\n {children}\n </DefaultLocalizerProvider>\n </LocaleProvider>\n );\n }\n\n return (\n <PowerPortalsProContext.Provider value={value}>\n <UnsavedChangesRegistryProvider>{localizationContent}</UnsavedChangesRegistryProvider>\n </PowerPortalsProContext.Provider>\n );\n}\n\n/**\n * Internal helper mounted inside `<LocaleProvider>` whenever the\n * provider auto-mounts the localization stack. Watches the active\n * locale and clears the metadata caches (table / view / views-for-\n * table / environment-file-settings) on every change.\n *\n * The caches store server responses whose payload includes localized\n * display names that were resolved against the cookie / request\n * culture at the time of the fetch. Keying by name alone — as the\n * default in-memory caches do — would surface stale display names\n * from the previous locale after a `<LanguageDropdown>` selection,\n * even though the cookie + server response are correct.\n *\n * \"Clear on change\" was chosen over \"key by `{name, locale}`\" because\n * the cache interface stays simple (no per-call locale plumbing) and\n * the trade-off — a re-fetch when the user toggles back to a prior\n * locale — is negligible for these endpoints. Consumers shipping\n * their own cache implementations via the provider's\n * `tableMetadataCache` / etc. props benefit from the same wiring:\n * the framework calls `clear()` on them on locale change, so the\n * custom cache just needs to implement that method per the\n * `TableMetadataCache` interface contract.\n */\nfunction CacheLocaleSync(): null {\n const { locale } = useLocale();\n const {\n tableMetadataCache,\n viewMetadataCache,\n viewsForTableCache,\n organizationSettingsCache,\n } = usePowerPortalsPro();\n // Skip the first render — the caches are empty and the initial\n // locale resolution doesn't constitute a \"change.\" Clearing here\n // would force every consumer that mounts a record / grid below to\n // re-fetch on a fresh-cache miss that was already going to fire.\n const isFirstRun = useRef(true);\n useEffect(() => {\n if (isFirstRun.current) {\n isFirstRun.current = false;\n return;\n }\n tableMetadataCache.clear();\n viewMetadataCache.clear();\n viewsForTableCache.clear();\n // OrganizationSettings carries the localized Currency.name. Singleton-\n // shaped cache (no key) — `invalidate()` is the equivalent of\n // `clear()` for it.\n organizationSettingsCache.invalidate();\n }, [\n locale,\n tableMetadataCache,\n viewMetadataCache,\n viewsForTableCache,\n organizationSettingsCache,\n ]);\n return null;\n}\n","import type { LoginRequest, LoginResponse, SwitchIdentityResponse } from '@powerportalspro/core';\nimport type { AuthState } from './context.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\n/**\n * Auth state + actions, surfaced as a discriminated union spread. Compare against\n * the bare string literals or against {@link AuthStatus} constants — both are\n * type-narrowed equivalently:\n *\n * ```tsx\n * import { AuthStatus, useAuth } from '@powerportalspro/react';\n *\n * const auth = useAuth();\n * if (auth.status === AuthStatus.Loading) return <Spinner />;\n * if (auth.status === AuthStatus.Anonymous) return <SignInButton onClick={() => auth.login(...)} />;\n * if (auth.status === AuthStatus.Authenticated) return <span>Hi, {auth.user.email}</span>;\n * ```\n *\n * Behind the scenes the state lives on the {@link PowerPortalsProProvider} and\n * propagates via context, so all `useAuth` calls in the tree see the same value\n * and re-render on transitions.\n *\n * `login` mirrors {@link AuthClient.loginAsync} — same request/response shapes\n * but additionally re-fetches `/api/auth/me` on `Success` so the auth state\n * transitions to `authenticated` without an extra round-trip from the consumer.\n *\n * `logout` mirrors {@link AuthClient.logoutAsync} and transitions the state to\n * `anonymous` immediately on success.\n */\nexport type UseAuthResult = AuthState & {\n /** Re-runs `/api/auth/me` and updates the state. Call after side effects that may have changed the user (server-driven role updates, contact-record edits, etc.). */\n refresh: () => Promise<void>;\n /** Submits credentials. On `LoginResult.Success`, the state transitions to `authenticated`. */\n login: (request: LoginRequest) => Promise<LoginResponse>;\n /** Clears the auth cookie and transitions the state to `anonymous`. */\n logout: () => Promise<void>;\n /**\n * Swaps the auth cookie for the user's alt-identity sibling. Returns the\n * server's `SwitchIdentityResponse` so the caller can surface non-success\n * outcomes (`NoAltIdentity`, `AltIdentityNotFound`, `NotAuthenticated`).\n * On `Switched`, the in-memory auth state is refreshed automatically.\n */\n switchIdentity: () => Promise<SwitchIdentityResponse>;\n};\n\nexport function useAuth(): UseAuthResult {\n const { authState, refreshAuth, login, logout, switchIdentity } = usePowerPortalsPro();\n return {\n ...authState,\n refresh: refreshAuth,\n login,\n logout,\n switchIdentity,\n };\n}\n","import { type ReactNode } from 'react';\nimport { AuthStatus } from './context.js';\nimport { useAuth } from './use-auth.js';\n\nexport interface AuthorizedViewProps {\n /**\n * Children rendered when the principal is authenticated (and, if {@link roles}\n * is supplied, holds at least one of the listed roles).\n */\n children: ReactNode;\n /**\n * Optional role gate. When supplied, the principal must hold AT LEAST ONE of\n * the listed roles for the children to render\n * `<AuthorizeView Roles=\"Admin,Manager\">` (comma-separated = OR).\n *\n * Roles arrive on `auth.user.roles` from `/api/auth/me`. Per-role claims are\n * only emitted for systemuser-backed principals — contacts don't have roles\n * so a role gate also implicitly limits the surface to staff users.\n */\n roles?: string | readonly string[];\n /**\n * Rendered when the principal is anonymous, the auth state is still loading,\n * or the role check fails. Defaults to `null` (renders nothing).\n */\n fallback?: ReactNode;\n}\n\n/**\n * Headless auth gate — Renders\n * {@link AuthorizedViewProps.children} only when the principal is authenticated;\n * if {@link AuthorizedViewProps.roles} is supplied, the principal must hold at\n * least one of the listed roles. Otherwise renders {@link AuthorizedViewProps.fallback}\n * (default `null`).\n *\n * Loading is treated as \"not yet authorized\" — fallback renders during the\n * initial `/api/auth/me` round-trip so consumers don't briefly flash the\n * authorized UI before the cookie principal is hydrated.\n *\n * @example\n * ```tsx\n * <AuthorizedView>\n * <Link to=\"/Account/Manage\">Manage account</Link>\n * </AuthorizedView>\n *\n * <AuthorizedView roles=\"SystemAdmin\">\n * <Link to=\"/SiteAdmin\">Site admin</Link>\n * </AuthorizedView>\n *\n * <AuthorizedView roles={[\"Admin\", \"Manager\"]} fallback={<span>Restricted</span>}>\n * <SensitiveTools />\n * </AuthorizedView>\n * ```\n */\nexport function AuthorizedView({ children, roles, fallback = null }: AuthorizedViewProps) {\n const auth = useAuth();\n\n if (auth.status !== AuthStatus.Authenticated) return <>{fallback}</>;\n\n if (roles !== undefined) {\n const required = typeof roles === 'string' ? [roles] : roles;\n if (required.length > 0) {\n const userRoles = auth.user.roles ?? [];\n const hasAny = required.some((r) => userRoles.includes(r));\n if (!hasAny) return <>{fallback}</>;\n }\n }\n\n return <>{children}</>;\n}\n","import type { PowerPortalsProError } from '@powerportalspro/core';\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\n/**\n * Status values for query hooks ({@link useRecord}, {@link useFetchRecords},\n * {@link useTableMetadata}, …) and {@link RecordContext}.\n *\n * Hybrid shape — bare string-literal union as the type for clean narrowing in\n * `if (q.status === 'success')`-style branching, plus a const-object export so\n * consumers who prefer the refactor-safe form can write `q.status === QueryStatus.Success`.\n * Both compare against the same runtime string. Matches the auto-generated\n * `LoginResult`-style pattern from `@powerportalspro/core`.\n */\nexport const QueryStatus = {\n Loading: 'loading',\n Success: 'success',\n Error: 'error',\n} as const;\nexport type QueryStatus = (typeof QueryStatus)[keyof typeof QueryStatus];\n\n/**\n * Discriminated state surface returned by every query hook. `data` and `error` are\n * always present as fields (so consumers can read them at any time) but only carry\n * meaningful values when `status` matches. `refetch()` re-runs the query with the\n * current dependency values, regardless of current state.\n *\n * Loosely mirrors react-query's API shape so consumers familiar with that library\n * find the contract familiar — but with no caching, no dedup, no shared cache between\n * components. Each hook call owns its own fetch lifecycle.\n *\n * **`status` vs. `isFetching`** — `status` describes the *outcome* (what's\n * in `data`): `'loading'` = no data yet, `'success'` = data is populated,\n * `'error'` = the most recent fetch threw. `isFetching` describes the\n * *current request* — true whenever a network call is in flight,\n * regardless of whether prior data is still being shown. With\n * `keepPreviousData` enabled (see {@link useAsyncResource}), a deps-change\n * triggered refetch keeps `status='success'` and the prior `data`\n * populated while `isFetching` flips to true, so consumers can render a\n * scrim overlay instead of wiping the UI.\n */\nexport interface QueryResult<T> {\n readonly status: QueryStatus;\n readonly data: T | undefined;\n readonly error: PowerPortalsProError | undefined;\n /**\n * True whenever a network request is currently in flight, including\n * refetches triggered by deps changes or explicit `refetch()` calls.\n * Independent of `status`: when `keepPreviousData` is enabled, the hook\n * can be `status='success'` (prior data still on screen) AND\n * `isFetching=true` (new request running) at the same time.\n */\n readonly isFetching: boolean;\n /**\n * Re-runs the query immediately, bypassing the deps comparison. Returns a\n * promise that resolves when the next fetch settles successfully (or\n * rejects if the fetch errors). Awaiting is optional — callers that just\n * want to trigger a refresh can fire-and-forget.\n *\n * If the request is superseded by another deps change or another\n * `refetch()` call before it settles, the returned promise resolves anyway\n * (with the stale-but-final state of this `refetch` call's outcome) — that\n * matches the \"kick + observe\" semantics callers want for a save-then-refresh\n * flow, where the new data landing is what matters, not which trigger\n * caused it.\n */\n readonly refetch: () => Promise<void>;\n}\n\n/**\n * Internal hook used by every public query hook (useRecord, useFetchRecords, …) to\n * implement the loading / error / data state machine on top of an async fetcher.\n *\n * - Reruns when any dep in `deps` changes (consumer-controlled — pass primitive\n * comparable values).\n * - Aborts the in-flight request via AbortController when deps change or the\n * component unmounts.\n * - `enabled: false` puts the hook in the \"success-with-undefined-data\" idle state\n * without firing a request — useful for conditional fetches (e.g. don't fetch\n * until a parent dependency is ready).\n *\n * @param fetcher Receives an AbortSignal and returns the data. Don't memoize this\n * we capture it every render and the deps array drives reruns.\n * @param deps Stable values whose change should trigger a refetch. Treat as a\n * React `useEffect` dependency list.\n * @param enabled When false, the hook returns idle state and skips fetching.\n * @param keepPreviousData When true and a previous fetch succeeded, deps-change\n * refetches don't wipe `status`/`data` back to `'loading'`/`undefined`\n * — instead `status` stays `'success'`, `data` stays populated with\n * the prior result, and `isFetching` flips to true for the duration\n * of the new request. The grid (and any other consumer with already-\n * rendered content) can use this to scrim the UI rather than collapse\n * to a \"loading…\" placeholder. Defaults to false to preserve the\n * strict state-machine behavior every existing consumer relies on.\n */\nexport function useAsyncResource<T>(\n fetcher: (signal: AbortSignal) => Promise<T>,\n deps: readonly unknown[],\n enabled = true,\n keepPreviousData = false,\n): QueryResult<T> {\n type State = Pick<QueryResult<T>, 'status' | 'data' | 'error' | 'isFetching'>;\n const [state, setState] = useState<State>({\n status: enabled ? QueryStatus.Loading : QueryStatus.Success,\n data: undefined,\n error: undefined,\n isFetching: enabled,\n });\n const [refetchTick, setRefetchTick] = useState(0);\n\n // Pending resolvers/rejecters from `refetch()` calls. Drained when the next\n // fetch settles. Stored in a ref (not state) — they're imperative\n // continuations, not part of the rendered UI.\n type PendingResolver = { resolve: () => void; reject: (err: unknown) => void };\n const pendingResolversRef = useRef<PendingResolver[]>([]);\n\n useEffect(() => {\n if (!enabled) {\n setState({\n status: QueryStatus.Success,\n data: undefined,\n error: undefined,\n isFetching: false,\n });\n // Disabled state still resolves any pending refetch promises so awaiters\n // don't hang forever when a parent gates the query off mid-flight.\n const resolvers = pendingResolversRef.current;\n pendingResolversRef.current = [];\n for (const r of resolvers) r.resolve();\n return;\n }\n\n const controller = new AbortController();\n setState((prev) => {\n // `keepPreviousData` keeps the prior successful result on screen while a\n // deps-change refetch is in flight. Without it, every deps change wipes\n // back to loading + undefined — which is what existing consumers assume,\n // so this branch only applies when the caller opts in.\n if (keepPreviousData && prev.status === QueryStatus.Success && prev.data !== undefined) {\n // Already in keep-prior state? Skip the state update entirely — the\n // identity short-circuit avoids a wasted render on every re-run.\n if (prev.isFetching && prev.error === undefined) return prev;\n return { ...prev, error: undefined, isFetching: true };\n }\n // Same short-circuit on the fresh-loading path: if we're already in the\n // exact loading-with-fetching state the next branch produces, returning\n // prev avoids a needless render. (Original optimization in the pre-\n // isFetching version of this hook — kept to preserve perf invariants\n // every downstream test was depending on.)\n if (\n prev.status === QueryStatus.Loading &&\n prev.data === undefined &&\n prev.error === undefined &&\n prev.isFetching\n ) {\n return prev;\n }\n return {\n status: QueryStatus.Loading,\n data: undefined,\n error: undefined,\n isFetching: true,\n };\n });\n\n fetcher(controller.signal).then(\n (data) => {\n if (!controller.signal.aborted) {\n setState({\n status: QueryStatus.Success,\n data,\n error: undefined,\n isFetching: false,\n });\n }\n // Drain pending refetch resolvers whether or not this fetch\n // was superseded. The caller of `refetch()` wants to know the\n // operation FINISHED — they don't care whether a later\n // refetch happened to invalidate this one mid-flight. Without\n // this, an aborted-mid-flight refetch leaves its resolver\n // dangling forever and any `await refetch()` in\n // save-then-refresh code hangs the test.\n const resolvers = pendingResolversRef.current;\n pendingResolversRef.current = [];\n for (const r of resolvers) r.resolve();\n },\n (err: unknown) => {\n // Aborted requests aren't errors — they happen on every deps-change/unmount.\n // Drain resolvers (resolve, not reject) so awaiters don't hang\n // when the underlying fetch was just superseded.\n if (controller.signal.aborted || (err instanceof Error && err.name === 'AbortError')) {\n const resolvers = pendingResolversRef.current;\n pendingResolversRef.current = [];\n for (const r of resolvers) r.resolve();\n return;\n }\n // Errors wipe `data` regardless of keepPreviousData — a status='error'\n // state with stale data on screen is a confusing surface for consumers\n // (which one drives rendering?) and react-query handles it the same way.\n setState({\n status: QueryStatus.Error,\n data: undefined,\n error: err as PowerPortalsProError,\n isFetching: false,\n });\n const resolvers = pendingResolversRef.current;\n pendingResolversRef.current = [];\n for (const r of resolvers) r.reject(err);\n },\n );\n\n return () => {\n controller.abort();\n // Drain pending refetch resolvers on abort so callers awaiting\n // a `refetch()` promise don't hang when the request gets\n // cancelled by a deps change or unmount. Resolve (not reject)\n // the typical caller is \"save then refresh\" code that doesn't\n // care whether the post-save fetch completed or was superseded;\n // they only care that the operation finished.\n const resolvers = pendingResolversRef.current;\n pendingResolversRef.current = [];\n for (const r of resolvers) r.resolve();\n };\n // We intentionally rely on the consumer-supplied deps array — fetcher is captured\n // each render but only the deps drive when we re-fire. ESLint can't see through\n // this to validate, so disabling exhaustive-deps for this effect is correct.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [...deps, enabled, refetchTick]);\n\n const refetch = useCallback((): Promise<void> => {\n return new Promise<void>((resolve, reject) => {\n pendingResolversRef.current.push({ resolve, reject });\n setRefetchTick((n) => n + 1);\n });\n }, []);\n\n return { ...state, refetch };\n}\n","import type { TableRecord } from '@powerportalspro/core';\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\nexport interface UseRecordOptions {\n /** Column logical names to project. Omit for the table's full visible column set. */\n columns?: readonly string[];\n /** When false, the hook stays in the idle (success / data: undefined) state and skips fetching. Defaults to true. */\n enabled?: boolean;\n}\n\n/**\n * Fetches a single Dataverse record by table + id. Re-runs when `tableLogicalName`,\n * `recordId`, or the joined column list changes; aborts in-flight requests on unmount.\n *\n * The `data` field is the raw `TableRecord` envelope (id, tableName, properties,\n * permissions, formattedValues). Drill into `data.properties[columnName]` for typed\n * column values; `data.formattedValues[columnName]` for the display string Dataverse\n * supplied (already formatted in the user's locale).\n *\n * Permission enforcement happens server-side — an unauthorized read surfaces as a\n * `UnauthorizedAccessError` in `error`, not a partial record.\n */\nexport function useRecord(\n tableLogicalName: string,\n recordId: string,\n options?: UseRecordOptions,\n): QueryResult<TableRecord> {\n const { ppp } = usePowerPortalsPro();\n const columns = options?.columns;\n const enabled = options?.enabled ?? true;\n\n return useAsyncResource(\n (signal) => ppp.retrieveRecordAsync(tableLogicalName, recordId, columns, signal),\n // Joining the columns array is the cheapest way to get a stable dep without\n // forcing the consumer to memoize. Column lists are short (typically <20).\n [ppp, tableLogicalName, recordId, columns?.join(',') ?? ''],\n enabled,\n );\n}\n","import type { RetrieveRecordsResponse } from '@powerportalspro/core';\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\nexport interface UseFetchRecordsOptions {\n /** When false, the hook stays in the idle (success / data: undefined) state and skips fetching. Defaults to true. */\n enabled?: boolean;\n}\n\n/**\n * Runs a FetchXML query. Re-runs when the `fetchXml` string changes; aborts\n * in-flight requests on unmount. The query string is the FetchXML XML literal\n * the same format Dataverse natively accepts.\n *\n * `data.records` is the matched record list; `data.pagingCookie` and friends drive\n * the standard FetchXML paging UX. Sorting, filtering, and link-entity joins are all\n * encoded in the FetchXML — for grid scenarios a higher-level hook (in\n * `@powerportalspro/react-fluent`'s eventual `MainGrid`) will manage the FetchXML\n * lifecycle around view changes.\n */\nexport function useFetchRecords(\n fetchXml: string,\n options?: UseFetchRecordsOptions,\n): QueryResult<RetrieveRecordsResponse> {\n const { ppp } = usePowerPortalsPro();\n const enabled = options?.enabled ?? true;\n\n return useAsyncResource(\n (signal) => ppp.retrieveRecordsAsync(fetchXml, signal),\n [ppp, fetchXml],\n enabled,\n );\n}\n","import type { GridDataRequest, GridDataResponse } from '@powerportalspro/core';\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\nexport interface UseGridDataOptions {\n /** When false, the hook stays in the idle (success / data: undefined) state and skips fetching. Defaults to true. */\n enabled?: boolean;\n /**\n * When true, deps-change refetches (new page, new sort, new search text)\n * hold the prior successful response on screen while the next request is\n * in flight — `status` stays `'success'`, `data` stays populated, and\n * `isFetching` flips to true so consumers can scrim the UI. Defaults to\n * false, matching the strict state-machine behavior the other hooks use.\n * Grids and other long-lived data surfaces will typically want this on.\n */\n keepPreviousData?: boolean;\n}\n\n/**\n * Grid-data counterpart to {@link useFetchRecords}. Calls the server's\n * `POST /api/grids/data` endpoint, which composes the final FetchXML via\n * the shared `IFetchXmlQueryComposer` — applying search across the view's\n * searchable columns, replacing sorts, AND-merging filters, etc.\n *\n * Response includes both the row data (`tableRecords`, `pagingInfo`) and a\n * server-resolved `columns` array — display names already localized, types\n * for cell dispatch, sortability flags, widths. Grid consumers render\n * straight from the response without a separate table-metadata round-trip.\n *\n * Re-runs whenever any field of the `request` changes (compared via JSON\n * serialization — cheap, and the request is a small flat object). Aborts\n * in-flight requests on unmount or when the request changes.\n *\n * At least one of `request.viewId` or `request.fetchXml` must be supplied\n * (neither produces a 400 from the server). Both may be supplied together —\n * the `fetchXml` is the query and the `viewId` names the view for localized\n * column-title resolution.\n */\nexport function useGridData(\n request: GridDataRequest,\n options?: UseGridDataOptions,\n): QueryResult<GridDataResponse> {\n const { ppp } = usePowerPortalsPro();\n const enabled = options?.enabled ?? true;\n const keepPreviousData = options?.keepPreviousData ?? false;\n // Stringify the request so the dep array is value-based, not identity-\n // based — consumers commonly construct the request inline on each\n // render, which would otherwise force a refetch every render.\n const requestKey = JSON.stringify(request);\n\n return useAsyncResource(\n (signal) => ppp.loadGridAsync(request, signal),\n [ppp, requestKey],\n enabled,\n keepPreviousData,\n );\n}\n","import type {\n ChartAggregation,\n ChartData,\n ColumnSort,\n GridFilterBase,\n GridDataRequest,\n PowerPortalsProError,\n ResolvedColumn,\n TableSecurityPermission,\n TableRecord,\n} from '@powerportalspro/core';\nimport { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\nimport { QueryStatus } from './internal/use-async-resource.js';\nimport { useGridData } from './use-grid-data.js';\n\n/**\n * Soft cross-filter — a chart slice the user clicked but didn't drill\n * through. Drives styling on consumers (chart: emphasize matching\n * slice + dim others; grid: dim non-matching rows) WITHOUT changing\n * the server-side query. Distinct from the hard filter\n * ({@link ViewDataSourceProps.filter}) which IS sent to the server\n * and re-fetches both row page and aggregations.\n */\nexport interface ViewDataSourceHighlight {\n /** Column the user clicked into — the slice's group-by column. */\n readonly column: string;\n /** Raw column value to match. Compared by strict equality. */\n readonly value: unknown;\n}\n\n/**\n * Options accepted by {@link useViewDataSource}. Split into two roles:\n *\n * - **Props** (forwarded straight into every request) — `viewId` /\n * `fetchXml` source, `staticFilters` (always-applied, never cleared\n * by mutators — the SubGrid relationship-filter case), `aggregations`\n * spec, behavior knobs (`enabled`, `keepPreviousData`, `debounceMs`).\n * - **Initial state** (seeds once on mount, mutators take over)\n * `initialFilter`, `initialSort`, `initialSearch`, `initialPage`,\n * `initialPageSize`, `initialHighlight`.\n *\n * The same datasource instance feeds a `<MainGrid>` and a\n * `<DataverseChart>` (or two of either) — they all read the same row\n * page + aggregations, so filter changes from any view land\n * everywhere in one round-trip.\n */\nexport interface UseViewDataSourceOptions {\n /**\n * Stored Dataverse view to query. When set alongside {@link fetchXml},\n * the FetchXML drives the query and this id only supplies the view's\n * identity for view-specific localized column titles (no savedquery is\n * loaded). At least one of {@link viewId} / {@link fetchXml} must be set.\n */\n viewId?: string;\n /**\n * Caller-supplied FetchXML to query. Useful for custom (in-memory) views.\n * May be combined with {@link viewId} to give such a view a stable\n * identity for localization (see its docs).\n */\n fetchXml?: string;\n /**\n * Filters that the datasource never mutates — always sent verbatim\n * with every request. The primary use case is `<SubGrid>` passing a\n * `RelationshipFilter` to scope rows to a parent record; the\n * datasource's user-driven {@link ViewDataSource.filter} state\n * layers on top.\n */\n staticFilters?: readonly GridFilterBase[];\n /**\n * Optional aggregations to compute alongside the row page. Each\n * entry becomes one entry in `GridDataRequest.Aggregations`; the\n * server returns one `ChartData` per entry in\n * {@link ViewDataSource.aggregationResults}, same order.\n * <c>undefined</c> / empty → no chart pass; the response's\n * `aggregations` slot is `null`. Treated as a prop (changes\n * re-fetch) — if you need it to be mutable state inside the\n * datasource, lift it to the consumer.\n */\n aggregations?: readonly ChartAggregation[];\n\n /** Seed for {@link ViewDataSource.filter}. Defaults to empty. */\n initialFilter?: readonly GridFilterBase[];\n /** Seed for {@link ViewDataSource.sort}. Defaults to empty. */\n initialSort?: readonly ColumnSort[];\n /** Seed for {@link ViewDataSource.search}. Defaults to empty string. */\n initialSearch?: string;\n /** Seed for {@link ViewDataSource.page}. Defaults to 1. */\n initialPage?: number;\n /** Seed for {@link ViewDataSource.pageSize}. Defaults to 50. */\n initialPageSize?: number;\n /** Seed for {@link ViewDataSource.highlight}. Defaults to undefined. */\n initialHighlight?: ViewDataSourceHighlight;\n\n /**\n * Debounce window in ms for the actual server request. State setter\n * calls land synchronously (consumers reading `ds.search` see the\n * latest typed value), but the network fetch fires once the\n * debounce settles. Defaults to 300ms; pass 0 to disable.\n */\n debounceMs?: number;\n /** When false, no fetch fires. Defaults to true. */\n enabled?: boolean;\n /**\n * Keep the prior successful response on screen during deps-change\n * refetches. Defaults to true — grids and charts typically want\n * scrim-during-refetch over wiping the UI.\n */\n keepPreviousData?: boolean;\n}\n\n/**\n * The reactive view of one query, shared by every consumer (grid,\n * chart, side-panel, etc.) wired to the same {@link useViewDataSource}\n * call. All consumers see the same row page + aggregations because\n * the server returns them in a single response (atomically consistent\n * — no race where the chart shows aggregations for an old filter\n * while the grid shows rows for the new one).\n *\n * Reads (`filter` / `sort` / `search` / `rows` / `aggregationResults`)\n * are snapshots at the time the hook ran. Mutators\n * (`setFilter` / `setSearch` / `setHighlight` / etc.) trigger\n * re-renders of the hook owner, which propagates to all consumers.\n *\n * Stable identity per snapshot — the returned object only changes\n * reference when one of its fields actually changes. Safe to pass\n * through `React.memo`'d component trees.\n */\nexport interface ViewDataSource {\n // ---- Query state — owned, mutable via the setters below ----------------\n\n /**\n * User-driven filter applied on top of {@link UseViewDataSourceOptions.staticFilters}.\n * Always shipped as an AND-merge with the view's base filter.\n */\n readonly filter: readonly GridFilterBase[];\n readonly sort: readonly ColumnSort[];\n readonly search: string;\n readonly page: number;\n readonly pageSize: number;\n /**\n * Soft cross-filter for chart slice clicks — drives client-side\n * styling on consumers (chart slice emphasis, grid row dimming)\n * without changing the server query. Set to `undefined` to clear.\n * Distinct from {@link filter}, which IS a hard filter.\n */\n readonly highlight: ViewDataSourceHighlight | undefined;\n\n // ---- Forwarded from options (consumer-controlled) ----------------------\n\n readonly aggregations: readonly ChartAggregation[];\n\n // ---- Server response ---------------------------------------------------\n\n readonly status: QueryStatus;\n readonly rows: readonly TableRecord[];\n readonly totalCount: number | string | null | undefined;\n readonly columns: readonly ResolvedColumn[];\n /**\n * Current user's combined table-level `TableSecurityPermission` mask for\n * the view's table — comes from the server response's\n * `GridDataResponse.tablePermissions`. Mirrors the per-row\n * permissions but at table scope, so toolbar buttons (`<MainGrid>`'s\n * New / Delete and consumer-built equivalents) can gate off one\n * authoritative value instead of sampling row[0] as a proxy.\n * `undefined` until the first response lands.\n */\n readonly tablePermissions: TableSecurityPermission | undefined;\n /**\n * One {@link ChartData} per requested aggregation, in request order.\n * Empty when no aggregations were requested OR when the response\n * hasn't landed yet — consumers can render an empty / spinner state\n * uniformly.\n */\n readonly aggregationResults: readonly ChartData[];\n readonly error: PowerPortalsProError | undefined;\n /** True whenever a fetch is in flight (including debounced refetches). */\n readonly isFetching: boolean;\n /**\n * True only when the in-flight request actually carries the\n * `aggregations` spec — i.e., when the chart's data is about to\n * change. The skip-on-sort optimization above means that a column-\n * header click on a sibling grid fires a row-page-only refetch the\n * server processes without re-running aggregations, and this flag\n * stays false through that. Chart consumers ({@link DataverseChart})\n * gate their loading overlay on this so a sort-only refetch doesn't\n * flash a spinner over an unchanged chart.\n *\n * Always false when {@link aggregations} is empty (no chart data\n * being requested at all).\n */\n readonly isFetchingAggregations: boolean;\n\n // ---- Mutators ----------------------------------------------------------\n\n setFilter(filter: readonly GridFilterBase[]): void;\n setSort(sort: readonly ColumnSort[]): void;\n setSearch(search: string): void;\n setPage(page: number): void;\n setPageSize(pageSize: number): void;\n setHighlight(highlight: ViewDataSourceHighlight | undefined): void;\n /** Re-fires the current request immediately (skipping the debounce). */\n refresh(): Promise<void>;\n}\n\n/**\n * Internal reducer state. Mutators dispatch one of the action shapes\n * below; the request build pulls these values + the option props.\n */\ninterface State {\n readonly filter: readonly GridFilterBase[];\n readonly sort: readonly ColumnSort[];\n readonly search: string;\n readonly page: number;\n readonly pageSize: number;\n readonly highlight: ViewDataSourceHighlight | undefined;\n}\n\ntype Action =\n | { type: 'setFilter'; filter: readonly GridFilterBase[] }\n | { type: 'setSort'; sort: readonly ColumnSort[] }\n | { type: 'setSearch'; search: string }\n | { type: 'setPage'; page: number }\n | { type: 'setPageSize'; pageSize: number }\n | { type: 'setHighlight'; highlight: ViewDataSourceHighlight | undefined };\n\nfunction reduce(state: State, action: Action): State {\n switch (action.type) {\n // Filter / sort / search / pageSize changes invalidate the current\n // page — the new result set has different rows, the user expects to\n // land on page 1. Mirrors what `<MainGrid>` does for standalone\n // mode. Highlight changes DON'T reset the page (purely client-side).\n case 'setFilter':\n return { ...state, filter: action.filter, page: 1 };\n case 'setSort':\n return { ...state, sort: action.sort, page: 1 };\n case 'setSearch':\n return { ...state, search: action.search, page: 1 };\n case 'setPage':\n return { ...state, page: action.page };\n case 'setPageSize':\n return { ...state, pageSize: action.pageSize, page: 1 };\n case 'setHighlight':\n return { ...state, highlight: action.highlight };\n }\n}\n\n/**\n * Shared {@link GridDataRequest} + chart-spec state for grid+chart\n * scenarios. Hosts the filter / sort / search / page / aggregations\n * once for any combination of `<MainGrid dataSource={ds}>` and\n * `<DataverseChart dataSource={ds}>` consumers — one server request\n * keeps the row page and the chart aggregations atomically consistent.\n *\n * Standalone usage (no chart) still works: omit the `aggregations`\n * option and the datasource is just a hoisted-state grid hook.\n *\n * **Typical usage:**\n *\n * ```tsx\n * function OpportunitiesPage() {\n * const ds = useViewDataSource({\n * viewId: openOpportunitiesViewId,\n * aggregations: [\n * { groupBy: { column: 'stepname' },\n * aggregateColumn: 'estimatedvalue',\n * aggregate: AggregateType.Sum },\n * ],\n * });\n * return (\n * <>\n * <DataverseChart dataSource={ds} type=\"bar\" />\n * <MainGrid dataSource={ds} />\n * </>\n * );\n * }\n * ```\n */\nexport function useViewDataSource(options: UseViewDataSourceOptions): ViewDataSource {\n // Stable behavior knobs — captured once on mount so options changes\n // between renders don't churn timer setup. The deps that DO matter\n // (viewId, fetchXml, staticFilters, aggregations) drive the request\n // itself, picked up via the request-build memo.\n const debounceMs = options.debounceMs ?? 300;\n const enabled = options.enabled ?? true;\n const keepPreviousData = options.keepPreviousData ?? true;\n\n const [state, dispatch] = useReducer(reduce, undefined, () => ({\n filter: options.initialFilter ?? EMPTY_FILTERS,\n sort: options.initialSort ?? EMPTY_SORTS,\n search: options.initialSearch ?? '',\n page: options.initialPage ?? 1,\n pageSize: options.initialPageSize ?? 50,\n highlight: options.initialHighlight,\n }));\n\n // Aggregation-skip optimization. Aggregations are sort/page/pageSize-\n // independent server-side (the row page slices the result; the chart\n // sees the full filtered set), so re-running them when the user just\n // clicked a column header or paged is wasted work.\n // Strategy:\n // 1. Fingerprint the aggregation-AFFECTING inputs (viewId / fetchXml\n // / staticFilters / state.filter / state.search / aggregations\n // spec).\n // 2. Track which fingerprint the LAST successful response was for.\n // 3. When building the next request, only include the `aggregations`\n // spec if the current fingerprint differs from the last one we\n // got back. Sort/page changes leave the fingerprint identical →\n // no spec sent → server's GridService skips the entire\n // aggregation pass.\n // 4. When the response comes back without aggregations (because we\n // skipped), the chart falls back to the cached results from the\n // last fetch that did include them — see `aggregationResults`\n // below.\n // `null` fingerprint means \"no aggregations requested at all\" — the\n // optimization short-circuits and we never include them.\n const aggregationFingerprint = useMemo(() => {\n if (!options.aggregations || options.aggregations.length === 0) return null;\n return JSON.stringify({\n viewId: options.viewId,\n fetchXml: options.fetchXml,\n staticFilters: options.staticFilters,\n filter: state.filter,\n search: state.search,\n aggregations: options.aggregations,\n });\n }, [\n options.viewId,\n options.fetchXml,\n options.staticFilters,\n options.aggregations,\n state.filter,\n state.search,\n ]);\n\n // `null` until the first aggregation response lands. Updated by the\n // post-response effect below. Read inside `request` via ref so\n // changes between renders don't churn the memo identity (the memo's\n // own dep on `aggregationFingerprint` already handles re-evaluation\n // when fingerprint changes).\n const lastFetchedAggregationFingerprintRef = useRef<string | null>(null);\n const cachedAggregationResultsRef = useRef<readonly ChartData[]>(EMPTY_AGGREGATIONS);\n\n // Memoize the un-debounced request so its identity only changes when\n // a field actually changes. State changes flow in immediately;\n // debouncing happens at the level below. The `aggregations` slot is\n // included only when the fingerprint differs from what's cached\n // sort/page changes leave it omitted.\n const request = useMemo<GridDataRequest>(() => {\n const filters = [\n ...(options.staticFilters ?? []),\n ...state.filter,\n ];\n const includeAggregations =\n aggregationFingerprint !== null\n && aggregationFingerprint !== lastFetchedAggregationFingerprintRef.current;\n return {\n ...(options.viewId !== undefined && { viewId: options.viewId }),\n ...(options.fetchXml !== undefined && { fetchXml: options.fetchXml }),\n sorts: state.sort as ColumnSort[],\n pageNumber: state.page,\n pageSize: state.pageSize,\n ...(state.search !== '' && { searchText: state.search }),\n ...(filters.length > 0 && { filters }),\n ...(includeAggregations && options.aggregations && {\n aggregations: options.aggregations as ChartAggregation[],\n }),\n };\n }, [\n options.viewId,\n options.fetchXml,\n options.staticFilters,\n options.aggregations,\n state.filter,\n state.sort,\n state.search,\n state.page,\n state.pageSize,\n aggregationFingerprint,\n ]);\n\n // Debounce the request that feeds useGridData — state updates\n // remain synchronous (so consumers see `ds.search` update on every\n // keystroke) but the network fetch only fires once typing settles.\n // `debounceMs={0}` disables the debounce entirely; useful for tests\n // and for programmatic (non-user-driven) flows.\n const debouncedRequest = useDebouncedValue(request, debounceMs);\n\n const query = useGridData(debouncedRequest, { enabled, keepPreviousData });\n\n // Mutators — stable callbacks so consumers can pass them through\n // `useCallback`/effect deps without churn.\n const setFilter = useCallback((filter: readonly GridFilterBase[]) => {\n dispatch({ type: 'setFilter', filter });\n }, []);\n const setSort = useCallback((sort: readonly ColumnSort[]) => {\n dispatch({ type: 'setSort', sort });\n }, []);\n const setSearch = useCallback((search: string) => {\n dispatch({ type: 'setSearch', search });\n }, []);\n const setPage = useCallback((page: number) => {\n dispatch({ type: 'setPage', page });\n }, []);\n const setPageSize = useCallback((pageSize: number) => {\n dispatch({ type: 'setPageSize', pageSize });\n }, []);\n const setHighlight = useCallback((highlight: ViewDataSourceHighlight | undefined) => {\n dispatch({ type: 'setHighlight', highlight });\n }, []);\n // `refresh()` bypasses the debounce — useGridData's refetch fires\n // immediately. The promise resolves when the next fetch settles\n // (success or error).\n const refresh = useCallback(async () => {\n await query.refetch();\n }, [query.refetch]);\n\n const data = query.data;\n const rows = data?.tableRecords ?? EMPTY_RECORDS;\n const columns = data?.columns ?? EMPTY_COLUMNS;\n const totalCount = data?.pagingInfo?.totalRecordCount;\n const tablePermissions = data?.tablePermissions;\n const aggregations = options.aggregations ?? EMPTY_AGGREGATION_SPEC;\n\n // True only when the in-flight (debounced) request actually carries\n // the aggregations spec — when the skip-on-sort optimization\n // omitted it, the chart's data won't change and consumers shouldn't\n // show a chart-loading spinner. The same in-flight request still\n // drives the grid's row-page scrim via the generic `isFetching`.\n const isFetchingAggregations =\n query.isFetching\n && debouncedRequest.aggregations !== undefined\n && debouncedRequest.aggregations !== null\n && debouncedRequest.aggregations.length > 0;\n\n // After a successful response that INCLUDED aggregations, cache them\n // + record the fingerprint they correspond to. The next request's\n // build decides whether to re-include aggregations by comparing\n // fingerprints — sort/page changes leave the fingerprint identical,\n // server skips the aggregation pass, and the cached value carries\n // forward.\n // GATED ON DATA-INSTANCE CHANGE (via lastDataRef), not on the deps\n // array alone. Without this guard, a fingerprint change (typed\n // search) fires the effect BEFORE the new fetch lands — at that\n // moment `data` is still the prior response (kept on screen by\n // `keepPreviousData`), so `data.aggregations` carries the OLD\n // aggregations. The effect would then mislabel the OLD aggregations\n // with the NEW fingerprint, and a follow-up sort would pull stale\n // data out of the cache. Compare data by identity to update only\n // when a new response actually arrived.\n const lastSeenDataRef = useRef<typeof data>(undefined);\n useEffect(() => {\n if (data === lastSeenDataRef.current) return;\n lastSeenDataRef.current = data;\n if (data?.aggregations) {\n cachedAggregationResultsRef.current = data.aggregations;\n lastFetchedAggregationFingerprintRef.current = aggregationFingerprint;\n }\n }, [data, aggregationFingerprint]);\n\n // Effective aggregationResults: latest server response (if it\n // included aggregations) OR the cached value from the last fetch\n // that did. Falls through to empty when the spec is currently\n // empty, regardless of what's cached — consumer toggled\n // aggregations off, chart should show empty.\n const aggregationResults =\n (options.aggregations?.length ?? 0) === 0\n ? EMPTY_AGGREGATIONS\n : data?.aggregations ?? cachedAggregationResultsRef.current;\n\n return useMemo<ViewDataSource>(\n () => ({\n filter: state.filter,\n sort: state.sort,\n search: state.search,\n page: state.page,\n pageSize: state.pageSize,\n highlight: state.highlight,\n aggregations,\n status: query.status,\n rows,\n totalCount,\n columns,\n tablePermissions,\n aggregationResults,\n error: query.error,\n isFetching: query.isFetching,\n isFetchingAggregations,\n setFilter,\n setSort,\n setSearch,\n setPage,\n setPageSize,\n setHighlight,\n refresh,\n }),\n [\n state.filter,\n state.sort,\n state.search,\n state.page,\n state.pageSize,\n state.highlight,\n aggregations,\n query.status,\n rows,\n totalCount,\n columns,\n tablePermissions,\n aggregationResults,\n query.error,\n query.isFetching,\n isFetchingAggregations,\n setFilter,\n setSort,\n setSearch,\n setPage,\n setPageSize,\n setHighlight,\n refresh,\n ],\n );\n}\n\n// Stable module-level empties so the returned snapshot identity only\n// flips when something actually changes. Cheaper than recreating arrays\n// per render.\nconst EMPTY_FILTERS: readonly GridFilterBase[] = [];\nconst EMPTY_SORTS: readonly ColumnSort[] = [];\nconst EMPTY_RECORDS: readonly TableRecord[] = [];\nconst EMPTY_COLUMNS: readonly ResolvedColumn[] = [];\nconst EMPTY_AGGREGATIONS: readonly ChartData[] = [];\nconst EMPTY_AGGREGATION_SPEC: readonly ChartAggregation[] = [];\n\n/**\n * Light-weight value debouncer. Returns the latest value after no\n * change for {@code delayMs} ms. {@code delayMs=0} short-circuits\n * (returns the live value with no scheduling) — useful for tests and\n * programmatic flows that don't want debounce noise. Compared by\n * JSON-stringified identity so equivalent-but-fresh objects (e.g.\n * `request = useMemo(...)` producing structurally equal output) don't\n * spuriously fire the debounce.\n */\nfunction useDebouncedValue<T>(value: T, delayMs: number): T {\n const [debounced, setDebounced] = useState(value);\n // Avoid scheduling a no-op timer when nothing changed. JSON\n // stringify on a small request object is cheap and keeps the\n // useEffect-cleanup churn from cancelling a real in-flight debounce\n // (e.g. parent re-renders inline-rebuilding the request).\n const valueKey = JSON.stringify(value);\n const previousKeyRef = useRef(valueKey);\n\n useEffect(() => {\n if (delayMs === 0) {\n // No debounce — sync the latest value immediately.\n setDebounced(value);\n previousKeyRef.current = valueKey;\n return;\n }\n if (previousKeyRef.current === valueKey) return;\n const timer = setTimeout(() => {\n setDebounced(value);\n previousKeyRef.current = valueKey;\n }, delayMs);\n return () => clearTimeout(timer);\n // value is captured in the closure; valueKey is the change\n // signal. Including `value` in deps would churn identity-driven\n // restarts of the timer when the value's reference changes but\n // its content doesn't. eslint-disable-next-line\n // react-hooks/exhaustive-deps\n }, [valueKey, delayMs]);\n\n return debounced;\n}\n","import type { TableMetadata } from '@powerportalspro/core';\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\nexport interface UseTableMetadataOptions {\n enabled?: boolean;\n}\n\n/**\n * Fetches the column + relationship metadata for one Dataverse table.\n * Routed through the {@link TableMetadataCache} on the provider — first\n * call kicks off a server round-trip, concurrent calls dedupe onto the\n * same in-flight promise, and resolved values are reused for the\n * lifetime of the `PowerPortalsProClient`. Critical for the inline-\n * editable grid, where N visible rows each mount their own\n * `<RecordContext>` and would otherwise thunder N parallel\n * `retrieveTableMetadataAsync` requests at toggle time.\n *\n * Coerces the cache's `null` \"table not found\" return to `undefined` in\n * the query result so downstream consumers can treat metadata absence\n * uniformly via `data == null`.\n */\nexport function useTableMetadata(\n tableLogicalName: string,\n options?: UseTableMetadataOptions,\n): QueryResult<TableMetadata> {\n const { tableMetadataCache } = usePowerPortalsPro();\n const enabled = options?.enabled ?? true;\n\n return useAsyncResource(\n async () => {\n const result = await tableMetadataCache.getAsync(tableLogicalName);\n // The cache returns `null` only when the table doesn't exist. The\n // hook's `data` slot is typed as `TableMetadata | undefined`; map\n // null → undefined so downstream `data == null` checks work\n // uniformly. (`useAsyncResource` treats `undefined` data + status:\n // 'success' as \"no result.\")\n return (result ?? undefined) as TableMetadata;\n },\n [tableMetadataCache, tableLogicalName],\n enabled,\n );\n}\n","import type { TableSecurityPermission } from '@powerportalspro/core';\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\nexport interface UseTablePermissionsOptions {\n enabled?: boolean;\n}\n\n/**\n * Fetches the current user's combined table-level `TableSecurityPermission`\n * mask for one Dataverse table — exposed so consumers can drive\n * \"Can the user create / delete on this table?\" gates without firing a\n * grid query.\n *\n * When a `<MainGrid>` / `<SubGrid>` is on the page the grid response\n * already carries the same value as `GridDataResponse.tablePermissions`\n * and toolbar buttons read it from there; reach for this hook when\n * you're rendering UI outside a grid (a custom toolbar, conditional\n * navigation, a \"permission required\" tooltip) and don't want to fire\n * a grid query just to read the permission bits.\n *\n * Returns `data: TableSecurityPermission` once the fetch resolves. The\n * caller compares via bit math, e.g.\n * `(data & TableSecurityPermission.Create) === TableSecurityPermission.Create`.\n *\n * Empty `tableLogicalName` returns `data: TableSecurityPermission.None` (0)\n * without firing a server round-trip — matches the server-side cache\n * which returns `None` for null/empty table names.\n */\nexport function useTablePermissions(\n tableLogicalName: string,\n options?: UseTablePermissionsOptions,\n): QueryResult<TableSecurityPermission> {\n const { ppp } = usePowerPortalsPro();\n const enabled = (options?.enabled ?? true) && tableLogicalName.length > 0;\n\n return useAsyncResource(\n async () => ppp.retrieveTablePermissionsAsync(tableLogicalName),\n [ppp, tableLogicalName],\n enabled,\n );\n}\n","import type { ViewMetadata } from '@powerportalspro/core';\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\nexport interface UseViewMetadataOptions {\n enabled?: boolean;\n}\n\n/**\n * Fetches metadata for one Dataverse view by its id (a GUID string).\n * Routed through the {@link ViewMetadataCache} on the provider — first\n * call hits the server, concurrent calls dedupe, resolved values are\n * reused for the lifetime of the `PowerPortalsProClient`. Use\n * {@link useViewsForTable} when you want every view for a table instead.\n */\nexport function useViewMetadata(\n viewId: string,\n options?: UseViewMetadataOptions,\n): QueryResult<ViewMetadata> {\n const { viewMetadataCache } = usePowerPortalsPro();\n const enabled = options?.enabled ?? true;\n\n return useAsyncResource(\n async () => {\n const result = await viewMetadataCache.getAsync(viewId);\n return (result ?? undefined) as ViewMetadata;\n },\n [viewMetadataCache, viewId],\n enabled,\n );\n}\n","import type { ViewMetadata } from '@powerportalspro/core';\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\nexport interface UseViewsForTableOptions {\n enabled?: boolean;\n}\n\n/**\n * Lists every view for a given table. Drives view-pickers and default-view\n * resolution. Routed through {@link ViewsForTableCache} so two grids on\n * the same page targeting the same table share one fetch.\n */\nexport function useViewsForTable(\n tableLogicalName: string,\n options?: UseViewsForTableOptions,\n): QueryResult<readonly ViewMetadata[]> {\n const { viewsForTableCache } = usePowerPortalsPro();\n const enabled = options?.enabled ?? true;\n\n return useAsyncResource(\n () => viewsForTableCache.getAsync(tableLogicalName),\n [viewsForTableCache, tableLogicalName],\n enabled,\n );\n}\n","import type { components } from '@powerportalspro/core';\nimport { useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\ntype OrganizationSettings = components['schemas']['OrganizationSettings'];\n\nexport interface UseOrganizationSettingsOptions {\n enabled?: boolean;\n}\n\n/**\n * Fetches the org-wide settings (default currency, etc.) sourced from the\n * Dataverse `organization` record.\n *\n * Routed through {@link OrganizationSettingsCache} on the provider\n * concurrent callers (e.g. multiple create-mode `<RecordContext>`s on the\n * same page) share a single fetch, and the resolved value is reused\n * across re-mounts for the lifetime of the `PowerPortalsProClient`.\n *\n * Primary consumer is the create-mode `<RecordContext>` seed: the framework\n * pre-fills `record.currency` with `defaultCurrency` so `<MoneyEdit>`\n * renders the correct symbol on brand-new records before save. Custom\n * components that need other org-level defaults can read them here too.\n */\nexport function useOrganizationSettings(\n options?: UseOrganizationSettingsOptions,\n): QueryResult<OrganizationSettings> {\n const { organizationSettingsCache } = usePowerPortalsPro();\n const enabled = options?.enabled ?? true;\n\n return useAsyncResource(\n async () => {\n const result = await organizationSettingsCache.getAsync();\n return (result ?? undefined) as OrganizationSettings;\n },\n [organizationSettingsCache],\n enabled,\n );\n}\n","import { useContext } from 'react';\nimport { QueryStatus, useAsyncResource, type QueryResult } from './internal/use-async-resource.js';\nimport { LocalizationManifestContext } from './localizer.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\nexport interface UseSupportedLocalesOptions {\n /**\n * Gates the fetch. Defaults to `true` — the manifest is tiny (a\n * handful of locale codes) and effectively free to fetch. Set\n * `false` only when the consumer wants to suppress the call until\n * some later condition (e.g. only fetch after auth resolves).\n */\n enabled?: boolean;\n}\n\n/**\n * Returns the framework's configured supported UI locales. Reads from\n * the {@link LocalizationManifestContext} populated by\n * `DefaultLocalizerProvider`'s tier-1 manifest fetch — so call sites\n * (typically `<LanguageDropdown>`) reuse the already-loaded data\n * instead of issuing their own `/localizations/version` round-trip\n * every time the hook mounts. The previous implementation re-fetched\n * on each mount, which surfaced as one extra `/localizations/version`\n * request on every page navigation in apps where the dropdown sits\n * inside route-level chrome.\n *\n * Fallback path: when no `DefaultLocalizerProvider` is mounted (rare\n * consumers who wired their own i18next setup via\n * `localizationUrl={false}`), the hook still issues its own fetch so\n * the API works in isolation. The fallback uses the same\n * `useAsyncResource` shape it always did, so the {@link QueryResult}\n * surface is unchanged.\n *\n * Server-side configuration is exposed through the localization manifest\n * endpoint at `/localizations/version`. Order matches the server's\n * configured preference, default culture first.\n */\nexport function useSupportedLocales(\n options?: UseSupportedLocalesOptions,\n): QueryResult<readonly string[]> {\n const { ppp } = usePowerPortalsPro();\n const enabled = options?.enabled ?? true;\n const manifestState = useContext(LocalizationManifestContext);\n // Skip the local fetch when the provider's manifest is in scope\n // the provider has either already fetched (data available) OR is\n // about to fetch (data undefined but will update soon). Either way,\n // we read from the provider's state instead of duplicating the\n // round-trip. `useAsyncResource` still runs (so the hook returns\n // the same shape), but with `enabled: false` it stays idle.\n const providerScoped = manifestState !== null;\n const fallback = useAsyncResource(\n async () => {\n const manifest = await ppp.retrieveLocalizationBundleManifestAsync();\n return manifest.supportedLocales;\n },\n [ppp],\n enabled && !providerScoped,\n );\n\n if (providerScoped) {\n // Mirror the QueryResult shape so consumers get the same fields\n // regardless of which code path served the data. `data` is undefined\n // while the provider's fetch is still in flight; `status` reflects\n // that as Loading. Once the provider populates the supported-locale\n // list, this hook re-evaluates and flips to Success.\n if (manifestState.supportedLocales === undefined) {\n return {\n status: enabled ? QueryStatus.Loading : QueryStatus.Success,\n data: undefined,\n error: undefined,\n isFetching: enabled,\n refetch: fallback.refetch,\n };\n }\n return {\n status: QueryStatus.Success,\n data: manifestState.supportedLocales,\n error: undefined,\n isFetching: false,\n refetch: fallback.refetch,\n };\n }\n\n return fallback;\n}\n","import { useEffect, useState } from 'react';\n\n/**\n * Reads the value of a URL query-string parameter and re-renders when\n * it changes (via browser back / forward, programmatic\n * `history.pushState` / `replaceState`, or hash-only navigation).\n * Router-agnostic — uses the platform `URLSearchParams` API directly\n * so consumers don't have to pull in a specific router's hook.\n *\n * Returns `null` when the parameter is absent. SSR-safe — returns\n * `null` when `window` is undefined.\n *\n * **Detecting in-app navigation:** `popstate` covers back / forward\n * but the History API has no built-in event for `pushState` /\n * `replaceState` calls. We patch both at module load to dispatch a\n * synthetic `pushstate` / `replacestate` event, so consumers using\n * any router (react-router, TanStack Router, Next.js, hand-rolled)\n * get correct re-renders without taking a router dependency.\n *\n * Multiple components subscribing to the same parameter all re-read\n * synchronously — each call sites runs its own `URLSearchParams`\n * parse, which is cheap.\n */\nexport function useQueryParam(name: string): string | null {\n const [value, setValue] = useState<string | null>(() => readParam(name));\n\n useEffect(() => {\n if (typeof window === 'undefined') return undefined;\n installHistoryPatch();\n setValue(readParam(name));\n const handler = () => setValue(readParam(name));\n window.addEventListener('popstate', handler);\n window.addEventListener(PUSH_EVENT, handler);\n window.addEventListener(REPLACE_EVENT, handler);\n return () => {\n window.removeEventListener('popstate', handler);\n window.removeEventListener(PUSH_EVENT, handler);\n window.removeEventListener(REPLACE_EVENT, handler);\n };\n }, [name]);\n\n return value;\n}\n\nconst PUSH_EVENT = 'ppp:pushstate';\nconst REPLACE_EVENT = 'ppp:replacestate';\n\nlet historyPatched = false;\nfunction installHistoryPatch(): void {\n if (historyPatched) return;\n if (typeof window === 'undefined' || typeof window.history === 'undefined') return;\n historyPatched = true;\n const origPush = window.history.pushState.bind(window.history);\n const origReplace = window.history.replaceState.bind(window.history);\n window.history.pushState = function (...args: Parameters<History['pushState']>) {\n origPush(...args);\n window.dispatchEvent(new Event(PUSH_EVENT));\n };\n window.history.replaceState = function (...args: Parameters<History['replaceState']>) {\n origReplace(...args);\n window.dispatchEvent(new Event(REPLACE_EVENT));\n };\n}\n\nfunction readParam(name: string): string | null {\n if (typeof window === 'undefined') return null;\n try {\n const params = new URLSearchParams(window.location.search);\n return params.get(name);\n } catch {\n return null;\n }\n}\n","import { useEffect } from 'react';\n\n/**\n * Registers a `beforeunload` listener while `condition` is `true`. The\n * listener calls `preventDefault()` (and sets `returnValue = ''` for legacy\n * compatibility) so the browser shows its native \"Leave site?\" prompt.\n *\n * Internal helper — both `MainContext` and `RecordContext` use this to\n * implement the auto unsaved-changes warning at the top-most context level.\n * Public consumers should use {@link useUnsavedChangesWarning} which adds\n * the MainContext.isDirty wiring.\n */\nexport function useBeforeUnloadWhen(condition: boolean): void {\n useEffect(() => {\n if (!condition) return;\n const handler = (e: BeforeUnloadEvent) => {\n // Modern browsers ignore any custom message and show their own prompt;\n // setting `returnValue` is the legacy fallback that's still required by\n // some implementations to actually trigger the prompt.\n e.preventDefault();\n e.returnValue = '';\n };\n window.addEventListener('beforeunload', handler);\n return () => window.removeEventListener('beforeunload', handler);\n }, [condition]);\n}\n","import type { OrganizationRequest } from '@powerportalspro/core';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { ReactNode } from 'react';\nimport { useBeforeUnloadWhen } from './internal/use-before-unload-when.js';\nimport { useUnsavedChangesRegistry } from './unsaved-changes-registry.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\n\n/**\n * Per-descendant registration the {@link MainContext} keeps for save/refresh\n * coordination + dirty aggregation. Each child component (typically a\n * {@link RecordContext}, a `MainGrid`/`SubGrid`, or a many-to-many editor)\n * calls {@link MainContextValue.registerDescendant} on mount and the returned\n * unregister function on unmount.\n *\n * The parent uses the registry to orchestrate save / refresh / validate /\n * aggregate `IsDirty`. Save is transactional: the parent collects\n * {@link getRequests} across every descendant and ships them in a single\n * `executeMultiple` round-trip.\n *\n * `isDirty` is a **getter** (`() => boolean`) — the parent calls it every\n * time it recomputes the aggregate. Descendants should back it with a\n * ref so it reads the latest value without re-registering. When the\n * value flips, the descendant calls\n * {@link MainContextValue.notifyDescendantDirtyChanged} to prompt the\n * parent to re-read; this is materially cheaper than tearing down and\n * re-creating the registration on every transition, which used to\n * cascade re-renders through every {@link MainContext} consumer and\n * dropped focus on the first keystroke in inline-edit cells.\n */\nexport interface MainContextDescendant {\n /**\n * Returns all pending Dataverse operations for this descendant subtree.\n * Returns an empty array when nothing is dirty. The parent collects these\n * across every descendant and ships them in one transactional\n * `executeMultiple` batch. Descendants that themselves contain nested\n * descendants (e.g. a {@link RecordContext} hosting a `SubGrid`) should\n * include the nested requests in the returned array so the entire subtree\n * lands in a single batch.\n */\n readonly getRequests: () => OrganizationRequest[];\n /**\n * Returns `true` when the descendant subtree is in a state suitable for\n * save. The parent calls this before collecting {@link getRequests};\n * returning `false` aborts the save without firing any server-side\n * operations.\n *\n * For descendants whose validation lives in a parallel system (e.g.\n * {@link RecordContext} which routes per-column validation through\n * `ValidationContext`), this can simply return `true` and let the\n * dedicated validation surface drive the abort decision separately.\n */\n readonly validate: () => boolean | Promise<boolean>;\n /** Reloads this descendant subtree from the server. */\n readonly refresh: () => Promise<void>;\n /**\n * Discards every pending edit in this descendant subtree, reverting back\n * to the loaded baseline WITHOUT re-fetching from the server. The \"Cancel\"\n * / \"Discard\" primitive.\n *\n * Implementations should clear their own pending queue (`pendingChanges`\n * for `RecordContext`, the queued row updates / creates / associates /\n * disassociates for `MainGrid`, the pending adds / removes for\n * `<ManyToManyLookupEdit>`, etc.) AND cascade to any nested descendants\n * so the entire subtree is clean afterward.\n */\n readonly resetState: () => void;\n /**\n * Getter the parent calls when recomputing aggregate dirty state.\n * Back it with a ref so the latest value is visible without\n * re-registering; call\n * {@link MainContextValue.notifyDescendantDirtyChanged} whenever the\n * underlying value transitions so the parent knows to re-read.\n */\n readonly isDirty: () => boolean;\n}\n\nexport interface MainContextValue {\n /**\n * Transactionally saves the entire descendant subtree:\n *\n * 1. Calls {@link MainContextProps.onBeforeSave} (if wired). Throwing\n * aborts the save before any server call.\n * 2. Runs {@link validate} across descendants; returns early without\n * saving when any descendant reports invalid.\n * 3. Collects {@link MainContextDescendant.getRequests} from every\n * registered descendant.\n * 4. If non-empty, ships the combined array in one\n * `executeMultipleAsync` round-trip so the operations land\n * transactionally on the server.\n * 5. Calls {@link refreshAll} so the local state picks up server-applied\n * side-effects (computed columns, plugin defaults, the modifiedon\n * timestamp, etc.).\n *\n * Throws on the first server-side failure; later steps are skipped.\n */\n readonly saveAll: () => Promise<void>;\n\n /** Refreshes every registered descendant. */\n readonly refreshAll: () => Promise<void>;\n\n /**\n * Discards every pending edit in every registered descendant — the\n * \"Cancel\" / \"Discard\" primitive that returns the entire subtree to its\n * loaded baseline WITHOUT re-fetching from the server.\n *\n * Synchronous: descendants clear their local state in their own\n * `setState` calls. Doesn't itself await anything.\n */\n readonly resetState: () => void;\n\n /**\n * Runs {@link MainContextDescendant.validate} across every registered\n * descendant and returns `false` if any of them invalidates. Awaitable\n * so descendants can perform async validation (e.g. async column\n * validators routed through `ValidationContext.validateAll`).\n */\n readonly validate: () => Promise<boolean>;\n\n /**\n * Aggregates every pending Dataverse operation across the descendant\n * subtree without firing them. Returns an empty array when nothing is\n * dirty. Useful for previewing the payload {@link saveAll} would ship\n * (audit log, dry-run UI, custom transactional flow that wants to\n * append extra requests before calling\n * `IPowerPortalsPro.executeMultipleAsync` directly).\n *\n * Walks the registered grids, child contexts, and M2M editors. Consumers\n * that just want to save should call {@link saveAll}; this is the escape\n * hatch for the rarer dry-run / merge-with-extras case.\n */\n readonly getRequests: () => OrganizationRequest[];\n\n /**\n * Adds `descendant` to the registry and returns an unregister function. Each\n * descendant should call this once on mount and call the returned function\n * on unmount, typically inside a single `useEffect` whose deps DON'T include\n * the descendant's local `isDirty` — instead, signal dirty transitions via\n * {@link notifyDescendantDirtyChanged}. The descendant's\n * {@link MainContextDescendant.isDirty} getter is what the parent reads.\n */\n readonly registerDescendant: (descendant: MainContextDescendant) => () => void;\n\n /**\n * Called by a descendant when its\n * {@link MainContextDescendant.isDirty} return value transitions.\n * Prompts the parent to recompute its aggregate without forcing the\n * descendant to unregister + re-register itself. The recommended\n * idiom is a `useEffect` keyed on the local `isDirty` value.\n */\n readonly notifyDescendantDirtyChanged: () => void;\n\n /**\n * Aggregated dirty state — `true` if any registered descendant currently\n * reports dirty. Flips reactively as descendants register/unregister/notify;\n * consumers subscribed via {@link useMainContext} re-render on transitions.\n */\n readonly isDirty: boolean;\n\n /**\n * Resolved value of {@link MainContextProps.warnOnUnsavedChanges} for this\n * context — `true` means the auto unsaved-changes warning is active; `false`\n * means it's suppressed. Children read this to inherit the effective value\n * when their own prop is omitted.\n */\n readonly warnOnUnsavedChanges: boolean;\n /**\n * Resolved value of {@link MainContextProps.forceSuccessfulValidationBeforeSave}.\n * When `true` (the default), save flows in this subtree gate on\n * validation passing — `SaveContextButton`, the dialog buttons inside\n * `<OpenRecordGridButton>` / `<NewRecordGridButton>`, and\n * {@link saveAll} all abort when any registered column validator reports\n * an error. When `false`, those gates are bypassed and the save proceeds\n * regardless. The server's own column validation (Dataverse required-on-\n * update / type checks) still applies.\n *\n * Children read this to inherit the effective value when their own prop\n * is omitted. Per-button overrides (e.g. on `<OpenRecordGridButton>`)\n * take precedence over the cascaded value for the scope of that button.\n */\n readonly forceSuccessfulValidationBeforeSave: boolean;\n}\n\nconst MainContextContext = createContext<MainContextValue | null>(null);\nMainContextContext.displayName = 'MainContext';\n\nexport interface MainContextProps {\n children: ReactNode;\n /**\n * Optional pre-save hook. Throw (or return a rejected promise) to cancel\n * the save — subsequent descendants are not saved.\n */\n onBeforeSave?: () => void | Promise<void>;\n /**\n * Auto-register a browser `beforeunload` warning when this context (or any\n * descendant) is dirty.\n *\n * Resolution rules:\n * - `true` / `false` — explicit override at this level.\n * - `undefined` (default) — inherit from the parent\n * {@link MainContext} / {@link RecordContext}, falling back to `true` at\n * the root. Set `false` on the outer context of e.g. a demo site to\n * suppress the prompt across the whole subtree without touching every\n * nested context.\n *\n * Only the **top-most** MainContext / RecordContext on the page actually\n * registers the listener — when contexts nest, the inner ones skip\n * registration to avoid duplicate `beforeunload` handlers (the browser would\n * still only prompt once, but the duplicates are wasteful). Soft navigation\n * (e.g. `react-router` `<Link>` clicks) is NOT covered — that's\n * router-specific; pair with the router's blocker hook plus\n * {@link useWarnAboutUnsavedChanges}.\n */\n warnOnUnsavedChanges?: boolean;\n /**\n * When `true` (default), descendants' save flows abort if any registered\n * column validator reports an error — covers `<SaveContextButton>`, the\n * dialog buttons on `<OpenRecordGridButton>` / `<NewRecordGridButton>`,\n * and {@link MainContextValue.saveAll}'s pre-flight. When `false`, those\n * gates are bypassed and the save proceeds regardless. Useful for draft-\n * style \"save what you have\" flows or when the consumer wants to defer\n * validation to a downstream review step.\n *\n * Resolution rules (same as {@link warnOnUnsavedChanges}):\n * - `true` / `false` — explicit override at this level.\n * - `undefined` (default) — inherit from the parent\n * {@link MainContext} / {@link RecordContext}, falling back to `true`\n * at the root.\n */\n forceSuccessfulValidationBeforeSave?: boolean;\n}\n\n/**\n * A context root that coordinates save and refresh across descendant components\n * ({@link RecordContext}, future SubGrid / many-to-many editors). Headless:\n * provides state and orchestration via React context, no rendering of its own\n * beyond `children`.\n *\n * Nesting is supported — a child MainContext doesn't replace its parent's\n * registry; it's just a separate root for its own subtree. Descendants always\n * register with their nearest ancestor.\n *\n * Deferred surface (and how to layer it on later):\n * - **Dirty tracking.** Consumers bring their own form layer (react-hook-form,\n * formik, vanilla state). When the dirty story solidifies in a project, an\n * `isDirty: () => boolean` getter on `MainContextDescendant` plus an aggregate\n * getter on `MainContextValue` is the natural extension.\n * - **Unsaved-changes warning** dialog on navigation. Driven by dirty tracking.\n * - **Validation orchestration**. Same — needs the form layer.\n */\nexport function MainContext({\n children,\n onBeforeSave,\n warnOnUnsavedChanges,\n forceSuccessfulValidationBeforeSave,\n}: MainContextProps) {\n // Read parent BEFORE we wrap children in our own provider — `useContext`\n // here returns whatever's above us, so `null` means \"we're the top-most\n // MainContext on the page\". Used to gate the beforeunload registration so\n // nested contexts don't double-register, AND to inherit unset prop values\n // from the parent's already-resolved state.\n const parentMainContext = useContext(MainContextContext);\n const isTopMost = parentMainContext === null;\n // Resolution: explicit prop > parent's resolved value > root default (true).\n const resolvedWarnOnUnsavedChanges =\n warnOnUnsavedChanges ?? parentMainContext?.warnOnUnsavedChanges ?? true;\n // Same resolution pattern as `warnOnUnsavedChanges`. Default `true` matches\n //.\n const resolvedForceSuccessfulValidationBeforeSave =\n forceSuccessfulValidationBeforeSave ??\n parentMainContext?.forceSuccessfulValidationBeforeSave ??\n true;\n // Set keeps insertion order in modern JS, giving deterministic first-\n // registered-saves-first ordering. Stored in a ref (not state) so re-\n // registrations on every keystroke don't trigger a full MainContext\n // re-render — the `version` counter below is what notifies React to\n // recompute the aggregate.\n const descendantsRef = useRef<Set<MainContextDescendant>>(new Set());\n // Bumped on every register/unregister. The aggregate `isDirty` `useMemo`\n // depends on this counter, so React knows to re-evaluate when the descendant\n // set changes (without paying the cost of putting the Set itself in state).\n const [version, setVersion] = useState(0);\n\n const { ppp } = usePowerPortalsPro();\n\n const refreshAll = useCallback(async () => {\n for (const descendant of descendantsRef.current) {\n await descendant.refresh();\n }\n }, []);\n\n // Cascade resetState across every descendant. Synchronous — descendants\n // are expected to do their own `setState` calls, which React batches into\n // a single re-render at the end of the calling event.\n const resetState = useCallback(() => {\n for (const descendant of descendantsRef.current) {\n descendant.resetState();\n }\n }, []);\n\n const validate = useCallback(async () => {\n for (const descendant of descendantsRef.current) {\n const ok = await descendant.validate();\n if (!ok) return false;\n }\n return true;\n }, []);\n\n const saveAll = useCallback(async () => {\n if (onBeforeSave) await onBeforeSave();\n // Validate the whole subtree before touching the server. Aborting here\n // avoids partial writes when one descendant is invalid. Skipped when\n // `forceSuccessfulValidationBeforeSave` is `false` — consumer opted into\n // \"save what's there, server arbitrates the rest\" semantics.\n if (resolvedForceSuccessfulValidationBeforeSave) {\n const valid = await validate();\n if (!valid) return;\n }\n // Collect requests across descendants. Each descendant is responsible\n // for including its own nested descendants' requests in the returned\n // array — this MainContext doesn't recurse into descendant trees.\n const requests: OrganizationRequest[] = [];\n for (const descendant of descendantsRef.current) {\n requests.push(...descendant.getRequests());\n }\n // No work to do — nothing was dirty. Skip the round-trip and the\n // subsequent refresh; refreshing a clean subtree would be a wasted\n // re-fetch.\n if (requests.length === 0) return;\n // One transactional batch. We pass `returnResponses: false` for now\n // because the current React port doesn't support CreateRequests\n // (no new-record flow yet) — none of the response payloads carry\n // information the client needs that wouldn't already come back in\n // `refreshAll`. When CreateRequest support lands and the client\n // needs newly-assigned record ids, this flips to `true` and an\n // id-distribution pass walks the response array.\n await ppp.executeMultipleAsync(requests, { returnResponses: false });\n // Reload everything so server-side computed columns / plugin defaults\n // / formatted values / file ids land back on the local state. Matches\n // → `RefreshAsync(true)` step.\n await refreshAll();\n }, [onBeforeSave, validate, ppp, refreshAll, resolvedForceSuccessfulValidationBeforeSave]);\n\n const registerDescendant = useCallback((descendant: MainContextDescendant) => {\n descendantsRef.current.add(descendant);\n setVersion((v) => v + 1);\n return () => {\n descendantsRef.current.delete(descendant);\n setVersion((v) => v + 1);\n };\n }, []);\n\n // Bumps `version` so the aggregate `isDirty` memo below re-reads each\n // descendant's `isDirty()` getter. Descendants call this when their\n // local dirty state flips — no need to re-register the descendant\n // object itself, which avoids the cascade of context re-renders that\n // used to drop focus from the active inline editor on the first\n // keystroke (the registry teardown + re-add fired two version bumps\n // and forced the MainContext provider value identity to flip).\n const notifyDescendantDirtyChanged = useCallback(() => {\n setVersion((v) => v + 1);\n }, []);\n\n // Recomputes when `version` bumps (descendants joined / left / signalled\n // a dirty transition). The dep on `version` looks unused but is the\n // trigger — refs don't subscribe React to changes on their own.\n const isDirty = useMemo(() => {\n void version;\n for (const descendant of descendantsRef.current) {\n if (descendant.isDirty()) return true;\n }\n return false;\n }, [version]);\n\n // Aggregate getRequests across all descendants — used when this\n // MainContext is itself a descendant of an OUTER MainContext (nested\n // contexts case). The outer parent calls this descendant's getRequests\n // and expects all nested requests in the returned array.\n const getRequests = useCallback((): OrganizationRequest[] => {\n const all: OrganizationRequest[] = [];\n for (const descendant of descendantsRef.current) {\n all.push(...descendant.getRequests());\n }\n return all;\n }, []);\n\n const value = useMemo<MainContextValue>(\n () => ({\n saveAll,\n refreshAll,\n resetState,\n validate,\n getRequests,\n registerDescendant,\n notifyDescendantDirtyChanged,\n isDirty,\n warnOnUnsavedChanges: resolvedWarnOnUnsavedChanges,\n forceSuccessfulValidationBeforeSave: resolvedForceSuccessfulValidationBeforeSave,\n }),\n [\n saveAll,\n refreshAll,\n resetState,\n validate,\n getRequests,\n registerDescendant,\n notifyDescendantDirtyChanged,\n isDirty,\n resolvedWarnOnUnsavedChanges,\n resolvedForceSuccessfulValidationBeforeSave,\n ],\n );\n\n // Auto-register with the OUTER MainContext (if any) — same pattern\n // RecordContext uses. Means a nested MainContext bubbles its aggregated\n // save / refresh / isDirty up to its parent, so the top-most MainContext's\n // isDirty includes the entire subtree. We forward our own\n // aggregated getRequests / validate so the outer's transactional save\n // sweeps the full descendant tree.\n // `isDirty` is passed as a ref-backed getter and dropped from the\n // dep array so the registration sticks across dirty-state transitions\n // — see the \"first-keystroke focus loss\" comment on\n // `notifyDescendantDirtyChanged` above. The `notify` effect below\n // bumps the outer when our local aggregate flips.\n useEffect(() => {\n if (!parentMainContext) return;\n return parentMainContext.registerDescendant({\n getRequests,\n validate,\n refresh: refreshAll,\n resetState,\n isDirty: () => isDirtyRef.current,\n });\n // Key on `registerDescendant` identity (stable per MainContext\n // instance) rather than the parent value object — see the same\n // pattern in `record-context.tsx` for the infinite-loop rationale.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [parentMainContext?.registerDescendant, getRequests, validate, refreshAll, resetState]);\n\n // Tell the outer to re-read our aggregate when it flips. Read the\n // parent reference via a ref (declared below `isDirtyRef`) so the\n // effect doesn't re-fire when the outer's value identity changes\n // which it does every time its own isDirty flips, creating an\n // infinite loop between the two layers if we depended on\n // parentMainContext directly.\n const parentMainContextRef = useRef(parentMainContext);\n parentMainContextRef.current = parentMainContext;\n useEffect(() => {\n parentMainContextRef.current?.notifyDescendantDirtyChanged();\n }, [isDirty]);\n\n // Auto unsaved-changes warning — only top-most fires (avoid duplicate\n // listeners when contexts nest). Browser-level only (covers tab close /\n // refresh / URL bar); soft nav is router-specific.\n useBeforeUnloadWhen(isTopMost && resolvedWarnOnUnsavedChanges && isDirty);\n\n // Register with the page-wide unsaved-changes registry — only the\n // topmost MainContext does so, since its `isDirty` aggregate already\n // covers every descendant via the existing registerDescendant chain.\n // Sibling top-level MainContexts on the same page each register\n // independently; the router-blocker guard reads `isAnyDirty()` so\n // ANY topmost dirty source blocks navigation with one prompt.\n // `warnOnUnsavedChanges=false` opts the whole subtree out of guarding\n // even when dirty — matches the beforeunload gate above.\n const unsavedChangesRegistry = useUnsavedChangesRegistry();\n const isDirtyRef = useRef(isDirty);\n isDirtyRef.current = isDirty;\n const resolvedWarnRef = useRef(resolvedWarnOnUnsavedChanges);\n resolvedWarnRef.current = resolvedWarnOnUnsavedChanges;\n useEffect(() => {\n if (!isTopMost) return;\n if (!unsavedChangesRegistry) return;\n // The reporter's `isDirty` reads through to the ref so subscribers\n // see the latest aggregate without us re-registering on every\n // edit (which would churn the registry's listener set every\n // keystroke). The notify-on-change effect below pokes the registry\n // when our local `isDirty` actually flips.\n return unsavedChangesRegistry.register({\n isDirty: () => resolvedWarnRef.current && isDirtyRef.current,\n });\n }, [isTopMost, unsavedChangesRegistry]);\n // Bump the registry's listeners whenever this MainContext's aggregate\n // changes so subscribers re-read. Otherwise the registry stays silent\n // through user edits since the reporter's identity doesn't change.\n useEffect(() => {\n if (!isTopMost) return;\n if (!unsavedChangesRegistry) return;\n unsavedChangesRegistry.notifyDirtyChanged();\n }, [isTopMost, unsavedChangesRegistry, isDirty, resolvedWarnOnUnsavedChanges]);\n\n return <MainContextContext.Provider value={value}>{children}</MainContextContext.Provider>;\n}\n\n/**\n * Returns the nearest ancestor {@link MainContext} value, or `null` if there is\n * none. Components like {@link RecordContext} use this to optionally register\n * themselves for save/refresh coordination — `null` is the standalone case where\n * the component manages its own save without a coordinator.\n */\nexport function useMainContext(): MainContextValue | null {\n return useContext(MainContextContext);\n}\n\n/**\n * Provides an existing {@link MainContextValue} to a subtree. Used by\n * {@link RecordContext} to expose itself as a `MainContext` for nested\n * descendants — keeps the raw `MainContextContext` internal while still\n * letting framework components compose multiple `MainContext` levels.\n *\n * Not generally needed by application code — use `<MainContext>` instead,\n * which builds the value internally.\n */\nexport function MainContextProvider({\n value,\n children,\n}: {\n value: MainContextValue;\n children: ReactNode;\n}) {\n return <MainContextContext.Provider value={value}>{children}</MainContextContext.Provider>;\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\n/** Options passed to {@link OverlayService.show}. */\nexport interface OverlayOptions {\n /**\n * Target id to scope the overlay. Undefined / empty = global (full-screen);\n * any other value routes to a matching scoped `<OverlayTarget>` registered\n * with the same id.\n */\n targetId?: string | undefined;\n /** Text to display alongside the spinner (e.g. `\"Loading...\"`, `\"Saving...\"`). */\n description?: string | undefined;\n /** Whether to show a spinner. Defaults to `true`. */\n showProgress?: boolean | undefined;\n}\n\n/**\n * A live overlay handle returned by {@link OverlayService.show}. Pass it to\n * {@link OverlayService.hide} to dismiss exactly that overlay (matters when\n * concurrent show calls are stacked on the same target).\n */\nexport interface OverlayInstance extends OverlayOptions {\n /** Stable id assigned by the service at show time. */\n readonly id: string;\n}\n\n/**\n * Headless overlay service contract. Maintains a per-target stack of\n * overlay instances; the topmost instance for a target is what renders.\n * Concrete implementations (e.g. `<FluentOverlayProvider>` from\n * `@powerportalspro/react-fluent`) provide the actual UI.\n *\n * Reads happen via the {@link useOverlayForTarget} hook — that's how\n * components subscribe to \"what overlay should I currently render\". The\n * service interface itself is write-only (show / hide / hideAll).\n */\nexport interface OverlayService {\n /** Push an overlay onto its target's stack. Returns the handle to dismiss it. */\n show(options?: OverlayOptions): OverlayInstance;\n /** Remove a specific overlay from its target's stack. No-op if already removed. */\n hide(overlay: OverlayInstance): void;\n /** Clear every overlay on every target. */\n hideAll(): void;\n}\n\ninterface OverlayContextValue {\n readonly service: OverlayService;\n /**\n * Per-target stacks. Keyed by `targetId ?? ''` (empty string = global).\n * Last item in each stack is the topmost / currently-rendered overlay.\n * Mutated only via the service; consumers read via {@link useOverlayForTarget}.\n */\n readonly overlaysByTarget: ReadonlyMap<string, readonly OverlayInstance[]>;\n}\n\nconst OverlayContext = createContext<OverlayContextValue | null>(null);\nOverlayContext.displayName = 'OverlayService';\n\n/** Returns the nearest {@link OverlayService}, or `null` when no provider is mounted. */\nexport function useOverlayService(): OverlayService | null {\n return useContext(OverlayContext)?.service ?? null;\n}\n\n/**\n * Returns the topmost overlay currently active for the given target id (or\n * `null` if there is none). Subscribers re-render when their target's stack\n * changes — this is how overlay-rendering components like\n * `<FluentOverlayProvider>` and `<FluentOverlayTarget>` know what to draw.\n */\nexport function useOverlayForTarget(targetId?: string): OverlayInstance | null {\n const ctx = useContext(OverlayContext);\n if (!ctx) return null;\n const stack = ctx.overlaysByTarget.get(targetId ?? '') ?? [];\n return stack[stack.length - 1] ?? null;\n}\n\n/**\n * Provides an externally-built {@link OverlayService} + state to the subtree.\n * Real implementations like `<FluentOverlayProvider>` use this internally;\n * tests can use it to inject a mock service.\n */\nexport function OverlayServiceProvider({\n value,\n children,\n}: {\n value: OverlayContextValue;\n children: ReactNode;\n}) {\n return <OverlayContext.Provider value={value}>{children}</OverlayContext.Provider>;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation helper — creates an OverlayService backed by React state.\n// Lives in @powerportalspro/react so the FluentOverlayProvider (and any\n// future non-Fluent provider) can share the same logic; only the rendering\n// differs between implementations.\n// ---------------------------------------------------------------------------\n\n/**\n * Hook that builds a state-backed {@link OverlayService} + the overlays-by-target\n * map needed for the {@link OverlayServiceProvider} value. Used by overlay\n * provider components — wraps children with `<OverlayServiceProvider value={...}>`\n * and renders the actual overlay UI based on the returned `overlaysByTarget`.\n */\nexport function useOverlayServiceState(): OverlayContextValue {\n const [overlaysByTarget, setOverlaysByTarget] = useState<\n ReadonlyMap<string, readonly OverlayInstance[]>\n >(() => new Map());\n // Stable id counter shared across all show() calls for this provider.\n const nextIdRef = useRef(1);\n\n const service = useMemo<OverlayService>(\n () => ({\n show(options) {\n const id = `overlay-${nextIdRef.current++}`;\n const instance: OverlayInstance = { id, ...options };\n setOverlaysByTarget((prev) => {\n const key = options?.targetId ?? '';\n const next = new Map(prev);\n next.set(key, [...(next.get(key) ?? []), instance]);\n return next;\n });\n return instance;\n },\n hide(overlay) {\n setOverlaysByTarget((prev) => {\n const key = overlay.targetId ?? '';\n const stack = prev.get(key) ?? [];\n const idx = stack.findIndex((o) => o.id === overlay.id);\n if (idx < 0) return prev;\n const next = new Map(prev);\n if (stack.length === 1) {\n next.delete(key);\n } else {\n next.set(key, [...stack.slice(0, idx), ...stack.slice(idx + 1)]);\n }\n return next;\n });\n },\n hideAll() {\n setOverlaysByTarget(new Map());\n },\n }),\n [],\n );\n\n return useMemo(() => ({ service, overlaysByTarget }), [service, overlaysByTarget]);\n}\n\n// ---------------------------------------------------------------------------\n// Convenience helpers for \"show overlay during async op\". Works with no\n// service present (just runs the op without UI feedback) so consumers don't\n// have to null-check before every call.\n// ---------------------------------------------------------------------------\n\n/**\n * Run an async operation while showing an overlay. Hides on success or\n * failure (the overlay is dismissed in `finally`, so failures don't leave a\n * stuck spinner).\n */\nexport async function runWithOverlayAsync<T>(\n service: OverlayService | null,\n options: OverlayOptions,\n operation: () => Promise<T>,\n): Promise<T> {\n if (!service) return operation();\n const handle = service.show(options);\n try {\n return await operation();\n } finally {\n service.hide(handle);\n }\n}\n\n/**\n * Hook variant of {@link runWithOverlayAsync}. Returns a stable function bound\n * to the current overlay service. No-op (just runs the op) when no service is\n * present.\n */\nexport function useRunWithOverlay(): <T>(\n options: OverlayOptions,\n operation: () => Promise<T>,\n) => Promise<T> {\n const service = useOverlayService();\n return useCallback(\n <T,>(options: OverlayOptions, operation: () => Promise<T>) =>\n runWithOverlayAsync(service, options, operation),\n [service],\n );\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\n\n/**\n * A single column's validator. Returns the list of error messages produced by\n * the current value (empty list = valid). Sync or async — async validators\n * run sequentially during {@link ValidationContextValue.validateAll}.\n *\n * Implementations must read whatever state they need (current value, required\n * flag, metadata) at call time — typically from refs — because the validator\n * is invoked from outside the registering component's render cycle.\n */\nexport type ColumnValidator = () => string[] | Promise<string[]>;\n\n/**\n * Per-form-scope validation registry. One instance per logical form scope\n * (auto-mounted by `<RecordContext>`); edit components below it register\n * their validators and read back any errors.\n *\n * Reads happen via {@link useColumnValidationErrors} — that's how edit\n * components subscribe to \"do I have errors right now?\" without re-rendering\n * on every other column's transition.\n */\nexport interface ValidationContextValue {\n /**\n * Register a column's validator. Returns the unregister function. The\n * recommended idiom is a `useEffect` whose cleanup unregisters; on column\n * rename or unmount the registration is replaced cleanly.\n */\n readonly register: (columnName: string, validator: ColumnValidator) => () => void;\n /** Run a single column's validator and update its error state. */\n readonly validateColumn: (columnName: string) => Promise<void>;\n /**\n * Run every registered validator and update all error states. Returns\n * `true` when nothing failed. Used by save flows (e.g. `SaveContextButton`)\n * to gate a save on overall validity.\n */\n readonly validateAll: () => Promise<boolean>;\n /** Drop all errors. Useful when the underlying record reloads. */\n readonly clearErrors: () => void;\n /**\n * Aggregated validity — `false` if any registered column currently has\n * errors. Reactive; consumers subscribed via `useValidationContext` re-render\n * on transitions.\n */\n readonly isValid: boolean;\n /**\n * Current errors keyed by column logical name. Re-rendered subscribers see\n * the latest snapshot. Use {@link useColumnValidationErrors} for a\n * column-scoped read that doesn't churn on unrelated transitions.\n */\n readonly errors: ReadonlyMap<string, readonly string[]>;\n}\n\nconst ValidationContextContext = createContext<ValidationContextValue | null>(null);\nValidationContextContext.displayName = 'ValidationContext';\n\n/**\n * Returns the nearest {@link ValidationContextValue}, or `null` when no\n * provider is mounted. Validation is optional — a form rendered outside a\n * `<RecordContext>` (or anywhere without an explicit `<ValidationProvider>`)\n * just doesn't run validators.\n */\nexport function useValidationContext(): ValidationContextValue | null {\n return useContext(ValidationContextContext);\n}\n\n/**\n * Returns the current error messages for a single column (empty array if\n * valid; `undefined` if no validation provider is mounted). Edit components\n * call this to drive their inline error display.\n */\nexport function useColumnValidationErrors(\n columnName: string,\n): readonly string[] | undefined {\n const ctx = useValidationContext();\n if (!ctx) return undefined;\n return ctx.errors.get(columnName);\n}\n\n/**\n * Mounts a fresh validation scope for descendants. `<RecordContext>`\n * auto-mounts one — manual use is for edge cases (e.g. a non-record form).\n */\nexport function ValidationProvider({ children }: { children: ReactNode }) {\n const validatorsRef = useRef<Map<string, ColumnValidator>>(new Map());\n const [errors, setErrors] = useState<ReadonlyMap<string, readonly string[]>>(\n () => new Map(),\n );\n\n const register = useCallback(\n (columnName: string, validator: ColumnValidator) => {\n validatorsRef.current.set(columnName, validator);\n return () => {\n validatorsRef.current.delete(columnName);\n setErrors((prev) => {\n if (!prev.has(columnName)) return prev;\n const next = new Map(prev);\n next.delete(columnName);\n return next;\n });\n };\n },\n [],\n );\n\n const validateColumn = useCallback(async (columnName: string) => {\n const validator = validatorsRef.current.get(columnName);\n if (!validator) return;\n const fieldErrors = await validator();\n setErrors((prev) => {\n // Avoid creating a new Map if nothing changed — keeps subscribers from\n // re-rendering on every keystroke when the validator output is stable.\n const existing = prev.get(columnName) ?? [];\n if (\n existing.length === fieldErrors.length &&\n existing.every((e, i) => e === fieldErrors[i])\n ) {\n return prev;\n }\n const next = new Map(prev);\n if (fieldErrors.length === 0) {\n next.delete(columnName);\n } else {\n next.set(columnName, fieldErrors);\n }\n return next;\n });\n }, []);\n\n const validateAll = useCallback(async () => {\n const next = new Map<string, readonly string[]>();\n for (const [col, validator] of validatorsRef.current) {\n const fieldErrors = await validator();\n if (fieldErrors.length > 0) {\n next.set(col, fieldErrors);\n }\n }\n setErrors(next);\n return next.size === 0;\n }, []);\n\n const clearErrors = useCallback(() => {\n setErrors(new Map());\n }, []);\n\n const isValid = errors.size === 0;\n\n const value = useMemo<ValidationContextValue>(\n () => ({ register, validateColumn, validateAll, clearErrors, isValid, errors }),\n [register, validateColumn, validateAll, clearErrors, isValid, errors],\n );\n\n return (\n <ValidationContextContext.Provider value={value}>\n {children}\n </ValidationContextContext.Provider>\n );\n}\n","import type {\n components,\n CreateRequest,\n OrganizationRequest,\n PowerPortalsProError,\n TableRecord,\n UpdateRequest,\n} from '@powerportalspro/core';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { QueryStatus } from './internal/use-async-resource.js';\nimport {\n MainContextProvider,\n useMainContext,\n type MainContextDescendant,\n type MainContextProps,\n type MainContextValue,\n} from './main-context.js';\nimport { useBeforeUnloadWhen } from './internal/use-before-unload-when.js';\nimport { useUnsavedChangesRegistry } from './unsaved-changes-registry.js';\nimport { useLocalizer } from './localizer.js';\nimport { useOverlayService, type OverlayInstance } from './overlay-service.js';\nimport { usePowerPortalsPro } from './use-power-portals-pro.js';\nimport { useRecord } from './use-record.js';\nimport { useQueryParam } from './use-query-param.js';\nimport { useTableMetadata } from './use-table-metadata.js';\nimport { ValidationProvider, useValidationContext } from './validation-context.js';\n\ntype ColumnValueBase = components['schemas']['ColumnValueBase'];\ntype TableMetadata = components['schemas']['TableMetadata'];\n\n/**\n * Structural equality for two column values, treating `null` and `undefined`\n * as equivalent (\"no value\"). Used by the change tracker to detect reverts\n * if a user edits a column and then sets it back to the loaded value, the\n * column drops out of the dirty set rather than staying flagged.\n *\n * Uses `JSON.stringify` for the structural compare; fine for the wire-format\n * shapes we deal with (consistent key order from the server and edit\n * components), and avoids pulling in a deep-equal dep.\n */\nfunction valuesEqual(\n a: ColumnValueBase | null | undefined,\n b: ColumnValueBase | null | undefined,\n): boolean {\n const aEmpty = a === null || a === undefined;\n const bEmpty = b === null || b === undefined;\n if (aEmpty && bEmpty) return true;\n if (aEmpty !== bEmpty) return false;\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\nconst GUID_PATTERN =\n /^\\{?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\}?$/i;\n\n/**\n * Parses a single URL query-string value into the `ColumnValueBase` shape\n * the wire expects, dispatched by the column's metadata `$type`. Returns\n * `null` when the value can't be parsed for the column kind, when the\n * column is a File / Image column (no sensible URL encoding), or when\n * the column type is unknown.\n *\n * Lookup values use a JSON shape: `?customerid={\"tableName\":\"account\",\"value\":\"<guid>\",\"name\":\"Foo\"}`\n * with case-insensitive property names.\n */\nfunction parseQueryDefault(\n metadata: { $type?: number },\n rawValue: string,\n): ColumnValueBase | null {\n switch (metadata.$type) {\n // StringMetadata + MemoMetadata both ride on StringValue. The TS\n // discriminators are distinct so we list both arms.\n case 14:\n case 7:\n return { $type: 14, value: rawValue };\n case 5: {\n const n = Number(rawValue);\n return Number.isFinite(n) && Number.isInteger(n) ? { $type: 5, value: n } : null;\n }\n case 18: {\n const n = Number(rawValue);\n return Number.isFinite(n) && Number.isInteger(n) ? { $type: 18, value: n } : null;\n }\n case 3: {\n const n = Number(rawValue);\n return Number.isFinite(n) ? { $type: 3, value: n } : null;\n }\n case 4: {\n const n = Number(rawValue);\n return Number.isFinite(n) ? { $type: 4, value: n } : null;\n }\n case 8: {\n const n = Number(rawValue);\n return Number.isFinite(n) ? { $type: 8, value: n } : null;\n }\n case 0: {\n const lower = rawValue.trim().toLowerCase();\n if (lower === 'true') return { $type: 0, value: true };\n if (lower === 'false') return { $type: 0, value: false };\n return null;\n }\n case 2: {\n const ts = Date.parse(rawValue);\n if (Number.isNaN(ts)) return null;\n return { $type: 2, value: new Date(ts).toISOString() };\n }\n case 11: {\n const n = Number(rawValue);\n return Number.isFinite(n) && Number.isInteger(n) ? { $type: 11, value: n } : null;\n }\n case 40: {\n const parts = rawValue\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n const values: { value: number }[] = [];\n for (const p of parts) {\n const n = Number(p);\n if (Number.isFinite(n) && Number.isInteger(n)) values.push({ value: n });\n }\n return values.length > 0 ? { $type: 40, value: values } : null;\n }\n case 6: {\n try {\n const parsed: unknown = JSON.parse(rawValue);\n if (!parsed || typeof parsed !== 'object') return null;\n const findKey = (target: string): string | undefined =>\n Object.keys(parsed as object).find(\n (k) => k.toLowerCase() === target.toLowerCase(),\n );\n const obj = parsed as Record<string, unknown>;\n const tableNameKey = findKey('tableName');\n const valueKey = findKey('value');\n const nameKey = findKey('name');\n const tableName = tableNameKey ? obj[tableNameKey] : undefined;\n const value = valueKey ? obj[valueKey] : undefined;\n const name = nameKey ? obj[nameKey] : null;\n if (typeof tableName !== 'string' || tableName.length === 0) return null;\n if (typeof value !== 'string' || value.length === 0) return null;\n return {\n $type: 6,\n tableName,\n value,\n name: typeof name === 'string' ? name : null,\n };\n } catch {\n return null;\n }\n }\n case 15: {\n if (!GUID_PATTERN.test(rawValue)) return null;\n return { $type: 15, value: rawValue.replace(/[{}]/g, '') };\n }\n default:\n // FileMetadata (41), ImageMetadata (42), or unknown — no URL form.\n return null;\n }\n}\n\n/**\n * Walks the URL query string, looks up each parameter in the table's column\n * metadata, parses, and returns a `Record<string, ColumnValueBase>` of the\n * successfully-parsed values. Best-effort: malformed values for any single\n * column silently skip — a bad URL doesn't break new-record load.\n */\nfunction parseQueryDefaultsFromUrl(\n search: string,\n tableMetadata: TableMetadata,\n): Record<string, ColumnValueBase> {\n const out: Record<string, ColumnValueBase> = {};\n if (!search) return out;\n const params = new URLSearchParams(search);\n for (const [columnName, rawValue] of params) {\n if (!columnName) continue;\n const meta = tableMetadata.columns.find(\n (c) => (c as { columnName?: string }).columnName === columnName,\n );\n if (!meta) continue;\n try {\n const parsed = parseQueryDefault(meta, rawValue);\n if (parsed) out[columnName] = parsed;\n } catch {\n // Per-column try/catch — see helper doc.\n }\n }\n return out;\n}\n\nexport interface RecordContextValue {\n /** Discriminator for the load lifecycle — same {@link QueryStatus} the query hooks use. */\n readonly status: QueryStatus;\n /** Most recent record from the server (or undefined while loading / on error). */\n readonly record: TableRecord | undefined;\n /** Last error thrown by the load. Undefined on success. */\n readonly error: PowerPortalsProError | undefined;\n /**\n * Re-fetches this record from the server (replacing any local mutations) and\n * cascades to every nested descendant's `refresh()`.\n */\n readonly reload: () => Promise<void>;\n /**\n * Persists this record AND any nested descendants (grids, M2M editors, child\n * `<RecordContext>`s) — `RecordContext` IS-A `MainContext`, so `save()` here\n * is the same as `MainContext.saveAll()` would be at this level.\n *\n * Order: this record's PATCH first, then each registered descendant's `save()`\n * in registration order. Each descendant decides its own dirty-skip; saving a\n * clean RecordContext is a no-op for the record itself but still cascades to\n * nested descendants in case they have their own pending changes.\n *\n * Calls {@link RecordContextProps.onBeforeSave} (when provided) before issuing\n * the PATCH; throwing from that callback cancels the entire save (no descendants\n * run either). When inside an outer `<MainContext>`, this is also what gets\n * wired into the outer `MainContext.saveAll()` for this subtree.\n */\n readonly save: () => Promise<void>;\n /**\n * Aggregates every pending Dataverse operation across this record AND\n * every nested descendant — the own `UpdateRequest` / `CreateRequest`\n * (when dirty) followed by each nested descendant's `getRequests()`, in\n * registration order. Returns an empty array when nothing is dirty.\n *\n * Use for previewing the payload {@link save} would ship, for dry-run\n * UIs, or to merge the records into a custom `executeMultiple` batch\n * alongside consumer-supplied requests. Consumers that just want to\n * persist should call {@link save}.\n */\n readonly getRequests: () => OrganizationRequest[];\n /**\n * Discards every pending edit on this record AND in every nested\n * descendant, reverting back to the loaded baseline WITHOUT re-fetching\n * from the server. The \"Cancel\" / \"Discard\" primitive — same surface\n * as `MainContextValue.resetState`, also drops the record's own\n * baseline so it re-loads clean.\n *\n * Validation errors tracked against the dropped pending values are\n * also cleared. Synchronous: descendants do their own `setState` calls.\n */\n readonly resetState: () => void;\n /**\n * Immutably updates one column on the in-memory record. Triggers a re-render of all\n * descendants reading the record (typed-column hooks bind to this from\n * `@powerportalspro/react-fluent`'s edit components and similar). Pass `null` or\n * `undefined` to clear the column. Marks the column dirty in {@link dirtyColumns}\n * and sets `record.isDirty = true`.\n *\n * No-op if no record has loaded yet — children should gate input on `status === 'success'`.\n */\n readonly setColumnValue: (\n columnName: string,\n value: ColumnValueBase | null | undefined,\n ) => void;\n /**\n * Patches the loaded baseline with a fresh value for one column WITHOUT\n * marking the column dirty. Use for late-arriving server-side data — file\n * binaries lazy-loaded by `<ImageEdit>`/`<FileEdit>`, async-resolved\n * formatted values, secondary fetches that fill in fields the initial\n * record query didn't populate. NOT for user edits — those go through\n * {@link setColumnValue}.\n *\n * If the column already has a pending edit, the hydration is skipped to\n * avoid silently clobbering the user's in-progress change. This\n * distinguishes \"load filled this in\" from \"user typed this\" — the\n * former sets the baseline, the latter sets a pending value compared\n * against the baseline for dirty calculation.\n *\n * No-op if no record has loaded yet.\n */\n readonly hydrateColumnValue: (\n columnName: string,\n value: ColumnValueBase | null,\n ) => void;\n /**\n * Table metadata for the parent record's table — column types, required levels,\n * isValidForCreate/Update flags, etc. Loaded in parallel with the record itself.\n *\n * Independent of `status`: this is `undefined` while metadata is loading, on\n * metadata fetch failure, or for tables that have no metadata. The record's own\n * `status` reflects only the record query — metadata is best-effort, edit\n * components fall back to consumer-passed props when it's absent. This keeps\n * forms rendering as soon as the record is ready instead of blocking on a\n * second round-trip.\n *\n * Use {@link useColumnMetadata} from `@powerportalspro/react` to look up a\n * single column's entry.\n */\n readonly tableMetadata: TableMetadata | undefined;\n /**\n * `true` when at least one column on this record has been modified, OR when\n * any nested descendant (child `<RecordContext>`, grid, M2M editor) reports\n * dirty.\n *\n * Use this for Save-button enablement, navigation-warning gates, or any UI\n * that should react to the existence of unsaved changes anywhere in the\n * record's subtree. For a more focused check (just this record's columns,\n * ignoring nested descendants), inspect {@link dirtyColumns} directly.\n */\n readonly isDirty: boolean;\n /**\n * The set of column logical names that have been modified since the record\n * loaded or was last saved. Cleared on successful {@link save} and when a\n * fresh record lands from {@link reload}.\n *\n * Useful for per-field dirty indicators (a small marker next to a label),\n * for save filtering (only ship changed columns), or for diff-style review\n * UIs that show what's about to be persisted.\n */\n readonly dirtyColumns: ReadonlySet<string>;\n /**\n * Stable id this RecordContext uses when calling {@link OverlayService.show}\n * during load. Pass to a scoped overlay container (e.g.\n * `<FluentOverlayTarget targetId={ctx.overlayTargetId}>`) inside the\n * RecordContext subtree to render the loading spinner localized to that\n * region rather than full-screen.\n *\n * Generated once per RecordContext instance via React's `useId`. If no\n * `<OverlayService>` is mounted, the show calls no-op silently.\n */\n readonly overlayTargetId: string;\n /**\n * Runs the context-level {@link RecordContextProps.onBeforeDelete}\n * callback (when configured), surfacing any thrown error to the\n * caller so the delete can be cancelled. No-op when no callback is\n * wired. Delete-style buttons should `await` this BEFORE issuing\n * their own pre-delete prompts or the actual `deleteRecordAsync`\n * call. Parallels how `MainContext.saveAll` invokes\n * {@link MainContextProps.onBeforeSave}.\n */\n readonly runBeforeDelete: () => Promise<void>;\n}\n\nconst RecordContextContext = createContext<RecordContextValue | null>(null);\nRecordContextContext.displayName = 'RecordContext';\n\n/**\n * `RecordContext` extends {@link MainContextProps} because every `RecordContext`\n * conceptually IS-A `MainContext` — literally\n * inherits from `MainContext.razor.cs` and `override`s `IsDirty` to add the\n * record's own dirty state on top of the base aggregation. Inheriting the props\n * means `onBeforeSave` (and any future cross-cutting `MainContextProps`) is\n * available on a standalone `<RecordContext>` without a wrapping `<MainContext>`.\n */\nexport interface RecordContextProps extends MainContextProps {\n /** Logical name of the Dataverse table (e.g. `\"account\"`, `\"contact\"`). */\n table: string;\n /**\n * Primary id of the record to edit. Omit (or pass empty / `Guid.Empty`)\n * to enter **create mode**: no fetch fires, the in-memory record starts\n * either empty or from {@link initialRecord}, and {@link save} emits a\n * `CreateRequest` instead of an `UpdateRequest` on commit.\n */\n id?: string;\n /**\n * Optional pre-populated record to start from in **create mode** (when\n * {@link id} is omitted). Use to seed default values — typical case is\n * a SubGrid-hosted new-record dialog pre-filling the parent foreign-key\n * column (`parentcustomerid`, `contact_customer_accounts` etc.). Ignored\n * in edit mode — when `id` is set the server-loaded record is the\n * baseline. The `tableName` on `initialRecord` should match {@link table};\n * `id` will be ignored either way (a create starts without one).\n */\n initialRecord?: TableRecord;\n /**\n * Pre-loaded record to use as the baseline instead of fetching from the\n * server. When provided in edit mode (with {@link id} set), the network\n * fetch is skipped entirely and the supplied record becomes the\n * `loadedRecord` baseline — `status` immediately reads `'success'`.\n *\n * The intended consumer is the inline-editable grid: each row already\n * has its record in hand from the grid's `useGridData` fetch, so per-\n * row RecordContexts would otherwise issue N redundant `retrieveRecord`\n * round-trips just to re-fetch what's already on screen. Passing\n * `loadedRecord={row}` short-circuits that.\n *\n * Identity change re-baselines: a fresh reference is treated like a\n * fresh load, which seeds `loadedRecord` and resets `pendingChanges`\n * (mirroring the success-effect's behavior on a real refetch). Pass a\n * stable / memoized reference if you want pending edits to persist\n * across re-renders.\n *\n * Ignored in create mode ({@link id} omitted) — {@link initialRecord}\n * is the create-mode baseline.\n */\n loadedRecord?: TableRecord;\n /**\n * Pending column changes to apply on top of the server-loaded record.\n * Used by the dialog-based Edit flow when reopening a row that already\n * has queued updates from a prior session — the queued values become\n * the dialog's initial pendingChanges, so reverting to the server value\n * cleanly drops the column from `pendingChanges` (matches the loaded\n * baseline → removed) and the eventual save can detect \"no net change\"\n * by inspecting `dirtyColumns`.\n *\n * Applied once when the load lands; further user edits drive the\n * pending map normally via `setColumnValue`. Identity change after the\n * initial apply re-applies (rare — typically the prop is stable for\n * the lifetime of a dialog instance).\n */\n initialPendingChanges?: ReadonlyMap<string, ColumnValueBase | null>;\n /** Optional column allow-list. Omit for the table's full visible column set. */\n columns?: readonly string[];\n /** Fired once after a successful initial load (and again after any subsequent reload). */\n onRecordLoaded?: (record: TableRecord) => void;\n /**\n * Name of the URL query-string parameter to read the record's id\n * from. When set, the parameter's current value overrides\n * {@link id} — useful for routes shaped like\n * `/accounts/edit?accountId=...` where the consumer would\n * otherwise wire `useSearchParams` + pass to `id` by hand.\n *\n * Router-agnostic — uses `useQueryParam` under the hood which\n * reads from `window.location.search` and subscribes to back /\n * forward + programmatic `pushState` / `replaceState` so any\n * router's navigation triggers an id re-read.\n *\n * When the parameter is absent (or empty / Guid.Empty), the\n * RecordContext enters create mode — same semantic as omitting\n * the `id` prop entirely.\n */\n queryParameterName?: string;\n /**\n * Cancellable hook fired before this record is deleted. Throw from\n * the callback to abort — the delete request never goes out and\n * subsequent `onDeleted` hooks don't run either.\n *\n * Surfaces as `useRecordContext().onBeforeDelete` for delete-style\n * buttons (the framework `<DeleteContextButton>`, or consumer-built\n * delete actions) to consult before issuing `deleteRecordAsync`.\n * Per-button `onBeforeDelete` props chain after this — context first\n * (record-scoped policy), button second (action-specific).\n *\n * Use this for context-level deletion gates: \"deleted records can't\n * have active child records\" / \"only owners can delete\" / etc.\n * Throwing with a meaningful error message is fine — the framework\n * delete button routes thrown errors through its `onError` path or\n * the dialog service's error display.\n */\n onBeforeDelete?: () => void | Promise<void>;\n}\n\n/**\n * Loads a single Dataverse record and provides it to descendants via React\n * context. Children call {@link useRecordContext} to read the record + status.\n *\n * Also acts as a `MainContext` for its own subtree — nested grids, M2M editors,\n * and child `<RecordContext>`s register with this `RecordContext` instead of\n * skipping past it to a further-up `<MainContext>`. The aggregated\n * `isDirty`/`saveAll` exposed to a wrapping outer `<MainContext>` includes both\n * this record's own pending edits AND every nested descendant's state, so a\n * single outer `<SaveButton>` covers everything beneath it.\n *\n * Auto-registers with a parent {@link MainContext} (when present) so\n * {@link MainContext.saveAll} / {@link MainContext.refreshAll} include this record.\n * Without a parent, `save()` and `reload()` are still callable directly from\n * descendants via the context.\n *\n * Deferred surface (the headless coordination skeleton; rounded out later):\n * - **`OnBeforeSave` / `OnBeforeDelete`** cancellable callbacks at this level\n * (MainContext already exposes `onBeforeSave`).\n * - **Delete handler** with confirmation flow.\n */\n/**\n * Wrapper that auto-mounts a {@link ValidationProvider} around the inner\n * record-context logic. The split lets the inner component call\n * `useValidationContext()` (e.g. to clear errors on reload) — that hook\n * resolves the provider this wrapper just rendered.\n *\n * The two-component shape is invisible to consumers — `<RecordContext>` is\n * the only public surface.\n */\nexport function RecordContext(props: RecordContextProps) {\n return (\n <ValidationProvider>\n <RecordContextInner {...props} />\n </ValidationProvider>\n );\n}\n\nfunction RecordContextInner({\n children,\n table,\n id: idProp,\n queryParameterName,\n initialRecord,\n loadedRecord: loadedRecordProp,\n initialPendingChanges,\n columns,\n onRecordLoaded,\n onBeforeSave,\n onBeforeDelete,\n warnOnUnsavedChanges,\n forceSuccessfulValidationBeforeSave,\n}: RecordContextProps) {\n // Resolve the consumer-supplied id: when `queryParameterName` is set,\n // the URL's named parameter wins (\n // `QueryParameterName` precedence). Empty / absent parameters fall\n // through to `idProp`, which itself may be undefined → create mode.\n // The hook is unconditional even when `queryParameterName` is\n // empty — React-rule-of-hooks demands a fixed call order — but\n // passing `''` reads nothing.\n const queryId = useQueryParam(queryParameterName ?? '');\n const resolvedId =\n queryParameterName && queryId !== null && queryId.length > 0\n ? queryId\n : idProp;\n const propIsEmptyId = !resolvedId || resolvedId === '00000000-0000-0000-0000-000000000000';\n\n // Effective id — starts from the resolved id (a non-Empty GUID puts\n // us in edit mode), and gets overwritten with the server-assigned id\n // after a successful create.\n // as updates rather than creates. State (not derived) so the prop /\n // URL can flip create → edit without remounting; an explicit effect\n // below resyncs from the resolved id when it changes.\n const [effectiveId, setEffectiveId] = useState<string | undefined>(\n propIsEmptyId ? undefined : resolvedId,\n );\n // Re-sync when the resolved id changes (e.g. consumer navigates from\n // one record to another, or the URL's query parameter changes).\n // When the resolved id matches the auto-flipped value we just set,\n // this is a no-op — state equality short-circuits the re-render.\n useEffect(() => {\n setEffectiveId(propIsEmptyId ? undefined : resolvedId);\n }, [resolvedId, propIsEmptyId]);\n\n // Mode discrimination — derived from the effective id so post-save\n // the context behaves as edit mode without waiting for the consumer\n // / URL to update. The create-mode flag gates the entire fetch\n // path below and the save path's CreateRequest-vs-UpdateRequest\n // selection.\n const isCreateMode = effectiveId === undefined;\n // Skip-fetch mode: consumer supplied a pre-loaded record (typically the\n // inline-editable grid passing its already-fetched row), so the\n // server round-trip is unnecessary. Only meaningful in edit mode\n // create mode uses `initialRecord` as its baseline regardless.\n const isSkipFetchMode = !isCreateMode && loadedRecordProp !== undefined;\n const { ppp, organizationSettingsCache } = usePowerPortalsPro();\n const mainContext = useMainContext();\n // The auto-mounted ValidationProvider is the wrapping component, so this\n // resolves to its state. Used to clear errors when fresh data lands and to\n // expose `validate()` on the RecordContextValue so save flows can gate.\n const validation = useValidationContext();\n // RecordContext IS-A MainContext. When it's the only one on the page (no\n // outer MainContext), it owns the top-most beforeunload warning.\n const isTopMost = mainContext === null;\n // Same resolution rule as MainContext: explicit prop > parent's resolved\n // value > root default (true). Lets a consumer disable the warning at any\n // level and have it propagate down without re-stating the prop.\n const resolvedWarnOnUnsavedChanges =\n warnOnUnsavedChanges ?? mainContext?.warnOnUnsavedChanges ?? true;\n // Same resolution pattern. Default `true`\n // `MainContext.ForceSuccessfulValidationBeforeSave = true`.\n const resolvedForceSuccessfulValidationBeforeSave =\n forceSuccessfulValidationBeforeSave ??\n mainContext?.forceSuccessfulValidationBeforeSave ??\n true;\n const overlayService = useOverlayService();\n const localizer = useLocalizer();\n // Stable per-instance overlay target — use it from a scoped overlay\n // container if you want the loading spinner localized to this RecordContext.\n const overlayTargetId = useId();\n // Skip the server fetch entirely in create mode (no record yet) and in\n // skip-fetch mode (consumer supplied the record). `useRecord` still\n // mounts in both — hook order has to stay stable across renders — but\n // is disabled, which leaves it in `status='success'` with `data=undefined`\n // (see `useAsyncResource`). Pass a placeholder id to keep the hook's\n // type happy; it's never read when enabled=false.\n const query = useRecord(table, effectiveId ?? '', {\n ...(columns ? { columns } : {}),\n enabled: !isCreateMode && !isSkipFetchMode,\n });\n // Fire metadata fetch in parallel with the record fetch. Independent of the\n // record's status so descendants get richer behavior progressively as metadata\n // arrives, but the form doesn't block on it.\n const metadataQuery = useTableMetadata(table);\n\n // The clean baseline as last loaded from the server (or as last successfully\n // saved — see `save()`). The dirty tracker compares pending edits against\n // this snapshot, so a user edit-then-revert reads as clean instead of dirty.\n // Lazy-init from `loadedRecord` prop (skip-fetch mode) so descendants see\n // the baseline on the very first render rather than after a follow-up\n // effect. Without this, edit components would mount against an\n // `undefined` record for one paint, fire their per-column validators\n // against missing values, and flash transient \"required\" errors.\n const [loadedRecord, setLoadedRecord] = useState<TableRecord | undefined>(\n () => (isSkipFetchMode ? loadedRecordProp : undefined),\n );\n // Per-column pending edits keyed by column name. `null` means \"explicitly\n // cleared\".\n // `dirtyColumns`/`isDirty`/the exposed `record` are all derived from this +\n // `loadedRecord`, so there's a single source of truth for unsaved state.\n const [pendingChanges, setPendingChanges] = useState<ReadonlyMap<string, ColumnValueBase | null>>(\n () => new Map(),\n );\n\n // Capture the latest onRecordLoaded / onBeforeSave in refs so the save() and\n // load-effect closures don't have to depend on consumer callback identity.\n const onRecordLoadedRef = useRef(onRecordLoaded);\n onRecordLoadedRef.current = onRecordLoaded;\n const onBeforeSaveRef = useRef(onBeforeSave);\n onBeforeSaveRef.current = onBeforeSave;\n // Stash mutable state in refs so saveAll/reload close over them without\n // re-creating each render. Without this, `useRecord`'s query object (new\n // identity every render) plus React's strict no-stale-closure rules would\n // give us a fresh `reload` reference on every render → fresh\n // `innerMainContext` → nested descendants' useEffects re-run → infinite\n // register/version loop.\n const refetchRef = useRef(query.refetch);\n refetchRef.current = query.refetch;\n const recordRef = useRef<TableRecord | undefined>(undefined);\n const loadedRecordRef = useRef<TableRecord | undefined>(undefined);\n const ownDirtyRef = useRef(false);\n const pendingChangesRef = useRef<ReadonlyMap<string, ColumnValueBase | null>>(new Map());\n pendingChangesRef.current = pendingChanges;\n\n // Stash validation in a ref so the load effect doesn't have to depend on\n // its identity (which churns on every error transition).\n const validationRef = useRef(validation);\n validationRef.current = validation;\n\n // Create-mode seeding. Runs once when entering create mode so the\n // loadedRecord baseline reflects whatever defaults the caller wired up\n // via `initialRecord` — typically a parent-FK lookup pre-populated by\n // a SubGrid-hosted new-record dialog. The pending-changes map starts\n // empty (the user hasn't typed yet), so the record reads \"clean\" until\n // they make the first edit; isDirty drives the Save-button enable\n // gate. Mirrors create-new\n // branch — same intent, expressed inline as a one-shot effect rather\n // than as the `InitializeRecordCoreInnerAsync` else-branch.\n // We also fall through to a bare-empty record when `initialRecord` is\n // omitted, so a consumer who just passes `<RecordContext table=\"contact\" />`\n // gets a blank-form dialog without having to construct a stub.\n const isCreateModeRef = useRef(isCreateMode);\n isCreateModeRef.current = isCreateMode;\n // Sync the resolved-flag ref so `saveAll` stays stable while still\n // honoring the latest prop / cascade resolution. Same pattern\n // `onBeforeSaveRef` and `isCreateModeRef` use.\n const forceSuccessfulValidationBeforeSaveRef = useRef(\n resolvedForceSuccessfulValidationBeforeSave,\n );\n forceSuccessfulValidationBeforeSaveRef.current =\n resolvedForceSuccessfulValidationBeforeSave;\n // Read through refs in `saveAll` so its identity stays stable across\n // `mainContext` re-mounts and stays in sync with the latest\n // `queryParameterName` prop without forcing the callback to re-create.\n const queryParameterNameRef = useRef(queryParameterName);\n queryParameterNameRef.current = queryParameterName;\n const isTopMostRef = useRef(mainContext === null);\n isTopMostRef.current = mainContext === null;\n useEffect(() => {\n if (!isCreateMode) return;\n // Pre-generate a GUID into `_idForCreate` (NOT `id`) so descendants\n // — `<ManyToManyLookupEdit>`, SubGrid parent-FK pre-fill, anything\n // emitting a cross-record wire reference — can resolve a stable\n // parent identity even before the record is saved, while\n // `record.id` remains undefined to flag \"not yet persisted.\" The\n // temp GUID flows through to the `CreateRequest` body in\n // `buildOwnSaveRequest`; the server's in-batch resolver\n // (`ResolveAssociateRecordAsync`) matches AssociateRequest /\n // child-FK lookups against either `id` or `_idForCreate`, so a\n // Create + Associate pair within one `ExecuteMultiple` resolves\n // correctly. Mirrors field +\n // the implicit `TableRecord -> TableRecordReference` conversion.\n // An explicit `initialRecord.id` still wins (rare but supported\n // — useful for workflows that import data with pre-assigned ids);\n // otherwise we seed `_idForCreate` and leave `id` unset.\n const baseSeed = initialRecord ?? { tableName: table, properties: {} };\n const seeded: TableRecord =\n baseSeed.id !== undefined\n ? baseSeed\n : { ...baseSeed, _idForCreate: crypto.randomUUID() };\n setLoadedRecord(seeded);\n setPendingChanges(new Map());\n // Only re-seeds when entering create mode for the first time or when\n // the caller swaps `initialRecord` to a new identity. The pending\n // changes wipe is intentional — switching default values mid-edit\n // would otherwise leave the user's typed values dangling against a\n // baseline they don't match. A re-seed regenerates `_idForCreate` too,\n // matching the wipe's \"fresh form\" intent.\n }, [isCreateMode, initialRecord, table]);\n\n // URL query-string column defaults.QueryString()` loop in `RecordContext.razor.cs:386-413`\n // — when a `queryParameterName` is set, every URL param whose name matches\n // a column on the table seeds that column's value on the new record. Gated\n // on:\n // - create mode (URL defaults only apply to fresh records)\n // - `queryParameterName` set (the opt-in marker — the context skips URL\n // reading without it)\n // - table metadata loaded (we need column $type discriminators to parse)\n // - the seeded baseline exists (so the prior effect has run)\n // - the user hasn't started typing (`pendingChanges` is empty) — if they\n // have, we'd clobber their edits\n // Layered on top of `initialRecord`: URL wins for any column it specifies\n // (matches the explicit intent of constructing a `?col=val` link from e.g.\n // `<NavigateNewRecordGridButton>`). Applied as `loadedRecord` mutation, NOT\n // `pendingChanges`, so the record reads \"clean\" after the layering — the\n // SaveContextButton's enable gate only fires on the user's first edit, and\n // a `CreateRequest` ships the whole property set regardless of the\n // dirty-tracker's state.ResetState()`.\n useEffect(() => {\n if (!isCreateMode) return;\n if (!queryParameterName) return;\n if (!metadataQuery.data) return;\n if (!loadedRecord) return;\n if (pendingChangesRef.current.size > 0) return;\n const search = typeof window !== 'undefined' ? window.location.search : '';\n const defaults = parseQueryDefaultsFromUrl(search, metadataQuery.data);\n const cols = Object.keys(defaults);\n if (cols.length === 0) return;\n let changed = false;\n const nextProperties: Record<string, ColumnValueBase> = {\n ...(loadedRecord.properties ?? {}),\n };\n for (const col of cols) {\n const next = defaults[col]!;\n if (!valuesEqual(nextProperties[col], next)) {\n nextProperties[col] = next;\n changed = true;\n }\n }\n if (!changed) return;\n setLoadedRecord((current) =>\n current ? { ...current, properties: nextProperties } : current,\n );\n }, [isCreateMode, queryParameterName, metadataQuery.data, loadedRecord]);\n\n // Create-mode default-currency seed.cs\n // create-mode block: when a brand-new record is seeded, pre-fill its\n // currency (and the `transactioncurrencyid` lookup when the table supports\n // it) from the org's default currency so `<MoneyEdit>` renders the right\n // symbol before save.\n // Best-effort: a failed fetch (transient network, no base currency\n // configured) leaves `record.currency` unset and the editor falls back to\n // its generic Money icon. Not fatal for form load.\n // Gated mirror of the URL-defaults effect above so the two stay in\n // lockstep: same isCreateMode + metadataQuery + loadedRecord + idle-user\n // gates so we never clobber user edits or fight a server response.\n useEffect(() => {\n if (!isCreateMode) return;\n if (!metadataQuery.data) return;\n if (!loadedRecord) return;\n if (pendingChangesRef.current.size > 0) return;\n if (loadedRecord.currency) return; // Already seeded — bail.\n\n let cancelled = false;\n (async () => {\n try {\n const settings = await organizationSettingsCache.getAsync();\n if (cancelled) return;\n const defaultCurrency = settings?.defaultCurrency;\n if (!defaultCurrency) return;\n\n const tableHasCurrencyLookup =\n metadataQuery.data!.columns?.some((c) => c.columnName === 'transactioncurrencyid') ?? false;\n setLoadedRecord((current) => {\n if (!current) return current;\n // Re-check inside the setter — the user may have started typing\n // between scheduling and execution.\n if (current.currency) return current;\n const nextProperties: Record<string, ColumnValueBase> = {\n ...(current.properties ?? {}),\n };\n if (tableHasCurrencyLookup && !nextProperties['transactioncurrencyid']) {\n nextProperties['transactioncurrencyid'] = {\n $type: 6, // ColumnType.Lookup — matches the discriminator on LookupValue\n value: defaultCurrency.id,\n tableName: 'transactioncurrency',\n name: defaultCurrency.name,\n } as ColumnValueBase;\n }\n return { ...current, currency: defaultCurrency, properties: nextProperties };\n });\n } catch {\n // Swallowed by design — see the block comment above.\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [isCreateMode, metadataQuery.data, loadedRecord, organizationSettingsCache]);\n\n // Skip-fetch re-baseline. When the consumer-supplied `loadedRecord`\n // prop changes identity (typically after a grid refresh delivered fresh\n // row data), treat it as if a fetch had just completed: set the\n // baseline, re-seed pending changes from `initialPendingChanges` (or\n // empty), and clear validation errors. Pass a stable / memoized\n // reference if you want pending edits to persist across re-renders.\n useEffect(() => {\n if (!isSkipFetchMode || !loadedRecordProp) return;\n setLoadedRecord(loadedRecordProp);\n setPendingChanges(\n initialPendingChanges && initialPendingChanges.size > 0\n ? new Map(initialPendingChanges)\n : new Map(),\n );\n validationRef.current?.clearErrors();\n onRecordLoadedRef.current?.(loadedRecordProp);\n }, [isSkipFetchMode, loadedRecordProp, initialPendingChanges]);\n\n useEffect(() => {\n if (query.status === 'success' && query.data) {\n setLoadedRecord(query.data);\n // Fresh server data — seed pendingChanges from the caller-supplied\n // overlay (or empty when no overlay). Used by the dialog-based\n // Edit flow to apply previously-queued dirty columns onto the\n // freshly-loaded server record. Keeping these as pendingChanges\n // (rather than mutating loadedRecord) means a user revert-to-\n // server-value cleanly removes the column from the map — the\n // baseline-match check in `setColumnValue` sees the original\n // server value and the new typed value collide, and drops the\n // entry. (Reload semantics still apply otherwise: server wins\n // over local edits across reloads\n // RefreshAsync.)\n setPendingChanges(\n initialPendingChanges && initialPendingChanges.size > 0\n ? new Map(initialPendingChanges)\n : new Map(),\n );\n // Drop any validation errors that were tracked against the previous\n // record — fresh data is a clean slate.\n validationRef.current?.clearErrors();\n onRecordLoadedRef.current?.(query.data);\n }\n // Re-running on `initialPendingChanges` identity change is intentional\n // — a caller swapping the overlay between renders should re-seed.\n // In practice the overlay is stable per dialog session.\n }, [query.status, query.data, initialPendingChanges]);\n\n // Derived view of the record: loadedRecord with pendingChanges applied.\n // Consumers see one merged record; internally we keep the baseline + diff\n // separate so the dirty calculation can compare against original values.\n loadedRecordRef.current = loadedRecord;\n const record = useMemo<TableRecord | undefined>(() => {\n if (!loadedRecord) return undefined;\n if (pendingChanges.size === 0) return loadedRecord;\n const properties: Record<string, ColumnValueBase> = { ...(loadedRecord.properties ?? {}) };\n for (const [col, val] of pendingChanges) {\n if (val === null) {\n delete properties[col];\n } else {\n properties[col] = val;\n }\n }\n return { ...loadedRecord, properties, isDirty: true };\n }, [loadedRecord, pendingChanges]);\n\n const dirtyColumns = useMemo<ReadonlySet<string>>(\n () => new Set(pendingChanges.keys()),\n [pendingChanges],\n );\n const ownDirty = pendingChanges.size > 0;\n recordRef.current = record;\n ownDirtyRef.current = ownDirty;\n\n // Nested descendants — grids, M2M editors, child <RecordContext>s. RecordContext\n // serves as their MainContext. Same registry pattern as MainContext: stable\n // ref + version counter so re-registrations don't churn React.\n const nestedRef = useRef<Set<MainContextDescendant>>(new Set());\n const [nestedVersion, setNestedVersion] = useState(0);\n const nestedDirty = useMemo(() => {\n void nestedVersion;\n for (const d of nestedRef.current) {\n if (d.isDirty()) return true;\n }\n return false;\n }, [nestedVersion]);\n const isDirty = ownDirty || nestedDirty;\n\n const registerNested = useCallback((descendant: MainContextDescendant) => {\n nestedRef.current.add(descendant);\n setNestedVersion((v) => v + 1);\n return () => {\n nestedRef.current.delete(descendant);\n setNestedVersion((v) => v + 1);\n };\n }, []);\n\n // Counterpart to `MainContext`'s `notifyDescendantDirtyChanged` — see\n // its comment for the focus-loss rationale. Descendants whose `isDirty`\n // getter starts returning a different value call this so this\n // RecordContext re-aggregates without forcing them to unregister +\n // re-register their descendant object (which used to cascade context\n // re-renders down to every inline editor on the row).\n const notifyNestedDirtyChanged = useCallback(() => {\n setNestedVersion((v) => v + 1);\n }, []);\n\n // Build the per-RecordContext save request — UpdateRequest for an\n // existing record (id present), CreateRequest for a new record being\n // composed in create mode. Returns null when nothing is dirty (no work\n // to do). Pulled out so it can feed both the public saveAll AND the\n // outward {@link MainContextDescendant.getRequests} that registers\n // with a parent MainContext.\n // **Update path** (id present, default):\n // `RecordContext.GetRequests` returning an UpdateRequest for\n // `TableRecord.GetChangedRecord()`. Only the dirty columns ride along\n // unchanged columns are excluded so server-side audit logs reflect\n // actual changes. `null` entries in pendingChanges are explicit-clear\n // signals (user cleared a lookup, removed a file, etc.); the server's\n // update handler interprets nulls and translates them into the right\n // operation.\n // **Create path** (create mode): ships the FULL property set — both\n // the `initialRecord` defaults (parent FK pre-fills, etc.) AND the\n // user's edits, since the server has no baseline to merge against.\n // Mirrors ExecuteMultiple dispatch\n // where new TableRecords flow through `CreateRequest` rather than\n // `UpdateRequest`.\n // The cast on `properties` keeps null-clear semantics flowing despite\n // the wire shape modeling it as no-null — same caveat the update path\n // already lives with.\n const buildOwnSaveRequest = useCallback(():\n | UpdateRequest\n | CreateRequest\n | null => {\n if (!recordRef.current || !ownDirtyRef.current) return null;\n const current = recordRef.current;\n if (isCreateModeRef.current) {\n // Take the merged record (loaded baseline + pending edits) verbatim\n // `recordRef.current` is already the merged view that descendants\n // see, so every column with a value lands in the create payload.\n const allProperties: Record<string, ColumnValueBase | null> = {\n ...(current.properties as Record<string, ColumnValueBase | null>),\n };\n return {\n $type: 'CreateRequest',\n record: {\n tableName: current.tableName,\n // Include the client-pre-generated GUID. The seed effect writes\n // it into `_idForCreate`; the server's in-batch resolver matches\n // AssociateRequest / child-FK references against either `id` or\n // `_idForCreate`, so a Create + Associate pair within one\n // `ExecuteMultiple` resolves to the same record.\n // `id` is also forwarded when the consumer supplied an explicit\n // one via `initialRecord` (rare — pre-assigned ids on data\n // import).\n ...(current.id !== undefined && { id: current.id }),\n ...(current._idForCreate !== undefined &&\n current._idForCreate !== null && {\n _idForCreate: current._idForCreate,\n }),\n properties: allProperties as Record<string, ColumnValueBase>,\n },\n };\n }\n const partialProperties: Record<string, ColumnValueBase | null> = {};\n for (const [col, val] of pendingChangesRef.current) {\n partialProperties[col] = val;\n }\n const partialRecord: TableRecord = {\n tableName: current.tableName,\n properties: partialProperties as Record<string, ColumnValueBase>,\n ...(current.id !== undefined && { id: current.id }),\n };\n return { $type: 'UpdateRequest', record: partialRecord };\n }, []);\n\n // Aggregated requests for THIS subtree: own UpdateRequest (if dirty)\n // followed by every nested descendant's getRequests, in registration\n // order. The outer MainContext.saveAll calls this on each direct\n // descendant and concatenates the results into a single executeMultiple\n // batch.\n const getRequests = useCallback((): OrganizationRequest[] => {\n const all: OrganizationRequest[] = [];\n const own = buildOwnSaveRequest();\n if (own) all.push(own);\n for (const descendant of nestedRef.current) {\n all.push(...descendant.getRequests());\n }\n return all;\n }, [buildOwnSaveRequest]);\n\n // Aggregate validate. Defaults to true at the RecordContext level —\n // per-column validation is already routed through the parallel\n // `ValidationContext` surface (driven by `SaveContextButton`). Nested\n // descendants drive their own validate() (e.g. a future editable grid\n // may invalidate when a queued row fails its rules); this RecordContext\n // just defers to them.\n const validate = useCallback(async (): Promise<boolean> => {\n for (const descendant of nestedRef.current) {\n if (!(await descendant.validate())) return false;\n }\n return true;\n }, []);\n\n // Public + inner saveAll. Uses the same transactional flow the outer\n // MainContext.saveAll uses, so calling this directly on a RecordContext\n // (standalone, no outer MainContext) produces identical server-side\n // behavior to wrapping it in a MainContext and calling that's saveAll.\n // Stable across renders — reads state through refs so innerMainContext's\n // memo identity stays steady when the user types.\n const saveAll = useCallback(async () => {\n // Defensive guard against calling save before the record has loaded\n // catches programmer errors (forgot to gate on `status === 'success'`).\n if (!loadedRecordRef.current) {\n throw new Error('RecordContext.save called before the record loaded.');\n }\n // onBeforeSave cancels the entire subtree, not just the own-record\n // PATCH\n // aborts before any child save runs.\n await onBeforeSaveRef.current?.();\n // Structural validation cascade. Per-column validation runs separately\n // through ValidationContext (driven by SaveContextButton) — keeps the\n // per-edit error UX intact when this RC is the top-most context.\n // Skipped when `forceSuccessfulValidationBeforeSave` is `false`.\n if (forceSuccessfulValidationBeforeSaveRef.current) {\n const valid = await validate();\n if (!valid) return;\n }\n // Re-build the own request inline (rather than via getRequests\n // which also concatenates descendants') so we keep a handle on\n // whether it was a CreateRequest — needed below to find the\n // matching CreateResponse and flip create → edit mode.\n const ownReq = buildOwnSaveRequest();\n const descendantReqs: OrganizationRequest[] = [];\n for (const descendant of nestedRef.current) {\n descendantReqs.push(...descendant.getRequests());\n }\n const requests = ownReq ? [ownReq, ...descendantReqs] : descendantReqs;\n if (requests.length > 0) {\n // Ask the server to return individual responses so the CreateResponse\n // for `ownReq` (when applicable) surfaces the new record id back to\n // us — without this, a create succeeds but the context stays stuck\n // in create mode forever.\n const responses = await ppp.executeMultipleAsync(requests, { returnResponses: true });\n // Auto-flip create → edit when own was a CreateRequest. Response\n // index 0 corresponds to ownReq (it was placed first above).\n // Setting `effectiveId` cascades into the `useRecord` query via\n // its deps, which auto-fires a fetch and clears pendingChanges\n // through the success effect; no explicit refetch needed for the\n // create case.\n if (ownReq?.$type === 'CreateRequest') {\n const ownResp = responses[0] as\n | { $type?: string; id?: string }\n | undefined;\n if (ownResp?.$type === 'CreateResponse' && ownResp.id) {\n setEffectiveId(ownResp.id);\n // Post-create URL navigation.\n // `RecordContext.SaveAsync` → `_navigationManager.NavigateTo`\n // branch: when this is the top-most context and the URL\n // doesn't already carry the new id under `queryParameterName`,\n // rewrite the URL to point at the freshly-created record.\n // `replaceState` (not pushState) so the \"new record\" URL\n // doesn't hang around in history — pressing Back should\n // return to wherever the user came from, not back to the\n // create form for the now-existing record.\n // `NavigationHistory.PopCurrent()` + `NavigateTo` pair.\n const qpn = queryParameterNameRef.current;\n if (qpn && isTopMostRef.current && typeof window !== 'undefined') {\n const existing = new URLSearchParams(window.location.search).get(\n qpn,\n );\n if (!existing) {\n const newUrl = new URL(window.location.href);\n newUrl.searchParams.set(qpn, ownResp.id);\n window.history.replaceState(\n window.history.state,\n '',\n newUrl.toString(),\n );\n }\n }\n }\n }\n // Clear the local dirty markers after the round-trip succeeds. The\n // refetch below would also clear them via the success effect when its\n // fresh data lands, but clearing here gives the UI an immediate\n // \"saved\" state without waiting for the GET.\n setPendingChanges(new Map());\n }\n // Descendants with side-channel commits (file uploads, M2M\n // associations going through endpoints other than executeMultiple)\n // still need a refresh pass even when the batched request set is\n // empty — they may have committed their own work and need their\n // post-save state pulled in. Always run the cascade below\n // regardless of `requests.length`.\n // `MainContext.SaveAsync` which calls `RefreshAsync(true)` on\n // every descendant after a save attempt, batch-or-no-batch.\n // Refresh own record (skipped in create mode — the `effectiveId`\n // flip above already kicks off a fresh fetch via the query dep\n // change; the explicit refetch path is only meaningful for update\n // saves where the id hasn't changed) + cascade to nested\n // descendants regardless of mode so child grids / record contexts\n // pick up any changes.\n if (!isCreateModeRef.current) {\n // Fire-and-forget the post-save refetch. Awaiting it would let\n // the test framework's `act()` wait for the in-flight fetch to\n // settle — which never completes deterministically in a\n // unit-test runner, since the fakeFetch resolves on a microtask\n // and `act()` keeps scheduling renders for each setState in the\n // success chain. The local dirty state is already cleared\n // synchronously above; the refetch's data lands via the success\n // effect when it eventually completes, with no observer depending\n // on the await to finish.\n void refetchRef.current();\n }\n for (const descendant of nestedRef.current) {\n await descendant.refresh();\n }\n }, [ppp, validate, buildOwnSaveRequest]);\n\n const reload = useCallback(async () => {\n refetchRef.current();\n for (const descendant of nestedRef.current) {\n await descendant.refresh();\n }\n }, []);\n\n // Discard own pending edits + clear validation + cascade to nested\n // descendants. The \"Cancel\" / \"Discard\" primitive\n // `RecordContext.ResetState()` which overrides `MainContext.ResetState()`\n // to also wipe `_originalPropertyValues`. Synchronous; React batches all\n // the setState calls into one re-render.\n const resetState = useCallback(() => {\n setPendingChanges(new Map());\n validationRef.current?.clearErrors();\n for (const descendant of nestedRef.current) {\n descendant.resetState();\n }\n }, []);\n\n const setColumnValue = useCallback(\n (columnName: string, value: ColumnValueBase | null | undefined) => {\n setPendingChanges((prev) => {\n if (!loadedRecord) return prev;\n const original = loadedRecord.properties?.[columnName];\n const newValue = value ?? null; // normalize undefined → null\n\n // If the new value matches the loaded baseline, drop this column from\n // the pending set — the edit cancels itself out and the column reads\n // clean again. Mirrors\n // branch in `TableRecord.cs`.\n if (valuesEqual(original, newValue)) {\n if (!prev.has(columnName)) return prev;\n const next = new Map(prev);\n next.delete(columnName);\n return next;\n }\n\n // Skip the state update if the pending value already matches what\n // we'd write — saves a render when consumers fire change events\n // repeatedly with the same value (e.g. text inputs on every keystroke\n // that lands the same character).\n const existing = prev.has(columnName) ? prev.get(columnName) ?? null : undefined;\n if (existing !== undefined && valuesEqual(existing, newValue)) return prev;\n\n const next = new Map(prev);\n next.set(columnName, newValue);\n return next;\n });\n },\n [loadedRecord],\n );\n\n // Updates the loaded baseline directly so the column's `record.properties[…]`\n // changes WITHOUT being marked dirty. The use case is late-arriving server\n // data — file binaries lazy-loaded by `<ImageEdit>`/`<FileEdit>` once the\n // record's metadata-only initial load surfaces the file id, async-resolved\n // formatted-value lookups, etc. Skips when the column has a pending edit so\n // the lazy fetch doesn't clobber what the user is currently typing.\n const hydrateColumnValue = useCallback(\n (columnName: string, value: ColumnValueBase | null) => {\n setLoadedRecord((current) => {\n if (!current) return current;\n // Pending-edit guard. Reading the latest `pendingChanges` via setState\n // would require an awkward double-setter pattern; instead we read the\n // closed-over `pendingChanges` and accept that a hydration arriving\n // simultaneously with an edit MAY land. In practice the lazy-load\n // path fires from `useEffect` AFTER the initial render — by then any\n // pre-existing pending change is already in `pendingChanges` and this\n // closure sees it. Edits that race in during the fetch are extremely\n // unlikely given the typical 100ms+ network round-trip.\n if (pendingChanges.has(columnName)) return current;\n\n const previous = current.properties?.[columnName];\n // Wire-shape `properties` is `{[k]: ColumnValueBase}` — no null\n // allowed. Clearing means deleting the key so the column reads as\n // \"absent\" rather than \"present with null value\", which matches\n // how Dataverse round-trips an unset column.\n if (value === null) {\n if (previous === undefined) return current;\n const nextProperties = { ...(current.properties ?? {}) };\n delete nextProperties[columnName];\n return { ...current, properties: nextProperties };\n }\n\n if (valuesEqual(previous, value)) return current;\n return {\n ...current,\n properties: { ...(current.properties ?? {}), [columnName]: value },\n };\n });\n },\n [pendingChanges],\n );\n\n // Auto-show a scoped loading overlay while the record is fetching. Hides\n // when the load completes (success or error) or when the RecordContext\n // unmounts. No-op when no <OverlayService> is mounted — overlays are\n // optional UX, not a hard dependency.\n const loadingOverlayRef = useRef<OverlayInstance | null>(null);\n useEffect(() => {\n if (!overlayService) return;\n if (query.status === 'loading') {\n // Only one loading overlay at a time per RecordContext — don't stack\n // if React effects run a second time while already loading.\n if (!loadingOverlayRef.current) {\n loadingOverlayRef.current = overlayService.show({\n targetId: overlayTargetId,\n description: localizer.t('app.messages.record-loading'),\n });\n }\n } else if (loadingOverlayRef.current) {\n overlayService.hide(loadingOverlayRef.current);\n loadingOverlayRef.current = null;\n }\n return () => {\n if (loadingOverlayRef.current) {\n overlayService.hide(loadingOverlayRef.current);\n loadingOverlayRef.current = null;\n }\n };\n }, [overlayService, query.status, overlayTargetId, localizer]);\n\n // Mirror the current aggregate `isDirty` into a ref so the registered\n // descendant's getter (and the page-wide unsaved-changes registry's\n // reporter further down) can read the latest value without forcing\n // those registrations to re-run on every transition. Declared BEFORE\n // the registerDescendant effect because the effect's closure captures\n // `isDirtyRef` and runs on first mount.\n const isDirtyRef = useRef(isDirty);\n isDirtyRef.current = isDirty;\n // Same trick for the outer MainContext reference — the parent's value\n // identity churns every time ITS `isDirty` aggregate flips (because\n // `isDirty` lives in the value memo's deps). If our notify effect\n // depended on the live `mainContext` value, the cycle\n // child.isDirty flip → notify → parent.isDirty flip → parent value\n // identity flip → child notify effect re-fires → notify again\n // would loop until React detected an infinite update. Reading the\n // mainContext via a ref breaks the cycle: the notify effect only\n // re-fires on actual local `isDirty` transitions, and always uses\n // the latest mainContext snapshot when it does.\n const mainContextRef = useRef(mainContext);\n mainContextRef.current = mainContext;\n\n // Auto-register with the OUTER MainContext (if one wraps this RecordContext).\n // We register the AGGREGATED save/refresh/isDirty so the outer sees one\n // descendant whose state already includes nested children — outer.saveAll\n // ends up calling our saveAll which does own + nested in one shot.\n // `isDirty` is a ref-backed getter and dropped from the dep array so\n // the registration sticks across dirty-state transitions. The first\n // keystroke in any inline-edit cell flips isDirty here → before this\n // change, the effect re-fired, tearing down + re-creating the\n // descendant on the outer, which cascaded context re-renders down\n // through the SubGrid / MainGrid tree and dropped focus from the\n // active input. The `notify` effect below pokes the outer when our\n // local aggregate flips so its `nestedDirty` still re-evaluates.\n useEffect(() => {\n if (!mainContext) return;\n return mainContext.registerDescendant({\n getRequests,\n validate,\n refresh: reload,\n resetState,\n isDirty: () => isDirtyRef.current,\n });\n // Key on `mainContext.registerDescendant` (stable per MainContext\n // instance via useCallback([])) rather than the whole `mainContext`\n // value object — the value identity churns every time the outer\n // RecordContext's own isDirty aggregate flips, and re-firing this\n // registration in response would tear the descendant out of the\n // registry (briefly flipping aggregate to false) and then re-add it\n // (flipping it back to true), which churns the value identity\n // again in an infinite loop. Reading via the function identity\n // means a re-register only happens when the consumer actually\n // mounts a different MainContext instance.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [mainContext?.registerDescendant, getRequests, validate, reload, resetState]);\n\n // Notify the outer MainContext that our local aggregate flipped so it\n // re-reads `isDirty()` and updates its own aggregate. Read mainContext\n // via the ref so we don't re-fire when the parent's value identity\n // changes (which it does every time ITS isDirty flips, which is\n // exactly when WE'D be notifying it of OUR new dirty state — an\n // infinite loop without the ref indirection).\n useEffect(() => {\n mainContextRef.current?.notifyDescendantDirtyChanged();\n }, [isDirty]);\n\n // Auto unsaved-changes warning when this is the top-most context (no outer\n // MainContext wrapping us). Same pattern as MainContext — wired here too\n // because a standalone <RecordContext> with no outer wrapper IS the\n // top-most context on the page.\n useBeforeUnloadWhen(isTopMost && resolvedWarnOnUnsavedChanges && isDirty);\n\n // Same registry registration as `MainContext` — when this RecordContext\n // is itself topmost (no outer MainContext wraps it) it reports its\n // aggregate `isDirty` to the page-wide registry so the router-blocker\n // guard can prompt on navigation. Reading through refs (already wired\n // above) avoids re-registering on every keystroke;\n // `notifyDirtyChanged()` below fires on the actual aggregate flip.\n const unsavedChangesRegistry = useUnsavedChangesRegistry();\n const resolvedWarnRef = useRef(resolvedWarnOnUnsavedChanges);\n resolvedWarnRef.current = resolvedWarnOnUnsavedChanges;\n useEffect(() => {\n if (!isTopMost) return;\n if (!unsavedChangesRegistry) return;\n return unsavedChangesRegistry.register({\n isDirty: () => resolvedWarnRef.current && isDirtyRef.current,\n });\n }, [isTopMost, unsavedChangesRegistry]);\n useEffect(() => {\n if (!isTopMost) return;\n if (!unsavedChangesRegistry) return;\n unsavedChangesRegistry.notifyDirtyChanged();\n }, [isTopMost, unsavedChangesRegistry, isDirty, resolvedWarnOnUnsavedChanges]);\n\n // Inner MainContextValue this RecordContext provides for its subtree\n // makes nested grids/M2M editors/child <RecordContext>s register with us\n // instead of skipping past to a further-up MainContext.\n const innerMainContext = useMemo<MainContextValue>(\n () => ({\n saveAll,\n refreshAll: reload,\n resetState,\n validate,\n getRequests,\n registerDescendant: registerNested,\n notifyDescendantDirtyChanged: notifyNestedDirtyChanged,\n isDirty,\n warnOnUnsavedChanges: resolvedWarnOnUnsavedChanges,\n forceSuccessfulValidationBeforeSave: resolvedForceSuccessfulValidationBeforeSave,\n }),\n [\n saveAll,\n reload,\n resetState,\n validate,\n getRequests,\n registerNested,\n notifyNestedDirtyChanged,\n isDirty,\n resolvedWarnOnUnsavedChanges,\n resolvedForceSuccessfulValidationBeforeSave,\n ],\n );\n\n // Stash `onBeforeDelete` in a ref so the callback identity can churn\n // (consumers often pass inline arrow functions) without invalidating\n // the context value or our `runBeforeDelete` callback. Each render\n // overwrites the ref with the latest closure; click-time invocation\n // reads `.current` and gets the up-to-date handler.\n const onBeforeDeleteRef = useRef<typeof onBeforeDelete>(onBeforeDelete);\n onBeforeDeleteRef.current = onBeforeDelete;\n const runBeforeDelete = useCallback(async () => {\n const handler = onBeforeDeleteRef.current;\n if (handler) await handler();\n }, []);\n\n const value = useMemo<RecordContextValue>(\n () => ({\n status: query.status,\n record,\n error: query.error,\n reload,\n save: saveAll,\n getRequests,\n resetState,\n setColumnValue,\n hydrateColumnValue,\n tableMetadata: metadataQuery.data,\n isDirty,\n dirtyColumns,\n overlayTargetId,\n runBeforeDelete,\n }),\n [\n query.status,\n query.error,\n record,\n reload,\n saveAll,\n getRequests,\n resetState,\n setColumnValue,\n hydrateColumnValue,\n metadataQuery.data,\n isDirty,\n dirtyColumns,\n overlayTargetId,\n runBeforeDelete,\n ],\n );\n\n return (\n <MainContextProvider value={innerMainContext}>\n <RecordContextContext.Provider value={value}>{children}</RecordContextContext.Provider>\n </MainContextProvider>\n );\n}\n\n/**\n * Reads the nearest ancestor {@link RecordContext}'s value. Throws when called\n * outside one — same diagnostic pattern as `useAuth`.\n *\n * @example\n * ```tsx\n * import { QueryStatus, useRecordContext } from '@powerportalspro/react';\n *\n * function ContactDetails() {\n * const { status, record } = useRecordContext();\n * if (status === QueryStatus.Loading) return <Spinner />;\n * if (status === QueryStatus.Error) return <ErrorBanner />;\n * return <h1>{record!.formattedValues?.name}</h1>;\n * }\n * ```\n */\nexport function useRecordContext(): RecordContextValue {\n const ctx = useContext(RecordContextContext);\n if (!ctx) {\n throw new Error(\n 'useRecordContext must be used inside <RecordContext>. Wrap the component tree where you need the record.',\n );\n }\n return ctx;\n}\n\n/**\n * Non-throwing variant of {@link useRecordContext} — returns the nearest\n * `<RecordContext>`'s value when one is present, otherwise `null`. Useful\n * for components that work either inside or outside a record context\n * (e.g. `<SubGrid>` picks up the cascading record when one is available\n * but accepts an explicit `record` prop as an alternative).\n *\n * Prefer {@link useRecordContext} when the component absolutely requires\n * the context — the loud throw catches wiring bugs early; this hook is\n * for the genuinely optional case.\n */\nexport function useRecordContextOptional(): RecordContextValue | null {\n return useContext(RecordContextContext);\n}\n","import type { components } from '@powerportalspro/core';\nimport type { ReactNode } from 'react';\nimport { RecordContext, useRecordContext } from './record-context.js';\n\ntype Schemas = components['schemas'];\n\n/**\n * The empty-GUID sentinel Dataverse round-trips for cleared lookups.\n * Treated as \"no value\" so an explicitly-cleared lookup short-circuits\n * the nested context.\n */\nconst EMPTY_GUID = '00000000-0000-0000-0000-000000000000';\n\nexport interface LookupRecordContextProps {\n /**\n * Logical name of the lookup column on the parent `<RecordContext>`'s\n * record. The column must resolve to a `LookupValue` (typed\n * `$type: 6`) — the component reads the lookup's `tableName` + `value`\n * and spawns a nested `<RecordContext>` for the referenced record.\n *\n * Required.\n */\n columnName: string;\n\n /**\n * Optional pre-save cancellable hook forwarded to the nested\n * `<RecordContext>`. Throwing (or returning a rejected promise) from\n * this callback aborts the save before any server call — the same\n * semantic as `<RecordContext onBeforeSave={…}>` directly.\n */\n onBeforeSave?: () => void | Promise<void>;\n\n /**\n * Children rendered inside the nested context. Typical contents:\n * edit components (`<TextEdit>`, `<NumberEdit>`, etc.) bound to\n * columns on the LOOKED-UP record's table — e.g. an\n * `<TextEdit columnName=\"firstname\" />` inside a\n * `<LookupRecordContext columnName=\"primarycontactid\">` on an account\n * form edits the linked contact's first name.\n */\n children?: ReactNode;\n\n /**\n * When `false`, renders nothing. Defaults to `true`. Useful for\n * conditionally hiding the section without unmounting the\n * declaration.\n */\n isVisible?: boolean;\n}\n\n/**\n * Branches off into a nested `<RecordContext>` for the record referenced\n * by a lookup column on the surrounding `<RecordContext>`. A thin\n * wrapper that resolves the lookup value, gates on its presence, and\n * spawns the nested context.\n *\n * Edit components inside the lookup context bind to columns on the\n * LOOKED-UP record's table, not the parent's. Changes flow through the\n * surrounding save cascade automatically via the descendant-registry\n * plumbing every `<RecordContext>` already wires up — the nested record\n * registers with the outermost `<MainContext>` (or\n * `<RecordContext>`-as-MainContext) and its pending edits join the\n * single transactional `executeMultiple` batch on save.\n *\n * **Conditional render:** the nested `<RecordContext>` is only\n * instantiated when the lookup actually has a value (non-null +\n * non-empty-GUID + has `tableName`). Empty lookups render nothing\n * avoids spinning up nested contexts, EditContext validators,\n * descendant registrations, etc. for every empty lookup column on a\n * form. With ~10 lookups on a complex form, the per-render lifecycle\n * overhead is large enough to produce a measurable freeze without this\n * guard.\n *\n * ```tsx\n * <RecordContext table=\"account\" id={accountId}>\n * <Section>\n * <SectionColumn header={<Text>Account</Text>}>\n * <TextEdit columnName=\"name\" />\n * <TextEdit columnName=\"accountnumber\" />\n * </SectionColumn>\n *\n * <SectionColumn header={<Text>Primary contact</Text>}>\n * <LookupRecordContext columnName=\"primarycontactid\">\n * {/* These edits bind to the linked CONTACT, not the account *\\/}\n * <TextEdit columnName=\"firstname\" />\n * <TextEdit columnName=\"lastname\" />\n * <TextEdit columnName=\"emailaddress1\" type=\"email\" />\n * </LookupRecordContext>\n * </SectionColumn>\n * </Section>\n * </RecordContext>\n * ```\n *\n * Renders `null` when:\n * - the surrounding `<RecordContext>` hasn't loaded yet,\n * - the parent column doesn't carry a `LookupValue`,\n * - the lookup is the empty GUID (Dataverse's \"no value\" sentinel), or\n * - the lookup is missing its `tableName`.\n *\n * `<LookupRecordContext>` is read-then-branch only — consumers who want\n * to change the lookup value programmatically use\n * `useLookupColumn(columnName)` from the parent context instead.\n */\nexport function LookupRecordContext({\n columnName,\n onBeforeSave,\n children,\n isVisible = true,\n}: LookupRecordContextProps) {\n // Hook order: useRecordContext FIRST so the rules-of-hooks invariant\n // holds across the early-return branches below. The hook throws if\n // mounted outside a parent <RecordContext> — the user-facing diagnostic\n // matches the pattern <SubGrid>/<TextEdit>/etc. follow.\n const parent = useRecordContext();\n if (!isVisible) return null;\n\n const raw = parent.record?.properties?.[columnName] as\n | Schemas['ColumnValueBaseLookupValue']\n | undefined;\n\n // Three short-circuit conditions, each preserving the \"no DOM, no\n // nested lifecycle\" guarantee that makes empty-lookup-heavy forms\n // cheap:\n // - no raw value (lookup never set, or parent record still loading)\n // - empty-GUID sentinel (Dataverse round-trips cleared lookups as\n // Guid.Empty — both shapes mean \"no record to load\")\n // - missing tableName (defensive — without a target table the\n // nested useRecord call would have nothing to query)\n if (!raw?.value || raw.value === EMPTY_GUID || !raw.tableName) {\n return null;\n }\n\n return (\n <RecordContext\n table={raw.tableName}\n id={raw.value}\n {...(onBeforeSave && { onBeforeSave })}\n >\n {children}\n </RecordContext>\n );\n}\n","import type { components } from '@powerportalspro/core';\nimport { useRecordContext } from './record-context.js';\n\ntype ColumnMetadataBase = components['schemas']['ColumnMetadataBase'];\n\n/**\n * Returns the column metadata for the given column name from the parent\n * {@link RecordContext}'s table metadata. Returns `undefined` when:\n * - the table metadata hasn't loaded yet (the request is still in flight),\n * - the table metadata fetch failed,\n * - the column doesn't exist on the table.\n *\n * Edit components use this to auto-resolve defaults (`maxLength`, `min`,\n * `max`, `precision`, `required`, `readOnly`) when consumer-passed props\n * are absent. Pass-through props always win — metadata-derived defaults\n * only fill the slots the consumer left unset.\n *\n * Throws if not used inside a `<RecordContext>`, since column metadata only\n * makes sense relative to a record's table.\n */\nexport function useColumnMetadata(columnName: string): ColumnMetadataBase | undefined {\n const { tableMetadata } = useRecordContext();\n return tableMetadata?.columns.find((c) => c.columnName === columnName);\n}\n","import { createContext, useCallback, useContext } from 'react';\nimport type { ReactNode } from 'react';\nimport { useLocalizer, type Localizer } from './localizer.js';\n\n/**\n * Outcome of a dialog interaction. `cancelled` is `true` when the user\n * dismissed the dialog (clicked the secondary button on a confirmation,\n * pressed Escape, clicked the backdrop, or otherwise rejected the action).\n *\n * Kept as an object rather than a naked boolean so we can extend with\n * `data?` etc. when custom-content dialogs land without a wire-breaking\n * change.\n */\nexport interface DialogResult {\n /** True when the user dismissed/cancelled, false when they confirmed. */\n readonly cancelled: boolean;\n}\n\n/** Common shape for all single-button dialogs (success / warning / error / info). */\nexport interface DialogOptions {\n /** Dialog title. When omitted, the implementation supplies a sensible default for the variant. */\n title?: string;\n /** Label for the primary (and only) action button. Defaults to `\"OK\"`. */\n primaryText?: string;\n}\n\n/** Options for a confirmation dialog (two action buttons). */\nexport interface ConfirmationDialogOptions extends DialogOptions {\n /** Label for the primary (affirmative) button. Defaults to `\"Yes\"`. */\n primaryText?: string;\n /** Label for the secondary (negative) button. Defaults to `\"No\"`. */\n secondaryText?: string;\n}\n\n/**\n * Headless dialog service contract. The framework calls these methods to\n * surface success / warning / error / info messages and confirmation\n * prompts; consumers provide the concrete implementation via\n * {@link DialogServiceProvider} (typically the Fluent UI implementation\n * that ships with `@powerportalspro/react-fluent`, but any dialog UI\n * works as long as it satisfies this contract).\n *\n * **Message format.** The `message` argument is treated as trusted HTML\n * markup — the framework's bundled localization strings (e.g.\n * `app.messages.unsaved-changes`) rely on this for inline `<br/>`,\n * `<strong>`, `<em>` tags. Don't pass unsanitized user input.\n *\n * Each method returns a {@link DialogResult} so the same shape covers both\n * single-button (always `{ cancelled: false }` once dismissed) and\n * confirmation dialogs.\n */\nexport interface DialogService {\n showSuccessAsync(message: string, options?: DialogOptions): Promise<DialogResult>;\n showWarningAsync(message: string, options?: DialogOptions): Promise<DialogResult>;\n showErrorAsync(message: string, options?: DialogOptions): Promise<DialogResult>;\n showInfoAsync(message: string, options?: DialogOptions): Promise<DialogResult>;\n showConfirmationAsync(\n message: string,\n options?: ConfirmationDialogOptions,\n ): Promise<DialogResult>;\n}\n\nconst DialogServiceContext = createContext<DialogService | null>(null);\nDialogServiceContext.displayName = 'DialogService';\n\n/**\n * Reads the nearest ancestor {@link DialogServiceProvider}. Throws when called\n * outside one — the framework relies on the dialog service for unsaved-changes\n * warnings, error reporting, etc.; throwing surfaces missing-provider bugs at\n * mount time instead of producing silent no-op dialogs.\n */\nexport function useDialogService(): DialogService {\n const ctx = useContext(DialogServiceContext);\n if (!ctx) {\n throw new Error(\n 'useDialogService must be used inside a <DialogServiceProvider>. ' +\n 'Wrap your app in <FluentDialogProvider> from @powerportalspro/react-fluent ' +\n '(or your own implementation) so framework dialogs have somewhere to render.',\n );\n }\n return ctx;\n}\n\n/**\n * Provides a {@link DialogService} implementation to the subtree. UI libraries\n * that ship a `DialogService` (e.g. `@powerportalspro/react-fluent`'s\n * `<FluentDialogProvider>`) wrap their tree with this internally.\n */\nexport function DialogServiceProvider({\n value,\n children,\n}: {\n value: DialogService;\n children: ReactNode;\n}) {\n return <DialogServiceContext.Provider value={value}>{children}</DialogServiceContext.Provider>;\n}\n\n// ---------------------------------------------------------------------------\n// Framework-specific helpers — exposed as standalone functions that take a\n// `DialogService` rather than a second interface. Keeps the API surface flat\n// (one service contract, one context) and lets consumers compose these into\n// their own helpers without inheritance plumbing.\n// ---------------------------------------------------------------------------\n\n/**\n * Prompts the user about unsaved changes. Returns `true` if the user wants\n * to continue (discarding their edits), `false` if they want to cancel and\n * keep editing.\n *\n * Used by the framework before navigation, refresh, and any other\n * operation that would discard pending edits. Application code can also\n * call it directly for custom flows.\n *\n * Strings come from the supplied {@link Localizer} (default English from\n * the framework's bundled JSON). Keys: `app.messages.unsaved-changes`\n * (body), `app.messages.unsaved-changes-title` (title),\n * `app.buttons.yes.label` / `app.buttons.no.label` (button labels).\n */\nexport async function warnAboutUnsavedChangesAsync(\n dialog: DialogService,\n localizer: Localizer,\n): Promise<boolean> {\n const result = await dialog.showConfirmationAsync(localizer.t('app.messages.unsaved-changes'), {\n title: localizer.t('app.messages.unsaved-changes-title'),\n primaryText: localizer.t('app.buttons.yes.label'),\n secondaryText: localizer.t('app.buttons.no.label'),\n });\n return !result.cancelled;\n}\n\n/**\n * Hook variant of {@link warnAboutUnsavedChangesAsync}. Returns a stable\n * function bound to the current dialog service + localizer from context.\n */\nexport function useWarnAboutUnsavedChanges(): () => Promise<boolean> {\n const dialog = useDialogService();\n const localizer = useLocalizer();\n return useCallback(\n () => warnAboutUnsavedChangesAsync(dialog, localizer),\n [dialog, localizer],\n );\n}\n","import { useBeforeUnloadWhen } from './internal/use-before-unload-when.js';\nimport { useWarnAboutUnsavedChanges } from './dialog-service.js';\nimport { useMainContext } from './main-context.js';\nimport { useIsAnyDirty } from './unsaved-changes-registry.js';\n\n/**\n * Standalone hook that registers a `beforeunload` handler while the nearest\n * `MainContext` / `RecordContext` reports `isDirty`.\n *\n * **You usually don't need this.** Both `<MainContext>` and `<RecordContext>`\n * register the warning automatically when they're the top-most context on\n * the page (controlled via the `warnOnUnsavedChanges` prop, default `true`).\n * This hook is exported for cases where you want to wire the warning to a\n * dirty signal that doesn't come from a MainContext (e.g. a `react-hook-form`\n * `formState.isDirty`) or from a non-top-most context.\n *\n * **What this covers:** browser-level navigation — closing the tab, hitting\n * the back button, refreshing the page, typing a new URL. The browser's\n * native prompt fires; the message it shows is browser-controlled (you\n * cannot customize it — security restriction since Chrome 60+).\n *\n * **What this does NOT cover:** in-app route changes via `react-router`'s\n * `<Link>` or `navigate()`. Those don't trigger `beforeunload` because the\n * page never actually unloads. For soft-nav blocking, use\n * {@link useUnsavedChangesGuard} from any router's blocker callback (see\n * its JSDoc for canonical patterns for react-router, TanStack Router,\n * Wouter, etc.).\n */\nexport function useUnsavedChangesWarning(): void {\n const ctx = useMainContext();\n const isDirty = ctx?.isDirty ?? false;\n useBeforeUnloadWhen(isDirty);\n}\n\n/**\n * Headless controller for building a router blocker that prompts the user\n * before discarding unsaved edits. Bundles the two primitives any soft-nav\n * guard needs:\n *\n * - `isAnyDirty` — reactive boolean tracking whether any registered\n * top-most {@link MainContext} / {@link RecordContext} on the page is\n * dirty (read-through to {@link useIsAnyDirty}).\n * - `warn` — async function that opens the framework confirmation dialog\n * and resolves `true` if the user confirmed discarding the edits,\n * `false` if they cancelled (read-through to\n * {@link useWarnAboutUnsavedChanges}).\n *\n * Router-agnostic — this package has zero router dependencies. Pair it\n * with whichever router blocker your app uses:\n *\n * **react-router-dom (data router):** use the pre-built\n * `<RouterUnsavedChangesGuard>` from `@powerportalspro/react-fluent` — it\n * wraps this hook + react-router's `useBlocker`. Or roll your own:\n *\n * ```tsx\n * import { useBlocker } from 'react-router-dom';\n * function MyGuard() {\n * const guard = useUnsavedChangesGuard();\n * const blocker = useBlocker(\n * ({ currentLocation, nextLocation }) =>\n * guard.isAnyDirty &&\n * currentLocation.pathname !== nextLocation.pathname,\n * );\n * useEffect(() => {\n * if (blocker.state !== 'blocked') return;\n * let cancelled = false;\n * (async () => {\n * const proceed = await guard.warn();\n * if (cancelled) return;\n * if (proceed) blocker.proceed();\n * else blocker.reset();\n * })();\n * return () => { cancelled = true; };\n * }, [blocker, guard]);\n * return null;\n * }\n * ```\n *\n * **TanStack Router:** use `useBlocker({ blockerFn })`. The blocker fn\n * returns `true` to allow the navigation, `false` to block. Pump the\n * `warn` result through inverted:\n *\n * ```tsx\n * import { useBlocker } from '@tanstack/react-router';\n * function MyGuard() {\n * const guard = useUnsavedChangesGuard();\n * useBlocker({\n * blockerFn: async () => {\n * if (!guard.isAnyDirty) return true; // allow\n * return await guard.warn(); // user choice\n * },\n * });\n * return null;\n * }\n * ```\n *\n * **Wouter:** doesn't ship a blocker API. Hand-roll by intercepting\n * `<Link>` clicks at the app shell — call `event.preventDefault()` and\n * route through `guard.warn()` before calling `navigate(to)`.\n *\n * **Next.js App Router:** `router.events` is gone in the App Router; the\n * community pattern is to intercept `<Link>` clicks similar to Wouter.\n * Same flow with `guard.warn()`.\n *\n * Same-pathname navigations (search-param tweaks, `replace: true` to the\n * current URL) should not be blocked — the user isn't actually leaving\n * the page. The react-router-dom guard above filters those out via the\n * `currentLocation.pathname !== nextLocation.pathname` check; the TanStack\n * blocker fn can do the equivalent using its passed-in location args.\n */\nexport interface UnsavedChangesGuardController {\n /**\n * Reactive — `true` when at least one top-most\n * {@link MainContext} / {@link RecordContext} registered with the page's\n * {@link UnsavedChangesRegistry} reports dirty. Subscribes via\n * {@link useSyncExternalStore} so it re-renders the calling component\n * exactly when the aggregate flips.\n */\n readonly isAnyDirty: boolean;\n /**\n * Opens the framework's \"discard your changes?\" confirmation dialog\n * and resolves `true` if the user confirmed proceeding (discarding\n * pending edits), `false` if they cancelled (staying on the page).\n * Stable identity across renders when the underlying dialog service\n * / localizer haven't changed, so it's safe to put in effect deps.\n */\n readonly warn: () => Promise<boolean>;\n}\n\nexport function useUnsavedChangesGuard(): UnsavedChangesGuardController {\n const isAnyDirty = useIsAnyDirty();\n const warn = useWarnAboutUnsavedChanges();\n return { isAnyDirty, warn };\n}\n","export const VERSION = '0.1.0';\n\n// Caret-safe masking primitives. Use `useMaskedTextField` to wire masking\n// onto a bare <input>; the Fluent-wrapped `<MaskedTextField>` in\n// @powerportalspro/react-fluent is built on top of this hook.\nexport {\n MaskMode,\n reformat,\n type MaskOptions,\n type NumericOptions,\n type ReformatResult,\n} from './masking/reformat.js';\nexport {\n useMaskedTextField,\n type UseMaskedTextFieldOptions,\n type UseMaskedTextFieldReturn,\n} from './masking/use-masked-text-field.js';\n\nexport {\n AuthStatus,\n PowerPortalsProContext,\n type AuthState,\n type PowerPortalsProContextValue,\n} from './context.js';\nexport { PowerPortalsProProvider, type PowerPortalsProProviderProps } from './provider.js';\n\nexport { usePowerPortalsPro } from './use-power-portals-pro.js';\nexport { useAuth, type UseAuthResult } from './use-auth.js';\nexport { AuthorizedView, type AuthorizedViewProps } from './authorized-view.js';\n\nexport { useRecord, type UseRecordOptions } from './use-record.js';\nexport { useFetchRecords, type UseFetchRecordsOptions } from './use-fetch-records.js';\nexport { useGridData, type UseGridDataOptions } from './use-grid-data.js';\nexport {\n useViewDataSource,\n type ViewDataSource,\n type ViewDataSourceHighlight,\n type UseViewDataSourceOptions,\n} from './use-view-data-source.js';\nexport { useTableMetadata, type UseTableMetadataOptions } from './use-table-metadata.js';\nexport { useTablePermissions, type UseTablePermissionsOptions } from './use-table-permissions.js';\nexport { useViewMetadata, type UseViewMetadataOptions } from './use-view-metadata.js';\nexport { useViewsForTable, type UseViewsForTableOptions } from './use-views-for-table.js';\nexport {\n useOrganizationSettings,\n type UseOrganizationSettingsOptions,\n} from './use-organization-settings.js';\nexport {\n useSupportedLocales,\n type UseSupportedLocalesOptions,\n} from './use-supported-locales.js';\nexport { useQueryParam } from './use-query-param.js';\n// Client-side metadata caches. Consumers can swap the default in-memory\n// implementations for custom ones (e.g. localStorage-backed for cross-\n// session persistence) via the provider's `tableMetadataCache` /\n// `viewMetadataCache` props.\nexport type {\n OrganizationSettingsCache,\n TableMetadataCache,\n ViewMetadataCache,\n ViewsForTableCache,\n} from './caches/types.js';\nexport {\n InMemoryOrganizationSettingsCache,\n InMemoryTableMetadataCache,\n InMemoryViewMetadataCache,\n InMemoryViewsForTableCache,\n} from './caches/in-memory-cache.js';\n\nexport { QueryStatus, type QueryResult } from './internal/use-async-resource.js';\n\nexport {\n MainContext,\n MainContextProvider,\n useMainContext,\n type MainContextProps,\n type MainContextValue,\n type MainContextDescendant,\n} from './main-context.js';\n\nexport {\n RecordContext,\n useRecordContext,\n useRecordContextOptional,\n type RecordContextProps,\n type RecordContextValue,\n} from './record-context.js';\n\n// `<LookupRecordContext>` — branches off into a nested `<RecordContext>`\n// for the record referenced by a lookup column on the parent context.\n// Lets consumers inline-edit the looked-up record's fields with changes\n// folded into the parent's transactional save. Lazy — only spins up the\n// nested context when the lookup actually carries a target record.\nexport {\n LookupRecordContext,\n type LookupRecordContextProps,\n} from './lookup-record-context.js';\n\nexport { useColumnMetadata } from './use-column-metadata.js';\n\nexport {\n DialogServiceProvider,\n useDialogService,\n warnAboutUnsavedChangesAsync,\n useWarnAboutUnsavedChanges,\n type DialogService,\n type DialogResult,\n type DialogOptions,\n type ConfirmationDialogOptions,\n} from './dialog-service.js';\n\nexport {\n OverlayServiceProvider,\n useOverlayService,\n useOverlayForTarget,\n useOverlayServiceState,\n useRunWithOverlay,\n runWithOverlayAsync,\n type OverlayService,\n type OverlayInstance,\n type OverlayOptions,\n} from './overlay-service.js';\n\nexport {\n useUnsavedChangesWarning,\n useUnsavedChangesGuard,\n type UnsavedChangesGuardController,\n} from './unsaved-changes-warning.js';\nexport {\n UnsavedChangesRegistryProvider,\n useUnsavedChangesRegistry,\n useIsAnyDirty,\n type UnsavedChangesRegistry,\n type UnsavedChangesReporter,\n type UnsavedChangesRegistryProviderProps,\n} from './unsaved-changes-registry.js';\n\nexport {\n ValidationProvider,\n useValidationContext,\n useColumnValidationErrors,\n type ValidationContextValue,\n type ColumnValidator,\n} from './validation-context.js';\n\nexport {\n LocalizerProvider,\n DefaultLocalizerProvider,\n LocaleProvider,\n useLocalizer,\n useT,\n usePrefixedT,\n useLocale,\n useLocalization,\n useLocalizationLoaded,\n useLocalizationPrefetcher,\n createLocalizer,\n flattenStrings,\n interpolate,\n resolveLocalizationUrl,\n detectLocale,\n getAspNetCultureCookie,\n setAspNetCultureCookie,\n getLocaleFromUrlPath,\n useUrlCultureStrategy,\n useDefaultLocale,\n shouldPrefixLocale,\n stripLocalePrefix,\n buildLocalePath,\n ASP_NET_CULTURE_COOKIE,\n DEFAULT_URL_LOCALE_PATTERN,\n type Localizer,\n type LocalizationPrefetcher,\n type LocaleState,\n type LocaleProviderProps,\n type CreateLocalizerOptions,\n type DefaultLocalizerProviderProps,\n type UrlCultureStrategyName,\n} from './localizer.js';\n"]}
|