@dexterai/x402 3.11.0 → 3.12.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.
Files changed (41) hide show
  1. package/dist/adapters/index.d.cts +2 -2
  2. package/dist/adapters/index.d.ts +2 -2
  3. package/dist/batch-settlement/index.d.cts +3 -3
  4. package/dist/batch-settlement/index.d.ts +3 -3
  5. package/dist/batch-settlement/seller/index.d.cts +4 -4
  6. package/dist/batch-settlement/seller/index.d.ts +4 -4
  7. package/dist/client/index.cjs +1 -1
  8. package/dist/client/index.d.cts +12 -3
  9. package/dist/client/index.d.ts +12 -3
  10. package/dist/client/index.js +1 -1
  11. package/dist/react/index.cjs +1 -1
  12. package/dist/react/index.d.cts +4 -4
  13. package/dist/react/index.d.ts +4 -4
  14. package/dist/react/index.js +1 -1
  15. package/dist/server/index.cjs +4 -4
  16. package/dist/server/index.d.cts +11 -7
  17. package/dist/server/index.d.ts +11 -7
  18. package/dist/server/index.js +3 -3
  19. package/dist/tab/adapters/solana/index.cjs +1 -1
  20. package/dist/tab/adapters/solana/index.d.cts +21 -26
  21. package/dist/tab/adapters/solana/index.d.ts +21 -26
  22. package/dist/tab/adapters/solana/index.js +1 -1
  23. package/dist/tab/index.cjs +4 -4
  24. package/dist/tab/index.d.cts +16 -4
  25. package/dist/tab/index.d.ts +16 -4
  26. package/dist/tab/index.js +3 -3
  27. package/dist/tab/seller/index.cjs +4 -4
  28. package/dist/tab/seller/index.d.cts +1 -0
  29. package/dist/tab/seller/index.d.ts +1 -0
  30. package/dist/tab/seller/index.js +4 -4
  31. package/dist/{types-C8lyIOmX.d.cts → types-BdS_uvNs.d.cts} +1 -1
  32. package/dist/{types-BtibqM00.d.cts → types-D1HhKTg7.d.cts} +21 -1
  33. package/dist/{types-BtibqM00.d.ts → types-D1HhKTg7.d.ts} +21 -1
  34. package/dist/{types-D9VMq7In.d.ts → types-DY8xKheW.d.ts} +1 -1
  35. package/dist/{types-CiPcPs0w.d.cts → types-WMqs5xII.d.cts} +1 -1
  36. package/dist/{types-CTl7yVq6.d.ts → types-_wdHzVG-.d.ts} +1 -1
  37. package/dist/{types-RxdlGPaG.d.cts → types-xQu1U4xk.d.cts} +4 -2
  38. package/dist/{types-RxdlGPaG.d.ts → types-xQu1U4xk.d.ts} +4 -2
  39. package/dist/{x402-client-CHrU2Bs6.d.cts → x402-client-DmRbvqoc.d.cts} +1 -1
  40. package/dist/{x402-client-CzseAnIt.d.ts → x402-client-Ug0vrj74.d.ts} +1 -1
  41. package/package.json +2 -2
@@ -1,7 +1,11 @@
1
- import { O as OpenTabOptions, T as Tab, R as ResumeTabOptions } from '../types-BtibqM00.js';
2
- export { S as SessionScopeExceededError, b as TabCloseResult, c as TabClosedError, a as TabState, U as UnsupportedNetworkError, V as VaultAdapter } from '../types-BtibqM00.js';
3
- import { HumanAmount, AtomicAmount } from '@dexterai/vault/types';
1
+ import { O as OpenTabOptions, T as Tab, R as ResumeTabOptions } from '../types-D1HhKTg7.js';
2
+ export { S as SessionScopeExceededError, b as TabCloseResult, c as TabClosedError, a as TabState, U as UnsupportedNetworkError, V as VaultAdapter } from '../types-D1HhKTg7.js';
3
+ import { HumanAmount, AtomicAmount, SignedVoucher } from '@dexterai/vault/types';
4
4
  export { AtomicAmount, HumanAmount, SessionKey, SessionScope, SignedVoucher, TabNetworkId, VoucherPayload } from '@dexterai/vault/types';
5
+ export { SessionRegisterMessageArgs, SessionRevokeMessageArgs, VoucherPayloadBytes, buildVoucherMessage, sessionRegisterMessage, sessionRevokeMessage, voucherPayloadMessage } from '@dexterai/vault/messages';
6
+ export { BuildRegisterSessionKeyArgs, BuildRevokeSessionKeyArgs, buildRegisterSessionKeyInstruction, buildRevokeSessionKeyInstruction } from '@dexterai/vault/instructions';
7
+ export { buildSecp256r1VerifyInstruction } from '@dexterai/vault/precompile';
8
+ export { DEXTER_VAULT_PROGRAM_ID, INSTRUCTIONS_SYSVAR_ID, SECP256R1_PROGRAM_ID } from '@dexterai/vault/constants';
5
9
 
6
10
  /**
7
11
  * The `Tab` runtime — the live object returned by `openTab()`.
@@ -27,7 +31,15 @@ declare const DEFAULT_FACILITATOR_URL = "https://x402.dexter.cash";
27
31
  */
28
32
  declare function humanToAtomic(human: HumanAmount, decimals?: number): AtomicAmount;
29
33
  declare function atomicToHuman(atomic: AtomicAmount, decimals?: number): HumanAmount;
34
+ /**
35
+ * Serialize a signed voucher to the `X-Tab-Voucher` header value:
36
+ * base64-encoded JSON with hex-encoded byte fields. This is THE wire
37
+ * encoding the seller middleware and the facilitator's `/tab/settle`
38
+ * endpoint both parse — `stream()` uses it, and `payAndFetch` uses it to
39
+ * pay a `tab`-scheme accepts entry directly.
40
+ */
41
+ declare function voucherToHeader(signed: SignedVoucher): string;
30
42
  declare function openTab(options: OpenTabOptions): Promise<Tab>;
31
43
  declare function resumeTab(_options: ResumeTabOptions): Promise<Tab>;
32
44
 
33
- export { DEFAULT_FACILITATOR_URL, OpenTabOptions, ResumeTabOptions, Tab, atomicToHuman, humanToAtomic, openTab, resumeTab };
45
+ export { DEFAULT_FACILITATOR_URL, OpenTabOptions, ResumeTabOptions, Tab, atomicToHuman, humanToAtomic, openTab, resumeTab, voucherToHeader };
package/dist/tab/index.js CHANGED
@@ -1,6 +1,6 @@
1
- var m=class extends Error{constructor(r){super(`Network ${r} is not yet supported by @dexterai/x402/tab`);this.network=r;this.name="UnsupportedNetworkError"}},d=class extends Error{constructor(r,n){super(`Session scope exceeded: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="SessionScopeExceededError"}},c=class extends Error{constructor(r){super(`Tab ${r} is already closed`);this.channelId=r;this.name="TabClosedError"}};import{PublicKey as w}from"@solana/web3.js";import{bytesToHex as u}from"@noble/hashes/utils";import B from"tweetnacl";import{sessionRegisterMessage as N,sessionRevokeMessage as R,voucherPayloadMessage as x,buildVoucherMessage as O}from"@dexterai/vault/messages";import{sha256 as T}from"@noble/hashes/sha256";function b(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let r=new TextEncoder().encode(e.sellerUrl),n=T.create();return n.update(e.vaultPda.toBytes()),n.update(r),n.update(t),n.digest()}var v=3600,A="https://x402.dexter.cash",f=6;function y(e,t=f){if(!/^\d+(\.\d+)?$/.test(e))throw new Error(`amount must be a non-negative decimal string, got "${e}"`);let[r,n=""]=e.split(".");if(n.length>t)throw new Error(`amount "${e}" has more than ${t} decimals`);let i=n.padEnd(t,"0"),o=`${r}${i}`.replace(/^0+(?=\d)/,"");return o===""?"0":o}function p(e,t=f){if(!/^\d+$/.test(e))throw new Error(`atomic must be a non-negative integer string, got "${e}"`);let r=e.padStart(t+1,"0"),n=r.slice(0,-t).replace(/^0+(?=\d)/,"")||"0",i=r.slice(-t).replace(/0+$/,"");return i?`${n}.${i}`:n}var g=class{channelId;network;internals;cumulativeAtomic=0n;sequenceNumber=0;closed=!1;lastSignedVoucher=null;constructor(t){this.internals=t,this.channelId=t.channelIdHex,this.network=t.network}get state(){let t=this.internals.totalCapAtomic-this.cumulativeAtomic,r=Math.floor(Date.now()/1e3);return{isOpen:!this.closed,spent:p(this.cumulativeAtomic.toString()),remaining:p(t.toString()),expiresInSec:Math.max(0,this.internals.expiresAtUnix-r)}}async signNextVoucher(t){if(this.closed)throw new c(this.channelId);let r=BigInt(t);if(r<=0n)throw new Error(`voucher increment must be > 0, got ${t}`);if(r>this.internals.perUnitCapAtomic)throw new d("cap_exceeded",`single voucher increment ${r} exceeds perUnitCap ${this.internals.perUnitCapAtomic}`);let n=this.cumulativeAtomic+r;if(n>this.internals.totalCapAtomic)throw new d("cap_exceeded",`cumulative ${n} would exceed totalCap ${this.internals.totalCapAtomic}`);this.sequenceNumber+=1,this.cumulativeAtomic=n;let i={channelId:this.channelId,cumulativeAmount:this.cumulativeAtomic.toString(),sequenceNumber:this.sequenceNumber},o=await this.internals.vault.signWithSession(this.internals.session,i);return this.lastSignedVoucher=o,o}async stream(t,r){if(this.closed)throw new c(this.channelId);let n=await this.signNextVoucher(this.internals.perUnitCapAtomic.toString()),i=Buffer.from(JSON.stringify({payload:n.payload,sessionPublicKey:u(n.sessionPublicKey),sessionRegistration:u(n.sessionRegistration),sessionSignature:u(n.sessionSignature)}),"utf8").toString("base64"),o=new Headers(r?.headers);o.set("X-Tab-Voucher",i),o.set("Accept","text/event-stream");let s=await fetch(t,{...r,headers:o});if(!s.ok){let a=await s.text().catch(()=>"");throw new Error(`tab.stream HTTP ${s.status}: ${a.slice(0,500)}`)}if(!s.body)throw new Error("tab.stream response has no body");return C(s.body)}async close(){if(this.closed)throw new c(this.channelId);let t="";return this.lastSignedVoucher&&this.cumulativeAtomic>0n&&(t=await I(this.internals.facilitatorUrl,this.lastSignedVoucher,this.internals.network)),await this.internals.vault.signCloseTab(this.internals.session,this.channelId,this.cumulativeAtomic.toString()),this.closed=!0,this.internals.session.privateKey.fill(0),{settledAmount:p(this.cumulativeAtomic.toString()),settleTx:t}}};async function I(e,t,r){let n=`${e.replace(/\/$/,"")}/tab/settle`,i={channelId:t.payload.channelId,cumulativeAmount:t.payload.cumulativeAmount,sequenceNumber:t.payload.sequenceNumber,sessionPublicKey:u(t.sessionPublicKey),sessionSignature:u(t.sessionSignature),sessionRegistration:u(t.sessionRegistration),network:r},o=await fetch(n,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(i)}),s=await o.text();if(!o.ok)throw new Error(`tab settle ${o.status}: ${s.slice(0,500)}`);let a;try{a=JSON.parse(s)}catch{throw new Error(`tab settle returned non-JSON: ${s.slice(0,200)}`)}if(!a.settleTx)throw new Error(`tab settle returned no settleTx: ${s.slice(0,200)}`);return a.settleTx}async function U(e){if(e.network!==e.vault.network)throw new m(`options.network (${e.network}) doesn't match vault.network (${e.vault.network})`);if(e.network!=="solana:mainnet")throw new m(e.network);let t=BigInt(Math.floor(Math.random()*4294967295)),r=new w(e.vault.vaultPda),n=b({vaultPda:r,sellerUrl:e.seller,nonce:BigInt(t)}),i=u(n),o=BigInt(y(e.perUnitCap)),s=BigInt(y(e.totalCap));if(o<=0n)throw new Error("perUnitCap must be > 0");if(s<o)throw new Error("totalCap must be >= perUnitCap");let a=e.sessionDuration??v,l=Math.floor(Date.now()/1e3)+a,h={channelId:i,maxAmountAtomic:s.toString(),expiresAtUnix:l,allowedCounterparty:P(e.seller)},S=await e.vault.authorizeSession(h);return new g({vault:e.vault,network:e.network,seller:e.seller,session:S,channelIdHex:i,channelIdBytes:n,perUnitCapAtomic:o,totalCapAtomic:s,expiresAtUnix:l,facilitatorUrl:e.facilitatorUrl??A})}async function k(e){throw new Error("resumeTab is Phase 3 work. Session keys are memory-only by design; recovery requires reading active_session on chain and re-authorizing. Tracked in dexter-vault roadmap.")}function P(e){if(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(e))try{return new w(e),e}catch{}throw new Error(`seller must be a base58 Solana pubkey for Phase 2 (got "${e}"). URL-based counterparty resolution lands in Phase 3 (seller middleware).`)}async function*C(e){let t=e.getReader(),r=new TextDecoder,n="";try{for(;;){let{done:i,value:o}=await t.read();if(i)break;n+=r.decode(o,{stream:!0});let s;for(;(s=n.indexOf(`
1
+ var m=class extends Error{constructor(r){super(`Network ${r} is not yet supported by @dexterai/x402/tab`);this.network=r;this.name="UnsupportedNetworkError"}},d=class extends Error{constructor(r,n){super(`Session scope exceeded: ${r}${n?` (${n})`:""}`);this.reason=r;this.name="SessionScopeExceededError"}},c=class extends Error{constructor(r){super(`Tab ${r} is already closed`);this.channelId=r;this.name="TabClosedError"}};import{PublicKey as S}from"@solana/web3.js";import{bytesToHex as u}from"@noble/hashes/utils";import z from"tweetnacl";import{sessionRegisterMessage as I,sessionRevokeMessage as R,voucherPayloadMessage as b,buildVoucherMessage as U}from"@dexterai/vault/messages";import{sha256 as V}from"@noble/hashes/sha256";function A(e){let t=new Uint8Array(8);new DataView(t.buffer).setBigUint64(0,e.nonce,!0);let r=new TextEncoder().encode(e.sellerUrl),n=V.create();return n.update(e.vaultPda.toBytes()),n.update(r),n.update(t),n.digest()}var C=3600,w="https://x402.dexter.cash",f=6;function p(e,t=f){if(!/^\d+(\.\d+)?$/.test(e))throw new Error(`amount must be a non-negative decimal string, got "${e}"`);let[r,n=""]=e.split(".");if(n.length>t)throw new Error(`amount "${e}" has more than ${t} decimals`);let s=n.padEnd(t,"0"),o=`${r}${s}`.replace(/^0+(?=\d)/,"");return o===""?"0":o}function g(e,t=f){if(!/^\d+$/.test(e))throw new Error(`atomic must be a non-negative integer string, got "${e}"`);let r=e.padStart(t+1,"0"),n=r.slice(0,-t).replace(/^0+(?=\d)/,"")||"0",s=r.slice(-t).replace(/0+$/,"");return s?`${n}.${s}`:n}var y=class{channelId;network;counterparty;internals;cumulativeAtomic=0n;sequenceNumber=0;closed=!1;lastSignedVoucher=null;previousSignedVoucher=null;constructor(t){this.internals=t,this.channelId=t.channelIdHex,this.network=t.network,this.counterparty=t.counterparty}get state(){let t=this.internals.totalCapAtomic-this.cumulativeAtomic,r=Math.floor(Date.now()/1e3);return{isOpen:!this.closed,spent:g(this.cumulativeAtomic.toString()),remaining:g(t.toString()),expiresInSec:Math.max(0,this.internals.expiresAtUnix-r)}}async signNextVoucher(t){if(this.closed)throw new c(this.channelId);let r=BigInt(t);if(r<=0n)throw new Error(`voucher increment must be > 0, got ${t}`);if(r>this.internals.perUnitCapAtomic)throw new d("cap_exceeded",`single voucher increment ${r} exceeds perUnitCap ${this.internals.perUnitCapAtomic}`);let n=this.cumulativeAtomic+r;if(n>this.internals.totalCapAtomic)throw new d("cap_exceeded",`cumulative ${n} would exceed totalCap ${this.internals.totalCapAtomic}`);let s=this.sequenceNumber+1,o={channelId:this.channelId,cumulativeAmount:n.toString(),sequenceNumber:s},i=await this.internals.vault.signWithSession(this.internals.session,o);return this.previousSignedVoucher=this.lastSignedVoucher,this.lastSignedVoucher=i,this.sequenceNumber=s,this.cumulativeAtomic=n,i}rollbackVoucher(t){let r=this.lastSignedVoucher;if(!r||r.payload.sequenceNumber!==t.payload.sequenceNumber||r.payload.cumulativeAmount!==t.payload.cumulativeAmount)return!1;let n=this.previousSignedVoucher;if(n)this.sequenceNumber=n.payload.sequenceNumber,this.cumulativeAtomic=BigInt(n.payload.cumulativeAmount),this.lastSignedVoucher=n;else if(r.payload.sequenceNumber===1)this.sequenceNumber=0,this.cumulativeAtomic=0n,this.lastSignedVoucher=null;else return!1;return this.previousSignedVoucher=null,!0}async stream(t,r){if(this.closed)throw new c(this.channelId);let n=await this.signNextVoucher(this.internals.perUnitCapAtomic.toString()),s=new Headers(r?.headers);s.set("X-Tab-Voucher",v(n)),s.set("Accept","text/event-stream");let o=await fetch(t,{...r,headers:s});if(!o.ok){let i=await o.text().catch(()=>"");throw new Error(`tab.stream HTTP ${o.status}: ${i.slice(0,500)}`)}if(!o.body)throw new Error("tab.stream response has no body");return N(o.body)}async close(){if(this.closed)throw new c(this.channelId);let t="";return this.lastSignedVoucher&&this.cumulativeAtomic>0n&&(t=await k(this.internals.facilitatorUrl,this.lastSignedVoucher,this.internals.network)),await this.internals.vault.signCloseTab(this.internals.session,this.channelId,this.cumulativeAtomic.toString()),this.closed=!0,this.internals.session.privateKey.fill(0),{settledAmount:g(this.cumulativeAtomic.toString()),settleTx:t}}};function v(e){return Buffer.from(JSON.stringify({payload:e.payload,sessionPublicKey:u(e.sessionPublicKey),sessionRegistration:u(e.sessionRegistration),sessionSignature:u(e.sessionSignature)}),"utf8").toString("base64")}async function k(e,t,r){let n=`${e.replace(/\/$/,"")}/tab/settle`,s={channelId:t.payload.channelId,cumulativeAmount:t.payload.cumulativeAmount,sequenceNumber:t.payload.sequenceNumber,sessionPublicKey:u(t.sessionPublicKey),sessionSignature:u(t.sessionSignature),sessionRegistration:u(t.sessionRegistration),network:r},o=await fetch(n,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)}),i=await o.text();if(!o.ok)throw new Error(`tab settle ${o.status}: ${i.slice(0,500)}`);let a;try{a=JSON.parse(i)}catch{throw new Error(`tab settle returned non-JSON: ${i.slice(0,200)}`)}if(!a.settleTx)throw new Error(`tab settle returned no settleTx: ${i.slice(0,200)}`);return a.settleTx}async function P(e){if(e.network!==e.vault.network)throw new m(`options.network (${e.network}) doesn't match vault.network (${e.vault.network})`);if(e.network!=="solana:mainnet")throw new m(e.network);let t=BigInt(Math.floor(Math.random()*4294967295)),r=new S(e.vault.vaultPda),n=A({vaultPda:r,sellerUrl:e.seller,nonce:BigInt(t)}),s=u(n),o=BigInt(p(e.perUnitCap)),i=BigInt(p(e.totalCap));if(o<=0n)throw new Error("perUnitCap must be > 0");if(i<o)throw new Error("totalCap must be >= perUnitCap");if(e.revolvingCapacity!==void 0&&BigInt(p(e.revolvingCapacity))<=0n)throw new Error("revolvingCapacity must be > 0");let a=e.sessionDuration??C,l=Math.floor(Date.now()/1e3)+a,h=E(e.seller),x={channelId:s,maxAmountAtomic:i.toString(),revolvingCapacityAtomic:e.revolvingCapacity?p(e.revolvingCapacity):i.toString(),expiresAtUnix:l,allowedCounterparty:h},T=await e.vault.authorizeSession(x);return new y({vault:e.vault,network:e.network,seller:e.seller,counterparty:h,session:T,channelIdHex:s,channelIdBytes:n,perUnitCapAtomic:o,totalCapAtomic:i,expiresAtUnix:l,facilitatorUrl:e.facilitatorUrl??w})}async function K(e){throw new Error("resumeTab is Phase 3 work. Session keys are memory-only by design; recovery requires reading active_session on chain and re-authorizing. Tracked in dexter-vault roadmap.")}function E(e){if(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(e))try{return new S(e),e}catch{}throw new Error(`seller must be a base58 Solana pubkey for Phase 2 (got "${e}"). URL-based counterparty resolution lands in Phase 3 (seller middleware).`)}async function*N(e){let t=e.getReader(),r=new TextDecoder,n="";try{for(;;){let{done:s,value:o}=await t.read();if(s)break;n+=r.decode(o,{stream:!0});let i;for(;(i=n.indexOf(`
2
2
 
3
- `))!==-1;){let a=n.slice(0,s);n=n.slice(s+2);let l=$(a);if(l.eventName==="end")return;if(l.data!==null){let h=l.data.replace(/\\n/g,`
3
+ `))!==-1;){let a=n.slice(0,i);n=n.slice(i+2);let l=$(a);if(l.eventName==="end")return;if(l.data!==null){let h=l.data.replace(/\\n/g,`
4
4
  `);yield new TextEncoder().encode(h)}}}}finally{t.releaseLock()}}function $(e){let t=null,r=[];for(let n of e.split(`
5
5
  `))n.startsWith("event:")?t=n.slice(6).trim():n.startsWith("data:")&&r.push(n.slice(5).trimStart());return{eventName:t,data:r.length?r.join(`
6
- `):null}}export{A as DEFAULT_FACILITATOR_URL,d as SessionScopeExceededError,c as TabClosedError,m as UnsupportedNetworkError,p as atomicToHuman,y as humanToAtomic,U as openTab,k as resumeTab};
6
+ `):null}}import{buildRegisterSessionKeyInstruction as O,buildRevokeSessionKeyInstruction as _,deriveSwigWalletAddress as oe}from"@dexterai/vault/instructions";import{buildSecp256r1VerifyInstruction as H}from"@dexterai/vault/precompile";import{DEXTER_VAULT_PROGRAM_ID as M,SECP256R1_PROGRAM_ID as B,INSTRUCTIONS_SYSVAR_ID as D}from"@dexterai/vault/constants";export{w as DEFAULT_FACILITATOR_URL,M as DEXTER_VAULT_PROGRAM_ID,D as INSTRUCTIONS_SYSVAR_ID,B as SECP256R1_PROGRAM_ID,d as SessionScopeExceededError,c as TabClosedError,m as UnsupportedNetworkError,g as atomicToHuman,O as buildRegisterSessionKeyInstruction,_ as buildRevokeSessionKeyInstruction,H as buildSecp256r1VerifyInstruction,U as buildVoucherMessage,p as humanToAtomic,P as openTab,K as resumeTab,I as sessionRegisterMessage,R as sessionRevokeMessage,b as voucherPayloadMessage,v as voucherToHeader};
@@ -1,6 +1,6 @@
1
- "use strict";var te=Object.create;var P=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var oe=Object.getPrototypeOf,ie=Object.prototype.hasOwnProperty;var se=(e,t)=>{for(var n in t)P(e,n,{get:t[n],enumerable:!0})},z=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of re(t))!ie.call(e,o)&&o!==n&&P(e,o,{get:()=>t[o],enumerable:!(r=ne(t,o))||r.enumerable});return e};var L=(e,t,n)=>(n=e!=null?te(oe(e)):{},z(t||!e||!e.__esModule?P(n,"default",{value:e,enumerable:!0}):n,e)),ae=e=>z(P({},"__esModule",{value:!0}),e);var Se={};se(Se,{FileVoucherStore:()=>_,InMemoryVoucherStore:()=>A,InvalidRegistrationError:()=>m,InvalidVoucherError:()=>l,InvalidVoucherSignatureError:()=>g,OnChainVerificationError:()=>y,ScopeViolationError:()=>c,TAB_VOUCHER_HEADER:()=>R,enforceScope:()=>U,openSse:()=>Q,parseRegistration:()=>I,readVaultState:()=>O,requireTab:()=>Z,tabMiddleware:()=>Y,verifyRegistrationOnChain:()=>k,verifyVoucherSignature:()=>V});module.exports=ae(Se);var l=class extends Error{constructor(n,r){super(`Invalid voucher: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidVoucherError"}};var X=require("@solana/web3.js");var G=L(require("tweetnacl"),1),ue=require("@noble/hashes/sha256"),le=require("@noble/curves/p256"),T=require("@solana/web3.js");var w=require("@dexterai/vault/messages");var F=require("@dexterai/vault/instructions"),ce=require("@dexterai/vault/precompile"),b=require("@dexterai/vault/constants");var K="OTS_SESSION_REGISTER_V1",m=class extends Error{constructor(n,r){super(`Invalid registration: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidRegistrationError"}};function I(e){if(e.length!==180)throw new m("wrong_length",`expected 180, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,K.length));if(t!==K)throw new m("wrong_domain",`got "${t}"`);for(let s=K.length;s<32;s++)if(e[s]!==0)throw new m("wrong_domain",`non-NUL padding at byte ${s}`);let n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=new T.PublicKey(e.slice(32,64)),o=new T.PublicKey(e.slice(64,96)),u=e.slice(96,128),p=n.getBigUint64(128,!0),f=n.getBigInt64(136,!0),i=new T.PublicKey(e.slice(144,176)),a=n.getUint32(176,!0);if(!r.equals(b.DEXTER_VAULT_PROGRAM_ID))throw new m("wrong_program",`${r.toBase58()} is not ${b.DEXTER_VAULT_PROGRAM_ID.toBase58()}`);if(p===0n)throw new m("cap_zero");let d=BigInt(Math.floor(Date.now()/1e3));if(f<=d)throw new m("expiry_in_past",`expires_at=${f}, now=${d}`);return{programId:r,vaultPda:o,sessionPubkey:new Uint8Array(u),maxAmount:p,expiresAt:f,allowedCounterparty:i,nonce:a}}var j=10,y=class extends Error{constructor(n,r){super(`On-chain verification failed: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="OnChainVerificationError"}};async function O(e,t){let n=await e.getAccountInfo(t,"finalized");if(!n)throw new y("vault_not_found",t.toBase58());if(!n.owner.equals(b.DEXTER_VAULT_PROGRAM_ID))throw new y("wrong_program",`owner ${n.owner.toBase58()} is not the vault program`);let r=n.data,o=new Uint8Array(r.slice(j,j+33)),a=84+(r[83]===1?48:0)+32+32;if(r[a]!==1)return{passkeyPubkey:o,activeSessionPubkey:null};let s=a+1,h=new Uint8Array(r.slice(s,s+32));return{passkeyPubkey:o,activeSessionPubkey:h}}async function k(e,t){let n=await O(e,t.vaultPda);if(n.activeSessionPubkey===null)throw new y("session_not_active","vault has no active_session \u2014 was it revoked?");if(!me(n.activeSessionPubkey,t.sessionPubkey))throw new y("session_pubkey_mismatch",`on-chain ${J(n.activeSessionPubkey)} != registration ${J(t.sessionPubkey)}`);return{passkeyPubkey:n.passkeyPubkey}}var g=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function V(e,t){if(t.length!==32)throw new g(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new g(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new g(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let n=(0,w.voucherPayloadMessage)({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!G.default.sign.detached.verify(n,e.sessionSignature,e.sessionPublicKey))throw new g("ed25519 verify rejected")}var c=class extends Error{constructor(n,r){super(`Scope violation: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="ScopeViolationError"}};function U(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new c("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let n=BigInt(Math.floor(Date.now()/1e3));if(n>=e.registration.expiresAt)throw new c("session_expired",`now=${n} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new c("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let r=BigInt(e.previousCumulativeAtomic);if(t<=r)throw new c("non_monotonic",`cumulative=${t} not > previous=${r}`)}}function me(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}function J(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}var S=require("fs"),$=require("path");function de(e){return{payload:e.payload,sessionPublicKey:N(e.sessionPublicKey),sessionRegistration:N(e.sessionRegistration),sessionSignature:N(e.sessionSignature)}}function pe(e){return{payload:e.payload,sessionPublicKey:H(e.sessionPublicKey),sessionRegistration:H(e.sessionRegistration),sessionSignature:H(e.sessionSignature)}}function N(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function H(e){if(e.length%2!==0)throw new Error(`hex length must be even, got ${e.length}`);let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}var A=class{map=new Map;async get(t){return this.map.get(t)??null}async set(t,n){this.map.set(t,n)}async delete(t){this.map.delete(t)}},_=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return(0,$.join)(this.dir,`${t}.json`)}async get(t){try{let n=await S.promises.readFile(this.pathFor(t),"utf8");return pe(JSON.parse(n))}catch(n){if(n?.code==="ENOENT")return null;throw n}}async set(t,n){let r=this.pathFor(t);await S.promises.mkdir((0,$.dirname)(r),{recursive:!0});let o=`${r}.tmp`;await S.promises.writeFile(o,JSON.stringify(de(n))),await S.promises.rename(o,r)}async delete(t){try{await S.promises.unlink(this.pathFor(t))}catch(n){if(n?.code!=="ENOENT")throw n}}};var ye=require("@solana/web3.js"),fe=require("@noble/hashes/utils");var he=L(require("tweetnacl"),1);var ge=require("@noble/hashes/sha256");var W=6;function v(e,t=W){if(!/^\d+(\.\d+)?$/.test(e))throw new Error(`amount must be a non-negative decimal string, got "${e}"`);let[n,r=""]=e.split(".");if(r.length>t)throw new Error(`amount "${e}" has more than ${t} decimals`);let o=r.padEnd(t,"0"),u=`${n}${o}`.replace(/^0+(?=\d)/,"");return u===""?"0":u}function x(e,t=W){if(!/^\d+$/.test(e))throw new Error(`atomic must be a non-negative integer string, got "${e}"`);let n=e.padStart(t+1,"0"),r=n.slice(0,-t).replace(/^0+(?=\d)/,"")||"0",o=n.slice(-t).replace(/0+$/,"");return o?`${r}.${o}`:r}var R="x-tab-voucher",B=class{map=new Map;get(t){return this.map.get(t)}set(t,n){this.map.set(t,n)}update(t,n){let r=this.map.get(t);r&&(r.lastCumulativeAtomic=n)}delete(t){this.map.delete(t)}},M=class{constructor(t,n,r,o){this.chargeImpl=o;this.channelId=t,this.network=n,this.cumulativeAtomic=r}channelId;network;sessionPublicKey=null;cumulativeAtomic;cumulative(){return x(this.cumulativeAtomic.toString())}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function we(e){if(typeof e!="string"||e.length===0)throw new l("signature_invalid",`missing ${R} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new l("signature_invalid","malformed base64")}let n;try{n=JSON.parse(t)}catch{throw new l("signature_invalid","malformed JSON")}if(!n||typeof n!="object"||!n.payload||!n.sessionPublicKey)throw new l("signature_invalid","missing required fields");return{payload:n.payload,sessionPublicKey:E(n.sessionPublicKey),sessionRegistration:E(n.sessionRegistration),sessionSignature:E(n.sessionSignature)}}function E(e){if(typeof e!="string"||e.length%2!==0)throw new l("signature_invalid",`bad hex: ${typeof e}`);let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}function be(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new l("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return E(e)}function Y(e){let t=e.store??new A,n=new B,r=typeof e.sellerPubkey=="string"?new X.PublicKey(e.sellerPubkey):e.sellerPubkey,o=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(v(e.perUnit))*100n;return async(u,p,f)=>{try{let i=we(u.headers[R]),a=i.payload.channelId,d=be(a),s=n.get(a);if(!s){let C=I(i.sessionRegistration);await k(e.connection,C),s={registration:C,lastCumulativeAtomic:"0"},n.set(a,s)}V(i,d),U({registration:s.registration,voucher:i,expectedCounterparty:r,previousCumulativeAtomic:s.lastCumulativeAtomic});let h=BigInt(i.payload.cumulativeAmount),ee=BigInt(s.lastCumulativeAtomic),q=h-ee;if(q>o)throw new c("cumulative_exceeds_cap",`single voucher increment ${q} exceeds maxPerVoucherAtomic ${o}`);await t.set(a,i),n.update(a,i.payload.cumulativeAmount);let D=new M(a,e.network,h,async C=>{throw new Error("SellerTab.charge() is not driven by the route handler; the buyer presents a fresh voucher per chunk. Use openSse(res, tab) for the metered-stream pattern.")});D.setSessionPublicKey(i.sessionPublicKey),u.tab=D,f()}catch(i){if(i instanceof l||i instanceof m||i instanceof y||i instanceof g||i instanceof c){p.status(402).json({error:"invalid_voucher",reason:i.reason??"unknown",detail:i.message});return}f(i)}}}function Z(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function Q(e,t){if(!t.tab)throw new Error("openSse requires options.tab");e.headersSent||(e.setHeader("Content-Type","text/event-stream"),e.setHeader("Cache-Control","no-cache"),e.setHeader("Connection","keep-alive"),typeof e.flushHeaders=="function"&&e.flushHeaders());let n=t.tab,r=BigInt(v(n.cumulative())),o=t.perUnit?BigInt(v(t.perUnit)):null,u=0n,p=!1;function f(d=1){if(p)return Promise.reject(new Error("meter ended"));if(o===null)return Promise.reject(new Error("charge() needs options.perUnit"));let s=o*BigInt(d),h=u+s;return h>r?Promise.reject(new c("cumulative_exceeds_cap",`chunk would push request total to ${x(h.toString())} beyond voucher-authorized budget ${x(r.toString())}`)):(u=h,Promise.resolve())}function i(d){if(p)throw new Error("meter ended");let h=(typeof d=="string"?d:Buffer.from(d).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${h}
1
+ "use strict";var te=Object.create;var P=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var oe=Object.getPrototypeOf,ie=Object.prototype.hasOwnProperty;var se=(e,t)=>{for(var n in t)P(e,n,{get:t[n],enumerable:!0})},L=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of re(t))!ie.call(e,o)&&o!==n&&P(e,o,{get:()=>t[o],enumerable:!(r=ne(t,o))||r.enumerable});return e};var F=(e,t,n)=>(n=e!=null?te(oe(e)):{},L(t||!e||!e.__esModule?P(n,"default",{value:e,enumerable:!0}):n,e)),ae=e=>L(P({},"__esModule",{value:!0}),e);var Se={};se(Se,{FileVoucherStore:()=>U,InMemoryVoucherStore:()=>v,InvalidRegistrationError:()=>d,InvalidVoucherError:()=>m,InvalidVoucherSignatureError:()=>h,OnChainVerificationError:()=>y,ScopeViolationError:()=>u,TAB_VOUCHER_HEADER:()=>E,enforceScope:()=>C,openSse:()=>Q,parseRegistration:()=>I,readVaultState:()=>N,requireTab:()=>Z,tabMiddleware:()=>Y,verifyRegistrationOnChain:()=>V,verifyVoucherSignature:()=>k});module.exports=ae(Se);var m=class extends Error{constructor(n,r){super(`Invalid voucher: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidVoucherError"}};var X=require("@solana/web3.js");var G=F(require("tweetnacl"),1),ue=require("@noble/hashes/sha256"),le=require("@noble/curves/p256"),T=require("@solana/web3.js");var w=require("@dexterai/vault/messages");var K=require("@dexterai/vault/instructions"),ce=require("@dexterai/vault/precompile"),b=require("@dexterai/vault/constants");var O="OTS_SESSION_REGISTER_V2",d=class extends Error{constructor(n,r){super(`Invalid registration: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidRegistrationError"}};function I(e){if(e.length!==188)throw new d("wrong_length",`expected 188, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,O.length));if(t!==O)throw new d("wrong_domain",`got "${t}"`);for(let a=O.length;a<32;a++)if(e[a]!==0)throw new d("wrong_domain",`non-NUL padding at byte ${a}`);let n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=new T.PublicKey(e.slice(32,64)),o=new T.PublicKey(e.slice(64,96)),l=e.slice(96,128),p=n.getBigUint64(128,!0),f=n.getBigInt64(136,!0),i=new T.PublicKey(e.slice(144,176)),c=n.getUint32(176,!0),g=n.getBigUint64(180,!0);if(!r.equals(b.DEXTER_VAULT_PROGRAM_ID))throw new d("wrong_program",`${r.toBase58()} is not ${b.DEXTER_VAULT_PROGRAM_ID.toBase58()}`);if(p===0n)throw new d("cap_zero");let s=BigInt(Math.floor(Date.now()/1e3));if(f<=s)throw new d("expiry_in_past",`expires_at=${f}, now=${s}`);return{programId:r,vaultPda:o,sessionPubkey:new Uint8Array(l),maxAmount:p,expiresAt:f,allowedCounterparty:i,nonce:c,maxRevolvingCapacity:g}}var j=10,y=class extends Error{constructor(n,r){super(`On-chain verification failed: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="OnChainVerificationError"}};async function N(e,t){let n=await e.getAccountInfo(t,"finalized");if(!n)throw new y("vault_not_found",t.toBase58());if(!n.owner.equals(b.DEXTER_VAULT_PROGRAM_ID))throw new y("wrong_program",`owner ${n.owner.toBase58()} is not the vault program`);let r=n.data,o=new Uint8Array(r.slice(j,j+33)),c=84+(r[83]===1?48:0)+32+32;if(r[c]!==1)return{passkeyPubkey:o,activeSessionPubkey:null};let s=c+1,a=new Uint8Array(r.slice(s,s+32));return{passkeyPubkey:o,activeSessionPubkey:a}}async function V(e,t){let n=await N(e,t.vaultPda);if(n.activeSessionPubkey===null)throw new y("session_not_active","vault has no active_session \u2014 was it revoked?");if(!me(n.activeSessionPubkey,t.sessionPubkey))throw new y("session_pubkey_mismatch",`on-chain ${J(n.activeSessionPubkey)} != registration ${J(t.sessionPubkey)}`);return{passkeyPubkey:n.passkeyPubkey}}var h=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function k(e,t){if(t.length!==32)throw new h(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new h(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new h(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let n=(0,w.voucherPayloadMessage)({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!G.default.sign.detached.verify(n,e.sessionSignature,e.sessionPublicKey))throw new h("ed25519 verify rejected")}var u=class extends Error{constructor(n,r){super(`Scope violation: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="ScopeViolationError"}};function C(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new u("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let n=BigInt(Math.floor(Date.now()/1e3));if(n>=e.registration.expiresAt)throw new u("session_expired",`now=${n} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new u("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let r=BigInt(e.previousCumulativeAtomic);if(t<=r)throw new u("non_monotonic",`cumulative=${t} not > previous=${r}`)}}function me(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}function J(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}var S=require("fs"),_=require("path");function de(e){return{payload:e.payload,sessionPublicKey:H(e.sessionPublicKey),sessionRegistration:H(e.sessionRegistration),sessionSignature:H(e.sessionSignature)}}function pe(e){return{payload:e.payload,sessionPublicKey:B(e.sessionPublicKey),sessionRegistration:B(e.sessionRegistration),sessionSignature:B(e.sessionSignature)}}function H(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function B(e){if(e.length%2!==0)throw new Error(`hex length must be even, got ${e.length}`);let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}var v=class{map=new Map;async get(t){return this.map.get(t)??null}async set(t,n){this.map.set(t,n)}async delete(t){this.map.delete(t)}},U=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return(0,_.join)(this.dir,`${t}.json`)}async get(t){try{let n=await S.promises.readFile(this.pathFor(t),"utf8");return pe(JSON.parse(n))}catch(n){if(n?.code==="ENOENT")return null;throw n}}async set(t,n){let r=this.pathFor(t);await S.promises.mkdir((0,_.dirname)(r),{recursive:!0});let o=`${r}.tmp`;await S.promises.writeFile(o,JSON.stringify(de(n))),await S.promises.rename(o,r)}async delete(t){try{await S.promises.unlink(this.pathFor(t))}catch(n){if(n?.code!=="ENOENT")throw n}}};var ye=require("@solana/web3.js"),fe=require("@noble/hashes/utils");var ge=F(require("tweetnacl"),1);var he=require("@noble/hashes/sha256");var W=6;function A(e,t=W){if(!/^\d+(\.\d+)?$/.test(e))throw new Error(`amount must be a non-negative decimal string, got "${e}"`);let[n,r=""]=e.split(".");if(r.length>t)throw new Error(`amount "${e}" has more than ${t} decimals`);let o=r.padEnd(t,"0"),l=`${n}${o}`.replace(/^0+(?=\d)/,"");return l===""?"0":l}function x(e,t=W){if(!/^\d+$/.test(e))throw new Error(`atomic must be a non-negative integer string, got "${e}"`);let n=e.padStart(t+1,"0"),r=n.slice(0,-t).replace(/^0+(?=\d)/,"")||"0",o=n.slice(-t).replace(/0+$/,"");return o?`${r}.${o}`:r}var E="x-tab-voucher",M=class{map=new Map;get(t){return this.map.get(t)}set(t,n){this.map.set(t,n)}update(t,n){let r=this.map.get(t);r&&(r.lastCumulativeAtomic=n)}delete(t){this.map.delete(t)}},q=class{constructor(t,n,r,o){this.chargeImpl=o;this.channelId=t,this.network=n,this.cumulativeAtomic=r}channelId;network;sessionPublicKey=null;cumulativeAtomic;cumulative(){return x(this.cumulativeAtomic.toString())}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function we(e){if(typeof e!="string"||e.length===0)throw new m("signature_invalid",`missing ${E} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new m("signature_invalid","malformed base64")}let n;try{n=JSON.parse(t)}catch{throw new m("signature_invalid","malformed JSON")}if(!n||typeof n!="object"||!n.payload||!n.sessionPublicKey)throw new m("signature_invalid","missing required fields");return{payload:n.payload,sessionPublicKey:$(n.sessionPublicKey),sessionRegistration:$(n.sessionRegistration),sessionSignature:$(n.sessionSignature)}}function $(e){if(typeof e!="string"||e.length%2!==0)throw new m("signature_invalid",`bad hex: ${typeof e}`);let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}function be(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new m("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return $(e)}function Y(e){let t=e.store??new v,n=new M,r=typeof e.sellerPubkey=="string"?new X.PublicKey(e.sellerPubkey):e.sellerPubkey,o=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(A(e.perUnit))*100n;return async(l,p,f)=>{try{let i=we(l.headers[E]),c=i.payload.channelId,g=be(c),s=n.get(c);if(!s){let R=I(i.sessionRegistration);await V(e.connection,R),s={registration:R,lastCumulativeAtomic:"0"},n.set(c,s)}k(i,g),C({registration:s.registration,voucher:i,expectedCounterparty:r,previousCumulativeAtomic:s.lastCumulativeAtomic});let a=BigInt(i.payload.cumulativeAmount),ee=BigInt(s.lastCumulativeAtomic),D=a-ee;if(D>o)throw new u("cumulative_exceeds_cap",`single voucher increment ${D} exceeds maxPerVoucherAtomic ${o}`);await t.set(c,i),n.update(c,i.payload.cumulativeAmount);let z=new q(c,e.network,a,async R=>{throw new Error("SellerTab.charge() is not driven by the route handler; the buyer presents a fresh voucher per chunk. Use openSse(res, tab) for the metered-stream pattern.")});z.setSessionPublicKey(i.sessionPublicKey),l.tab=z,f()}catch(i){if(i instanceof m||i instanceof d||i instanceof y||i instanceof h||i instanceof u){p.status(402).json({error:"invalid_voucher",reason:i.reason??"unknown",detail:i.message});return}f(i)}}}function Z(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function Q(e,t){if(!t.tab)throw new Error("openSse requires options.tab");e.headersSent||(e.setHeader("Content-Type","text/event-stream"),e.setHeader("Cache-Control","no-cache"),e.setHeader("Connection","keep-alive"),typeof e.flushHeaders=="function"&&e.flushHeaders());let n=t.tab,r=BigInt(A(n.cumulative())),o=t.perUnit?BigInt(A(t.perUnit)):null,l=0n,p=!1;function f(g=1){if(p)return Promise.reject(new Error("meter ended"));if(o===null)return Promise.reject(new Error("charge() needs options.perUnit"));let s=o*BigInt(g),a=l+s;return a>r?Promise.reject(new u("cumulative_exceeds_cap",`chunk would push request total to ${x(a.toString())} beyond voucher-authorized budget ${x(r.toString())}`)):(l=a,Promise.resolve())}function i(g){if(p)throw new Error("meter ended");let a=(typeof g=="string"?g:Buffer.from(g).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${a}
2
2
 
3
- `)}function a(){p||(p=!0,e.write(`event: end
4
- data: {"chargedAtomic":"${u}"}
3
+ `)}function c(){p||(p=!0,e.write(`event: end
4
+ data: {"chargedAtomic":"${l}"}
5
5
 
6
- `),e.end())}return{charge:f,send:i,end:a}}0&&(module.exports={FileVoucherStore,InMemoryVoucherStore,InvalidRegistrationError,InvalidVoucherError,InvalidVoucherSignatureError,OnChainVerificationError,ScopeViolationError,TAB_VOUCHER_HEADER,enforceScope,openSse,parseRegistration,readVaultState,requireTab,tabMiddleware,verifyRegistrationOnChain,verifyVoucherSignature});
6
+ `),e.end())}return{charge:f,send:i,end:c}}0&&(module.exports={FileVoucherStore,InMemoryVoucherStore,InvalidRegistrationError,InvalidVoucherError,InvalidVoucherSignatureError,OnChainVerificationError,ScopeViolationError,TAB_VOUCHER_HEADER,enforceScope,openSse,parseRegistration,readVaultState,requireTab,tabMiddleware,verifyRegistrationOnChain,verifyVoucherSignature});
@@ -219,6 +219,7 @@ interface ParsedRegistration {
219
219
  expiresAt: bigint;
220
220
  allowedCounterparty: PublicKey;
221
221
  nonce: number;
222
+ maxRevolvingCapacity: bigint;
222
223
  }
223
224
  declare class InvalidRegistrationError extends Error {
224
225
  readonly reason: 'wrong_length' | 'wrong_domain' | 'wrong_program' | 'expiry_in_past' | 'cap_zero';
@@ -219,6 +219,7 @@ interface ParsedRegistration {
219
219
  expiresAt: bigint;
220
220
  allowedCounterparty: PublicKey;
221
221
  nonce: number;
222
+ maxRevolvingCapacity: bigint;
222
223
  }
223
224
  declare class InvalidRegistrationError extends Error {
224
225
  readonly reason: 'wrong_length' | 'wrong_domain' | 'wrong_program' | 'expiry_in_past' | 'cap_zero';
@@ -1,6 +1,6 @@
1
- var m=class extends Error{constructor(n,r){super(`Invalid voucher: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidVoucherError"}};import{PublicKey as Y}from"@solana/web3.js";import F from"tweetnacl";import{sha256 as Se}from"@noble/hashes/sha256";import{p256 as ve}from"@noble/curves/p256";import{PublicKey as I}from"@solana/web3.js";import{sessionRegisterMessage as se,sessionRevokeMessage as ae,voucherPayloadMessage as T,buildVoucherMessage as ce}from"@dexterai/vault/messages";import{buildRegisterSessionKeyInstruction as me,buildRevokeSessionKeyInstruction as de}from"@dexterai/vault/instructions";import{buildSecp256r1VerifyInstruction as he}from"@dexterai/vault/precompile";import{DEXTER_VAULT_PROGRAM_ID as v,SECP256R1_PROGRAM_ID as ye,INSTRUCTIONS_SYSVAR_ID as fe}from"@dexterai/vault/constants";var k="OTS_SESSION_REGISTER_V1",d=class extends Error{constructor(n,r){super(`Invalid registration: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidRegistrationError"}};function V(e){if(e.length!==180)throw new d("wrong_length",`expected 180, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,k.length));if(t!==k)throw new d("wrong_domain",`got "${t}"`);for(let s=k.length;s<32;s++)if(e[s]!==0)throw new d("wrong_domain",`non-NUL padding at byte ${s}`);let n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=new I(e.slice(32,64)),i=new I(e.slice(64,96)),c=e.slice(96,128),p=n.getBigUint64(128,!0),g=n.getBigInt64(136,!0),o=new I(e.slice(144,176)),a=n.getUint32(176,!0);if(!r.equals(v))throw new d("wrong_program",`${r.toBase58()} is not ${v.toBase58()}`);if(p===0n)throw new d("cap_zero");let l=BigInt(Math.floor(Date.now()/1e3));if(g<=l)throw new d("expiry_in_past",`expires_at=${g}, now=${l}`);return{programId:r,vaultPda:i,sessionPubkey:new Uint8Array(c),maxAmount:p,expiresAt:g,allowedCounterparty:o,nonce:a}}var M=10,f=class extends Error{constructor(n,r){super(`On-chain verification failed: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="OnChainVerificationError"}};async function D(e,t){let n=await e.getAccountInfo(t,"finalized");if(!n)throw new f("vault_not_found",t.toBase58());if(!n.owner.equals(v))throw new f("wrong_program",`owner ${n.owner.toBase58()} is not the vault program`);let r=n.data,i=new Uint8Array(r.slice(M,M+33)),a=84+(r[83]===1?48:0)+32+32;if(r[a]!==1)return{passkeyPubkey:i,activeSessionPubkey:null};let s=a+1,h=new Uint8Array(r.slice(s,s+32));return{passkeyPubkey:i,activeSessionPubkey:h}}async function U(e,t){let n=await D(e,t.vaultPda);if(n.activeSessionPubkey===null)throw new f("session_not_active","vault has no active_session \u2014 was it revoked?");if(!j(n.activeSessionPubkey,t.sessionPubkey))throw new f("session_pubkey_mismatch",`on-chain ${q(n.activeSessionPubkey)} != registration ${q(t.sessionPubkey)}`);return{passkeyPubkey:n.passkeyPubkey}}var y=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function _(e,t){if(t.length!==32)throw new y(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new y(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new y(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let n=T({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!F.sign.detached.verify(n,e.sessionSignature,e.sessionPublicKey))throw new y("ed25519 verify rejected")}var u=class extends Error{constructor(n,r){super(`Scope violation: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="ScopeViolationError"}};function $(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new u("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let n=BigInt(Math.floor(Date.now()/1e3));if(n>=e.registration.expiresAt)throw new u("session_expired",`now=${n} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new u("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let r=BigInt(e.previousCumulativeAtomic);if(t<=r)throw new u("non_monotonic",`cumulative=${t} not > previous=${r}`)}}function j(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}function q(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}import{promises as w}from"fs";import{join as J,dirname as G}from"path";function W(e){return{payload:e.payload,sessionPublicKey:E(e.sessionPublicKey),sessionRegistration:E(e.sessionRegistration),sessionSignature:E(e.sessionSignature)}}function X(e){return{payload:e.payload,sessionPublicKey:R(e.sessionPublicKey),sessionRegistration:R(e.sessionRegistration),sessionSignature:R(e.sessionSignature)}}function E(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function R(e){if(e.length%2!==0)throw new Error(`hex length must be even, got ${e.length}`);let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}var b=class{map=new Map;async get(t){return this.map.get(t)??null}async set(t,n){this.map.set(t,n)}async delete(t){this.map.delete(t)}},C=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return J(this.dir,`${t}.json`)}async get(t){try{let n=await w.readFile(this.pathFor(t),"utf8");return X(JSON.parse(n))}catch(n){if(n?.code==="ENOENT")return null;throw n}}async set(t,n){let r=this.pathFor(t);await w.mkdir(G(r),{recursive:!0});let i=`${r}.tmp`;await w.writeFile(i,JSON.stringify(W(n))),await w.rename(i,r)}async delete(t){try{await w.unlink(this.pathFor(t))}catch(n){if(n?.code!=="ENOENT")throw n}}};import{PublicKey as ze}from"@solana/web3.js";import{bytesToHex as Fe}from"@noble/hashes/utils";import Re from"tweetnacl";import{sha256 as Oe}from"@noble/hashes/sha256";var z=6;function S(e,t=z){if(!/^\d+(\.\d+)?$/.test(e))throw new Error(`amount must be a non-negative decimal string, got "${e}"`);let[n,r=""]=e.split(".");if(r.length>t)throw new Error(`amount "${e}" has more than ${t} decimals`);let i=r.padEnd(t,"0"),c=`${n}${i}`.replace(/^0+(?=\d)/,"");return c===""?"0":c}function A(e,t=z){if(!/^\d+$/.test(e))throw new Error(`atomic must be a non-negative integer string, got "${e}"`);let n=e.padStart(t+1,"0"),r=n.slice(0,-t).replace(/^0+(?=\d)/,"")||"0",i=n.slice(-t).replace(/0+$/,"");return i?`${r}.${i}`:r}var N="x-tab-voucher",K=class{map=new Map;get(t){return this.map.get(t)}set(t,n){this.map.set(t,n)}update(t,n){let r=this.map.get(t);r&&(r.lastCumulativeAtomic=n)}delete(t){this.map.delete(t)}},O=class{constructor(t,n,r,i){this.chargeImpl=i;this.channelId=t,this.network=n,this.cumulativeAtomic=r}channelId;network;sessionPublicKey=null;cumulativeAtomic;cumulative(){return A(this.cumulativeAtomic.toString())}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function Z(e){if(typeof e!="string"||e.length===0)throw new m("signature_invalid",`missing ${N} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new m("signature_invalid","malformed base64")}let n;try{n=JSON.parse(t)}catch{throw new m("signature_invalid","malformed JSON")}if(!n||typeof n!="object"||!n.payload||!n.sessionPublicKey)throw new m("signature_invalid","missing required fields");return{payload:n.payload,sessionPublicKey:x(n.sessionPublicKey),sessionRegistration:x(n.sessionRegistration),sessionSignature:x(n.sessionSignature)}}function x(e){if(typeof e!="string"||e.length%2!==0)throw new m("signature_invalid",`bad hex: ${typeof e}`);let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}function Q(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new m("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return x(e)}function ee(e){let t=e.store??new b,n=new K,r=typeof e.sellerPubkey=="string"?new Y(e.sellerPubkey):e.sellerPubkey,i=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(S(e.perUnit))*100n;return async(c,p,g)=>{try{let o=Z(c.headers[N]),a=o.payload.channelId,l=Q(a),s=n.get(a);if(!s){let P=V(o.sessionRegistration);await U(e.connection,P),s={registration:P,lastCumulativeAtomic:"0"},n.set(a,s)}_(o,l),$({registration:s.registration,voucher:o,expectedCounterparty:r,previousCumulativeAtomic:s.lastCumulativeAtomic});let h=BigInt(o.payload.cumulativeAmount),L=BigInt(s.lastCumulativeAtomic),H=h-L;if(H>i)throw new u("cumulative_exceeds_cap",`single voucher increment ${H} exceeds maxPerVoucherAtomic ${i}`);await t.set(a,o),n.update(a,o.payload.cumulativeAmount);let B=new O(a,e.network,h,async P=>{throw new Error("SellerTab.charge() is not driven by the route handler; the buyer presents a fresh voucher per chunk. Use openSse(res, tab) for the metered-stream pattern.")});B.setSessionPublicKey(o.sessionPublicKey),c.tab=B,g()}catch(o){if(o instanceof m||o instanceof d||o instanceof f||o instanceof y||o instanceof u){p.status(402).json({error:"invalid_voucher",reason:o.reason??"unknown",detail:o.message});return}g(o)}}}function te(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function ne(e,t){if(!t.tab)throw new Error("openSse requires options.tab");e.headersSent||(e.setHeader("Content-Type","text/event-stream"),e.setHeader("Cache-Control","no-cache"),e.setHeader("Connection","keep-alive"),typeof e.flushHeaders=="function"&&e.flushHeaders());let n=t.tab,r=BigInt(S(n.cumulative())),i=t.perUnit?BigInt(S(t.perUnit)):null,c=0n,p=!1;function g(l=1){if(p)return Promise.reject(new Error("meter ended"));if(i===null)return Promise.reject(new Error("charge() needs options.perUnit"));let s=i*BigInt(l),h=c+s;return h>r?Promise.reject(new u("cumulative_exceeds_cap",`chunk would push request total to ${A(h.toString())} beyond voucher-authorized budget ${A(r.toString())}`)):(c=h,Promise.resolve())}function o(l){if(p)throw new Error("meter ended");let h=(typeof l=="string"?l:Buffer.from(l).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${h}
1
+ var m=class extends Error{constructor(n,r){super(`Invalid voucher: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidVoucherError"}};import{PublicKey as Y}from"@solana/web3.js";import F from"tweetnacl";import{sha256 as ve}from"@noble/hashes/sha256";import{p256 as xe}from"@noble/curves/p256";import{PublicKey as I}from"@solana/web3.js";import{sessionRegisterMessage as se,sessionRevokeMessage as ae,voucherPayloadMessage as T,buildVoucherMessage as ce}from"@dexterai/vault/messages";import{buildRegisterSessionKeyInstruction as me,buildRevokeSessionKeyInstruction as de,deriveSwigWalletAddress as pe}from"@dexterai/vault/instructions";import{buildSecp256r1VerifyInstruction as he}from"@dexterai/vault/precompile";import{DEXTER_VAULT_PROGRAM_ID as A,SECP256R1_PROGRAM_ID as fe,INSTRUCTIONS_SYSVAR_ID as we}from"@dexterai/vault/constants";var V="OTS_SESSION_REGISTER_V2",d=class extends Error{constructor(n,r){super(`Invalid registration: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="InvalidRegistrationError"}};function k(e){if(e.length!==188)throw new d("wrong_length",`expected 188, got ${e.length}`);let t=new TextDecoder().decode(e.slice(0,V.length));if(t!==V)throw new d("wrong_domain",`got "${t}"`);for(let a=V.length;a<32;a++)if(e[a]!==0)throw new d("wrong_domain",`non-NUL padding at byte ${a}`);let n=new DataView(e.buffer,e.byteOffset,e.byteLength),r=new I(e.slice(32,64)),i=new I(e.slice(64,96)),u=e.slice(96,128),p=n.getBigUint64(128,!0),h=n.getBigInt64(136,!0),o=new I(e.slice(144,176)),c=n.getUint32(176,!0),g=n.getBigUint64(180,!0);if(!r.equals(A))throw new d("wrong_program",`${r.toBase58()} is not ${A.toBase58()}`);if(p===0n)throw new d("cap_zero");let s=BigInt(Math.floor(Date.now()/1e3));if(h<=s)throw new d("expiry_in_past",`expires_at=${h}, now=${s}`);return{programId:r,vaultPda:i,sessionPubkey:new Uint8Array(u),maxAmount:p,expiresAt:h,allowedCounterparty:o,nonce:c,maxRevolvingCapacity:g}}var M=10,f=class extends Error{constructor(n,r){super(`On-chain verification failed: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="OnChainVerificationError"}};async function D(e,t){let n=await e.getAccountInfo(t,"finalized");if(!n)throw new f("vault_not_found",t.toBase58());if(!n.owner.equals(A))throw new f("wrong_program",`owner ${n.owner.toBase58()} is not the vault program`);let r=n.data,i=new Uint8Array(r.slice(M,M+33)),c=84+(r[83]===1?48:0)+32+32;if(r[c]!==1)return{passkeyPubkey:i,activeSessionPubkey:null};let s=c+1,a=new Uint8Array(r.slice(s,s+32));return{passkeyPubkey:i,activeSessionPubkey:a}}async function C(e,t){let n=await D(e,t.vaultPda);if(n.activeSessionPubkey===null)throw new f("session_not_active","vault has no active_session \u2014 was it revoked?");if(!j(n.activeSessionPubkey,t.sessionPubkey))throw new f("session_pubkey_mismatch",`on-chain ${q(n.activeSessionPubkey)} != registration ${q(t.sessionPubkey)}`);return{passkeyPubkey:n.passkeyPubkey}}var y=class extends Error{constructor(t){super(`Invalid voucher signature${t?`: ${t}`:""}`),this.name="InvalidVoucherSignatureError"}};function U(e,t){if(t.length!==32)throw new y(`channelIdBytes must be 32 bytes, got ${t.length}`);if(e.sessionPublicKey.length!==32)throw new y(`sessionPublicKey must be 32 bytes, got ${e.sessionPublicKey.length}`);if(e.sessionSignature.length!==64)throw new y(`sessionSignature must be 64 bytes, got ${e.sessionSignature.length}`);let n=T({channelId:t,cumulativeAmount:BigInt(e.payload.cumulativeAmount),sequenceNumber:e.payload.sequenceNumber});if(!F.sign.detached.verify(n,e.sessionSignature,e.sessionPublicKey))throw new y("ed25519 verify rejected")}var l=class extends Error{constructor(n,r){super(`Scope violation: ${n}${r?` (${r})`:""}`);this.reason=n;this.name="ScopeViolationError"}};function _(e){let t=BigInt(e.voucher.payload.cumulativeAmount);if(t>e.registration.maxAmount)throw new l("cumulative_exceeds_cap",`${t} > ${e.registration.maxAmount}`);let n=BigInt(Math.floor(Date.now()/1e3));if(n>=e.registration.expiresAt)throw new l("session_expired",`now=${n} >= expiresAt=${e.registration.expiresAt}`);if(!e.registration.allowedCounterparty.equals(e.expectedCounterparty))throw new l("wrong_counterparty",`${e.registration.allowedCounterparty.toBase58()} != ${e.expectedCounterparty.toBase58()}`);if(e.previousCumulativeAtomic!==void 0){let r=BigInt(e.previousCumulativeAtomic);if(t<=r)throw new l("non_monotonic",`cumulative=${t} not > previous=${r}`)}}function j(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}function q(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}import{promises as w}from"fs";import{join as J,dirname as G}from"path";function W(e){return{payload:e.payload,sessionPublicKey:$(e.sessionPublicKey),sessionRegistration:$(e.sessionRegistration),sessionSignature:$(e.sessionSignature)}}function X(e){return{payload:e.payload,sessionPublicKey:E(e.sessionPublicKey),sessionRegistration:E(e.sessionRegistration),sessionSignature:E(e.sessionSignature)}}function $(e){let t="";for(let n of e)t+=n.toString(16).padStart(2,"0");return t}function E(e){if(e.length%2!==0)throw new Error(`hex length must be even, got ${e.length}`);let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}var b=class{map=new Map;async get(t){return this.map.get(t)??null}async set(t,n){this.map.set(t,n)}async delete(t){this.map.delete(t)}},R=class{constructor(t){this.dir=t}pathFor(t){if(!/^[a-z0-9_-]+$/i.test(t))throw new Error(`unsafe channelId for filesystem: ${t}`);return J(this.dir,`${t}.json`)}async get(t){try{let n=await w.readFile(this.pathFor(t),"utf8");return X(JSON.parse(n))}catch(n){if(n?.code==="ENOENT")return null;throw n}}async set(t,n){let r=this.pathFor(t);await w.mkdir(G(r),{recursive:!0});let i=`${r}.tmp`;await w.writeFile(i,JSON.stringify(W(n))),await w.rename(i,r)}async delete(t){try{await w.unlink(this.pathFor(t))}catch(n){if(n?.code!=="ENOENT")throw n}}};import{PublicKey as Le}from"@solana/web3.js";import{bytesToHex as je}from"@noble/hashes/utils";import Re from"tweetnacl";import{sha256 as Ne}from"@noble/hashes/sha256";var z=6;function S(e,t=z){if(!/^\d+(\.\d+)?$/.test(e))throw new Error(`amount must be a non-negative decimal string, got "${e}"`);let[n,r=""]=e.split(".");if(r.length>t)throw new Error(`amount "${e}" has more than ${t} decimals`);let i=r.padEnd(t,"0"),u=`${n}${i}`.replace(/^0+(?=\d)/,"");return u===""?"0":u}function v(e,t=z){if(!/^\d+$/.test(e))throw new Error(`atomic must be a non-negative integer string, got "${e}"`);let n=e.padStart(t+1,"0"),r=n.slice(0,-t).replace(/^0+(?=\d)/,"")||"0",i=n.slice(-t).replace(/0+$/,"");return i?`${r}.${i}`:r}var N="x-tab-voucher",K=class{map=new Map;get(t){return this.map.get(t)}set(t,n){this.map.set(t,n)}update(t,n){let r=this.map.get(t);r&&(r.lastCumulativeAtomic=n)}delete(t){this.map.delete(t)}},O=class{constructor(t,n,r,i){this.chargeImpl=i;this.channelId=t,this.network=n,this.cumulativeAtomic=r}channelId;network;sessionPublicKey=null;cumulativeAtomic;cumulative(){return v(this.cumulativeAtomic.toString())}bumpCumulative(t){this.cumulativeAtomic=t}setSessionPublicKey(t){this.sessionPublicKey=t}async charge(t){return this.chargeImpl(t)}};function Z(e){if(typeof e!="string"||e.length===0)throw new m("signature_invalid",`missing ${N} header`);let t;try{t=Buffer.from(e,"base64").toString("utf8")}catch{throw new m("signature_invalid","malformed base64")}let n;try{n=JSON.parse(t)}catch{throw new m("signature_invalid","malformed JSON")}if(!n||typeof n!="object"||!n.payload||!n.sessionPublicKey)throw new m("signature_invalid","missing required fields");return{payload:n.payload,sessionPublicKey:x(n.sessionPublicKey),sessionRegistration:x(n.sessionRegistration),sessionSignature:x(n.sessionSignature)}}function x(e){if(typeof e!="string"||e.length%2!==0)throw new m("signature_invalid",`bad hex: ${typeof e}`);let t=new Uint8Array(e.length/2);for(let n=0;n<t.length;n++)t[n]=parseInt(e.substr(n*2,2),16);return t}function Q(e){if(!/^[0-9a-f]{64}$/i.test(e))throw new m("signature_invalid",`channelId must be 64-char hex, got "${e}"`);return x(e)}function ee(e){let t=e.store??new b,n=new K,r=typeof e.sellerPubkey=="string"?new Y(e.sellerPubkey):e.sellerPubkey,i=e.maxPerVoucherAtomic?BigInt(e.maxPerVoucherAtomic):BigInt(S(e.perUnit))*100n;return async(u,p,h)=>{try{let o=Z(u.headers[N]),c=o.payload.channelId,g=Q(c),s=n.get(c);if(!s){let P=k(o.sessionRegistration);await C(e.connection,P),s={registration:P,lastCumulativeAtomic:"0"},n.set(c,s)}U(o,g),_({registration:s.registration,voucher:o,expectedCounterparty:r,previousCumulativeAtomic:s.lastCumulativeAtomic});let a=BigInt(o.payload.cumulativeAmount),L=BigInt(s.lastCumulativeAtomic),H=a-L;if(H>i)throw new l("cumulative_exceeds_cap",`single voucher increment ${H} exceeds maxPerVoucherAtomic ${i}`);await t.set(c,o),n.update(c,o.payload.cumulativeAmount);let B=new O(c,e.network,a,async P=>{throw new Error("SellerTab.charge() is not driven by the route handler; the buyer presents a fresh voucher per chunk. Use openSse(res, tab) for the metered-stream pattern.")});B.setSessionPublicKey(o.sessionPublicKey),u.tab=B,h()}catch(o){if(o instanceof m||o instanceof d||o instanceof f||o instanceof y||o instanceof l){p.status(402).json({error:"invalid_voucher",reason:o.reason??"unknown",detail:o.message});return}h(o)}}}function te(e){if(!e.tab)throw new Error("req.tab is missing \u2014 did tabMiddleware run on this route?");return e.tab}function ne(e,t){if(!t.tab)throw new Error("openSse requires options.tab");e.headersSent||(e.setHeader("Content-Type","text/event-stream"),e.setHeader("Cache-Control","no-cache"),e.setHeader("Connection","keep-alive"),typeof e.flushHeaders=="function"&&e.flushHeaders());let n=t.tab,r=BigInt(S(n.cumulative())),i=t.perUnit?BigInt(S(t.perUnit)):null,u=0n,p=!1;function h(g=1){if(p)return Promise.reject(new Error("meter ended"));if(i===null)return Promise.reject(new Error("charge() needs options.perUnit"));let s=i*BigInt(g),a=u+s;return a>r?Promise.reject(new l("cumulative_exceeds_cap",`chunk would push request total to ${v(a.toString())} beyond voucher-authorized budget ${v(r.toString())}`)):(u=a,Promise.resolve())}function o(g){if(p)throw new Error("meter ended");let a=(typeof g=="string"?g:Buffer.from(g).toString("utf8")).replace(/\n/g,"\\n");e.write(`data: ${a}
2
2
 
3
- `)}function a(){p||(p=!0,e.write(`event: end
4
- data: {"chargedAtomic":"${c}"}
3
+ `)}function c(){p||(p=!0,e.write(`event: end
4
+ data: {"chargedAtomic":"${u}"}
5
5
 
6
- `),e.end())}return{charge:g,send:o,end:a}}export{C as FileVoucherStore,b as InMemoryVoucherStore,d as InvalidRegistrationError,m as InvalidVoucherError,y as InvalidVoucherSignatureError,f as OnChainVerificationError,u as ScopeViolationError,N as TAB_VOUCHER_HEADER,$ as enforceScope,ne as openSse,V as parseRegistration,D as readVaultState,te as requireTab,ee as tabMiddleware,U as verifyRegistrationOnChain,_ as verifyVoucherSignature};
6
+ `),e.end())}return{charge:h,send:o,end:c}}export{R as FileVoucherStore,b as InMemoryVoucherStore,d as InvalidRegistrationError,m as InvalidVoucherError,y as InvalidVoucherSignatureError,f as OnChainVerificationError,l as ScopeViolationError,N as TAB_VOUCHER_HEADER,_ as enforceScope,ne as openSse,k as parseRegistration,D as readVaultState,te as requireTab,ee as tabMiddleware,C as verifyRegistrationOnChain,U as verifyVoucherSignature};
@@ -1,5 +1,5 @@
1
1
  import { ClientChannelStorage } from '@x402/evm/batch-settlement/client';
2
- import { E as EvmWallet } from './types-RxdlGPaG.cjs';
2
+ import { E as EvmWallet } from './types-xQu1U4xk.cjs';
3
3
 
4
4
  /**
5
5
  * Buyer withdrawal escape hatch for batch-settlement escrow channels.
@@ -1,4 +1,4 @@
1
- import { TabNetworkId, HumanAmount, SessionScope, SessionKey, VoucherPayload, SignedVoucher, AtomicAmount } from '@dexterai/vault/types';
1
+ import { TabNetworkId, HumanAmount, AtomicAmount, SignedVoucher, SessionScope, SessionKey, VoucherPayload } from '@dexterai/vault/types';
2
2
 
3
3
  /**
4
4
  * @dexterai/x402/tab — type contract for the OTS-backed streaming payment module.
@@ -72,8 +72,23 @@ interface Tab {
72
72
  readonly channelId: string;
73
73
  /** Which network the underlying vault lives on. */
74
74
  readonly network: TabNetworkId;
75
+ /**
76
+ * The seller this tab was opened against — the base58 pubkey resolved
77
+ * from `OpenTabOptions.seller` at open time (the same value bound into
78
+ * the session scope's `allowedCounterparty`). payAndFetch matches it
79
+ * against a `tab`-scheme option's `payTo` before spending this tab.
80
+ */
81
+ readonly counterparty: string;
75
82
  /** Live state. Re-reads after every voucher exchange. */
76
83
  readonly state: TabState;
84
+ /**
85
+ * Sign the next cumulative voucher for an increment, WITHOUT sending any
86
+ * request. This is the negotiation primitive: payAndFetch uses it to pay a
87
+ * `tab`-scheme accepts entry by attaching the X-Tab-Voucher header itself.
88
+ * Same counter, same scope enforcement as stream() — throws
89
+ * SessionScopeExceededError past the cap.
90
+ */
91
+ signNextVoucher(incrementAtomic: AtomicAmount): Promise<SignedVoucher>;
77
92
  /**
78
93
  * Streamed paid request. Returns an async iterable of chunks. Voucher
79
94
  * signing is internal: the seller demands a fresh session-signed voucher
@@ -119,6 +134,11 @@ interface OpenTabOptions {
119
134
  perUnitCap: HumanAmount;
120
135
  /** Max cumulative for the WHOLE tab — the session-key cap. */
121
136
  totalCap: HumanAmount;
137
+ /** Revolving capacity cap (human units, same scale as perUnitCap/totalCap).
138
+ * Optional; defaults to totalCap. The on-chain meter (current_outstanding)
139
+ * is checked against this — set it below totalCap to force capacity to
140
+ * revolve (turnover > 1). */
141
+ revolvingCapacity?: HumanAmount;
122
142
  /** Session expiry, seconds from now. Default: 3600 (1 hour). */
123
143
  sessionDuration?: number;
124
144
  /** Facilitator base URL. Default: https://facilitator.dexter.cash, overridable. */
@@ -1,4 +1,4 @@
1
- import { TabNetworkId, HumanAmount, SessionScope, SessionKey, VoucherPayload, SignedVoucher, AtomicAmount } from '@dexterai/vault/types';
1
+ import { TabNetworkId, HumanAmount, AtomicAmount, SignedVoucher, SessionScope, SessionKey, VoucherPayload } from '@dexterai/vault/types';
2
2
 
3
3
  /**
4
4
  * @dexterai/x402/tab — type contract for the OTS-backed streaming payment module.
@@ -72,8 +72,23 @@ interface Tab {
72
72
  readonly channelId: string;
73
73
  /** Which network the underlying vault lives on. */
74
74
  readonly network: TabNetworkId;
75
+ /**
76
+ * The seller this tab was opened against — the base58 pubkey resolved
77
+ * from `OpenTabOptions.seller` at open time (the same value bound into
78
+ * the session scope's `allowedCounterparty`). payAndFetch matches it
79
+ * against a `tab`-scheme option's `payTo` before spending this tab.
80
+ */
81
+ readonly counterparty: string;
75
82
  /** Live state. Re-reads after every voucher exchange. */
76
83
  readonly state: TabState;
84
+ /**
85
+ * Sign the next cumulative voucher for an increment, WITHOUT sending any
86
+ * request. This is the negotiation primitive: payAndFetch uses it to pay a
87
+ * `tab`-scheme accepts entry by attaching the X-Tab-Voucher header itself.
88
+ * Same counter, same scope enforcement as stream() — throws
89
+ * SessionScopeExceededError past the cap.
90
+ */
91
+ signNextVoucher(incrementAtomic: AtomicAmount): Promise<SignedVoucher>;
77
92
  /**
78
93
  * Streamed paid request. Returns an async iterable of chunks. Voucher
79
94
  * signing is internal: the seller demands a fresh session-signed voucher
@@ -119,6 +134,11 @@ interface OpenTabOptions {
119
134
  perUnitCap: HumanAmount;
120
135
  /** Max cumulative for the WHOLE tab — the session-key cap. */
121
136
  totalCap: HumanAmount;
137
+ /** Revolving capacity cap (human units, same scale as perUnitCap/totalCap).
138
+ * Optional; defaults to totalCap. The on-chain meter (current_outstanding)
139
+ * is checked against this — set it below totalCap to force capacity to
140
+ * revolve (turnover > 1). */
141
+ revolvingCapacity?: HumanAmount;
122
142
  /** Session expiry, seconds from now. Default: 3600 (1 hour). */
123
143
  sessionDuration?: number;
124
144
  /** Facilitator base URL. Default: https://facilitator.dexter.cash, overridable. */
@@ -1,6 +1,6 @@
1
1
  import { RequestHandler } from 'express';
2
2
  import { ChannelStorage } from '@x402/evm/batch-settlement/server';
3
- import { b as CloseReceipt } from './types-CTl7yVq6.js';
3
+ import { b as CloseReceipt } from './types-_wdHzVG-.js';
4
4
 
5
5
  /**
6
6
  * Result of closing one channel from the seller side. Either a settlement
@@ -1,6 +1,6 @@
1
1
  import { RequestHandler } from 'express';
2
2
  import { ChannelStorage } from '@x402/evm/batch-settlement/server';
3
- import { b as CloseReceipt } from './types-C8lyIOmX.cjs';
3
+ import { b as CloseReceipt } from './types-BdS_uvNs.cjs';
4
4
 
5
5
  /**
6
6
  * Result of closing one channel from the seller side. Either a settlement
@@ -1,5 +1,5 @@
1
1
  import { ClientChannelStorage } from '@x402/evm/batch-settlement/client';
2
- import { E as EvmWallet } from './types-RxdlGPaG.js';
2
+ import { E as EvmWallet } from './types-xQu1U4xk.js';
3
3
 
4
4
  /**
5
5
  * Buyer withdrawal escape hatch for batch-settlement escrow channels.
@@ -84,9 +84,11 @@ interface PaymentAccept {
84
84
  /**
85
85
  * Payment scheme: 'exact' for EIP-3009 chains, 'exact-approval' for
86
86
  * approval-based chains like BSC, 'batch-settlement' for the EVM
87
- * escrow-channel batching scheme (discrete API purchases, gas-amortized).
87
+ * escrow-channel batching scheme (discrete API purchases, gas-amortized),
88
+ * 'tab' (SVM only) for streaming session-key vouchers against an
89
+ * on-chain vault.
88
90
  */
89
- scheme: 'exact' | 'exact-approval' | 'batch-settlement';
91
+ scheme: 'exact' | 'exact-approval' | 'batch-settlement' | 'tab';
90
92
  /** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */
91
93
  network: string;
92
94
  /** Payment amount in atomic units (x402 v2 spec field) */
@@ -84,9 +84,11 @@ interface PaymentAccept {
84
84
  /**
85
85
  * Payment scheme: 'exact' for EIP-3009 chains, 'exact-approval' for
86
86
  * approval-based chains like BSC, 'batch-settlement' for the EVM
87
- * escrow-channel batching scheme (discrete API purchases, gas-amortized).
87
+ * escrow-channel batching scheme (discrete API purchases, gas-amortized),
88
+ * 'tab' (SVM only) for streaming session-key vouchers against an
89
+ * on-chain vault.
88
90
  */
89
- scheme: 'exact' | 'exact-approval' | 'batch-settlement';
91
+ scheme: 'exact' | 'exact-approval' | 'batch-settlement' | 'tab';
90
92
  /** CAIP-2 network identifier (v1: 'solana', v2: 'solana:5eykt...') */
91
93
  network: string;
92
94
  /** Payment amount in atomic units (x402 v2 spec field) */
@@ -1,5 +1,5 @@
1
1
  import { SponsoredRecommendation, SponsoredAccessSettlementInfo } from '@dexterai/x402-ads-types';
2
- import { C as ChainAdapter, W as WalletSet, A as AccessPassClientConfig, P as PaymentAccept, S as SettlementProbe } from './types-RxdlGPaG.cjs';
2
+ import { C as ChainAdapter, W as WalletSet, A as AccessPassClientConfig, P as PaymentAccept, S as SettlementProbe } from './types-xQu1U4xk.cjs';
3
3
 
4
4
  /**
5
5
  * Sponsored Access (Ads for Agents) — Client Helpers
@@ -1,5 +1,5 @@
1
1
  import { SponsoredRecommendation, SponsoredAccessSettlementInfo } from '@dexterai/x402-ads-types';
2
- import { C as ChainAdapter, W as WalletSet, A as AccessPassClientConfig, P as PaymentAccept, S as SettlementProbe } from './types-RxdlGPaG.js';
2
+ import { C as ChainAdapter, W as WalletSet, A as AccessPassClientConfig, P as PaymentAccept, S as SettlementProbe } from './types-xQu1U4xk.js';
3
3
 
4
4
  /**
5
5
  * Sponsored Access (Ads for Agents) — Client Helpers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexterai/x402",
3
- "version": "3.11.0",
3
+ "version": "3.12.0",
4
4
  "description": "Full-stack x402 SDK - add paid API monetization to any endpoint. Express middleware, React hooks, Access Pass, dynamic pricing. Solana, Base, Polygon, Arbitrum, Optimism, Avalanche, SKALE.",
5
5
  "author": "Dexter",
6
6
  "license": "MIT",
@@ -76,7 +76,7 @@
76
76
  "release:major": "npm version major && npm publish --access public"
77
77
  },
78
78
  "dependencies": {
79
- "@dexterai/vault": "^0.1.3",
79
+ "@dexterai/vault": "^0.4.2",
80
80
  "@dexterai/x402-ads-types": "^0.2.0",
81
81
  "@dexterai/x402-core": "^1.4.0",
82
82
  "@noble/curves": "^1.9.7",