@powerportalspro/react 5.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,2333 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var core = require('@powerportalspro/core');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // src/context.ts
8
+ var AuthStatus = {
9
+ Loading: "loading",
10
+ Anonymous: "anonymous",
11
+ Authenticated: "authenticated"
12
+ };
13
+ var PowerPortalsProContext = react.createContext(null);
14
+
15
+ // src/caches/in-memory-cache.ts
16
+ var InMemoryCache = class {
17
+ constructor(fetcher) {
18
+ this.fetcher = fetcher;
19
+ }
20
+ fetcher;
21
+ resolved = /* @__PURE__ */ new Map();
22
+ inFlight = /* @__PURE__ */ new Map();
23
+ async getAsync(key) {
24
+ const cached = this.resolved.get(key);
25
+ if (cached !== void 0) return cached;
26
+ const pending = this.inFlight.get(key);
27
+ if (pending) return pending;
28
+ const promise = (async () => {
29
+ try {
30
+ const result = await this.fetcher(key);
31
+ if (result !== null) this.resolved.set(key, result);
32
+ return result;
33
+ } finally {
34
+ this.inFlight.delete(key);
35
+ }
36
+ })();
37
+ this.inFlight.set(key, promise);
38
+ return promise;
39
+ }
40
+ invalidate(key) {
41
+ this.resolved.delete(key);
42
+ }
43
+ clear() {
44
+ this.resolved.clear();
45
+ }
46
+ };
47
+ var InMemoryTableMetadataCache = class {
48
+ cache;
49
+ constructor(ppp) {
50
+ this.cache = new InMemoryCache(
51
+ (tableLogicalName) => ppp.retrieveTableMetadataAsync(tableLogicalName)
52
+ );
53
+ }
54
+ getAsync(tableLogicalName) {
55
+ return this.cache.getAsync(tableLogicalName);
56
+ }
57
+ invalidate(tableLogicalName) {
58
+ this.cache.invalidate(tableLogicalName);
59
+ }
60
+ clear() {
61
+ this.cache.clear();
62
+ }
63
+ };
64
+ var InMemoryViewMetadataCache = class {
65
+ cache;
66
+ constructor(ppp) {
67
+ this.cache = new InMemoryCache(
68
+ (viewId) => ppp.retrieveViewMetadataAsync(viewId)
69
+ );
70
+ }
71
+ getAsync(viewId) {
72
+ return this.cache.getAsync(viewId);
73
+ }
74
+ invalidate(viewId) {
75
+ this.cache.invalidate(viewId);
76
+ }
77
+ clear() {
78
+ this.cache.clear();
79
+ }
80
+ };
81
+ var InMemoryEnvironmentFileSettingsCache = class _InMemoryEnvironmentFileSettingsCache {
82
+ static KEY = "environment-file-settings";
83
+ cache;
84
+ constructor(ppp) {
85
+ this.cache = new InMemoryCache(
86
+ () => ppp.getEnvironmentFileSettingsAsync()
87
+ );
88
+ }
89
+ getAsync() {
90
+ return this.cache.getAsync(_InMemoryEnvironmentFileSettingsCache.KEY);
91
+ }
92
+ invalidate() {
93
+ this.cache.invalidate(_InMemoryEnvironmentFileSettingsCache.KEY);
94
+ }
95
+ };
96
+ var InMemoryViewsForTableCache = class {
97
+ cache;
98
+ constructor(ppp) {
99
+ this.cache = new InMemoryCache(async (tableLogicalName) => {
100
+ const result = await ppp.retrieveViewsForTableAsync(tableLogicalName);
101
+ return result ?? [];
102
+ });
103
+ }
104
+ async getAsync(tableLogicalName) {
105
+ const result = await this.cache.getAsync(tableLogicalName);
106
+ return result ?? [];
107
+ }
108
+ invalidate(tableLogicalName) {
109
+ this.cache.invalidate(tableLogicalName);
110
+ }
111
+ clear() {
112
+ this.cache.clear();
113
+ }
114
+ };
115
+ function usePowerPortalsPro() {
116
+ const ctx = react.useContext(PowerPortalsProContext);
117
+ if (!ctx) {
118
+ throw new Error(
119
+ "usePowerPortalsPro must be used inside <PowerPortalsProProvider>. Wrap your app at or near the root."
120
+ );
121
+ }
122
+ return ctx;
123
+ }
124
+ var LocalizerContext = react.createContext(null);
125
+ LocalizerContext.displayName = "Localizer";
126
+ function useLocalizer() {
127
+ const ctx = react.useContext(LocalizerContext);
128
+ if (!ctx) {
129
+ throw new Error(
130
+ "useLocalizer must be used inside a <LocalizerProvider>. PowerPortalsProProvider auto-mounts a default localizer; ensure your tree is wrapped."
131
+ );
132
+ }
133
+ return ctx;
134
+ }
135
+ function useT() {
136
+ return useLocalizer().t;
137
+ }
138
+ function usePrefixedT(prefix) {
139
+ const t = useT();
140
+ return react.useCallback(
141
+ (key, args) => t(`${prefix}.${key}`, args),
142
+ [t, prefix]
143
+ );
144
+ }
145
+ var LocalizationPrefetcherContext = react.createContext(null);
146
+ LocalizationPrefetcherContext.displayName = "LocalizationPrefetcher";
147
+ function useLocalizationPrefetcher() {
148
+ return react.useContext(LocalizationPrefetcherContext);
149
+ }
150
+ var NOOP_UNSUBSCRIBE = () => void 0;
151
+ function useLocalization(prefixes) {
152
+ const prefetcher = useLocalizationPrefetcher();
153
+ const prefixesKey = react.useMemo(() => {
154
+ if (prefixes.length === 0) return "";
155
+ const unique = Array.from(new Set(prefixes));
156
+ unique.sort();
157
+ return unique.join("\n");
158
+ }, [prefixes]);
159
+ react.useEffect(() => {
160
+ if (!prefetcher) return;
161
+ if (prefixesKey === "") return;
162
+ const tokens = prefixesKey.split("\n");
163
+ prefetcher.prefetch(tokens);
164
+ return prefetcher.subscribe(() => {
165
+ prefetcher.prefetch(tokens);
166
+ });
167
+ }, [prefetcher, prefixesKey]);
168
+ }
169
+ function useLocalizationLoaded(prefixes) {
170
+ const prefetcher = useLocalizationPrefetcher();
171
+ const subscribe = react.useCallback(
172
+ (listener) => prefetcher?.subscribe(listener) ?? NOOP_UNSUBSCRIBE,
173
+ [prefetcher]
174
+ );
175
+ const getSnapshot = react.useCallback(
176
+ () => prefetcher ? prefetcher.isLoaded(prefixes) : true,
177
+ [prefetcher, prefixes]
178
+ );
179
+ return react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshotAlwaysLoaded);
180
+ }
181
+ var getServerSnapshotAlwaysLoaded = () => true;
182
+ function LocalizerProvider({
183
+ value,
184
+ children
185
+ }) {
186
+ return /* @__PURE__ */ jsxRuntime.jsx(LocalizerContext.Provider, { value, children });
187
+ }
188
+ function flattenStrings(obj, prefix = "") {
189
+ const out = {};
190
+ for (const [k, v] of Object.entries(obj)) {
191
+ const key = prefix ? `${prefix}.${k}` : k;
192
+ if (typeof v === "string") {
193
+ out[key] = v;
194
+ } else if (v !== null && typeof v === "object" && !Array.isArray(v)) {
195
+ Object.assign(out, flattenStrings(v, key));
196
+ }
197
+ }
198
+ return out;
199
+ }
200
+ function interpolate(template, args) {
201
+ if (!args || args.length === 0) return template;
202
+ return template.replace(/\{(\d+)(?::[^}]*)?\}/g, (match, indexStr) => {
203
+ const idx = Number(indexStr);
204
+ if (idx < 0 || idx >= args.length) return match;
205
+ return String(args[idx]);
206
+ });
207
+ }
208
+ function createLocalizer(strings, options) {
209
+ const warnedKeys = /* @__PURE__ */ new Set();
210
+ return {
211
+ t(key, args) {
212
+ const template = strings[key];
213
+ if (template === void 0) {
214
+ if (options?.onMissingKey && !warnedKeys.has(key)) {
215
+ warnedKeys.add(key);
216
+ options.onMissingKey(key);
217
+ }
218
+ return key;
219
+ }
220
+ return interpolate(template, args);
221
+ }
222
+ };
223
+ }
224
+ async function fetchLocalizationJson(url) {
225
+ const response = await fetch(url);
226
+ if (!response.ok) throw new Error(`HTTP ${response.status} fetching ${url}`);
227
+ return await response.json();
228
+ }
229
+ function resolveLocalizationUrl(template, locale) {
230
+ return template.replace(/\{locale\}/g, locale);
231
+ }
232
+ var ASP_NET_CULTURE_COOKIE = ".AspNetCore.Culture";
233
+ function getAspNetCultureCookie() {
234
+ if (typeof document === "undefined" || !document.cookie) return null;
235
+ for (const part of document.cookie.split("; ")) {
236
+ const eq = part.indexOf("=");
237
+ if (eq < 0) continue;
238
+ const name = part.substring(0, eq);
239
+ if (name !== ASP_NET_CULTURE_COOKIE) continue;
240
+ const value = decodeURIComponent(part.substring(eq + 1));
241
+ const cMatch = value.match(/c=([^|]+)/);
242
+ if (cMatch?.[1]) return cMatch[1];
243
+ return value || null;
244
+ }
245
+ return null;
246
+ }
247
+ function setAspNetCultureCookie(culture, options) {
248
+ if (typeof document === "undefined") return;
249
+ const path = options?.path ?? "/";
250
+ const maxAge = options?.maxAgeSeconds ?? 31536e3;
251
+ const value = encodeURIComponent(`c=${culture}|uic=${culture}`);
252
+ document.cookie = `${ASP_NET_CULTURE_COOKIE}=${value}; path=${path}; max-age=${maxAge}; samesite=lax`;
253
+ }
254
+ var DEFAULT_URL_LOCALE_PATTERN = /^\/([a-z]{2}(?:-[a-zA-Z]{2,4})?)(?:\/|$)/;
255
+ function getLocaleFromUrlPath(pattern = DEFAULT_URL_LOCALE_PATTERN) {
256
+ if (typeof window === "undefined") return null;
257
+ const match = window.location.pathname.match(pattern);
258
+ return match?.[1] ?? null;
259
+ }
260
+ function detectLocale(options) {
261
+ const {
262
+ matchUrl = true,
263
+ urlPattern = DEFAULT_URL_LOCALE_PATTERN,
264
+ matchBrowser = true,
265
+ fallback = "en"
266
+ } = options ?? {};
267
+ if (matchUrl) {
268
+ const fromUrl = getLocaleFromUrlPath(urlPattern);
269
+ if (fromUrl) return fromUrl;
270
+ }
271
+ const fromCookie = getAspNetCultureCookie();
272
+ if (fromCookie) return fromCookie;
273
+ if (matchBrowser && typeof navigator !== "undefined" && navigator.language) {
274
+ const primary = navigator.language.split("-")[0];
275
+ if (primary) return primary;
276
+ }
277
+ return fallback;
278
+ }
279
+ var MISSING_KEY_FLUSH_DELAY_MS = 75;
280
+ var DEFAULT_BUNDLE_SENTINEL = "defaultBundle";
281
+ function coerceKeyToToken(key) {
282
+ if (!key) return null;
283
+ const lower = key.toLowerCase();
284
+ if (!lower.startsWith("tables.")) return null;
285
+ const afterTables = lower.substring("tables.".length);
286
+ const firstDot = afterTables.indexOf(".");
287
+ if (firstDot < 0) return `tables.${afterTables}`;
288
+ const tableName = afterTables.substring(0, firstDot);
289
+ const afterTable = afterTables.substring(firstDot + 1);
290
+ if (afterTable.startsWith("views.")) {
291
+ const afterViews = afterTable.substring("views.".length);
292
+ const nextDot = afterViews.indexOf(".");
293
+ const viewId = nextDot < 0 ? afterViews : afterViews.substring(0, nextDot);
294
+ if (!viewId) return null;
295
+ return `views.${viewId}`;
296
+ }
297
+ return `tables.${tableName}`;
298
+ }
299
+ function coercePrefixToToken(prefix) {
300
+ if (!prefix) return null;
301
+ const lower = prefix.toLowerCase();
302
+ if (lower.startsWith("views.")) {
303
+ const afterViews = lower.substring("views.".length);
304
+ const nextDot = afterViews.indexOf(".");
305
+ const viewId = nextDot < 0 ? afterViews : afterViews.substring(0, nextDot);
306
+ return viewId ? `views.${viewId}` : null;
307
+ }
308
+ return coerceKeyToToken(prefix);
309
+ }
310
+ var LocaleContext = react.createContext(null);
311
+ LocaleContext.displayName = "Locale";
312
+ var LocalizationManifestContext = react.createContext(null);
313
+ LocalizationManifestContext.displayName = "LocalizationManifest";
314
+ function useLocale() {
315
+ const ctx = react.useContext(LocaleContext);
316
+ return ctx ?? { locale: "en", setLocale: noopSetLocale };
317
+ }
318
+ var noopSetLocale = (_) => void 0;
319
+ function LocaleProvider({
320
+ initialLocale = "en",
321
+ autoDetect = true,
322
+ urlPattern,
323
+ persistToCookie = true,
324
+ locale: controlledLocale,
325
+ onLocaleChange,
326
+ children
327
+ }) {
328
+ const [internalLocale, setInternalLocale] = react.useState(() => {
329
+ if (!autoDetect) return initialLocale;
330
+ return detectLocale({
331
+ matchUrl: urlPattern !== null,
332
+ ...urlPattern instanceof RegExp && { urlPattern },
333
+ fallback: initialLocale
334
+ });
335
+ });
336
+ const isControlled = controlledLocale !== void 0;
337
+ const locale = isControlled ? controlledLocale : internalLocale;
338
+ const setLocale = react.useCallback(
339
+ (next) => {
340
+ if (!isControlled) setInternalLocale(next);
341
+ if (persistToCookie) setAspNetCultureCookie(next);
342
+ onLocaleChange?.(next);
343
+ },
344
+ [isControlled, persistToCookie, onLocaleChange]
345
+ );
346
+ const value = react.useMemo(() => ({ locale, setLocale }), [locale, setLocale]);
347
+ return /* @__PURE__ */ jsxRuntime.jsx(LocaleContext.Provider, { value, children });
348
+ }
349
+ function defaultMissingKeyWarning(key) {
350
+ console.warn(`PowerPortalsPro: missing localization key "${key}"`);
351
+ }
352
+ function DefaultLocalizerProvider({
353
+ urls,
354
+ url,
355
+ onMissingKey = defaultMissingKeyWarning,
356
+ prefixes,
357
+ children
358
+ }) {
359
+ const [cache, setCache] = react.useState({});
360
+ const [supportedLocales, setSupportedLocales] = react.useState(void 0);
361
+ const { locale } = useLocale();
362
+ const { ppp } = usePowerPortalsPro();
363
+ const localeRef = react.useRef(locale);
364
+ localeRef.current = locale;
365
+ const pppRef = react.useRef(ppp);
366
+ pppRef.current = ppp;
367
+ const resolvedLocaleRef = react.useRef(null);
368
+ const thumbprintsRef = react.useRef(null);
369
+ const pendingTokensRef = react.useRef(/* @__PURE__ */ new Set());
370
+ const triedTokensRef = react.useRef(/* @__PURE__ */ new Set());
371
+ const pendingTimerRef = react.useRef(null);
372
+ const settledTokensRef = react.useRef(/* @__PURE__ */ new Set());
373
+ const listenersRef = react.useRef(/* @__PURE__ */ new Set());
374
+ const baselinePrefixesRef = react.useRef([]);
375
+ const baselineTokensRef = react.useRef([]);
376
+ const baselinePrefixesKey = react.useMemo(() => {
377
+ const input = prefixes ?? [];
378
+ if (input.length === 0) {
379
+ baselinePrefixesRef.current = [];
380
+ baselineTokensRef.current = [];
381
+ return "";
382
+ }
383
+ const unique = Array.from(new Set(input));
384
+ unique.sort();
385
+ baselinePrefixesRef.current = unique;
386
+ const tokens = [];
387
+ for (const p of unique) {
388
+ const t = coercePrefixToToken(p);
389
+ if (t !== null) tokens.push(t);
390
+ }
391
+ baselineTokensRef.current = tokens;
392
+ return unique.join("\n");
393
+ }, [prefixes]);
394
+ const notifyListeners = react.useCallback(() => {
395
+ const snapshot = Array.from(listenersRef.current);
396
+ for (const listener of snapshot) listener();
397
+ }, []);
398
+ const previousRunRef = react.useRef(null);
399
+ const flushPendingRef = react.useRef(() => void 0);
400
+ const overlayTemplates = urls ?? (url !== void 0 ? [url] : []);
401
+ const resolvedOverlayUrls = overlayTemplates.map((t) => resolveLocalizationUrl(t, locale));
402
+ const overlayUrlsKey = resolvedOverlayUrls.join("\n");
403
+ react.useEffect(() => {
404
+ let cancelled = false;
405
+ const previous = previousRunRef.current;
406
+ const isLocaleOrUrlChange = previous !== null && (previous.locale !== locale || previous.overlayKey !== overlayUrlsKey);
407
+ previousRunRef.current = { locale, overlayKey: overlayUrlsKey };
408
+ if (isLocaleOrUrlChange) {
409
+ setCache({});
410
+ setSupportedLocales(void 0);
411
+ pendingTokensRef.current = /* @__PURE__ */ new Set();
412
+ triedTokensRef.current = /* @__PURE__ */ new Set();
413
+ if (pendingTimerRef.current !== null) {
414
+ clearTimeout(pendingTimerRef.current);
415
+ pendingTimerRef.current = null;
416
+ }
417
+ if (settledTokensRef.current.size > 0) {
418
+ settledTokensRef.current = /* @__PURE__ */ new Set();
419
+ notifyListeners();
420
+ }
421
+ }
422
+ resolvedLocaleRef.current = null;
423
+ thumbprintsRef.current = null;
424
+ const overlayList = overlayUrlsKey.split("\n").filter((u) => u.length > 0);
425
+ (async () => {
426
+ try {
427
+ const manifest = await pppRef.current.retrieveLocalizationBundleManifestAsync();
428
+ if (cancelled) return;
429
+ setSupportedLocales(manifest.supportedLocales);
430
+ const resolvedLocale = manifest.supportedLocales.includes(locale) ? locale : manifest.supportedLocales[0] ?? locale;
431
+ const thumbs = await pppRef.current.retrieveLocalizationThumbprintsAsync(resolvedLocale);
432
+ if (cancelled) return;
433
+ resolvedLocaleRef.current = resolvedLocale;
434
+ thumbprintsRef.current = thumbs;
435
+ const [bundleData, ...overlayDatas] = await Promise.all([
436
+ pppRef.current.retrieveLocalizationBundleAsync(resolvedLocale, thumbs.bundle),
437
+ ...overlayList.map(fetchLocalizationJson)
438
+ ]);
439
+ if (cancelled) return;
440
+ const merged = {};
441
+ Object.assign(merged, flattenStrings(bundleData));
442
+ for (const data of overlayDatas) {
443
+ Object.assign(merged, flattenStrings(data));
444
+ }
445
+ setCache(merged);
446
+ } catch (e) {
447
+ if (cancelled) return;
448
+ console.error(
449
+ "PowerPortalsPro: failed to load localization bundle; staying on key-fallback localizer.",
450
+ e
451
+ );
452
+ }
453
+ if (cancelled) return;
454
+ settledTokensRef.current.add(DEFAULT_BUNDLE_SENTINEL);
455
+ for (const token of baselineTokensRef.current) {
456
+ if (triedTokensRef.current.has(token)) continue;
457
+ if (pendingTokensRef.current.has(token)) continue;
458
+ pendingTokensRef.current.add(token);
459
+ }
460
+ if (pendingTokensRef.current.size > 0 && pendingTimerRef.current === null) {
461
+ pendingTimerRef.current = setTimeout(
462
+ () => flushPendingRef.current(),
463
+ MISSING_KEY_FLUSH_DELAY_MS
464
+ );
465
+ }
466
+ notifyListeners();
467
+ })();
468
+ return () => {
469
+ cancelled = true;
470
+ if (pendingTimerRef.current !== null) {
471
+ clearTimeout(pendingTimerRef.current);
472
+ pendingTimerRef.current = null;
473
+ }
474
+ };
475
+ }, [locale, overlayUrlsKey]);
476
+ const flushPending = react.useCallback(async () => {
477
+ pendingTimerRef.current = null;
478
+ const tokensToFetch = Array.from(pendingTokensRef.current);
479
+ if (tokensToFetch.length === 0) return;
480
+ pendingTokensRef.current = /* @__PURE__ */ new Set();
481
+ for (const tok of tokensToFetch) {
482
+ triedTokensRef.current.add(tok);
483
+ }
484
+ const thumbs = thumbprintsRef.current;
485
+ const requestLocale = resolvedLocaleRef.current;
486
+ if (!thumbs || !requestLocale) {
487
+ for (const tok of tokensToFetch) {
488
+ triedTokensRef.current.delete(tok);
489
+ pendingTokensRef.current.add(tok);
490
+ }
491
+ return;
492
+ }
493
+ const fetches = tokensToFetch.map(async (token) => {
494
+ try {
495
+ if (token.startsWith("tables.")) {
496
+ const tableName = token.substring("tables.".length);
497
+ const thumb = thumbs.tables[tableName];
498
+ if (!thumb) return null;
499
+ return await pppRef.current.retrieveLocalizationTableBundleAsync(
500
+ tableName,
501
+ requestLocale,
502
+ thumb
503
+ );
504
+ }
505
+ if (token.startsWith("views.")) {
506
+ const viewId = token.substring("views.".length);
507
+ const thumb = thumbs.views[viewId];
508
+ if (!thumb) return null;
509
+ return await pppRef.current.retrieveLocalizationViewBundleAsync(
510
+ viewId,
511
+ requestLocale,
512
+ thumb
513
+ );
514
+ }
515
+ return null;
516
+ } catch (e) {
517
+ console.warn(`PowerPortalsPro: localization fetch failed for token ${token}`, e);
518
+ triedTokensRef.current.delete(token);
519
+ return null;
520
+ }
521
+ });
522
+ const responses = await Promise.all(fetches);
523
+ if (requestLocale !== resolvedLocaleRef.current) return;
524
+ let settledChanged = false;
525
+ for (const tok of tokensToFetch) {
526
+ if (!settledTokensRef.current.has(tok)) {
527
+ settledTokensRef.current.add(tok);
528
+ settledChanged = true;
529
+ }
530
+ }
531
+ const merged = {};
532
+ for (const data of responses) {
533
+ if (data === null) continue;
534
+ Object.assign(merged, flattenStrings(data));
535
+ }
536
+ if (Object.keys(merged).length > 0) {
537
+ setCache((prev) => ({ ...prev, ...merged }));
538
+ }
539
+ if (settledChanged) {
540
+ notifyListeners();
541
+ }
542
+ }, [notifyListeners]);
543
+ flushPendingRef.current = flushPending;
544
+ react.useEffect(() => {
545
+ if (!settledTokensRef.current.has(DEFAULT_BUNDLE_SENTINEL)) return;
546
+ let added = false;
547
+ for (const token of baselineTokensRef.current) {
548
+ if (triedTokensRef.current.has(token)) continue;
549
+ if (pendingTokensRef.current.has(token)) continue;
550
+ pendingTokensRef.current.add(token);
551
+ added = true;
552
+ }
553
+ if (pendingTokensRef.current.size > 0 && pendingTimerRef.current === null) {
554
+ pendingTimerRef.current = setTimeout(flushPending, MISSING_KEY_FLUSH_DELAY_MS);
555
+ }
556
+ if (added) notifyListeners();
557
+ }, [baselinePrefixesKey, flushPending, notifyListeners]);
558
+ const queueMissingKey = react.useCallback(
559
+ (key) => {
560
+ const token = coerceKeyToToken(key);
561
+ if (token === null) return;
562
+ if (triedTokensRef.current.has(token)) return;
563
+ if (pendingTokensRef.current.has(token)) return;
564
+ pendingTokensRef.current.add(token);
565
+ if (pendingTimerRef.current === null) {
566
+ pendingTimerRef.current = setTimeout(flushPending, MISSING_KEY_FLUSH_DELAY_MS);
567
+ }
568
+ },
569
+ [flushPending]
570
+ );
571
+ const onMissingKeyCombined = react.useCallback(
572
+ (key) => {
573
+ onMissingKey(key);
574
+ queueMissingKey(key);
575
+ },
576
+ [onMissingKey, queueMissingKey]
577
+ );
578
+ const localizer = react.useMemo(
579
+ () => createLocalizer(cache, { onMissingKey: onMissingKeyCombined }),
580
+ [cache, onMissingKeyCombined]
581
+ );
582
+ const prefetcher = react.useMemo(
583
+ () => ({
584
+ prefetch(prefixes2) {
585
+ for (const prefix of prefixes2) {
586
+ const token = coercePrefixToToken(prefix);
587
+ if (token === null) continue;
588
+ if (triedTokensRef.current.has(token)) continue;
589
+ if (pendingTokensRef.current.has(token)) continue;
590
+ pendingTokensRef.current.add(token);
591
+ }
592
+ if (pendingTokensRef.current.size > 0 && pendingTimerRef.current === null) {
593
+ pendingTimerRef.current = setTimeout(flushPending, MISSING_KEY_FLUSH_DELAY_MS);
594
+ }
595
+ },
596
+ isLoaded(prefixes2) {
597
+ if (!settledTokensRef.current.has(DEFAULT_BUNDLE_SENTINEL)) return false;
598
+ for (const token of baselineTokensRef.current) {
599
+ if (!settledTokensRef.current.has(token)) return false;
600
+ }
601
+ for (const prefix of prefixes2) {
602
+ const token = coercePrefixToToken(prefix);
603
+ if (token === null) continue;
604
+ if (!settledTokensRef.current.has(token)) return false;
605
+ }
606
+ return true;
607
+ },
608
+ subscribe(listener) {
609
+ listenersRef.current.add(listener);
610
+ return () => {
611
+ listenersRef.current.delete(listener);
612
+ };
613
+ }
614
+ }),
615
+ [flushPending]
616
+ );
617
+ const manifestContextValue = react.useMemo(
618
+ () => ({ supportedLocales }),
619
+ [supportedLocales]
620
+ );
621
+ return /* @__PURE__ */ jsxRuntime.jsx(LocalizationManifestContext.Provider, { value: manifestContextValue, children: /* @__PURE__ */ jsxRuntime.jsx(LocalizationPrefetcherContext.Provider, { value: prefetcher, children: /* @__PURE__ */ jsxRuntime.jsx(LocalizerProvider, { value: localizer, children }) }) });
622
+ }
623
+ var UnsavedChangesRegistryContext = react.createContext(null);
624
+ UnsavedChangesRegistryContext.displayName = "UnsavedChangesRegistry";
625
+ function UnsavedChangesRegistryProvider({ children }) {
626
+ const reportersRef = react.useRef(/* @__PURE__ */ new Set());
627
+ const listenersRef = react.useRef(/* @__PURE__ */ new Set());
628
+ const notify = react.useCallback(() => {
629
+ for (const listener of listenersRef.current) listener();
630
+ }, []);
631
+ const register = react.useCallback(
632
+ (reporter) => {
633
+ reportersRef.current.add(reporter);
634
+ notify();
635
+ return () => {
636
+ reportersRef.current.delete(reporter);
637
+ notify();
638
+ };
639
+ },
640
+ [notify]
641
+ );
642
+ const subscribe = react.useCallback((listener) => {
643
+ listenersRef.current.add(listener);
644
+ return () => {
645
+ listenersRef.current.delete(listener);
646
+ };
647
+ }, []);
648
+ const isAnyDirty = react.useCallback(() => {
649
+ for (const reporter of reportersRef.current) {
650
+ if (reporter.isDirty()) return true;
651
+ }
652
+ return false;
653
+ }, []);
654
+ const registry = react.useMemo(
655
+ () => ({ register, subscribe, isAnyDirty, notifyDirtyChanged: notify }),
656
+ [register, subscribe, isAnyDirty, notify]
657
+ );
658
+ return /* @__PURE__ */ jsxRuntime.jsx(UnsavedChangesRegistryContext.Provider, { value: registry, children });
659
+ }
660
+ function useUnsavedChangesRegistry() {
661
+ return react.useContext(UnsavedChangesRegistryContext);
662
+ }
663
+ function useIsAnyDirty() {
664
+ const registry = useUnsavedChangesRegistry();
665
+ const subscribe = react.useCallback(
666
+ (listener) => {
667
+ if (!registry) return () => {
668
+ };
669
+ return registry.subscribe(listener);
670
+ },
671
+ [registry]
672
+ );
673
+ const getSnapshot = react.useCallback(() => {
674
+ if (!registry) return false;
675
+ return registry.isAnyDirty();
676
+ }, [registry]);
677
+ return react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
678
+ }
679
+ function PowerPortalsProProvider({
680
+ children,
681
+ transport,
682
+ localizationUrl,
683
+ localizationUrls,
684
+ localizationPrefixes,
685
+ tableMetadataCache,
686
+ viewMetadataCache,
687
+ viewsForTableCache,
688
+ environmentFileSettingsCache
689
+ }) {
690
+ const transportRef = react.useRef(null);
691
+ if (transportRef.current === null) {
692
+ transportRef.current = transport ?? new core.Transport();
693
+ }
694
+ const transportInstance = transportRef.current;
695
+ const auth = react.useMemo(() => new core.AuthClient(transportInstance), [transportInstance]);
696
+ const ppp = react.useMemo(() => new core.PowerPortalsProClient(transportInstance), [transportInstance]);
697
+ const resolvedTableMetadataCache = react.useMemo(
698
+ () => tableMetadataCache ?? new InMemoryTableMetadataCache(ppp),
699
+ [ppp, tableMetadataCache]
700
+ );
701
+ const resolvedViewMetadataCache = react.useMemo(
702
+ () => viewMetadataCache ?? new InMemoryViewMetadataCache(ppp),
703
+ [ppp, viewMetadataCache]
704
+ );
705
+ const resolvedViewsForTableCache = react.useMemo(
706
+ () => viewsForTableCache ?? new InMemoryViewsForTableCache(ppp),
707
+ [ppp, viewsForTableCache]
708
+ );
709
+ const resolvedEnvironmentFileSettingsCache = react.useMemo(
710
+ () => environmentFileSettingsCache ?? new InMemoryEnvironmentFileSettingsCache(ppp),
711
+ [ppp, environmentFileSettingsCache]
712
+ );
713
+ const [authState, setAuthState] = react.useState({ status: AuthStatus.Loading });
714
+ const refreshAuth = react.useCallback(async () => {
715
+ try {
716
+ const me = await auth.getCurrentUserAsync();
717
+ if (me.isAuthenticated) {
718
+ setAuthState({ status: AuthStatus.Authenticated, user: me });
719
+ } else {
720
+ setAuthState({ status: AuthStatus.Anonymous });
721
+ }
722
+ } catch {
723
+ setAuthState({ status: AuthStatus.Anonymous });
724
+ }
725
+ }, [auth]);
726
+ const login = react.useCallback(
727
+ async (request) => {
728
+ const response = await auth.loginAsync(request);
729
+ if (response.result === core.LoginResult.Success) {
730
+ await refreshAuth();
731
+ }
732
+ return response;
733
+ },
734
+ [auth, refreshAuth]
735
+ );
736
+ const logout = react.useCallback(async () => {
737
+ await auth.logoutAsync();
738
+ setAuthState({ status: AuthStatus.Anonymous });
739
+ }, [auth]);
740
+ const switchIdentity = react.useCallback(async () => {
741
+ const response = await auth.switchIdentityAsync();
742
+ if (response.result === core.SwitchIdentityResult.Switched) {
743
+ await refreshAuth();
744
+ }
745
+ return response;
746
+ }, [auth, refreshAuth]);
747
+ react.useEffect(() => {
748
+ refreshAuth();
749
+ }, [refreshAuth]);
750
+ const value = react.useMemo(
751
+ () => ({
752
+ transport: transportInstance,
753
+ auth,
754
+ ppp,
755
+ tableMetadataCache: resolvedTableMetadataCache,
756
+ viewMetadataCache: resolvedViewMetadataCache,
757
+ viewsForTableCache: resolvedViewsForTableCache,
758
+ environmentFileSettingsCache: resolvedEnvironmentFileSettingsCache,
759
+ authState,
760
+ refreshAuth,
761
+ login,
762
+ logout,
763
+ switchIdentity
764
+ }),
765
+ [
766
+ transportInstance,
767
+ auth,
768
+ ppp,
769
+ resolvedTableMetadataCache,
770
+ resolvedViewMetadataCache,
771
+ resolvedViewsForTableCache,
772
+ resolvedEnvironmentFileSettingsCache,
773
+ authState,
774
+ refreshAuth,
775
+ login,
776
+ logout,
777
+ switchIdentity
778
+ ]
779
+ );
780
+ let localizationContent;
781
+ if (localizationUrl === false) {
782
+ localizationContent = children;
783
+ } else {
784
+ const overlayUrls = [];
785
+ if (typeof localizationUrl === "string") overlayUrls.push(localizationUrl);
786
+ if (localizationUrls) overlayUrls.push(...localizationUrls);
787
+ localizationContent = /* @__PURE__ */ jsxRuntime.jsxs(LocaleProvider, { children: [
788
+ /* @__PURE__ */ jsxRuntime.jsx(CacheLocaleSync, {}),
789
+ /* @__PURE__ */ jsxRuntime.jsx(
790
+ DefaultLocalizerProvider,
791
+ {
792
+ ...overlayUrls.length > 0 && { urls: overlayUrls },
793
+ ...localizationPrefixes !== void 0 && { prefixes: localizationPrefixes },
794
+ children
795
+ }
796
+ )
797
+ ] });
798
+ }
799
+ return /* @__PURE__ */ jsxRuntime.jsx(PowerPortalsProContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx(UnsavedChangesRegistryProvider, { children: localizationContent }) });
800
+ }
801
+ function CacheLocaleSync() {
802
+ const { locale } = useLocale();
803
+ const {
804
+ tableMetadataCache,
805
+ viewMetadataCache,
806
+ viewsForTableCache,
807
+ environmentFileSettingsCache
808
+ } = usePowerPortalsPro();
809
+ const isFirstRun = react.useRef(true);
810
+ react.useEffect(() => {
811
+ if (isFirstRun.current) {
812
+ isFirstRun.current = false;
813
+ return;
814
+ }
815
+ tableMetadataCache.clear();
816
+ viewMetadataCache.clear();
817
+ viewsForTableCache.clear();
818
+ environmentFileSettingsCache.invalidate();
819
+ }, [
820
+ locale,
821
+ tableMetadataCache,
822
+ viewMetadataCache,
823
+ viewsForTableCache,
824
+ environmentFileSettingsCache
825
+ ]);
826
+ return null;
827
+ }
828
+
829
+ // src/use-auth.ts
830
+ function useAuth() {
831
+ const { authState, refreshAuth, login, logout, switchIdentity } = usePowerPortalsPro();
832
+ return {
833
+ ...authState,
834
+ refresh: refreshAuth,
835
+ login,
836
+ logout,
837
+ switchIdentity
838
+ };
839
+ }
840
+ function AuthorizedView({ children, roles, fallback = null }) {
841
+ const auth = useAuth();
842
+ if (auth.status !== AuthStatus.Authenticated) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
843
+ if (roles !== void 0) {
844
+ const required = typeof roles === "string" ? [roles] : roles;
845
+ if (required.length > 0) {
846
+ const userRoles = auth.user.roles ?? [];
847
+ const hasAny = required.some((r) => userRoles.includes(r));
848
+ if (!hasAny) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
849
+ }
850
+ }
851
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
852
+ }
853
+ var QueryStatus = {
854
+ Loading: "loading",
855
+ Success: "success",
856
+ Error: "error"
857
+ };
858
+ function useAsyncResource(fetcher, deps, enabled = true, keepPreviousData = false) {
859
+ const [state, setState] = react.useState({
860
+ status: enabled ? QueryStatus.Loading : QueryStatus.Success,
861
+ data: void 0,
862
+ error: void 0,
863
+ isFetching: enabled
864
+ });
865
+ const [refetchTick, setRefetchTick] = react.useState(0);
866
+ const pendingResolversRef = react.useRef([]);
867
+ react.useEffect(() => {
868
+ if (!enabled) {
869
+ setState({
870
+ status: QueryStatus.Success,
871
+ data: void 0,
872
+ error: void 0,
873
+ isFetching: false
874
+ });
875
+ const resolvers = pendingResolversRef.current;
876
+ pendingResolversRef.current = [];
877
+ for (const r of resolvers) r.resolve();
878
+ return;
879
+ }
880
+ const controller = new AbortController();
881
+ setState((prev) => {
882
+ if (keepPreviousData && prev.status === QueryStatus.Success && prev.data !== void 0) {
883
+ if (prev.isFetching && prev.error === void 0) return prev;
884
+ return { ...prev, error: void 0, isFetching: true };
885
+ }
886
+ if (prev.status === QueryStatus.Loading && prev.data === void 0 && prev.error === void 0 && prev.isFetching) {
887
+ return prev;
888
+ }
889
+ return {
890
+ status: QueryStatus.Loading,
891
+ data: void 0,
892
+ error: void 0,
893
+ isFetching: true
894
+ };
895
+ });
896
+ fetcher(controller.signal).then(
897
+ (data) => {
898
+ if (!controller.signal.aborted) {
899
+ setState({
900
+ status: QueryStatus.Success,
901
+ data,
902
+ error: void 0,
903
+ isFetching: false
904
+ });
905
+ }
906
+ const resolvers = pendingResolversRef.current;
907
+ pendingResolversRef.current = [];
908
+ for (const r of resolvers) r.resolve();
909
+ },
910
+ (err) => {
911
+ if (controller.signal.aborted || err instanceof Error && err.name === "AbortError") {
912
+ const resolvers2 = pendingResolversRef.current;
913
+ pendingResolversRef.current = [];
914
+ for (const r of resolvers2) r.resolve();
915
+ return;
916
+ }
917
+ setState({
918
+ status: QueryStatus.Error,
919
+ data: void 0,
920
+ error: err,
921
+ isFetching: false
922
+ });
923
+ const resolvers = pendingResolversRef.current;
924
+ pendingResolversRef.current = [];
925
+ for (const r of resolvers) r.reject(err);
926
+ }
927
+ );
928
+ return () => {
929
+ controller.abort();
930
+ const resolvers = pendingResolversRef.current;
931
+ pendingResolversRef.current = [];
932
+ for (const r of resolvers) r.resolve();
933
+ };
934
+ }, [...deps, enabled, refetchTick]);
935
+ const refetch = react.useCallback(() => {
936
+ return new Promise((resolve, reject) => {
937
+ pendingResolversRef.current.push({ resolve, reject });
938
+ setRefetchTick((n) => n + 1);
939
+ });
940
+ }, []);
941
+ return { ...state, refetch };
942
+ }
943
+
944
+ // src/use-record.ts
945
+ function useRecord(tableLogicalName, recordId, options) {
946
+ const { ppp } = usePowerPortalsPro();
947
+ const columns = options?.columns;
948
+ const enabled = options?.enabled ?? true;
949
+ return useAsyncResource(
950
+ (signal) => ppp.retrieveRecordAsync(tableLogicalName, recordId, columns, signal),
951
+ // Joining the columns array is the cheapest way to get a stable dep without
952
+ // forcing the consumer to memoize. Column lists are short (typically <20).
953
+ [ppp, tableLogicalName, recordId, columns?.join(",") ?? ""],
954
+ enabled
955
+ );
956
+ }
957
+
958
+ // src/use-fetch-records.ts
959
+ function useFetchRecords(fetchXml, options) {
960
+ const { ppp } = usePowerPortalsPro();
961
+ const enabled = options?.enabled ?? true;
962
+ return useAsyncResource(
963
+ (signal) => ppp.retrieveRecordsAsync(fetchXml, signal),
964
+ [ppp, fetchXml],
965
+ enabled
966
+ );
967
+ }
968
+
969
+ // src/use-grid-data.ts
970
+ function useGridData(request, options) {
971
+ const { ppp } = usePowerPortalsPro();
972
+ const enabled = options?.enabled ?? true;
973
+ const keepPreviousData = options?.keepPreviousData ?? false;
974
+ const requestKey = JSON.stringify(request);
975
+ return useAsyncResource(
976
+ (signal) => ppp.loadGridAsync(request, signal),
977
+ [ppp, requestKey],
978
+ enabled,
979
+ keepPreviousData
980
+ );
981
+ }
982
+ function reduce(state, action) {
983
+ switch (action.type) {
984
+ // Filter / sort / search / pageSize changes invalidate the current
985
+ // page — the new result set has different rows, the user expects to
986
+ // land on page 1. Mirrors what `<MainGrid>` does for standalone
987
+ // mode. Highlight changes DON'T reset the page (purely client-side).
988
+ case "setFilter":
989
+ return { ...state, filter: action.filter, page: 1 };
990
+ case "setSort":
991
+ return { ...state, sort: action.sort, page: 1 };
992
+ case "setSearch":
993
+ return { ...state, search: action.search, page: 1 };
994
+ case "setPage":
995
+ return { ...state, page: action.page };
996
+ case "setPageSize":
997
+ return { ...state, pageSize: action.pageSize, page: 1 };
998
+ case "setHighlight":
999
+ return { ...state, highlight: action.highlight };
1000
+ }
1001
+ }
1002
+ function useViewDataSource(options) {
1003
+ const debounceMs = options.debounceMs ?? 300;
1004
+ const enabled = options.enabled ?? true;
1005
+ const keepPreviousData = options.keepPreviousData ?? true;
1006
+ const [state, dispatch] = react.useReducer(reduce, void 0, () => ({
1007
+ filter: options.initialFilter ?? EMPTY_FILTERS,
1008
+ sort: options.initialSort ?? EMPTY_SORTS,
1009
+ search: options.initialSearch ?? "",
1010
+ page: options.initialPage ?? 1,
1011
+ pageSize: options.initialPageSize ?? 50,
1012
+ highlight: options.initialHighlight
1013
+ }));
1014
+ const aggregationFingerprint = react.useMemo(() => {
1015
+ if (!options.aggregations || options.aggregations.length === 0) return null;
1016
+ return JSON.stringify({
1017
+ viewId: options.viewId,
1018
+ fetchXml: options.fetchXml,
1019
+ staticFilters: options.staticFilters,
1020
+ filter: state.filter,
1021
+ search: state.search,
1022
+ aggregations: options.aggregations
1023
+ });
1024
+ }, [
1025
+ options.viewId,
1026
+ options.fetchXml,
1027
+ options.staticFilters,
1028
+ options.aggregations,
1029
+ state.filter,
1030
+ state.search
1031
+ ]);
1032
+ const lastFetchedAggregationFingerprintRef = react.useRef(null);
1033
+ const cachedAggregationResultsRef = react.useRef(EMPTY_AGGREGATIONS);
1034
+ const request = react.useMemo(() => {
1035
+ const filters = [
1036
+ ...options.staticFilters ?? [],
1037
+ ...state.filter
1038
+ ];
1039
+ const includeAggregations = aggregationFingerprint !== null && aggregationFingerprint !== lastFetchedAggregationFingerprintRef.current;
1040
+ return {
1041
+ ...options.viewId !== void 0 && { viewId: options.viewId },
1042
+ ...options.fetchXml !== void 0 && { fetchXml: options.fetchXml },
1043
+ sorts: state.sort,
1044
+ pageNumber: state.page,
1045
+ pageSize: state.pageSize,
1046
+ ...state.search !== "" && { searchText: state.search },
1047
+ ...filters.length > 0 && { filters },
1048
+ ...includeAggregations && options.aggregations && {
1049
+ aggregations: options.aggregations
1050
+ }
1051
+ };
1052
+ }, [
1053
+ options.viewId,
1054
+ options.fetchXml,
1055
+ options.staticFilters,
1056
+ options.aggregations,
1057
+ state.filter,
1058
+ state.sort,
1059
+ state.search,
1060
+ state.page,
1061
+ state.pageSize,
1062
+ aggregationFingerprint
1063
+ ]);
1064
+ const debouncedRequest = useDebouncedValue(request, debounceMs);
1065
+ const query = useGridData(debouncedRequest, { enabled, keepPreviousData });
1066
+ const setFilter = react.useCallback((filter) => {
1067
+ dispatch({ type: "setFilter", filter });
1068
+ }, []);
1069
+ const setSort = react.useCallback((sort) => {
1070
+ dispatch({ type: "setSort", sort });
1071
+ }, []);
1072
+ const setSearch = react.useCallback((search) => {
1073
+ dispatch({ type: "setSearch", search });
1074
+ }, []);
1075
+ const setPage = react.useCallback((page) => {
1076
+ dispatch({ type: "setPage", page });
1077
+ }, []);
1078
+ const setPageSize = react.useCallback((pageSize) => {
1079
+ dispatch({ type: "setPageSize", pageSize });
1080
+ }, []);
1081
+ const setHighlight = react.useCallback((highlight) => {
1082
+ dispatch({ type: "setHighlight", highlight });
1083
+ }, []);
1084
+ const refresh = react.useCallback(async () => {
1085
+ await query.refetch();
1086
+ }, [query.refetch]);
1087
+ const data = query.data;
1088
+ const rows = data?.tableRecords ?? EMPTY_RECORDS;
1089
+ const columns = data?.columns ?? EMPTY_COLUMNS;
1090
+ const totalCount = data?.pagingInfo?.totalRecordCount;
1091
+ const tablePermissions = data?.tablePermissions;
1092
+ const aggregations = options.aggregations ?? EMPTY_AGGREGATION_SPEC;
1093
+ const isFetchingAggregations = query.isFetching && debouncedRequest.aggregations !== void 0 && debouncedRequest.aggregations !== null && debouncedRequest.aggregations.length > 0;
1094
+ const lastSeenDataRef = react.useRef(void 0);
1095
+ react.useEffect(() => {
1096
+ if (data === lastSeenDataRef.current) return;
1097
+ lastSeenDataRef.current = data;
1098
+ if (data?.aggregations) {
1099
+ cachedAggregationResultsRef.current = data.aggregations;
1100
+ lastFetchedAggregationFingerprintRef.current = aggregationFingerprint;
1101
+ }
1102
+ }, [data, aggregationFingerprint]);
1103
+ const aggregationResults = (options.aggregations?.length ?? 0) === 0 ? EMPTY_AGGREGATIONS : data?.aggregations ?? cachedAggregationResultsRef.current;
1104
+ return react.useMemo(
1105
+ () => ({
1106
+ filter: state.filter,
1107
+ sort: state.sort,
1108
+ search: state.search,
1109
+ page: state.page,
1110
+ pageSize: state.pageSize,
1111
+ highlight: state.highlight,
1112
+ aggregations,
1113
+ status: query.status,
1114
+ rows,
1115
+ totalCount,
1116
+ columns,
1117
+ tablePermissions,
1118
+ aggregationResults,
1119
+ error: query.error,
1120
+ isFetching: query.isFetching,
1121
+ isFetchingAggregations,
1122
+ setFilter,
1123
+ setSort,
1124
+ setSearch,
1125
+ setPage,
1126
+ setPageSize,
1127
+ setHighlight,
1128
+ refresh
1129
+ }),
1130
+ [
1131
+ state.filter,
1132
+ state.sort,
1133
+ state.search,
1134
+ state.page,
1135
+ state.pageSize,
1136
+ state.highlight,
1137
+ aggregations,
1138
+ query.status,
1139
+ rows,
1140
+ totalCount,
1141
+ columns,
1142
+ tablePermissions,
1143
+ aggregationResults,
1144
+ query.error,
1145
+ query.isFetching,
1146
+ isFetchingAggregations,
1147
+ setFilter,
1148
+ setSort,
1149
+ setSearch,
1150
+ setPage,
1151
+ setPageSize,
1152
+ setHighlight,
1153
+ refresh
1154
+ ]
1155
+ );
1156
+ }
1157
+ var EMPTY_FILTERS = [];
1158
+ var EMPTY_SORTS = [];
1159
+ var EMPTY_RECORDS = [];
1160
+ var EMPTY_COLUMNS = [];
1161
+ var EMPTY_AGGREGATIONS = [];
1162
+ var EMPTY_AGGREGATION_SPEC = [];
1163
+ function useDebouncedValue(value, delayMs) {
1164
+ const [debounced, setDebounced] = react.useState(value);
1165
+ const valueKey = JSON.stringify(value);
1166
+ const previousKeyRef = react.useRef(valueKey);
1167
+ react.useEffect(() => {
1168
+ if (delayMs === 0) {
1169
+ setDebounced(value);
1170
+ previousKeyRef.current = valueKey;
1171
+ return;
1172
+ }
1173
+ if (previousKeyRef.current === valueKey) return;
1174
+ const timer = setTimeout(() => {
1175
+ setDebounced(value);
1176
+ previousKeyRef.current = valueKey;
1177
+ }, delayMs);
1178
+ return () => clearTimeout(timer);
1179
+ }, [valueKey, delayMs]);
1180
+ return debounced;
1181
+ }
1182
+
1183
+ // src/use-table-metadata.ts
1184
+ function useTableMetadata(tableLogicalName, options) {
1185
+ const { tableMetadataCache } = usePowerPortalsPro();
1186
+ const enabled = options?.enabled ?? true;
1187
+ return useAsyncResource(
1188
+ async () => {
1189
+ const result = await tableMetadataCache.getAsync(tableLogicalName);
1190
+ return result ?? void 0;
1191
+ },
1192
+ [tableMetadataCache, tableLogicalName],
1193
+ enabled
1194
+ );
1195
+ }
1196
+
1197
+ // src/use-table-permissions.ts
1198
+ function useTablePermissions(tableLogicalName, options) {
1199
+ const { ppp } = usePowerPortalsPro();
1200
+ const enabled = (options?.enabled ?? true) && tableLogicalName.length > 0;
1201
+ return useAsyncResource(
1202
+ async () => ppp.retrieveTablePermissionsAsync(tableLogicalName),
1203
+ [ppp, tableLogicalName],
1204
+ enabled
1205
+ );
1206
+ }
1207
+
1208
+ // src/use-view-metadata.ts
1209
+ function useViewMetadata(viewId, options) {
1210
+ const { viewMetadataCache } = usePowerPortalsPro();
1211
+ const enabled = options?.enabled ?? true;
1212
+ return useAsyncResource(
1213
+ async () => {
1214
+ const result = await viewMetadataCache.getAsync(viewId);
1215
+ return result ?? void 0;
1216
+ },
1217
+ [viewMetadataCache, viewId],
1218
+ enabled
1219
+ );
1220
+ }
1221
+
1222
+ // src/use-views-for-table.ts
1223
+ function useViewsForTable(tableLogicalName, options) {
1224
+ const { viewsForTableCache } = usePowerPortalsPro();
1225
+ const enabled = options?.enabled ?? true;
1226
+ return useAsyncResource(
1227
+ () => viewsForTableCache.getAsync(tableLogicalName),
1228
+ [viewsForTableCache, tableLogicalName],
1229
+ enabled
1230
+ );
1231
+ }
1232
+
1233
+ // src/use-environment-file-settings.ts
1234
+ function useEnvironmentFileSettings(options) {
1235
+ const { environmentFileSettingsCache } = usePowerPortalsPro();
1236
+ const enabled = options?.enabled ?? true;
1237
+ return useAsyncResource(
1238
+ async () => {
1239
+ const result = await environmentFileSettingsCache.getAsync();
1240
+ return result ?? void 0;
1241
+ },
1242
+ [environmentFileSettingsCache],
1243
+ enabled
1244
+ );
1245
+ }
1246
+ function useSupportedLocales(options) {
1247
+ const { ppp } = usePowerPortalsPro();
1248
+ const enabled = options?.enabled ?? true;
1249
+ const manifestState = react.useContext(LocalizationManifestContext);
1250
+ const providerScoped = manifestState !== null;
1251
+ const fallback = useAsyncResource(
1252
+ async () => {
1253
+ const manifest = await ppp.retrieveLocalizationBundleManifestAsync();
1254
+ return manifest.supportedLocales;
1255
+ },
1256
+ [ppp],
1257
+ enabled && !providerScoped
1258
+ );
1259
+ if (providerScoped) {
1260
+ if (manifestState.supportedLocales === void 0) {
1261
+ return {
1262
+ status: enabled ? QueryStatus.Loading : QueryStatus.Success,
1263
+ data: void 0,
1264
+ error: void 0,
1265
+ isFetching: enabled,
1266
+ refetch: fallback.refetch
1267
+ };
1268
+ }
1269
+ return {
1270
+ status: QueryStatus.Success,
1271
+ data: manifestState.supportedLocales,
1272
+ error: void 0,
1273
+ isFetching: false,
1274
+ refetch: fallback.refetch
1275
+ };
1276
+ }
1277
+ return fallback;
1278
+ }
1279
+ function useQueryParam(name) {
1280
+ const [value, setValue] = react.useState(() => readParam(name));
1281
+ react.useEffect(() => {
1282
+ if (typeof window === "undefined") return void 0;
1283
+ installHistoryPatch();
1284
+ setValue(readParam(name));
1285
+ const handler = () => setValue(readParam(name));
1286
+ window.addEventListener("popstate", handler);
1287
+ window.addEventListener(PUSH_EVENT, handler);
1288
+ window.addEventListener(REPLACE_EVENT, handler);
1289
+ return () => {
1290
+ window.removeEventListener("popstate", handler);
1291
+ window.removeEventListener(PUSH_EVENT, handler);
1292
+ window.removeEventListener(REPLACE_EVENT, handler);
1293
+ };
1294
+ }, [name]);
1295
+ return value;
1296
+ }
1297
+ var PUSH_EVENT = "ppp:pushstate";
1298
+ var REPLACE_EVENT = "ppp:replacestate";
1299
+ var historyPatched = false;
1300
+ function installHistoryPatch() {
1301
+ if (historyPatched) return;
1302
+ if (typeof window === "undefined" || typeof window.history === "undefined") return;
1303
+ historyPatched = true;
1304
+ const origPush = window.history.pushState.bind(window.history);
1305
+ const origReplace = window.history.replaceState.bind(window.history);
1306
+ window.history.pushState = function(...args) {
1307
+ origPush(...args);
1308
+ window.dispatchEvent(new Event(PUSH_EVENT));
1309
+ };
1310
+ window.history.replaceState = function(...args) {
1311
+ origReplace(...args);
1312
+ window.dispatchEvent(new Event(REPLACE_EVENT));
1313
+ };
1314
+ }
1315
+ function readParam(name) {
1316
+ if (typeof window === "undefined") return null;
1317
+ try {
1318
+ const params = new URLSearchParams(window.location.search);
1319
+ return params.get(name);
1320
+ } catch {
1321
+ return null;
1322
+ }
1323
+ }
1324
+ function useBeforeUnloadWhen(condition) {
1325
+ react.useEffect(() => {
1326
+ if (!condition) return;
1327
+ const handler = (e) => {
1328
+ e.preventDefault();
1329
+ e.returnValue = "";
1330
+ };
1331
+ window.addEventListener("beforeunload", handler);
1332
+ return () => window.removeEventListener("beforeunload", handler);
1333
+ }, [condition]);
1334
+ }
1335
+ var MainContextContext = react.createContext(null);
1336
+ MainContextContext.displayName = "MainContext";
1337
+ function MainContext({
1338
+ children,
1339
+ onBeforeSave,
1340
+ warnOnUnsavedChanges,
1341
+ forceSuccessfulValidationBeforeSave
1342
+ }) {
1343
+ const parentMainContext = react.useContext(MainContextContext);
1344
+ const isTopMost = parentMainContext === null;
1345
+ const resolvedWarnOnUnsavedChanges = warnOnUnsavedChanges ?? parentMainContext?.warnOnUnsavedChanges ?? true;
1346
+ const resolvedForceSuccessfulValidationBeforeSave = forceSuccessfulValidationBeforeSave ?? parentMainContext?.forceSuccessfulValidationBeforeSave ?? true;
1347
+ const descendantsRef = react.useRef(/* @__PURE__ */ new Set());
1348
+ const [version, setVersion] = react.useState(0);
1349
+ const { ppp } = usePowerPortalsPro();
1350
+ const refreshAll = react.useCallback(async () => {
1351
+ for (const descendant of descendantsRef.current) {
1352
+ await descendant.refresh();
1353
+ }
1354
+ }, []);
1355
+ const resetState = react.useCallback(() => {
1356
+ for (const descendant of descendantsRef.current) {
1357
+ descendant.resetState();
1358
+ }
1359
+ }, []);
1360
+ const validate = react.useCallback(async () => {
1361
+ for (const descendant of descendantsRef.current) {
1362
+ const ok = await descendant.validate();
1363
+ if (!ok) return false;
1364
+ }
1365
+ return true;
1366
+ }, []);
1367
+ const saveAll = react.useCallback(async () => {
1368
+ if (onBeforeSave) await onBeforeSave();
1369
+ if (resolvedForceSuccessfulValidationBeforeSave) {
1370
+ const valid = await validate();
1371
+ if (!valid) return;
1372
+ }
1373
+ const requests = [];
1374
+ for (const descendant of descendantsRef.current) {
1375
+ requests.push(...descendant.getRequests());
1376
+ }
1377
+ if (requests.length === 0) return;
1378
+ await ppp.executeMultipleAsync(requests, { returnResponses: false });
1379
+ await refreshAll();
1380
+ }, [onBeforeSave, validate, ppp, refreshAll, resolvedForceSuccessfulValidationBeforeSave]);
1381
+ const registerDescendant = react.useCallback((descendant) => {
1382
+ descendantsRef.current.add(descendant);
1383
+ setVersion((v) => v + 1);
1384
+ return () => {
1385
+ descendantsRef.current.delete(descendant);
1386
+ setVersion((v) => v + 1);
1387
+ };
1388
+ }, []);
1389
+ const notifyDescendantDirtyChanged = react.useCallback(() => {
1390
+ setVersion((v) => v + 1);
1391
+ }, []);
1392
+ const isDirty = react.useMemo(() => {
1393
+ for (const descendant of descendantsRef.current) {
1394
+ if (descendant.isDirty()) return true;
1395
+ }
1396
+ return false;
1397
+ }, [version]);
1398
+ const getRequests = react.useCallback(() => {
1399
+ const all = [];
1400
+ for (const descendant of descendantsRef.current) {
1401
+ all.push(...descendant.getRequests());
1402
+ }
1403
+ return all;
1404
+ }, []);
1405
+ const value = react.useMemo(
1406
+ () => ({
1407
+ saveAll,
1408
+ refreshAll,
1409
+ resetState,
1410
+ validate,
1411
+ getRequests,
1412
+ registerDescendant,
1413
+ notifyDescendantDirtyChanged,
1414
+ isDirty,
1415
+ warnOnUnsavedChanges: resolvedWarnOnUnsavedChanges,
1416
+ forceSuccessfulValidationBeforeSave: resolvedForceSuccessfulValidationBeforeSave
1417
+ }),
1418
+ [
1419
+ saveAll,
1420
+ refreshAll,
1421
+ resetState,
1422
+ validate,
1423
+ getRequests,
1424
+ registerDescendant,
1425
+ notifyDescendantDirtyChanged,
1426
+ isDirty,
1427
+ resolvedWarnOnUnsavedChanges,
1428
+ resolvedForceSuccessfulValidationBeforeSave
1429
+ ]
1430
+ );
1431
+ react.useEffect(() => {
1432
+ if (!parentMainContext) return;
1433
+ return parentMainContext.registerDescendant({
1434
+ getRequests,
1435
+ validate,
1436
+ refresh: refreshAll,
1437
+ resetState,
1438
+ isDirty: () => isDirtyRef.current
1439
+ });
1440
+ }, [parentMainContext?.registerDescendant, getRequests, validate, refreshAll, resetState]);
1441
+ const parentMainContextRef = react.useRef(parentMainContext);
1442
+ parentMainContextRef.current = parentMainContext;
1443
+ react.useEffect(() => {
1444
+ parentMainContextRef.current?.notifyDescendantDirtyChanged();
1445
+ }, [isDirty]);
1446
+ useBeforeUnloadWhen(isTopMost && resolvedWarnOnUnsavedChanges && isDirty);
1447
+ const unsavedChangesRegistry = useUnsavedChangesRegistry();
1448
+ const isDirtyRef = react.useRef(isDirty);
1449
+ isDirtyRef.current = isDirty;
1450
+ const resolvedWarnRef = react.useRef(resolvedWarnOnUnsavedChanges);
1451
+ resolvedWarnRef.current = resolvedWarnOnUnsavedChanges;
1452
+ react.useEffect(() => {
1453
+ if (!isTopMost) return;
1454
+ if (!unsavedChangesRegistry) return;
1455
+ return unsavedChangesRegistry.register({
1456
+ isDirty: () => resolvedWarnRef.current && isDirtyRef.current
1457
+ });
1458
+ }, [isTopMost, unsavedChangesRegistry]);
1459
+ react.useEffect(() => {
1460
+ if (!isTopMost) return;
1461
+ if (!unsavedChangesRegistry) return;
1462
+ unsavedChangesRegistry.notifyDirtyChanged();
1463
+ }, [isTopMost, unsavedChangesRegistry, isDirty, resolvedWarnOnUnsavedChanges]);
1464
+ return /* @__PURE__ */ jsxRuntime.jsx(MainContextContext.Provider, { value, children });
1465
+ }
1466
+ function useMainContext() {
1467
+ return react.useContext(MainContextContext);
1468
+ }
1469
+ function MainContextProvider({
1470
+ value,
1471
+ children
1472
+ }) {
1473
+ return /* @__PURE__ */ jsxRuntime.jsx(MainContextContext.Provider, { value, children });
1474
+ }
1475
+ var OverlayContext = react.createContext(null);
1476
+ OverlayContext.displayName = "OverlayService";
1477
+ function useOverlayService() {
1478
+ return react.useContext(OverlayContext)?.service ?? null;
1479
+ }
1480
+ function useOverlayForTarget(targetId) {
1481
+ const ctx = react.useContext(OverlayContext);
1482
+ if (!ctx) return null;
1483
+ const stack = ctx.overlaysByTarget.get(targetId ?? "") ?? [];
1484
+ return stack[stack.length - 1] ?? null;
1485
+ }
1486
+ function OverlayServiceProvider({
1487
+ value,
1488
+ children
1489
+ }) {
1490
+ return /* @__PURE__ */ jsxRuntime.jsx(OverlayContext.Provider, { value, children });
1491
+ }
1492
+ function useOverlayServiceState() {
1493
+ const [overlaysByTarget, setOverlaysByTarget] = react.useState(() => /* @__PURE__ */ new Map());
1494
+ const nextIdRef = react.useRef(1);
1495
+ const service = react.useMemo(
1496
+ () => ({
1497
+ show(options) {
1498
+ const id = `overlay-${nextIdRef.current++}`;
1499
+ const instance = { id, ...options };
1500
+ setOverlaysByTarget((prev) => {
1501
+ const key = options?.targetId ?? "";
1502
+ const next = new Map(prev);
1503
+ next.set(key, [...next.get(key) ?? [], instance]);
1504
+ return next;
1505
+ });
1506
+ return instance;
1507
+ },
1508
+ hide(overlay) {
1509
+ setOverlaysByTarget((prev) => {
1510
+ const key = overlay.targetId ?? "";
1511
+ const stack = prev.get(key) ?? [];
1512
+ const idx = stack.findIndex((o) => o.id === overlay.id);
1513
+ if (idx < 0) return prev;
1514
+ const next = new Map(prev);
1515
+ if (stack.length === 1) {
1516
+ next.delete(key);
1517
+ } else {
1518
+ next.set(key, [...stack.slice(0, idx), ...stack.slice(idx + 1)]);
1519
+ }
1520
+ return next;
1521
+ });
1522
+ },
1523
+ hideAll() {
1524
+ setOverlaysByTarget(/* @__PURE__ */ new Map());
1525
+ }
1526
+ }),
1527
+ []
1528
+ );
1529
+ return react.useMemo(() => ({ service, overlaysByTarget }), [service, overlaysByTarget]);
1530
+ }
1531
+ async function runWithOverlayAsync(service, options, operation) {
1532
+ if (!service) return operation();
1533
+ const handle = service.show(options);
1534
+ try {
1535
+ return await operation();
1536
+ } finally {
1537
+ service.hide(handle);
1538
+ }
1539
+ }
1540
+ function useRunWithOverlay() {
1541
+ const service = useOverlayService();
1542
+ return react.useCallback(
1543
+ (options, operation) => runWithOverlayAsync(service, options, operation),
1544
+ [service]
1545
+ );
1546
+ }
1547
+ var ValidationContextContext = react.createContext(null);
1548
+ ValidationContextContext.displayName = "ValidationContext";
1549
+ function useValidationContext() {
1550
+ return react.useContext(ValidationContextContext);
1551
+ }
1552
+ function useColumnValidationErrors(columnName) {
1553
+ const ctx = useValidationContext();
1554
+ if (!ctx) return void 0;
1555
+ return ctx.errors.get(columnName);
1556
+ }
1557
+ function ValidationProvider({ children }) {
1558
+ const validatorsRef = react.useRef(/* @__PURE__ */ new Map());
1559
+ const [errors, setErrors] = react.useState(
1560
+ () => /* @__PURE__ */ new Map()
1561
+ );
1562
+ const register = react.useCallback(
1563
+ (columnName, validator) => {
1564
+ validatorsRef.current.set(columnName, validator);
1565
+ return () => {
1566
+ validatorsRef.current.delete(columnName);
1567
+ setErrors((prev) => {
1568
+ if (!prev.has(columnName)) return prev;
1569
+ const next = new Map(prev);
1570
+ next.delete(columnName);
1571
+ return next;
1572
+ });
1573
+ };
1574
+ },
1575
+ []
1576
+ );
1577
+ const validateColumn = react.useCallback(async (columnName) => {
1578
+ const validator = validatorsRef.current.get(columnName);
1579
+ if (!validator) return;
1580
+ const fieldErrors = await validator();
1581
+ setErrors((prev) => {
1582
+ const existing = prev.get(columnName) ?? [];
1583
+ if (existing.length === fieldErrors.length && existing.every((e, i) => e === fieldErrors[i])) {
1584
+ return prev;
1585
+ }
1586
+ const next = new Map(prev);
1587
+ if (fieldErrors.length === 0) {
1588
+ next.delete(columnName);
1589
+ } else {
1590
+ next.set(columnName, fieldErrors);
1591
+ }
1592
+ return next;
1593
+ });
1594
+ }, []);
1595
+ const validateAll = react.useCallback(async () => {
1596
+ const next = /* @__PURE__ */ new Map();
1597
+ for (const [col, validator] of validatorsRef.current) {
1598
+ const fieldErrors = await validator();
1599
+ if (fieldErrors.length > 0) {
1600
+ next.set(col, fieldErrors);
1601
+ }
1602
+ }
1603
+ setErrors(next);
1604
+ return next.size === 0;
1605
+ }, []);
1606
+ const clearErrors = react.useCallback(() => {
1607
+ setErrors(/* @__PURE__ */ new Map());
1608
+ }, []);
1609
+ const isValid = errors.size === 0;
1610
+ const value = react.useMemo(
1611
+ () => ({ register, validateColumn, validateAll, clearErrors, isValid, errors }),
1612
+ [register, validateColumn, validateAll, clearErrors, isValid, errors]
1613
+ );
1614
+ return /* @__PURE__ */ jsxRuntime.jsx(ValidationContextContext.Provider, { value, children });
1615
+ }
1616
+ function valuesEqual(a, b) {
1617
+ const aEmpty = a === null || a === void 0;
1618
+ const bEmpty = b === null || b === void 0;
1619
+ if (aEmpty && bEmpty) return true;
1620
+ if (aEmpty !== bEmpty) return false;
1621
+ return JSON.stringify(a) === JSON.stringify(b);
1622
+ }
1623
+ var GUID_PATTERN = /^\{?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}?$/i;
1624
+ function parseQueryDefault(metadata, rawValue) {
1625
+ switch (metadata.$type) {
1626
+ // StringMetadata + MemoMetadata both ride on StringValue. In Blazor
1627
+ // MemoMetadata derives from StringMetadata so a single `StringMetadata`
1628
+ // pattern catches both; in TS the discriminators are distinct so we
1629
+ // list both arms.
1630
+ case 14:
1631
+ case 7:
1632
+ return { $type: 14, value: rawValue };
1633
+ case 5: {
1634
+ const n = Number(rawValue);
1635
+ return Number.isFinite(n) && Number.isInteger(n) ? { $type: 5, value: n } : null;
1636
+ }
1637
+ case 18: {
1638
+ const n = Number(rawValue);
1639
+ return Number.isFinite(n) && Number.isInteger(n) ? { $type: 18, value: n } : null;
1640
+ }
1641
+ case 3: {
1642
+ const n = Number(rawValue);
1643
+ return Number.isFinite(n) ? { $type: 3, value: n } : null;
1644
+ }
1645
+ case 4: {
1646
+ const n = Number(rawValue);
1647
+ return Number.isFinite(n) ? { $type: 4, value: n } : null;
1648
+ }
1649
+ case 8: {
1650
+ const n = Number(rawValue);
1651
+ return Number.isFinite(n) ? { $type: 8, value: n } : null;
1652
+ }
1653
+ case 0: {
1654
+ const lower = rawValue.trim().toLowerCase();
1655
+ if (lower === "true") return { $type: 0, value: true };
1656
+ if (lower === "false") return { $type: 0, value: false };
1657
+ return null;
1658
+ }
1659
+ case 2: {
1660
+ const ts = Date.parse(rawValue);
1661
+ if (Number.isNaN(ts)) return null;
1662
+ return { $type: 2, value: new Date(ts).toISOString() };
1663
+ }
1664
+ case 11: {
1665
+ const n = Number(rawValue);
1666
+ return Number.isFinite(n) && Number.isInteger(n) ? { $type: 11, value: n } : null;
1667
+ }
1668
+ case 40: {
1669
+ const parts = rawValue.split(",").map((s) => s.trim()).filter(Boolean);
1670
+ const values = [];
1671
+ for (const p of parts) {
1672
+ const n = Number(p);
1673
+ if (Number.isFinite(n) && Number.isInteger(n)) values.push({ value: n });
1674
+ }
1675
+ return values.length > 0 ? { $type: 40, value: values } : null;
1676
+ }
1677
+ case 6: {
1678
+ try {
1679
+ const parsed = JSON.parse(rawValue);
1680
+ if (!parsed || typeof parsed !== "object") return null;
1681
+ const findKey = (target) => Object.keys(parsed).find(
1682
+ (k) => k.toLowerCase() === target.toLowerCase()
1683
+ );
1684
+ const obj = parsed;
1685
+ const tableNameKey = findKey("tableName");
1686
+ const valueKey = findKey("value");
1687
+ const nameKey = findKey("name");
1688
+ const tableName = tableNameKey ? obj[tableNameKey] : void 0;
1689
+ const value = valueKey ? obj[valueKey] : void 0;
1690
+ const name = nameKey ? obj[nameKey] : null;
1691
+ if (typeof tableName !== "string" || tableName.length === 0) return null;
1692
+ if (typeof value !== "string" || value.length === 0) return null;
1693
+ return {
1694
+ $type: 6,
1695
+ tableName,
1696
+ value,
1697
+ name: typeof name === "string" ? name : null
1698
+ };
1699
+ } catch {
1700
+ return null;
1701
+ }
1702
+ }
1703
+ case 15: {
1704
+ if (!GUID_PATTERN.test(rawValue)) return null;
1705
+ return { $type: 15, value: rawValue.replace(/[{}]/g, "") };
1706
+ }
1707
+ default:
1708
+ return null;
1709
+ }
1710
+ }
1711
+ function parseQueryDefaultsFromUrl(search, tableMetadata) {
1712
+ const out = {};
1713
+ if (!search) return out;
1714
+ const params = new URLSearchParams(search);
1715
+ for (const [columnName, rawValue] of params) {
1716
+ if (!columnName) continue;
1717
+ const meta = tableMetadata.columns.find(
1718
+ (c) => c.columnName === columnName
1719
+ );
1720
+ if (!meta) continue;
1721
+ try {
1722
+ const parsed = parseQueryDefault(meta, rawValue);
1723
+ if (parsed) out[columnName] = parsed;
1724
+ } catch {
1725
+ }
1726
+ }
1727
+ return out;
1728
+ }
1729
+ var RecordContextContext = react.createContext(null);
1730
+ RecordContextContext.displayName = "RecordContext";
1731
+ function RecordContext(props) {
1732
+ return /* @__PURE__ */ jsxRuntime.jsx(ValidationProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(RecordContextInner, { ...props }) });
1733
+ }
1734
+ function RecordContextInner({
1735
+ children,
1736
+ table,
1737
+ id: idProp,
1738
+ queryParameterName,
1739
+ initialRecord,
1740
+ loadedRecord: loadedRecordProp,
1741
+ initialPendingChanges,
1742
+ columns,
1743
+ onRecordLoaded,
1744
+ onBeforeSave,
1745
+ onBeforeDelete,
1746
+ warnOnUnsavedChanges,
1747
+ forceSuccessfulValidationBeforeSave
1748
+ }) {
1749
+ const queryId = useQueryParam(queryParameterName ?? "");
1750
+ const resolvedId = queryParameterName && queryId !== null && queryId.length > 0 ? queryId : idProp;
1751
+ const propIsEmptyId = !resolvedId || resolvedId === "00000000-0000-0000-0000-000000000000";
1752
+ const [effectiveId, setEffectiveId] = react.useState(
1753
+ propIsEmptyId ? void 0 : resolvedId
1754
+ );
1755
+ react.useEffect(() => {
1756
+ setEffectiveId(propIsEmptyId ? void 0 : resolvedId);
1757
+ }, [resolvedId, propIsEmptyId]);
1758
+ const isCreateMode = effectiveId === void 0;
1759
+ const isSkipFetchMode = !isCreateMode && loadedRecordProp !== void 0;
1760
+ const { ppp } = usePowerPortalsPro();
1761
+ const mainContext = useMainContext();
1762
+ const validation = useValidationContext();
1763
+ const isTopMost = mainContext === null;
1764
+ const resolvedWarnOnUnsavedChanges = warnOnUnsavedChanges ?? mainContext?.warnOnUnsavedChanges ?? true;
1765
+ const resolvedForceSuccessfulValidationBeforeSave = forceSuccessfulValidationBeforeSave ?? mainContext?.forceSuccessfulValidationBeforeSave ?? true;
1766
+ const overlayService = useOverlayService();
1767
+ const localizer = useLocalizer();
1768
+ const overlayTargetId = react.useId();
1769
+ const query = useRecord(table, effectiveId ?? "", {
1770
+ ...columns ? { columns } : {},
1771
+ enabled: !isCreateMode && !isSkipFetchMode
1772
+ });
1773
+ const metadataQuery = useTableMetadata(table);
1774
+ const [loadedRecord, setLoadedRecord] = react.useState(
1775
+ () => isSkipFetchMode ? loadedRecordProp : void 0
1776
+ );
1777
+ const [pendingChanges, setPendingChanges] = react.useState(
1778
+ () => /* @__PURE__ */ new Map()
1779
+ );
1780
+ const onRecordLoadedRef = react.useRef(onRecordLoaded);
1781
+ onRecordLoadedRef.current = onRecordLoaded;
1782
+ const onBeforeSaveRef = react.useRef(onBeforeSave);
1783
+ onBeforeSaveRef.current = onBeforeSave;
1784
+ const refetchRef = react.useRef(query.refetch);
1785
+ refetchRef.current = query.refetch;
1786
+ const recordRef = react.useRef(void 0);
1787
+ const loadedRecordRef = react.useRef(void 0);
1788
+ const ownDirtyRef = react.useRef(false);
1789
+ const pendingChangesRef = react.useRef(/* @__PURE__ */ new Map());
1790
+ pendingChangesRef.current = pendingChanges;
1791
+ const validationRef = react.useRef(validation);
1792
+ validationRef.current = validation;
1793
+ const isCreateModeRef = react.useRef(isCreateMode);
1794
+ isCreateModeRef.current = isCreateMode;
1795
+ const forceSuccessfulValidationBeforeSaveRef = react.useRef(
1796
+ resolvedForceSuccessfulValidationBeforeSave
1797
+ );
1798
+ forceSuccessfulValidationBeforeSaveRef.current = resolvedForceSuccessfulValidationBeforeSave;
1799
+ const queryParameterNameRef = react.useRef(queryParameterName);
1800
+ queryParameterNameRef.current = queryParameterName;
1801
+ const isTopMostRef = react.useRef(mainContext === null);
1802
+ isTopMostRef.current = mainContext === null;
1803
+ react.useEffect(() => {
1804
+ if (!isCreateMode) return;
1805
+ const baseSeed = initialRecord ?? { tableName: table, properties: {} };
1806
+ const seeded = baseSeed.id !== void 0 ? baseSeed : { ...baseSeed, _idForCreate: crypto.randomUUID() };
1807
+ setLoadedRecord(seeded);
1808
+ setPendingChanges(/* @__PURE__ */ new Map());
1809
+ }, [isCreateMode, initialRecord, table]);
1810
+ react.useEffect(() => {
1811
+ if (!isCreateMode) return;
1812
+ if (!queryParameterName) return;
1813
+ if (!metadataQuery.data) return;
1814
+ if (!loadedRecord) return;
1815
+ if (pendingChangesRef.current.size > 0) return;
1816
+ const search = typeof window !== "undefined" ? window.location.search : "";
1817
+ const defaults = parseQueryDefaultsFromUrl(search, metadataQuery.data);
1818
+ const cols = Object.keys(defaults);
1819
+ if (cols.length === 0) return;
1820
+ let changed = false;
1821
+ const nextProperties = {
1822
+ ...loadedRecord.properties ?? {}
1823
+ };
1824
+ for (const col of cols) {
1825
+ const next = defaults[col];
1826
+ if (!valuesEqual(nextProperties[col], next)) {
1827
+ nextProperties[col] = next;
1828
+ changed = true;
1829
+ }
1830
+ }
1831
+ if (!changed) return;
1832
+ setLoadedRecord(
1833
+ (current) => current ? { ...current, properties: nextProperties } : current
1834
+ );
1835
+ }, [isCreateMode, queryParameterName, metadataQuery.data, loadedRecord]);
1836
+ react.useEffect(() => {
1837
+ if (!isSkipFetchMode || !loadedRecordProp) return;
1838
+ setLoadedRecord(loadedRecordProp);
1839
+ setPendingChanges(
1840
+ initialPendingChanges && initialPendingChanges.size > 0 ? new Map(initialPendingChanges) : /* @__PURE__ */ new Map()
1841
+ );
1842
+ validationRef.current?.clearErrors();
1843
+ onRecordLoadedRef.current?.(loadedRecordProp);
1844
+ }, [isSkipFetchMode, loadedRecordProp, initialPendingChanges]);
1845
+ react.useEffect(() => {
1846
+ if (query.status === "success" && query.data) {
1847
+ setLoadedRecord(query.data);
1848
+ setPendingChanges(
1849
+ initialPendingChanges && initialPendingChanges.size > 0 ? new Map(initialPendingChanges) : /* @__PURE__ */ new Map()
1850
+ );
1851
+ validationRef.current?.clearErrors();
1852
+ onRecordLoadedRef.current?.(query.data);
1853
+ }
1854
+ }, [query.status, query.data, initialPendingChanges]);
1855
+ loadedRecordRef.current = loadedRecord;
1856
+ const record = react.useMemo(() => {
1857
+ if (!loadedRecord) return void 0;
1858
+ if (pendingChanges.size === 0) return loadedRecord;
1859
+ const properties = { ...loadedRecord.properties ?? {} };
1860
+ for (const [col, val] of pendingChanges) {
1861
+ if (val === null) {
1862
+ delete properties[col];
1863
+ } else {
1864
+ properties[col] = val;
1865
+ }
1866
+ }
1867
+ return { ...loadedRecord, properties, isDirty: true };
1868
+ }, [loadedRecord, pendingChanges]);
1869
+ const dirtyColumns = react.useMemo(
1870
+ () => new Set(pendingChanges.keys()),
1871
+ [pendingChanges]
1872
+ );
1873
+ const ownDirty = pendingChanges.size > 0;
1874
+ recordRef.current = record;
1875
+ ownDirtyRef.current = ownDirty;
1876
+ const nestedRef = react.useRef(/* @__PURE__ */ new Set());
1877
+ const [nestedVersion, setNestedVersion] = react.useState(0);
1878
+ const nestedDirty = react.useMemo(() => {
1879
+ for (const d of nestedRef.current) {
1880
+ if (d.isDirty()) return true;
1881
+ }
1882
+ return false;
1883
+ }, [nestedVersion]);
1884
+ const isDirty = ownDirty || nestedDirty;
1885
+ const registerNested = react.useCallback((descendant) => {
1886
+ nestedRef.current.add(descendant);
1887
+ setNestedVersion((v) => v + 1);
1888
+ return () => {
1889
+ nestedRef.current.delete(descendant);
1890
+ setNestedVersion((v) => v + 1);
1891
+ };
1892
+ }, []);
1893
+ const notifyNestedDirtyChanged = react.useCallback(() => {
1894
+ setNestedVersion((v) => v + 1);
1895
+ }, []);
1896
+ const buildOwnSaveRequest = react.useCallback(() => {
1897
+ if (!recordRef.current || !ownDirtyRef.current) return null;
1898
+ const current = recordRef.current;
1899
+ if (isCreateModeRef.current) {
1900
+ const allProperties = {
1901
+ ...current.properties
1902
+ };
1903
+ return {
1904
+ $type: "CreateRequest",
1905
+ record: {
1906
+ tableName: current.tableName,
1907
+ // Include the client-pre-generated GUID. The seed effect writes
1908
+ // it into `_idForCreate` (Blazor parity); the server's in-batch
1909
+ // resolver matches AssociateRequest / child-FK references
1910
+ // against either `id` or `_idForCreate`, so a Create + Associate
1911
+ // pair within one `ExecuteMultiple` resolves to the same record.
1912
+ // `id` is also forwarded when the consumer supplied an explicit
1913
+ // one via `initialRecord` (rare — pre-assigned ids on data
1914
+ // import).
1915
+ ...current.id !== void 0 && { id: current.id },
1916
+ ...current._idForCreate !== void 0 && current._idForCreate !== null && {
1917
+ _idForCreate: current._idForCreate
1918
+ },
1919
+ properties: allProperties
1920
+ }
1921
+ };
1922
+ }
1923
+ const partialProperties = {};
1924
+ for (const [col, val] of pendingChangesRef.current) {
1925
+ partialProperties[col] = val;
1926
+ }
1927
+ const partialRecord = {
1928
+ tableName: current.tableName,
1929
+ properties: partialProperties,
1930
+ ...current.id !== void 0 && { id: current.id }
1931
+ };
1932
+ return { $type: "UpdateRequest", record: partialRecord };
1933
+ }, []);
1934
+ const getRequests = react.useCallback(() => {
1935
+ const all = [];
1936
+ const own = buildOwnSaveRequest();
1937
+ if (own) all.push(own);
1938
+ for (const descendant of nestedRef.current) {
1939
+ all.push(...descendant.getRequests());
1940
+ }
1941
+ return all;
1942
+ }, [buildOwnSaveRequest]);
1943
+ const validate = react.useCallback(async () => {
1944
+ for (const descendant of nestedRef.current) {
1945
+ if (!await descendant.validate()) return false;
1946
+ }
1947
+ return true;
1948
+ }, []);
1949
+ const saveAll = react.useCallback(async () => {
1950
+ if (!loadedRecordRef.current) {
1951
+ throw new Error("RecordContext.save called before the record loaded.");
1952
+ }
1953
+ await onBeforeSaveRef.current?.();
1954
+ if (forceSuccessfulValidationBeforeSaveRef.current) {
1955
+ const valid = await validate();
1956
+ if (!valid) return;
1957
+ }
1958
+ const ownReq = buildOwnSaveRequest();
1959
+ const descendantReqs = [];
1960
+ for (const descendant of nestedRef.current) {
1961
+ descendantReqs.push(...descendant.getRequests());
1962
+ }
1963
+ const requests = ownReq ? [ownReq, ...descendantReqs] : descendantReqs;
1964
+ if (requests.length > 0) {
1965
+ const responses = await ppp.executeMultipleAsync(requests, { returnResponses: true });
1966
+ if (ownReq?.$type === "CreateRequest") {
1967
+ const ownResp = responses[0];
1968
+ if (ownResp?.$type === "CreateResponse" && ownResp.id) {
1969
+ setEffectiveId(ownResp.id);
1970
+ const qpn = queryParameterNameRef.current;
1971
+ if (qpn && isTopMostRef.current && typeof window !== "undefined") {
1972
+ const existing = new URLSearchParams(window.location.search).get(
1973
+ qpn
1974
+ );
1975
+ if (!existing) {
1976
+ const newUrl = new URL(window.location.href);
1977
+ newUrl.searchParams.set(qpn, ownResp.id);
1978
+ window.history.replaceState(
1979
+ window.history.state,
1980
+ "",
1981
+ newUrl.toString()
1982
+ );
1983
+ }
1984
+ }
1985
+ }
1986
+ }
1987
+ setPendingChanges(/* @__PURE__ */ new Map());
1988
+ }
1989
+ if (!isCreateModeRef.current) {
1990
+ void refetchRef.current();
1991
+ }
1992
+ for (const descendant of nestedRef.current) {
1993
+ await descendant.refresh();
1994
+ }
1995
+ }, [ppp, validate, buildOwnSaveRequest]);
1996
+ const reload = react.useCallback(async () => {
1997
+ refetchRef.current();
1998
+ for (const descendant of nestedRef.current) {
1999
+ await descendant.refresh();
2000
+ }
2001
+ }, []);
2002
+ const resetState = react.useCallback(() => {
2003
+ setPendingChanges(/* @__PURE__ */ new Map());
2004
+ validationRef.current?.clearErrors();
2005
+ for (const descendant of nestedRef.current) {
2006
+ descendant.resetState();
2007
+ }
2008
+ }, []);
2009
+ const setColumnValue = react.useCallback(
2010
+ (columnName, value2) => {
2011
+ setPendingChanges((prev) => {
2012
+ if (!loadedRecord) return prev;
2013
+ const original = loadedRecord.properties?.[columnName];
2014
+ const newValue = value2 ?? null;
2015
+ if (valuesEqual(original, newValue)) {
2016
+ if (!prev.has(columnName)) return prev;
2017
+ const next2 = new Map(prev);
2018
+ next2.delete(columnName);
2019
+ return next2;
2020
+ }
2021
+ const existing = prev.has(columnName) ? prev.get(columnName) ?? null : void 0;
2022
+ if (existing !== void 0 && valuesEqual(existing, newValue)) return prev;
2023
+ const next = new Map(prev);
2024
+ next.set(columnName, newValue);
2025
+ return next;
2026
+ });
2027
+ },
2028
+ [loadedRecord]
2029
+ );
2030
+ const hydrateColumnValue = react.useCallback(
2031
+ (columnName, value2) => {
2032
+ setLoadedRecord((current) => {
2033
+ if (!current) return current;
2034
+ if (pendingChanges.has(columnName)) return current;
2035
+ const previous = current.properties?.[columnName];
2036
+ if (value2 === null) {
2037
+ if (previous === void 0) return current;
2038
+ const nextProperties = { ...current.properties ?? {} };
2039
+ delete nextProperties[columnName];
2040
+ return { ...current, properties: nextProperties };
2041
+ }
2042
+ if (valuesEqual(previous, value2)) return current;
2043
+ return {
2044
+ ...current,
2045
+ properties: { ...current.properties ?? {}, [columnName]: value2 }
2046
+ };
2047
+ });
2048
+ },
2049
+ [pendingChanges]
2050
+ );
2051
+ const loadingOverlayRef = react.useRef(null);
2052
+ react.useEffect(() => {
2053
+ if (!overlayService) return;
2054
+ if (query.status === "loading") {
2055
+ if (!loadingOverlayRef.current) {
2056
+ loadingOverlayRef.current = overlayService.show({
2057
+ targetId: overlayTargetId,
2058
+ description: localizer.t("app.messages.record-loading")
2059
+ });
2060
+ }
2061
+ } else if (loadingOverlayRef.current) {
2062
+ overlayService.hide(loadingOverlayRef.current);
2063
+ loadingOverlayRef.current = null;
2064
+ }
2065
+ return () => {
2066
+ if (loadingOverlayRef.current) {
2067
+ overlayService.hide(loadingOverlayRef.current);
2068
+ loadingOverlayRef.current = null;
2069
+ }
2070
+ };
2071
+ }, [overlayService, query.status, overlayTargetId, localizer]);
2072
+ const isDirtyRef = react.useRef(isDirty);
2073
+ isDirtyRef.current = isDirty;
2074
+ const mainContextRef = react.useRef(mainContext);
2075
+ mainContextRef.current = mainContext;
2076
+ react.useEffect(() => {
2077
+ if (!mainContext) return;
2078
+ return mainContext.registerDescendant({
2079
+ getRequests,
2080
+ validate,
2081
+ refresh: reload,
2082
+ resetState,
2083
+ isDirty: () => isDirtyRef.current
2084
+ });
2085
+ }, [mainContext?.registerDescendant, getRequests, validate, reload, resetState]);
2086
+ react.useEffect(() => {
2087
+ mainContextRef.current?.notifyDescendantDirtyChanged();
2088
+ }, [isDirty]);
2089
+ useBeforeUnloadWhen(isTopMost && resolvedWarnOnUnsavedChanges && isDirty);
2090
+ const unsavedChangesRegistry = useUnsavedChangesRegistry();
2091
+ const resolvedWarnRef = react.useRef(resolvedWarnOnUnsavedChanges);
2092
+ resolvedWarnRef.current = resolvedWarnOnUnsavedChanges;
2093
+ react.useEffect(() => {
2094
+ if (!isTopMost) return;
2095
+ if (!unsavedChangesRegistry) return;
2096
+ return unsavedChangesRegistry.register({
2097
+ isDirty: () => resolvedWarnRef.current && isDirtyRef.current
2098
+ });
2099
+ }, [isTopMost, unsavedChangesRegistry]);
2100
+ react.useEffect(() => {
2101
+ if (!isTopMost) return;
2102
+ if (!unsavedChangesRegistry) return;
2103
+ unsavedChangesRegistry.notifyDirtyChanged();
2104
+ }, [isTopMost, unsavedChangesRegistry, isDirty, resolvedWarnOnUnsavedChanges]);
2105
+ const innerMainContext = react.useMemo(
2106
+ () => ({
2107
+ saveAll,
2108
+ refreshAll: reload,
2109
+ resetState,
2110
+ validate,
2111
+ getRequests,
2112
+ registerDescendant: registerNested,
2113
+ notifyDescendantDirtyChanged: notifyNestedDirtyChanged,
2114
+ isDirty,
2115
+ warnOnUnsavedChanges: resolvedWarnOnUnsavedChanges,
2116
+ forceSuccessfulValidationBeforeSave: resolvedForceSuccessfulValidationBeforeSave
2117
+ }),
2118
+ [
2119
+ saveAll,
2120
+ reload,
2121
+ resetState,
2122
+ validate,
2123
+ getRequests,
2124
+ registerNested,
2125
+ notifyNestedDirtyChanged,
2126
+ isDirty,
2127
+ resolvedWarnOnUnsavedChanges,
2128
+ resolvedForceSuccessfulValidationBeforeSave
2129
+ ]
2130
+ );
2131
+ const onBeforeDeleteRef = react.useRef(onBeforeDelete);
2132
+ onBeforeDeleteRef.current = onBeforeDelete;
2133
+ const runBeforeDelete = react.useCallback(async () => {
2134
+ const handler = onBeforeDeleteRef.current;
2135
+ if (handler) await handler();
2136
+ }, []);
2137
+ const value = react.useMemo(
2138
+ () => ({
2139
+ status: query.status,
2140
+ record,
2141
+ error: query.error,
2142
+ reload,
2143
+ save: saveAll,
2144
+ getRequests,
2145
+ resetState,
2146
+ setColumnValue,
2147
+ hydrateColumnValue,
2148
+ tableMetadata: metadataQuery.data,
2149
+ isDirty,
2150
+ dirtyColumns,
2151
+ overlayTargetId,
2152
+ runBeforeDelete
2153
+ }),
2154
+ [
2155
+ query.status,
2156
+ query.error,
2157
+ record,
2158
+ reload,
2159
+ saveAll,
2160
+ getRequests,
2161
+ resetState,
2162
+ setColumnValue,
2163
+ hydrateColumnValue,
2164
+ metadataQuery.data,
2165
+ isDirty,
2166
+ dirtyColumns,
2167
+ overlayTargetId,
2168
+ runBeforeDelete
2169
+ ]
2170
+ );
2171
+ return /* @__PURE__ */ jsxRuntime.jsx(MainContextProvider, { value: innerMainContext, children: /* @__PURE__ */ jsxRuntime.jsx(RecordContextContext.Provider, { value, children }) });
2172
+ }
2173
+ function useRecordContext() {
2174
+ const ctx = react.useContext(RecordContextContext);
2175
+ if (!ctx) {
2176
+ throw new Error(
2177
+ "useRecordContext must be used inside <RecordContext>. Wrap the component tree where you need the record."
2178
+ );
2179
+ }
2180
+ return ctx;
2181
+ }
2182
+ function useRecordContextOptional() {
2183
+ return react.useContext(RecordContextContext);
2184
+ }
2185
+ var EMPTY_GUID = "00000000-0000-0000-0000-000000000000";
2186
+ function LookupRecordContext({
2187
+ columnName,
2188
+ onBeforeSave,
2189
+ children,
2190
+ isVisible = true
2191
+ }) {
2192
+ const parent = useRecordContext();
2193
+ if (!isVisible) return null;
2194
+ const raw = parent.record?.properties?.[columnName];
2195
+ if (!raw?.value || raw.value === EMPTY_GUID || !raw.tableName) {
2196
+ return null;
2197
+ }
2198
+ return /* @__PURE__ */ jsxRuntime.jsx(
2199
+ RecordContext,
2200
+ {
2201
+ table: raw.tableName,
2202
+ id: raw.value,
2203
+ ...onBeforeSave && { onBeforeSave },
2204
+ children
2205
+ }
2206
+ );
2207
+ }
2208
+
2209
+ // src/use-column-metadata.ts
2210
+ function useColumnMetadata(columnName) {
2211
+ const { tableMetadata } = useRecordContext();
2212
+ return tableMetadata?.columns.find((c) => c.columnName === columnName);
2213
+ }
2214
+ var DialogServiceContext = react.createContext(null);
2215
+ DialogServiceContext.displayName = "DialogService";
2216
+ function useDialogService() {
2217
+ const ctx = react.useContext(DialogServiceContext);
2218
+ if (!ctx) {
2219
+ throw new Error(
2220
+ "useDialogService must be used inside a <DialogServiceProvider>. Wrap your app in <FluentDialogProvider> from @powerportalspro/react-fluent (or your own implementation) so framework dialogs have somewhere to render."
2221
+ );
2222
+ }
2223
+ return ctx;
2224
+ }
2225
+ function DialogServiceProvider({
2226
+ value,
2227
+ children
2228
+ }) {
2229
+ return /* @__PURE__ */ jsxRuntime.jsx(DialogServiceContext.Provider, { value, children });
2230
+ }
2231
+ async function warnAboutUnsavedChangesAsync(dialog, localizer) {
2232
+ const result = await dialog.showConfirmationAsync(localizer.t("app.messages.unsaved-changes"), {
2233
+ title: localizer.t("app.messages.unsaved-changes-title"),
2234
+ primaryText: localizer.t("app.buttons.yes.label"),
2235
+ secondaryText: localizer.t("app.buttons.no.label")
2236
+ });
2237
+ return !result.cancelled;
2238
+ }
2239
+ function useWarnAboutUnsavedChanges() {
2240
+ const dialog = useDialogService();
2241
+ const localizer = useLocalizer();
2242
+ return react.useCallback(
2243
+ () => warnAboutUnsavedChangesAsync(dialog, localizer),
2244
+ [dialog, localizer]
2245
+ );
2246
+ }
2247
+
2248
+ // src/unsaved-changes-warning.ts
2249
+ function useUnsavedChangesWarning() {
2250
+ const ctx = useMainContext();
2251
+ const isDirty = ctx?.isDirty ?? false;
2252
+ useBeforeUnloadWhen(isDirty);
2253
+ }
2254
+ function useUnsavedChangesGuard() {
2255
+ const isAnyDirty = useIsAnyDirty();
2256
+ const warn = useWarnAboutUnsavedChanges();
2257
+ return { isAnyDirty, warn };
2258
+ }
2259
+
2260
+ // src/index.ts
2261
+ var VERSION = "0.1.0";
2262
+
2263
+ exports.ASP_NET_CULTURE_COOKIE = ASP_NET_CULTURE_COOKIE;
2264
+ exports.AuthStatus = AuthStatus;
2265
+ exports.AuthorizedView = AuthorizedView;
2266
+ exports.DEFAULT_URL_LOCALE_PATTERN = DEFAULT_URL_LOCALE_PATTERN;
2267
+ exports.DefaultLocalizerProvider = DefaultLocalizerProvider;
2268
+ exports.DialogServiceProvider = DialogServiceProvider;
2269
+ exports.InMemoryEnvironmentFileSettingsCache = InMemoryEnvironmentFileSettingsCache;
2270
+ exports.InMemoryTableMetadataCache = InMemoryTableMetadataCache;
2271
+ exports.InMemoryViewMetadataCache = InMemoryViewMetadataCache;
2272
+ exports.InMemoryViewsForTableCache = InMemoryViewsForTableCache;
2273
+ exports.LocaleProvider = LocaleProvider;
2274
+ exports.LocalizerProvider = LocalizerProvider;
2275
+ exports.LookupRecordContext = LookupRecordContext;
2276
+ exports.MainContext = MainContext;
2277
+ exports.MainContextProvider = MainContextProvider;
2278
+ exports.OverlayServiceProvider = OverlayServiceProvider;
2279
+ exports.PowerPortalsProContext = PowerPortalsProContext;
2280
+ exports.PowerPortalsProProvider = PowerPortalsProProvider;
2281
+ exports.QueryStatus = QueryStatus;
2282
+ exports.RecordContext = RecordContext;
2283
+ exports.UnsavedChangesRegistryProvider = UnsavedChangesRegistryProvider;
2284
+ exports.VERSION = VERSION;
2285
+ exports.ValidationProvider = ValidationProvider;
2286
+ exports.createLocalizer = createLocalizer;
2287
+ exports.detectLocale = detectLocale;
2288
+ exports.flattenStrings = flattenStrings;
2289
+ exports.getAspNetCultureCookie = getAspNetCultureCookie;
2290
+ exports.getLocaleFromUrlPath = getLocaleFromUrlPath;
2291
+ exports.interpolate = interpolate;
2292
+ exports.resolveLocalizationUrl = resolveLocalizationUrl;
2293
+ exports.runWithOverlayAsync = runWithOverlayAsync;
2294
+ exports.setAspNetCultureCookie = setAspNetCultureCookie;
2295
+ exports.useAuth = useAuth;
2296
+ exports.useColumnMetadata = useColumnMetadata;
2297
+ exports.useColumnValidationErrors = useColumnValidationErrors;
2298
+ exports.useDialogService = useDialogService;
2299
+ exports.useEnvironmentFileSettings = useEnvironmentFileSettings;
2300
+ exports.useFetchRecords = useFetchRecords;
2301
+ exports.useGridData = useGridData;
2302
+ exports.useIsAnyDirty = useIsAnyDirty;
2303
+ exports.useLocale = useLocale;
2304
+ exports.useLocalization = useLocalization;
2305
+ exports.useLocalizationLoaded = useLocalizationLoaded;
2306
+ exports.useLocalizationPrefetcher = useLocalizationPrefetcher;
2307
+ exports.useLocalizer = useLocalizer;
2308
+ exports.useMainContext = useMainContext;
2309
+ exports.useOverlayForTarget = useOverlayForTarget;
2310
+ exports.useOverlayService = useOverlayService;
2311
+ exports.useOverlayServiceState = useOverlayServiceState;
2312
+ exports.usePowerPortalsPro = usePowerPortalsPro;
2313
+ exports.usePrefixedT = usePrefixedT;
2314
+ exports.useQueryParam = useQueryParam;
2315
+ exports.useRecord = useRecord;
2316
+ exports.useRecordContext = useRecordContext;
2317
+ exports.useRecordContextOptional = useRecordContextOptional;
2318
+ exports.useRunWithOverlay = useRunWithOverlay;
2319
+ exports.useSupportedLocales = useSupportedLocales;
2320
+ exports.useT = useT;
2321
+ exports.useTableMetadata = useTableMetadata;
2322
+ exports.useTablePermissions = useTablePermissions;
2323
+ exports.useUnsavedChangesGuard = useUnsavedChangesGuard;
2324
+ exports.useUnsavedChangesRegistry = useUnsavedChangesRegistry;
2325
+ exports.useUnsavedChangesWarning = useUnsavedChangesWarning;
2326
+ exports.useValidationContext = useValidationContext;
2327
+ exports.useViewDataSource = useViewDataSource;
2328
+ exports.useViewMetadata = useViewMetadata;
2329
+ exports.useViewsForTable = useViewsForTable;
2330
+ exports.useWarnAboutUnsavedChanges = useWarnAboutUnsavedChanges;
2331
+ exports.warnAboutUnsavedChangesAsync = warnAboutUnsavedChangesAsync;
2332
+ //# sourceMappingURL=index.cjs.map
2333
+ //# sourceMappingURL=index.cjs.map