@flonkid/kyc 1.7.0 → 1.8.1
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 +122 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -2
- package/dist/index.d.ts +37 -2
- package/dist/index.js +122 -41
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { useRef, useMemo, useEffect } from 'react';
|
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/shared/constants.ts
|
|
5
|
-
var SDK_VERSION = "1.
|
|
5
|
+
var SDK_VERSION = "1.8.1";
|
|
6
6
|
var DEFAULT_WIDGET_URL = "https://widget.flonk.id";
|
|
7
7
|
var DEFAULT_API_BASE = "https://api.flonk.id/v1";
|
|
8
8
|
var WIDGET_EVENTS = {
|
|
@@ -88,37 +88,69 @@ async function fetchWithTimeout(url, init = {}, timeoutMs = DEFAULT_FETCH_TIMEOU
|
|
|
88
88
|
clearTimeout(timer);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
+
var widgetTokenInflight = /* @__PURE__ */ new Map();
|
|
91
92
|
async function fetchWidgetToken(pk, apiBase) {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
93
|
+
const key = `${apiBase}|${pk}`;
|
|
94
|
+
const existing = widgetTokenInflight.get(key);
|
|
95
|
+
if (existing) return existing;
|
|
96
|
+
const promise = (async () => {
|
|
97
|
+
const res = await fetchWithTimeout(`${apiBase}/public/widget-token`, {
|
|
98
|
+
headers: { "x-kyc-pk": pk },
|
|
99
|
+
credentials: "include"
|
|
100
|
+
});
|
|
101
|
+
if (!res.ok) {
|
|
102
|
+
let message = `Widget token request failed (${res.status})`;
|
|
103
|
+
try {
|
|
104
|
+
const b = await res.json();
|
|
105
|
+
message = b.error || b.message || message;
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
throw new Error(message);
|
|
102
109
|
}
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
110
|
+
return res.json();
|
|
111
|
+
})().finally(() => {
|
|
112
|
+
widgetTokenInflight.delete(key);
|
|
113
|
+
});
|
|
114
|
+
widgetTokenInflight.set(key, promise);
|
|
115
|
+
return promise;
|
|
106
116
|
}
|
|
107
|
-
|
|
117
|
+
var BRANDING_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
118
|
+
var brandingCache = /* @__PURE__ */ new Map();
|
|
119
|
+
function brandingCacheKey(opts) {
|
|
120
|
+
if (opts.pk) return `pk:${opts.pk}`;
|
|
121
|
+
if (opts.sessionId) return `sid:${opts.sessionId}`;
|
|
122
|
+
if (opts.clientId) return `cid:${opts.clientId}`;
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
async function requestDesignTokens(apiBase, opts) {
|
|
108
126
|
const params = [];
|
|
109
127
|
if (opts.sessionId) params.push(`sessionId=${encodeURIComponent(opts.sessionId)}`);
|
|
110
128
|
else if (opts.clientId) params.push(`clientId=${encodeURIComponent(opts.clientId)}`);
|
|
111
129
|
const url = `${apiBase}/public/design-tokens${params.length ? "?" + params.join("&") : ""}`;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
const res = await fetchWithTimeout(
|
|
131
|
+
url,
|
|
132
|
+
{ headers: opts.pk ? { "x-kyc-pk": opts.pk } : {}, credentials: "omit" },
|
|
133
|
+
8e3
|
|
134
|
+
);
|
|
135
|
+
return res.ok ? res.json() : null;
|
|
136
|
+
}
|
|
137
|
+
async function fetchDesignTokens(apiBase, opts) {
|
|
138
|
+
const key = brandingCacheKey(opts);
|
|
139
|
+
if (!key) return null;
|
|
140
|
+
const now = Date.now();
|
|
141
|
+
const cached = brandingCache.get(key);
|
|
142
|
+
if (cached && cached.expiresAt > now) {
|
|
143
|
+
return cached.promise;
|
|
121
144
|
}
|
|
145
|
+
const promise = requestDesignTokens(apiBase, opts).catch(() => null);
|
|
146
|
+
brandingCache.set(key, { promise, expiresAt: now + BRANDING_CACHE_TTL_MS });
|
|
147
|
+
promise.then((tokens) => {
|
|
148
|
+
if (tokens == null) brandingCache.delete(key);
|
|
149
|
+
});
|
|
150
|
+
return promise;
|
|
151
|
+
}
|
|
152
|
+
function preloadDesignTokens(apiBase, opts) {
|
|
153
|
+
return fetchDesignTokens(apiBase, opts);
|
|
122
154
|
}
|
|
123
155
|
function validateServerUrl(url) {
|
|
124
156
|
if (url.startsWith("/")) return;
|
|
@@ -135,25 +167,35 @@ function validateServerUrl(url) {
|
|
|
135
167
|
throw new Error(`Invalid serverUrl: ${url}`);
|
|
136
168
|
}
|
|
137
169
|
}
|
|
170
|
+
var sessionCreateInflight = /* @__PURE__ */ new Map();
|
|
138
171
|
async function fetchSessionFromServer(serverUrl, clientMetadata, requestHeaders) {
|
|
139
172
|
validateServerUrl(serverUrl);
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
173
|
+
const key = `${serverUrl}|${JSON.stringify(clientMetadata ?? null)}`;
|
|
174
|
+
const existing = sessionCreateInflight.get(key);
|
|
175
|
+
if (existing) return existing;
|
|
176
|
+
const promise = (async () => {
|
|
177
|
+
const res = await fetchWithTimeout(serverUrl, {
|
|
178
|
+
method: "POST",
|
|
179
|
+
headers: { "Content-Type": "application/json", ...requestHeaders },
|
|
180
|
+
credentials: "include",
|
|
181
|
+
body: JSON.stringify({ clientMetadata })
|
|
182
|
+
});
|
|
183
|
+
if (!res.ok) {
|
|
184
|
+
let message = `Session request failed (${res.status})`;
|
|
185
|
+
try {
|
|
186
|
+
const body = await res.json();
|
|
187
|
+
if (body.error) message = body.error;
|
|
188
|
+
else if (body.message) message = body.message;
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
throw new Error(message);
|
|
153
192
|
}
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
193
|
+
return res.json();
|
|
194
|
+
})().finally(() => {
|
|
195
|
+
sessionCreateInflight.delete(key);
|
|
196
|
+
});
|
|
197
|
+
sessionCreateInflight.set(key, promise);
|
|
198
|
+
return promise;
|
|
157
199
|
}
|
|
158
200
|
async function fetchPublicSession(apiBase, sessionId, embedToken) {
|
|
159
201
|
const res = await fetchWithTimeout(`${apiBase}/public/session/${sessionId}`, {
|
|
@@ -703,6 +745,26 @@ var FlonkKYC = class {
|
|
|
703
745
|
this.widgetUrl = (options.widgetUrl || DEFAULT_WIDGET_URL).replace(/\/$/, "");
|
|
704
746
|
this.apiBase = (options.apiBase || DEFAULT_API_BASE).replace(/\/$/, "");
|
|
705
747
|
}
|
|
748
|
+
/**
|
|
749
|
+
* Warm the project's branding (colors) ahead of time so the widget paints the
|
|
750
|
+
* brand color on the first frame, with no branding round-trip at click time.
|
|
751
|
+
*
|
|
752
|
+
* Call it early — on page mount, route enter, or hover of the "verify" button
|
|
753
|
+
* — well before `init()`. The result is cached (module-level, 5-min TTL) and
|
|
754
|
+
* every subsequent `init()`/`open()` for the same key reads from it. Safe to
|
|
755
|
+
* call repeatedly; concurrent calls dedupe. Never throws.
|
|
756
|
+
*
|
|
757
|
+
* @example
|
|
758
|
+
* // in a layout effect, long before the user clicks "Verify"
|
|
759
|
+
* FlonkKYC.preloadBranding({ publishableKey: 'pk_live_...' });
|
|
760
|
+
*/
|
|
761
|
+
static preloadBranding(opts) {
|
|
762
|
+
const apiBase = (opts.apiBase || DEFAULT_API_BASE).replace(/\/$/, "");
|
|
763
|
+
return preloadDesignTokens(apiBase, {
|
|
764
|
+
pk: opts.publishableKey,
|
|
765
|
+
sessionId: opts.sessionId
|
|
766
|
+
}).then(() => void 0);
|
|
767
|
+
}
|
|
706
768
|
// ── Public API ───────────────────────────────────────
|
|
707
769
|
/**
|
|
708
770
|
* Open the KYC verification widget.
|
|
@@ -970,6 +1032,11 @@ var FlonkKYC = class {
|
|
|
970
1032
|
if (session.mlAutoCaptureEnabled) params.mlAutoCaptureEnabled = "true";
|
|
971
1033
|
if (session.mlCropEnabled !== false) params.mlCropEnabled = "true";
|
|
972
1034
|
if (session.mlVerifyEnabled) params.mlVerifyEnabled = "true";
|
|
1035
|
+
if (session.vaultReuseEnabled) params.vaultReuseEnabled = "true";
|
|
1036
|
+
if (session.vaultReuseChallenge) params.vaultReuseChallenge = session.vaultReuseChallenge;
|
|
1037
|
+
if (session.faceAutoCapture) params.faceAutoCapture = JSON.stringify(session.faceAutoCapture);
|
|
1038
|
+
if (session.project) params.project = JSON.stringify(session.project);
|
|
1039
|
+
params.policyReady = "1";
|
|
973
1040
|
if (designTokens?.colors) {
|
|
974
1041
|
params.designTokens = JSON.stringify(designTokens);
|
|
975
1042
|
}
|
|
@@ -1075,6 +1142,10 @@ function FlonkKYCWidget({
|
|
|
1075
1142
|
const callbacksRef = useRef({ onSuccess, onError, onCancel, onReady });
|
|
1076
1143
|
callbacksRef.current = { onSuccess, onError, onCancel, onReady };
|
|
1077
1144
|
const sdk = useMemo(() => new FlonkKYC({ widgetUrl, apiBase }), [widgetUrl, apiBase]);
|
|
1145
|
+
useEffect(() => {
|
|
1146
|
+
if (!publishableKey && !sessionId) return;
|
|
1147
|
+
void FlonkKYC.preloadBranding({ publishableKey, sessionId, apiBase });
|
|
1148
|
+
}, [publishableKey, sessionId, apiBase]);
|
|
1078
1149
|
const generationRef = useRef(0);
|
|
1079
1150
|
useEffect(() => {
|
|
1080
1151
|
if (!autoOpen) return;
|
|
@@ -1117,7 +1188,17 @@ function FlonkKYCWidget({
|
|
|
1117
1188
|
}, [sdk, publishableKey, serverUrl, sessionId, embedToken, lang, overlayColor, allowManualUpload, autoOpen]);
|
|
1118
1189
|
return /* @__PURE__ */ jsx("div", { ref: mountRef });
|
|
1119
1190
|
}
|
|
1191
|
+
function FlonkKYCBrandingPreloader({
|
|
1192
|
+
publishableKey,
|
|
1193
|
+
sessionId,
|
|
1194
|
+
apiBase
|
|
1195
|
+
}) {
|
|
1196
|
+
useEffect(() => {
|
|
1197
|
+
void FlonkKYC.preloadBranding({ publishableKey, sessionId, apiBase });
|
|
1198
|
+
}, [publishableKey, sessionId, apiBase]);
|
|
1199
|
+
return null;
|
|
1200
|
+
}
|
|
1120
1201
|
|
|
1121
|
-
export { FlonkError, FlonkKYC, FlonkKYCWidget, FlonkValidationError };
|
|
1202
|
+
export { FlonkError, FlonkKYC, FlonkKYCBrandingPreloader, FlonkKYCWidget, FlonkValidationError };
|
|
1122
1203
|
//# sourceMappingURL=index.js.map
|
|
1123
1204
|
//# sourceMappingURL=index.js.map
|