selfstore 1.8.0 → 1.8.2

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 (47) hide show
  1. package/README.md +6 -1
  2. package/dist/advanced.d.ts +5 -5
  3. package/dist/advanced.js +7 -7
  4. package/dist/backups.d.ts +1 -1
  5. package/dist/backups.js +2 -2
  6. package/dist/{cache-lock-7D6N42ZP.js → cache-lock-RCN3JJ4Q.js} +4 -4
  7. package/dist/{cache-lock-7D6N42ZP.js.map → cache-lock-RCN3JJ4Q.js.map} +1 -1
  8. package/dist/{chunk-CT2HTWA7.js → chunk-453KRCMM.js} +4 -4
  9. package/dist/{chunk-CT2HTWA7.js.map → chunk-453KRCMM.js.map} +1 -1
  10. package/dist/{chunk-XBPJRUA3.js → chunk-5NS7YQNP.js} +18 -10
  11. package/dist/chunk-5NS7YQNP.js.map +1 -0
  12. package/dist/{chunk-ZV2S3I76.js → chunk-7N2YR3QW.js} +31 -10
  13. package/dist/chunk-7N2YR3QW.js.map +1 -0
  14. package/dist/{chunk-ZDHTKFAL.js → chunk-BNDFMU3J.js} +6 -6
  15. package/dist/{chunk-ZDHTKFAL.js.map → chunk-BNDFMU3J.js.map} +1 -1
  16. package/dist/{chunk-SFF7KKM4.js → chunk-CPN2KUD5.js} +2 -2
  17. package/dist/chunk-CPN2KUD5.js.map +1 -0
  18. package/dist/{chunk-53WWPEML.js → chunk-HDR75TQ7.js} +3 -3
  19. package/dist/{chunk-53WWPEML.js.map → chunk-HDR75TQ7.js.map} +1 -1
  20. package/dist/{chunk-CM3HSBCE.js → chunk-HK4VPO65.js} +3 -3
  21. package/dist/{chunk-CM3HSBCE.js.map → chunk-HK4VPO65.js.map} +1 -1
  22. package/dist/{chunk-OZS7BSC2.js → chunk-JMJZIZKY.js} +19 -6
  23. package/dist/chunk-JMJZIZKY.js.map +1 -0
  24. package/dist/{chunk-OIAXYI35.js → chunk-U5F4GI7C.js} +3 -3
  25. package/dist/{chunk-OIAXYI35.js.map → chunk-U5F4GI7C.js.map} +1 -1
  26. package/dist/{drive-DF08V8-L.d.ts → drive-l_4-m1eT.d.ts} +1 -1
  27. package/dist/{flows-dwgDgv6_.d.ts → flows-C_uookek.d.ts} +2 -2
  28. package/dist/flows.d.ts +3 -3
  29. package/dist/flows.js +8 -8
  30. package/dist/groups.js +4 -4
  31. package/dist/households.d.ts +1 -1
  32. package/dist/index.d.ts +4 -4
  33. package/dist/index.js +21 -12
  34. package/dist/index.js.map +1 -1
  35. package/dist/local-2YREU4Q2.js +7 -0
  36. package/dist/{local-5QN7V24I.js.map → local-2YREU4Q2.js.map} +1 -1
  37. package/dist/{local-ipQv_Pbv.d.ts → local-bfv4Ld7s.d.ts} +2 -2
  38. package/dist/{store-CbGrlMME.d.ts → store-BiQ-BuVl.d.ts} +8 -2
  39. package/dist/widgets.d.ts +3 -3
  40. package/dist/widgets.js +11 -9
  41. package/dist/widgets.js.map +1 -1
  42. package/package.json +2 -2
  43. package/dist/chunk-OZS7BSC2.js.map +0 -1
  44. package/dist/chunk-SFF7KKM4.js.map +0 -1
  45. package/dist/chunk-XBPJRUA3.js.map +0 -1
  46. package/dist/chunk-ZV2S3I76.js.map +0 -1
  47. package/dist/local-5QN7V24I.js +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "selfstore",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "Local-first storage for browser apps: an IndexedDB working copy, portable encrypted ZIP backups (AES-256-GCM over Argon2id), durable homes (disk, Google Drive, WebDAV) and a serverless multi-device merge.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -121,7 +121,7 @@
121
121
  "happy-dom": "^20.10.6",
122
122
  "jscpd": "^5.0.12",
123
123
  "knip": "^6.29.0",
124
- "lint-staged": "^16.4.0",
124
+ "lint-staged": "^17.2.0",
125
125
  "prettier": "^3.9.6",
126
126
  "simple-git-hooks": "^2.13.1",
127
127
  "tsup": "^8.3.5",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/flows/connect.ts","../src/flows/share.ts","../src/flows/join.ts"],"names":["connect","config","DEFAULT_DEADLINE_MS"],"mappings":";;;;;;AAsLA,IAAM,mBAAA,GAAsB,GAAA;AAG5B,SAAS,QAAQ,GAAA,EAGf;AACD,EAAA,IAAI,QAAQ,QAAA,EAAU,OAAO,EAAE,QAAA,EAAU,eAAA,EAAiB,SAAS,SAAA,EAAU;AAC7E,EAAA,IAAI,QAAQ,SAAA,EAAW,OAAO,EAAE,QAAA,EAAU,gBAAA,EAAkB,SAAS,UAAA,EAAW;AAChF,EAAA,OAAO,EAAE,QAAA,EAAU,OAAA,EAAS,OAAA,EAAS,QAAA,EAAS;AAC/C;AAEO,SAAS,WAAA,CACf,KAAA,EACA,OAAA,EACA,OAAA,GAA8B,EAAC,EACjB;AACd,EAAA,MAAM,IAAA,GAAO,UAAA,IAAc,KAAA,GAAQ,KAAA,CAAM,QAAA,GAAW,KAAA;AACpD,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,mBAAA;AACzC,EAAA,MAAM,KAAA,GAAS,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAoB,MAAA;AAAA,IACrD,CAAC,CAAA,KAAA,CAAO,CAAA,KAAM,OAAA,IAAW,CAAA,KAAM,MAAA,IAAU,CAAA,KAAM,QAAA,IAAY,CAAA,KAAM,IAAA,KAAS,OAAA,CAAQ,CAAC,CAAA,IAAK;AAAA,GACzF;AAEA,EAAA,MAAM,IAAI,WAAA,CAA6B;AAAA,IACtC,IAAA,EAAM,QAAA;AAAA,IACN,KAAA;AAAA,IACA,IAAA,EAAM,IAAA;AAAA,IACN,OAAA,EAAS,IAAA;AAAA,IACT,SAAA,EAAW,KAAA;AAAA,IACX,SAAA,EAAW,KAAA;AAAA,IACX,aAAA,EAAe,KAAA;AAAA,IACf,IAAA,EAAM,KAAA;AAAA,IACN,KAAA,EAAO;AAAA,GACP,CAAA;AAKD,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,IAAI,MAAA,GAA8B,IAAA;AAClC,EAAA,IAAI,QAAA,GAA0B,IAAA;AAG9B,EAAA,IAAI,eAAA,GAAkB,KAAA;AAEtB,EAAA,MAAM,eAAe,MAAY;AAChC,IAAA,UAAA,EAAA;AACA,IAAA,MAAA,GAAS,IAAA;AACT,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,eAAA,GAAkB,KAAA;AAClB,IAAA,CAAA,CAAE,GAAA,CAAI;AAAA,MACL,IAAA,EAAM,QAAA;AAAA,MACN,IAAA,EAAM,IAAA;AAAA,MACN,OAAA,EAAS,IAAA;AAAA,MACT,SAAA,EAAW,KAAA;AAAA,MACX,SAAA,EAAW,KAAA;AAAA,MACX,aAAA,EAAe,KAAA;AAAA,MACf,IAAA,EAAM,KAAA;AAAA,MACN,KAAA,EAAO;AAAA,KACP,CAAA;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,IAAA,GAAO,CAAC,CAAA,KAAqB;AAClC,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,OAAO,KAAA,EAAO,YAAA,CAAa,CAAC,CAAA,EAAG,CAAA;AAAA,EAC7D,CAAA;AAGA,EAAA,MAAM,aAAa,YAAoC;AACtD,IAAA,MAAM,WAAW,OAAA,CAAQ,QAAA;AACzB,IAAA,IAAI,QAAA,KAAa,QAAW,OAAO,IAAA;AACnC,IAAA,OAAO,OAAO,QAAA,KAAa,UAAA,GAAa,MAAM,UAAS,GAAI,QAAA;AAAA,EAC5D,CAAA;AAEA,EAAA,SAAS,YAAA,CACR,IAAA,EACA,OAAA,GAA6B,QAAA,EACV;AACnB,IAAA,MAAM,IAAA,GAAO,QAAQ,IAAI,CAAA;AACzB,IAAA,IAAI,IAAA,IAAQ,MAAM,OAAO,IAAA;AACzB,IAAA,IAAI,OAAO,IAAA,KAAS,UAAA,EAAY,OAAO,IAAA;AACvC,IAAA,IAAI,SAAS,OAAA,EAAS;AACrB,MAAA,MAAM,IAAA,GAAO,IAAA;AACb,MAAA,OAAO,MAAM,OAAA,CAAa,EAAE,IAAA,EAAM,EAAA,EAAI,KAAK,EAAA,EAAI,QAAA,EAAU,IAAA,CAAK,UAAA,EAAY,CAAA;AAAA,IAC3E;AACA,IAAA,IAAI,SAAS,MAAA,EAAQ;AAGpB,MAAA,IAAI,OAAO,IAAA,KAAS,QAAA,IAAY,EAAE,SAAS,IAAA,CAAA,EAAO;AACjD,QAAA,MAAM,GAAA,GAAO,KAAgE,OAAO,CAAA;AACpF,QAAA,IAAI,GAAA,IAAO,MAAM,OAAO,IAAA;AACxB,QAAA,IAAI,OAAO,GAAA,KAAQ,UAAA,EAAY,OAAO,GAAA;AACtC,QAAA,OAAO,YAAY,MAAA,GAChB,MAAM,aAAiB,EAAE,EAAA,EAAI,KAAK,EAAA,EAAI,IACtC,MAAMA,QAAAA,CAAY,EAAE,EAAA,EAAI,IAAA,CAAK,IAAI,QAAA,EAAU,IAAA,CAAK,YAAY,CAAA;AAAA,MAChE;AACA,MAAA,OAAO,MAAMA,SAAY,EAAE,EAAA,EAAI,KAAK,EAAA,EAAI,QAAA,EAAU,IAAA,CAAK,UAAA,EAAY,CAAA;AAAA,IACpE;AACA,IAAA,IAAI,SAAS,IAAA,EAAM;AAClB,MAAA,MAAMC,OAAAA,GAAS,IAAA;AACf,MAAA,OAAO,MAAMD,SAAU,EAAE,EAAA,EAAI,KAAK,EAAA,EAAI,MAAA,EAAAC,SAAQ,CAAA;AAAA,IAC/C;AACA,IAAA,MAAM,MAAA,GAAS,IAAA;AACf,IAAA,OAAO,MAAMD,QAAAA,CAAc,EAAE,IAAI,IAAA,CAAK,EAAA,EAAI,QAAQ,CAAA;AAAA,EACnD;AAGA,EAAA,SAAS,gBAAgB,IAAA,EAAyB;AACjD,IAAA,MAAM,MAAM,EAAE,UAAA;AACd,IAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,aAAA,EAAe,MAAM,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,CAAA;AAC5D,IAAA,KAAK,IAAA,CAAK,MAAA,CACR,aAAA,EAAc,CACd,KAAK,MAAM;AACX,MAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,MAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,WAAA,EAAa,SAAS,QAAA,EAAU,IAAA,EAAM,OAAO,CAAA;AAAA,IAC5D,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,CAAA,KAAM;AACb,MAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,MAAA,IAAA,CAAK,CAAC,CAAA;AAAA,IACP,CAAC,CAAA;AAAA,EACH;AAIA,EAAA,eAAe,eAAA,CAAgB,KAAa,CAAA,EAAgC;AAC3E,IAAA,MAAM,IAAA,GAAO,MAAM,YAAA,CAAa,IAAA,CAAK,OAAO,aAAA,CAAc,CAAC,CAAA,EAAG,UAAA,EAAY,iBAAiB,CAAA;AAC3F,IAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,IAAA,MAAA,GAAS,CAAA;AACT,IAAA,eAAA,GAAkB,IAAA,CAAK,SAAA;AACvB,IAAA,CAAA,CAAE,GAAA,CAAI,EAAE,SAAA,EAAW,IAAA,CAAK,WAAW,SAAA,EAAW,IAAA,CAAK,WAAW,CAAA;AAC9D,IAAA,IAAI,CAAC,KAAK,SAAA,EAAW;AAGpB,MAAA,MAAM,MAAA,CAAO,GAAA,EAAK,gBAAA,EAAkB,SAAS,CAAA;AAC7C,MAAA;AAAA,IACD;AACA,IAAA,IAAI,KAAK,SAAA,EAAW;AACnB,MAAA,IAAI,QAAQ,WAAA,EAAa;AAOxB,QAAA,MAAM,MAAA,CAAO,GAAA,EAAK,eAAA,EAAiB,SAAS,CAAA;AAC5C,QAAA;AAAA,MACD;AAIA,MAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,UAAA,EAAY,MAAM,KAAA,EAAO,aAAA,EAAe,OAAO,CAAA;AAC7D,MAAA;AAAA,IACD;AACA,IAAA,gBAAA,CAAiB,GAAG,CAAA;AAAA,EACrB;AAKA,EAAA,SAAS,iBAAiB,GAAA,EAAmB;AAC5C,IAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,IAAA,IAAI,OAAA,CAAQ,YAAA,IAAe,KAAM,IAAA,EAAM;AACtC,MAAA,CAAA,CAAE,IAAI,EAAE,IAAA,EAAM,UAAA,EAAY,IAAA,EAAM,OAAO,CAAA;AACvC,MAAA;AAAA,IACD;AACA,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,iBAAA,IAAqB,OAAO,CAAA;AACzD,IAAA,KAAK,MAAA,CAAO,GAAA,EAAK,IAAA,CAAK,QAAA,EAAU,KAAK,OAAO,CAAA;AAAA,EAC7C;AAEA,EAAA,eAAe,MAAA,CACd,GAAA,EACA,QAAA,EACA,OAAA,EACA,OAAO,KAAA,EACS;AAChB,IAAA,IAAI,GAAA,KAAQ,UAAA,IAAc,CAAC,MAAA,EAAQ;AACnC,IAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACpB,IAAA,IAAI;AAKH,MAAA,MAAM,MAAA,GAAS,QAAA,KAAa,eAAA,GAAkB,IAAA,GAAO,MAAM,UAAA,EAAW,CAAA;AACtE,MAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,MAAA,MAAM,YAAA;AAAA,QACL,IAAA,CAAK,OAAO,YAAA,CAAa,MAAA,EAAQ,EAAE,QAAA,EAAU,MAAA,EAAQ,QAAA,EAAU,IAAA,EAAM,CAAA;AAAA,QACrE,UAAA;AAAA,QACA;AAAA,OACD;AACA,MAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,WAAA,EAAa,SAAS,IAAA,EAAM,KAAA,EAAO,KAAA,EAAO,IAAA,EAAM,CAAA;AAAA,IAC/D,SAAS,CAAA,EAAG;AACX,MAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,MAAA,IAAA,CAAK,CAAC,CAAA;AAAA,IACP;AAAA,EACD;AAEA,EAAA,OAAO;AAAA,IACN,IAAI,QAAA,GAAW;AACd,MAAA,OAAO,CAAA,CAAE,QAAA;AAAA,IACV,CAAA;AAAA,IACA,WAAW,CAAA,CAAE,SAAA;AAAA,IAEb,MAAA,CAAO,IAAA,EAAmB,OAAA,GAA6B,QAAA,EAAgB;AACtE,MAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,CAAA,CAAE,QAAA;AACzB,MAAA,IAAI,IAAA,IAAS,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,OAAA,EAAU;AACrD,MAAA,MAAM,SAAA,GAAY,YAAA,CAAa,IAAA,EAAM,OAAO,CAAA;AAC5C,MAAA,IAAI,CAAC,SAAA,EAAW;AAChB,MAAA,IACE,IAAA,KAAS,YAAY,OAAA,CAAQ,MAAA,KAAW,QACxC,IAAA,KAAS,IAAA,IAAQ,OAAA,CAAQ,EAAA,KAAO,IAAA,EAChC;AACD,QAAA,CAAA,CAAE,IAAI,EAAE,IAAA,EAAM,QAAQ,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AACzC,QAAA;AAAA,MACD;AAIA,MAAA,MAAM,aAAA,GACL,OAAA,CAAQ,IAAA,KAAS,IAAA,IAChB,OAAO,OAAA,CAAQ,IAAA,KAAS,QAAA,IACvB,OAAA,CAAQ,KAA8B,MAAA,KAAW,MAAA,IAClD,OAAQ,OAAA,CAAQ,KAA8B,MAAA,KAAW,UAAA;AAC3D,MAAA,MAAM,UAAA,GAAa,IAAA,KAAS,MAAA,IAAU,OAAA,KAAY,QAAA,IAAY,aAAA;AAC9D,MAAA,IAAI,UAAA,IAAc,CAAC,WAAA,EAAgB,EAAG;AACrC,QAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,QAAA;AAAA,MACD;AACA,MAAA,MAAM,MAAM,EAAE,UAAA;AACd,MAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,aAAA,EAAe,MAAM,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,CAAA;AAG5D,MAAA,KAAK,SAAA,EAAU,CACb,IAAA,CAAK,OAAO,CAAA,KAAM;AAClB,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,IAAI,CAAC,CAAA,EAAG;AAKP,UAAA,IAAI,UAAA,IAAc,CAAC,WAAA,EAAgB,EAAG;AACrC,YAAA,eAAA,CAAgB,IAAI,CAAA;AACpB,YAAA;AAAA,UACD;AAIA,UAAA,IAAI,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,IAAA,EAAM;AACvC,YAAA,IAAA;AAAA,cACC,IAAI,cAAA;AAAA,gBACH,oBAAA;AAAA,gBACA;AAAA;AACD,aACD;AAAA,UACD,CAAA,MAAO;AACN,YAAA,YAAA,EAAa;AAAA,UACd;AACA,UAAA;AAAA,QACD;AACA,QAAA,MAAM,eAAA,CAAgB,KAAK,CAAC,CAAA;AAAA,MAC7B,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,CAAA,KAAM;AACb,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,IAAA,CAAK,CAAC,CAAA;AAAA,MACP,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,aAAa,MAAA,EAA4B;AACxC,MAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,CAAA,CAAE,QAAA;AACzB,MAAA,IAAI,IAAA,IAAQ,SAAS,MAAA,EAAQ;AAC7B,MAAA,MAAM,MAAM,EAAE,UAAA;AACd,MAAA,CAAA,CAAE,IAAI,EAAE,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,MAAM,CAAA;AACzC,MAAA,KAAK,YAAA,CAAaA,QAAAA,CAAc,EAAE,EAAA,EAAI,KAAK,EAAA,EAAI,MAAA,EAAQ,CAAA,EAAG,UAAA,EAAY,mBAAmB,CAAA,CACvF,IAAA,CAAK,OAAO,CAAA,KAAM;AAClB,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,IAAI,CAAC,CAAA,EAAG;AAGP,UAAA,IAAA;AAAA,YACC,IAAI,cAAA;AAAA,cACH,oBAAA;AAAA,cACA;AAAA;AACD,WACD;AACA,UAAA;AAAA,QACD;AACA,QAAA,MAAM,eAAA,CAAgB,KAAK,CAAC,CAAA;AAAA,MAC7B,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,CAAA,KAAM;AACb,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,IAAA,CAAK,CAAC,CAAA;AAAA,MACP,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,SAAS,MAAA,EAAwB;AAChC,MAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,CAAA,CAAE,QAAA;AACzB,MAAA,IAAI,IAAA,IAAQ,SAAS,MAAA,EAAQ;AAC7B,MAAA,MAAM,MAAM,EAAE,UAAA;AACd,MAAA,CAAA,CAAE,IAAI,EAAE,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,MAAM,CAAA;AACzC,MAAA,KAAK,YAAA,CAAaA,QAAAA,CAAU,EAAE,EAAA,EAAI,KAAK,EAAA,EAAI,MAAA,EAAQ,CAAA,EAAG,UAAA,EAAY,iBAAiB,CAAA,CACjF,IAAA,CAAK,OAAO,CAAA,KAAM;AAClB,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,IAAI,CAAC,CAAA,EAAG;AAGP,UAAA,IAAA;AAAA,YACC,IAAI,cAAA;AAAA,cACH,oBAAA;AAAA,cACA;AAAA;AACD,WACD;AACA,UAAA;AAAA,QACD;AACA,QAAA,MAAM,eAAA,CAAgB,KAAK,CAAC,CAAA;AAAA,MAC7B,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,CAAA,KAAM;AACb,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,IAAA,CAAK,CAAC,CAAA;AAAA,MACP,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,eAAe,EAAA,EAAkB;AAChC,MAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,CAAA,CAAE,QAAA;AACzB,MAAA,IAAI,IAAA,IAAQ,IAAA,KAAS,UAAA,IAAc,CAAC,MAAA,EAAQ;AAC5C,MAAA,MAAM,GAAA,GAAM,UAAA;AACZ,MAAA,CAAA,CAAE,IAAI,EAAE,IAAA,EAAM,IAAA,EAAM,aAAA,EAAe,OAAO,CAAA;AAC1C,MAAA,KAAA,CAAM,YAAY;AACjB,QAAA,IAAI;AAIH,UAAA,MAAM,OAAO,MAAM,YAAA,CAAa,OAAQ,IAAA,EAAK,EAAG,YAAY,iBAAiB,CAAA;AAC7E,UAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,UAAA,IAAI,CAAC,IAAA,EAAM;AACV,YAAA,MAAM,IAAI,cAAA,CAAe,oBAAA,EAAsB,qCAAqC,CAAA;AAAA,UACrF;AACA,UAAA,MAAM,QAAQ,IAAI,CAAA,CAAE,YAAA,CAAa,EAAE,EAAE,IAAA,EAAK;AAC1C,UAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,UAAA,QAAA,GAAW,EAAA;AACX,UAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,KAAA,EAAO,CAAA;AACrB,UAAA,gBAAA,CAAiB,GAAG,CAAA;AAAA,QACrB,SAAS,CAAA,EAAG;AACX,UAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,UAAA,IAAI,gBAAA,CAAiB,CAAC,CAAA,IAAK,CAAA,CAAE,SAAS,gBAAA,EAAkB;AACvD,YAAA,CAAA,CAAE,IAAI,EAAE,IAAA,EAAM,KAAA,EAAO,aAAA,EAAe,MAAM,CAAA;AAC1C,YAAA;AAAA,UACD;AACA,UAAA,IAAA,CAAK,CAAC,CAAA;AAAA,QACP;AAAA,MACD,CAAA,GAAG;AAAA,IACJ,CAAA;AAAA,IAEA,SAAA,GAAkB;AACjB,MAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,CAAA,CAAE,QAAA;AACzB,MAAA,IAAI,IAAA,IAAQ,IAAA,KAAS,UAAA,IAAc,CAAC,MAAA,EAAQ;AAO5C,MAAA,QAAA,GAAW,IAAA;AACX,MAAA,KAAK,MAAA,CAAO,UAAA,EAAY,gBAAA,EAAkB,UAAA,EAAY,IAAI,CAAA;AAAA,IAC3D,CAAA;AAAA,IAEA,gBAAgB,GAAA,EAA8B;AAC7C,MAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,CAAA,CAAE,QAAA;AACzB,MAAA,IAAI,IAAA,IAAQ,SAAS,UAAA,EAAY;AACjC,MAAA,MAAM,IAAA,GAAO,QAAQ,GAAG,CAAA;AACxB,MAAA,KAAK,MAAA,CAAO,UAAA,EAAY,IAAA,CAAK,QAAA,EAAU,KAAK,OAAO,CAAA;AAAA,IACpD,CAAA;AAAA,IAEA,MAAA,GAAe;AACd,MAAA,IAAI,CAAA,CAAE,QAAA,CAAS,IAAA,KAAS,WAAA,EAAa;AACrC,MAAA,YAAA,EAAa;AAAA,IACd,CAAA;AAAA,IAEA,KAAA,GAAc;AACb,MAAA,IAAI,CAAA,CAAE,QAAA,CAAS,IAAA,KAAS,OAAA,EAAS;AACjC,MAAA,YAAA,EAAa;AAAA,IACd;AAAA,GACD;AACD;;;ACheA,IAAME,oBAAAA,GAAsB,GAAA;AAErB,SAAS,SAAA,CAAU,MAAA,EAAqB,OAAA,GAAmC,EAAC,EAAc;AAChG,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAcA,oBAAAA;AAEzC,EAAA,MAAM,IAAI,WAAA,CAA2B;AAAA,IACpC,OAAO,EAAC;AAAA,IACR,SAAS,EAAC;AAAA,IACV,IAAA,EAAM,SAAA;AAAA,IACN,KAAA,EAAO,KAAA;AAAA,IACP,KAAA,EAAO,IAAA;AAAA,IACP,gBAAA,EAAkB,OAAO,MAAA,CAAO,YAAA,KAAiB,UAAA;AAAA,IACjD,YAAA,EAAc,OAAO,MAAA,CAAO,SAAA,KAAc;AAAA,GAC1C,CAAA;AAGD,EAAA,SAAS,MAAM,EAAA,EAAuC;AACrD,IAAA,IAAI,CAAA,CAAE,QAAA,CAAS,IAAA,KAAS,IAAA,IAAQ,EAAE,OAAO,SAAA,IAAa,CAAA,CAAE,QAAA,CAAS,IAAA,KAAS,SAAA,CAAA,EAAY;AACrF,MAAA,OAAO,KAAA;AAAA,IACR;AACA,IAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,EAAA,EAAI,CAAA;AAClB,IAAA,OAAO,IAAA;AAAA,EACR;AAEA,EAAA,eAAe,SAAA,GAA2B;AACzC,IAAA,IAAI;AACH,MAAA,MAAM,EAAE,KAAA,EAAO,OAAA,EAAQ,GAAI,MAAM,aAAa,MAAA,CAAO,IAAA,EAAK,EAAG,UAAA,EAAY,mBAAmB,CAAA;AAC5F,MAAA,CAAA,CAAE,GAAA,CAAI,EAAE,KAAA,EAAO,OAAA,EAAS,IAAA,EAAM,MAAM,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,IAAA,EAAM,CAAA;AAAA,IAChE,SAAS,CAAA,EAAG;AAGX,MAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,MAAM,KAAA,EAAO,YAAA,CAAa,CAAC,CAAA,EAAG,CAAA;AAAA,IAC1D;AAAA,EACD;AAEA,EAAA,MAAM,IAAA,GAAkB;AAAA,IACvB,IAAI,QAAA,GAAW;AACd,MAAA,OAAO,CAAA,CAAE,QAAA;AAAA,IACV,CAAA;AAAA,IACA,WAAW,CAAA,CAAE,SAAA;AAAA,IAEb,MAAM,OAAA,GAAyB;AAC9B,MAAA,IAAI,EAAE,QAAA,CAAS,IAAA,KAAS,QAAQ,CAAA,CAAE,QAAA,CAAS,SAAS,SAAA,EAAW;AAC/D,MAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,SAAA,EAAW,CAAA;AACzB,MAAA,MAAM,SAAA,EAAU;AAAA,IACjB,CAAA;AAAA,IAEA,MAAM,WAAW,IAAA,EAAqC;AACrD,MAAA,IAAI,CAAC,KAAA,CAAM,QAAQ,CAAA,EAAG,OAAO,IAAA;AAC7B,MAAA,IAAI;AACH,QAAA,MAAM,IAAA,GAAO,MAAM,YAAA,CAAa,MAAA,CAAO,WAAW,IAAI,CAAA,EAAG,YAAY,mBAAmB,CAAA;AACxF,QAAA,CAAA,CAAE,GAAA,CAAI,EAAE,KAAA,EAAO,CAAC,GAAG,CAAA,CAAE,QAAA,CAAS,KAAA,EAAO,IAAI,CAAA,EAAG,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AACrE,QAAA,OAAO,IAAA;AAAA,MACR,SAAS,CAAA,EAAG;AACX,QAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,IAAA,EAAM,OAAO,YAAA,CAAa,CAAC,GAAG,CAAA;AAC5C,QAAA,OAAO,IAAA;AAAA,MACR;AAAA,IACD,CAAA;AAAA,IAEA,MAAM,WAAW,EAAA,EAA8B;AAC9C,MAAA,IAAI,CAAC,KAAA,CAAM,QAAQ,CAAA,EAAG,OAAO,KAAA;AAC7B,MAAA,IAAI;AACH,QAAA,MAAM,aAAa,MAAA,CAAO,UAAA,CAAW,EAAE,CAAA,EAAG,YAAY,mBAAmB,CAAA;AAEzE,QAAA,CAAA,CAAE,IAAI,EAAE,KAAA,EAAO,CAAA,CAAE,QAAA,CAAS,MAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,EAAE,CAAA,EAAG,MAAM,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AACrF,QAAA,OAAO,IAAA;AAAA,MACR,SAAS,CAAA,EAAG;AACX,QAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,IAAA,EAAM,OAAO,YAAA,CAAa,CAAC,GAAG,CAAA;AAC5C,QAAA,OAAO,KAAA;AAAA,MACR;AAAA,IACD,CAAA;AAAA,IAEA,MAAM,aAAa,EAAA,EAA8B;AAChD,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,YAAA,EAAc,IAAA,CAAK,MAAM,CAAA;AAC/C,MAAA,IAAI,CAAC,MAAA,EAAQ;AACZ,QAAA,MAAM,IAAI,UAAU,mEAAmE,CAAA;AAAA,MACxF;AACA,MAAA,MAAM,GAAA,GAAM,EAAE,QAAA,CAAS,OAAA,CAAQ,KAAK,CAAC,EAAA,KAAO,EAAA,CAAG,EAAA,KAAO,EAAE,CAAA;AACxD,MAAA,IAAI,KAAK,IAAA,EAAM;AACd,QAAA,MAAM,IAAI,SAAA;AAAA,UACT;AAAA,SACD;AAAA,MACD;AACA,MAAA,IAAI,CAAC,KAAA,CAAM,QAAQ,CAAA,EAAG,OAAO,KAAA;AAC7B,MAAA,IAAI;AACH,QAAA,MAAM,YAAA,CAAa,MAAA,CAAO,EAAE,CAAA,EAAG,YAAY,qBAAqB,CAAA;AAChE,QAAA,CAAA,CAAE,GAAA,CAAI;AAAA,UACL,OAAA,EAAS,EAAE,QAAA,CAAS,OAAA,CAAQ,OAAO,CAAC,EAAA,KAAO,EAAA,CAAG,EAAA,KAAO,EAAE,CAAA;AAAA,UACvD,IAAA,EAAM,IAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACP,CAAA;AACD,QAAA,OAAO,IAAA;AAAA,MACR,SAAS,CAAA,EAAG;AACX,QAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,IAAA,EAAM,OAAO,YAAA,CAAa,CAAC,GAAG,CAAA;AAC5C,QAAA,OAAO,KAAA;AAAA,MACR;AAAA,IACD,CAAA;AAAA,IAEA,MAAM,SAAA,GAA8B;AACnC,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,SAAA,EAAW,IAAA,CAAK,MAAM,CAAA;AAC5C,MAAA,IAAI,CAAC,MAAA,EAAQ;AACZ,QAAA,MAAM,IAAI,UAAU,+DAA+D,CAAA;AAAA,MACpF;AACA,MAAA,IAAI,CAAC,KAAA,CAAM,YAAY,CAAA,EAAG,OAAO,KAAA;AACjC,MAAA,IAAI;AACH,QAAA,MAAM,YAAA,CAAa,MAAA,EAAO,EAAG,UAAA,EAAY,kBAAkB,CAAA;AAE3D,QAAA,CAAA,CAAE,GAAA,CAAI,EAAE,KAAA,EAAO,EAAC,EAAG,OAAA,EAAS,EAAC,EAAG,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,CAAA;AACzD,QAAA,OAAO,IAAA;AAAA,MACR,SAAS,CAAA,EAAG;AACX,QAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,IAAA,EAAM,OAAO,YAAA,CAAa,CAAC,GAAG,CAAA;AAC5C,QAAA,OAAO,KAAA;AAAA,MACR;AAAA,IACD;AAAA,GACD;AAGA,EAAA,KAAK,SAAA,EAAU;AAEf,EAAA,OAAO,IAAA;AACR;;;ACpIA,IAAMA,oBAAAA,GAAsB,GAAA;AAErB,SAAS,QAAA,CACf,IAAA,EACA,MAAA,EACA,OAAA,GAAmC,EAAC,EACzB;AACX,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAcA,oBAAAA;AAEzC,EAAA,MAAM,IAAI,WAAA,CAA0B;AAAA,IACnC,IAAA;AAAA,IACA,IAAA,EAAM,YAAA;AAAA,IACN,OAAA,EAAS,IAAA;AAAA,IACT,gBAAA,EAAkB,OAAO,MAAA,CAAO,aAAA,KAAkB,UAAA;AAAA,IAClD,IAAA,EAAM,IAAA;AAAA,IACN,KAAA,EAAO;AAAA,GACP,CAAA;AAED,EAAA,IAAI,UAAA,GAAa,CAAA;AAEjB,EAAA,SAAS,OAAA,GAAgB;AACxB,IAAA,MAAM,MAAM,EAAE,UAAA;AACd,IAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,YAAA,EAAc,MAAM,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AACrD,IAAA,KAAK,YAAA,CAAa,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAG,YAAY,gBAAgB,CAAA,CAClE,IAAA,CAAK,CAAC,CAAA,KAAM;AACZ,MAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,MAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,OAAA,EAAS,SAAS,CAAA,EAAG,IAAA,EAAM,OAAO,CAAA;AAAA,IACjD,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,CAAA,KAAM;AACb,MAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,MAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,OAAO,KAAA,EAAO,YAAA,CAAa,CAAC,CAAA,EAAG,CAAA;AAAA,IAC7D,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAA,EAAQ;AAER,EAAA,OAAO;AAAA,IACN,IAAI,QAAA,GAAW;AACd,MAAA,OAAO,CAAA,CAAE,QAAA;AAAA,IACV,CAAA;AAAA,IACA,WAAW,CAAA,CAAE,SAAA;AAAA,IAEb,MAAA,GAAe;AACd,MAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,CAAA,CAAE,QAAA;AACzB,MAAA,IAAI,IAAA,IAAQ,SAAS,OAAA,EAAS;AAC9B,MAAA,MAAM,MAAM,EAAE,UAAA;AACd,MAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,SAAA,EAAW,MAAM,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AAClD,MAAA,KAAK,YAAA,CAAa,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,EAAG,YAAY,SAAS,CAAA,CACxD,IAAA,CAAK,CAAC,OAAA,KAAY;AAClB,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,IAAI,YAAY,IAAA,EAAM;AAGrB,UAAA,CAAA,CAAE,IAAI,EAAE,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,OAAO,CAAA;AACpC,UAAA;AAAA,QACD;AACA,QAAA,CAAA,CAAE,IAAI,EAAE,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,OAAO,CAAA;AAAA,MACrC,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,CAAA,KAAM;AACb,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,OAAO,KAAA,EAAO,YAAA,CAAa,CAAC,CAAA,EAAG,CAAA;AAAA,MAC7D,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,aAAA,GAAsB;AACrB,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,aAAA,EAAe,IAAA,CAAK,MAAM,CAAA;AAClD,MAAA,IAAI,CAAC,QAAA,EAAU;AACf,MAAA,MAAM,EAAE,IAAA,EAAM,IAAA,EAAK,GAAI,CAAA,CAAE,QAAA;AACzB,MAAA,IAAI,QAAS,IAAA,KAAS,OAAA,IAAW,IAAA,KAAS,WAAA,IAAe,SAAS,OAAA,EAAU;AAC5E,MAAA,MAAM,MAAM,EAAE,UAAA;AACd,MAAA,CAAA,CAAE,IAAI,EAAE,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA;AACjC,MAAA,KAAK,QAAA,EAAS,CACZ,IAAA,CAAK,MAAM;AACX,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,OAAA,EAAQ;AAAA,MACT,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,CAAA,KAAM;AACb,QAAA,IAAI,QAAQ,UAAA,EAAY;AACxB,QAAA,CAAA,CAAE,GAAA,CAAI,EAAE,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,OAAO,KAAA,EAAO,YAAA,CAAa,CAAC,CAAA,EAAG,CAAA;AAAA,MAC7D,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IAEA,KAAA,GAAc;AACb,MAAA,IAAI,CAAA,CAAE,QAAA,CAAS,IAAA,KAAS,OAAA,EAAS;AACjC,MAAA,OAAA,EAAQ;AAAA,IACT;AAAA,GACD;AACD","file":"chunk-OZS7BSC2.js","sourcesContent":["// connectFlow: the \"where does my data live\" journey as a headless state\n// machine. It owns ordering and failure rules that took a series of\n// production hotfixes to get right, each covered by a test:\n//\n// choose -> (form) -> authorizing -> (password) -> (conflict) -> connected\n// \\-> error\n//\n// One popup per gesture (re-entry ignored while a leg is in flight), and a\n// cancelled popup is not an error - back to 'choose', silently. An encrypted\n// backup's password is proven by a trial read while the store is untouched,\n// so a wrong password can never half-connect (deferUnlock opts out: adopt\n// locked, unlock where the host chooses). overwrite() is the forgotten-\n// password escape: local wiped first, tiny blank write. Merge is the default\n// reconciliation; the destructive choices hide behind 'conflict', which only\n// appears when the app declared local data worth asking about. Network legs\n// run under a deadline; human steps never do. A cancelled journey stays\n// dead - late resolutions are discarded (generation counter).\n\nimport type { LocalStore, StoreError } from '../persistence/store';\nimport type { BackupTarget } from '../persistence/target';\nimport type { KV } from '../persistence/cache';\nimport { restore } from '../selfstore/fluent';\nimport { SelfstoreError, isSelfstoreError } from '../selfstore/errors';\nimport { connect as driveConnect, type DriveAuth } from '../persistence/targets/drive';\nimport {\n\tconnect as fileConnect,\n\tisSupported as fileIsSupported,\n\topenExisting as fileOpenExisting\n} from '../persistence/targets/file';\nimport { connect as webdavConnect, type WebdavConfig } from '../persistence/targets/webdav';\nimport { connect as s3Connect, type S3Config } from '../persistence/targets/s3';\nimport { makeMachine, toStoreError, withDeadline, type FlowStore } from './machine';\n\nexport type ConnectKind = 'drive' | 'file' | 'webdav' | 's3';\n\n/** Produce a connected BackupTarget, or null when the user cancelled the\n * gesture (consent refused, picker closed). Custom connectors slot custom\n * targets - or test fakes - into the same journey. */\nexport type Connector = () => Promise<BackupTarget | null>;\n\n/** Which destinations the flow offers, and how to open each one. The offer\n * renders in this OBJECT's insertion order: the host decides which\n * destination leads by how it writes the object, not by a ranking here. */\nexport interface ConnectTargets {\n\t/** Google Drive: the auth strategy (e.g. gisDriveAuth), or a custom connector. */\n\tdrive?: DriveAuth | Connector;\n\t/** Local file via the browser's picker (true), or a custom connector. On\n\t * browsers without the File System Access API the flow degrades to the\n\t * manual download mode by itself. The object form offers TWO gestures on\n\t * the same destination: `create` (save picker: start or overwrite a backup\n\t * file) and `open` (open picker: adopt an EXISTING backup file - it then\n\t * goes through the same inspect/password/resolution journey). */\n\tfile?: true | Connector | { create?: true | Connector; open?: true | Connector };\n\t/** WebDAV: true renders a 'form' step and expects submitWebdav(config);\n\t * a fixed WebdavConfig skips the form; or a custom connector. */\n\twebdav?: true | WebdavConfig | Connector;\n\t/** S3-compatible bucket (Amazon S3, R2, B2, MinIO): true renders a 'form'\n\t * step and expects submitS3(config); a fixed S3Config skips the form; or a\n\t * custom connector. Offer it alongside `webdav` and the widget groups both\n\t * under one \"your server\" entry with a WebDAV/S3 toggle. */\n\ts3?: true | S3Config | Connector;\n}\n\n/** What the flows need from the store. The simple store exposes this as\n * `store.flowHost`; an advanced-store app builds it by hand. */\nexport interface FlowHost {\n\tengine: LocalStore;\n\tkv: KV;\n\t/** Backup file name on the destination (the simple store uses `app.zip`). */\n\tbackupName: string;\n}\n\n/** What every flow and widget accepts as \"the store\": the simple facade, which\n * carries its ports on `flowHost`, or a hand-built host. */\nexport type StoreLike = FlowHost | { flowHost: FlowHost };\n\nexport interface ConnectFlowOptions {\n\t/** Declare when this device holds data worth a question: only then does an\n\t * existing backup raise the 'conflict' step with the destructive choices.\n\t * Default: never - the flow applies defaultResolution. */\n\thasLocalData?: () => boolean;\n\t/** How to reconcile with a destination that already holds a backup when the\n\t * conflict step is not raised. Default 'merge' - the only choice that\n\t * loses nothing on either side. 'resume' adopts the backup (the\n\t * destination wins; this device's working copy is replaced), 'replace'\n\t * overwrites the backup with this device. An encrypted backup still asks\n\t * for its password first, whatever the resolution; an empty destination\n\t * is not concerned (this device's data simply becomes its content). */\n\tdefaultResolution?: ConnectResolution;\n\t/** Skip the password step on an encrypted backup: adopt it AS is (resume)\n\t * and complete with the store locked - nothing readable, nothing\n\t * clobbered - for the host to unlock on its own surface (engine.unlock)\n\t * after the connect. Adoption is forced whatever defaultResolution says:\n\t * without the proven key a merge is impossible and a replace would\n\t * destroy a backup no one proved they own. Hosts that let a device\n\t * holding real local data connect should keep the password-first\n\t * journey - adoption replaces the working copy. */\n\tdeferUnlock?: boolean;\n\t/**\n\t * A secret the host already holds (an app passphrase, a derived key), used\n\t * to encrypt the backup when this journey creates or adopts one that is NOT\n\t * already encrypted - an empty destination, or a plaintext file.\n\t *\n\t * Two things need it. A store built with `requireEncryption` refuses a\n\t * passwordless attach outright (ENCRYPTION_REQUIRED), so without this the\n\t * connect journey cannot complete at all. And even without that setting, a\n\t * fresh backup is better born encrypted than written in clear and protected\n\t * on a second write, which would leave cleartext on the destination in\n\t * between.\n\t *\n\t * Never used against a destination whose backup is already encrypted: that\n\t * one is proven through the password step (or adopted locked under\n\t * `deferUnlock`), so a host secret that happens to differ can neither fail\n\t * the attach nor overwrite what it cannot read. Resolved lazily, so the\n\t * secret is only read when a connect actually needs it. Must satisfy the\n\t * store's password policy.\n\t */\n\tpassword?: string | (() => string | Promise<string>);\n\t/** Deadline for each network leg (inspect, trial-read, attach), ms. */\n\tdeadlineMs?: number;\n}\n\nexport type ConnectStep =\n\t'choose' | 'form' | 'authorizing' | 'password' | 'conflict' | 'connected' | 'error';\n\n/** How to reconcile with a destination that already holds a backup. */\nexport type ConnectResolution = 'merge' | 'resume' | 'replace';\n\n/** What connecting did. 'merged': both sides folded. 'started': empty\n * destination now holds this device's data. 'resumed': the backup won,\n * this device adopted it. 'replaced': this device won, the backup was\n * overwritten. 'manual': degraded download-on-demand file mode. */\nexport type ConnectFlowOutcome = 'merged' | 'started' | 'resumed' | 'replaced' | 'manual';\n\nexport interface ConnectSnapshot {\n\tstep: ConnectStep;\n\t/** The destinations to offer, in the order given. */\n\tkinds: readonly ConnectKind[];\n\t/** The destination being connected (null while choosing). */\n\tkind: ConnectKind | null;\n\t/** Set when step === 'connected'. */\n\toutcome: ConnectFlowOutcome | null;\n\t/** Known after inspection: the destination already holds a backup. */\n\thasBackup: boolean;\n\t/** ...and that backup is encrypted. */\n\tencrypted: boolean;\n\t/** The last submitPassword() was wrong (stay on 'password', say so). */\n\tpasswordError: boolean;\n\t/** An async leg is in flight (render a spinner, disable the actions). */\n\tbusy: boolean;\n\t/** Set when step === 'error': show `labelKey`, log `message`. */\n\terror: StoreError | null;\n}\n\nexport interface ConnectFlow extends FlowStore<ConnectSnapshot> {\n\t/** Pick a destination. Call it inside the user's click: the consent popup /\n\t * file picker opens synchronously from the gesture. Ignored while a leg is\n\t * already in flight (one popup per gesture). For a `file` target declared\n\t * with the object form, `variant` picks the gesture: 'create' (default,\n\t * save picker) or 'open' (adopt an existing backup file). */\n\tchoose(kind: ConnectKind, variant?: 'create' | 'open'): void;\n\t/** 'form' step (webdav: true): submit the server config. */\n\tsubmitWebdav(config: WebdavConfig): void;\n\t/** 'form' step (s3: true): submit the bucket config. */\n\tsubmitS3(config: S3Config): void;\n\t/** 'password' step: prove the backup's password. Wrong password sets\n\t * passwordError and stays here; nothing is attached either way. */\n\tsubmitPassword(password: string): void;\n\t/** 'password' step escape hatch: give up on a forgotten password and start a\n\t * fresh backup over the protected one. The encrypted backup is overwritten\n\t * by this device's data (possibly empty) and lost for good - the only way\n\t * out when the password cannot be recovered. */\n\toverwrite(): void;\n\t/** 'conflict' step: pick the reconciliation. */\n\tresolveConflict(how: ConnectResolution): void;\n\t/** Abandon the journey in progress: back to 'choose'. A late resolution of\n\t * the abandoned leg is discarded. */\n\tcancel(): void;\n\t/** 'error' step: back to 'choose' to try again. */\n\tretry(): void;\n}\n\nconst DEFAULT_DEADLINE_MS = 30_000;\n\n/** A resolution's attach plan: which strategy runs, which outcome it names. */\nfunction planFor(how: ConnectResolution): {\n\tstrategy: 'merge' | 'replace-local' | 'replace-remote';\n\toutcome: ConnectFlowOutcome;\n} {\n\tif (how === 'resume') return { strategy: 'replace-local', outcome: 'resumed' };\n\tif (how === 'replace') return { strategy: 'replace-remote', outcome: 'replaced' };\n\treturn { strategy: 'merge', outcome: 'merged' };\n}\n\nexport function connectFlow(\n\tstore: StoreLike,\n\ttargets: ConnectTargets,\n\toptions: ConnectFlowOptions = {}\n): ConnectFlow {\n\tconst host = 'flowHost' in store ? store.flowHost : store;\n\tconst deadlineMs = options.deadlineMs ?? DEFAULT_DEADLINE_MS;\n\tconst kinds = (Object.keys(targets) as ConnectKind[]).filter(\n\t\t(k) => (k === 'drive' || k === 'file' || k === 'webdav' || k === 's3') && targets[k] != null\n\t);\n\n\tconst m = makeMachine<ConnectSnapshot>({\n\t\tstep: 'choose',\n\t\tkinds,\n\t\tkind: null,\n\t\toutcome: null,\n\t\thasBackup: false,\n\t\tencrypted: false,\n\t\tpasswordError: false,\n\t\tbusy: false,\n\t\terror: null\n\t});\n\n\t// The journey's working set. `generation` invalidates in-flight legs on\n\t// cancel(): whoever resolves late compares its captured generation and\n\t// drops its result instead of resurrecting an abandoned journey.\n\tlet generation = 0;\n\tlet target: BackupTarget | null = null;\n\tlet password: string | null = null;\n\t// Set at inspection: the destination already holds an ENCRYPTED backup, so\n\t// its key is the one that matters and the host secret must stay out of it.\n\tlet remoteEncrypted = false;\n\n\tconst backToChoose = (): void => {\n\t\tgeneration++;\n\t\ttarget = null;\n\t\tpassword = null;\n\t\tremoteEncrypted = false;\n\t\tm.set({\n\t\t\tstep: 'choose',\n\t\t\tkind: null,\n\t\t\toutcome: null,\n\t\t\thasBackup: false,\n\t\t\tencrypted: false,\n\t\t\tpasswordError: false,\n\t\t\tbusy: false,\n\t\t\terror: null\n\t\t});\n\t};\n\n\tconst fail = (e: unknown): void => {\n\t\tpassword = null;\n\t\tm.set({ step: 'error', busy: false, error: toStoreError(e) });\n\t};\n\n\t/** The host's own secret, resolved on demand (see options.password). */\n\tconst hostSecret = async (): Promise<string | null> => {\n\t\tconst supplied = options.password;\n\t\tif (supplied === undefined) return null;\n\t\treturn typeof supplied === 'function' ? await supplied() : supplied;\n\t};\n\n\tfunction connectorFor(\n\t\tkind: ConnectKind,\n\t\tvariant: 'create' | 'open' = 'create'\n\t): Connector | null {\n\t\tconst spec = targets[kind];\n\t\tif (spec == null) return null;\n\t\tif (typeof spec === 'function') return spec;\n\t\tif (kind === 'drive') {\n\t\t\tconst auth = spec as DriveAuth;\n\t\t\treturn () => driveConnect({ auth, kv: host.kv, fileName: host.backupName });\n\t\t}\n\t\tif (kind === 'file') {\n\t\t\t// Object form: each gesture has its own spec ('open' has no built-in\n\t\t\t// meaning for other kinds, so it resolves here only).\n\t\t\tif (typeof spec === 'object' && !('url' in spec)) {\n\t\t\t\tconst sub = (spec as { create?: true | Connector; open?: true | Connector })[variant];\n\t\t\t\tif (sub == null) return null;\n\t\t\t\tif (typeof sub === 'function') return sub;\n\t\t\t\treturn variant === 'open'\n\t\t\t\t\t? () => fileOpenExisting({ kv: host.kv })\n\t\t\t\t\t: () => fileConnect({ kv: host.kv, fileName: host.backupName });\n\t\t\t}\n\t\t\treturn () => fileConnect({ kv: host.kv, fileName: host.backupName });\n\t\t}\n\t\tif (kind === 's3') {\n\t\t\tconst config = spec as S3Config; // 's3: true' goes through the form step\n\t\t\treturn () => s3Connect({ kv: host.kv, config });\n\t\t}\n\t\tconst config = spec as WebdavConfig; // 'webdav: true' goes through the form step\n\t\treturn () => webdavConnect({ kv: host.kv, config });\n\t}\n\n\t/** No picker on this browser: download-on-demand instead of a dead button. */\n\tfunction degradeToManual(kind: ConnectKind): void {\n\t\tconst gen = ++generation;\n\t\tm.set({ step: 'authorizing', kind, busy: true, error: null });\n\t\tvoid host.engine\n\t\t\t.setManualFile()\n\t\t\t.then(() => {\n\t\t\t\tif (gen !== generation) return;\n\t\t\t\tm.set({ step: 'connected', outcome: 'manual', busy: false });\n\t\t\t})\n\t\t\t.catch((e) => {\n\t\t\t\tif (gen !== generation) return;\n\t\t\t\tfail(e);\n\t\t\t});\n\t}\n\n\t/** After the target is in hand: inspect, then route to password / conflict /\n\t * attach. Everything here is network - deadline-bounded, generation-checked. */\n\tasync function inspectAndRoute(gen: number, t: BackupTarget): Promise<void> {\n\t\tconst info = await withDeadline(host.engine.inspectTarget(t), deadlineMs, 'The destination');\n\t\tif (gen !== generation) return;\n\t\ttarget = t;\n\t\tremoteEncrypted = info.encrypted;\n\t\tm.set({ hasBackup: info.hasBackup, encrypted: info.encrypted });\n\t\tif (!info.hasBackup) {\n\t\t\t// Empty destination: this device's data becomes its content. Nothing\n\t\t\t// to ask, nothing to lose.\n\t\t\tawait attach(gen, 'replace-remote', 'started');\n\t\t\treturn;\n\t\t}\n\t\tif (info.encrypted) {\n\t\t\tif (options.deferUnlock) {\n\t\t\t\t// The host owns the unlock surface: adopt the backup as it stands.\n\t\t\t\t// A null password on an encrypted file lands the store locked -\n\t\t\t\t// nothing readable, nothing clobbered - until engine.unlock()\n\t\t\t\t// proves the password wherever the host asks for it. The conflict\n\t\t\t\t// step is moot here: without the key, merge cannot read the remote\n\t\t\t\t// and replace would destroy a backup no one proved they own.\n\t\t\t\tawait attach(gen, 'replace-local', 'resumed');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Ask before touching anything. The password gates every resolution,\n\t\t\t// including 'replace': overwriting an encrypted backup demands proof\n\t\t\t// of ownership, or a typo away from someone's data.\n\t\t\tm.set({ step: 'password', busy: false, passwordError: false });\n\t\t\treturn;\n\t\t}\n\t\trouteAfterUnlock(gen);\n\t}\n\n\t/** Password proven (or none needed): either ask the conflict question, or\n\t * apply the default resolution - 'merge' unless the host chose otherwise,\n\t * the only default that loses nothing on either side. */\n\tfunction routeAfterUnlock(gen: number): void {\n\t\tif (gen !== generation) return;\n\t\tif (options.hasLocalData?.() === true) {\n\t\t\tm.set({ step: 'conflict', busy: false });\n\t\t\treturn;\n\t\t}\n\t\tconst plan = planFor(options.defaultResolution ?? 'merge');\n\t\tvoid attach(gen, plan.strategy, plan.outcome);\n\t}\n\n\tasync function attach(\n\t\tgen: number,\n\t\tstrategy: 'merge' | 'replace-local' | 'replace-remote',\n\t\toutcome: ConnectFlowOutcome,\n\t\twipe = false\n\t): Promise<void> {\n\t\tif (gen !== generation || !target) return;\n\t\tm.set({ busy: true });\n\t\ttry {\n\t\t\t// A backup that is already encrypted carries its own key: it was proven\n\t\t\t// at the password step, or deliberately left locked. Everywhere else -\n\t\t\t// an empty destination, a plaintext one - the host secret (when it\n\t\t\t// offers one) makes the backup encrypted from its very first write.\n\t\t\tconst secret = password ?? (remoteEncrypted ? null : await hostSecret());\n\t\t\tif (gen !== generation) return;\n\t\t\tawait withDeadline(\n\t\t\t\thost.engine.attachTarget(target, { password: secret, strategy, wipe }),\n\t\t\t\tdeadlineMs,\n\t\t\t\t'The destination'\n\t\t\t);\n\t\t\tif (gen !== generation) return;\n\t\t\tpassword = null;\n\t\t\tm.set({ step: 'connected', outcome, busy: false, error: null });\n\t\t} catch (e) {\n\t\t\tif (gen !== generation) return;\n\t\t\tfail(e);\n\t\t}\n\t}\n\n\treturn {\n\t\tget snapshot() {\n\t\t\treturn m.snapshot;\n\t\t},\n\t\tsubscribe: m.subscribe,\n\n\t\tchoose(kind: ConnectKind, variant: 'create' | 'open' = 'create'): void {\n\t\t\tconst { step, busy } = m.snapshot;\n\t\t\tif (busy || (step !== 'choose' && step !== 'error')) return; // one popup per gesture\n\t\t\tconst connector = connectorFor(kind, variant);\n\t\t\tif (!connector) return;\n\t\t\tif (\n\t\t\t\t(kind === 'webdav' && targets.webdav === true) ||\n\t\t\t\t(kind === 's3' && targets.s3 === true)\n\t\t\t) {\n\t\t\t\tm.set({ step: 'form', kind, error: null });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Degraded manual mode only makes sense for CREATING a backup through\n\t\t\t// the built-in save picker: adopting an existing file needs the real\n\t\t\t// picker, and a custom connector owns its own fallback.\n\t\t\tconst builtinCreate =\n\t\t\t\ttargets.file === true ||\n\t\t\t\t(typeof targets.file === 'object' &&\n\t\t\t\t\t(targets.file as { create?: unknown }).create !== undefined &&\n\t\t\t\t\ttypeof (targets.file as { create?: unknown }).create !== 'function');\n\t\t\tconst canDegrade = kind === 'file' && variant === 'create' && builtinCreate;\n\t\t\tif (canDegrade && !fileIsSupported()) {\n\t\t\t\tdegradeToManual(kind);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst gen = ++generation;\n\t\t\tm.set({ step: 'authorizing', kind, busy: true, error: null });\n\t\t\t// The connector runs the human gesture (popup, picker): no deadline on\n\t\t\t// people. The network legs after it are bounded in inspectAndRoute.\n\t\t\tvoid connector()\n\t\t\t\t.then(async (t) => {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tif (!t) {\n\t\t\t\t\t\t// The picker was there, and refused: this browser has no file\n\t\t\t\t\t\t// mode, we just could not know before asking. Same landing as\n\t\t\t\t\t\t// a browser without the API - the offer must not send the\n\t\t\t\t\t\t// practitioner back to a button that will refuse again.\n\t\t\t\t\t\tif (canDegrade && !fileIsSupported()) {\n\t\t\t\t\t\t\tdegradeToManual(kind);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Drive/file/custom answer null for a cancelled popup or picker:\n\t\t\t\t\t\t// not an error, no message. A FIXED webdav/s3 config answering\n\t\t\t\t\t\t// null means the server did not: that one is a retryable error.\n\t\t\t\t\t\tif (kind === 'webdav' || kind === 's3') {\n\t\t\t\t\t\t\tfail(\n\t\t\t\t\t\t\t\tnew SelfstoreError(\n\t\t\t\t\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t\t\t\t\t'The server did not answer (address, credentials or CORS).'\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbackToChoose();\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tawait inspectAndRoute(gen, t);\n\t\t\t\t})\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tfail(e);\n\t\t\t\t});\n\t\t},\n\n\t\tsubmitWebdav(config: WebdavConfig): void {\n\t\t\tconst { step, busy } = m.snapshot;\n\t\t\tif (busy || step !== 'form') return;\n\t\t\tconst gen = ++generation;\n\t\t\tm.set({ step: 'authorizing', busy: true });\n\t\t\tvoid withDeadline(webdavConnect({ kv: host.kv, config }), deadlineMs, 'The WebDAV server')\n\t\t\t\t.then(async (t) => {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tif (!t) {\n\t\t\t\t\t\t// webdavConnect answers null when the server does not: with an\n\t\t\t\t\t\t// explicit config this is a wrong-address error, not a cancel.\n\t\t\t\t\t\tfail(\n\t\t\t\t\t\t\tnew SelfstoreError(\n\t\t\t\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t\t\t\t'The WebDAV server did not answer (URL, credentials or CORS).'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tawait inspectAndRoute(gen, t);\n\t\t\t\t})\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tfail(e);\n\t\t\t\t});\n\t\t},\n\n\t\tsubmitS3(config: S3Config): void {\n\t\t\tconst { step, busy } = m.snapshot;\n\t\t\tif (busy || step !== 'form') return;\n\t\t\tconst gen = ++generation;\n\t\t\tm.set({ step: 'authorizing', busy: true });\n\t\t\tvoid withDeadline(s3Connect({ kv: host.kv, config }), deadlineMs, 'The S3 endpoint')\n\t\t\t\t.then(async (t) => {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tif (!t) {\n\t\t\t\t\t\t// s3Connect answers null when the endpoint does not: with an\n\t\t\t\t\t\t// explicit config this is a wrong-config error, not a cancel.\n\t\t\t\t\t\tfail(\n\t\t\t\t\t\t\tnew SelfstoreError(\n\t\t\t\t\t\t\t\t'TARGET_UNAVAILABLE',\n\t\t\t\t\t\t\t\t'The S3 endpoint did not answer (endpoint, credentials or CORS).'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tawait inspectAndRoute(gen, t);\n\t\t\t\t})\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tfail(e);\n\t\t\t\t});\n\t\t},\n\n\t\tsubmitPassword(pw: string): void {\n\t\t\tconst { step, busy } = m.snapshot;\n\t\t\tif (busy || step !== 'password' || !target) return;\n\t\t\tconst gen = generation;\n\t\t\tm.set({ busy: true, passwordError: false });\n\t\t\tvoid (async () => {\n\t\t\t\ttry {\n\t\t\t\t\t// PROVE the password against the real backup while the store is\n\t\t\t\t\t// still untouched: download and trial-read. A wrong password is a\n\t\t\t\t\t// retype, never a locked store or a gate.\n\t\t\t\t\tconst blob = await withDeadline(target!.load(), deadlineMs, 'The destination');\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tif (!blob) {\n\t\t\t\t\t\tthrow new SelfstoreError('TARGET_UNAVAILABLE', 'The backup could not be downloaded.');\n\t\t\t\t\t}\n\t\t\t\t\tawait restore(blob).withPassword(pw).read();\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tpassword = pw;\n\t\t\t\t\tm.set({ busy: false });\n\t\t\t\t\trouteAfterUnlock(gen);\n\t\t\t\t} catch (e) {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tif (isSelfstoreError(e) && e.code === 'DECRYPT_FAILED') {\n\t\t\t\t\t\tm.set({ busy: false, passwordError: true }); // stay, say so, retype\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tfail(e);\n\t\t\t\t}\n\t\t\t})();\n\t\t},\n\n\t\toverwrite(): void {\n\t\t\tconst { step, busy } = m.snapshot;\n\t\t\tif (busy || step !== 'password' || !target) return;\n\t\t\t// The password is forgotten: stop proving it and START fresh over the\n\t\t\t// protected backup. wipe:true empties local first (fresh sync meta), so\n\t\t\t// this writes a blank new backup - never this device's existing data -\n\t\t\t// and the owner is never stuck behind a lost secret. A blank write is\n\t\t\t// also tiny, so it cannot time out the way pushing a large local store\n\t\t\t// would.\n\t\t\tpassword = null;\n\t\t\tvoid attach(generation, 'replace-remote', 'replaced', true);\n\t\t},\n\n\t\tresolveConflict(how: ConnectResolution): void {\n\t\t\tconst { step, busy } = m.snapshot;\n\t\t\tif (busy || step !== 'conflict') return;\n\t\t\tconst plan = planFor(how);\n\t\t\tvoid attach(generation, plan.strategy, plan.outcome);\n\t\t},\n\n\t\tcancel(): void {\n\t\t\tif (m.snapshot.step === 'connected') return; // done is done\n\t\t\tbackToChoose();\n\t\t},\n\n\t\tretry(): void {\n\t\t\tif (m.snapshot.step !== 'error') return;\n\t\t\tbackToChoose();\n\t\t}\n\t};\n}\n","// shareFlow: the \"who can see this\" panel as a headless machine over an\n// injected ShareEngine - how links travel (Drive public link, relay,\n// mailbox) is the app's business. The flow owns what every such panel gets\n// wrong first: a transient listing failure never blanks it (last-known\n// links stay, flagged stale - a member who saw an empty panel once will\n// revoke something in panic); one operation at a time, so a double-click\n// cannot create two links; removals leave the snapshot only after the\n// engine confirmed them, never optimistically; removing yourself is refused\n// up front (leaving is its own journey, not a row action). Every leg runs\n// under a deadline and reports the stable { code, labelKey, message } shape.\n\nimport type { StoreError } from '../persistence/store';\nimport { makeMachine, toStoreError, withDeadline, type FlowStore } from './machine';\n\nexport type ShareLevel = 'read' | 'write';\n\nexport interface ShareLinkInfo {\n\t/** Engine-scoped stable id (revoke keys on it). */\n\tid: string;\n\t/** The invitation URL to hand out. */\n\turl: string;\n\tlevel: ShareLevel;\n}\n\nexport interface ShareMemberInfo {\n\t/** Engine-scoped stable id (removeMember keys on it). */\n\tid: string;\n\tlabel?: string;\n\t/** This row is the local user. */\n\tself?: boolean;\n\t/** This row owns the share (the app may render a badge, the flow does not care). */\n\towner?: boolean;\n}\n\n/** The app-side port the flow drives. Implementations wrap whatever transport\n * the app shares through; every method may throw (SelfstoreError preferred,\n * anything is normalized). */\nexport interface ShareEngine {\n\t/** The current truth: active links and members. */\n\tlist(): Promise<{ links: ShareLinkInfo[]; members: ShareMemberInfo[] }>;\n\tcreateLink(opts: { level: ShareLevel }): Promise<ShareLinkInfo>;\n\trevokeLink(id: string): Promise<void>;\n\t/** Optional: engines without member management simply omit it. */\n\tremoveMember?(id: string): Promise<void>;\n\t/** Optional: END the whole share in one gesture - every link dead, every\n\t * member out. Engines whose transport has such a move (an invitation to\n\t * kill, a file to re-privatize) expose it; the widget renders the action\n\t * only then. */\n\trevokeAll?(): Promise<void>;\n}\n\nexport type ShareBusy = 'refresh' | 'create' | 'revoke' | 'remove' | 'revoke-all' | null;\n\nexport interface ShareSnapshot {\n\tlinks: readonly ShareLinkInfo[];\n\tmembers: readonly ShareMemberInfo[];\n\t/** The operation in flight, or null. */\n\tbusy: ShareBusy;\n\t/** The last refresh failed: the lists shown are the last-known good ones. */\n\tstale: boolean;\n\t/** The last operation's problem (the lists are never cleared by an error). */\n\terror: StoreError | null;\n\t/** True once the engine offers removeMember (render the row action at all). */\n\tcanRemoveMembers: boolean;\n\t/** True once the engine offers revokeAll (render the end-the-share action). */\n\tcanRevokeAll: boolean;\n}\n\nexport interface ShareFlow extends FlowStore<ShareSnapshot> {\n\t/** Re-list from the engine. Failure keeps the last-known lists (stale). */\n\trefresh(): Promise<void>;\n\t/** Create an invitation link. Resolves to it, or null when refused (an\n\t * operation was already in flight). */\n\tcreateLink(opts: { level: ShareLevel }): Promise<ShareLinkInfo | null>;\n\t/** Revoke a link. The link leaves the snapshot only once the engine\n\t * confirmed. Resolves to whether it did. */\n\trevokeLink(id: string): Promise<boolean>;\n\t/** Remove a member (never yourself - refused up front). Resolves to whether\n\t * the engine confirmed. */\n\tremoveMember(id: string): Promise<boolean>;\n\t/** End the whole share: every link and member leaves the snapshot only\n\t * once the engine confirmed. Resolves to whether it did. */\n\trevokeAll(): Promise<boolean>;\n}\n\nconst DEFAULT_DEADLINE_MS = 30_000;\n\nexport function shareFlow(engine: ShareEngine, options: { deadlineMs?: number } = {}): ShareFlow {\n\tconst deadlineMs = options.deadlineMs ?? DEFAULT_DEADLINE_MS;\n\n\tconst m = makeMachine<ShareSnapshot>({\n\t\tlinks: [],\n\t\tmembers: [],\n\t\tbusy: 'refresh',\n\t\tstale: false,\n\t\terror: null,\n\t\tcanRemoveMembers: typeof engine.removeMember === 'function',\n\t\tcanRevokeAll: typeof engine.revokeAll === 'function'\n\t});\n\n\t/** Serialize the operations: one at a time, the others answer \"refused\". */\n\tfunction begin(op: Exclude<ShareBusy, null>): boolean {\n\t\tif (m.snapshot.busy !== null && !(op === 'refresh' && m.snapshot.busy === 'refresh')) {\n\t\t\treturn false;\n\t\t}\n\t\tm.set({ busy: op });\n\t\treturn true;\n\t}\n\n\tasync function doRefresh(): Promise<void> {\n\t\ttry {\n\t\t\tconst { links, members } = await withDeadline(engine.list(), deadlineMs, 'The share listing');\n\t\t\tm.set({ links, members, busy: null, stale: false, error: null });\n\t\t} catch (e) {\n\t\t\t// Keep the last-known lists: a hiccup must never paint the share as\n\t\t\t// gone. Just say the view may be behind.\n\t\t\tm.set({ busy: null, stale: true, error: toStoreError(e) });\n\t\t}\n\t}\n\n\tconst flow: ShareFlow = {\n\t\tget snapshot() {\n\t\t\treturn m.snapshot;\n\t\t},\n\t\tsubscribe: m.subscribe,\n\n\t\tasync refresh(): Promise<void> {\n\t\t\tif (m.snapshot.busy !== null && m.snapshot.busy !== 'refresh') return;\n\t\t\tm.set({ busy: 'refresh' });\n\t\t\tawait doRefresh();\n\t\t},\n\n\t\tasync createLink(opts): Promise<ShareLinkInfo | null> {\n\t\t\tif (!begin('create')) return null;\n\t\t\ttry {\n\t\t\t\tconst link = await withDeadline(engine.createLink(opts), deadlineMs, 'Creating the link');\n\t\t\t\tm.set({ links: [...m.snapshot.links, link], busy: null, error: null });\n\t\t\t\treturn link;\n\t\t\t} catch (e) {\n\t\t\t\tm.set({ busy: null, error: toStoreError(e) });\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\n\t\tasync revokeLink(id: string): Promise<boolean> {\n\t\t\tif (!begin('revoke')) return false;\n\t\t\ttry {\n\t\t\t\tawait withDeadline(engine.revokeLink(id), deadlineMs, 'Revoking the link');\n\t\t\t\t// Confirmed: NOW it leaves the snapshot.\n\t\t\t\tm.set({ links: m.snapshot.links.filter((l) => l.id !== id), busy: null, error: null });\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tm.set({ busy: null, error: toStoreError(e) });\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\n\t\tasync removeMember(id: string): Promise<boolean> {\n\t\t\tconst remove = engine.removeMember?.bind(engine);\n\t\t\tif (!remove) {\n\t\t\t\tthrow new TypeError('shareFlow: this engine does not manage members (no removeMember).');\n\t\t\t}\n\t\t\tconst row = m.snapshot.members.find((mb) => mb.id === id);\n\t\t\tif (row?.self) {\n\t\t\t\tthrow new TypeError(\n\t\t\t\t\t'shareFlow: refusing to remove yourself - leaving a share is its own journey, not a row action.'\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!begin('remove')) return false;\n\t\t\ttry {\n\t\t\t\tawait withDeadline(remove(id), deadlineMs, 'Removing the member');\n\t\t\t\tm.set({\n\t\t\t\t\tmembers: m.snapshot.members.filter((mb) => mb.id !== id),\n\t\t\t\t\tbusy: null,\n\t\t\t\t\terror: null\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tm.set({ busy: null, error: toStoreError(e) });\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\n\t\tasync revokeAll(): Promise<boolean> {\n\t\t\tconst revoke = engine.revokeAll?.bind(engine);\n\t\t\tif (!revoke) {\n\t\t\t\tthrow new TypeError('shareFlow: this engine does not end the share (no revokeAll).');\n\t\t\t}\n\t\t\tif (!begin('revoke-all')) return false;\n\t\t\ttry {\n\t\t\t\tawait withDeadline(revoke(), deadlineMs, 'Ending the share');\n\t\t\t\t// Confirmed: the share is over, every link and member with it.\n\t\t\t\tm.set({ links: [], members: [], busy: null, error: null });\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\tm.set({ busy: null, error: toStoreError(e) });\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t};\n\n\t// The panel opens on the truth: list right away (failures land as stale).\n\tvoid doRefresh();\n\n\treturn flow;\n}\n","// joinFlow: opening an invitation link, as a headless machine over an\n// injected JoinEngine. Preview first, join on an explicit yes. The flow\n// holds the link itself, so a re-render, a login roundtrip or an account\n// switch cannot lose the invitation out from under the user. Joining is\n// single-flight. Mismatch (\"this device already follows another share\") and\n// no-invite (\"spent, or not for this account\") are outcomes with their own\n// steps, not generic errors - each has a different way out. Switching\n// accounts re-previews under the new account. A cancelled or superseded leg\n// cannot resurrect the flow, and every network leg runs under a deadline.\n\nimport type { StoreError } from '../persistence/store';\nimport { makeMachine, toStoreError, withDeadline, type FlowStore } from './machine';\nimport type { ShareLevel } from './share';\n\n/** What the user is about to join - enough to render an honest question. */\nexport interface JoinPreview {\n\t/** The share's display name, when the link carries one. */\n\tlabel?: string;\n\t/** Who shared it (a name, an email - whatever the engine knows). */\n\tfrom?: string;\n\tlevel?: ShareLevel;\n}\n\n/** How joining ended, other than plain failure:\n * 'joined' - this device now follows the share.\n * 'mismatch' - this device already follows a DIFFERENT share: joining a\n * second one is refused (leave the first, or use another\n * device/profile).\n * 'no-invite' - the invitation is spent, expired, or aimed at another\n * account (switching accounts may be the fix). */\nexport type JoinOutcome = 'joined' | 'mismatch' | 'no-invite';\n\n/** The app-side port: how a link is read and honoured on this transport. */\nexport interface JoinEngine {\n\t/** Read-only look at the invitation. Must change NOTHING. */\n\tpreview(link: string): Promise<JoinPreview>;\n\t/** Consume the invitation and wire the share. Resolve null when the user\n\t * backed out of a popup the engine had to open (an account chooser, a\n\t * consent screen): the flow returns to 'ready' silently - backing out of\n\t * a human gesture is not an error, here as everywhere in these flows. */\n\tjoin(link: string): Promise<JoinOutcome | null>;\n\t/** Optional: re-run the account chooser (the invite may belong to another\n\t * account). The flow re-previews after it. */\n\tswitchAccount?(): Promise<void>;\n}\n\nexport type JoinStep =\n\t'previewing' | 'ready' | 'joining' | 'joined' | 'mismatch' | 'no-invite' | 'error';\n\nexport interface JoinSnapshot {\n\t/** The invitation this flow was opened with - held here so the UI can\n\t * always find it again. */\n\tlink: string;\n\tstep: JoinStep;\n\t/** Set once previewed (kept through errors, so retry re-renders context). */\n\tpreview: JoinPreview | null;\n\t/** The engine offers an account switch. */\n\tcanSwitchAccount: boolean;\n\tbusy: boolean;\n\t/** Set when step === 'error': show `labelKey`, log `message`. */\n\terror: StoreError | null;\n}\n\nexport interface JoinFlow extends FlowStore<JoinSnapshot> {\n\t/** Say yes: consume the invitation. Only from 'ready'. */\n\taccept(): void;\n\t/** Re-run the account chooser, then preview again under the new account.\n\t * Available from 'ready', 'no-invite' and 'error' when the engine offers it. */\n\tswitchAccount(): void;\n\t/** From 'error': preview again and resume the journey. */\n\tretry(): void;\n}\n\nconst DEFAULT_DEADLINE_MS = 30_000;\n\nexport function joinFlow(\n\tlink: string,\n\tengine: JoinEngine,\n\toptions: { deadlineMs?: number } = {}\n): JoinFlow {\n\tconst deadlineMs = options.deadlineMs ?? DEFAULT_DEADLINE_MS;\n\n\tconst m = makeMachine<JoinSnapshot>({\n\t\tlink,\n\t\tstep: 'previewing',\n\t\tpreview: null,\n\t\tcanSwitchAccount: typeof engine.switchAccount === 'function',\n\t\tbusy: true,\n\t\terror: null\n\t});\n\n\tlet generation = 0;\n\n\tfunction preview(): void {\n\t\tconst gen = ++generation;\n\t\tm.set({ step: 'previewing', busy: true, error: null });\n\t\tvoid withDeadline(engine.preview(link), deadlineMs, 'The invitation')\n\t\t\t.then((p) => {\n\t\t\t\tif (gen !== generation) return;\n\t\t\t\tm.set({ step: 'ready', preview: p, busy: false });\n\t\t\t})\n\t\t\t.catch((e) => {\n\t\t\t\tif (gen !== generation) return;\n\t\t\t\tm.set({ step: 'error', busy: false, error: toStoreError(e) });\n\t\t\t});\n\t}\n\n\tpreview();\n\n\treturn {\n\t\tget snapshot() {\n\t\t\treturn m.snapshot;\n\t\t},\n\t\tsubscribe: m.subscribe,\n\n\t\taccept(): void {\n\t\t\tconst { step, busy } = m.snapshot;\n\t\t\tif (busy || step !== 'ready') return; // single-flight: one join per yes\n\t\t\tconst gen = ++generation;\n\t\t\tm.set({ step: 'joining', busy: true, error: null });\n\t\t\tvoid withDeadline(engine.join(link), deadlineMs, 'Joining')\n\t\t\t\t.then((outcome) => {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tif (outcome === null) {\n\t\t\t\t\t\t// The user backed out of the engine's popup: the question is\n\t\t\t\t\t\t// still live, the preview still good - just ask again.\n\t\t\t\t\t\tm.set({ step: 'ready', busy: false });\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tm.set({ step: outcome, busy: false });\n\t\t\t\t})\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tm.set({ step: 'error', busy: false, error: toStoreError(e) });\n\t\t\t\t});\n\t\t},\n\n\t\tswitchAccount(): void {\n\t\t\tconst switcher = engine.switchAccount?.bind(engine);\n\t\t\tif (!switcher) return;\n\t\t\tconst { step, busy } = m.snapshot;\n\t\t\tif (busy || (step !== 'ready' && step !== 'no-invite' && step !== 'error')) return;\n\t\t\tconst gen = ++generation;\n\t\t\tm.set({ busy: true, error: null });\n\t\t\tvoid switcher()\n\t\t\t\t.then(() => {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tpreview(); // the other account may hold the right invite: look again\n\t\t\t\t})\n\t\t\t\t.catch((e) => {\n\t\t\t\t\tif (gen !== generation) return;\n\t\t\t\t\tm.set({ step: 'error', busy: false, error: toStoreError(e) });\n\t\t\t\t});\n\t\t},\n\n\t\tretry(): void {\n\t\t\tif (m.snapshot.step !== 'error') return;\n\t\t\tpreview();\n\t\t}\n\t};\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/selfstore/errors.ts"],"names":[],"mappings":";AAkCO,SAAS,cAAc,IAAA,EAAkC;AAC/D,EAAA,OAAO,QAAA,GAAW,IAAA,CAAK,WAAA,EAAY,CAAE,OAAA,CAAQ,WAAA,EAAa,CAAC,EAAA,EAAI,CAAA,KAAc,CAAA,CAAE,WAAA,EAAa,CAAA;AAC7F;AAEO,IAAM,cAAA,GAAN,cAA6B,KAAA,CAAM;AAAA,EACzC,WAAA,CACU,MACT,OAAA,EACC;AACD,IAAA,KAAA,CAAM,OAAO,CAAA;AAHJ,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAIT,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AAAA,EACb;AAAA,EALU,IAAA;AAMX;AASO,IAAM,gBAAA,GAAN,cAA+B,cAAA,CAAe;AAAA,EACpD,WAAA,CAAY,UAAU,mDAAA,EAAqD;AAC1E,IAAA,KAAA,CAAM,gBAAgB,OAAO,CAAA;AAC7B,IAAA,IAAA,CAAK,IAAA,GAAO,kBAAA;AAAA,EACb;AACD;AAMO,SAAS,iBAAiB,CAAA,EAAiC;AACjE,EAAA,OACC,CAAA,YAAa,KAAA,IACb,OAAQ,CAAA,CAAyB,IAAA,KAAS,aACzC,CAAA,CAAE,IAAA,KAAS,gBAAA,IAAoB,CAAA,CAAE,IAAA,KAAS,kBAAA,CAAA;AAE7C;AAGO,SAAS,cAAc,CAAA,EAAqB;AAClD,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,KAAM,MAAM,OAAO,KAAA;AAChD,EAAA,OAAQ,EAAyB,IAAA,KAAS,cAAA;AAC3C","file":"chunk-SFF7KKM4.js","sourcesContent":["// One stable error-code enum for the whole library, format layer and\n// persistence layer alike. Codes may be added in a minor release (keep a\n// default branch in exhaustive switches); existing codes never change meaning.\n\nexport type SelfstoreErrorCode =\n\t// backup format / crypto\n\t| 'BAD_FORMAT' // not a backup file / corrupt framing\n\t| 'UNSUPPORTED_VERSION' // file (or cipher/KDF) from a newer format version\n\t| 'PASSWORD_REQUIRED' // encrypted backup opened without a password, or vault used before unlock()\n\t| 'DECRYPT_FAILED' // wrong password, or tampered/corrupt ciphertext\n\t| 'TOO_LARGE' // archive entry exceeds the size guard (zip-bomb defence)\n\t| 'VERIFY_FAILED' // a freshly written backup did not read back as expected: do not announce it\n\t// durable targets / store lifecycle\n\t| 'AUTH_EXPIRED' // access genuinely lost: only a user gesture (reconnect) fixes it\n\t| 'TARGET_UNAVAILABLE' // transient: offline, cold start, 5xx - retry later\n\t| 'TARGET_WRITE_FAILED' // destination refused or failed the write (non-auth), retryable\n\t| 'TARGET_GONE' // destination permanently unwritable (file deleted, permission withdrawn, storage full); retrying cannot fix it\n\t| 'NOT_CONNECTED' // no connected destination (no file id, no config)\n\t| 'WEAK_PASSWORD' // password fails the store's configured passwordPolicy\n\t| 'ENCRYPTION_REQUIRED' // store set requireEncryption: a plaintext attach, backup or export is refused\n\t| 'UNEXPECTEDLY_UNENCRYPTED' // downgrade guard: expected encrypted, found plaintext\n\t| 'SCHEMA_TOO_NEW' // data written by a newer app schema: update the app to sync again\n\t// passwordless groups (see PEERS.md)\n\t| 'IDENTITY_REQUIRED' // group-encrypted copy opened without a member identity\n\t| 'SIGNATURE_INVALID' // copy/manifest signature failed, or author not a trusted member\n\t| 'NOT_A_RECIPIENT' // valid group copy, but no envelope for this identity\n\t| 'MANIFEST_ROLLBACK'; // membership manifest older than one already applied\n\n/**\n * i18n key for an error code: lower-camel-cased under `error.`, e.g.\n * 'error.authExpired'. The library ships the key, the app owns the copy;\n * consumers never have to show the raw developer message. Deterministic, so\n * codes added later get a key for free - keep a fallback for unknown ones.\n */\nexport function errorLabelKey(code: SelfstoreErrorCode): string {\n\treturn 'error.' + code.toLowerCase().replace(/_([a-z])/g, (_m, c: string) => c.toUpperCase());\n}\n\nexport class SelfstoreError extends Error {\n\tconstructor(\n\t\treadonly code: SelfstoreErrorCode,\n\t\tmessage: string\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'SelfstoreError';\n\t}\n}\n\n/**\n * Genuine loss of access to a destination (session gone, token revoked):\n * only a user gesture fixes it. Anything else a target throws is treated as\n * transient and retried, so a network hiccup never raises the blocking\n * reconnect gate over a still-valid connection. Custom targets and DriveAuth\n * implementations throw this to signal the genuine case.\n */\nexport class AuthExpiredError extends SelfstoreError {\n\tconstructor(message = 'Access to the destination expired or was revoked.') {\n\t\tsuper('AUTH_EXPIRED', message);\n\t\tthis.name = 'AuthExpiredError';\n\t}\n}\n\n/**\n * Shape check rather than instanceof, so it survives two bundled copies of\n * the library. The name check keeps foreign coded errors (ENOENT...) out.\n */\nexport function isSelfstoreError(e: unknown): e is SelfstoreError {\n\treturn (\n\t\te instanceof Error &&\n\t\ttypeof (e as { code?: unknown }).code === 'string' &&\n\t\t(e.name === 'SelfstoreError' || e.name === 'AuthExpiredError')\n\t);\n}\n\n/** True on genuine access loss, false on transient blips. */\nexport function isAuthExpired(e: unknown): boolean {\n\tif (typeof e !== 'object' || e === null) return false;\n\treturn (e as { code?: unknown }).code === 'AUTH_EXPIRED';\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/persistence/targets/abort.ts","../src/persistence/targets/drive.ts","../src/persistence/targets/desktop.ts","../src/persistence/targets/file.ts","../src/persistence/targets/webdav.ts","../src/persistence/targets/sigv4.ts","../src/persistence/targets/s3.ts"],"names":["token","connect","fromSession","openExisting","DATA_DEADLINE_MS","META_DEADLINE_MS","CONFIG_KEY","KEY_KEY","configKey","saveConfig","loadConfig","labelOf","versionOf","fromConfig"],"mappings":"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","file":"chunk-XBPJRUA3.js","sourcesContent":["/**\n * Deadline + life-signal composition for target requests. Every target fetch\n * carries a per-request deadline; composing it with the target's LIFE signal\n * lets abortInFlight() (the user detaching the destination) cut a suspended\n * request NOW instead of letting the detach wait behind the deadline. The\n * composition is done by hand (not AbortSignal.any) so behaviour is identical\n * on every engine the library supports.\n */\n\nimport { SelfstoreError } from '../../selfstore';\n\nexport function boundedSignal(deadlineMs: number, life?: AbortSignal): AbortSignal {\n\tconst deadline = AbortSignal.timeout(deadlineMs);\n\tif (!life) return deadline;\n\tif (life.aborted) return life;\n\tconst ctl = new AbortController();\n\tdeadline.addEventListener('abort', () => ctl.abort(deadline.reason), { once: true });\n\tlife.addEventListener('abort', () => ctl.abort(life.reason), { once: true });\n\treturn ctl.signal;\n}\n\n/** A target's resettable LIFE line. cut() aborts everything bound to the\n * CURRENT signal (with the standard transient detach reason) and re-arms,\n * so work started after the cut lives normally. cut is `this`-free: targets\n * expose it directly as their abortInFlight. */\nexport function lifeLine(): { current(): AbortSignal; cut(): void } {\n\tlet ctl = new AbortController();\n\treturn {\n\t\tcurrent: () => ctl.signal,\n\t\tcut(): void {\n\t\t\tconst c = ctl;\n\t\t\tctl = new AbortController();\n\t\t\tc.abort(new SelfstoreError('TARGET_UNAVAILABLE', 'Request aborted: target detaching.'));\n\t\t}\n\t};\n}\n","/**\n * Google Drive backup target. Does the Drive REST file operations (find-or-create,\n * upload, download) for a single backup file. It only ships bytes; the store\n * decides whether they are encrypted. How an access token is obtained (a browser\n * OAuth flow, a backend broker, ...) is injected via DriveAuth, so this target\n * stays free of any specific auth strategy or backend.\n *\n * Beyond the standard connect/fromSession lifecycle, this module exposes\n * shared-file primitives (preview, adopt, findOrCreateOwnFile) for apps whose\n * users point several devices - or several people - at one Drive-hosted backup\n * file: preview it read-only, then adopt it as this device's backup.\n */\n\nimport type { BackupTarget } from '../target';\nimport type { KV } from '../cache';\nimport { boundedSignal, lifeLine } from './abort';\nimport { AuthExpiredError, SelfstoreError, isAuthExpired } from '../../selfstore';\n\n/** KV key holding the connected backup's Drive file id. Public because app-level\n * flows that operate on the same file (e.g. sharing it) need to read it. */\nexport const FILE_ID_KEY = 'driveFileId';\nconst DRIVE_UPLOAD = 'https://www.googleapis.com/upload/drive/v3/files';\nconst DRIVE_FILES = 'https://www.googleapis.com/drive/v3/files';\n\n/** A hung request must not spin forever behind a dead connection - the very\n * \"long moment trying to connect\" a stuck backup shows. A deadline turns the\n * hang into an ordinary error (transient: the store retries), never a gate.\n * Data transfers get more room than metadata pokes. AbortSignal.timeout is\n * minted per fetch (a fired signal cannot be reused across driveFetch's retry). */\nconst DATA_DEADLINE_MS = 30_000;\nconst META_DEADLINE_MS = 15_000;\n\n/** How the target gets access. Injected, so the auth strategy lives in the app.\n * Error protocol: a genuine loss of access (the session is gone, re-consent\n * needed) must throw AuthExpiredError - or any error with code 'AUTH_EXPIRED' -\n * while a transient offline/cold-start failure throws a plain error, so the\n * store retries it instead of raising the reconnect gate. */\nexport interface DriveAuth {\n\t/** A fresh access token, or throw if access must be re-established. Pass\n\t * `{ forceRefresh: true }` to bypass any cached token and mint a new one:\n\t * the target calls this after a 401 to tell a merely STALE token (a refresh\n\t * fixes it, no gate) from a genuinely lost session (the refresh 401s too).\n\t * An implementation that cannot force a refresh may ignore the flag; the\n\t * target then retries with the same token, no worse than before.\n\t * `signal` fires when the target's in-flight work is cut (the user is\n\t * detaching): an implementation that runs its own retry loop should abort\n\t * it then (pass the signal to its fetches, stop retrying); ignoring the\n\t * signal only means the loop runs to its own deadline first. */\n\ttoken(opts?: { forceRefresh?: boolean; signal?: AbortSignal }): Promise<string>;\n\t/** Re-establish access with a user gesture; resolves to whether it worked. */\n\treconnect(): Promise<boolean>;\n\t/** Forget the connection, locally and server-side. */\n\tforget(): Promise<void>;\n}\n\n/** Run a Drive request; if Google answers 401 the access token may just be\n * STALE (rejected before its nominal expiry - rotation, an early revoke), so\n * force a fresh token and try once more. Only a SECOND 401 means the session\n * is genuinely gone. This is what stops a stale-token blip - common when a\n * flow makes many Drive calls, e.g. group-mode republishing every converge -\n * from raising the blocking reconnect gate over a still-valid connection. */\nasync function driveFetch(\n\tauth: DriveAuth,\n\trun: (token: string) => Promise<Response>,\n\tsignal?: AbortSignal\n): Promise<Response> {\n\tconst r = await run(await auth.token({ signal }));\n\tif (r.status !== 401) return r;\n\treturn run(await auth.token({ forceRefresh: true, signal }));\n}\n\nexport interface DriveOptions {\n\tauth: DriveAuth;\n\tkv: KV;\n\t/** Name of the backup file created in the user's Drive. */\n\tfileName: string;\n}\n\nasync function findExistingFile(token: string, fileNames: string[]): Promise<string | null> {\n\tfor (const name of fileNames) {\n\t\t// 'me' in owners: a shared backup carries the same name but lives on someone\n\t\t// else's Drive - only an OWNED file may be (re)connected as the personal\n\t\t// backup, or a fresh connect could silently re-adopt theirs.\n\t\t// Escape the name for Drive's query grammar (backslash then single-quote):\n\t\t// a name carrying a quote must not break out of the `name='...'` clause and\n\t\t// alter the filter (encodeURIComponent handles transport, not query syntax).\n\t\tconst safe = name.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n\t\tconst q = encodeURIComponent(`name='${safe}' and trashed=false and 'me' in owners`);\n\t\tconst r = await fetch(`${DRIVE_FILES}?q=${q}&fields=files(id)&pageSize=1`, {\n\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t});\n\t\tif (!r.ok) continue;\n\t\tconst data: { files?: { id: string }[] } = await r.json();\n\t\tconst id = data.files?.[0]?.id;\n\t\tif (id) return id;\n\t}\n\treturn null;\n}\n\n/** Classify a non-OK Drive write we could not otherwise handle (the 401 case is\n * the caller's). A 404 - the bound file is gone (deleted, trashed, id no longer\n * valid) - or a 403 that is not a rate limit (permission withdrawn, storage\n * full) is PERMANENT: retrying writes the same doomed request forever (the\n * silent \"momentarily unreachable\" trap), so the store must raise its gate and\n * let the user reconnect to a fresh file or free space. A 403 rate limit and\n * everything else (5xx, network) stay transient. The body carries Drive's\n * `reason` (e.g. userRateLimitExceeded vs storageQuotaExceeded); absent, a bare\n * 403 is treated as permanent (the safer default - a real rate limit names\n * itself). */\nfunction isPermanentDriveWrite(status: number, body: string): boolean {\n\tif (status === 404) return true;\n\tif (status === 403) return !/ratelimit|dailylimit/i.test(body);\n\treturn false;\n}\n\nasync function createFile(token: string, fileName: string): Promise<string> {\n\tconst boundary = 'SLFS_BOUNDARY';\n\tconst meta = JSON.stringify({ name: fileName, mimeType: 'application/octet-stream' });\n\tconst body = [\n\t\t`--${boundary}\\r\\nContent-Type: application/json; charset=UTF-8\\r\\n\\r\\n`,\n\t\tmeta,\n\t\t`\\r\\n--${boundary}\\r\\nContent-Type: application/octet-stream\\r\\n\\r\\n\\r\\n--${boundary}--`\n\t].join('');\n\tconst r = await fetch(`${DRIVE_UPLOAD}?uploadType=multipart`, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t'Content-Type': `multipart/related; boundary=${boundary}`\n\t\t},\n\t\tbody,\n\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t});\n\tif (!r.ok) throw new SelfstoreError('TARGET_WRITE_FAILED', `Drive create failed: ${r.status}`);\n\tconst data: { id: string } = await r.json();\n\treturn data.id;\n}\n\nfunction makeTarget(opts: DriveOptions, fixedId?: string): BackupTarget {\n\tconst { auth, kv } = opts;\n\t// A target is BOUND to one file for its whole life: the id is the fixed one,\n\t// or the remembered id CAPTURED on first use - never re-read per operation.\n\t// The old lazy per-call read let a backup SWITCH re-point the remembered id\n\t// while the OUTGOING target was still attached: its final courtesy flush\n\t// then wrote the old store (data and sync bookkeeping) into the file being\n\t// opened, silently merging two \"isolated\" backups.\n\tlet captured: string | undefined = fixedId;\n\tconst fileIdOf = async (): Promise<string | undefined> =>\n\t\t(captured ??= await kv.get<string>(FILE_ID_KEY));\n\t// The instance's LIFE line: abortInFlight() (a user detach) cuts every\n\t// suspended request now instead of at its deadline. Requests mint their\n\t// signal per call, so work started AFTER a cut lives normally.\n\tconst life = lifeLine();\n\tconst bounded = (ms: number): AbortSignal => boundedSignal(ms, life.current());\n\treturn {\n\t\tkind: 'drive',\n\t\tlabel: 'Google Drive',\n\t\tabortInFlight: life.cut,\n\t\tasync save(blob: Blob): Promise<string | null> {\n\t\t\tconst fileId = await fileIdOf();\n\t\t\tif (!fileId) throw new SelfstoreError('NOT_CONNECTED', 'Drive not connected.');\n\t\t\t// fields=version: the response reports the file's new version, so the\n\t\t\t// store records exactly OUR write (no marker race with other replicas).\n\t\t\tconst r = await driveFetch(\n\t\t\t\tauth,\n\t\t\t\t(token) =>\n\t\t\t\t\tfetch(`${DRIVE_UPLOAD}/${fileId}?uploadType=media&fields=version`, {\n\t\t\t\t\t\tmethod: 'PATCH',\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\tAuthorization: `Bearer ${token}`,\n\t\t\t\t\t\t\t'Content-Type': 'application/octet-stream'\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: blob,\n\t\t\t\t\t\tsignal: bounded(DATA_DEADLINE_MS)\n\t\t\t\t\t}),\n\t\t\t\tlife.current()\n\t\t\t);\n\t\t\tif (!r.ok) {\n\t\t\t\t// A 401 that survives a forced token refresh (driveFetch) is a genuine\n\t\t\t\t// loss of access, not a stale-token blip. A 404 (the bound file is\n\t\t\t\t// gone) or a non-rate-limit 403 (permission withdrawn, storage full) is\n\t\t\t\t// PERMANENT: gate so the user reconnects to a fresh file, never loop it\n\t\t\t\t// as transient. Everything else (5xx, network) stays transient.\n\t\t\t\tif (r.status === 401) throw new AuthExpiredError('Drive rejected the token (401).');\n\t\t\t\tconst body = await r.text().catch(() => '');\n\t\t\t\tif (isPermanentDriveWrite(r.status, body)) {\n\t\t\t\t\tthrow new SelfstoreError(\n\t\t\t\t\t\t'TARGET_GONE',\n\t\t\t\t\t\t`Drive backup unwritable (${r.status}): the file is gone or access was withdrawn.`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow new SelfstoreError('TARGET_WRITE_FAILED', `Drive upload failed: ${r.status}`);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data: { version?: string | number } = await r.json();\n\t\t\t\treturn data.version != null ? String(data.version) : null;\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\tasync load(): Promise<Blob | null> {\n\t\t\tconst fileId = await fileIdOf();\n\t\t\tif (!fileId) return null;\n\t\t\tconst r = await driveFetch(\n\t\t\t\tauth,\n\t\t\t\t(token) =>\n\t\t\t\t\tfetch(`${DRIVE_FILES}/${fileId}?alt=media`, {\n\t\t\t\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\t\t\t\tsignal: bounded(DATA_DEADLINE_MS)\n\t\t\t\t\t}),\n\t\t\t\tlife.current()\n\t\t\t);\n\t\t\t// A failed read must never pass for an empty file: null tells the store\n\t\t\t// \"nothing there\", and a fresh connect would then overwrite the backup\n\t\t\t// this download merely failed to fetch. Only a 404 - the file is\n\t\t\t// genuinely gone - reads as empty; a 401 that survived driveFetch's\n\t\t\t// forced refresh is a real loss of access; anything else is a typed\n\t\t\t// transient failure (the sync legs retry it, connect flows surface it).\n\t\t\tif (r.status === 404) return null;\n\t\t\tif (r.status === 401) throw new AuthExpiredError('Drive rejected the token (401).');\n\t\t\tif (!r.ok) {\n\t\t\t\tthrow new SelfstoreError('TARGET_UNAVAILABLE', `Drive download failed: ${r.status}`);\n\t\t\t}\n\t\t\treturn r.blob();\n\t\t},\n\t\tasync stat(): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\tconst fileId = await fileIdOf();\n\t\t\t\tif (!fileId) return null;\n\t\t\t\tconst r = await driveFetch(\n\t\t\t\t\tauth,\n\t\t\t\t\t(token) =>\n\t\t\t\t\t\tfetch(`${DRIVE_FILES}/${fileId}?fields=version`, {\n\t\t\t\t\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\t\t\t\t\tsignal: bounded(META_DEADLINE_MS)\n\t\t\t\t\t\t}),\n\t\t\t\t\tlife.current()\n\t\t\t\t);\n\t\t\t\tif (!r.ok) return null;\n\t\t\t\tconst data: { version?: string | number } = await r.json();\n\t\t\t\treturn data.version != null ? String(data.version) : null;\n\t\t\t} catch {\n\t\t\t\treturn null; // offline or token lapse: \"cannot tell\", never throws\n\t\t\t}\n\t\t},\n\t\tasync isReady(): Promise<boolean> {\n\t\t\ttry {\n\t\t\t\tawait auth.token({ signal: life.current() });\n\t\t\t\treturn true;\n\t\t\t} catch (e) {\n\t\t\t\t// A transient token failure means \"not writable right now\", not a lost\n\t\t\t\t// session: report false so the store retries. Only a genuine auth loss\n\t\t\t\t// propagates, so the boot path can raise the reconnect gate.\n\t\t\t\tif (isAuthExpired(e)) throw e;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\t\tasync reconnect(): Promise<boolean> {\n\t\t\treturn auth.reconnect();\n\t\t},\n\t\tasync disconnect(): Promise<void> {\n\t\t\tawait auth.forget();\n\t\t\t// Delete the remembered id only while it still points at this target's\n\t\t\t// file: after a backup switch the key already belongs to the new\n\t\t\t// target, and a late disconnect of the old one must not orphan it.\n\t\t\tconst current = await kv.get<string>(FILE_ID_KEY);\n\t\t\tconst own = await fileIdOf();\n\t\t\tif (!current || !own || current === own) await kv.del(FILE_ID_KEY);\n\t\t}\n\t};\n}\n\n/** First-time connect: authorize (a user gesture), then find-or-create the file.\n * Returns the target, or null if the user cancelled the consent. */\nexport async function connect(opts: DriveOptions): Promise<BackupTarget | null> {\n\tif (!(await opts.auth.reconnect())) return null;\n\tconst token = await opts.auth.token();\n\tlet fileId = await opts.kv.get<string>(FILE_ID_KEY);\n\tif (!fileId) {\n\t\tfileId =\n\t\t\t(await findExistingFile(token, [opts.fileName])) ?? (await createFile(token, opts.fileName));\n\t\tawait opts.kv.set(FILE_ID_KEY, fileId);\n\t}\n\treturn makeTarget(opts);\n}\n\n/** Rebuild the target from a past session's saved connection (no UI), or null. */\nexport async function fromSession(opts: DriveOptions): Promise<BackupTarget | null> {\n\treturn (await opts.kv.get<string>(FILE_ID_KEY)) ? makeTarget(opts) : null;\n}\n\n/** A read-only-intent preview of one specific (typically shared) file, before\n * adopting it. Writes NOTHING to the KV: until the user confirms, the device's\n * own backup wiring must stay untouched. */\nexport function preview(opts: DriveOptions, fileId: string): BackupTarget {\n\treturn makeTarget(opts, fileId);\n}\n\n/** Adopt a specific (typically shared) file as this device's backup. Persists\n * the file id, so next sessions restore it like any Drive backup. */\nexport async function adopt(opts: DriveOptions, fileId: string): Promise<BackupTarget> {\n\tawait opts.kv.set(FILE_ID_KEY, fileId);\n\treturn makeTarget(opts);\n}\n\n/** Find (or create) the user's own backup file, ignoring the currently adopted\n * id (which may point at someone else's shared file). Reuses the live session\n * when possible and only falls back to the consent popup. Adopts NOTHING: the\n * caller decides whether to load the file or start it blank. Null when the\n * consent is refused. */\nexport async function findOrCreateOwnFile(\n\topts: DriveOptions\n): Promise<{ fileId: string; created: boolean } | null> {\n\tlet token: string;\n\ttry {\n\t\ttoken = await opts.auth.token();\n\t} catch {\n\t\tif (!(await opts.auth.reconnect())) return null;\n\t\ttoken = await opts.auth.token();\n\t}\n\tconst existing = await findExistingFile(token, [opts.fileName]);\n\tif (existing) return { fileId: existing, created: false };\n\treturn { fileId: await createFile(token, opts.fileName), created: true };\n}\n\n/** One row of `listBackups()`: what Drive reports about a file the app can\n * see. Whether the backup is encrypted is not here on purpose - that lives\n * inside the file (inspect it, or remember it app-side); a listing must stay\n * one cheap metadata call, never N downloads. */\nexport interface DriveBackupInfo {\n\tid: string;\n\tname: string;\n\t/** RFC 3339 timestamp of the last write, as Drive reports it. */\n\tmodifiedTime: string | null;\n\t/** Size in bytes, when Drive reports one. */\n\tsize: number | null;\n}\n\n/** List the files this app can see on the user's own Drive (the drive.file\n * scope already narrows visibility to files the app created or was handed).\n * `nameContains` narrows server-side; selfstore ships no naming convention -\n * the app applies its own exact rules on what comes back (its backups, its\n * internal files). Newest first. */\nexport async function listBackups(opts: {\n\tauth: DriveAuth;\n\tnameContains?: string;\n}): Promise<DriveBackupInfo[]> {\n\tconst clauses = ['trashed=false', \"'me' in owners\"];\n\tif (opts.nameContains) {\n\t\tconst safe = opts.nameContains.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n\t\tclauses.push(`name contains '${safe}'`);\n\t}\n\tconst q = encodeURIComponent(clauses.join(' and '));\n\tconst fields = encodeURIComponent('files(id,name,modifiedTime,size)');\n\tconst r = await driveFetch(opts.auth, (token) =>\n\t\tfetch(`${DRIVE_FILES}?q=${q}&fields=${fields}&pageSize=100&orderBy=modifiedTime%20desc`, {\n\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t})\n\t);\n\tif (r.status === 401) throw new AuthExpiredError('Drive rejected the token (401).');\n\tif (!r.ok) throw new SelfstoreError('TARGET_UNAVAILABLE', `Drive list failed: ${r.status}`);\n\tconst data: {\n\t\tfiles?: { id: string; name: string; modifiedTime?: string; size?: string | number }[];\n\t} = await r.json();\n\treturn (data.files ?? []).map((f) => ({\n\t\tid: f.id,\n\t\tname: f.name,\n\t\tmodifiedTime: f.modifiedTime ?? null,\n\t\tsize: f.size != null ? Number(f.size) : null\n\t}));\n}\n\n/** Create a brand-new empty backup file with this exact name and answer its\n * id. Refuses when a non-trashed owned file already carries the name: Drive\n * happily stores duplicate names, and a duplicate would poison every\n * find-by-name reconnect that follows. Adopts nothing - the caller decides\n * to attach it (typically with `wipe: true` so it starts blank). */\nexport async function createBackup(opts: {\n\tauth: DriveAuth;\n\tfileName: string;\n}): Promise<{ fileId: string }> {\n\tconst token = await opts.auth.token();\n\tconst existing = await findExistingFile(token, [opts.fileName]);\n\tif (existing) {\n\t\tthrow new SelfstoreError(\n\t\t\t'TARGET_WRITE_FAILED',\n\t\t\t`A file named \"${opts.fileName}\" already exists on Drive.`\n\t\t);\n\t}\n\treturn { fileId: await createFile(token, opts.fileName) };\n}\n\n/** Delete a backup file from Drive, for good (not the trash: the file's data\n * must actually be gone, not lingering). A 404 is success - deleting what is\n * already gone is not an error. The caller owns the guard against deleting\n * the currently attached file. */\nexport async function deleteBackup(opts: { auth: DriveAuth; fileId: string }): Promise<void> {\n\tconst r = await driveFetch(opts.auth, (token) =>\n\t\tfetch(`${DRIVE_FILES}/${opts.fileId}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: { Authorization: `Bearer ${token}` },\n\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t})\n\t);\n\tif (r.status === 401) throw new AuthExpiredError('Drive rejected the token (401).');\n\tif (r.status === 404) return;\n\tif (!r.ok) throw new SelfstoreError('TARGET_WRITE_FAILED', `Drive delete failed: ${r.status}`);\n}\n\n/** Rename a backup file (metadata only - the id, the content and every open\n * target bound to it are untouched). Refuses when another owned file already\n * carries the new name, for the same reason createBackup does: a duplicate\n * poisons find-by-name. A 404 means the file is gone, not a success here. */\nexport async function renameBackup(opts: {\n\tauth: DriveAuth;\n\tfileId: string;\n\tfileName: string;\n}): Promise<void> {\n\tconst token = await opts.auth.token();\n\tconst existing = await findExistingFile(token, [opts.fileName]);\n\tif (existing && existing !== opts.fileId) {\n\t\tthrow new SelfstoreError(\n\t\t\t'TARGET_WRITE_FAILED',\n\t\t\t`A file named \"${opts.fileName}\" already exists on Drive.`\n\t\t);\n\t}\n\tif (existing === opts.fileId) return; // already this name: nothing to do\n\tconst r = await driveFetch(opts.auth, (token) =>\n\t\tfetch(`${DRIVE_FILES}/${opts.fileId}?fields=id`, {\n\t\t\tmethod: 'PATCH',\n\t\t\theaders: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify({ name: opts.fileName }),\n\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t})\n\t);\n\tif (r.status === 401) throw new AuthExpiredError('Drive rejected the token (401).');\n\tif (!r.ok) throw new SelfstoreError('TARGET_WRITE_FAILED', `Drive rename failed: ${r.status}`);\n}\n","/**\n * Disk-file backup target for a desktop shell (Tauri, and anything exposing the\n * same filesystem and dialog calls).\n *\n * The browser's File System Access API is Chromium-only and, inside a native\n * webview, usually absent altogether: on macOS and Linux the shell embeds\n * WebKit, which never shipped it. So the file destination would silently\n * degrade to download-on-demand in the one place where writing a real file is\n * easiest. This target restores it.\n *\n * What changes against the browser target is what gets persisted: a PATH, not a\n * handle. A path survives a restart, an app update and a backup of the profile\n * directory, and it needs no permission to be re-granted at each launch. The\n * store therefore reconnects silently where the browser asks for a click.\n *\n * The shell's own calls are INJECTED, never imported: this library keeps three\n * runtime dependencies, and a desktop toolkit is not going to be the fourth for\n * the sake of the web build that will never call it.\n */\n\nimport type { BackupTarget } from '../target';\nimport type { KV } from '../cache';\nimport { SelfstoreError } from '../../selfstore';\n\nconst PATH_KEY = 'desktopFilePath';\n\n/** Filter shape shared by the save and open dialogs. */\nexport interface DesktopDialogFilter {\n\tname: string;\n\textensions: string[];\n}\n\n/**\n * The filesystem and dialog calls this target needs, shaped so a host can pass\n * its shell's functions straight through with no adapter of its own.\n *\n * With Tauri v2 the whole wiring is the two plugin imports:\n *\n * ```ts\n * import { readFile, writeFile, stat, exists } from '@tauri-apps/plugin-fs';\n * import { save, open } from '@tauri-apps/plugin-dialog';\n * import { useDesktopFiles } from 'selfstore';\n *\n * useDesktopFiles({ readFile, writeFile, stat, exists, save, open });\n * ```\n *\n * Do that once at start-up and every `file` destination in this library runs on\n * a real path. Nothing else in a host changes.\n */\nexport interface DesktopFileBridge {\n\treadFile(path: string): Promise<Uint8Array>;\n\twriteFile(path: string, data: Uint8Array): Promise<void>;\n\t/** File metadata. Only `mtime` is read, and a shell that cannot report it\n\t * may leave it absent: the store then falls back to its own bookkeeping. */\n\tstat(path: string): Promise<{ mtime?: Date | number | null } | null>;\n\texists(path: string): Promise<boolean>;\n\t/** Save dialog. Resolves to the chosen path, or null when cancelled. */\n\tsave(options: { defaultPath?: string; filters?: DesktopDialogFilter[] }): Promise<string | null>;\n\t/** Open dialog. Resolves to the chosen path, or null when cancelled. The\n\t * array form is accepted so a host can pass its shell's function unchanged. */\n\topen(options: {\n\t\tmultiple?: boolean;\n\t\tfilters?: DesktopDialogFilter[];\n\t}): Promise<string | string[] | null>;\n}\n\nlet bridge: DesktopFileBridge | null = null;\n\n/**\n * Register the desktop shell's filesystem and dialog calls. Call once at\n * start-up, before opening the store. Passing null unregisters, which is what a\n * test wants between cases.\n */\nexport function useDesktopFiles(b: DesktopFileBridge | null): void {\n\tbridge = b;\n}\n\n/** True once a shell has been registered: the file destination then writes a\n * real path instead of asking the browser for a handle. */\nexport function hasDesktopFiles(): boolean {\n\treturn bridge != null;\n}\n\nconst FILTERS: DesktopDialogFilter[] = [{ name: 'Backup', extensions: ['zip'] }];\n\n/** Last path segment, for the label. Windows and POSIX separators both, since\n * the same build runs on either. */\nfunction baseName(path: string): string {\n\tconst parts = path.split(/[\\\\/]/);\n\treturn parts[parts.length - 1] || path;\n}\n\n/** Epoch milliseconds as the version marker, or null when the shell cannot\n * tell. Never throws: a marker that cannot be read is \"cannot tell\", and the\n * store treats that as no information rather than as a fault. */\nasync function markerOf(b: DesktopFileBridge, path: string): Promise<string | null> {\n\ttry {\n\t\tconst info = await b.stat(path);\n\t\tconst mtime = info?.mtime;\n\t\tif (mtime == null) return null;\n\t\tconst ms = mtime instanceof Date ? mtime.getTime() : Number(mtime);\n\t\treturn Number.isFinite(ms) ? String(ms) : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Only the first path, whichever shape the shell's open dialog returns. */\nfunction firstPath(picked: string | string[] | null): string | null {\n\tif (picked == null) return null;\n\treturn Array.isArray(picked) ? (picked[0] ?? null) : picked;\n}\n\nfunction fromPath(path: string, b: DesktopFileBridge, kv: KV): BackupTarget {\n\t// reconnect() can move the destination to another file, so the path the\n\t// closure writes to has to be able to change under the object the store\n\t// already holds.\n\tlet current = path;\n\treturn {\n\t\tkind: 'file',\n\t\tget label(): string {\n\t\t\treturn baseName(current);\n\t\t},\n\t\tasync save(blob: Blob): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\tawait b.writeFile(current, new Uint8Array(await blob.arrayBuffer()));\n\t\t\t} catch (e) {\n\t\t\t\t// No AuthExpiredError here on purpose. A desktop write fails because\n\t\t\t\t// the disk is full, the volume is unplugged or the file is locked by\n\t\t\t\t// another program - all transient, all resolved without the user\n\t\t\t\t// re-granting anything. Raising the reconnect gate would ask them to\n\t\t\t\t// re-pick a file that is exactly where they left it.\n\t\t\t\tthrow new SelfstoreError('TARGET_WRITE_FAILED', `File write failed: ${String(e)}`);\n\t\t\t}\n\t\t\treturn markerOf(b, current);\n\t\t},\n\t\tasync load(): Promise<Blob | null> {\n\t\t\ttry {\n\t\t\t\tconst bytes = await b.readFile(current);\n\t\t\t\treturn new Blob([new Uint8Array(bytes)]);\n\t\t\t} catch {\n\t\t\t\t// A path chosen through the save dialog names a file that does not\n\t\t\t\t// exist yet. \"No backup there\" is the honest answer, not a failure.\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\tasync stat(): Promise<string | null> {\n\t\t\treturn markerOf(b, current);\n\t\t},\n\t\tasync isReady(): Promise<boolean> {\n\t\t\t// A desktop write needs no user gesture and no grant that can lapse, so\n\t\t\t// the answer is yes even when the file is not there yet: refusing until\n\t\t\t// it exists would deadlock the very first save, which is what creates\n\t\t\t// it. A volume that went away surfaces when save() throws, and that\n\t\t\t// reads as transient - which is what an unplugged drive is.\n\t\t\treturn true;\n\t\t},\n\t\tasync reconnect(): Promise<boolean> {\n\t\t\tconst picked = await b.save({ defaultPath: current, filters: FILTERS });\n\t\t\tif (!picked) return false;\n\t\t\tcurrent = picked;\n\t\t\tawait kv.set(PATH_KEY, picked);\n\t\t\treturn true;\n\t\t},\n\t\tasync disconnect(): Promise<void> {\n\t\t\tawait kv.del(PATH_KEY);\n\t\t}\n\t};\n}\n\nexport interface DesktopConnectOptions {\n\t/** Where the chosen path is persisted across launches. */\n\tkv: KV;\n\t/** Suggested name for the backup file. */\n\tfileName: string;\n}\n\n/** Ask for a file to write the backup to, and remember it. Null when the user\n * cancelled, or when no shell is registered. */\nexport async function connect(opts: DesktopConnectOptions): Promise<BackupTarget | null> {\n\tif (!bridge) return null;\n\tconst picked = await bridge.save({ defaultPath: opts.fileName, filters: FILTERS });\n\tif (!picked) return null;\n\tawait opts.kv.set(PATH_KEY, picked);\n\treturn fromPath(picked, bridge, opts.kv);\n}\n\n/** Ask for an EXISTING backup file and adopt it as the destination; the connect\n * journey then reads it like any backup. Null when cancelled. */\nexport async function openExisting(opts: { kv: KV }): Promise<BackupTarget | null> {\n\tif (!bridge) return null;\n\tconst picked = firstPath(await bridge.open({ multiple: false, filters: FILTERS }));\n\tif (!picked) return null;\n\tawait opts.kv.set(PATH_KEY, picked);\n\treturn fromPath(picked, bridge, opts.kv);\n}\n\n/** Rebuild the target from the path kept in a past launch, or null. Unlike the\n * browser handle this needs no permission check, so a desktop app reopens on\n * its file with nothing asked of the user. */\nexport async function fromSession(opts: { kv: KV }): Promise<BackupTarget | null> {\n\tif (!bridge) return null;\n\tconst path = await opts.kv.get<string>(PATH_KEY);\n\treturn path ? fromPath(path, bridge, opts.kv) : null;\n}\n","/**\n * Disk-file backup target (Chromium only). The user picks a backup file once via\n * the File System Access API; the store then re-writes that real file on every\n * change. The handle is persisted through the injected KV, so the target stays\n * storage-agnostic. The browser re-confirms write permission once per session\n * (a one-click reconnect).\n */\n\nimport type { BackupTarget } from '../target';\nimport type { KV } from '../cache';\nimport { AuthExpiredError, SelfstoreError } from '../../selfstore';\nimport * as desktop from './desktop';\n\nconst HANDLE_KEY = 'fileHandle';\n\ninterface FileHandle {\n\tname: string;\n\tcreateWritable(): Promise<{ write(data: Blob): Promise<void>; close(): Promise<void> }>;\n\tgetFile(): Promise<File>;\n\tqueryPermission(d: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n\trequestPermission(d: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n}\n\ninterface PickerWindow {\n\tshowSaveFilePicker?: (o: {\n\t\tsuggestedName?: string;\n\t\ttypes?: { description: string; accept: Record<string, string[]> }[];\n\t}) => Promise<FileHandle>;\n\tshowOpenFilePicker?: (o: {\n\t\tmultiple?: boolean;\n\t\ttypes?: { description: string; accept: Record<string, string[]> }[];\n\t}) => Promise<FileHandle[]>;\n}\n\nexport interface FileConnectOptions {\n\t/** Where the picked file handle is persisted across sessions. */\n\tkv: KV;\n\t/** Suggested name for the backup file the user creates/picks. */\n\tfileName: string;\n}\n\n/** Set once a picker that EXISTS has refused to open. Presence is not\n * capability: some browsers ship the methods and throw on call unless a flag\n * is turned on, so a host that trusts `'showSaveFilePicker' in window` offers\n * a file mode that can never work, and the button looks broken. The attempt is\n * the only honest probe there is - it costs one click, once. */\nlet refused = false;\n\n/** Was the call made inside a live user gesture?\n *\n * A picker REQUIRES transient activation, and a call made without one throws\n * the same DOMException as a browser that ships the picker and refuses it. Read\n * as a refusal, one mistimed call condemns the file mode for the whole session:\n * the host then falls back to manual downloads on a Chrome that was never\n * asked properly. A throw only teaches us about the browser when the browser\n * was actually allowed to answer. Undefined support (no userActivation) reads\n * as \"cannot tell\" - and cannot-tell must not accuse. */\nfunction insideGesture(): boolean {\n\tconst ua = typeof navigator !== 'undefined' ? navigator.userActivation : undefined;\n\treturn ua ? ua.isActive : true;\n}\n\n/** True when a re-writable file can be picked: a registered desktop shell, or\n * a browser that ships the File System Access API (Chromium) and has not\n * already refused to open a picker in this session.\n *\n * A desktop shell answers first and unconditionally. Inside a native webview\n * the browser probe is not merely negative, it is misleading: WebKit never\n * shipped the API, so the honest capability there is the shell's, and asking\n * the webview instead would degrade the file mode in the one environment where\n * writing a real file is easiest. */\nexport function isSupported(): boolean {\n\tif (desktop.hasDesktopFiles()) return true;\n\treturn !refused && typeof window !== 'undefined' && 'showSaveFilePicker' in window;\n}\n\n/** True when the browser can let the user point at an EXISTING file. Hosts must\n * gate that gesture on this: unlike creating, it has no download fallback, so\n * offering it where it cannot run leaves a button that answers nothing. The\n * refusal is shared with `isSupported` - a browser that refuses one picker\n * refuses the family. */\nexport function isOpenSupported(): boolean {\n\tif (desktop.hasDesktopFiles()) return true;\n\treturn !refused && typeof window !== 'undefined' && 'showOpenFilePicker' in window;\n}\n\n/** Run a picker. Cancelling and refusing both mean \"no file\", but only a\n * refusal says something about the browser - and it must not surface as an\n * error, or the host reports a fault where it should offer another way. */\nasync function ask<T>(open: () => Promise<T>): Promise<T | null> {\n\tconst asked = insideGesture();\n\ttry {\n\t\treturn await open();\n\t} catch (e) {\n\t\tif (e instanceof DOMException && e.name === 'AbortError') return null; // user cancelled\n\t\t// Outside a gesture the browser refused the CALL, not the feature: keep\n\t\t// the file mode intact so the host can ask again from a real click.\n\t\trefused = asked;\n\t\treturn null;\n\t}\n}\n\n/** A revoked/denied file permission is a genuine loss of access: the store must\n * raise the reconnect gate (one click re-grants). Everything else is transient. */\nfunction isPermissionLoss(e: unknown): boolean {\n\treturn e instanceof DOMException && (e.name === 'NotAllowedError' || e.name === 'SecurityError');\n}\n\nfunction fromHandle(handle: FileHandle, kv: KV): BackupTarget {\n\treturn {\n\t\tkind: 'file',\n\t\tlabel: handle.name,\n\t\tasync save(blob: Blob): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\tconst writable = await handle.createWritable();\n\t\t\t\tawait writable.write(blob);\n\t\t\t\tawait writable.close();\n\t\t\t} catch (e) {\n\t\t\t\tif (isPermissionLoss(e)) {\n\t\t\t\t\tthrow new AuthExpiredError('File permission was revoked.');\n\t\t\t\t}\n\t\t\t\tthrow new SelfstoreError('TARGET_WRITE_FAILED', `File write failed: ${String(e)}`);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\t// The file's new mtime = OUR write's version marker (shared-disk case).\n\t\t\t\treturn String((await handle.getFile()).lastModified);\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\tasync load(): Promise<Blob | null> {\n\t\t\treturn handle.getFile();\n\t\t},\n\t\tasync stat(): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\treturn String((await handle.getFile()).lastModified);\n\t\t\t} catch {\n\t\t\t\treturn null; // permission not granted yet: \"cannot tell\", never throws\n\t\t\t}\n\t\t},\n\t\tasync isReady(): Promise<boolean> {\n\t\t\treturn (await handle.queryPermission({ mode: 'readwrite' })) === 'granted';\n\t\t},\n\t\tasync reconnect(): Promise<boolean> {\n\t\t\treturn (await handle.requestPermission({ mode: 'readwrite' })) === 'granted';\n\t\t},\n\t\tasync disconnect(): Promise<void> {\n\t\t\tawait kv.del(HANDLE_KEY);\n\t\t}\n\t};\n}\n\n/** Prompt the user to choose/create the backup file. Persists the handle for\n * next sessions. Returns the target, or null if cancelled - or if the browser\n * refused to open a picker at all, which `isSupported()` then reports. */\nexport async function connect(opts: FileConnectOptions): Promise<BackupTarget | null> {\n\tif (desktop.hasDesktopFiles()) return desktop.connect(opts);\n\tconst picker = (window as unknown as PickerWindow).showSaveFilePicker;\n\tif (!picker) return null;\n\tconst handle = await ask(() =>\n\t\tpicker({\n\t\t\tsuggestedName: opts.fileName,\n\t\t\ttypes: [{ description: 'Backup', accept: { 'application/zip': ['.zip'] } }]\n\t\t})\n\t);\n\tif (!handle) return null;\n\tawait opts.kv.set(HANDLE_KEY, handle);\n\treturn fromHandle(handle, opts.kv);\n}\n\n/** Prompt the user to pick an EXISTING backup file and adopt it as the\n * destination (the connect journey then reads it like any backup: password,\n * conflict, resume). Tries to raise the grant to readwrite up front so the\n * first save does not stall on a second prompt. Returns null if cancelled. */\nexport async function openExisting(opts: { kv: KV }): Promise<BackupTarget | null> {\n\tif (desktop.hasDesktopFiles()) return desktop.openExisting(opts);\n\tconst picker = (window as unknown as PickerWindow).showOpenFilePicker;\n\tif (!picker) return null;\n\tconst picked = await ask(() =>\n\t\tpicker({\n\t\t\tmultiple: false,\n\t\t\ttypes: [{ description: 'Backup', accept: { 'application/zip': ['.zip'] } }]\n\t\t})\n\t);\n\tconst handle = picked?.[0];\n\tif (!handle) return null;\n\t// The upgrade to readwrite is a COURTESY, not a condition. It can fail for a\n\t// reason that has nothing to do with the file - the open picker consumes the\n\t// transient activation a permission prompt needs, so a browser may answer\n\t// 'prompt' without ever asking anyone. Throwing the picked file away then\n\t// dropped the host back on its own screen with nothing said and nothing\n\t// changed: the user pointed at their file and the app appeared to ignore it.\n\t// So we adopt it either way. Reading works on the grant the picker gave, and\n\t// a write that cannot happen raises the store's own reconnect gate, whose\n\t// one click re-asks from inside a real gesture - the only place it can work.\n\t// Outside `ask`: a refusal here is about THIS file, never about the picker,\n\t// which has just proved it opens.\n\ttry {\n\t\tawait handle.requestPermission({ mode: 'readwrite' });\n\t} catch {\n\t\t/* no prompt was possible: the reconnect gesture will ask for real */\n\t}\n\tawait opts.kv.set(HANDLE_KEY, handle);\n\treturn fromHandle(handle, opts.kv);\n}\n\n/** Rebuild the target from what a past session persisted, or null. On a desktop\n * shell that is a path and it reconnects silently; in a browser it is a handle\n * and the first write may still ask for the permission back. */\nexport async function fromSession(opts: { kv: KV }): Promise<BackupTarget | null> {\n\tif (desktop.hasDesktopFiles()) return desktop.fromSession(opts);\n\tconst handle = await opts.kv.get<FileHandle>(HANDLE_KEY);\n\treturn handle ? fromHandle(handle, opts.kv) : null;\n}\n","/**\n * WebDAV backup target: the store re-writes one .selfstore file on a WebDAV\n * server the USER controls (Nextcloud, ownCloud, a plain WebDAV mount...). No\n * Google, no vendor broker - the browser talks straight to the server the user\n * owns. The bytes are the same end-to-end-encrypted .selfstore, so the server\n * only ever holds ciphertext.\n *\n * Two things the environment must allow, both documented for the user:\n * - the server must send CORS for the app origin (methods PUT/GET/HEAD and the\n * Authorization header), since browsers block cross-origin WebDAV otherwise;\n * - the app's CSP connect-src must permit the server host.\n *\n * Credentials are stored ENCRYPTED at rest under a per-device non-extractable\n * key, so a set-and-forget connection never leaves a cleartext password in\n * IndexedDB. The version marker prefers ETag, falling back to Last-Modified\n * (the latter is CORS-safelisted, so it is readable without extra server config).\n */\n\nimport type { BackupTarget, PeerSource } from '../target';\nimport type { KV } from '../cache';\nimport { seal, unseal, isEnvelope, newDeviceKey } from '../cache-crypto';\nimport { boundedSignal, lifeLine } from './abort';\nimport { AuthExpiredError, SelfstoreError } from '../../selfstore';\n\nconst CONFIG_KEY = 'webdavConfig';\nconst KEY_KEY = 'webdavKey';\n\n/** A hung server must not spin forever: a deadline turns the hang into an\n * ordinary error the store retries (never a gate). Transfers get more room than\n * the HEAD poke. */\nconst DATA_DEADLINE_MS = 30_000;\nconst META_DEADLINE_MS = 15_000;\n\nexport interface WebdavConfig {\n\t/** Full URL of the backup file, e.g. https://cloud.me/remote.php/dav/files/me/backup.zip */\n\turl: string;\n\tusername: string;\n\tpassword: string;\n}\n\nexport interface WebdavConnectOptions {\n\t/** Where the (encrypted) server config is persisted across sessions. */\n\tkv: KV;\n\tconfig: WebdavConfig;\n}\n\nasync function configKey(kv: KV): Promise<CryptoKey> {\n\tconst existing = await kv.get<CryptoKey>(KEY_KEY);\n\tif (existing) return existing;\n\tconst fresh = await newDeviceKey();\n\tawait kv.set(KEY_KEY, fresh);\n\treturn fresh;\n}\n\nasync function saveConfig(kv: KV, c: WebdavConfig): Promise<void> {\n\tconst bytes = new TextEncoder().encode(JSON.stringify(c));\n\tawait kv.set(CONFIG_KEY, await seal(await configKey(kv), bytes));\n}\n\nasync function loadConfig(kv: KV): Promise<WebdavConfig | null> {\n\tconst stored = await kv.get(CONFIG_KEY);\n\tif (!isEnvelope(stored)) return null;\n\tconst bytes = await unseal(await configKey(kv), stored);\n\treturn JSON.parse(new TextDecoder().decode(bytes)) as WebdavConfig;\n}\n\nfunction labelOf(url: string): string {\n\ttry {\n\t\treturn new URL(url).host;\n\t} catch {\n\t\treturn 'WebDAV';\n\t}\n}\n\n/** WebDAV authenticates with HTTP Basic - the password is sent on every request.\n * Over plain http that is cleartext on the wire, so refuse it. Allow https, and\n * http only for loopback (local dev against a container on the same machine). */\nfunction assertSecureUrl(url: string): void {\n\tlet u: URL;\n\ttry {\n\t\tu = new URL(url);\n\t} catch {\n\t\tthrow new TypeError(`webdavTarget: invalid URL \"${url}\".`);\n\t}\n\tconst loopback =\n\t\tu.hostname === 'localhost' || u.hostname === '127.0.0.1' || u.hostname === '[::1]';\n\tif (u.protocol !== 'https:' && !(u.protocol === 'http:' && loopback)) {\n\t\tthrow new TypeError(\n\t\t\t`webdavTarget: refusing \"${u.protocol}\" - Basic-auth credentials need https (http is allowed only for localhost).`\n\t\t);\n\t}\n}\n\nfunction versionOf(res: Response): string | null {\n\treturn res.headers.get('ETag') ?? res.headers.get('Last-Modified') ?? null;\n}\n\nfunction fromConfig(c: WebdavConfig, kv: KV): BackupTarget {\n\tconst headers = { Authorization: 'Basic ' + btoa(`${c.username}:${c.password}`) };\n\t// The instance's LIFE line: abortInFlight() (a user detach) cuts every\n\t// suspended request now instead of at its deadline (see the Drive target).\n\tconst life = lifeLine();\n\tconst bounded = (ms: number): AbortSignal => boundedSignal(ms, life.current());\n\n\t/** One HEAD, or null on a network/CORS failure (transient - the caller decides\n\t * what \"cannot reach\" means for it). */\n\tasync function probe(): Promise<Response | null> {\n\t\ttry {\n\t\t\treturn await fetch(c.url, {\n\t\t\t\tmethod: 'HEAD',\n\t\t\t\theaders,\n\t\t\t\tsignal: bounded(META_DEADLINE_MS)\n\t\t\t});\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tasync function stat(): Promise<string | null> {\n\t\tconst res = await probe();\n\t\treturn res && res.ok ? versionOf(res) : null; // 404 / error / offline: \"cannot tell\"\n\t}\n\tasync function ready(): Promise<boolean> {\n\t\tconst res = await probe();\n\t\tif (!res) return false; // offline / CORS blocked: transient, the store retries later\n\t\t// 401/403 = the stored credentials are rejected: a genuine loss of access\n\t\t// only the user can fix (re-enter the config), so gate rather than loop as\n\t\t// \"momentarily unreachable\". 404 = file not created yet, still writable.\n\t\tif (res.status === 401 || res.status === 403) {\n\t\t\tthrow new AuthExpiredError(`WebDAV rejected the credentials (${res.status}).`);\n\t\t}\n\t\treturn res.ok || res.status === 404;\n\t}\n\t/** Credentials are stored; a reconnect just re-checks reachability. It can\n\t * never mint new credentials, so a rejection is reported as false (never\n\t * thrown) - only re-running connect() with a fresh config fixes bad creds. */\n\tasync function reconnectProbe(): Promise<boolean> {\n\t\tconst res = await probe();\n\t\treturn !!res && (res.ok || res.status === 404);\n\t}\n\n\treturn {\n\t\tkind: 'webdav',\n\t\tlabel: labelOf(c.url),\n\t\tabortInFlight: life.cut,\n\t\tasync save(blob: Blob): Promise<string | null> {\n\t\t\tconst res = await fetch(c.url, {\n\t\t\t\tmethod: 'PUT',\n\t\t\t\theaders,\n\t\t\t\tbody: blob,\n\t\t\t\tsignal: bounded(DATA_DEADLINE_MS)\n\t\t\t});\n\t\t\t// 401/403 = the server rejected the stored credentials: a genuine loss of\n\t\t\t// access the user must fix. Anything else (5xx, conflict) is transient.\n\t\t\tif (res.status === 401 || res.status === 403) {\n\t\t\t\tthrow new AuthExpiredError(`WebDAV rejected the credentials (${res.status}).`);\n\t\t\t}\n\t\t\tif (!res.ok)\n\t\t\t\tthrow new SelfstoreError('TARGET_WRITE_FAILED', `WebDAV PUT failed: ${res.status}`);\n\t\t\treturn versionOf(res) ?? (await stat());\n\t\t},\n\t\tasync load(): Promise<Blob | null> {\n\t\t\tconst res = await fetch(c.url, {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders,\n\t\t\t\tsignal: bounded(DATA_DEADLINE_MS)\n\t\t\t});\n\t\t\tif (res.status === 404) return null;\n\t\t\tif (res.status === 401 || res.status === 403) {\n\t\t\t\tthrow new AuthExpiredError(`WebDAV rejected the credentials (${res.status}).`);\n\t\t\t}\n\t\t\tif (!res.ok)\n\t\t\t\tthrow new SelfstoreError('TARGET_UNAVAILABLE', `WebDAV GET failed: ${res.status}`);\n\t\t\treturn res.blob();\n\t\t},\n\t\tstat,\n\t\tisReady: ready,\n\t\treconnect: reconnectProbe, // stored credentials: re-check reachability, no gesture\n\t\tasync disconnect(): Promise<void> {\n\t\t\tawait kv.del(CONFIG_KEY);\n\t\t\tawait kv.del(KEY_KEY);\n\t\t}\n\t};\n}\n\nexport interface WebdavPeerOptions {\n\t/** URL of ANOTHER member's published copy, read-only: a Nextcloud/ownCloud\n\t * read-only share link, or a file URL they granted you read access to. */\n\turl: string;\n\t/** Optional Basic-auth credentials, when the read-only share needs them.\n\t * Omit for a public/tokenised share link (a Nextcloud `/s/<token>/download`\n\t * URL) that needs none. Held in memory only - a peer is re-attached each\n\t * session (see attachPeer), so nothing is persisted here. */\n\tusername?: string;\n\tpassword?: string;\n\t/** Display label (defaults to the URL host). */\n\tlabel?: string;\n}\n\n/** A read-only WebDAV peer: another member's published copy, fetched over a\n * shared URL, for `store.attachPeer`. It is the read half of a WebDAV target\n * (GET + HEAD only, never a write), so a group can share read-write without\n * Google Drive - each member publishes to their own WebDAV and shares a\n * read-only link. Failures follow the peer protocol: a 401/403 is a genuine\n * loss of read access (AuthExpiredError), 404 means \"not published yet\"\n * (null), anything else is transient. */\nexport function peer(opts: WebdavPeerOptions): PeerSource {\n\t// Basic-auth credentials are cleartext on the wire, so require https when they\n\t// are present; a credential-less public link carries only ciphertext, so any\n\t// host it lives on is the member's own choice.\n\tif (opts.username !== undefined) assertSecureUrl(opts.url);\n\tconst headers =\n\t\topts.username !== undefined\n\t\t\t? { Authorization: 'Basic ' + btoa(`${opts.username}:${opts.password ?? ''}`) }\n\t\t\t: undefined;\n\treturn {\n\t\tlabel: opts.label ?? labelOf(opts.url),\n\t\tasync load(): Promise<Blob | null> {\n\t\t\tconst res = await fetch(opts.url, {\n\t\t\t\tmethod: 'GET',\n\t\t\t\theaders,\n\t\t\t\tsignal: AbortSignal.timeout(DATA_DEADLINE_MS)\n\t\t\t});\n\t\t\tif (res.status === 404) return null; // the member has not published yet\n\t\t\tif (res.status === 401 || res.status === 403) {\n\t\t\t\tthrow new AuthExpiredError(`WebDAV peer rejected the credentials (${res.status}).`);\n\t\t\t}\n\t\t\tif (!res.ok) {\n\t\t\t\tthrow new SelfstoreError('TARGET_UNAVAILABLE', `WebDAV peer GET failed: ${res.status}`);\n\t\t\t}\n\t\t\treturn res.blob();\n\t\t},\n\t\tasync stat(): Promise<string | null> {\n\t\t\ttry {\n\t\t\t\tconst res = await fetch(opts.url, {\n\t\t\t\t\tmethod: 'HEAD',\n\t\t\t\t\theaders,\n\t\t\t\t\tsignal: AbortSignal.timeout(META_DEADLINE_MS)\n\t\t\t\t});\n\t\t\t\treturn res.ok ? versionOf(res) : null; // cannot tell: fall through to a full load\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t};\n}\n\n/** Validate + persist the config, returning the target, or null when the server\n * is unreachable, rejects the credentials, or blocks the cross-origin request. */\nexport async function connect(opts: WebdavConnectOptions): Promise<BackupTarget | null> {\n\tassertSecureUrl(opts.config.url); // cleartext credentials over http are refused up front\n\tconst target = fromConfig(opts.config, opts.kv);\n\t// isReady() now THROWS on a 401/403 (so a later rejection gates the store); at\n\t// first connect a rejection just means \"wrong credentials, try again\", so a\n\t// throw here reads the same as false - report null, persist nothing.\n\tlet ok: boolean;\n\ttry {\n\t\tok = await target.isReady();\n\t} catch {\n\t\tok = false;\n\t}\n\tif (!ok) return null;\n\tawait saveConfig(opts.kv, opts.config);\n\treturn target;\n}\n\n/** Rebuild the target from the encrypted config persisted last session, or null. */\nexport async function fromSession(opts: { kv: KV }): Promise<BackupTarget | null> {\n\tconst config = await loadConfig(opts.kv);\n\treturn config ? fromConfig(config, opts.kv) : null;\n}\n","// AWS Signature Version 4 for S3 REST requests, in the browser with WebCrypto.\n// Enough of the spec to sign one object's GET/PUT/HEAD - no streaming, no\n// chunked upload, no presigned URLs. Works against AWS S3 and any S3-compatible\n// endpoint (Cloudflare R2, Backblaze B2, MinIO) since they all verify SigV4.\n//\n// Signed headers are host, x-amz-content-sha256 and x-amz-date, plus whatever\n// the caller adds. The browser sets Host itself from the URL and forbids\n// overriding it, so we sign the host derived from that same URL - the value the\n// browser will send - and never try to set the header.\n\nconst encoder = new TextEncoder();\n\nfunction hex(bytes: Uint8Array): string {\n\tlet out = '';\n\tfor (const b of bytes) out += b.toString(16).padStart(2, '0');\n\treturn out;\n}\n\nexport async function sha256Hex(bytes: Uint8Array): Promise<string> {\n\treturn hex(new Uint8Array(await crypto.subtle.digest('SHA-256', bytes as BufferSource)));\n}\n\n/** SHA-256 of the empty body: the payload hash for every GET/HEAD. */\nexport const EMPTY_PAYLOAD_SHA256 =\n\t'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';\n\nasync function hmac(key: Uint8Array, data: Uint8Array): Promise<Uint8Array> {\n\tconst k = await crypto.subtle.importKey(\n\t\t'raw',\n\t\tkey as BufferSource,\n\t\t{ name: 'HMAC', hash: 'SHA-256' },\n\t\tfalse,\n\t\t['sign']\n\t);\n\treturn new Uint8Array(await crypto.subtle.sign('HMAC', k, data as BufferSource));\n}\n\n// RFC 3986 encoding, applied per path segment (the '/' between segments stays a\n// separator). encodeURIComponent leaves !*'() alone; SigV4 requires them encoded.\nfunction encodeSegment(s: string): string {\n\treturn encodeURIComponent(s).replace(\n\t\t/[!*'()]/g,\n\t\t(c) => '%' + c.charCodeAt(0).toString(16).toUpperCase()\n\t);\n}\n\n/** Canonicalise an object path for both the request URL and the signature, so\n * the two can never disagree on encoding. Pass the RAW key path (unencoded). */\nexport function encodeS3Path(path: string): string {\n\treturn path\n\t\t.split('/')\n\t\t.map((seg) => encodeSegment(seg))\n\t\t.join('/');\n}\n\nexport interface SignS3Input {\n\tmethod: string;\n\t/** Scheme + host(+port), e.g. https://s3.eu-west-3.amazonaws.com */\n\torigin: string;\n\t/** Raw (unencoded) object path, e.g. /my-bucket/backups/app.selfstore */\n\tpath: string;\n\tregion: string;\n\taccessKeyId: string;\n\tsecretAccessKey: string;\n\t/** Hex SHA-256 of the request body (EMPTY_PAYLOAD_SHA256 for GET/HEAD). */\n\tpayloadHashHex: string;\n\t/** Extra headers to sign, e.g. { range: 'bytes=0-0' }. Names are lowercased. */\n\textraHeaders?: Record<string, string>;\n\t/** Basic timestamp (YYYYMMDDTHHMMSSZ). Injected in tests; defaults to now. */\n\tamzDate?: string;\n\tservice?: string;\n}\n\nexport interface SignedS3Request {\n\t/** The fully-encoded URL to fetch. */\n\turl: string;\n\t/** Headers to send: Authorization, x-amz-date, x-amz-content-sha256, extras. */\n\theaders: Record<string, string>;\n}\n\n/** ISO basic UTC now, YYYYMMDDTHHMMSSZ. */\nfunction basicAmzDate(): string {\n\treturn new Date().toISOString().replace(/[:-]|\\.\\d{3}/g, '');\n}\n\n/** Sign one S3 request and return the URL plus the headers to send. */\nexport async function signS3(input: SignS3Input): Promise<SignedS3Request> {\n\tconst service = input.service ?? 's3';\n\tconst amzDate = input.amzDate ?? basicAmzDate();\n\tconst dateStamp = amzDate.slice(0, 8);\n\tconst { host } = new URL(input.origin);\n\tconst canonicalUri = encodeS3Path(input.path);\n\n\tconst headers: Record<string, string> = {\n\t\thost,\n\t\t'x-amz-content-sha256': input.payloadHashHex,\n\t\t'x-amz-date': amzDate\n\t};\n\tfor (const [name, value] of Object.entries(input.extraHeaders ?? {})) {\n\t\theaders[name.toLowerCase()] = value;\n\t}\n\tconst signedNames = Object.keys(headers).sort();\n\tconst canonicalHeaders = signedNames.map((n) => `${n}:${headers[n].trim()}\\n`).join('');\n\tconst signedHeaders = signedNames.join(';');\n\n\tconst canonicalRequest = [\n\t\tinput.method,\n\t\tcanonicalUri,\n\t\t'', // no query string on any of our single-object requests\n\t\tcanonicalHeaders,\n\t\tsignedHeaders,\n\t\tinput.payloadHashHex\n\t].join('\\n');\n\n\tconst scope = `${dateStamp}/${input.region}/${service}/aws4_request`;\n\tconst stringToSign = [\n\t\t'AWS4-HMAC-SHA256',\n\t\tamzDate,\n\t\tscope,\n\t\tawait sha256Hex(encoder.encode(canonicalRequest))\n\t].join('\\n');\n\n\tconst kDate = await hmac(\n\t\tencoder.encode('AWS4' + input.secretAccessKey),\n\t\tencoder.encode(dateStamp)\n\t);\n\tconst kRegion = await hmac(kDate, encoder.encode(input.region));\n\tconst kService = await hmac(kRegion, encoder.encode(service));\n\tconst kSigning = await hmac(kService, encoder.encode('aws4_request'));\n\tconst signature = hex(await hmac(kSigning, encoder.encode(stringToSign)));\n\n\tconst authorization =\n\t\t`AWS4-HMAC-SHA256 Credential=${input.accessKeyId}/${scope}, ` +\n\t\t`SignedHeaders=${signedHeaders}, Signature=${signature}`;\n\n\t// Send every signed header except host (the browser sets Host itself and\n\t// forbids overriding it - we signed the value it will send).\n\tconst sendHeaders: Record<string, string> = { authorization };\n\tfor (const n of signedNames) if (n !== 'host') sendHeaders[n] = headers[n];\n\n\treturn { url: input.origin.replace(/\\/$/, '') + canonicalUri, headers: sendHeaders };\n}\n","/**\n * S3 backup target: the store re-writes one object on an S3-compatible bucket\n * the USER controls - Amazon S3, Cloudflare R2, Backblaze B2, MinIO, any server\n * that speaks the S3 REST API and SigV4. No vendor broker: the browser signs\n * each request itself and talks straight to the endpoint. The bytes are the\n * same end-to-end-encrypted .selfstore, so the bucket only ever holds\n * ciphertext.\n *\n * Two things the environment must allow, both documented for the user:\n * - the bucket must send CORS for the app origin: allowed methods GET/PUT/HEAD,\n * allowed headers authorization/x-amz-*, and ExposeHeaders ETag (so the\n * version marker is readable), or browsers block the cross-origin request;\n * - the app's CSP connect-src must permit the endpoint host.\n *\n * Credentials (the secret access key) are stored ENCRYPTED at rest under a\n * per-device non-extractable key, exactly like the WebDAV target, so a\n * set-and-forget connection never leaves a cleartext secret in IndexedDB.\n *\n * Path style is the default: the object lives at endpoint/bucket/key, which\n * every S3-compatible server accepts and needs no per-bucket DNS. Set\n * forcePathStyle:false for AWS virtual-hosted style (bucket as a host label).\n */\n\nimport type { BackupTarget } from '../target';\nimport type { KV } from '../cache';\nimport { seal, unseal, isEnvelope, newDeviceKey } from '../cache-crypto';\nimport { boundedSignal, lifeLine } from './abort';\nimport { AuthExpiredError, SelfstoreError } from '../../selfstore';\nimport { signS3, sha256Hex, EMPTY_PAYLOAD_SHA256 } from './sigv4';\n\nconst CONFIG_KEY = 's3Config';\nconst KEY_KEY = 's3Key';\n\nconst DATA_DEADLINE_MS = 30_000;\nconst META_DEADLINE_MS = 15_000;\n\nexport interface S3Config {\n\t/** Endpoint origin, e.g. https://s3.eu-west-3.amazonaws.com,\n\t * https://<account>.r2.cloudflarestorage.com, https://minio.example.com. */\n\tendpoint: string;\n\t/** Signing region (\"eu-west-3\"; R2 uses \"auto\"; MinIO any configured value). */\n\tregion: string;\n\tbucket: string;\n\t/** Object key (the backup file), e.g. backups/app.selfstore. */\n\tkey: string;\n\taccessKeyId: string;\n\tsecretAccessKey: string;\n\t/** endpoint/bucket/key (default) vs bucket.endpoint/key. Default true. */\n\tforcePathStyle?: boolean;\n}\n\nexport interface S3ConnectOptions {\n\t/** Where the (encrypted) config is persisted across sessions. */\n\tkv: KV;\n\tconfig: S3Config;\n}\n\nasync function configKey(kv: KV): Promise<CryptoKey> {\n\tconst existing = await kv.get<CryptoKey>(KEY_KEY);\n\tif (existing) return existing;\n\tconst fresh = await newDeviceKey();\n\tawait kv.set(KEY_KEY, fresh);\n\treturn fresh;\n}\n\nasync function saveConfig(kv: KV, c: S3Config): Promise<void> {\n\tconst bytes = new TextEncoder().encode(JSON.stringify(c));\n\tawait kv.set(CONFIG_KEY, await seal(await configKey(kv), bytes));\n}\n\nasync function loadConfig(kv: KV): Promise<S3Config | null> {\n\tconst stored = await kv.get(CONFIG_KEY);\n\tif (!isEnvelope(stored)) return null;\n\tconst bytes = await unseal(await configKey(kv), stored);\n\treturn JSON.parse(new TextDecoder().decode(bytes)) as S3Config;\n}\n\n/** SigV4 puts the secret on the wire only as a derived signature, but the\n * request still needs https: an http endpoint would expose the object bytes\n * and headers. Allow http for loopback (a local MinIO in dev) only. */\nfunction assertSecureEndpoint(endpoint: string): URL {\n\tlet u: URL;\n\ttry {\n\t\tu = new URL(endpoint);\n\t} catch {\n\t\tthrow new TypeError(`s3Target: invalid endpoint \"${endpoint}\".`);\n\t}\n\tconst loopback =\n\t\tu.hostname === 'localhost' || u.hostname === '127.0.0.1' || u.hostname === '[::1]';\n\tif (u.protocol !== 'https:' && !(u.protocol === 'http:' && loopback)) {\n\t\tthrow new TypeError(\n\t\t\t`s3Target: refusing \"${u.protocol}\" - S3 requests need https (http is allowed only for localhost).`\n\t\t);\n\t}\n\treturn u;\n}\n\nfunction labelOf(c: S3Config): string {\n\ttry {\n\t\treturn `${c.bucket} (${new URL(c.endpoint).host})`;\n\t} catch {\n\t\treturn c.bucket || 'S3';\n\t}\n}\n\nfunction versionOf(res: Response): string | null {\n\treturn res.headers.get('ETag') ?? res.headers.get('Last-Modified') ?? null;\n}\n\nfunction fromConfig(c: S3Config, kv: KV): BackupTarget {\n\tconst endpointUrl = assertSecureEndpoint(c.endpoint);\n\tconst pathStyle = c.forcePathStyle ?? true;\n\t// Path style keeps one host and no per-bucket DNS; virtual-hosted folds the\n\t// bucket into the host label (AWS's preferred form).\n\tconst origin = pathStyle\n\t\t? `${endpointUrl.protocol}//${endpointUrl.host}`\n\t\t: `${endpointUrl.protocol}//${c.bucket}.${endpointUrl.host}`;\n\tconst objectPath = pathStyle ? `/${c.bucket}/${c.key}` : `/${c.key}`;\n\t// The instance's LIFE line: abortInFlight() (a user detach) cuts every\n\t// suspended request now instead of at its deadline (see the Drive target).\n\tconst life = lifeLine();\n\n\tasync function request(\n\t\tmethod: string,\n\t\tpayloadHashHex: string,\n\t\tbody?: BodyInit,\n\t\textraHeaders?: Record<string, string>,\n\t\tdeadlineMs = DATA_DEADLINE_MS\n\t): Promise<Response> {\n\t\tconst signed = await signS3({\n\t\t\tmethod,\n\t\t\torigin,\n\t\t\tpath: objectPath,\n\t\t\tregion: c.region,\n\t\t\taccessKeyId: c.accessKeyId,\n\t\t\tsecretAccessKey: c.secretAccessKey,\n\t\t\tpayloadHashHex,\n\t\t\textraHeaders\n\t\t});\n\t\treturn fetch(signed.url, {\n\t\t\tmethod,\n\t\t\theaders: signed.headers,\n\t\t\tbody,\n\t\t\tsignal: boundedSignal(deadlineMs, life.current())\n\t\t});\n\t}\n\n\t/** One HEAD, or null on a network/CORS failure (transient). */\n\tasync function probe(): Promise<Response | null> {\n\t\ttry {\n\t\t\treturn await request('HEAD', EMPTY_PAYLOAD_SHA256, undefined, undefined, META_DEADLINE_MS);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tasync function stat(): Promise<string | null> {\n\t\tconst res = await probe();\n\t\treturn res && res.ok ? versionOf(res) : null; // 404 / error / offline: cannot tell\n\t}\n\tasync function ready(): Promise<boolean> {\n\t\tconst res = await probe();\n\t\tif (!res) return false; // offline / CORS blocked: transient, retried later\n\t\t// 403 = the credentials are rejected (a HEAD with GetObject permission\n\t\t// returns 404, not 403, for a missing object - so 403 is a real auth loss,\n\t\t// not \"no backup yet\"). 404 = object not created yet, still writable.\n\t\tif (res.status === 403) {\n\t\t\tthrow new AuthExpiredError('S3 rejected the credentials (403).');\n\t\t}\n\t\treturn res.ok || res.status === 404;\n\t}\n\t/** Stored credentials: a reconnect just re-checks reachability, never mints new\n\t * keys, so a rejection is reported as false rather than thrown. */\n\tasync function reconnectProbe(): Promise<boolean> {\n\t\tconst res = await probe();\n\t\treturn !!res && (res.ok || res.status === 404);\n\t}\n\n\treturn {\n\t\tkind: 's3',\n\t\tlabel: labelOf(c),\n\t\tabortInFlight: life.cut,\n\t\tasync save(blob: Blob): Promise<string | null> {\n\t\t\tconst bytes = new Uint8Array(await blob.arrayBuffer());\n\t\t\tconst res = await request('PUT', await sha256Hex(bytes), bytes);\n\t\t\tif (res.status === 403) {\n\t\t\t\tthrow new AuthExpiredError('S3 rejected the credentials (403).');\n\t\t\t}\n\t\t\tif (!res.ok) throw new SelfstoreError('TARGET_WRITE_FAILED', `S3 PUT failed: ${res.status}`);\n\t\t\treturn versionOf(res) ?? (await stat());\n\t\t},\n\t\tasync load(): Promise<Blob | null> {\n\t\t\tconst res = await request('GET', EMPTY_PAYLOAD_SHA256);\n\t\t\tif (res.status === 404) return null;\n\t\t\tif (res.status === 403) {\n\t\t\t\tthrow new AuthExpiredError('S3 rejected the credentials (403).');\n\t\t\t}\n\t\t\tif (!res.ok) throw new SelfstoreError('TARGET_UNAVAILABLE', `S3 GET failed: ${res.status}`);\n\t\t\treturn res.blob();\n\t\t},\n\t\tstat,\n\t\tisReady: ready,\n\t\treconnect: reconnectProbe,\n\t\tasync disconnect(): Promise<void> {\n\t\t\tawait kv.del(CONFIG_KEY);\n\t\t\tawait kv.del(KEY_KEY);\n\t\t}\n\t};\n}\n\n/** Validate + persist the config, returning the target, or null when the bucket\n * is unreachable, rejects the credentials, or blocks the cross-origin request. */\nexport async function connect(opts: S3ConnectOptions): Promise<BackupTarget | null> {\n\tassertSecureEndpoint(opts.config.endpoint);\n\tconst target = fromConfig(opts.config, opts.kv);\n\t// isReady() throws on a 403 (a later rejection gates the store); at first\n\t// connect a rejection just means \"wrong credentials, try again\", so a throw\n\t// here reads the same as false - report null, persist nothing.\n\tlet ok: boolean;\n\ttry {\n\t\tok = await target.isReady();\n\t} catch {\n\t\tok = false;\n\t}\n\tif (!ok) return null;\n\tawait saveConfig(opts.kv, opts.config);\n\treturn target;\n}\n\n/** Rebuild the target from the encrypted config persisted last session, or null. */\nexport async function fromSession(opts: { kv: KV }): Promise<BackupTarget | null> {\n\tconst config = await loadConfig(opts.kv);\n\treturn config ? fromConfig(config, opts.kv) : null;\n}\n"]}