selfstore 1.8.1 → 1.8.3

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 (58) hide show
  1. package/README.md +5 -0
  2. package/dist/advanced.d.ts +10 -6
  3. package/dist/advanced.js +14 -13
  4. package/dist/advanced.js.map +1 -1
  5. package/dist/backups.d.ts +31 -179
  6. package/dist/backups.js +40 -3
  7. package/dist/backups.js.map +1 -1
  8. package/dist/{cache-lock-7D6N42ZP.js → cache-lock-KWJUORTS.js} +3 -4
  9. package/dist/cache-lock-KWJUORTS.js.map +1 -0
  10. package/dist/{chunk-CM3HSBCE.js → chunk-3OBJOMQ2.js} +28 -4
  11. package/dist/chunk-3OBJOMQ2.js.map +1 -0
  12. package/dist/{chunk-53WWPEML.js → chunk-DCEXWSM6.js} +3 -3
  13. package/dist/{chunk-53WWPEML.js.map → chunk-DCEXWSM6.js.map} +1 -1
  14. package/dist/{chunk-ZV2S3I76.js → chunk-ELDSPUTS.js} +30 -10
  15. package/dist/chunk-ELDSPUTS.js.map +1 -0
  16. package/dist/chunk-J5MFXJU6.js +310 -0
  17. package/dist/chunk-J5MFXJU6.js.map +1 -0
  18. package/dist/{chunk-ZDHTKFAL.js → chunk-MGDXR3RB.js} +5 -6
  19. package/dist/chunk-MGDXR3RB.js.map +1 -0
  20. package/dist/{chunk-CT2HTWA7.js → chunk-O3Y6FORN.js} +3 -4
  21. package/dist/chunk-O3Y6FORN.js.map +1 -0
  22. package/dist/{chunk-OZS7BSC2.js → chunk-PNX7AJYO.js} +26 -12
  23. package/dist/chunk-PNX7AJYO.js.map +1 -0
  24. package/dist/{chunk-XBPJRUA3.js → chunk-TKWU52LK.js} +41 -319
  25. package/dist/chunk-TKWU52LK.js.map +1 -0
  26. package/dist/{chunk-OIAXYI35.js → chunk-W22V5CRM.js} +3 -3
  27. package/dist/{chunk-OIAXYI35.js.map → chunk-W22V5CRM.js.map} +1 -1
  28. package/dist/{drive-DF08V8-L.d.ts → drive-CTYvGg_7.d.ts} +23 -110
  29. package/dist/{flows-dwgDgv6_.d.ts → flows-1EntUZ91.d.ts} +3 -2
  30. package/dist/flows.d.ts +4 -3
  31. package/dist/flows.js +16 -16
  32. package/dist/flows.js.map +1 -1
  33. package/dist/groups.js +3 -4
  34. package/dist/groups.js.map +1 -1
  35. package/dist/households.d.ts +1 -1
  36. package/dist/index.d.ts +5 -4
  37. package/dist/index.js +24 -18
  38. package/dist/index.js.map +1 -1
  39. package/dist/{local-ipQv_Pbv.d.ts → local-C52j3DeH.d.ts} +2 -2
  40. package/dist/local-DPS35F5C.js +6 -0
  41. package/dist/{local-5QN7V24I.js.map → local-DPS35F5C.js.map} +1 -1
  42. package/dist/manager-DZ1c_aSS.d.ts +181 -0
  43. package/dist/s3-DvsnA-FK.d.ts +112 -0
  44. package/dist/{store-CbGrlMME.d.ts → store-BiQ-BuVl.d.ts} +8 -2
  45. package/dist/widgets.d.ts +5 -4
  46. package/dist/widgets.js +11 -9
  47. package/dist/widgets.js.map +1 -1
  48. package/package.json +1 -1
  49. package/dist/cache-lock-7D6N42ZP.js.map +0 -1
  50. package/dist/chunk-CM3HSBCE.js.map +0 -1
  51. package/dist/chunk-CT2HTWA7.js.map +0 -1
  52. package/dist/chunk-OZS7BSC2.js.map +0 -1
  53. package/dist/chunk-SFF7KKM4.js +0 -29
  54. package/dist/chunk-SFF7KKM4.js.map +0 -1
  55. package/dist/chunk-XBPJRUA3.js.map +0 -1
  56. package/dist/chunk-ZDHTKFAL.js.map +0 -1
  57. package/dist/chunk-ZV2S3I76.js.map +0 -1
  58. package/dist/local-5QN7V24I.js +0 -7
package/README.md CHANGED
@@ -26,6 +26,11 @@ host.
26
26
  Docs, guides, a live demo and honest comparisons with the alternatives:
27
27
  **[selfstore.dev](https://selfstore.dev)**.
28
28
 
29
+ Or run the whole pitch in a browser tab, nothing installed:
30
+ **[open the playground in StackBlitz](https://stackblitz.com/github/selfstoredev/selfstore/tree/main/examples/playground)**
31
+ - notes that survive a reload, a real file on the disk as their home, and a
32
+ genuine encrypted ZIP ([source](examples/playground)).
33
+
29
34
  ## Going further is one call each
30
35
 
31
36
  ```ts
@@ -1,10 +1,12 @@
1
- import { B as BackupTarget } from './store-CbGrlMME.js';
2
- export { A as AuthExpiredError, a as BuiltinTargetKind, E as EncodeOptions, H as Header, K as KdfParams, L as LocalStore, b as LocalStoreOptions, c as LocalStoreState, M as MirrorState, d as Mode, e as PasswordCheck, f as PasswordPolicy, g as PasswordRequirement, P as PeerSource, h as PeerState, R as RESERVED_COLLECTION_PREFIX, i as RESERVED_STORE_MODES, j as RecipientStanza, k as ReplicaState, S as SelfstoreError, l as SelfstoreErrorCode, m as Severity, n as Snapshot, o as SnapshotFile, p as StatusAction, q as StatusDescriptor, r as StatusInput, s as StorageState, t as StoreError, u as StoreGroupConfig, v as SyncJournalEntry, w as SyncSource, T as TargetKind, x as checkPasswordPolicy, y as createLocalStore, z as deriveStatus, C as errorLabelKey, D as isAuthExpired, F as isReservedStoreMode, G as isSelfstoreError } from './store-CbGrlMME.js';
1
+ import { B as BackupTarget } from './store-BiQ-BuVl.js';
2
+ export { A as AuthExpiredError, a as BuiltinTargetKind, E as EncodeOptions, H as Header, K as KdfParams, L as LocalStore, b as LocalStoreOptions, c as LocalStoreState, M as MirrorState, d as Mode, e as PasswordCheck, f as PasswordPolicy, g as PasswordRequirement, P as PeerSource, h as PeerState, R as RESERVED_COLLECTION_PREFIX, i as RESERVED_STORE_MODES, j as RecipientStanza, k as ReplicaState, S as SelfstoreError, l as SelfstoreErrorCode, m as Severity, n as Snapshot, o as SnapshotFile, p as StatusAction, q as StatusDescriptor, r as StatusInput, s as StorageState, t as StoreError, u as StoreGroupConfig, v as SyncJournalEntry, w as SyncSource, T as TargetKind, x as checkPasswordPolicy, y as createLocalStore, z as deriveStatus, C as errorLabelKey, D as isAuthExpired, F as isReservedStoreMode, G as isSelfstoreError } from './store-BiQ-BuVl.js';
3
3
  import { K as KV } from './cache-ilsK3Tki.js';
4
4
  export { C as CachedFile, L as LocalCache, e as LockableCache, i as indexedDbCache, f as isLockableCache, m as memoryCache } from './cache-ilsK3Tki.js';
5
- export { d as datedName, e as exportSnapshot, g as gisDriveAuth, i as importSnapshot, a as inspect, b as isEncrypted, p as pickFromDisk, s as saveToDisk } from './local-ipQv_Pbv.js';
6
- import { c as connect$1, f as fromSession$1, p as preview, a as adopt, b as findOrCreateOwnFile, l as listBackups, d as createBackup, e as deleteBackup, r as renameBackup, g as connect$2, h as fromSession$2, i as connect$3, j as fromSession$3, k as peer } from './drive-DF08V8-L.js';
7
- export { D as DriveAuth, m as DriveBackupInfo, n as DriveOptions, S as S3Config, o as S3ConnectOptions, W as WebdavConfig, q as WebdavConnectOptions, s as WebdavPeerOptions } from './drive-DF08V8-L.js';
5
+ export { d as datedName, e as exportSnapshot, g as gisDriveAuth, i as importSnapshot, a as inspect, b as isEncrypted, p as pickFromDisk, s as saveToDisk } from './local-C52j3DeH.js';
6
+ import { c as connect$2, f as fromSession$2, a as connect$3, b as fromSession$3, p as peer } from './s3-DvsnA-FK.js';
7
+ export { S as S3Config, d as S3ConnectOptions, W as WebdavConfig, e as WebdavConnectOptions, g as WebdavPeerOptions } from './s3-DvsnA-FK.js';
8
+ import { c as connect$1, f as fromSession$1, p as preview, a as adopt, b as findOrCreateOwnFile, l as listBackups, d as createBackup, e as deleteBackup, r as renameBackup, g as account } from './drive-CTYvGg_7.js';
9
+ export { h as DriveAccount, i as DriveAuth, j as DriveBackupInfo, D as DriveOptions } from './drive-CTYvGg_7.js';
8
10
  import './sync.js';
9
11
 
10
12
  /**
@@ -143,7 +145,8 @@ declare const s3Target: {
143
145
  * file, adopt it as this device's backup, find-or-create the user's own file)
144
146
  * and the backup-management ops (list the app's files with dates, create a
145
147
  * named empty backup, rename one, delete one for good) - the raw pieces a "my
146
- * backups" panel builds on. */
148
+ * backups" panel builds on. `account()` names WHICH Google account holds them,
149
+ * because a brand name is not an address. */
147
150
  declare const driveTarget: {
148
151
  connect: typeof connect$1;
149
152
  fromSession: typeof fromSession$1;
@@ -154,6 +157,7 @@ declare const driveTarget: {
154
157
  createBackup: typeof createBackup;
155
158
  deleteBackup: typeof deleteBackup;
156
159
  renameBackup: typeof renameBackup;
160
+ account: typeof account;
157
161
  FILE_ID_KEY: string;
158
162
  };
159
163
 
package/dist/advanced.js CHANGED
@@ -1,12 +1,12 @@
1
- export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache } from './chunk-ZV2S3I76.js';
2
- export { datedName, pickFromDisk, saveToDisk } from './chunk-53WWPEML.js';
1
+ export { RESERVED_STORE_MODES, createLocalStore, deriveStatus, gisDriveAuth, indexedDbCache, isLockableCache, isReservedStoreMode, memoryCache } from './chunk-ELDSPUTS.js';
2
+ export { datedName, pickFromDisk, saveToDisk } from './chunk-DCEXWSM6.js';
3
3
  import './chunk-SKWHKM25.js';
4
- import { FILE_ID_KEY, renameBackup, deleteBackup, createBackup, listBackups, findOrCreateOwnFile, adopt, preview, fromSession, connect, openExisting, isOpenSupported, isSupported, fromSession2, connect2, fromSession4, connect4, peer, fromSession3, connect3 } from './chunk-XBPJRUA3.js';
5
- export { RESERVED_COLLECTION_PREFIX, checkPasswordPolicy, exportSnapshot, importSnapshot, inspect, isEncrypted } from './chunk-ZDHTKFAL.js';
6
- import './chunk-CT2HTWA7.js';
4
+ import { openExisting, isOpenSupported, isSupported, fromSession as fromSession$1, connect as connect$1, fromSession3, connect3, peer, fromSession2, connect2 } from './chunk-TKWU52LK.js';
5
+ import { FILE_ID_KEY, account, renameBackup, deleteBackup, createBackup, listBackups, findOrCreateOwnFile, adopt, preview, fromSession, connect } from './chunk-J5MFXJU6.js';
6
+ export { RESERVED_COLLECTION_PREFIX, checkPasswordPolicy, exportSnapshot, importSnapshot, inspect, isEncrypted } from './chunk-MGDXR3RB.js';
7
7
  import './chunk-QBCU7OOP.js';
8
- import './chunk-CM3HSBCE.js';
9
- export { AuthExpiredError, SelfstoreError, errorLabelKey, isAuthExpired, isSelfstoreError } from './chunk-SFF7KKM4.js';
8
+ import './chunk-O3Y6FORN.js';
9
+ export { AuthExpiredError, SelfstoreError, errorLabelKey, isAuthExpired, isSelfstoreError } from './chunk-3OBJOMQ2.js';
10
10
 
11
11
  // src/persistence/installation.ts
12
12
  function isSafari(ua) {
@@ -36,8 +36,8 @@ function storageAdvice() {
36
36
 
37
37
  // src/entries/advanced.ts
38
38
  var fileTarget = {
39
- connect: connect2,
40
- fromSession: fromSession2,
39
+ connect: connect$1,
40
+ fromSession: fromSession$1,
41
41
  isSupported,
42
42
  isOpenSupported,
43
43
  /** Let the user point at an EXISTING backup file and adopt it as the
@@ -46,13 +46,13 @@ var fileTarget = {
46
46
  openExisting
47
47
  };
48
48
  var webdavTarget = {
49
- connect: connect3,
50
- fromSession: fromSession3,
49
+ connect: connect2,
50
+ fromSession: fromSession2,
51
51
  peer
52
52
  };
53
53
  var s3Target = {
54
- connect: connect4,
55
- fromSession: fromSession4
54
+ connect: connect3,
55
+ fromSession: fromSession3
56
56
  };
57
57
  var driveTarget = {
58
58
  connect,
@@ -64,6 +64,7 @@ var driveTarget = {
64
64
  createBackup,
65
65
  deleteBackup,
66
66
  renameBackup,
67
+ account,
67
68
  FILE_ID_KEY
68
69
  };
69
70
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/persistence/installation.ts","../src/entries/advanced.ts"],"names":["connect","fromSession"],"mappings":";;;;;;;;;;;AAuCA,SAAS,SAAS,EAAA,EAAqB;AACtC,EAAA,OAAO,UAAA,CAAW,IAAA,CAAK,EAAE,CAAA,IAAK,CAAC,uBAAA,CAAwB,IAAA,CAAK,EAAE,CAAA,IAAK,CAAC,eAAA,CAAgB,IAAA,CAAK,EAAE,CAAA;AAC5F;AAEA,SAAS,YAAA,GAAwB;AAChC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,KAAA;AAC1C,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,UAAA,GAAa,4BAA4B,GAAG,OAAA,KAAY,IAAA;AAEnF,EAAA,MAAM,GAAA,GAAO,UAAmD,UAAA,KAAe,IAAA;AAC/E,EAAA,OAAO,WAAA,IAAe,GAAA;AACvB;AAOO,SAAS,aAAA,GAA+B;AAC9C,EAAA,IAAI,OAAO,SAAA,KAAc,WAAA,IAAe,OAAO,WAAW,WAAA,EAAa;AACtE,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,SAAA,EAAW,KAAA,EAAM;AAAA,EACzC;AACA,EAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,EAAA,IAAI,CAAC,QAAA,CAAS,SAAA,CAAU,SAAS,KAAK,SAAA,EAAW;AAChD,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,SAAA,EAAU;AAAA,EAClC;AACA,EAAA,MAAM,QAAQ,kBAAA,CAAmB,IAAA,CAAK,UAAU,SAAS,CAAA,IAAK,UAAU,cAAA,GAAiB,CAAA;AACzF,EAAA,OAAO;AAAA,IACN,IAAA,EAAM,mBAAA;AAAA,IACN,MAAA,EAAQ,QAAQ,wBAAA,GAA2B,iBAAA;AAAA,IAC3C,SAAA,EAAW;AAAA,GACZ;AACD;;;AC6DO,IAAM,UAAA,GAAa;AAAA,EACzB,OAAA,EAASA,QAAAA;AAAA,EACT,WAAA,EAAaC,YAAAA;AAAA,EACb,WAAA;AAAA,EACA,eAAA;AAAA;AAAA;AAAA;AAAA,EAIA;AACD;AAKO,IAAM,YAAA,GAAe;AAAA,EAC3B,OAAA,EAASD,QAAAA;AAAA,EACT,WAAA,EAAaC,YAAAA;AAAA,EACb;AACD;AAMO,IAAM,QAAA,GAAW;AAAA,EACvB,OAAA,EAASD,QAAAA;AAAA,EACT,WAAA,EAAaC;AACd;AAOO,IAAM,WAAA,GAAc;AAAA,EAC1B,OAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,mBAAA;AAAA,EACA,WAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA;AACD","file":"advanced.js","sourcesContent":["/**\n * Whether this browser will keep the local store, and what the user can do\n * about it.\n *\n * One browser does not merely inconvenience a local-first app, it DELETES its\n * data: Safari erases script-writable storage after seven days of browser use\n * without interaction with the site. Someone who closes for three weeks comes\n * back to an empty app, and nothing warned them - which is also the moment they\n * notice it last.\n *\n * Adding the app to the Dock or the Home Screen switches its storage to\n * persistent mode, which WebKit exempts from its eviction triggers. WebKit does\n * not write anywhere that this neutralises the inactivity rule by name, so this\n * module reports an advisory, never a guarantee.\n *\n * The library ships no user-facing copy: it names the situation, the host writes\n * the sentence.\n */\n\n/** What stands between this browser and keeping the data. */\nexport type StorageRisk =\n\t/** Nothing known against it: the store survives until the user clears it. */\n\t| 'none'\n\t/** Data is erased when the tab closes (private browsing). Refuse to set up here. */\n\t| 'ephemeral'\n\t/** Erased after a stretch of not opening the app, unless it is installed. */\n\t| 'evicted-when-idle';\n\nexport interface StorageAdvice {\n\trisk: StorageRisk;\n\t/** The gesture that removes the risk, when there is one. Stable key; the\n\t * host owns the wording and the platform-specific instructions. */\n\tremedy?: 'install-to-dock' | 'install-to-home-screen';\n\t/** Already running as an installed app, so the remedy is done. */\n\tinstalled: boolean;\n}\n\n/** Real Safari, not Chrome or Edge on macOS - all three carry \"Safari\" in the\n * user agent, and only Safari carries none of the others' tokens. */\nfunction isSafari(ua: string): boolean {\n\treturn /Safari\\//.test(ua) && !/Chrome|Chromium|Edg\\//.test(ua) && !/OPR\\/|Android/.test(ua);\n}\n\nfunction isStandalone(): boolean {\n\tif (typeof window === 'undefined') return false;\n\tconst displayMode = window.matchMedia?.('(display-mode: standalone)')?.matches === true;\n\t// iOS and iPadOS kept their own flag, older than display-mode.\n\tconst ios = (navigator as Navigator & { standalone?: boolean }).standalone === true;\n\treturn displayMode || ios;\n}\n\n/**\n * What this browser will do with the local store, and the one gesture that\n * changes it. Call it before setting up, and again in whatever screen owns the\n * data: the advice disappears on its own once the remedy is applied.\n */\nexport function storageAdvice(): StorageAdvice {\n\tif (typeof navigator === 'undefined' || typeof window === 'undefined') {\n\t\treturn { risk: 'none', installed: false };\n\t}\n\tconst installed = isStandalone();\n\tif (!isSafari(navigator.userAgent) || installed) {\n\t\treturn { risk: 'none', installed };\n\t}\n\tconst touch = /iPhone|iPad|iPod/.test(navigator.userAgent) || navigator.maxTouchPoints > 1;\n\treturn {\n\t\trisk: 'evicted-when-idle',\n\t\tremedy: touch ? 'install-to-home-screen' : 'install-to-dock',\n\t\tinstalled: false\n\t};\n}\n","/**\n * selfstore/advanced - the pull-model store and the machinery under it.\n *\n * The simple `selfstore()` facade (package root) owns its data. This entry\n * inverts that: with createLocalStore your app owns the data and hands the\n * store a gather/apply pair - use it when state lives in your own reactive\n * model (Svelte runes, Redux, signals) and the facade's collections would be\n * a second copy. Everything here also backs `store.advanced` on the simple\n * store: same machinery, same instance, so the two styles compose.\n *\n * Also here: the durable destinations (disk file, Google Drive, WebDAV) as\n * connectable targets, the BackupTarget contract for writing your own, the\n * caches, the headless status derivation, and the functional codec for the\n * backup file format (the fluent one lives at the package root).\n */\n\n// --- The pull-model store: your app owns the data ---\nexport {\n\tcreateLocalStore,\n\ttype LocalStore,\n\ttype LocalStoreOptions,\n\ttype LocalStoreState,\n\ttype StoreError,\n\ttype Mode,\n\ttype TargetKind,\n\ttype SyncSource,\n\ttype SyncJournalEntry,\n\ttype PeerState,\n\ttype MirrorState,\n\ttype ReplicaState,\n\ttype StoreGroupConfig\n} from '../persistence/store';\n\n// --- On-device caches (the store's working copy) ---\nexport {\n\tindexedDbCache,\n\tmemoryCache,\n\tisLockableCache,\n\ttype LocalCache,\n\ttype LockableCache,\n\ttype CachedFile,\n\ttype KV\n} from '../persistence/cache';\n\n// --- Headless status: map store state to your UI copy ---\nexport {\n\tderiveStatus,\n\ttype StatusDescriptor,\n\ttype StatusInput,\n\ttype Severity,\n\ttype StorageState,\n\ttype StatusAction\n} from '../persistence/status';\n\n// --- Will this browser keep the data, and what can the user do about it ---\nexport { storageAdvice, type StorageAdvice, type StorageRisk } from '../persistence/installation';\n\n// --- The target contract (write your own destination) ---\nexport {\n\tRESERVED_STORE_MODES,\n\tisReservedStoreMode,\n\ttype BackupTarget,\n\ttype BuiltinTargetKind,\n\ttype PeerSource\n} from '../persistence/target';\n\n// --- The backup file format, functional style (fluent lives at the root) ---\nexport {\n\texportSnapshot,\n\timportSnapshot,\n\tinspect,\n\tisEncrypted,\n\tRESERVED_COLLECTION_PREFIX,\n\ttype Snapshot,\n\ttype SnapshotFile,\n\ttype Header,\n\ttype KdfParams,\n\ttype RecipientStanza,\n\ttype EncodeOptions\n} from '../selfstore';\n\n// --- Password strength policy (the store enforces it; the UI previews it) ---\nexport {\n\tcheckPasswordPolicy,\n\ttype PasswordPolicy,\n\ttype PasswordCheck,\n\ttype PasswordRequirement\n} from '../selfstore';\n\n// --- The full error contract (AuthExpiredError is what a custom target or\n// DriveAuth THROWS to signal genuine access loss; everything else it\n// throws is treated as transient and retried) ---\nexport {\n\tSelfstoreError,\n\tAuthExpiredError,\n\tisSelfstoreError,\n\tisAuthExpired,\n\terrorLabelKey,\n\ttype SelfstoreErrorCode\n} from '../selfstore';\n\n// --- Durable destinations (opt-in), each implementing BackupTarget ---\n// Explicit namespace objects: every member is deliberately public, so a future\n// internal helper in a target module can never leak into the contract.\nimport {\n\tconnect as fileConnect,\n\tfromSession as fileFromSession,\n\tisSupported as fileIsSupported,\n\tisOpenSupported as fileIsOpenSupported,\n\topenExisting as fileOpenExisting\n} from '../persistence/targets/file';\nimport {\n\tconnect as webdavConnect,\n\tfromSession as webdavFromSession,\n\tpeer as webdavPeer\n} from '../persistence/targets/webdav';\nimport { connect as s3Connect, fromSession as s3FromSession } from '../persistence/targets/s3';\nimport {\n\tconnect as driveConnect,\n\tfromSession as driveFromSession,\n\tpreview as drivePreview,\n\tadopt as driveAdopt,\n\tfindOrCreateOwnFile as driveFindOrCreateOwnFile,\n\tlistBackups as driveListBackups,\n\tcreateBackup as driveCreateBackup,\n\tdeleteBackup as driveDeleteBackup,\n\trenameBackup as driveRenameBackup,\n\tFILE_ID_KEY as DRIVE_FILE_ID_KEY\n} from '../persistence/targets/drive';\n\n/** Disk-file destination (File System Access, Chromium). */\nexport const fileTarget = {\n\tconnect: fileConnect,\n\tfromSession: fileFromSession,\n\tisSupported: fileIsSupported,\n\tisOpenSupported: fileIsOpenSupported,\n\t/** Let the user point at an EXISTING backup file and adopt it as the\n\t * destination. Gate on isOpenSupported(): unlike creating, this has no\n\t * download fallback. */\n\topenExisting: fileOpenExisting\n};\n\n/** WebDAV destination (Nextcloud, ownCloud, any server the user controls), plus\n * `peer()`: a read-only source over another member's shared WebDAV link, so a\n * group can share read-write without Google Drive (see store.attachPeer). */\nexport const webdavTarget = {\n\tconnect: webdavConnect,\n\tfromSession: webdavFromSession,\n\tpeer: webdavPeer\n};\n\n/** S3-compatible destination (Amazon S3, Cloudflare R2, Backblaze B2, MinIO):\n * the browser signs each request with SigV4 and writes one object on a bucket\n * the user controls, no vendor broker. The bucket only ever holds the same\n * end-to-end-encrypted .selfstore bytes. */\nexport const s3Target = {\n\tconnect: s3Connect,\n\tfromSession: s3FromSession\n};\n\n/** Google Drive destination, plus the shared-file primitives (preview a specific\n * file, adopt it as this device's backup, find-or-create the user's own file)\n * and the backup-management ops (list the app's files with dates, create a\n * named empty backup, rename one, delete one for good) - the raw pieces a \"my\n * backups\" panel builds on. */\nexport const driveTarget = {\n\tconnect: driveConnect,\n\tfromSession: driveFromSession,\n\tpreview: drivePreview,\n\tadopt: driveAdopt,\n\tfindOrCreateOwnFile: driveFindOrCreateOwnFile,\n\tlistBackups: driveListBackups,\n\tcreateBackup: driveCreateBackup,\n\tdeleteBackup: driveDeleteBackup,\n\trenameBackup: driveRenameBackup,\n\tFILE_ID_KEY: DRIVE_FILE_ID_KEY\n};\n\nexport type { FileConnectOptions } from '../persistence/targets/file';\nexport type { DriveAuth, DriveOptions, DriveBackupInfo } from '../persistence/targets/drive';\nexport type {\n\tWebdavConfig,\n\tWebdavConnectOptions,\n\tWebdavPeerOptions\n} from '../persistence/targets/webdav';\nexport type { S3Config, S3ConnectOptions } from '../persistence/targets/s3';\nexport { gisDriveAuth } from '../persistence/targets/drive-auth-gis';\nexport { saveToDisk, pickFromDisk, datedName } from '../selfstore/targets/local';\n"]}
1
+ {"version":3,"sources":["../src/persistence/installation.ts","../src/entries/advanced.ts"],"names":["connect","fromSession"],"mappings":";;;;;;;;;;;AAuCA,SAAS,SAAS,EAAA,EAAqB;AACtC,EAAA,OAAO,UAAA,CAAW,IAAA,CAAK,EAAE,CAAA,IAAK,CAAC,uBAAA,CAAwB,IAAA,CAAK,EAAE,CAAA,IAAK,CAAC,eAAA,CAAgB,IAAA,CAAK,EAAE,CAAA;AAC5F;AAEA,SAAS,YAAA,GAAwB;AAChC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,KAAA;AAC1C,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,UAAA,GAAa,4BAA4B,GAAG,OAAA,KAAY,IAAA;AAEnF,EAAA,MAAM,GAAA,GAAO,UAAmD,UAAA,KAAe,IAAA;AAC/E,EAAA,OAAO,WAAA,IAAe,GAAA;AACvB;AAOO,SAAS,aAAA,GAA+B;AAC9C,EAAA,IAAI,OAAO,SAAA,KAAc,WAAA,IAAe,OAAO,WAAW,WAAA,EAAa;AACtE,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,SAAA,EAAW,KAAA,EAAM;AAAA,EACzC;AACA,EAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,EAAA,IAAI,CAAC,QAAA,CAAS,SAAA,CAAU,SAAS,KAAK,SAAA,EAAW;AAChD,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,SAAA,EAAU;AAAA,EAClC;AACA,EAAA,MAAM,QAAQ,kBAAA,CAAmB,IAAA,CAAK,UAAU,SAAS,CAAA,IAAK,UAAU,cAAA,GAAiB,CAAA;AACzF,EAAA,OAAO;AAAA,IACN,IAAA,EAAM,mBAAA;AAAA,IACN,MAAA,EAAQ,QAAQ,wBAAA,GAA2B,iBAAA;AAAA,IAC3C,SAAA,EAAW;AAAA,GACZ;AACD;;;AC8DO,IAAM,UAAA,GAAa;AAAA,EACzB,OAAA,EAASA,SAAAA;AAAA,EACT,WAAA,EAAaC,aAAAA;AAAA,EACb,WAAA;AAAA,EACA,eAAA;AAAA;AAAA;AAAA;AAAA,EAIA;AACD;AAKO,IAAM,YAAA,GAAe;AAAA,EAC3B,OAAA,EAASD,QAAAA;AAAA,EACT,WAAA,EAAaC,YAAAA;AAAA,EACb;AACD;AAMO,IAAM,QAAA,GAAW;AAAA,EACvB,OAAA,EAASD,QAAAA;AAAA,EACT,WAAA,EAAaC;AACd;AAQO,IAAM,WAAA,GAAc;AAAA,EAC1B,OAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA;AAAA,EACA,KAAA;AAAA,EACA,mBAAA;AAAA,EACA,WAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA,OAAA;AAAA,EACA;AACD","file":"advanced.js","sourcesContent":["/**\n * Whether this browser will keep the local store, and what the user can do\n * about it.\n *\n * One browser does not merely inconvenience a local-first app, it DELETES its\n * data: Safari erases script-writable storage after seven days of browser use\n * without interaction with the site. Someone who closes for three weeks comes\n * back to an empty app, and nothing warned them - which is also the moment they\n * notice it last.\n *\n * Adding the app to the Dock or the Home Screen switches its storage to\n * persistent mode, which WebKit exempts from its eviction triggers. WebKit does\n * not write anywhere that this neutralises the inactivity rule by name, so this\n * module reports an advisory, never a guarantee.\n *\n * The library ships no user-facing copy: it names the situation, the host writes\n * the sentence.\n */\n\n/** What stands between this browser and keeping the data. */\nexport type StorageRisk =\n\t/** Nothing known against it: the store survives until the user clears it. */\n\t| 'none'\n\t/** Data is erased when the tab closes (private browsing). Refuse to set up here. */\n\t| 'ephemeral'\n\t/** Erased after a stretch of not opening the app, unless it is installed. */\n\t| 'evicted-when-idle';\n\nexport interface StorageAdvice {\n\trisk: StorageRisk;\n\t/** The gesture that removes the risk, when there is one. Stable key; the\n\t * host owns the wording and the platform-specific instructions. */\n\tremedy?: 'install-to-dock' | 'install-to-home-screen';\n\t/** Already running as an installed app, so the remedy is done. */\n\tinstalled: boolean;\n}\n\n/** Real Safari, not Chrome or Edge on macOS - all three carry \"Safari\" in the\n * user agent, and only Safari carries none of the others' tokens. */\nfunction isSafari(ua: string): boolean {\n\treturn /Safari\\//.test(ua) && !/Chrome|Chromium|Edg\\//.test(ua) && !/OPR\\/|Android/.test(ua);\n}\n\nfunction isStandalone(): boolean {\n\tif (typeof window === 'undefined') return false;\n\tconst displayMode = window.matchMedia?.('(display-mode: standalone)')?.matches === true;\n\t// iOS and iPadOS kept their own flag, older than display-mode.\n\tconst ios = (navigator as Navigator & { standalone?: boolean }).standalone === true;\n\treturn displayMode || ios;\n}\n\n/**\n * What this browser will do with the local store, and the one gesture that\n * changes it. Call it before setting up, and again in whatever screen owns the\n * data: the advice disappears on its own once the remedy is applied.\n */\nexport function storageAdvice(): StorageAdvice {\n\tif (typeof navigator === 'undefined' || typeof window === 'undefined') {\n\t\treturn { risk: 'none', installed: false };\n\t}\n\tconst installed = isStandalone();\n\tif (!isSafari(navigator.userAgent) || installed) {\n\t\treturn { risk: 'none', installed };\n\t}\n\tconst touch = /iPhone|iPad|iPod/.test(navigator.userAgent) || navigator.maxTouchPoints > 1;\n\treturn {\n\t\trisk: 'evicted-when-idle',\n\t\tremedy: touch ? 'install-to-home-screen' : 'install-to-dock',\n\t\tinstalled: false\n\t};\n}\n","/**\n * selfstore/advanced - the pull-model store and the machinery under it.\n *\n * The simple `selfstore()` facade (package root) owns its data. This entry\n * inverts that: with createLocalStore your app owns the data and hands the\n * store a gather/apply pair - use it when state lives in your own reactive\n * model (Svelte runes, Redux, signals) and the facade's collections would be\n * a second copy. Everything here also backs `store.advanced` on the simple\n * store: same machinery, same instance, so the two styles compose.\n *\n * Also here: the durable destinations (disk file, Google Drive, WebDAV) as\n * connectable targets, the BackupTarget contract for writing your own, the\n * caches, the headless status derivation, and the functional codec for the\n * backup file format (the fluent one lives at the package root).\n */\n\n// --- The pull-model store: your app owns the data ---\nexport {\n\tcreateLocalStore,\n\ttype LocalStore,\n\ttype LocalStoreOptions,\n\ttype LocalStoreState,\n\ttype StoreError,\n\ttype Mode,\n\ttype TargetKind,\n\ttype SyncSource,\n\ttype SyncJournalEntry,\n\ttype PeerState,\n\ttype MirrorState,\n\ttype ReplicaState,\n\ttype StoreGroupConfig\n} from '../persistence/store';\n\n// --- On-device caches (the store's working copy) ---\nexport {\n\tindexedDbCache,\n\tmemoryCache,\n\tisLockableCache,\n\ttype LocalCache,\n\ttype LockableCache,\n\ttype CachedFile,\n\ttype KV\n} from '../persistence/cache';\n\n// --- Headless status: map store state to your UI copy ---\nexport {\n\tderiveStatus,\n\ttype StatusDescriptor,\n\ttype StatusInput,\n\ttype Severity,\n\ttype StorageState,\n\ttype StatusAction\n} from '../persistence/status';\n\n// --- Will this browser keep the data, and what can the user do about it ---\nexport { storageAdvice, type StorageAdvice, type StorageRisk } from '../persistence/installation';\n\n// --- The target contract (write your own destination) ---\nexport {\n\tRESERVED_STORE_MODES,\n\tisReservedStoreMode,\n\ttype BackupTarget,\n\ttype BuiltinTargetKind,\n\ttype PeerSource\n} from '../persistence/target';\n\n// --- The backup file format, functional style (fluent lives at the root) ---\nexport {\n\texportSnapshot,\n\timportSnapshot,\n\tinspect,\n\tisEncrypted,\n\tRESERVED_COLLECTION_PREFIX,\n\ttype Snapshot,\n\ttype SnapshotFile,\n\ttype Header,\n\ttype KdfParams,\n\ttype RecipientStanza,\n\ttype EncodeOptions\n} from '../selfstore';\n\n// --- Password strength policy (the store enforces it; the UI previews it) ---\nexport {\n\tcheckPasswordPolicy,\n\ttype PasswordPolicy,\n\ttype PasswordCheck,\n\ttype PasswordRequirement\n} from '../selfstore';\n\n// --- The full error contract (AuthExpiredError is what a custom target or\n// DriveAuth THROWS to signal genuine access loss; everything else it\n// throws is treated as transient and retried) ---\nexport {\n\tSelfstoreError,\n\tAuthExpiredError,\n\tisSelfstoreError,\n\tisAuthExpired,\n\terrorLabelKey,\n\ttype SelfstoreErrorCode\n} from '../selfstore';\n\n// --- Durable destinations (opt-in), each implementing BackupTarget ---\n// Explicit namespace objects: every member is deliberately public, so a future\n// internal helper in a target module can never leak into the contract.\nimport {\n\tconnect as fileConnect,\n\tfromSession as fileFromSession,\n\tisSupported as fileIsSupported,\n\tisOpenSupported as fileIsOpenSupported,\n\topenExisting as fileOpenExisting\n} from '../persistence/targets/file';\nimport {\n\tconnect as webdavConnect,\n\tfromSession as webdavFromSession,\n\tpeer as webdavPeer\n} from '../persistence/targets/webdav';\nimport { connect as s3Connect, fromSession as s3FromSession } from '../persistence/targets/s3';\nimport {\n\tconnect as driveConnect,\n\tfromSession as driveFromSession,\n\tpreview as drivePreview,\n\tadopt as driveAdopt,\n\tfindOrCreateOwnFile as driveFindOrCreateOwnFile,\n\tlistBackups as driveListBackups,\n\tcreateBackup as driveCreateBackup,\n\tdeleteBackup as driveDeleteBackup,\n\trenameBackup as driveRenameBackup,\n\taccount as driveAccount,\n\tFILE_ID_KEY as DRIVE_FILE_ID_KEY\n} from '../persistence/targets/drive';\n\n/** Disk-file destination (File System Access, Chromium). */\nexport const fileTarget = {\n\tconnect: fileConnect,\n\tfromSession: fileFromSession,\n\tisSupported: fileIsSupported,\n\tisOpenSupported: fileIsOpenSupported,\n\t/** Let the user point at an EXISTING backup file and adopt it as the\n\t * destination. Gate on isOpenSupported(): unlike creating, this has no\n\t * download fallback. */\n\topenExisting: fileOpenExisting\n};\n\n/** WebDAV destination (Nextcloud, ownCloud, any server the user controls), plus\n * `peer()`: a read-only source over another member's shared WebDAV link, so a\n * group can share read-write without Google Drive (see store.attachPeer). */\nexport const webdavTarget = {\n\tconnect: webdavConnect,\n\tfromSession: webdavFromSession,\n\tpeer: webdavPeer\n};\n\n/** S3-compatible destination (Amazon S3, Cloudflare R2, Backblaze B2, MinIO):\n * the browser signs each request with SigV4 and writes one object on a bucket\n * the user controls, no vendor broker. The bucket only ever holds the same\n * end-to-end-encrypted .selfstore bytes. */\nexport const s3Target = {\n\tconnect: s3Connect,\n\tfromSession: s3FromSession\n};\n\n/** Google Drive destination, plus the shared-file primitives (preview a specific\n * file, adopt it as this device's backup, find-or-create the user's own file)\n * and the backup-management ops (list the app's files with dates, create a\n * named empty backup, rename one, delete one for good) - the raw pieces a \"my\n * backups\" panel builds on. `account()` names WHICH Google account holds them,\n * because a brand name is not an address. */\nexport const driveTarget = {\n\tconnect: driveConnect,\n\tfromSession: driveFromSession,\n\tpreview: drivePreview,\n\tadopt: driveAdopt,\n\tfindOrCreateOwnFile: driveFindOrCreateOwnFile,\n\tlistBackups: driveListBackups,\n\tcreateBackup: driveCreateBackup,\n\tdeleteBackup: driveDeleteBackup,\n\trenameBackup: driveRenameBackup,\n\taccount: driveAccount,\n\tFILE_ID_KEY: DRIVE_FILE_ID_KEY\n};\n\nexport type { FileConnectOptions } from '../persistence/targets/file';\nexport type {\n\tDriveAuth,\n\tDriveOptions,\n\tDriveBackupInfo,\n\tDriveAccount\n} from '../persistence/targets/drive';\nexport type {\n\tWebdavConfig,\n\tWebdavConnectOptions,\n\tWebdavPeerOptions\n} from '../persistence/targets/webdav';\nexport type { S3Config, S3ConnectOptions } from '../persistence/targets/s3';\nexport { gisDriveAuth } from '../persistence/targets/drive-auth-gis';\nexport { saveToDisk, pickFromDisk, datedName } from '../selfstore/targets/local';\n"]}
package/dist/backups.d.ts CHANGED
@@ -1,182 +1,34 @@
1
- import { K as KV } from './cache-ilsK3Tki.js';
2
- import { B as BackupTarget, L as LocalStore } from './store-CbGrlMME.js';
3
- import { F as FlowStore } from './machine-ALMkT3mk.js';
1
+ import { B as BackupsHost } from './manager-DZ1c_aSS.js';
2
+ export { a as BackupFileInfo, b as BackupOwner, c as BackupRow, d as BackupsErrorCode, e as BackupsKeys, f as BackupsManager, g as BackupsNaming, h as BackupsSnapshot, K as KnownBackups, i as createBackupsManager } from './manager-DZ1c_aSS.js';
3
+ import { D as DriveOptions } from './drive-CTYvGg_7.js';
4
+ import './cache-ilsK3Tki.js';
5
+ import './store-BiQ-BuVl.js';
4
6
  import './sync.js';
7
+ import './machine-ALMkT3mk.js';
5
8
 
6
- /** What the destination reports about a listed file (one cheap metadata call). */
7
- interface BackupFileInfo {
8
- id: string;
9
- name: string;
10
- /** RFC 3339 last-write timestamp, when the destination reports one. */
11
- modifiedTime: string | null;
12
- }
13
- /** The destination I/O the manager drives - the host wires its own adapter
14
- * (e.g. over the Drive target ops). Every method may throw; the manager turns
15
- * failures into stable error codes. */
16
- interface BackupsHost {
17
- /** The target kind the managed files live on (e.g. 'drive'): the manager
18
- * only stamps an active file while the store is attached to this kind. */
19
- kind: string;
20
- /** The kv key the attached file id is persisted under (the same one the
21
- * target's connect path reads first). */
22
- activeIdKey: string;
23
- /** List candidate backup files on the account. The manager applies the
24
- * naming rules on what comes back; internal files simply do not match. */
25
- list(): Promise<BackupFileInfo[]>;
26
- /** A target over a FIXED file id - never re-resolved by name. The fixed id
27
- * is what keeps two backups from silently merging mid-switch. */
28
- open(fileId: string): BackupTarget;
29
- /** Create an empty file under this name and return its id. Refuse a name
30
- * the account already carries (a duplicate poisons find-by-name). */
31
- create(fileName: string): Promise<{
32
- fileId: string;
33
- }>;
34
- /** Delete a file for good. */
35
- remove(fileId: string): Promise<void>;
36
- /** Rename a file in place (metadata only: the id must not change). */
37
- rename?(fileId: string, fileName: string): Promise<void>;
38
- /** Find (or create) the PERSONAL file by its canonical name, ignoring any
39
- * currently adopted id. Null when the user backs out of the session. */
40
- findOrCreatePersonal(): Promise<{
41
- fileId: string;
42
- created: boolean;
43
- } | null>;
44
- /** Make sure a usable session exists before a remote call (re-consent when
45
- * it lapsed). False = the user refused; the gesture aborts as 'cancelled'.
46
- * Absent = sessions are the target's own business. */
47
- ensureSession?(): Promise<boolean>;
48
- /** Resolve the owner label of a file this account does not own, for the
49
- * shared registry ("shared by X"). Best-effort. */
50
- fileOwner?(fileId: string): Promise<{
51
- email: string | null;
52
- name: string | null;
53
- } | null>;
54
- /** File ids that must never enter the registry even though the store may
55
- * attach them (e.g. a sharing engine's published copy file). */
56
- excludedFileIds?(): Promise<(string | null | undefined)[]>;
57
- }
58
- /** How backup files are named on the destination. */
59
- interface BackupsNaming {
60
- /** The personal, unnamed file ("App.zip"). Its label reads as null. */
61
- canonicalName: string;
62
- /** label -> file name. Default: "Stem (label).ext" derived from
63
- * canonicalName ("App.zip" + "family" -> "App (family).zip"). */
64
- namedFileFor?(label: string): string;
65
- /** file name -> parsed label; null = the personal file; undefined = not one
66
- * of ours (dropped from listings). Default matches the default namedFileFor. */
67
- parseLabel?(name: string): string | null | undefined;
68
- }
69
- /** The kv keys the manager persists under - override to keep a pre-existing
70
- * install's records. */
71
- interface BackupsKeys {
72
- /** The known-backups registry. */
73
- registry?: string;
74
- /** fileId -> encrypted? map. */
75
- encrypted?: string;
76
- /** fileId -> shared? map. */
77
- shared?: string;
78
- /** Set while the ACTIVE file is not the personal one (another account's). */
79
- joined?: string;
80
- /** Cached owner label of that file. */
81
- owner?: string;
82
- }
83
- /** Every backup this device has connected to: its own personal file and each
84
- * OTHER account's file it was let into. Isolated snapshots, all of them. */
85
- interface KnownBackups {
86
- personalFileId: string | null;
87
- shared: {
88
- fileId: string;
89
- ownerEmail: string | null;
90
- ownerName: string | null;
91
- }[];
92
- }
93
- /** One row of the "my backups" list: a file this account owns. */
94
- interface BackupRow {
95
- fileId: string;
96
- name: string;
97
- /** Parsed display label; null for the personal, unnamed file. */
98
- label: string | null;
99
- /** Last-write time, epoch ms (null when the destination did not say). */
100
- modifiedAt: number | null;
101
- /** true/false once known (opened, created or probed); null until then. */
102
- encrypted: boolean | null;
103
- /** true/false once learned (while this file was the active one); null
104
- * until then. A share link existing is the shared state. */
105
- shared: boolean | null;
106
- }
107
- /** The owner label of a joined (another account's) file. */
108
- interface BackupOwner {
109
- email: string | null;
110
- name: string | null;
111
- }
112
- /** Stable failure codes; the host maps them to its own copy. */
113
- type BackupsErrorCode = 'cancelled' | 'gone' | 'failed';
114
- interface BackupsSnapshot {
115
- registry: KnownBackups;
116
- /** The file currently attached, when the store is on the managed kind. */
117
- activeFileId: string | null;
118
- /** The active file is not the personal one (someone else's). */
119
- joined: boolean;
120
- /** Who owns the joined file, when known. */
121
- owner: BackupOwner | null;
122
- /** The last gesture's failure, or null. Reset at each gesture start. */
123
- lastError: BackupsErrorCode | null;
124
- }
125
- interface BackupsManager extends FlowStore<BackupsSnapshot> {
126
- /** Boot: load registry + joined/owner from kv and stamp the active file.
127
- * Kv-only, no destination call. */
128
- hydrate(): Promise<void>;
129
- /** Re-read the registry and restamp the active file. Kv-only, cheap. */
130
- refresh(): Promise<void>;
131
- /** After an attach the manager did not make (a connect widget, a join):
132
- * record the now-active file in the registry and restamp joined/owner. */
133
- markActive(fileId: string): Promise<void>;
134
- /** Live list of this account's own files, per the naming rules, decorated
135
- * with the learned memories. One destination listing call. */
136
- list(): Promise<BackupRow[]>;
137
- /** Learn a listed file's encrypted bit without opening it for real: one
138
- * download + header read, remembered for good. Null when unreachable. */
139
- probeEncryption(fileId: string): Promise<boolean | null>;
140
- /** Record the ACTIVE file's live share state, keyed by file id, so its row
141
- * stays truthful after a switch away. */
142
- noteShared(fileId: string, shared: boolean): Promise<void>;
143
- /** Open a known backup: an isolated snapshot replaces the local data.
144
- * 'encrypted' = collect its passphrase and call again with it. A file the
145
- * destination reports GONE is pruned from the registry (lastError 'gone'). */
146
- openBackup(fileId: string, passphrase?: string | null): Promise<'ok' | 'encrypted' | 'failed'>;
147
- /** Open (or create blank) the personal file. */
148
- openPersonal(passphrase?: string | null): Promise<'ok' | 'encrypted' | 'failed'>;
149
- /** Create a brand-new empty named backup and open it (starts blank - never
150
- * seeded from the currently loaded file). */
151
- createNamed(label: string): Promise<'ok' | 'failed'>;
152
- /** Record a file in the SHARED registry under the sharer's label, without
153
- * opening it. For a joined portfolio's dedicated wallet: the file may live
154
- * on this very account, yet it presents as "shared by X", never as mine. */
155
- registerShared(fileId: string, owner: BackupOwner): Promise<void>;
156
- /** Create a joined portfolio's dedicated wallet: a file under the given
157
- * name (outside the own-files naming on purpose), attached BLANK - never
158
- * seeded from the previously loaded silo - and recorded in the shared
159
- * registry under the sharer's label. The host stages the shared content
160
- * afterwards (restore the projection, then save). */
161
- createShared(fileName: string, owner: BackupOwner): Promise<'ok' | 'failed'>;
162
- /** Rename a file in place (id unchanged, so renaming the ACTIVE file never
163
- * detaches it). The host target refuses a name already carried. */
164
- renameBackup(fileId: string, label: string): Promise<'ok' | 'failed'>;
165
- /** Delete a file for good. Deleting the ACTIVE one detaches first (the app
166
- * goes device-only, local data stays), so nothing writes mid-delete. */
167
- deleteBackup(fileId: string): Promise<boolean>;
168
- /** Drop a KNOWN shared (another account's) file from this device's
169
- * registry. Forgetting only - the owner's file is never touched. */
170
- forgetShared(fileId: string): Promise<void>;
171
- /** The file name a label produces (the create form's preview line). */
172
- fileNameFor(label: string): string;
173
- }
174
- declare function createBackupsManager(deps: {
175
- store: LocalStore;
176
- kv: KV;
177
- host: BackupsHost;
178
- naming: BackupsNaming;
179
- keys?: BackupsKeys;
180
- }): BackupsManager;
9
+ interface DriveBackupsHostOptions extends DriveOptions {
10
+ /** Narrow the listing server-side to names containing this fragment (the
11
+ * app's own stem, typically). The manager still applies its naming rules on
12
+ * what comes back, so this is a bandwidth hint and never a filter to rely
13
+ * on for correctness. Default: the whole visible listing. */
14
+ nameContains?: string;
15
+ }
16
+ /**
17
+ * The Drive destination as a `BackupsHost`, ready for `createBackupsManager`
18
+ * (and therefore for `<selfstore-backups>`).
19
+ *
20
+ * ```ts
21
+ * const manager = createBackupsManager({
22
+ * store: store.engine,
23
+ * kv: store.kv,
24
+ * host: driveBackupsHost({ auth, kv: store.kv, fileName: 'app.zip' }),
25
+ * naming: { canonicalName: 'app.zip' }
26
+ * });
27
+ * ```
28
+ *
29
+ * The `drive.file` scope already narrows what a listing can see to the files
30
+ * this app created or was handed, so nothing here can enumerate a user's Drive.
31
+ */
32
+ declare function driveBackupsHost(opts: DriveBackupsHostOptions): BackupsHost;
181
33
 
182
- export { type BackupFileInfo, type BackupOwner, type BackupRow, type BackupsErrorCode, type BackupsHost, type BackupsKeys, type BackupsManager, type BackupsNaming, type BackupsSnapshot, type KnownBackups, createBackupsManager };
34
+ export { BackupsHost, type DriveBackupsHostOptions, driveBackupsHost };
package/dist/backups.js CHANGED
@@ -1,5 +1,42 @@
1
- import { makeMachine } from './chunk-OIAXYI35.js';
2
- import './chunk-SFF7KKM4.js';
1
+ import { makeMachine } from './chunk-W22V5CRM.js';
2
+ import { FILE_ID_KEY, findOrCreateOwnFile, renameBackup, deleteBackup, createBackup, preview, listBackups } from './chunk-J5MFXJU6.js';
3
+ import './chunk-MGDXR3RB.js';
4
+ import './chunk-O3Y6FORN.js';
5
+ import './chunk-3OBJOMQ2.js';
6
+
7
+ // src/backups/drive-host.ts
8
+ function driveBackupsHost(opts) {
9
+ return {
10
+ kind: "drive",
11
+ activeIdKey: FILE_ID_KEY,
12
+ async list() {
13
+ return listBackups({ auth: opts.auth, nameContains: opts.nameContains });
14
+ },
15
+ open(fileId) {
16
+ return preview(opts, fileId);
17
+ },
18
+ async create(fileName) {
19
+ return createBackup({ auth: opts.auth, fileName });
20
+ },
21
+ async remove(fileId) {
22
+ return deleteBackup({ auth: opts.auth, fileId });
23
+ },
24
+ async rename(fileId, fileName) {
25
+ return renameBackup({ auth: opts.auth, fileId, fileName });
26
+ },
27
+ async findOrCreatePersonal() {
28
+ return findOrCreateOwnFile(opts);
29
+ },
30
+ async ensureSession() {
31
+ try {
32
+ await opts.auth.token();
33
+ return true;
34
+ } catch {
35
+ return opts.auth.reconnect();
36
+ }
37
+ }
38
+ };
39
+ }
3
40
 
4
41
  // src/backups/manager.ts
5
42
  var DEFAULT_KEYS = {
@@ -355,6 +392,6 @@ function createBackupsManager(deps) {
355
392
  };
356
393
  }
357
394
 
358
- export { createBackupsManager };
395
+ export { createBackupsManager, driveBackupsHost };
359
396
  //# sourceMappingURL=backups.js.map
360
397
  //# sourceMappingURL=backups.js.map