@yourroad/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # YourRoad SDK
2
+
3
+ A standalone, framework-independent TypeScript browser SDK that hosts embedded YourRoad experiences over a partner website.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install @yourroad/sdk
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import { YourRoad } from '@yourroad/sdk';
15
+
16
+ const yourRoad = YourRoad.init({ partnerKey: 'yr_test_example' });
17
+
18
+ const unsubscribe = yourRoad.on('closed', () => console.log('closed'));
19
+ await yourRoad.open({
20
+ surface: 'pre-approval',
21
+ context: { vehicle: { id: 'abc', price: 24_495 }, page: { type: 'vehicle-detail' } },
22
+ });
23
+ yourRoad.updateContext({ vehicle: { price: 23_995 } });
24
+ yourRoad.close();
25
+ unsubscribe();
26
+ yourRoad.destroy();
27
+ ```
28
+
29
+ ### Bootstrap and lifecycle
30
+
31
+ `init()` validates local configuration. With no journey marker it makes no session-state network request; with a valid marker it makes one conditional probe. The `partnerKey` is publishable rather than secret. On an explicit cold `open()`, the SDK creates a journey session and asks the YourRoad bootstrap service to authorize that partner key together with the browser-supplied Origin and the requested surface. Both must succeed before the overlay mounts. Bootstrap itself creates no customer, finance, journey, or resumable session state.
32
+
33
+ Each initialization is independent. Concurrent calls share one opening operation, and repeated `open()` on an already-open instance is an idempotent no-op. `close()` cancels a pending bootstrap or closes an open overlay while keeping the instance reusable. `destroy()` is idempotent and all other later operations throw `YourRoadError` with code `DESTROYED`.
34
+
35
+ Context objects are copied at entry. Updates shallow-merge the top level and each known nested section; properties explicitly set to `undefined` are ignored rather than deleting existing values. Context is retained only in memory and is never sent during bootstrap. The iframe URL remains server-authoritative, and the SDK derives the trusted runtime origin from that authorised URL. It accepts the runtime's `ready` message only from the exact iframe window and origin, then replies with an acknowledgement, the full current context, and the opaque journey session UUID in that order. After that handshake, `updateContext()` sends the newly merged full context snapshot.
36
+
37
+ Factual vehicle context can include `imageUrl`, `transmission`, `bodyType`, and `listingUrl`. `page.url` identifies the current host page, while `vehicle.listingUrl` is the durable partner-owned URL for returning to the exact vehicle listing. Finance data and customer PII do not belong in SDK context.
38
+
39
+ The host origin may persist one opaque journey UUID under `yr_journey_session_pre_approval_v1`. The value is the UUID string itself, not JSON; expiry, partner data, context, credentials, and status metadata are not stored. The session ID never enters bootstrap, iframe URLs, or context and crosses only the exact-origin, exact-window trusted bridge. An invalid marker is cleared. An expired marker is also cleared silently during passive browsing while its UUID is retained only in page memory so the runtime can present the expired journey; a replacement is created only after the runtime's explicit trusted **Start again** request. Runtime mutation tokens, journey snapshots, and snapshot versions never enter the SDK.
40
+
41
+ The overlay mounts directly under `body` and retains host-owned scrim, close, focus, scroll, and Escape behavior. Host-side Escape handling cannot receive keystrokes focused inside a cross-origin iframe.
42
+
43
+ ## Explicit limitations
44
+
45
+ The SDK does **not** provide customer authentication, finance logic, runtime journey claim/hydration/persistence, or production partner configuration. No finance/customer PII belongs in SDK context. Apart from the single opaque host-origin journey UUID described above, no journey, runtime token, customer, or finance data is persisted by the SDK.
package/dist/index.cjs ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";var E=Object.defineProperty;var k=(r,t,e)=>t in r?E(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var o=(r,t,e)=>k(r,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const S="https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/bootstrap-embed-access";function A(r){if(typeof r!="object"||r===null||Array.isArray(r))throw new Error("Invalid bootstrap response.");const{iframeUrl:t,expiresAt:e}=r;if(typeof t!="string"||t.trim()==="")throw new Error("Invalid bootstrap response.");let s;try{s=new URL(t)}catch{throw new Error("Invalid bootstrap response.")}if(s.protocol!=="http:"&&s.protocol!=="https:")throw new Error("Invalid bootstrap response.");if(typeof e!="string"||e.trim()===""||Number.isNaN(Date.parse(e)))throw new Error("Invalid bootstrap response.");return{iframeUrl:t,expiresAt:e}}async function O(r,t){const e=await fetch(S,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r),credentials:"omit",cache:"no-store",signal:t});if(!e.ok)throw new Error("Bootstrap request was unsuccessful.");return A(await e.json())}const f=r=>Object.fromEntries(Object.entries(r??{}).filter(([,t])=>t!==void 0));function b(r,t){const e={...r,...f(t)};for(const s of["vehicle","dealer","page"])t[s]!==void 0&&(e[s]={...r[s],...f(t[s])});return e}const h=r=>b({},r??{}),a=1;function I(r){const t=new URL(r);if(t.protocol!=="https:"&&t.protocol!=="http:")throw new TypeError("The iframe URL must use HTTP(S).");return t.origin}const m=r=>{if(r===null||typeof r!="object")return!1;const t=Object.getPrototypeOf(r);return t===Object.prototype||t===null},R=r=>m(r)?r.source==="yourroad.embed"&&r.version===a&&r.type==="ready"&&r.payload===void 0&&(r.requestId===void 0||typeof r.requestId=="string"):!1,j=r=>m(r)&&Object.keys(r).length===3&&r.source==="yourroad.embed"&&r.version===a&&r.type==="journey_session_restart";class T{constructor(t,e,s,i,n){o(this,"ready",!1);o(this,"started",!1);o(this,"handleMessage",t=>{if(t.origin!==this.trustedRuntimeOrigin)return;const e=this.iframe.contentWindow;if(!(e===null||t.source!==e)){if(this.ready){j(t.data)&&this.onJourneySessionRestart();return}R(t.data)&&(this.ready=!0,this.post({source:"yourroad.sdk",version:a,type:"ack"}),this.post({source:"yourroad.sdk",version:a,type:"context",payload:h(this.getCurrentContext())}),this.sendJourneySession(this.getJourneySessionId()))}});this.iframe=t,this.trustedRuntimeOrigin=e,this.getCurrentContext=s,this.getJourneySessionId=i,this.onJourneySessionRestart=n}start(){this.started||(window.addEventListener("message",this.handleMessage),this.started=!0)}contextUpdated(t){this.ready&&this.post({source:"yourroad.sdk",version:a,type:"context_updated",payload:h(t)})}destroy(){this.started&&window.removeEventListener("message",this.handleMessage),this.started=!1,this.ready=!1}sendJourneySession(t){this.ready&&this.post({source:"yourroad.sdk",version:a,type:"journey_session",payload:{sessionId:t}})}post(t){const e=this.iframe.contentWindow;if(e!==null)try{e.postMessage(t,this.trustedRuntimeOrigin)}catch{}}}class c extends Error{constructor(e,s,i){super(s,i);o(this,"name","YourRoadError");this.code=e}}class _{constructor(){o(this,"listeners",new Map)}on(t,e){const s=this.listeners.get(t)??new Set;return s.add(e),this.listeners.set(t,s),()=>s.delete(e)}emit(t,e){var s;(s=this.listeners.get(t))==null||s.forEach(i=>i(e))}clear(){this.listeners.clear()}}class N{constructor(){o(this,"previous",null)}capture(){this.previous=document.activeElement instanceof HTMLElement?document.activeElement:null}moveTo(t){t.focus()}restore(){var t;(t=this.previous)!=null&&t.isConnected&&this.previous.focus(),this.previous=null}}class C{constructor(){o(this,"saved");o(this,"scrollX",0);o(this,"scrollY",0)}lock(){if(this.saved)return;const t=document.body.style;this.saved={position:t.position,top:t.top,left:t.left,right:t.right,width:t.width,overflow:t.overflow},this.scrollX=window.scrollX,this.scrollY=window.scrollY,Object.assign(t,{position:"fixed",top:`-${this.scrollY}px`,left:`-${this.scrollX}px`,right:"0",width:"100%",overflow:"hidden"})}unlock(){this.saved&&(Object.assign(document.body.style,this.saved),this.saved=void 0,window.scrollTo(this.scrollX,this.scrollY))}}class Y{constructor(t,e){o(this,"element");this.element=document.createElement("iframe"),this.element.dataset.yourroadFrame="",this.element.title="YourRoad pre-approval",this.element.src=t,this.element.addEventListener("error",e,{once:!0})}destroy(){this.element.remove()}}const L=2147483e3,D=`
2
+ [data-yourroad-overlay-root] { all: initial; position: fixed; inset: 0; z-index: ${L}; display: block; font-family: system-ui, sans-serif; color: #111; }
3
+ [data-yourroad-scrim] { all: initial; position: absolute; inset: 0; display: block; background: rgba(0, 0, 0, .55); }
4
+ [data-yourroad-panel] { all: initial; box-sizing: border-box; position: absolute; inset-block: 0; inset-inline-end: 0; display: flex; flex-direction: column; width: min(520px, 92vw); height: 100vh; height: 100dvh; background: #fff; box-shadow: -8px 0 30px rgba(0,0,0,.2); }
5
+ [data-yourroad-close] { all: unset; box-sizing: border-box; position: absolute; z-index: 1; inset-block-start: max(12px, env(safe-area-inset-top)); inset-inline-end: max(12px, env(safe-area-inset-right)); width: 44px; height: 44px; display: grid; place-items: center; border-radius: 999px; background: #fff; color: #111; box-shadow: 0 1px 8px rgba(0,0,0,.25); cursor: pointer; font: 28px/1 system-ui, sans-serif; }
6
+ [data-yourroad-close]:focus-visible { outline: 3px solid #1769e0; outline-offset: 2px; }
7
+ [data-yourroad-frame] { all: initial; box-sizing: border-box; display: block; width: 100%; height: 100%; border: 0; background: #fff; }
8
+ @media (max-width: 600px) { [data-yourroad-panel] { width: 100vw; max-width: none; } }
9
+ `;function K(){const r=document.head.querySelector("style[data-yourroad-sdk]");if(r)return r;const t=document.createElement("style");return t.dataset.yourroadSdk="",t.textContent=D,document.head.append(t),t}class P{constructor(t,e,s){o(this,"root");o(this,"closeButton");o(this,"iframe");o(this,"abort",new AbortController);K(),this.root=document.createElement("div"),this.root.dataset.yourroadOverlayRoot="",this.root.setAttribute("role","dialog"),this.root.setAttribute("aria-modal","true"),this.root.setAttribute("aria-label","YourRoad pre-approval");const i=document.createElement("div");i.dataset.yourroadScrim="";const n=document.createElement("div");n.dataset.yourroadPanel="",this.closeButton=document.createElement("button"),this.closeButton.type="button",this.closeButton.dataset.yourroadClose="",this.closeButton.setAttribute("aria-label","Close YourRoad"),this.closeButton.textContent="×",this.iframe=new Y(t,s),n.append(this.closeButton,this.iframe.element),this.root.append(i,n);const d=this.abort.signal;i.addEventListener("click",e,{signal:d}),this.closeButton.addEventListener("click",e,{signal:d}),document.addEventListener("keydown",l=>{l.key==="Escape"&&e()},{signal:d})}mount(){document.body.append(this.root)}destroy(){var t;this.abort.abort(),this.iframe.destroy(),this.root.remove(),document.querySelector("[data-yourroad-overlay-root]")||(t=document.head.querySelector("style[data-yourroad-sdk]"))==null||t.remove()}}const u="yr_journey_session_pre_approval_v1",p=r=>typeof r=="string"&&/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(r);function U(){try{return globalThis.localStorage}catch{return}}function B(r=U()){const t=()=>{try{r==null||r.removeItem(u)}catch{}};return{read(){let e;try{e=(r==null?void 0:r.getItem(u))??null}catch{return}if(e!==null){if(!p(e)){t();return}return e}},write(e){if(p(e))try{r==null||r.setItem(u,e)}catch{}},clear:t}}const J="https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/create-embed-journey-session",q="https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/probe-embed-journey-session",v=r=>{if(r===null||typeof r!="object")return!1;const t=Object.getPrototypeOf(r);return t===Object.prototype||t===null},w=r=>typeof r=="string"&&r.trim()!==""&&!Number.isNaN(Date.parse(r)),g=(r,t)=>({method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r),credentials:"omit",cache:"no-store",signal:t});async function y(r,t){const e=await fetch(J,g({partnerKey:r,surface:"pre-approval"},t));if(e.status!==201)throw new Error("Journey session creation was unsuccessful.");const s=await e.json();if(!v(s)||!p(s.sessionId)||!w(s.expiresAt))throw new Error("Invalid journey session response.");return s.sessionId}async function M(r,t){const e=await fetch(q,g({partnerKey:r,surface:"pre-approval",sessionId:t})),s=await e.json();if(!v(s))throw new Error("Invalid journey session probe response.");if(e.status===200&&s.status==="resumable"&&s.sessionId===t&&w(s.expiresAt))return{status:"resumable",sessionId:t};if(e.status===410&&s.status==="expired"&&s.code==="SESSION_EXPIRED")return{status:"expired"};if(e.status===404&&s.status==="invalid"&&s.code==="SESSION_INVALID")return{status:"invalid"};throw new Error("Journey session probe was unsuccessful.")}class F{constructor(t,e=B()){o(this,"state",{kind:"cold"});o(this,"restart");this.partnerKey=t,this.marker=e;const s=e.read();if(!s)return;const i=this.probe(s);this.state={kind:"probing",completion:i}}async resolveForOpen(t){if(this.state.kind==="probing"&&await this.state.completion,this.state.kind==="probe_failed")throw new Error("Journey session validity could not be established.");if(this.state.kind==="active"||this.state.kind==="expired")return this.state.sessionId;const e=await y(this.partnerKey,t);return this.marker.write(e),this.state={kind:"active",sessionId:e},e}restartExpired(t){if(this.state.kind!=="expired")return;if(this.restart)return this.restart;const e=y(this.partnerKey,t).then(s=>(this.marker.write(s),this.state={kind:"active",sessionId:s},s)).finally(()=>{this.restart===e&&(this.restart=void 0)});return this.restart=e,e}async probe(t){try{const e=await M(this.partnerKey,t);e.status==="resumable"?this.state={kind:"active",sessionId:t}:e.status==="expired"?(this.marker.clear(),this.state={kind:"expired",sessionId:t}):(this.marker.clear(),this.state={kind:"cold"})}catch{this.state={kind:"probe_failed"}}}}class X{constructor(t){o(this,"events",new _);o(this,"scroll",new C);o(this,"focus",new N);o(this,"overlay");o(this,"bridge");o(this,"context",{});o(this,"destroyed",!1);o(this,"opening");o(this,"bootstrapAbort");o(this,"restartAbort");o(this,"session");this.config=t,this.session=new F(t.partnerKey)}async open(t){if(this.assertUsable(),!t||t.surface!=="pre-approval")throw this.fail("INVALID_SURFACE",'The only supported surface is "pre-approval".');if(this.overlay)return;if(this.opening)return this.opening;this.context=h(t.context);const e=new AbortController;this.bootstrapAbort=e;const s=this.performOpen(t.surface,e);return this.opening=s,s}updateContext(t){var e;this.assertUsable(),this.context=b(this.context,t),(e=this.bridge)==null||e.contextUpdated(this.context)}close(){var t,e;this.assertUsable(),this.bootstrapAbort&&(this.bootstrapAbort.abort(),this.bootstrapAbort=void 0,this.opening=void 0),(t=this.restartAbort)==null||t.abort(),this.restartAbort=void 0,this.overlay&&((e=this.bridge)==null||e.destroy(),this.bridge=void 0,this.overlay.destroy(),this.overlay=void 0,this.scroll.unlock(),this.focus.restore(),this.events.emit("closed",void 0))}on(t,e){return this.assertUsable(),this.events.on(t,e)}destroy(){this.destroyed||(this.close(),this.events.clear(),this.context={},this.destroyed=!0)}async performOpen(t,e){var s,i;try{const[n,d]=await Promise.all([O({partnerKey:this.config.partnerKey,surface:t},e.signal),this.session.resolveForOpen(e.signal)]),{iframeUrl:l}=n;if(e.signal.aborted||this.destroyed||this.bootstrapAbort!==e)return;this.focus.capture();try{this.overlay=new P(l,()=>this.close(),()=>this.fail("IFRAME_LOAD_FAILED","The embedded experience failed to load.")),this.bridge=new T(this.overlay.iframe.element,I(l),()=>h(this.context),()=>d,()=>this.restartExpiredSession(this.bridge)),this.bridge.start(),this.overlay.mount(),this.scroll.lock(),this.focus.moveTo(this.overlay.closeButton),this.events.emit("opened",void 0)}catch(x){throw(s=this.bridge)==null||s.destroy(),this.bridge=void 0,(i=this.overlay)==null||i.destroy(),this.overlay=void 0,this.scroll.unlock(),this.focus.restore(),this.fail("OVERLAY_CREATION_FAILED","The YourRoad overlay could not be created.",x)}}catch(n){if(e.signal.aborted)return;throw n instanceof c?n:this.fail("BOOTSTRAP_FAILED","The YourRoad experience could not be started securely.",n)}finally{this.bootstrapAbort===e&&(this.bootstrapAbort=void 0,this.opening=void 0)}}restartExpiredSession(t){if(this.destroyed||t===void 0||t!==this.bridge||this.restartAbort)return;const e=new AbortController;this.restartAbort=e;const s=this.session.restartExpired(e.signal);if(!s){this.restartAbort=void 0;return}s.then(i=>{!this.destroyed&&!e.signal.aborted&&this.bridge===t&&t.sendJourneySession(i)}).catch(()=>{}).finally(()=>{this.restartAbort===e&&(this.restartAbort=void 0)})}assertUsable(){if(this.destroyed)throw new c("DESTROYED","This YourRoad SDK instance has been destroyed.")}fail(t,e,s){const i=new c(t,e,s===void 0?void 0:{cause:s});return this.events.emit("error",i),i}}function z(r){return new X(r)}class V{static init(t){if(!t||typeof t.partnerKey!="string"||t.partnerKey.trim()==="")throw new c("INVALID_INITIALISATION","partnerKey must be a non-empty string.");return z({partnerKey:t.partnerKey})}}exports.YourRoad=V;exports.YourRoadError=c;
10
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/bootstrap/bootstrapClient.ts","../src/context/mergeContext.ts","../src/bridge/protocol.ts","../src/bridge/BridgeController.ts","../src/errors/YourRoadError.ts","../src/events/EventEmitter.ts","../src/lifecycle/focus.ts","../src/lifecycle/scrollLock.ts","../src/iframe/IframeController.ts","../src/overlay/styles.ts","../src/overlay/OverlayController.ts","../src/session/sessionMarker.ts","../src/session/sessionClient.ts","../src/session/SessionController.ts","../src/client/YourRoadClient.ts","../src/index.ts"],"sourcesContent":["import type { YourRoadSurface } from '../config/types';\n\nconst BOOTSTRAP_ENDPOINT = 'https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/bootstrap-embed-access';\n\ninterface BootstrapRequest {\n partnerKey: string;\n surface: YourRoadSurface;\n}\n\nexport interface BootstrapResult {\n iframeUrl: string;\n expiresAt: string;\n}\n\nfunction validateResponse(value: unknown): BootstrapResult {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('Invalid bootstrap response.');\n const { iframeUrl, expiresAt } = value as Record<string, unknown>;\n if (typeof iframeUrl !== 'string' || iframeUrl.trim() === '') throw new Error('Invalid bootstrap response.');\n let parsedUrl: URL;\n try { parsedUrl = new URL(iframeUrl); } catch { throw new Error('Invalid bootstrap response.'); }\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') throw new Error('Invalid bootstrap response.');\n if (typeof expiresAt !== 'string' || expiresAt.trim() === '' || Number.isNaN(Date.parse(expiresAt))) {\n throw new Error('Invalid bootstrap response.');\n }\n return { iframeUrl, expiresAt };\n}\n\nexport async function requestBootstrap(request: BootstrapRequest, signal: AbortSignal): Promise<BootstrapResult> {\n const response = await fetch(BOOTSTRAP_ENDPOINT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(request),\n credentials: 'omit',\n cache: 'no-store',\n signal,\n });\n if (!response.ok) throw new Error('Bootstrap request was unsuccessful.');\n return validateResponse(await response.json());\n}\n","import type { YourRoadContext, YourRoadContextUpdate } from './types';\n\nconst defined = <T extends object>(value: T | undefined): Partial<T> =>\n Object.fromEntries(Object.entries(value ?? {}).filter(([, item]) => item !== undefined)) as Partial<T>;\n\n/** Merges known context sections. Explicit `undefined` values are ignored. */\nexport function mergeContext(current: YourRoadContext, update: YourRoadContextUpdate): YourRoadContext {\n const next: YourRoadContext = { ...current, ...defined(update) };\n for (const key of ['vehicle', 'dealer', 'page'] as const) {\n if (update[key] !== undefined) next[key] = { ...current[key], ...defined(update[key]) };\n }\n return next;\n}\n\nexport const copyContext = (context?: YourRoadContext): YourRoadContext => mergeContext({}, context ?? {});\n","import type { YourRoadContext } from '../context/types';\n\nexport const BRIDGE_VERSION = 1 as const;\n\nexport type BridgeMessage<T = unknown> = {\n source: 'yourroad.sdk' | 'yourroad.embed';\n version: typeof BRIDGE_VERSION;\n type: string;\n requestId?: string;\n payload?: T;\n};\n\nexport type SdkBridgeMessage =\n | BridgeMessage & { source: 'yourroad.sdk'; type: 'ack' }\n | BridgeMessage<YourRoadContext> & { source: 'yourroad.sdk'; type: 'context' | 'context_updated'; payload: YourRoadContext }\n | BridgeMessage<{ sessionId: string }> & { source: 'yourroad.sdk'; type: 'journey_session'; payload: { sessionId: string } };\n","import { copyContext } from '../context/mergeContext';\nimport type { YourRoadContext } from '../context/types';\nimport { BRIDGE_VERSION, type SdkBridgeMessage } from './protocol';\n\nexport function runtimeOriginFromIframeUrl(iframeUrl: string): string {\n const url = new URL(iframeUrl);\n if (url.protocol !== 'https:' && url.protocol !== 'http:') throw new TypeError('The iframe URL must use HTTP(S).');\n return url.origin;\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (value === null || typeof value !== 'object') return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\n\nconst isReadyMessage = (value: unknown): boolean => {\n if (!isPlainObject(value)) return false;\n return value.source === 'yourroad.embed'\n && value.version === BRIDGE_VERSION\n && value.type === 'ready'\n && value.payload === undefined\n && (value.requestId === undefined || typeof value.requestId === 'string');\n};\n\nconst isRestartMessage = (value: unknown): boolean => isPlainObject(value)\n && Object.keys(value).length === 3\n && value.source === 'yourroad.embed'\n && value.version === BRIDGE_VERSION\n && value.type === 'journey_session_restart';\n\nexport class BridgeController {\n private ready = false;\n private started = false;\n\n constructor(\n private readonly iframe: HTMLIFrameElement,\n private readonly trustedRuntimeOrigin: string,\n private readonly getCurrentContext: () => YourRoadContext,\n private readonly getJourneySessionId: () => string,\n private readonly onJourneySessionRestart: () => void,\n ) {}\n\n start(): void {\n if (this.started) return;\n window.addEventListener('message', this.handleMessage);\n this.started = true;\n }\n\n contextUpdated(currentContext: YourRoadContext): void {\n if (!this.ready) return;\n this.post({\n source: 'yourroad.sdk',\n version: BRIDGE_VERSION,\n type: 'context_updated',\n payload: copyContext(currentContext),\n });\n }\n\n destroy(): void {\n if (this.started) window.removeEventListener('message', this.handleMessage);\n this.started = false;\n this.ready = false;\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n if (event.origin !== this.trustedRuntimeOrigin) return;\n const iframeWindow = this.iframe.contentWindow;\n if (iframeWindow === null || event.source !== iframeWindow) return;\n if (this.ready) {\n if (isRestartMessage(event.data)) this.onJourneySessionRestart();\n return;\n }\n if (!isReadyMessage(event.data)) return;\n this.ready = true;\n this.post({ source: 'yourroad.sdk', version: BRIDGE_VERSION, type: 'ack' });\n this.post({\n source: 'yourroad.sdk',\n version: BRIDGE_VERSION,\n type: 'context',\n payload: copyContext(this.getCurrentContext()),\n });\n this.sendJourneySession(this.getJourneySessionId());\n };\n\n sendJourneySession(sessionId: string): void {\n if (!this.ready) return;\n this.post({ source: 'yourroad.sdk', version: BRIDGE_VERSION, type: 'journey_session', payload: { sessionId } });\n }\n\n private post(message: SdkBridgeMessage): void {\n const iframeWindow = this.iframe.contentWindow;\n if (iframeWindow === null) return;\n try {\n iframeWindow.postMessage(message, this.trustedRuntimeOrigin);\n } catch {\n // The iframe may be removed concurrently; bridge delivery is best-effort.\n }\n }\n}\n","export type YourRoadErrorCode = 'INVALID_INITIALISATION' | 'INVALID_SURFACE' | 'DESTROYED' | 'BOOTSTRAP_FAILED' | 'OVERLAY_CREATION_FAILED' | 'IFRAME_LOAD_FAILED';\n\nexport class YourRoadError extends Error {\n readonly name = 'YourRoadError';\n constructor(public readonly code: YourRoadErrorCode, message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n","export type Handler<T> = (payload: T) => void;\n\nexport class EventEmitter<Events extends object> {\n private listeners = new Map<keyof Events, Set<Handler<never>>>();\n\n on<K extends keyof Events>(event: K, handler: Handler<Events[K]>): () => void {\n const handlers = this.listeners.get(event) ?? new Set();\n handlers.add(handler as Handler<never>);\n this.listeners.set(event, handlers);\n return () => handlers.delete(handler as Handler<never>);\n }\n\n emit<K extends keyof Events>(event: K, payload: Events[K]): void {\n this.listeners.get(event)?.forEach((handler) => handler(payload as never));\n }\n\n clear(): void { this.listeners.clear(); }\n}\n","export class FocusLifecycle {\n private previous: HTMLElement | null = null;\n capture(): void { this.previous = document.activeElement instanceof HTMLElement ? document.activeElement : null; }\n moveTo(element: HTMLElement): void { element.focus(); }\n restore(): void {\n if (this.previous?.isConnected) this.previous.focus();\n this.previous = null;\n }\n}\n","interface SavedStyles { position: string; top: string; left: string; right: string; width: string; overflow: string }\n\nexport class ScrollLock {\n private saved?: SavedStyles;\n private scrollX = 0;\n private scrollY = 0;\n\n lock(): void {\n if (this.saved) return;\n const style = document.body.style;\n this.saved = { position: style.position, top: style.top, left: style.left, right: style.right, width: style.width, overflow: style.overflow };\n this.scrollX = window.scrollX;\n this.scrollY = window.scrollY;\n Object.assign(style, { position: 'fixed', top: `-${this.scrollY}px`, left: `-${this.scrollX}px`, right: '0', width: '100%', overflow: 'hidden' });\n }\n\n unlock(): void {\n if (!this.saved) return;\n Object.assign(document.body.style, this.saved);\n this.saved = undefined;\n window.scrollTo(this.scrollX, this.scrollY);\n }\n}\n","export class IframeController {\n readonly element: HTMLIFrameElement;\n constructor(source: string, onError: () => void) {\n this.element = document.createElement('iframe');\n this.element.dataset.yourroadFrame = '';\n this.element.title = 'YourRoad pre-approval';\n this.element.src = source;\n this.element.addEventListener('error', onError, { once: true });\n }\n destroy(): void { this.element.remove(); }\n}\n","export const SDK_Z_INDEX = 2147483000;\n\nexport const SDK_STYLES = `\n[data-yourroad-overlay-root] { all: initial; position: fixed; inset: 0; z-index: ${SDK_Z_INDEX}; display: block; font-family: system-ui, sans-serif; color: #111; }\n[data-yourroad-scrim] { all: initial; position: absolute; inset: 0; display: block; background: rgba(0, 0, 0, .55); }\n[data-yourroad-panel] { all: initial; box-sizing: border-box; position: absolute; inset-block: 0; inset-inline-end: 0; display: flex; flex-direction: column; width: min(520px, 92vw); height: 100vh; height: 100dvh; background: #fff; box-shadow: -8px 0 30px rgba(0,0,0,.2); }\n[data-yourroad-close] { all: unset; box-sizing: border-box; position: absolute; z-index: 1; inset-block-start: max(12px, env(safe-area-inset-top)); inset-inline-end: max(12px, env(safe-area-inset-right)); width: 44px; height: 44px; display: grid; place-items: center; border-radius: 999px; background: #fff; color: #111; box-shadow: 0 1px 8px rgba(0,0,0,.25); cursor: pointer; font: 28px/1 system-ui, sans-serif; }\n[data-yourroad-close]:focus-visible { outline: 3px solid #1769e0; outline-offset: 2px; }\n[data-yourroad-frame] { all: initial; box-sizing: border-box; display: block; width: 100%; height: 100%; border: 0; background: #fff; }\n@media (max-width: 600px) { [data-yourroad-panel] { width: 100vw; max-width: none; } }\n`;\n\nexport function ensureStyles(): HTMLStyleElement {\n const existing = document.head.querySelector<HTMLStyleElement>('style[data-yourroad-sdk]');\n if (existing) return existing;\n const style = document.createElement('style');\n style.dataset.yourroadSdk = '';\n style.textContent = SDK_STYLES;\n document.head.append(style);\n return style;\n}\n","import { IframeController } from '../iframe/IframeController';\nimport { ensureStyles } from './styles';\n\nexport class OverlayController {\n readonly root: HTMLDivElement;\n readonly closeButton: HTMLButtonElement;\n readonly iframe: IframeController;\n private readonly abort = new AbortController();\n\n constructor(source: string, onClose: () => void, onFrameError: () => void) {\n ensureStyles();\n this.root = document.createElement('div');\n this.root.dataset.yourroadOverlayRoot = '';\n this.root.setAttribute('role', 'dialog');\n this.root.setAttribute('aria-modal', 'true');\n this.root.setAttribute('aria-label', 'YourRoad pre-approval');\n const scrim = document.createElement('div');\n scrim.dataset.yourroadScrim = '';\n const panel = document.createElement('div');\n panel.dataset.yourroadPanel = '';\n this.closeButton = document.createElement('button');\n this.closeButton.type = 'button';\n this.closeButton.dataset.yourroadClose = '';\n this.closeButton.setAttribute('aria-label', 'Close YourRoad');\n this.closeButton.textContent = '×';\n this.iframe = new IframeController(source, onFrameError);\n panel.append(this.closeButton, this.iframe.element);\n this.root.append(scrim, panel);\n const signal = this.abort.signal;\n scrim.addEventListener('click', onClose, { signal });\n this.closeButton.addEventListener('click', onClose, { signal });\n document.addEventListener('keydown', (event) => { if (event.key === 'Escape') onClose(); }, { signal });\n }\n\n mount(): void { document.body.append(this.root); }\n destroy(): void {\n this.abort.abort();\n this.iframe.destroy();\n this.root.remove();\n if (!document.querySelector('[data-yourroad-overlay-root]')) {\n document.head.querySelector('style[data-yourroad-sdk]')?.remove();\n }\n }\n}\n","export const JOURNEY_SESSION_MARKER_KEY = 'yr_journey_session_pre_approval_v1';\n\nexport const isUuid = (value: unknown): value is string =>\n typeof value === 'string'\n && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);\n\nexport interface SessionMarker {\n read(): string | undefined;\n write(sessionId: string): void;\n clear(): void;\n}\n\ntype MarkerStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;\n\nfunction resolveStorage(): MarkerStorage | undefined {\n try { return globalThis.localStorage; } catch { return undefined; }\n}\n\nexport function createSessionMarker(storage: MarkerStorage | undefined = resolveStorage()): SessionMarker {\n const clear = (): void => {\n try { storage?.removeItem(JOURNEY_SESSION_MARKER_KEY); } catch { /* Storage is optional. */ }\n };\n return {\n read() {\n let value: string | null;\n try { value = storage?.getItem(JOURNEY_SESSION_MARKER_KEY) ?? null; } catch { return undefined; }\n if (value === null) return undefined;\n if (!isUuid(value)) { clear(); return undefined; }\n return value;\n },\n write(sessionId) {\n if (!isUuid(sessionId)) return;\n try { storage?.setItem(JOURNEY_SESSION_MARKER_KEY, sessionId); } catch { /* Keep the in-memory session usable. */ }\n },\n clear,\n };\n}\n","import { isUuid } from './sessionMarker';\n\nconst CREATE_ENDPOINT = 'https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/create-embed-journey-session';\nconst PROBE_ENDPOINT = 'https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/probe-embed-journey-session';\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (value === null || typeof value !== 'object') return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\nconst isDate = (value: unknown): value is string => typeof value === 'string' && value.trim() !== '' && !Number.isNaN(Date.parse(value));\nconst options = (body: object, signal?: AbortSignal): RequestInit => ({\n method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body),\n credentials: 'omit', cache: 'no-store', signal,\n});\n\nexport async function createJourneySession(partnerKey: string, signal?: AbortSignal): Promise<string> {\n const response = await fetch(CREATE_ENDPOINT, options({ partnerKey, surface: 'pre-approval' }, signal));\n if (response.status !== 201) throw new Error('Journey session creation was unsuccessful.');\n const value: unknown = await response.json();\n if (!isPlainObject(value) || !isUuid(value.sessionId) || !isDate(value.expiresAt)) throw new Error('Invalid journey session response.');\n return value.sessionId;\n}\n\nexport type ProbeResult = { status: 'resumable'; sessionId: string } | { status: 'expired' } | { status: 'invalid' };\n\nexport async function probeJourneySession(partnerKey: string, sessionId: string): Promise<ProbeResult> {\n const response = await fetch(PROBE_ENDPOINT, options({ partnerKey, surface: 'pre-approval', sessionId }));\n const value: unknown = await response.json();\n if (!isPlainObject(value)) throw new Error('Invalid journey session probe response.');\n if (response.status === 200 && value.status === 'resumable' && value.sessionId === sessionId && isDate(value.expiresAt)) {\n return { status: 'resumable', sessionId };\n }\n if (response.status === 410 && value.status === 'expired' && value.code === 'SESSION_EXPIRED') return { status: 'expired' };\n if (response.status === 404 && value.status === 'invalid' && value.code === 'SESSION_INVALID') return { status: 'invalid' };\n throw new Error('Journey session probe was unsuccessful.');\n}\n","import { createJourneySession, probeJourneySession } from './sessionClient';\nimport { createSessionMarker, type SessionMarker } from './sessionMarker';\n\ntype State =\n | { kind: 'cold' }\n | { kind: 'probing'; completion: Promise<void> }\n | { kind: 'active'; sessionId: string }\n | { kind: 'expired'; sessionId: string }\n | { kind: 'probe_failed' };\n\nexport class SessionController {\n private state: State = { kind: 'cold' };\n private restart?: Promise<string>;\n\n constructor(private readonly partnerKey: string, private readonly marker: SessionMarker = createSessionMarker()) {\n const sessionId = marker.read();\n if (!sessionId) return;\n const completion = this.probe(sessionId);\n this.state = { kind: 'probing', completion };\n }\n\n async resolveForOpen(signal: AbortSignal): Promise<string> {\n if (this.state.kind === 'probing') await this.state.completion;\n if (this.state.kind === 'probe_failed') throw new Error('Journey session validity could not be established.');\n if (this.state.kind === 'active' || this.state.kind === 'expired') return this.state.sessionId;\n const sessionId = await createJourneySession(this.partnerKey, signal);\n this.marker.write(sessionId);\n this.state = { kind: 'active', sessionId };\n return sessionId;\n }\n\n restartExpired(signal: AbortSignal): Promise<string> | undefined {\n if (this.state.kind !== 'expired') return undefined;\n if (this.restart) return this.restart;\n const operation = createJourneySession(this.partnerKey, signal).then((sessionId) => {\n this.marker.write(sessionId);\n this.state = { kind: 'active', sessionId };\n return sessionId;\n }).finally(() => { if (this.restart === operation) this.restart = undefined; });\n this.restart = operation;\n return operation;\n }\n\n private async probe(sessionId: string): Promise<void> {\n try {\n const result = await probeJourneySession(this.partnerKey, sessionId);\n if (result.status === 'resumable') this.state = { kind: 'active', sessionId };\n else if (result.status === 'expired') { this.marker.clear(); this.state = { kind: 'expired', sessionId }; }\n else { this.marker.clear(); this.state = { kind: 'cold' }; }\n } catch { this.state = { kind: 'probe_failed' }; }\n }\n}\n","import type { YourRoadInitConfig, YourRoadSurface } from '../config/types';\nimport { requestBootstrap } from '../bootstrap/bootstrapClient';\nimport { BridgeController, runtimeOriginFromIframeUrl } from '../bridge/BridgeController';\nimport { copyContext, mergeContext } from '../context/mergeContext';\nimport type { YourRoadContext, YourRoadContextUpdate } from '../context/types';\nimport { YourRoadError } from '../errors/YourRoadError';\nimport { EventEmitter } from '../events/EventEmitter';\nimport { FocusLifecycle } from '../lifecycle/focus';\nimport { ScrollLock } from '../lifecycle/scrollLock';\nimport { OverlayController } from '../overlay/OverlayController';\nimport { SessionController } from '../session/SessionController';\n\nexport interface OpenOptions { surface: YourRoadSurface; context?: YourRoadContext }\nexport interface YourRoadEvents { opened: undefined; closed: undefined; error: YourRoadError }\n\nexport interface YourRoadClient {\n open(options: OpenOptions): Promise<void>;\n updateContext(update: YourRoadContextUpdate): void;\n close(): void;\n on<K extends keyof YourRoadEvents>(event: K, handler: (payload: YourRoadEvents[K]) => void): () => void;\n destroy(): void;\n}\n\nclass YourRoadClientImpl implements YourRoadClient {\n private readonly events = new EventEmitter<YourRoadEvents>();\n private readonly scroll = new ScrollLock();\n private readonly focus = new FocusLifecycle();\n private overlay?: OverlayController;\n private bridge?: BridgeController;\n private context: YourRoadContext = {};\n private destroyed = false;\n private opening?: Promise<void>;\n private bootstrapAbort?: AbortController;\n private restartAbort?: AbortController;\n private readonly session: SessionController;\n\n constructor(private readonly config: Readonly<YourRoadInitConfig>) {\n this.session = new SessionController(config.partnerKey);\n }\n\n async open(options: OpenOptions): Promise<void> {\n this.assertUsable();\n if (!options || options.surface !== 'pre-approval') throw this.fail('INVALID_SURFACE', 'The only supported surface is \"pre-approval\".');\n if (this.overlay) return;\n if (this.opening) return this.opening;\n this.context = copyContext(options.context);\n const abort = new AbortController();\n this.bootstrapAbort = abort;\n const operation = this.performOpen(options.surface, abort);\n this.opening = operation;\n return operation;\n }\n\n updateContext(update: YourRoadContextUpdate): void {\n this.assertUsable();\n this.context = mergeContext(this.context, update);\n this.bridge?.contextUpdated(this.context);\n }\n\n close(): void {\n this.assertUsable();\n if (this.bootstrapAbort) {\n this.bootstrapAbort.abort();\n this.bootstrapAbort = undefined;\n this.opening = undefined;\n }\n this.restartAbort?.abort();\n this.restartAbort = undefined;\n if (!this.overlay) return;\n this.bridge?.destroy();\n this.bridge = undefined;\n this.overlay.destroy();\n this.overlay = undefined;\n this.scroll.unlock();\n this.focus.restore();\n this.events.emit('closed', undefined);\n }\n\n on<K extends keyof YourRoadEvents>(event: K, handler: (payload: YourRoadEvents[K]) => void): () => void {\n this.assertUsable();\n return this.events.on(event, handler);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.close();\n this.events.clear();\n this.context = {};\n this.destroyed = true;\n }\n\n private async performOpen(surface: YourRoadSurface, abort: AbortController): Promise<void> {\n try {\n const [bootstrap, sessionId] = await Promise.all([\n requestBootstrap({ partnerKey: this.config.partnerKey, surface }, abort.signal),\n this.session.resolveForOpen(abort.signal),\n ]);\n const { iframeUrl } = bootstrap;\n if (abort.signal.aborted || this.destroyed || this.bootstrapAbort !== abort) return;\n this.focus.capture();\n try {\n this.overlay = new OverlayController(iframeUrl, () => this.close(), () => this.fail('IFRAME_LOAD_FAILED', 'The embedded experience failed to load.'));\n this.bridge = new BridgeController(\n this.overlay.iframe.element,\n runtimeOriginFromIframeUrl(iframeUrl),\n () => copyContext(this.context),\n () => sessionId,\n () => this.restartExpiredSession(this.bridge),\n );\n this.bridge.start();\n this.overlay.mount();\n this.scroll.lock();\n this.focus.moveTo(this.overlay.closeButton);\n this.events.emit('opened', undefined);\n } catch (cause) {\n this.bridge?.destroy();\n this.bridge = undefined;\n this.overlay?.destroy();\n this.overlay = undefined;\n this.scroll.unlock();\n this.focus.restore();\n throw this.fail('OVERLAY_CREATION_FAILED', 'The YourRoad overlay could not be created.', cause);\n }\n } catch (cause) {\n if (abort.signal.aborted) return;\n if (cause instanceof YourRoadError) throw cause;\n throw this.fail('BOOTSTRAP_FAILED', 'The YourRoad experience could not be started securely.', cause);\n } finally {\n if (this.bootstrapAbort === abort) {\n this.bootstrapAbort = undefined;\n this.opening = undefined;\n }\n }\n }\n\n private restartExpiredSession(bridge: BridgeController | undefined): void {\n if (this.destroyed || bridge === undefined || bridge !== this.bridge || this.restartAbort) return;\n const restartAbort = new AbortController();\n this.restartAbort = restartAbort;\n const operation = this.session.restartExpired(restartAbort.signal);\n if (!operation) { this.restartAbort = undefined; return; }\n void operation.then((sessionId) => {\n if (!this.destroyed && !restartAbort.signal.aborted && this.bridge === bridge) bridge.sendJourneySession(sessionId);\n }).catch(() => { /* The runtime may offer a later explicit retry. */ }).finally(() => {\n if (this.restartAbort === restartAbort) this.restartAbort = undefined;\n });\n }\n\n private assertUsable(): void { if (this.destroyed) throw new YourRoadError('DESTROYED', 'This YourRoad SDK instance has been destroyed.'); }\n private fail(code: ConstructorParameters<typeof YourRoadError>[0], message: string, cause?: unknown): YourRoadError {\n const error = new YourRoadError(code, message, cause === undefined ? undefined : { cause });\n this.events.emit('error', error);\n return error;\n }\n}\n\nexport function createYourRoadClient(config: Readonly<YourRoadInitConfig>): YourRoadClient {\n return new YourRoadClientImpl(config);\n}\n","import { createYourRoadClient, type YourRoadClient } from './client/YourRoadClient';\nimport type { YourRoadInitConfig } from './config/types';\nimport { YourRoadError } from './errors/YourRoadError';\n\nexport type { OpenOptions, YourRoadClient, YourRoadEvents } from './client/YourRoadClient';\nexport type { YourRoadInitConfig, YourRoadSurface } from './config/types';\nexport type { DealerContext, PageContext, VehicleContext, YourRoadContext, YourRoadContextUpdate } from './context/types';\nexport { YourRoadError };\n\nexport class YourRoad {\n static init(config: YourRoadInitConfig): YourRoadClient {\n if (!config || typeof config.partnerKey !== 'string' || config.partnerKey.trim() === '') {\n throw new YourRoadError('INVALID_INITIALISATION', 'partnerKey must be a non-empty string.');\n }\n return createYourRoadClient({ partnerKey: config.partnerKey });\n }\n}\n"],"names":["BOOTSTRAP_ENDPOINT","validateResponse","value","iframeUrl","expiresAt","parsedUrl","requestBootstrap","request","signal","response","defined","item","mergeContext","current","update","next","key","copyContext","context","BRIDGE_VERSION","runtimeOriginFromIframeUrl","url","isPlainObject","prototype","isReadyMessage","isRestartMessage","BridgeController","iframe","trustedRuntimeOrigin","getCurrentContext","getJourneySessionId","onJourneySessionRestart","__publicField","event","iframeWindow","currentContext","sessionId","message","YourRoadError","code","options","EventEmitter","handler","handlers","payload","_a","FocusLifecycle","element","ScrollLock","style","IframeController","source","onError","SDK_Z_INDEX","SDK_STYLES","ensureStyles","existing","OverlayController","onClose","onFrameError","scrim","panel","JOURNEY_SESSION_MARKER_KEY","isUuid","resolveStorage","createSessionMarker","storage","clear","CREATE_ENDPOINT","PROBE_ENDPOINT","isDate","body","createJourneySession","partnerKey","probeJourneySession","SessionController","marker","completion","operation","result","YourRoadClientImpl","config","abort","_b","surface","bootstrap","cause","bridge","restartAbort","error","createYourRoadClient","YourRoad"],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ export declare interface DealerContext {
2
+ id?: string;
3
+ name?: string;
4
+ }
5
+
6
+ export declare interface OpenOptions {
7
+ surface: YourRoadSurface;
8
+ context?: YourRoadContext;
9
+ }
10
+
11
+ export declare interface PageContext {
12
+ type?: string;
13
+ source?: string;
14
+ url?: string;
15
+ }
16
+
17
+ export declare interface VehicleContext {
18
+ id?: string;
19
+ source?: string;
20
+ sourceId?: string;
21
+ registration?: string;
22
+ make?: string;
23
+ model?: string;
24
+ variant?: string;
25
+ year?: number;
26
+ price?: number;
27
+ mileage?: number;
28
+ fuelType?: string;
29
+ imageUrl?: string;
30
+ transmission?: string;
31
+ bodyType?: string;
32
+ listingUrl?: string;
33
+ }
34
+
35
+ export declare class YourRoad {
36
+ static init(config: YourRoadInitConfig): YourRoadClient;
37
+ }
38
+
39
+ export declare interface YourRoadClient {
40
+ open(options: OpenOptions): Promise<void>;
41
+ updateContext(update: YourRoadContextUpdate): void;
42
+ close(): void;
43
+ on<K extends keyof YourRoadEvents>(event: K, handler: (payload: YourRoadEvents[K]) => void): () => void;
44
+ destroy(): void;
45
+ }
46
+
47
+ export declare interface YourRoadContext {
48
+ placement?: string;
49
+ vehicle?: VehicleContext;
50
+ dealer?: DealerContext;
51
+ page?: PageContext;
52
+ }
53
+
54
+ export declare type YourRoadContextUpdate = {
55
+ placement?: string;
56
+ vehicle?: Partial<VehicleContext>;
57
+ dealer?: Partial<DealerContext>;
58
+ page?: Partial<PageContext>;
59
+ };
60
+
61
+ export declare class YourRoadError extends Error {
62
+ readonly code: YourRoadErrorCode;
63
+ readonly name = "YourRoadError";
64
+ constructor(code: YourRoadErrorCode, message: string, options?: ErrorOptions);
65
+ }
66
+
67
+ declare type YourRoadErrorCode = 'INVALID_INITIALISATION' | 'INVALID_SURFACE' | 'DESTROYED' | 'BOOTSTRAP_FAILED' | 'OVERLAY_CREATION_FAILED' | 'IFRAME_LOAD_FAILED';
68
+
69
+ export declare interface YourRoadEvents {
70
+ opened: undefined;
71
+ closed: undefined;
72
+ error: YourRoadError;
73
+ }
74
+
75
+ export declare interface YourRoadInitConfig {
76
+ partnerKey: string;
77
+ }
78
+
79
+ export declare type YourRoadSurface = 'pre-approval';
80
+
81
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,405 @@
1
+ var E = Object.defineProperty;
2
+ var k = (s, t, e) => t in s ? E(s, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[t] = e;
3
+ var o = (s, t, e) => k(s, typeof t != "symbol" ? t + "" : t, e);
4
+ const S = "https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/bootstrap-embed-access";
5
+ function A(s) {
6
+ if (typeof s != "object" || s === null || Array.isArray(s)) throw new Error("Invalid bootstrap response.");
7
+ const { iframeUrl: t, expiresAt: e } = s;
8
+ if (typeof t != "string" || t.trim() === "") throw new Error("Invalid bootstrap response.");
9
+ let r;
10
+ try {
11
+ r = new URL(t);
12
+ } catch {
13
+ throw new Error("Invalid bootstrap response.");
14
+ }
15
+ if (r.protocol !== "http:" && r.protocol !== "https:") throw new Error("Invalid bootstrap response.");
16
+ if (typeof e != "string" || e.trim() === "" || Number.isNaN(Date.parse(e)))
17
+ throw new Error("Invalid bootstrap response.");
18
+ return { iframeUrl: t, expiresAt: e };
19
+ }
20
+ async function O(s, t) {
21
+ const e = await fetch(S, {
22
+ method: "POST",
23
+ headers: { "Content-Type": "application/json" },
24
+ body: JSON.stringify(s),
25
+ credentials: "omit",
26
+ cache: "no-store",
27
+ signal: t
28
+ });
29
+ if (!e.ok) throw new Error("Bootstrap request was unsuccessful.");
30
+ return A(await e.json());
31
+ }
32
+ const f = (s) => Object.fromEntries(Object.entries(s ?? {}).filter(([, t]) => t !== void 0));
33
+ function b(s, t) {
34
+ const e = { ...s, ...f(t) };
35
+ for (const r of ["vehicle", "dealer", "page"])
36
+ t[r] !== void 0 && (e[r] = { ...s[r], ...f(t[r]) });
37
+ return e;
38
+ }
39
+ const h = (s) => b({}, s ?? {}), a = 1;
40
+ function I(s) {
41
+ const t = new URL(s);
42
+ if (t.protocol !== "https:" && t.protocol !== "http:") throw new TypeError("The iframe URL must use HTTP(S).");
43
+ return t.origin;
44
+ }
45
+ const m = (s) => {
46
+ if (s === null || typeof s != "object") return !1;
47
+ const t = Object.getPrototypeOf(s);
48
+ return t === Object.prototype || t === null;
49
+ }, R = (s) => m(s) ? s.source === "yourroad.embed" && s.version === a && s.type === "ready" && s.payload === void 0 && (s.requestId === void 0 || typeof s.requestId == "string") : !1, j = (s) => m(s) && Object.keys(s).length === 3 && s.source === "yourroad.embed" && s.version === a && s.type === "journey_session_restart";
50
+ class T {
51
+ constructor(t, e, r, i, n) {
52
+ o(this, "ready", !1);
53
+ o(this, "started", !1);
54
+ o(this, "handleMessage", (t) => {
55
+ if (t.origin !== this.trustedRuntimeOrigin) return;
56
+ const e = this.iframe.contentWindow;
57
+ if (!(e === null || t.source !== e)) {
58
+ if (this.ready) {
59
+ j(t.data) && this.onJourneySessionRestart();
60
+ return;
61
+ }
62
+ R(t.data) && (this.ready = !0, this.post({ source: "yourroad.sdk", version: a, type: "ack" }), this.post({
63
+ source: "yourroad.sdk",
64
+ version: a,
65
+ type: "context",
66
+ payload: h(this.getCurrentContext())
67
+ }), this.sendJourneySession(this.getJourneySessionId()));
68
+ }
69
+ });
70
+ this.iframe = t, this.trustedRuntimeOrigin = e, this.getCurrentContext = r, this.getJourneySessionId = i, this.onJourneySessionRestart = n;
71
+ }
72
+ start() {
73
+ this.started || (window.addEventListener("message", this.handleMessage), this.started = !0);
74
+ }
75
+ contextUpdated(t) {
76
+ this.ready && this.post({
77
+ source: "yourroad.sdk",
78
+ version: a,
79
+ type: "context_updated",
80
+ payload: h(t)
81
+ });
82
+ }
83
+ destroy() {
84
+ this.started && window.removeEventListener("message", this.handleMessage), this.started = !1, this.ready = !1;
85
+ }
86
+ sendJourneySession(t) {
87
+ this.ready && this.post({ source: "yourroad.sdk", version: a, type: "journey_session", payload: { sessionId: t } });
88
+ }
89
+ post(t) {
90
+ const e = this.iframe.contentWindow;
91
+ if (e !== null)
92
+ try {
93
+ e.postMessage(t, this.trustedRuntimeOrigin);
94
+ } catch {
95
+ }
96
+ }
97
+ }
98
+ class l extends Error {
99
+ constructor(e, r, i) {
100
+ super(r, i);
101
+ o(this, "name", "YourRoadError");
102
+ this.code = e;
103
+ }
104
+ }
105
+ class _ {
106
+ constructor() {
107
+ o(this, "listeners", /* @__PURE__ */ new Map());
108
+ }
109
+ on(t, e) {
110
+ const r = this.listeners.get(t) ?? /* @__PURE__ */ new Set();
111
+ return r.add(e), this.listeners.set(t, r), () => r.delete(e);
112
+ }
113
+ emit(t, e) {
114
+ var r;
115
+ (r = this.listeners.get(t)) == null || r.forEach((i) => i(e));
116
+ }
117
+ clear() {
118
+ this.listeners.clear();
119
+ }
120
+ }
121
+ class N {
122
+ constructor() {
123
+ o(this, "previous", null);
124
+ }
125
+ capture() {
126
+ this.previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
127
+ }
128
+ moveTo(t) {
129
+ t.focus();
130
+ }
131
+ restore() {
132
+ var t;
133
+ (t = this.previous) != null && t.isConnected && this.previous.focus(), this.previous = null;
134
+ }
135
+ }
136
+ class C {
137
+ constructor() {
138
+ o(this, "saved");
139
+ o(this, "scrollX", 0);
140
+ o(this, "scrollY", 0);
141
+ }
142
+ lock() {
143
+ if (this.saved) return;
144
+ const t = document.body.style;
145
+ this.saved = { position: t.position, top: t.top, left: t.left, right: t.right, width: t.width, overflow: t.overflow }, this.scrollX = window.scrollX, this.scrollY = window.scrollY, Object.assign(t, { position: "fixed", top: `-${this.scrollY}px`, left: `-${this.scrollX}px`, right: "0", width: "100%", overflow: "hidden" });
146
+ }
147
+ unlock() {
148
+ this.saved && (Object.assign(document.body.style, this.saved), this.saved = void 0, window.scrollTo(this.scrollX, this.scrollY));
149
+ }
150
+ }
151
+ class L {
152
+ constructor(t, e) {
153
+ o(this, "element");
154
+ this.element = document.createElement("iframe"), this.element.dataset.yourroadFrame = "", this.element.title = "YourRoad pre-approval", this.element.src = t, this.element.addEventListener("error", e, { once: !0 });
155
+ }
156
+ destroy() {
157
+ this.element.remove();
158
+ }
159
+ }
160
+ const D = 2147483e3, Y = `
161
+ [data-yourroad-overlay-root] { all: initial; position: fixed; inset: 0; z-index: ${D}; display: block; font-family: system-ui, sans-serif; color: #111; }
162
+ [data-yourroad-scrim] { all: initial; position: absolute; inset: 0; display: block; background: rgba(0, 0, 0, .55); }
163
+ [data-yourroad-panel] { all: initial; box-sizing: border-box; position: absolute; inset-block: 0; inset-inline-end: 0; display: flex; flex-direction: column; width: min(520px, 92vw); height: 100vh; height: 100dvh; background: #fff; box-shadow: -8px 0 30px rgba(0,0,0,.2); }
164
+ [data-yourroad-close] { all: unset; box-sizing: border-box; position: absolute; z-index: 1; inset-block-start: max(12px, env(safe-area-inset-top)); inset-inline-end: max(12px, env(safe-area-inset-right)); width: 44px; height: 44px; display: grid; place-items: center; border-radius: 999px; background: #fff; color: #111; box-shadow: 0 1px 8px rgba(0,0,0,.25); cursor: pointer; font: 28px/1 system-ui, sans-serif; }
165
+ [data-yourroad-close]:focus-visible { outline: 3px solid #1769e0; outline-offset: 2px; }
166
+ [data-yourroad-frame] { all: initial; box-sizing: border-box; display: block; width: 100%; height: 100%; border: 0; background: #fff; }
167
+ @media (max-width: 600px) { [data-yourroad-panel] { width: 100vw; max-width: none; } }
168
+ `;
169
+ function K() {
170
+ const s = document.head.querySelector("style[data-yourroad-sdk]");
171
+ if (s) return s;
172
+ const t = document.createElement("style");
173
+ return t.dataset.yourroadSdk = "", t.textContent = Y, document.head.append(t), t;
174
+ }
175
+ class U {
176
+ constructor(t, e, r) {
177
+ o(this, "root");
178
+ o(this, "closeButton");
179
+ o(this, "iframe");
180
+ o(this, "abort", new AbortController());
181
+ K(), this.root = document.createElement("div"), this.root.dataset.yourroadOverlayRoot = "", this.root.setAttribute("role", "dialog"), this.root.setAttribute("aria-modal", "true"), this.root.setAttribute("aria-label", "YourRoad pre-approval");
182
+ const i = document.createElement("div");
183
+ i.dataset.yourroadScrim = "";
184
+ const n = document.createElement("div");
185
+ n.dataset.yourroadPanel = "", this.closeButton = document.createElement("button"), this.closeButton.type = "button", this.closeButton.dataset.yourroadClose = "", this.closeButton.setAttribute("aria-label", "Close YourRoad"), this.closeButton.textContent = "×", this.iframe = new L(t, r), n.append(this.closeButton, this.iframe.element), this.root.append(i, n);
186
+ const d = this.abort.signal;
187
+ i.addEventListener("click", e, { signal: d }), this.closeButton.addEventListener("click", e, { signal: d }), document.addEventListener("keydown", (c) => {
188
+ c.key === "Escape" && e();
189
+ }, { signal: d });
190
+ }
191
+ mount() {
192
+ document.body.append(this.root);
193
+ }
194
+ destroy() {
195
+ var t;
196
+ this.abort.abort(), this.iframe.destroy(), this.root.remove(), document.querySelector("[data-yourroad-overlay-root]") || (t = document.head.querySelector("style[data-yourroad-sdk]")) == null || t.remove();
197
+ }
198
+ }
199
+ const u = "yr_journey_session_pre_approval_v1", p = (s) => typeof s == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(s);
200
+ function B() {
201
+ try {
202
+ return globalThis.localStorage;
203
+ } catch {
204
+ return;
205
+ }
206
+ }
207
+ function P(s = B()) {
208
+ const t = () => {
209
+ try {
210
+ s == null || s.removeItem(u);
211
+ } catch {
212
+ }
213
+ };
214
+ return {
215
+ read() {
216
+ let e;
217
+ try {
218
+ e = (s == null ? void 0 : s.getItem(u)) ?? null;
219
+ } catch {
220
+ return;
221
+ }
222
+ if (e !== null) {
223
+ if (!p(e)) {
224
+ t();
225
+ return;
226
+ }
227
+ return e;
228
+ }
229
+ },
230
+ write(e) {
231
+ if (p(e))
232
+ try {
233
+ s == null || s.setItem(u, e);
234
+ } catch {
235
+ }
236
+ },
237
+ clear: t
238
+ };
239
+ }
240
+ const J = "https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/create-embed-journey-session", q = "https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/probe-embed-journey-session", v = (s) => {
241
+ if (s === null || typeof s != "object") return !1;
242
+ const t = Object.getPrototypeOf(s);
243
+ return t === Object.prototype || t === null;
244
+ }, w = (s) => typeof s == "string" && s.trim() !== "" && !Number.isNaN(Date.parse(s)), x = (s, t) => ({
245
+ method: "POST",
246
+ headers: { "Content-Type": "application/json" },
247
+ body: JSON.stringify(s),
248
+ credentials: "omit",
249
+ cache: "no-store",
250
+ signal: t
251
+ });
252
+ async function y(s, t) {
253
+ const e = await fetch(J, x({ partnerKey: s, surface: "pre-approval" }, t));
254
+ if (e.status !== 201) throw new Error("Journey session creation was unsuccessful.");
255
+ const r = await e.json();
256
+ if (!v(r) || !p(r.sessionId) || !w(r.expiresAt)) throw new Error("Invalid journey session response.");
257
+ return r.sessionId;
258
+ }
259
+ async function M(s, t) {
260
+ const e = await fetch(q, x({ partnerKey: s, surface: "pre-approval", sessionId: t })), r = await e.json();
261
+ if (!v(r)) throw new Error("Invalid journey session probe response.");
262
+ if (e.status === 200 && r.status === "resumable" && r.sessionId === t && w(r.expiresAt))
263
+ return { status: "resumable", sessionId: t };
264
+ if (e.status === 410 && r.status === "expired" && r.code === "SESSION_EXPIRED") return { status: "expired" };
265
+ if (e.status === 404 && r.status === "invalid" && r.code === "SESSION_INVALID") return { status: "invalid" };
266
+ throw new Error("Journey session probe was unsuccessful.");
267
+ }
268
+ class F {
269
+ constructor(t, e = P()) {
270
+ o(this, "state", { kind: "cold" });
271
+ o(this, "restart");
272
+ this.partnerKey = t, this.marker = e;
273
+ const r = e.read();
274
+ if (!r) return;
275
+ const i = this.probe(r);
276
+ this.state = { kind: "probing", completion: i };
277
+ }
278
+ async resolveForOpen(t) {
279
+ if (this.state.kind === "probing" && await this.state.completion, this.state.kind === "probe_failed") throw new Error("Journey session validity could not be established.");
280
+ if (this.state.kind === "active" || this.state.kind === "expired") return this.state.sessionId;
281
+ const e = await y(this.partnerKey, t);
282
+ return this.marker.write(e), this.state = { kind: "active", sessionId: e }, e;
283
+ }
284
+ restartExpired(t) {
285
+ if (this.state.kind !== "expired") return;
286
+ if (this.restart) return this.restart;
287
+ const e = y(this.partnerKey, t).then((r) => (this.marker.write(r), this.state = { kind: "active", sessionId: r }, r)).finally(() => {
288
+ this.restart === e && (this.restart = void 0);
289
+ });
290
+ return this.restart = e, e;
291
+ }
292
+ async probe(t) {
293
+ try {
294
+ const e = await M(this.partnerKey, t);
295
+ e.status === "resumable" ? this.state = { kind: "active", sessionId: t } : e.status === "expired" ? (this.marker.clear(), this.state = { kind: "expired", sessionId: t }) : (this.marker.clear(), this.state = { kind: "cold" });
296
+ } catch {
297
+ this.state = { kind: "probe_failed" };
298
+ }
299
+ }
300
+ }
301
+ class X {
302
+ constructor(t) {
303
+ o(this, "events", new _());
304
+ o(this, "scroll", new C());
305
+ o(this, "focus", new N());
306
+ o(this, "overlay");
307
+ o(this, "bridge");
308
+ o(this, "context", {});
309
+ o(this, "destroyed", !1);
310
+ o(this, "opening");
311
+ o(this, "bootstrapAbort");
312
+ o(this, "restartAbort");
313
+ o(this, "session");
314
+ this.config = t, this.session = new F(t.partnerKey);
315
+ }
316
+ async open(t) {
317
+ if (this.assertUsable(), !t || t.surface !== "pre-approval") throw this.fail("INVALID_SURFACE", 'The only supported surface is "pre-approval".');
318
+ if (this.overlay) return;
319
+ if (this.opening) return this.opening;
320
+ this.context = h(t.context);
321
+ const e = new AbortController();
322
+ this.bootstrapAbort = e;
323
+ const r = this.performOpen(t.surface, e);
324
+ return this.opening = r, r;
325
+ }
326
+ updateContext(t) {
327
+ var e;
328
+ this.assertUsable(), this.context = b(this.context, t), (e = this.bridge) == null || e.contextUpdated(this.context);
329
+ }
330
+ close() {
331
+ var t, e;
332
+ this.assertUsable(), this.bootstrapAbort && (this.bootstrapAbort.abort(), this.bootstrapAbort = void 0, this.opening = void 0), (t = this.restartAbort) == null || t.abort(), this.restartAbort = void 0, this.overlay && ((e = this.bridge) == null || e.destroy(), this.bridge = void 0, this.overlay.destroy(), this.overlay = void 0, this.scroll.unlock(), this.focus.restore(), this.events.emit("closed", void 0));
333
+ }
334
+ on(t, e) {
335
+ return this.assertUsable(), this.events.on(t, e);
336
+ }
337
+ destroy() {
338
+ this.destroyed || (this.close(), this.events.clear(), this.context = {}, this.destroyed = !0);
339
+ }
340
+ async performOpen(t, e) {
341
+ var r, i;
342
+ try {
343
+ const [n, d] = await Promise.all([
344
+ O({ partnerKey: this.config.partnerKey, surface: t }, e.signal),
345
+ this.session.resolveForOpen(e.signal)
346
+ ]), { iframeUrl: c } = n;
347
+ if (e.signal.aborted || this.destroyed || this.bootstrapAbort !== e) return;
348
+ this.focus.capture();
349
+ try {
350
+ this.overlay = new U(c, () => this.close(), () => this.fail("IFRAME_LOAD_FAILED", "The embedded experience failed to load.")), this.bridge = new T(
351
+ this.overlay.iframe.element,
352
+ I(c),
353
+ () => h(this.context),
354
+ () => d,
355
+ () => this.restartExpiredSession(this.bridge)
356
+ ), this.bridge.start(), this.overlay.mount(), this.scroll.lock(), this.focus.moveTo(this.overlay.closeButton), this.events.emit("opened", void 0);
357
+ } catch (g) {
358
+ throw (r = this.bridge) == null || r.destroy(), this.bridge = void 0, (i = this.overlay) == null || i.destroy(), this.overlay = void 0, this.scroll.unlock(), this.focus.restore(), this.fail("OVERLAY_CREATION_FAILED", "The YourRoad overlay could not be created.", g);
359
+ }
360
+ } catch (n) {
361
+ if (e.signal.aborted) return;
362
+ throw n instanceof l ? n : this.fail("BOOTSTRAP_FAILED", "The YourRoad experience could not be started securely.", n);
363
+ } finally {
364
+ this.bootstrapAbort === e && (this.bootstrapAbort = void 0, this.opening = void 0);
365
+ }
366
+ }
367
+ restartExpiredSession(t) {
368
+ if (this.destroyed || t === void 0 || t !== this.bridge || this.restartAbort) return;
369
+ const e = new AbortController();
370
+ this.restartAbort = e;
371
+ const r = this.session.restartExpired(e.signal);
372
+ if (!r) {
373
+ this.restartAbort = void 0;
374
+ return;
375
+ }
376
+ r.then((i) => {
377
+ !this.destroyed && !e.signal.aborted && this.bridge === t && t.sendJourneySession(i);
378
+ }).catch(() => {
379
+ }).finally(() => {
380
+ this.restartAbort === e && (this.restartAbort = void 0);
381
+ });
382
+ }
383
+ assertUsable() {
384
+ if (this.destroyed) throw new l("DESTROYED", "This YourRoad SDK instance has been destroyed.");
385
+ }
386
+ fail(t, e, r) {
387
+ const i = new l(t, e, r === void 0 ? void 0 : { cause: r });
388
+ return this.events.emit("error", i), i;
389
+ }
390
+ }
391
+ function z(s) {
392
+ return new X(s);
393
+ }
394
+ class $ {
395
+ static init(t) {
396
+ if (!t || typeof t.partnerKey != "string" || t.partnerKey.trim() === "")
397
+ throw new l("INVALID_INITIALISATION", "partnerKey must be a non-empty string.");
398
+ return z({ partnerKey: t.partnerKey });
399
+ }
400
+ }
401
+ export {
402
+ $ as YourRoad,
403
+ l as YourRoadError
404
+ };
405
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/bootstrap/bootstrapClient.ts","../src/context/mergeContext.ts","../src/bridge/protocol.ts","../src/bridge/BridgeController.ts","../src/errors/YourRoadError.ts","../src/events/EventEmitter.ts","../src/lifecycle/focus.ts","../src/lifecycle/scrollLock.ts","../src/iframe/IframeController.ts","../src/overlay/styles.ts","../src/overlay/OverlayController.ts","../src/session/sessionMarker.ts","../src/session/sessionClient.ts","../src/session/SessionController.ts","../src/client/YourRoadClient.ts","../src/index.ts"],"sourcesContent":["import type { YourRoadSurface } from '../config/types';\n\nconst BOOTSTRAP_ENDPOINT = 'https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/bootstrap-embed-access';\n\ninterface BootstrapRequest {\n partnerKey: string;\n surface: YourRoadSurface;\n}\n\nexport interface BootstrapResult {\n iframeUrl: string;\n expiresAt: string;\n}\n\nfunction validateResponse(value: unknown): BootstrapResult {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('Invalid bootstrap response.');\n const { iframeUrl, expiresAt } = value as Record<string, unknown>;\n if (typeof iframeUrl !== 'string' || iframeUrl.trim() === '') throw new Error('Invalid bootstrap response.');\n let parsedUrl: URL;\n try { parsedUrl = new URL(iframeUrl); } catch { throw new Error('Invalid bootstrap response.'); }\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') throw new Error('Invalid bootstrap response.');\n if (typeof expiresAt !== 'string' || expiresAt.trim() === '' || Number.isNaN(Date.parse(expiresAt))) {\n throw new Error('Invalid bootstrap response.');\n }\n return { iframeUrl, expiresAt };\n}\n\nexport async function requestBootstrap(request: BootstrapRequest, signal: AbortSignal): Promise<BootstrapResult> {\n const response = await fetch(BOOTSTRAP_ENDPOINT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(request),\n credentials: 'omit',\n cache: 'no-store',\n signal,\n });\n if (!response.ok) throw new Error('Bootstrap request was unsuccessful.');\n return validateResponse(await response.json());\n}\n","import type { YourRoadContext, YourRoadContextUpdate } from './types';\n\nconst defined = <T extends object>(value: T | undefined): Partial<T> =>\n Object.fromEntries(Object.entries(value ?? {}).filter(([, item]) => item !== undefined)) as Partial<T>;\n\n/** Merges known context sections. Explicit `undefined` values are ignored. */\nexport function mergeContext(current: YourRoadContext, update: YourRoadContextUpdate): YourRoadContext {\n const next: YourRoadContext = { ...current, ...defined(update) };\n for (const key of ['vehicle', 'dealer', 'page'] as const) {\n if (update[key] !== undefined) next[key] = { ...current[key], ...defined(update[key]) };\n }\n return next;\n}\n\nexport const copyContext = (context?: YourRoadContext): YourRoadContext => mergeContext({}, context ?? {});\n","import type { YourRoadContext } from '../context/types';\n\nexport const BRIDGE_VERSION = 1 as const;\n\nexport type BridgeMessage<T = unknown> = {\n source: 'yourroad.sdk' | 'yourroad.embed';\n version: typeof BRIDGE_VERSION;\n type: string;\n requestId?: string;\n payload?: T;\n};\n\nexport type SdkBridgeMessage =\n | BridgeMessage & { source: 'yourroad.sdk'; type: 'ack' }\n | BridgeMessage<YourRoadContext> & { source: 'yourroad.sdk'; type: 'context' | 'context_updated'; payload: YourRoadContext }\n | BridgeMessage<{ sessionId: string }> & { source: 'yourroad.sdk'; type: 'journey_session'; payload: { sessionId: string } };\n","import { copyContext } from '../context/mergeContext';\nimport type { YourRoadContext } from '../context/types';\nimport { BRIDGE_VERSION, type SdkBridgeMessage } from './protocol';\n\nexport function runtimeOriginFromIframeUrl(iframeUrl: string): string {\n const url = new URL(iframeUrl);\n if (url.protocol !== 'https:' && url.protocol !== 'http:') throw new TypeError('The iframe URL must use HTTP(S).');\n return url.origin;\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (value === null || typeof value !== 'object') return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\n\nconst isReadyMessage = (value: unknown): boolean => {\n if (!isPlainObject(value)) return false;\n return value.source === 'yourroad.embed'\n && value.version === BRIDGE_VERSION\n && value.type === 'ready'\n && value.payload === undefined\n && (value.requestId === undefined || typeof value.requestId === 'string');\n};\n\nconst isRestartMessage = (value: unknown): boolean => isPlainObject(value)\n && Object.keys(value).length === 3\n && value.source === 'yourroad.embed'\n && value.version === BRIDGE_VERSION\n && value.type === 'journey_session_restart';\n\nexport class BridgeController {\n private ready = false;\n private started = false;\n\n constructor(\n private readonly iframe: HTMLIFrameElement,\n private readonly trustedRuntimeOrigin: string,\n private readonly getCurrentContext: () => YourRoadContext,\n private readonly getJourneySessionId: () => string,\n private readonly onJourneySessionRestart: () => void,\n ) {}\n\n start(): void {\n if (this.started) return;\n window.addEventListener('message', this.handleMessage);\n this.started = true;\n }\n\n contextUpdated(currentContext: YourRoadContext): void {\n if (!this.ready) return;\n this.post({\n source: 'yourroad.sdk',\n version: BRIDGE_VERSION,\n type: 'context_updated',\n payload: copyContext(currentContext),\n });\n }\n\n destroy(): void {\n if (this.started) window.removeEventListener('message', this.handleMessage);\n this.started = false;\n this.ready = false;\n }\n\n private readonly handleMessage = (event: MessageEvent): void => {\n if (event.origin !== this.trustedRuntimeOrigin) return;\n const iframeWindow = this.iframe.contentWindow;\n if (iframeWindow === null || event.source !== iframeWindow) return;\n if (this.ready) {\n if (isRestartMessage(event.data)) this.onJourneySessionRestart();\n return;\n }\n if (!isReadyMessage(event.data)) return;\n this.ready = true;\n this.post({ source: 'yourroad.sdk', version: BRIDGE_VERSION, type: 'ack' });\n this.post({\n source: 'yourroad.sdk',\n version: BRIDGE_VERSION,\n type: 'context',\n payload: copyContext(this.getCurrentContext()),\n });\n this.sendJourneySession(this.getJourneySessionId());\n };\n\n sendJourneySession(sessionId: string): void {\n if (!this.ready) return;\n this.post({ source: 'yourroad.sdk', version: BRIDGE_VERSION, type: 'journey_session', payload: { sessionId } });\n }\n\n private post(message: SdkBridgeMessage): void {\n const iframeWindow = this.iframe.contentWindow;\n if (iframeWindow === null) return;\n try {\n iframeWindow.postMessage(message, this.trustedRuntimeOrigin);\n } catch {\n // The iframe may be removed concurrently; bridge delivery is best-effort.\n }\n }\n}\n","export type YourRoadErrorCode = 'INVALID_INITIALISATION' | 'INVALID_SURFACE' | 'DESTROYED' | 'BOOTSTRAP_FAILED' | 'OVERLAY_CREATION_FAILED' | 'IFRAME_LOAD_FAILED';\n\nexport class YourRoadError extends Error {\n readonly name = 'YourRoadError';\n constructor(public readonly code: YourRoadErrorCode, message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n","export type Handler<T> = (payload: T) => void;\n\nexport class EventEmitter<Events extends object> {\n private listeners = new Map<keyof Events, Set<Handler<never>>>();\n\n on<K extends keyof Events>(event: K, handler: Handler<Events[K]>): () => void {\n const handlers = this.listeners.get(event) ?? new Set();\n handlers.add(handler as Handler<never>);\n this.listeners.set(event, handlers);\n return () => handlers.delete(handler as Handler<never>);\n }\n\n emit<K extends keyof Events>(event: K, payload: Events[K]): void {\n this.listeners.get(event)?.forEach((handler) => handler(payload as never));\n }\n\n clear(): void { this.listeners.clear(); }\n}\n","export class FocusLifecycle {\n private previous: HTMLElement | null = null;\n capture(): void { this.previous = document.activeElement instanceof HTMLElement ? document.activeElement : null; }\n moveTo(element: HTMLElement): void { element.focus(); }\n restore(): void {\n if (this.previous?.isConnected) this.previous.focus();\n this.previous = null;\n }\n}\n","interface SavedStyles { position: string; top: string; left: string; right: string; width: string; overflow: string }\n\nexport class ScrollLock {\n private saved?: SavedStyles;\n private scrollX = 0;\n private scrollY = 0;\n\n lock(): void {\n if (this.saved) return;\n const style = document.body.style;\n this.saved = { position: style.position, top: style.top, left: style.left, right: style.right, width: style.width, overflow: style.overflow };\n this.scrollX = window.scrollX;\n this.scrollY = window.scrollY;\n Object.assign(style, { position: 'fixed', top: `-${this.scrollY}px`, left: `-${this.scrollX}px`, right: '0', width: '100%', overflow: 'hidden' });\n }\n\n unlock(): void {\n if (!this.saved) return;\n Object.assign(document.body.style, this.saved);\n this.saved = undefined;\n window.scrollTo(this.scrollX, this.scrollY);\n }\n}\n","export class IframeController {\n readonly element: HTMLIFrameElement;\n constructor(source: string, onError: () => void) {\n this.element = document.createElement('iframe');\n this.element.dataset.yourroadFrame = '';\n this.element.title = 'YourRoad pre-approval';\n this.element.src = source;\n this.element.addEventListener('error', onError, { once: true });\n }\n destroy(): void { this.element.remove(); }\n}\n","export const SDK_Z_INDEX = 2147483000;\n\nexport const SDK_STYLES = `\n[data-yourroad-overlay-root] { all: initial; position: fixed; inset: 0; z-index: ${SDK_Z_INDEX}; display: block; font-family: system-ui, sans-serif; color: #111; }\n[data-yourroad-scrim] { all: initial; position: absolute; inset: 0; display: block; background: rgba(0, 0, 0, .55); }\n[data-yourroad-panel] { all: initial; box-sizing: border-box; position: absolute; inset-block: 0; inset-inline-end: 0; display: flex; flex-direction: column; width: min(520px, 92vw); height: 100vh; height: 100dvh; background: #fff; box-shadow: -8px 0 30px rgba(0,0,0,.2); }\n[data-yourroad-close] { all: unset; box-sizing: border-box; position: absolute; z-index: 1; inset-block-start: max(12px, env(safe-area-inset-top)); inset-inline-end: max(12px, env(safe-area-inset-right)); width: 44px; height: 44px; display: grid; place-items: center; border-radius: 999px; background: #fff; color: #111; box-shadow: 0 1px 8px rgba(0,0,0,.25); cursor: pointer; font: 28px/1 system-ui, sans-serif; }\n[data-yourroad-close]:focus-visible { outline: 3px solid #1769e0; outline-offset: 2px; }\n[data-yourroad-frame] { all: initial; box-sizing: border-box; display: block; width: 100%; height: 100%; border: 0; background: #fff; }\n@media (max-width: 600px) { [data-yourroad-panel] { width: 100vw; max-width: none; } }\n`;\n\nexport function ensureStyles(): HTMLStyleElement {\n const existing = document.head.querySelector<HTMLStyleElement>('style[data-yourroad-sdk]');\n if (existing) return existing;\n const style = document.createElement('style');\n style.dataset.yourroadSdk = '';\n style.textContent = SDK_STYLES;\n document.head.append(style);\n return style;\n}\n","import { IframeController } from '../iframe/IframeController';\nimport { ensureStyles } from './styles';\n\nexport class OverlayController {\n readonly root: HTMLDivElement;\n readonly closeButton: HTMLButtonElement;\n readonly iframe: IframeController;\n private readonly abort = new AbortController();\n\n constructor(source: string, onClose: () => void, onFrameError: () => void) {\n ensureStyles();\n this.root = document.createElement('div');\n this.root.dataset.yourroadOverlayRoot = '';\n this.root.setAttribute('role', 'dialog');\n this.root.setAttribute('aria-modal', 'true');\n this.root.setAttribute('aria-label', 'YourRoad pre-approval');\n const scrim = document.createElement('div');\n scrim.dataset.yourroadScrim = '';\n const panel = document.createElement('div');\n panel.dataset.yourroadPanel = '';\n this.closeButton = document.createElement('button');\n this.closeButton.type = 'button';\n this.closeButton.dataset.yourroadClose = '';\n this.closeButton.setAttribute('aria-label', 'Close YourRoad');\n this.closeButton.textContent = '×';\n this.iframe = new IframeController(source, onFrameError);\n panel.append(this.closeButton, this.iframe.element);\n this.root.append(scrim, panel);\n const signal = this.abort.signal;\n scrim.addEventListener('click', onClose, { signal });\n this.closeButton.addEventListener('click', onClose, { signal });\n document.addEventListener('keydown', (event) => { if (event.key === 'Escape') onClose(); }, { signal });\n }\n\n mount(): void { document.body.append(this.root); }\n destroy(): void {\n this.abort.abort();\n this.iframe.destroy();\n this.root.remove();\n if (!document.querySelector('[data-yourroad-overlay-root]')) {\n document.head.querySelector('style[data-yourroad-sdk]')?.remove();\n }\n }\n}\n","export const JOURNEY_SESSION_MARKER_KEY = 'yr_journey_session_pre_approval_v1';\n\nexport const isUuid = (value: unknown): value is string =>\n typeof value === 'string'\n && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);\n\nexport interface SessionMarker {\n read(): string | undefined;\n write(sessionId: string): void;\n clear(): void;\n}\n\ntype MarkerStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;\n\nfunction resolveStorage(): MarkerStorage | undefined {\n try { return globalThis.localStorage; } catch { return undefined; }\n}\n\nexport function createSessionMarker(storage: MarkerStorage | undefined = resolveStorage()): SessionMarker {\n const clear = (): void => {\n try { storage?.removeItem(JOURNEY_SESSION_MARKER_KEY); } catch { /* Storage is optional. */ }\n };\n return {\n read() {\n let value: string | null;\n try { value = storage?.getItem(JOURNEY_SESSION_MARKER_KEY) ?? null; } catch { return undefined; }\n if (value === null) return undefined;\n if (!isUuid(value)) { clear(); return undefined; }\n return value;\n },\n write(sessionId) {\n if (!isUuid(sessionId)) return;\n try { storage?.setItem(JOURNEY_SESSION_MARKER_KEY, sessionId); } catch { /* Keep the in-memory session usable. */ }\n },\n clear,\n };\n}\n","import { isUuid } from './sessionMarker';\n\nconst CREATE_ENDPOINT = 'https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/create-embed-journey-session';\nconst PROBE_ENDPOINT = 'https://tsajlkcfcvjqqjxfigbw.supabase.co/functions/v1/probe-embed-journey-session';\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (value === null || typeof value !== 'object') return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\nconst isDate = (value: unknown): value is string => typeof value === 'string' && value.trim() !== '' && !Number.isNaN(Date.parse(value));\nconst options = (body: object, signal?: AbortSignal): RequestInit => ({\n method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body),\n credentials: 'omit', cache: 'no-store', signal,\n});\n\nexport async function createJourneySession(partnerKey: string, signal?: AbortSignal): Promise<string> {\n const response = await fetch(CREATE_ENDPOINT, options({ partnerKey, surface: 'pre-approval' }, signal));\n if (response.status !== 201) throw new Error('Journey session creation was unsuccessful.');\n const value: unknown = await response.json();\n if (!isPlainObject(value) || !isUuid(value.sessionId) || !isDate(value.expiresAt)) throw new Error('Invalid journey session response.');\n return value.sessionId;\n}\n\nexport type ProbeResult = { status: 'resumable'; sessionId: string } | { status: 'expired' } | { status: 'invalid' };\n\nexport async function probeJourneySession(partnerKey: string, sessionId: string): Promise<ProbeResult> {\n const response = await fetch(PROBE_ENDPOINT, options({ partnerKey, surface: 'pre-approval', sessionId }));\n const value: unknown = await response.json();\n if (!isPlainObject(value)) throw new Error('Invalid journey session probe response.');\n if (response.status === 200 && value.status === 'resumable' && value.sessionId === sessionId && isDate(value.expiresAt)) {\n return { status: 'resumable', sessionId };\n }\n if (response.status === 410 && value.status === 'expired' && value.code === 'SESSION_EXPIRED') return { status: 'expired' };\n if (response.status === 404 && value.status === 'invalid' && value.code === 'SESSION_INVALID') return { status: 'invalid' };\n throw new Error('Journey session probe was unsuccessful.');\n}\n","import { createJourneySession, probeJourneySession } from './sessionClient';\nimport { createSessionMarker, type SessionMarker } from './sessionMarker';\n\ntype State =\n | { kind: 'cold' }\n | { kind: 'probing'; completion: Promise<void> }\n | { kind: 'active'; sessionId: string }\n | { kind: 'expired'; sessionId: string }\n | { kind: 'probe_failed' };\n\nexport class SessionController {\n private state: State = { kind: 'cold' };\n private restart?: Promise<string>;\n\n constructor(private readonly partnerKey: string, private readonly marker: SessionMarker = createSessionMarker()) {\n const sessionId = marker.read();\n if (!sessionId) return;\n const completion = this.probe(sessionId);\n this.state = { kind: 'probing', completion };\n }\n\n async resolveForOpen(signal: AbortSignal): Promise<string> {\n if (this.state.kind === 'probing') await this.state.completion;\n if (this.state.kind === 'probe_failed') throw new Error('Journey session validity could not be established.');\n if (this.state.kind === 'active' || this.state.kind === 'expired') return this.state.sessionId;\n const sessionId = await createJourneySession(this.partnerKey, signal);\n this.marker.write(sessionId);\n this.state = { kind: 'active', sessionId };\n return sessionId;\n }\n\n restartExpired(signal: AbortSignal): Promise<string> | undefined {\n if (this.state.kind !== 'expired') return undefined;\n if (this.restart) return this.restart;\n const operation = createJourneySession(this.partnerKey, signal).then((sessionId) => {\n this.marker.write(sessionId);\n this.state = { kind: 'active', sessionId };\n return sessionId;\n }).finally(() => { if (this.restart === operation) this.restart = undefined; });\n this.restart = operation;\n return operation;\n }\n\n private async probe(sessionId: string): Promise<void> {\n try {\n const result = await probeJourneySession(this.partnerKey, sessionId);\n if (result.status === 'resumable') this.state = { kind: 'active', sessionId };\n else if (result.status === 'expired') { this.marker.clear(); this.state = { kind: 'expired', sessionId }; }\n else { this.marker.clear(); this.state = { kind: 'cold' }; }\n } catch { this.state = { kind: 'probe_failed' }; }\n }\n}\n","import type { YourRoadInitConfig, YourRoadSurface } from '../config/types';\nimport { requestBootstrap } from '../bootstrap/bootstrapClient';\nimport { BridgeController, runtimeOriginFromIframeUrl } from '../bridge/BridgeController';\nimport { copyContext, mergeContext } from '../context/mergeContext';\nimport type { YourRoadContext, YourRoadContextUpdate } from '../context/types';\nimport { YourRoadError } from '../errors/YourRoadError';\nimport { EventEmitter } from '../events/EventEmitter';\nimport { FocusLifecycle } from '../lifecycle/focus';\nimport { ScrollLock } from '../lifecycle/scrollLock';\nimport { OverlayController } from '../overlay/OverlayController';\nimport { SessionController } from '../session/SessionController';\n\nexport interface OpenOptions { surface: YourRoadSurface; context?: YourRoadContext }\nexport interface YourRoadEvents { opened: undefined; closed: undefined; error: YourRoadError }\n\nexport interface YourRoadClient {\n open(options: OpenOptions): Promise<void>;\n updateContext(update: YourRoadContextUpdate): void;\n close(): void;\n on<K extends keyof YourRoadEvents>(event: K, handler: (payload: YourRoadEvents[K]) => void): () => void;\n destroy(): void;\n}\n\nclass YourRoadClientImpl implements YourRoadClient {\n private readonly events = new EventEmitter<YourRoadEvents>();\n private readonly scroll = new ScrollLock();\n private readonly focus = new FocusLifecycle();\n private overlay?: OverlayController;\n private bridge?: BridgeController;\n private context: YourRoadContext = {};\n private destroyed = false;\n private opening?: Promise<void>;\n private bootstrapAbort?: AbortController;\n private restartAbort?: AbortController;\n private readonly session: SessionController;\n\n constructor(private readonly config: Readonly<YourRoadInitConfig>) {\n this.session = new SessionController(config.partnerKey);\n }\n\n async open(options: OpenOptions): Promise<void> {\n this.assertUsable();\n if (!options || options.surface !== 'pre-approval') throw this.fail('INVALID_SURFACE', 'The only supported surface is \"pre-approval\".');\n if (this.overlay) return;\n if (this.opening) return this.opening;\n this.context = copyContext(options.context);\n const abort = new AbortController();\n this.bootstrapAbort = abort;\n const operation = this.performOpen(options.surface, abort);\n this.opening = operation;\n return operation;\n }\n\n updateContext(update: YourRoadContextUpdate): void {\n this.assertUsable();\n this.context = mergeContext(this.context, update);\n this.bridge?.contextUpdated(this.context);\n }\n\n close(): void {\n this.assertUsable();\n if (this.bootstrapAbort) {\n this.bootstrapAbort.abort();\n this.bootstrapAbort = undefined;\n this.opening = undefined;\n }\n this.restartAbort?.abort();\n this.restartAbort = undefined;\n if (!this.overlay) return;\n this.bridge?.destroy();\n this.bridge = undefined;\n this.overlay.destroy();\n this.overlay = undefined;\n this.scroll.unlock();\n this.focus.restore();\n this.events.emit('closed', undefined);\n }\n\n on<K extends keyof YourRoadEvents>(event: K, handler: (payload: YourRoadEvents[K]) => void): () => void {\n this.assertUsable();\n return this.events.on(event, handler);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.close();\n this.events.clear();\n this.context = {};\n this.destroyed = true;\n }\n\n private async performOpen(surface: YourRoadSurface, abort: AbortController): Promise<void> {\n try {\n const [bootstrap, sessionId] = await Promise.all([\n requestBootstrap({ partnerKey: this.config.partnerKey, surface }, abort.signal),\n this.session.resolveForOpen(abort.signal),\n ]);\n const { iframeUrl } = bootstrap;\n if (abort.signal.aborted || this.destroyed || this.bootstrapAbort !== abort) return;\n this.focus.capture();\n try {\n this.overlay = new OverlayController(iframeUrl, () => this.close(), () => this.fail('IFRAME_LOAD_FAILED', 'The embedded experience failed to load.'));\n this.bridge = new BridgeController(\n this.overlay.iframe.element,\n runtimeOriginFromIframeUrl(iframeUrl),\n () => copyContext(this.context),\n () => sessionId,\n () => this.restartExpiredSession(this.bridge),\n );\n this.bridge.start();\n this.overlay.mount();\n this.scroll.lock();\n this.focus.moveTo(this.overlay.closeButton);\n this.events.emit('opened', undefined);\n } catch (cause) {\n this.bridge?.destroy();\n this.bridge = undefined;\n this.overlay?.destroy();\n this.overlay = undefined;\n this.scroll.unlock();\n this.focus.restore();\n throw this.fail('OVERLAY_CREATION_FAILED', 'The YourRoad overlay could not be created.', cause);\n }\n } catch (cause) {\n if (abort.signal.aborted) return;\n if (cause instanceof YourRoadError) throw cause;\n throw this.fail('BOOTSTRAP_FAILED', 'The YourRoad experience could not be started securely.', cause);\n } finally {\n if (this.bootstrapAbort === abort) {\n this.bootstrapAbort = undefined;\n this.opening = undefined;\n }\n }\n }\n\n private restartExpiredSession(bridge: BridgeController | undefined): void {\n if (this.destroyed || bridge === undefined || bridge !== this.bridge || this.restartAbort) return;\n const restartAbort = new AbortController();\n this.restartAbort = restartAbort;\n const operation = this.session.restartExpired(restartAbort.signal);\n if (!operation) { this.restartAbort = undefined; return; }\n void operation.then((sessionId) => {\n if (!this.destroyed && !restartAbort.signal.aborted && this.bridge === bridge) bridge.sendJourneySession(sessionId);\n }).catch(() => { /* The runtime may offer a later explicit retry. */ }).finally(() => {\n if (this.restartAbort === restartAbort) this.restartAbort = undefined;\n });\n }\n\n private assertUsable(): void { if (this.destroyed) throw new YourRoadError('DESTROYED', 'This YourRoad SDK instance has been destroyed.'); }\n private fail(code: ConstructorParameters<typeof YourRoadError>[0], message: string, cause?: unknown): YourRoadError {\n const error = new YourRoadError(code, message, cause === undefined ? undefined : { cause });\n this.events.emit('error', error);\n return error;\n }\n}\n\nexport function createYourRoadClient(config: Readonly<YourRoadInitConfig>): YourRoadClient {\n return new YourRoadClientImpl(config);\n}\n","import { createYourRoadClient, type YourRoadClient } from './client/YourRoadClient';\nimport type { YourRoadInitConfig } from './config/types';\nimport { YourRoadError } from './errors/YourRoadError';\n\nexport type { OpenOptions, YourRoadClient, YourRoadEvents } from './client/YourRoadClient';\nexport type { YourRoadInitConfig, YourRoadSurface } from './config/types';\nexport type { DealerContext, PageContext, VehicleContext, YourRoadContext, YourRoadContextUpdate } from './context/types';\nexport { YourRoadError };\n\nexport class YourRoad {\n static init(config: YourRoadInitConfig): YourRoadClient {\n if (!config || typeof config.partnerKey !== 'string' || config.partnerKey.trim() === '') {\n throw new YourRoadError('INVALID_INITIALISATION', 'partnerKey must be a non-empty string.');\n }\n return createYourRoadClient({ partnerKey: config.partnerKey });\n }\n}\n"],"names":["BOOTSTRAP_ENDPOINT","validateResponse","value","iframeUrl","expiresAt","parsedUrl","requestBootstrap","request","signal","response","defined","item","mergeContext","current","update","next","key","copyContext","context","BRIDGE_VERSION","runtimeOriginFromIframeUrl","url","isPlainObject","prototype","isReadyMessage","isRestartMessage","BridgeController","iframe","trustedRuntimeOrigin","getCurrentContext","getJourneySessionId","onJourneySessionRestart","__publicField","event","iframeWindow","currentContext","sessionId","message","YourRoadError","code","options","EventEmitter","handler","handlers","payload","_a","FocusLifecycle","element","ScrollLock","style","IframeController","source","onError","SDK_Z_INDEX","SDK_STYLES","ensureStyles","existing","OverlayController","onClose","onFrameError","scrim","panel","JOURNEY_SESSION_MARKER_KEY","isUuid","resolveStorage","createSessionMarker","storage","clear","CREATE_ENDPOINT","PROBE_ENDPOINT","isDate","body","createJourneySession","partnerKey","probeJourneySession","SessionController","marker","completion","operation","result","YourRoadClientImpl","config","abort","_b","surface","bootstrap","cause","bridge","restartAbort","error","createYourRoadClient","YourRoad"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@yourroad/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Framework-independent browser SDK for hosting YourRoad embedded experiences",
5
+ "publishConfig": {
6
+ "access": "public",
7
+ "registry": "https://registry.npmjs.org/"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Jisaiah9/YourRoad-SDK.git"
12
+ },
13
+ "homepage": "https://github.com/Jisaiah9/YourRoad-SDK#readme",
14
+ "type": "module",
15
+ "main": "./dist/index.cjs",
16
+ "module": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js",
22
+ "require": "./dist/index.cjs"
23
+ }
24
+ },
25
+ "files": ["dist", "README.md"],
26
+ "scripts": {
27
+ "build": "vite build",
28
+ "test": "vitest run",
29
+ "test:browser": "npm run build && playwright test",
30
+ "test:browser:list": "playwright test --list",
31
+ "verify:package": "npm run build && node verification/package-consumer.mjs",
32
+ "verify:release": "npm run typecheck && npm test && npm run build && npm run verify:package",
33
+ "prepublishOnly": "npm run verify:release",
34
+ "typecheck": "tsc --noEmit"
35
+ },
36
+ "devDependencies": {
37
+ "@playwright/test": "^1.62.1",
38
+ "@types/node": "^22.10.0",
39
+ "jsdom": "^25.0.1",
40
+ "typescript": "^5.7.2",
41
+ "vite": "^6.0.3",
42
+ "vite-plugin-dts": "^4.4.0",
43
+ "vitest": "^3.2.4"
44
+ },
45
+ "engines": { "node": ">=18" },
46
+ "license": "UNLICENSED"
47
+ }