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.
- package/README.md +1 -1
- package/dist/advanced.d.ts +8 -4
- package/dist/advanced.js +14 -13
- package/dist/advanced.js.map +1 -1
- package/dist/backups.d.ts +31 -179
- package/dist/backups.js +40 -3
- package/dist/backups.js.map +1 -1
- package/dist/{cache-lock-RCN3JJ4Q.js → cache-lock-KWJUORTS.js} +3 -4
- package/dist/cache-lock-KWJUORTS.js.map +1 -0
- package/dist/{chunk-HK4VPO65.js → chunk-3OBJOMQ2.js} +28 -4
- package/dist/chunk-3OBJOMQ2.js.map +1 -0
- package/dist/{chunk-HDR75TQ7.js → chunk-DCEXWSM6.js} +3 -3
- package/dist/{chunk-HDR75TQ7.js.map → chunk-DCEXWSM6.js.map} +1 -1
- package/dist/{chunk-7N2YR3QW.js → chunk-ELDSPUTS.js} +7 -8
- package/dist/chunk-ELDSPUTS.js.map +1 -0
- package/dist/chunk-J5MFXJU6.js +310 -0
- package/dist/chunk-J5MFXJU6.js.map +1 -0
- package/dist/{chunk-BNDFMU3J.js → chunk-MGDXR3RB.js} +5 -6
- package/dist/chunk-MGDXR3RB.js.map +1 -0
- package/dist/{chunk-453KRCMM.js → chunk-O3Y6FORN.js} +3 -4
- package/dist/chunk-O3Y6FORN.js.map +1 -0
- package/dist/{chunk-JMJZIZKY.js → chunk-PNX7AJYO.js} +13 -12
- package/dist/chunk-PNX7AJYO.js.map +1 -0
- package/dist/{chunk-5NS7YQNP.js → chunk-TKWU52LK.js} +26 -312
- package/dist/chunk-TKWU52LK.js.map +1 -0
- package/dist/{chunk-U5F4GI7C.js → chunk-W22V5CRM.js} +3 -3
- package/dist/{chunk-U5F4GI7C.js.map → chunk-W22V5CRM.js.map} +1 -1
- package/dist/{drive-l_4-m1eT.d.ts → drive-CTYvGg_7.d.ts} +23 -110
- package/dist/{flows-C_uookek.d.ts → flows-1EntUZ91.d.ts} +2 -1
- package/dist/flows.d.ts +3 -2
- package/dist/flows.js +16 -16
- package/dist/flows.js.map +1 -1
- package/dist/groups.js +3 -4
- package/dist/groups.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +18 -18
- package/dist/index.js.map +1 -1
- package/dist/{local-bfv4Ld7s.d.ts → local-C52j3DeH.d.ts} +1 -1
- package/dist/local-DPS35F5C.js +6 -0
- package/dist/{local-2YREU4Q2.js.map → local-DPS35F5C.js.map} +1 -1
- package/dist/manager-DZ1c_aSS.d.ts +181 -0
- package/dist/s3-DvsnA-FK.d.ts +112 -0
- package/dist/widgets.d.ts +90 -7
- package/dist/widgets.js +281 -18
- package/dist/widgets.js.map +1 -1
- package/llms.txt +13 -2
- package/package.json +1 -1
- package/dist/cache-lock-RCN3JJ4Q.js.map +0 -1
- package/dist/chunk-453KRCMM.js.map +0 -1
- package/dist/chunk-5NS7YQNP.js.map +0 -1
- package/dist/chunk-7N2YR3QW.js.map +0 -1
- package/dist/chunk-BNDFMU3J.js.map +0 -1
- package/dist/chunk-CPN2KUD5.js +0 -29
- package/dist/chunk-CPN2KUD5.js.map +0 -1
- package/dist/chunk-HK4VPO65.js.map +0 -1
- package/dist/chunk-JMJZIZKY.js.map +0 -1
- package/dist/local-2YREU4Q2.js +0 -7
package/dist/widgets.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import './chunk-
|
|
4
|
-
import './chunk-
|
|
5
|
-
import './chunk-
|
|
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-
|
|
8
|
-
import './chunk-
|
|
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
|
-
|
|
1953
|
-
if (!s) return null;
|
|
1954
|
-
return "flowHost" in s ? s.flowHost : s;
|
|
1967
|
+
return hostOf(this.#store);
|
|
1955
1968
|
}
|
|
1956
1969
|
wire() {
|
|
1957
|
-
|
|
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
|