selfstore 1.8.3 → 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 CHANGED
@@ -162,7 +162,7 @@ too, as subpaths of the same install:
162
162
  |---|---|---|
163
163
  | `selfstore` | the simple store + backup files | almost always: start here |
164
164
  | `selfstore/flows` | the user journeys (connect a destination, share panel, join an invitation) as headless, tested state machines | you are building the connect/share/join UI and want the ordering and failure rules - one popup per gesture, password proven before attach, merge by default - already right |
165
- | `selfstore/widgets` | the same journeys as drop-in web components (`<selfstore-connect>`, `<selfstore-gate>`, `<selfstore-share>`, `<selfstore-join>`) - framework-free, themable via CSS custom properties and `::part()`, reworded/localized via a `labels` map | you want the screens ready-made and your app's look on top |
165
+ | `selfstore/widgets` | the same journeys as drop-in web components (`<selfstore-connect>`, `<selfstore-gate>`, `<selfstore-destination>`, `<selfstore-share>`, `<selfstore-join>`) - framework-free, themable via CSS custom properties and `::part()`, reworded/localized via a `labels` map | you want the screens ready-made and your app's look on top |
166
166
  | `selfstore/advanced` | the pull-model store (`createLocalStore`), custom `BackupTarget`s, caches, headless status derivation, functional codec | your state lives in its own reactive model (Svelte runes, Redux), or you are writing a destination (S3, your KV...) |
167
167
  | `selfstore/groups` | passwordless group encryption: per-member identities, sealed envelopes, signed membership manifests | several PEOPLE share one encrypted store without a shared password ([PEERS.md](./PEERS.md)) |
168
168
  | `selfstore/sync` | the bare merge engine (HLC + per-collection strategies, no CRDT runtime) | you only want the algorithm inside your own persistence ([SYNC docs in SPEC.md](./SPEC.md)) |
package/dist/advanced.js CHANGED
@@ -1,6 +1,6 @@
1
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
2
  import './chunk-SKWHKM25.js';
3
+ export { datedName, pickFromDisk, saveToDisk } from './chunk-DCEXWSM6.js';
4
4
  import { openExisting, isOpenSupported, isSupported, fromSession as fromSession$1, connect as connect$1, fromSession3, connect3, peer, fromSession2, connect2 } from './chunk-TKWU52LK.js';
5
5
  import { FILE_ID_KEY, account, renameBackup, deleteBackup, createBackup, listBackups, findOrCreateOwnFile, adopt, preview, fromSession, connect } from './chunk-J5MFXJU6.js';
6
6
  export { RESERVED_COLLECTION_PREFIX, checkPasswordPolicy, exportSnapshot, importSnapshot, inspect, isEncrypted } from './chunk-MGDXR3RB.js';
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { indexedDbCache, memoryCache, isLockableCache, createLocalStore } from './chunk-ELDSPUTS.js';
2
2
  export { gisDriveAuth } from './chunk-ELDSPUTS.js';
3
+ import './chunk-SKWHKM25.js';
3
4
  import { saveToDisk } from './chunk-DCEXWSM6.js';
4
5
  export { pickFromDisk, saveToDisk } from './chunk-DCEXWSM6.js';
5
- import './chunk-SKWHKM25.js';
6
6
  import { fromSession as fromSession$1, fromSession2, isSupported, connect3, connect2, connect } from './chunk-TKWU52LK.js';
7
7
  export { hasDesktopFiles, useDesktopFiles } from './chunk-TKWU52LK.js';
8
8
  import { fromSession, connect as connect$1 } from './chunk-J5MFXJU6.js';
package/dist/widgets.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { S as StoreLike, C as ConnectTargets, a as ConnectFlowOptions, b as ConnectKind, c as ConnectFlow, d as ShareEngine, e as ShareLevel, f as ShareFlow, J as JoinEngine, g as JoinFlow, R as ReplicaFlow } from './flows-1EntUZ91.js';
1
+ import { F as FlowHost, S as StoreLike, C as ConnectTargets, a as ConnectFlowOptions, b as ConnectKind, c as ConnectFlow, d as ShareEngine, e as ShareLevel, f as ShareFlow, J as JoinEngine, g as JoinFlow, R as ReplicaFlow } from './flows-1EntUZ91.js';
2
2
  import { f as BackupsManager } from './manager-DZ1c_aSS.js';
3
3
  import './store-BiQ-BuVl.js';
4
4
  import './cache-ilsK3Tki.js';
@@ -22,6 +22,10 @@ declare abstract class FlowWidget extends HTMLElement {
22
22
  protected packs(): Record<string, WidgetLabels>;
23
23
  /** Build the current view into the given container. */
24
24
  protected abstract view(into: HTMLElement): void;
25
+ /** Follow an engine: rerender on every notification, replacing any previous
26
+ * subscription. A no-op before connection and on a null host, so a host may
27
+ * assign the store from a reactive effect that runs more than once. */
28
+ protected follow(host: FlowHost | null): void;
25
29
  constructor();
26
30
  /** Partial override map, merged over the EN defaults (also the i18n hook). */
27
31
  get labels(): WidgetLabels;
@@ -523,10 +527,88 @@ declare class SelfstoreGateElement extends FlowWidget {
523
527
  protected view(into: HTMLElement): void;
524
528
  }
525
529
 
530
+ /**
531
+ * <selfstore-destination>: where the data is kept, once it has a home.
532
+ *
533
+ * The counterpart of <selfstore-gate>. The gate asks the question before there
534
+ * is an answer; this panel states the answer and carries the gestures that act
535
+ * on it - export a copy, change destination, stop saving here.
536
+ *
537
+ * const el = document.querySelector('selfstore-destination');
538
+ * el.store = store;
539
+ * el.targets = { drive: driveAuth, file: true }; // enables "change"
540
+ *
541
+ * It DOES the work rather than asking the host to. Exporting a copy, detaching
542
+ * and re-connecting are all the engine's own operations, so a panel that only
543
+ * emitted intentions would leave the same two hundred lines in every app - the
544
+ * very lines this widget exists to delete.
545
+ *
546
+ * The one exception is structural, not a choice: **loading a copy back cannot
547
+ * be done from here.** The library holds no records; the app's `apply` does.
548
+ * The engine exposes `exportBlob()` and no import, so the panel asks for that
549
+ * one through `selfstore-destination-action` and the host runs its own import.
550
+ *
551
+ * Two things stay the host's, deliberately: the WORDS (every label is
552
+ * overridable, and a pack ships per language) and the VETO on what is
553
+ * destructive (`confirmAction`, which defaults to proceeding). Neither is
554
+ * customisation - they are the decisions a library must not take for an app.
555
+ */
556
+
557
+ /** What the panel is about to do, for the host's veto. */
558
+ interface DestinationAction {
559
+ type: 'detach';
560
+ /** The destination's own name, when it has one. */
561
+ label: string | null;
562
+ }
563
+ declare class SelfstoreDestinationElement extends FlowWidget {
564
+ #private;
565
+ protected defaults(): WidgetLabels;
566
+ protected packs(): Record<string, WidgetLabels>;
567
+ constructor();
568
+ /** The simple store (anything exposing `flowHost`), or a hand-built FlowHost. */
569
+ get store(): StoreLike | null;
570
+ set store(v: StoreLike | null);
571
+ /** The destinations offered when the user changes their mind. Given them, the
572
+ * panel runs the connect journey in place; without them the button is absent
573
+ * rather than dead - a host that cannot offer a choice should not show one. */
574
+ get targets(): ConnectTargets | null;
575
+ set targets(v: ConnectTargets | null);
576
+ /** Who holds the destination ("someone@example.com"), when the host knows.
577
+ * For Drive that is `driveTarget.account()`: a brand name is not an address,
578
+ * and several accounts look alike. */
579
+ get account(): string | null;
580
+ set account(v: string | null);
581
+ /** Optional glyph per destination kind, same contract as the connect cards. */
582
+ get icons(): Partial<Record<ConnectKind, string>>;
583
+ set icons(v: Partial<Record<ConnectKind, string>> | null);
584
+ /** Veto on what is destructive. Returns false (or throws) to stop; absent
585
+ * means proceed. The panel never invents its own confirm dialog: an app has
586
+ * its own words and its own modal. */
587
+ get confirmAction(): ((a: DestinationAction) => boolean | Promise<boolean>) | null;
588
+ set confirmAction(fn: ((a: DestinationAction) => boolean | Promise<boolean>) | null);
589
+ connectedCallback(): void;
590
+ private host;
591
+ private wire;
592
+ /** Run a gesture, keeping the panel honest while it is in flight. */
593
+ private run;
594
+ /** A portable copy, on the user's own terms. A closed save dialog wrote
595
+ * nothing, so it must not clear the pending-export nudge nor claim success -
596
+ * that is the download that lies about itself. */
597
+ private exportCopy;
598
+ /** Stop saving to this destination. The local data stays, and so does the
599
+ * backup already written: this detaches, it never deletes. */
600
+ private detach;
601
+ /** The connect journey, in place. Built like the gate builds it, so the
602
+ * destinations, the password step and the cancel path are the library's
603
+ * tested ones rather than a second implementation. */
604
+ private connectView;
605
+ protected view(into: HTMLElement): void;
606
+ }
607
+
526
608
  /** Register the elements as <PREFIX-connect>, <PREFIX-share>, <PREFIX-join>,
527
- * <PREFIX-status>, <PREFIX-backups> and <PREFIX-gate> (default prefix
528
- * 'selfstore'). Safe to call twice; throws in environments without custom
529
- * elements (browser code only). */
609
+ * <PREFIX-status>, <PREFIX-backups>, <PREFIX-gate> and
610
+ * <PREFIX-destination> (default prefix 'selfstore'). Safe to call twice;
611
+ * throws in environments without custom elements (browser code only). */
530
612
  declare function defineSelfstoreWidgets(prefix?: string): void;
531
613
 
532
- export { type BackupsAction, FlowWidget, SelfstoreBackupsElement, SelfstoreConnectElement, SelfstoreGateElement, SelfstoreJoinElement, SelfstoreShareElement, SelfstoreStatusElement, type ShareAction, type WebdavPreset, type WidgetLabels, defineSelfstoreWidgets };
614
+ export { type BackupsAction, type DestinationAction, FlowWidget, SelfstoreBackupsElement, SelfstoreConnectElement, SelfstoreDestinationElement, SelfstoreGateElement, SelfstoreJoinElement, SelfstoreShareElement, SelfstoreStatusElement, type ShareAction, type WebdavPreset, type WidgetLabels, defineSelfstoreWidgets };
package/dist/widgets.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { saveToDisk, datedName } from './chunk-DCEXWSM6.js';
1
2
  import { connectFlow, shareFlow, joinFlow } from './chunk-PNX7AJYO.js';
2
3
  import { isOpenSupported } from './chunk-TKWU52LK.js';
3
4
  import './chunk-W22V5CRM.js';
@@ -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