@sekyro/web 0.1.0-rc.3 → 0.1.0-rc.5
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/CHANGELOG.md +30 -0
- package/README.md +44 -0
- package/dist/{capture-session-v3-CvIfcwST.d.ts → capture-session-v3-Okq0EQHG.d.ts} +38 -2
- package/dist/{capture-session-v3-COIO_F2w.d.cts → capture-session-v3-UQU_WjO2.d.cts} +38 -2
- package/dist/{chunk-FIXX2AXC.js → chunk-SGDXLAXR.js} +309 -10
- package/dist/chunk-SGDXLAXR.js.map +1 -0
- package/dist/{chunk-7TC6TOZ4.js → chunk-TG5IE552.js} +37 -4
- package/dist/chunk-TG5IE552.js.map +1 -0
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/core/index.cjs +36 -3
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +1 -1
- package/dist/{create-verification-CswF2As-.d.ts → create-verification-CA-3szPI.d.cts} +108 -8
- package/dist/{create-verification-CP18eq1Q.d.cts → create-verification-c98uAxbl.d.ts} +108 -8
- package/dist/{hosted-client-BpvBqu9f.d.cts → hosted-client-DWzCRYJK.d.cts} +14 -1
- package/dist/{hosted-client-BpvBqu9f.d.ts → hosted-client-DWzCRYJK.d.ts} +14 -1
- package/dist/index.cjs +384 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -3
- package/dist/index.d.ts +25 -3
- package/dist/index.js +61 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/index.cjs +261 -11
- package/dist/internal/index.cjs.map +1 -1
- package/dist/internal/index.d.cts +4 -4
- package/dist/internal/index.d.ts +4 -4
- package/dist/internal/index.js +12 -2
- package/dist/internal/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-7TC6TOZ4.js.map +0 -1
- package/dist/chunk-FIXX2AXC.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -36,8 +36,15 @@ __export(src_exports, {
|
|
|
36
36
|
SEKYRO_EVENTS: () => SEKYRO_EVENTS,
|
|
37
37
|
createLivenessCapture: () => createLivenessCapture,
|
|
38
38
|
createVerification: () => createVerification,
|
|
39
|
+
getLivenessAssetsStatus: () => getLivenessAssetsStatus,
|
|
40
|
+
hasMultipleCameras: () => hasMultipleCameras,
|
|
39
41
|
isKnownCode: () => isKnownCode,
|
|
40
|
-
openHosted: () => openHosted
|
|
42
|
+
openHosted: () => openHosted,
|
|
43
|
+
preloadLivenessAssets: () => preloadLivenessAssets,
|
|
44
|
+
queryCameraPermission: () => queryCameraPermission,
|
|
45
|
+
releaseLivenessAssets: () => releaseLivenessAssets,
|
|
46
|
+
subscribeCameraPermission: () => subscribeCameraPermission,
|
|
47
|
+
subscribeLivenessAssets: () => subscribeLivenessAssets
|
|
41
48
|
});
|
|
42
49
|
module.exports = __toCommonJS(src_exports);
|
|
43
50
|
|
|
@@ -952,7 +959,8 @@ var FaceGateV3 = class {
|
|
|
952
959
|
p: this.p,
|
|
953
960
|
factors,
|
|
954
961
|
guidanceKey: this.guidanceOf(factors, quality, q),
|
|
955
|
-
ready
|
|
962
|
+
ready,
|
|
963
|
+
accepted: q >= this.t.Q_GOOD
|
|
956
964
|
};
|
|
957
965
|
}
|
|
958
966
|
geometryOf(observation, input) {
|
|
@@ -1070,6 +1078,11 @@ var CaptureSessionV3 = class {
|
|
|
1070
1078
|
this.profiler = null;
|
|
1071
1079
|
this.lastGuidanceKey = null;
|
|
1072
1080
|
this.positioningStartedAt = 0;
|
|
1081
|
+
// Face-capture timing marks of the CURRENT attempt (engine clock; reset per run).
|
|
1082
|
+
this.cameraRequestedAt = null;
|
|
1083
|
+
this.cameraReadyAt = null;
|
|
1084
|
+
this.faceAcquiredAt = null;
|
|
1085
|
+
this.captureCompleteAt = null;
|
|
1073
1086
|
this.fired = false;
|
|
1074
1087
|
this.carriedDegrade = 0;
|
|
1075
1088
|
this.carriedMeans = [];
|
|
@@ -1096,10 +1109,15 @@ var CaptureSessionV3 = class {
|
|
|
1096
1109
|
handler(payload);
|
|
1097
1110
|
}
|
|
1098
1111
|
}
|
|
1099
|
-
async start() {
|
|
1112
|
+
async start(options = {}) {
|
|
1113
|
+
if (options.facingMode !== void 0) this.facing = options.facingMode;
|
|
1100
1114
|
this.attempt += 1;
|
|
1101
1115
|
await this.run(this.attempt);
|
|
1102
1116
|
}
|
|
1117
|
+
/** The camera the current/next attempt requests (start option, switchCamera toggles). */
|
|
1118
|
+
get facingMode() {
|
|
1119
|
+
return this.facing;
|
|
1120
|
+
}
|
|
1103
1121
|
/** Full reset + camera restart — the documented recovery path (v1 parity). */
|
|
1104
1122
|
retry() {
|
|
1105
1123
|
this.teardownAttempt();
|
|
@@ -1124,6 +1142,10 @@ var CaptureSessionV3 = class {
|
|
|
1124
1142
|
this.lastGuidanceKey = null;
|
|
1125
1143
|
this.carriedDegrade = 0;
|
|
1126
1144
|
this.carriedMeans = [];
|
|
1145
|
+
this.cameraRequestedAt = null;
|
|
1146
|
+
this.cameraReadyAt = null;
|
|
1147
|
+
this.faceAcquiredAt = null;
|
|
1148
|
+
this.captureCompleteAt = null;
|
|
1127
1149
|
this.setState("preflight");
|
|
1128
1150
|
const preflight = cameraPreflight();
|
|
1129
1151
|
if (preflight !== "OK") {
|
|
@@ -1133,6 +1155,7 @@ var CaptureSessionV3 = class {
|
|
|
1133
1155
|
this.setState("acquiring");
|
|
1134
1156
|
const getUserMedia = options.getUserMedia ?? ((constraints) => navigator.mediaDevices.getUserMedia(constraints));
|
|
1135
1157
|
let stream;
|
|
1158
|
+
this.cameraRequestedAt = this.now();
|
|
1136
1159
|
try {
|
|
1137
1160
|
stream = await getUserMedia(buildCaptureConstraints(detectIsMobile(), this.facing));
|
|
1138
1161
|
} catch (err) {
|
|
@@ -1148,6 +1171,7 @@ var CaptureSessionV3 = class {
|
|
|
1148
1171
|
options.video.srcObject = stream;
|
|
1149
1172
|
await options.video.play().catch(() => void 0);
|
|
1150
1173
|
if (attempt !== this.attempt) return;
|
|
1174
|
+
this.cameraReadyAt = this.now();
|
|
1151
1175
|
if (this.detector === null) {
|
|
1152
1176
|
const ladderOptions = {
|
|
1153
1177
|
...options.v3AssetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.v3AssetBaseUrl } : {}
|
|
@@ -1247,6 +1271,7 @@ var CaptureSessionV3 = class {
|
|
|
1247
1271
|
frameWidth: video.videoWidth,
|
|
1248
1272
|
frameHeight: video.videoHeight
|
|
1249
1273
|
});
|
|
1274
|
+
if (tick.accepted && this.faceAcquiredAt === null) this.faceAcquiredAt = tick.at;
|
|
1250
1275
|
this.emit("checks", {
|
|
1251
1276
|
engine: "v3",
|
|
1252
1277
|
progress: tick.p,
|
|
@@ -1260,6 +1285,7 @@ var CaptureSessionV3 = class {
|
|
|
1260
1285
|
}
|
|
1261
1286
|
if (tick.ready && this.stateValue === "positioning") {
|
|
1262
1287
|
this.fired = true;
|
|
1288
|
+
this.captureCompleteAt = tick.at;
|
|
1263
1289
|
void this.fire(attempt);
|
|
1264
1290
|
}
|
|
1265
1291
|
}
|
|
@@ -1354,7 +1380,21 @@ var CaptureSessionV3 = class {
|
|
|
1354
1380
|
finishCapture(artifacts) {
|
|
1355
1381
|
this.stopTracks();
|
|
1356
1382
|
this.setState("captured");
|
|
1357
|
-
this.
|
|
1383
|
+
const timing = this.captureTiming();
|
|
1384
|
+
this.emit("artifacts", timing === void 0 ? artifacts : { ...artifacts, timing });
|
|
1385
|
+
}
|
|
1386
|
+
/** The attempt's capture-phase timing (whole ms, ≥ 0); undefined when no mark landed. */
|
|
1387
|
+
captureTiming() {
|
|
1388
|
+
const span = (from, to) => from !== null && to !== null ? Math.max(0, Math.round(to - from)) : void 0;
|
|
1389
|
+
const cameraReadyMs = span(this.cameraRequestedAt, this.cameraReadyAt);
|
|
1390
|
+
const faceAcquireMs = span(this.cameraReadyAt, this.faceAcquiredAt);
|
|
1391
|
+
const captureMs = span(this.faceAcquiredAt, this.captureCompleteAt);
|
|
1392
|
+
const timing = {
|
|
1393
|
+
...cameraReadyMs !== void 0 ? { cameraReadyMs } : {},
|
|
1394
|
+
...faceAcquireMs !== void 0 ? { faceAcquireMs } : {},
|
|
1395
|
+
...captureMs !== void 0 ? { captureMs } : {}
|
|
1396
|
+
};
|
|
1397
|
+
return Object.keys(timing).length > 0 ? timing : void 0;
|
|
1358
1398
|
}
|
|
1359
1399
|
// ── defaults for the canvas-bound seams ────────────────────────────────────
|
|
1360
1400
|
async defaultKeyframe(video) {
|
|
@@ -1485,6 +1525,250 @@ function createEmitter() {
|
|
|
1485
1525
|
};
|
|
1486
1526
|
}
|
|
1487
1527
|
|
|
1528
|
+
// src/capture/preload.ts
|
|
1529
|
+
var PRELOAD_BLAZE_INIT_TIMEOUT_MS = 12e4;
|
|
1530
|
+
var PRELOAD_BUDGET_MS = 13e4;
|
|
1531
|
+
var IDLE_TIMEOUT_MS = 3e3;
|
|
1532
|
+
var IDLE_FALLBACK_MS = 1200;
|
|
1533
|
+
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("@sekyro/web.preload.v1");
|
|
1534
|
+
function registry() {
|
|
1535
|
+
const host = globalThis;
|
|
1536
|
+
host[REGISTRY_KEY] ?? (host[REGISTRY_KEY] = /* @__PURE__ */ new Map());
|
|
1537
|
+
return host[REGISTRY_KEY];
|
|
1538
|
+
}
|
|
1539
|
+
function normalizeBase(assetBaseUrl) {
|
|
1540
|
+
const base = (assetBaseUrl ?? DEFAULT_V3_ASSET_BASE_URL).replace(/\/+$/, "");
|
|
1541
|
+
return base === "" ? "/" : base;
|
|
1542
|
+
}
|
|
1543
|
+
function entryFor(base) {
|
|
1544
|
+
const map = registry();
|
|
1545
|
+
let entry = map.get(base);
|
|
1546
|
+
if (entry === void 0) {
|
|
1547
|
+
entry = { status: "idle", promise: null, scheduled: null, detectorPromise: null, detector: null, listeners: /* @__PURE__ */ new Set() };
|
|
1548
|
+
map.set(base, entry);
|
|
1549
|
+
}
|
|
1550
|
+
return entry;
|
|
1551
|
+
}
|
|
1552
|
+
function setStatus(entry, next) {
|
|
1553
|
+
if (entry.status === next) return;
|
|
1554
|
+
entry.status = next;
|
|
1555
|
+
for (const listener of [...entry.listeners]) listener(next);
|
|
1556
|
+
}
|
|
1557
|
+
var defaultLoader = (base) => createDetector({ preferred: ["blaze"], v3AssetBaseUrl: base, blazeInitTimeoutMs: PRELOAD_BLAZE_INIT_TIMEOUT_MS });
|
|
1558
|
+
var loader = defaultLoader;
|
|
1559
|
+
function canCapture() {
|
|
1560
|
+
return typeof window !== "undefined" && typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function";
|
|
1561
|
+
}
|
|
1562
|
+
function saveDataOn() {
|
|
1563
|
+
return navigator.connection?.saveData === true;
|
|
1564
|
+
}
|
|
1565
|
+
function scheduleIdle(run) {
|
|
1566
|
+
const w = window;
|
|
1567
|
+
if (typeof w.requestIdleCallback === "function") {
|
|
1568
|
+
const handle = w.requestIdleCallback(run, { timeout: IDLE_TIMEOUT_MS });
|
|
1569
|
+
return () => w.cancelIdleCallback?.(handle);
|
|
1570
|
+
}
|
|
1571
|
+
const timer = setTimeout(run, IDLE_FALLBACK_MS);
|
|
1572
|
+
return () => clearTimeout(timer);
|
|
1573
|
+
}
|
|
1574
|
+
function schedule(entry, base) {
|
|
1575
|
+
let resolve;
|
|
1576
|
+
const promise = new Promise((res) => {
|
|
1577
|
+
resolve = res;
|
|
1578
|
+
});
|
|
1579
|
+
let done = false;
|
|
1580
|
+
let cancel = () => void 0;
|
|
1581
|
+
const scheduled = {
|
|
1582
|
+
promise,
|
|
1583
|
+
settle: (outcome) => {
|
|
1584
|
+
if (done) return;
|
|
1585
|
+
done = true;
|
|
1586
|
+
cancel();
|
|
1587
|
+
if (entry.scheduled === scheduled) entry.scheduled = null;
|
|
1588
|
+
resolve(outcome);
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
entry.scheduled = scheduled;
|
|
1592
|
+
cancel = scheduleIdle(() => {
|
|
1593
|
+
if (done) return;
|
|
1594
|
+
scheduled.settle(begin(entry, base));
|
|
1595
|
+
});
|
|
1596
|
+
return promise;
|
|
1597
|
+
}
|
|
1598
|
+
function begin(entry, base) {
|
|
1599
|
+
if ((entry.status === "loading" || entry.status === "ready") && entry.promise !== null) return entry.promise;
|
|
1600
|
+
setStatus(entry, "loading");
|
|
1601
|
+
const detectorPromise = new Promise((resolve) => resolve(loader(base)));
|
|
1602
|
+
entry.detectorPromise = detectorPromise;
|
|
1603
|
+
let timer;
|
|
1604
|
+
const budget = new Promise((_, reject) => {
|
|
1605
|
+
timer = setTimeout(() => {
|
|
1606
|
+
detectorPromise.then((late) => late.destroy(), () => void 0);
|
|
1607
|
+
reject(new Error(`detector preload exceeded ${PRELOAD_BUDGET_MS} ms`));
|
|
1608
|
+
}, PRELOAD_BUDGET_MS);
|
|
1609
|
+
});
|
|
1610
|
+
const settled = Promise.race([detectorPromise, budget]).then(
|
|
1611
|
+
(detector) => {
|
|
1612
|
+
if (entry.detectorPromise !== detectorPromise) {
|
|
1613
|
+
detector.destroy();
|
|
1614
|
+
return entry.status;
|
|
1615
|
+
}
|
|
1616
|
+
entry.detector = detector;
|
|
1617
|
+
setStatus(entry, "ready");
|
|
1618
|
+
return "ready";
|
|
1619
|
+
},
|
|
1620
|
+
(err) => {
|
|
1621
|
+
if (entry.detectorPromise === detectorPromise) {
|
|
1622
|
+
entry.detectorPromise = null;
|
|
1623
|
+
entry.detector = null;
|
|
1624
|
+
setStatus(entry, "failed");
|
|
1625
|
+
console.warn("[@sekyro/web] face-detector preload failed:", err);
|
|
1626
|
+
}
|
|
1627
|
+
return entry.status;
|
|
1628
|
+
}
|
|
1629
|
+
);
|
|
1630
|
+
const promise = settled.finally(() => clearTimeout(timer));
|
|
1631
|
+
entry.promise = promise;
|
|
1632
|
+
entry.scheduled?.settle(promise);
|
|
1633
|
+
return promise;
|
|
1634
|
+
}
|
|
1635
|
+
function preloadLivenessAssets(options = {}) {
|
|
1636
|
+
if (!canCapture()) return Promise.resolve("idle");
|
|
1637
|
+
const base = normalizeBase(options.assetBaseUrl);
|
|
1638
|
+
const entry = entryFor(base);
|
|
1639
|
+
if (options.respectSaveData === true && saveDataOn()) return Promise.resolve(entry.status);
|
|
1640
|
+
if ((entry.status === "loading" || entry.status === "ready") && entry.promise !== null) return entry.promise;
|
|
1641
|
+
if (options.idle === true) return entry.scheduled?.promise ?? schedule(entry, base);
|
|
1642
|
+
return begin(entry, base);
|
|
1643
|
+
}
|
|
1644
|
+
function getLivenessAssetsStatus(assetBaseUrl) {
|
|
1645
|
+
return registry().get(normalizeBase(assetBaseUrl))?.status ?? "idle";
|
|
1646
|
+
}
|
|
1647
|
+
function subscribeLivenessAssets(listener, assetBaseUrl) {
|
|
1648
|
+
const entry = entryFor(normalizeBase(assetBaseUrl));
|
|
1649
|
+
entry.listeners.add(listener);
|
|
1650
|
+
return () => {
|
|
1651
|
+
entry.listeners.delete(listener);
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
function releaseLivenessAssets(assetBaseUrl) {
|
|
1655
|
+
const entry = registry().get(normalizeBase(assetBaseUrl));
|
|
1656
|
+
if (entry === void 0) return;
|
|
1657
|
+
entry.scheduled?.settle("idle");
|
|
1658
|
+
entry.detector?.destroy();
|
|
1659
|
+
entry.detector = null;
|
|
1660
|
+
entry.detectorPromise = null;
|
|
1661
|
+
entry.promise = null;
|
|
1662
|
+
setStatus(entry, "idle");
|
|
1663
|
+
}
|
|
1664
|
+
function nonOwningView(shared) {
|
|
1665
|
+
return {
|
|
1666
|
+
get tier() {
|
|
1667
|
+
return shared.tier;
|
|
1668
|
+
},
|
|
1669
|
+
get fallbackReason() {
|
|
1670
|
+
return shared.fallbackReason;
|
|
1671
|
+
},
|
|
1672
|
+
detect: (...args) => shared.detect(...args),
|
|
1673
|
+
destroy: () => {
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
function preloadedDetectorFactory(assetBaseUrl) {
|
|
1678
|
+
const base = normalizeBase(assetBaseUrl);
|
|
1679
|
+
const entry = registry().get(base);
|
|
1680
|
+
if (entry === void 0 || entry.detector === null && entry.detectorPromise === null && entry.scheduled === null) {
|
|
1681
|
+
return void 0;
|
|
1682
|
+
}
|
|
1683
|
+
return async () => {
|
|
1684
|
+
if (entry.scheduled !== null) void begin(entry, base);
|
|
1685
|
+
let waitingOn = entry.promise;
|
|
1686
|
+
while (entry.detector === null && waitingOn !== null) {
|
|
1687
|
+
await waitingOn;
|
|
1688
|
+
if (entry.promise === waitingOn) break;
|
|
1689
|
+
waitingOn = entry.promise;
|
|
1690
|
+
}
|
|
1691
|
+
return entry.detector !== null ? nonOwningView(entry.detector) : createDetector({ v3AssetBaseUrl: base });
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// src/capture/wake-lock.ts
|
|
1696
|
+
function wakeLockApi() {
|
|
1697
|
+
if (typeof navigator === "undefined" || !("wakeLock" in navigator)) return null;
|
|
1698
|
+
const api = navigator.wakeLock;
|
|
1699
|
+
return typeof api?.request === "function" ? api : null;
|
|
1700
|
+
}
|
|
1701
|
+
function isVisible() {
|
|
1702
|
+
return typeof document === "undefined" || document.visibilityState === "visible";
|
|
1703
|
+
}
|
|
1704
|
+
function releaseQuietly(sentinel) {
|
|
1705
|
+
try {
|
|
1706
|
+
void sentinel.release().catch(() => void 0);
|
|
1707
|
+
} catch {
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
function createScreenWakeLock(options = {}) {
|
|
1711
|
+
if (options.enabled === false) return { setActive: () => void 0 };
|
|
1712
|
+
let wanted = false;
|
|
1713
|
+
let sentinel = null;
|
|
1714
|
+
let inFlight = false;
|
|
1715
|
+
let listening = false;
|
|
1716
|
+
const acquire = () => {
|
|
1717
|
+
if (!wanted || inFlight || sentinel !== null && !sentinel.released) return;
|
|
1718
|
+
const api = wakeLockApi();
|
|
1719
|
+
if (api === null || !isVisible()) return;
|
|
1720
|
+
let request;
|
|
1721
|
+
try {
|
|
1722
|
+
request = api.request("screen");
|
|
1723
|
+
} catch {
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
inFlight = true;
|
|
1727
|
+
request.then(
|
|
1728
|
+
(granted) => {
|
|
1729
|
+
inFlight = false;
|
|
1730
|
+
if (!wanted) {
|
|
1731
|
+
releaseQuietly(granted);
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1734
|
+
sentinel = granted;
|
|
1735
|
+
granted.addEventListener?.("release", () => {
|
|
1736
|
+
if (sentinel === granted) sentinel = null;
|
|
1737
|
+
});
|
|
1738
|
+
},
|
|
1739
|
+
() => {
|
|
1740
|
+
inFlight = false;
|
|
1741
|
+
}
|
|
1742
|
+
);
|
|
1743
|
+
};
|
|
1744
|
+
const onVisibilityChange = () => {
|
|
1745
|
+
if (isVisible()) acquire();
|
|
1746
|
+
};
|
|
1747
|
+
const listen = (on) => {
|
|
1748
|
+
if (typeof document === "undefined" || on === listening) return;
|
|
1749
|
+
listening = on;
|
|
1750
|
+
if (on) document.addEventListener("visibilitychange", onVisibilityChange);
|
|
1751
|
+
else document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
1752
|
+
};
|
|
1753
|
+
return {
|
|
1754
|
+
setActive: (active) => {
|
|
1755
|
+
wanted = active;
|
|
1756
|
+
if (active) {
|
|
1757
|
+
if (wakeLockApi() === null) return;
|
|
1758
|
+
listen(true);
|
|
1759
|
+
acquire();
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1762
|
+
listen(false);
|
|
1763
|
+
if (sentinel !== null) {
|
|
1764
|
+
const held = sentinel;
|
|
1765
|
+
sentinel = null;
|
|
1766
|
+
releaseQuietly(held);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1488
1772
|
// src/capture/create-liveness-capture.ts
|
|
1489
1773
|
function mapGuidanceHint(key) {
|
|
1490
1774
|
switch (key) {
|
|
@@ -1548,12 +1832,22 @@ function createLivenessCaptureInternal(options, internals = {}) {
|
|
|
1548
1832
|
const now = internals.now ?? (() => performance.now());
|
|
1549
1833
|
const emitter = createEmitter();
|
|
1550
1834
|
const degraded = !isMediaRecorderSupported();
|
|
1835
|
+
const hostSeams = internals.engineOptions;
|
|
1836
|
+
const preloadAware = hostSeams?.createDetector === void 0 && hostSeams?.createWorker === void 0 ? {
|
|
1837
|
+
createDetector: () => {
|
|
1838
|
+
const warmed = preloadedDetectorFactory(options.assetBaseUrl);
|
|
1839
|
+
return warmed !== void 0 ? warmed() : createDetector(
|
|
1840
|
+
options.assetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.assetBaseUrl } : {}
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
} : {};
|
|
1551
1844
|
const engineOptions = {
|
|
1552
1845
|
video: options.video,
|
|
1553
1846
|
mode: degraded ? "photo" : "clip",
|
|
1554
1847
|
...options.facing !== void 0 ? { facingMode: options.facing } : {},
|
|
1555
1848
|
...options.assetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.assetBaseUrl } : {},
|
|
1556
|
-
...internals.engineOptions ?? {}
|
|
1849
|
+
...internals.engineOptions ?? {},
|
|
1850
|
+
...preloadAware
|
|
1557
1851
|
};
|
|
1558
1852
|
const engine = internals.createEngine?.(engineOptions) ?? new CaptureSessionV3(engineOptions);
|
|
1559
1853
|
let state = "idle";
|
|
@@ -1571,8 +1865,10 @@ function createLivenessCaptureInternal(options, internals = {}) {
|
|
|
1571
1865
|
frameHeight = options.video.videoHeight;
|
|
1572
1866
|
}
|
|
1573
1867
|
};
|
|
1868
|
+
const wakeLock = createScreenWakeLock({ enabled: options.keepScreenAwake !== false });
|
|
1574
1869
|
const setState = (next) => {
|
|
1575
1870
|
state = next;
|
|
1871
|
+
wakeLock.setActive(next === "starting" || next === "gating" || next === "recording");
|
|
1576
1872
|
emitter.emit("state", next);
|
|
1577
1873
|
};
|
|
1578
1874
|
const emitGate = () => emitter.emit("gate", { progress, hint, faceDetected });
|
|
@@ -1629,7 +1925,12 @@ function createLivenessCaptureInternal(options, internals = {}) {
|
|
|
1629
1925
|
height: options.video.videoHeight || frameHeight,
|
|
1630
1926
|
...clipMs !== void 0 ? { clipMs } : {},
|
|
1631
1927
|
captureChannel: detectIsMobile() ? "mobile" : "desktop",
|
|
1632
|
-
...degraded ? { degraded: "NO_RECORDER" } : {}
|
|
1928
|
+
...degraded ? { degraded: "NO_RECORDER" } : {},
|
|
1929
|
+
engine: "v3",
|
|
1930
|
+
// Face-capture timing: the engine's marks, forwarded flat (absent → absent).
|
|
1931
|
+
...artifacts.timing?.cameraReadyMs !== void 0 ? { cameraReadyMs: artifacts.timing.cameraReadyMs } : {},
|
|
1932
|
+
...artifacts.timing?.faceAcquireMs !== void 0 ? { faceAcquireMs: artifacts.timing.faceAcquireMs } : {},
|
|
1933
|
+
...artifacts.timing?.captureMs !== void 0 ? { captureMs: artifacts.timing.captureMs } : {}
|
|
1633
1934
|
});
|
|
1634
1935
|
}),
|
|
1635
1936
|
engine.on("error", ({ kind, retryable }) => {
|
|
@@ -1641,11 +1942,12 @@ function createLivenessCaptureInternal(options, internals = {}) {
|
|
|
1641
1942
|
return state;
|
|
1642
1943
|
},
|
|
1643
1944
|
on: (event, handler) => emitter.on(event, handler),
|
|
1644
|
-
start: () => engine.start(),
|
|
1945
|
+
start: (startOptions) => engine.start(startOptions?.facing !== void 0 ? { facingMode: startOptions.facing } : {}),
|
|
1645
1946
|
retry: () => engine.retry(),
|
|
1646
1947
|
switchCamera: () => engine.switchCamera(),
|
|
1647
1948
|
destroy: () => {
|
|
1648
1949
|
for (const unsubscribe of unsubscribes) unsubscribe();
|
|
1950
|
+
wakeLock.setActive(false);
|
|
1649
1951
|
engine.destroy();
|
|
1650
1952
|
emitter.clear();
|
|
1651
1953
|
}
|
|
@@ -1661,6 +1963,52 @@ function createLivenessCapture(options) {
|
|
|
1661
1963
|
return createLivenessCaptureInternal(options).handle;
|
|
1662
1964
|
}
|
|
1663
1965
|
|
|
1966
|
+
// src/capture/camera-probe.ts
|
|
1967
|
+
async function permissionStatus() {
|
|
1968
|
+
try {
|
|
1969
|
+
if (typeof navigator === "undefined" || typeof navigator.permissions?.query !== "function") return null;
|
|
1970
|
+
return await navigator.permissions.query({ name: "camera" });
|
|
1971
|
+
} catch {
|
|
1972
|
+
return null;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
function asState(state) {
|
|
1976
|
+
return state === "granted" || state === "prompt" || state === "denied" ? state : "unknown";
|
|
1977
|
+
}
|
|
1978
|
+
async function queryCameraPermission() {
|
|
1979
|
+
const status = await permissionStatus();
|
|
1980
|
+
return status === null ? "unknown" : asState(status.state);
|
|
1981
|
+
}
|
|
1982
|
+
function subscribeCameraPermission(listener) {
|
|
1983
|
+
let stopped = false;
|
|
1984
|
+
let status = null;
|
|
1985
|
+
const onChange = () => {
|
|
1986
|
+
if (!stopped && status !== null) listener(asState(status.state));
|
|
1987
|
+
};
|
|
1988
|
+
void permissionStatus().then((resolved) => {
|
|
1989
|
+
if (stopped) return;
|
|
1990
|
+
status = resolved;
|
|
1991
|
+
listener(resolved === null ? "unknown" : asState(resolved.state));
|
|
1992
|
+
resolved?.addEventListener?.("change", onChange);
|
|
1993
|
+
});
|
|
1994
|
+
return () => {
|
|
1995
|
+
stopped = true;
|
|
1996
|
+
status?.removeEventListener?.("change", onChange);
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
async function hasMultipleCameras() {
|
|
2000
|
+
if (typeof navigator === "undefined") return false;
|
|
2001
|
+
const ua = String(navigator.userAgent ?? "");
|
|
2002
|
+
if (/Android|iPhone|iPad|iPod/i.test(ua)) return true;
|
|
2003
|
+
if (/Macintosh/.test(ua) && (navigator.maxTouchPoints ?? 0) > 1) return true;
|
|
2004
|
+
try {
|
|
2005
|
+
const devices = await navigator.mediaDevices?.enumerateDevices?.() ?? [];
|
|
2006
|
+
return devices.filter((device) => device.kind === "videoinput").length >= 2;
|
|
2007
|
+
} catch {
|
|
2008
|
+
return false;
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
|
|
1664
2012
|
// src/client/errors.ts
|
|
1665
2013
|
var KNOWN_CODES = [
|
|
1666
2014
|
// auth / addressing (session-token.guard, controllers' own-session guards)
|
|
@@ -2204,7 +2552,12 @@ function buildLivenessSubmit(deps, clip, bareMime) {
|
|
|
2204
2552
|
deps.onPhase("checking");
|
|
2205
2553
|
return client.submitCheck(
|
|
2206
2554
|
"liveness",
|
|
2207
|
-
{
|
|
2555
|
+
{
|
|
2556
|
+
clipKey,
|
|
2557
|
+
attestation,
|
|
2558
|
+
captureChannel: deps.artifacts.captureChannel,
|
|
2559
|
+
capture: captureInputOf(deps.artifacts)
|
|
2560
|
+
},
|
|
2208
2561
|
{ idempotencyKey }
|
|
2209
2562
|
);
|
|
2210
2563
|
},
|
|
@@ -2215,6 +2568,17 @@ function buildLivenessSubmit(deps, clip, bareMime) {
|
|
|
2215
2568
|
}
|
|
2216
2569
|
);
|
|
2217
2570
|
}
|
|
2571
|
+
function captureInputOf(artifacts) {
|
|
2572
|
+
return {
|
|
2573
|
+
...artifacts.engine !== void 0 ? { engine: artifacts.engine } : {},
|
|
2574
|
+
width: artifacts.width,
|
|
2575
|
+
height: artifacts.height,
|
|
2576
|
+
...artifacts.clipMs !== void 0 ? { clipMs: artifacts.clipMs } : {},
|
|
2577
|
+
...artifacts.cameraReadyMs !== void 0 ? { cameraReadyMs: artifacts.cameraReadyMs } : {},
|
|
2578
|
+
...artifacts.faceAcquireMs !== void 0 ? { faceAcquireMs: artifacts.faceAcquireMs } : {},
|
|
2579
|
+
...artifacts.captureMs !== void 0 ? { captureMs: artifacts.captureMs } : {}
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2218
2582
|
function buildFaceMatchSubmit(deps, keyframe) {
|
|
2219
2583
|
let imageKey = null;
|
|
2220
2584
|
return () => runSubmission(deps, async (client, idempotencyKey) => {
|
|
@@ -2413,7 +2777,7 @@ function createVerificationInternal(options, internals) {
|
|
|
2413
2777
|
}
|
|
2414
2778
|
emitter.emit("result", result);
|
|
2415
2779
|
};
|
|
2416
|
-
let
|
|
2780
|
+
let registry2 = null;
|
|
2417
2781
|
let registryFailed = false;
|
|
2418
2782
|
let registryUnbound = false;
|
|
2419
2783
|
const submitRegistry = () => runSubmission(
|
|
@@ -2448,8 +2812,8 @@ function createVerificationInternal(options, internals) {
|
|
|
2448
2812
|
if (view !== null && isTerminalView(view)) emitResult(mapSessionResult(view, { autoRetry }));
|
|
2449
2813
|
};
|
|
2450
2814
|
const startRegistry = () => {
|
|
2451
|
-
|
|
2452
|
-
return
|
|
2815
|
+
registry2 ?? (registry2 = runRegistry());
|
|
2816
|
+
return registry2;
|
|
2453
2817
|
};
|
|
2454
2818
|
const getAttestation = async () => {
|
|
2455
2819
|
const current = await ensureView();
|
|
@@ -2564,7 +2928,7 @@ function createVerificationInternal(options, internals) {
|
|
|
2564
2928
|
return inner.state;
|
|
2565
2929
|
},
|
|
2566
2930
|
on: (event, handler) => inner.on(event, handler),
|
|
2567
|
-
start: () => inner.start(),
|
|
2931
|
+
start: (startOptions) => inner.start(startOptions),
|
|
2568
2932
|
retry: () => inner.retry(),
|
|
2569
2933
|
switchCamera: () => inner.switchCamera(),
|
|
2570
2934
|
destroy: () => {
|
|
@@ -2643,7 +3007,14 @@ function openHosted(hostedUrl, opts) {
|
|
|
2643
3007
|
SEKYRO_EVENTS,
|
|
2644
3008
|
createLivenessCapture,
|
|
2645
3009
|
createVerification,
|
|
3010
|
+
getLivenessAssetsStatus,
|
|
3011
|
+
hasMultipleCameras,
|
|
2646
3012
|
isKnownCode,
|
|
2647
|
-
openHosted
|
|
3013
|
+
openHosted,
|
|
3014
|
+
preloadLivenessAssets,
|
|
3015
|
+
queryCameraPermission,
|
|
3016
|
+
releaseLivenessAssets,
|
|
3017
|
+
subscribeCameraPermission,
|
|
3018
|
+
subscribeLivenessAssets
|
|
2648
3019
|
});
|
|
2649
3020
|
//# sourceMappingURL=index.cjs.map
|