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.
- package/CHANGELOG.md +28 -5
- package/dist/admin/index.cjs +355 -353
- package/dist/admin/index.cjs.map +1 -1
- package/dist/admin/index.js +16 -14
- package/dist/admin/index.js.map +1 -1
- package/dist/{chunk-EP4VCID7.js → chunk-3E2FN5JJ.js} +3 -3
- package/dist/{chunk-EP4VCID7.js.map → chunk-3E2FN5JJ.js.map} +1 -1
- package/dist/{chunk-TPBKRXEB.cjs → chunk-3RZDHDEY.cjs} +17 -17
- package/dist/{chunk-TPBKRXEB.cjs.map → chunk-3RZDHDEY.cjs.map} +1 -1
- package/dist/chunk-62HCQWZL.cjs +12 -0
- package/dist/chunk-62HCQWZL.cjs.map +1 -0
- package/dist/{chunk-ABWNV4N4.js → chunk-6FUVFVYY.js} +97 -4
- package/dist/chunk-6FUVFVYY.js.map +1 -0
- package/dist/{chunk-BUE2GJDN.js → chunk-762SWUEG.js} +3 -3
- package/dist/{chunk-BUE2GJDN.js.map → chunk-762SWUEG.js.map} +1 -1
- package/dist/{chunk-CCJ5XNWY.cjs → chunk-CK3L6Y43.cjs} +98 -2
- package/dist/chunk-CK3L6Y43.cjs.map +1 -0
- package/dist/{chunk-XBYLSJOS.cjs → chunk-DI77H7ED.cjs} +83 -83
- package/dist/{chunk-XBYLSJOS.cjs.map → chunk-DI77H7ED.cjs.map} +1 -1
- package/dist/{chunk-5DVTAEQC.js → chunk-EDLIJSMR.js} +5 -5
- package/dist/{chunk-5DVTAEQC.js.map → chunk-EDLIJSMR.js.map} +1 -1
- package/dist/{chunk-DABF4PUI.js → chunk-ITGVRN2S.js} +3 -3
- package/dist/{chunk-DABF4PUI.js.map → chunk-ITGVRN2S.js.map} +1 -1
- package/dist/{chunk-DUZG6A22.cjs → chunk-LO3GQFQ6.cjs} +4 -4
- package/dist/{chunk-DUZG6A22.cjs.map → chunk-LO3GQFQ6.cjs.map} +1 -1
- package/dist/{chunk-4LZXDRAD.cjs → chunk-QD4Z5DRT.cjs} +12 -12
- package/dist/{chunk-4LZXDRAD.cjs.map → chunk-QD4Z5DRT.cjs.map} +1 -1
- package/dist/{chunk-VAJ4D7EF.js → chunk-QMUIZHJO.js} +510 -16
- package/dist/chunk-QMUIZHJO.js.map +1 -0
- package/dist/{chunk-TV5QTT44.js → chunk-RW6TJMRK.js} +3 -3
- package/dist/{chunk-TV5QTT44.js.map → chunk-RW6TJMRK.js.map} +1 -1
- package/dist/{chunk-2D4Y4LOT.cjs → chunk-V6UQ6GIM.cjs} +551 -48
- package/dist/chunk-V6UQ6GIM.cjs.map +1 -0
- package/dist/chunk-XU7GP53M.js +10 -0
- package/dist/chunk-XU7GP53M.js.map +1 -0
- package/dist/{chunk-DDSNXKFN.cjs → chunk-XYIOM75D.cjs} +9 -9
- package/dist/{chunk-DDSNXKFN.cjs.map → chunk-XYIOM75D.cjs.map} +1 -1
- package/dist/core/index.cjs +153 -613
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +36 -1
- package/dist/core/index.d.ts +36 -1
- package/dist/core/index.js +21 -511
- package/dist/core/index.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/manager/index.cjs +239 -239
- package/dist/manager/index.js +6 -6
- package/dist/traveller/index.cjs +35 -35
- package/dist/traveller/index.js +5 -5
- package/package.json +1 -1
- package/dist/chunk-2D4Y4LOT.cjs.map +0 -1
- package/dist/chunk-2JABR4FF.js +0 -105
- package/dist/chunk-2JABR4FF.js.map +0 -1
- package/dist/chunk-ABWNV4N4.js.map +0 -1
- package/dist/chunk-CCJ5XNWY.cjs.map +0 -1
- package/dist/chunk-KDGBJPPZ.cjs +0 -110
- package/dist/chunk-KDGBJPPZ.cjs.map +0 -1
- package/dist/chunk-VAJ4D7EF.js.map +0 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { createDomainHook, useSnapshotList, classifyError } from './chunk-
|
|
2
|
-
import { useServerTime } from './chunk-
|
|
3
|
-
import { useOfflineMutation, useKaafilClient, useCapabilityRows, deriveCapabilityTriple,
|
|
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 [
|
|
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,
|
|
974
|
-
//# sourceMappingURL=chunk-
|
|
975
|
-
//# sourceMappingURL=chunk-
|
|
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
|