@gryt/core 0.33.0 → 0.33.1

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.
@@ -239,7 +239,10 @@ export function createApproverPairing(options) {
239
239
  }
240
240
  set({ phase: "sending", device, added });
241
241
  await sender.send(tail.take(snap, added), false);
242
- await clock.sleep(options.lateWindowMs ?? HISTORY_LATE_WINDOW_MS, abort.signal);
242
+ // Late old-epoch messages can only come from a group N was just added to.
243
+ if (Object.values(added).some((groups) => groups.length > 0)) {
244
+ await clock.sleep(options.lateWindowMs ?? HISTORY_LATE_WINDOW_MS, abort.signal);
245
+ }
243
246
  await sender.send(tail.take(snap, added), true);
244
247
  // N closes the session once it has every chunk; closing here would delete them.
245
248
  set({ phase: "done", device, added });
@@ -51,6 +51,9 @@ export interface NewDeviceOptions {
51
51
  history?: HistorySink;
52
52
  /** Opens the server the envelope names for the history. Without it, no history comes across. */
53
53
  historyStore?: HistoryStoreOpener;
54
+ /** This device's archive scope for a server. A's records are refiled under it, since the
55
+ desktop files by server key and the phone by host. */
56
+ localScope?: (host: string) => string;
54
57
  }
55
58
  export interface NewDevicePairing {
56
59
  readonly state: NewDeviceState;
@@ -3,6 +3,18 @@ import { sha256 } from "@noble/hashes/sha2.js";
3
3
  import { randomBytes } from "@noble/hashes/utils.js";
4
4
  import { mailbox, openJson, PairingEnded, reasonFor, sealJson, systemClock } from "./channel.js";
5
5
  import { createHistoryReceiver } from "./historyReceiver.js";
6
+ function refiled(sink, servers, localScope) {
7
+ if (!localScope)
8
+ return sink;
9
+ const hosts = new Map(servers.map((s) => [s.scope, s.host]));
10
+ // Asked per batch: the desktop's scope needs the server's key, pinned only once it has joined.
11
+ return {
12
+ put: (records) => sink.put(records.map((r) => {
13
+ const host = hosts.get(r.scope);
14
+ return host === undefined ? r : { ...r, scope: localScope(host) };
15
+ })),
16
+ };
17
+ }
6
18
  export const PAIRING_APPROVAL_MS = 60_000;
7
19
  const PAIRING_OIDC_SCOPE = "openid profile email offline_access";
8
20
  /** N: shows the QR and code, checks the emoji, opens the envelope, signs in, then says ready. */
@@ -162,7 +174,7 @@ export function createNewDevicePairing(options) {
162
174
  if (held?.host && held.transfer && options.history && options.historyStore) {
163
175
  receiver = createHistoryReceiver({
164
176
  store: options.historyStore(held.host, held.transfer),
165
- key: held.key, sink: options.history, clock, signal: abort.signal, onProgress: setHistory,
177
+ key: held.key, sink: refiled(options.history, envelope.servers, options.localScope), clock, signal: abort.signal, onProgress: setHistory,
166
178
  });
167
179
  receiver.listManifest(held.manifest.chunks);
168
180
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gryt/core",
3
- "version": "0.33.0",
3
+ "version": "0.33.1",
4
4
  "description": "Gryt's shared application logic: one implementation of the things the desktop and mobile apps both do.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {