@ionite/react 0.0.24-beta.20260910.1 → 0.0.24-beta.20260915.2

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.d.ts CHANGED
@@ -159,4 +159,4 @@ declare function TenantProvider({ id, strategy, children }: TenantProviderProps)
159
159
  declare function useTenant(): TenantContextValue;
160
160
  /** Non-throwing variant; returns undefined when no TenantProvider is present (parity with `useOptionalSSOContext`). */
161
161
  declare function useOptionalTenant(): TenantContextValue | undefined;
162
- export { useTenant, useSSOContext, useOptionalTenant, useOptionalSSOContext, useOptionalCIAMContext, useCIAMProviders, useCIAMContext, silentLogger, logoutCustomer, logout, getCustomer, defaultLogger, createTenantUrls, WorkforceUser2 as WorkforceUser, UseCIAMProvidersResult, TenantUrls, TenantRoutingStrategy2 as TenantRoutingStrategy, TenantProviderProps, TenantProvider, TenantPathNamespace, TenantContextValue, SignedOut, SignedIn, SignInLoading, SSOProvider, Logger5 as Logger, IdTokenClaims, Customer2 as Customer, CIAMProviderType, CIAMProviderListProps, CIAMProviderList, CIAMProviderDescriptor3 as CIAMProviderDescriptor, CIAMProviderColors, CIAMProviderBranding, CIAMProvider, AuthenticatedUser };
162
+ export { AuthenticatedUser, CIAMProvider, CIAMProviderBranding, CIAMProviderColors, CIAMProviderDescriptor3 as CIAMProviderDescriptor, CIAMProviderList, CIAMProviderListProps, CIAMProviderType, Customer2 as Customer, IdTokenClaims, Logger5 as Logger, SSOProvider, SignInLoading, SignedIn, SignedOut, TenantContextValue, TenantPathNamespace, TenantProvider, TenantProviderProps, TenantRoutingStrategy2 as TenantRoutingStrategy, TenantUrls, UseCIAMProvidersResult, WorkforceUser2 as WorkforceUser, createTenantUrls, defaultLogger, getCustomer, logout, logoutCustomer, silentLogger, useCIAMContext, useCIAMProviders, useOptionalCIAMContext, useOptionalSSOContext, useOptionalTenant, useSSOContext, useTenant };
package/dist/index.js CHANGED
@@ -1,604 +1 @@
1
- // packages/react/src/index.ts
2
- import { defaultLogger, silentLogger as silentLogger5 } from "@ionite/core";
3
-
4
- // packages/react/src/ciam-provider.tsx
5
- import { silentLogger as silentLogger2 } from "@ionite/core";
6
- import { createContext, useContext, useMemo } from "react";
7
-
8
- // packages/react/src/auth-resource.tsx
9
- import { silentLogger } from "@ionite/core";
10
- import { useCallback, useEffect, useRef, useState } from "react";
11
- var VALID_STORAGE_KEY = /^[a-zA-Z0-9._-]{1,1024}$/;
12
- function validateStorageKey(key, providerLabel) {
13
- if (!key.trim()) {
14
- throw new Error(`${providerLabel} storageKey must be a non-empty string.`);
15
- }
16
- if (!VALID_STORAGE_KEY.test(key)) {
17
- throw new Error(`${providerLabel} storageKey must be 1-1024 characters and contain only letters, numbers, dots, underscores, and hyphens.`);
18
- }
19
- }
20
- function resolveStorageKey(defaultKey, storageKey, providerLabel) {
21
- if (storageKey === undefined || storageKey === "") {
22
- return defaultKey;
23
- }
24
- validateStorageKey(storageKey, providerLabel);
25
- return storageKey;
26
- }
27
- function loadFromStorageForKey(storage, storageKey, codec, log = silentLogger) {
28
- if (typeof window === "undefined")
29
- return null;
30
- try {
31
- const store = storage === "local" ? localStorage : sessionStorage;
32
- const raw = store.getItem(storageKey);
33
- if (!raw)
34
- return null;
35
- const parsed = JSON.parse(raw);
36
- const validated = codec.validate(parsed);
37
- if (!validated) {
38
- store.removeItem(storageKey);
39
- return null;
40
- }
41
- const revived = codec.revive(validated);
42
- if (codec.isExpired(revived)) {
43
- store.removeItem(storageKey);
44
- return null;
45
- }
46
- return revived;
47
- } catch (error) {
48
- log.error("Error loading auth record from storage:", error);
49
- return null;
50
- }
51
- }
52
- async function fetchResourceFromServer(url, codec, fetchInit, log = silentLogger) {
53
- try {
54
- const response = await fetch(url, fetchInit);
55
- if (response.status === 401) {
56
- return { kind: "unauthenticated" };
57
- }
58
- if (!response.ok) {
59
- const error = new Error(`Failed to fetch auth record: ${response.status} ${response.statusText}`);
60
- log.error("Error fetching auth record from URL:", error);
61
- return { kind: "error", error };
62
- }
63
- const data = await response.json();
64
- const validated = codec.validate(data);
65
- if (!validated) {
66
- const error = new Error("Auth record from server failed validation.");
67
- log.error("Error validating auth record from URL:", error);
68
- return { kind: "error", error };
69
- }
70
- return { kind: "ok", value: codec.revive(validated) };
71
- } catch (err) {
72
- const error = err instanceof Error ? err : new Error(String(err));
73
- log.error("Error fetching auth record from URL:", error);
74
- return { kind: "error", error };
75
- }
76
- }
77
- function useAuthResource(options) {
78
- const { url, storage, storageKey, disableListener, logoutEventName, codec } = options;
79
- const [value, setValueState] = useState(null);
80
- const [isLoading, setIsLoading] = useState(!!url);
81
- const [error, setError] = useState(null);
82
- const fetchInitRef = useRef(options.fetchInit);
83
- fetchInitRef.current = options.fetchInit;
84
- const logRef = useRef(options.log);
85
- logRef.current = options.log;
86
- const saveToStorage = useCallback((next) => {
87
- if (storage === "memory" || typeof window === "undefined")
88
- return;
89
- try {
90
- const store = storage === "local" ? localStorage : sessionStorage;
91
- if (next === null) {
92
- store.removeItem(storageKey);
93
- } else {
94
- store.setItem(storageKey, JSON.stringify(next));
95
- }
96
- } catch (err) {
97
- logRef.current.error("Error saving auth record to storage:", err);
98
- }
99
- }, [storage, storageKey]);
100
- const setValue = useCallback((next) => {
101
- setValueState(next);
102
- saveToStorage(next);
103
- }, [saveToStorage]);
104
- const loadFromStorage = useCallback(() => {
105
- if (storage === "memory")
106
- return null;
107
- return loadFromStorageForKey(storage, storageKey, codec, logRef.current);
108
- }, [storage, storageKey, codec]);
109
- const refresh = useCallback(async () => {
110
- if (!url)
111
- return;
112
- setIsLoading(true);
113
- try {
114
- const outcome = await fetchResourceFromServer(url, codec, fetchInitRef.current, logRef.current);
115
- if (outcome.kind === "ok") {
116
- setValueState(outcome.value);
117
- setError(null);
118
- saveToStorage(outcome.value);
119
- } else if (outcome.kind === "unauthenticated") {
120
- setValueState(null);
121
- setError(null);
122
- saveToStorage(null);
123
- } else {
124
- setError(outcome.error);
125
- }
126
- } finally {
127
- setIsLoading(false);
128
- }
129
- }, [url, codec, saveToStorage]);
130
- useEffect(() => {
131
- const stored = loadFromStorage();
132
- if (stored)
133
- setValueState(stored);
134
- if (url) {
135
- refresh();
136
- } else {
137
- setIsLoading(false);
138
- }
139
- }, [loadFromStorage, url, refresh]);
140
- useEffect(() => {
141
- if (disableListener || storage === "memory")
142
- return;
143
- const handleStorageChange = (event) => {
144
- if (event.key !== storageKey)
145
- return;
146
- if (event.newValue === null) {
147
- setValueState(null);
148
- return;
149
- }
150
- try {
151
- const parsed = JSON.parse(event.newValue);
152
- const validated = codec.validate(parsed);
153
- if (!validated)
154
- return;
155
- const revived = codec.revive(validated);
156
- if (codec.isExpired(revived)) {
157
- setValueState(null);
158
- return;
159
- }
160
- setValueState(revived);
161
- } catch (err) {
162
- logRef.current.error("Error parsing auth record from storage event:", err);
163
- }
164
- };
165
- const handleLogout = () => {
166
- setValueState(null);
167
- setError(null);
168
- };
169
- window.addEventListener("storage", handleStorageChange);
170
- window.addEventListener(logoutEventName, handleLogout);
171
- return () => {
172
- window.removeEventListener("storage", handleStorageChange);
173
- window.removeEventListener(logoutEventName, handleLogout);
174
- };
175
- }, [disableListener, storage, storageKey, logoutEventName, codec]);
176
- return { value, setValue, isLoading, error };
177
- }
178
- async function getResource(url, codec, options) {
179
- const { storageKey, storage, fetchInit, log = silentLogger } = options;
180
- if (storage === "local" || storage === "session") {
181
- const fromStorage = loadFromStorageForKey(storage, storageKey, codec, log);
182
- if (fromStorage)
183
- return fromStorage;
184
- }
185
- if (url) {
186
- const outcome = await fetchResourceFromServer(url, codec, fetchInit, log);
187
- if (outcome.kind === "ok")
188
- return outcome.value;
189
- }
190
- return;
191
- }
192
- async function logoutResource(logoutUrl, config, options) {
193
- try {
194
- const response = await fetch(logoutUrl, {
195
- ...options?.fetchInit,
196
- headers: { Accept: "application/json", ...options?.fetchInit?.headers }
197
- });
198
- if (!response.ok) {
199
- return { success: false, error: `HTTP ${response.status}` };
200
- }
201
- const data = await response.json();
202
- if (!data.success) {
203
- return { success: false, error: data.message || "Logout failed" };
204
- }
205
- const redirect = typeof data.redirect === "string" ? data.redirect : undefined;
206
- if (typeof window !== "undefined") {
207
- const key = resolveStorageKey(config.defaultStorageKey, options?.storageKey, config.providerLabel);
208
- localStorage.removeItem(key);
209
- sessionStorage.removeItem(key);
210
- window.dispatchEvent(new CustomEvent(config.logoutEventName));
211
- if (redirect && options?.navigate !== false) {
212
- window.location.assign(redirect);
213
- }
214
- }
215
- return { success: true, redirect };
216
- } catch (error) {
217
- return {
218
- success: false,
219
- error: error instanceof Error ? error.message : "Network error"
220
- };
221
- }
222
- }
223
-
224
- // packages/react/src/ciam-provider.tsx
225
- import { jsx } from "react/jsx-runtime";
226
- var DEFAULT_STORAGE_KEY = "ion.ciam.customer";
227
- var LOGOUT_EVENT = "ion-ciam-logout";
228
- var PROVIDER_LABEL = "CIAMProvider";
229
- var customerCodec = {
230
- validate(raw) {
231
- if (!raw || typeof raw !== "object")
232
- return null;
233
- const candidate = raw;
234
- if (typeof candidate.userName !== "string" || typeof candidate.name !== "string" || typeof candidate.email !== "string") {
235
- return null;
236
- }
237
- return raw;
238
- },
239
- revive(customer) {
240
- if (customer.ciam?.expires) {
241
- customer.ciam.expires = new Date(customer.ciam.expires);
242
- }
243
- return customer;
244
- },
245
- isExpired(customer) {
246
- const expires = customer.ciam?.expires;
247
- if (!expires)
248
- return false;
249
- const time = expires instanceof Date ? expires.getTime() : new Date(expires).getTime();
250
- return Number.isFinite(time) && time <= Date.now();
251
- }
252
- };
253
- var CTX = createContext(undefined);
254
- function CIAMProvider({
255
- storageKey: storageKeyProp,
256
- customerUrl,
257
- storage = "memory",
258
- disableListener = false,
259
- fetchInit,
260
- log = silentLogger2,
261
- children
262
- }) {
263
- const storageKey = resolveStorageKey(DEFAULT_STORAGE_KEY, storageKeyProp, PROVIDER_LABEL);
264
- const { value, setValue, isLoading, error } = useAuthResource({
265
- url: customerUrl,
266
- storage,
267
- storageKey,
268
- disableListener,
269
- logoutEventName: LOGOUT_EVENT,
270
- codec: customerCodec,
271
- fetchInit,
272
- log
273
- });
274
- const contextValue = useMemo(() => ({ customer: value, setCustomer: setValue, isLoading, error }), [value, setValue, isLoading, error]);
275
- return /* @__PURE__ */ jsx(CTX.Provider, {
276
- value: contextValue,
277
- children
278
- });
279
- }
280
- function useCIAMContext() {
281
- const context = useContext(CTX);
282
- if (context === undefined) {
283
- throw new Error("useCIAMContext must be used within a CIAMProvider");
284
- }
285
- return context;
286
- }
287
- function useOptionalCIAMContext() {
288
- return useContext(CTX);
289
- }
290
- async function getCustomer(customerUrl, options) {
291
- return getResource(customerUrl, customerCodec, {
292
- storageKey: resolveStorageKey(DEFAULT_STORAGE_KEY, options?.storageKey, PROVIDER_LABEL),
293
- storage: options?.storage ?? "memory",
294
- fetchInit: options?.fetchInit,
295
- log: options?.log
296
- });
297
- }
298
- async function logoutCustomer(logoutUrl, options) {
299
- return logoutResource(logoutUrl, { defaultStorageKey: DEFAULT_STORAGE_KEY, logoutEventName: LOGOUT_EVENT, providerLabel: PROVIDER_LABEL }, options);
300
- }
301
- // packages/react/src/ciam-providers.tsx
302
- import { silentLogger as silentLogger3 } from "@ionite/core";
303
- import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
304
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
305
- function useCIAMProviders(providersUrl, log = silentLogger3) {
306
- const [providers, setProviders] = useState2([]);
307
- const [isLoading, setIsLoading] = useState2(Boolean(providersUrl));
308
- const [error, setError] = useState2(null);
309
- const refresh = useCallback2(() => {
310
- if (!providersUrl)
311
- return;
312
- let cancelled = false;
313
- setIsLoading(true);
314
- setError(null);
315
- fetch(providersUrl, { headers: { Accept: "application/json" } }).then(async (response) => {
316
- if (!response.ok) {
317
- throw new Error(`Failed to fetch CIAM providers: ${response.status} ${response.statusText}`);
318
- }
319
- const data = await response.json();
320
- if (!cancelled)
321
- setProviders(Array.isArray(data.providers) ? data.providers : []);
322
- }).catch((err) => {
323
- const normalized = err instanceof Error ? err : new Error(String(err));
324
- log.error("Error fetching CIAM providers:", normalized);
325
- if (!cancelled) {
326
- setProviders([]);
327
- setError(normalized);
328
- }
329
- }).finally(() => {
330
- if (!cancelled)
331
- setIsLoading(false);
332
- });
333
- return () => {
334
- cancelled = true;
335
- };
336
- }, [providersUrl, log]);
337
- useEffect2(() => {
338
- const cleanup = refresh();
339
- return cleanup;
340
- }, [refresh]);
341
- return { providers, isLoading, error, refresh };
342
- }
343
- function providerButtonStyle(provider, theme) {
344
- const background = theme === "dark" ? provider.colors?.dark : provider.colors?.light;
345
- return {
346
- display: "flex",
347
- alignItems: "center",
348
- gap: "0.5rem",
349
- width: "100%",
350
- padding: "0.625rem 0.875rem",
351
- border: "1px solid rgba(0,0,0,0.15)",
352
- borderRadius: "0.5rem",
353
- background: background ?? (theme === "dark" ? "#1f1f1f" : "#ffffff"),
354
- color: theme === "dark" ? "#ffffff" : "#111111",
355
- textDecoration: "none",
356
- font: "inherit",
357
- cursor: "pointer"
358
- };
359
- }
360
- function CIAMProviderList({
361
- providersUrl,
362
- limit,
363
- theme = "light",
364
- renderProvider,
365
- loadingFallback,
366
- emptyFallback,
367
- errorFallback,
368
- moreLabel = "Show more",
369
- className,
370
- log = silentLogger3
371
- }) {
372
- const { providers, isLoading, error, refresh } = useCIAMProviders(providersUrl, log);
373
- const [expanded, setExpanded] = useState2(false);
374
- if (isLoading) {
375
- return loadingFallback ?? null;
376
- }
377
- if (error) {
378
- return errorFallback ? errorFallback(error, refresh) : emptyFallback ?? null;
379
- }
380
- if (providers.length === 0) {
381
- return emptyFallback ?? null;
382
- }
383
- const visible = !expanded && typeof limit === "number" && limit > 0 ? providers.slice(0, limit) : providers;
384
- const hasMore = visible.length < providers.length;
385
- return /* @__PURE__ */ jsxs("div", {
386
- className,
387
- style: { display: "flex", flexDirection: "column", gap: "0.5rem" },
388
- "data-ion-ciam-providers": "",
389
- children: [
390
- visible.map((provider) => renderProvider ? /* @__PURE__ */ jsx2("span", {
391
- children: renderProvider(provider)
392
- }, provider.name) : /* @__PURE__ */ jsxs("a", {
393
- href: provider.loginUrl,
394
- style: providerButtonStyle(provider, theme),
395
- "data-ion-ciam-provider": provider.name,
396
- children: [
397
- provider.iconUrl ? /* @__PURE__ */ jsx2("img", {
398
- src: provider.iconUrl,
399
- alt: "",
400
- width: 20,
401
- height: 20,
402
- "aria-hidden": "true"
403
- }) : null,
404
- /* @__PURE__ */ jsxs("span", {
405
- children: [
406
- "Continue with ",
407
- provider.displayName
408
- ]
409
- })
410
- ]
411
- }, provider.name)),
412
- hasMore ? /* @__PURE__ */ jsx2("button", {
413
- type: "button",
414
- onClick: () => setExpanded(true),
415
- "data-ion-ciam-show-more": "",
416
- children: moreLabel
417
- }) : null
418
- ]
419
- });
420
- }
421
- // packages/react/src/sso-provider.tsx
422
- import { silentLogger as silentLogger4 } from "@ionite/core";
423
- import { createContext as createContext2, useContext as useContext2, useMemo as useMemo2 } from "react";
424
- import { jsx as jsx3 } from "react/jsx-runtime";
425
- var DEFAULT_STORAGE_KEY2 = "ion.sso.user";
426
- var LOGOUT_EVENT2 = "ion-sso-logout";
427
- var PROVIDER_LABEL2 = "SSOProvider";
428
- var userCodec = {
429
- validate(raw) {
430
- if (!raw || typeof raw !== "object")
431
- return null;
432
- const candidate = raw;
433
- if (typeof candidate.userName !== "string" || typeof candidate.name !== "string" || typeof candidate.email !== "string") {
434
- return null;
435
- }
436
- return raw;
437
- },
438
- revive(user) {
439
- if (user.sso?.expires) {
440
- user.sso.expires = new Date(user.sso.expires);
441
- }
442
- return user;
443
- },
444
- isExpired(user) {
445
- const expires = user.sso?.expires;
446
- if (!expires)
447
- return false;
448
- const time = expires instanceof Date ? expires.getTime() : new Date(expires).getTime();
449
- return Number.isFinite(time) && time <= Date.now();
450
- }
451
- };
452
- var CTX2 = createContext2(undefined);
453
- function SSOProvider({
454
- storageKey: storageKeyProp,
455
- userUrl,
456
- storage = "memory",
457
- disableListener = false,
458
- fetchInit,
459
- log = silentLogger4,
460
- children
461
- }) {
462
- const storageKey = resolveStorageKey(DEFAULT_STORAGE_KEY2, storageKeyProp, PROVIDER_LABEL2);
463
- const { value, setValue, isLoading, error } = useAuthResource({
464
- url: userUrl,
465
- storage,
466
- storageKey,
467
- disableListener,
468
- logoutEventName: LOGOUT_EVENT2,
469
- codec: userCodec,
470
- fetchInit,
471
- log
472
- });
473
- const contextValue = useMemo2(() => ({ user: value, setUser: setValue, isLoading, error }), [value, setValue, isLoading, error]);
474
- return /* @__PURE__ */ jsx3(CTX2.Provider, {
475
- value: contextValue,
476
- children
477
- });
478
- }
479
- function useSSOContext() {
480
- const context = useContext2(CTX2);
481
- if (context === undefined) {
482
- throw new Error("useSSOContext must be used within a SSOProvider");
483
- }
484
- return context;
485
- }
486
- function useOptionalSSOContext() {
487
- return useContext2(CTX2);
488
- }
489
- async function logout(logoutUrl, options) {
490
- return logoutResource(logoutUrl, { defaultStorageKey: DEFAULT_STORAGE_KEY2, logoutEventName: LOGOUT_EVENT2, providerLabel: PROVIDER_LABEL2 }, options);
491
- }
492
-
493
- // packages/react/src/sign-in-loading.tsx
494
- import { jsx as jsx4, Fragment } from "react/jsx-runtime";
495
- function SignInLoading({
496
- complete = false,
497
- audience = "workforce",
498
- children
499
- }) {
500
- const sso = useOptionalSSOContext();
501
- const ciam = useOptionalCIAMContext();
502
- const isLoading = audience === "customer" ? !!ciam?.isLoading : !!sso?.isLoading;
503
- if (isLoading && !complete)
504
- return /* @__PURE__ */ jsx4(Fragment, {
505
- children
506
- });
507
- return null;
508
- }
509
- // packages/react/src/signed-in.tsx
510
- import { jsx as jsx5, Fragment as Fragment2 } from "react/jsx-runtime";
511
- function SignedIn({
512
- audience = "workforce",
513
- children
514
- }) {
515
- const sso = useOptionalSSOContext();
516
- const ciam = useOptionalCIAMContext();
517
- const present = audience === "customer" ? !!ciam?.customer : !!sso?.user;
518
- if (present)
519
- return /* @__PURE__ */ jsx5(Fragment2, {
520
- children
521
- });
522
- return null;
523
- }
524
- // packages/react/src/signed-out.tsx
525
- import { jsx as jsx6, Fragment as Fragment3 } from "react/jsx-runtime";
526
- function SignedOut({
527
- audience = "workforce",
528
- children
529
- }) {
530
- const sso = useOptionalSSOContext();
531
- const ciam = useOptionalCIAMContext();
532
- const present = audience === "customer" ? !!ciam?.customer : !!sso?.user;
533
- const isLoading = audience === "customer" ? !!ciam?.isLoading : !!sso?.isLoading;
534
- if (present || isLoading)
535
- return null;
536
- return /* @__PURE__ */ jsx6(Fragment3, {
537
- children
538
- });
539
- }
540
- // packages/react/src/tenant-context.tsx
541
- import {
542
- buildTenantPath,
543
- DEFAULT_TENANT_API_NAMESPACE,
544
- DEFAULT_TENANT_UI_NAMESPACE,
545
- normalizeTenantRoutingStrategy
546
- } from "@ionite/core";
547
- import { createContext as createContext3, useContext as useContext3, useMemo as useMemo3 } from "react";
548
- import { jsx as jsx7 } from "react/jsx-runtime";
549
- var TenantContext = createContext3(null);
550
- function createTenantUrls(id, strategy) {
551
- const normalized = normalizeTenantRoutingStrategy(strategy);
552
- const uiNamespace = normalized.ui ?? DEFAULT_TENANT_UI_NAMESPACE;
553
- const apiNamespace = normalized.api ?? DEFAULT_TENANT_API_NAMESPACE;
554
- return {
555
- ui: (restPath = "/") => buildTenantPath(id, restPath, uiNamespace),
556
- api: (restPath = "/") => buildTenantPath(id, restPath, apiNamespace)
557
- };
558
- }
559
- function TenantProvider({ id, strategy, children }) {
560
- const value = useMemo3(() => {
561
- const normalized = normalizeTenantRoutingStrategy(strategy);
562
- return {
563
- id,
564
- strategy: normalized,
565
- urls: createTenantUrls(id, normalized)
566
- };
567
- }, [strategy, id]);
568
- return /* @__PURE__ */ jsx7(TenantContext.Provider, {
569
- value,
570
- children
571
- });
572
- }
573
- function useTenant() {
574
- const value = useContext3(TenantContext);
575
- if (!value) {
576
- throw new Error("useTenant() must be used within a TenantProvider");
577
- }
578
- return value;
579
- }
580
- function useOptionalTenant() {
581
- return useContext3(TenantContext) ?? undefined;
582
- }
583
- export {
584
- useTenant,
585
- useSSOContext,
586
- useOptionalTenant,
587
- useOptionalSSOContext,
588
- useOptionalCIAMContext,
589
- useCIAMProviders,
590
- useCIAMContext,
591
- silentLogger5 as silentLogger,
592
- logoutCustomer,
593
- logout,
594
- getCustomer,
595
- defaultLogger,
596
- createTenantUrls,
597
- TenantProvider,
598
- SignedOut,
599
- SignedIn,
600
- SignInLoading,
601
- SSOProvider,
602
- CIAMProviderList,
603
- CIAMProvider
604
- };
1
+ import{defaultLogger as Pr,silentLogger as Er}from"@ionite/core";import{silentLogger as ue}from"@ionite/core";import{createContext as ce,useContext as X,useMemo as le}from"react";import{silentLogger as b}from"@ionite/core";import{useCallback as I,useEffect as W,useRef as q,useState as k}from"react";var ie=/^[a-zA-Z0-9._-]{1,1024}$/;function ae(e,r){if(!e.trim())throw Error(`${r} storageKey must be a non-empty string.`);if(!ie.test(e))throw Error(`${r} storageKey must be 1-1024 characters and contain only letters, numbers, dots, underscores, and hyphens.`)}function x(e,r,t){if(r===void 0||r==="")return e;return ae(r,t),r}function z(e,r,t,n=b){if(typeof window>"u")return null;try{let o=e==="local"?localStorage:sessionStorage,s=o.getItem(r);if(!s)return null;let i=JSON.parse(s),a=t.validate(i);if(!a)return o.removeItem(r),null;let u=t.revive(a);if(t.isExpired(u))return o.removeItem(r),null;return u}catch(o){return n.error("Error loading auth record from storage:",o),null}}async function B(e,r,t,n=b){try{let o=await fetch(e,t);if(o.status===401)return{kind:"unauthenticated"};if(!o.ok){let a=Error(`Failed to fetch auth record: ${o.status} ${o.statusText}`);return n.error("Error fetching auth record from URL:",a),{kind:"error",error:a}}let s=await o.json(),i=r.validate(s);if(!i){let a=Error("Auth record from server failed validation.");return n.error("Error validating auth record from URL:",a),{kind:"error",error:a}}return{kind:"ok",value:r.revive(i)}}catch(o){let s=o instanceof Error?o:Error(String(o));return n.error("Error fetching auth record from URL:",s),{kind:"error",error:s}}}function R(e){let{url:r,storage:t,storageKey:n,disableListener:o,logoutEventName:s,codec:i}=e,[a,u]=k(null),[d,c]=k(!!r),[g,p]=k(null),T=q(e.fetchInit);T.current=e.fetchInit;let m=q(e.log);m.current=e.log;let v=I((l)=>{if(t==="memory"||typeof window>"u")return;try{let C=t==="local"?localStorage:sessionStorage;if(l===null)C.removeItem(n);else C.setItem(n,JSON.stringify(l))}catch(C){m.current.error("Error saving auth record to storage:",C)}},[t,n]),S=I((l)=>{u(l),v(l)},[v]),A=I(()=>{if(t==="memory")return null;return z(t,n,i,m.current)},[t,n,i]),f=I(async()=>{if(!r)return;c(!0);try{let l=await B(r,i,T.current,m.current);if(l.kind==="ok")u(l.value),p(null),v(l.value);else if(l.kind==="unauthenticated")u(null),p(null),v(null);else p(l.error)}finally{c(!1)}},[r,i,v]);return W(()=>{let l=A();if(l)u(l);if(r)f();else c(!1)},[A,r,f]),W(()=>{if(o||t==="memory")return;let l=(L)=>{if(L.key!==n)return;if(L.newValue===null){u(null);return}try{let w=JSON.parse(L.newValue),_=i.validate(w);if(!_)return;let F=i.revive(_);if(i.isExpired(F)){u(null);return}u(F)}catch(w){m.current.error("Error parsing auth record from storage event:",w)}},C=()=>{u(null),p(null)};return window.addEventListener("storage",l),window.addEventListener(s,C),()=>{window.removeEventListener("storage",l),window.removeEventListener(s,C)}},[o,t,n,s,i]),{value:a,setValue:S,isLoading:d,error:g}}async function G(e,r,t){let{storageKey:n,storage:o,fetchInit:s,log:i=b}=t;if(o==="local"||o==="session"){let a=z(o,n,r,i);if(a)return a}if(e){let a=await B(e,r,s,i);if(a.kind==="ok")return a.value}return}async function P(e,r,t){try{let n=await fetch(e,{...t?.fetchInit,headers:{Accept:"application/json",...t?.fetchInit?.headers}});if(!n.ok)return{success:!1,error:`HTTP ${n.status}`};let o=await n.json();if(!o.success)return{success:!1,error:o.message||"Logout failed"};let s=typeof o.redirect==="string"?o.redirect:void 0;if(typeof window<"u"){let i=x(r.defaultStorageKey,t?.storageKey,r.providerLabel);if(localStorage.removeItem(i),sessionStorage.removeItem(i),window.dispatchEvent(new CustomEvent(r.logoutEventName)),s&&t?.navigate!==!1)window.location.assign(s)}return{success:!0,redirect:s}}catch(n){return{success:!1,error:n instanceof Error?n.message:"Network error"}}}import{jsx as me}from"react/jsx-runtime";var O="ion.ciam.customer",J="ion-ciam-logout",M="CIAMProvider",Y={validate(e){if(!e||typeof e!=="object")return null;let r=e;if(typeof r.userName!=="string"||typeof r.name!=="string"||typeof r.email!=="string")return null;return e},revive(e){if(e.ciam?.expires)e.ciam.expires=new Date(e.ciam.expires);return e},isExpired(e){let r=e.ciam?.expires;if(!r)return!1;let t=r instanceof Date?r.getTime():new Date(r).getTime();return Number.isFinite(t)&&t<=Date.now()}},N=ce(void 0);function de({storageKey:e,customerUrl:r,storage:t="memory",disableListener:n=!1,fetchInit:o,log:s=ue,children:i}){let a=x(O,e,M),{value:u,setValue:d,isLoading:c,error:g}=R({url:r,storage:t,storageKey:a,disableListener:n,logoutEventName:J,codec:Y,fetchInit:o,log:s}),p=le(()=>({customer:u,setCustomer:d,isLoading:c,error:g}),[u,d,c,g]);return me(N.Provider,{value:p,children:i})}function ge(){let e=X(N);if(e===void 0)throw Error("useCIAMContext must be used within a CIAMProvider");return e}function y(){return X(N)}async function fe(e,r){return G(e,Y,{storageKey:x(O,r?.storageKey,M),storage:r?.storage??"memory",fetchInit:r?.fetchInit,log:r?.log})}async function pe(e,r){return P(e,{defaultStorageKey:O,logoutEventName:J,providerLabel:M},r)}import{silentLogger as H}from"@ionite/core";import{useCallback as ve,useEffect as Ce,useState as E}from"react";import{jsx as U,jsxs as D}from"react/jsx-runtime";function Z(e,r=H){let[t,n]=E([]),[o,s]=E(Boolean(e)),[i,a]=E(null),u=ve(()=>{if(!e)return;let d=!1;return s(!0),a(null),fetch(e,{headers:{Accept:"application/json"}}).then(async(c)=>{if(!c.ok)throw Error(`Failed to fetch CIAM providers: ${c.status} ${c.statusText}`);let g=await c.json();if(!d)n(Array.isArray(g.providers)?g.providers:[])}).catch((c)=>{let g=c instanceof Error?c:Error(String(c));if(r.error("Error fetching CIAM providers:",g),!d)n([]),a(g)}).finally(()=>{if(!d)s(!1)}),()=>{d=!0}},[e,r]);return Ce(()=>u(),[u]),{providers:t,isLoading:o,error:i,refresh:u}}function ye(e,r){return{display:"flex",alignItems:"center",gap:"0.5rem",width:"100%",padding:"0.625rem 0.875rem",border:"1px solid rgba(0,0,0,0.15)",borderRadius:"0.5rem",background:(r==="dark"?e.colors?.dark:e.colors?.light)??(r==="dark"?"#1f1f1f":"#ffffff"),color:r==="dark"?"#ffffff":"#111111",textDecoration:"none",font:"inherit",cursor:"pointer"}}function he({providersUrl:e,limit:r,theme:t="light",renderProvider:n,loadingFallback:o,emptyFallback:s,errorFallback:i,moreLabel:a="Show more",className:u,log:d=H}){let{providers:c,isLoading:g,error:p,refresh:T}=Z(e,d),[m,v]=E(!1);if(g)return o??null;if(p)return i?i(p,T):s??null;if(c.length===0)return s??null;let S=!m&&typeof r==="number"&&r>0?c.slice(0,r):c,A=S.length<c.length;return D("div",{className:u,style:{display:"flex",flexDirection:"column",gap:"0.5rem"},"data-ion-ciam-providers":"",children:[S.map((f)=>n?U("span",{children:n(f)},f.name):D("a",{href:f.loginUrl,style:ye(f,t),"data-ion-ciam-provider":f.name,children:[f.iconUrl?U("img",{src:f.iconUrl,alt:"",width:20,height:20,"aria-hidden":"true"}):null,D("span",{children:["Continue with ",f.displayName]})]},f.name)),A?U("button",{type:"button",onClick:()=>v(!0),"data-ion-ciam-show-more":"",children:a}):null]})}import{silentLogger as xe}from"@ionite/core";import{createContext as Te,useContext as j,useMemo as Se}from"react";import{jsx as Ee}from"react/jsx-runtime";var Q="ion.sso.user",ee="ion-sso-logout",re="SSOProvider",Ae={validate(e){if(!e||typeof e!=="object")return null;let r=e;if(typeof r.userName!=="string"||typeof r.name!=="string"||typeof r.email!=="string")return null;return e},revive(e){if(e.sso?.expires)e.sso.expires=new Date(e.sso.expires);return e},isExpired(e){let r=e.sso?.expires;if(!r)return!1;let t=r instanceof Date?r.getTime():new Date(r).getTime();return Number.isFinite(t)&&t<=Date.now()}},K=Te(void 0);function Ie({storageKey:e,userUrl:r,storage:t="memory",disableListener:n=!1,fetchInit:o,log:s=xe,children:i}){let a=x(Q,e,re),{value:u,setValue:d,isLoading:c,error:g}=R({url:r,storage:t,storageKey:a,disableListener:n,logoutEventName:ee,codec:Ae,fetchInit:o,log:s}),p=Se(()=>({user:u,setUser:d,isLoading:c,error:g}),[u,d,c,g]);return Ee(K.Provider,{value:p,children:i})}function Re(){let e=j(K);if(e===void 0)throw Error("useSSOContext must be used within a SSOProvider");return e}function h(){return j(K)}async function Pe(e,r){return P(e,{defaultStorageKey:Q,logoutEventName:ee,providerLabel:re},r)}import{jsx as ke,Fragment as we}from"react/jsx-runtime";function Le({complete:e=!1,audience:r="workforce",children:t}){let n=h(),o=y();if((r==="customer"?!!o?.isLoading:!!n?.isLoading)&&!e)return ke(we,{children:t});return null}import{jsx as Me,Fragment as Oe}from"react/jsx-runtime";function be({audience:e="workforce",children:r}){let t=h(),n=y();if(e==="customer"?!!n?.customer:!!t?.user)return Me(Oe,{children:r});return null}import{jsx as De,Fragment as Ue}from"react/jsx-runtime";function Ne({audience:e="workforce",children:r}){let t=h(),n=y(),o=e==="customer"?!!n?.customer:!!t?.user,s=e==="customer"?!!n?.isLoading:!!t?.isLoading;if(o||s)return null;return De(Ue,{children:r})}import{buildTenantPath as te,DEFAULT_TENANT_API_NAMESPACE as Ke,DEFAULT_TENANT_UI_NAMESPACE as Ve,normalizeTenantRoutingStrategy as oe}from"@ionite/core";import{createContext as _e,useContext as ne,useMemo as Fe}from"react";import{jsx as ze}from"react/jsx-runtime";var V=_e(null);function se(e,r){let t=oe(r),n=t.ui??Ve,o=t.api??Ke;return{ui:(s="/")=>te(e,s,n),api:(s="/")=>te(e,s,o)}}function We({id:e,strategy:r,children:t}){let n=Fe(()=>{let o=oe(r);return{id:e,strategy:o,urls:se(e,o)}},[r,e]);return ze(V.Provider,{value:n,children:t})}function qe(){let e=ne(V);if(!e)throw Error("useTenant() must be used within a TenantProvider");return e}function $e(){return ne(V)??void 0}export{de as CIAMProvider,he as CIAMProviderList,Ie as SSOProvider,Le as SignInLoading,be as SignedIn,Ne as SignedOut,We as TenantProvider,se as createTenantUrls,Pr as defaultLogger,fe as getCustomer,Pe as logout,pe as logoutCustomer,Er as silentLogger,ge as useCIAMContext,Z as useCIAMProviders,y as useOptionalCIAMContext,h as useOptionalSSOContext,$e as useOptionalTenant,Re as useSSOContext,qe as useTenant};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ionite/react",
3
- "version": "0.0.24-beta.20260910.1",
3
+ "version": "0.0.24-beta.20260915.2",
4
4
  "description": "ionite React Components",
5
5
  "private": false,
6
6
  "author": "ionite",
@@ -24,7 +24,7 @@
24
24
  "./package.json": "./package.json"
25
25
  },
26
26
  "dependencies": {
27
- "@ionite/core": "0.0.24-beta.20260910.1"
27
+ "@ionite/core": "0.0.24-beta.20260915.2"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": "^18.0.0 || ^19.0.0",