@seams/voiceid 0.43.0 → 0.45.0

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/README.md CHANGED
@@ -10,7 +10,7 @@ and the trusted host API. Existing voice enrollments keep their storage identity
10
10
  Install matching Wallet and VoiceID versions, then import the descriptor:
11
11
 
12
12
  ```sh
13
- pnpm add @seams/wallet@0.43.0 @seams/voiceid@0.43.0
13
+ pnpm add @seams/wallet@0.45.0 @seams/voiceid@0.45.0
14
14
  ```
15
15
 
16
16
  ```ts
@@ -26,8 +26,8 @@ const config = defineSeamsConfig({
26
26
  ```
27
27
 
28
28
  Choose `transcriptionModel: 'aurora'` for the smaller speech model. Citrinet is
29
- the default when this option is omitted. The choice applies to enrollment,
30
- unlock, and transaction approval. Each action keeps its chosen model throughout
29
+ the default when this option is omitted. The choice applies to enrollment
30
+ and unlock. Each action keeps its chosen model throughout
31
31
  preparation and recording.
32
32
 
33
33
  | Speech model | Uncompressed | Gzip |
@@ -47,35 +47,14 @@ loading in the trusted Wallet iframe. Passkey and Email OTP flows remain indepen
47
47
  The optional `VoiceBeam` and `VoiceAssistantCard` visuals are exported from
48
48
  `@seams/voiceid/react`. Import `@seams/voiceid/styles` when using the card.
49
49
 
50
- ## Configure a signing challenge
50
+ ## Signing after a VoiceID unlock
51
51
 
52
- Pass `voiceId.confirmationCommand` in the `options` of an existing reviewed NEAR
53
- or EVM signing request:
54
-
55
- ```ts
56
- const options = {
57
- voiceId: {
58
- confirmationCommand: 'Sell 100 NEAR for USDC at $5.5',
59
- },
60
- };
61
- ```
62
-
63
- The Wallet selects a short action-only challenge, such as “Approve this sale at
64
- once” or “Let the sale go through.” It omits amounts, assets, and prices from the
65
- spoken phrase. The full transaction review remains authoritative.
66
-
67
- The leading English verb selects the phrase family: `sell`, `buy`, `swap`, or
68
- `send`/`transfer`. Other wording uses the default random challenges. Commands
69
- must be nonempty strings of at most 256 characters without control characters.
70
- The application cannot supply the final phrase or change verification thresholds.
71
- Retries choose a different phrase within the selected family.
72
-
73
- This option changes wording only. It does not enable VoiceID for unsupported
74
- transactions or interpret a trading command. VoiceID currently supports reviewed
75
- native transfers on NEAR testnet and Arc testnet. Approval remains bound to the
76
- complete signing request and review deadline, including the supplied command.
77
- Enrollment and unlock retain the default phrase bank. This adds no model or API
78
- request and keeps audio in the Wallet iframe.
52
+ VoiceID works like a passkey. A VoiceID unlock opens a normal signing session.
53
+ Each operation on any chain shows the ordinary **Confirm**, with no further voice
54
+ check, within the session's uses and expiry. When the session runs out or
55
+ expires, the next operation asks for VoiceID again inside the Wallet, then signs.
56
+ It never asks for a passkey. Key export asks for one more voice check, bound to
57
+ that export, and never for a passkey or Email OTP.
79
58
 
80
59
  ## Install on the Wallet host
81
60
 
@@ -122,12 +101,8 @@ same card. Retry keeps verified assets and voice enrollment. Close the card to
122
101
  choose a passkey instead. A missing or incompatible host package requires an
123
102
  operator fix; other module failures include connection and reload instructions.
124
103
 
125
- Transaction reviews prepare VoiceID before enabling **Confirm with VoiceID**.
126
- The voice attempt timer starts after that click. The transaction's expiry remains
127
- in force while loading or retrying, so an expired transaction needs a fresh review.
128
-
129
- VoiceID remembers the microphone used for usable audio in this browser. Enrollment,
130
- unlock, and signing request that input even when the system default changes.
104
+ VoiceID remembers the microphone used for usable audio in this browser. Enrollment
105
+ and unlock request that input even when the system default changes.
131
106
  If it is unavailable, VoiceID can use the current default. Reconnecting the saved
132
107
  microphone restores it. Interrupted recordings require a new attempt. Voice
133
108
  enrollment and verification thresholds stay unchanged.
@@ -140,5 +115,5 @@ Splitting it removes that payload from Wallet-only installations. It does not
140
115
  reduce the bytes needed for the first VoiceID operation.
141
116
 
142
117
  Use the [public VoiceID guide](https://docs.wallet.seams.sh/concepts/auth-methods/voiceid)
143
- for enrollment, unlock, transfer approval, and storage behavior.
118
+ for enrollment, unlock, signing, and storage behavior.
144
119
  See [implementation notes](src/README.md) for code boundaries and enrollment storage.
@@ -0,0 +1 @@
1
+ import"./prompt-DsRKGfyb.js";import{a as e,i as t,o as n,s as r,t as i}from"./deviceBridge-C8Jp8coS.js";import{WorkerDeferred as a,prepareVoiceOwner as o,toError as s}from"../voiceid-host.js";const c=`http://www.w3.org/2000/svg`;async function l(e,t,n,r,i){let a=new u(e,t,n,r,i);try{await a.result}finally{a.dispose()}}var u=class{completion=new a;result=this.completion.promise;document;element;enrollment;enrollmentValue;enrollmentSummary;startOver;status;actions;busy=!1;closed=!1;constructor(e,t,n,r,i){this.parent=e,this.confirmChange=t,this.scope=n,this.signal=r,this.model=i,this.document=e.ownerDocument,this.element=this.document.createElement(`div`),this.enrollment=this.document.createElement(`div`),this.enrollmentValue=this.document.createElement(`span`),this.enrollmentSummary=this.document.createElement(`span`),this.startOver=this.document.createElement(`p`),this.status=this.document.createElement(`p`),this.element.className=`seams-voiceid-prompt seams-voiceid-management`;let a=this.document.createElement(`div`);a.className=`seams-voiceid-management-header`;let o=this.document.createElement(`button`);o.type=`button`,o.className=`seams-voiceid-management-back`,o.setAttribute(`aria-label`,`Back`),o.append(d(this.document,`M15 6l-6 6 6 6`,20)),o.addEventListener(`click`,this.cancel);let s=this.document.createElement(`h2`);s.textContent=`Manage voice`,a.append(o,s);let c=this.document.createElement(`p`);c.className=`seams-voiceid-management-intro`,c.textContent=`Voice unlock on this device. Each change asks for your passkey.`,this.element.append(a,c,this.enrollmentRow()),this.actions={reset:this.resetAction(),improve:this.action(`improve`,`Improve recognition`,!0),import:this.action(`import`,`Use saved enrollment`,!1),replace:this.startOverAction()},this.status.setAttribute(`role`,`status`),this.element.append(this.status),this.enrollmentValue.textContent=`Checking…`,this.actions.import.button.hidden=!0,this.render(null,!1,!1),this.parent.replaceChildren(this.element),this.signal.addEventListener(`abort`,this.cancel,{once:!0}),this.refresh(),o.focus()}enrollmentRow(){this.enrollment.className=`seams-voiceid-management-enrollment`;let e=this.document.createElement(`span`);return e.textContent=`Enrollment`,this.enrollmentValue.className=`seams-voiceid-management-value`,this.enrollmentSummary.className=`seams-voiceid-management-summary`,this.enrollmentSummary.append(this.enrollmentValue),this.enrollment.append(e,this.enrollmentSummary),this.enrollment}resetAction(){let e=this.document.createElement(`button`);e.type=`button`,e.className=`seams-voiceid-management-reset`,e.textContent=`Reset`,e.setAttribute(`aria-label`,`Reset improvements`),e.addEventListener(`click`,this.begin.bind(this,`reset`));let t=this.document.createElement(`span`);return t.className=`seams-voiceid-management-enrollment-detail`,this.enrollmentSummary.append(t),this.enrollment.append(e),{button:e,title:e,detail:t,frame:this.enrollment}}startOverAction(){return this.startOver.className=`seams-voiceid-management-section`,this.startOver.textContent=`Start over`,this.element.append(this.startOver),this.action(`replace`,`Record voice again`,!1)}action(e,t,n){let r=this.document.createElement(`button`);r.type=`button`,r.className=`seams-voiceid-management-action`;let i=this.document.createElement(`span`),a=this.document.createElement(`span`);a.className=`seams-voiceid-management-action-heading`;let o=this.document.createElement(`span`);o.id=`seams-voiceid-management-${e}-title`,o.textContent=t,a.append(o);let s=[];if(n){let t=this.document.createElement(`span`);t.id=`seams-voiceid-management-${e}-chip`,t.className=`seams-voiceid-management-chip`,t.textContent=`Experimental`,a.append(t),s.push(t.id)}let c=this.document.createElement(`span`);return c.id=`seams-voiceid-management-${e}-detail`,c.className=`seams-voiceid-management-action-detail`,s.push(c.id),i.append(a,c),r.append(i,d(this.document,`M9 6l6 6-6 6`,16)),r.setAttribute(`aria-labelledby`,o.id),r.setAttribute(`aria-describedby`,s.join(` `)),r.disabled=!0,r.addEventListener(`click`,this.begin.bind(this,e)),this.element.append(r),{button:r,title:o,detail:c,frame:r}}async refresh(){try{let e=await r(this.scope),t=e?null:await n();if(this.closed)return;this.render(e,t!==null,!0)}catch(e){this.completion.reject(s(e))}}render(e,t,n){let r=e?.adaptation??null,{improve:i,reset:a,import:o,replace:s}=this.actions;n&&(this.enrollment.dataset.enrollment=r?.kind??`missing`,this.enrollmentValue.textContent=r===null?`Not on this device`:r.kind===`adapted`?`Improved`:`Original`,i.button.hidden=e===null,o.button.hidden=!t),a.button.hidden=r?.kind!==`adapted`,a.detail.textContent=r?.kind===`adapted`?`Saved on this device`:``,i.title.textContent=r?.kind===`adapted`?`Improve again`:`Improve recognition`,i.detail.textContent=r?.kind===`adapted`?`Replace the added samples. Your original enrollment stays.`:`Add three samples. They stay until you replace or reset them.`,o.detail.textContent=`Import the voice enrollment saved in this browser.`,s.detail.textContent=r?.kind===`adapted`?`Replace your enrollment and remove improvements.`:`Replace your enrollment with three new samples.`,this.startOver.hidden=i.button.hidden&&o.button.hidden;for(let e of Object.values(this.actions))delete e.frame.dataset.state,e.button.disabled=!n;this.status.textContent=``,delete this.status.dataset.placement}show(e,t,n){let r=this.actions[e];if(this.status.textContent=n,r.button.hidden){this.status.dataset.placement=`dialog`;return}r.frame.dataset.state=t,r.detail.textContent=n}begin(e){if(!(this.busy||this.closed)){this.busy=!0;for(let e of Object.values(this.actions))e.button.disabled=!0;this.show(e,`pending`,`Confirm with your passkey to continue.`),this.perform(e)}}async perform(n){try{let i=await o(this.scope,`enrolled`),a=await r(this.scope),s=await f(n,i,a?{kind:`present`,enrollment:a}:{kind:`missing`},this.signal),c=await this.confirmChange(s);switch(this.signal.throwIfAborted(),s.kind){case`improve`:await t(this.parent,{kind:`improve`,scope:this.scope,guard:s.guard,snapshot:s.snapshot,confirmation:c},this.signal,this.model);break;case`replace`:await t(this.parent,{kind:`replace`,scope:this.scope,guard:s.guard,snapshot:s.snapshot,confirmation:c},this.signal,this.model);break;case`reset`:await e({kind:`reset`,scope:this.scope,guard:s.guard,snapshot:s.snapshot,confirmation:c,signal:this.signal});break;case`import`:await e({kind:`import`,scope:this.scope,guard:s.guard,snapshot:s.snapshot,baseline:s.baseline,confirmation:c,signal:this.signal});break;default:return p(s)}this.closed||this.completion.resolve()}catch(e){if(this.closed)return;this.parent.replaceChildren(this.element),await this.refresh(),this.closed||this.show(n,`error`,e instanceof Error?e.message:`VoiceID could not be updated.`)}finally{this.busy=!1}}cancel=()=>{this.closed||(this.closed=!0,this.parent.dispatchEvent(new Event(`cancel`,{bubbles:!0})),this.completion.reject(Error(`VoiceID management was cancelled.`)))};dispose(){this.closed=!0,this.signal.removeEventListener(`abort`,this.cancel),this.element.remove()}};function d(e,t,n){let r=e.createElementNS(c,`svg`);r.setAttribute(`viewBox`,`0 0 24 24`),r.setAttribute(`width`,String(n)),r.setAttribute(`height`,String(n)),r.setAttribute(`aria-hidden`,`true`);let i=e.createElementNS(c,`path`);return i.setAttribute(`d`,t),i.setAttribute(`fill`,`none`),i.setAttribute(`stroke`,`currentColor`),i.setAttribute(`stroke-width`,`1.75`),i.setAttribute(`stroke-linecap`,`round`),i.setAttribute(`stroke-linejoin`,`round`),r.append(i),r}async function f(e,t,r,i){switch(e){case`replace`:return{kind:e,guard:t,snapshot:r,signal:i};case`import`:{if(r.kind!==`missing`)throw Error(`The previous enrollment is unavailable.`);let a=await n();if(!a)throw Error(`The previous enrollment is unavailable.`);return{kind:e,guard:t,snapshot:r,signal:i,baseline:a}}case`improve`:case`reset`:if(r.kind!==`present`)throw Error(`Record or import your original voice first.`);return{kind:e,guard:t,snapshot:r,signal:i};default:return p(e)}}function p(e){throw Error(`Unsupported VoiceID enrollment management action.`)}async function m(e,t,n,r){t.throwIfAborted();let i=await e.prepareActivation();return t.throwIfAborted(),i.kind===`enroll_existing`?{kind:`management`,action:h.bind(null,r,i,n)}:{kind:`recording`,action:g.bind(null,r,i,n),expiresAtMs:null}}async function h(e,t,n,r){return await l(n,t.confirmChange,t.scope,r,e),r.throwIfAborted(),{kind:`activated`,walletId:t.scope.walletId,walletAuthMethodId:t.scope.walletAuthMethodId}}function g(e,t,n,r,a,o){return t.activate(i.bind(null,e,{parent:n,onCapture:a,phrase:o}),r)}export{m as prepareActivationSurface};
@@ -0,0 +1 @@
1
+ import{_ as e,a as t,c as n,d as r,f as i,g as a,h as o,i as s,m as c,o as ee,p as l,s as u,t as d,u as f,v as p}from"./prompt-DsRKGfyb.js";import{WorkerDeferred as m,assertVoiceOwner as h,base64UrlEncode as g,createBrowserVoiceDevice as _,isFiniteNumber as v,openBrowserVoiceFactor as y,parseVoiceIdDeviceAssertion as te,parseVoiceIdOperationAssertion as b,prepareVoiceOwner as x,readBrowserVoiceDevice as S,requireRecord as C,resolveActivatingVoiceEnrollmentScope as w,resolveVoiceEnrollmentScope as T,toError as E,voiceEnrollmentStorage as D,voiceIdOperationStatementBytes as O,voiceIdWalletSessionChallengeBytes as k}from"../voiceid-host.js";const A=`e04dd7a6a1e23fc7b89e47682ea9d0a3946cf7bc59d07922c3db3efc52ae4269`,j=`redimnet-b1:${A}`,M=new o({path:`/sdk/voiceid/redimnet-b1.onnx`,sha256:A,bytes:8859593,label:`Speaker verification`});async function N(e){let t=await M.prepare(),n=new(await(import(`./ort.wasm.bundle.min-7y-hlRN8.js`))).Tensor(`float32`,e,[1,e.length]);try{let e=await t.run({samples:n});try{let t=e.embedding;if(!t||!(t.data instanceof Float32Array))throw Error(`VoiceID model returned an invalid embedding.`);return P(Array.from(t.data))}finally{for(let t of Object.values(e))t.dispose()}}finally{n.dispose()}}function P(e){if(e.length!==192)throw Error(`VoiceID template dimensions are invalid.`);let t=0;for(let n of e){if(!Number.isFinite(n))throw Error(`VoiceID template is invalid.`);t+=n*n}if(t<1e-12)throw Error(`VoiceID template contains no signal.`);let n=Math.sqrt(t),r=[];for(let t of e)r.push(t/n);return r}function F(e,t){if(e.length!==192||t.length!==192)throw Error(`VoiceID templates are incompatible.`);let n=0;for(let r=0;r<e.length;r+=1)n+=e[r]*t[r];return n}function I(e){return e.asset}function L(e,t){return e+t.size}var R=class{element=document.createElement(`section`);status=document.createElement(`p`);meter=document.createElement(`progress`);retryButton=document.createElement(`button`);retry=null;cancelled=new m;reveal;received=new Map;models;assets;total;constructor(t,n,r){this.parent=t,this.signal=n,this.models=[M,l(r),f],this.assets=[e,...this.models.map(I)],this.total=this.assets.reduce(L,0),this.element.className=`seams-voiceid-model-download`,this.status.setAttribute(`role`,`status`),this.status.setAttribute(`aria-live`,`polite`),this.meter.setAttribute(`aria-label`,`VoiceID download`),this.meter.style.width=`100%`,this.retryButton.type=`button`,this.retryButton.className=`seams-voiceid-primary`,this.retryButton.textContent=`Retry loading`,this.retryButton.hidden=!0,this.retryButton.addEventListener(`click`,this.retryLoading),this.element.append(this.status,this.meter,this.retryButton),this.status.textContent=`Loading saved VoiceID…`,this.reveal=setTimeout(t.append.bind(t,this.element),300),n.addEventListener(`abort`,this.abort,{once:!0})}settle=()=>{clearTimeout(this.reveal),this.signal.removeEventListener(`abort`,this.abort),this.retryButton.removeEventListener(`click`,this.retryLoading)};abort=()=>{let e=new DOMException(`VoiceID preparation was cancelled.`,`AbortError`);this.cancelled.reject(e),this.retry?.reject(e)};retryLoading=()=>{this.retry?.resolve(),this.retry=null};async prepare(){this.signal.throwIfAborted(),await Promise.race([this.prepareUntilReady(),this.cancelled.promise])}async prepareUntilReady(){for(;;){this.signal.throwIfAborted(),this.retryButton.hidden=!0,this.meter.hidden=!1;try{for(let e of this.assets)await e.download(this.update.bind(this,e)),this.signal.throwIfAborted();this.preparing();for(let e of this.models)await e.prepare(),this.signal.throwIfAborted();this.ready();return}catch(e){if(this.signal.throwIfAborted(),!(e instanceof a))throw e;clearTimeout(this.reveal),this.parent.append(this.element),this.element.dataset.state=`failed`,this.meter.hidden=!0,this.status.textContent=e.message,this.retry=new m,this.retryButton.hidden=!1,this.retryButton.focus(),await this.retry.promise}}}update=(e,t)=>{switch(t.kind){case`checking_cache`:return;case`downloading`:this.received.set(e,t.received),this.element.dataset.state=`downloading`;break;case`ready`:this.received.set(e,e.size);break;default:}if(this.element.dataset.state!==`downloading`)return;let n=0;for(let e of this.received.values())n+=e;this.meter.max=this.total,this.meter.value=n,this.status.textContent=`Downloading VoiceID: ${(n/1e6).toFixed(1)} of ${(this.total/1e6).toFixed(1)} MB · ${Math.floor(n/this.total*100)}%`};preparing(){this.element.dataset.state=`preparing`,this.meter.removeAttribute(`value`),this.status.textContent=`Preparing VoiceID…`}ready(){this.element.dataset.state=`ready`,this.meter.hidden=!0,this.status.textContent=`VoiceID is ready.`}};async function z(e,t,n){let r=new R(e,t,n);try{await r.prepare()}finally{r.settle()}}const B=.2,V=D({model:j,parseEnrollment:U});function H(e){if(!Array.isArray(e)||!e.every(v))throw Error(`VoiceID template is invalid.`);let t=P(e);for(let n=0;n<e.length;n+=1)if(Math.abs(e[n]-t[n])>1e-6)throw Error(`VoiceID template is not normalized.`);return e.slice()}function U(e,t){if(e===void 0)return null;let n=C(e,`VoiceID enrollment`),r=C(n.scope,`VoiceID enrollment scope`);if(n.kind!==`browser_voiceid_enrollment_v2`||r.walletId!==t.walletId||r.authorityId!==t.authorityId||r.walletAuthMethodId!==t.walletAuthMethodId||r.deviceKeyId!==t.deviceKeyId||typeof n.generation!=`string`||!n.generation||typeof n.revision!=`number`||!Number.isSafeInteger(n.revision)||n.revision<0)throw Error(`VoiceID enrollment identity is invalid.`);if(n.model!==j)return null;let i=H(n.baseline),a={kind:`baseline`};try{let e=C(n.adaptation,`VoiceID improvements`);if(e.kind===`adapted`&&Array.isArray(e.samples)&&e.samples.length===3){let t=e.samples.map(H);G(i,t),a={kind:`adapted`,samples:t}}}catch{a={kind:`baseline`}}return{kind:`browser_voiceid_enrollment_v2`,scope:t,model:j,generation:n.generation,revision:n.revision,baseline:i,adaptation:a}}function W(e,t){let n=Array(192).fill(0);for(let e of t)for(let r=0;r<n.length;r+=1)n[r]+=e[r]/t.length;let r=P(n);for(let t=0;t<n.length;t+=1)n[t]=(1-B)*e[t]+B*r[t];return P(n)}function G(e,t){if(t.length!==3)throw Error(`Record three samples before saving voice improvements.`);for(let n of t)if(F(e,n)<.45)throw Error(`This sample differs from your original voice. Try again or re-record your enrollment.`);if(F(e,W(e,t))<.98)throw Error(`These samples differ too much from your original voice. Re-record your enrollment instead.`)}function K(e){return V.read(e)}async function q(e,t){let n=await V.readTemplate(e,t),r=n.adaptation;return{template:r.kind===`adapted`?W(n.baseline,r.samples):[...n.baseline],baseline:[...n.baseline],kind:r.kind}}async function J(){let e=await V.readImportable();if(e===void 0)return null;let t=C(e,`Previous VoiceID enrollment`);return t.kind!==`browser_voiceid_enrollment_v1`||t.model!==j?null:H(t.template)}async function Y(e){let{scope:t,signal:n}=e;switch(e.kind){case`activate`:return V.commit({kind:`activate`,scope:t,signal:n,guard:e.guard,snapshot:e.snapshot,baseline:H(e.baseline)});case`import`:return V.commit({kind:`import`,scope:t,signal:n,guard:e.guard,snapshot:e.snapshot,confirmation:e.confirmation,baseline:H(e.baseline)});case`replace`:return V.commit({kind:`replace`,scope:t,signal:n,guard:e.guard,snapshot:e.snapshot,confirmation:e.confirmation,baseline:H(e.baseline)});case`improve`:return G(e.snapshot.enrollment.baseline,e.samples),V.commit({kind:`improve`,scope:t,signal:n,guard:e.guard,snapshot:e.snapshot,confirmation:e.confirmation,samples:e.samples});case`reset`:return V.commit({kind:`reset`,scope:t,signal:n,guard:e.guard,snapshot:e.snapshot,confirmation:e.confirmation});default:return X(e)}}function X(e){throw Error(`Unsupported VoiceID enrollment change.`)}async function Z(e,t,n,r){await z(e,n,r),n.throwIfAborted();let i=new ne(e,t,n,r);try{await i.result}finally{i.dispose()}}var ne=class{completion=new m;result=this.completion.promise;status;prompt;phrase=c(null);record;cancel;samples=[];phase=`ready`;get closed(){return this.phase===`closed`}controller=new AbortController;guardTimer;checkingGuard=!1;constructor(e,t,n,r){this.plan=t,this.signal=n,this.model=r,this.prompt=new d(e.ownerDocument,`Enroll your voice in this browser`,this.phrase),this.prompt.title.textContent=`Read aloud · 0 of 3 accepted`,this.status=this.prompt.status,this.record=this.prompt.record,this.cancel=this.prompt.cancel,this.record.textContent=`Record sample 1`,this.record.addEventListener(`click`,this.capture),this.cancel.setAttribute(`aria-label`,`Cancel enrollment`),this.cancel.addEventListener(`click`,this.cancelled),this.prompt.ready(t.kind===`improve`?`Read naturally. Your original enrollment stays unchanged.`:`Read the full phrase at your normal pace.`),e.replaceChildren(this.prompt.element),n.addEventListener(`abort`,this.cancelled,{once:!0}),this.guardTimer=setInterval(this.checkGuard,250),n.aborted?this.cancelled():this.record.focus()}capture=()=>{this.collect()};async collect(){if(this.phase!==`ready`)return;let e=this.samples.length+1;try{u(),this.phase=`recording`,this.record.disabled=!0,this.status.textContent=`Opening microphone…`;let e=await t(this.controller.signal,this.prompt.capture);if(this.signal.aborted||this.closed){e.kind===`captured`&&e.samples.fill(0);return}if(e.kind===`insufficient_audio`){this.status.textContent=n(e.quality);return}if(this.plan.kind===`improve`&&e.quality.noise!==`normal`){e.samples.fill(0),this.status.textContent=`Use a quiet place to improve your voice enrollment.`;return}let a;try{if(await h(this.plan.scope,this.plan.guard),this.status.textContent=`Checking your voice…`,(await i(e.samples,this.phrase,this.model)).kind===`mismatch`)throw new r;a=await N(e.samples)}finally{e.samples.fill(0)}if(this.signal.aborted||this.closed){a.fill(0);return}if(this.plan.kind===`improve`&&F(this.plan.snapshot.enrollment.baseline,a)<.45)throw a.fill(0),Error(`Your voice did not match the original enrollment. To replace it, return to Manage voice and choose Record voice again.`);for(let e of this.samples)if(F(e,a)<.45)throw a.fill(0),Error(`This sample differs from the earlier samples. Use the same microphone and try again.`);if(this.samples.push(a),this.samples.length<3){this.prompt.accepted(),this.status.textContent=`Sample ${this.samples.length} accepted. Record the next sample.`,this.record.textContent=`Record sample ${this.samples.length+1}`;return}let o=Array(192).fill(0);for(let e of this.samples)for(let t=0;t<o.length;t+=1)o[t]+=e[t]/3;this.phase=`committing`,this.prompt.title.textContent=`3 of 3 accepted`,this.status.textContent=`Saving voice enrollment…`;let s=this.plan;switch(s.kind){case`activate`:await Y({kind:`activate`,scope:s.scope,guard:s.guard,snapshot:s.snapshot,baseline:P(o),signal:this.controller.signal});break;case`replace`:await Y({kind:`replace`,scope:s.scope,guard:s.guard,snapshot:s.snapshot,baseline:P(o),confirmation:s.confirmation,signal:this.controller.signal});break;case`improve`:await Y({kind:`improve`,scope:s.scope,guard:s.guard,snapshot:s.snapshot,samples:this.samples,confirmation:s.confirmation,signal:this.controller.signal});break;default:return re(s)}if(this.signal.aborted||this.closed)return;this.phase=`closed`,this.prompt.accepted(),this.status.textContent=`Voice enrollment saved.`,this.completion.resolve()}catch(e){if(!this.closed&&this.phase===`committing`){this.phase=`closed`,this.controller.abort(),this.completion.reject(E(e));return}!this.closed&&this.samples.length===3&&this.samples.pop()?.fill(0),this.closed||(this.status.textContent=e instanceof Error?e.message:`VoiceID enrollment failed.`)}finally{this.closed||(this.samples.length<e&&(this.record.textContent=`Retry sample ${e}`,this.status.textContent=`Sample ${e} was not accepted. ${this.status.textContent??``}`),this.phrase=c(this.phrase),this.prompt.setPhrase(this.phrase),this.prompt.title.textContent=`Read aloud · ${this.samples.length} of 3 accepted`,this.prompt.ready(this.status.textContent??``),this.phase=`ready`,this.record.disabled=!1)}}cancelled=()=>{this.closed||(this.phase=`closed`,this.controller.abort(),this.completion.reject(Error(`VoiceID enrollment was cancelled.`)))};checkGuard=()=>{this.closed||this.checkingGuard||(this.checkingGuard=!0,this.validateGuard())};async validateGuard(){try{await h(this.plan.scope,this.plan.guard)}catch(e){this.closed||(this.phase=`closed`,this.controller.abort(),this.completion.reject(E(e)))}finally{this.checkingGuard=!1}}dispose(){this.phase=`closed`,this.controller.abort(),clearInterval(this.guardTimer),this.signal.removeEventListener(`abort`,this.cancelled),this.prompt.dispose(),this.record.removeEventListener(`click`,this.capture),this.cancel.removeEventListener(`click`,this.cancelled);for(let e of this.samples)e.fill(0)}};function re(e){throw Error(`Unsupported VoiceID enrollment capture.`)}async function Q(e,n,r,a,o){let s=await t(n,r);if(s.kind===`insufficient_audio`)return n.throwIfAborted(),s;try{n.throwIfAborted();let t=await i(s.samples,a,o),r=await N(s.samples),c;try{n.throwIfAborted();let t=await q(e,n);try{c=F(r,t.template)}finally{t.template.fill(0),t.baseline.fill(0)}}finally{r.fill(0)}return c<.45?{kind:`mismatch`,quality:s.quality}:t.kind===`matched`?{kind:`matched`}:{kind:`phrase_mismatch`}}finally{s.samples.fill(0)}}async function ie(e,t,n){u();let r=await _(n.walletId,n.walletAuthMethodId),i=await w(r);if(await K(i))await $({kind:`activation`,scope:i,owner:await x(i,`activating`)},n.signal,t.onCapture,t.phrase,e);else{let r=await x(i,`activating`);await Z(t.parent,{kind:`activate`,scope:i,guard:r,snapshot:{kind:`missing`}},n.signal,e)}n.signal.throwIfAborted();let a=await y(r);if(n.signal.aborted)throw a.fill(0),Error(`VoiceID activation was cancelled.`);return{kind:`activated`,publicKeyB64u:r.publicKeyB64u,deviceKeyId:r.deviceKeyId,factorSecret32:a}}async function ae(e,t,n){ee();let{challenge:r}=n;if(r.requestOrigin!==location.origin||r.expiresAtMs<=Date.now())throw Error(`VoiceID challenge is expired or belongs to another Wallet origin.`);let i=await S(r.walletId,r.walletAuthMethodId);if(!i||i.deviceKeyId!==r.deviceKeyId)throw Error(`VoiceID device key is unavailable in this browser.`);if(await $({kind:`active`,scope:await T(r.walletId,r.walletAuthMethodId)},n.signal,t.onCapture,t.phrase,e),n.signal.throwIfAborted(),r.expiresAtMs<=Date.now())throw Error(`VoiceID challenge expired. Start unlock again.`);let a=await crypto.subtle.sign(`Ed25519`,i.privateKey,k(r)),o=await y(i);if(n.signal.aborted||r.expiresAtMs<=Date.now())throw o.fill(0),Error(`VoiceID unlock expired or was cancelled.`);return{kind:`unlocked`,assertion:te({kind:`voiceid_device_assertion_v1`,challenge:r,signatureB64u:g(new Uint8Array(a))}),factorSecret32:o}}async function oe(e,t,n){u();let{statement:r}=n;if(r.requestOrigin!==location.origin||r.expiresAtMs<=Date.now())throw Error(`VoiceID check is expired or belongs to another Wallet origin.`);let i=await S(r.walletId,r.walletAuthMethodId);if(!i||i.deviceKeyId!==r.deviceKeyId)throw Error(`VoiceID device key is unavailable in this browser.`);if(await $({kind:`active`,scope:await T(r.walletId,r.walletAuthMethodId)},n.signal,t.onCapture,t.phrase,e),n.signal.throwIfAborted(),r.expiresAtMs<=Date.now())throw Error(`VoiceID check expired. Start again.`);let a=await crypto.subtle.sign(`Ed25519`,i.privateKey,O(r)),o=await y(i);if(n.signal.aborted||r.expiresAtMs<=Date.now())throw o.fill(0),Error(`VoiceID check expired or was cancelled.`);return{kind:`confirmed`,assertion:b({kind:`voiceid_operation_assertion_v1`,statement:r,signatureB64u:g(new Uint8Array(a))}),factorSecret32:o}}async function $(e,t,n,i,a){let o=await Q(e,t,n,i,a);switch(o.kind){case`matched`:return;case`insufficient_audio`:throw new s(o.quality);case`mismatch`:throw new p(o.quality);case`phrase_mismatch`:throw new r;default:return se(o)}}function se(e){throw Error(`Unexpected voice result: ${String(e)}`)}export{Y as a,z as c,Z as i,oe as n,J as o,ae as r,K as s,ie as t};
@@ -1,4 +1,4 @@
1
- import{d as e,h as t,i as n,l as r,n as i,r as a,t as o,y as s}from"./prompt-yshUSnwN.js";import{SeamsWordmark as c,VOICE_ID_HOST_API_VERSION as l,WorkerDeferred as u,authMenuSuccessCopy as d,backIcon as f,createCspStylesheetManager as p,createWalletIframeSurfaceMeasurementReporter as m,getDefaultCspNonce as h,toError as g}from"../voiceid-host.js";const _=new WeakMap;var v=class{footer;constructor(e,t){this.prompt=t;let n=e.ownerDocument,r=_.get(n);r||(r=p({doc:n,baseCss:`
1
+ import{d as e,i as t,l as n,m as r,n as i,r as a,t as o,v as s}from"./prompt-DsRKGfyb.js";import{SeamsWordmark as c,VOICE_ID_HOST_API_VERSION as l,WorkerDeferred as u,authMenuSuccessCopy as d,backIcon as f,createCspStylesheetManager as p,createWalletIframeSurfaceMeasurementReporter as m,getDefaultCspNonce as h,toError as g}from"../voiceid-host.js";const _=new WeakMap;var v=class{footer;constructor(e,t){this.prompt=t;let n=e.ownerDocument,r=_.get(n);r||(r=p({doc:n,baseCss:`
2
2
  .seams-voiceid-activation-surface.seams-voiceid-unlock {
3
3
  box-sizing: border-box;
4
4
  overflow: hidden;
@@ -52,7 +52,8 @@ import{d as e,h as t,i as n,l as r,n as i,r as a,t as o,y as s}from"./prompt-ysh
52
52
  padding: 0.75rem 1.5rem 1.5rem;
53
53
  grid-template-rows: auto minmax(6rem, auto) 5rem minmax(3.375rem, auto) auto auto;
54
54
  }
55
- .seams-voiceid-unlock .seams-voiceid-record-slot:has(button[hidden]) {
55
+ /* Only a hidden record button empties the slot; the microphone choice is hidden by default. */
56
+ .seams-voiceid-unlock .seams-voiceid-record-slot:has(> .seams-voiceid-primary[hidden]) {
56
57
  display: none;
57
58
  }
58
59
  .seams-voiceid-unlock-footer {
@@ -77,4 +78,4 @@ import{d as e,h as t,i as n,l as r,n as i,r as a,t as o,y as s}from"./prompt-ysh
77
78
  color: var(--seams-colors-textPrimary, #eff2f3);
78
79
  }
79
80
  }
80
- `,nonce:h}),_.set(n,r)),r.ensureBase(),e.classList.add(`seams-voiceid-unlock`);let o=n.createElement(`div`);o.className=`seams-voiceid-unlock-header`,t.cancel.className=`seams-voiceid-unlock-back`,t.cancel.setAttribute(`aria-label`,`Back`),t.cancel.replaceChildren(),i(f(),t.cancel),o.append(t.cancel),e.prepend(o),this.footer=n.createElement(`div`),this.footer.className=`seams-voiceid-unlock-footer`,i([a(`span`,null,`Secured by`),` `,a(c,{})],this.footer),e.append(this.footer)}succeed(){let e=this.prompt.cancel;return e.animate([{opacity:1},{opacity:0}],{duration:150,easing:`ease-in`,fill:`forwards`}),{top:e.getBoundingClientRect().top,bottom:this.footer.getBoundingClientRect().top}}dispose(){i(null,this.prompt.cancel),i(null,this.footer)}},y=class{result;completion=new u;controller=new AbortController;element;activateButton;cancelButton;status;prompt;unlockFrame;autoStartUnlock;unmountCapture;measurement;phrase=t(null);state={kind:`preparing`};settlement=`closed`;expiryTimer=null;constructor(e,t,n,r){let i=t===`activation`?`Activate VoiceID`:`Unlock with VoiceID`,a=document.getElementById(`root`)??document.body;if(!a)throw Error(`Wallet activation surface has no mount root.`);this.element=document.createElement(`section`),this.element.className=`seams-wallet-ui seams-auth-method-activation-surface seams-voiceid-activation-surface`,this.element.setAttribute(`role`,`dialog`),this.element.setAttribute(`aria-modal`,`true`),this.element.setAttribute(`aria-label`,i),this.prompt=new o(document,i,this.phrase),this.element.append(this.prompt.element),this.unlockFrame=t===`unlock`?new v(this.element,this.prompt):null,this.autoStartUnlock=this.unlockFrame!==null&&r!==void 0,this.status=this.prompt.status,this.status.textContent=`Loading VoiceID…`,this.prompt.title.textContent=`VoiceID`,this.element.dataset.preparation=`loading`,this.activateButton=this.prompt.record,this.activateButton.textContent=i,this.activateButton.disabled=!0,this.activateButton.hidden=this.autoStartUnlock,this.activateButton.addEventListener(`click`,this.begin),this.cancelButton=this.prompt.cancel,this.cancelButton.addEventListener(`click`,this.cancel),this.element.addEventListener(`cancel`,this.cancel),this.unmountCapture=r?r(this.element):null,r||a.appendChild(this.element),this.measurement=!r&&n.kind===`wallet_iframe`?m({kind:`request_surface`,element:this.element,requestId:n.requestId,postMeasurement:n.postMeasurement}):null,this.cancelButton.focus(),this.result=this.completion.promise,this.initialize(e)}dispose(){this.measurement?.disconnect(),this.expiryTimer!==null&&clearTimeout(this.expiryTimer),this.element.removeEventListener(`cancel`,this.cancel),this.activateButton.removeEventListener(`click`,this.begin),this.cancelButton.removeEventListener(`click`,this.cancel),this.unlockFrame?.dispose(),this.prompt.dispose(),this.unmountCapture?this.unmountCapture(this.settlement):this.element.remove()}async initialize(e){try{await new Promise(S),this.controller.signal.throwIfAborted();let t=await e(this.controller.signal,{parent:this.element,progress:this.prompt.progress});if(this.state.kind===`closed`)return;if(t.kind===`management`){this.element.setAttribute(`aria-label`,`Manage voice`),this.element.dataset.preparation=`ready`,this.state={kind:`capturing`};let e=await t.action(this.controller.signal);this.controller.signal.throwIfAborted(),this.state={kind:`closed`},this.completion.resolve(e);return}let n=t.expiresAtMs===null?null:t.expiresAtMs-Date.now();if(n!==null&&n<=0)throw Error(`VoiceID challenge expired. Start unlock again.`);this.state={kind:`ready`,capture:t},this.expiryTimer=n===null?null:setTimeout(this.expire,n);for(let e of this.element.querySelectorAll(`.seams-voiceid-model-download`))e.remove();this.element.dataset.preparation=`ready`,this.prompt.title.textContent=`Read aloud`,this.status.textContent=`Read naturally, then pause.`,this.activateButton.disabled=!1,this.autoStartUnlock?this.begin():this.activateButton.focus()}catch(e){if(this.state.kind===`closed`)return;this.state={kind:`closed`},this.completion.reject(g(e))}}begin=()=>{if(this.state.kind!==`ready`)return;let e=this.state.capture;this.state={kind:`capturing`},this.perform(e)};async perform(i){this.activateButton.disabled=!0,this.status.textContent=`Opening microphone…`;try{let e=await i.action(this.controller.signal,this.prompt.capture,this.phrase);if(this.state.kind===`closed`)return;this.unlockFrame&&await this.showUnlocked(this.unlockFrame),this.state={kind:`closed`},this.completion.resolve(e)}catch(a){if(this.state.kind===`closed`)return;if(a instanceof n||a instanceof e||a instanceof r||a instanceof s){if(i.expiresAtMs!==null&&Date.now()>=i.expiresAtMs){this.expire();return}let e=this.phrase;this.phrase=t(e),this.prompt.setPhrase(this.phrase),this.state={kind:`ready`,capture:i},this.activateButton.hidden=!1,this.activateButton.textContent=`Try again`,this.prompt.ready(a.message),this.activateButton.focus();return}this.state={kind:`closed`},this.completion.reject(g(a))}}async showUnlocked(e){this.state={kind:`succeeding`},this.settlement=`succeeded`,this.expiryTimer!==null&&clearTimeout(this.expiryTimer),this.expiryTimer=null,this.cancelButton.disabled=!0;let t=performance.now(),n=d(`authenticated`);await this.prompt.succeed(n.headline,n.message,e.succeed());let r=Math.max(0,1300-(performance.now()-t));await this.element.animate([{opacity:1},{opacity:1,offset:r/(r+240),easing:`ease-in`},{opacity:0}],{duration:r+240,fill:`forwards`}).finished}cancel=()=>{this.state.kind===`closed`||this.state.kind===`succeeding`||(this.state={kind:`closed`},this.controller.abort(),this.completion.reject(new b))};expire=()=>{this.state.kind===`closed`||this.state.kind===`succeeding`||(this.state={kind:`closed`},this.controller.abort(),this.completion.reject(Error(`VoiceID challenge expired. Start unlock again.`)))}},b=class extends Error{constructor(){super(`VoiceID capture was cancelled.`),this.name=`VoiceIdCaptureCancelledError`}};async function x(e){let t=new y(e.prepare,e.purpose,e.measurement,e.mountCapture);try{return await t.result}finally{t.dispose()}}function S(e){requestAnimationFrame(C.bind(null,e))}function C(e){setTimeout(e,0)}const w=6;if(l!==6)throw Error(`VoiceID requires a matching Wallet host API.`);function T(e){return{unlock:E.bind(null,e),activate:O.bind(null,e),review:A.bind(null,e)}}function E(e,t){return x({purpose:`unlock`,measurement:t.surfaceMeasurementBinding,mountCapture:t.mountCapture,prepare:D.bind(null,e,t)})}async function D(e,t,n,r){let i=await import(`./unlock-CjaOTqR2.js`);return n.throwIfAborted(),i.prepareUnlockSurface(t,n,r.progress,e)}async function O(e,t){try{return await x({purpose:`activation`,measurement:t.surfaceMeasurementBinding,prepare:k.bind(null,e,t)})}catch(e){if(e instanceof b)return{kind:`cancelled`};throw e}}async function k(e,t,n,r){let i=await import(`./addVoiceId-DAtqPP1y.js`);return n.throwIfAborted(),i.prepareActivationSurface(t,n,r.parent,e)}async function A(e,t){return(await import(`./approval-CQfImado.js`)).VoiceIdApproval.forReview(t,e)}export{T as createVoiceIdHostPlugin,w as hostApiVersion};
81
+ `,nonce:h}),_.set(n,r)),r.ensureBase(),e.classList.add(`seams-voiceid-unlock`);let o=n.createElement(`div`);o.className=`seams-voiceid-unlock-header`,t.cancel.className=`seams-voiceid-unlock-back`,t.cancel.setAttribute(`aria-label`,`Back`),t.cancel.replaceChildren(),i(f(),t.cancel),o.append(t.cancel),e.prepend(o),this.footer=n.createElement(`div`),this.footer.className=`seams-voiceid-unlock-footer`,i([a(`span`,null,`Secured by`),` `,a(c,{})],this.footer),e.append(this.footer)}succeed(){let e=this.prompt.cancel;return e.animate([{opacity:1},{opacity:0}],{duration:150,easing:`ease-in`,fill:`forwards`}),{top:e.getBoundingClientRect().top,bottom:this.footer.getBoundingClientRect().top}}dispose(){i(null,this.prompt.cancel),i(null,this.footer)}};const y={activation:`Activate VoiceID`,unlock:`Unlock with VoiceID`,export:`Export keys with VoiceID`};var b=class{result;completion=new u;controller=new AbortController;element;activateButton;cancelButton;status;prompt;unlockFrame;autoStartUnlock;unmountCapture;measurement;phrase=r(null);state={kind:`preparing`};settlement=`closed`;expiryTimer=null;constructor(e,t,n,r){let i=y[t],a=document.getElementById(`root`)??document.body;if(!a)throw Error(`Wallet activation surface has no mount root.`);this.element=document.createElement(`section`),this.element.className=`seams-wallet-ui seams-auth-method-activation-surface seams-voiceid-activation-surface`,this.element.setAttribute(`role`,`dialog`),this.element.setAttribute(`aria-modal`,`true`),this.element.setAttribute(`aria-label`,i),this.prompt=new o(document,i,this.phrase),this.element.append(this.prompt.element),this.unlockFrame=t===`unlock`?new v(this.element,this.prompt):null,this.autoStartUnlock=this.unlockFrame!==null&&r!==void 0,this.status=this.prompt.status,this.status.textContent=`Loading VoiceID…`,this.prompt.title.textContent=`VoiceID`,this.element.dataset.preparation=`loading`,this.activateButton=this.prompt.record,this.activateButton.textContent=i,this.activateButton.disabled=!0,this.activateButton.hidden=this.autoStartUnlock,this.activateButton.addEventListener(`click`,this.begin),this.cancelButton=this.prompt.cancel,this.cancelButton.addEventListener(`click`,this.cancel),this.element.addEventListener(`cancel`,this.cancel),this.unmountCapture=r?r(this.element):null,r||a.appendChild(this.element),this.measurement=!r&&n.kind===`wallet_iframe`?m({kind:`request_surface`,element:this.element,requestId:n.requestId,postMeasurement:n.postMeasurement}):null,this.cancelButton.focus(),this.result=this.completion.promise,this.initialize(e)}dispose(){this.measurement?.disconnect(),this.expiryTimer!==null&&clearTimeout(this.expiryTimer),this.element.removeEventListener(`cancel`,this.cancel),this.activateButton.removeEventListener(`click`,this.begin),this.cancelButton.removeEventListener(`click`,this.cancel),this.unlockFrame?.dispose(),this.prompt.dispose(),this.unmountCapture?this.unmountCapture(this.settlement):this.element.remove()}async initialize(e){try{await new Promise(C),this.controller.signal.throwIfAborted();let t=await e(this.controller.signal,{parent:this.element,progress:this.prompt.progress});if(this.state.kind===`closed`)return;if(t.kind===`management`){this.element.setAttribute(`aria-label`,`Manage voice`),this.element.dataset.preparation=`ready`,this.state={kind:`capturing`};let e=await t.action(this.controller.signal);this.controller.signal.throwIfAborted(),this.state={kind:`closed`},this.completion.resolve(e);return}let n=t.expiresAtMs===null?null:t.expiresAtMs-Date.now();if(n!==null&&n<=0)throw Error(`VoiceID challenge expired. Start unlock again.`);this.state={kind:`ready`,capture:t},this.expiryTimer=n===null?null:setTimeout(this.expire,n);for(let e of this.element.querySelectorAll(`.seams-voiceid-model-download`))e.remove();this.element.dataset.preparation=`ready`,this.prompt.title.textContent=`Read aloud`,this.status.textContent=`Read naturally, then pause.`,this.activateButton.disabled=!1,this.autoStartUnlock?this.begin():this.activateButton.focus()}catch(e){if(this.state.kind===`closed`)return;this.state={kind:`closed`},this.completion.reject(g(e))}}begin=()=>{if(this.state.kind!==`ready`)return;let e=this.state.capture;this.state={kind:`capturing`},this.perform(e)};async perform(i){this.activateButton.disabled=!0,this.status.textContent=`Opening microphone…`;try{let e=await i.action(this.controller.signal,this.prompt.capture,this.phrase);if(this.state.kind===`closed`)return;this.unlockFrame&&await this.showUnlocked(this.unlockFrame),this.state={kind:`closed`},this.completion.resolve(e)}catch(a){if(this.state.kind===`closed`)return;if(a instanceof t||a instanceof e||a instanceof n||a instanceof s){if(i.expiresAtMs!==null&&Date.now()>=i.expiresAtMs){this.expire();return}let e=this.phrase;this.phrase=r(e),this.prompt.setPhrase(this.phrase),this.state={kind:`ready`,capture:i},this.activateButton.hidden=!1,this.activateButton.textContent=`Try again`,this.prompt.ready(a.message),this.activateButton.focus();return}this.state={kind:`closed`},this.completion.reject(g(a))}}async showUnlocked(e){this.state={kind:`succeeding`},this.settlement=`succeeded`,this.expiryTimer!==null&&clearTimeout(this.expiryTimer),this.expiryTimer=null,this.cancelButton.disabled=!0;let t=performance.now(),n=d(`authenticated`);await this.prompt.succeed(n.headline,n.message,e.succeed());let r=Math.max(0,1300-(performance.now()-t));await this.element.animate([{opacity:1},{opacity:1,offset:r/(r+240),easing:`ease-in`},{opacity:0}],{duration:r+240,fill:`forwards`}).finished}cancel=()=>{this.state.kind===`closed`||this.state.kind===`succeeding`||(this.state={kind:`closed`},this.controller.abort(),this.completion.reject(new x))};expire=()=>{this.state.kind===`closed`||this.state.kind===`succeeding`||(this.state={kind:`closed`},this.controller.abort(),this.completion.reject(Error(`VoiceID challenge expired. Start unlock again.`)))}},x=class extends Error{constructor(){super(`VoiceID capture was cancelled.`),this.name=`VoiceIdCaptureCancelledError`}};async function S(e){let t=new b(e.prepare,e.purpose,e.measurement,e.mountCapture);try{return await t.result}finally{t.dispose()}}function C(e){requestAnimationFrame(w.bind(null,e))}function w(e){setTimeout(e,0)}const T=8;if(l!==8)throw Error(`VoiceID requires a matching Wallet host API.`);function E(e){return{unlock:k.bind(null,e),activate:j.bind(null,e),confirmOperation:D.bind(null,e)}}function D(e,t){return S({purpose:`export`,measurement:t.surfaceMeasurementBinding,prepare:O.bind(null,e,t)})}async function O(e,t,n,r){let i=await import(`./operation-wZl5lO4B.js`);return n.throwIfAborted(),i.prepareOperationSurface(t,n,r.progress,e)}function k(e,t){return S({purpose:`unlock`,measurement:t.surfaceMeasurementBinding,mountCapture:t.mountCapture,prepare:A.bind(null,e,t)})}async function A(e,t,n,r){let i=await import(`./unlock-C0uEZewu.js`);return n.throwIfAborted(),i.prepareUnlockSurface(t,n,r.progress,e)}async function j(e,t){try{return await S({purpose:`activation`,measurement:t.surfaceMeasurementBinding,prepare:M.bind(null,e,t)})}catch(e){if(e instanceof x)return{kind:`cancelled`};throw e}}async function M(e,t,n,r){let i=await import(`./addVoiceId-B8P6WQsb.js`);return n.throwIfAborted(),i.prepareActivationSurface(t,n,r.parent,e)}export{E as createVoiceIdHostPlugin,T as hostApiVersion};
@@ -0,0 +1 @@
1
+ import"./prompt-DsRKGfyb.js";import{c as e,n as t,s as n}from"./deviceBridge-C8Jp8coS.js";async function r(t,r,a,o){let s=await t.prepareOperation(r);if(!await n(s.scope))throw Error(`Open Manage voice to import or record your enrollment on this device.`);await e(a,r,o),r.throwIfAborted();let c=await s.challenge(r);return r.throwIfAborted(),{kind:`recording`,expiresAtMs:c.expiresAtMs,action:i.bind(null,o,c)}}function i(e,n,r,i,a){return n.complete(t.bind(null,e,{onCapture:i,phrase:a}),r)}export{r as prepareOperationSurface};