@patientos/website-kit 0.2.6 → 0.2.8
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/README.md +54 -2
- package/dist/booking-block-D-UOAPx3.d.ts +74 -0
- package/dist/cart-block-B-ytc9us.d.ts +20 -0
- package/dist/cart-button-BCLFittB.d.ts +24 -0
- package/dist/cart-storage.d.ts +110 -0
- package/dist/cart-storage.js +39 -0
- package/dist/checkout-block-BeXwpqYe.d.ts +24 -0
- package/dist/chunk-2CZFJOKK.js +79 -0
- package/dist/{chunk-ZYX4TXBN.js → chunk-3T7UWKAH.js} +4 -310
- package/dist/{chunk-7ZOQ6UKG.js → chunk-4GKSSM5N.js} +578 -3163
- package/dist/chunk-4NF7SSIX.js +367 -0
- package/dist/chunk-7BUDUYXN.js +80 -0
- package/dist/chunk-FZ4WKWIE.js +310 -0
- package/dist/{chunk-ZZFBTLR4.js → chunk-HTVKKLWI.js} +20 -5
- package/dist/chunk-IOC6QLB7.js +159 -0
- package/dist/chunk-S3ZQQOQT.js +2057 -0
- package/dist/chunk-UTAMB2SK.js +214 -0
- package/dist/chunk-WQSJ46TP.js +19 -0
- package/dist/chunk-ZNH6TSYP.js +167 -0
- package/dist/index.d.ts +12 -32
- package/dist/index.js +88 -26
- package/dist/islands-impl/cart-button.d.ts +19 -0
- package/dist/islands-impl/cart-button.js +13 -0
- package/dist/islands-impl/cart.d.ts +31 -0
- package/dist/islands-impl/cart.js +11 -0
- package/dist/islands-impl/checkout.d.ts +6 -0
- package/dist/islands-impl/checkout.js +13 -0
- package/dist/islands-impl/store.d.ts +30 -0
- package/dist/islands-impl/store.js +16 -0
- package/dist/islands-impl.d.ts +14 -10
- package/dist/islands-impl.js +22 -7
- package/dist/islands-registry.js +13 -4
- package/dist/{portal-account-8uQc_Ncx.d.ts → portal-account-DRJyr3nz.d.ts} +1 -82
- package/dist/{portal-account.client-BBe_q9yC.d.ts → portal-account.client-Bke4NsgW.d.ts} +2 -1
- package/dist/portal-booking-client.d.ts +3 -2
- package/dist/portal-booking-client.js +2 -1
- package/dist/portal-client-my3q5GME.d.ts +83 -0
- package/dist/store-block-CFxnsfKU.d.ts +37 -0
- package/dist/store-catalog-p7TFOpfk.d.ts +78 -0
- package/dist/store-catalog.d.ts +3 -0
- package/dist/store-catalog.js +11 -0
- package/dist/website-kit.css +193 -0
- package/package.json +31 -1
- package/dist/checkout-block-BdpPIuQ5.d.ts +0 -136
|
@@ -1,315 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createPortalClient
|
|
3
|
+
} from "./chunk-FZ4WKWIE.js";
|
|
4
|
+
|
|
1
5
|
// src/portal-booking.client.tsx
|
|
2
6
|
import * as React4 from "react";
|
|
3
7
|
|
|
4
8
|
// src/portal-shared.client.tsx
|
|
5
9
|
import * as React from "react";
|
|
6
|
-
|
|
7
|
-
// src/portal-client.ts
|
|
8
|
-
var PORTAL_WHOAMI_PATH = "/portal/api/whoami";
|
|
9
|
-
var PORTAL_MAGIC_LINK_PATH = "/portal/api/magic-link";
|
|
10
|
-
var PORTAL_PREFILL_PATH = "/portal/api/prefill";
|
|
11
|
-
var PORTAL_PUBLIC_CLAIM_PATH = "/portal/api/public-claim";
|
|
12
|
-
var PORTAL_APPLICATION_HANDOFF_PATH = "/portal/api/application-run-handoffs";
|
|
13
|
-
var PORTAL_SESSION_EXCHANGE_PATH = "/portal/api/session-exchange";
|
|
14
|
-
var PORTAL_RETURN_PATH = "/portal/return";
|
|
15
|
-
var PORTAL_HANDOFF_FRAGMENT_PREFIX = "#patientos-handoff=";
|
|
16
|
-
var BRIDGE_TIMEOUT_MS = 15e3;
|
|
17
|
-
var BRIDGE_PROBE_TIMEOUT_MS = 8e3;
|
|
18
|
-
var PORTAL_BEARER_STORAGE_PREFIX = "patientos.portal.bearer:";
|
|
19
|
-
var parkedHandoffPayload = null;
|
|
20
|
-
var pendingHandoffTokens = /* @__PURE__ */ new Map();
|
|
21
|
-
var pendingHandoffExchanges = /* @__PURE__ */ new Map();
|
|
22
|
-
var memoryBearers = /* @__PURE__ */ new Map();
|
|
23
|
-
function onLocalhostDevPage() {
|
|
24
|
-
if (typeof window === "undefined") return false;
|
|
25
|
-
return window.location.protocol === "http:" && window.location.hostname === "localhost" && window.location.port !== "";
|
|
26
|
-
}
|
|
27
|
-
function localhostHandoffEnabled(apiOrigin) {
|
|
28
|
-
return apiOrigin !== "" && onLocalhostDevPage();
|
|
29
|
-
}
|
|
30
|
-
function bearerStorage() {
|
|
31
|
-
try {
|
|
32
|
-
return typeof window === "undefined" ? null : window.sessionStorage;
|
|
33
|
-
} catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function bearerStorageKey(apiOrigin) {
|
|
38
|
-
return `${PORTAL_BEARER_STORAGE_PREFIX}${apiOrigin}`;
|
|
39
|
-
}
|
|
40
|
-
function readBearer(apiOrigin) {
|
|
41
|
-
const memory = memoryBearers.get(apiOrigin);
|
|
42
|
-
if (memory) return memory;
|
|
43
|
-
try {
|
|
44
|
-
const stored = bearerStorage()?.getItem(bearerStorageKey(apiOrigin)) ?? null;
|
|
45
|
-
if (!stored || stored.length > 4096) {
|
|
46
|
-
clearBearer(apiOrigin);
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
memoryBearers.set(apiOrigin, stored);
|
|
50
|
-
return stored;
|
|
51
|
-
} catch {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function writeBearer(apiOrigin, token) {
|
|
56
|
-
memoryBearers.set(apiOrigin, token);
|
|
57
|
-
try {
|
|
58
|
-
bearerStorage()?.setItem(bearerStorageKey(apiOrigin), token);
|
|
59
|
-
} catch {
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function clearBearer(apiOrigin) {
|
|
63
|
-
memoryBearers.delete(apiOrigin);
|
|
64
|
-
try {
|
|
65
|
-
bearerStorage()?.removeItem(bearerStorageKey(apiOrigin));
|
|
66
|
-
} catch {
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
function decodeBase64UrlJson(raw) {
|
|
70
|
-
const b64 = raw.replace(/-/g, "+").replace(/_/g, "/");
|
|
71
|
-
const binary = atob(b64.padEnd(Math.ceil(b64.length / 4) * 4, "="));
|
|
72
|
-
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
73
|
-
return JSON.parse(new TextDecoder().decode(bytes));
|
|
74
|
-
}
|
|
75
|
-
function consumePortalHandoffFragment() {
|
|
76
|
-
if (!onLocalhostDevPage()) return;
|
|
77
|
-
const raw = window.location.hash;
|
|
78
|
-
if (!raw.startsWith(PORTAL_HANDOFF_FRAGMENT_PREFIX)) return;
|
|
79
|
-
let payload = null;
|
|
80
|
-
try {
|
|
81
|
-
const decoded = decodeBase64UrlJson(raw.slice(PORTAL_HANDOFF_FRAGMENT_PREFIX.length));
|
|
82
|
-
const candidate = decoded;
|
|
83
|
-
if (typeof candidate?.token === "string" && candidate.token.length > 0 && candidate.token.length <= 512 && typeof candidate.returnHash === "string") {
|
|
84
|
-
payload = { token: candidate.token, returnHash: candidate.returnHash };
|
|
85
|
-
}
|
|
86
|
-
} catch {
|
|
87
|
-
}
|
|
88
|
-
const restoredHash = payload?.returnHash ? `#${payload.returnHash}` : "";
|
|
89
|
-
window.history.replaceState(
|
|
90
|
-
window.history.state,
|
|
91
|
-
"",
|
|
92
|
-
`${window.location.pathname}${window.location.search}${restoredHash}`
|
|
93
|
-
);
|
|
94
|
-
if (payload) parkedHandoffPayload = payload;
|
|
95
|
-
}
|
|
96
|
-
function adoptParkedHandoff(apiOrigin) {
|
|
97
|
-
const payload = parkedHandoffPayload;
|
|
98
|
-
if (!payload || !localhostHandoffEnabled(apiOrigin)) return;
|
|
99
|
-
parkedHandoffPayload = null;
|
|
100
|
-
clearBearer(apiOrigin);
|
|
101
|
-
pendingHandoffTokens.set(apiOrigin, payload.token);
|
|
102
|
-
}
|
|
103
|
-
async function exchangePortalHandoff(apiOrigin, fetchImpl) {
|
|
104
|
-
const pending = pendingHandoffExchanges.get(apiOrigin);
|
|
105
|
-
if (pending) return pending;
|
|
106
|
-
const oneTimeToken = pendingHandoffTokens.get(apiOrigin);
|
|
107
|
-
if (!oneTimeToken) return readBearer(apiOrigin);
|
|
108
|
-
pendingHandoffTokens.delete(apiOrigin);
|
|
109
|
-
const exchange = (async () => {
|
|
110
|
-
const controller = new AbortController();
|
|
111
|
-
const timer = setTimeout(() => controller.abort(), BRIDGE_PROBE_TIMEOUT_MS);
|
|
112
|
-
try {
|
|
113
|
-
const response = await fetchImpl(`${apiOrigin}${PORTAL_SESSION_EXCHANGE_PATH}`, {
|
|
114
|
-
...PORTAL_REQUEST_INIT,
|
|
115
|
-
method: "POST",
|
|
116
|
-
headers: { accept: "application/json", "content-type": "application/json" },
|
|
117
|
-
body: JSON.stringify({ token: oneTimeToken }),
|
|
118
|
-
signal: controller.signal
|
|
119
|
-
});
|
|
120
|
-
const result = await readPortalResult(response);
|
|
121
|
-
const bearer = result.ok && typeof result.data?.token === "string" ? result.data.token : null;
|
|
122
|
-
if (!bearer) return null;
|
|
123
|
-
writeBearer(apiOrigin, bearer);
|
|
124
|
-
return bearer;
|
|
125
|
-
} catch {
|
|
126
|
-
pendingHandoffTokens.set(apiOrigin, oneTimeToken);
|
|
127
|
-
return null;
|
|
128
|
-
} finally {
|
|
129
|
-
clearTimeout(timer);
|
|
130
|
-
}
|
|
131
|
-
})();
|
|
132
|
-
pendingHandoffExchanges.set(apiOrigin, exchange);
|
|
133
|
-
try {
|
|
134
|
-
return await exchange;
|
|
135
|
-
} finally {
|
|
136
|
-
if (pendingHandoffExchanges.get(apiOrigin) === exchange) {
|
|
137
|
-
pendingHandoffExchanges.delete(apiOrigin);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
var PORTAL_REQUEST_INIT = {
|
|
142
|
-
credentials: "include",
|
|
143
|
-
cache: "no-store"
|
|
144
|
-
};
|
|
145
|
-
function normalizePortalApiOrigin(raw) {
|
|
146
|
-
let url;
|
|
147
|
-
try {
|
|
148
|
-
url = new URL(raw.trim());
|
|
149
|
-
} catch {
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
const loopback = url.hostname === "localhost" || url.hostname === "127.0.0.1";
|
|
153
|
-
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) return null;
|
|
154
|
-
if (url.username || url.password || url.search || url.hash) return null;
|
|
155
|
-
if (url.pathname !== "/" && url.pathname !== "") return null;
|
|
156
|
-
return url.origin;
|
|
157
|
-
}
|
|
158
|
-
function encodePortalReturnTarget(url) {
|
|
159
|
-
const bytes = new TextEncoder().encode(url);
|
|
160
|
-
let binary = "";
|
|
161
|
-
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
162
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
163
|
-
}
|
|
164
|
-
function sameOriginAvailable(portalOrigin) {
|
|
165
|
-
if (typeof window === "undefined") return false;
|
|
166
|
-
if (portalOrigin == null) {
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
const normalizedOrigin = portalOrigin.trim().replace(/\/+$/, "");
|
|
170
|
-
return normalizedOrigin !== "" && window.location.origin === normalizedOrigin;
|
|
171
|
-
}
|
|
172
|
-
async function readPortalResult(response) {
|
|
173
|
-
if (!response.ok) {
|
|
174
|
-
let error = null;
|
|
175
|
-
let body;
|
|
176
|
-
try {
|
|
177
|
-
const parsed = await response.json();
|
|
178
|
-
if (typeof parsed?.error === "string") error = parsed.error;
|
|
179
|
-
body = parsed;
|
|
180
|
-
} catch {
|
|
181
|
-
}
|
|
182
|
-
return { ok: false, status: response.status, error, body };
|
|
183
|
-
}
|
|
184
|
-
try {
|
|
185
|
-
return { ok: true, data: await response.json() };
|
|
186
|
-
} catch {
|
|
187
|
-
return { ok: false, status: null, error: null };
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
function createPortalClient(config = {}) {
|
|
191
|
-
const rawOrigin = config.apiOrigin?.trim() ?? "";
|
|
192
|
-
const normalizedOrigin = rawOrigin ? normalizePortalApiOrigin(rawOrigin) : "";
|
|
193
|
-
if (rawOrigin && !normalizedOrigin) {
|
|
194
|
-
console.error("[portal] ignoring a malformed portal API origin", rawOrigin);
|
|
195
|
-
}
|
|
196
|
-
const apiOrigin = normalizedOrigin ?? "";
|
|
197
|
-
const portalHref = config.portalUrl?.trim() ?? "";
|
|
198
|
-
const available = apiOrigin !== "" || sameOriginAvailable(config.portalOrigin);
|
|
199
|
-
const fetchImpl = config.fetchImpl ?? ((input, init) => globalThis.fetch(input, init));
|
|
200
|
-
const handoffEnabled = localhostHandoffEnabled(apiOrigin);
|
|
201
|
-
consumePortalHandoffFragment();
|
|
202
|
-
adoptParkedHandoff(apiOrigin);
|
|
203
|
-
const url = (path) => apiOrigin && path.startsWith("/") ? apiOrigin + path : path;
|
|
204
|
-
const fetchWithSession = async (input, init) => {
|
|
205
|
-
let belongsToApiOrigin = false;
|
|
206
|
-
if (handoffEnabled) {
|
|
207
|
-
try {
|
|
208
|
-
belongsToApiOrigin = new URL(input, window.location.href).origin === apiOrigin;
|
|
209
|
-
} catch {
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
const bearer = belongsToApiOrigin ? await exchangePortalHandoff(apiOrigin, fetchImpl) : null;
|
|
213
|
-
if (!bearer) return fetchImpl(input, init);
|
|
214
|
-
const headers = new Headers(init?.headers);
|
|
215
|
-
headers.set("authorization", `Bearer ${bearer}`);
|
|
216
|
-
return fetchImpl(input, { ...init, headers });
|
|
217
|
-
};
|
|
218
|
-
async function request(path, init, timeoutMs = BRIDGE_TIMEOUT_MS) {
|
|
219
|
-
const controller = new AbortController();
|
|
220
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
221
|
-
try {
|
|
222
|
-
const response = await fetchWithSession(url(path), {
|
|
223
|
-
...PORTAL_REQUEST_INIT,
|
|
224
|
-
...init,
|
|
225
|
-
signal: controller.signal
|
|
226
|
-
});
|
|
227
|
-
const result = await readPortalResult(response);
|
|
228
|
-
if (handoffEnabled) {
|
|
229
|
-
const body = result.ok ? result.data : null;
|
|
230
|
-
if (path === "/portal/api/sign-out" && result.ok || !result.ok && result.status === 401 || body?.signedIn === false) {
|
|
231
|
-
clearBearer(apiOrigin);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return result;
|
|
235
|
-
} catch {
|
|
236
|
-
return { ok: false, status: null, error: null };
|
|
237
|
-
} finally {
|
|
238
|
-
clearTimeout(timer);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
const get = (path) => request(path, { headers: { accept: "application/json" } });
|
|
242
|
-
const send = (path, method, body) => request(path, {
|
|
243
|
-
method,
|
|
244
|
-
headers: { accept: "application/json", "content-type": "application/json" },
|
|
245
|
-
body: JSON.stringify(body ?? {})
|
|
246
|
-
});
|
|
247
|
-
const del = (path) => request(path, { method: "DELETE", headers: { accept: "application/json" } });
|
|
248
|
-
const returnPath = (targetUrl) => {
|
|
249
|
-
if (typeof window === "undefined") return "/";
|
|
250
|
-
let target = targetUrl ?? window.location.href;
|
|
251
|
-
try {
|
|
252
|
-
const parsed2 = new URL(target, window.location.href);
|
|
253
|
-
if (parsed2.origin !== window.location.origin) target = window.location.href;
|
|
254
|
-
else target = parsed2.href;
|
|
255
|
-
} catch {
|
|
256
|
-
target = window.location.href;
|
|
257
|
-
}
|
|
258
|
-
const parsed = new URL(target);
|
|
259
|
-
const path = `${parsed.pathname || "/"}${parsed.search}${parsed.hash}`;
|
|
260
|
-
if (!apiOrigin && !targetUrl) return path;
|
|
261
|
-
return `${PORTAL_RETURN_PATH}?to=${encodePortalReturnTarget(target)}`;
|
|
262
|
-
};
|
|
263
|
-
return {
|
|
264
|
-
available,
|
|
265
|
-
apiOrigin,
|
|
266
|
-
portalHref,
|
|
267
|
-
url,
|
|
268
|
-
fetch: fetchWithSession,
|
|
269
|
-
async whoAmI() {
|
|
270
|
-
if (!available) return { signedIn: false };
|
|
271
|
-
const result = await request(
|
|
272
|
-
PORTAL_WHOAMI_PATH,
|
|
273
|
-
{ headers: { accept: "application/json" } },
|
|
274
|
-
BRIDGE_PROBE_TIMEOUT_MS
|
|
275
|
-
);
|
|
276
|
-
return result.ok && result.data?.signedIn === true ? result.data : { signedIn: false };
|
|
277
|
-
},
|
|
278
|
-
async requestMagicLink(email, redirect) {
|
|
279
|
-
if (!available) return false;
|
|
280
|
-
const result = await send(PORTAL_MAGIC_LINK_PATH, "POST", {
|
|
281
|
-
email,
|
|
282
|
-
redirect: redirect ?? returnPath()
|
|
283
|
-
});
|
|
284
|
-
return result.ok && result.data?.ok === true;
|
|
285
|
-
},
|
|
286
|
-
async prefill() {
|
|
287
|
-
if (!available) return null;
|
|
288
|
-
const result = await get(PORTAL_PREFILL_PATH);
|
|
289
|
-
return result.ok ? result.data : null;
|
|
290
|
-
},
|
|
291
|
-
async mintPublicClaim() {
|
|
292
|
-
if (!available) return null;
|
|
293
|
-
const result = await send(PORTAL_PUBLIC_CLAIM_PATH, "POST", {});
|
|
294
|
-
return result.ok ? result.data : null;
|
|
295
|
-
},
|
|
296
|
-
async redeemApplicationRunHandoff(handoffId) {
|
|
297
|
-
if (!available) return null;
|
|
298
|
-
const result = await send(
|
|
299
|
-
`${PORTAL_APPLICATION_HANDOFF_PATH}/${encodeURIComponent(handoffId)}/redeem`,
|
|
300
|
-
"POST",
|
|
301
|
-
{}
|
|
302
|
-
);
|
|
303
|
-
return result.ok ? result.data : null;
|
|
304
|
-
},
|
|
305
|
-
returnPath,
|
|
306
|
-
get,
|
|
307
|
-
send,
|
|
308
|
-
delete: del
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// src/portal-shared.client.tsx
|
|
313
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
314
11
|
var defaultPortalClient = createPortalClient();
|
|
315
12
|
var PORTAL_SIGN_OUT_URL = "/portal/api/sign-out";
|
|
@@ -1460,9 +1157,6 @@ export {
|
|
|
1460
1157
|
FlowErrorSummary,
|
|
1461
1158
|
focusField,
|
|
1462
1159
|
FlowLoading,
|
|
1463
|
-
normalizePortalApiOrigin,
|
|
1464
|
-
encodePortalReturnTarget,
|
|
1465
|
-
createPortalClient,
|
|
1466
1160
|
PatientOSApiError,
|
|
1467
1161
|
patientos,
|
|
1468
1162
|
persistClaimToken,
|