@patientos/website-kit 0.2.4 → 0.2.6
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 +39 -0
- package/dist/{checkout-block-Cog4H4I4.d.ts → checkout-block-BdpPIuQ5.d.ts} +23 -9
- package/dist/{chunk-S7U2HOV7.js → chunk-7ZOQ6UKG.js} +2515 -439
- package/dist/{chunk-BHVDY6SC.js → chunk-ZYX4TXBN.js} +200 -13
- package/dist/{chunk-Y67WZPV5.js → chunk-ZZFBTLR4.js} +1 -1
- package/dist/index.d.ts +32 -4
- package/dist/index.js +39 -14
- package/dist/islands-impl.d.ts +7 -6
- package/dist/islands-impl.js +2 -2
- package/dist/islands-registry.js +3 -3
- package/dist/{portal-account-pI1cWgsT.d.ts → portal-account-8uQc_Ncx.d.ts} +8 -2
- package/dist/{portal-account.client-DPRRmfCJ.d.ts → portal-account.client-BBe_q9yC.d.ts} +1 -1
- package/dist/portal-booking-client.d.ts +3 -2
- package/dist/portal-booking-client.js +1 -1
- package/dist/website-kit.css +3 -1
- package/package.json +3 -2
|
@@ -9,9 +9,135 @@ var PORTAL_WHOAMI_PATH = "/portal/api/whoami";
|
|
|
9
9
|
var PORTAL_MAGIC_LINK_PATH = "/portal/api/magic-link";
|
|
10
10
|
var PORTAL_PREFILL_PATH = "/portal/api/prefill";
|
|
11
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";
|
|
12
14
|
var PORTAL_RETURN_PATH = "/portal/return";
|
|
15
|
+
var PORTAL_HANDOFF_FRAGMENT_PREFIX = "#patientos-handoff=";
|
|
13
16
|
var BRIDGE_TIMEOUT_MS = 15e3;
|
|
14
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
|
+
}
|
|
15
141
|
var PORTAL_REQUEST_INIT = {
|
|
16
142
|
credentials: "include",
|
|
17
143
|
cache: "no-store"
|
|
@@ -71,17 +197,41 @@ function createPortalClient(config = {}) {
|
|
|
71
197
|
const portalHref = config.portalUrl?.trim() ?? "";
|
|
72
198
|
const available = apiOrigin !== "" || sameOriginAvailable(config.portalOrigin);
|
|
73
199
|
const fetchImpl = config.fetchImpl ?? ((input, init) => globalThis.fetch(input, init));
|
|
200
|
+
const handoffEnabled = localhostHandoffEnabled(apiOrigin);
|
|
201
|
+
consumePortalHandoffFragment();
|
|
202
|
+
adoptParkedHandoff(apiOrigin);
|
|
74
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
|
+
};
|
|
75
218
|
async function request(path, init, timeoutMs = BRIDGE_TIMEOUT_MS) {
|
|
76
219
|
const controller = new AbortController();
|
|
77
220
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
78
221
|
try {
|
|
79
|
-
const response = await
|
|
222
|
+
const response = await fetchWithSession(url(path), {
|
|
80
223
|
...PORTAL_REQUEST_INIT,
|
|
81
224
|
...init,
|
|
82
225
|
signal: controller.signal
|
|
83
226
|
});
|
|
84
|
-
|
|
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;
|
|
85
235
|
} catch {
|
|
86
236
|
return { ok: false, status: null, error: null };
|
|
87
237
|
} finally {
|
|
@@ -95,18 +245,27 @@ function createPortalClient(config = {}) {
|
|
|
95
245
|
body: JSON.stringify(body ?? {})
|
|
96
246
|
});
|
|
97
247
|
const del = (path) => request(path, { method: "DELETE", headers: { accept: "application/json" } });
|
|
98
|
-
const returnPath = () => {
|
|
248
|
+
const returnPath = (targetUrl) => {
|
|
99
249
|
if (typeof window === "undefined") return "/";
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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)}`;
|
|
103
262
|
};
|
|
104
263
|
return {
|
|
105
264
|
available,
|
|
106
265
|
apiOrigin,
|
|
107
266
|
portalHref,
|
|
108
267
|
url,
|
|
109
|
-
fetch:
|
|
268
|
+
fetch: fetchWithSession,
|
|
110
269
|
async whoAmI() {
|
|
111
270
|
if (!available) return { signedIn: false };
|
|
112
271
|
const result = await request(
|
|
@@ -134,6 +293,15 @@ function createPortalClient(config = {}) {
|
|
|
134
293
|
const result = await send(PORTAL_PUBLIC_CLAIM_PATH, "POST", {});
|
|
135
294
|
return result.ok ? result.data : null;
|
|
136
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
|
+
},
|
|
137
305
|
returnPath,
|
|
138
306
|
get,
|
|
139
307
|
send,
|
|
@@ -145,13 +313,9 @@ function createPortalClient(config = {}) {
|
|
|
145
313
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
146
314
|
var defaultPortalClient = createPortalClient();
|
|
147
315
|
var PORTAL_SIGN_OUT_URL = "/portal/api/sign-out";
|
|
148
|
-
function portalFetch(fetchImpl) {
|
|
149
|
-
return fetchImpl ?? ((input, init) => fetch(input, init));
|
|
150
|
-
}
|
|
151
316
|
var BOUND_PORTAL_CLIENT = /* @__PURE__ */ Symbol("boundPortalClient");
|
|
152
|
-
function portalFetchForClient(client2
|
|
153
|
-
const
|
|
154
|
-
const bound = (input, init) => fetcher(client2.url(input), init);
|
|
317
|
+
function portalFetchForClient(client2) {
|
|
318
|
+
const bound = (input, init) => client2.fetch(client2.url(input), init);
|
|
155
319
|
bound[BOUND_PORTAL_CLIENT] = client2;
|
|
156
320
|
return bound;
|
|
157
321
|
}
|
|
@@ -563,6 +727,25 @@ function resetSession() {
|
|
|
563
727
|
clientKey = "";
|
|
564
728
|
}
|
|
565
729
|
var flowKey = (requestId) => `pos:flow:${requestId}`;
|
|
730
|
+
var applicationRunKey = (runId) => `pos:application-run:${runId}`;
|
|
731
|
+
function saveApplicationRunToken(c, runId) {
|
|
732
|
+
const token = c.getApplicationRunToken(runId);
|
|
733
|
+
if (!token) return;
|
|
734
|
+
try {
|
|
735
|
+
session()?.setItem(applicationRunKey(runId), token);
|
|
736
|
+
} catch {
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
function restoreApplicationRunToken(c, runId) {
|
|
740
|
+
try {
|
|
741
|
+
const token = session()?.getItem(applicationRunKey(runId)) ?? null;
|
|
742
|
+
if (!token) return false;
|
|
743
|
+
c.setApplicationRunToken(runId, token);
|
|
744
|
+
return true;
|
|
745
|
+
} catch {
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
566
749
|
function saveFlowRecord(requestId, record) {
|
|
567
750
|
try {
|
|
568
751
|
session()?.setItem(flowKey(requestId), JSON.stringify(record));
|
|
@@ -1277,12 +1460,16 @@ export {
|
|
|
1277
1460
|
FlowErrorSummary,
|
|
1278
1461
|
focusField,
|
|
1279
1462
|
FlowLoading,
|
|
1463
|
+
normalizePortalApiOrigin,
|
|
1464
|
+
encodePortalReturnTarget,
|
|
1280
1465
|
createPortalClient,
|
|
1281
1466
|
PatientOSApiError,
|
|
1282
1467
|
patientos,
|
|
1283
1468
|
persistClaimToken,
|
|
1284
1469
|
adoptClaimToken,
|
|
1285
1470
|
resetSession,
|
|
1471
|
+
saveApplicationRunToken,
|
|
1472
|
+
restoreApplicationRunToken,
|
|
1286
1473
|
saveFlowRecord,
|
|
1287
1474
|
loadFlowRecord,
|
|
1288
1475
|
once,
|
package/dist/index.d.ts
CHANGED
|
@@ -2,12 +2,13 @@ export { BPOINT_SCRIPT_ORIGIN, BPOINT_SCRIPT_URL, CONSULT_CHUNK_PATH, THIRD_PART
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
export { ISLAND_ATTR, ISLAND_PROPS_ATTR, Island, IslandProps, IslandRegistry, mountIslandsWith, readIslandProps } from './islands.js';
|
|
4
4
|
export { ISLANDS, IslandName, mountIslands } from './islands-registry.js';
|
|
5
|
-
import { P as PortalClient, W as WhoAmI, a as PortalPrefill, b as PublicClaim } from './portal-account-
|
|
6
|
-
export {
|
|
7
|
-
export { B as BookingBlock, a as BookingBlockMarkerProps, b as BookingBlockProps, c as BookingTypeOption, C as Cart, d as CartMarkerProps, e as CartProps, f as CertificateFunnel, g as CertificateFunnelMarkerProps, h as CertificateFunnelProps, i as Checkout, j as CheckoutMarkerProps, k as CheckoutProps, F as FunnelPublicApi, l as FunnelServiceOption, S as Store, m as StoreMarkerProps, n as StoreProps } from './checkout-block-
|
|
5
|
+
import { P as PortalClient, W as WhoAmI, a as PortalPrefill, b as PublicClaim, F as FetchLike } from './portal-account-8uQc_Ncx.js';
|
|
6
|
+
export { c as PortalAccount, d as PortalAccountMarkerProps, e as PortalAccountProps, f as PortalAccountSurface, g as PortalClientConfig, h as PortalPanel, i as PortalPanelMarkerProps, j as PortalPanelProps, k as PortalResult, l as PortalSurface, m as createPortalClient } from './portal-account-8uQc_Ncx.js';
|
|
7
|
+
export { B as BookingBlock, a as BookingBlockMarkerProps, b as BookingBlockProps, c as BookingTypeOption, C as Cart, d as CartMarkerProps, e as CartProps, f as CertificateFunnel, g as CertificateFunnelMarkerProps, h as CertificateFunnelProps, i as Checkout, j as CheckoutMarkerProps, k as CheckoutProps, F as FunnelPublicApi, l as FunnelServiceOption, S as Store, m as StoreMarkerProps, n as StoreProps } from './checkout-block-BdpPIuQ5.js';
|
|
8
8
|
export { PatientSurfaceTheme, patientSurfaceTheme } from './patient-surface-theme.js';
|
|
9
9
|
export { Rgb, contrastRatio, deriveForeground, parseHexColor, relativeLuminance } from './contrast.js';
|
|
10
10
|
export { AnswerValue, Answers, ShowWhenCondition, evaluateShowWhen, visibleQuestions } from './show-when.js';
|
|
11
|
+
import '@patientos/public-sdk';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Theme tokens for the website-kit.
|
|
@@ -209,6 +210,33 @@ interface PortalSession {
|
|
|
209
210
|
*/
|
|
210
211
|
declare function usePortalSession(): PortalSession;
|
|
211
212
|
|
|
213
|
+
interface StoreClientConfig {
|
|
214
|
+
/** The validated PatientOS patient/API origin. Empty means same-origin. */
|
|
215
|
+
apiOrigin?: string | null;
|
|
216
|
+
/** Injectable browser transport for tests. */
|
|
217
|
+
fetchImpl?: FetchLike;
|
|
218
|
+
}
|
|
219
|
+
type StoreResult<T> = {
|
|
220
|
+
ok: true;
|
|
221
|
+
data: T;
|
|
222
|
+
} | {
|
|
223
|
+
ok: false;
|
|
224
|
+
status: number | null;
|
|
225
|
+
error: string | null;
|
|
226
|
+
message: string | null;
|
|
227
|
+
};
|
|
228
|
+
interface StoreClient {
|
|
229
|
+
readonly apiOrigin: string;
|
|
230
|
+
url(path: string): string;
|
|
231
|
+
get<T>(path: string): Promise<StoreResult<T>>;
|
|
232
|
+
post<T>(path: string, body: unknown, options?: {
|
|
233
|
+
signal?: AbortSignal;
|
|
234
|
+
}): Promise<StoreResult<T>>;
|
|
235
|
+
/** PatientOS magic-link sign-in that returns to this site's checkout. */
|
|
236
|
+
signInHref(returnPath?: string): string;
|
|
237
|
+
}
|
|
238
|
+
declare function createStoreClient(config?: StoreClientConfig): StoreClient;
|
|
239
|
+
|
|
212
240
|
declare const WEBSITE_KIT_VERSION = "0.1.0";
|
|
213
241
|
|
|
214
|
-
export { Button, type ButtonProps, type ButtonVariant, Container, type ContainerProps, FAQ, type FAQProps, type FaqItem, Hero, type HeroProps, Hours, type HoursProps, type HoursRow, MapEmbed, type MapEmbedProps, PortalClient, PortalPrefill, PortalProvider, type PortalProviderProps, type PortalSession, type PortalSessionStatus, PublicClaim, Row, type RowProps, Section, type SectionProps, type Service, ServiceGrid, type ServiceGridProps, Stack, type StackProps, TeamGrid, type TeamGridProps, type TeamMember, ThemeProvider, type ThemeProviderProps, WEBSITE_KIT_VERSION, WebsiteShell, type WebsiteShellProps, type WebsiteTheme, WhoAmI, defaultTheme, resolveTheme, themeToCssVars, usePortalClient, usePortalSession };
|
|
242
|
+
export { Button, type ButtonProps, type ButtonVariant, Container, type ContainerProps, FAQ, type FAQProps, type FaqItem, FetchLike, Hero, type HeroProps, Hours, type HoursProps, type HoursRow, MapEmbed, type MapEmbedProps, PortalClient, PortalPrefill, PortalProvider, type PortalProviderProps, type PortalSession, type PortalSessionStatus, PublicClaim, Row, type RowProps, Section, type SectionProps, type Service, ServiceGrid, type ServiceGridProps, Stack, type StackProps, type StoreClient, type StoreClientConfig, type StoreResult, TeamGrid, type TeamGridProps, type TeamMember, ThemeProvider, type ThemeProviderProps, WEBSITE_KIT_VERSION, WebsiteShell, type WebsiteShellProps, type WebsiteTheme, WhoAmI, createStoreClient, defaultTheme, resolveTheme, themeToCssVars, usePortalClient, usePortalSession };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ISLANDS,
|
|
3
3
|
mountIslands
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZZFBTLR4.js";
|
|
5
5
|
import {
|
|
6
6
|
PortalAccount,
|
|
7
7
|
PortalPanel,
|
|
8
8
|
PortalProvider,
|
|
9
|
+
createStoreClient,
|
|
9
10
|
getAppointmentTypes,
|
|
10
11
|
getClinic,
|
|
11
12
|
getPublicApi,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
getThemeTokens,
|
|
14
15
|
usePortalClient,
|
|
15
16
|
usePortalSession
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-7ZOQ6UKG.js";
|
|
17
18
|
import {
|
|
18
19
|
BPOINT_SCRIPT_ORIGIN,
|
|
19
20
|
BPOINT_SCRIPT_URL,
|
|
@@ -35,7 +36,7 @@ import {
|
|
|
35
36
|
} from "./chunk-THMT43MV.js";
|
|
36
37
|
import {
|
|
37
38
|
createPortalClient
|
|
38
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-ZYX4TXBN.js";
|
|
39
40
|
import {
|
|
40
41
|
contrastRatio,
|
|
41
42
|
deriveForeground,
|
|
@@ -403,6 +404,8 @@ function BookingBlock({
|
|
|
403
404
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
404
405
|
function Store({
|
|
405
406
|
categoryHandle,
|
|
407
|
+
productHandle,
|
|
408
|
+
storeApiOrigin,
|
|
406
409
|
title,
|
|
407
410
|
columns,
|
|
408
411
|
className
|
|
@@ -411,7 +414,7 @@ function Store({
|
|
|
411
414
|
Island,
|
|
412
415
|
{
|
|
413
416
|
name: "store",
|
|
414
|
-
props: { categoryHandle, title, columns },
|
|
417
|
+
props: { categoryHandle, productHandle, storeApiOrigin, title, columns },
|
|
415
418
|
className: ["sk-store", className].filter(Boolean).join(" "),
|
|
416
419
|
children: [
|
|
417
420
|
/* @__PURE__ */ jsx6("h2", { className: "sk-store__heading", children: title ?? "Shop" }),
|
|
@@ -423,20 +426,41 @@ function Store({
|
|
|
423
426
|
|
|
424
427
|
// src/cart-block.tsx
|
|
425
428
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
426
|
-
function Cart({ className }) {
|
|
427
|
-
return /* @__PURE__ */ jsxs6(
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
429
|
+
function Cart({ storeApiOrigin, className }) {
|
|
430
|
+
return /* @__PURE__ */ jsxs6(
|
|
431
|
+
Island,
|
|
432
|
+
{
|
|
433
|
+
name: "cart",
|
|
434
|
+
props: { storeApiOrigin },
|
|
435
|
+
className: ["sk-cart", className].filter(Boolean).join(" "),
|
|
436
|
+
children: [
|
|
437
|
+
/* @__PURE__ */ jsx7("h2", { className: "sk-cart__heading", children: "Your cart" }),
|
|
438
|
+
/* @__PURE__ */ jsx7("p", { className: "sk-cart__placeholder", children: "Your cart needs JavaScript to load." })
|
|
439
|
+
]
|
|
440
|
+
}
|
|
441
|
+
);
|
|
431
442
|
}
|
|
432
443
|
|
|
433
444
|
// src/checkout-block.tsx
|
|
434
445
|
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
435
|
-
function Checkout({
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
446
|
+
function Checkout({
|
|
447
|
+
storeApiOrigin,
|
|
448
|
+
ordersHref,
|
|
449
|
+
completionHref,
|
|
450
|
+
className
|
|
451
|
+
}) {
|
|
452
|
+
return /* @__PURE__ */ jsxs7(
|
|
453
|
+
Island,
|
|
454
|
+
{
|
|
455
|
+
name: "checkout",
|
|
456
|
+
props: { storeApiOrigin, ordersHref, completionHref },
|
|
457
|
+
className: ["sk-checkout", className].filter(Boolean).join(" "),
|
|
458
|
+
children: [
|
|
459
|
+
/* @__PURE__ */ jsx8("h2", { className: "sk-checkout__heading", children: "Checkout" }),
|
|
460
|
+
/* @__PURE__ */ jsx8("p", { className: "sk-checkout__placeholder", children: "Checkout needs JavaScript to load." })
|
|
461
|
+
]
|
|
462
|
+
}
|
|
463
|
+
);
|
|
440
464
|
}
|
|
441
465
|
|
|
442
466
|
// src/index.ts
|
|
@@ -476,6 +500,7 @@ export {
|
|
|
476
500
|
WebsiteShell,
|
|
477
501
|
contrastRatio,
|
|
478
502
|
createPortalClient,
|
|
503
|
+
createStoreClient,
|
|
479
504
|
defaultTheme,
|
|
480
505
|
deriveForeground,
|
|
481
506
|
evaluateShowWhen,
|
package/dist/islands-impl.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { b as BookingBlockProps, h as CertificateFunnelProps, n as StoreProps, e as CartProps, k as CheckoutProps } from './checkout-block-
|
|
3
|
-
import { j as PortalPanelProps, F as FetchLike } from './portal-account-
|
|
4
|
-
export { P as PortalAccountClient } from './portal-account.client-
|
|
2
|
+
import { b as BookingBlockProps, h as CertificateFunnelProps, n as StoreProps, e as CartProps, k as CheckoutProps } from './checkout-block-BdpPIuQ5.js';
|
|
3
|
+
import { j as PortalPanelProps, F as FetchLike } from './portal-account-8uQc_Ncx.js';
|
|
4
|
+
export { P as PortalAccountClient } from './portal-account.client-BBe_q9yC.js';
|
|
5
|
+
import '@patientos/public-sdk';
|
|
5
6
|
|
|
6
7
|
declare function BookingBlockClient(props: BookingBlockProps): React.ReactElement;
|
|
7
8
|
|
|
@@ -58,10 +59,10 @@ type PortalPanelClientProps = PortalPanelProps & {
|
|
|
58
59
|
};
|
|
59
60
|
declare function PortalPanelClient({ surface, portalUrl, portalApiOrigin, initialState, fetchImpl, }: PortalPanelClientProps): React.ReactElement;
|
|
60
61
|
|
|
61
|
-
declare function StoreClient({ categoryHandle, title, columns }: StoreProps): React.ReactElement;
|
|
62
|
+
declare function StoreClient({ categoryHandle, productHandle, storeApiOrigin, title, columns, }: StoreProps): React.ReactElement;
|
|
62
63
|
|
|
63
|
-
declare function CartClient(
|
|
64
|
+
declare function CartClient({ storeApiOrigin }: CartProps): React.ReactElement;
|
|
64
65
|
|
|
65
|
-
declare function CheckoutClient(
|
|
66
|
+
declare function CheckoutClient({ storeApiOrigin, ordersHref, completionHref, }: CheckoutProps): React.ReactElement;
|
|
66
67
|
|
|
67
68
|
export { BookingBlockClient, CartClient, CertificateFunnelClient, CheckoutClient, PortalPanelClient, StoreClient };
|
package/dist/islands-impl.js
CHANGED
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
PortalAccountClient,
|
|
7
7
|
PortalPanelClient,
|
|
8
8
|
StoreClient
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-7ZOQ6UKG.js";
|
|
10
10
|
import "./chunk-ZOF22TJA.js";
|
|
11
11
|
import "./chunk-QDO3SJWR.js";
|
|
12
12
|
import "./chunk-THMT43MV.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-ZYX4TXBN.js";
|
|
14
14
|
import "./chunk-MLKGABMK.js";
|
|
15
15
|
export {
|
|
16
16
|
BookingBlockClient,
|
package/dist/islands-registry.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ISLANDS,
|
|
3
3
|
mountIslands
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-ZZFBTLR4.js";
|
|
5
|
+
import "./chunk-7ZOQ6UKG.js";
|
|
6
6
|
import "./chunk-ZOF22TJA.js";
|
|
7
7
|
import "./chunk-QDO3SJWR.js";
|
|
8
8
|
import "./chunk-THMT43MV.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-ZYX4TXBN.js";
|
|
10
10
|
import "./chunk-MLKGABMK.js";
|
|
11
11
|
export {
|
|
12
12
|
ISLANDS,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ApplicationRun } from '@patientos/public-sdk';
|
|
1
2
|
import * as React from 'react';
|
|
2
3
|
|
|
3
4
|
/** The injectable browser transport. */
|
|
@@ -64,8 +65,13 @@ interface PortalClient {
|
|
|
64
65
|
prefill(): Promise<PortalPrefill | null>;
|
|
65
66
|
/** Mint a public claim only for the signed-in patient. */
|
|
66
67
|
mintPublicClaim(): Promise<PublicClaim | null>;
|
|
67
|
-
/**
|
|
68
|
-
|
|
68
|
+
/** Redeem a single-use magic-link continuation after patient sign-in. */
|
|
69
|
+
redeemApplicationRunHandoff(handoffId: string): Promise<{
|
|
70
|
+
run: ApplicationRun;
|
|
71
|
+
capabilityToken: string;
|
|
72
|
+
} | null>;
|
|
73
|
+
/** Page callback, using the app-side return hop when cross-origin. */
|
|
74
|
+
returnPath(targetUrl?: string): string;
|
|
69
75
|
/** Back-compat account-surface transport; callers still use constant paths. */
|
|
70
76
|
get<T>(path: string): Promise<PortalResult<T>>;
|
|
71
77
|
/** Back-compat account-surface JSON write; callers still use constant paths. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { F as FetchLike, k as PortalResult } from './portal-account-
|
|
3
|
-
import { a as PortalAppointment, b as PortalAppointmentsBody } from './portal-account.client-
|
|
2
|
+
import { F as FetchLike, k as PortalResult } from './portal-account-8uQc_Ncx.js';
|
|
3
|
+
import { a as PortalAppointment, b as PortalAppointmentsBody } from './portal-account.client-BBe_q9yC.js';
|
|
4
|
+
import '@patientos/public-sdk';
|
|
4
5
|
|
|
5
6
|
/** One bookable instant, collapsing the practitioners free at the same time. */
|
|
6
7
|
interface TimeSlot {
|
package/dist/website-kit.css
CHANGED
|
@@ -2868,7 +2868,9 @@ select.sk-identity__input[data-empty] {
|
|
|
2868
2868
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
2869
2869
|
}
|
|
2870
2870
|
}
|
|
2871
|
-
|
|
2871
|
+
/* Two catalogue columns fit ordinary 390–430px phones and keep a 54-item shop
|
|
2872
|
+
scannable. Below 380px one column preserves readable product names. */
|
|
2873
|
+
@media (max-width: 379px) {
|
|
2872
2874
|
.sk-store__grid {
|
|
2873
2875
|
grid-template-columns: minmax(0, 1fr);
|
|
2874
2876
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patientos/website-kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "PatientOS clinic website components and patient-facing interactive islands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -109,12 +109,13 @@
|
|
|
109
109
|
"@types/node": "^25.6.0",
|
|
110
110
|
"@types/react": "^19.2.14",
|
|
111
111
|
"@types/react-dom": "^19.2.3",
|
|
112
|
+
"jsdom": "^29.1.1",
|
|
112
113
|
"tsup": "^8.5.0",
|
|
113
114
|
"typescript": "^5.9.3",
|
|
114
115
|
"vitest": "^4.1.5",
|
|
115
116
|
"livekit-client": "^2.20.0"
|
|
116
117
|
},
|
|
117
118
|
"dependencies": {
|
|
118
|
-
"@patientos/public-sdk": "^0.2.
|
|
119
|
+
"@patientos/public-sdk": "^0.2.2"
|
|
119
120
|
}
|
|
120
121
|
}
|