@ghostlytools/cli 1.3.5 → 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/assets/{api-CgPyYfjH.js → api-Br3msjtR.js} +2 -2
  2. package/dist/assets/{api-Do7NjRg2.js → api-z9mlywrn.js} +9 -9
  3. package/dist/assets/{api-Do7NjRg2.js.map → api-z9mlywrn.js.map} +1 -1
  4. package/dist/assets/{apiKit-eh5qBKIl.js → apiKit-lX3Xx_hY.js} +2 -2
  5. package/dist/assets/{apiKit-eh5qBKIl.js.map → apiKit-lX3Xx_hY.js.map} +1 -1
  6. package/dist/assets/{apps-Cl9MtELY.js → apps-BUK6bEKW.js} +2 -2
  7. package/dist/assets/{apps-Cl9MtELY.js.map → apps-BUK6bEKW.js.map} +1 -1
  8. package/dist/assets/{apps-B83dRFga.js → apps-Dn7rCafG.js} +1 -1
  9. package/dist/assets/{arkade-B5Xn40bC.js → arkade-uqAXA5BT.js} +2 -2
  10. package/dist/assets/{arkade-B5Xn40bC.js.map → arkade-uqAXA5BT.js.map} +1 -1
  11. package/dist/assets/{backup-BjZcDFjL.js → backup-CJQK5Miu.js} +4 -4
  12. package/dist/assets/{backup-BjZcDFjL.js.map → backup-CJQK5Miu.js.map} +1 -1
  13. package/dist/assets/{backup-CDOh2tuw.js → backup-_JnqeFZb.js} +1 -1
  14. package/dist/assets/buttons-DDB9Xdha.js +2 -0
  15. package/dist/assets/{buttons-CUKHafux.js → buttons-DDvTuViY.js} +3 -3
  16. package/dist/assets/{buttons-CUKHafux.js.map → buttons-DDvTuViY.js.map} +1 -1
  17. package/dist/assets/{envelope-wspXCjle.js → envelope-BqvBb6hC.js} +2 -2
  18. package/dist/assets/{envelope-wspXCjle.js.map → envelope-BqvBb6hC.js.map} +1 -1
  19. package/dist/assets/{groupFiles-Cfrjo3YS.js → groupFiles-B1lL6wch.js} +3 -3
  20. package/dist/assets/{groupFiles-Cfrjo3YS.js.map → groupFiles-B1lL6wch.js.map} +1 -1
  21. package/dist/assets/{host-BdS-7nRW.js → host-DFzlKU3P.js} +8 -8
  22. package/dist/assets/{host-BdS-7nRW.js.map → host-DFzlKU3P.js.map} +1 -1
  23. package/dist/assets/{hyperdhtRelay-H97Vz73W.js → hyperdhtRelay-DwlOY6aD.js} +2 -2
  24. package/dist/assets/{hyperdhtRelay-H97Vz73W.js.map → hyperdhtRelay-DwlOY6aD.js.map} +1 -1
  25. package/dist/assets/{irohWeb-jErC-V0J.js → irohWeb-DZDCyL9b.js} +2 -2
  26. package/dist/assets/{irohWeb-jErC-V0J.js.map → irohWeb-DZDCyL9b.js.map} +1 -1
  27. package/dist/assets/{main-C14lA5aK.js → main-DgbdDDif.js} +14 -14
  28. package/dist/assets/{main-C14lA5aK.js.map → main-DgbdDDif.js.map} +1 -1
  29. package/dist/assets/{mainline-CYI1tZT7.js → mainline-BHE5-R3A.js} +2 -2
  30. package/dist/assets/{mainline-CYI1tZT7.js.map → mainline-BHE5-R3A.js.map} +1 -1
  31. package/dist/assets/{networkReason-CiHrd-kG.js → networkReason-Btv4b2C4.js} +2 -2
  32. package/dist/assets/{networkReason-CiHrd-kG.js.map → networkReason-Btv4b2C4.js.map} +1 -1
  33. package/dist/assets/{nwc-0JYm8Vys.js → nwc-z0pa5zql.js} +3 -3
  34. package/dist/assets/{nwc-0JYm8Vys.js.map → nwc-z0pa5zql.js.map} +1 -1
  35. package/dist/assets/{nwcProvider-2coJAsqr.js → nwcProvider-C1AhPoFW.js} +2 -2
  36. package/dist/assets/{nwcProvider-2coJAsqr.js.map → nwcProvider-C1AhPoFW.js.map} +1 -1
  37. package/dist/assets/{registry-sML83UvL.js → registry-C09xXyx-.js} +1 -1
  38. package/dist/assets/{registry-BptCF3tL.js → registry-JrnwUWmh.js} +3 -3
  39. package/dist/assets/{registry-BptCF3tL.js.map → registry-JrnwUWmh.js.map} +1 -1
  40. package/dist/assets/{resolve-2hGrvTen.js → resolve-Ssq1nDy4.js} +2 -2
  41. package/dist/assets/{resolve-2hGrvTen.js.map → resolve-Ssq1nDy4.js.map} +1 -1
  42. package/dist/assets/{server-BbPiCghe.js → server-D6FSznK5.js} +19 -13
  43. package/dist/assets/server-D6FSznK5.js.map +1 -0
  44. package/dist/assets/{src-BLzG4Sfv.js → src-XhjSWnp-.js} +7 -2
  45. package/dist/assets/{src-BLzG4Sfv.js.map → src-XhjSWnp-.js.map} +1 -1
  46. package/dist/assets/{turnRefs-ClWSgjb0.js → turnRefs-Cle33IoV.js} +2 -2
  47. package/dist/assets/{turnRefs-ClWSgjb0.js.map → turnRefs-Cle33IoV.js.map} +1 -1
  48. package/dist/assets/{usdt-zwyn_AQK.js → usdt-DO86yko2.js} +3 -3
  49. package/dist/assets/{usdt-zwyn_AQK.js.map → usdt-DO86yko2.js.map} +1 -1
  50. package/dist/ghostly.mjs +1 -1
  51. package/package.json +1 -1
  52. package/dist/assets/buttons-CFabREDv.js +0 -2
  53. package/dist/assets/server-BbPiCghe.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"arkade-B5Xn40bC.js","names":[],"sources":["../../../browser/src/engine/paymentAdapters/arkade.ts"],"sourcesContent":["import {\n Wallet, MnemonicIdentity, RestArkProvider, RestIndexerProvider, EsploraProvider,\n IndexedDBWalletRepository, IndexedDBContractRepository, ArkAddress, Transaction,\n assertSubmittedArkTxid, matchServerCheckpoints, assertAllowedSighashTypes, verifyTapscriptSignatures,\n isRecoverable, canRecoverOnchain, type StorageConfig,\n} from \"@arkade-os/sdk\";\nimport { tapLeafHash } from \"@scure/btc-signer/payment.js\";\nimport { assertWholeSats, arkAddressPrefix, validatePaymentTarget, type PaymentAdapter, type PaymentReview, type PaymentTarget } from \"@ghostly/core\";\n\nexport interface ArkConfig { network: \"bitcoin\" | \"regtest\" | \"signet\" | \"mutinynet\"; provider: string; explorer: string; serverKey: string; walletId: string }\nexport interface ArkPrepared { signedTx: string; checkpoints: string[]; txid: string; address: string; amount: number; fee: number; finalCheckpoints?: string[] }\nconst unbase64 = (s: string) => Uint8Array.from(atob(s),c=>c.charCodeAt(0));\nconst base64 = (bytes: Uint8Array) => btoa(Array.from(bytes,b=>String.fromCharCode(b)).join(\"\"));\nconst hex = (bytes: Uint8Array) => Array.from(bytes,b=>b.toString(16).padStart(2,\"0\")).join(\"\");\nclass PreparedOnly extends Error {}\nexport const ARK_NETWORKS: ArkConfig[\"network\"][] = [\"bitcoin\", \"mutinynet\", \"signet\", \"regtest\"];\n/** Payments go through review and approval; only the SDK's own settlement (renewal, onboarding) reaches the provider directly. */\nclass PreviewProvider extends RestArkProvider {\n authorizedFinalize?:{txid:string;run:(checkpoints:string[])=>Promise<void>};\n captured?: { signedTx:string; checkpoints:string[] };\n take() { return this.captured; }\n override async submitTx(signedTx:string,checkpoints:string[]):Promise<never> { this.captured={signedTx,checkpoints};throw new PreparedOnly(); }\n override async finalizeTx(txid:string,checkpoints:string[]):Promise<void> {\n if(this.authorizedFinalize?.txid!==txid)throw new Error(\"Preview cannot finalize an unapproved payment\");\n await this.authorizedFinalize.run(checkpoints);\n }\n}\nexport class ArkadeAdapter implements PaymentAdapter<ArkPrepared> {\n readonly method = \"arkade\" as const;\n private pending:Promise<unknown>=Promise.resolve();\n private serial<T>(run:()=>Promise<T>):Promise<T>{const next=this.pending.then(run,run);this.pending=next.catch(()=>{});return next;}\n prepare(target:PaymentTarget,amount:number,feeCap:number){return this.serial(()=>this.prepareOperation(target,amount,feeCap));}\n execute(review:PaymentReview,prepared:ArkPrepared,persist?:()=>Promise<void>){return this.serial(()=>this.executeOperation(review,prepared,persist));}\n reconcile(review:PaymentReview,prepared:ArkPrepared,persist?:()=>Promise<void>){return this.serial(()=>this.reconcileOperation(review,prepared,persist));}\n private constructor(readonly config: ArkConfig, private identity: MnemonicIdentity, private wallet: Wallet, private preview: PreviewProvider, private provider: RestArkProvider, private indexer: RestIndexerProvider,private repository:NonNullable<StorageConfig[\"walletRepository\"]>) {}\n static async connect(config: ArkConfig, mnemonic: string, storage?: StorageConfig): Promise<ArkadeAdapter> {\n if (!ARK_NETWORKS.includes(config.network)) throw new Error(\"Unsupported Ark network\");\n validatePaymentTarget({method:\"arkade\",network:config.network,provider:config.provider,asset:\"BTC\",unit:\"sat\",address:`${arkAddressPrefix(config.network)}configuration`,expiresAt:Date.now()+60000});\n validatePaymentTarget({method:\"arkade\",network:config.network,provider:config.explorer,asset:\"BTC\",unit:\"sat\",address:`${arkAddressPrefix(config.network)}configuration`,expiresAt:Date.now()+60000});\n const provider=new RestArkProvider(config.provider);\n const info=await provider.getInfo();\n if(info.network!==config.network || info.signerPubkey!==config.serverKey) throw new Error(\"Ark provider network or signing key changed. Review the wallet configuration.\");\n const identity=MnemonicIdentity.fromMnemonic(mnemonic,{isMainnet:config.network===\"bitcoin\"});\n const preview=new PreviewProvider(config.provider);\n const indexer=new RestIndexerProvider(config.provider);\n const repositories={...storage,walletRepository:storage?.walletRepository??new IndexedDBWalletRepository(`ghostly-ark-${config.walletId}`),contractRepository:storage?.contractRepository??new IndexedDBContractRepository(`ghostly-ark-${config.walletId}`)};\n const wallet=await Wallet.create({identity,arkProvider:preview,indexerProvider:indexer,onchainProvider:new EsploraProvider(config.explorer),\n // Renews VTXOs before they expire and settles on-chain deposits into Ark, so a wallet left alone keeps its funds.\n // A local regtest server expires batches within minutes; there the test drives settlement itself.\n settlementConfig:config.network===\"regtest\"?false:{boardingUtxoSweep:true},walletMode:\"hd\",storage:repositories});\n return new ArkadeAdapter(config,identity,wallet,preview,provider,indexer,repositories.walletRepository);\n }\n async address() { return this.wallet.getAddress(); }\n requestAddress() { return this.serial(async()=>(await this.wallet.getNewAddresses({types:[\"default\"],forceNew:true}))[0].address); }\n async balance() { return (await this.wallet.getBalance()).available; }\n /**\n * Sats whose batch expired before they were renewed, in three kinds, as a person can act on them:\n * - `sweeping`: past their expiry, but the server has not swept their batch yet (with any swept ones: none can be\n * recovered meanwhile). The SDK (0.4.76) counts them as\n * recoverable already, but it settles an unswept coin without the forfeit the server still asks for (arkd wants\n * one until the sweep): the batch fails (\"missing forfeit transactions\") and the server bans the coin for a while.\n * The sweep waits for the chain's time to pass the expiry: an hour or more on a real network, the expiry's blocks\n * on regtest.\n * - `recoverable`: swept, and a recovery takes them now.\n * - `small`: swept, but together under the server's dust limit: a recovery makes one new coin and the server makes\n * none that small, so `recoverVtxos` answers \"No recoverable VTXOs found\". They wait for more coins to expire.\n */\n async expired() {\n const now={timestamp:new Date()};\n let swept=0,sweeping=0;\n for(const vtxo of await this.wallet.getVtxos({withRecoverable:true,withUnrolled:false})) {\n if(isRecoverable(vtxo))swept+=vtxo.value;\n else if(!vtxo.isSwept&&canRecoverOnchain(vtxo,now))sweeping+=vtxo.value;\n }\n // What a recovery would hand back, over the same coins it would take (only asked when none is still sweeping),\n // net of the server's fees: `recoverVtxos` refuses under the dust limit, where this still answers above 0.\n const takes=!sweeping&&swept>0&&(await (await this.wallet.getVtxoManager()).getRecoverableBalance()).recoverable>=this.wallet.dustAmount;\n // While any waits for the sweep, none can be recovered: all of them wait.\n return sweeping?{recoverable:0,sweeping:sweeping+swept,small:0}:{recoverable:takes?swept:0,sweeping:0,small:takes?0:swept};\n }\n /**\n * When the first coin still spendable expires (ms), or undefined with none: a coin left alone past it is swept by the\n * server and comes back only through a recovery. An expiry in blocks is read against the explorer's tip, at about ten\n * minutes a block.\n */\n async expiresAt():Promise<number|undefined> {\n let at:number|undefined, height:number|undefined;\n for(const vtxo of await this.wallet.getVtxos({withRecoverable:false,withUnrolled:false})) {\n if(vtxo.isSwept||vtxo.isSpent)continue;\n if(vtxo.expiresAt instanceof Date){const ms=vtxo.expiresAt.getTime();if(Number.isFinite(ms))at=Math.min(at??ms,ms);}\n else if(typeof vtxo.expiresAtHeight===\"number\")height=Math.min(height??vtxo.expiresAtHeight,vtxo.expiresAtHeight);\n }\n if(height!==undefined){\n const tip=(await new EsploraProvider(this.config.explorer).getChainTip()).height;\n const ms=Date.now()+Math.max(0,height-tip)*600_000;\n at=Math.min(at??ms,ms);\n }\n return at;\n }\n /**\n * Moves swept outputs back into the balance, through the server's next batch. Refused while any expired coin is\n * not swept yet (`recoverVtxos` would name it too, and that batch fails for all of them), and when what is swept\n * is too little for a batch.\n */\n recover() { return this.serial(async()=>{\n const {recoverable,sweeping,small}=await this.expired();\n if(sweeping)throw new Error(\"Expired coins can be recovered once the Ark server has swept their batch. Try again later.\");\n if(small)throw new Error(\"Too few expired sats to recover on their own. They are recovered with the next coins that expire.\");\n if(!recoverable)throw new Error(\"Nothing to recover\");\n return (await this.wallet.getVtxoManager()).recoverVtxos();\n }); }\n /** On-chain deposits land here; the SDK's settlement moves them into Ark once confirmed. */\n boardingAddress() { return this.wallet.getBoardingAddress(); }\n /** Sats sent on-chain to the boarding address that are not in Ark yet. */\n async incoming() { return (await this.wallet.getBalance()).boarding.total; }\n async dispose() { await this.wallet.dispose(); }\n private async prepareOperation(target:PaymentTarget,amount:number,feeCap:number):Promise<{fee:number;prepared:ArkPrepared}> {\n validatePaymentTarget(target); assertWholeSats(amount); this.checkTarget(target);\n const info=await this.provider.getInfo();\n if(info.network!==this.config.network || info.signerPubkey!==this.config.serverKey)throw new Error(\"Ark provider configuration changed\");\n if(await this.balance()<amount)throw new Error(\"Insufficient Ark balance\");\n this.preview.captured=undefined;\n try { await this.wallet.send({address:target.address,amount}); }\n catch(error) {\n if(!(error instanceof PreparedOnly)) {\n // Provider errors may embed signed payment material; expose only this bounded message.\n // eslint-disable-next-line preserve-caught-error\n throw new Error(\"Could not prepare this Ark payment. Check funds, address and provider.\");\n }\n }\n const capture=this.preview.take();\n if(!capture)throw new Error(\"The Ark adapter did not prepare a transaction\");\n const tx=Transaction.fromPSBT(unbase64(capture.signedTx));\n const destination=ArkAddress.decode(target.address);\n let received=0n,input=0n,output=0n;\n for(let i=0;i<tx.inputsLength;i++){const prev=tx.getInput(i).witnessUtxo;if(!prev)throw new Error(\"Missing Ark input amount\");input+=prev.amount;}\n for(let i=0;i<tx.outputsLength;i++){const out=tx.getOutput(i);output+=out.amount??0n;if(out.script && [hex(destination.pkScript),hex(destination.subdustPkScript)].includes(hex(out.script)))received+=out.amount??0n;}\n const fee=Number(input-output);\n if(received!==BigInt(amount) || !Number.isSafeInteger(fee) || fee<0 || fee>feeCap)throw new Error(\"Prepared amount or fee does not match your review\");\n return {fee,prepared:{...capture,txid:tx.id,address:target.address,amount,fee}};\n }\n private async executeOperation(review:PaymentReview,p:ArkPrepared,persist?:()=>Promise<void>) {\n this.checkPrepared(review,p);\n const info=await this.provider.getInfo();\n if(info.network!==this.config.network || info.signerPubkey!==this.config.serverKey)throw new Error(\"Ark provider configuration changed\");\n const tx=Transaction.fromPSBT(unbase64(p.signedTx));\n const response=await this.provider.submitTx(p.signedTx,p.checkpoints);\n assertSubmittedArkTxid(response,tx,\"Ghostly Ark submission\");\n if(!response.finalArkTx)throw new Error(\"Provider returned no signed transaction\");\n this.verifyServer(Transaction.fromPSBT(unbase64(response.finalArkTx)),tx);\n const matched=matchServerCheckpoints(response.signedCheckpointTxs,p.checkpoints.map(c=>Transaction.fromPSBT(unbase64(c))),\"Ghostly Ark checkpoints\");\n const contracts=await (await this.wallet.getContractManager()).getContracts();\n const final=await Promise.all(matched.map(async ({server,local})=>{\n assertAllowedSighashTypes(server);this.verifyServer(server,local);\n let signed=server;\n for(let index=0;index<local.inputsLength;index++){\n const script=local.getInput(index).witnessUtxo?.script;\n const contract=script && contracts.find(c=>c.script===hex(script));\n if(!contract || contract.type!==\"default\")throw new Error(\"Checkpoint is not owned by this wallet profile\");\n const descriptor=contract.metadata?.signingDescriptor;\n const signer=typeof descriptor===\"string\" ? await this.wallet.signerForDescriptor(descriptor) : contract.params.pubKey===hex(await this.identity.xOnlyPublicKey()) ? this.identity : undefined;\n if(!signer)throw new Error(\"Checkpoint signing descriptor missing\");\n signed=await signer.sign(signed,[index]);\n }\n return base64(signed.toPSBT());\n }));\n p.finalCheckpoints=final;\n await persist?.();\n await this.provider.finalizeTx(p.txid,final);\n return {txid:p.txid,settled:true};\n }\n private async reconcileOperation(review:PaymentReview,p:ArkPrepared,persist?:()=>Promise<void>) {\n this.checkPrepared(review,p);\n if(await this.verifyReceipt(p.txid,p.address,p.amount))return {txid:p.txid,settled:true};\n if(!p.finalCheckpoints){\n // An empty, lagging indexer result can clear the SDK hint. The durable\n // Ghostly journal is authoritative that this approved attempt is unresolved.\n const state=await this.repository.getWalletState();\n if(!state)throw new Error(\"Ark wallet state is unavailable\");\n await this.repository.saveWalletState({...state,settings:{...state.settings,hasPendingTx:true}});\n // Ask for pending transactions using SDK ownership proofs. Only the previously\n // approved transaction may finalize; submitTx remains permanently blocked here.\n this.preview.authorizedFinalize={txid:p.txid,run:async checkpoints=>{\n const matched=matchServerCheckpoints(checkpoints,p.checkpoints.map(c=>Transaction.fromPSBT(unbase64(c))),\"Ghostly pending recovery\");\n for(const {server,local} of matched){assertAllowedSighashTypes(server);this.verifyServer(server,local);}\n p.finalCheckpoints=checkpoints;await persist?.();\n await this.provider.finalizeTx(p.txid,checkpoints);\n }};\n try {await this.wallet.finalizePendingTxs();}finally{this.preview.authorizedFinalize=undefined;}\n }\n // Resume only the same finalization, never a new send/submit after ambiguity.\n if(p.finalCheckpoints) {\n try {await this.provider.finalizeTx(p.txid,p.finalCheckpoints);}\n catch { /* A finalized transaction can reject repeated finalization; verify its receipt. */ }\n }\n return {txid:p.txid,settled:await this.verifyReceipt(p.txid,p.address,p.amount)};\n }\n /**\n * A virtual output paying `address` at least `amount`, made after `since` and none of `claimed`: how a\n * request paid from another Ark wallet is seen, with no receipt from the payer.\n */\n async received(address:string,amount:number,since:number,claimed:ReadonlySet<string>):Promise<string|undefined> {\n const destination=ArkAddress.decode(address);\n const scripts=[hex(destination.pkScript),hex(destination.subdustPkScript)];\n const {vtxos}=await this.indexer.getVtxos({scripts});\n return vtxos.find(v=>/^[a-f0-9]{64}$/.test(v.txid) && v.value>=amount && scripts.includes(v.script) && !claimed.has(v.txid) && v.createdAt.getTime()>=since-60_000)?.txid;\n }\n async verifyReceipt(txid:string,address:string,amount:number):Promise<boolean> {\n if(!/^[a-f0-9]{64}$/.test(txid))return false;\n const destination=ArkAddress.decode(address);\n const result=await this.indexer.getVtxos({outpoints:[{txid,vout:0}]});\n return result.vtxos.some(v=>v.txid===txid && v.value===amount && [hex(destination.pkScript),hex(destination.subdustPkScript)].includes(v.script));\n }\n private checkTarget(t:PaymentTarget) {\n if(t.method!==this.method || t.provider!==this.config.provider || t.network!==this.config.network || t.asset!==\"BTC\" || t.unit!==\"sat\")throw new Error(\"Payment method, network or provider does not match this wallet\");\n const decoded=ArkAddress.decode(t.address);\n const pinned=this.config.serverKey.replace(/^(02|03)(?=[a-f0-9]{64}$)/,\"\");\n if(hex(decoded.serverPubKey)!==pinned || decoded.hrp!==(this.config.network===\"bitcoin\"?\"ark\":\"tark\"))throw new Error(\"Ark address belongs to another provider or network\");\n }\n private verifyServer(server:Transaction,local:Transaction) {\n const key=this.config.serverKey.replace(/^(02|03)(?=[a-f0-9]{64}$)/,\"\");\n for(let index=0;index<local.inputsLength;index++){\n const leaf=local.getInput(index).tapLeafScript?.[0]?.[1];\n if(!leaf)throw new Error(\"Missing reviewed spend leaf\");\n verifyTapscriptSignatures(server,index,[key],undefined,undefined,tapLeafHash(leaf.subarray(0,-1),leaf[leaf.length-1]));\n }\n }\n private checkPrepared(r:PaymentReview,p:ArkPrepared) {\n this.checkTarget(r);\n if(p.address!==r.address || p.amount!==r.amount || p.fee!==r.fee || p.fee>r.feeCap || Transaction.fromPSBT(unbase64(p.signedTx)).id!==p.txid)throw new Error(\"Prepared payment does not match the approved review\");\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"arkade-uqAXA5BT.js","names":[],"sources":["../../../browser/src/engine/paymentAdapters/arkade.ts"],"sourcesContent":["import {\n Wallet, MnemonicIdentity, RestArkProvider, RestIndexerProvider, EsploraProvider,\n IndexedDBWalletRepository, IndexedDBContractRepository, ArkAddress, Transaction,\n assertSubmittedArkTxid, matchServerCheckpoints, assertAllowedSighashTypes, verifyTapscriptSignatures,\n isRecoverable, canRecoverOnchain, type StorageConfig,\n} from \"@arkade-os/sdk\";\nimport { tapLeafHash } from \"@scure/btc-signer/payment.js\";\nimport { assertWholeSats, arkAddressPrefix, validatePaymentTarget, type PaymentAdapter, type PaymentReview, type PaymentTarget } from \"@ghostly/core\";\n\nexport interface ArkConfig { network: \"bitcoin\" | \"regtest\" | \"signet\" | \"mutinynet\"; provider: string; explorer: string; serverKey: string; walletId: string }\nexport interface ArkPrepared { signedTx: string; checkpoints: string[]; txid: string; address: string; amount: number; fee: number; finalCheckpoints?: string[] }\nconst unbase64 = (s: string) => Uint8Array.from(atob(s),c=>c.charCodeAt(0));\nconst base64 = (bytes: Uint8Array) => btoa(Array.from(bytes,b=>String.fromCharCode(b)).join(\"\"));\nconst hex = (bytes: Uint8Array) => Array.from(bytes,b=>b.toString(16).padStart(2,\"0\")).join(\"\");\nclass PreparedOnly extends Error {}\nexport const ARK_NETWORKS: ArkConfig[\"network\"][] = [\"bitcoin\", \"mutinynet\", \"signet\", \"regtest\"];\n/** Payments go through review and approval; only the SDK's own settlement (renewal, onboarding) reaches the provider directly. */\nclass PreviewProvider extends RestArkProvider {\n authorizedFinalize?:{txid:string;run:(checkpoints:string[])=>Promise<void>};\n captured?: { signedTx:string; checkpoints:string[] };\n take() { return this.captured; }\n override async submitTx(signedTx:string,checkpoints:string[]):Promise<never> { this.captured={signedTx,checkpoints};throw new PreparedOnly(); }\n override async finalizeTx(txid:string,checkpoints:string[]):Promise<void> {\n if(this.authorizedFinalize?.txid!==txid)throw new Error(\"Preview cannot finalize an unapproved payment\");\n await this.authorizedFinalize.run(checkpoints);\n }\n}\nexport class ArkadeAdapter implements PaymentAdapter<ArkPrepared> {\n readonly method = \"arkade\" as const;\n private pending:Promise<unknown>=Promise.resolve();\n private serial<T>(run:()=>Promise<T>):Promise<T>{const next=this.pending.then(run,run);this.pending=next.catch(()=>{});return next;}\n prepare(target:PaymentTarget,amount:number,feeCap:number){return this.serial(()=>this.prepareOperation(target,amount,feeCap));}\n execute(review:PaymentReview,prepared:ArkPrepared,persist?:()=>Promise<void>){return this.serial(()=>this.executeOperation(review,prepared,persist));}\n reconcile(review:PaymentReview,prepared:ArkPrepared,persist?:()=>Promise<void>){return this.serial(()=>this.reconcileOperation(review,prepared,persist));}\n private constructor(readonly config: ArkConfig, private identity: MnemonicIdentity, private wallet: Wallet, private preview: PreviewProvider, private provider: RestArkProvider, private indexer: RestIndexerProvider,private repository:NonNullable<StorageConfig[\"walletRepository\"]>) {}\n static async connect(config: ArkConfig, mnemonic: string, storage?: StorageConfig): Promise<ArkadeAdapter> {\n if (!ARK_NETWORKS.includes(config.network)) throw new Error(\"Unsupported Ark network\");\n validatePaymentTarget({method:\"arkade\",network:config.network,provider:config.provider,asset:\"BTC\",unit:\"sat\",address:`${arkAddressPrefix(config.network)}configuration`,expiresAt:Date.now()+60000});\n validatePaymentTarget({method:\"arkade\",network:config.network,provider:config.explorer,asset:\"BTC\",unit:\"sat\",address:`${arkAddressPrefix(config.network)}configuration`,expiresAt:Date.now()+60000});\n const provider=new RestArkProvider(config.provider);\n const info=await provider.getInfo();\n if(info.network!==config.network || info.signerPubkey!==config.serverKey) throw new Error(\"Ark provider network or signing key changed. Review the wallet configuration.\");\n const identity=MnemonicIdentity.fromMnemonic(mnemonic,{isMainnet:config.network===\"bitcoin\"});\n const preview=new PreviewProvider(config.provider);\n const indexer=new RestIndexerProvider(config.provider);\n const repositories={...storage,walletRepository:storage?.walletRepository??new IndexedDBWalletRepository(`ghostly-ark-${config.walletId}`),contractRepository:storage?.contractRepository??new IndexedDBContractRepository(`ghostly-ark-${config.walletId}`)};\n const wallet=await Wallet.create({identity,arkProvider:preview,indexerProvider:indexer,onchainProvider:new EsploraProvider(config.explorer),\n // Renews VTXOs before they expire and settles on-chain deposits into Ark, so a wallet left alone keeps its funds.\n // A local regtest server expires batches within minutes; there the test drives settlement itself.\n settlementConfig:config.network===\"regtest\"?false:{boardingUtxoSweep:true},walletMode:\"hd\",storage:repositories});\n return new ArkadeAdapter(config,identity,wallet,preview,provider,indexer,repositories.walletRepository);\n }\n async address() { return this.wallet.getAddress(); }\n requestAddress() { return this.serial(async()=>(await this.wallet.getNewAddresses({types:[\"default\"],forceNew:true}))[0].address); }\n async balance() { return (await this.wallet.getBalance()).available; }\n /**\n * Sats whose batch expired before they were renewed, in three kinds, as a person can act on them:\n * - `sweeping`: past their expiry, but the server has not swept their batch yet (with any swept ones: none can be\n * recovered meanwhile). The SDK (0.4.76) counts them as\n * recoverable already, but it settles an unswept coin without the forfeit the server still asks for (arkd wants\n * one until the sweep): the batch fails (\"missing forfeit transactions\") and the server bans the coin for a while.\n * The sweep waits for the chain's time to pass the expiry: an hour or more on a real network, the expiry's blocks\n * on regtest.\n * - `recoverable`: swept, and a recovery takes them now.\n * - `small`: swept, but together under the server's dust limit: a recovery makes one new coin and the server makes\n * none that small, so `recoverVtxos` answers \"No recoverable VTXOs found\". They wait for more coins to expire.\n */\n async expired() {\n const now={timestamp:new Date()};\n let swept=0,sweeping=0;\n for(const vtxo of await this.wallet.getVtxos({withRecoverable:true,withUnrolled:false})) {\n if(isRecoverable(vtxo))swept+=vtxo.value;\n else if(!vtxo.isSwept&&canRecoverOnchain(vtxo,now))sweeping+=vtxo.value;\n }\n // What a recovery would hand back, over the same coins it would take (only asked when none is still sweeping),\n // net of the server's fees: `recoverVtxos` refuses under the dust limit, where this still answers above 0.\n const takes=!sweeping&&swept>0&&(await (await this.wallet.getVtxoManager()).getRecoverableBalance()).recoverable>=this.wallet.dustAmount;\n // While any waits for the sweep, none can be recovered: all of them wait.\n return sweeping?{recoverable:0,sweeping:sweeping+swept,small:0}:{recoverable:takes?swept:0,sweeping:0,small:takes?0:swept};\n }\n /**\n * When the first coin still spendable expires (ms), or undefined with none: a coin left alone past it is swept by the\n * server and comes back only through a recovery. An expiry in blocks is read against the explorer's tip, at about ten\n * minutes a block.\n */\n async expiresAt():Promise<number|undefined> {\n let at:number|undefined, height:number|undefined;\n for(const vtxo of await this.wallet.getVtxos({withRecoverable:false,withUnrolled:false})) {\n if(vtxo.isSwept||vtxo.isSpent)continue;\n if(vtxo.expiresAt instanceof Date){const ms=vtxo.expiresAt.getTime();if(Number.isFinite(ms))at=Math.min(at??ms,ms);}\n else if(typeof vtxo.expiresAtHeight===\"number\")height=Math.min(height??vtxo.expiresAtHeight,vtxo.expiresAtHeight);\n }\n if(height!==undefined){\n const tip=(await new EsploraProvider(this.config.explorer).getChainTip()).height;\n const ms=Date.now()+Math.max(0,height-tip)*600_000;\n at=Math.min(at??ms,ms);\n }\n return at;\n }\n /**\n * Moves swept outputs back into the balance, through the server's next batch. Refused while any expired coin is\n * not swept yet (`recoverVtxos` would name it too, and that batch fails for all of them), and when what is swept\n * is too little for a batch.\n */\n recover() { return this.serial(async()=>{\n const {recoverable,sweeping,small}=await this.expired();\n if(sweeping)throw new Error(\"Expired coins can be recovered once the Ark server has swept their batch. Try again later.\");\n if(small)throw new Error(\"Too few expired sats to recover on their own. They are recovered with the next coins that expire.\");\n if(!recoverable)throw new Error(\"Nothing to recover\");\n return (await this.wallet.getVtxoManager()).recoverVtxos();\n }); }\n /** On-chain deposits land here; the SDK's settlement moves them into Ark once confirmed. */\n boardingAddress() { return this.wallet.getBoardingAddress(); }\n /** Sats sent on-chain to the boarding address that are not in Ark yet. */\n async incoming() { return (await this.wallet.getBalance()).boarding.total; }\n async dispose() { await this.wallet.dispose(); }\n private async prepareOperation(target:PaymentTarget,amount:number,feeCap:number):Promise<{fee:number;prepared:ArkPrepared}> {\n validatePaymentTarget(target); assertWholeSats(amount); this.checkTarget(target);\n const info=await this.provider.getInfo();\n if(info.network!==this.config.network || info.signerPubkey!==this.config.serverKey)throw new Error(\"Ark provider configuration changed\");\n if(await this.balance()<amount)throw new Error(\"Insufficient Ark balance\");\n this.preview.captured=undefined;\n try { await this.wallet.send({address:target.address,amount}); }\n catch(error) {\n if(!(error instanceof PreparedOnly)) {\n // Provider errors may embed signed payment material; expose only this bounded message.\n // eslint-disable-next-line preserve-caught-error\n throw new Error(\"Could not prepare this Ark payment. Check funds, address and provider.\");\n }\n }\n const capture=this.preview.take();\n if(!capture)throw new Error(\"The Ark adapter did not prepare a transaction\");\n const tx=Transaction.fromPSBT(unbase64(capture.signedTx));\n const destination=ArkAddress.decode(target.address);\n let received=0n,input=0n,output=0n;\n for(let i=0;i<tx.inputsLength;i++){const prev=tx.getInput(i).witnessUtxo;if(!prev)throw new Error(\"Missing Ark input amount\");input+=prev.amount;}\n for(let i=0;i<tx.outputsLength;i++){const out=tx.getOutput(i);output+=out.amount??0n;if(out.script && [hex(destination.pkScript),hex(destination.subdustPkScript)].includes(hex(out.script)))received+=out.amount??0n;}\n const fee=Number(input-output);\n if(received!==BigInt(amount) || !Number.isSafeInteger(fee) || fee<0 || fee>feeCap)throw new Error(\"Prepared amount or fee does not match your review\");\n return {fee,prepared:{...capture,txid:tx.id,address:target.address,amount,fee}};\n }\n private async executeOperation(review:PaymentReview,p:ArkPrepared,persist?:()=>Promise<void>) {\n this.checkPrepared(review,p);\n const info=await this.provider.getInfo();\n if(info.network!==this.config.network || info.signerPubkey!==this.config.serverKey)throw new Error(\"Ark provider configuration changed\");\n const tx=Transaction.fromPSBT(unbase64(p.signedTx));\n const response=await this.provider.submitTx(p.signedTx,p.checkpoints);\n assertSubmittedArkTxid(response,tx,\"Ghostly Ark submission\");\n if(!response.finalArkTx)throw new Error(\"Provider returned no signed transaction\");\n this.verifyServer(Transaction.fromPSBT(unbase64(response.finalArkTx)),tx);\n const matched=matchServerCheckpoints(response.signedCheckpointTxs,p.checkpoints.map(c=>Transaction.fromPSBT(unbase64(c))),\"Ghostly Ark checkpoints\");\n const contracts=await (await this.wallet.getContractManager()).getContracts();\n const final=await Promise.all(matched.map(async ({server,local})=>{\n assertAllowedSighashTypes(server);this.verifyServer(server,local);\n let signed=server;\n for(let index=0;index<local.inputsLength;index++){\n const script=local.getInput(index).witnessUtxo?.script;\n const contract=script && contracts.find(c=>c.script===hex(script));\n if(!contract || contract.type!==\"default\")throw new Error(\"Checkpoint is not owned by this wallet profile\");\n const descriptor=contract.metadata?.signingDescriptor;\n const signer=typeof descriptor===\"string\" ? await this.wallet.signerForDescriptor(descriptor) : contract.params.pubKey===hex(await this.identity.xOnlyPublicKey()) ? this.identity : undefined;\n if(!signer)throw new Error(\"Checkpoint signing descriptor missing\");\n signed=await signer.sign(signed,[index]);\n }\n return base64(signed.toPSBT());\n }));\n p.finalCheckpoints=final;\n await persist?.();\n await this.provider.finalizeTx(p.txid,final);\n return {txid:p.txid,settled:true};\n }\n private async reconcileOperation(review:PaymentReview,p:ArkPrepared,persist?:()=>Promise<void>) {\n this.checkPrepared(review,p);\n if(await this.verifyReceipt(p.txid,p.address,p.amount))return {txid:p.txid,settled:true};\n if(!p.finalCheckpoints){\n // An empty, lagging indexer result can clear the SDK hint. The durable\n // Ghostly journal is authoritative that this approved attempt is unresolved.\n const state=await this.repository.getWalletState();\n if(!state)throw new Error(\"Ark wallet state is unavailable\");\n await this.repository.saveWalletState({...state,settings:{...state.settings,hasPendingTx:true}});\n // Ask for pending transactions using SDK ownership proofs. Only the previously\n // approved transaction may finalize; submitTx remains permanently blocked here.\n this.preview.authorizedFinalize={txid:p.txid,run:async checkpoints=>{\n const matched=matchServerCheckpoints(checkpoints,p.checkpoints.map(c=>Transaction.fromPSBT(unbase64(c))),\"Ghostly pending recovery\");\n for(const {server,local} of matched){assertAllowedSighashTypes(server);this.verifyServer(server,local);}\n p.finalCheckpoints=checkpoints;await persist?.();\n await this.provider.finalizeTx(p.txid,checkpoints);\n }};\n try {await this.wallet.finalizePendingTxs();}finally{this.preview.authorizedFinalize=undefined;}\n }\n // Resume only the same finalization, never a new send/submit after ambiguity.\n if(p.finalCheckpoints) {\n try {await this.provider.finalizeTx(p.txid,p.finalCheckpoints);}\n catch { /* A finalized transaction can reject repeated finalization; verify its receipt. */ }\n }\n return {txid:p.txid,settled:await this.verifyReceipt(p.txid,p.address,p.amount)};\n }\n /**\n * A virtual output paying `address` at least `amount`, made after `since` and none of `claimed`: how a\n * request paid from another Ark wallet is seen, with no receipt from the payer.\n */\n async received(address:string,amount:number,since:number,claimed:ReadonlySet<string>):Promise<string|undefined> {\n const destination=ArkAddress.decode(address);\n const scripts=[hex(destination.pkScript),hex(destination.subdustPkScript)];\n const {vtxos}=await this.indexer.getVtxos({scripts});\n return vtxos.find(v=>/^[a-f0-9]{64}$/.test(v.txid) && v.value>=amount && scripts.includes(v.script) && !claimed.has(v.txid) && v.createdAt.getTime()>=since-60_000)?.txid;\n }\n async verifyReceipt(txid:string,address:string,amount:number):Promise<boolean> {\n if(!/^[a-f0-9]{64}$/.test(txid))return false;\n const destination=ArkAddress.decode(address);\n const result=await this.indexer.getVtxos({outpoints:[{txid,vout:0}]});\n return result.vtxos.some(v=>v.txid===txid && v.value===amount && [hex(destination.pkScript),hex(destination.subdustPkScript)].includes(v.script));\n }\n private checkTarget(t:PaymentTarget) {\n if(t.method!==this.method || t.provider!==this.config.provider || t.network!==this.config.network || t.asset!==\"BTC\" || t.unit!==\"sat\")throw new Error(\"Payment method, network or provider does not match this wallet\");\n const decoded=ArkAddress.decode(t.address);\n const pinned=this.config.serverKey.replace(/^(02|03)(?=[a-f0-9]{64}$)/,\"\");\n if(hex(decoded.serverPubKey)!==pinned || decoded.hrp!==(this.config.network===\"bitcoin\"?\"ark\":\"tark\"))throw new Error(\"Ark address belongs to another provider or network\");\n }\n private verifyServer(server:Transaction,local:Transaction) {\n const key=this.config.serverKey.replace(/^(02|03)(?=[a-f0-9]{64}$)/,\"\");\n for(let index=0;index<local.inputsLength;index++){\n const leaf=local.getInput(index).tapLeafScript?.[0]?.[1];\n if(!leaf)throw new Error(\"Missing reviewed spend leaf\");\n verifyTapscriptSignatures(server,index,[key],undefined,undefined,tapLeafHash(leaf.subarray(0,-1),leaf[leaf.length-1]));\n }\n }\n private checkPrepared(r:PaymentReview,p:ArkPrepared) {\n this.checkTarget(r);\n if(p.address!==r.address || p.amount!==r.amount || p.fee!==r.fee || p.fee>r.feeCap || Transaction.fromPSBT(unbase64(p.signedTx)).id!==p.txid)throw new Error(\"Prepared payment does not match the approved review\");\n }\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { wl as toBase64Url } from "./src-BLzG4Sfv.js";
1
+ import { wl as toBase64Url } from "./src-XhjSWnp-.js";
2
2
  import { c as fileStore } from "./idb-B5L8wuOm.js";
3
- import { i as fromBase64Url, r as open$1 } from "./envelope-wspXCjle.js";
3
+ import { i as fromBase64Url, r as open$1 } from "./envelope-BqvBb6hC.js";
4
4
  import { t as CliError } from "./errors-B-zMxbcd.js";
5
- import { _ as str, n as bool } from "./apiKit-eh5qBKIl.js";
5
+ import { _ as str, n as bool } from "./apiKit-lX3Xx_hY.js";
6
6
  import { d as profilePaths, n as RESTORED_MARK, u as profileExists } from "./profiles-gPCjLOQq.js";
7
7
  import { existsSync } from "node:fs";
8
8
  import { link, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
@@ -728,4 +728,4 @@ async function writeWhole(paths, payload) {
728
728
  //#endregion
729
729
  export { backupFileProtection as n, restoreProfile as r, BACKUP_METHODS as t };
730
730
 
731
- //# sourceMappingURL=backup-BjZcDFjL.js.map
731
+ //# sourceMappingURL=backup-CJQK5Miu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"backup-BjZcDFjL.js","names":["join","openEnvelope"],"sources":["../../../browser/src/backup/stream.ts","../../../browser/src/backup/light.ts","../../src/backup.ts"],"sourcesContent":["import { fromBase64Url, toBase64Url } from \"./codec\";\n\n/**\n * A backup written and read a piece at a time (WISP 05, envelope version 2), so a profile with years of files never\n * has to fit in memory. The file is one line of clear JSON (the header), then frames:\n *\n * header line: {\"format\":\"ghostly-backup\",\"version\":2,\"protection\":\"passphrase\"|\"none\",...}\\n\n * frame: kind (1 byte) | length (4 bytes, big endian) | body\n *\n * A frame carries one record: a piece of JSON (gzip or plain) or a piece of a file's bytes, at most `FRAME_BYTES`\n * of them. The last frame is the final marker; a file that ends without it was cut short and is refused.\n *\n * - `passphrase`: the key comes from the passphrase as in version 1 (PBKDF2-SHA256, 600 000 rounds, 16 byte salt).\n * Every frame is sealed alone with AES-256-GCM: its nonce is the header's 7 random bytes, the frame's number\n * (4 bytes) and its kind (1 byte), and the SHA-256 of the header line is its associated data. A frame changed,\n * moved, dropped or retyped, a changed header, or a missing final marker fails to open.\n * - `none`: nothing is encrypted. A SHA-256 chain runs over the header line and every frame, and the final marker\n * holds its last value, so a damaged or cut file is refused. It proves nothing about who made the file.\n */\nexport const FRAME_BYTES = 1024 * 1024;\n/** No frame is larger than this: a reader refuses one that says it is, before reading it. */\nconst MAX_FRAME_BYTES = 64 * 1024 * 1024;\n/** One piece of JSON, decompressed: past it the backup is refused, not read. */\nconst MAX_JSON_BYTES = 256 * 1024 * 1024;\nconst MAX_HEADER_BYTES = 4096;\nconst ITERATIONS = 600_000;\nconst TAG_BYTES = 16;\n\nconst KIND = { gzipJson: 1, json: 2, bytes: 3, final: 255 } as const;\n\n/**\n * The envelope version of a bundle that carries a device set (WISP 06 § Compatibility and rollout): an app from before\n * WISP 06 refuses it with \"This backup comes from a newer Ghostly\", rather than restoring a copy that stops nothing.\n * WISP 06 names it version 2, but WISP 05's streamed bundle took 2 first; every other bundle is still 2.\n */\nexport const DEVICE_SET_BACKUP_VERSION = 3;\n\nexport interface BackupHeader {\n format: \"ghostly-backup\";\n version: 2 | 3;\n protection: \"passphrase\" | \"none\";\n kdf?: { name: \"PBKDF2-SHA256\"; iterations: number; salt: string };\n cipher?: { name: \"AES-256-GCM\"; nonce: string };\n check?: { name: \"SHA-256-chain\" };\n}\n\n/** Where a backup is written: in order, a piece at a time. */\nexport interface BackupSink { write(bytes: Uint8Array): Promise<void> }\n/** Where a backup is read from: any range, so nothing is read before it is needed. */\nexport interface BackupSource { readonly size: number; read(offset: number, length: number): Promise<Uint8Array> }\n/** One record of a backup: JSON text, or a piece of the bytes of the file the JSON before it announced. */\nexport type BackupRecord = { json: string; bytes?: undefined } | { bytes: Uint8Array; json?: undefined };\n\n/** Thrown when the work was cancelled (`AbortSignal`): nothing is wrong with the backup. */\nexport class BackupCancelled extends Error {\n constructor() { super(\"Cancelled\"); this.name = \"AbortError\"; }\n}\nexport const isCancelled = (error: unknown) => (error as { name?: string })?.name === \"AbortError\";\nconst check = (signal?: AbortSignal) => { if (signal?.aborted) throw new BackupCancelled(); };\n\n/** A Blob (a picked file) as a source, read in slices: never `stream()`, which WebKit fails on for some stored Blobs. */\nexport const blobSource = (blob: Blob): BackupSource => ({ size: blob.size, read: async (offset, length) => new Uint8Array(await blob.slice(offset, offset + length).arrayBuffer()) });\nexport const bytesSource = (bytes: Uint8Array): BackupSource => ({ size: bytes.length, read: async (offset, length) => bytes.subarray(offset, offset + length) });\n/** A sink that keeps what is written (tests, and a backup small enough to hand over whole). */\nexport function memorySink(): BackupSink & { bytes(): Uint8Array } {\n const parts: Uint8Array[] = [];\n return {\n write: async (bytes) => { parts.push(bytes.slice()); },\n bytes: () => { const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0)); let at = 0; for (const p of parts) { out.set(p, at); at += p.length; } return out; },\n };\n}\n\nconst canCompress = () => typeof CompressionStream !== \"undefined\";\nasync function through(bytes: Uint8Array, stream: CompressionStream | DecompressionStream, limit = Infinity): Promise<Uint8Array> {\n const reader = new Blob([bytes as BlobPart]).stream().pipeThrough(stream).getReader();\n const chunks: Uint8Array[] = [];\n let size = 0;\n for (let next = await reader.read(); !next.done; next = await reader.read()) {\n size += next.value.length;\n if (size > limit) { await reader.cancel(); throw new Error(\"This backup is too large to restore\"); }\n chunks.push(next.value);\n }\n if (chunks.length === 1) return chunks[0];\n const out = new Uint8Array(size);\n let at = 0;\n for (const chunk of chunks) { out.set(chunk, at); at += chunk.length; }\n return out;\n}\n\nasync function keyFrom(passphrase: string, salt: Uint8Array, iterations: number): Promise<CryptoKey> {\n const material = await crypto.subtle.importKey(\"raw\", new TextEncoder().encode(passphrase), \"PBKDF2\", false, [\"deriveKey\"]);\n return crypto.subtle.deriveKey({ name: \"PBKDF2\", hash: \"SHA-256\", salt: salt as BufferSource, iterations }, material, { name: \"AES-GCM\", length: 256 }, false, [\"encrypt\", \"decrypt\"]);\n}\nconst sha256 = async (...parts: Uint8Array[]) => new Uint8Array(await crypto.subtle.digest(\"SHA-256\", join(parts) as BufferSource));\nfunction join(parts: Uint8Array[]): Uint8Array {\n if (parts.length === 1) return parts[0];\n const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));\n let at = 0;\n for (const part of parts) { out.set(part, at); at += part.length; }\n return out;\n}\nfunction prefix(kind: number, length: number): Uint8Array {\n const out = new Uint8Array(5);\n out[0] = kind;\n new DataView(out.buffer).setUint32(1, length);\n return out;\n}\nfunction nonce(base: Uint8Array, frame: number, kind: number): Uint8Array {\n const out = new Uint8Array(12);\n out.set(base, 0);\n new DataView(out.buffer).setUint32(7, frame);\n out[11] = kind;\n return out;\n}\n\n/** Writes a backup: `json` and `bytes` in the order they are to be read, then `finish`. */\nexport class BackupWriter {\n private frame = 0;\n /** Bytes handed to the sink so far. */\n written = 0;\n private finished = false;\n /** Set once the sink refused a frame: the error it gave, thrown again at every later write. */\n private failed: { error: unknown } | null = null;\n\n private constructor(private readonly sink: BackupSink, private readonly key: CryptoKey | null, private readonly base: Uint8Array, private chain: Uint8Array, private readonly signal?: AbortSignal) {}\n\n /** `passphrase` null: a backup anyone can read (the caller has made sure that is what the person chose). */\n static async start(sink: BackupSink, passphrase: string | null, signal?: AbortSignal, version: 2 | 3 = 2): Promise<BackupWriter> {\n if (passphrase !== null && passphrase.length < 12) throw new Error(\"Use at least 12 characters for the backup passphrase\");\n check(signal);\n const salt = crypto.getRandomValues(new Uint8Array(16)), base = crypto.getRandomValues(new Uint8Array(7));\n const header: BackupHeader = passphrase === null\n ? { format: \"ghostly-backup\", version, protection: \"none\", check: { name: \"SHA-256-chain\" } }\n : { format: \"ghostly-backup\", version, protection: \"passphrase\", kdf: { name: \"PBKDF2-SHA256\", iterations: ITERATIONS, salt: toBase64Url(salt) }, cipher: { name: \"AES-256-GCM\", nonce: toBase64Url(base) } };\n const line = new TextEncoder().encode(`${JSON.stringify(header)}\\n`);\n const key = passphrase === null ? null : await keyFrom(passphrase, salt, ITERATIONS);\n const writer = new BackupWriter(sink, key, base, await sha256(line), signal);\n await sink.write(line);\n writer.written = line.length;\n return writer;\n }\n\n private async put(kind: number, plain: Uint8Array): Promise<void> {\n check(this.signal);\n if (this.failed) throw this.failed.error;\n if (this.finished) throw new Error(\"This backup is already finished\");\n if (this.frame >= 0xffffffff) throw new Error(\"This backup is too large\");\n // What a reader refuses is not written: found out now, not when the backup is needed.\n if (plain.length > MAX_FRAME_BYTES) throw new Error(\"A record of this profile is too large for a backup\");\n let out: Uint8Array;\n if (this.key) {\n // The header's digest is every frame's associated data: a frame of another backup, or under a changed header, fails.\n const sealed = new Uint8Array(await crypto.subtle.encrypt({ name: \"AES-GCM\", iv: nonce(this.base, this.frame, kind) as BufferSource, additionalData: this.chain as BufferSource }, this.key, plain as BufferSource));\n out = join([prefix(kind, sealed.length), sealed]);\n } else {\n const head = prefix(kind, kind === KIND.final ? 32 : plain.length);\n if (kind === KIND.final) out = join([head, this.chain]);\n else { out = join([head, plain]); this.chain = await sha256(this.chain, out); }\n }\n this.frame += 1;\n // A frame the sink did not take (no room left, say) is the end of this backup: every frame after it would be\n // numbered past a gap, and the file would never open. Nothing more is written, whatever the caller does next.\n try { await this.sink.write(out); } catch (error) { this.failed = { error }; throw error; }\n this.written += out.length;\n }\n\n /** One piece of JSON, compressed where the platform can. */\n async json(text: string): Promise<void> {\n const plain = new TextEncoder().encode(text);\n if (plain.length > MAX_JSON_BYTES) throw new Error(\"A record of this profile is too large for a backup\");\n if (canCompress()) await this.put(KIND.gzipJson, await through(plain, new CompressionStream(\"gzip\")));\n else await this.put(KIND.json, plain);\n }\n\n /** Bytes of the file the last JSON announced, cut into frames. */\n async bytes(chunk: Uint8Array): Promise<void> {\n for (let at = 0; at < chunk.length; at += FRAME_BYTES) await this.put(KIND.bytes, chunk.subarray(at, Math.min(chunk.length, at + FRAME_BYTES)));\n }\n\n /** The final marker: without it a reader refuses the backup. Resolves to the size of the whole backup. */\n async finish(): Promise<number> {\n await this.put(KIND.final, new Uint8Array());\n this.finished = true;\n return this.written;\n }\n}\n\n/** The first line of a version 2 backup, or null when this is not one (version 1 is one JSON object with no line end). */\nexport async function readBackupHeader(source: BackupSource): Promise<{ header: BackupHeader; line: Uint8Array } | null> {\n const start = await source.read(0, Math.min(MAX_HEADER_BYTES, source.size));\n const end = start.indexOf(10);\n if (end < 0) return null;\n let header: BackupHeader;\n try { header = JSON.parse(new TextDecoder().decode(start.subarray(0, end))) as BackupHeader; } catch { return null; }\n if (header?.format !== \"ghostly-backup\" || typeof header.version !== \"number\" || header.version < 2) return null;\n if (header.version !== 2 && header.version !== DEVICE_SET_BACKUP_VERSION) throw new Error(\"This backup comes from a newer Ghostly; update to restore it\");\n if (header.protection === \"none\") {\n if (header.check?.name !== \"SHA-256-chain\") throw new Error(\"Unsupported backup format\");\n } else if (header.protection === \"passphrase\") {\n if (header.kdf?.name !== \"PBKDF2-SHA256\" || !(header.kdf.iterations >= ITERATIONS) || header.kdf.iterations > 10_000_000 || header.cipher?.name !== \"AES-256-GCM\") throw new Error(\"Unsupported backup encryption\");\n } else throw new Error(\"Unsupported backup format\");\n return { header, line: start.slice(0, end + 1) };\n}\n\n/** How a backup is protected, read from its clear header without opening it: version 1 is always sealed. */\nexport async function backupProtection(source: BackupSource): Promise<\"passphrase\" | \"none\"> {\n return (await readBackupHeader(source))?.header.protection ?? \"passphrase\";\n}\n\nconst DAMAGED = \"This backup is damaged: it was changed or cut short\";\n\n/** Reads a version 2 backup record by record. Nothing of a frame is handed out before it passed its check. */\nexport class BackupReader {\n private frame = 0;\n private at: number;\n private block: Uint8Array = new Uint8Array();\n private blockAt = 0;\n private done = false;\n\n private constructor(private readonly source: BackupSource, private readonly key: CryptoKey | null, private readonly base: Uint8Array, private chain: Uint8Array, start: number, private readonly signal?: AbortSignal) { this.at = start; }\n\n get protection(): \"passphrase\" | \"none\" { return this.key ? \"passphrase\" : \"none\"; }\n\n /** Null when `source` is not a version 2 backup. `passphrase` is needed, and used, only for a sealed one. */\n static async open(source: BackupSource, passphrase?: string, signal?: AbortSignal): Promise<BackupReader | null> {\n const found = await readBackupHeader(source);\n if (!found) return null;\n check(signal);\n const { header, line } = found;\n if (header.protection === \"none\") return new BackupReader(source, null, new Uint8Array(), await sha256(line), line.length, signal);\n let key: CryptoKey, base: Uint8Array;\n try {\n base = fromBase64Url(header.cipher!.nonce);\n if (base.length !== 7) throw new Error(\"nonce\");\n key = await keyFrom(passphrase ?? \"\", fromBase64Url(header.kdf!.salt), header.kdf!.iterations);\n } catch { throw new Error(\"Unsupported backup encryption\"); }\n return new BackupReader(source, key, base, await sha256(line), line.length, signal);\n }\n\n /** Bytes of the source read so far (for progress). */\n get position(): number { return this.at; }\n\n /** `length` bytes at the reading point, through a read-ahead block so a source is asked a few times per megabyte, not per frame. */\n private async take(length: number): Promise<Uint8Array> {\n if (this.at + length > this.source.size) throw new Error(DAMAGED);\n const from = this.at - this.blockAt;\n if (from < 0 || from + length > this.block.length) {\n this.block = await this.source.read(this.at, Math.min(this.source.size - this.at, Math.max(length, 4 * FRAME_BYTES)));\n this.blockAt = this.at;\n if (this.block.length < length) throw new Error(DAMAGED);\n }\n const out = this.block.subarray(this.at - this.blockAt, this.at - this.blockAt + length);\n this.at += length;\n return out;\n }\n\n /** The next record, or null at the end: only then is the backup known to be whole. */\n async next(): Promise<BackupRecord | null> {\n check(this.signal);\n if (this.done) return null;\n const head = await this.take(5);\n const kind = head[0], length = new DataView(head.buffer, head.byteOffset, 5).getUint32(1);\n if (length > MAX_FRAME_BYTES + TAG_BYTES || !Object.values(KIND).includes(kind as never)) throw new Error(DAMAGED);\n const headCopy = head.slice();\n const body = await this.take(length);\n let plain: Uint8Array;\n if (this.key) {\n try {\n plain = new Uint8Array(await crypto.subtle.decrypt({ name: \"AES-GCM\", iv: nonce(this.base, this.frame, kind) as BufferSource, additionalData: this.chain as BufferSource }, this.key, body as BufferSource));\n } catch { throw new Error(this.frame === 0 ? \"Wrong passphrase, or the backup was changed\" : DAMAGED); }\n } else if (kind === KIND.final) {\n if (body.length !== 32 || body.some((byte, i) => byte !== this.chain[i])) throw new Error(DAMAGED);\n plain = new Uint8Array();\n } else {\n this.chain = await sha256(this.chain, headCopy, body);\n plain = body.slice();\n }\n this.frame += 1;\n if (kind === KIND.final) {\n if (plain.length || this.at !== this.source.size) throw new Error(DAMAGED);\n this.done = true;\n return null;\n }\n if (kind === KIND.bytes) return { bytes: plain };\n const text = kind === KIND.gzipJson ? await through(plain, new DecompressionStream(\"gzip\"), MAX_JSON_BYTES).catch((error: unknown) => { throw error instanceof Error && error.message.startsWith(\"This backup is too large\") ? error : new Error(DAMAGED); }) : plain;\n return { json: new TextDecoder().decode(text) };\n }\n}\n","/**\n * A light backup (WISP 05 § Light backups): everything of a profile but the bytes of its larger files. Their messages\n * and records stay; after a restore they show as not in this backup.\n *\n * The cut: a file of at most 1 MiB keeps its bytes (a short clip, a sticker, a screenshot, a document), and so does a\n * voice message of at most 4 MiB. Voice messages are part of the conversation, and the longest one the apps record\n * (15 minutes at 32 kbit/s, `VOICE_LIMITS`) is about 3.6 MB, so every recorded one stays. Photos from a camera\n * (usually 2 to 8 MB), videos and large files are what is left out.\n */\nexport const LIGHT_FILE_BYTES = 1024 * 1024;\nexport const LIGHT_VOICE_BYTES = 4 * 1024 * 1024;\n\n/** What a light bundle says of itself in its first record: its cut, and what it left out. */\nexport interface LightMark {\n maxFileBytes: number;\n maxVoiceBytes: number;\n /** How many files were left out, and their bytes. */\n files: number;\n bytes: number;\n}\n\n/** Whether a file of this size keeps its bytes in a light backup. */\nexport const keptInLight = (size: number, voice: boolean): boolean => size <= (voice ? LIGHT_VOICE_BYTES : LIGHT_FILE_BYTES);\n","import { existsSync } from \"node:fs\";\nimport { link, mkdir, open, readdir, readFile, rename, rm, stat, writeFile, type FileHandle } from \"node:fs/promises\";\nimport { basename, dirname, join, relative, resolve, sep } from \"node:path\";\nimport { open as openEnvelope } from \"@ghostly/browser/backup/envelope\";\nimport { BackupReader, BackupWriter, FRAME_BYTES, readBackupHeader, type BackupSource } from \"@ghostly/browser/backup/stream\";\nimport { LIGHT_FILE_BYTES, LIGHT_VOICE_BYTES, keptInLight, type LightMark } from \"@ghostly/browser/backup/light\";\nimport { fileStore } from \"@ghostly/browser/shared/idb\";\nimport { bool, str, type Method } from \"./apiKit\";\nimport { CliError } from \"./errors\";\nimport { RESTORED_MARK, profileExists, profilePaths, type ProfilePaths } from \"./profiles\";\n\n/**\n * A headless profile's backup: the WISP 05 envelope around the profile itself, its store and its files, written and\n * read a piece at a time (envelope version 2), so a profile with large files never has to fit in memory. It restores\n * into a new headless profile; the app's own backups are made from its page's storage and are not this format\n * (WISP 1100 § Parity). A backup an older CLI made (version 1: everything in one sealed JSON document) still restores.\n */\nconst FORMAT = \"ghostly-cli-profile/2\";\nconst FORMAT_1 = \"ghostly-cli-profile/1\";\n\n/** Version 1: the store and every file as base64 in one document. */\ninterface Payload1 { format: typeof FORMAT_1; createdAt: number; store: string; files: { path: string; data: string }[] }\n/** Version 2, in order: the head, then each file (the store first) followed by its bytes, then the end. */\n/**\n * `light`: a light backup (WISP 05 § Light backups) left the bytes of these files out. Their records are in the store;\n * the restored profile marks them when it first starts (`markLeftOutFiles`).\n */\ntype BackupRecord =\n | { t: \"profile\"; format: typeof FORMAT; createdAt: number; files: number; bytes: number; light?: LightMark & { ids: string[] } }\n | { t: \"file\"; kind: \"store\" | \"file\"; path?: string; size: number }\n | { t: \"end\"; files: number; bytes: number };\n\nasync function walk(root: string, dir = root, out: string[] = []): Promise<string[]> {\n let names: string[];\n try { names = await readdir(dir); } catch { return out; }\n for (const name of names) {\n const path = join(dir, name);\n if ((await stat(path)).isDirectory()) await walk(root, path, out);\n else out.push(relative(root, path));\n }\n return out;\n}\n\n/** A file's first `size` bytes into the backup, a frame at a time. One that got shorter meanwhile stops the backup. */\nasync function copyIn(writer: BackupWriter, path: string, size: number): Promise<void> {\n const handle = await open(path, \"r\");\n try {\n const buffer = new Uint8Array(FRAME_BYTES);\n for (let at = 0; at < size;) {\n const { bytesRead } = await handle.read(buffer, 0, Math.min(FRAME_BYTES, size - at), at);\n if (!bytesRead) throw new CliError(\"unavailable\", `${path} changed while it was backed up: try again`);\n await writer.bytes(buffer.subarray(0, bytesRead));\n at += bytesRead;\n }\n } finally { await handle.close(); }\n}\n\nexport const BACKUP_METHODS: Record<string, Method> = {\n /**\n * Writes the backup to `path` (resolved by the command), sealed with `passphrase`, or not encrypted when\n * `noPassphrase` is true: one of the two, never a guess. The store is written whole for it, every value inline. It is\n * written beside `path` and only takes its name once it is complete: a backup that fails leaves no file. `light`:\n * the files over the cut (`keptInLight`) are left out, their records kept.\n */\n async \"profile.backup\"(ctx, params) {\n const path = resolve(str(params, \"path\", true));\n // `refused`, not `confirm` (exit 5, \"pass --force\"): backup takes no flag that writes over a file.\n if (existsSync(path)) throw new CliError(\"refused\", `${path} exists: choose another name`);\n const passphrase = str(params, \"passphrase\"), unsealed = bool(params, \"noPassphrase\");\n if (!passphrase && !unsealed) throw new CliError(\"bad_request\", \"passphrase is required (or noPassphrase: true for a backup anyone can read)\");\n if (passphrase && unsealed) throw new CliError(\"bad_request\", \"Give a passphrase or noPassphrase, not both\");\n // The folder it goes in, checked first: a missing one is named, not the file written beside `path`.\n const folder = dirname(path);\n if (!(await stat(folder)).isDirectory()) throw new CliError(\"not_found\", `No such file or folder: ${folder}`);\n const paths = ctx.runtime.paths;\n // The store as one snapshot with every value in it (the messages too, which the profile keeps in rows.db): a backup\n // any ghostly restores, this one or an older one.\n const store = join(paths.db, `backup-store-${process.pid}.bin`);\n await ctx.runtime.store.exportSnapshot(store);\n const light = bool(params, \"light\");\n const all = await Promise.all((await walk(paths.files)).map(async (name) => ({ name, size: (await stat(join(paths.files, name))).size })));\n // A file's name is its id: only one that could be a voice message under its larger cut is looked up.\n const kept = async (file: { name: string; size: number }) => keptInLight(file.size, false) || (file.size <= LIGHT_VOICE_BYTES && !!(await fileStore.get(basename(file.name)).catch(() => undefined))?.metadata?.voice);\n const files: typeof all = [], leftOut: typeof all = [];\n for (const file of all) (!light || (await kept(file)) ? files : leftOut).push(file);\n const storeSize = (await stat(store)).size, bytes = files.reduce((sum, file) => sum + file.size, 0);\n const leftOutBytes = leftOut.reduce((sum, file) => sum + file.size, 0);\n\n const partial = `${path}.partial-${process.pid}`;\n let out: FileHandle | null = null;\n try {\n out = await openFile(partial);\n const handle = out;\n let writer: BackupWriter;\n try { writer = await BackupWriter.start({ write: async (chunk) => { await handle.write(chunk); } }, unsealed ? null : passphrase!); } catch (error) { throw new CliError(\"bad_request\", error instanceof Error ? error.message : String(error)); }\n const put = (record: BackupRecord) => writer.json(JSON.stringify(record));\n await put({ t: \"profile\", format: FORMAT, createdAt: Date.now(), files: files.length, bytes, ...(light && { light: { maxFileBytes: LIGHT_FILE_BYTES, maxVoiceBytes: LIGHT_VOICE_BYTES, files: leftOut.length, bytes: leftOutBytes, ids: leftOut.map((file) => basename(file.name)) } }) });\n await put({ t: \"file\", kind: \"store\", size: storeSize });\n await copyIn(writer, store, storeSize);\n for (const file of files) {\n await put({ t: \"file\", kind: \"file\", path: file.name.split(sep).join(\"/\"), size: file.size });\n await copyIn(writer, join(paths.files, file.name), file.size);\n }\n await put({ t: \"end\", files: files.length, bytes });\n const written = await writer.finish();\n await handle.sync();\n await handle.close();\n out = null;\n // Its name only now, and never over a file that appeared meanwhile.\n try { await link(partial, path); } catch (error) { throw (error as NodeJS.ErrnoException).code === \"EEXIST\" ? new CliError(\"refused\", `${path} exists: choose another name`) : error; }\n return { path, bytes: written, files: files.length, protection: unsealed ? \"none\" : \"passphrase\", ...(light && { light: true, leftOut: leftOut.length, leftOutBytes }) };\n } finally {\n await out?.close().catch(() => {});\n await rm(partial, { force: true });\n await rm(store, { force: true });\n }\n },\n};\n\nasync function openFile(path: string): Promise<FileHandle> {\n try { return await open(path, \"wx\", 0o600); } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") { await rm(path, { force: true }); return open(path, \"wx\", 0o600); }\n throw error;\n }\n}\n\n/** A file on disk as a backup's source: read in ranges, never whole. A folder opens, and is refused as one here. */\nasync function fileSource(path: string): Promise<BackupSource & { close(): Promise<void> }> {\n const handle = await open(path, \"r\");\n const found = await handle.stat();\n if (found.isDirectory()) { await handle.close(); throw new CliError(\"bad_request\", `${path} is a folder: name a file`); }\n const { size } = found;\n return {\n size,\n read: async (offset, length) => {\n const buffer = new Uint8Array(Math.max(0, Math.min(length, size - offset)));\n let got = 0;\n while (got < buffer.length) { const { bytesRead } = await handle.read(buffer, got, buffer.length - got, offset + got); if (!bytesRead) break; got += bytesRead; }\n return buffer.subarray(0, got);\n },\n close: () => handle.close(),\n };\n}\n\n/** A version 1 backup is one JSON object, and `seal` writes its format first. */\nconst VERSION_1_START = /^\\s*\\{\\s*\"format\"\\s*:\\s*\"ghostly-backup\"/;\n\n/**\n * Whether the backup at `file` is sealed with a passphrase or was made without one, read from its clear header.\n * A file that is neither version is refused here, before any passphrase is asked for: the path is what is wrong.\n */\nexport async function backupFileProtection(file: string): Promise<\"passphrase\" | \"none\"> {\n const source = await fileSource(file);\n try {\n let found: Awaited<ReturnType<typeof readBackupHeader>>;\n try { found = await readBackupHeader(source); } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n if (found) return found.header.protection;\n if (!VERSION_1_START.test(new TextDecoder().decode(await source.read(0, 256)))) throw new CliError(\"refused\", \"This is not a Ghostly backup\");\n return \"passphrase\";\n } finally { await source.close(); }\n}\n\n/** Where a path named by a backup lands inside the new profile's files folder; never outside it. */\nfunction inside(root: string, path: unknown): string {\n const target = typeof path === \"string\" && path ? resolve(root, path) : \"\";\n if (!target.startsWith(resolve(root) + sep)) throw new CliError(\"refused\", \"The backup names a file outside its folder\");\n return target;\n}\n\n/** While a restore writes it, the profile's folder is not a profile: no profile name starts with a dot (`listProfiles`). */\nconst RESTORING = /^\\.restoring-(.+)-(\\d+)$/;\n\n/**\n * The folder a restore writes into, beside the profiles: it takes the profile's name only once everything is in it,\n * so a restore that is stopped at any moment (Ctrl-C, a kill, the power) leaves no profile, whole or half-made.\n * What an earlier restore that was stopped left behind is removed here, once the process that made it is gone.\n */\nasync function restoringFolder(home: string, name: string): Promise<ProfilePaths> {\n const root = join(home, \"profiles\");\n await mkdir(root, { recursive: true, mode: 0o700 });\n for (const entry of await readdir(root)) {\n const pid = Number(RESTORING.exec(entry)?.[2]);\n if (!pid) continue;\n let alive = pid === process.pid;\n if (!alive) try { process.kill(pid, 0); alive = true; } catch (error) { alive = (error as NodeJS.ErrnoException).code === \"EPERM\"; }\n if (!alive) await rm(join(root, entry), { recursive: true, force: true });\n }\n const dir = join(root, `.restoring-${name}-${process.pid}`);\n await rm(dir, { recursive: true, force: true });\n await mkdir(dir, { mode: 0o700 });\n return { ...profilePaths(home, name), dir, db: join(dir, \"db\"), files: join(dir, \"files\") };\n}\n\n/**\n * The folder is whole: marked as a restored copy (`RESTORED_MARK`, which names the files a light backup left out),\n * then given the profile's name. Never over a profile.\n */\nasync function finishRestore(home: string, name: string, work: ProfilePaths, leftOut: string[] = []): Promise<ProfilePaths> {\n const paths = profilePaths(home, name);\n await writeFile(join(work.dir, RESTORED_MARK), leftOut.length ? JSON.stringify({ leftOut }) : \"\", { mode: 0o600 });\n if (existsSync(paths.dir)) throw new CliError(\"refused\", `Profile ${name} exists: restore into a new name`);\n await rename(work.dir, paths.dir);\n return paths;\n}\n\n/**\n * Opens the backup at `file` into a new profile (never over an existing one), with its passphrase when it has one.\n * Runs with no engine: nothing is open yet. A backup that is refused, damaged or cut short leaves no profile behind,\n * and neither does a restore that is stopped while it runs (`restoringFolder`). The profile is marked as a restored\n * copy: its engine, the first time it starts, treats the money in it as a restore does (`RESTORED_MARK`).\n */\nexport async function restoreProfile(home: string, name: string, file: string, passphrase?: string): Promise<ProfilePaths> {\n if (profileExists(home, name)) throw new CliError(\"refused\", `Profile ${name} exists: restore into a new name`);\n const source = await fileSource(file);\n let work: ProfilePaths | null = null;\n try {\n let reader: BackupReader | null;\n try { reader = await BackupReader.open(source, passphrase); } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n if (!reader) {\n const payload = await readWhole(file, passphrase ?? \"\");\n work = await restoringFolder(home, name);\n await writeWhole(work, payload);\n return await finishRestore(home, name, work);\n }\n const next = async () => { try { return await reader.next(); } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); } };\n const parse = (json: string) => { try { return JSON.parse(json) as BackupRecord; } catch { throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\"); } };\n\n // The first record is read (and with it the passphrase checked) before any folder is made.\n const first = await next();\n const head = first?.json === undefined ? null : parse(first.json);\n if (head?.t !== \"profile\" || head.format !== FORMAT) throw new CliError(\"refused\", \"This backup is not a headless Ghostly profile\");\n const leftOut = Array.isArray(head.light?.ids) ? head.light.ids.filter((id): id is string => typeof id === \"string\") : [];\n work = await restoringFolder(home, name);\n const paths = work;\n await mkdir(paths.db, { recursive: true, mode: 0o700 });\n let out: { handle: FileHandle; left: number } | null = null, files = 0, bytes = 0, ended = false, stored = false;\n try {\n for (let record = await next(); record; record = await next()) {\n if (ended) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\");\n if (record.bytes) {\n if (!out || record.bytes.length > out.left) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\");\n await out.handle.write(record.bytes);\n out.left -= record.bytes.length;\n continue;\n }\n if (out) { if (out.left) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\"); await out.handle.close(); out = null; }\n const value = parse(record.json);\n if (value.t === \"end\") { ended = value.files === files && value.bytes === bytes; if (!ended) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\"); continue; }\n if (value.t !== \"file\" || !Number.isSafeInteger(value.size) || value.size < 0) continue;\n let target: string;\n if (value.kind === \"store\") { target = join(paths.db, \"snapshot.bin\"); stored = true; }\n else { target = inside(paths.files, value.path); await mkdir(dirname(target), { recursive: true, mode: 0o700 }); files += 1; bytes += value.size; }\n out = { handle: await open(target, \"wx\", 0o600), left: value.size };\n }\n if (out || !ended || !stored) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\");\n } finally { await out?.handle.close().catch(() => {}); }\n return await finishRestore(home, name, work, leftOut);\n } catch (error) {\n if (work) await rm(work.dir, { recursive: true, force: true });\n throw error;\n } finally {\n await source.close();\n }\n}\n\n/** A version 1 backup: one sealed JSON document, read whole. */\nasync function readWhole(file: string, passphrase: string): Promise<Payload1> {\n let payload: Payload1;\n try { payload = JSON.parse(await openEnvelope(await readFile(file, \"utf8\"), passphrase)) as Payload1; } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n if (payload?.format !== FORMAT_1 || typeof payload.store !== \"string\" || !Array.isArray(payload.files)) throw new CliError(\"refused\", \"This backup is not a headless Ghostly profile\");\n return payload;\n}\n\nasync function writeWhole(paths: ProfilePaths, payload: Payload1): Promise<void> {\n await mkdir(paths.db, { recursive: true, mode: 0o700 });\n await writeFile(join(paths.db, \"snapshot.bin\"), Buffer.from(payload.store, \"base64\"), { mode: 0o600 });\n for (const entry of payload.files) {\n const target = inside(paths.files, entry.path);\n await mkdir(dirname(target), { recursive: true, mode: 0o700 });\n await writeFile(target, Buffer.from(entry.data, \"base64\"), { mode: 0o600 });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"backup-CJQK5Miu.js","names":["join","openEnvelope"],"sources":["../../../browser/src/backup/stream.ts","../../../browser/src/backup/light.ts","../../src/backup.ts"],"sourcesContent":["import { fromBase64Url, toBase64Url } from \"./codec\";\n\n/**\n * A backup written and read a piece at a time (WISP 05, envelope version 2), so a profile with years of files never\n * has to fit in memory. The file is one line of clear JSON (the header), then frames:\n *\n * header line: {\"format\":\"ghostly-backup\",\"version\":2,\"protection\":\"passphrase\"|\"none\",...}\\n\n * frame: kind (1 byte) | length (4 bytes, big endian) | body\n *\n * A frame carries one record: a piece of JSON (gzip or plain) or a piece of a file's bytes, at most `FRAME_BYTES`\n * of them. The last frame is the final marker; a file that ends without it was cut short and is refused.\n *\n * - `passphrase`: the key comes from the passphrase as in version 1 (PBKDF2-SHA256, 600 000 rounds, 16 byte salt).\n * Every frame is sealed alone with AES-256-GCM: its nonce is the header's 7 random bytes, the frame's number\n * (4 bytes) and its kind (1 byte), and the SHA-256 of the header line is its associated data. A frame changed,\n * moved, dropped or retyped, a changed header, or a missing final marker fails to open.\n * - `none`: nothing is encrypted. A SHA-256 chain runs over the header line and every frame, and the final marker\n * holds its last value, so a damaged or cut file is refused. It proves nothing about who made the file.\n */\nexport const FRAME_BYTES = 1024 * 1024;\n/** No frame is larger than this: a reader refuses one that says it is, before reading it. */\nconst MAX_FRAME_BYTES = 64 * 1024 * 1024;\n/** One piece of JSON, decompressed: past it the backup is refused, not read. */\nconst MAX_JSON_BYTES = 256 * 1024 * 1024;\nconst MAX_HEADER_BYTES = 4096;\nconst ITERATIONS = 600_000;\nconst TAG_BYTES = 16;\n\nconst KIND = { gzipJson: 1, json: 2, bytes: 3, final: 255 } as const;\n\n/**\n * The envelope version of a bundle that carries a device set (WISP 06 § Compatibility and rollout): an app from before\n * WISP 06 refuses it with \"This backup comes from a newer Ghostly\", rather than restoring a copy that stops nothing.\n * WISP 06 names it version 2, but WISP 05's streamed bundle took 2 first; every other bundle is still 2.\n */\nexport const DEVICE_SET_BACKUP_VERSION = 3;\n\nexport interface BackupHeader {\n format: \"ghostly-backup\";\n version: 2 | 3;\n protection: \"passphrase\" | \"none\";\n kdf?: { name: \"PBKDF2-SHA256\"; iterations: number; salt: string };\n cipher?: { name: \"AES-256-GCM\"; nonce: string };\n check?: { name: \"SHA-256-chain\" };\n}\n\n/** Where a backup is written: in order, a piece at a time. */\nexport interface BackupSink { write(bytes: Uint8Array): Promise<void> }\n/** Where a backup is read from: any range, so nothing is read before it is needed. */\nexport interface BackupSource { readonly size: number; read(offset: number, length: number): Promise<Uint8Array> }\n/** One record of a backup: JSON text, or a piece of the bytes of the file the JSON before it announced. */\nexport type BackupRecord = { json: string; bytes?: undefined } | { bytes: Uint8Array; json?: undefined };\n\n/** Thrown when the work was cancelled (`AbortSignal`): nothing is wrong with the backup. */\nexport class BackupCancelled extends Error {\n constructor() { super(\"Cancelled\"); this.name = \"AbortError\"; }\n}\nexport const isCancelled = (error: unknown) => (error as { name?: string })?.name === \"AbortError\";\nconst check = (signal?: AbortSignal) => { if (signal?.aborted) throw new BackupCancelled(); };\n\n/** A Blob (a picked file) as a source, read in slices: never `stream()`, which WebKit fails on for some stored Blobs. */\nexport const blobSource = (blob: Blob): BackupSource => ({ size: blob.size, read: async (offset, length) => new Uint8Array(await blob.slice(offset, offset + length).arrayBuffer()) });\nexport const bytesSource = (bytes: Uint8Array): BackupSource => ({ size: bytes.length, read: async (offset, length) => bytes.subarray(offset, offset + length) });\n/** A sink that keeps what is written (tests, and a backup small enough to hand over whole). */\nexport function memorySink(): BackupSink & { bytes(): Uint8Array } {\n const parts: Uint8Array[] = [];\n return {\n write: async (bytes) => { parts.push(bytes.slice()); },\n bytes: () => { const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0)); let at = 0; for (const p of parts) { out.set(p, at); at += p.length; } return out; },\n };\n}\n\nconst canCompress = () => typeof CompressionStream !== \"undefined\";\nasync function through(bytes: Uint8Array, stream: CompressionStream | DecompressionStream, limit = Infinity): Promise<Uint8Array> {\n const reader = new Blob([bytes as BlobPart]).stream().pipeThrough(stream).getReader();\n const chunks: Uint8Array[] = [];\n let size = 0;\n for (let next = await reader.read(); !next.done; next = await reader.read()) {\n size += next.value.length;\n if (size > limit) { await reader.cancel(); throw new Error(\"This backup is too large to restore\"); }\n chunks.push(next.value);\n }\n if (chunks.length === 1) return chunks[0];\n const out = new Uint8Array(size);\n let at = 0;\n for (const chunk of chunks) { out.set(chunk, at); at += chunk.length; }\n return out;\n}\n\nasync function keyFrom(passphrase: string, salt: Uint8Array, iterations: number): Promise<CryptoKey> {\n const material = await crypto.subtle.importKey(\"raw\", new TextEncoder().encode(passphrase), \"PBKDF2\", false, [\"deriveKey\"]);\n return crypto.subtle.deriveKey({ name: \"PBKDF2\", hash: \"SHA-256\", salt: salt as BufferSource, iterations }, material, { name: \"AES-GCM\", length: 256 }, false, [\"encrypt\", \"decrypt\"]);\n}\nconst sha256 = async (...parts: Uint8Array[]) => new Uint8Array(await crypto.subtle.digest(\"SHA-256\", join(parts) as BufferSource));\nfunction join(parts: Uint8Array[]): Uint8Array {\n if (parts.length === 1) return parts[0];\n const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));\n let at = 0;\n for (const part of parts) { out.set(part, at); at += part.length; }\n return out;\n}\nfunction prefix(kind: number, length: number): Uint8Array {\n const out = new Uint8Array(5);\n out[0] = kind;\n new DataView(out.buffer).setUint32(1, length);\n return out;\n}\nfunction nonce(base: Uint8Array, frame: number, kind: number): Uint8Array {\n const out = new Uint8Array(12);\n out.set(base, 0);\n new DataView(out.buffer).setUint32(7, frame);\n out[11] = kind;\n return out;\n}\n\n/** Writes a backup: `json` and `bytes` in the order they are to be read, then `finish`. */\nexport class BackupWriter {\n private frame = 0;\n /** Bytes handed to the sink so far. */\n written = 0;\n private finished = false;\n /** Set once the sink refused a frame: the error it gave, thrown again at every later write. */\n private failed: { error: unknown } | null = null;\n\n private constructor(private readonly sink: BackupSink, private readonly key: CryptoKey | null, private readonly base: Uint8Array, private chain: Uint8Array, private readonly signal?: AbortSignal) {}\n\n /** `passphrase` null: a backup anyone can read (the caller has made sure that is what the person chose). */\n static async start(sink: BackupSink, passphrase: string | null, signal?: AbortSignal, version: 2 | 3 = 2): Promise<BackupWriter> {\n if (passphrase !== null && passphrase.length < 12) throw new Error(\"Use at least 12 characters for the backup passphrase\");\n check(signal);\n const salt = crypto.getRandomValues(new Uint8Array(16)), base = crypto.getRandomValues(new Uint8Array(7));\n const header: BackupHeader = passphrase === null\n ? { format: \"ghostly-backup\", version, protection: \"none\", check: { name: \"SHA-256-chain\" } }\n : { format: \"ghostly-backup\", version, protection: \"passphrase\", kdf: { name: \"PBKDF2-SHA256\", iterations: ITERATIONS, salt: toBase64Url(salt) }, cipher: { name: \"AES-256-GCM\", nonce: toBase64Url(base) } };\n const line = new TextEncoder().encode(`${JSON.stringify(header)}\\n`);\n const key = passphrase === null ? null : await keyFrom(passphrase, salt, ITERATIONS);\n const writer = new BackupWriter(sink, key, base, await sha256(line), signal);\n await sink.write(line);\n writer.written = line.length;\n return writer;\n }\n\n private async put(kind: number, plain: Uint8Array): Promise<void> {\n check(this.signal);\n if (this.failed) throw this.failed.error;\n if (this.finished) throw new Error(\"This backup is already finished\");\n if (this.frame >= 0xffffffff) throw new Error(\"This backup is too large\");\n // What a reader refuses is not written: found out now, not when the backup is needed.\n if (plain.length > MAX_FRAME_BYTES) throw new Error(\"A record of this profile is too large for a backup\");\n let out: Uint8Array;\n if (this.key) {\n // The header's digest is every frame's associated data: a frame of another backup, or under a changed header, fails.\n const sealed = new Uint8Array(await crypto.subtle.encrypt({ name: \"AES-GCM\", iv: nonce(this.base, this.frame, kind) as BufferSource, additionalData: this.chain as BufferSource }, this.key, plain as BufferSource));\n out = join([prefix(kind, sealed.length), sealed]);\n } else {\n const head = prefix(kind, kind === KIND.final ? 32 : plain.length);\n if (kind === KIND.final) out = join([head, this.chain]);\n else { out = join([head, plain]); this.chain = await sha256(this.chain, out); }\n }\n this.frame += 1;\n // A frame the sink did not take (no room left, say) is the end of this backup: every frame after it would be\n // numbered past a gap, and the file would never open. Nothing more is written, whatever the caller does next.\n try { await this.sink.write(out); } catch (error) { this.failed = { error }; throw error; }\n this.written += out.length;\n }\n\n /** One piece of JSON, compressed where the platform can. */\n async json(text: string): Promise<void> {\n const plain = new TextEncoder().encode(text);\n if (plain.length > MAX_JSON_BYTES) throw new Error(\"A record of this profile is too large for a backup\");\n if (canCompress()) await this.put(KIND.gzipJson, await through(plain, new CompressionStream(\"gzip\")));\n else await this.put(KIND.json, plain);\n }\n\n /** Bytes of the file the last JSON announced, cut into frames. */\n async bytes(chunk: Uint8Array): Promise<void> {\n for (let at = 0; at < chunk.length; at += FRAME_BYTES) await this.put(KIND.bytes, chunk.subarray(at, Math.min(chunk.length, at + FRAME_BYTES)));\n }\n\n /** The final marker: without it a reader refuses the backup. Resolves to the size of the whole backup. */\n async finish(): Promise<number> {\n await this.put(KIND.final, new Uint8Array());\n this.finished = true;\n return this.written;\n }\n}\n\n/** The first line of a version 2 backup, or null when this is not one (version 1 is one JSON object with no line end). */\nexport async function readBackupHeader(source: BackupSource): Promise<{ header: BackupHeader; line: Uint8Array } | null> {\n const start = await source.read(0, Math.min(MAX_HEADER_BYTES, source.size));\n const end = start.indexOf(10);\n if (end < 0) return null;\n let header: BackupHeader;\n try { header = JSON.parse(new TextDecoder().decode(start.subarray(0, end))) as BackupHeader; } catch { return null; }\n if (header?.format !== \"ghostly-backup\" || typeof header.version !== \"number\" || header.version < 2) return null;\n if (header.version !== 2 && header.version !== DEVICE_SET_BACKUP_VERSION) throw new Error(\"This backup comes from a newer Ghostly; update to restore it\");\n if (header.protection === \"none\") {\n if (header.check?.name !== \"SHA-256-chain\") throw new Error(\"Unsupported backup format\");\n } else if (header.protection === \"passphrase\") {\n if (header.kdf?.name !== \"PBKDF2-SHA256\" || !(header.kdf.iterations >= ITERATIONS) || header.kdf.iterations > 10_000_000 || header.cipher?.name !== \"AES-256-GCM\") throw new Error(\"Unsupported backup encryption\");\n } else throw new Error(\"Unsupported backup format\");\n return { header, line: start.slice(0, end + 1) };\n}\n\n/** How a backup is protected, read from its clear header without opening it: version 1 is always sealed. */\nexport async function backupProtection(source: BackupSource): Promise<\"passphrase\" | \"none\"> {\n return (await readBackupHeader(source))?.header.protection ?? \"passphrase\";\n}\n\nconst DAMAGED = \"This backup is damaged: it was changed or cut short\";\n\n/** Reads a version 2 backup record by record. Nothing of a frame is handed out before it passed its check. */\nexport class BackupReader {\n private frame = 0;\n private at: number;\n private block: Uint8Array = new Uint8Array();\n private blockAt = 0;\n private done = false;\n\n private constructor(private readonly source: BackupSource, private readonly key: CryptoKey | null, private readonly base: Uint8Array, private chain: Uint8Array, start: number, private readonly signal?: AbortSignal) { this.at = start; }\n\n get protection(): \"passphrase\" | \"none\" { return this.key ? \"passphrase\" : \"none\"; }\n\n /** Null when `source` is not a version 2 backup. `passphrase` is needed, and used, only for a sealed one. */\n static async open(source: BackupSource, passphrase?: string, signal?: AbortSignal): Promise<BackupReader | null> {\n const found = await readBackupHeader(source);\n if (!found) return null;\n check(signal);\n const { header, line } = found;\n if (header.protection === \"none\") return new BackupReader(source, null, new Uint8Array(), await sha256(line), line.length, signal);\n let key: CryptoKey, base: Uint8Array;\n try {\n base = fromBase64Url(header.cipher!.nonce);\n if (base.length !== 7) throw new Error(\"nonce\");\n key = await keyFrom(passphrase ?? \"\", fromBase64Url(header.kdf!.salt), header.kdf!.iterations);\n } catch { throw new Error(\"Unsupported backup encryption\"); }\n return new BackupReader(source, key, base, await sha256(line), line.length, signal);\n }\n\n /** Bytes of the source read so far (for progress). */\n get position(): number { return this.at; }\n\n /** `length` bytes at the reading point, through a read-ahead block so a source is asked a few times per megabyte, not per frame. */\n private async take(length: number): Promise<Uint8Array> {\n if (this.at + length > this.source.size) throw new Error(DAMAGED);\n const from = this.at - this.blockAt;\n if (from < 0 || from + length > this.block.length) {\n this.block = await this.source.read(this.at, Math.min(this.source.size - this.at, Math.max(length, 4 * FRAME_BYTES)));\n this.blockAt = this.at;\n if (this.block.length < length) throw new Error(DAMAGED);\n }\n const out = this.block.subarray(this.at - this.blockAt, this.at - this.blockAt + length);\n this.at += length;\n return out;\n }\n\n /** The next record, or null at the end: only then is the backup known to be whole. */\n async next(): Promise<BackupRecord | null> {\n check(this.signal);\n if (this.done) return null;\n const head = await this.take(5);\n const kind = head[0], length = new DataView(head.buffer, head.byteOffset, 5).getUint32(1);\n if (length > MAX_FRAME_BYTES + TAG_BYTES || !Object.values(KIND).includes(kind as never)) throw new Error(DAMAGED);\n const headCopy = head.slice();\n const body = await this.take(length);\n let plain: Uint8Array;\n if (this.key) {\n try {\n plain = new Uint8Array(await crypto.subtle.decrypt({ name: \"AES-GCM\", iv: nonce(this.base, this.frame, kind) as BufferSource, additionalData: this.chain as BufferSource }, this.key, body as BufferSource));\n } catch { throw new Error(this.frame === 0 ? \"Wrong passphrase, or the backup was changed\" : DAMAGED); }\n } else if (kind === KIND.final) {\n if (body.length !== 32 || body.some((byte, i) => byte !== this.chain[i])) throw new Error(DAMAGED);\n plain = new Uint8Array();\n } else {\n this.chain = await sha256(this.chain, headCopy, body);\n plain = body.slice();\n }\n this.frame += 1;\n if (kind === KIND.final) {\n if (plain.length || this.at !== this.source.size) throw new Error(DAMAGED);\n this.done = true;\n return null;\n }\n if (kind === KIND.bytes) return { bytes: plain };\n const text = kind === KIND.gzipJson ? await through(plain, new DecompressionStream(\"gzip\"), MAX_JSON_BYTES).catch((error: unknown) => { throw error instanceof Error && error.message.startsWith(\"This backup is too large\") ? error : new Error(DAMAGED); }) : plain;\n return { json: new TextDecoder().decode(text) };\n }\n}\n","/**\n * A light backup (WISP 05 § Light backups): everything of a profile but the bytes of its larger files. Their messages\n * and records stay; after a restore they show as not in this backup.\n *\n * The cut: a file of at most 1 MiB keeps its bytes (a short clip, a sticker, a screenshot, a document), and so does a\n * voice message of at most 4 MiB. Voice messages are part of the conversation, and the longest one the apps record\n * (15 minutes at 32 kbit/s, `VOICE_LIMITS`) is about 3.6 MB, so every recorded one stays. Photos from a camera\n * (usually 2 to 8 MB), videos and large files are what is left out.\n */\nexport const LIGHT_FILE_BYTES = 1024 * 1024;\nexport const LIGHT_VOICE_BYTES = 4 * 1024 * 1024;\n\n/** What a light bundle says of itself in its first record: its cut, and what it left out. */\nexport interface LightMark {\n maxFileBytes: number;\n maxVoiceBytes: number;\n /** How many files were left out, and their bytes. */\n files: number;\n bytes: number;\n}\n\n/** Whether a file of this size keeps its bytes in a light backup. */\nexport const keptInLight = (size: number, voice: boolean): boolean => size <= (voice ? LIGHT_VOICE_BYTES : LIGHT_FILE_BYTES);\n","import { existsSync } from \"node:fs\";\nimport { link, mkdir, open, readdir, readFile, rename, rm, stat, writeFile, type FileHandle } from \"node:fs/promises\";\nimport { basename, dirname, join, relative, resolve, sep } from \"node:path\";\nimport { open as openEnvelope } from \"@ghostly/browser/backup/envelope\";\nimport { BackupReader, BackupWriter, FRAME_BYTES, readBackupHeader, type BackupSource } from \"@ghostly/browser/backup/stream\";\nimport { LIGHT_FILE_BYTES, LIGHT_VOICE_BYTES, keptInLight, type LightMark } from \"@ghostly/browser/backup/light\";\nimport { fileStore } from \"@ghostly/browser/shared/idb\";\nimport { bool, str, type Method } from \"./apiKit\";\nimport { CliError } from \"./errors\";\nimport { RESTORED_MARK, profileExists, profilePaths, type ProfilePaths } from \"./profiles\";\n\n/**\n * A headless profile's backup: the WISP 05 envelope around the profile itself, its store and its files, written and\n * read a piece at a time (envelope version 2), so a profile with large files never has to fit in memory. It restores\n * into a new headless profile; the app's own backups are made from its page's storage and are not this format\n * (WISP 1100 § Parity). A backup an older CLI made (version 1: everything in one sealed JSON document) still restores.\n */\nconst FORMAT = \"ghostly-cli-profile/2\";\nconst FORMAT_1 = \"ghostly-cli-profile/1\";\n\n/** Version 1: the store and every file as base64 in one document. */\ninterface Payload1 { format: typeof FORMAT_1; createdAt: number; store: string; files: { path: string; data: string }[] }\n/** Version 2, in order: the head, then each file (the store first) followed by its bytes, then the end. */\n/**\n * `light`: a light backup (WISP 05 § Light backups) left the bytes of these files out. Their records are in the store;\n * the restored profile marks them when it first starts (`markLeftOutFiles`).\n */\ntype BackupRecord =\n | { t: \"profile\"; format: typeof FORMAT; createdAt: number; files: number; bytes: number; light?: LightMark & { ids: string[] } }\n | { t: \"file\"; kind: \"store\" | \"file\"; path?: string; size: number }\n | { t: \"end\"; files: number; bytes: number };\n\nasync function walk(root: string, dir = root, out: string[] = []): Promise<string[]> {\n let names: string[];\n try { names = await readdir(dir); } catch { return out; }\n for (const name of names) {\n const path = join(dir, name);\n if ((await stat(path)).isDirectory()) await walk(root, path, out);\n else out.push(relative(root, path));\n }\n return out;\n}\n\n/** A file's first `size` bytes into the backup, a frame at a time. One that got shorter meanwhile stops the backup. */\nasync function copyIn(writer: BackupWriter, path: string, size: number): Promise<void> {\n const handle = await open(path, \"r\");\n try {\n const buffer = new Uint8Array(FRAME_BYTES);\n for (let at = 0; at < size;) {\n const { bytesRead } = await handle.read(buffer, 0, Math.min(FRAME_BYTES, size - at), at);\n if (!bytesRead) throw new CliError(\"unavailable\", `${path} changed while it was backed up: try again`);\n await writer.bytes(buffer.subarray(0, bytesRead));\n at += bytesRead;\n }\n } finally { await handle.close(); }\n}\n\nexport const BACKUP_METHODS: Record<string, Method> = {\n /**\n * Writes the backup to `path` (resolved by the command), sealed with `passphrase`, or not encrypted when\n * `noPassphrase` is true: one of the two, never a guess. The store is written whole for it, every value inline. It is\n * written beside `path` and only takes its name once it is complete: a backup that fails leaves no file. `light`:\n * the files over the cut (`keptInLight`) are left out, their records kept.\n */\n async \"profile.backup\"(ctx, params) {\n const path = resolve(str(params, \"path\", true));\n // `refused`, not `confirm` (exit 5, \"pass --force\"): backup takes no flag that writes over a file.\n if (existsSync(path)) throw new CliError(\"refused\", `${path} exists: choose another name`);\n const passphrase = str(params, \"passphrase\"), unsealed = bool(params, \"noPassphrase\");\n if (!passphrase && !unsealed) throw new CliError(\"bad_request\", \"passphrase is required (or noPassphrase: true for a backup anyone can read)\");\n if (passphrase && unsealed) throw new CliError(\"bad_request\", \"Give a passphrase or noPassphrase, not both\");\n // The folder it goes in, checked first: a missing one is named, not the file written beside `path`.\n const folder = dirname(path);\n if (!(await stat(folder)).isDirectory()) throw new CliError(\"not_found\", `No such file or folder: ${folder}`);\n const paths = ctx.runtime.paths;\n // The store as one snapshot with every value in it (the messages too, which the profile keeps in rows.db): a backup\n // any ghostly restores, this one or an older one.\n const store = join(paths.db, `backup-store-${process.pid}.bin`);\n await ctx.runtime.store.exportSnapshot(store);\n const light = bool(params, \"light\");\n const all = await Promise.all((await walk(paths.files)).map(async (name) => ({ name, size: (await stat(join(paths.files, name))).size })));\n // A file's name is its id: only one that could be a voice message under its larger cut is looked up.\n const kept = async (file: { name: string; size: number }) => keptInLight(file.size, false) || (file.size <= LIGHT_VOICE_BYTES && !!(await fileStore.get(basename(file.name)).catch(() => undefined))?.metadata?.voice);\n const files: typeof all = [], leftOut: typeof all = [];\n for (const file of all) (!light || (await kept(file)) ? files : leftOut).push(file);\n const storeSize = (await stat(store)).size, bytes = files.reduce((sum, file) => sum + file.size, 0);\n const leftOutBytes = leftOut.reduce((sum, file) => sum + file.size, 0);\n\n const partial = `${path}.partial-${process.pid}`;\n let out: FileHandle | null = null;\n try {\n out = await openFile(partial);\n const handle = out;\n let writer: BackupWriter;\n try { writer = await BackupWriter.start({ write: async (chunk) => { await handle.write(chunk); } }, unsealed ? null : passphrase!); } catch (error) { throw new CliError(\"bad_request\", error instanceof Error ? error.message : String(error)); }\n const put = (record: BackupRecord) => writer.json(JSON.stringify(record));\n await put({ t: \"profile\", format: FORMAT, createdAt: Date.now(), files: files.length, bytes, ...(light && { light: { maxFileBytes: LIGHT_FILE_BYTES, maxVoiceBytes: LIGHT_VOICE_BYTES, files: leftOut.length, bytes: leftOutBytes, ids: leftOut.map((file) => basename(file.name)) } }) });\n await put({ t: \"file\", kind: \"store\", size: storeSize });\n await copyIn(writer, store, storeSize);\n for (const file of files) {\n await put({ t: \"file\", kind: \"file\", path: file.name.split(sep).join(\"/\"), size: file.size });\n await copyIn(writer, join(paths.files, file.name), file.size);\n }\n await put({ t: \"end\", files: files.length, bytes });\n const written = await writer.finish();\n await handle.sync();\n await handle.close();\n out = null;\n // Its name only now, and never over a file that appeared meanwhile.\n try { await link(partial, path); } catch (error) { throw (error as NodeJS.ErrnoException).code === \"EEXIST\" ? new CliError(\"refused\", `${path} exists: choose another name`) : error; }\n return { path, bytes: written, files: files.length, protection: unsealed ? \"none\" : \"passphrase\", ...(light && { light: true, leftOut: leftOut.length, leftOutBytes }) };\n } finally {\n await out?.close().catch(() => {});\n await rm(partial, { force: true });\n await rm(store, { force: true });\n }\n },\n};\n\nasync function openFile(path: string): Promise<FileHandle> {\n try { return await open(path, \"wx\", 0o600); } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") { await rm(path, { force: true }); return open(path, \"wx\", 0o600); }\n throw error;\n }\n}\n\n/** A file on disk as a backup's source: read in ranges, never whole. A folder opens, and is refused as one here. */\nasync function fileSource(path: string): Promise<BackupSource & { close(): Promise<void> }> {\n const handle = await open(path, \"r\");\n const found = await handle.stat();\n if (found.isDirectory()) { await handle.close(); throw new CliError(\"bad_request\", `${path} is a folder: name a file`); }\n const { size } = found;\n return {\n size,\n read: async (offset, length) => {\n const buffer = new Uint8Array(Math.max(0, Math.min(length, size - offset)));\n let got = 0;\n while (got < buffer.length) { const { bytesRead } = await handle.read(buffer, got, buffer.length - got, offset + got); if (!bytesRead) break; got += bytesRead; }\n return buffer.subarray(0, got);\n },\n close: () => handle.close(),\n };\n}\n\n/** A version 1 backup is one JSON object, and `seal` writes its format first. */\nconst VERSION_1_START = /^\\s*\\{\\s*\"format\"\\s*:\\s*\"ghostly-backup\"/;\n\n/**\n * Whether the backup at `file` is sealed with a passphrase or was made without one, read from its clear header.\n * A file that is neither version is refused here, before any passphrase is asked for: the path is what is wrong.\n */\nexport async function backupFileProtection(file: string): Promise<\"passphrase\" | \"none\"> {\n const source = await fileSource(file);\n try {\n let found: Awaited<ReturnType<typeof readBackupHeader>>;\n try { found = await readBackupHeader(source); } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n if (found) return found.header.protection;\n if (!VERSION_1_START.test(new TextDecoder().decode(await source.read(0, 256)))) throw new CliError(\"refused\", \"This is not a Ghostly backup\");\n return \"passphrase\";\n } finally { await source.close(); }\n}\n\n/** Where a path named by a backup lands inside the new profile's files folder; never outside it. */\nfunction inside(root: string, path: unknown): string {\n const target = typeof path === \"string\" && path ? resolve(root, path) : \"\";\n if (!target.startsWith(resolve(root) + sep)) throw new CliError(\"refused\", \"The backup names a file outside its folder\");\n return target;\n}\n\n/** While a restore writes it, the profile's folder is not a profile: no profile name starts with a dot (`listProfiles`). */\nconst RESTORING = /^\\.restoring-(.+)-(\\d+)$/;\n\n/**\n * The folder a restore writes into, beside the profiles: it takes the profile's name only once everything is in it,\n * so a restore that is stopped at any moment (Ctrl-C, a kill, the power) leaves no profile, whole or half-made.\n * What an earlier restore that was stopped left behind is removed here, once the process that made it is gone.\n */\nasync function restoringFolder(home: string, name: string): Promise<ProfilePaths> {\n const root = join(home, \"profiles\");\n await mkdir(root, { recursive: true, mode: 0o700 });\n for (const entry of await readdir(root)) {\n const pid = Number(RESTORING.exec(entry)?.[2]);\n if (!pid) continue;\n let alive = pid === process.pid;\n if (!alive) try { process.kill(pid, 0); alive = true; } catch (error) { alive = (error as NodeJS.ErrnoException).code === \"EPERM\"; }\n if (!alive) await rm(join(root, entry), { recursive: true, force: true });\n }\n const dir = join(root, `.restoring-${name}-${process.pid}`);\n await rm(dir, { recursive: true, force: true });\n await mkdir(dir, { mode: 0o700 });\n return { ...profilePaths(home, name), dir, db: join(dir, \"db\"), files: join(dir, \"files\") };\n}\n\n/**\n * The folder is whole: marked as a restored copy (`RESTORED_MARK`, which names the files a light backup left out),\n * then given the profile's name. Never over a profile.\n */\nasync function finishRestore(home: string, name: string, work: ProfilePaths, leftOut: string[] = []): Promise<ProfilePaths> {\n const paths = profilePaths(home, name);\n await writeFile(join(work.dir, RESTORED_MARK), leftOut.length ? JSON.stringify({ leftOut }) : \"\", { mode: 0o600 });\n if (existsSync(paths.dir)) throw new CliError(\"refused\", `Profile ${name} exists: restore into a new name`);\n await rename(work.dir, paths.dir);\n return paths;\n}\n\n/**\n * Opens the backup at `file` into a new profile (never over an existing one), with its passphrase when it has one.\n * Runs with no engine: nothing is open yet. A backup that is refused, damaged or cut short leaves no profile behind,\n * and neither does a restore that is stopped while it runs (`restoringFolder`). The profile is marked as a restored\n * copy: its engine, the first time it starts, treats the money in it as a restore does (`RESTORED_MARK`).\n */\nexport async function restoreProfile(home: string, name: string, file: string, passphrase?: string): Promise<ProfilePaths> {\n if (profileExists(home, name)) throw new CliError(\"refused\", `Profile ${name} exists: restore into a new name`);\n const source = await fileSource(file);\n let work: ProfilePaths | null = null;\n try {\n let reader: BackupReader | null;\n try { reader = await BackupReader.open(source, passphrase); } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n if (!reader) {\n const payload = await readWhole(file, passphrase ?? \"\");\n work = await restoringFolder(home, name);\n await writeWhole(work, payload);\n return await finishRestore(home, name, work);\n }\n const next = async () => { try { return await reader.next(); } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); } };\n const parse = (json: string) => { try { return JSON.parse(json) as BackupRecord; } catch { throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\"); } };\n\n // The first record is read (and with it the passphrase checked) before any folder is made.\n const first = await next();\n const head = first?.json === undefined ? null : parse(first.json);\n if (head?.t !== \"profile\" || head.format !== FORMAT) throw new CliError(\"refused\", \"This backup is not a headless Ghostly profile\");\n const leftOut = Array.isArray(head.light?.ids) ? head.light.ids.filter((id): id is string => typeof id === \"string\") : [];\n work = await restoringFolder(home, name);\n const paths = work;\n await mkdir(paths.db, { recursive: true, mode: 0o700 });\n let out: { handle: FileHandle; left: number } | null = null, files = 0, bytes = 0, ended = false, stored = false;\n try {\n for (let record = await next(); record; record = await next()) {\n if (ended) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\");\n if (record.bytes) {\n if (!out || record.bytes.length > out.left) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\");\n await out.handle.write(record.bytes);\n out.left -= record.bytes.length;\n continue;\n }\n if (out) { if (out.left) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\"); await out.handle.close(); out = null; }\n const value = parse(record.json);\n if (value.t === \"end\") { ended = value.files === files && value.bytes === bytes; if (!ended) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\"); continue; }\n if (value.t !== \"file\" || !Number.isSafeInteger(value.size) || value.size < 0) continue;\n let target: string;\n if (value.kind === \"store\") { target = join(paths.db, \"snapshot.bin\"); stored = true; }\n else { target = inside(paths.files, value.path); await mkdir(dirname(target), { recursive: true, mode: 0o700 }); files += 1; bytes += value.size; }\n out = { handle: await open(target, \"wx\", 0o600), left: value.size };\n }\n if (out || !ended || !stored) throw new CliError(\"refused\", \"This backup is damaged: it was changed or cut short\");\n } finally { await out?.handle.close().catch(() => {}); }\n return await finishRestore(home, name, work, leftOut);\n } catch (error) {\n if (work) await rm(work.dir, { recursive: true, force: true });\n throw error;\n } finally {\n await source.close();\n }\n}\n\n/** A version 1 backup: one sealed JSON document, read whole. */\nasync function readWhole(file: string, passphrase: string): Promise<Payload1> {\n let payload: Payload1;\n try { payload = JSON.parse(await openEnvelope(await readFile(file, \"utf8\"), passphrase)) as Payload1; } catch (error) { throw new CliError(\"refused\", error instanceof Error ? error.message : String(error)); }\n if (payload?.format !== FORMAT_1 || typeof payload.store !== \"string\" || !Array.isArray(payload.files)) throw new CliError(\"refused\", \"This backup is not a headless Ghostly profile\");\n return payload;\n}\n\nasync function writeWhole(paths: ProfilePaths, payload: Payload1): Promise<void> {\n await mkdir(paths.db, { recursive: true, mode: 0o700 });\n await writeFile(join(paths.db, \"snapshot.bin\"), Buffer.from(payload.store, \"base64\"), { mode: 0o600 });\n for (const entry of payload.files) {\n const target = inside(paths.files, entry.path);\n await mkdir(dirname(target), { recursive: true, mode: 0o700 });\n await writeFile(target, Buffer.from(entry.data, \"base64\"), { mode: 0o600 });\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { n as backupFileProtection, r as restoreProfile } from "./backup-BjZcDFjL.js";
1
+ import { n as backupFileProtection, r as restoreProfile } from "./backup-CJQK5Miu.js";
2
2
  export { backupFileProtection, restoreProfile };
@@ -0,0 +1,2 @@
1
+ import { r as buttonsOf } from "./buttons-DDvTuViY.js";
2
+ export { buttonsOf };
@@ -1,6 +1,6 @@
1
- import { $a as buttonLabelClash, Cl as randomBytes, Za as STATUS_CARD_LIMITS, fi as BUTTONS_CAPABILITY, no as checkStatusCard, wl as toBase64Url } from "./src-BLzG4Sfv.js";
1
+ import { $a as buttonLabelClash, Cl as randomBytes, Za as STATUS_CARD_LIMITS, fi as BUTTONS_CAPABILITY, no as checkStatusCard, wl as toBase64Url } from "./src-XhjSWnp-.js";
2
2
  import { t as CliError } from "./errors-B-zMxbcd.js";
3
- import { _ as str, d as messageOf, f as node, i as chatOrGroup, m as oneOf, n as bool, p as num } from "./apiKit-eh5qBKIl.js";
3
+ import { _ as str, d as messageOf, f as node, i as chatOrGroup, m as oneOf, n as bool, p as num } from "./apiKit-lX3Xx_hY.js";
4
4
  import { sha256 } from "@noble/hashes/sha2.js";
5
5
  import { createBase58check } from "@scure/base";
6
6
  import { wordlist } from "@scure/bip39/wordlists/english.js";
@@ -493,4 +493,4 @@ var BUTTON_METHODS = {
493
493
  //#endregion
494
494
  export { waitForEdit as a, findSecret as c, pressable as i, buttonsCard as n, waitForGroupFrame as o, buttonsOf as r, waitForMessage as s, BUTTON_METHODS as t };
495
495
 
496
- //# sourceMappingURL=buttons-CUKHafux.js.map
496
+ //# sourceMappingURL=buttons-DDvTuViY.js.map