@creditchektechsupport/react-sdk 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,456 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var src_exports = {};
23
+ __export(src_exports, {
24
+ CreditChekButton: () => CreditChekButton,
25
+ CreditChekError: () => CreditChekError,
26
+ WIDGET_URL: () => WIDGET_URL,
27
+ buildWidgetUrl: () => buildWidgetUrl,
28
+ openCreditChekWidget: () => openCreditChekWidget,
29
+ useCreditChek: () => useCreditChek
30
+ });
31
+ module.exports = __toCommonJS(src_exports);
32
+
33
+ // src/useCreditChek.ts
34
+ var import_react = require("react");
35
+
36
+ // src/constants.ts
37
+ var WIDGET_URL = "https://securedwidget.creditchek.africa/";
38
+ var ENVIRONMENTS = ["production", "development"];
39
+ var LOCAL_WIDGET_URL = "";
40
+ var WIDGET_SOURCE = "react";
41
+
42
+ // src/errors.ts
43
+ var CreditChekError = class extends Error {
44
+ constructor(code, message, options) {
45
+ super(message);
46
+ this.name = "CreditChekError";
47
+ this.code = code;
48
+ if (options && "cause" in options) {
49
+ this.cause = options.cause;
50
+ }
51
+ }
52
+ };
53
+
54
+ // src/buildWidgetUrl.ts
55
+ var PREFILL_KEYS = ["firstName", "lastName", "dob", "bvn", "nin", "email"];
56
+ function resolveWidgetUrl(config) {
57
+ return config.widgetUrl || LOCAL_WIDGET_URL || WIDGET_URL;
58
+ }
59
+ function assertWidgetConfig(config) {
60
+ if (!config.publicKey || typeof config.publicKey !== "string") {
61
+ throw new CreditChekError("invalid_config", "`publicKey` is required.");
62
+ }
63
+ if (config.modules && config.modules.length === 0) {
64
+ throw new CreditChekError("invalid_config", "`modules` must list at least one step, or be left out.");
65
+ }
66
+ if (config.environment !== void 0 && !ENVIRONMENTS.includes(config.environment)) {
67
+ throw new CreditChekError("invalid_config", '`environment` must be "production" or "development".');
68
+ }
69
+ try {
70
+ new URL(resolveWidgetUrl(config));
71
+ } catch (cause) {
72
+ throw new CreditChekError("invalid_config", "`widgetUrl` is not a valid URL.", { cause });
73
+ }
74
+ }
75
+ function buildWidgetUrl(config, sessionId) {
76
+ var _a, _b, _c;
77
+ assertWidgetConfig(config);
78
+ if (!sessionId) {
79
+ throw new CreditChekError("invalid_config", "`sessionId` is required.");
80
+ }
81
+ const url = new URL(resolveWidgetUrl(config));
82
+ const params = url.searchParams;
83
+ params.set("publicKey", config.publicKey);
84
+ params.set("sessionId", sessionId);
85
+ params.set("module", ((_a = config.modules) != null ? _a : ["identity"]).join(","));
86
+ params.set("source", WIDGET_SOURCE);
87
+ if (config.environment) params.set("environment", config.environment);
88
+ const hostOrigin = typeof window !== "undefined" ? window.location.origin : "";
89
+ if (hostOrigin && hostOrigin !== "null") params.set("hostOrigin", hostOrigin);
90
+ if (config.themeColor) params.set("themeColor", config.themeColor.replace(/^#/, ""));
91
+ for (const key of PREFILL_KEYS) {
92
+ const value = (_c = (_b = config.prefill) == null ? void 0 : _b[key]) == null ? void 0 : _c.trim();
93
+ if (value) params.set(key, value);
94
+ }
95
+ return url.toString();
96
+ }
97
+
98
+ // src/modal.ts
99
+ var STYLE_ID = "creditchek-modal-styles";
100
+ var TRANSITION_MS = 280;
101
+ var CSS = `
102
+ .creditchek-modal{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:24px}
103
+ .creditchek-modal__backdrop{position:absolute;inset:0;background:rgba(16,24,40,.6);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition:opacity ${TRANSITION_MS}ms ease}
104
+ .creditchek-modal__panel{position:relative;width:min(460px,100%);height:min(780px,100%);background:#F9FAFB;border-radius:16px;overflow:hidden;box-shadow:0 24px 48px -12px rgba(16,24,40,.35);opacity:0;transform:translateY(24px) scale(.98);transition:opacity ${TRANSITION_MS}ms ease,transform ${TRANSITION_MS}ms cubic-bezier(.2,.8,.2,1)}
105
+ .creditchek-modal--open .creditchek-modal__backdrop{opacity:1}
106
+ .creditchek-modal--open .creditchek-modal__panel{opacity:1;transform:none}
107
+ .creditchek-modal__frame{position:absolute;inset:0;width:100%;height:100%;border:0;opacity:0;transition:opacity 200ms ease}
108
+ .creditchek-modal--ready .creditchek-modal__frame{opacity:1}
109
+ .creditchek-modal__loader{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;transition:opacity 200ms ease}
110
+ .creditchek-modal--ready .creditchek-modal__loader{opacity:0;pointer-events:none}
111
+ .creditchek-modal__spinner{width:36px;height:36px;border-radius:50%;border:3px solid rgba(0,70,230,.15);border-top-color:var(--creditchek-accent,#0046E6);animation:creditchek-spin .9s linear infinite}
112
+ .creditchek-modal__close{position:absolute;top:10px;right:10px;z-index:1;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border:0;border-radius:999px;background:rgba(255,255,255,.9);color:#344054;cursor:pointer;box-shadow:0 1px 2px rgba(16,24,40,.12)}
113
+ .creditchek-modal__close:hover{background:#fff}
114
+ .creditchek-modal__close:focus-visible{outline:2px solid var(--creditchek-accent,#0046E6);outline-offset:2px}
115
+ @keyframes creditchek-spin{to{transform:rotate(360deg)}}
116
+ @media (max-width:560px){
117
+ .creditchek-modal{padding:0}
118
+ .creditchek-modal__panel{width:100%;height:100%;border-radius:0;transform:translateY(100%)}
119
+ }
120
+ @media (prefers-reduced-motion:reduce){
121
+ .creditchek-modal__backdrop,.creditchek-modal__panel,.creditchek-modal__frame,.creditchek-modal__loader{transition:none}
122
+ .creditchek-modal__panel{transform:none}
123
+ .creditchek-modal__spinner{animation-duration:2.4s}
124
+ }
125
+ `;
126
+ var CLOSE_ICON = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>';
127
+ function injectStyles() {
128
+ if (document.getElementById(STYLE_ID)) return;
129
+ const style = document.createElement("style");
130
+ style.id = STYLE_ID;
131
+ style.textContent = CSS;
132
+ document.head.appendChild(style);
133
+ }
134
+ function createModal({ accentColor: accentColor2, onDismiss }) {
135
+ injectStyles();
136
+ const previousFocus = document.activeElement;
137
+ const previousOverflow = document.body.style.overflow;
138
+ document.body.style.overflow = "hidden";
139
+ const root = document.createElement("div");
140
+ root.className = "creditchek-modal";
141
+ root.setAttribute("role", "dialog");
142
+ root.setAttribute("aria-modal", "true");
143
+ root.setAttribute("aria-label", "CreditChek verification");
144
+ if (accentColor2) root.style.setProperty("--creditchek-accent", accentColor2);
145
+ const backdrop = document.createElement("div");
146
+ backdrop.className = "creditchek-modal__backdrop";
147
+ const panel = document.createElement("div");
148
+ panel.className = "creditchek-modal__panel";
149
+ const closeButton = document.createElement("button");
150
+ closeButton.type = "button";
151
+ closeButton.className = "creditchek-modal__close";
152
+ closeButton.setAttribute("aria-label", "Close verification");
153
+ closeButton.innerHTML = CLOSE_ICON;
154
+ const loader = document.createElement("div");
155
+ loader.className = "creditchek-modal__loader";
156
+ loader.setAttribute("role", "status");
157
+ loader.setAttribute("aria-label", "Loading");
158
+ loader.innerHTML = '<span class="creditchek-modal__spinner"></span>';
159
+ const frame = document.createElement("iframe");
160
+ frame.className = "creditchek-modal__frame";
161
+ frame.title = "CreditChek verification";
162
+ frame.setAttribute("allow", "camera; microphone; fullscreen");
163
+ const sentinel = () => {
164
+ const el = document.createElement("span");
165
+ el.tabIndex = 0;
166
+ return el;
167
+ };
168
+ const startSentinel = sentinel();
169
+ const endSentinel = sentinel();
170
+ startSentinel.addEventListener("focus", () => frame.focus());
171
+ endSentinel.addEventListener("focus", () => closeButton.focus());
172
+ panel.append(startSentinel, closeButton, loader, frame, endSentinel);
173
+ root.append(backdrop, panel);
174
+ document.body.appendChild(root);
175
+ let destroyed = false;
176
+ let revealed = false;
177
+ const onKeyDown = (event) => {
178
+ if (event.key === "Escape") {
179
+ event.preventDefault();
180
+ onDismiss();
181
+ }
182
+ };
183
+ document.addEventListener("keydown", onKeyDown);
184
+ closeButton.addEventListener("click", () => onDismiss());
185
+ void root.offsetWidth;
186
+ root.classList.add("creditchek-modal--open");
187
+ closeButton.focus();
188
+ const reveal = () => {
189
+ if (revealed || destroyed) return;
190
+ revealed = true;
191
+ root.classList.add("creditchek-modal--ready");
192
+ frame.focus();
193
+ };
194
+ return {
195
+ frame,
196
+ navigate(url) {
197
+ if (destroyed) return;
198
+ frame.addEventListener("load", reveal, { once: true });
199
+ frame.src = url;
200
+ },
201
+ reveal,
202
+ focus() {
203
+ if (!destroyed) (revealed ? frame : closeButton).focus();
204
+ },
205
+ destroy() {
206
+ if (destroyed) return;
207
+ destroyed = true;
208
+ document.removeEventListener("keydown", onKeyDown);
209
+ root.classList.remove("creditchek-modal--open");
210
+ const remove = () => {
211
+ root.remove();
212
+ document.body.style.overflow = previousOverflow;
213
+ if (previousFocus && document.contains(previousFocus)) previousFocus.focus();
214
+ };
215
+ const reducedMotion = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
216
+ if (reducedMotion) remove();
217
+ else setTimeout(remove, TRANSITION_MS);
218
+ }
219
+ };
220
+ }
221
+
222
+ // src/openWidget.ts
223
+ var MODULES = ["identity", "liveness"];
224
+ function parseStepEvent(data) {
225
+ if (!data || typeof data !== "object") return null;
226
+ const { module: module2, status } = data;
227
+ if (!MODULES.includes(module2)) return null;
228
+ if (status !== "successful" && status !== "failed") return null;
229
+ return { module: module2, status };
230
+ }
231
+ function messageType(data) {
232
+ return data && typeof data === "object" ? data.type : void 0;
233
+ }
234
+ function accentColor(themeColor) {
235
+ const hex = themeColor == null ? void 0 : themeColor.replace(/^#/, "");
236
+ return hex && /^[0-9a-f]{3,8}$/i.test(hex) ? `#${hex}` : void 0;
237
+ }
238
+ function openCreditChekWidget(options) {
239
+ const { sessionId: sessionSource, onStep, onClose, onError } = options;
240
+ const reportError = onError != null ? onError : ((error) => console.error(error));
241
+ let modal = null;
242
+ let sessionId;
243
+ let widgetClosed = false;
244
+ let settled = false;
245
+ const steps = [];
246
+ const settle = (callback) => {
247
+ if (settled) return;
248
+ settled = true;
249
+ window.removeEventListener("message", handleMessage);
250
+ callback();
251
+ };
252
+ const fail = (error) => {
253
+ settle(() => {
254
+ modal == null ? void 0 : modal.destroy();
255
+ reportError(error);
256
+ });
257
+ };
258
+ const completeIfClosed = () => {
259
+ if (!widgetClosed || sessionId === void 0) return;
260
+ const result = { sessionId, steps: [...steps] };
261
+ settle(() => onClose == null ? void 0 : onClose(result));
262
+ };
263
+ const closeWidget = () => {
264
+ if (widgetClosed) return;
265
+ widgetClosed = true;
266
+ modal == null ? void 0 : modal.destroy();
267
+ completeIfClosed();
268
+ };
269
+ function handleMessage(event) {
270
+ if (settled || !modal || event.source !== modal.frame.contentWindow) return;
271
+ if (event.origin !== widgetOrigin) return;
272
+ const type = messageType(event.data);
273
+ if (type === "ready") {
274
+ modal.reveal();
275
+ return;
276
+ }
277
+ if (type === "close") {
278
+ closeWidget();
279
+ return;
280
+ }
281
+ const step = parseStepEvent(event.data);
282
+ if (step) {
283
+ steps.push(step);
284
+ onStep == null ? void 0 : onStep(step);
285
+ }
286
+ }
287
+ const handle = {
288
+ close() {
289
+ if (!settled) closeWidget();
290
+ },
291
+ focus() {
292
+ if (!settled && !widgetClosed) modal == null ? void 0 : modal.focus();
293
+ },
294
+ detach() {
295
+ if (settled) return;
296
+ settled = true;
297
+ window.removeEventListener("message", handleMessage);
298
+ modal == null ? void 0 : modal.destroy();
299
+ },
300
+ get isOpen() {
301
+ return !settled && !widgetClosed && !!modal;
302
+ }
303
+ };
304
+ let widgetOrigin;
305
+ let initialUrl;
306
+ try {
307
+ assertWidgetConfig(options);
308
+ widgetOrigin = new URL(resolveWidgetUrl(options)).origin;
309
+ if (typeof sessionSource === "string") {
310
+ sessionId = sessionSource;
311
+ initialUrl = buildWidgetUrl(options, sessionSource);
312
+ }
313
+ } catch (error) {
314
+ fail(error instanceof CreditChekError ? error : new CreditChekError("invalid_config", String(error)));
315
+ return handle;
316
+ }
317
+ modal = createModal({ accentColor: accentColor(options.themeColor), onDismiss: closeWidget });
318
+ window.addEventListener("message", handleMessage);
319
+ if (typeof sessionSource === "string") {
320
+ modal.navigate(initialUrl);
321
+ return handle;
322
+ }
323
+ let pendingSession;
324
+ try {
325
+ pendingSession = Promise.resolve(sessionSource());
326
+ } catch (cause) {
327
+ pendingSession = Promise.reject(cause);
328
+ }
329
+ pendingSession.then(
330
+ (id) => {
331
+ if (settled) return;
332
+ if (!id || typeof id !== "string") {
333
+ fail(new CreditChekError("session_failed", "The `sessionId` function did not return a session ID."));
334
+ return;
335
+ }
336
+ sessionId = id;
337
+ if (widgetClosed) {
338
+ completeIfClosed();
339
+ return;
340
+ }
341
+ modal.navigate(buildWidgetUrl(options, id));
342
+ },
343
+ (cause) => {
344
+ fail(new CreditChekError("session_failed", "Could not create a CreditChek session.", { cause }));
345
+ }
346
+ );
347
+ return handle;
348
+ }
349
+
350
+ // src/useCreditChek.ts
351
+ function useCreditChek(options) {
352
+ const optionsRef = (0, import_react.useRef)(options);
353
+ const handleRef = (0, import_react.useRef)(null);
354
+ const [isOpen, setIsOpen] = (0, import_react.useState)(false);
355
+ const [steps, setSteps] = (0, import_react.useState)([]);
356
+ const [error, setError] = (0, import_react.useState)(null);
357
+ (0, import_react.useEffect)(() => {
358
+ optionsRef.current = options;
359
+ });
360
+ (0, import_react.useEffect)(() => () => {
361
+ var _a;
362
+ return (_a = handleRef.current) == null ? void 0 : _a.detach();
363
+ }, []);
364
+ const open = (0, import_react.useCallback)((sessionId) => {
365
+ const current = handleRef.current;
366
+ if (current == null ? void 0 : current.isOpen) {
367
+ current.focus();
368
+ return;
369
+ }
370
+ setSteps([]);
371
+ setError(null);
372
+ setIsOpen(true);
373
+ const handle = openCreditChekWidget({
374
+ ...optionsRef.current,
375
+ sessionId,
376
+ onStep: (step) => {
377
+ var _a, _b;
378
+ setSteps((previous) => [...previous, step]);
379
+ (_b = (_a = optionsRef.current).onStep) == null ? void 0 : _b.call(_a, step);
380
+ },
381
+ onClose: (result) => {
382
+ var _a, _b;
383
+ handleRef.current = null;
384
+ setIsOpen(false);
385
+ (_b = (_a = optionsRef.current).onClose) == null ? void 0 : _b.call(_a, result);
386
+ },
387
+ onError: (err) => {
388
+ var _a, _b;
389
+ handleRef.current = null;
390
+ setIsOpen(false);
391
+ setError(err);
392
+ (_b = (_a = optionsRef.current).onError) == null ? void 0 : _b.call(_a, err);
393
+ }
394
+ });
395
+ handleRef.current = handle.isOpen ? handle : null;
396
+ }, []);
397
+ const close = (0, import_react.useCallback)(() => {
398
+ var _a;
399
+ return (_a = handleRef.current) == null ? void 0 : _a.close();
400
+ }, []);
401
+ return { open, close, isOpen, steps, error };
402
+ }
403
+
404
+ // src/CreditChekButton.tsx
405
+ var import_jsx_runtime = require("react/jsx-runtime");
406
+ function CreditChekButton({
407
+ sessionId,
408
+ publicKey,
409
+ modules,
410
+ themeColor,
411
+ prefill,
412
+ environment,
413
+ widgetUrl,
414
+ onStep,
415
+ onClose,
416
+ onError,
417
+ onClick,
418
+ children = "Verify with CreditChek",
419
+ type = "button",
420
+ ...buttonProps
421
+ }) {
422
+ const { open, isOpen } = useCreditChek({
423
+ publicKey,
424
+ modules,
425
+ themeColor,
426
+ prefill,
427
+ environment,
428
+ widgetUrl,
429
+ onStep,
430
+ onClose,
431
+ onError
432
+ });
433
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
434
+ "button",
435
+ {
436
+ ...buttonProps,
437
+ type,
438
+ "aria-busy": isOpen || void 0,
439
+ onClick: (event) => {
440
+ onClick == null ? void 0 : onClick(event);
441
+ if (!event.defaultPrevented) open(sessionId);
442
+ },
443
+ children
444
+ }
445
+ );
446
+ }
447
+ // Annotate the CommonJS export names for ESM import in node:
448
+ 0 && (module.exports = {
449
+ CreditChekButton,
450
+ CreditChekError,
451
+ WIDGET_URL,
452
+ buildWidgetUrl,
453
+ openCreditChekWidget,
454
+ useCreditChek
455
+ });
456
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/useCreditChek.ts","../src/constants.ts","../src/errors.ts","../src/buildWidgetUrl.ts","../src/modal.ts","../src/openWidget.ts","../src/CreditChekButton.tsx"],"sourcesContent":["export { useCreditChek } from \"./useCreditChek\";\nexport type { UseCreditChekOptions, UseCreditChekResult } from \"./useCreditChek\";\nexport { CreditChekButton } from \"./CreditChekButton\";\nexport type { CreditChekButtonProps } from \"./CreditChekButton\";\nexport { openCreditChekWidget } from \"./openWidget\";\nexport { buildWidgetUrl } from \"./buildWidgetUrl\";\nexport { CreditChekError } from \"./errors\";\nexport type { CreditChekErrorCode } from \"./errors\";\nexport { WIDGET_URL } from \"./constants\";\nexport type {\n CreditChekEnvironment,\n OpenWidgetOptions,\n SessionIdSource,\n WidgetCallbacks,\n WidgetCloseResult,\n WidgetConfig,\n WidgetHandle,\n WidgetModule,\n WidgetPrefill,\n WidgetStepEvent,\n} from \"./types\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { CreditChekError } from \"./errors\";\nimport { openCreditChekWidget } from \"./openWidget\";\nimport type { SessionIdSource, WidgetCallbacks, WidgetConfig, WidgetHandle, WidgetStepEvent } from \"./types\";\n\nexport interface UseCreditChekOptions extends WidgetConfig, WidgetCallbacks {}\n\nexport interface UseCreditChekResult {\n /**\n * Opens the widget in a modal on this page.\n * Pass a function to create the session on click; see {@link OpenWidgetOptions.sessionId}.\n * If the widget is already open, this focuses it instead.\n */\n open: (sessionId: SessionIdSource) => void;\n /** Closes the widget. `onClose` fires as normal. */\n close: () => void;\n /** True from `open` until the widget closes or an error occurs. */\n isOpen: boolean;\n /** Step events received since the last `open`. */\n steps: WidgetStepEvent[];\n /** The error from the last `open`, if any. */\n error: CreditChekError | null;\n}\n\n/**\n * Opens and tracks the CreditChek widget.\n *\n * Options are read when `open` is called, so they can change between renders.\n * If the component unmounts while the widget is open, the modal is removed and\n * callbacks stop firing.\n */\nexport function useCreditChek(options: UseCreditChekOptions): UseCreditChekResult {\n const optionsRef = useRef(options);\n const handleRef = useRef<WidgetHandle | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const [steps, setSteps] = useState<WidgetStepEvent[]>([]);\n const [error, setError] = useState<CreditChekError | null>(null);\n\n useEffect(() => {\n optionsRef.current = options;\n });\n\n useEffect(() => () => handleRef.current?.detach(), []);\n\n const open = useCallback((sessionId: SessionIdSource) => {\n const current = handleRef.current;\n if (current?.isOpen) {\n current.focus();\n return;\n }\n\n setSteps([]);\n setError(null);\n setIsOpen(true);\n\n const handle = openCreditChekWidget({\n ...optionsRef.current,\n sessionId,\n onStep: (step) => {\n setSteps((previous) => [...previous, step]);\n optionsRef.current.onStep?.(step);\n },\n onClose: (result) => {\n handleRef.current = null;\n setIsOpen(false);\n optionsRef.current.onClose?.(result);\n },\n onError: (err) => {\n handleRef.current = null;\n setIsOpen(false);\n setError(err);\n optionsRef.current.onError?.(err);\n },\n });\n // Bad config fails synchronously, and the handle is already dead\n handleRef.current = handle.isOpen ? handle : null;\n }, []);\n\n const close = useCallback(() => handleRef.current?.close(), []);\n\n return { open, close, isOpen, steps, error };\n}\n","import type { CreditChekEnvironment } from \"./types\";\n\nexport const WIDGET_URL = \"https://securedwidget.creditchek.africa/\";\n\nexport const ENVIRONMENTS: readonly CreditChekEnvironment[] = [\"production\", \"development\"];\n\n/** TEMPORARY: sends the widget to a locally running copy. Set to `\"\"` to restore `WIDGET_URL`. */\nexport const LOCAL_WIDGET_URL = \"\";\n\n/** Tells the widget to report each finished step back to this window with `postMessage`. */\nexport const WIDGET_SOURCE = \"react\";\n","export type CreditChekErrorCode =\n /** The `sessionId` function threw or returned an empty value. */\n | \"session_failed\"\n /** Required options are missing or invalid. */\n | \"invalid_config\";\n\nexport class CreditChekError extends Error {\n readonly code: CreditChekErrorCode;\n\n constructor(code: CreditChekErrorCode, message: string, options?: { cause?: unknown }) {\n super(message);\n this.name = \"CreditChekError\";\n this.code = code;\n if (options && \"cause\" in options) {\n (this as { cause?: unknown }).cause = options.cause;\n }\n }\n}\n","import { ENVIRONMENTS, LOCAL_WIDGET_URL, WIDGET_SOURCE, WIDGET_URL } from \"./constants\";\nimport { CreditChekError } from \"./errors\";\nimport type { WidgetConfig, WidgetPrefill } from \"./types\";\n\nconst PREFILL_KEYS: (keyof WidgetPrefill)[] = [\"firstName\", \"lastName\", \"dob\", \"bvn\", \"nin\", \"email\"];\n\n/** The widget address for this config, without any query string. */\nexport function resolveWidgetUrl(config: Pick<WidgetConfig, \"widgetUrl\">): string {\n return config.widgetUrl || LOCAL_WIDGET_URL || WIDGET_URL;\n}\n\n/** Throws a `CreditChekError` with code `invalid_config` if the config can't open a widget. */\nexport function assertWidgetConfig(config: WidgetConfig): void {\n if (!config.publicKey || typeof config.publicKey !== \"string\") {\n throw new CreditChekError(\"invalid_config\", \"`publicKey` is required.\");\n }\n if (config.modules && config.modules.length === 0) {\n throw new CreditChekError(\"invalid_config\", \"`modules` must list at least one step, or be left out.\");\n }\n if (config.environment !== undefined && !ENVIRONMENTS.includes(config.environment)) {\n throw new CreditChekError(\"invalid_config\", '`environment` must be \"production\" or \"development\".');\n }\n try {\n new URL(resolveWidgetUrl(config));\n } catch (cause) {\n throw new CreditChekError(\"invalid_config\", \"`widgetUrl` is not a valid URL.\", { cause });\n }\n}\n\n/** Builds the full widget URL for a session. */\nexport function buildWidgetUrl(config: WidgetConfig, sessionId: string): string {\n assertWidgetConfig(config);\n if (!sessionId) {\n throw new CreditChekError(\"invalid_config\", \"`sessionId` is required.\");\n }\n\n const url = new URL(resolveWidgetUrl(config));\n // URLSearchParams URL-encodes the public key, which can contain / + =\n const params = url.searchParams;\n params.set(\"publicKey\", config.publicKey);\n params.set(\"sessionId\", sessionId);\n params.set(\"module\", (config.modules ?? [\"identity\"]).join(\",\"));\n params.set(\"source\", WIDGET_SOURCE);\n // Without it the widget picks live or sandbox from the key's type\n if (config.environment) params.set(\"environment\", config.environment);\n\n // Lets the widget address its messages to this page only\n const hostOrigin = typeof window !== \"undefined\" ? window.location.origin : \"\";\n if (hostOrigin && hostOrigin !== \"null\") params.set(\"hostOrigin\", hostOrigin);\n\n if (config.themeColor) params.set(\"themeColor\", config.themeColor.replace(/^#/, \"\"));\n\n for (const key of PREFILL_KEYS) {\n const value = config.prefill?.[key]?.trim();\n if (value) params.set(key, value);\n }\n\n return url.toString();\n}\n","const STYLE_ID = \"creditchek-modal-styles\";\nconst TRANSITION_MS = 280;\n\nconst CSS = `\n.creditchek-modal{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:24px}\n.creditchek-modal__backdrop{position:absolute;inset:0;background:rgba(16,24,40,.6);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition:opacity ${TRANSITION_MS}ms ease}\n.creditchek-modal__panel{position:relative;width:min(460px,100%);height:min(780px,100%);background:#F9FAFB;border-radius:16px;overflow:hidden;box-shadow:0 24px 48px -12px rgba(16,24,40,.35);opacity:0;transform:translateY(24px) scale(.98);transition:opacity ${TRANSITION_MS}ms ease,transform ${TRANSITION_MS}ms cubic-bezier(.2,.8,.2,1)}\n.creditchek-modal--open .creditchek-modal__backdrop{opacity:1}\n.creditchek-modal--open .creditchek-modal__panel{opacity:1;transform:none}\n.creditchek-modal__frame{position:absolute;inset:0;width:100%;height:100%;border:0;opacity:0;transition:opacity 200ms ease}\n.creditchek-modal--ready .creditchek-modal__frame{opacity:1}\n.creditchek-modal__loader{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;transition:opacity 200ms ease}\n.creditchek-modal--ready .creditchek-modal__loader{opacity:0;pointer-events:none}\n.creditchek-modal__spinner{width:36px;height:36px;border-radius:50%;border:3px solid rgba(0,70,230,.15);border-top-color:var(--creditchek-accent,#0046E6);animation:creditchek-spin .9s linear infinite}\n.creditchek-modal__close{position:absolute;top:10px;right:10px;z-index:1;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border:0;border-radius:999px;background:rgba(255,255,255,.9);color:#344054;cursor:pointer;box-shadow:0 1px 2px rgba(16,24,40,.12)}\n.creditchek-modal__close:hover{background:#fff}\n.creditchek-modal__close:focus-visible{outline:2px solid var(--creditchek-accent,#0046E6);outline-offset:2px}\n@keyframes creditchek-spin{to{transform:rotate(360deg)}}\n@media (max-width:560px){\n .creditchek-modal{padding:0}\n .creditchek-modal__panel{width:100%;height:100%;border-radius:0;transform:translateY(100%)}\n}\n@media (prefers-reduced-motion:reduce){\n .creditchek-modal__backdrop,.creditchek-modal__panel,.creditchek-modal__frame,.creditchek-modal__loader{transition:none}\n .creditchek-modal__panel{transform:none}\n .creditchek-modal__spinner{animation-duration:2.4s}\n}\n`;\n\nconst CLOSE_ICON =\n '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path d=\"M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/></svg>';\n\nfunction injectStyles(): void {\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent = CSS;\n document.head.appendChild(style);\n}\n\nexport interface ModalOptions {\n /** Spinner and focus-ring colour. */\n accentColor?: string;\n /** Called when the customer dismisses the modal with the close button or Escape. */\n onDismiss: () => void;\n}\n\nexport interface Modal {\n readonly frame: HTMLIFrameElement;\n /** Loads the widget. The loader stays up until the widget reports it's ready, or the page loads. */\n navigate(url: string): void;\n /** Swaps the loader for the widget. */\n reveal(): void;\n focus(): void;\n /** Animates the modal out and removes it. */\n destroy(): void;\n}\n\n/** Renders the modal that hosts the widget iframe. Plain DOM, so it works with or without React. */\nexport function createModal({ accentColor, onDismiss }: ModalOptions): Modal {\n injectStyles();\n\n const previousFocus = document.activeElement as HTMLElement | null;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n const root = document.createElement(\"div\");\n root.className = \"creditchek-modal\";\n root.setAttribute(\"role\", \"dialog\");\n root.setAttribute(\"aria-modal\", \"true\");\n root.setAttribute(\"aria-label\", \"CreditChek verification\");\n if (accentColor) root.style.setProperty(\"--creditchek-accent\", accentColor);\n\n const backdrop = document.createElement(\"div\");\n backdrop.className = \"creditchek-modal__backdrop\";\n\n const panel = document.createElement(\"div\");\n panel.className = \"creditchek-modal__panel\";\n\n const closeButton = document.createElement(\"button\");\n closeButton.type = \"button\";\n closeButton.className = \"creditchek-modal__close\";\n closeButton.setAttribute(\"aria-label\", \"Close verification\");\n closeButton.innerHTML = CLOSE_ICON;\n\n const loader = document.createElement(\"div\");\n loader.className = \"creditchek-modal__loader\";\n loader.setAttribute(\"role\", \"status\");\n loader.setAttribute(\"aria-label\", \"Loading\");\n loader.innerHTML = '<span class=\"creditchek-modal__spinner\"></span>';\n\n const frame = document.createElement(\"iframe\");\n frame.className = \"creditchek-modal__frame\";\n frame.title = \"CreditChek verification\";\n // Camera and microphone for the liveness check\n frame.setAttribute(\"allow\", \"camera; microphone; fullscreen\");\n\n // Focus sentinels keep Tab inside the modal, even as focus moves in and out of the iframe\n const sentinel = () => {\n const el = document.createElement(\"span\");\n el.tabIndex = 0;\n return el;\n };\n const startSentinel = sentinel();\n const endSentinel = sentinel();\n startSentinel.addEventListener(\"focus\", () => frame.focus());\n endSentinel.addEventListener(\"focus\", () => closeButton.focus());\n\n panel.append(startSentinel, closeButton, loader, frame, endSentinel);\n root.append(backdrop, panel);\n document.body.appendChild(root);\n\n let destroyed = false;\n let revealed = false;\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n onDismiss();\n }\n };\n document.addEventListener(\"keydown\", onKeyDown);\n closeButton.addEventListener(\"click\", () => onDismiss());\n\n // Start the enter transition on the next frame, after the initial styles apply\n void root.offsetWidth;\n root.classList.add(\"creditchek-modal--open\");\n closeButton.focus();\n\n const reveal = () => {\n if (revealed || destroyed) return;\n revealed = true;\n root.classList.add(\"creditchek-modal--ready\");\n frame.focus();\n };\n\n return {\n frame,\n navigate(url) {\n if (destroyed) return;\n // Older widget builds don't send \"ready\", so the first page load also reveals it\n frame.addEventListener(\"load\", reveal, { once: true });\n frame.src = url;\n },\n reveal,\n focus() {\n if (!destroyed) (revealed ? frame : closeButton).focus();\n },\n destroy() {\n if (destroyed) return;\n destroyed = true;\n document.removeEventListener(\"keydown\", onKeyDown);\n root.classList.remove(\"creditchek-modal--open\");\n\n const remove = () => {\n root.remove();\n document.body.style.overflow = previousOverflow;\n if (previousFocus && document.contains(previousFocus)) previousFocus.focus();\n };\n const reducedMotion =\n typeof window.matchMedia === \"function\" && window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n if (reducedMotion) remove();\n else setTimeout(remove, TRANSITION_MS);\n },\n };\n}\n","import { assertWidgetConfig, buildWidgetUrl, resolveWidgetUrl } from \"./buildWidgetUrl\";\nimport { CreditChekError } from \"./errors\";\nimport { createModal, type Modal } from \"./modal\";\nimport type { OpenWidgetOptions, WidgetHandle, WidgetModule, WidgetStepEvent } from \"./types\";\n\nconst MODULES: readonly WidgetModule[] = [\"identity\", \"liveness\"];\n\nfunction parseStepEvent(data: unknown): WidgetStepEvent | null {\n if (!data || typeof data !== \"object\") return null;\n const { module, status } = data as Record<string, unknown>;\n if (!MODULES.includes(module as WidgetModule)) return null;\n if (status !== \"successful\" && status !== \"failed\") return null;\n return { module: module as WidgetModule, status };\n}\n\nfunction messageType(data: unknown): unknown {\n return data && typeof data === \"object\" ? (data as Record<string, unknown>).type : undefined;\n}\n\nfunction accentColor(themeColor: string | undefined): string | undefined {\n const hex = themeColor?.replace(/^#/, \"\");\n return hex && /^[0-9a-f]{3,8}$/i.test(hex) ? `#${hex}` : undefined;\n}\n\n/**\n * Opens the CreditChek widget in a modal on the current page, and tracks it until it closes.\n *\n * `sessionId` can be a function that creates the session: the modal shows a loading\n * spinner until it resolves.\n *\n * Exactly one of `onClose` or `onError` fires for each call.\n */\nexport function openCreditChekWidget(options: OpenWidgetOptions): WidgetHandle {\n const { sessionId: sessionSource, onStep, onClose, onError } = options;\n const reportError = onError ?? ((error: CreditChekError) => console.error(error));\n\n let modal: Modal | null = null;\n let sessionId: string | undefined;\n let widgetClosed = false;\n let settled = false;\n const steps: WidgetStepEvent[] = [];\n\n const settle = (callback: () => void) => {\n if (settled) return;\n settled = true;\n window.removeEventListener(\"message\", handleMessage);\n callback();\n };\n\n const fail = (error: CreditChekError) => {\n settle(() => {\n modal?.destroy();\n reportError(error);\n });\n };\n\n const completeIfClosed = () => {\n // While the session is still being created there is nothing to report yet;\n // the session callback finishes the job once it knows the ID.\n if (!widgetClosed || sessionId === undefined) return;\n const result = { sessionId, steps: [...steps] };\n settle(() => onClose?.(result));\n };\n\n const closeWidget = () => {\n if (widgetClosed) return;\n widgetClosed = true;\n modal?.destroy();\n completeIfClosed();\n };\n\n function handleMessage(event: MessageEvent) {\n if (settled || !modal || event.source !== modal.frame.contentWindow) return;\n if (event.origin !== widgetOrigin) return;\n\n const type = messageType(event.data);\n if (type === \"ready\") {\n modal.reveal();\n return;\n }\n if (type === \"close\") {\n closeWidget();\n return;\n }\n const step = parseStepEvent(event.data);\n if (step) {\n steps.push(step);\n onStep?.(step);\n }\n }\n\n const handle: WidgetHandle = {\n close() {\n if (!settled) closeWidget();\n },\n focus() {\n if (!settled && !widgetClosed) modal?.focus();\n },\n detach() {\n if (settled) return;\n settled = true;\n window.removeEventListener(\"message\", handleMessage);\n modal?.destroy();\n },\n get isOpen() {\n return !settled && !widgetClosed && !!modal;\n },\n };\n\n let widgetOrigin: string;\n let initialUrl: string | undefined;\n try {\n assertWidgetConfig(options);\n widgetOrigin = new URL(resolveWidgetUrl(options)).origin;\n if (typeof sessionSource === \"string\") {\n sessionId = sessionSource;\n initialUrl = buildWidgetUrl(options, sessionSource);\n }\n } catch (error) {\n fail(error instanceof CreditChekError ? error : new CreditChekError(\"invalid_config\", String(error)));\n return handle;\n }\n\n modal = createModal({ accentColor: accentColor(options.themeColor), onDismiss: closeWidget });\n window.addEventListener(\"message\", handleMessage);\n\n if (typeof sessionSource === \"string\") {\n modal.navigate(initialUrl!);\n return handle;\n }\n\n let pendingSession: Promise<string>;\n try {\n pendingSession = Promise.resolve(sessionSource());\n } catch (cause) {\n pendingSession = Promise.reject(cause);\n }\n pendingSession.then(\n (id) => {\n if (settled) return;\n if (!id || typeof id !== \"string\") {\n fail(new CreditChekError(\"session_failed\", \"The `sessionId` function did not return a session ID.\"));\n return;\n }\n sessionId = id;\n if (widgetClosed) {\n // The customer closed the modal while the session was being created\n completeIfClosed();\n return;\n }\n modal!.navigate(buildWidgetUrl(options, id));\n },\n (cause) => {\n fail(new CreditChekError(\"session_failed\", \"Could not create a CreditChek session.\", { cause }));\n },\n );\n\n return handle;\n}\n","import type { ButtonHTMLAttributes, MouseEvent, ReactNode } from \"react\";\nimport type { SessionIdSource } from \"./types\";\nimport { useCreditChek, type UseCreditChekOptions } from \"./useCreditChek\";\n\nexport interface CreditChekButtonProps\n extends UseCreditChekOptions,\n Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"onError\" | keyof UseCreditChekOptions> {\n /** The session to open, or a function that creates one on click. */\n sessionId: SessionIdSource;\n /** Runs before the widget opens. Call `event.preventDefault()` to stop it opening. */\n onClick?: (event: MouseEvent<HTMLButtonElement>) => void;\n children?: ReactNode;\n}\n\n/** A button that opens the CreditChek widget in a modal. Other props are passed to the `<button>`. */\nexport function CreditChekButton({\n sessionId,\n publicKey,\n modules,\n themeColor,\n prefill,\n environment,\n widgetUrl,\n onStep,\n onClose,\n onError,\n onClick,\n children = \"Verify with CreditChek\",\n type = \"button\",\n ...buttonProps\n}: CreditChekButtonProps) {\n const { open, isOpen } = useCreditChek({\n publicKey,\n modules,\n themeColor,\n prefill,\n environment,\n widgetUrl,\n onStep,\n onClose,\n onError,\n });\n\n return (\n <button\n {...buttonProps}\n type={type}\n aria-busy={isOpen || undefined}\n onClick={(event) => {\n onClick?.(event);\n if (!event.defaultPrevented) open(sessionId);\n }}\n >\n {children}\n </button>\n );\n}\n"],"mappings":"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","names":["accentColor","module"]}
@@ -0,0 +1,148 @@
1
+ import * as react from 'react';
2
+ import { ButtonHTMLAttributes, MouseEvent, ReactNode } from 'react';
3
+
4
+ type CreditChekErrorCode =
5
+ /** The `sessionId` function threw or returned an empty value. */
6
+ "session_failed"
7
+ /** Required options are missing or invalid. */
8
+ | "invalid_config";
9
+ declare class CreditChekError extends Error {
10
+ readonly code: CreditChekErrorCode;
11
+ constructor(code: CreditChekErrorCode, message: string, options?: {
12
+ cause?: unknown;
13
+ });
14
+ }
15
+
16
+ /** A step the customer goes through in the widget, in the order you list them. */
17
+ type WidgetModule = "identity" | "liveness";
18
+ /** Whether the widget runs live (`production`) or in test mode (`development`). */
19
+ type CreditChekEnvironment = "production" | "development";
20
+ /** Customer details you already hold. Prefilled identity fields are locked in the widget. */
21
+ interface WidgetPrefill {
22
+ firstName?: string;
23
+ lastName?: string;
24
+ /** `YYYY-MM-DD` */
25
+ dob?: string;
26
+ /** 11-digit BVN */
27
+ bvn?: string;
28
+ /** 11-digit NIN */
29
+ nin?: string;
30
+ email?: string;
31
+ }
32
+ /** Everything that shapes the widget URL, apart from the session. */
33
+ interface WidgetConfig {
34
+ /** Your public key. Must belong to the same app as the secret key that created the session. */
35
+ publicKey: string;
36
+ /** Steps to run, in order. Defaults to `["identity"]`. Put `identity` before `liveness`. */
37
+ modules?: WidgetModule[];
38
+ /** Brand colour as hex, with or without `#`, e.g. `"#0046E6"`. */
39
+ themeColor?: string;
40
+ prefill?: WidgetPrefill;
41
+ /**
42
+ * Sent to the widget as the `environment` query parameter. `"development"` runs it in test mode
43
+ * and shows its Test mode notice. Left out, the widget takes live or test mode from the public key.
44
+ */
45
+ environment?: CreditChekEnvironment;
46
+ /** Override the widget address, e.g. for a preview build. */
47
+ widgetUrl?: string;
48
+ }
49
+ /**
50
+ * Sent by the widget as each step finishes.
51
+ *
52
+ * Use it for progress in your UI only. Decide whether the customer is verified on
53
+ * your server, by reading the session with your secret key.
54
+ */
55
+ interface WidgetStepEvent {
56
+ module: WidgetModule;
57
+ status: "successful" | "failed";
58
+ }
59
+ /** Passed to `onClose` once the widget has closed, for any reason. */
60
+ interface WidgetCloseResult {
61
+ sessionId: string;
62
+ /** Step events received while the widget was open, oldest first. */
63
+ steps: WidgetStepEvent[];
64
+ }
65
+ /** A session ID, or a function that creates one (usually by calling your server). */
66
+ type SessionIdSource = string | (() => string | Promise<string>);
67
+ interface WidgetCallbacks {
68
+ /** Called as each step finishes. See {@link WidgetStepEvent}. */
69
+ onStep?: (event: WidgetStepEvent) => void;
70
+ /**
71
+ * Called once the widget closes: the customer finished, hit an error screen, or closed it.
72
+ * Closing does not mean the customer passed. Check the session on your server.
73
+ */
74
+ onClose?: (result: WidgetCloseResult) => void;
75
+ /** Called if the widget can't be opened, or `sessionId` fails. `onClose` is not called after this. */
76
+ onError?: (error: CreditChekError) => void;
77
+ }
78
+ interface OpenWidgetOptions extends WidgetConfig, WidgetCallbacks {
79
+ /**
80
+ * The session to open, or a function that creates one.
81
+ * With a function, the modal opens straight away and shows a spinner until the session is ready.
82
+ */
83
+ sessionId: SessionIdSource;
84
+ }
85
+ interface WidgetHandle {
86
+ /** Close the widget. `onClose` fires as normal. */
87
+ close(): void;
88
+ /** Move keyboard focus into the widget. */
89
+ focus(): void;
90
+ /** Remove the modal without calling `onClose`. No further callbacks fire. */
91
+ detach(): void;
92
+ /** Whether the widget is still open and tracked. */
93
+ readonly isOpen: boolean;
94
+ }
95
+
96
+ interface UseCreditChekOptions extends WidgetConfig, WidgetCallbacks {
97
+ }
98
+ interface UseCreditChekResult {
99
+ /**
100
+ * Opens the widget in a modal on this page.
101
+ * Pass a function to create the session on click; see {@link OpenWidgetOptions.sessionId}.
102
+ * If the widget is already open, this focuses it instead.
103
+ */
104
+ open: (sessionId: SessionIdSource) => void;
105
+ /** Closes the widget. `onClose` fires as normal. */
106
+ close: () => void;
107
+ /** True from `open` until the widget closes or an error occurs. */
108
+ isOpen: boolean;
109
+ /** Step events received since the last `open`. */
110
+ steps: WidgetStepEvent[];
111
+ /** The error from the last `open`, if any. */
112
+ error: CreditChekError | null;
113
+ }
114
+ /**
115
+ * Opens and tracks the CreditChek widget.
116
+ *
117
+ * Options are read when `open` is called, so they can change between renders.
118
+ * If the component unmounts while the widget is open, the modal is removed and
119
+ * callbacks stop firing.
120
+ */
121
+ declare function useCreditChek(options: UseCreditChekOptions): UseCreditChekResult;
122
+
123
+ interface CreditChekButtonProps extends UseCreditChekOptions, Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onError" | keyof UseCreditChekOptions> {
124
+ /** The session to open, or a function that creates one on click. */
125
+ sessionId: SessionIdSource;
126
+ /** Runs before the widget opens. Call `event.preventDefault()` to stop it opening. */
127
+ onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
128
+ children?: ReactNode;
129
+ }
130
+ /** A button that opens the CreditChek widget in a modal. Other props are passed to the `<button>`. */
131
+ declare function CreditChekButton({ sessionId, publicKey, modules, themeColor, prefill, environment, widgetUrl, onStep, onClose, onError, onClick, children, type, ...buttonProps }: CreditChekButtonProps): react.JSX.Element;
132
+
133
+ /**
134
+ * Opens the CreditChek widget in a modal on the current page, and tracks it until it closes.
135
+ *
136
+ * `sessionId` can be a function that creates the session: the modal shows a loading
137
+ * spinner until it resolves.
138
+ *
139
+ * Exactly one of `onClose` or `onError` fires for each call.
140
+ */
141
+ declare function openCreditChekWidget(options: OpenWidgetOptions): WidgetHandle;
142
+
143
+ /** Builds the full widget URL for a session. */
144
+ declare function buildWidgetUrl(config: WidgetConfig, sessionId: string): string;
145
+
146
+ declare const WIDGET_URL = "https://securedwidget.creditchek.africa/";
147
+
148
+ export { CreditChekButton, type CreditChekButtonProps, type CreditChekEnvironment, CreditChekError, type CreditChekErrorCode, type OpenWidgetOptions, type SessionIdSource, type UseCreditChekOptions, type UseCreditChekResult, WIDGET_URL, type WidgetCallbacks, type WidgetCloseResult, type WidgetConfig, type WidgetHandle, type WidgetModule, type WidgetPrefill, type WidgetStepEvent, buildWidgetUrl, openCreditChekWidget, useCreditChek };