kaafil-react-uikit 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +44 -5
  2. package/dist/admin/index.cjs +355 -353
  3. package/dist/admin/index.cjs.map +1 -1
  4. package/dist/admin/index.js +16 -14
  5. package/dist/admin/index.js.map +1 -1
  6. package/dist/{chunk-EP4VCID7.js → chunk-3E2FN5JJ.js} +3 -3
  7. package/dist/{chunk-EP4VCID7.js.map → chunk-3E2FN5JJ.js.map} +1 -1
  8. package/dist/{chunk-TPBKRXEB.cjs → chunk-3RZDHDEY.cjs} +17 -17
  9. package/dist/{chunk-TPBKRXEB.cjs.map → chunk-3RZDHDEY.cjs.map} +1 -1
  10. package/dist/chunk-62HCQWZL.cjs +12 -0
  11. package/dist/chunk-62HCQWZL.cjs.map +1 -0
  12. package/dist/{chunk-ABWNV4N4.js → chunk-6FUVFVYY.js} +97 -4
  13. package/dist/chunk-6FUVFVYY.js.map +1 -0
  14. package/dist/{chunk-BUE2GJDN.js → chunk-762SWUEG.js} +3 -3
  15. package/dist/{chunk-BUE2GJDN.js.map → chunk-762SWUEG.js.map} +1 -1
  16. package/dist/{chunk-CCJ5XNWY.cjs → chunk-CK3L6Y43.cjs} +98 -2
  17. package/dist/chunk-CK3L6Y43.cjs.map +1 -0
  18. package/dist/{chunk-XBYLSJOS.cjs → chunk-DI77H7ED.cjs} +83 -83
  19. package/dist/{chunk-XBYLSJOS.cjs.map → chunk-DI77H7ED.cjs.map} +1 -1
  20. package/dist/{chunk-5DVTAEQC.js → chunk-EDLIJSMR.js} +5 -5
  21. package/dist/{chunk-5DVTAEQC.js.map → chunk-EDLIJSMR.js.map} +1 -1
  22. package/dist/{chunk-VAJ4D7EF.js → chunk-FEJL6GKJ.js} +513 -20
  23. package/dist/chunk-FEJL6GKJ.js.map +1 -0
  24. package/dist/{chunk-2D4Y4LOT.cjs → chunk-IBVCH6GC.cjs} +553 -51
  25. package/dist/chunk-IBVCH6GC.cjs.map +1 -0
  26. package/dist/{chunk-DABF4PUI.js → chunk-ITGVRN2S.js} +3 -3
  27. package/dist/{chunk-DABF4PUI.js.map → chunk-ITGVRN2S.js.map} +1 -1
  28. package/dist/{chunk-DUZG6A22.cjs → chunk-LO3GQFQ6.cjs} +4 -4
  29. package/dist/{chunk-DUZG6A22.cjs.map → chunk-LO3GQFQ6.cjs.map} +1 -1
  30. package/dist/{chunk-4LZXDRAD.cjs → chunk-QD4Z5DRT.cjs} +12 -12
  31. package/dist/{chunk-4LZXDRAD.cjs.map → chunk-QD4Z5DRT.cjs.map} +1 -1
  32. package/dist/{chunk-TV5QTT44.js → chunk-RW6TJMRK.js} +3 -3
  33. package/dist/{chunk-TV5QTT44.js.map → chunk-RW6TJMRK.js.map} +1 -1
  34. package/dist/chunk-XU7GP53M.js +10 -0
  35. package/dist/chunk-XU7GP53M.js.map +1 -0
  36. package/dist/{chunk-DDSNXKFN.cjs → chunk-XYIOM75D.cjs} +9 -9
  37. package/dist/{chunk-DDSNXKFN.cjs.map → chunk-XYIOM75D.cjs.map} +1 -1
  38. package/dist/core/index.cjs +153 -613
  39. package/dist/core/index.cjs.map +1 -1
  40. package/dist/core/index.d.cts +36 -1
  41. package/dist/core/index.d.ts +36 -1
  42. package/dist/core/index.js +21 -511
  43. package/dist/core/index.js.map +1 -1
  44. package/dist/index.css +1 -1
  45. package/dist/manager/index.cjs +239 -239
  46. package/dist/manager/index.js +6 -6
  47. package/dist/traveller/index.cjs +35 -35
  48. package/dist/traveller/index.js +5 -5
  49. package/package.json +2 -2
  50. package/dist/chunk-2D4Y4LOT.cjs.map +0 -1
  51. package/dist/chunk-2JABR4FF.js +0 -105
  52. package/dist/chunk-2JABR4FF.js.map +0 -1
  53. package/dist/chunk-ABWNV4N4.js.map +0 -1
  54. package/dist/chunk-CCJ5XNWY.cjs.map +0 -1
  55. package/dist/chunk-KDGBJPPZ.cjs +0 -110
  56. package/dist/chunk-KDGBJPPZ.cjs.map +0 -1
  57. package/dist/chunk-VAJ4D7EF.js.map +0 -1
@@ -1,8 +1,10 @@
1
- import { createDomainHook, useSnapshotList, classifyError } from './chunk-EP4VCID7.js';
2
- import { useServerTime } from './chunk-DABF4PUI.js';
3
- import { useOfflineMutation, useKaafilClient, useCapabilityRows, deriveCapabilityTriple, useRequiredContext, ConfigContext } from './chunk-ABWNV4N4.js';
4
- import { useCallback, useState, useEffect, useRef, useMemo } from 'react';
5
- import { SyncOpMethod } from 'kaafil-js/client';
1
+ import { createDomainHook, useSnapshotList, classifyError } from './chunk-3E2FN5JJ.js';
2
+ import { useServerTime } from './chunk-ITGVRN2S.js';
3
+ import { useOfflineMutation, useKaafilClient, useCapabilityRows, deriveCapabilityTriple, tokenStyle, ConfigContext, KaafilClientContext, usePersona, useSession, OfflineEngineContext, SessionContext, BrandShell, I18nShell, isDevelopment } from './chunk-6FUVFVYY.js';
4
+ import { useCallback, createContext, useState, useEffect, useRef, useMemo, useContext, Component, Fragment } from 'react';
5
+ import { SyncOpMethod, KaafilClient } from 'kaafil-js/client';
6
+ import { createPortal } from 'react-dom';
7
+ import { jsx, Fragment as Fragment$1, jsxs } from 'react/jsx-runtime';
6
8
 
7
9
  function useAgencyAdminMe() {
8
10
  const client = useKaafilClient();
@@ -290,7 +292,7 @@ function useAgencyManagerProfile(managerRef) {
290
292
  }
291
293
  function useAgencySettings() {
292
294
  const client = useKaafilClient();
293
- const [document, setDocument] = useState(void 0);
295
+ const [document2, setDocument] = useState(void 0);
294
296
  const [loaded, setLoaded] = useState(false);
295
297
  const [lastFetchFailed, setLastFetchFailed] = useState(false);
296
298
  const refresh = useCallback(async () => {
@@ -314,7 +316,7 @@ function useAgencySettings() {
314
316
  },
315
317
  [client, refresh]
316
318
  );
317
- return { document, loaded, lastFetchFailed, refresh, patchSettings };
319
+ return { document: document2, loaded, lastFetchFailed, refresh, patchSettings };
318
320
  }
319
321
  function useAgencyTrip(tripRef) {
320
322
  const client = useKaafilClient();
@@ -468,7 +470,7 @@ var DirectoriesWriteError = class _DirectoriesWriteError extends Error {
468
470
  };
469
471
  function useDirectories(agencyRef) {
470
472
  const client = useKaafilClient();
471
- const serverTime = useServerTime();
473
+ useServerTime();
472
474
  const makeManagersFetch = useCallback(
473
475
  (filters) => async (cursor) => {
474
476
  const page = await client.agencies.managers.listPage({
@@ -557,8 +559,7 @@ function useDirectories(agencyRef) {
557
559
  {
558
560
  externalTravellerId: input.externalTravellerId,
559
561
  fullName: input.fullName,
560
- phone: input.phone,
561
- sourceUpdatedAt: serverTime.now()
562
+ phone: input.phone
562
563
  }
563
564
  ]
564
565
  });
@@ -582,7 +583,7 @@ function useDirectories(agencyRef) {
582
583
  throw new DirectoriesWriteError(classifyError(error));
583
584
  }
584
585
  },
585
- [client, serverTime]
586
+ [client]
586
587
  );
587
588
  const createAgencyTraveller = useCallback(
588
589
  async (input) => {
@@ -742,12 +743,6 @@ function usePaginatedList(fetch, options) {
742
743
  loadPrev
743
744
  };
744
745
  }
745
-
746
- // src/core/hooks/useResolvedSetting.ts
747
- function useResolvedSetting(key) {
748
- const config = useRequiredContext(ConfigContext, "useResolvedSetting", "ConfigShell");
749
- return config.resolveSetting(key);
750
- }
751
746
  var SHARE_SECTION_KEYS = [
752
747
  "itinerary",
753
748
  "rooming",
@@ -889,6 +884,504 @@ function useTravellerFormResponses(tripRef, travellerRef) {
889
884
  }, [refresh]);
890
885
  return { responses, readFailed, refresh };
891
886
  }
887
+ var ThemeAttributesContext = createContext(null);
888
+ var FAMILY_DEFAULT_FRAME = {
889
+ admin: "desk",
890
+ manager: "field",
891
+ traveller: "document"
892
+ };
893
+ var DESK_MINIMUM_CONTAINER_WIDTH_PX = 640;
894
+ function ShadowIsolationBoundary({
895
+ className,
896
+ children
897
+ }) {
898
+ const hostRef = useRef(null);
899
+ const [shadowContainer, setShadowContainer] = useState(null);
900
+ useEffect(() => {
901
+ const host = hostRef.current;
902
+ if (host === null) {
903
+ return;
904
+ }
905
+ const shadow = host.shadowRoot ?? host.attachShadow({ mode: "open" });
906
+ let container = shadow.querySelector("div[data-kf-shadow-content]");
907
+ if (container === null) {
908
+ container = document.createElement("div");
909
+ container.setAttribute("data-kf-shadow-content", "");
910
+ shadow.appendChild(container);
911
+ }
912
+ setShadowContainer(container);
913
+ }, []);
914
+ return /* @__PURE__ */ jsx("div", { ref: hostRef, className, "data-kf-isolation": "shadow", children: shadowContainer !== null ? createPortal(children, shadowContainer) : null });
915
+ }
916
+ function useDeskMinimumWidthWarning(rootRef, frame) {
917
+ useEffect(() => {
918
+ if (frame !== "desk" || !isDevelopment() || typeof ResizeObserver === "undefined") {
919
+ return;
920
+ }
921
+ const node = rootRef.current;
922
+ if (node === null) {
923
+ return;
924
+ }
925
+ let warned = false;
926
+ const observer = new ResizeObserver((entries) => {
927
+ const entry = entries[0];
928
+ if (entry === void 0 || warned) {
929
+ return;
930
+ }
931
+ if (entry.contentRect.width < DESK_MINIMUM_CONTAINER_WIDTH_PX) {
932
+ warned = true;
933
+ console.warn(
934
+ `kaafil-react-uikit: the 'desk' frame's .kf-root is narrower than the stated minimum container width (05-theming-and-branding.md \xA74.4). AppShell never auto-swaps frames \u2014 mount the manager family, or pass frame="field" explicitly, for this container.`
935
+ );
936
+ }
937
+ });
938
+ observer.observe(node);
939
+ return () => observer.disconnect();
940
+ }, [rootRef, frame]);
941
+ }
942
+ function useUnfilledConnectivitySlotWarning(frame, connectivitySlot) {
943
+ useEffect(() => {
944
+ if (frame === "field" && connectivitySlot === void 0 && isDevelopment()) {
945
+ console.warn(
946
+ `kaafil-react-uikit: AppShell's "field" frame has no connectivitySlot filled \u2014 the permanently-visible connectivity/sync region this frame reserves is empty (01-app-shell.md, briefing \xA727.2).`
947
+ );
948
+ }
949
+ }, [frame, connectivitySlot]);
950
+ }
951
+ function AppShell({
952
+ frame,
953
+ family,
954
+ isolation = "scoped",
955
+ navSlot,
956
+ actionBarSlot,
957
+ connectivitySlot,
958
+ children,
959
+ style
960
+ }) {
961
+ const themeAttributes = useContext(ThemeAttributesContext);
962
+ const rootRef = useRef(null);
963
+ const resolvedFrame = frame ?? (family !== void 0 ? FAMILY_DEFAULT_FRAME[family] : "document");
964
+ useDeskMinimumWidthWarning(rootRef, resolvedFrame);
965
+ useUnfilledConnectivitySlotWarning(resolvedFrame, connectivitySlot);
966
+ const dataAttributes = {
967
+ "data-kf-frame": resolvedFrame
968
+ };
969
+ if (family !== void 0) {
970
+ dataAttributes["data-kf-family"] = family;
971
+ }
972
+ if (themeAttributes !== null) {
973
+ dataAttributes["data-kf-theme"] = themeAttributes.theme;
974
+ if (themeAttributes.density !== void 0) {
975
+ dataAttributes["data-kf-density"] = themeAttributes.density;
976
+ }
977
+ }
978
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
979
+ resolvedFrame === "desk" && navSlot !== void 0 ? /* @__PURE__ */ jsx("div", { className: "kf-app-shell__nav", children: navSlot }) : null,
980
+ /* @__PURE__ */ jsx("div", { className: "kf-app-shell__content", children }),
981
+ resolvedFrame === "field" && actionBarSlot !== void 0 ? /* @__PURE__ */ jsx("div", { className: "kf-app-shell__action-bar", children: actionBarSlot }) : null,
982
+ resolvedFrame === "field" && connectivitySlot !== void 0 ? /* @__PURE__ */ jsx("div", { className: "kf-app-shell__connectivity", children: connectivitySlot }) : null,
983
+ /* @__PURE__ */ jsx("div", { className: "kf-app-shell__live-region", role: "status", "aria-live": "polite" })
984
+ ] });
985
+ if (isolation === "shadow") {
986
+ return /* @__PURE__ */ jsx(
987
+ "div",
988
+ {
989
+ ref: rootRef,
990
+ className: "kf-root",
991
+ "data-kf-isolation": "shadow",
992
+ ...dataAttributes,
993
+ style: tokenStyle(style),
994
+ children: /* @__PURE__ */ jsx(ShadowIsolationBoundary, { className: "kf-app-shell__shadow-host", children: content })
995
+ }
996
+ );
997
+ }
998
+ return /* @__PURE__ */ jsx(
999
+ "div",
1000
+ {
1001
+ ref: rootRef,
1002
+ className: "kf-root",
1003
+ "data-kf-isolation": "scoped",
1004
+ ...dataAttributes,
1005
+ style: tokenStyle(style),
1006
+ children: content
1007
+ }
1008
+ );
1009
+ }
1010
+ function ConfigShell({ seedConfig = {}, children }) {
1011
+ const value = useMemo(
1012
+ () => ({
1013
+ resolveSetting(key) {
1014
+ const agencyValue = seedConfig.agency?.[key];
1015
+ if (agencyValue !== void 0) {
1016
+ return agencyValue;
1017
+ }
1018
+ const tenantValue = seedConfig.tenant?.[key];
1019
+ return tenantValue === void 0 ? void 0 : tenantValue;
1020
+ }
1021
+ }),
1022
+ [seedConfig.agency, seedConfig.tenant]
1023
+ );
1024
+ return /* @__PURE__ */ jsx(ConfigContext.Provider, { value, children });
1025
+ }
1026
+ function classifyForFallback(error) {
1027
+ const classification = classifyError(error);
1028
+ if (classification.code === "RESOURCE_NOT_FOUND") {
1029
+ return { classification: "isolation", code: classification.code };
1030
+ }
1031
+ if (classification.path === "parked" || classification.unsatisfiableScheme) {
1032
+ return { classification: "parked", code: classification.code };
1033
+ }
1034
+ return { classification: "retryable", code: classification.code };
1035
+ }
1036
+ var FALLBACK_STYLE = {
1037
+ fontFamily: "system-ui, sans-serif",
1038
+ padding: "1.5rem",
1039
+ border: "1px solid #999",
1040
+ borderRadius: "4px",
1041
+ color: "#1a1a1a",
1042
+ background: "#fff"
1043
+ };
1044
+ function DefaultFallback({
1045
+ ctx,
1046
+ onRetry
1047
+ }) {
1048
+ const role = ctx.classification === "retryable" ? "status" : "alert";
1049
+ const ariaLive = ctx.classification === "retryable" ? "polite" : "assertive";
1050
+ const message = ctx.classification === "isolation" ? "Not found." : ctx.classification === "parked" ? "This action can't be completed right now." : "Something went wrong.";
1051
+ return /* @__PURE__ */ jsxs("div", { style: FALLBACK_STYLE, role, "aria-live": ariaLive, children: [
1052
+ /* @__PURE__ */ jsx("p", { style: { margin: 0 }, children: message }),
1053
+ ctx.classification === "retryable" ? /* @__PURE__ */ jsx("button", { type: "button", onClick: onRetry, style: { marginTop: "0.75rem" }, children: "Try again" }) : null
1054
+ ] });
1055
+ }
1056
+ var ErrorShell = class extends Component {
1057
+ state = { caught: null, retryKey: 0 };
1058
+ static getDerivedStateFromError(error) {
1059
+ return { caught: { error } };
1060
+ }
1061
+ handleRetry = () => {
1062
+ this.setState((current) => ({ caught: null, retryKey: current.retryKey + 1 }));
1063
+ };
1064
+ render() {
1065
+ if (this.state.caught !== null) {
1066
+ const ctx = classifyForFallback(this.state.caught.error);
1067
+ if (this.props.fallbackSlot !== void 0) {
1068
+ return this.props.fallbackSlot(ctx);
1069
+ }
1070
+ return /* @__PURE__ */ jsx(DefaultFallback, { ctx, onRetry: this.handleRetry });
1071
+ }
1072
+ return /* @__PURE__ */ jsx(Fragment, { children: this.props.children }, this.state.retryKey);
1073
+ }
1074
+ };
1075
+ var SessionExpiryBridgeContext = createContext(void 0);
1076
+ function OfflineShell({
1077
+ storage,
1078
+ blob,
1079
+ conflictResolver,
1080
+ readEntity,
1081
+ scope,
1082
+ children
1083
+ }) {
1084
+ const client = useContext(KaafilClientContext);
1085
+ const persona = usePersona();
1086
+ const session = useSession();
1087
+ const reportExpired = useContext(SessionExpiryBridgeContext);
1088
+ const [engine, setEngine] = useState(null);
1089
+ const engineRef = useRef(null);
1090
+ const derivedScope = scope ?? `${persona}:${session.agencyRef ?? "share"}`;
1091
+ useEffect(() => {
1092
+ if (session.status !== "authenticated" || client === null) {
1093
+ return;
1094
+ }
1095
+ const newEngine = client.openOffline({
1096
+ storage,
1097
+ scope: derivedScope,
1098
+ ...readEntity !== void 0 ? { readEntity } : {},
1099
+ ...conflictResolver !== void 0 ? { conflictResolver } : {},
1100
+ ...blob !== void 0 ? { blob } : {}
1101
+ // GAP flagged in this phase's report: a share session's local
1102
+ // `expiresAt` enforcement (`OpenOfflineOptions.share`,
1103
+ // `07-offline-and-sync.md §8`) needs an `expiresAt` this shell has no
1104
+ // source for yet — it is not on `useSession()`'s derived shape and no
1105
+ // prop carries it. Omitted here rather than guessed; a share engine
1106
+ // opens without the local expiry/grace/purge enforcement until that
1107
+ // value has a wire source.
1108
+ });
1109
+ engineRef.current = newEngine;
1110
+ let opened = false;
1111
+ let disposed = false;
1112
+ void newEngine.open().then(() => {
1113
+ opened = true;
1114
+ if (disposed) {
1115
+ newEngine.close();
1116
+ return;
1117
+ }
1118
+ if (engineRef.current === newEngine) setEngine(newEngine);
1119
+ }).catch(() => {
1120
+ if (!disposed && engineRef.current === newEngine) setEngine(null);
1121
+ });
1122
+ const unsubscribeSessionExpired = persona !== "share" ? newEngine.events.on("session.expired", () => reportExpired?.("session")) : void 0;
1123
+ const unsubscribeShareExpired = persona === "share" ? newEngine.events.on("share.expired", () => reportExpired?.("share")) : void 0;
1124
+ const unsubscribeShareRevoked = persona === "share" ? newEngine.events.on("share.revoked", () => reportExpired?.("share")) : void 0;
1125
+ return () => {
1126
+ unsubscribeSessionExpired?.();
1127
+ unsubscribeShareExpired?.();
1128
+ unsubscribeShareRevoked?.();
1129
+ disposed = true;
1130
+ if (opened) newEngine.close();
1131
+ if (engineRef.current === newEngine) {
1132
+ engineRef.current = null;
1133
+ }
1134
+ };
1135
+ }, [client, derivedScope, session.status, persona, reportExpired]);
1136
+ return /* @__PURE__ */ jsx(OfflineEngineContext.Provider, { value: engine, children });
1137
+ }
1138
+
1139
+ // src/core/shells/internal/jwtKind.ts
1140
+ function base64UrlDecode(segment) {
1141
+ const padded = segment.replace(/-/g, "+").replace(/_/g, "/");
1142
+ const withPadding = padded + "=".repeat((4 - padded.length % 4) % 4);
1143
+ return atob(withPadding);
1144
+ }
1145
+ function decodeManagerOrAgencyAdminKind(accessToken) {
1146
+ const segments = accessToken.split(".");
1147
+ const payloadSegment = segments[1];
1148
+ if (segments.length !== 3 || payloadSegment === void 0) {
1149
+ throw new Error(
1150
+ 'SessionShell could not parse "accessToken" as a JWT \u2014 it does not have the three dot-separated segments every manager/agencyAdmin session token has.'
1151
+ );
1152
+ }
1153
+ let claim;
1154
+ try {
1155
+ claim = JSON.parse(base64UrlDecode(payloadSegment));
1156
+ } catch {
1157
+ throw new Error(
1158
+ `SessionShell could not decode "accessToken"'s payload segment as JSON \u2014 the value handed to <KaafilUIKitProvider accessToken> is not a well-formed JWT.`
1159
+ );
1160
+ }
1161
+ const kind = claim.kind;
1162
+ if (kind === "agencyAdmin") {
1163
+ return "agencyAdmin";
1164
+ }
1165
+ if (kind === "managerSession" || kind === "manager") {
1166
+ return "managerSession";
1167
+ }
1168
+ throw new Error(
1169
+ `SessionShell read an unrecognised "kind" claim ("${String(kind)}") off "accessToken" \u2014 expected the manager or agencyAdmin session JWT shape (00-decisions.md U-003).`
1170
+ );
1171
+ }
1172
+ async function resolveCredential(props) {
1173
+ if (props.credentialResolver !== void 0) {
1174
+ const resolved = await props.credentialResolver();
1175
+ if ("shareToken" in resolved) {
1176
+ return { kind: "share", shareToken: resolved.shareToken };
1177
+ }
1178
+ const kind2 = decodeManagerOrAgencyAdminKind(resolved.accessToken) === "agencyAdmin" ? "agencyAdmin" : "manager";
1179
+ return { kind: kind2, ...resolved };
1180
+ }
1181
+ if (props.shareToken !== void 0) {
1182
+ return { kind: "share", shareToken: props.shareToken };
1183
+ }
1184
+ const kind = decodeManagerOrAgencyAdminKind(props.accessToken) === "agencyAdmin" ? "agencyAdmin" : "manager";
1185
+ return {
1186
+ kind,
1187
+ accessToken: props.accessToken,
1188
+ refreshToken: props.refreshToken,
1189
+ agencyRef: props.agencyRef
1190
+ };
1191
+ }
1192
+ function SessionShell(props) {
1193
+ const { environment = "live", onRefresh, onSessionExpired, children } = props;
1194
+ const [state, setState] = useState(null);
1195
+ const expiredCallbackFiredRef = useRef(false);
1196
+ const currentPersonaRef = useRef("manager");
1197
+ const reportExpired = useCallback(() => {
1198
+ if (expiredCallbackFiredRef.current) {
1199
+ return;
1200
+ }
1201
+ expiredCallbackFiredRef.current = true;
1202
+ onSessionExpired?.(currentPersonaRef.current);
1203
+ setState(
1204
+ (current) => current === null ? current : { ...current, session: { ...current.session, status: "expired" } }
1205
+ );
1206
+ }, [onSessionExpired]);
1207
+ const accessTokenDep = "accessToken" in props ? props.accessToken : void 0;
1208
+ const refreshTokenDep = "refreshToken" in props ? props.refreshToken : void 0;
1209
+ const agencyRefDep = "agencyRef" in props ? props.agencyRef : void 0;
1210
+ const shareTokenDep = "shareToken" in props ? props.shareToken : void 0;
1211
+ useEffect(() => {
1212
+ let cancelled = false;
1213
+ const client = new KaafilClient({ environment });
1214
+ expiredCallbackFiredRef.current = false;
1215
+ async function open() {
1216
+ const credential = await resolveCredential(props);
1217
+ if (cancelled) {
1218
+ return;
1219
+ }
1220
+ currentPersonaRef.current = credential.kind;
1221
+ if (credential.kind === "share") {
1222
+ client.share.open({ token: credential.shareToken });
1223
+ } else if (credential.kind === "agencyAdmin") {
1224
+ client.admin.open({
1225
+ accessToken: credential.accessToken,
1226
+ refreshToken: credential.refreshToken,
1227
+ agencyRef: credential.agencyRef,
1228
+ ...onRefresh !== void 0 ? { onRefresh } : {}
1229
+ });
1230
+ } else {
1231
+ client.session.open({
1232
+ accessToken: credential.accessToken,
1233
+ refreshToken: credential.refreshToken,
1234
+ agencyRef: credential.agencyRef,
1235
+ ...onRefresh !== void 0 ? { onRefresh } : {}
1236
+ });
1237
+ }
1238
+ setState({
1239
+ client,
1240
+ session: {
1241
+ status: "authenticated",
1242
+ persona: credential.kind,
1243
+ agencyRef: credential.agencyRef,
1244
+ environment
1245
+ }
1246
+ });
1247
+ }
1248
+ void open();
1249
+ return () => {
1250
+ cancelled = true;
1251
+ client.close();
1252
+ };
1253
+ }, [environment, accessTokenDep, refreshTokenDep, agencyRefDep, shareTokenDep]);
1254
+ if (state === null) {
1255
+ return /* @__PURE__ */ jsx(SessionExpiryBridgeContext.Provider, { value: reportExpired, children: /* @__PURE__ */ jsx(
1256
+ SessionContext.Provider,
1257
+ {
1258
+ value: { status: "opening", persona: "manager", agencyRef: void 0, environment },
1259
+ children
1260
+ }
1261
+ ) });
1262
+ }
1263
+ return /* @__PURE__ */ jsx(SessionExpiryBridgeContext.Provider, { value: reportExpired, children: /* @__PURE__ */ jsx(KaafilClientContext.Provider, { value: state.client, children: /* @__PURE__ */ jsx(SessionContext.Provider, { value: state.session, children }) }) });
1264
+ }
1265
+ function emit(onEvent, event) {
1266
+ if (onEvent === void 0) {
1267
+ return;
1268
+ }
1269
+ try {
1270
+ onEvent(event);
1271
+ } catch {
1272
+ }
1273
+ }
1274
+ function TelemetryShell({ onEvent, children }) {
1275
+ const session = useSession();
1276
+ const serverTime = useServerTime();
1277
+ const previousStatusRef = useRef(void 0);
1278
+ useEffect(() => {
1279
+ const previousStatus = previousStatusRef.current;
1280
+ if (serverTime.sinceLastSync !== void 0) {
1281
+ if (session.status === "authenticated" && previousStatus !== "authenticated") {
1282
+ emit(onEvent, {
1283
+ type: "session.opened",
1284
+ timestamp: serverTime.now(),
1285
+ persona: session.persona
1286
+ });
1287
+ }
1288
+ if (session.status === "expired" && previousStatus !== "expired") {
1289
+ emit(onEvent, {
1290
+ type: "session.expired",
1291
+ timestamp: serverTime.now(),
1292
+ persona: session.persona
1293
+ });
1294
+ }
1295
+ }
1296
+ previousStatusRef.current = session.status;
1297
+ }, [session.status, serverTime.sinceLastSync]);
1298
+ return /* @__PURE__ */ jsx(Fragment$1, { children });
1299
+ }
1300
+ function resolveSystemTheme() {
1301
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
1302
+ return "light";
1303
+ }
1304
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1305
+ }
1306
+ function ThemeShell({ theme = "system", density, children }) {
1307
+ const [systemTheme, setSystemTheme] = useState(
1308
+ () => theme === "system" ? resolveSystemTheme() : theme
1309
+ );
1310
+ useEffect(() => {
1311
+ if (theme !== "system" || typeof window === "undefined" || typeof window.matchMedia !== "function") {
1312
+ return;
1313
+ }
1314
+ const query = window.matchMedia("(prefers-color-scheme: dark)");
1315
+ const listener = (event) => {
1316
+ setSystemTheme(event.matches ? "dark" : "light");
1317
+ };
1318
+ query.addEventListener("change", listener);
1319
+ return () => query.removeEventListener("change", listener);
1320
+ }, [theme]);
1321
+ const resolvedTheme = theme === "system" ? systemTheme : theme;
1322
+ const value = useMemo(() => ({ theme: resolvedTheme, density }), [resolvedTheme, density]);
1323
+ return /* @__PURE__ */ jsx(ThemeAttributesContext.Provider, { value, children });
1324
+ }
1325
+ var DefaultCapabilityViewsContext = createContext({});
1326
+ function useDefaultCapabilityViews() {
1327
+ return useContext(DefaultCapabilityViewsContext);
1328
+ }
1329
+ var BuildShareUrlContext = createContext(null);
1330
+ function useBuildShareUrl() {
1331
+ return useContext(BuildShareUrlContext);
1332
+ }
1333
+ function KaafilUIKitProvider(props) {
1334
+ const {
1335
+ environment,
1336
+ onSessionExpired,
1337
+ density,
1338
+ theme,
1339
+ brand,
1340
+ locale,
1341
+ isolation,
1342
+ onEvent,
1343
+ storage,
1344
+ blob,
1345
+ conflictResolver,
1346
+ readEntity,
1347
+ offlineScope,
1348
+ seedConfig,
1349
+ defaultCapabilityViews,
1350
+ buildShareUrl,
1351
+ children,
1352
+ ...credential
1353
+ } = props;
1354
+ const inner = /* @__PURE__ */ jsx(BuildShareUrlContext.Provider, { value: buildShareUrl ?? null, children: /* @__PURE__ */ jsx(DefaultCapabilityViewsContext.Provider, { value: defaultCapabilityViews ?? {}, children: /* @__PURE__ */ jsx(ConfigShell, { seedConfig, children: /* @__PURE__ */ jsx(
1355
+ BrandShell,
1356
+ {
1357
+ appName: brand?.appName,
1358
+ logo: brand?.logo,
1359
+ logoMark: brand?.logoMark,
1360
+ favicon: brand?.favicon,
1361
+ headStrategy: brand?.headStrategy,
1362
+ children: /* @__PURE__ */ jsx(TelemetryShell, { onEvent, children })
1363
+ }
1364
+ ) }) }) });
1365
+ return /* @__PURE__ */ jsx(ErrorShell, { children: /* @__PURE__ */ jsx(ThemeShell, { theme, density, children: /* @__PURE__ */ jsx(I18nShell, { locale, children: /* @__PURE__ */ jsx(AppShell, { isolation, children: /* @__PURE__ */ jsx(
1366
+ SessionShell,
1367
+ {
1368
+ ...credential,
1369
+ environment,
1370
+ onSessionExpired,
1371
+ children: storage !== void 0 ? /* @__PURE__ */ jsx(
1372
+ OfflineShell,
1373
+ {
1374
+ storage,
1375
+ blob,
1376
+ conflictResolver,
1377
+ readEntity,
1378
+ scope: offlineScope,
1379
+ children: inner
1380
+ }
1381
+ ) : inner
1382
+ }
1383
+ ) }) }) }) });
1384
+ }
892
1385
 
893
1386
  // src/core/routing/deepLinks.ts
894
1387
  var CLOSEOUT_ACTION_TAB = {
@@ -970,6 +1463,6 @@ var TRIP_TAB_KEY_SET = {
970
1463
  forms: true
971
1464
  };
972
1465
 
973
- export { CLOSEOUT_ACTION_TAB, familySafeSections, fromSearchParams, isTripTabKey, resolveBlockerDeepLink, toSearchParams, useAgencyAdminMe, useAgencyChecklistTemplates, useAgencyForms, useAgencyJourneyTriggers, useAgencyManagerProfile, useAgencySettings, useAgencyTrip, useAgencyTripManifest, useAgencyTrips, useBookings, useCapabilityStatuses, useDirectories, usePaginatedList, useResolvedSetting, useShareLinks, useTravellerFormResponses };
974
- //# sourceMappingURL=chunk-VAJ4D7EF.js.map
975
- //# sourceMappingURL=chunk-VAJ4D7EF.js.map
1466
+ export { AppShell, CLOSEOUT_ACTION_TAB, ConfigShell, ErrorShell, KaafilUIKitProvider, OfflineShell, SessionShell, TelemetryShell, ThemeShell, familySafeSections, fromSearchParams, isTripTabKey, resolveBlockerDeepLink, toSearchParams, useAgencyAdminMe, useAgencyChecklistTemplates, useAgencyForms, useAgencyJourneyTriggers, useAgencyManagerProfile, useAgencySettings, useAgencyTrip, useAgencyTripManifest, useAgencyTrips, useBookings, useBuildShareUrl, useCapabilityStatuses, useDefaultCapabilityViews, useDirectories, usePaginatedList, useShareLinks, useTravellerFormResponses };
1467
+ //# sourceMappingURL=chunk-FEJL6GKJ.js.map
1468
+ //# sourceMappingURL=chunk-FEJL6GKJ.js.map