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