@sekyro/web 0.1.0-rc.4 → 0.1.0-rc.6

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +44 -0
  3. package/dist/{capture-session-v3-uXJr1Wlm.d.ts → capture-session-v3-Okq0EQHG.d.ts} +9 -2
  4. package/dist/{capture-session-v3-DEInCOUg.d.cts → capture-session-v3-UQU_WjO2.d.cts} +9 -2
  5. package/dist/{chunk-TBANUT3X.js → chunk-R3SJQHYU.js} +296 -8
  6. package/dist/chunk-R3SJQHYU.js.map +1 -0
  7. package/dist/{chunk-EWRNPKTR.js → chunk-TG5IE552.js} +7 -2
  8. package/dist/{chunk-EWRNPKTR.js.map → chunk-TG5IE552.js.map} +1 -1
  9. package/dist/client/index.cjs.map +1 -1
  10. package/dist/client/index.d.cts +1 -1
  11. package/dist/client/index.d.ts +1 -1
  12. package/dist/core/index.cjs +6 -1
  13. package/dist/core/index.cjs.map +1 -1
  14. package/dist/core/index.d.cts +2 -2
  15. package/dist/core/index.d.ts +2 -2
  16. package/dist/core/index.js +1 -1
  17. package/dist/{create-verification-Dkb2tXuY.d.cts → create-verification-BKc9NFQj.d.cts} +92 -8
  18. package/dist/{create-verification-Ce1y5kJR.d.ts → create-verification-DjcMZHAQ.d.ts} +92 -8
  19. package/dist/{hosted-client-DWzCRYJK.d.cts → hosted-client-DJXq_CB9.d.cts} +29 -1
  20. package/dist/{hosted-client-DWzCRYJK.d.ts → hosted-client-DJXq_CB9.d.ts} +29 -1
  21. package/dist/index.cjs +341 -9
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +25 -3
  24. package/dist/index.d.ts +25 -3
  25. package/dist/index.js +61 -4
  26. package/dist/index.js.map +1 -1
  27. package/dist/internal/index.cjs +218 -7
  28. package/dist/internal/index.cjs.map +1 -1
  29. package/dist/internal/index.d.cts +4 -4
  30. package/dist/internal/index.d.ts +4 -4
  31. package/dist/internal/index.js +12 -2
  32. package/dist/internal/index.js.map +1 -1
  33. package/package.json +1 -1
  34. package/dist/chunk-TBANUT3X.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
 
@@ -1102,10 +1109,15 @@ var CaptureSessionV3 = class {
1102
1109
  handler(payload);
1103
1110
  }
1104
1111
  }
1105
- async start() {
1112
+ async start(options = {}) {
1113
+ if (options.facingMode !== void 0) this.facing = options.facingMode;
1106
1114
  this.attempt += 1;
1107
1115
  await this.run(this.attempt);
1108
1116
  }
1117
+ /** The camera the current/next attempt requests (start option, switchCamera toggles). */
1118
+ get facingMode() {
1119
+ return this.facing;
1120
+ }
1109
1121
  /** Full reset + camera restart — the documented recovery path (v1 parity). */
1110
1122
  retry() {
1111
1123
  this.teardownAttempt();
@@ -1513,6 +1525,250 @@ function createEmitter() {
1513
1525
  };
1514
1526
  }
1515
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
+
1516
1772
  // src/capture/create-liveness-capture.ts
1517
1773
  function mapGuidanceHint(key) {
1518
1774
  switch (key) {
@@ -1576,12 +1832,22 @@ function createLivenessCaptureInternal(options, internals = {}) {
1576
1832
  const now = internals.now ?? (() => performance.now());
1577
1833
  const emitter = createEmitter();
1578
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
+ } : {};
1579
1844
  const engineOptions = {
1580
1845
  video: options.video,
1581
1846
  mode: degraded ? "photo" : "clip",
1582
1847
  ...options.facing !== void 0 ? { facingMode: options.facing } : {},
1583
1848
  ...options.assetBaseUrl !== void 0 ? { v3AssetBaseUrl: options.assetBaseUrl } : {},
1584
- ...internals.engineOptions ?? {}
1849
+ ...internals.engineOptions ?? {},
1850
+ ...preloadAware
1585
1851
  };
1586
1852
  const engine = internals.createEngine?.(engineOptions) ?? new CaptureSessionV3(engineOptions);
1587
1853
  let state = "idle";
@@ -1599,8 +1865,10 @@ function createLivenessCaptureInternal(options, internals = {}) {
1599
1865
  frameHeight = options.video.videoHeight;
1600
1866
  }
1601
1867
  };
1868
+ const wakeLock = createScreenWakeLock({ enabled: options.keepScreenAwake !== false });
1602
1869
  const setState = (next) => {
1603
1870
  state = next;
1871
+ wakeLock.setActive(next === "starting" || next === "gating" || next === "recording");
1604
1872
  emitter.emit("state", next);
1605
1873
  };
1606
1874
  const emitGate = () => emitter.emit("gate", { progress, hint, faceDetected });
@@ -1674,11 +1942,12 @@ function createLivenessCaptureInternal(options, internals = {}) {
1674
1942
  return state;
1675
1943
  },
1676
1944
  on: (event, handler) => emitter.on(event, handler),
1677
- start: () => engine.start(),
1945
+ start: (startOptions) => engine.start(startOptions?.facing !== void 0 ? { facingMode: startOptions.facing } : {}),
1678
1946
  retry: () => engine.retry(),
1679
1947
  switchCamera: () => engine.switchCamera(),
1680
1948
  destroy: () => {
1681
1949
  for (const unsubscribe of unsubscribes) unsubscribe();
1950
+ wakeLock.setActive(false);
1682
1951
  engine.destroy();
1683
1952
  emitter.clear();
1684
1953
  }
@@ -1694,6 +1963,52 @@ function createLivenessCapture(options) {
1694
1963
  return createLivenessCaptureInternal(options).handle;
1695
1964
  }
1696
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
+
1697
2012
  // src/client/errors.ts
1698
2013
  var KNOWN_CODES = [
1699
2014
  // auth / addressing (session-token.guard, controllers' own-session guards)
@@ -2462,7 +2777,7 @@ function createVerificationInternal(options, internals) {
2462
2777
  }
2463
2778
  emitter.emit("result", result);
2464
2779
  };
2465
- let registry = null;
2780
+ let registry2 = null;
2466
2781
  let registryFailed = false;
2467
2782
  let registryUnbound = false;
2468
2783
  const submitRegistry = () => runSubmission(
@@ -2497,8 +2812,8 @@ function createVerificationInternal(options, internals) {
2497
2812
  if (view !== null && isTerminalView(view)) emitResult(mapSessionResult(view, { autoRetry }));
2498
2813
  };
2499
2814
  const startRegistry = () => {
2500
- registry ?? (registry = runRegistry());
2501
- return registry;
2815
+ registry2 ?? (registry2 = runRegistry());
2816
+ return registry2;
2502
2817
  };
2503
2818
  const getAttestation = async () => {
2504
2819
  const current = await ensureView();
@@ -2613,7 +2928,7 @@ function createVerificationInternal(options, internals) {
2613
2928
  return inner.state;
2614
2929
  },
2615
2930
  on: (event, handler) => inner.on(event, handler),
2616
- start: () => inner.start(),
2931
+ start: (startOptions) => inner.start(startOptions),
2617
2932
  retry: () => inner.retry(),
2618
2933
  switchCamera: () => inner.switchCamera(),
2619
2934
  destroy: () => {
@@ -2627,6 +2942,16 @@ function createVerificationInternal(options, internals) {
2627
2942
  consent: () => ensureConsent(),
2628
2943
  startRegistry,
2629
2944
  liveness,
2945
+ voiceClips: async () => {
2946
+ if (destroyed) return null;
2947
+ try {
2948
+ const current = await ensureView();
2949
+ if (destroyed) return null;
2950
+ return current.voice ?? null;
2951
+ } catch {
2952
+ return null;
2953
+ }
2954
+ },
2630
2955
  on: (event, handler) => emitter.on(event, handler),
2631
2956
  destroy: () => {
2632
2957
  destroyed = true;
@@ -2692,7 +3017,14 @@ function openHosted(hostedUrl, opts) {
2692
3017
  SEKYRO_EVENTS,
2693
3018
  createLivenessCapture,
2694
3019
  createVerification,
3020
+ getLivenessAssetsStatus,
3021
+ hasMultipleCameras,
2695
3022
  isKnownCode,
2696
- openHosted
3023
+ openHosted,
3024
+ preloadLivenessAssets,
3025
+ queryCameraPermission,
3026
+ releaseLivenessAssets,
3027
+ subscribeCameraPermission,
3028
+ subscribeLivenessAssets
2697
3029
  });
2698
3030
  //# sourceMappingURL=index.cjs.map