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

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 (118) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/LICENSE +25 -16
  3. package/README.md +6 -8
  4. package/THIRD-PARTY-NOTICES.txt +154 -0
  5. package/dist/background-monitor-A7FKJOAG.js +1 -0
  6. package/dist/capture-session-doc-YBE3UMJJ.js +1 -0
  7. package/dist/{capture-session-v3-Okq0EQHG.d.ts → capture-session-v3-CQK-G-qX.d.ts} +72 -3
  8. package/dist/{capture-session-v3-UQU_WjO2.d.cts → capture-session-v3-Dvob-uH2.d.cts} +72 -3
  9. package/dist/chunk-4RS2YIL4.js +1 -0
  10. package/dist/chunk-7V2M6Y2W.js +1 -0
  11. package/dist/chunk-7ZCZY3ZP.js +1 -0
  12. package/dist/chunk-A3FUGHAH.js +2 -0
  13. package/dist/chunk-BPE5NSQ2.js +1 -0
  14. package/dist/chunk-CJHMFPL6.js +1 -0
  15. package/dist/chunk-DFTCAWMP.js +1 -0
  16. package/dist/chunk-DK5YPBDQ.js +1 -0
  17. package/dist/chunk-DQWW4OCK.js +1 -0
  18. package/dist/chunk-FHJ7HO3M.js +1 -0
  19. package/dist/chunk-FPXL63KZ.js +1 -0
  20. package/dist/chunk-G6LKLN4U.js +1 -0
  21. package/dist/chunk-GI3UFGRQ.js +1 -0
  22. package/dist/chunk-LQ7C44JG.js +1 -0
  23. package/dist/chunk-M43ET4NJ.js +1 -0
  24. package/dist/chunk-MXWLCDFX.js +1 -0
  25. package/dist/chunk-MY6EXEL2.js +1 -0
  26. package/dist/chunk-O5WKNZ2C.js +1 -0
  27. package/dist/chunk-OKWG3PVH.js +1 -0
  28. package/dist/chunk-POIQGEZM.js +1 -0
  29. package/dist/chunk-RI3E7GBN.js +1 -0
  30. package/dist/chunk-RNRFHUJA.js +1 -0
  31. package/dist/chunk-RVHAKRAI.js +1 -0
  32. package/dist/chunk-SMKCBUJL.js +1 -0
  33. package/dist/chunk-SXZCL5NO.js +1 -0
  34. package/dist/chunk-TDLIXFDT.js +1 -0
  35. package/dist/chunk-XLN5OT25.js +1 -0
  36. package/dist/chunk-YDQBVWXG.js +1 -0
  37. package/dist/chunk-YPG7CB4O.js +1 -0
  38. package/dist/client/index.cjs +1 -357
  39. package/dist/client/index.d.cts +13 -3
  40. package/dist/client/index.d.ts +13 -3
  41. package/dist/client/index.js +1 -15
  42. package/dist/core/index.cjs +1 -1639
  43. package/dist/core/index.d.cts +3 -3
  44. package/dist/core/index.d.ts +3 -3
  45. package/dist/core/index.js +1 -139
  46. package/dist/core/ocr/worker.bundle.js +42 -0
  47. package/dist/core/ocr/worker.cjs +42 -0
  48. package/dist/core/ocr/worker.d.cts +84 -0
  49. package/dist/core/ocr/worker.d.ts +84 -0
  50. package/dist/core/ocr/worker.js +41 -0
  51. package/dist/core/v3/detect-worker.cjs +1 -392
  52. package/dist/core/v3/detect-worker.d.cts +1 -1
  53. package/dist/core/v3/detect-worker.d.ts +1 -1
  54. package/dist/core/v3/detect-worker.js +1 -13
  55. package/dist/create-document-capture-T4APHJMY.js +1 -0
  56. package/dist/create-portrait-capture-B57GE5FU.js +1 -0
  57. package/dist/create-verification-C0AVIcVW.d.ts +2787 -0
  58. package/dist/create-verification-DoFpgzok.d.cts +2787 -0
  59. package/dist/{detect-worker-CKXHlXx9.d.cts → detect-worker-DclNxkZT.d.cts} +6 -0
  60. package/dist/{detect-worker-CKXHlXx9.d.ts → detect-worker-DclNxkZT.d.ts} +6 -0
  61. package/dist/detector-scanic-YCDNLCGV.js +1 -0
  62. package/dist/icao-gate-D8qm1O2t.d.cts +1655 -0
  63. package/dist/icao-gate-D8qm1O2t.d.ts +1655 -0
  64. package/dist/icao-gate-PWBMKRGC.js +1 -0
  65. package/dist/icao-tuning-4GGVX6B4.js +1 -0
  66. package/dist/index.cjs +1 -3030
  67. package/dist/index.d.cts +52 -6
  68. package/dist/index.d.ts +52 -6
  69. package/dist/index.js +1 -85
  70. package/dist/internal/index.cjs +1 -4439
  71. package/dist/internal/index.d.cts +352 -312
  72. package/dist/internal/index.d.ts +352 -312
  73. package/dist/internal/index.js +1 -657
  74. package/dist/keyframe-rank-YAJLYJYF.js +1 -0
  75. package/dist/landmarker-KB7LVXAU.js +1 -0
  76. package/dist/mrz-band-VYHOTDHY.js +1 -0
  77. package/dist/plan-runner-UBK4B4CG.js +1 -0
  78. package/dist/popup/index.cjs +1 -76
  79. package/dist/popup/index.js +1 -11
  80. package/dist/scanic-ml-stub-KW7IZHUT.js +1 -0
  81. package/dist/segmenter-USLNBX65.js +1 -0
  82. package/dist/turnstile-FATNZ4AG.js +1 -0
  83. package/package.json +25 -10
  84. package/sbom.cdx.json +195 -0
  85. package/dist/chunk-MWNFSNMP.js +0 -52
  86. package/dist/chunk-MWNFSNMP.js.map +0 -1
  87. package/dist/chunk-NILNKLK6.js +0 -50
  88. package/dist/chunk-NILNKLK6.js.map +0 -1
  89. package/dist/chunk-NWF46H7K.js +0 -359
  90. package/dist/chunk-NWF46H7K.js.map +0 -1
  91. package/dist/chunk-R3SJQHYU.js +0 -1173
  92. package/dist/chunk-R3SJQHYU.js.map +0 -1
  93. package/dist/chunk-TG5IE552.js +0 -1128
  94. package/dist/chunk-TG5IE552.js.map +0 -1
  95. package/dist/chunk-WY6U7AIK.js +0 -193
  96. package/dist/chunk-WY6U7AIK.js.map +0 -1
  97. package/dist/chunk-YVFF4Q4G.js +0 -327
  98. package/dist/chunk-YVFF4Q4G.js.map +0 -1
  99. package/dist/client/index.cjs.map +0 -1
  100. package/dist/client/index.js.map +0 -1
  101. package/dist/core/index.cjs.map +0 -1
  102. package/dist/core/index.js.map +0 -1
  103. package/dist/core/v3/detect-worker.cjs.map +0 -1
  104. package/dist/core/v3/detect-worker.js.map +0 -1
  105. package/dist/create-verification-BKc9NFQj.d.cts +0 -455
  106. package/dist/create-verification-DjcMZHAQ.d.ts +0 -455
  107. package/dist/detector-scanic-4C6IEW2K.js +0 -787
  108. package/dist/detector-scanic-4C6IEW2K.js.map +0 -1
  109. package/dist/hosted-client-DJXq_CB9.d.cts +0 -449
  110. package/dist/hosted-client-DJXq_CB9.d.ts +0 -449
  111. package/dist/index.cjs.map +0 -1
  112. package/dist/index.js.map +0 -1
  113. package/dist/internal/index.cjs.map +0 -1
  114. package/dist/internal/index.js.map +0 -1
  115. package/dist/popup/index.cjs.map +0 -1
  116. package/dist/popup/index.js.map +0 -1
  117. package/dist/scanic-ml-stub-S2OZ6HEF.js +0 -10
  118. package/dist/scanic-ml-stub-S2OZ6HEF.js.map +0 -1
@@ -0,0 +1 @@
1
+ import{e as U}from"./chunk-A3FUGHAH.js";import{a as V,b as O,e as A,f as w,i as F}from"./chunk-MY6EXEL2.js";import{o as H}from"./chunk-M43ET4NJ.js";import"./chunk-CJHMFPL6.js";import"./chunk-RI3E7GBN.js";import{A as L,F as T,i as M}from"./chunk-YPG7CB4O.js";import"./chunk-SMKCBUJL.js";import"./chunk-MXWLCDFX.js";import{x}from"./chunk-RNRFHUJA.js";import"./chunk-G6LKLN4U.js";import"./chunk-TDLIXFDT.js";import"./chunk-7ZCZY3ZP.js";import"./chunk-BPE5NSQ2.js";import"./chunk-GI3UFGRQ.js";import{j as I}from"./chunk-YDQBVWXG.js";import"./chunk-RVHAKRAI.js";import"./chunk-7V2M6Y2W.js";import"./chunk-DQWW4OCK.js";import"./chunk-POIQGEZM.js";import"./chunk-FHJ7HO3M.js";import"./chunk-FPXL63KZ.js";import{g as B}from"./chunk-4RS2YIL4.js";import"./chunk-LQ7C44JG.js";import"./chunk-OKWG3PVH.js";import"./chunk-DFTCAWMP.js";import"./chunk-DK5YPBDQ.js";function _(t){let a=null,c=Promise.resolve(),u=!1,g=null,y=null,C=null,h=!1,m=()=>{a?.controller.handle.destroy(),a=null},k=()=>{let i=t.currentView(),s=i===null?null:w(i,{isMobile:t.isMobile()});return s?.kind==="portrait"?s:null},P=async(i,s)=>{if(t.destroyed())return;s.setFlowState("uploading"),u=!1;let d=null;try{await t.runner().submit("portrait_icao",async(n,r)=>{if(d===null){let p=await I(n,"image","image/jpeg",i.image.size,{engine:"portrait"});await n.uploadToSpaces(p.uploadUrl,i.image,"image/jpeg"),d=(await n.completeMedia(p.key)).key}s.setFlowState("checking");let l={media:{portrait:d},gate:B(i.gate),...i.tuningVersion!==void 0?{tuningVersion:i.tuningVersion}:{}};return n.submitCheck("portrait_icao",l,{idempotencyKey:r})},()=>{g=d})}catch{u=!0}if(s.setFlowState("done"),t.destroyed())return;let e=k();(e===null||e.phase!=="capture")&&a?.controller===s&&m(),t.runner().republish()},b=i=>t.runner().submit("face_match",(s,d)=>s.submitCheck("face_match",g!==null?{imageKey:g}:{},{idempotencyKey:d})).then(()=>{},()=>{u=!0,i()});return{bind:(i,s)=>{if(i.phase==="match")return m(),y!==i.attemptsRemaining&&!u&&(y=i.attemptsRemaining,b(s)),{...i,uploadFailed:u,resubmit:()=>(u=!1,y=i.attemptsRemaining,s(),b(s)),capture:()=>{throw new Error("the portrait was already accepted")}};y=null;let d=t.keptPortraitInput?.()??null;return d!==null&&(h||d!==C&&i.report===null&&i.reason===void 0&&!i.retakeOnly)?(h||(C=d,h=!0,t.runner().submit("portrait_icao",(e,n)=>e.submitCheck("portrait_icao",d,{idempotencyKey:n}),()=>{g=d.media.portrait}).catch(()=>{}).finally(()=>{h=!1,s()})),{kind:"processing",key:"processing",context:i.context,section:i.section,stalled:!1,refresh:()=>Promise.resolve()}):{...i,uploadFailed:u,resubmit:()=>Promise.resolve(),capture:e=>{if(a!==null&&a.video===e.video)return a.controller.handle;m(),u=!1;let n=H({video:e.video,tuning:i.tuning,tuningVersion:i.tuningVersion,...i.icao!=null?{policy:i.icao.policy}:{},...e.keepScreenAwake!==void 0?{keepScreenAwake:e.keepScreenAwake}:{},...e.assetBaseUrl!==void 0?{assetBaseUrl:e.assetBaseUrl}:{}},t.internals);return a={video:e.video,controller:n},n.handle.on("captured",r=>{c=c.then(()=>P(r,n)).catch(()=>{})}),n.handle}}},destroy:m}}function z(t){let a=t;if(typeof a.apiOrigin!="string"||typeof a.sessionToken!="string")return;let c=`${a.apiOrigin}/api/v2/hosted/capture-reports`,u=a.sessionToken;return g=>{try{fetch(c,{method:"POST",headers:{"content-type":"application/json","x-session-token":u},body:JSON.stringify({reports:g}),keepalive:!0}).catch(()=>{})}catch{}}}function j(t){let a=t.apiOrigin;return U("sdk",typeof a=="string"&&/staging|sandbox|localhost/.test(a)?"sandbox":"live")}function oe(t){let a=null,c=null,u=null,g=Promise.resolve(),y=()=>{u?.flow.handle.destroy(),u=null},C=async(e,n)=>{if(t.destroyed())return;n.setFlowState("uploading");let r=null,l,p=!1;try{await a.submit("doc_ocr",async(S,K)=>{if(r===null){let R=await I(S,"image","image/jpeg",e.image.size,{engine:"document"});await S.uploadToSpaces(R.uploadUrl,e.image,"image/jpeg"),r=(await S.completeMedia(R.key)).key}return n.setFlowState("checking"),l===void 0&&(l=n.takeDeviceRead(e)),S.submitCheck("doc_ocr",{imageKey:r,side:e.side,...l!==null?{deviceRead:l}:{}},{idempotencyKey:K})})}catch{p=!0}finally{n.setFlowState("done")}if(t.destroyed())return;if(p){n.settle(e.side,{ok:!1,issue:"technical"});return}let v=t.currentView(),o=v===null?null:w(v,{isMobile:t.isMobile()});if(o?.kind!=="document"){n.settle(e.side,{ok:!0}),u?.flow===n?y():n.handle.destroy();return}if(o.reason==="document_type_not_accepted"){c=null,u?.flow===n&&y(),a.republish();return}e.side==="single"||o.acceptedSides.includes(e.side)||o.reason==="side_missing"?(n.settle(e.side,{ok:!0}),c?.documentType!=="passport"&&n.handle.setSide(o.side)):n.settle(e.side,{ok:!1,issue:"reread"})},h=(e,n)=>({...e,issuer:c?.issuer??null,documentType:c?.documentType??null,captureSide:c===null?null:c.documentType==="passport"?"single":e.side,selectDocument:(r,l)=>{if(!O(e.documents).includes(r)||!V(e.documents,r).includes(l))throw new RangeError(`${l} from ${r} is not accepted`);(c?.issuer!==r||c.documentType!==l)&&y(),c={issuer:r,documentType:l},n()},capture:r=>{if(c===null)throw new Error("selectDocument() first");if(u!==null&&u.video===r.video)return u.flow.handle;y();let l=c,p=l.documentType==="passport"?"single":e.acceptedSides.length>0?e.side:void 0,v=x({video:r.video,documentType:l.documentType,issuer:l.issuer,...p!==void 0?{side:p}:{},...r.keepScreenAwake!==void 0?{keepScreenAwake:r.keepScreenAwake}:{},...r.liveMrz!==void 0?{liveMrz:r.liveMrz}:{}},{...t.documentInternals??{},...(()=>{let o=t.currentView()?.capture?.document,f=t.documentInternals?.blurRefuseSharpnessMax??o?.scan?.blurRefuseSharpnessMax;return{engineOptions:{camera:M(o?.camera),shutter:T(o?.scan),guideLock:L(o?.guide),...t.documentInternals?.engineOptions??{}},...typeof f=="number"&&Number.isFinite(f)&&f>=0&&f<=1?{blurRefuseSharpnessMax:f}:{}}})(),deferRead:!0,...(()=>{let o=t.currentView();if(o===null||t.documentInternals?.webOcr!==void 0)return{};let f=z(t.runtime.client);return{webOcr:{captureDocument:o.capture?.document,salt:o.sessionId,baseUrl:j(t.runtime.client),...f!==void 0?{reports:f}:{}}}})()});return u={video:r.video,flow:v},v.handle.on("captured",o=>{g=g.then(()=>C(o,v)).catch(()=>{})}),v.handle}}),m=null,k=null,P=new Set,b=t.runtime.client;t.runtime.client=new Proxy(b,{get(e,n){if(n==="submitCheck")return(k??e).submitCheck.bind(k??e);let r=Reflect.get(e,n);return typeof r=="function"?r.bind(e):r}});let D=()=>{m?.destroy(),m=null,k=null},i=e=>{let n=t.currentView(),r=n===null?null:w(n,{isMobile:t.isMobile()});if(n===null||r?.kind!=="liveness"||r.livePhoto?.icao!==!0)return;if(m===null){let o=A({tuning:n.capture?.icao??null,tuningVersion:n.capture?.tuningVersion??null,...r.icao!=null?{policy:r.icao.policy}:{},still:r.livePhoto.still,...e.assetBaseUrl!==void 0?{assetBaseUrl:e.assetBaseUrl}:{}},t.livePhotoInternals);m=o,k=o.decorate(b),o.onGate(f=>{for(let S of P)S(f)})}let l=m,p=t.baseCapture??{},v=p.engineOptions?.telemetry;return{...p,engineOptions:{...p.engineOptions??{},measureQuality:l.engineOptions.measureQuality,telemetry:o=>{v?.(o),l.engineOptions.telemetry(o)}}}},s=_({keptPortraitInput:()=>m?.lastPortraitInput()??null,runner:()=>a,currentView:t.currentView,destroyed:t.destroyed,isMobile:t.isMobile,...t.portraitInternals!==void 0?{internals:t.portraitInternals}:{}}),d=F({...t,bindDocument:h,bindPortrait:s.bind,onLivePhoto:e=>(P.add(e),()=>P.delete(e))});return a=d,{...d,captureInternals:i,destroy:()=>{y(),s.destroy(),D(),P.clear()}}}export{oe as createPlanRunner,j as sdkAssetBase};
@@ -1,76 +1 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/popup/index.ts
21
- var popup_exports = {};
22
- __export(popup_exports, {
23
- SEKYRO_EVENTS: () => SEKYRO_EVENTS,
24
- open: () => open,
25
- openHosted: () => openHosted
26
- });
27
- module.exports = __toCommonJS(popup_exports);
28
- var SEKYRO_EVENTS = [
29
- "session.completed",
30
- "session.failed",
31
- "session.review_pending",
32
- "session.expired",
33
- "session.cancelled"
34
- ];
35
- var WINDOW_NAME = "sekyro-verify";
36
- var WINDOW_FEATURES = "width=480,height=720,noopener=false";
37
- var EVENT_NAMES = new Set(SEKYRO_EVENTS);
38
- function isSekyroEvent(data) {
39
- if (data === null || typeof data !== "object") return false;
40
- const d = data;
41
- return d.source === "sekyro" && d.version === 1 && typeof d.sessionId === "string" && typeof d.event === "string" && EVENT_NAMES.has(d.event);
42
- }
43
- function open(opts) {
44
- const origin = new URL(opts.hostedUrl).origin;
45
- const popup = window.open(opts.hostedUrl, WINDOW_NAME, WINDOW_FEATURES);
46
- if (!popup) return { error: "popup_blocked" };
47
- const listener = (ev) => {
48
- if (ev.source !== popup) return;
49
- if (ev.origin !== origin) return;
50
- if (!isSekyroEvent(ev.data)) return;
51
- const { source, version, event, sessionId } = ev.data;
52
- opts.onEvent({ source, version, event, sessionId });
53
- };
54
- window.addEventListener("message", listener);
55
- return {
56
- close() {
57
- window.removeEventListener("message", listener);
58
- popup.close();
59
- }
60
- };
61
- }
62
- function openHosted(hostedUrl, opts) {
63
- if (opts.mode === "redirect") {
64
- window.location.assign(hostedUrl);
65
- return { close() {
66
- } };
67
- }
68
- return open({ hostedUrl, onEvent: opts.onEvent ?? (() => void 0) });
69
- }
70
- // Annotate the CommonJS export names for ESM import in node:
71
- 0 && (module.exports = {
72
- SEKYRO_EVENTS,
73
- open,
74
- openHosted
75
- });
76
- //# sourceMappingURL=index.cjs.map
1
+ "use strict";var s=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var f=(n,e)=>{for(var t in e)s(n,t,{get:e[t],enumerable:!0})},y=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of E(e))!l.call(n,o)&&o!==t&&s(n,o,{get:()=>e[o],enumerable:!(r=a(e,o))||r.enumerable});return n};var k=n=>y(s({},"__esModule",{value:!0}),n);var m={};f(m,{SEKYRO_EVENTS:()=>i,open:()=>p,openHosted:()=>x});module.exports=k(m);var i=["session.completed","session.failed","session.review_pending","session.expired","session.cancelled"],S="sekyro-verify",g="width=480,height=720,noopener=false",w=new Set(i);function O(n){if(n===null||typeof n!="object")return!1;let e=n;return e.source==="sekyro"&&e.version===1&&typeof e.sessionId=="string"&&typeof e.event=="string"&&w.has(e.event)}function p(n){let e=new URL(n.hostedUrl).origin,t=window.open(n.hostedUrl,S,g);if(!t)return{error:"popup_blocked"};let r=o=>{if(o.source!==t||o.origin!==e||!O(o.data))return;let{source:d,version:c,event:v,sessionId:u}=o.data;n.onEvent({source:d,version:c,event:v,sessionId:u})};return window.addEventListener("message",r),{close(){window.removeEventListener("message",r),t.close()}}}function x(n,e){return e.mode==="redirect"?(window.location.assign(n),{close(){}}):p({hostedUrl:n,onEvent:e.onEvent??(()=>{})})}0&&(module.exports={SEKYRO_EVENTS,open,openHosted});
@@ -1,11 +1 @@
1
- import {
2
- SEKYRO_EVENTS,
3
- open,
4
- openHosted
5
- } from "../chunk-NILNKLK6.js";
6
- export {
7
- SEKYRO_EVENTS,
8
- open,
9
- openHosted
10
- };
11
- //# sourceMappingURL=index.js.map
1
+ import{a,b,c}from"../chunk-SXZCL5NO.js";export{a as SEKYRO_EVENTS,b as open,c as openHosted};
@@ -0,0 +1 @@
1
+ function e(){throw new Error("scanic ML detector is not vendored (classical pipeline only)")}var t={};export{t as default,e as initMlDetector};
@@ -0,0 +1 @@
1
+ var c="selfie_segmenter.tflite";function l(t){if(t===void 0||t.length===0)return null;let r=t.length===1,n=t[0],s=Float32Array.from(n.getAsFloat32Array());if(!r)for(let e=0;e<s.length;e+=1)s[e]=1-s[e];return{data:s,width:n.width,height:n.height}}async function u(t){let r=t.replace(/\/+$/,""),n=await import("@mediapipe/tasks-vision"),s=await n.FilesetResolver.forVisionTasks(r),e=await n.ImageSegmenter.createFromOptions(s,{baseOptions:{modelAssetPath:`${r}/${c}`,delegate:"CPU"},runningMode:"VIDEO",outputConfidenceMasks:!0,outputCategoryMask:!1});return{segment(i,a){try{let o=e.segmentForVideo(i,a);try{return l(o.confidenceMasks)}finally{o.close?.()}}catch{return null}},close(){try{e.close()}catch{}}}}export{c as SEGMENTER_MODEL,u as createSelfieSegmenter,l as personMaskOf};
@@ -0,0 +1 @@
1
+ import{a,b,c,d,e}from"./chunk-O5WKNZ2C.js";export{b as TURNSTILE_EXECUTE_TIMEOUT_MS,a as TURNSTILE_SCRIPT_SRC,e as executeTurnstile,d as loadTurnstile,c as resetTurnstileForTests};
package/package.json CHANGED
@@ -1,17 +1,13 @@
1
1
  {
2
2
  "name": "@sekyro/web",
3
- "version": "0.1.0-rc.6",
4
- "description": "Sekyro web SDK — browser liveness capture, hosted-verification client and popup embed for Sekyro identity verification",
5
- "license": "UNLICENSED",
3
+ "version": "0.1.0-rc.8",
4
+ "description": "Sekyro AI web SDK \u2014 browser liveness capture, hosted-verification client and popup embed for Sekyro AI identity verification",
5
+ "license": "SEE LICENSE IN LICENSE",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git+https://github.com/IBTIKAR-Technologies/sekyro-sdks.git",
8
+ "url": "git+https://github.com/IBTIKAR-Technologies/ibtikar-identity.git",
9
9
  "directory": "packages/sekyro-web"
10
10
  },
11
- "homepage": "https://github.com/IBTIKAR-Technologies/sekyro-sdks/tree/main/packages/sekyro-web#readme",
12
- "bugs": {
13
- "url": "https://github.com/IBTIKAR-Technologies/sekyro-sdks/issues"
14
- },
15
11
  "keywords": [
16
12
  "sekyro",
17
13
  "kyc",
@@ -61,6 +57,17 @@
61
57
  "default": "./dist/core/v3/detect-worker.cjs"
62
58
  }
63
59
  },
60
+ "./core/ocr/worker": {
61
+ "import": {
62
+ "types": "./dist/core/ocr/worker.d.ts",
63
+ "default": "./dist/core/ocr/worker.js"
64
+ },
65
+ "require": {
66
+ "types": "./dist/core/ocr/worker.d.cts",
67
+ "default": "./dist/core/ocr/worker.cjs"
68
+ }
69
+ },
70
+ "./core/ocr/worker.bundle.js": "./dist/core/ocr/worker.bundle.js",
64
71
  "./client": {
65
72
  "import": {
66
73
  "types": "./dist/client/index.d.ts",
@@ -96,14 +103,21 @@
96
103
  "dist",
97
104
  "README.md",
98
105
  "CHANGELOG.md",
99
- "LICENSE"
106
+ "LICENSE",
107
+ "THIRD-PARTY-NOTICES.txt",
108
+ "sbom.cdx.json"
100
109
  ],
101
110
  "scripts": {
102
111
  "build": "tsup",
103
112
  "test": "vitest run",
104
113
  "typecheck": "tsc --noEmit",
105
114
  "api:report": "api-extractor run --local",
106
- "api:check": "api-extractor run"
115
+ "api:check": "api-extractor run",
116
+ "check:lazy-doc": "node scripts/check-lazy-doc.mjs",
117
+ "check:lazy-portrait": "node scripts/check-lazy-portrait.mjs",
118
+ "size": "node scripts/check-size.mjs",
119
+ "gen:ocr-assets": "node scripts/gen-ocr-assets.mjs",
120
+ "check:ocr-assets": "node scripts/gen-ocr-assets.mjs --check && node scripts/check-ocr-assets.mjs"
107
121
  },
108
122
  "dependencies": {
109
123
  "@mediapipe/tasks-vision": "0.10.35"
@@ -112,6 +126,7 @@
112
126
  "@microsoft/api-extractor": "^7.59.4",
113
127
  "@types/node": "^22",
114
128
  "@vitest/coverage-v8": "^3",
129
+ "jpeg-js": "0.4.4",
115
130
  "jsdom": "^25",
116
131
  "tsup": "^8",
117
132
  "typescript": "^5.7",
package/sbom.cdx.json ADDED
@@ -0,0 +1,195 @@
1
+ {
2
+ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
3
+ "bomFormat": "CycloneDX",
4
+ "specVersion": "1.6",
5
+ "version": 1,
6
+ "metadata": {
7
+ "tools": {
8
+ "components": [
9
+ {
10
+ "type": "application",
11
+ "name": "npm",
12
+ "version": "11.12.1"
13
+ },
14
+ {
15
+ "type": "library",
16
+ "name": "cyclonedx-library",
17
+ "group": "@cyclonedx",
18
+ "version": "10.3.0",
19
+ "author": "Jan Kowalleck",
20
+ "description": "Core functionality of CycloneDX for JavaScript (Node.js or WebBrowser).",
21
+ "licenses": [
22
+ {
23
+ "license": {
24
+ "id": "Apache-2.0"
25
+ }
26
+ }
27
+ ],
28
+ "externalReferences": [
29
+ {
30
+ "url": "https://github.com/CycloneDX/cyclonedx-javascript-library/issues",
31
+ "type": "issue-tracker",
32
+ "comment": "as detected from PackageJson property \"bugs.url\""
33
+ },
34
+ {
35
+ "url": "git+https://github.com/CycloneDX/cyclonedx-javascript-library.git",
36
+ "type": "vcs",
37
+ "comment": "as detected from PackageJson property \"repository.url\""
38
+ },
39
+ {
40
+ "url": "https://github.com/CycloneDX/cyclonedx-javascript-library#readme",
41
+ "type": "website",
42
+ "comment": "as detected from PackageJson property \"homepage\""
43
+ }
44
+ ],
45
+ "properties": [
46
+ {
47
+ "name": "cdx:npm:package:constraint:engine:node",
48
+ "value": ">=20.18.0"
49
+ }
50
+ ]
51
+ },
52
+ {
53
+ "type": "application",
54
+ "name": "cyclonedx-npm",
55
+ "group": "@cyclonedx",
56
+ "version": "6.0.1",
57
+ "author": "Jan Kowalleck",
58
+ "description": "Create CycloneDX Software Bill of Materials (SBOM) from NPM projects.",
59
+ "licenses": [
60
+ {
61
+ "license": {
62
+ "id": "Apache-2.0"
63
+ }
64
+ }
65
+ ],
66
+ "externalReferences": [
67
+ {
68
+ "url": "https://github.com/CycloneDX/cyclonedx-node-npm/issues",
69
+ "type": "issue-tracker",
70
+ "comment": "as detected from PackageJson property \"bugs.url\""
71
+ },
72
+ {
73
+ "url": "git+https://github.com/CycloneDX/cyclonedx-node-npm.git",
74
+ "type": "vcs",
75
+ "comment": "as detected from PackageJson property \"repository.url\""
76
+ },
77
+ {
78
+ "url": "https://github.com/CycloneDX/cyclonedx-node-npm#readme",
79
+ "type": "website",
80
+ "comment": "as detected from PackageJson property \"homepage\""
81
+ }
82
+ ],
83
+ "properties": [
84
+ {
85
+ "name": "cdx:npm:package:constraint:engine:node",
86
+ "value": ">=20.18.0"
87
+ },
88
+ {
89
+ "name": "cdx:npm:package:constraint:engine:npm",
90
+ "value": ">=9"
91
+ }
92
+ ]
93
+ }
94
+ ]
95
+ },
96
+ "component": {
97
+ "type": "application",
98
+ "name": "web",
99
+ "group": "@sekyro",
100
+ "version": "0.1.0-rc.8",
101
+ "bom-ref": "@sekyro/web@0.1.0-rc.8",
102
+ "description": "Sekyro AI web SDK — browser liveness capture, hosted-verification client and popup embed for Sekyro AI identity verification",
103
+ "licenses": [
104
+ {
105
+ "license": {
106
+ "name": "SEE LICENSE IN LICENSE",
107
+ "acknowledgement": "declared"
108
+ }
109
+ }
110
+ ],
111
+ "purl": "pkg:npm/%40sekyro/web@0.1.0-rc.8?vcs_url=git%2Bhttps%3A%2F%2Fgithub.com%2FIBTIKAR-Technologies%2Fibtikar-identity.git%23packages%2Fsekyro-web",
112
+ "externalReferences": [
113
+ {
114
+ "url": "https://github.com/IBTIKAR-Technologies/ibtikar-identity/issues",
115
+ "type": "issue-tracker",
116
+ "comment": "as detected from PackageJson property \"bugs.url\""
117
+ },
118
+ {
119
+ "url": "git+https://github.com/IBTIKAR-Technologies/ibtikar-identity.git#packages/sekyro-web",
120
+ "type": "vcs",
121
+ "comment": "as detected from PackageJson property \"repository.url\" and \"repository.directory\""
122
+ },
123
+ {
124
+ "url": "https://github.com/IBTIKAR-Technologies/ibtikar-identity#readme",
125
+ "type": "website",
126
+ "comment": "as detected from PackageJson property \"homepage\""
127
+ }
128
+ ],
129
+ "properties": [
130
+ {
131
+ "name": "cdx:npm:package:constraint:engine:node",
132
+ "value": ">=20"
133
+ },
134
+ {
135
+ "name": "cdx:npm:package:path",
136
+ "value": ""
137
+ }
138
+ ]
139
+ },
140
+ "properties": [
141
+ {
142
+ "name": "cdx:reproducible",
143
+ "value": "true"
144
+ }
145
+ ]
146
+ },
147
+ "components": [
148
+ {
149
+ "type": "library",
150
+ "name": "tasks-vision",
151
+ "group": "@mediapipe",
152
+ "version": "0.10.35",
153
+ "bom-ref": "@sekyro/web@0.1.0-rc.8|@mediapipe/tasks-vision@0.10.35",
154
+ "licenses": [
155
+ {
156
+ "license": {
157
+ "id": "Apache-2.0",
158
+ "acknowledgement": "declared"
159
+ }
160
+ }
161
+ ],
162
+ "purl": "pkg:npm/%40mediapipe/tasks-vision@0.10.35",
163
+ "externalReferences": [
164
+ {
165
+ "url": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.35.tgz",
166
+ "type": "distribution",
167
+ "hashes": [
168
+ {
169
+ "alg": "SHA-512",
170
+ "content": "1cebda77055113a242fb8e67c988669f2c27af987f27c299bce794355386f6aff4f3be6966022dce7141f5b453bcb736eb861226a899d4db08a424ad270fde82"
171
+ }
172
+ ],
173
+ "comment": "as detected from npm-ls property \"resolved\" and property \"integrity\""
174
+ }
175
+ ],
176
+ "properties": [
177
+ {
178
+ "name": "cdx:npm:package:path",
179
+ "value": "node_modules/@mediapipe/tasks-vision"
180
+ }
181
+ ]
182
+ }
183
+ ],
184
+ "dependencies": [
185
+ {
186
+ "ref": "@sekyro/web@0.1.0-rc.8",
187
+ "dependsOn": [
188
+ "@sekyro/web@0.1.0-rc.8|@mediapipe/tasks-vision@0.10.35"
189
+ ]
190
+ },
191
+ {
192
+ "ref": "@sekyro/web@0.1.0-rc.8|@mediapipe/tasks-vision@0.10.35"
193
+ }
194
+ ]
195
+ }
@@ -1,52 +0,0 @@
1
- // src/core/v3/frame-quality.ts
2
- var SHARP_REF = 180;
3
- var EXPOSURE_BAND = [100, 190];
4
- var EXPOSURE_FALLOFF = 64;
5
- function lumaAt(data, index) {
6
- return 0.299 * (data[index] ?? 0) + 0.587 * (data[index + 1] ?? 0) + 0.114 * (data[index + 2] ?? 0);
7
- }
8
- function meanLuma(image) {
9
- const { data, width, height } = image;
10
- const pixels = width * height;
11
- if (pixels === 0) return 0;
12
- let sum = 0;
13
- for (let i = 0; i < pixels; i += 1) sum += lumaAt(data, i * 4);
14
- return sum / pixels;
15
- }
16
- function sharpness(image) {
17
- const { data, width, height } = image;
18
- if (width < 3 || height < 3) return 0;
19
- const gray = new Float32Array(width * height);
20
- for (let i = 0; i < gray.length; i += 1) gray[i] = lumaAt(data, i * 4);
21
- let sum = 0;
22
- let sumSquares = 0;
23
- let count = 0;
24
- for (let y = 1; y < height - 1; y += 1) {
25
- for (let x = 1; x < width - 1; x += 1) {
26
- const i = y * width + x;
27
- const response = (gray[i - width] ?? 0) + (gray[i - 1] ?? 0) + (gray[i + 1] ?? 0) + (gray[i + width] ?? 0) - 4 * (gray[i] ?? 0);
28
- sum += response;
29
- sumSquares += response * response;
30
- count += 1;
31
- }
32
- }
33
- if (count === 0) return 0;
34
- const mean = sum / count;
35
- const variance = sumSquares / count - mean * mean;
36
- return Math.min(1, Math.max(0, variance) / SHARP_REF);
37
- }
38
- function exposure(image) {
39
- const mean = meanLuma(image);
40
- const [low, high] = EXPOSURE_BAND;
41
- const distance = mean < low ? low - mean : mean > high ? mean - high : 0;
42
- return Math.max(0, 1 - distance / EXPOSURE_FALLOFF);
43
- }
44
-
45
- export {
46
- SHARP_REF,
47
- EXPOSURE_BAND,
48
- meanLuma,
49
- sharpness,
50
- exposure
51
- };
52
- //# sourceMappingURL=chunk-MWNFSNMP.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/v3/frame-quality.ts"],"sourcesContent":["// frame-quality (Task 8) — pure, deterministic image measures on a small\n// (≤96px) face crop. No canvas access here: callers hand in ImageData; that\n// keeps these functions golden-testable and worker-portable.\n\n/** Laplacian-variance normalization reference — crops whose edge variance\n * reaches this value (or beyond) count as fully sharp. */\nexport const SHARP_REF = 180;\n\n/** Mean-luma comfort band: inside → exposure 1; outside → linear falloff.\n * Dark bound raised 70→100 (operator ruling 2026-07-07): face crops in the\n * 70–100 range passed the gate but kept drawing dark-face review verdicts —\n * gating them here converts that into pre-capture more_light coaching. */\nexport const EXPOSURE_BAND: readonly [number, number] = [100, 190];\n\n/** Luma distance that zeroes the exposure factor (≈ one stop past the band). */\nconst EXPOSURE_FALLOFF = 64;\n\n/** Rec.601 luma. */\nfunction lumaAt(data: Uint8ClampedArray, index: number): number {\n return (\n 0.299 * (data[index] ?? 0) + 0.587 * (data[index + 1] ?? 0) + 0.114 * (data[index + 2] ?? 0)\n );\n}\n\nexport function meanLuma(image: ImageData): number {\n const { data, width, height } = image;\n const pixels = width * height;\n if (pixels === 0) return 0;\n let sum = 0;\n for (let i = 0; i < pixels; i += 1) sum += lumaAt(data, i * 4);\n return sum / pixels;\n}\n\n/**\n * Sharpness = variance of the 3×3 Laplacian (∇² kernel [0,1,0;1,−4,1;0,1,0])\n * over the interior pixels, normalized 0..1 against SHARP_REF. Flat crop → 0;\n * hard edges → 1 (clamped).\n */\nexport function sharpness(image: ImageData): number {\n const { data, width, height } = image;\n if (width < 3 || height < 3) return 0;\n const gray = new Float32Array(width * height);\n for (let i = 0; i < gray.length; i += 1) gray[i] = lumaAt(data, i * 4);\n let sum = 0;\n let sumSquares = 0;\n let count = 0;\n for (let y = 1; y < height - 1; y += 1) {\n for (let x = 1; x < width - 1; x += 1) {\n const i = y * width + x;\n const response =\n (gray[i - width] ?? 0) +\n (gray[i - 1] ?? 0) +\n (gray[i + 1] ?? 0) +\n (gray[i + width] ?? 0) -\n 4 * (gray[i] ?? 0);\n sum += response;\n sumSquares += response * response;\n count += 1;\n }\n }\n if (count === 0) return 0;\n const mean = sum / count;\n const variance = sumSquares / count - mean * mean;\n // VARIANCE against the reference (the plan's formula). Field lesson\n // (Salem's iPhone, 2026-07-07): sqrt(variance)/REF put every real face\n // crop at ~0.03-0.1 — permanently under the steady_camera threshold and\n // crushing q to zero, so the gate could never fire. Real crops have\n // Laplacian variance well above 180; a genuinely soft/blurred crop does not.\n return Math.min(1, Math.max(0, variance) / SHARP_REF);\n}\n\n/** Exposure factor: 1 inside the band; linear falloff to 0 over\n * EXPOSURE_FALLOFF luma outside it. */\nexport function exposure(image: ImageData): number {\n const mean = meanLuma(image);\n const [low, high] = EXPOSURE_BAND;\n const distance = mean < low ? low - mean : mean > high ? mean - high : 0;\n return Math.max(0, 1 - distance / EXPOSURE_FALLOFF);\n}\n"],"mappings":";AAMO,IAAM,YAAY;AAMlB,IAAM,gBAA2C,CAAC,KAAK,GAAG;AAGjE,IAAM,mBAAmB;AAGzB,SAAS,OAAO,MAAyB,OAAuB;AAC9D,SACE,SAAS,KAAK,KAAK,KAAK,KAAK,SAAS,KAAK,QAAQ,CAAC,KAAK,KAAK,SAAS,KAAK,QAAQ,CAAC,KAAK;AAE9F;AAEO,SAAS,SAAS,OAA0B;AACjD,QAAM,EAAE,MAAM,OAAO,OAAO,IAAI;AAChC,QAAM,SAAS,QAAQ;AACvB,MAAI,WAAW,EAAG,QAAO;AACzB,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK,EAAG,QAAO,OAAO,MAAM,IAAI,CAAC;AAC7D,SAAO,MAAM;AACf;AAOO,SAAS,UAAU,OAA0B;AAClD,QAAM,EAAE,MAAM,OAAO,OAAO,IAAI;AAChC,MAAI,QAAQ,KAAK,SAAS,EAAG,QAAO;AACpC,QAAM,OAAO,IAAI,aAAa,QAAQ,MAAM;AAC5C,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,EAAG,MAAK,CAAC,IAAI,OAAO,MAAM,IAAI,CAAC;AACrE,MAAI,MAAM;AACV,MAAI,aAAa;AACjB,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,SAAS,GAAG,KAAK,GAAG;AACtC,aAAS,IAAI,GAAG,IAAI,QAAQ,GAAG,KAAK,GAAG;AACrC,YAAM,IAAI,IAAI,QAAQ;AACtB,YAAM,YACH,KAAK,IAAI,KAAK,KAAK,MACnB,KAAK,IAAI,CAAC,KAAK,MACf,KAAK,IAAI,CAAC,KAAK,MACf,KAAK,IAAI,KAAK,KAAK,KACpB,KAAK,KAAK,CAAC,KAAK;AAClB,aAAO;AACP,oBAAc,WAAW;AACzB,eAAS;AAAA,IACX;AAAA,EACF;AACA,MAAI,UAAU,EAAG,QAAO;AACxB,QAAM,OAAO,MAAM;AACnB,QAAM,WAAW,aAAa,QAAQ,OAAO;AAM7C,SAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,IAAI,SAAS;AACtD;AAIO,SAAS,SAAS,OAA0B;AACjD,QAAM,OAAO,SAAS,KAAK;AAC3B,QAAM,CAAC,KAAK,IAAI,IAAI;AACpB,QAAM,WAAW,OAAO,MAAM,MAAM,OAAO,OAAO,OAAO,OAAO,OAAO;AACvE,SAAO,KAAK,IAAI,GAAG,IAAI,WAAW,gBAAgB;AACpD;","names":[]}
@@ -1,50 +0,0 @@
1
- // src/popup/index.ts
2
- var SEKYRO_EVENTS = [
3
- "session.completed",
4
- "session.failed",
5
- "session.review_pending",
6
- "session.expired",
7
- "session.cancelled"
8
- ];
9
- var WINDOW_NAME = "sekyro-verify";
10
- var WINDOW_FEATURES = "width=480,height=720,noopener=false";
11
- var EVENT_NAMES = new Set(SEKYRO_EVENTS);
12
- function isSekyroEvent(data) {
13
- if (data === null || typeof data !== "object") return false;
14
- const d = data;
15
- return d.source === "sekyro" && d.version === 1 && typeof d.sessionId === "string" && typeof d.event === "string" && EVENT_NAMES.has(d.event);
16
- }
17
- function open(opts) {
18
- const origin = new URL(opts.hostedUrl).origin;
19
- const popup = window.open(opts.hostedUrl, WINDOW_NAME, WINDOW_FEATURES);
20
- if (!popup) return { error: "popup_blocked" };
21
- const listener = (ev) => {
22
- if (ev.source !== popup) return;
23
- if (ev.origin !== origin) return;
24
- if (!isSekyroEvent(ev.data)) return;
25
- const { source, version, event, sessionId } = ev.data;
26
- opts.onEvent({ source, version, event, sessionId });
27
- };
28
- window.addEventListener("message", listener);
29
- return {
30
- close() {
31
- window.removeEventListener("message", listener);
32
- popup.close();
33
- }
34
- };
35
- }
36
- function openHosted(hostedUrl, opts) {
37
- if (opts.mode === "redirect") {
38
- window.location.assign(hostedUrl);
39
- return { close() {
40
- } };
41
- }
42
- return open({ hostedUrl, onEvent: opts.onEvent ?? (() => void 0) });
43
- }
44
-
45
- export {
46
- SEKYRO_EVENTS,
47
- open,
48
- openHosted
49
- };
50
- //# sourceMappingURL=chunk-NILNKLK6.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/popup/index.ts"],"sourcesContent":["// @sekyro/web/popup — the partner-facing popup embed wrapper\n// (Plan 6 Task 12). Framework-free browser code.\n//\n// REBRAND-CRITICAL WIRE CONTRACT (frozen on first partner integration):\n// - `source: 'sekyro'`, `version: 1`, window name `'sekyro-verify'`;\n// - event names mirror the webhook catalog (`session.completed` /\n// `session.failed` / `session.review_pending` / `session.expired`) plus the\n// page-only `session.cancelled` (user closed the popup mid-flow);\n// - payload is the event name + sessionId ONLY — never scores, PII, or\n// outcome detail (partners fetch the verdict server-side via API/webhooks).\n//\n// Security posture: the hosted page posts to the org's allowlisted origin\n// (backend `sdkAllowedOrigins`, surfaced as `HostedSessionView.sdkOrigin`) and\n// this listener double-checks BOTH the sender origin (must be the hosted URL's\n// origin) AND the message shape before forwarding anything to the partner.\n\n/** The frozen SDK event-name vocabulary, in catalog order. */\nexport const SEKYRO_EVENTS = [\n \"session.completed\",\n \"session.failed\",\n \"session.review_pending\",\n \"session.expired\",\n \"session.cancelled\",\n] as const;\n\n/** One SDK event name — webhook-catalog terminal names + `session.cancelled`. */\nexport type SekyroEventName = (typeof SEKYRO_EVENTS)[number];\n\n/** The frozen postMessage payload: event name + sessionId ONLY. */\nexport interface SekyroEvent {\n source: \"sekyro\";\n version: 1;\n event: SekyroEventName;\n sessionId: string;\n}\n\nexport interface OpenOptions {\n /** The `hosted_url` returned by `POST /api/v2/sessions` (create result). */\n hostedUrl: string;\n /** Called for every allowlist-validated event posted by the hosted page. */\n onEvent: (event: SekyroEvent) => void;\n}\n\n/** Live popup handle — `close()` detaches the listener and closes the window. */\nexport interface PopupHandle {\n close(): void;\n}\n\n/** `window.open` returned null: blocked by the browser (or a popup blocker). */\nexport interface PopupBlocked {\n error: \"popup_blocked\";\n}\n\n/** FROZEN window name — partners may target it (e.g. to re-focus the popup). */\nconst WINDOW_NAME = \"sekyro-verify\";\n\n/** Popup geometry; `noopener=false` keeps `window.opener` alive for the page's postMessage. */\nconst WINDOW_FEATURES = \"width=480,height=720,noopener=false\";\n\nconst EVENT_NAMES: ReadonlySet<string> = new Set(SEKYRO_EVENTS);\n\n/** True when the payload matches the frozen {@link SekyroEvent} shape exactly. */\nfunction isSekyroEvent(data: unknown): data is SekyroEvent {\n if (data === null || typeof data !== \"object\") return false;\n const d = data as Partial<SekyroEvent>;\n return (\n d.source === \"sekyro\" &&\n d.version === 1 &&\n typeof d.sessionId === \"string\" &&\n typeof d.event === \"string\" &&\n EVENT_NAMES.has(d.event)\n );\n}\n\n/**\n * Open the hosted verification flow in a popup and subscribe to its lifecycle\n * events. Incoming messages are dropped unless ALL hold:\n * - the sender window (`ev.source`) is THIS popup — closes the spoof path of\n * a same-origin iframe/window posting on the hosted origin's behalf\n * (`ev.source` is identity-stable across popup navigations);\n * - the sender origin is exactly the hosted URL's origin;\n * - the payload matches the frozen {@link SekyroEvent} shape (allowlisted\n * event name, string sessionId).\n *\n * Returns `{error: 'popup_blocked'}` when the browser refuses the popup —\n * callers should fall back to a full redirect to `hostedUrl`.\n *\n * Usage notes:\n * - ONE active popup per page: the window name is shared and every `open()`\n * installs a fresh listener, so `close()` the previous handle before\n * reopening (a reused named window would otherwise feed stale listeners).\n * - Events are at-least-once HINTS, not a system of record: a popup reload\n * mid-flow fires `session.cancelled`, and a reload after a terminal state\n * re-posts the terminal event — partners MUST confirm outcomes server-side\n * via webhooks or the sessions API.\n * - The popup `features` geometry is currently fixed ({@link WINDOW_FEATURES});\n * an options field for it is reserved for a later version.\n */\nexport function open(opts: OpenOptions): PopupHandle | PopupBlocked {\n const origin = new URL(opts.hostedUrl).origin;\n const popup = window.open(opts.hostedUrl, WINDOW_NAME, WINDOW_FEATURES);\n if (!popup) return { error: \"popup_blocked\" };\n\n const listener = (ev: MessageEvent): void => {\n if (ev.source !== popup) return;\n if (ev.origin !== origin) return;\n if (!isSekyroEvent(ev.data)) return;\n const { source, version, event, sessionId } = ev.data;\n // Re-project onto the frozen shape so extra properties never reach the partner.\n opts.onEvent({ source, version, event, sessionId });\n };\n window.addEventListener(\"message\", listener);\n\n return {\n close(): void {\n window.removeEventListener(\"message\", listener);\n popup.close();\n },\n };\n}\n\nexport interface OpenHostedOptions {\n /** `'popup'` opens a named window (the `open()` embed); `'redirect'` navigates this tab. */\n mode: \"popup\" | \"redirect\";\n /** Popup mode only — redirect mode never returns to this page. */\n onEvent?: (event: SekyroEvent) => void;\n}\n\n/**\n * Spec §3.1 entry: `openHosted(hostedUrl, { mode, onEvent })`. Popup mode is\n * the `open()` embed; a blocked popup returns `{error: 'popup_blocked'}` so the\n * caller can fall back to `mode: 'redirect'` (never done implicitly — leaving\n * the partner page is their call). Redirect mode navigates and returns a\n * no-op handle.\n */\nexport function openHosted(hostedUrl: string, opts: OpenHostedOptions): PopupHandle | PopupBlocked {\n if (opts.mode === \"redirect\") {\n window.location.assign(hostedUrl);\n return { close(): void {} };\n }\n return open({ hostedUrl, onEvent: opts.onEvent ?? (() => undefined) });\n}\n"],"mappings":";AAiBO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA+BA,IAAM,cAAc;AAGpB,IAAM,kBAAkB;AAExB,IAAM,cAAmC,IAAI,IAAI,aAAa;AAG9D,SAAS,cAAc,MAAoC;AACzD,MAAI,SAAS,QAAQ,OAAO,SAAS,SAAU,QAAO;AACtD,QAAM,IAAI;AACV,SACE,EAAE,WAAW,YACb,EAAE,YAAY,KACd,OAAO,EAAE,cAAc,YACvB,OAAO,EAAE,UAAU,YACnB,YAAY,IAAI,EAAE,KAAK;AAE3B;AA0BO,SAAS,KAAK,MAA+C;AAClE,QAAM,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE;AACvC,QAAM,QAAQ,OAAO,KAAK,KAAK,WAAW,aAAa,eAAe;AACtE,MAAI,CAAC,MAAO,QAAO,EAAE,OAAO,gBAAgB;AAE5C,QAAM,WAAW,CAAC,OAA2B;AAC3C,QAAI,GAAG,WAAW,MAAO;AACzB,QAAI,GAAG,WAAW,OAAQ;AAC1B,QAAI,CAAC,cAAc,GAAG,IAAI,EAAG;AAC7B,UAAM,EAAE,QAAQ,SAAS,OAAO,UAAU,IAAI,GAAG;AAEjD,SAAK,QAAQ,EAAE,QAAQ,SAAS,OAAO,UAAU,CAAC;AAAA,EACpD;AACA,SAAO,iBAAiB,WAAW,QAAQ;AAE3C,SAAO;AAAA,IACL,QAAc;AACZ,aAAO,oBAAoB,WAAW,QAAQ;AAC9C,YAAM,MAAM;AAAA,IACd;AAAA,EACF;AACF;AAgBO,SAAS,WAAW,WAAmB,MAAqD;AACjG,MAAI,KAAK,SAAS,YAAY;AAC5B,WAAO,SAAS,OAAO,SAAS;AAChC,WAAO,EAAE,QAAc;AAAA,IAAC,EAAE;AAAA,EAC5B;AACA,SAAO,KAAK,EAAE,WAAW,SAAS,KAAK,YAAY,MAAM,QAAW,CAAC;AACvE;","names":[]}