selfstore 1.8.2 → 1.8.4

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 (57) hide show
  1. package/README.md +1 -1
  2. package/dist/advanced.d.ts +8 -4
  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-RCN3JJ4Q.js → cache-lock-KWJUORTS.js} +3 -4
  9. package/dist/cache-lock-KWJUORTS.js.map +1 -0
  10. package/dist/{chunk-HK4VPO65.js → chunk-3OBJOMQ2.js} +28 -4
  11. package/dist/chunk-3OBJOMQ2.js.map +1 -0
  12. package/dist/{chunk-HDR75TQ7.js → chunk-DCEXWSM6.js} +3 -3
  13. package/dist/{chunk-HDR75TQ7.js.map → chunk-DCEXWSM6.js.map} +1 -1
  14. package/dist/{chunk-7N2YR3QW.js → chunk-ELDSPUTS.js} +7 -8
  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-BNDFMU3J.js → chunk-MGDXR3RB.js} +5 -6
  19. package/dist/chunk-MGDXR3RB.js.map +1 -0
  20. package/dist/{chunk-453KRCMM.js → chunk-O3Y6FORN.js} +3 -4
  21. package/dist/chunk-O3Y6FORN.js.map +1 -0
  22. package/dist/{chunk-JMJZIZKY.js → chunk-PNX7AJYO.js} +13 -12
  23. package/dist/chunk-PNX7AJYO.js.map +1 -0
  24. package/dist/{chunk-5NS7YQNP.js → chunk-TKWU52LK.js} +26 -312
  25. package/dist/chunk-TKWU52LK.js.map +1 -0
  26. package/dist/{chunk-U5F4GI7C.js → chunk-W22V5CRM.js} +3 -3
  27. package/dist/{chunk-U5F4GI7C.js.map → chunk-W22V5CRM.js.map} +1 -1
  28. package/dist/{drive-l_4-m1eT.d.ts → drive-CTYvGg_7.d.ts} +23 -110
  29. package/dist/{flows-C_uookek.d.ts → flows-1EntUZ91.d.ts} +2 -1
  30. package/dist/flows.d.ts +3 -2
  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/index.d.ts +3 -2
  36. package/dist/index.js +18 -18
  37. package/dist/index.js.map +1 -1
  38. package/dist/{local-bfv4Ld7s.d.ts → local-C52j3DeH.d.ts} +1 -1
  39. package/dist/local-DPS35F5C.js +6 -0
  40. package/dist/{local-2YREU4Q2.js.map → local-DPS35F5C.js.map} +1 -1
  41. package/dist/manager-DZ1c_aSS.d.ts +181 -0
  42. package/dist/s3-DvsnA-FK.d.ts +112 -0
  43. package/dist/widgets.d.ts +90 -7
  44. package/dist/widgets.js +281 -18
  45. package/dist/widgets.js.map +1 -1
  46. package/llms.txt +13 -2
  47. package/package.json +1 -1
  48. package/dist/cache-lock-RCN3JJ4Q.js.map +0 -1
  49. package/dist/chunk-453KRCMM.js.map +0 -1
  50. package/dist/chunk-5NS7YQNP.js.map +0 -1
  51. package/dist/chunk-7N2YR3QW.js.map +0 -1
  52. package/dist/chunk-BNDFMU3J.js.map +0 -1
  53. package/dist/chunk-CPN2KUD5.js +0 -29
  54. package/dist/chunk-CPN2KUD5.js.map +0 -1
  55. package/dist/chunk-HK4VPO65.js.map +0 -1
  56. package/dist/chunk-JMJZIZKY.js.map +0 -1
  57. package/dist/local-2YREU4Q2.js +0 -7
package/dist/widgets.js CHANGED
@@ -1,11 +1,12 @@
1
- import { connectFlow, shareFlow, joinFlow } from './chunk-JMJZIZKY.js';
2
- import { isOpenSupported } from './chunk-5NS7YQNP.js';
3
- import './chunk-BNDFMU3J.js';
4
- import './chunk-U5F4GI7C.js';
5
- import './chunk-453KRCMM.js';
1
+ import { saveToDisk, datedName } from './chunk-DCEXWSM6.js';
2
+ import { connectFlow, shareFlow, joinFlow } from './chunk-PNX7AJYO.js';
3
+ import { isOpenSupported } from './chunk-TKWU52LK.js';
4
+ import './chunk-W22V5CRM.js';
5
+ import './chunk-J5MFXJU6.js';
6
+ import './chunk-MGDXR3RB.js';
6
7
  import './chunk-QBCU7OOP.js';
7
- import './chunk-HK4VPO65.js';
8
- import './chunk-CPN2KUD5.js';
8
+ import './chunk-O3Y6FORN.js';
9
+ import './chunk-3OBJOMQ2.js';
9
10
 
10
11
  // src/widgets/icons.ts
11
12
  var INK = "#8a8f98";
@@ -26,6 +27,10 @@ var defaultIcons = {
26
27
  };
27
28
 
28
29
  // src/widgets/base.ts
30
+ function hostOf(store) {
31
+ if (!store) return null;
32
+ return "flowHost" in store ? store.flowHost : store;
33
+ }
29
34
  var baseStyles = `
30
35
  :host {
31
36
  display: block;
@@ -376,6 +381,16 @@ var FlowWidget = class extends HTMLElement {
376
381
  packs() {
377
382
  return {};
378
383
  }
384
+ /** Follow an engine: rerender on every notification, replacing any previous
385
+ * subscription. A no-op before connection and on a null host, so a host may
386
+ * assign the store from a reactive effect that runs more than once. */
387
+ follow(host) {
388
+ this.unsub?.();
389
+ this.unsub = null;
390
+ if (!this.isConnected || !host) return;
391
+ this.unsub = host.engine.subscribe(() => this.rerender());
392
+ this.rerender();
393
+ }
379
394
  constructor() {
380
395
  super();
381
396
  this.root = this.attachShadow({ mode: "open" });
@@ -1949,18 +1964,10 @@ var SelfstoreStatusElement = class extends FlowWidget {
1949
1964
  this.wire();
1950
1965
  }
1951
1966
  host() {
1952
- const s = this.#store;
1953
- if (!s) return null;
1954
- return "flowHost" in s ? s.flowHost : s;
1967
+ return hostOf(this.#store);
1955
1968
  }
1956
1969
  wire() {
1957
- if (!this.isConnected) return;
1958
- this.unsub?.();
1959
- this.unsub = null;
1960
- const host = this.host();
1961
- if (!host) return;
1962
- this.unsub = host.engine.subscribe(() => this.rerender());
1963
- this.rerender();
1970
+ this.follow(this.host());
1964
1971
  }
1965
1972
  view(into) {
1966
1973
  const host = this.host();
@@ -3512,6 +3519,261 @@ var SelfstoreGateElement = class extends FlowWidget {
3512
3519
  }
3513
3520
  };
3514
3521
 
3522
+ // src/widgets/destination.ts
3523
+ var EN7 = {
3524
+ "destination.heading": "Where your data is kept",
3525
+ "destination.kind.drive": "Google Drive",
3526
+ "destination.kind.file": "A file on this device",
3527
+ "destination.kind.webdav": "Your own server",
3528
+ "destination.kind.s3": "Your own bucket",
3529
+ "destination.kind.device": "This device only",
3530
+ "destination.encrypted": "Password-protected",
3531
+ "destination.export": "Export a copy",
3532
+ "destination.exported": "The copy is written.",
3533
+ "destination.exportCancelled": "Nothing was written.",
3534
+ "destination.restore": "Load a copy",
3535
+ "destination.change": "Change destination",
3536
+ "destination.detach": "Stop saving here",
3537
+ "destination.detached": "Stopped. Your data stays on this device.",
3538
+ "destination.back": "Back",
3539
+ "error.generic": "Something went wrong."
3540
+ };
3541
+ var FR7 = {
3542
+ "destination.heading": "O\xF9 vos donn\xE9es sont enregistr\xE9es",
3543
+ "destination.kind.file": "Un fichier sur cet appareil",
3544
+ "destination.kind.webdav": "Votre serveur",
3545
+ "destination.kind.s3": "Votre bucket",
3546
+ "destination.kind.device": "Cet appareil seulement",
3547
+ "destination.encrypted": "Prot\xE9g\xE9e par un mot de passe",
3548
+ "destination.export": "Exporter une copie",
3549
+ "destination.exported": "La copie est \xE9crite.",
3550
+ "destination.exportCancelled": "Rien n'a \xE9t\xE9 \xE9crit.",
3551
+ "destination.restore": "Charger une copie",
3552
+ "destination.change": "Changer de destination",
3553
+ "destination.detach": "Ne plus enregistrer ici",
3554
+ "destination.detached": "Arr\xEAt\xE9. Vos donn\xE9es restent sur cet appareil.",
3555
+ "destination.back": "Retour",
3556
+ "error.generic": "Quelque chose n'a pas fonctionn\xE9."
3557
+ };
3558
+ var DESTINATION_STYLES = `
3559
+ [part~='destination-card'] { align-items: flex-start; }
3560
+ [part~='destination-actions'] {
3561
+ display: flex;
3562
+ flex-wrap: wrap;
3563
+ gap: var(--_gap);
3564
+ }
3565
+ [part~='destination-heading'] {
3566
+ margin: 0;
3567
+ font-size: 1rem;
3568
+ font-weight: 600;
3569
+ }
3570
+ `;
3571
+ function siblingTag(own, name) {
3572
+ const suffix = "-destination";
3573
+ return own.endsWith(suffix) ? `${own.slice(0, -suffix.length)}-${name}` : `selfstore-${name}`;
3574
+ }
3575
+ var SelfstoreDestinationElement = class extends FlowWidget {
3576
+ #store = null;
3577
+ #targets = null;
3578
+ #account = null;
3579
+ #icons = {};
3580
+ #confirm = null;
3581
+ #changing = false;
3582
+ #busy = false;
3583
+ #message = null;
3584
+ defaults() {
3585
+ return EN7;
3586
+ }
3587
+ packs() {
3588
+ return { fr: FR7 };
3589
+ }
3590
+ constructor() {
3591
+ super();
3592
+ this.root.append(h("style", {}, DESTINATION_STYLES));
3593
+ }
3594
+ /** The simple store (anything exposing `flowHost`), or a hand-built FlowHost. */
3595
+ get store() {
3596
+ return this.#store;
3597
+ }
3598
+ set store(v) {
3599
+ if (v === this.#store) return;
3600
+ this.#store = v;
3601
+ this.wire();
3602
+ }
3603
+ /** The destinations offered when the user changes their mind. Given them, the
3604
+ * panel runs the connect journey in place; without them the button is absent
3605
+ * rather than dead - a host that cannot offer a choice should not show one. */
3606
+ get targets() {
3607
+ return this.#targets;
3608
+ }
3609
+ set targets(v) {
3610
+ this.#targets = v;
3611
+ this.rerender();
3612
+ }
3613
+ /** Who holds the destination ("someone@example.com"), when the host knows.
3614
+ * For Drive that is `driveTarget.account()`: a brand name is not an address,
3615
+ * and several accounts look alike. */
3616
+ get account() {
3617
+ return this.#account;
3618
+ }
3619
+ set account(v) {
3620
+ this.#account = v || null;
3621
+ this.rerender();
3622
+ }
3623
+ /** Optional glyph per destination kind, same contract as the connect cards. */
3624
+ get icons() {
3625
+ return this.#icons;
3626
+ }
3627
+ set icons(v) {
3628
+ this.#icons = v ?? {};
3629
+ this.rerender();
3630
+ }
3631
+ /** Veto on what is destructive. Returns false (or throws) to stop; absent
3632
+ * means proceed. The panel never invents its own confirm dialog: an app has
3633
+ * its own words and its own modal. */
3634
+ get confirmAction() {
3635
+ return this.#confirm;
3636
+ }
3637
+ set confirmAction(fn) {
3638
+ this.#confirm = fn;
3639
+ }
3640
+ connectedCallback() {
3641
+ super.connectedCallback();
3642
+ this.wire();
3643
+ }
3644
+ host() {
3645
+ return hostOf(this.#store);
3646
+ }
3647
+ wire() {
3648
+ this.follow(this.host());
3649
+ }
3650
+ /** Run a gesture, keeping the panel honest while it is in flight. */
3651
+ async run(job) {
3652
+ this.#busy = true;
3653
+ this.#message = null;
3654
+ this.rerender();
3655
+ try {
3656
+ this.#message = await job();
3657
+ } catch {
3658
+ this.#message = this.t("error.generic");
3659
+ } finally {
3660
+ this.#busy = false;
3661
+ this.rerender();
3662
+ }
3663
+ }
3664
+ /** A portable copy, on the user's own terms. A closed save dialog wrote
3665
+ * nothing, so it must not clear the pending-export nudge nor claim success -
3666
+ * that is the download that lies about itself. */
3667
+ exportCopy() {
3668
+ const host = this.host();
3669
+ if (!host) return Promise.resolve();
3670
+ return this.run(async () => {
3671
+ const blob = await host.engine.exportBlob();
3672
+ const written = await saveToDisk(blob, datedName(host.backupName));
3673
+ if (!written) return this.t("destination.exportCancelled");
3674
+ host.engine.markDownloaded();
3675
+ return this.t("destination.exported");
3676
+ });
3677
+ }
3678
+ /** Stop saving to this destination. The local data stays, and so does the
3679
+ * backup already written: this detaches, it never deletes. */
3680
+ detach(label) {
3681
+ const host = this.host();
3682
+ if (!host) return Promise.resolve();
3683
+ return this.run(async () => {
3684
+ try {
3685
+ if (this.#confirm && !await this.#confirm({ type: "detach", label })) return null;
3686
+ } catch {
3687
+ return null;
3688
+ }
3689
+ await host.engine.detachTarget();
3690
+ this.emit("selfstore-destination-detached");
3691
+ return this.t("destination.detached");
3692
+ });
3693
+ }
3694
+ /** The connect journey, in place. Built like the gate builds it, so the
3695
+ * destinations, the password step and the cancel path are the library's
3696
+ * tested ones rather than a second implementation. */
3697
+ connectView(into) {
3698
+ const el = document.createElement(
3699
+ siblingTag(this.localName, "connect")
3700
+ );
3701
+ el.store = this.#store;
3702
+ el.targets = this.#targets;
3703
+ el.icons = this.#icons;
3704
+ el.labels = this.labels;
3705
+ const done = () => {
3706
+ this.#changing = false;
3707
+ this.rerender();
3708
+ };
3709
+ el.addEventListener("selfstore-connected", done);
3710
+ el.addEventListener("selfstore-cancelled", done);
3711
+ put(
3712
+ into,
3713
+ h(
3714
+ "button",
3715
+ { part: "link destination-back", type: "button", onclick: done },
3716
+ this.t("destination.back")
3717
+ ),
3718
+ el
3719
+ );
3720
+ }
3721
+ view(into) {
3722
+ const host = this.host();
3723
+ if (!host) return;
3724
+ if (this.#changing && this.#targets) return this.connectView(into);
3725
+ const { targetKind, label, encrypted } = host.engine.state;
3726
+ const kind = this.t(`destination.kind.${targetKind}`);
3727
+ const title = kind === `destination.kind.${targetKind}` ? label ?? targetKind : kind;
3728
+ const sub = [this.#account, label && label !== title ? label : null].filter(Boolean).join(" \xB7 ");
3729
+ const icon = this.#icons[targetKind];
3730
+ const status = document.createElement(siblingTag(this.localName, "status"));
3731
+ status.setAttribute("variant", "row");
3732
+ status.labels = this.labels;
3733
+ status.icons = this.#icons;
3734
+ status.store = this.#store;
3735
+ const act = (key, onclick) => h(
3736
+ "button",
3737
+ { part: "button", type: "button", disabled: this.#busy ? "" : null, onclick },
3738
+ this.t(key)
3739
+ );
3740
+ put(
3741
+ into,
3742
+ this.heading("destination-heading", "destination.heading"),
3743
+ h(
3744
+ "div",
3745
+ { part: "card destination-card" },
3746
+ icon ? h("img", { part: "icon", src: icon, alt: "" }) : null,
3747
+ h(
3748
+ "div",
3749
+ {},
3750
+ h("div", { part: "title" }, title),
3751
+ sub ? h("div", { part: "sub" }, sub) : null,
3752
+ encrypted ? h("div", { part: "sub" }, this.t("destination.encrypted")) : null
3753
+ )
3754
+ ),
3755
+ status,
3756
+ h(
3757
+ "div",
3758
+ { part: "destination-actions" },
3759
+ act("destination.export", () => void this.exportCopy()),
3760
+ // The library holds no records, so loading a copy back is the host's
3761
+ // own gesture - see the note at the top of this file.
3762
+ act(
3763
+ "destination.restore",
3764
+ () => this.emit("selfstore-destination-action", { action: "restore" })
3765
+ ),
3766
+ this.#targets ? act("destination.change", () => {
3767
+ this.#changing = true;
3768
+ this.rerender();
3769
+ }) : null,
3770
+ act("destination.detach", () => void this.detach(label))
3771
+ ),
3772
+ this.#message ? h("div", { part: "sub destination-message", role: "status" }, this.#message) : null
3773
+ );
3774
+ }
3775
+ };
3776
+
3515
3777
  // src/entries/widgets.ts
3516
3778
  function defineSelfstoreWidgets(prefix = "selfstore") {
3517
3779
  const define = (name, ctor) => {
@@ -3523,8 +3785,9 @@ function defineSelfstoreWidgets(prefix = "selfstore") {
3523
3785
  define(`${prefix}-status`, SelfstoreStatusElement);
3524
3786
  define(`${prefix}-backups`, SelfstoreBackupsElement);
3525
3787
  define(`${prefix}-gate`, SelfstoreGateElement);
3788
+ define(`${prefix}-destination`, SelfstoreDestinationElement);
3526
3789
  }
3527
3790
 
3528
- export { FlowWidget, SelfstoreBackupsElement, SelfstoreConnectElement, SelfstoreGateElement, SelfstoreJoinElement, SelfstoreShareElement, SelfstoreStatusElement, defineSelfstoreWidgets };
3791
+ export { FlowWidget, SelfstoreBackupsElement, SelfstoreConnectElement, SelfstoreDestinationElement, SelfstoreGateElement, SelfstoreJoinElement, SelfstoreShareElement, SelfstoreStatusElement, defineSelfstoreWidgets };
3529
3792
  //# sourceMappingURL=widgets.js.map
3530
3793
  //# sourceMappingURL=widgets.js.map