selfstore 1.8.16 → 1.8.18

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/dist/flows.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { b as ConnectKind, S as StoreLike, C as ConnectTargets } from './connect-DaZFPJdt.js';
2
- export { c as ConnectFlow, a as ConnectFlowOptions, d as ConnectFlowOutcome, e as ConnectResolution, f as ConnectSnapshot, g as ConnectStep, h as Connector, F as FlowHost, R as ResumeOffer, i as connectFlow } from './connect-DaZFPJdt.js';
3
- import { S as StoreError, k as ReplicaState, B as BackupTarget } from './store-CiLoRJmy.js';
1
+ import { b as ConnectKind, S as StoreLike, C as ConnectTargets } from './connect-BTVzRJY8.js';
2
+ export { c as ConnectFlow, a as ConnectFlowOptions, d as ConnectFlowOutcome, e as ConnectResolution, f as ConnectSnapshot, g as ConnectStep, h as Connector, F as FlowHost, R as ResumeOffer, i as connectFlow } from './connect-BTVzRJY8.js';
3
+ import { S as StoreError, k as ReplicaState, B as BackupTarget } from './store-Cs6QIzzG.js';
4
4
  import { F as FlowStore } from './machine-ALMkT3mk.js';
5
5
  export { w as withDeadline } from './machine-ALMkT3mk.js';
6
- import { W as WebdavConfig, S as S3Config } from './s3-DgyTwMRc.js';
6
+ import { W as WebdavConfig, S as S3Config } from './s3-0qqIyphl.js';
7
7
  import './cache-ilsK3Tki.js';
8
- import './drive-B2fm71qD.js';
8
+ import './drive-CY_Vsj7V.js';
9
9
  import './sync.js';
10
10
 
11
11
  /** The one replica this flow manages, as its id on the engine. */
@@ -1,4 +1,4 @@
1
- import { B as BackupTarget, P as PeerSource, L as LocalStore } from './store-CiLoRJmy.js';
1
+ import { B as BackupTarget, P as PeerSource, L as LocalStore } from './store-Cs6QIzzG.js';
2
2
  import { K as KV } from './cache-ilsK3Tki.js';
3
3
  import './sync.js';
4
4
 
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
- import { o as Snapshot, E as EncodeOptions, H as Header, f as PasswordPolicy, B as BackupTarget, r as StatusDescriptor, S as StoreError, c as LocalStoreState, L as LocalStore } from './store-CiLoRJmy.js';
2
- export { d as Mode, e as PasswordCheck, g as PasswordRequirement, l as SelfstoreError, m as SelfstoreErrorCode, n as Severity, p as SnapshotFile, q as StatusAction, t as StorageState, T as TargetKind, x as checkPasswordPolicy, C as errorLabelKey, G as isSelfstoreError } from './store-CiLoRJmy.js';
1
+ import { o as Snapshot, E as EncodeOptions, H as Header, f as PasswordPolicy, B as BackupTarget, r as StatusDescriptor, S as StoreError, c as LocalStoreState, L as LocalStore } from './store-Cs6QIzzG.js';
2
+ export { d as Mode, e as PasswordCheck, g as PasswordRequirement, l as SelfstoreError, m as SelfstoreErrorCode, n as Severity, p as SnapshotFile, q as StatusAction, t as StorageState, T as TargetKind, x as checkPasswordPolicy, C as errorLabelKey, G as isSelfstoreError } from './store-Cs6QIzzG.js';
3
3
  import { L as LocalCache, K as KV } from './cache-ilsK3Tki.js';
4
4
  import { SyncConfig } from './sync.js';
5
- import { R as ResumeOffer, C as ConnectTargets } from './connect-DaZFPJdt.js';
6
- import { f as BackupsManager } from './manager-BqzTVaep.js';
7
- import { a as DriveAuth } from './drive-B2fm71qD.js';
8
- import { W as WebdavConfig, S as S3Config } from './s3-DgyTwMRc.js';
9
- export { f as changePassword, g as gisDriveAuth, p as pickFromDisk, s as saveToDisk } from './local-CldSa9EY.js';
5
+ import { R as ResumeOffer, C as ConnectTargets } from './connect-BTVzRJY8.js';
6
+ import { f as BackupsManager } from './manager-DhoIbJyg.js';
7
+ import { a as DriveAuth } from './drive-CY_Vsj7V.js';
8
+ import { W as WebdavConfig, S as S3Config } from './s3-0qqIyphl.js';
9
+ export { f as changePassword, g as gisDriveAuth, p as pickFromDisk, s as saveToDisk } from './local-Dh6eGiK9.js';
10
10
  import './machine-ALMkT3mk.js';
11
11
 
12
12
  declare const BACKUP_EXTENSION = ".zip";
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { indexedDbCache, memoryCache, gisDriveAuth, isLockableCache, createLocalStore } from './chunk-TXAMWBG5.js';
2
- export { gisDriveAuth } from './chunk-TXAMWBG5.js';
1
+ import { indexedDbCache, memoryCache, gisDriveAuth, isLockableCache, createLocalStore } from './chunk-RBIQRTZR.js';
2
+ export { gisDriveAuth } from './chunk-RBIQRTZR.js';
3
3
  import './chunk-SKWHKM25.js';
4
4
  import { saveToDisk } from './chunk-DCEXWSM6.js';
5
5
  export { pickFromDisk, saveToDisk } from './chunk-DCEXWSM6.js';
@@ -1,5 +1,5 @@
1
- import { o as Snapshot, E as EncodeOptions, H as Header } from './store-CiLoRJmy.js';
2
- import { a as DriveAuth } from './drive-B2fm71qD.js';
1
+ import { o as Snapshot, E as EncodeOptions, H as Header } from './store-Cs6QIzzG.js';
2
+ import { a as DriveAuth } from './drive-CY_Vsj7V.js';
3
3
 
4
4
  /** Serialize a snapshot to a portable backup blob (compressed; encrypted if a password is given). */
5
5
  declare function exportSnapshot(snapshot: Snapshot, opts: EncodeOptions): Promise<Blob>;
@@ -1,5 +1,5 @@
1
1
  import { K as KV } from './cache-ilsK3Tki.js';
2
- import { B as BackupTarget, L as LocalStore } from './store-CiLoRJmy.js';
2
+ import { B as BackupTarget, L as LocalStore } from './store-Cs6QIzzG.js';
3
3
  import { F as FlowStore } from './machine-ALMkT3mk.js';
4
4
 
5
5
  /** What the destination reports about a listed file (one cheap metadata call). */
@@ -1,4 +1,4 @@
1
- import { B as BackupTarget, P as PeerSource } from './store-CiLoRJmy.js';
1
+ import { B as BackupTarget, P as PeerSource } from './store-Cs6QIzzG.js';
2
2
  import { K as KV } from './cache-ilsK3Tki.js';
3
3
 
4
4
  /**
@@ -229,6 +229,13 @@ interface StatusInput {
229
229
  locked: boolean;
230
230
  /** Degraded file mode has changes awaiting a manual download. */
231
231
  pendingDownload: boolean;
232
+ /** A portable copy was written at some point, and whether anything has been
233
+ * entered since. Without a durable destination, this is the difference
234
+ * between "never saved anywhere" and "saved this morning". */
235
+ copy?: {
236
+ at: number | null;
237
+ stale: boolean;
238
+ };
232
239
  }
233
240
  /** Map the store's raw flags to a single, ranked status; the most important wins. */
234
241
  declare function deriveStatus(input: StatusInput): StatusDescriptor;
@@ -359,6 +366,24 @@ interface LocalStoreState {
359
366
  locked: boolean;
360
367
  saving: boolean;
361
368
  lastSavedAt: number | null;
369
+ /**
370
+ * When a PORTABLE COPY was last written (an export the user asked for), or
371
+ * null if never. Distinct from `lastSavedAt`, which is the durable
372
+ * destination: a copy is a snapshot that leaves, a destination is kept up to
373
+ * date.
374
+ */
375
+ lastCopyAt: number | null;
376
+ /**
377
+ * Entries have been recorded since that copy was written.
378
+ *
379
+ * The store knew this only in its download-only mode, where the copy IS the
380
+ * save. Everywhere else nobody followed it, so an app that let its user
381
+ * export could say nothing about a copy going stale - and one of them found
382
+ * out the hard way that a copy written in the morning was still announced as
383
+ * up to date in the evening. False while no copy was ever written: there is
384
+ * nothing to be stale.
385
+ */
386
+ copyIsStale: boolean;
362
387
  status: StatusDescriptor;
363
388
  /** The last durable-target problem (upload/refresh), or null. */
364
389
  lastError: StoreError | null;
package/dist/widgets.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { S as StoreLike, F as FlowHost, C as ConnectTargets, a as ConnectFlowOptions, b as ConnectKind, c as ConnectFlow } from './connect-DaZFPJdt.js';
1
+ import { S as StoreLike, F as FlowHost, C as ConnectTargets, a as ConnectFlowOptions, b as ConnectKind, c as ConnectFlow } from './connect-BTVzRJY8.js';
2
2
  import { ShareEngine, ShareLevel, ShareFlow, JoinEngine, JoinFlow, ReplicaFlow } from './flows.js';
3
- import { f as BackupsManager } from './manager-BqzTVaep.js';
4
- import './store-CiLoRJmy.js';
3
+ import { f as BackupsManager } from './manager-DhoIbJyg.js';
4
+ import './store-Cs6QIzzG.js';
5
5
  import './cache-ilsK3Tki.js';
6
6
  import './sync.js';
7
- import './drive-B2fm71qD.js';
8
- import './s3-DgyTwMRc.js';
7
+ import './drive-CY_Vsj7V.js';
8
+ import './s3-0qqIyphl.js';
9
9
  import './machine-ALMkT3mk.js';
10
10
 
11
11
  /** Flat key -> string map; widgets merge a partial override over EN defaults. */
@@ -746,6 +746,7 @@ declare class SelfstoreStorageElement extends FlowWidget {
746
746
 
747
747
  declare class SelfstoreAccountElement extends FlowWidget {
748
748
  #private;
749
+ static get observedAttributes(): string[];
749
750
  protected defaults(): WidgetLabels;
750
751
  protected packs(): Record<string, WidgetLabels>;
751
752
  constructor();
@@ -755,10 +756,23 @@ declare class SelfstoreAccountElement extends FlowWidget {
755
756
  * the user picks one. Returns false (or throws) to stop. */
756
757
  get confirmAction(): ((a: DestinationAction) => boolean | Promise<boolean>) | null;
757
758
  set confirmAction(fn: ((a: DestinationAction) => boolean | Promise<boolean>) | null);
759
+ /**
760
+ * What the trigger says: the DESTINATION it goes to (default), or the STATE
761
+ * it is in.
762
+ *
763
+ * Naming the place is right when there is a choice - it makes local-first
764
+ * visible without opening anything. It says nothing at all when an app
765
+ * offers exactly one destination: "a file" where a file is the only thing it
766
+ * could ever be. Such an app states a condition instead - saved, unsaved,
767
+ * locked - which is the part that changes.
768
+ */
769
+ get trigger(): 'destination' | 'status';
770
+ set trigger(v: 'destination' | 'status' | null);
758
771
  /** Whether the menu is on screen. Settable, so a host can close it from its
759
772
  * own router after the settings item navigated away. */
760
773
  get open(): boolean;
761
774
  set open(v: boolean);
775
+ attributeChangedCallback(name: string): void;
762
776
  connectedCallback(): void;
763
777
  disconnectedCallback(): void;
764
778
  /** While the menu is open: a clock for the relative time, and the two ways
@@ -791,6 +805,13 @@ declare class SelfstoreAccountElement extends FlowWidget {
791
805
  /** Change backup: detach, and let the first-run screen take it from there.
792
806
  * The data stays; only the destination is let go. */
793
807
  private change;
808
+ /**
809
+ * What the trigger reads. The destination by default; the state when an app
810
+ * has only one destination to offer, where naming it says nothing that ever
811
+ * changes. The state comes from <selfstore-status>'s own words, so the two
812
+ * surfaces cannot drift.
813
+ */
814
+ private said;
794
815
  private card;
795
816
  protected view(into: HTMLElement): void;
796
817
  /**
package/dist/widgets.js CHANGED
@@ -2087,6 +2087,8 @@ var EN5 = {
2087
2087
  "status.justNow": "just now",
2088
2088
  "status.ephemeral": "Nothing is saved",
2089
2089
  "status.cacheOnly": "Never saved anywhere yet",
2090
+ "status.copy": "Last copy written {when}. Nothing has changed since.",
2091
+ "status.copyStale": "Last copy written {when}. What you typed since is not in it.",
2090
2092
  "status.saving": "Saving...",
2091
2093
  "status.saved": "Saved to {label}, at every change",
2092
2094
  "status.needsAttention": "Reconnect to continue",
@@ -2103,6 +2105,8 @@ var FR5 = {
2103
2105
  "status.justNow": "a l'instant",
2104
2106
  "status.ephemeral": "Rien n'est enregistr\xE9",
2105
2107
  "status.cacheOnly": "Vos donn\xE9es n'ont jamais \xE9t\xE9 enregistr\xE9es ailleurs",
2108
+ "status.copy": "Derni\xE8re copie \xE9crite {when}. Rien n'a chang\xE9 depuis.",
2109
+ "status.copyStale": "Derni\xE8re copie \xE9crite {when}. Vos saisies depuis n'y sont pas.",
2106
2110
  "status.saving": "Enregistrement...",
2107
2111
  "status.saved": "Enregistr\xE9 dans {label}, \xE0 chaque changement",
2108
2112
  "status.needsAttention": "Acc\xE8s \xE0 retrouver",
@@ -2152,11 +2156,20 @@ var SelfstoreStatusElement = class extends FlowWidget {
2152
2156
  const { status, targetKind, label } = host.engine.state;
2153
2157
  const sansLieu = (cle) => {
2154
2158
  const jumelle = `${cle}.placeless`;
2155
- const copie = this.t(jumelle);
2156
- return copie === jumelle ? this.t(cle) : copie;
2159
+ const copie = this.t(jumelle, vars);
2160
+ return copie === jumelle ? this.t(cle, vars) : copie;
2157
2161
  };
2158
- const { lastSavedAt } = host.engine.state;
2159
- const text = label ? this.t(status.labelKey, { label }) : sansLieu(status.labelKey);
2162
+ const selonLeType = (cle) => {
2163
+ const jumelle = `${cle}.${targetKind}`;
2164
+ const copie = this.t(jumelle, vars);
2165
+ return copie === jumelle ? null : copie;
2166
+ };
2167
+ const { lastSavedAt, lastCopyAt } = host.engine.state;
2168
+ const vars = {
2169
+ label: label ?? "",
2170
+ when: lastCopyAt ? this.since(lastCopyAt, this.t("status.justNow")) : ""
2171
+ };
2172
+ const text = selonLeType(status.labelKey) ?? (label ? this.t(status.labelKey, vars) : sansLieu(status.labelKey));
2160
2173
  const dot = h("span", {
2161
2174
  part: `status-dot sev-${status.severity}`,
2162
2175
  "aria-hidden": "true"
@@ -2214,7 +2227,7 @@ var SelfstoreStatusElement = class extends FlowWidget {
2214
2227
  const place = where !== `status.where.${targetKind}` ? where : own ?? named ?? label ?? "";
2215
2228
  const when = lastSavedAt ? this.since(lastSavedAt, this.t("status.justNow")) : "";
2216
2229
  const sub = [place, status.state === "saved" ? when : ""].filter(Boolean).join(", ");
2217
- const title = sub ? sansLieu(status.labelKey) : text;
2230
+ const title = selonLeType(status.labelKey) ?? (sub ? sansLieu(status.labelKey) : text);
2218
2231
  put(
2219
2232
  into,
2220
2233
  h(
@@ -4123,6 +4136,9 @@ var ACCOUNT_STYLES = `
4123
4136
  of ZERO - the pill collapsed to its dot in a flex header, with the label
4124
4137
  overflowing off screen. Nothing here queries its own width. */
4125
4138
  [part~='stack'] { container-type: normal; display: block; }
4139
+ /* A state in the trigger is a line already: no dot of its own, no action - the
4140
+ press opens the menu, where the gesture lives. */
4141
+ [part~='account-trigger-status'] [part~='status-action'] { display: none; }
4126
4142
  [part~='account-trigger'] {
4127
4143
  display: inline-flex;
4128
4144
  align-items: center;
@@ -4228,7 +4244,11 @@ var ACCOUNT_STYLES = `
4228
4244
  `;
4229
4245
  var TICK_MS = 3e4;
4230
4246
  var SelfstoreAccountElement = class extends FlowWidget {
4247
+ static get observedAttributes() {
4248
+ return ["trigger"];
4249
+ }
4231
4250
  #confirm = null;
4251
+ #trigger = "destination";
4232
4252
  #open = false;
4233
4253
  #error = null;
4234
4254
  #timer = null;
@@ -4261,6 +4281,23 @@ var SelfstoreAccountElement = class extends FlowWidget {
4261
4281
  set confirmAction(fn) {
4262
4282
  this.#confirm = fn;
4263
4283
  }
4284
+ /**
4285
+ * What the trigger says: the DESTINATION it goes to (default), or the STATE
4286
+ * it is in.
4287
+ *
4288
+ * Naming the place is right when there is a choice - it makes local-first
4289
+ * visible without opening anything. It says nothing at all when an app
4290
+ * offers exactly one destination: "a file" where a file is the only thing it
4291
+ * could ever be. Such an app states a condition instead - saved, unsaved,
4292
+ * locked - which is the part that changes.
4293
+ */
4294
+ get trigger() {
4295
+ return this.#trigger;
4296
+ }
4297
+ set trigger(v) {
4298
+ this.#trigger = v === "status" ? "status" : "destination";
4299
+ this.rerender();
4300
+ }
4264
4301
  /** Whether the menu is on screen. Settable, so a host can close it from its
4265
4302
  * own router after the settings item navigated away. */
4266
4303
  get open() {
@@ -4272,8 +4309,16 @@ var SelfstoreAccountElement = class extends FlowWidget {
4272
4309
  this.watch();
4273
4310
  this.rerender();
4274
4311
  }
4312
+ attributeChangedCallback(name) {
4313
+ if (name === "trigger") {
4314
+ const value = this.attr(name);
4315
+ if (value !== null) this.trigger = value;
4316
+ }
4317
+ }
4275
4318
  connectedCallback() {
4276
4319
  super.connectedCallback();
4320
+ const trigger = this.attr("trigger");
4321
+ if (trigger !== null) this.trigger = trigger;
4277
4322
  this.follow(this.hostOf());
4278
4323
  }
4279
4324
  disconnectedCallback() {
@@ -4375,6 +4420,23 @@ var SelfstoreAccountElement = class extends FlowWidget {
4375
4420
  this.rerender();
4376
4421
  }
4377
4422
  }
4423
+ /**
4424
+ * What the trigger reads. The destination by default; the state when an app
4425
+ * has only one destination to offer, where naming it says nothing that ever
4426
+ * changes. The state comes from <selfstore-status>'s own words, so the two
4427
+ * surfaces cannot drift.
4428
+ */
4429
+ said(host) {
4430
+ if (this.#trigger !== "status") return this.named(host);
4431
+ const el = document.createElement(
4432
+ siblingTag(this.localName, "account", "status")
4433
+ );
4434
+ el.setAttribute("variant", "line");
4435
+ el.setAttribute("part", "account-trigger-status");
4436
+ el.labels = this.labels;
4437
+ el.store = this.store;
4438
+ return el;
4439
+ }
4378
4440
  card(host) {
4379
4441
  const { targetKind } = host.engine.state;
4380
4442
  const icon = this.icons[targetKind];
@@ -4415,7 +4477,7 @@ var SelfstoreAccountElement = class extends FlowWidget {
4415
4477
  // The severity colors the DOT, never the label: a whole line turning
4416
4478
  // amber reads as a warning about the destination's name.
4417
4479
  h("span", { part: `account-dot sev-${status.severity}` }),
4418
- h("span", {}, this.named(host))
4480
+ h("span", { part: "account-trigger-text" }, this.said(host))
4419
4481
  );
4420
4482
  const menu = this.#open ? h(
4421
4483
  "div",