@checktiv/sdk-web 1.2.0 → 1.4.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 CHANGED
@@ -23,6 +23,93 @@ npm install @checktiv/sdk-web
23
23
  `react` and `react-dom` are **optional peer dependencies**. Install them only if you
24
24
  use the `./react` subpath. Everything else works without React.
25
25
 
26
+ ## Quickstart
27
+
28
+ The recommended integration is the zero-lifecycle `mount()`. Your backend mints one
29
+ durable, low-privilege `clientToken` per applicant; the SDK exchanges it for
30
+ short-lived working tokens and refreshes them internally, so you write no
31
+ token-lifecycle code.
32
+
33
+ **Backend** - your server holds the secret key and returns a `clientToken`:
34
+
35
+ ```js
36
+ import { sessions } from '@checktiv/sdk-web';
37
+
38
+ // POST /kyc/session (your backend)
39
+ const { clientToken } = await sessions.create(
40
+ { templateId: 'wt_...', applicant: { externalId: user.id } },
41
+ {
42
+ apiBase: 'https://api.us.checktiv.com',
43
+ secretKey: process.env.CHECKTIV_SECRET_KEY, // ah_sk_* - never in the browser
44
+ idempotencyKey: user.id, // retries and new-device re-entry never double-mint
45
+ },
46
+ );
47
+ // Return ONLY the clientToken to the browser.
48
+ ```
49
+
50
+ `sessions.create` throws if it runs in a browser: it needs your secret key, so call
51
+ it only from your server. The `clientToken` it returns can resume the applicant's own
52
+ session; it never reads verification results or applicant data.
53
+
54
+ **Frontend** - mount and forget:
55
+
56
+ ```js
57
+ import { mount } from '@checktiv/sdk-web';
58
+ import '@checktiv/sdk-web/idv'; // self-registers the managed IDV module
59
+ import '@checktiv/sdk-web/fraud'; // omit if the workflow never declares fraud
60
+ import '@checktiv/sdk-web/capture-ui/style.css'; // REQUIRED: styles the capture surface
61
+
62
+ const handle = mount(document.getElementById('checktiv-container'), {
63
+ publishableKey: 'ah_pk_us_test_...', // safe to expose - origin-pinned, no scopes
64
+ // Called ONCE for the cold start (or a new device), never on every expiry.
65
+ fetchToken: () =>
66
+ fetch('/kyc/session', { method: 'POST' })
67
+ .then((res) => res.json())
68
+ .then((data) => data.clientToken),
69
+ onConsent: () => showConsentPrompt(), // required only if the session declares fraud
70
+ onEvent: (event) => console.log(event),
71
+ onComplete: ({ sessionId }) => {
72
+ // Capture is complete. Wait for the webhook verdict before acting.
73
+ showMessage('Verification submitted. You will hear from us shortly.');
74
+ },
75
+ layout: 'immersive', // optional: full-screen capture on phones
76
+ theme: { primaryColor: '#0b5fff' }, // optional: match your brand
77
+ });
78
+
79
+ // Later, when navigating away:
80
+ // handle.destroy();
81
+ ```
82
+
83
+ Key properties of this design:
84
+
85
+ - **No token-refresh code.** `fetchToken` fires once; the SDK mints and refreshes the
86
+ short-lived working token internally, so mid-flow expiry never surfaces to you.
87
+ - **Your secret key never touches client code.** Only `sessions.create` (server-side)
88
+ uses it; the browser holds just the low-privilege `clientToken`.
89
+ - **The server drives composition.** `mount()` renders whatever modules the session
90
+ declares (identity verification, the consent-gated fraud module, custom forms), so
91
+ you change the journey server-side without shipping new client code.
92
+ - **Import `@checktiv/sdk-web/capture-ui/style.css`.** The managed capture surface
93
+ ships its styles as a separate stylesheet; without this import the capture screen
94
+ renders unstyled and collapses to a small box.
95
+
96
+ `onComplete` (and `checktiv.idv.submitted`) means capture finished, not that
97
+ verification passed. The authoritative verdict arrives on your server as a signed
98
+ webhook.
99
+
100
+ ### Cross-device handoff
101
+
102
+ The handle returned by `mount()` exposes `openCrossDevice()` so a desktop applicant
103
+ can continue on their phone. Pass a `crossDeviceCopy` map, render your own "Continue
104
+ on your phone" trigger, and call `handle.openCrossDevice()` from it: the SDK mints the
105
+ one-time link, opens the QR overlay, runs the completion poll, and fires `onComplete`
106
+ when the phone finishes. The trigger is desktop only (it warn-no-ops on a touch
107
+ device, where capture runs on the phone directly), so gate your control to a
108
+ fine-pointer device. Pass an optional `onOpenCrossDevice` mint hook to mint the link
109
+ on your own backend instead of the SDK-owned default. Import
110
+ `@checktiv/sdk-web/idv/cross-device` once to enable the overlay (the CDN bundle
111
+ registers it for you). See the developer docs for the full contract.
112
+
26
113
  ## CDN usage
27
114
 
28
115
  Load the moving pointer for the latest release:
@@ -46,19 +133,25 @@ release notes, so the exact bytes your page loads are locked and verified:
46
133
  ```
47
134
 
48
135
  Use the region host that matches your account (for example `sdk.us.checktiv.com` or
49
- `sdk.eu.checktiv.com`). The CDN bundle exposes the same API as the npm package on the
50
- `window.Checktiv` global.
136
+ `sdk.eu.checktiv.com`). The CDN bundle exposes the same API on the `window.Checktiv`
137
+ global and includes every module, so no per-module import is needed.
51
138
 
52
- ## Quickstart: the two-line surface
139
+ ## Alternative: the per-request browser-token path (`init()`)
140
+
141
+ If your integration mints a fresh browser token on every page load instead of holding
142
+ a durable `clientToken`, use `init()` with a `getSessionToken` callback. `init()`
143
+ makes no network call; it sets up the client and the token callback, and
144
+ `mountProvisioned()` renders the server-declared modules.
53
145
 
54
146
  ```js
55
147
  import { init } from '@checktiv/sdk-web';
56
- import '@checktiv/sdk-web/idv'; // self-registers the managed IDV module
148
+ import '@checktiv/sdk-web/idv';
149
+ import '@checktiv/sdk-web/capture-ui/style.css'; // REQUIRED for the managed capture surface
57
150
 
58
151
  const client = init({
59
152
  publishableKey: 'ah_pk_us_test_...',
60
153
  getSessionToken: async (ctx) => {
61
- // Your server exchanges its secret key for a short-lived browser token.
154
+ // Your server mints a short-lived browser token from its secret key.
62
155
  const res = await fetch('/api/checktiv/token', {
63
156
  method: 'POST',
64
157
  headers: { 'Content-Type': 'application/json' },
@@ -66,96 +159,67 @@ const client = init({
66
159
  });
67
160
  return (await res.json()).token;
68
161
  },
162
+ theme: { primaryColor: '#0b5fff' }, // optional
69
163
  });
70
164
 
71
- // Server-driven: renders whatever modules the session declares.
72
165
  const handle = client.mountProvisioned({
73
166
  target: document.getElementById('checktiv-container'),
74
167
  onEvent: (event) => console.log(event),
75
168
  });
76
-
77
- // Later, when navigating away:
78
- // handle.destroy();
169
+ // handle.destroy() when navigating away.
79
170
  ```
80
171
 
81
- Key properties of this design:
82
-
83
- - `init()` makes **no network call**. It only sets up the client and the token
84
- callback.
85
- - Your **secret key never touches client code**. `getSessionToken` returns a
86
- short-lived browser token that your server mints; the SDK only ever holds that
87
- token.
88
- - **The server drives composition.** `mountProvisioned()` reads the modules the
89
- session declares and renders each one, so you can change the journey server-side
90
- without shipping new client code.
91
-
92
172
  ## Published subpaths
93
173
 
94
174
  Every entry below is part of the published API. Import a module subpath (like
95
175
  `./idv`) once anywhere in your app: importing it runs a one-time self-registration
96
- side effect so `mountProvisioned()` and `mount('idv')` can resolve that module. If a
97
- session needs a module you did not import, the SDK throws a clear, actionable error
98
- telling you which subpath to add, instead of failing silently.
99
-
100
- | Subpath | Purpose |
101
- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
102
- | `.` / `@checktiv/sdk-web` | Core entry: `init(opts)` returns a `ChecktivClient`. Tree-shakeable and lightweight. |
103
- | `./idv` | Managed identity-verification module. Import for its self-registration side effect so `mountProvisioned` / `mount('idv')` resolve it. |
104
- | `./fraud` | Consent-gated fraud-signal collection. Import for its self-registration side effect. |
105
- | `./custom-form` | Custom intake-form module (server-authored field definitions). Import for its self-registration side effect. |
106
- | `./custom-form/style.css` | Stylesheet for the custom-form module. `<link>` or `import` this into the host page. |
107
- | `./react` | React wrapper: `ChecktivProvider`, `useChecktiv()`, `ChecktivIdv`. Requires the `react` / `react-dom` peer dependencies. |
108
- | `./capture` | Advanced: headless host-side capture controller (`createCaptureController`). No default UI. |
109
- | `./capture-ui` | Advanced: batteries-included capture renderer (`mount()`), built on `./capture`. |
110
- | `./cross-device` | Cross-device QR / copy-link handoff overlay (`mountCrossDevice`). |
111
- | `./idv/cross-device` | Cross-device wiring for the managed IDV module. |
112
- | `./agents` | Machine-readable steering manifest (JSON) for coding-agent tooling. |
176
+ side effect so `mount()` / `mountProvisioned()` / `mount('idv')` can resolve that
177
+ module. If a session needs a module you did not import, the SDK throws a clear,
178
+ actionable error telling you which subpath to add, instead of failing silently.
179
+
180
+ | Subpath | Purpose |
181
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
182
+ | `.` / `@checktiv/sdk-web` | Core entry: the zero-lifecycle `mount(target, opts)` + `sessions.create`, plus the `init(opts)` client. Tree-shakeable and lightweight. |
183
+ | `./idv` | Managed identity-verification module. Import for its self-registration side effect so `mount()` / `mount('idv')` resolve it. |
184
+ | `./fraud` | Consent-gated fraud-signal collection. Import for its self-registration side effect. |
185
+ | `./custom-form` | Custom intake-form module (server-authored field definitions). Import for its self-registration side effect. |
186
+ | `./custom-form/style.css` | Stylesheet for the custom-form module. `<link>` or `import` this into the host page. |
187
+ | `./capture-ui/style.css` | Stylesheet for the managed IDV capture surface. Import it (the managed module renders through it) or the capture screen is unstyled. |
188
+ | `./react` | React wrappers: `ChecktivJourney` (zero-lifecycle `mount`), plus `ChecktivProvider` / `useChecktiv()` / `ChecktivIdv` (the `init()` path). |
189
+ | `./capture` | Advanced: headless host-side capture controller (`createCaptureController`). No default UI. |
190
+ | `./capture-ui` | Advanced: batteries-included capture renderer (`mount()`), built on `./capture`. |
191
+ | `./cross-device` | Cross-device QR / copy-link handoff overlay (`mountCrossDevice`). |
192
+ | `./idv/cross-device` | Cross-device wiring for the managed IDV module. |
193
+ | `./agents` | Machine-readable steering manifest (JSON) for coding-agent tooling. |
113
194
 
114
195
  ## Managed IDV module (`./idv`)
115
196
 
116
197
  The managed IDV module renders the full identity-verification capture experience for
117
- you. Import it for its registration side effect, then let the provisioned session
118
- render it, or mount it explicitly:
198
+ you. Import it for its registration side effect and the capture stylesheet, then let
199
+ the provisioned session render it, or mount it explicitly on an `init()` client:
119
200
 
120
201
  ```js
121
- import { init } from '@checktiv/sdk-web';
122
202
  import '@checktiv/sdk-web/idv';
123
-
124
- const client = init({ publishableKey: 'ah_pk_us_live_...', getSessionToken });
125
-
126
- const idv = client.mount('idv', {
127
- target: '#checktiv-container',
128
- onEvent: (event) => {
129
- // Events are named `checktiv.idv.*`, for example `checktiv.idv.ready`.
130
- console.log(event.type);
131
- },
132
- });
133
-
134
- // idv.destroy() when you are done.
203
+ import '@checktiv/sdk-web/capture-ui/style.css';
135
204
  ```
136
205
 
137
- Events are `checktiv.<module>.<name>`. The event stream is forward-open: new event
138
- types can appear over time, so handle the ones you care about and ignore the rest -
139
- do not write an exhaustive switch that assumes a fixed set.
206
+ Events are `checktiv.<module>.<name>`, for example `checktiv.idv.ready`. The event
207
+ stream is forward-open: new event types can appear over time, so handle the ones you
208
+ care about and ignore the rest - do not write an exhaustive switch that assumes a
209
+ fixed set.
140
210
 
141
211
  ## Fraud module (`./fraud`)
142
212
 
143
213
  Fraud-signal collection is **consent-gated and default-deny**: it collects nothing
144
- until your `onConsent` callback resolves truthy.
214
+ until your `onConsent` callback resolves truthy. Pass `onConsent` to `mount()` (or to
215
+ `mount('fraud')` on an `init()` client) whenever the session declares the fraud
216
+ module.
145
217
 
146
218
  ```js
147
- import { init } from '@checktiv/sdk-web';
148
219
  import '@checktiv/sdk-web/fraud';
149
220
 
150
- const client = init({ publishableKey: 'ah_pk_us_live_...', getSessionToken });
151
-
152
- const fraud = client.mount('fraud', {
153
- target: '#checktiv-container',
154
- onConsent: () => userHasConsented(), // boolean or Promise<boolean>
155
- onEvent: (event) => console.log(event), // `checktiv.fraud.*`
156
- });
157
-
158
- // fraud.destroy() when you are done.
221
+ // With mount(): pass onConsent in the mount options.
222
+ // onConsent: () => userHasConsented(), // boolean or Promise<boolean>
159
223
  ```
160
224
 
161
225
  ## Custom-form module (`./custom-form`)
@@ -171,19 +235,53 @@ import '@checktiv/sdk-web/custom-form/style.css';
171
235
 
172
236
  The module always renders a continue control (even for a content-only screen) so the
173
237
  journey can advance, and emits `checktiv.custom_form.submitted` when the applicant
174
- completes it. When you use `mountProvisioned`, pass `customFormConfig` and
175
- `customFormCopy` alongside `target`.
238
+ completes it. Pass `customFormConfig` and `customFormCopy` alongside `target`.
176
239
 
177
240
  ## React integration (`./react`)
178
241
 
179
- The React wrapper provides `ChecktivProvider`, the `useChecktiv()` hook, and the
180
- `ChecktivIdv` component. It is SSR-safe (it renders `null` on the server and only
181
- initializes in the browser), and `react` / `react-dom` are supplied by your app as
182
- optional peers, so there is a single React instance.
242
+ `react` / `react-dom` are supplied by your app as optional peers, so there is a single
243
+ React instance. Both wrappers are SSR-safe (they render `null` on the server and only
244
+ initialize in the browser). Import the capture stylesheet alongside the modules.
245
+
246
+ ### Recommended: `<ChecktivJourney>` (zero-lifecycle)
247
+
248
+ `<ChecktivJourney>` is the React wrapper for `mount()`. It needs no provider or
249
+ context: it manages the mount and teardown lifecycle for you (SSR-safe,
250
+ StrictMode-safe), and forwards every `mount()` option as a prop.
251
+
252
+ ```tsx
253
+ import { ChecktivJourney } from '@checktiv/sdk-web/react';
254
+ import '@checktiv/sdk-web/idv';
255
+ import '@checktiv/sdk-web/fraud';
256
+ import '@checktiv/sdk-web/capture-ui/style.css';
257
+
258
+ export function CheckIn() {
259
+ return (
260
+ <ChecktivJourney
261
+ publishableKey="ah_pk_us_test_..."
262
+ fetchToken={() => fetchClientToken()} // returns your durable clientToken
263
+ onConsent={() => showConsent()} // required only if the session declares fraud
264
+ onEvent={(e) => console.log(e)}
265
+ onComplete={({ sessionId }) => showDone(sessionId)}
266
+ layout="immersive" // optional: full-screen capture on phones
267
+ theme={{ primaryColor: '#0b5fff' }} // optional
268
+ />
269
+ );
270
+ }
271
+ ```
272
+
273
+ Pass a `ref` to reach the in-SDK self-service resend (`ref.current.requestResend(email)`).
274
+
275
+ ### The `init()` path: `<ChecktivProvider>` + `<ChecktivIdv>`
276
+
277
+ If you use the per-request browser-token flow (`init()` + `getSessionToken`), wrap
278
+ your tree in `<ChecktivProvider>` and render `<ChecktivIdv>`; `useChecktiv()` returns
279
+ the client from context.
183
280
 
184
281
  ```tsx
185
282
  import { ChecktivProvider, ChecktivIdv } from '@checktiv/sdk-web/react';
186
- import '@checktiv/sdk-web/idv'; // register the managed IDV module
283
+ import '@checktiv/sdk-web/idv';
284
+ import '@checktiv/sdk-web/capture-ui/style.css';
187
285
 
188
286
  export function App() {
189
287
  return (
@@ -197,8 +295,7 @@ export function App() {
197
295
  }
198
296
  ```
199
297
 
200
- `ChecktivIdv` mounts the managed IDV module and tears it down on unmount. Read the
201
- hosted developer docs for the full SSR-safe integration pattern.
298
+ `ChecktivIdv` mounts the managed IDV module and tears it down on unmount.
202
299
 
203
300
  ## Advanced: host-side capture (`./capture`, `./capture-ui`)
204
301
 
@@ -210,7 +307,8 @@ level escape hatches for hosts that need to own the capture surface themselves:
210
307
  capture controller with no default UI. You inject token and submit functions and
211
308
  drive the state machine yourself.
212
309
  - `./capture-ui` exports `mount(element, options)`, a batteries-included default
213
- renderer built on `./capture` that returns a `{ destroy }` handle.
310
+ renderer built on `./capture` that returns a `{ destroy }` handle. Import
311
+ `@checktiv/sdk-web/capture-ui/style.css` for its styles.
214
312
 
215
313
  ## Cross-device handoff (`./cross-device`)
216
314
 
@@ -234,7 +332,12 @@ migrating an older snippet:
234
332
  The package ships self-contained type declarations. Import types from the root:
235
333
 
236
334
  ```ts
237
- import type { ChecktivClient, ChecktivEvent, ChecktivIdvEvent } from '@checktiv/sdk-web';
335
+ import type {
336
+ ChecktivClient,
337
+ ChecktivEvent,
338
+ ChecktivIdvEvent,
339
+ MountOptions,
340
+ } from '@checktiv/sdk-web';
238
341
  ```
239
342
 
240
343
  The `ChecktivEvent` union is forward-open, so a handler typed against it keeps
@@ -25,7 +25,7 @@ runnable recipes. Read both.
25
25
 
26
26
  9. **The SDK adds X-Publishable-Key itself on the customer path.** Pass `publishableKey` to `Checktiv.init(...)` and stop there. On the customer (pk-present) path the managed transport attaches the `X-Publishable-Key` header to its own `bt_*` data-plane calls internally. Do NOT also hand-add an `X-Publishable-Key` header yourself; double-adding it is wrong. (The first-party hosted path used internally by the verify app carries no publishable key and sends none.)
27
27
 
28
- 10. **Cross-device QR handoff is built into the managed IDV module.** Cross-device handoff (a desktop applicant finishes on their phone via a QR + copy-link overlay) is OWNED BY THE MANAGED IDV MODULE. Render it with `client.mount('idv', { crossDeviceCopy, onEvent })` and the returned handle exposes `openCrossDevice()`: calling it opens the overlay OVER the still-live capture frame, runs the completion poll, and emits `checktiv.idv.submitted` when the phone finishes so your host reloads. You MUST `import '@checktiv/sdk-web/idv/cross-device';` to enable the overlay (the CDN script-tag bundle already includes it); without that import `openCrossDevice()` is a graceful, actionable no-op, as it also is before the applicant verification resolves or when no `crossDeviceCopy` was supplied. `IdvMountOptions` carries `crossDeviceCopy` (host-injected strings; the `unavailableMessage` key is required so no arm renders blank) and the OPTIONAL `onOpenCrossDevice` mint override; `IdvHandle` exposes `openCrossDevice?()`, so call it as `handle.openCrossDevice?.()`. NOTE: `mountProvisioned` and the zero-lifecycle `Checktiv.mount` entry do NOT expose the overlay: use the explicit `mount('idv', ...)` call when you need `openCrossDevice()`.
28
+ 10. **Cross-device QR handoff is built into the managed IDV module.** Cross-device handoff (a desktop applicant finishes on their phone via a QR + copy-link overlay) is OWNED BY THE MANAGED IDV MODULE. Render it with `client.mount('idv', { crossDeviceCopy, onEvent })` and the returned handle exposes `openCrossDevice()`: calling it opens the overlay OVER the still-live capture frame, runs the completion poll, and emits `checktiv.idv.submitted` when the phone finishes so your host reloads. You MUST `import '@checktiv/sdk-web/idv/cross-device';` to enable the overlay (the CDN script-tag bundle already includes it); without that import `openCrossDevice()` is a graceful, actionable no-op, as it also is before the applicant verification resolves or when no `crossDeviceCopy` was supplied. `IdvMountOptions` carries `crossDeviceCopy` (host-injected strings; the `unavailableMessage` key is required so no arm renders blank) and the OPTIONAL `onOpenCrossDevice` mint override; `IdvHandle` exposes `openCrossDevice?()`, so call it as `handle.openCrossDevice?.()`. The zero-lifecycle `Checktiv.mount(...)` and its React wrapper `<ChecktivJourney>` ALSO expose `openCrossDevice()` on their handle (reach the React handle via a `ref`), carrying the same `crossDeviceCopy` / optional `onOpenCrossDevice` options; on that path the SDK self-mints the handoff on the working-token plane by default (see below), and the CDN script-tag bundle registers the opener too. The trigger is a DESKTOP affordance: `openCrossDevice()` is a graceful warn-no-op on a coarse-pointer (touch) device, because immersive phone capture and the desktop cross-device overlay are mutually exclusive per device, so gate your trigger to desktop.
29
29
 
30
30
  Who mints the one-time handoff link depends on the token plane:
31
31
  • Working-token plane: the module mints the link ITSELF (it calls its own handoff endpoint `POST /sdk/v1/sessions/me/handoff`, authenticated by the applicant's short-lived working token, and caps the per-overlay "refresh link" re-mints). Supply `onOpenCrossDevice` ONLY to point the mint at your own backend instead.
@@ -74,7 +74,7 @@
74
74
  {
75
75
  "id": "cross-device-handoff-hook",
76
76
  "title": "Cross-device QR handoff is built into the managed IDV module",
77
- "body": "Cross-device handoff (a desktop applicant finishes on their phone via a QR + copy-link overlay) is OWNED BY THE MANAGED IDV MODULE. Render it with `client.mount('idv', { crossDeviceCopy, onEvent })` and the returned handle exposes `openCrossDevice()`: calling it opens the overlay OVER the still-live capture frame, runs the completion poll, and emits `checktiv.idv.submitted` when the phone finishes so your host reloads. You MUST `import '@checktiv/sdk-web/idv/cross-device';` to enable the overlay (the CDN script-tag bundle already includes it); without that import `openCrossDevice()` is a graceful, actionable no-op, as it also is before the applicant verification resolves or when no `crossDeviceCopy` was supplied. `IdvMountOptions` carries `crossDeviceCopy` (host-injected strings; the `unavailableMessage` key is required so no arm renders blank) and the OPTIONAL `onOpenCrossDevice` mint override; `IdvHandle` exposes `openCrossDevice?()`, so call it as `handle.openCrossDevice?.()`. NOTE: `mountProvisioned` and the zero-lifecycle `Checktiv.mount` entry do NOT expose the overlay: use the explicit `mount('idv', ...)` call when you need `openCrossDevice()`.\n\nWho mints the one-time handoff link depends on the token plane:\n • Working-token plane: the module mints the link ITSELF (it calls its own handoff endpoint `POST /sdk/v1/sessions/me/handoff`, authenticated by the applicant's short-lived working token, and caps the per-overlay \"refresh link\" re-mints). Supply `onOpenCrossDevice` ONLY to point the mint at your own backend instead.\n • Browser-token (`bt_*`) plane: the SDK CANNOT self-mint (the handoff route rejects a `bt_*` bearer), so you MUST supply `onOpenCrossDevice` backed by a server-held credential.\n\n`onOpenCrossDevice: CrossDeviceHandoffHook` is `() => Promise<{ kind: 'ok'; url: string } | { kind: 'unavailable' }>`. Return `{ kind: 'ok', url }` with the journey URL (which MUST be `https:`: the SDK collapses `javascript:`, `http:`, and `data:` schemes to `'unavailable'`) or `{ kind: 'unavailable' }` when the mint fails. The hook is re-callable for the overlay's \"refresh link\" affordance; the SDK caps re-mints per overlay.\n\nFor a FULLY CUSTOM IDV renderer built on `@checktiv/sdk-web/capture` (NOT the managed module), the STANDALONE exports drive the overlay yourself and you always supply the mint hook: `@checktiv/sdk-web/idv/cross-device` (`openCrossDeviceOverlay(opts)` opens the overlay, `preloadCrossDeviceChunk()` warms the lazy chunk) and `@checktiv/sdk-web/cross-device` (`mountCrossDevice(target, props)` renders the bare panel on a screen you fully control). `openCrossDeviceOverlay` takes `{ target, onOpenCrossDevice, copy, isMobile, emit, onClose }` plus an optional `poll` config, and returns a handle with `setCompleting()` (keep the QR mounted with a waiting label while your page reloads) and `destroy()` (idempotent). Set `isMobile: true` to suppress the QR on a phone (a same-device scan is circular). The SDK NEVER writes `window.location`; navigation is always the host's, off the emitted event.\n\nThree token-free overlay events (NONE carries the url / OTL / short-code / expiry hint):\n • `checktiv.idv.cross_device_opened`: the mint returned `ok` and the overlay is shown.\n • `checktiv.idv.cross_device_unavailable`: the mint returned `unavailable` or URL validation failed.\n • `checktiv.idv.cross_device_capped`: the completion poll hit its total time cap without the phone finishing. NOT an error and NOT a verdict: the QR panel stays mounted so the applicant can still finish on their phone."
77
+ "body": "Cross-device handoff (a desktop applicant finishes on their phone via a QR + copy-link overlay) is OWNED BY THE MANAGED IDV MODULE. Render it with `client.mount('idv', { crossDeviceCopy, onEvent })` and the returned handle exposes `openCrossDevice()`: calling it opens the overlay OVER the still-live capture frame, runs the completion poll, and emits `checktiv.idv.submitted` when the phone finishes so your host reloads. You MUST `import '@checktiv/sdk-web/idv/cross-device';` to enable the overlay (the CDN script-tag bundle already includes it); without that import `openCrossDevice()` is a graceful, actionable no-op, as it also is before the applicant verification resolves or when no `crossDeviceCopy` was supplied. `IdvMountOptions` carries `crossDeviceCopy` (host-injected strings; the `unavailableMessage` key is required so no arm renders blank) and the OPTIONAL `onOpenCrossDevice` mint override; `IdvHandle` exposes `openCrossDevice?()`, so call it as `handle.openCrossDevice?.()`. The zero-lifecycle `Checktiv.mount(...)` and its React wrapper `<ChecktivJourney>` ALSO expose `openCrossDevice()` on their handle (reach the React handle via a `ref`), carrying the same `crossDeviceCopy` / optional `onOpenCrossDevice` options; on that path the SDK self-mints the handoff on the working-token plane by default (see below), and the CDN script-tag bundle registers the opener too. The trigger is a DESKTOP affordance: `openCrossDevice()` is a graceful warn-no-op on a coarse-pointer (touch) device, because immersive phone capture and the desktop cross-device overlay are mutually exclusive per device, so gate your trigger to desktop.\n\nWho mints the one-time handoff link depends on the token plane:\n • Working-token plane: the module mints the link ITSELF (it calls its own handoff endpoint `POST /sdk/v1/sessions/me/handoff`, authenticated by the applicant's short-lived working token, and caps the per-overlay \"refresh link\" re-mints). Supply `onOpenCrossDevice` ONLY to point the mint at your own backend instead.\n • Browser-token (`bt_*`) plane: the SDK CANNOT self-mint (the handoff route rejects a `bt_*` bearer), so you MUST supply `onOpenCrossDevice` backed by a server-held credential.\n\n`onOpenCrossDevice: CrossDeviceHandoffHook` is `() => Promise<{ kind: 'ok'; url: string } | { kind: 'unavailable' }>`. Return `{ kind: 'ok', url }` with the journey URL (which MUST be `https:`: the SDK collapses `javascript:`, `http:`, and `data:` schemes to `'unavailable'`) or `{ kind: 'unavailable' }` when the mint fails. The hook is re-callable for the overlay's \"refresh link\" affordance; the SDK caps re-mints per overlay.\n\nFor a FULLY CUSTOM IDV renderer built on `@checktiv/sdk-web/capture` (NOT the managed module), the STANDALONE exports drive the overlay yourself and you always supply the mint hook: `@checktiv/sdk-web/idv/cross-device` (`openCrossDeviceOverlay(opts)` opens the overlay, `preloadCrossDeviceChunk()` warms the lazy chunk) and `@checktiv/sdk-web/cross-device` (`mountCrossDevice(target, props)` renders the bare panel on a screen you fully control). `openCrossDeviceOverlay` takes `{ target, onOpenCrossDevice, copy, isMobile, emit, onClose }` plus an optional `poll` config, and returns a handle with `setCompleting()` (keep the QR mounted with a waiting label while your page reloads) and `destroy()` (idempotent). Set `isMobile: true` to suppress the QR on a phone (a same-device scan is circular). The SDK NEVER writes `window.location`; navigation is always the host's, off the emitted event.\n\nThree token-free overlay events (NONE carries the url / OTL / short-code / expiry hint):\n • `checktiv.idv.cross_device_opened`: the mint returned `ok` and the overlay is shown.\n • `checktiv.idv.cross_device_unavailable`: the mint returned `unavailable` or URL validation failed.\n • `checktiv.idv.cross_device_capped`: the completion poll hit its total time cap without the phone finishing. NOT an error and NOT a verdict: the QR panel stays mounted so the applicant can still finish on their phone."
78
78
  }
79
79
  ],
80
80
  "recipes": [
@@ -133,6 +133,7 @@ declare function createCustomForm(ctx: SubstrateContext, opts: CustomFormMountOp
133
133
 
134
134
  interface ProvisionedHandle {
135
135
  destroy(): void;
136
+ openCrossDevice?(): void;
136
137
  }
137
138
  interface MountProvisionedOptions {
138
139
  target: HTMLElement | string;
@@ -146,6 +147,8 @@ interface MountProvisionedOptions {
146
147
  synthetic?: IdvMountOptions['synthetic'];
147
148
  customFormConfig?: CustomFormApplicantConfig;
148
149
  customFormCopy?: CustomFormCopy;
150
+ crossDeviceCopy?: CrossDeviceCopy;
151
+ onOpenCrossDevice?: CrossDeviceHandoffHook;
149
152
  }
150
153
 
151
154
  interface GetSessionTokenContext {
@@ -270,6 +273,12 @@ type MountOptions = MountScope & {
270
273
  readonly fetchImpl?: typeof fetch;
271
274
  readonly resumeKey?: string;
272
275
  readonly shortCode?: string;
276
+ readonly layout?: CaptureLayout;
277
+ readonly theme?: {
278
+ readonly primaryColor?: string;
279
+ };
280
+ readonly crossDeviceCopy?: CrossDeviceCopy;
281
+ readonly onOpenCrossDevice?: CrossDeviceHandoffHook;
273
282
  };
274
283
  interface SessionsCreateApplicant {
275
284
  readonly externalId?: string;
@@ -0,0 +1,10 @@
1
+ import { getModule } from './chunk-GFHNOZVO.js';
2
+ import { assertNever } from './chunk-UBQTPS5J.js';
3
+ import { createTokenManager, resolveSession, warnWrongTokenPlane, createChecktivError } from './chunk-7NIKMJFJ.js';
4
+ import { R, k } from './chunk-XOGHMNBX.js';
5
+ import { resolveApiBaseOrigin, sdkPublicSettings, shortCodeRefreshPath, PUBLISHABLE_KEY_HEADER, SESSIONS_ME_REFRESH_LINK_PATH, WORKING_TOKEN_MINT_PATH } from './chunk-ACQKQLL2.js';
6
+ import { getServiceOrigin } from './chunk-QFA3K5ZV.js';
7
+
8
+ var PUBLISHABLE_KEY_REGEX=/^ah_pk_(us|eu)_(live|test)_[A-Za-z0-9_-]{8,}$/;var InvalidPublishableKeyError=class extends Error{constructor(message){super(message);this.name="InvalidPublishableKeyError";}};function parsePublishableKey(key){const match=PUBLISHABLE_KEY_REGEX.exec(key);if(!match){throw new InvalidPublishableKeyError(`Invalid publishable key format. Expected ah_pk_<us|eu>_<live|test>_<token>, got: ${maskKey(key)}`)}const region=match[1];const mode=match[2];if(region!=="us"&&region!=="eu"){throw new InvalidPublishableKeyError(`Invalid region in publishable key: ${maskKey(key)}`)}if(mode!=="test"&&mode!=="live"){throw new InvalidPublishableKeyError(`Invalid mode in publishable key: ${maskKey(key)}`)}return {region,mode}}function maskKey(key){if(key.length<=8)return "***";return `${key.slice(0,6)}...${key.slice(-2)}`}var HOST_TAG="checktiv-root";var MOUNTED_FLAG="__checktivMounted";var LEGACY_HOST_ATTR="data-autohost-kyc-host";var HOST_ATTR="data-checktiv-host";var shadowCache=new WeakMap;function mountShadowHost(target){const existing=findExistingHost(target);if(existing){const cached=shadowCache.get(existing);if(cached)return cached}const host=document.createElement("div");host.setAttribute(HOST_ATTR,HOST_TAG);host.setAttribute(LEGACY_HOST_ATTR,HOST_TAG);host[MOUNTED_FLAG]=true;target.appendChild(host);const shadow=host.attachShadow({mode:"closed"});shadowCache.set(host,shadow);return shadow}function findExistingHost(target){for(const child of Array.from(target.children)){if(!(child instanceof HTMLElement))continue;const candidate=child;if(candidate[MOUNTED_FLAG]===true)return candidate}return null}function moduleErrorEvent(name,error){switch(name){case "idv":return {type:"checktiv.idv.error",error};case "fraud":return {type:"checktiv.fraud.error",error};case "custom_form":return {type:"checktiv.custom_form.error",error}}}function missingModuleMessage(name){return `This session requires the '${name}' module. Import '@checktiv/sdk-web/${name}' in your app.`}function missingModuleError(name){const base=createChecktivError("sdk_load_failed");return moduleErrorEvent(name,{...base,message:missingModuleMessage(name)})}function consentGateRequiredError(){const base=createChecktivError("sdk_load_failed");return moduleErrorEvent("fraud",{...base,message:"This session includes the 'fraud' module, which needs a consent gate. Pass onConsent to mountProvisioned() to enable it."})}function customFormConfigRequiredError(){const base=createChecktivError("sdk_load_failed");return moduleErrorEvent("custom_form",{...base,message:"This session includes the 'custom_form' module, which needs its form config. Pass customFormConfig to mountProvisioned() to enable it."})}function unmappedStepTypeError(stepType){const base=createChecktivError("sdk_load_failed");return moduleErrorEvent("idv",{...base,message:`This session's current step ('${stepType}') has no matching visual module in this SDK build. Upgrade '@checktiv/sdk-web' to a version that renders this step.`})}var MODULE_IS_VISUAL={idv:true,fraud:false,custom_form:true};var STEP_TYPE_TO_VISUAL_MODULE={id_verification:"idv",custom_form:"custom_form"};var stepTypeToVisualModuleView=STEP_TYPE_TO_VISUAL_MODULE;function resolveCurrentStepVisualModule(stepType){return stepTypeToVisualModuleView[stepType]}function defaultLoadModule(name){if(name==="idv"){const factory2=getModule("idv");if(factory2===void 0)return Promise.resolve(null);return Promise.resolve({mount:(ctx,opts)=>factory2(ctx,{target:opts.target,onEvent:opts.onEvent,copy:opts.copy,layout:opts.layout,onBeforeSubmit:opts.onBeforeSubmit,onBehaviorSegment:opts.onBehaviorSegment,synthetic:opts.synthetic,crossDeviceCopy:opts.crossDeviceCopy,onOpenCrossDevice:opts.onOpenCrossDevice})})}if(name==="custom_form"){const factory2=getModule("custom_form");if(factory2===void 0)return Promise.resolve(null);return Promise.resolve({mount:(ctx,opts)=>{if(opts.customFormConfig===void 0){opts.onEvent?.(customFormConfigRequiredError());return {destroy:()=>void 0}}return factory2(ctx,{target:opts.target,config:opts.customFormConfig,copy:opts.customFormCopy,onEvent:opts.onEvent,session:opts.session})}})}const factory=getModule("fraud");if(factory===void 0)return Promise.resolve(null);return Promise.resolve({mount:(ctx,opts)=>{const onConsent=opts.onConsent;if(onConsent===void 0){opts.onEvent?.(consentGateRequiredError());return {destroy:()=>void 0}}return factory(ctx,{onConsent,onEvent:opts.onEvent})}})}function moduleErrorFor(name,code){return moduleErrorEvent(name,createChecktivError(code))}function createMountProvisioned(ctx,opts,deps={}){const emit=e=>opts.onEvent?.(e);const fetchImpl=opts.fetchImpl??fetch;const resolveSession2=deps.resolveSession??(()=>resolveSession(ctx,fetchImpl));const loadModule=deps.loadModule??defaultLoadModule;const handles=[];let destroyed=false;let idvOpenCrossDevice=null;void(async()=>{try{await composeModules();}catch(err){if(destroyed)return;console.warn("[Checktiv] mountProvisioned composition failed.",err);emit(moduleErrorFor("idv","sdk_load_failed"));}})();async function composeModules(){const resolution=await resolveSession2();if(destroyed)return;let session;switch(resolution.kind){case "ok":session=resolution.session;break;case "wrong_token":warnWrongTokenPlane();emit(moduleErrorFor("idv","wrong_token_type"));return;case "unresolved":emit(moduleErrorFor("idv","session_expired"));return;default:return assertNever(resolution)}const currentStepVisualModule=resolveCurrentStepVisualModule(session.stepType);if(currentStepVisualModule===void 0&&session.modules.some(m=>MODULE_IS_VISUAL[m])){emit(unmappedStepTypeError(session.stepType));return}for(const name of session.modules){if(destroyed)return;if(MODULE_IS_VISUAL[name]&&name!==currentStepVisualModule){continue}if(name==="fraud"&&opts.onConsent===void 0){emit(consentGateRequiredError());continue}const mod=await loadModule(name);if(destroyed)return;if(mod===null){emit(missingModuleError(name));continue}let moduleTarget;let containerToRemove=null;if(MODULE_IS_VISUAL[name]){const resolvedParent=typeof opts.target==="string"?typeof document!=="undefined"?document.querySelector(opts.target):null:opts.target;if(resolvedParent!==null){const container=document.createElement("div");resolvedParent.appendChild(container);containerToRemove=container;moduleTarget=container;}else {moduleTarget=opts.target;}}else {moduleTarget=opts.target;}let innerHandle;try{innerHandle=mod.mount(ctx,{target:moduleTarget,onEvent:opts.onEvent,onConsent:opts.onConsent,copy:opts.copy,layout:opts.layout,onBeforeSubmit:opts.onBeforeSubmit,onBehaviorSegment:opts.onBehaviorSegment,synthetic:opts.synthetic,crossDeviceCopy:opts.crossDeviceCopy,onOpenCrossDevice:opts.onOpenCrossDevice,customFormConfig:opts.customFormConfig,customFormCopy:opts.customFormCopy,session:{sessionId:session.sessionId,stepId:session.stepId}});}catch(err){containerToRemove?.remove();throw err}if(name==="idv"){idvOpenCrossDevice=innerHandle.openCrossDevice??null;}const capturedContainer=containerToRemove;const handle={destroy(){try{innerHandle.destroy();}finally{capturedContainer?.remove();}}};if(destroyed){handle.destroy();return}handles.push(handle);}}return {destroy(){destroyed=true;while(handles.length>0){const handle=handles.pop();handle?.destroy();}},openCrossDevice(){idvOpenCrossDevice?.();}}}function init(opts,deps={}){const scope="publishableKey"in opts?{...parsePublishableKey(opts.publishableKey),publishableKey:opts.publishableKey}:{region:opts.region,mode:opts.mode,publishableKey:void 0};let sessionId="";const apiBaseOrigin=resolveApiBaseOrigin(opts.apiBase,scope.region,scope.mode);const ctx={region:scope.region,mode:scope.mode,publishableKey:scope.publishableKey,apiBaseOrigin,theme:opts.theme,locale:opts.locale??"en",betas:opts.betas??[],...opts.selfiePromptMap!==void 0?{selfiePromptMap:opts.selfiePromptMap}:{},...opts.selfieChromeCopy!==void 0?{selfieChromeCopy:opts.selfieChromeCopy}:{},tokens:deps.tokens??createTokenManager(opts.getSessionToken,()=>sessionId),getSessionId:()=>sessionId,setSessionId:id=>{sessionId=id;}};function mount2(...args){if(args[0]==="idv"){const factory2=getModule("idv");if(factory2===void 0)return missingIdvHandle(args[1]);return factory2(ctx,args[1])}if(args[0]==="custom_form"){const factory2=getModule("custom_form");if(factory2===void 0)return missingCustomFormHandle(args[1]);return factory2(ctx,args[1])}const factory=getModule("fraud");if(factory===void 0)return missingFraudHandle(args[1]);return factory(ctx,args[1])}return {mountProvisioned:provOpts=>createMountProvisioned(ctx,provOpts),mount:mount2}}function missingIdvHandle(opts){const base=createChecktivError("sdk_load_failed");opts.onEvent?.({type:"checktiv.idv.error",error:{...base,message:missingModuleMessage("idv")}});return {destroy:()=>void 0,unmount:()=>void 0}}function missingFraudHandle(opts){const base=createChecktivError("sdk_load_failed");opts.onEvent?.({type:"checktiv.fraud.error",error:{...base,message:missingModuleMessage("fraud")}});return {destroy:()=>void 0}}function missingCustomFormHandle(opts){const base=createChecktivError("sdk_load_failed");const error={type:"checktiv.custom_form.error",error:{...base,message:missingModuleMessage("custom_form")}};opts.onEvent?.(error);return {destroy:()=>void 0}}var DEFAULT_REFRESH_MARGIN_MS=6e4;function createWorkingTokenManager(source,deps={}){const now=deps.now??(()=>Date.now());const setTimer=deps.setTimer??((fn,ms)=>setTimeout(fn,ms));const clearTimer=deps.clearTimer??(handle=>clearTimeout(handle));const marginMs=deps.marginMs??DEFAULT_REFRESH_MARGIN_MS;const onProactiveError=deps.onProactiveError;let cached=null;let pending=null;let timer=null;let stopped=false;const clearScheduled=()=>{if(timer!==null){clearTimer(timer);timer=null;}};const scheduleProactive=expiresAt=>{clearScheduled();if(stopped)return;const expiryMs=Date.parse(expiresAt);if(Number.isNaN(expiryMs))return;const delay=expiryMs-now()-marginMs;if(delay<=0)return;timer=setTimer(()=>{timer=null;void doRefresh("expired").catch(err=>onProactiveError?.(err));},delay);};const mintOnce=reason=>source(reason).then(minted=>{scheduleProactive(minted.expiresAt);return minted.token});const doRefresh=reason=>{if(pending!==null)return pending;const p=mintOnce(reason);pending=p;cached=p;void p.then(()=>{if(pending===p)pending=null;},()=>{if(pending===p)pending=null;if(cached===p)cached=null;});return p};return {get(){if(cached!==null)return cached;const p=mintOnce("initial");cached=p;void p.catch(()=>{if(cached===p)cached=null;});return p},refresh(reason){return doRefresh(reason)},stop(){stopped=true;clearScheduled();}}}var RESUME_CLIENT_TOKEN_KEY_PREFIX="checktiv.rct.";function safeLocalStorage(){try{return globalThis.localStorage??null}catch{return null}}function createClientTokenStore(resumeKey,storage=safeLocalStorage()){const key=RESUME_CLIENT_TOKEN_KEY_PREFIX+resumeKey;return {read(){if(storage===null)return null;try{const raw=storage.getItem(key);return raw!==null&&raw.length>0?raw:null}catch{return null}},write(token){if(storage===null)return;try{storage.setItem(key,token);}catch{}},clear(){if(storage===null)return;try{storage.removeItem(key);}catch{}}}}async function requestSelfServiceResend(params){const fetchImpl=params.fetchImpl??fetch;const body=JSON.stringify({confirmEmail:params.confirmEmail});try{let res;if(params.mode==="short_code"){res=await fetchImpl(params.origin+shortCodeRefreshPath(params.shortCode),{method:"POST",headers:{"content-type":"application/json"},body});}return {requested:res.ok}}catch{return {requested:false}}}var version="1.4.0";var initialised=new WeakSet;function resolveVerifyOrigin(parsed){return getServiceOrigin(sdkPublicSettings.apps.verify.subdomain,"prod",parsed.region,sdkPublicSettings)}function buildPopupUrl(parsed,opts){const origin=resolveVerifyOrigin(parsed);if(opts.applicantLinkToken){return `${origin}/start?lt=${encodeURIComponent(opts.applicantLinkToken)}`}return `${origin}/`}function launchPopup(url){const w=460;const h=760;const left=Math.max(0,Math.floor((window.screen.width-w)/2));const top=Math.max(0,Math.floor((window.screen.height-h)/2));const features=`popup=yes,width=${w},height=${h},left=${left},top=${top}`;const popup=window.open(url,"checktiv",features);if(!popup){window.location.href=url;return null}return popup}var COMPLETE_EVENT_TYPE="autohost.kyc.complete";function isCompletionMessage(data){if(typeof data!=="object"||data===null)return false;if(!("type"in data))return false;return data.type===COMPLETE_EVENT_TYPE}function listenForCompletion(expectedOrigin,onComplete){if(!onComplete)return;const handler=event=>{if(event.origin!==expectedOrigin)return;const data=event.data;if(!isCompletionMessage(data))return;onComplete(data);};window.addEventListener("message",handler);}function buildButton(onClick){const children="Start verification";return k("button",{type:"button",onClick,style:'display: inline-flex; align-items: center; justify-content: center; padding: 10px 20px; font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; font-size: 14px; font-weight: 600; color: #fff; background: #0b5fff; border: none; border-radius: 6px; cursor: pointer;'},children)}function resolveTarget(opts){if(opts.target)return opts.target;return document.querySelector("[data-checktiv]")??document.querySelector("[data-autohost-kyc]")}function mountButton(opts){const parsed=parsePublishableKey(opts.publishableKey);const target=resolveTarget(opts);if(!target){console.warn("[Checktiv] No mount target found. Either pass `target` explicitly or add `<div data-checktiv>` to your page.");return}if(initialised.has(target))return;initialised.add(target);const shadow=mountShadowHost(target);const verifyOrigin=resolveVerifyOrigin(parsed);listenForCompletion(verifyOrigin,opts.onComplete);const onClick=()=>{const popupUrl=buildPopupUrl(parsed,opts);launchPopup(popupUrl);};R(buildButton(onClick),shadow);}function parseWorkingTokenMintResponse(body){if(typeof body!=="object"||body===null)return null;if(!("workingToken"in body)||!("expiresAt"in body))return null;const workingToken=body.workingToken;const expiresAt=body.expiresAt;if(typeof workingToken!=="string"||workingToken.length===0)return null;if(typeof expiresAt!=="string"||expiresAt.length===0)return null;return {workingToken,expiresAt}}async function mintWorkingTokenFacet(origin,clientToken,publishableKey,fetchImpl){const headers={Authorization:`Bearer ${clientToken}`};if(publishableKey!==void 0)headers[PUBLISHABLE_KEY_HEADER]=publishableKey;const res=await fetchImpl(origin+WORKING_TOKEN_MINT_PATH,{method:"POST",headers});if(!res.ok){throw new Error(`Checktiv working-token mint failed (status ${res.status}).`)}let body;try{body=await res.json();}catch{throw new Error("Checktiv working-token mint returned a non-JSON body.")}const parsed=parseWorkingTokenMintResponse(body);if(parsed===null)throw new Error("Checktiv working-token mint returned a malformed body.");return parsed}function completionSessionId(e){if(e.type!=="checktiv.idv.submitted"&&e.type!=="checktiv.custom_form.submitted"){return null}const sid="sessionId"in e?e.sessionId:void 0;return typeof sid==="string"&&sid.length>0?sid:null}function isSessionExpiredEvent(e){if(!e.type.endsWith(".error")||!("error"in e))return false;const err=e.error;if(typeof err!=="object"||err===null||!("code"in err))return false;return err.code==="session_expired"}function mount(target,opts){const fetchImpl=opts.fetchImpl??fetch;const scope="publishableKey"in opts?{...parsePublishableKey(opts.publishableKey),publishableKey:opts.publishableKey}:{region:opts.region,mode:opts.mode,publishableKey:void 0};const mintOrigin=resolveApiBaseOrigin(opts.apiBase,scope.region,scope.mode);const store=opts.resumeKey!==void 0?createClientTokenStore(opts.resumeKey):null;let clientTokenPromise=null;const getClientToken=()=>{if(clientTokenPromise!==null)return clientTokenPromise;const persisted=store?.read()??null;if(persisted!==null){clientTokenPromise=Promise.resolve(persisted);return clientTokenPromise}const p=opts.fetchToken();clientTokenPromise=p;void p.then(tok=>store?.write(tok),()=>{if(clientTokenPromise===p)clientTokenPromise=null;});return p};const mintWorkingToken=async()=>{const clientToken=await getClientToken();const minted=await mintWorkingTokenFacet(mintOrigin,clientToken,scope.publishableKey,fetchImpl);return {token:minted.workingToken,expiresAt:minted.expiresAt}};const tokens=createWorkingTokenManager(mintWorkingToken);const base={getSessionToken:()=>tokens.get(),...opts.apiBase!==void 0?{apiBase:opts.apiBase}:{},...opts.theme!==void 0?{theme:opts.theme}:{}};const initOptions=scope.publishableKey!==void 0?{...base,publishableKey:scope.publishableKey}:{...base,region:scope.region,mode:scope.mode};const needsChannel=opts.onEvent!==void 0||opts.onComplete!==void 0||store!==null;const onEvent=needsChannel?e=>{opts.onEvent?.(e);const sessionId=completionSessionId(e);if(sessionId!==null){store?.clear();opts.onComplete?.({sessionId});}else if(isSessionExpiredEvent(e)){store?.clear();}}:void 0;const client=init(initOptions,{tokens});const handle=client.mountProvisioned({target,fetchImpl,onEvent,onConsent:opts.onConsent,...opts.layout!==void 0?{layout:opts.layout}:{},...opts.crossDeviceCopy!==void 0?{crossDeviceCopy:opts.crossDeviceCopy}:{},...opts.onOpenCrossDevice!==void 0?{onOpenCrossDevice:opts.onOpenCrossDevice}:{}});const requestResend=confirmEmail=>{if(opts.shortCode===void 0)return Promise.resolve({requested:false});return requestSelfServiceResend({mode:"short_code",origin:mintOrigin,shortCode:opts.shortCode,confirmEmail,fetchImpl})};return {destroy(){handle.destroy();tokens.stop();},openCrossDevice:()=>handle.openCrossDevice?.(),requestResend}}function applicantToWire(a){const wire={};if(a.externalId!==void 0)wire.external_id=a.externalId;if(a.email!==void 0)wire.email=a.email;if(a.phone!==void 0)wire.phone=a.phone;if(a.firstName!==void 0)wire.first_name=a.firstName;if(a.lastName!==void 0)wire.last_name=a.lastName;if(a.dateOfBirth!==void 0)wire.date_of_birth=a.dateOfBirth;if(a.referenceName!==void 0)wire.reference_name=a.referenceName;return wire}function readCreatedClientToken(body){if(typeof body!=="object"||body===null||!("data"in body))return null;const data=body.data;if(typeof data!=="object"||data===null||!("client_token"in data))return null;const clientToken=data.client_token;return typeof clientToken==="string"&&clientToken.length>0?clientToken:null}var sessions={async create(input,options){if(typeof window!=="undefined"||typeof document!=="undefined"){throw new Error("checktiv.sessions.create must run on your server: it needs your secret key. Never call it in the browser.")}const fetchImpl=options.fetchImpl??fetch;const headers={Authorization:`Bearer ${options.secretKey}`,"content-type":"application/json"};if(options.idempotencyKey!==void 0)headers["Idempotency-Key"]=options.idempotencyKey;const body=JSON.stringify({workflow_template_id:input.templateId,applicant:applicantToWire(input.applicant)});const res=await fetchImpl(`${options.apiBase}/v1/sessions`,{method:"POST",headers,body});if(!res.ok){throw new Error(`Checktiv sessions.create failed (status ${res.status}). Verify the apiBase origin and secret key.`)}let parsed;try{parsed=await res.json();}catch{throw new Error("Checktiv sessions.create returned a non-JSON response.")}const clientToken=readCreatedClientToken(parsed);if(clientToken===null){throw new Error("Checktiv sessions.create response carried no client token. Enable the working-token capability for this environment, then retry.")}return {clientToken}}};
9
+
10
+ export { InvalidPublishableKeyError, init, mount, mountButton, parsePublishableKey, sessions, version };
@@ -1,5 +1,5 @@
1
1
  import { IDV_DOCUMENT_TYPES } from './chunk-IQKHRPJR.js';
2
- import { sdkPublicSettings, SESSIONS_ME_PATH, dataPlaneHeaders } from './chunk-ITLSDZHK.js';
2
+ import { sdkPublicSettings, SESSIONS_ME_PATH, dataPlaneHeaders } from './chunk-ACQKQLL2.js';
3
3
 
4
4
  function createTokenManager(source,sessionId){const mint=reason=>typeof source==="string"?Promise.resolve(source):source({sessionId:sessionId(),reason});let cached=null;let pending=null;return {get(){if(cached!==null)return cached;const p=mint("initial");cached=p;void p.catch(()=>{if(cached===p)cached=null;});return p},refresh(reason){if(pending!==null)return pending;const p=mint(reason);pending=p;cached=p;void p.then(()=>{if(pending===p)pending=null;},()=>{if(pending===p)pending=null;if(cached===p)cached=null;});return p}}}async function withTokenRetry(tokens,fn,isUnauthorized){const first=await tokens.get();let result;let error;try{result=await fn(first);}catch(err){error=err;}if(error===void 0&&result!==void 0&&!isUnauthorized(result,void 0)){return result}if(error!==void 0&&!isUnauthorized(void 0,error)){throw error}const fresh=await tokens.refresh("401");return fn(fresh)}var SDK_MODULES=["idv","fraud","custom_form"];var BIOMETRIC_MODES=["none","selfie","liveness"];var DOCUMENT_TYPES=IDV_DOCUMENT_TYPES;var SDK_MODES=["live","test"];var DATA_REGIONS=Object.keys(sdkPublicSettings.regions);function isRecord(v){return typeof v==="object"&&v!==null}function isNonEmptyString(v){return typeof v==="string"&&v.length>0}function isSdkModule(v){return typeof v==="string"&&SDK_MODULES.includes(v)}function isBiometricMode(v){return typeof v==="string"&&BIOMETRIC_MODES.includes(v)}function isSdkMode(v){return typeof v==="string"&&SDK_MODES.includes(v)}function isSdkRegion(v){return typeof v==="string"&&DATA_REGIONS.includes(v)}function isDocumentType(v){return typeof v==="string"&&DOCUMENT_TYPES.includes(v)}function parseIdvConfig(value){if(!isRecord(value))return null;const documentTypes=value.document_types;const biometricMode=value.biometric_mode;const minAge=value.min_age;if(!Array.isArray(documentTypes)||documentTypes.length<1)return null;const types=[];for(const t of documentTypes){if(!isDocumentType(t))return null;types.push(t);}if(!isBiometricMode(biometricMode))return null;if(minAge!==void 0&&typeof minAge!=="number")return null;const config={documentTypes:types,biometricMode};if(typeof minAge==="number")config.minAge=minAge;return config}function parseSessionData(body){if(!isRecord(body)||!isRecord(body.data))return null;const d=body.data;const sessionId=d.session_id;const stepId=d.step_id;const stepType=d.step_type;if(!isNonEmptyString(sessionId)||!isNonEmptyString(stepId)||!isNonEmptyString(stepType)||!isSdkMode(d.mode)||!isSdkRegion(d.region)){return null}if(!Array.isArray(d.modules)||!d.modules.every(isSdkModule))return null;const modules=d.modules;const wantsIdv=stepType==="id_verification";let config=null;if(wantsIdv){config=parseIdvConfig(d.config);if(config===null)return null}return {sessionId,stepId,stepType,config,mode:d.mode,region:d.region,modules}}function isJourneyPlaneBody(body){if(!isRecord(body)||!isRecord(body.data))return false;const d=body.data;if("session_id"in d)return false;return isRecord(d.session)||isRecord(d.snapshot)}function warnWrongTokenPlane(){console.warn("[Checktiv] getSessionToken returned a non-browser token. Pass a browser token (bt_*) to getSessionToken, or use Checktiv.mount() with a client token.");}async function resolveSession(ctx,fetchImpl){const origin=ctx.apiBaseOrigin;let res;try{res=await withTokenRetry(ctx.tokens,token=>fetchImpl(origin+SESSIONS_ME_PATH,{method:"GET",headers:dataPlaneHeaders(ctx,token)}),result=>result?.status===401);}catch{return {kind:"unresolved"}}if(!res.ok)return {kind:"unresolved"};let body;try{body=await res.json();}catch{return {kind:"unresolved"}}const data=parseSessionData(body);if(data!==null){ctx.setSessionId(data.sessionId);return {kind:"ok",session:data}}if(isJourneyPlaneBody(body))return {kind:"wrong_token"};return {kind:"unresolved"}}var ERROR_TABLE={origin_not_allowed:{recoverable:false,recovery:"contact_operator",message:"This origin is not allowed for the publishable key. Add it to the allowed origins list in the Checktiv console."},token_expired:{recoverable:true,recovery:"retry",message:"The session token expired. The SDK is refreshing it; if this persists, mint a new token from your backend."},session_expired:{recoverable:false,recovery:"refresh_session",message:"The verification session is no longer valid. Start a new verification from your backend."},wrong_token_type:{recoverable:false,recovery:"contact_operator",message:"This verification could not start. Contact the site operator."},protocol_mismatch:{recoverable:false,recovery:"contact_operator",message:"The SDK version is out of date. Update to the latest published version."},camera_denied:{recoverable:true,recovery:"cross_device",message:"Camera access was blocked. Allow camera access, or continue on another device."},camera_unsupported:{recoverable:true,recovery:"cross_device",message:"Your camera does not meet the minimum resolution needed for capture. Try another device, or continue on your phone."},cv_gate_failed:{recoverable:true,recovery:"retry",message:"We could not read the image clearly. Try again with better lighting and a steady hand."},upload_failed:{recoverable:true,recovery:"retry",message:"The upload did not complete. Check the connection and try again."},sdk_load_failed:{recoverable:true,recovery:"cross_device",message:"Verification could not start on this device. Try again, or continue on another device."},biometric_unsupported:{recoverable:false,recovery:"contact_operator",message:"This capture is not supported on this page. Contact the site operator."},isolation_required:{recoverable:false,recovery:"contact_operator",message:"This page configuration is not supported. Contact the site operator."}};Object.keys(ERROR_TABLE).filter(key=>key in ERROR_TABLE);function createChecktivError(code){const entry=ERROR_TABLE[code];return {code,...entry}}function mapHttpStatusToError(status){if(status===401)return "token_expired";if(status===403)return "origin_not_allowed";if(status===429)return "upload_failed";if(status>=500)return "upload_failed";return "session_expired"}
5
5
 
@@ -0,0 +1,5 @@
1
+ import { getServiceOrigin } from './chunk-QFA3K5ZV.js';
2
+
3
+ var sdkPublicSettings={"project":{"marketingApex":"checktiv.com"},"apps":{"verify":{"subdomain":"verify"},"workspace-embed":{"subdomain":"workspace"}},"services":{"sdk-api":{"subdomain":"sdk-api"},"sdk-cdn":{"subdomain":"sdk"}},"regions":{"us":{"apex":"us.checktiv.com"},"eu":{"apex":"eu.checktiv.com"}}};var PUBLISHABLE_KEY_HEADER="X-Publishable-Key";function dataPlaneHeaders(ctx,token,opts={}){const headers={Authorization:`Bearer ${token}`};if(ctx.publishableKey!==void 0){headers[PUBLISHABLE_KEY_HEADER]=ctx.publishableKey;}if(opts.withJsonBody)headers["content-type"]="application/json";return headers}function resolveSdkApiOrigin(region,_mode,_hostname){return getServiceOrigin(sdkPublicSettings.services["sdk-api"].subdomain,"prod",region,sdkPublicSettings)}function isCanonicalOrigin(value){if(typeof value!=="string"||value.length===0)return false;try{return new URL(value).origin===value}catch{return false}}function resolveApiBaseOrigin(apiBase,region,mode,hostname){if(isCanonicalOrigin(apiBase))return apiBase;return resolveSdkApiOrigin(region)}var SESSIONS_ME_PATH="/sdk/v1/sessions/me";var WORKING_TOKEN_MINT_PATH="/sdk/v1/working-token";var CAPTURE_TOKEN_MINT_PATH="/sdk/v1/idv/capture-token";function stepSubmitPath(_sessionId,stepId){return `${SESSIONS_ME_PATH}/steps/${encodeURIComponent(stepId)}/submit`}var TELEMETRY_PATH="/sdk/v1/telemetry";var SESSIONS_ME_REFRESH_LINK_PATH="/sdk/v1/sessions/me/refresh-link";function shortCodeRefreshPath(code){return `/sdk/v1/short-codes/${encodeURIComponent(code)}/refresh`}function formUploadUrlPath(stepId){return `${SESSIONS_ME_PATH}/steps/${encodeURIComponent(stepId)}/form-upload-url`}
4
+
5
+ export { CAPTURE_TOKEN_MINT_PATH, PUBLISHABLE_KEY_HEADER, SESSIONS_ME_PATH, SESSIONS_ME_REFRESH_LINK_PATH, TELEMETRY_PATH, WORKING_TOKEN_MINT_PATH, dataPlaneHeaders, formUploadUrlPath, isCanonicalOrigin, resolveApiBaseOrigin, sdkPublicSettings, shortCodeRefreshPath, stepSubmitPath };
@@ -1,4 +1,4 @@
1
- import { createChecktivError } from './chunk-QVDWM4KK.js';
1
+ import { createChecktivError } from './chunk-7NIKMJFJ.js';
2
2
 
3
3
  function createChecktivErrorEvent(code){return {type:"checktiv.idv.error",error:createChecktivError(code)}}var CAPTURE_ERROR_CODE={cv_gate_failed:"cv_gate_failed",upload_failed:"upload_failed",sdk_load_failed:"sdk_load_failed",biometric_unsupported:"biometric_unsupported",camera_unsupported:"camera_unsupported",selfie_camera_denied:"camera_denied",liveness_camera_denied:"camera_denied",liveness_runtime_error:"sdk_load_failed",liveness_timeout:"sdk_load_failed",liveness_server_error:"sdk_load_failed",liveness_cancelled:"sdk_load_failed",protocol_mismatch:"protocol_mismatch"};function mapStateToEvent(state,ctx){switch(state.kind){case "minting":case "waiting_for_init":case "submitting":return null;case "ready":return {type:"checktiv.idv.ready",config:ctx.config};case "capturing":if(state.phase==="coaching"&&state.coaching!==null){return {type:"checktiv.idv.coaching",hint:state.coaching}}return {type:"checktiv.idv.phase",phase:state.phase};case "mint_error":return createChecktivErrorEvent("session_expired");case "submit_error":return createChecktivErrorEvent("upload_failed");case "submit_pending":return {type:"checktiv.idv.processing"};case "capture_superseded":return {type:"checktiv.idv.capture_superseded"};case "token_expired":return createChecktivErrorEvent("token_expired");case "capture_error":return createChecktivErrorEvent(CAPTURE_ERROR_CODE[state.errorKind]);default:return null}}var opener=null;function registerCrossDeviceOpener(fn){opener=fn;}function getCrossDeviceOpener(){return opener}
4
4
 
@@ -1,4 +1,4 @@
1
- export { E as ChecktivCustomFormEvent, H as CustomFormCopy, J as CustomFormFieldCopy, K as CustomFormHandle, L as CustomFormMountOptions, N as createCustomForm } from './api-DyExBfAQ.js';
1
+ export { E as ChecktivCustomFormEvent, H as CustomFormCopy, J as CustomFormFieldCopy, K as CustomFormHandle, L as CustomFormMountOptions, N as createCustomForm } from './api-CkHzWaTo.js';
2
2
  import './machine-CuZalPFX.js';
3
3
  import './copy-Dfmk11S_.js';
4
4
  import './copy-DCeQ4JKx.js';
@@ -1,10 +1,10 @@
1
1
  import { registerModule } from './chunk-GFHNOZVO.js';
2
2
  import { assertNever } from './chunk-UBQTPS5J.js';
3
- import { warnWrongTokenPlane, withTokenRetry, isRecord, isJourneyPlaneBody, createChecktivError, mapHttpStatusToError } from './chunk-QVDWM4KK.js';
3
+ import { warnWrongTokenPlane, withTokenRetry, isRecord, isJourneyPlaneBody, createChecktivError, mapHttpStatusToError } from './chunk-7NIKMJFJ.js';
4
4
  import { u } from './chunk-HV2QKVJX.js';
5
5
  import { R, k, S } from './chunk-XOGHMNBX.js';
6
6
  import './chunk-IQKHRPJR.js';
7
- import { SESSIONS_ME_PATH, dataPlaneHeaders, stepSubmitPath, formUploadUrlPath } from './chunk-ITLSDZHK.js';
7
+ import { SESSIONS_ME_PATH, dataPlaneHeaders, stepSubmitPath, formUploadUrlPath } from './chunk-ACQKQLL2.js';
8
8
  import './chunk-QFA3K5ZV.js';
9
9
  import './chunk-AIK7CDV6.js';
10
10
 
package/dist/fraud.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { f as ChecktivFraudEvent, F as FraudHandle, i as FraudMountOptions, O as createFraud } from './api-DyExBfAQ.js';
1
+ export { f as ChecktivFraudEvent, F as FraudHandle, i as FraudMountOptions, O as createFraud } from './api-CkHzWaTo.js';
2
2
  import './machine-CuZalPFX.js';
3
3
  import './copy-Dfmk11S_.js';
4
4
  import './copy-DCeQ4JKx.js';
package/dist/fraud.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { registerModule } from './chunk-GFHNOZVO.js';
2
2
  import { assertNever } from './chunk-UBQTPS5J.js';
3
- import { resolveSession, createChecktivError, warnWrongTokenPlane, withTokenRetry } from './chunk-QVDWM4KK.js';
3
+ import { resolveSession, createChecktivError, warnWrongTokenPlane, withTokenRetry } from './chunk-7NIKMJFJ.js';
4
4
  import './chunk-IQKHRPJR.js';
5
- import { TELEMETRY_PATH, dataPlaneHeaders } from './chunk-ITLSDZHK.js';
5
+ import { TELEMETRY_PATH, dataPlaneHeaders } from './chunk-ACQKQLL2.js';
6
6
  import './chunk-QFA3K5ZV.js';
7
7
  import { __publicField } from './chunk-AIK7CDV6.js';
8
8
 
@@ -1,4 +1,4 @@
1
- import { A as SubstrateContext, h as CrossDeviceHandoffHook, g as ChecktivIdvEvent } from '../api-DyExBfAQ.js';
1
+ import { A as SubstrateContext, h as CrossDeviceHandoffHook, g as ChecktivIdvEvent } from '../api-CkHzWaTo.js';
2
2
  import { C as CrossDeviceCopy } from '../copy-DCeQ4JKx.js';
3
3
  import { CrossDevicePanelProps, CrossDeviceHandle } from '../cross-device.js';
4
4
  import '../machine-CuZalPFX.js';
@@ -1,7 +1,7 @@
1
- import { registerCrossDeviceOpener, createChecktivErrorEvent } from '../chunk-LTMLIUPC.js';
2
- import { withTokenRetry, createChecktivError, parseSessionData } from '../chunk-QVDWM4KK.js';
1
+ import { registerCrossDeviceOpener, createChecktivErrorEvent } from '../chunk-ZXE45FN5.js';
2
+ import { withTokenRetry, createChecktivError, parseSessionData } from '../chunk-7NIKMJFJ.js';
3
3
  import '../chunk-IQKHRPJR.js';
4
- import { SESSIONS_ME_PATH, dataPlaneHeaders } from '../chunk-ITLSDZHK.js';
4
+ import { SESSIONS_ME_PATH, dataPlaneHeaders } from '../chunk-ACQKQLL2.js';
5
5
  import '../chunk-QFA3K5ZV.js';
6
6
  import '../chunk-AIK7CDV6.js';
7
7
 
package/dist/idv.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import './machine-CuZalPFX.js';
2
2
  import './copy-Dfmk11S_.js';
3
- export { h as CrossDeviceHandoffHook, l as IdvHandle, m as IdvMountOptions, B as REMINT_READY_TIMEOUT_MS, D as createIdv } from './api-DyExBfAQ.js';
3
+ export { h as CrossDeviceHandoffHook, l as IdvHandle, m as IdvMountOptions, B as REMINT_READY_TIMEOUT_MS, D as createIdv } from './api-CkHzWaTo.js';
4
4
  import './copy-DCeQ4JKx.js';
package/dist/idv.js CHANGED
@@ -1,17 +1,17 @@
1
- import { createChecktivErrorEvent, mapStateToEvent, getCrossDeviceOpener } from './chunk-LTMLIUPC.js';
1
+ import { createChecktivErrorEvent, mapStateToEvent, getCrossDeviceOpener } from './chunk-ZXE45FN5.js';
2
2
  import { registerModule } from './chunk-GFHNOZVO.js';
3
3
  import { assertNever } from './chunk-UBQTPS5J.js';
4
- import { resolveSession, warnWrongTokenPlane, createChecktivError, withTokenRetry, mapHttpStatusToError } from './chunk-QVDWM4KK.js';
4
+ import { resolveSession, warnWrongTokenPlane, createChecktivError, withTokenRetry, mapHttpStatusToError } from './chunk-7NIKMJFJ.js';
5
5
  import { mount } from './chunk-5MNNRYD5.js';
6
6
  import './chunk-HV2QKVJX.js';
7
7
  import './chunk-XOGHMNBX.js';
8
8
  import './chunk-5AQCMTZV.js';
9
9
  import { CURRENT_PROTOCOL_VERSION } from './chunk-6U53T32A.js';
10
10
  import './chunk-IQKHRPJR.js';
11
- import { stepSubmitPath, dataPlaneHeaders, CAPTURE_TOKEN_MINT_PATH, isCanonicalOrigin } from './chunk-ITLSDZHK.js';
11
+ import { stepSubmitPath, dataPlaneHeaders, CAPTURE_TOKEN_MINT_PATH, isCanonicalOrigin } from './chunk-ACQKQLL2.js';
12
12
  import './chunk-QFA3K5ZV.js';
13
13
  import './chunk-AIK7CDV6.js';
14
14
 
15
- var PLATFORM_BRAND_DEFAULT={primaryColor:"#2563eb",secondaryColor:"#ff5e4d",colorMode:"auto"};var SURFACE={light:{bg:"#fafaf7",surface1:"#ffffff"},dark:{bg:"#0b0c0f",surface1:"#15171c"}};var WHITE="#ffffff";var NEAR_BLACK="#0b0c0f";var ACCENT_TARGET=3;var ADAPT_STEP=.04;var ADAPT_MAX_ITERS=24;var HOVER_DELTA=.08;var SECONDARY_HUE_SHIFT=150;var HEX_RE=/^#[0-9a-fA-F]{6}$/;function toHex(r,g,b){const ch=v=>Math.max(0,Math.min(255,Math.round(v))).toString(16).padStart(2,"0");return `#${ch(r)}${ch(g)}${ch(b)}`}function parseHex(hex){if(typeof hex!=="string"||!HEX_RE.test(hex))return null;const n=Number.parseInt(hex.slice(1),16);return {r:n>>16&255,g:n>>8&255,b:n&255}}function linearize(channel){const s=channel/255;return s<=.03928?s/12.92:Math.pow((s+.055)/1.055,2.4)}function relativeLuminance(rgb){return .2126*linearize(rgb.r)+.7152*linearize(rgb.g)+.0722*linearize(rgb.b)}function assessContrast(fg,bg){const f=parseHex(fg);const b=parseHex(bg);if(!f||!b)return 1;const l1=relativeLuminance(f);const l2=relativeLuminance(b);const hi=Math.max(l1,l2);const lo=Math.min(l1,l2);return (hi+.05)/(lo+.05)}function rgbToHsl({r,g,b}){const rn=r/255;const gn=g/255;const bn=b/255;const max=Math.max(rn,gn,bn);const min=Math.min(rn,gn,bn);const l=(max+min)/2;let h=0;let s=0;if(max!==min){const d=max-min;s=l>.5?d/(2-max-min):d/(max+min);switch(max){case rn:h=(gn-bn)/d+(gn<bn?6:0);break;case gn:h=(bn-rn)/d+2;break;default:h=(rn-gn)/d+4;break}h/=6;}return {h:h*360,s,l}}function hueToRgb(p,q,tRaw){let t=tRaw;if(t<0)t+=1;if(t>1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<1/2)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p}function hslToRgb({h,s,l}){const hn=(h%360+360)%360/360;let r;let g;let b;if(s===0){r=g=b=l;}else {const q=l<.5?l*(1+s):l+s-l*s;const p=2*l-q;r=hueToRgb(p,q,hn+1/3);g=hueToRgb(p,q,hn);b=hueToRgb(p,q,hn-1/3);}return {r:Math.round(r*255),g:Math.round(g*255),b:Math.round(b*255)}}function deriveSecondaryHue(hex){const rgb=parseHex(hex);if(!rgb)return PLATFORM_BRAND_DEFAULT.secondaryColor;const hsl=rgbToHsl(rgb);const out=hslToRgb({...hsl,h:(hsl.h+SECONDARY_HUE_SHIFT)%360});return toHex(out.r,out.g,out.b)}function shiftLightness(hex,delta){const rgb=parseHex(hex);if(!rgb)return toHex(0,0,0);const hsl=rgbToHsl(rgb);const out=hslToRgb({...hsl,l:Math.max(0,Math.min(1,hsl.l+delta))});return toHex(out.r,out.g,out.b)}function onColorFor(bg){const white=assessContrast(WHITE,bg);const dark=assessContrast(NEAR_BLACK,bg);return white>=dark?WHITE:NEAR_BLACK}function adaptAccent(hex,surface,target,warnings,field,mode){const rgb=parseHex(hex);if(!rgb){return toHex(0,0,0)}const hsl=rgbToHsl(rgb);const lighten=relativeLuminance(parseHex(surface)??{r:255,g:255,b:255})<.5;const rawRatio=assessContrast(hex,surface);let l=hsl.l;let out=toHex(rgb.r,rgb.g,rgb.b);for(let i=0;i<ADAPT_MAX_ITERS;i+=1){if(assessContrast(out,surface)>=target)break;l=Math.max(0,Math.min(1,l+(lighten?ADAPT_STEP:-ADAPT_STEP)));const o=hslToRgb({h:hsl.h,s:hsl.s,l});out=toHex(o.r,o.g,o.b);}const finalRatio=assessContrast(out,surface);if(rawRatio<target||finalRatio<target){warnings.push({field,mode,ratio:Number(rawRatio.toFixed(2))});}return out}function deriveMode(input,mode,warnings){const surf=SURFACE[mode];const primary=adaptAccent(input.primaryColor,surf.bg,ACCENT_TARGET,warnings,"primary",mode);const rawSecondary=input.secondaryColor??deriveSecondaryHue(input.primaryColor);const secondary=adaptAccent(rawSecondary,surf.bg,ACCENT_TARGET,warnings,"secondary",mode);const primaryHover=shiftLightness(primary,mode==="light"?-HOVER_DELTA:HOVER_DELTA);const onPrimary=onColorFor(primary);const onSecondary=onColorFor(secondary);const ring=primary;return {primary,primaryHover,onPrimary,secondary,onSecondary,ring}}function toSafeBrandColors(input){if(!input||parseHex(input.primaryColor)===null){return {primaryColor:PLATFORM_BRAND_DEFAULT.primaryColor,secondaryColor:PLATFORM_BRAND_DEFAULT.secondaryColor,colorMode:PLATFORM_BRAND_DEFAULT.colorMode}}const secondaryColor=typeof input.secondaryColor==="string"&&parseHex(input.secondaryColor)!==null?input.secondaryColor:null;const colorMode="auto";return {primaryColor:input.primaryColor,secondaryColor,colorMode}}function deriveBrandTokens(input){const safe=toSafeBrandColors(input);const warnings=[];return {light:deriveMode(safe,"light",warnings),dark:deriveMode(safe,"dark",warnings),warnings}}function defaultPrefersDark(){if(typeof window==="undefined"||typeof window.matchMedia!=="function"){return false}try{return window.matchMedia("(prefers-color-scheme: dark)").matches}catch{return false}}function defaultForcedMode(){if(typeof document==="undefined")return null;const dataTheme=document.documentElement.dataset.theme;return dataTheme==="light"||dataTheme==="dark"?dataTheme:null}function brandColorsFrom(primaryColor){return {primaryColor,secondaryColor:PLATFORM_BRAND_DEFAULT.secondaryColor,colorMode:"auto"}}function resolveCaptureTheme(ctxTheme,opts={}){const prefersDark=opts.prefersDark??defaultPrefersDark;const forcedMode=opts.forcedMode??defaultForcedMode;const colorMode=forcedMode()??(prefersDark()?"dark":"light");const primaryColor=ctxTheme?.primaryColor;if(typeof primaryColor!=="string"||primaryColor.length===0){return {colorMode,brandTokens:null}}const brandTokens=deriveBrandTokens(brandColorsFrom(primaryColor))[colorMode];return {colorMode,brandTokens}}var LEASE_STORAGE_KEY_PREFIX="checktiv.idv.lease.";function isRecord(v){return typeof v==="object"&&v!==null}function storageKey(sessionId){return LEASE_STORAGE_KEY_PREFIX+sessionId}function safeLocalStorage(){try{return globalThis.localStorage??null}catch{return null}}function generateHolderDeviceId(){const c=globalThis.crypto;if(c&&typeof c.randomUUID==="function"){return c.randomUUID()}const bytes=new Uint8Array(16);c.getRandomValues(bytes);let hex="";for(const b of bytes)hex+=b.toString(16).padStart(2,"0");return hex}function loadPersistedLease(sessionId){const ls=safeLocalStorage();if(ls===null)return null;let raw;try{raw=ls.getItem(storageKey(sessionId));}catch{return null}if(raw===null)return null;let parsed;try{parsed=JSON.parse(raw);}catch{return null}if(!isRecord(parsed))return null;const{holderDeviceId,leaseGeneration}=parsed;if(typeof holderDeviceId!=="string"||holderDeviceId.length===0)return null;const gen=typeof leaseGeneration==="number"&&Number.isInteger(leaseGeneration)&&leaseGeneration>=0?leaseGeneration:void 0;return gen===void 0?{holderDeviceId}:{holderDeviceId,leaseGeneration:gen}}function persistLease(sessionId,record){const ls=safeLocalStorage();if(ls===null)return;try{ls.setItem(storageKey(sessionId),JSON.stringify(record));}catch{}}function loadOrCreatePersistedLease(sessionId){const persisted=loadPersistedLease(sessionId);if(persisted!==null)return persisted;const record={holderDeviceId:generateHolderDeviceId()};persistLease(sessionId,record);return record}var MAX_REMINTS=3;function isRecord2(v){return typeof v==="object"&&v!==null}function asMode(value){return value==="live"||value==="test"?value:null}function asTestHint(value){return value==="pass"||value==="review"||value==="fail"?value:null}function parseMintBody(body){if(!isRecord2(body)||!isRecord2(body.data))return null;const d=body.data;const captureToken=d.capture_token;const embedOrigin=d.embed_origin;const runIndex=d.run_index;const embedContextToken=d.embed_context_token;if(typeof captureToken!=="string"||captureToken.length===0)return null;if(!isCanonicalOrigin(embedOrigin))return null;if(typeof runIndex!=="number"||!Number.isInteger(runIndex)||runIndex<0)return null;if(typeof embedContextToken!=="string"||embedContextToken.length===0)return null;const mode=asMode(d.mode);const testHint=asTestHint(d.test_hint);return {captureToken,embedOrigin,runIndex,embedContextToken,...mode!==null?{mode}:{},...testHint!==null?{testHint}:{}}}function buildCaptureSubmitBody(input){return {runIndex:input.runIndex,r2Keys:input.r2Keys,holderDeviceId:input.holderDeviceId,...input.leaseGeneration!==void 0?{leaseGeneration:input.leaseGeneration}:{}}}function createIdvTransport(ctx,opts){const fetchImpl=opts.fetchImpl??fetch;const origin=ctx.apiBaseOrigin;let embedContextToken=null;let mintMode;let mintTestHint;let remintCount=0;const persistedLease=loadOrCreatePersistedLease(opts.sessionId);const holderDeviceId=persistedLease.holderDeviceId;const leaseGeneration=persistedLease.leaseGeneration;async function mintToken(){const res=await withTokenRetry(ctx.tokens,token=>fetchImpl(origin+CAPTURE_TOKEN_MINT_PATH,{method:"POST",headers:dataPlaneHeaders(ctx,token,{withJsonBody:true}),body:JSON.stringify({stepId:opts.stepId})}),result=>result?.status===401);if(!res.ok){throw createChecktivError(mapHttpStatusToError(res.status))}let body;try{body=await res.json();}catch{opts.onError?.(createChecktivErrorEvent("sdk_load_failed"));throw createChecktivError("sdk_load_failed")}const parsed=parseMintBody(body);if(parsed===null){opts.onError?.(createChecktivErrorEvent("sdk_load_failed"));throw createChecktivError("sdk_load_failed")}embedContextToken=parsed.embedContextToken;mintMode=parsed.mode;mintTestHint=parsed.testHint;return {captureToken:parsed.captureToken,embedOrigin:parsed.embedOrigin,runIndex:parsed.runIndex}}function getEmbedSrc(embedOrigin){if(embedContextToken===null){opts.onError?.(createChecktivErrorEvent("sdk_load_failed"));throw createChecktivError("sdk_load_failed")}return `${embedOrigin}/idv/capture?ctx=${encodeURIComponent(embedContextToken)}`}async function submit(runIndex,r2Keys){let res;try{res=await withTokenRetry(ctx.tokens,token=>fetchImpl(origin+stepSubmitPath(opts.sessionId,opts.stepId??""),{method:"POST",headers:dataPlaneHeaders(ctx,token,{withJsonBody:true}),body:JSON.stringify(buildCaptureSubmitBody({runIndex,r2Keys,holderDeviceId,leaseGeneration}))}),result=>result?.status===401);}catch{return {ok:false,reason:"failed"}}if(res.ok)return {ok:true};if(res.status===401)return {ok:false,reason:"token_expired"};if(res.status===409)return {ok:false,reason:"capture_superseded"};if(res.status===503&&res.headers.has("Retry-After")){return {ok:false,reason:"signal_pending"}}return {ok:false,reason:"failed"}}function initPayload(){return {config:opts.config(),locale:ctx.locale,theme:resolveCaptureTheme(ctx.theme),protocolVersion:CURRENT_PROTOCOL_VERSION,...mintMode!==void 0?{mode:mintMode}:{},...mintTestHint!==void 0?{testHint:mintTestHint}:{},...ctx.selfiePromptMap!==void 0?{selfiePromptMap:ctx.selfiePromptMap}:{},...ctx.selfieChromeCopy!==void 0?{selfieChromeCopy:ctx.selfieChromeCopy}:{}}}async function remint(){if(remintCount>=MAX_REMINTS){opts.onError?.(createChecktivErrorEvent("session_expired"));return null}remintCount+=1;try{return await mintToken()}catch{return null}}function failProtocolMismatch(){opts.onError?.(createChecktivErrorEvent("protocol_mismatch"));}return {mintToken,submit,getEmbedSrc,initPayload,remint,failProtocolMismatch}}function after(delayMs,fn){return setTimeout(fn,delayMs)}function createSyntheticIdv(opts,config,sessionId,script){const emit=e=>opts.onEvent?.(e);const delayMs=script?.stepDelayMs??0;const timers=[];let destroyed=false;function step(offsetMs,fn){timers.push(after(offsetMs,()=>{if(!destroyed)fn();}));}step(delayMs*0,()=>{emit({type:"checktiv.idv.ready",config});});if(script?.injectError!==void 0){const code=script.injectError;step(delayMs*1,()=>{emit(createChecktivErrorEvent(code));});}else {step(delayMs*1,()=>{emit({type:"checktiv.idv.phase",phase:"document_front"});});step(delayMs*2,()=>{emit({type:"checktiv.idv.submitted",sessionId});});}const destroy=()=>{if(destroyed)return;destroyed=true;for(const id of timers)clearTimeout(id);};return {destroy,unmount:destroy}}function resolveTarget(target){if(typeof target!=="string")return target;if(typeof document==="undefined")return null;return document.querySelector(target)}function toCaptureConfig(config){return {documentTypes:config.documentTypes,biometricMode:config.biometricMode,requireMrzMatch:false,requireExpiryValid:false,requireNameMatch:false,documentAuthenticity:false,...config.minAge!==void 0?{minAge:config.minAge}:{}}}var REMINT_READY_TIMEOUT_MS=15e3;function noopHandle(){return {destroy(){},unmount(){},openCrossDevice(){}}}function isCoarsePointerViewport(){return typeof window!=="undefined"&&typeof window.matchMedia==="function"&&window.matchMedia("(pointer: coarse)").matches}function createIdv(ctx,opts){const element=resolveTarget(opts.target);if(element===null){console.warn('[Checktiv] mount("idv"): no target element found. Pass an element or a selector that resolves.');return noopHandle()}const emit=e=>opts.onEvent?.(e);const fetchImpl=opts.fetchImpl??fetch;let teardown=null;let cancelled=false;let crossDeviceSession=null;let crossDeviceOverlay=null;const closeCrossDevice=()=>{if(crossDeviceOverlay!==null){crossDeviceOverlay.destroy();crossDeviceOverlay=null;}};const openCrossDevice=()=>{if(cancelled)return;if(crossDeviceOverlay!==null)return;const sess=crossDeviceSession;if(sess===null){console.warn('[Checktiv] mount("idv"): openCrossDevice() called before the session resolved.');return}const copy=opts.crossDeviceCopy;if(copy===void 0){console.warn('[Checktiv] mount("idv"): openCrossDevice() requires `crossDeviceCopy` (with `unavailableMessage`).');return}const openOverlay=getCrossDeviceOpener();if(openOverlay===null){console.warn(`[Checktiv] mount("idv"): cross-device is not enabled. Import '@checktiv/sdk-web/idv/cross-device' in your app.`);return}crossDeviceOverlay=openOverlay({ctx,fetchImpl,target:sess.element,stepId:sess.stepId,sessionId:sess.sessionId,copy,isMobile:isCoarsePointerViewport(),onOpenCrossDevice:opts.onOpenCrossDevice,emit,onClose:closeCrossDevice});};void(async()=>{try{await setupCapture(element);}catch(err){if(cancelled)return;console.warn('[Checktiv] mount("idv") setup failed.',err);emit(createChecktivErrorEvent("sdk_load_failed"));}})();async function setupCapture(element2){const resolution=await resolveSession(ctx,fetchImpl);if(cancelled)return;let session;switch(resolution.kind){case "ok":session=resolution.session;break;case "wrong_token":warnWrongTokenPlane();emit(createChecktivErrorEvent("wrong_token_type"));return;case "unresolved":emit(createChecktivErrorEvent("session_expired"));return;default:return assertNever(resolution)}const idvConfig=session.config;if(idvConfig===null){emit(createChecktivErrorEvent("session_expired"));return}const readyConfig={documentTypes:idvConfig.documentTypes,biometricMode:idvConfig.biometricMode};if(ctx.mode==="test"&&opts.synthetic!==void 0){const synthHandle=createSyntheticIdv(opts,readyConfig,session.sessionId,opts.synthetic);teardown=()=>synthHandle.destroy();return}const captureConfig=toCaptureConfig(idvConfig);crossDeviceSession={sessionId:session.sessionId,stepId:session.stepId,element:element2};const transport=createIdvTransport(ctx,{fetchImpl,sessionId:session.sessionId,stepId:session.stepId,config:()=>captureConfig,onError:emit});let pinnedOrigin=null;let everReady=false;const onState=state=>{if(state.kind==="ready"){pinnedOrigin=state.embedOrigin;everReady=true;}if(state.kind==="capture_error"&&state.errorKind==="protocol_mismatch"){transport.failProtocolMismatch();return}const event=mapStateToEvent(state,{config:readyConfig,sessionId:session.sessionId});if(event!==null)emit(event);};const onControllerEvent=event=>{if(event.type==="all-complete"){emit({type:"checktiv.idv.submitted",sessionId:session.sessionId});}};const handle=mount(element2,{mintToken:transport.mintToken,submit:transport.submit,getEmbedSrc:transport.getEmbedSrc,initPayload:transport.initPayload,copy:opts.copy??{},layout:opts.layout,onBeforeSubmit:opts.onBeforeSubmit,onBehaviorSegment:opts.onBehaviorSegment,onState,onControllerEvent});let reminting=false;let awaitingRemintReady=false;let remintReadyTimeoutHandle=null;const clearRemintReadyTimeout=()=>{if(remintReadyTimeoutHandle!==null){clearTimeout(remintReadyTimeoutHandle);remintReadyTimeoutHandle=null;}};const armRemintReadyTimeout=()=>{clearRemintReadyTimeout();remintReadyTimeoutHandle=setTimeout(()=>{remintReadyTimeoutHandle=null;if(!awaitingRemintReady)return;if(cancelled)return;void(async()=>{const minted=await transport.remint();if(cancelled||minted===null){awaitingRemintReady=false;reminting=false;return}if(!awaitingRemintReady){reminting=false;return}const frame=element2.querySelector("iframe.idv-capture-frame");if(frame===null){awaitingRemintReady=false;reminting=false;return}armRemintReadyTimeout();frame.src=transport.getEmbedSrc(minted.embedOrigin);})();},REMINT_READY_TIMEOUT_MS);};const isCaptureReady=data=>typeof data==="object"&&data!==null&&"type"in data&&data.type==="capture:ready";const onReloadMessage=mev=>{if(pinnedOrigin===null||mev.origin!==pinnedOrigin)return;if(!everReady)return;if(!isCaptureReady(mev.data))return;const pinnedFrame=element2.querySelector("iframe.idv-capture-frame");if(pinnedFrame===null)return;if(mev.source!==null&&mev.source!==pinnedFrame.contentWindow)return;if(reminting){if(awaitingRemintReady){clearRemintReadyTimeout();awaitingRemintReady=false;reminting=false;}return}reminting=true;void(async()=>{const minted=await transport.remint();if(cancelled||minted===null){reminting=false;return}const frame=element2.querySelector("iframe.idv-capture-frame");if(frame===null){reminting=false;return}awaitingRemintReady=true;armRemintReadyTimeout();frame.src=transport.getEmbedSrc(minted.embedOrigin);})();};if(typeof window!=="undefined"){window.addEventListener("message",onReloadMessage);}teardown=()=>{clearRemintReadyTimeout();if(typeof window!=="undefined"){window.removeEventListener("message",onReloadMessage);}handle.destroy();};}const destroy=()=>{cancelled=true;closeCrossDevice();if(teardown!==null){teardown();teardown=null;}};return {destroy,unmount:destroy,openCrossDevice}}registerModule("idv",createIdv);
15
+ var PLATFORM_BRAND_DEFAULT={primaryColor:"#2563eb",secondaryColor:"#ff5e4d",colorMode:"auto"};var SURFACE={light:{bg:"#fafaf7",surface1:"#ffffff"},dark:{bg:"#0b0c0f",surface1:"#15171c"}};var WHITE="#ffffff";var NEAR_BLACK="#0b0c0f";var ACCENT_TARGET=3;var ADAPT_STEP=.04;var ADAPT_MAX_ITERS=24;var HOVER_DELTA=.08;var SECONDARY_HUE_SHIFT=150;var HEX_RE=/^#[0-9a-fA-F]{6}$/;function toHex(r,g,b){const ch=v=>Math.max(0,Math.min(255,Math.round(v))).toString(16).padStart(2,"0");return `#${ch(r)}${ch(g)}${ch(b)}`}function parseHex(hex){if(typeof hex!=="string"||!HEX_RE.test(hex))return null;const n=Number.parseInt(hex.slice(1),16);return {r:n>>16&255,g:n>>8&255,b:n&255}}function linearize(channel){const s=channel/255;return s<=.03928?s/12.92:Math.pow((s+.055)/1.055,2.4)}function relativeLuminance(rgb){return .2126*linearize(rgb.r)+.7152*linearize(rgb.g)+.0722*linearize(rgb.b)}function assessContrast(fg,bg){const f=parseHex(fg);const b=parseHex(bg);if(!f||!b)return 1;const l1=relativeLuminance(f);const l2=relativeLuminance(b);const hi=Math.max(l1,l2);const lo=Math.min(l1,l2);return (hi+.05)/(lo+.05)}function rgbToHsl({r,g,b}){const rn=r/255;const gn=g/255;const bn=b/255;const max=Math.max(rn,gn,bn);const min=Math.min(rn,gn,bn);const l=(max+min)/2;let h=0;let s=0;if(max!==min){const d=max-min;s=l>.5?d/(2-max-min):d/(max+min);switch(max){case rn:h=(gn-bn)/d+(gn<bn?6:0);break;case gn:h=(bn-rn)/d+2;break;default:h=(rn-gn)/d+4;break}h/=6;}return {h:h*360,s,l}}function hueToRgb(p,q,tRaw){let t=tRaw;if(t<0)t+=1;if(t>1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<1/2)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p}function hslToRgb({h,s,l}){const hn=(h%360+360)%360/360;let r;let g;let b;if(s===0){r=g=b=l;}else {const q=l<.5?l*(1+s):l+s-l*s;const p=2*l-q;r=hueToRgb(p,q,hn+1/3);g=hueToRgb(p,q,hn);b=hueToRgb(p,q,hn-1/3);}return {r:Math.round(r*255),g:Math.round(g*255),b:Math.round(b*255)}}function deriveSecondaryHue(hex){const rgb=parseHex(hex);if(!rgb)return PLATFORM_BRAND_DEFAULT.secondaryColor;const hsl=rgbToHsl(rgb);const out=hslToRgb({...hsl,h:(hsl.h+SECONDARY_HUE_SHIFT)%360});return toHex(out.r,out.g,out.b)}function shiftLightness(hex,delta){const rgb=parseHex(hex);if(!rgb)return toHex(0,0,0);const hsl=rgbToHsl(rgb);const out=hslToRgb({...hsl,l:Math.max(0,Math.min(1,hsl.l+delta))});return toHex(out.r,out.g,out.b)}function onColorFor(bg){const white=assessContrast(WHITE,bg);const dark=assessContrast(NEAR_BLACK,bg);return white>=dark?WHITE:NEAR_BLACK}function adaptAccent(hex,surface,target,warnings,field,mode){const rgb=parseHex(hex);if(!rgb){return toHex(0,0,0)}const hsl=rgbToHsl(rgb);const lighten=relativeLuminance(parseHex(surface)??{r:255,g:255,b:255})<.5;const rawRatio=assessContrast(hex,surface);let l=hsl.l;let out=toHex(rgb.r,rgb.g,rgb.b);for(let i=0;i<ADAPT_MAX_ITERS;i+=1){if(assessContrast(out,surface)>=target)break;l=Math.max(0,Math.min(1,l+(lighten?ADAPT_STEP:-ADAPT_STEP)));const o=hslToRgb({h:hsl.h,s:hsl.s,l});out=toHex(o.r,o.g,o.b);}const finalRatio=assessContrast(out,surface);if(rawRatio<target||finalRatio<target){warnings.push({field,mode,ratio:Number(rawRatio.toFixed(2))});}return out}function deriveMode(input,mode,warnings){const surf=SURFACE[mode];const primary=adaptAccent(input.primaryColor,surf.bg,ACCENT_TARGET,warnings,"primary",mode);const rawSecondary=input.secondaryColor??deriveSecondaryHue(input.primaryColor);const secondary=adaptAccent(rawSecondary,surf.bg,ACCENT_TARGET,warnings,"secondary",mode);const primaryHover=shiftLightness(primary,mode==="light"?-HOVER_DELTA:HOVER_DELTA);const onPrimary=onColorFor(primary);const onSecondary=onColorFor(secondary);const ring=primary;return {primary,primaryHover,onPrimary,secondary,onSecondary,ring}}function toSafeBrandColors(input){if(!input||parseHex(input.primaryColor)===null){return {primaryColor:PLATFORM_BRAND_DEFAULT.primaryColor,secondaryColor:PLATFORM_BRAND_DEFAULT.secondaryColor,colorMode:PLATFORM_BRAND_DEFAULT.colorMode}}const secondaryColor=typeof input.secondaryColor==="string"&&parseHex(input.secondaryColor)!==null?input.secondaryColor:null;const colorMode="auto";return {primaryColor:input.primaryColor,secondaryColor,colorMode}}function deriveBrandTokens(input){const safe=toSafeBrandColors(input);const warnings=[];return {light:deriveMode(safe,"light",warnings),dark:deriveMode(safe,"dark",warnings),warnings}}function defaultPrefersDark(){if(typeof window==="undefined"||typeof window.matchMedia!=="function"){return false}try{return window.matchMedia("(prefers-color-scheme: dark)").matches}catch{return false}}function defaultForcedMode(){if(typeof document==="undefined")return null;const dataTheme=document.documentElement.dataset.theme;return dataTheme==="light"||dataTheme==="dark"?dataTheme:null}function brandColorsFrom(primaryColor){return {primaryColor,secondaryColor:PLATFORM_BRAND_DEFAULT.secondaryColor,colorMode:"auto"}}function resolveCaptureTheme(ctxTheme,opts={}){const prefersDark=opts.prefersDark??defaultPrefersDark;const forcedMode=opts.forcedMode??defaultForcedMode;const colorMode=forcedMode()??(prefersDark()?"dark":"light");const primaryColor=ctxTheme?.primaryColor;if(typeof primaryColor!=="string"||primaryColor.length===0){return {colorMode,brandTokens:null}}const brandTokens=deriveBrandTokens(brandColorsFrom(primaryColor))[colorMode];return {colorMode,brandTokens}}var LEASE_STORAGE_KEY_PREFIX="checktiv.idv.lease.";function isRecord(v){return typeof v==="object"&&v!==null}function storageKey(sessionId){return LEASE_STORAGE_KEY_PREFIX+sessionId}function safeLocalStorage(){try{return globalThis.localStorage??null}catch{return null}}function generateHolderDeviceId(){const c=globalThis.crypto;if(c&&typeof c.randomUUID==="function"){return c.randomUUID()}const bytes=new Uint8Array(16);c.getRandomValues(bytes);let hex="";for(const b of bytes)hex+=b.toString(16).padStart(2,"0");return hex}function loadPersistedLease(sessionId){const ls=safeLocalStorage();if(ls===null)return null;let raw;try{raw=ls.getItem(storageKey(sessionId));}catch{return null}if(raw===null)return null;let parsed;try{parsed=JSON.parse(raw);}catch{return null}if(!isRecord(parsed))return null;const{holderDeviceId,leaseGeneration}=parsed;if(typeof holderDeviceId!=="string"||holderDeviceId.length===0)return null;const gen=typeof leaseGeneration==="number"&&Number.isInteger(leaseGeneration)&&leaseGeneration>=0?leaseGeneration:void 0;return gen===void 0?{holderDeviceId}:{holderDeviceId,leaseGeneration:gen}}function persistLease(sessionId,record){const ls=safeLocalStorage();if(ls===null)return;try{ls.setItem(storageKey(sessionId),JSON.stringify(record));}catch{}}function loadOrCreatePersistedLease(sessionId){const persisted=loadPersistedLease(sessionId);if(persisted!==null)return persisted;const record={holderDeviceId:generateHolderDeviceId()};persistLease(sessionId,record);return record}var MAX_REMINTS=3;function isRecord2(v){return typeof v==="object"&&v!==null}function asMode(value){return value==="live"||value==="test"?value:null}function asTestHint(value){return value==="pass"||value==="review"||value==="fail"?value:null}function parseMintBody(body){if(!isRecord2(body)||!isRecord2(body.data))return null;const d=body.data;const captureToken=d.capture_token;const embedOrigin=d.embed_origin;const runIndex=d.run_index;const embedContextToken=d.embed_context_token;if(typeof captureToken!=="string"||captureToken.length===0)return null;if(!isCanonicalOrigin(embedOrigin))return null;if(typeof runIndex!=="number"||!Number.isInteger(runIndex)||runIndex<0)return null;if(typeof embedContextToken!=="string"||embedContextToken.length===0)return null;const mode=asMode(d.mode);const testHint=asTestHint(d.test_hint);return {captureToken,embedOrigin,runIndex,embedContextToken,...mode!==null?{mode}:{},...testHint!==null?{testHint}:{}}}function buildCaptureSubmitBody(input){return {runIndex:input.runIndex,r2Keys:input.r2Keys,holderDeviceId:input.holderDeviceId,...input.leaseGeneration!==void 0?{leaseGeneration:input.leaseGeneration}:{}}}function createIdvTransport(ctx,opts){const fetchImpl=opts.fetchImpl??fetch;const origin=ctx.apiBaseOrigin;let embedContextToken=null;let mintMode;let mintTestHint;let remintCount=0;const persistedLease=loadOrCreatePersistedLease(opts.sessionId);const holderDeviceId=persistedLease.holderDeviceId;const leaseGeneration=persistedLease.leaseGeneration;async function mintToken(){const res=await withTokenRetry(ctx.tokens,token=>fetchImpl(origin+CAPTURE_TOKEN_MINT_PATH,{method:"POST",headers:dataPlaneHeaders(ctx,token,{withJsonBody:true}),body:JSON.stringify({stepId:opts.stepId})}),result=>result?.status===401);if(!res.ok){throw createChecktivError(mapHttpStatusToError(res.status))}let body;try{body=await res.json();}catch{opts.onError?.(createChecktivErrorEvent("sdk_load_failed"));throw createChecktivError("sdk_load_failed")}const parsed=parseMintBody(body);if(parsed===null){opts.onError?.(createChecktivErrorEvent("sdk_load_failed"));throw createChecktivError("sdk_load_failed")}embedContextToken=parsed.embedContextToken;mintMode=parsed.mode;mintTestHint=parsed.testHint;return {captureToken:parsed.captureToken,embedOrigin:parsed.embedOrigin,runIndex:parsed.runIndex}}function getEmbedSrc(embedOrigin){if(embedContextToken===null){opts.onError?.(createChecktivErrorEvent("sdk_load_failed"));throw createChecktivError("sdk_load_failed")}return `${embedOrigin}/idv/capture?ctx=${encodeURIComponent(embedContextToken)}`}async function submit(runIndex,r2Keys){let res;try{res=await withTokenRetry(ctx.tokens,token=>fetchImpl(origin+stepSubmitPath(opts.sessionId,opts.stepId??""),{method:"POST",headers:dataPlaneHeaders(ctx,token,{withJsonBody:true}),body:JSON.stringify(buildCaptureSubmitBody({runIndex,r2Keys,holderDeviceId,leaseGeneration}))}),result=>result?.status===401);}catch{return {ok:false,reason:"failed"}}if(res.ok)return {ok:true};if(res.status===401)return {ok:false,reason:"token_expired"};if(res.status===409)return {ok:false,reason:"capture_superseded"};if(res.status===503&&res.headers.has("Retry-After")){return {ok:false,reason:"signal_pending"}}return {ok:false,reason:"failed"}}function initPayload(){return {config:opts.config(),locale:ctx.locale,theme:resolveCaptureTheme(ctx.theme),protocolVersion:CURRENT_PROTOCOL_VERSION,...mintMode!==void 0?{mode:mintMode}:{},...mintTestHint!==void 0?{testHint:mintTestHint}:{},...ctx.selfiePromptMap!==void 0?{selfiePromptMap:ctx.selfiePromptMap}:{},...ctx.selfieChromeCopy!==void 0?{selfieChromeCopy:ctx.selfieChromeCopy}:{}}}async function remint(){if(remintCount>=MAX_REMINTS){opts.onError?.(createChecktivErrorEvent("session_expired"));return null}remintCount+=1;try{return await mintToken()}catch{return null}}function failProtocolMismatch(){opts.onError?.(createChecktivErrorEvent("protocol_mismatch"));}return {mintToken,submit,getEmbedSrc,initPayload,remint,failProtocolMismatch}}function after(delayMs,fn){return setTimeout(fn,delayMs)}function createSyntheticIdv(opts,config,sessionId,script){const emit=e=>opts.onEvent?.(e);const delayMs=script?.stepDelayMs??0;const timers=[];let destroyed=false;function step(offsetMs,fn){timers.push(after(offsetMs,()=>{if(!destroyed)fn();}));}step(delayMs*0,()=>{emit({type:"checktiv.idv.ready",config});});if(script?.injectError!==void 0){const code=script.injectError;step(delayMs*1,()=>{emit(createChecktivErrorEvent(code));});}else {step(delayMs*1,()=>{emit({type:"checktiv.idv.phase",phase:"document_front"});});step(delayMs*2,()=>{emit({type:"checktiv.idv.submitted",sessionId});});}const destroy=()=>{if(destroyed)return;destroyed=true;for(const id of timers)clearTimeout(id);};return {destroy,unmount:destroy}}function resolveTarget(target){if(typeof target!=="string")return target;if(typeof document==="undefined")return null;return document.querySelector(target)}function toCaptureConfig(config){return {documentTypes:config.documentTypes,biometricMode:config.biometricMode,requireMrzMatch:false,requireExpiryValid:false,requireNameMatch:false,documentAuthenticity:false,...config.minAge!==void 0?{minAge:config.minAge}:{}}}var REMINT_READY_TIMEOUT_MS=15e3;function noopHandle(){return {destroy(){},unmount(){},openCrossDevice(){}}}function isCoarsePointerViewport(){return typeof window!=="undefined"&&typeof window.matchMedia==="function"&&window.matchMedia("(pointer: coarse)").matches}function createIdv(ctx,opts){const element=resolveTarget(opts.target);if(element===null){console.warn('[Checktiv] mount("idv"): no target element found. Pass an element or a selector that resolves.');return noopHandle()}const emit=e=>opts.onEvent?.(e);const fetchImpl=opts.fetchImpl??fetch;let teardown=null;let cancelled=false;let crossDeviceSession=null;let crossDeviceOverlay=null;const closeCrossDevice=()=>{if(crossDeviceOverlay!==null){crossDeviceOverlay.destroy();crossDeviceOverlay=null;}};const openCrossDevice=()=>{if(cancelled)return;if(crossDeviceOverlay!==null)return;if(isCoarsePointerViewport()){console.warn('[Checktiv] mount("idv"): openCrossDevice() is a desktop affordance; ignoring the call on a touch device (the immersive capture layout already owns the phone).');return}const sess=crossDeviceSession;if(sess===null){console.warn('[Checktiv] mount("idv"): openCrossDevice() called before the session resolved.');return}const copy=opts.crossDeviceCopy;if(copy===void 0){console.warn('[Checktiv] mount("idv"): openCrossDevice() requires `crossDeviceCopy` (with `unavailableMessage`).');return}const openOverlay=getCrossDeviceOpener();if(openOverlay===null){console.warn(`[Checktiv] mount("idv"): cross-device is not enabled. Import '@checktiv/sdk-web/idv/cross-device' in your app.`);return}crossDeviceOverlay=openOverlay({ctx,fetchImpl,target:sess.element,stepId:sess.stepId,sessionId:sess.sessionId,copy,isMobile:isCoarsePointerViewport(),onOpenCrossDevice:opts.onOpenCrossDevice,emit,onClose:closeCrossDevice});};void(async()=>{try{await setupCapture(element);}catch(err){if(cancelled)return;console.warn('[Checktiv] mount("idv") setup failed.',err);emit(createChecktivErrorEvent("sdk_load_failed"));}})();async function setupCapture(element2){const resolution=await resolveSession(ctx,fetchImpl);if(cancelled)return;let session;switch(resolution.kind){case "ok":session=resolution.session;break;case "wrong_token":warnWrongTokenPlane();emit(createChecktivErrorEvent("wrong_token_type"));return;case "unresolved":emit(createChecktivErrorEvent("session_expired"));return;default:return assertNever(resolution)}const idvConfig=session.config;if(idvConfig===null){emit(createChecktivErrorEvent("session_expired"));return}const readyConfig={documentTypes:idvConfig.documentTypes,biometricMode:idvConfig.biometricMode};if(ctx.mode==="test"&&opts.synthetic!==void 0){const synthHandle=createSyntheticIdv(opts,readyConfig,session.sessionId,opts.synthetic);teardown=()=>synthHandle.destroy();return}const captureConfig=toCaptureConfig(idvConfig);crossDeviceSession={sessionId:session.sessionId,stepId:session.stepId,element:element2};const transport=createIdvTransport(ctx,{fetchImpl,sessionId:session.sessionId,stepId:session.stepId,config:()=>captureConfig,onError:emit});let pinnedOrigin=null;let everReady=false;const onState=state=>{if(state.kind==="ready"){pinnedOrigin=state.embedOrigin;everReady=true;}if(state.kind==="capture_error"&&state.errorKind==="protocol_mismatch"){transport.failProtocolMismatch();return}const event=mapStateToEvent(state,{config:readyConfig,sessionId:session.sessionId});if(event!==null)emit(event);};const onControllerEvent=event=>{if(event.type==="all-complete"){emit({type:"checktiv.idv.submitted",sessionId:session.sessionId});}};const handle=mount(element2,{mintToken:transport.mintToken,submit:transport.submit,getEmbedSrc:transport.getEmbedSrc,initPayload:transport.initPayload,copy:opts.copy??{},layout:opts.layout,onBeforeSubmit:opts.onBeforeSubmit,onBehaviorSegment:opts.onBehaviorSegment,onState,onControllerEvent});let reminting=false;let awaitingRemintReady=false;let remintReadyTimeoutHandle=null;const clearRemintReadyTimeout=()=>{if(remintReadyTimeoutHandle!==null){clearTimeout(remintReadyTimeoutHandle);remintReadyTimeoutHandle=null;}};const armRemintReadyTimeout=()=>{clearRemintReadyTimeout();remintReadyTimeoutHandle=setTimeout(()=>{remintReadyTimeoutHandle=null;if(!awaitingRemintReady)return;if(cancelled)return;void(async()=>{const minted=await transport.remint();if(cancelled||minted===null){awaitingRemintReady=false;reminting=false;return}if(!awaitingRemintReady){reminting=false;return}const frame=element2.querySelector("iframe.idv-capture-frame");if(frame===null){awaitingRemintReady=false;reminting=false;return}armRemintReadyTimeout();frame.src=transport.getEmbedSrc(minted.embedOrigin);})();},REMINT_READY_TIMEOUT_MS);};const isCaptureReady=data=>typeof data==="object"&&data!==null&&"type"in data&&data.type==="capture:ready";const onReloadMessage=mev=>{if(pinnedOrigin===null||mev.origin!==pinnedOrigin)return;if(!everReady)return;if(!isCaptureReady(mev.data))return;const pinnedFrame=element2.querySelector("iframe.idv-capture-frame");if(pinnedFrame===null)return;if(mev.source!==null&&mev.source!==pinnedFrame.contentWindow)return;if(reminting){if(awaitingRemintReady){clearRemintReadyTimeout();awaitingRemintReady=false;reminting=false;}return}reminting=true;void(async()=>{const minted=await transport.remint();if(cancelled||minted===null){reminting=false;return}const frame=element2.querySelector("iframe.idv-capture-frame");if(frame===null){reminting=false;return}awaitingRemintReady=true;armRemintReadyTimeout();frame.src=transport.getEmbedSrc(minted.embedOrigin);})();};if(typeof window!=="undefined"){window.addEventListener("message",onReloadMessage);}teardown=()=>{clearRemintReadyTimeout();if(typeof window!=="undefined"){window.removeEventListener("message",onReloadMessage);}handle.destroy();};}const destroy=()=>{cancelled=true;closeCrossDevice();if(teardown!==null){teardown();teardown=null;}};return {destroy,unmount:destroy,openCrossDevice}}registerModule("idv",createIdv);
16
16
 
17
17
  export { REMINT_READY_TIMEOUT_MS, createIdv };
@@ -0,0 +1,35 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ReactElement } from 'react';
3
+ import { g as ChecktivIdvEvent, M as MountOptions, C as ChecktivInitOptions, z as ChecktivInitScope, a as ChecktivClient } from './api-CkHzWaTo.js';
4
+ import { C as CaptureLayout, a as CaptureBehaviorMessage } from './machine-CuZalPFX.js';
5
+ import { M as MountCopy } from './copy-Dfmk11S_.js';
6
+
7
+ interface SelfServiceResendResult {
8
+ readonly requested: boolean;
9
+ }
10
+
11
+ interface ChecktivProviderPropsBase {
12
+ getSessionToken: ChecktivInitOptions['getSessionToken'];
13
+ theme?: ChecktivInitOptions['theme'];
14
+ locale?: string;
15
+ children: ReactNode;
16
+ }
17
+ type ChecktivProviderProps = ChecktivProviderPropsBase & ChecktivInitScope;
18
+ declare function ChecktivProvider(props: ChecktivProviderProps): ReactElement | null;
19
+ declare function useChecktiv(): ChecktivClient;
20
+ interface ChecktivIdvProps {
21
+ onEvent: (e: ChecktivIdvEvent) => void;
22
+ copy?: MountCopy;
23
+ layout?: CaptureLayout;
24
+ onBeforeSubmit?: () => Promise<void>;
25
+ onBehaviorSegment?: (segment: Pick<CaptureBehaviorMessage, 'steps' | 'traits'>) => void;
26
+ }
27
+ declare function ChecktivIdv({ onEvent, copy, layout, onBeforeSubmit, onBehaviorSegment, }: ChecktivIdvProps): ReactElement | null;
28
+ type ChecktivJourneyProps = MountOptions;
29
+ interface ChecktivJourneyHandle {
30
+ requestResend(confirmEmail: string): Promise<SelfServiceResendResult>;
31
+ openCrossDevice(): void;
32
+ }
33
+ declare const ChecktivJourney: react.ForwardRefExoticComponent<MountOptions & react.RefAttributes<ChecktivJourneyHandle>>;
34
+
35
+ export { type ChecktivIdvProps as C, type SelfServiceResendResult as S, type ChecktivJourneyHandle as a, type ChecktivJourneyProps as b, type ChecktivProviderProps as c, ChecktivIdv as d, ChecktivJourney as e, ChecktivProvider as f, type ChecktivProviderPropsBase as g, useChecktiv as u };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { C as ChecktivInitOptions, T as TokenManager, a as ChecktivClient, P as ProvisionedHandle, M as MountOptions, I as InitOptions, b as ChecktivSessions } from './api-DyExBfAQ.js';
2
- export { c as ChecktivError, d as ChecktivErrorCode, e as ChecktivEvent, f as ChecktivFraudEvent, g as ChecktivIdvEvent, h as CrossDeviceHandoffHook, F as FraudHandle, i as FraudMountOptions, G as GetSessionToken, j as GetSessionTokenContext, k as IdvCoachingHint, l as IdvHandle, m as IdvMountOptions, n as InvalidPublishableKeyError, o as MountCompleteResult, p as MountProvisionedOptions, q as MountScope, r as ParsedPublishableKey, R as ResolvedIdvConfig, S as SdkMode, s as SdkRegion, t as SessionsCreateApplicant, u as SessionsCreateInput, v as SessionsCreateOptions, w as SessionsCreateResult, x as SyntheticIdvScript, y as parsePublishableKey } from './api-DyExBfAQ.js';
3
- export { ChecktivIdvProps, ChecktivProviderProps } from './react.js';
1
+ import { C as ChecktivInitOptions, T as TokenManager, a as ChecktivClient, P as ProvisionedHandle, M as MountOptions, I as InitOptions, b as ChecktivSessions } from './api-CkHzWaTo.js';
2
+ export { c as ChecktivError, d as ChecktivErrorCode, e as ChecktivEvent, f as ChecktivFraudEvent, g as ChecktivIdvEvent, h as CrossDeviceHandoffHook, F as FraudHandle, i as FraudMountOptions, G as GetSessionToken, j as GetSessionTokenContext, k as IdvCoachingHint, l as IdvHandle, m as IdvMountOptions, n as InvalidPublishableKeyError, o as MountCompleteResult, p as MountProvisionedOptions, q as MountScope, r as ParsedPublishableKey, R as ResolvedIdvConfig, S as SdkMode, s as SdkRegion, t as SessionsCreateApplicant, u as SessionsCreateInput, v as SessionsCreateOptions, w as SessionsCreateResult, x as SyntheticIdvScript, y as parsePublishableKey } from './api-CkHzWaTo.js';
3
+ import { S as SelfServiceResendResult } from './index-Ch6p6GHo.js';
4
+ export { C as ChecktivIdvProps, a as ChecktivJourneyHandle, b as ChecktivJourneyProps, c as ChecktivProviderProps } from './index-Ch6p6GHo.js';
5
+ export { C as CrossDeviceCopy } from './copy-DCeQ4JKx.js';
4
6
  import './machine-CuZalPFX.js';
5
7
  import './copy-Dfmk11S_.js';
6
- import './copy-DCeQ4JKx.js';
7
8
  import 'react';
8
9
 
9
10
  interface InitDeps {
@@ -11,16 +12,13 @@ interface InitDeps {
11
12
  }
12
13
  declare function init(opts: ChecktivInitOptions, deps?: InitDeps): ChecktivClient;
13
14
 
14
- interface SelfServiceResendResult {
15
- readonly requested: boolean;
16
- }
17
-
18
15
  declare const version: string;
19
16
  declare function mountButton(opts: InitOptions): void;
20
17
  interface MountHandle extends ProvisionedHandle {
21
18
  requestResend(confirmEmail: string): Promise<SelfServiceResendResult>;
19
+ openCrossDevice?(): void;
22
20
  }
23
21
  declare function mount(target: HTMLElement | string, opts: MountOptions): MountHandle;
24
22
  declare const sessions: ChecktivSessions;
25
23
 
26
- export { ChecktivClient, ChecktivInitOptions, ChecktivSessions, InitOptions, type MountHandle, MountOptions, ProvisionedHandle, type SelfServiceResendResult, init, mount, mountButton, sessions, version };
24
+ export { ChecktivClient, ChecktivInitOptions, ChecktivSessions, InitOptions, type MountHandle, MountOptions, ProvisionedHandle, SelfServiceResendResult, init, mount, mountButton, sessions, version };
package/dist/index.js CHANGED
@@ -1,14 +1,9 @@
1
- import { parsePublishableKey, init } from './chunk-BEZGAQ74.js';
2
- export { InvalidPublishableKeyError, init, parsePublishableKey } from './chunk-BEZGAQ74.js';
1
+ export { InvalidPublishableKeyError, init, mount, mountButton, parsePublishableKey, sessions, version } from './chunk-2ZXL5RVX.js';
3
2
  import './chunk-GFHNOZVO.js';
4
3
  import './chunk-UBQTPS5J.js';
5
- import './chunk-QVDWM4KK.js';
6
- import { R, k } from './chunk-XOGHMNBX.js';
4
+ import './chunk-7NIKMJFJ.js';
5
+ import './chunk-XOGHMNBX.js';
7
6
  import './chunk-IQKHRPJR.js';
8
- import { resolveApiBaseOrigin, sdkPublicSettings, shortCodeRefreshPath, PUBLISHABLE_KEY_HEADER, SESSIONS_ME_REFRESH_LINK_PATH, WORKING_TOKEN_MINT_PATH } from './chunk-ITLSDZHK.js';
9
- import { getServiceOrigin } from './chunk-QFA3K5ZV.js';
7
+ import './chunk-ACQKQLL2.js';
8
+ import './chunk-QFA3K5ZV.js';
10
9
  import './chunk-AIK7CDV6.js';
11
-
12
- var HOST_TAG="checktiv-root";var MOUNTED_FLAG="__checktivMounted";var LEGACY_HOST_ATTR="data-autohost-kyc-host";var HOST_ATTR="data-checktiv-host";var shadowCache=new WeakMap;function mountShadowHost(target){const existing=findExistingHost(target);if(existing){const cached=shadowCache.get(existing);if(cached)return cached}const host=document.createElement("div");host.setAttribute(HOST_ATTR,HOST_TAG);host.setAttribute(LEGACY_HOST_ATTR,HOST_TAG);host[MOUNTED_FLAG]=true;target.appendChild(host);const shadow=host.attachShadow({mode:"closed"});shadowCache.set(host,shadow);return shadow}function findExistingHost(target){for(const child of Array.from(target.children)){if(!(child instanceof HTMLElement))continue;const candidate=child;if(candidate[MOUNTED_FLAG]===true)return candidate}return null}var DEFAULT_REFRESH_MARGIN_MS=6e4;function createWorkingTokenManager(source,deps={}){const now=deps.now??(()=>Date.now());const setTimer=deps.setTimer??((fn,ms)=>setTimeout(fn,ms));const clearTimer=deps.clearTimer??(handle=>clearTimeout(handle));const marginMs=deps.marginMs??DEFAULT_REFRESH_MARGIN_MS;const onProactiveError=deps.onProactiveError;let cached=null;let pending=null;let timer=null;let stopped=false;const clearScheduled=()=>{if(timer!==null){clearTimer(timer);timer=null;}};const scheduleProactive=expiresAt=>{clearScheduled();if(stopped)return;const expiryMs=Date.parse(expiresAt);if(Number.isNaN(expiryMs))return;const delay=expiryMs-now()-marginMs;if(delay<=0)return;timer=setTimer(()=>{timer=null;void doRefresh("expired").catch(err=>onProactiveError?.(err));},delay);};const mintOnce=reason=>source(reason).then(minted=>{scheduleProactive(minted.expiresAt);return minted.token});const doRefresh=reason=>{if(pending!==null)return pending;const p=mintOnce(reason);pending=p;cached=p;void p.then(()=>{if(pending===p)pending=null;},()=>{if(pending===p)pending=null;if(cached===p)cached=null;});return p};return {get(){if(cached!==null)return cached;const p=mintOnce("initial");cached=p;void p.catch(()=>{if(cached===p)cached=null;});return p},refresh(reason){return doRefresh(reason)},stop(){stopped=true;clearScheduled();}}}var RESUME_CLIENT_TOKEN_KEY_PREFIX="checktiv.rct.";function safeLocalStorage(){try{return globalThis.localStorage??null}catch{return null}}function createClientTokenStore(resumeKey,storage=safeLocalStorage()){const key=RESUME_CLIENT_TOKEN_KEY_PREFIX+resumeKey;return {read(){if(storage===null)return null;try{const raw=storage.getItem(key);return raw!==null&&raw.length>0?raw:null}catch{return null}},write(token){if(storage===null)return;try{storage.setItem(key,token);}catch{}},clear(){if(storage===null)return;try{storage.removeItem(key);}catch{}}}}async function requestSelfServiceResend(params){const fetchImpl=params.fetchImpl??fetch;const body=JSON.stringify({confirmEmail:params.confirmEmail});try{let res;if(params.mode==="short_code"){res=await fetchImpl(params.origin+shortCodeRefreshPath(params.shortCode),{method:"POST",headers:{"content-type":"application/json"},body});}return {requested:res.ok}}catch{return {requested:false}}}var version="1.2.0";var initialised=new WeakSet;function resolveVerifyOrigin(parsed){return getServiceOrigin(sdkPublicSettings.apps.verify.subdomain,"prod",parsed.region,sdkPublicSettings)}function buildPopupUrl(parsed,opts){const origin=resolveVerifyOrigin(parsed);if(opts.applicantLinkToken){return `${origin}/start?lt=${encodeURIComponent(opts.applicantLinkToken)}`}return `${origin}/`}function launchPopup(url){const w=460;const h=760;const left=Math.max(0,Math.floor((window.screen.width-w)/2));const top=Math.max(0,Math.floor((window.screen.height-h)/2));const features=`popup=yes,width=${w},height=${h},left=${left},top=${top}`;const popup=window.open(url,"checktiv",features);if(!popup){window.location.href=url;return null}return popup}var COMPLETE_EVENT_TYPE="autohost.kyc.complete";function isCompletionMessage(data){if(typeof data!=="object"||data===null)return false;if(!("type"in data))return false;return data.type===COMPLETE_EVENT_TYPE}function listenForCompletion(expectedOrigin,onComplete){if(!onComplete)return;const handler=event=>{if(event.origin!==expectedOrigin)return;const data=event.data;if(!isCompletionMessage(data))return;onComplete(data);};window.addEventListener("message",handler);}function buildButton(onClick){const children="Start verification";return k("button",{type:"button",onClick,style:'display: inline-flex; align-items: center; justify-content: center; padding: 10px 20px; font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; font-size: 14px; font-weight: 600; color: #fff; background: #0b5fff; border: none; border-radius: 6px; cursor: pointer;'},children)}function resolveTarget(opts){if(opts.target)return opts.target;return document.querySelector("[data-checktiv]")??document.querySelector("[data-autohost-kyc]")}function mountButton(opts){const parsed=parsePublishableKey(opts.publishableKey);const target=resolveTarget(opts);if(!target){console.warn("[Checktiv] No mount target found. Either pass `target` explicitly or add `<div data-checktiv>` to your page.");return}if(initialised.has(target))return;initialised.add(target);const shadow=mountShadowHost(target);const verifyOrigin=resolveVerifyOrigin(parsed);listenForCompletion(verifyOrigin,opts.onComplete);const onClick=()=>{const popupUrl=buildPopupUrl(parsed,opts);launchPopup(popupUrl);};R(buildButton(onClick),shadow);}function parseWorkingTokenMintResponse(body){if(typeof body!=="object"||body===null)return null;if(!("workingToken"in body)||!("expiresAt"in body))return null;const workingToken=body.workingToken;const expiresAt=body.expiresAt;if(typeof workingToken!=="string"||workingToken.length===0)return null;if(typeof expiresAt!=="string"||expiresAt.length===0)return null;return {workingToken,expiresAt}}async function mintWorkingTokenFacet(origin,clientToken,publishableKey,fetchImpl){const headers={Authorization:`Bearer ${clientToken}`};if(publishableKey!==void 0)headers[PUBLISHABLE_KEY_HEADER]=publishableKey;const res=await fetchImpl(origin+WORKING_TOKEN_MINT_PATH,{method:"POST",headers});if(!res.ok){throw new Error(`Checktiv working-token mint failed (status ${res.status}).`)}let body;try{body=await res.json();}catch{throw new Error("Checktiv working-token mint returned a non-JSON body.")}const parsed=parseWorkingTokenMintResponse(body);if(parsed===null)throw new Error("Checktiv working-token mint returned a malformed body.");return parsed}function completionSessionId(e){if(e.type!=="checktiv.idv.submitted"&&e.type!=="checktiv.custom_form.submitted"){return null}const sid="sessionId"in e?e.sessionId:void 0;return typeof sid==="string"&&sid.length>0?sid:null}function isSessionExpiredEvent(e){if(!e.type.endsWith(".error")||!("error"in e))return false;const err=e.error;if(typeof err!=="object"||err===null||!("code"in err))return false;return err.code==="session_expired"}function mount(target,opts){const fetchImpl=opts.fetchImpl??fetch;const scope="publishableKey"in opts?{...parsePublishableKey(opts.publishableKey),publishableKey:opts.publishableKey}:{region:opts.region,mode:opts.mode,publishableKey:void 0};const mintOrigin=resolveApiBaseOrigin(opts.apiBase,scope.region,scope.mode);const store=opts.resumeKey!==void 0?createClientTokenStore(opts.resumeKey):null;let clientTokenPromise=null;const getClientToken=()=>{if(clientTokenPromise!==null)return clientTokenPromise;const persisted=store?.read()??null;if(persisted!==null){clientTokenPromise=Promise.resolve(persisted);return clientTokenPromise}const p=opts.fetchToken();clientTokenPromise=p;void p.then(tok=>store?.write(tok),()=>{if(clientTokenPromise===p)clientTokenPromise=null;});return p};const mintWorkingToken=async()=>{const clientToken=await getClientToken();const minted=await mintWorkingTokenFacet(mintOrigin,clientToken,scope.publishableKey,fetchImpl);return {token:minted.workingToken,expiresAt:minted.expiresAt}};const tokens=createWorkingTokenManager(mintWorkingToken);const base={getSessionToken:()=>tokens.get(),...opts.apiBase!==void 0?{apiBase:opts.apiBase}:{}};const initOptions=scope.publishableKey!==void 0?{...base,publishableKey:scope.publishableKey}:{...base,region:scope.region,mode:scope.mode};const needsChannel=opts.onEvent!==void 0||opts.onComplete!==void 0||store!==null;const onEvent=needsChannel?e=>{opts.onEvent?.(e);const sessionId=completionSessionId(e);if(sessionId!==null){store?.clear();opts.onComplete?.({sessionId});}else if(isSessionExpiredEvent(e)){store?.clear();}}:void 0;const client=init(initOptions,{tokens});const handle=client.mountProvisioned({target,fetchImpl,onEvent,onConsent:opts.onConsent});const requestResend=confirmEmail=>{if(opts.shortCode===void 0)return Promise.resolve({requested:false});return requestSelfServiceResend({mode:"short_code",origin:mintOrigin,shortCode:opts.shortCode,confirmEmail,fetchImpl})};return {destroy(){handle.destroy();tokens.stop();},requestResend}}function applicantToWire(a){const wire={};if(a.externalId!==void 0)wire.external_id=a.externalId;if(a.email!==void 0)wire.email=a.email;if(a.phone!==void 0)wire.phone=a.phone;if(a.firstName!==void 0)wire.first_name=a.firstName;if(a.lastName!==void 0)wire.last_name=a.lastName;if(a.dateOfBirth!==void 0)wire.date_of_birth=a.dateOfBirth;if(a.referenceName!==void 0)wire.reference_name=a.referenceName;return wire}function readCreatedClientToken(body){if(typeof body!=="object"||body===null||!("data"in body))return null;const data=body.data;if(typeof data!=="object"||data===null||!("client_token"in data))return null;const clientToken=data.client_token;return typeof clientToken==="string"&&clientToken.length>0?clientToken:null}var sessions={async create(input,options){if(typeof window!=="undefined"||typeof document!=="undefined"){throw new Error("checktiv.sessions.create must run on your server: it needs your secret key. Never call it in the browser.")}const fetchImpl=options.fetchImpl??fetch;const headers={Authorization:`Bearer ${options.secretKey}`,"content-type":"application/json"};if(options.idempotencyKey!==void 0)headers["Idempotency-Key"]=options.idempotencyKey;const body=JSON.stringify({workflow_template_id:input.templateId,applicant:applicantToWire(input.applicant)});const res=await fetchImpl(`${options.apiBase}/v1/sessions`,{method:"POST",headers,body});if(!res.ok){throw new Error(`Checktiv sessions.create failed (status ${res.status}). Verify the apiBase origin and secret key.`)}let parsed;try{parsed=await res.json();}catch{throw new Error("Checktiv sessions.create returned a non-JSON response.")}const clientToken=readCreatedClientToken(parsed);if(clientToken===null){throw new Error("Checktiv sessions.create response carried no client token. Enable the working-token capability for this environment, then retry.")}return {clientToken}}};
13
-
14
- export { mount, mountButton, sessions, version };
package/dist/react.d.ts CHANGED
@@ -1,25 +1,6 @@
1
- import { ReactNode, ReactElement } from 'react';
2
- import { g as ChecktivIdvEvent, C as ChecktivInitOptions, z as ChecktivInitScope, a as ChecktivClient } from './api-DyExBfAQ.js';
3
- import { C as CaptureLayout, a as CaptureBehaviorMessage } from './machine-CuZalPFX.js';
4
- import { M as MountCopy } from './copy-Dfmk11S_.js';
1
+ import 'react';
2
+ import './api-CkHzWaTo.js';
3
+ import './machine-CuZalPFX.js';
4
+ import './copy-Dfmk11S_.js';
5
+ export { d as ChecktivIdv, C as ChecktivIdvProps, e as ChecktivJourney, a as ChecktivJourneyHandle, b as ChecktivJourneyProps, f as ChecktivProvider, c as ChecktivProviderProps, g as ChecktivProviderPropsBase, u as useChecktiv } from './index-Ch6p6GHo.js';
5
6
  import './copy-DCeQ4JKx.js';
6
-
7
- interface ChecktivProviderPropsBase {
8
- getSessionToken: ChecktivInitOptions['getSessionToken'];
9
- theme?: ChecktivInitOptions['theme'];
10
- locale?: string;
11
- children: ReactNode;
12
- }
13
- type ChecktivProviderProps = ChecktivProviderPropsBase & ChecktivInitScope;
14
- declare function ChecktivProvider(props: ChecktivProviderProps): ReactElement | null;
15
- declare function useChecktiv(): ChecktivClient;
16
- interface ChecktivIdvProps {
17
- onEvent: (e: ChecktivIdvEvent) => void;
18
- copy?: MountCopy;
19
- layout?: CaptureLayout;
20
- onBeforeSubmit?: () => Promise<void>;
21
- onBehaviorSegment?: (segment: Pick<CaptureBehaviorMessage, 'steps' | 'traits'>) => void;
22
- }
23
- declare function ChecktivIdv({ onEvent, copy, layout, onBeforeSubmit, onBehaviorSegment, }: ChecktivIdvProps): ReactElement | null;
24
-
25
- export { ChecktivIdv, type ChecktivIdvProps, ChecktivProvider, type ChecktivProviderProps, type ChecktivProviderPropsBase, useChecktiv };
package/dist/react.js CHANGED
@@ -1,14 +1,15 @@
1
- import { init } from './chunk-BEZGAQ74.js';
1
+ import { mount, init } from './chunk-2ZXL5RVX.js';
2
2
  import './chunk-GFHNOZVO.js';
3
3
  import './chunk-UBQTPS5J.js';
4
- import './chunk-QVDWM4KK.js';
4
+ import './chunk-7NIKMJFJ.js';
5
+ import './chunk-XOGHMNBX.js';
5
6
  import './chunk-IQKHRPJR.js';
6
- import './chunk-ITLSDZHK.js';
7
+ import './chunk-ACQKQLL2.js';
7
8
  import './chunk-QFA3K5ZV.js';
8
9
  import './chunk-AIK7CDV6.js';
9
- import { createContext, useMemo, useContext, useRef, useEffect } from 'react';
10
+ import { createContext, forwardRef, useRef, useImperativeHandle, useEffect, useMemo, useContext } from 'react';
10
11
  import { jsx } from 'react/jsx-runtime';
11
12
 
12
- var ChecktivContext=createContext(null);function ChecktivProvider(props){if(typeof window==="undefined")return null;return jsx(ProviderImpl,{...props})}function ProviderImpl(props){const{getSessionToken,theme,locale,children}=props;const scope="publishableKey"in props?{publishableKey:props.publishableKey}:{region:props.region,mode:props.mode};const pk="publishableKey"in props?props.publishableKey:void 0;const region="publishableKey"in props?void 0:props.region;const mode="publishableKey"in props?void 0:props.mode;const themeKey=JSON.stringify(theme??null);const client=useMemo(()=>init({...scope,getSessionToken,theme,locale}),[pk,region,mode,getSessionToken,themeKey,locale]);return jsx(ChecktivContext.Provider,{value:client,children})}function useChecktiv(){const client=useContext(ChecktivContext);if(client===null){throw new Error("useChecktiv must be used within a <ChecktivProvider>. Wrap the component tree that needs the Checktiv client with <ChecktivProvider>.")}return client}function ChecktivIdv({onEvent,copy,layout,onBeforeSubmit,onBehaviorSegment}){const client=useChecktiv();const containerRef=useRef(null);const handleRef=useRef(null);const mountConfigRef=useRef({copy,layout,onBeforeSubmit,onBehaviorSegment});const onEventRef=useRef(onEvent);onEventRef.current=onEvent;useEffect(()=>{if(typeof window==="undefined")return;if(containerRef.current===null)return;if(handleRef.current!==null)return;const mountConfig=mountConfigRef.current;handleRef.current=client.mount("idv",{target:containerRef.current,onEvent:e=>onEventRef.current(e),...mountConfig.copy!==void 0?{copy:mountConfig.copy}:{},...mountConfig.layout!==void 0?{layout:mountConfig.layout}:{},...mountConfig.onBeforeSubmit!==void 0?{onBeforeSubmit:mountConfig.onBeforeSubmit}:{},...mountConfig.onBehaviorSegment!==void 0?{onBehaviorSegment:mountConfig.onBehaviorSegment}:{}});return ()=>{handleRef.current?.destroy();handleRef.current=null;}},[client]);if(typeof window==="undefined")return null;return jsx("div",{ref:containerRef})}
13
+ var ChecktivContext=createContext(null);function ChecktivProvider(props){if(typeof window==="undefined")return null;return jsx(ProviderImpl,{...props})}function ProviderImpl(props){const{getSessionToken,theme,locale,children}=props;const scope="publishableKey"in props?{publishableKey:props.publishableKey}:{region:props.region,mode:props.mode};const pk="publishableKey"in props?props.publishableKey:void 0;const region="publishableKey"in props?void 0:props.region;const mode="publishableKey"in props?void 0:props.mode;const themeKey=JSON.stringify(theme??null);const client=useMemo(()=>init({...scope,getSessionToken,theme,locale}),[pk,region,mode,getSessionToken,themeKey,locale]);return jsx(ChecktivContext.Provider,{value:client,children})}function useChecktiv(){const client=useContext(ChecktivContext);if(client===null){throw new Error("useChecktiv must be used within a <ChecktivProvider>. Wrap the component tree that needs the Checktiv client with <ChecktivProvider>.")}return client}function ChecktivIdv({onEvent,copy,layout,onBeforeSubmit,onBehaviorSegment}){const client=useChecktiv();const containerRef=useRef(null);const handleRef=useRef(null);const mountConfigRef=useRef({copy,layout,onBeforeSubmit,onBehaviorSegment});const onEventRef=useRef(onEvent);onEventRef.current=onEvent;useEffect(()=>{if(typeof window==="undefined")return;if(containerRef.current===null)return;if(handleRef.current!==null)return;const mountConfig=mountConfigRef.current;handleRef.current=client.mount("idv",{target:containerRef.current,onEvent:e=>onEventRef.current(e),...mountConfig.copy!==void 0?{copy:mountConfig.copy}:{},...mountConfig.layout!==void 0?{layout:mountConfig.layout}:{},...mountConfig.onBeforeSubmit!==void 0?{onBeforeSubmit:mountConfig.onBeforeSubmit}:{},...mountConfig.onBehaviorSegment!==void 0?{onBehaviorSegment:mountConfig.onBehaviorSegment}:{}});return ()=>{handleRef.current?.destroy();handleRef.current=null;}},[client]);if(typeof window==="undefined")return null;return jsx("div",{ref:containerRef})}var ChecktivJourneyImpl=(props,ref)=>{const containerRef=useRef(null);const handleRef=useRef(null);const scope="publishableKey"in props?{publishableKey:props.publishableKey}:{region:props.region,mode:props.mode};const pk="publishableKey"in props?props.publishableKey:void 0;const region="publishableKey"in props?void 0:props.region;const mode="publishableKey"in props?void 0:props.mode;const journeyConfig={fetchToken:props.fetchToken,onConsent:props.onConsent,layout:props.layout,theme:props.theme,resumeKey:props.resumeKey,shortCode:props.shortCode,apiBase:props.apiBase,fetchImpl:props.fetchImpl,crossDeviceCopy:props.crossDeviceCopy,onOpenCrossDevice:props.onOpenCrossDevice};const mountConfigRef=useRef(journeyConfig);mountConfigRef.current=journeyConfig;const onEventRef=useRef(props.onEvent);onEventRef.current=props.onEvent;const onCompleteRef=useRef(props.onComplete);onCompleteRef.current=props.onComplete;useImperativeHandle(ref,()=>({requestResend:confirmEmail=>handleRef.current?.requestResend(confirmEmail)??Promise.resolve({requested:false}),openCrossDevice:()=>handleRef.current?.openCrossDevice?.()}),[]);useEffect(()=>{if(typeof window==="undefined")return;if(containerRef.current===null)return;if(handleRef.current!==null)return;const cfg=mountConfigRef.current;const opts={...scope,fetchToken:cfg.fetchToken,onEvent:e=>onEventRef.current?.(e),onComplete:r=>onCompleteRef.current?.(r),...cfg.onConsent!==void 0?{onConsent:cfg.onConsent}:{},...cfg.layout!==void 0?{layout:cfg.layout}:{},...cfg.theme!==void 0?{theme:cfg.theme}:{},...cfg.resumeKey!==void 0?{resumeKey:cfg.resumeKey}:{},...cfg.shortCode!==void 0?{shortCode:cfg.shortCode}:{},...cfg.apiBase!==void 0?{apiBase:cfg.apiBase}:{},...cfg.fetchImpl!==void 0?{fetchImpl:cfg.fetchImpl}:{},...cfg.crossDeviceCopy!==void 0?{crossDeviceCopy:cfg.crossDeviceCopy}:{},...cfg.onOpenCrossDevice!==void 0?{onOpenCrossDevice:cfg.onOpenCrossDevice}:{}};handleRef.current=mount(containerRef.current,opts);return ()=>{handleRef.current?.destroy();handleRef.current=null;}},[pk,region,mode]);if(typeof window==="undefined")return null;return jsx("div",{ref:containerRef})};var ChecktivJourney=forwardRef(ChecktivJourneyImpl);
13
14
 
14
- export { ChecktivIdv, ChecktivProvider, useChecktiv };
15
+ export { ChecktivIdv, ChecktivJourney, ChecktivProvider, useChecktiv };
@@ -1,4 +1,4 @@
1
- import { s as SdkRegion } from './api-DyExBfAQ.js';
1
+ import { s as SdkRegion } from './api-CkHzWaTo.js';
2
2
  import './machine-CuZalPFX.js';
3
3
  import './copy-Dfmk11S_.js';
4
4
  import './copy-DCeQ4JKx.js';
package/dist/workspace.js CHANGED
@@ -1,7 +1,7 @@
1
- import { isCanonicalOrigin, sdkPublicSettings } from './chunk-ITLSDZHK.js';
1
+ import { isCanonicalOrigin, sdkPublicSettings } from './chunk-ACQKQLL2.js';
2
2
  import { getServiceOrigin } from './chunk-QFA3K5ZV.js';
3
3
  import './chunk-AIK7CDV6.js';
4
4
 
5
- function isOriginTargetedPoster(value){return typeof value==="object"&&value!==null&&"postMessage"in value}function initTransport(options,announce){const{win,allowedOrigins,parse,onMessage,accept,reAnnounce}=options;if(!Array.isArray(allowedOrigins)||allowedOrigins.length===0){return {teardown:()=>{}}}const allowlist=new Set(allowedOrigins);const onMessageListener=event=>{if(!allowlist.has(event.origin))return;const parsed=parse(event.data);if(parsed===null)return;if(accept&&!accept(parsed,event.origin))return;const source=event.source;if(!isOriginTargetedPoster(source))return;const post=message=>source.postMessage(message,event.origin);onMessage(parsed,post,event.origin);};win.addEventListener("message",onMessageListener);const broadcast=message=>{for(const origin of allowlist){win.parent.postMessage(message,origin);}};let timer=null;if(reAnnounce!==void 0&&reAnnounce.maxAttempts>0){const scheduler=reAnnounce.schedule??{set:(cb,ms)=>setTimeout(cb,ms),clear:handle=>clearTimeout(handle)};let attemptsLeft=reAnnounce.maxAttempts;const tick=()=>{if(attemptsLeft<=0){timer=null;return}attemptsLeft-=1;broadcast(reAnnounce.message);timer=attemptsLeft>0?scheduler.set(tick,reAnnounce.intervalMs):null;};timer=scheduler.set(tick,reAnnounce.intervalMs);return {teardown:()=>{win.removeEventListener("message",onMessageListener);if(timer!==null){scheduler.clear(timer);timer=null;}}}}return {teardown:()=>{win.removeEventListener("message",onMessageListener);}}}var REVIEWER_COMPONENT="reviewer";var LOADER_PROTOCOL_VERSION=1;var NAVIGATION_EVENTS=new Set(["session_complete","navigate_away"]);function parseWorkspaceInbound(data){if(typeof data!=="object"||data===null)return null;const record=data;const type=record.type;if(type==="wk:ready"){if(record.component!==REVIEWER_COMPONENT)return null;if(typeof record.sessionId!=="string"||record.sessionId.length===0)return null;if(typeof record.nonce!=="string"||record.nonce.length===0)return null;return {type:"wk:ready",component:REVIEWER_COMPONENT,sessionId:record.sessionId,nonce:record.nonce,...typeof record.protocolVersion==="number"?{protocolVersion:record.protocolVersion}:{}}}if(type==="wk:token_refresh"){return {type:"wk:token_refresh",...typeof record.protocolVersion==="number"?{protocolVersion:record.protocolVersion}:{}}}if(type==="wk:event"){if(record.component!==REVIEWER_COMPONENT)return null;const event=record.event;if(typeof event!=="string"||!NAVIGATION_EVENTS.has(event)){return null}return {type:"wk:event",component:REVIEWER_COMPONENT,event,...typeof record.protocolVersion==="number"?{protocolVersion:record.protocolVersion}:{}}}if(type==="wk:error"){const code=record.code;if(code!=="session_expired"&&code!=="protocol_mismatch"&&code!=="origin_rejected"&&code!=="nonce_mismatch"){return null}return {type:"wk:error",code,...typeof record.protocolVersion==="number"?{protocolVersion:record.protocolVersion}:{}}}return null}function makeNonce(){const c=globalThis.crypto;if(c&&typeof c.randomUUID==="function")return c.randomUUID();return `wkn_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`}function buildInitMessage(dataToken,nonce){return {type:"wk:init",component:REVIEWER_COMPONENT,dataToken,nonce,protocolVersion:LOADER_PROTOCOL_VERSION}}function buildErrorMessage(code){return {type:"wk:error",code,protocolVersion:LOADER_PROTOCOL_VERSION}}function wireWorkspaceTransport(options){const{win,workspaceOrigin,currentNonce,handlers}=options;return initTransport({win,allowedOrigins:[workspaceOrigin],parse:parseWorkspaceInbound,accept:message=>{if(message.type!=="wk:ready")return true;const current=currentNonce();if(current===null)return true;return message.nonce===current},onMessage:(message,post,origin)=>{switch(message.type){case "wk:ready":handlers.onReady(message,post,origin);return;case "wk:token_refresh":handlers.onTokenRefresh(post,origin);return;case "wk:event":handlers.onNavigation(message.event,origin);return;case "wk:error":handlers.onError(message,origin);return;default:return assertNeverInbound(message)}}})}function assertNeverInbound(message){throw new Error(`Unhandled workspace inbound message: ${JSON.stringify(message)}`)}function createWorkspaceTokenPair(getToken,sessionId){let cached=null;let pending=null;let liveDataToken=null;const mint=reason=>{const p=getToken({sessionId:sessionId(),reason});void p.then(bundle=>{liveDataToken=bundle.dataToken;},()=>{});return p};const initialBundle=()=>{if(cached!==null)return cached;const p=mint("initial");cached=p;void p.catch(()=>{if(cached===p)cached=null;});return p};return {framingToken(){return initialBundle().then(b=>b.framingToken)},currentDataToken(){return liveDataToken},dataToken(){return initialBundle().then(b=>b.dataToken)},refreshDataToken(){if(pending!==null)return pending.then(b=>b.dataToken);const p=mint("refresh");pending=p;cached=p;void p.then(()=>{if(pending===p)pending=null;},()=>{if(pending===p)pending=null;if(cached===p)cached=null;});return p.then(b=>b.dataToken)}}}function resolveWorkspaceOrigin(region){return getServiceOrigin(sdkPublicSettings.apps["workspace-embed"].subdomain,"prod",region,sdkPublicSettings)}function resolveWorkspaceBaseOrigin(workspaceBaseUrl,region){if(isCanonicalOrigin(workspaceBaseUrl))return workspaceBaseUrl;return resolveWorkspaceOrigin(region)}var REVIEWER_PATH_PREFIX="/reviewer";function buildReviewerSrc(origin,sessionId,framingToken,theme){const path=`${REVIEWER_PATH_PREFIX}/${encodeURIComponent(sessionId)}`;const url=new URL(path,`${origin}/`);url.searchParams.set("ctx",framingToken);if(theme?.primaryColor!==void 0&&theme.primaryColor.length>0){url.searchParams.set("primary",theme.primaryColor);}if(theme?.colorMode!==void 0){url.searchParams.set("mode",theme.colorMode);}if(theme?.logoUrl!==void 0&&theme.logoUrl.length>0){url.searchParams.set("logo",theme.logoUrl);}if(theme?.brandName!==void 0&&theme.brandName.length>0){url.searchParams.set("brand",theme.brandName);}return url.toString()}var IFRAME_SANDBOX="allow-scripts allow-same-origin allow-downloads";function createReviewerController(deps){return {mount(options){return mountReviewer(deps,options)}}}function mountReviewer(deps,options){const doc=deps.doc??deps.win.document;const{sessionId,target,onEvent}=options;const tokens=createWorkspaceTokenPair(deps.getToken,()=>sessionId);const workspaceOrigin=resolveWorkspaceBaseOrigin(deps.workspaceBaseUrl,deps.region);let currentNonce=null;let consecutiveRefreshRejects=0;const container=doc.createElement("div");target.appendChild(container);const iframe=doc.createElement("iframe");iframe.setAttribute("title","Identity review");iframe.setAttribute("sandbox",IFRAME_SANDBOX);iframe.setAttribute("referrerpolicy","no-referrer");container.appendChild(iframe);let destroyed=false;let transport=null;const emit=event=>{if(destroyed)return;try{onEvent?.(event);}catch{}};const deliverInit=(dataToken,post)=>{if(destroyed)return;currentNonce=makeNonce();post(buildInitMessage(dataToken,currentNonce));};transport=wireWorkspaceTransport({win:deps.win,workspaceOrigin,currentNonce:()=>currentNonce,handlers:{onReady:(_message,post)=>{consecutiveRefreshRejects=0;if(currentNonce!==null)return;void tokens.dataToken().then(dataToken=>{if(destroyed)return;deliverInit(dataToken,post);}).catch(()=>{emit({type:"checktiv.workspace.error",code:"session_expired",recoverable:true,recovery:"retry_review"});});},onTokenRefresh:post=>{void tokens.refreshDataToken().then(fresh=>{consecutiveRefreshRejects=0;deliverInit(fresh,post);}).catch(()=>{if(destroyed)return;consecutiveRefreshRejects+=1;if(consecutiveRefreshRejects>=2){post(buildErrorMessage("session_expired"));emit({type:"checktiv.workspace.error",code:"session_expired",recoverable:true,recovery:"retry_review"});}});},onNavigation:intent=>{emit({type:"checktiv.workspace.navigate",intent});},onError:message=>{const recoverable=message.code==="session_expired";emit({type:"checktiv.workspace.error",code:message.code,recoverable,recovery:recoverable?"retry_review":"reload_host"});}}});void tokens.framingToken().then(framingToken=>{if(destroyed)return;iframe.src=buildReviewerSrc(workspaceOrigin,sessionId,framingToken,deps.theme);}).catch(()=>{emit({type:"checktiv.workspace.error",code:"session_expired",recoverable:true,recovery:"retry_review"});});return {destroy(){if(destroyed)return;destroyed=true;transport?.teardown();transport=null;container.remove();}}}function workspace(config){const controller=createReviewerController({getToken:config.getToken,region:config.region,workspaceBaseUrl:config.workspaceBaseUrl,theme:config.theme,win:window});return {mount(_component,options){return controller.mount(options)}}}
5
+ function isOriginTargetedPoster(value){return typeof value==="object"&&value!==null&&"postMessage"in value}function initTransport(options,announce){const{win,allowedOrigins,parse,onMessage,accept,reAnnounce}=options;if(!Array.isArray(allowedOrigins)||allowedOrigins.length===0){return {teardown:()=>{}}}const allowlist=new Set(allowedOrigins);const onMessageListener=event=>{if(!allowlist.has(event.origin))return;const parsed=parse(event.data);if(parsed===null)return;if(accept&&!accept(parsed,event.origin))return;const source=event.source;if(!isOriginTargetedPoster(source))return;const post=message=>source.postMessage(message,event.origin);onMessage(parsed,post,event.origin);};win.addEventListener("message",onMessageListener);const broadcast=message=>{for(const origin of allowlist){win.parent.postMessage(message,origin);}};let timer=null;if(reAnnounce!==void 0&&reAnnounce.maxAttempts>0){const scheduler=reAnnounce.schedule??{set:(cb,ms)=>setTimeout(cb,ms),clear:handle=>clearTimeout(handle)};let attemptsLeft=reAnnounce.maxAttempts;const tick=()=>{if(attemptsLeft<=0){timer=null;return}attemptsLeft-=1;broadcast(reAnnounce.message);timer=attemptsLeft>0?scheduler.set(tick,reAnnounce.intervalMs):null;};timer=scheduler.set(tick,reAnnounce.intervalMs);return {teardown:()=>{win.removeEventListener("message",onMessageListener);if(timer!==null){scheduler.clear(timer);timer=null;}}}}return {teardown:()=>{win.removeEventListener("message",onMessageListener);}}}var REVIEWER_COMPONENT="reviewer";var LOADER_PROTOCOL_VERSION=1;var NAVIGATION_EVENTS=new Set(["session_complete","navigate_away"]);function parseWorkspaceInbound(data){if(typeof data!=="object"||data===null)return null;const record=data;const type=record.type;if(type==="wk:ready"){if(record.component!==REVIEWER_COMPONENT)return null;if(typeof record.sessionId!=="string"||record.sessionId.length===0)return null;if(typeof record.nonce!=="string"||record.nonce.length===0)return null;return {type:"wk:ready",component:REVIEWER_COMPONENT,sessionId:record.sessionId,nonce:record.nonce,...typeof record.protocolVersion==="number"?{protocolVersion:record.protocolVersion}:{}}}if(type==="wk:token_refresh"){return {type:"wk:token_refresh",...typeof record.protocolVersion==="number"?{protocolVersion:record.protocolVersion}:{}}}if(type==="wk:event"){if(record.component!==REVIEWER_COMPONENT)return null;const event=record.event;if(typeof event!=="string"||!NAVIGATION_EVENTS.has(event)){return null}return {type:"wk:event",component:REVIEWER_COMPONENT,event,...typeof record.protocolVersion==="number"?{protocolVersion:record.protocolVersion}:{}}}if(type==="wk:error"){const code=record.code;if(code!=="session_expired"&&code!=="protocol_mismatch"&&code!=="origin_rejected"&&code!=="nonce_mismatch"){return null}return {type:"wk:error",code,...typeof record.protocolVersion==="number"?{protocolVersion:record.protocolVersion}:{}}}return null}function makeNonce(){const c=globalThis.crypto;if(c&&typeof c.randomUUID==="function")return c.randomUUID();return `wkn_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`}function buildInitMessage(dataToken,nonce){return {type:"wk:init",component:REVIEWER_COMPONENT,dataToken,nonce,protocolVersion:LOADER_PROTOCOL_VERSION}}function buildErrorMessage(code){return {type:"wk:error",code,protocolVersion:LOADER_PROTOCOL_VERSION}}function wireWorkspaceTransport(options){const{win,workspaceOrigin,currentNonce,handlers}=options;return initTransport({win,allowedOrigins:[workspaceOrigin],parse:parseWorkspaceInbound,accept:message=>{if(message.type!=="wk:ready")return true;const current=currentNonce();if(current===null)return true;return message.nonce===current},onMessage:(message,post,origin)=>{switch(message.type){case "wk:ready":handlers.onReady(message,post,origin);return;case "wk:token_refresh":handlers.onTokenRefresh(post,origin);return;case "wk:event":handlers.onNavigation(message.event,origin);return;case "wk:error":handlers.onError(message,origin);return;default:return assertNeverInbound(message)}}})}function assertNeverInbound(message){throw new Error(`Unhandled workspace inbound message: ${JSON.stringify(message)}`)}function createWorkspaceTokenPair(getToken,sessionId){let cached=null;let pending=null;let liveDataToken=null;const mint=reason=>{const p=getToken({sessionId:sessionId(),reason});void p.then(bundle=>{liveDataToken=bundle.dataToken;},()=>{});return p};const initialBundle=()=>{if(cached!==null)return cached;const p=mint("initial");cached=p;void p.catch(()=>{if(cached===p)cached=null;});return p};return {framingToken(){return initialBundle().then(b=>b.framingToken)},currentDataToken(){return liveDataToken},dataToken(){return initialBundle().then(b=>b.dataToken)},refreshDataToken(){if(pending!==null)return pending.then(b=>b.dataToken);const p=mint("refresh");pending=p;cached=p;void p.then(()=>{if(pending===p)pending=null;},()=>{if(pending===p)pending=null;if(cached===p)cached=null;});return p.then(b=>b.dataToken)}}}function resolveWorkspaceOrigin(region){return getServiceOrigin(sdkPublicSettings.apps["workspace-embed"].subdomain,"prod",region,sdkPublicSettings)}function resolveWorkspaceBaseOrigin(workspaceBaseUrl,region){if(isCanonicalOrigin(workspaceBaseUrl))return workspaceBaseUrl;return resolveWorkspaceOrigin(region)}var REVIEWER_PATH_PREFIX="/reviewer";function buildReviewerSrc(origin,sessionId,framingToken,theme){const path=`${REVIEWER_PATH_PREFIX}/${encodeURIComponent(sessionId)}`;const url=new URL(path,`${origin}/`);url.searchParams.set("ctx",framingToken);if(theme?.primaryColor!==void 0&&theme.primaryColor.length>0){url.searchParams.set("primary",theme.primaryColor);}if(theme?.colorMode!==void 0){url.searchParams.set("mode",theme.colorMode);}if(theme?.logoUrl!==void 0&&theme.logoUrl.length>0){url.searchParams.set("logo",theme.logoUrl);}if(theme?.brandName!==void 0&&theme.brandName.length>0){url.searchParams.set("brand",theme.brandName);}return url.toString()}var IFRAME_SANDBOX="allow-scripts allow-same-origin allow-downloads";function createReviewerController(deps){return {mount(options){return mountReviewer(deps,options)}}}function mountReviewer(deps,options){const doc=deps.doc??deps.win.document;const{sessionId,target,onEvent}=options;const tokens=createWorkspaceTokenPair(deps.getToken,()=>sessionId);const workspaceOrigin=resolveWorkspaceBaseOrigin(deps.workspaceBaseUrl,deps.region);let currentNonce=null;let consecutiveRefreshRejects=0;const container=doc.createElement("div");container.style.width="100%";container.style.height="100%";target.appendChild(container);const iframe=doc.createElement("iframe");iframe.setAttribute("title","Identity review");iframe.setAttribute("sandbox",IFRAME_SANDBOX);iframe.setAttribute("referrerpolicy","no-referrer");Object.assign(iframe.style,{width:"100%",height:"100%",border:"0",display:"block"});container.appendChild(iframe);let destroyed=false;let transport=null;const emit=event=>{if(destroyed)return;try{onEvent?.(event);}catch{}};const deliverInit=(dataToken,post)=>{if(destroyed)return;currentNonce=makeNonce();post(buildInitMessage(dataToken,currentNonce));};transport=wireWorkspaceTransport({win:deps.win,workspaceOrigin,currentNonce:()=>currentNonce,handlers:{onReady:(_message,post)=>{consecutiveRefreshRejects=0;if(currentNonce!==null)return;void tokens.dataToken().then(dataToken=>{if(destroyed)return;deliverInit(dataToken,post);}).catch(()=>{emit({type:"checktiv.workspace.error",code:"session_expired",recoverable:true,recovery:"retry_review"});});},onTokenRefresh:post=>{void tokens.refreshDataToken().then(fresh=>{consecutiveRefreshRejects=0;deliverInit(fresh,post);}).catch(()=>{if(destroyed)return;consecutiveRefreshRejects+=1;if(consecutiveRefreshRejects>=2){post(buildErrorMessage("session_expired"));emit({type:"checktiv.workspace.error",code:"session_expired",recoverable:true,recovery:"retry_review"});}});},onNavigation:intent=>{emit({type:"checktiv.workspace.navigate",intent});},onError:message=>{const recoverable=message.code==="session_expired";emit({type:"checktiv.workspace.error",code:message.code,recoverable,recovery:recoverable?"retry_review":"reload_host"});}}});void tokens.framingToken().then(framingToken=>{if(destroyed)return;iframe.src=buildReviewerSrc(workspaceOrigin,sessionId,framingToken,deps.theme);}).catch(()=>{emit({type:"checktiv.workspace.error",code:"session_expired",recoverable:true,recovery:"retry_review"});});return {destroy(){if(destroyed)return;destroyed=true;transport?.teardown();transport=null;container.remove();}}}function workspace(config){const controller=createReviewerController({getToken:config.getToken,region:config.region,workspaceBaseUrl:config.workspaceBaseUrl,theme:config.theme,win:window});return {mount(_component,options){return controller.mount(options)}}}
6
6
 
7
7
  export { workspace };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checktiv/sdk-web",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "private": false,
5
5
  "license": "Apache-2.0",
6
6
  "//publishConfigNameNote": "pnpm does NOT apply `publishConfig.name` (the publishConfig override list at https://pnpm.io/package_json#publishconfig excludes `name`; `pnpm pack` leaves the tarball `name` as the workspace name). The published `name` rewrite to `@checktiv/sdk-web` happens in the root `.pnpmfile.cjs` `beforePacking` hook (pnpm >= 10.28) instead — see that file's header. This field is retained ONLY as documentation of intent; the hook deletes it from the packed manifest. The packed-name gate in __tests__/dist-shape.test.ts reads the REAL extracted tarball, not this field.",
@@ -60,10 +60,11 @@
60
60
  "./package.json": "./package.json"
61
61
  },
62
62
  "//challengeExportNote": "The `./challenge` subpath (top-level `exports` only, ABSENT from `publishConfig.exports`) is the generic parent-side challenge relay kernel consumed by `apps/verify` (ChallengeIframe). It stays OFF the npm-publish boundary (no schema-mirror / dist-shape / bundle-budget / CDN build), like `./agent`. tsup emits `dist/challenge.js` from the entry map for the in-repo `import` condition.",
63
- "//sideEffectsNote": "The module-load seam (src/substrate/module-registry.ts) makes the `./idv`, `./fraud`, and `./custom-form` entries SIDE-EFFECTFUL: each calls registerModule(...) at import-eval time so the substrate's mount('idv'|'fraud'|'custom_form')/mountProvisioned can resolve it. `./idv/cross-device` is ALSO side-effectful: it calls registerCrossDeviceOpener(...) (src/idv/cross-device-registry.ts) so createIdv().openCrossDevice() (S13/B2) resolves the overlay WITHOUT idv/index.ts importing the heavy orchestrator (keeps the CDN sdk.js flat). A blanket `sideEffects: false` would let a consumer's bundler tree-shake those registrations away (breaking the seams), so this lists ONLY the side-effectful entry files (src + published dist forms). Everything else still tree-shakes, so the `.` and `./react` entries stay Zod-free and per-subpath tree-shaking holds. When plan-C rewrites this manifest, KEEP this array (do NOT replace it with `false`).",
63
+ "//sideEffectsNote": "The module-load seam (src/substrate/module-registry.ts) makes the `./idv`, `./fraud`, and `./custom-form` entries SIDE-EFFECTFUL: each calls registerModule(...) at import-eval time so the substrate's mount('idv'|'fraud'|'custom_form')/mountProvisioned can resolve it. `./idv/cross-device` is ALSO side-effectful: it calls registerCrossDeviceOpener(...) (src/idv/cross-device-registry.ts) so createIdv().openCrossDevice() (S13/B2) resolves the overlay WITHOUT idv/index.ts importing the heavy orchestrator (keeps the CDN sdk.js flat). `src/idv/cross-device-cdn.ts` is the CDN-ONLY counterpart (imported for its side effect by src/cdn.ts, NOT a published subpath so it has no dist form): it registers a wrapper opener that injects a runtime chunk-URL importer so the CDN IIFE (window.Checktiv.mount) resolves the overlay by loading the pre-built /v1/cross-device.js chunk by URL instead of inlining the QR panel into sdk.js. Its registration side effect MUST be listed here or esbuild tree-shakes the side-effect-only `import './idv/cross-device-cdn.js'` in src/cdn.ts, leaving getCrossDeviceOpener() null in the IIFE. A blanket `sideEffects: false` would let a consumer's bundler tree-shake those registrations away (breaking the seams), so this lists ONLY the side-effectful entry files (src + published dist forms). Everything else still tree-shakes, so the `.` and `./react` entries stay Zod-free and per-subpath tree-shaking holds. When plan-C rewrites this manifest, KEEP this array (do NOT replace it with `false`).",
64
64
  "sideEffects": [
65
65
  "./src/idv/index.ts",
66
66
  "./src/idv/cross-device.ts",
67
+ "./src/idv/cross-device-cdn.ts",
67
68
  "./src/fraud/index.ts",
68
69
  "./src/custom-form/index.ts",
69
70
  "./src/cdn.ts",
@@ -112,10 +113,10 @@
112
113
  "tsup": "^8.5.0",
113
114
  "typescript": "5.9.3",
114
115
  "vitest": "^4.1.9",
116
+ "@rf/config": "0.0.0",
115
117
  "@rf/iam-core": "0.0.1",
116
118
  "@rf/schemas": "0.1.0",
117
- "@rf/theme": "0.1.0",
118
- "@rf/config": "0.0.0"
119
+ "@rf/theme": "0.1.0"
119
120
  },
120
121
  "scripts": {
121
122
  "build": "tsup && node scripts/generate-agent-steering.mjs && pnpm run build:embed-capture && pnpm run build:embed-challenge && pnpm run build:host-capture && pnpm run build:cross-device",
@@ -1,8 +0,0 @@
1
- import { getModule } from './chunk-GFHNOZVO.js';
2
- import { assertNever } from './chunk-UBQTPS5J.js';
3
- import { createTokenManager, resolveSession, warnWrongTokenPlane, createChecktivError } from './chunk-QVDWM4KK.js';
4
- import { resolveApiBaseOrigin } from './chunk-ITLSDZHK.js';
5
-
6
- var PUBLISHABLE_KEY_REGEX=/^ah_pk_(us|eu)_(live|test)_[A-Za-z0-9_-]{8,}$/;var InvalidPublishableKeyError=class extends Error{constructor(message){super(message);this.name="InvalidPublishableKeyError";}};function parsePublishableKey(key){const match=PUBLISHABLE_KEY_REGEX.exec(key);if(!match){throw new InvalidPublishableKeyError(`Invalid publishable key format. Expected ah_pk_<us|eu>_<live|test>_<token>, got: ${maskKey(key)}`)}const region=match[1];const mode=match[2];if(region!=="us"&&region!=="eu"){throw new InvalidPublishableKeyError(`Invalid region in publishable key: ${maskKey(key)}`)}if(mode!=="test"&&mode!=="live"){throw new InvalidPublishableKeyError(`Invalid mode in publishable key: ${maskKey(key)}`)}return {region,mode}}function maskKey(key){if(key.length<=8)return "***";return `${key.slice(0,6)}...${key.slice(-2)}`}function moduleErrorEvent(name,error){switch(name){case "idv":return {type:"checktiv.idv.error",error};case "fraud":return {type:"checktiv.fraud.error",error};case "custom_form":return {type:"checktiv.custom_form.error",error}}}function missingModuleMessage(name){return `This session requires the '${name}' module. Import '@checktiv/sdk-web/${name}' in your app.`}function missingModuleError(name){const base=createChecktivError("sdk_load_failed");return moduleErrorEvent(name,{...base,message:missingModuleMessage(name)})}function consentGateRequiredError(){const base=createChecktivError("sdk_load_failed");return moduleErrorEvent("fraud",{...base,message:"This session includes the 'fraud' module, which needs a consent gate. Pass onConsent to mountProvisioned() to enable it."})}function customFormConfigRequiredError(){const base=createChecktivError("sdk_load_failed");return moduleErrorEvent("custom_form",{...base,message:"This session includes the 'custom_form' module, which needs its form config. Pass customFormConfig to mountProvisioned() to enable it."})}function unmappedStepTypeError(stepType){const base=createChecktivError("sdk_load_failed");return moduleErrorEvent("idv",{...base,message:`This session's current step ('${stepType}') has no matching visual module in this SDK build. Upgrade '@checktiv/sdk-web' to a version that renders this step.`})}var MODULE_IS_VISUAL={idv:true,fraud:false,custom_form:true};var STEP_TYPE_TO_VISUAL_MODULE={id_verification:"idv",custom_form:"custom_form"};var stepTypeToVisualModuleView=STEP_TYPE_TO_VISUAL_MODULE;function resolveCurrentStepVisualModule(stepType){return stepTypeToVisualModuleView[stepType]}function defaultLoadModule(name){if(name==="idv"){const factory2=getModule("idv");if(factory2===void 0)return Promise.resolve(null);return Promise.resolve({mount:(ctx,opts)=>factory2(ctx,{target:opts.target,onEvent:opts.onEvent,copy:opts.copy,layout:opts.layout,onBeforeSubmit:opts.onBeforeSubmit,onBehaviorSegment:opts.onBehaviorSegment,synthetic:opts.synthetic})})}if(name==="custom_form"){const factory2=getModule("custom_form");if(factory2===void 0)return Promise.resolve(null);return Promise.resolve({mount:(ctx,opts)=>{if(opts.customFormConfig===void 0){opts.onEvent?.(customFormConfigRequiredError());return {destroy:()=>void 0}}return factory2(ctx,{target:opts.target,config:opts.customFormConfig,copy:opts.customFormCopy,onEvent:opts.onEvent,session:opts.session})}})}const factory=getModule("fraud");if(factory===void 0)return Promise.resolve(null);return Promise.resolve({mount:(ctx,opts)=>{const onConsent=opts.onConsent;if(onConsent===void 0){opts.onEvent?.(consentGateRequiredError());return {destroy:()=>void 0}}return factory(ctx,{onConsent,onEvent:opts.onEvent})}})}function moduleErrorFor(name,code){return moduleErrorEvent(name,createChecktivError(code))}function createMountProvisioned(ctx,opts,deps={}){const emit=e=>opts.onEvent?.(e);const fetchImpl=opts.fetchImpl??fetch;const resolveSession2=deps.resolveSession??(()=>resolveSession(ctx,fetchImpl));const loadModule=deps.loadModule??defaultLoadModule;const handles=[];let destroyed=false;void(async()=>{try{await composeModules();}catch(err){if(destroyed)return;console.warn("[Checktiv] mountProvisioned composition failed.",err);emit(moduleErrorFor("idv","sdk_load_failed"));}})();async function composeModules(){const resolution=await resolveSession2();if(destroyed)return;let session;switch(resolution.kind){case "ok":session=resolution.session;break;case "wrong_token":warnWrongTokenPlane();emit(moduleErrorFor("idv","wrong_token_type"));return;case "unresolved":emit(moduleErrorFor("idv","session_expired"));return;default:return assertNever(resolution)}const currentStepVisualModule=resolveCurrentStepVisualModule(session.stepType);if(currentStepVisualModule===void 0&&session.modules.some(m=>MODULE_IS_VISUAL[m])){emit(unmappedStepTypeError(session.stepType));return}for(const name of session.modules){if(destroyed)return;if(MODULE_IS_VISUAL[name]&&name!==currentStepVisualModule){continue}if(name==="fraud"&&opts.onConsent===void 0){emit(consentGateRequiredError());continue}const mod=await loadModule(name);if(destroyed)return;if(mod===null){emit(missingModuleError(name));continue}let moduleTarget;let containerToRemove=null;if(MODULE_IS_VISUAL[name]){const resolvedParent=typeof opts.target==="string"?typeof document!=="undefined"?document.querySelector(opts.target):null:opts.target;if(resolvedParent!==null){const container=document.createElement("div");resolvedParent.appendChild(container);containerToRemove=container;moduleTarget=container;}else {moduleTarget=opts.target;}}else {moduleTarget=opts.target;}let innerHandle;try{innerHandle=mod.mount(ctx,{target:moduleTarget,onEvent:opts.onEvent,onConsent:opts.onConsent,copy:opts.copy,layout:opts.layout,onBeforeSubmit:opts.onBeforeSubmit,onBehaviorSegment:opts.onBehaviorSegment,synthetic:opts.synthetic,customFormConfig:opts.customFormConfig,customFormCopy:opts.customFormCopy,session:{sessionId:session.sessionId,stepId:session.stepId}});}catch(err){containerToRemove?.remove();throw err}const capturedContainer=containerToRemove;const handle={destroy(){try{innerHandle.destroy();}finally{capturedContainer?.remove();}}};if(destroyed){handle.destroy();return}handles.push(handle);}}return {destroy(){destroyed=true;while(handles.length>0){const handle=handles.pop();handle?.destroy();}}}}function init(opts,deps={}){const scope="publishableKey"in opts?{...parsePublishableKey(opts.publishableKey),publishableKey:opts.publishableKey}:{region:opts.region,mode:opts.mode,publishableKey:void 0};let sessionId="";const apiBaseOrigin=resolveApiBaseOrigin(opts.apiBase,scope.region,scope.mode);const ctx={region:scope.region,mode:scope.mode,publishableKey:scope.publishableKey,apiBaseOrigin,theme:opts.theme,locale:opts.locale??"en",betas:opts.betas??[],...opts.selfiePromptMap!==void 0?{selfiePromptMap:opts.selfiePromptMap}:{},...opts.selfieChromeCopy!==void 0?{selfieChromeCopy:opts.selfieChromeCopy}:{},tokens:deps.tokens??createTokenManager(opts.getSessionToken,()=>sessionId),getSessionId:()=>sessionId,setSessionId:id=>{sessionId=id;}};function mount(...args){if(args[0]==="idv"){const factory2=getModule("idv");if(factory2===void 0)return missingIdvHandle(args[1]);return factory2(ctx,args[1])}if(args[0]==="custom_form"){const factory2=getModule("custom_form");if(factory2===void 0)return missingCustomFormHandle(args[1]);return factory2(ctx,args[1])}const factory=getModule("fraud");if(factory===void 0)return missingFraudHandle(args[1]);return factory(ctx,args[1])}return {mountProvisioned:provOpts=>createMountProvisioned(ctx,provOpts),mount}}function missingIdvHandle(opts){const base=createChecktivError("sdk_load_failed");opts.onEvent?.({type:"checktiv.idv.error",error:{...base,message:missingModuleMessage("idv")}});return {destroy:()=>void 0,unmount:()=>void 0}}function missingFraudHandle(opts){const base=createChecktivError("sdk_load_failed");opts.onEvent?.({type:"checktiv.fraud.error",error:{...base,message:missingModuleMessage("fraud")}});return {destroy:()=>void 0}}function missingCustomFormHandle(opts){const base=createChecktivError("sdk_load_failed");const error={type:"checktiv.custom_form.error",error:{...base,message:missingModuleMessage("custom_form")}};opts.onEvent?.(error);return {destroy:()=>void 0}}
7
-
8
- export { InvalidPublishableKeyError, init, parsePublishableKey };
@@ -1,5 +0,0 @@
1
- import { getServiceOrigin } from './chunk-QFA3K5ZV.js';
2
-
3
- var sdkPublicSettings={"project":{"marketingApex":"checktiv.com"},"apps":{"verify":{"subdomain":"verify"},"workspace-embed":{"subdomain":"workspace"}},"services":{"sdk-api":{"subdomain":"sdk-api"}},"regions":{"us":{"apex":"us.checktiv.com"},"eu":{"apex":"eu.checktiv.com"}}};var PUBLISHABLE_KEY_HEADER="X-Publishable-Key";function dataPlaneHeaders(ctx,token,opts={}){const headers={Authorization:`Bearer ${token}`};if(ctx.publishableKey!==void 0){headers[PUBLISHABLE_KEY_HEADER]=ctx.publishableKey;}if(opts.withJsonBody)headers["content-type"]="application/json";return headers}function resolveSdkApiOrigin(region,_mode,_hostname){return getServiceOrigin(sdkPublicSettings.services["sdk-api"].subdomain,"prod",region,sdkPublicSettings)}function isCanonicalOrigin(value){if(typeof value!=="string"||value.length===0)return false;try{return new URL(value).origin===value}catch{return false}}function resolveApiBaseOrigin(apiBase,region,mode,hostname){if(isCanonicalOrigin(apiBase))return apiBase;return resolveSdkApiOrigin(region)}var SESSIONS_ME_PATH="/sdk/v1/sessions/me";var WORKING_TOKEN_MINT_PATH="/sdk/v1/working-token";var CAPTURE_TOKEN_MINT_PATH="/sdk/v1/idv/capture-token";function stepSubmitPath(_sessionId,stepId){return `${SESSIONS_ME_PATH}/steps/${encodeURIComponent(stepId)}/submit`}var TELEMETRY_PATH="/sdk/v1/telemetry";var SESSIONS_ME_REFRESH_LINK_PATH="/sdk/v1/sessions/me/refresh-link";function shortCodeRefreshPath(code){return `/sdk/v1/short-codes/${encodeURIComponent(code)}/refresh`}function formUploadUrlPath(stepId){return `${SESSIONS_ME_PATH}/steps/${encodeURIComponent(stepId)}/form-upload-url`}
4
-
5
- export { CAPTURE_TOKEN_MINT_PATH, PUBLISHABLE_KEY_HEADER, SESSIONS_ME_PATH, SESSIONS_ME_REFRESH_LINK_PATH, TELEMETRY_PATH, WORKING_TOKEN_MINT_PATH, dataPlaneHeaders, formUploadUrlPath, isCanonicalOrigin, resolveApiBaseOrigin, sdkPublicSettings, shortCodeRefreshPath, stepSubmitPath };