@codemem/core 0.22.0 → 0.22.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.
Files changed (33) hide show
  1. package/dist/better-sqlite-coordinator-runtime.d.ts.map +1 -1
  2. package/dist/coordinator-api-CEbQ6LHE.js +1575 -0
  3. package/dist/coordinator-api-CEbQ6LHE.js.map +1 -0
  4. package/dist/coordinator-api.d.ts +23 -2
  5. package/dist/coordinator-api.d.ts.map +1 -1
  6. package/dist/coordinator-invites.d.ts.map +1 -1
  7. package/dist/coordinator-runtime.d.ts +4 -1
  8. package/dist/coordinator-runtime.d.ts.map +1 -1
  9. package/dist/d1-coordinator-runtime.d.ts +2 -1
  10. package/dist/d1-coordinator-runtime.d.ts.map +1 -1
  11. package/dist/index.d.ts +4 -3
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +3061 -1935
  14. package/dist/index.js.map +1 -1
  15. package/dist/internal/cloudflare-coordinator.d.ts +2 -0
  16. package/dist/internal/cloudflare-coordinator.d.ts.map +1 -1
  17. package/dist/internal/cloudflare-coordinator.js +94 -93
  18. package/dist/internal/cloudflare-coordinator.js.map +1 -1
  19. package/dist/request-rate-limit.d.ts +12 -0
  20. package/dist/request-rate-limit.d.ts.map +1 -0
  21. package/dist/sync-auth-constants.d.ts +2 -0
  22. package/dist/sync-auth-constants.d.ts.map +1 -0
  23. package/dist/sync-auth.d.ts +0 -1
  24. package/dist/sync-auth.d.ts.map +1 -1
  25. package/dist/sync-daemon.d.ts +7 -0
  26. package/dist/sync-daemon.d.ts.map +1 -1
  27. package/dist/sync-fingerprint.d.ts +3 -0
  28. package/dist/sync-fingerprint.d.ts.map +1 -0
  29. package/dist/sync-identity.d.ts +1 -2
  30. package/dist/sync-identity.d.ts.map +1 -1
  31. package/package.json +2 -1
  32. package/dist/coordinator-api-CimbM4rk.js +0 -2482
  33. package/dist/coordinator-api-CimbM4rk.js.map +0 -1
@@ -1,5 +1,7 @@
1
+ export type { CoordinatorRequestVerifier, CoordinatorRuntimeDeps, CoordinatorVerifyRequestInput, CreateCoordinatorAppOptions, } from "../coordinator-api.js";
1
2
  export type { CreateD1CoordinatorAppOptions } from "../d1-coordinator-runtime.js";
2
3
  export { createD1CoordinatorApp } from "../d1-coordinator-runtime.js";
3
4
  export type { D1DatabaseLike, D1PreparedStatementLike } from "../d1-coordinator-store.js";
4
5
  export { D1CoordinatorStore } from "../d1-coordinator-store.js";
6
+ export { DEFAULT_TIME_WINDOW_S } from "../sync-auth-constants.js";
5
7
  //# sourceMappingURL=cloudflare-coordinator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare-coordinator.d.ts","sourceRoot":"","sources":["../../src/internal/cloudflare-coordinator.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"cloudflare-coordinator.d.ts","sourceRoot":"","sources":["../../src/internal/cloudflare-coordinator.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,0BAA0B,EAC1B,sBAAsB,EACtB,6BAA6B,EAC7B,2BAA2B,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -1,10 +1,10 @@
1
- import { t as e } from "../coordinator-api-CimbM4rk.js";
1
+ import { r as e, t } from "../coordinator-api-CEbQ6LHE.js";
2
2
  //#region src/d1-coordinator-store.ts
3
- function t(e) {
3
+ function n(e) {
4
4
  if (e == null) throw Error("expected row");
5
5
  return e;
6
6
  }
7
- function n(e) {
7
+ function r(e) {
8
8
  let t = e.trim();
9
9
  if (!t) return "";
10
10
  let n = t.includes("://") ? t : `http://${t}`;
@@ -15,7 +15,7 @@ function n(e) {
15
15
  return "";
16
16
  }
17
17
  }
18
- function r(e) {
18
+ function i(e) {
19
19
  if (!e) return "";
20
20
  try {
21
21
  let t = new URL(e), n = t.hostname.toLowerCase();
@@ -23,18 +23,18 @@ function r(e) {
23
23
  } catch {}
24
24
  return e;
25
25
  }
26
- function i(e, t) {
27
- let i = [], a = /* @__PURE__ */ new Set();
26
+ function a(e, t) {
27
+ let n = [], a = /* @__PURE__ */ new Set();
28
28
  for (let o of [...e, ...t]) {
29
- let e = n(o), t = r(e);
30
- !e || a.has(t) || (a.add(t), i.push(e));
29
+ let e = r(o), t = i(e);
30
+ !e || a.has(t) || (a.add(t), n.push(e));
31
31
  }
32
- return i;
32
+ return n;
33
33
  }
34
- function a() {
34
+ function o() {
35
35
  return (/* @__PURE__ */ new Date()).toISOString();
36
36
  }
37
- function o(e, t) {
37
+ function s(e, t) {
38
38
  return e <= t ? {
39
39
  low: e,
40
40
  high: t
@@ -43,156 +43,156 @@ function o(e, t) {
43
43
  high: e
44
44
  };
45
45
  }
46
- function s(e) {
46
+ function c(e) {
47
47
  return e instanceof Error && /unique|constraint/i.test(e.message);
48
48
  }
49
- function c(e) {
49
+ function l(e) {
50
50
  let t = new Uint8Array(e);
51
51
  globalThis.crypto.getRandomValues(t);
52
52
  let n = [];
53
53
  for (let e of t) n.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"[e % 64] ?? "A");
54
54
  return n.join("");
55
55
  }
56
- async function l(e) {
56
+ async function u(e) {
57
57
  let t = await e.all();
58
58
  return Array.isArray(t?.results) ? t.results : [];
59
59
  }
60
- async function u(e) {
60
+ async function d(e) {
61
61
  return await e.first();
62
62
  }
63
- async function d(e) {
63
+ async function f(e) {
64
64
  let t = await e.run();
65
65
  return Number(t?.meta?.changes ?? 0);
66
66
  }
67
- var f = class {
67
+ var p = class {
68
68
  db;
69
69
  constructor(e) {
70
70
  this.db = e;
71
71
  }
72
72
  async close() {}
73
73
  async createGroup(e, t) {
74
- await this.db.prepare("INSERT OR IGNORE INTO groups(group_id, display_name, created_at) VALUES (?, ?, ?)").bind(e, t ?? null, a()).run();
74
+ await this.db.prepare("INSERT OR IGNORE INTO groups(group_id, display_name, created_at) VALUES (?, ?, ?)").bind(e, t ?? null, o()).run();
75
75
  }
76
76
  async getGroup(e) {
77
- let n = await u(this.db.prepare("SELECT group_id, display_name, created_at FROM groups WHERE group_id = ?").bind(e));
78
- return n ? t(n) : null;
77
+ let t = await d(this.db.prepare("SELECT group_id, display_name, created_at FROM groups WHERE group_id = ?").bind(e));
78
+ return t ? n(t) : null;
79
79
  }
80
80
  async listGroups() {
81
- return (await l(this.db.prepare("SELECT group_id, display_name, created_at FROM groups ORDER BY created_at ASC"))).map((e) => t(e));
81
+ return (await u(this.db.prepare("SELECT group_id, display_name, created_at FROM groups ORDER BY created_at ASC"))).map((e) => n(e));
82
82
  }
83
83
  async enrollDevice(e, t) {
84
- await this.db.prepare("INSERT INTO enrolled_devices(\n group_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n ) VALUES (?, ?, ?, ?, ?, 1, ?)\n ON CONFLICT(group_id, device_id) DO UPDATE SET\n public_key = excluded.public_key,\n fingerprint = excluded.fingerprint,\n display_name = excluded.display_name,\n enabled = 1").bind(e, t.deviceId, t.publicKey, t.fingerprint, t.displayName ?? null, a()).run();
84
+ await this.db.prepare("INSERT INTO enrolled_devices(\n group_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n ) VALUES (?, ?, ?, ?, ?, 1, ?)\n ON CONFLICT(group_id, device_id) DO UPDATE SET\n public_key = excluded.public_key,\n fingerprint = excluded.fingerprint,\n display_name = excluded.display_name,\n enabled = 1").bind(e, t.deviceId, t.publicKey, t.fingerprint, t.displayName ?? null, o()).run();
85
85
  }
86
- async listEnrolledDevices(e, n) {
87
- let r = n ? "" : "AND enabled = 1";
88
- return (await l(this.db.prepare(`SELECT group_id, device_id, public_key, fingerprint, display_name, enabled, created_at
86
+ async listEnrolledDevices(e, t) {
87
+ let r = t ? "" : "AND enabled = 1";
88
+ return (await u(this.db.prepare(`SELECT group_id, device_id, public_key, fingerprint, display_name, enabled, created_at
89
89
  FROM enrolled_devices
90
90
  WHERE group_id = ? ${r}
91
- ORDER BY created_at ASC, device_id ASC`).bind(e))).map((e) => t(e));
91
+ ORDER BY created_at ASC, device_id ASC`).bind(e))).map((e) => n(e));
92
92
  }
93
- async getEnrollment(e, n) {
94
- let r = await u(this.db.prepare("SELECT group_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n FROM enrolled_devices\n WHERE group_id = ? AND device_id = ? AND enabled = 1").bind(e, n));
95
- return r ? t(r) : null;
93
+ async getEnrollment(e, t) {
94
+ let r = await d(this.db.prepare("SELECT group_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n FROM enrolled_devices\n WHERE group_id = ? AND device_id = ? AND enabled = 1").bind(e, t));
95
+ return r ? n(r) : null;
96
96
  }
97
97
  async renameDevice(e, t, n) {
98
- return await d(this.db.prepare("UPDATE enrolled_devices SET display_name = ?\n WHERE group_id = ? AND device_id = ?").bind(n, e, t)) > 0;
98
+ return await f(this.db.prepare("UPDATE enrolled_devices SET display_name = ?\n WHERE group_id = ? AND device_id = ?").bind(n, e, t)) > 0;
99
99
  }
100
100
  async setDeviceEnabled(e, t, n) {
101
- return await d(this.db.prepare("UPDATE enrolled_devices SET enabled = ?\n WHERE group_id = ? AND device_id = ?").bind(n ? 1 : 0, e, t)) > 0;
101
+ return await f(this.db.prepare("UPDATE enrolled_devices SET enabled = ?\n WHERE group_id = ? AND device_id = ?").bind(n ? 1 : 0, e, t)) > 0;
102
102
  }
103
103
  async removeDevice(e, t) {
104
- return await this.db.prepare("DELETE FROM presence_records WHERE group_id = ? AND device_id = ?").bind(e, t).run(), await this.db.prepare("DELETE FROM coordinator_reciprocal_approvals WHERE group_id = ? AND (requesting_device_id = ? OR requested_device_id = ?)").bind(e, t, t).run(), await d(this.db.prepare("DELETE FROM enrolled_devices WHERE group_id = ? AND device_id = ?").bind(e, t)) > 0;
104
+ return await this.db.prepare("DELETE FROM presence_records WHERE group_id = ? AND device_id = ?").bind(e, t).run(), await this.db.prepare("DELETE FROM coordinator_reciprocal_approvals WHERE group_id = ? AND (requesting_device_id = ? OR requested_device_id = ?)").bind(e, t, t).run(), await f(this.db.prepare("DELETE FROM enrolled_devices WHERE group_id = ? AND device_id = ?").bind(e, t)) > 0;
105
105
  }
106
106
  async recordNonce(e, t, n) {
107
- return await d(this.db.prepare("INSERT OR IGNORE INTO request_nonces(device_id, nonce, created_at) VALUES (?, ?, ?)").bind(e, t, n)) > 0;
107
+ return await f(this.db.prepare("INSERT OR IGNORE INTO request_nonces(device_id, nonce, created_at) VALUES (?, ?, ?)").bind(e, t, n)) > 0;
108
108
  }
109
109
  async cleanupNonces(e) {
110
110
  await this.db.prepare("DELETE FROM request_nonces WHERE created_at < ?").bind(e).run();
111
111
  }
112
112
  async createInvite(e) {
113
- let n = a(), r = c(12), i = c(24), o = await this.getGroup(e.groupId);
114
- return await this.db.prepare("INSERT INTO coordinator_invites(\n invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL)").bind(r, e.groupId, i, e.policy, e.expiresAt, n, e.createdBy ?? null, o?.display_name ?? null).run(), t(await u(this.db.prepare("SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n FROM coordinator_invites WHERE invite_id = ?").bind(r)));
113
+ let t = o(), r = l(12), i = l(24), a = await this.getGroup(e.groupId);
114
+ return await this.db.prepare("INSERT INTO coordinator_invites(\n invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL)").bind(r, e.groupId, i, e.policy, e.expiresAt, t, e.createdBy ?? null, a?.display_name ?? null).run(), n(await d(this.db.prepare("SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n FROM coordinator_invites WHERE invite_id = ?").bind(r)));
115
115
  }
116
116
  async getInviteByToken(e) {
117
- let n = await u(this.db.prepare("SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n FROM coordinator_invites\n WHERE token = ?\n AND revoked_at IS NULL\n AND expires_at > ?").bind(e, a()));
118
- return n ? t(n) : null;
117
+ let t = await d(this.db.prepare("SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n FROM coordinator_invites\n WHERE token = ?\n AND revoked_at IS NULL\n AND expires_at > ?").bind(e, o()));
118
+ return t ? n(t) : null;
119
119
  }
120
120
  async listInvites(e) {
121
- return (await l(this.db.prepare("SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n FROM coordinator_invites WHERE group_id = ?\n ORDER BY created_at DESC").bind(e))).map((e) => t(e));
121
+ return (await u(this.db.prepare("SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n FROM coordinator_invites WHERE group_id = ?\n ORDER BY created_at DESC").bind(e))).map((e) => n(e));
122
122
  }
123
123
  async createJoinRequest(e) {
124
- let n = a(), r = c(12);
125
- return await this.db.prepare("INSERT INTO coordinator_join_requests(\n request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n ) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, NULL, NULL)").bind(r, e.groupId, e.deviceId, e.publicKey, e.fingerprint, e.displayName ?? null, e.token, n).run(), t(await u(this.db.prepare("SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n FROM coordinator_join_requests WHERE request_id = ?").bind(r)));
124
+ let t = o(), r = l(12);
125
+ return await this.db.prepare("INSERT INTO coordinator_join_requests(\n request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n ) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, NULL, NULL)").bind(r, e.groupId, e.deviceId, e.publicKey, e.fingerprint, e.displayName ?? null, e.token, t).run(), n(await d(this.db.prepare("SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n FROM coordinator_join_requests WHERE request_id = ?").bind(r)));
126
126
  }
127
- async listJoinRequests(e, n) {
128
- let r = n ?? "pending";
129
- return (await l(this.db.prepare("SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n FROM coordinator_join_requests\n WHERE group_id = ? AND status = ?\n ORDER BY created_at ASC, device_id ASC").bind(e, r))).map((e) => t(e));
127
+ async listJoinRequests(e, t) {
128
+ let r = t ?? "pending";
129
+ return (await u(this.db.prepare("SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n FROM coordinator_join_requests\n WHERE group_id = ? AND status = ?\n ORDER BY created_at ASC, device_id ASC").bind(e, r))).map((e) => n(e));
130
130
  }
131
131
  async reviewJoinRequest(e) {
132
- let n = await u(this.db.prepare("SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status,\n created_at, reviewed_at, reviewed_by\n FROM coordinator_join_requests WHERE request_id = ?").bind(e.requestId));
133
- if (!n) return null;
134
- if (n.status !== "pending") return {
135
- ...t(n),
132
+ let t = await d(this.db.prepare("SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status,\n created_at, reviewed_at, reviewed_by\n FROM coordinator_join_requests WHERE request_id = ?").bind(e.requestId));
133
+ if (!t) return null;
134
+ if (t.status !== "pending") return {
135
+ ...n(t),
136
136
  _no_transition: !0
137
137
  };
138
- let r = a(), i = e.approved ? "approved" : "denied";
138
+ let r = o(), i = e.approved ? "approved" : "denied";
139
139
  if (this.db.batch) {
140
140
  let t = [];
141
- t.push(this.db.prepare("UPDATE coordinator_join_requests\n SET status = ?, reviewed_at = ?, reviewed_by = ?\n WHERE request_id = ? AND status = 'pending'").bind(i, r, e.reviewedBy ?? null, e.requestId)), e.approved && t.push(this.db.prepare("INSERT INTO enrolled_devices(\n group_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n )\n SELECT group_id, device_id, public_key, fingerprint, display_name, 1, ?\n FROM coordinator_join_requests\n WHERE request_id = ?\n AND status = 'approved'\n AND reviewed_at = ?\n ON CONFLICT(group_id, device_id) DO UPDATE SET\n public_key = excluded.public_key,\n fingerprint = excluded.fingerprint,\n display_name = excluded.display_name,\n enabled = 1").bind(a(), e.requestId, r)), await this.db.batch(t);
141
+ t.push(this.db.prepare("UPDATE coordinator_join_requests\n SET status = ?, reviewed_at = ?, reviewed_by = ?\n WHERE request_id = ? AND status = 'pending'").bind(i, r, e.reviewedBy ?? null, e.requestId)), e.approved && t.push(this.db.prepare("INSERT INTO enrolled_devices(\n group_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n )\n SELECT group_id, device_id, public_key, fingerprint, display_name, 1, ?\n FROM coordinator_join_requests\n WHERE request_id = ?\n AND status = 'approved'\n AND reviewed_at = ?\n ON CONFLICT(group_id, device_id) DO UPDATE SET\n public_key = excluded.public_key,\n fingerprint = excluded.fingerprint,\n display_name = excluded.display_name,\n enabled = 1").bind(o(), e.requestId, r)), await this.db.batch(t);
142
142
  } else {
143
- if (await d(this.db.prepare("UPDATE coordinator_join_requests\n SET status = ?, reviewed_at = ?, reviewed_by = ?\n WHERE request_id = ? AND status = 'pending'").bind(i, r, e.reviewedBy ?? null, e.requestId)) === 0) {
144
- let n = await u(this.db.prepare("SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n FROM coordinator_join_requests WHERE request_id = ?").bind(e.requestId));
145
- return n ? {
146
- ...t(n),
143
+ if (await f(this.db.prepare("UPDATE coordinator_join_requests\n SET status = ?, reviewed_at = ?, reviewed_by = ?\n WHERE request_id = ? AND status = 'pending'").bind(i, r, e.reviewedBy ?? null, e.requestId)) === 0) {
144
+ let t = await d(this.db.prepare("SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n FROM coordinator_join_requests WHERE request_id = ?").bind(e.requestId));
145
+ return t ? {
146
+ ...n(t),
147
147
  _no_transition: !0
148
148
  } : null;
149
149
  }
150
- e.approved && await this.enrollDevice(n.group_id, {
151
- deviceId: n.device_id,
152
- fingerprint: n.fingerprint,
153
- publicKey: n.public_key,
154
- displayName: (n.display_name ?? "").trim() || null
150
+ e.approved && await this.enrollDevice(t.group_id, {
151
+ deviceId: t.device_id,
152
+ fingerprint: t.fingerprint,
153
+ publicKey: t.public_key,
154
+ displayName: (t.display_name ?? "").trim() || null
155
155
  });
156
156
  }
157
- let o = await u(this.db.prepare("SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n FROM coordinator_join_requests WHERE request_id = ?").bind(e.requestId));
158
- return o ? t(o) : null;
157
+ let a = await d(this.db.prepare("SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n FROM coordinator_join_requests WHERE request_id = ?").bind(e.requestId));
158
+ return a ? n(a) : null;
159
159
  }
160
160
  async createReciprocalApproval(e) {
161
- let n = e.groupId.trim(), r = e.requestingDeviceId.trim(), i = e.requestedDeviceId.trim();
162
- if (!n || !r || !i) throw Error("groupId, requestingDeviceId, and requestedDeviceId are required.");
161
+ let t = e.groupId.trim(), r = e.requestingDeviceId.trim(), i = e.requestedDeviceId.trim();
162
+ if (!t || !r || !i) throw Error("groupId, requestingDeviceId, and requestedDeviceId are required.");
163
163
  if (r === i) throw Error("requesting and requested device ids must differ.");
164
- let l = o(r, i), d = await u(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals\n WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n ORDER BY created_at DESC LIMIT 1").bind(n, r, i));
165
- if (d) return t(d);
166
- let f = await u(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals\n WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n ORDER BY created_at DESC LIMIT 1").bind(n, i, r));
164
+ let a = s(r, i), u = await d(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals\n WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n ORDER BY created_at DESC LIMIT 1").bind(t, r, i));
165
+ if (u) return n(u);
166
+ let f = await d(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals\n WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n ORDER BY created_at DESC LIMIT 1").bind(t, i, r));
167
167
  if (f) {
168
- let e = a();
169
- return await this.db.prepare("UPDATE coordinator_reciprocal_approvals SET status = 'completed', resolved_at = ? WHERE request_id = ?").bind(e, f.request_id).run(), t(await u(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals WHERE request_id = ?").bind(f.request_id)));
168
+ let e = o();
169
+ return await this.db.prepare("UPDATE coordinator_reciprocal_approvals SET status = 'completed', resolved_at = ? WHERE request_id = ?").bind(e, f.request_id).run(), n(await d(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals WHERE request_id = ?").bind(f.request_id)));
170
170
  }
171
- let p = c(12), m = a();
171
+ let p = l(12), m = o();
172
172
  try {
173
- await this.db.prepare("INSERT INTO coordinator_reciprocal_approvals(\n request_id,\n group_id,\n requesting_device_id,\n requested_device_id,\n pending_pair_low_device_id,\n pending_pair_high_device_id,\n status,\n created_at,\n resolved_at\n ) VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, NULL)").bind(p, n, r, i, l.low, l.high, m).run();
173
+ await this.db.prepare("INSERT INTO coordinator_reciprocal_approvals(\n request_id,\n group_id,\n requesting_device_id,\n requested_device_id,\n pending_pair_low_device_id,\n pending_pair_high_device_id,\n status,\n created_at,\n resolved_at\n ) VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, NULL)").bind(p, t, r, i, a.low, a.high, m).run();
174
174
  } catch (e) {
175
- if (!s(e)) throw e;
176
- let o = await u(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals\n WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n ORDER BY created_at DESC LIMIT 1").bind(n, r, i));
177
- if (o) return t(o);
178
- let c = await u(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals\n WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n ORDER BY created_at DESC LIMIT 1").bind(n, i, r));
179
- if (c) {
180
- let e = a();
181
- return await this.db.prepare("UPDATE coordinator_reciprocal_approvals SET status = 'completed', resolved_at = ? WHERE request_id = ?").bind(e, c.request_id).run(), t(await u(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals WHERE request_id = ?").bind(c.request_id)));
175
+ if (!c(e)) throw e;
176
+ let a = await d(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals\n WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n ORDER BY created_at DESC LIMIT 1").bind(t, r, i));
177
+ if (a) return n(a);
178
+ let s = await d(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals\n WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n ORDER BY created_at DESC LIMIT 1").bind(t, i, r));
179
+ if (s) {
180
+ let e = o();
181
+ return await this.db.prepare("UPDATE coordinator_reciprocal_approvals SET status = 'completed', resolved_at = ? WHERE request_id = ?").bind(e, s.request_id).run(), n(await d(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals WHERE request_id = ?").bind(s.request_id)));
182
182
  }
183
183
  throw e;
184
184
  }
185
- return t(await u(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals WHERE request_id = ?").bind(p)));
185
+ return n(await d(this.db.prepare("SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n FROM coordinator_reciprocal_approvals WHERE request_id = ?").bind(p)));
186
186
  }
187
187
  async listReciprocalApprovals(e) {
188
- let n = e.direction === "incoming" ? "requested_device_id" : "requesting_device_id", r = e.status?.trim() || "pending";
189
- return (await l(this.db.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at
188
+ let t = e.direction === "incoming" ? "requested_device_id" : "requesting_device_id", r = e.status?.trim() || "pending";
189
+ return (await u(this.db.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at
190
190
  FROM coordinator_reciprocal_approvals
191
- WHERE group_id = ? AND ${n} = ? AND status = ?
192
- ORDER BY created_at ASC, request_id ASC`).bind(e.groupId, e.deviceId, r))).map((e) => t(e));
191
+ WHERE group_id = ? AND ${t} = ? AND status = ?
192
+ ORDER BY created_at ASC, request_id ASC`).bind(e.groupId, e.deviceId, r))).map((e) => n(e));
193
193
  }
194
194
  async upsertPresence(e) {
195
- let t = /* @__PURE__ */ new Date(), n = new Date(t.getTime() + e.ttlS * 1e3).toISOString(), r = i([], e.addresses);
195
+ let t = /* @__PURE__ */ new Date(), n = new Date(t.getTime() + e.ttlS * 1e3).toISOString(), r = a([], e.addresses);
196
196
  return await this.db.prepare("INSERT INTO presence_records(group_id, device_id, addresses_json, last_seen_at, expires_at, capabilities_json)\n VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT(group_id, device_id) DO UPDATE SET\n addresses_json = excluded.addresses_json,\n last_seen_at = excluded.last_seen_at,\n expires_at = excluded.expires_at,\n capabilities_json = excluded.capabilities_json").bind(e.groupId, e.deviceId, JSON.stringify(r), t.toISOString(), n, JSON.stringify(e.capabilities ?? {})).run(), {
197
197
  group_id: e.groupId,
198
198
  device_id: e.deviceId,
@@ -201,20 +201,20 @@ var f = class {
201
201
  };
202
202
  }
203
203
  async listGroupPeers(e, t) {
204
- let n = a();
205
- return (await l(this.db.prepare("SELECT enrolled_devices.device_id, enrolled_devices.public_key, enrolled_devices.fingerprint, enrolled_devices.display_name,\n presence_records.addresses_json, presence_records.last_seen_at, presence_records.expires_at,\n presence_records.capabilities_json\n FROM enrolled_devices\n LEFT JOIN presence_records\n ON presence_records.group_id = enrolled_devices.group_id\n AND presence_records.device_id = enrolled_devices.device_id\n WHERE enrolled_devices.group_id = ?\n AND enrolled_devices.enabled = 1\n AND enrolled_devices.device_id != ?\n ORDER BY enrolled_devices.device_id ASC").bind(e, t))).map((e) => {
204
+ let n = o();
205
+ return (await u(this.db.prepare("SELECT enrolled_devices.device_id, enrolled_devices.public_key, enrolled_devices.fingerprint, enrolled_devices.display_name,\n presence_records.addresses_json, presence_records.last_seen_at, presence_records.expires_at,\n presence_records.capabilities_json\n FROM enrolled_devices\n LEFT JOIN presence_records\n ON presence_records.group_id = enrolled_devices.group_id\n AND presence_records.device_id = enrolled_devices.device_id\n WHERE enrolled_devices.group_id = ?\n AND enrolled_devices.enabled = 1\n AND enrolled_devices.device_id != ?\n ORDER BY enrolled_devices.device_id ASC").bind(e, t))).map((e) => {
206
206
  let t = String(e.expires_at ?? "").trim(), r = !0;
207
207
  if (t) {
208
208
  let e = new Date(t);
209
209
  r = Number.isNaN(e.getTime()) || e.toISOString() <= n;
210
210
  }
211
- let a = r ? [] : i([], JSON.parse(String(e.addresses_json ?? "[]")));
211
+ let i = r ? [] : a([], JSON.parse(String(e.addresses_json ?? "[]")));
212
212
  return {
213
213
  device_id: String(e.device_id ?? ""),
214
214
  public_key: String(e.public_key ?? ""),
215
215
  fingerprint: String(e.fingerprint ?? ""),
216
216
  display_name: e.display_name ?? null,
217
- addresses: a,
217
+ addresses: i,
218
218
  last_seen_at: e.last_seen_at ?? null,
219
219
  expires_at: e.expires_at ?? null,
220
220
  stale: r,
@@ -225,16 +225,17 @@ var f = class {
225
225
  };
226
226
  //#endregion
227
227
  //#region src/d1-coordinator-runtime.ts
228
- function p(t) {
229
- return e({
230
- storeFactory: () => new f(t.db),
228
+ function m(e) {
229
+ return t({
230
+ storeFactory: () => new p(e.db),
231
231
  runtime: {
232
- adminSecret: () => String(t.adminSecret ?? "").trim() || null,
233
- now: t.now ?? (() => (/* @__PURE__ */ new Date()).toISOString())
234
- }
232
+ adminSecret: () => String(e.adminSecret ?? "").trim() || null,
233
+ now: e.now ?? (() => (/* @__PURE__ */ new Date()).toISOString())
234
+ },
235
+ requestVerifier: e.requestVerifier
235
236
  });
236
237
  }
237
238
  //#endregion
238
- export { f as D1CoordinatorStore, p as createD1CoordinatorApp };
239
+ export { p as D1CoordinatorStore, e as DEFAULT_TIME_WINDOW_S, m as createD1CoordinatorApp };
239
240
 
240
241
  //# sourceMappingURL=cloudflare-coordinator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare-coordinator.js","names":[],"sources":["../../src/d1-coordinator-store.ts","../../src/d1-coordinator-runtime.ts"],"sourcesContent":["import type {\n\tCoordinatorCreateInviteInput,\n\tCoordinatorCreateJoinRequestInput,\n\tCoordinatorCreateReciprocalApprovalInput,\n\tCoordinatorEnrollDeviceInput,\n\tCoordinatorEnrollment,\n\tCoordinatorGroup,\n\tCoordinatorInvite,\n\tCoordinatorJoinRequest,\n\tCoordinatorJoinRequestReviewResult,\n\tCoordinatorListReciprocalApprovalsInput,\n\tCoordinatorPeerRecord,\n\tCoordinatorPresenceRecord,\n\tCoordinatorReciprocalApproval,\n\tCoordinatorReviewJoinRequestInput,\n\tCoordinatorStore,\n\tCoordinatorUpsertPresenceInput,\n} from \"./coordinator-store-contract.js\";\n\ninterface D1RunResultLike {\n\tmeta?: {\n\t\tchanges?: number;\n\t};\n}\n\n/**\n * Experimental D1 adapter scaffold.\n *\n * This is intentionally internal for now while the sync-only store contract\n * is still being validated against an async backend shape.\n */\n\nexport interface D1PreparedStatementLike {\n\tbind(...values: unknown[]): D1PreparedStatementLike;\n\tfirst<T = unknown>(): Promise<T | null>;\n\trun(): Promise<unknown>;\n\tall<T = unknown>(): Promise<{ results?: T[] }>;\n\traw<T = unknown>(): Promise<T[]>;\n}\n\nexport interface D1DatabaseLike {\n\tprepare(query: string): D1PreparedStatementLike;\n\tbatch?(statements: D1PreparedStatementLike[]): Promise<unknown[]>;\n\texec?(query: string): Promise<unknown>;\n}\n\nfunction notImplemented(method: string): never {\n\tthrow new Error(`D1CoordinatorStore.${method} is not implemented yet.`);\n}\n\nfunction rowToRecord<T>(row: unknown): T {\n\tif (row == null) throw new Error(\"expected row\");\n\treturn row as T;\n}\n\nfunction normalizeAddress(address: string): string {\n\tconst value = address.trim();\n\tif (!value) return \"\";\n\tconst withScheme = value.includes(\"://\") ? value : `http://${value}`;\n\ttry {\n\t\tconst url = new URL(withScheme);\n\t\tif (!url.hostname) return \"\";\n\t\tif (url.port && (Number(url.port) <= 0 || Number(url.port) > 65535)) return \"\";\n\t\treturn url.origin + url.pathname.replace(/\\/+$/, \"\");\n\t} catch {\n\t\treturn \"\";\n\t}\n}\n\nfunction addressDedupeKey(address: string): string {\n\tif (!address) return \"\";\n\ttry {\n\t\tconst parsed = new URL(address);\n\t\tconst host = parsed.hostname.toLowerCase();\n\t\tif (\n\t\t\t(parsed.protocol === \"http:\" || parsed.protocol === \"\") &&\n\t\t\thost &&\n\t\t\tparsed.port &&\n\t\t\tparsed.pathname === \"/\"\n\t\t) {\n\t\t\treturn `${host}:${parsed.port}`;\n\t\t}\n\t} catch {}\n\treturn address;\n}\n\nfunction mergeAddresses(existing: string[], candidates: string[]): string[] {\n\tconst normalized: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const address of [...existing, ...candidates]) {\n\t\tconst cleaned = normalizeAddress(address);\n\t\tconst key = addressDedupeKey(cleaned);\n\t\tif (!cleaned || seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tnormalized.push(cleaned);\n\t}\n\treturn normalized;\n}\n\nfunction nowISO(): string {\n\treturn new Date().toISOString();\n}\n\nfunction reciprocalPendingPair(\n\trequestingDeviceId: string,\n\trequestedDeviceId: string,\n): {\n\tlow: string;\n\thigh: string;\n} {\n\treturn requestingDeviceId <= requestedDeviceId\n\t\t? { low: requestingDeviceId, high: requestedDeviceId }\n\t\t: { low: requestedDeviceId, high: requestingDeviceId };\n}\n\nfunction isUniqueConstraintError(error: unknown): boolean {\n\treturn error instanceof Error && /unique|constraint/i.test(error.message);\n}\n\nfunction tokenUrlSafe(bytes: number): string {\n\tconst alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\tconst random = new Uint8Array(bytes);\n\tglobalThis.crypto.getRandomValues(random);\n\tconst output: string[] = [];\n\tfor (const byte of random) {\n\t\toutput.push(alphabet[byte % alphabet.length] ?? \"A\");\n\t}\n\treturn output.join(\"\");\n}\n\nasync function allRows<T>(statement: D1PreparedStatementLike): Promise<T[]> {\n\tconst result = await statement.all<T>();\n\treturn Array.isArray(result?.results) ? result.results : [];\n}\n\nasync function firstRow<T>(statement: D1PreparedStatementLike): Promise<T | null> {\n\treturn await statement.first<T>();\n}\n\nasync function runChanges(statement: D1PreparedStatementLike): Promise<number> {\n\tconst result = (await statement.run()) as D1RunResultLike | undefined;\n\treturn Number(result?.meta?.changes ?? 0);\n}\n\nexport class D1CoordinatorStore implements CoordinatorStore {\n\treadonly db: D1DatabaseLike;\n\n\tconstructor(db: D1DatabaseLike) {\n\t\tthis.db = db;\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// No-op for D1 bindings.\n\t}\n\n\tasync createGroup(_groupId: string, _displayName?: string | null): Promise<void> {\n\t\tawait this.db\n\t\t\t.prepare(\"INSERT OR IGNORE INTO groups(group_id, display_name, created_at) VALUES (?, ?, ?)\")\n\t\t\t.bind(_groupId, _displayName ?? null, nowISO())\n\t\t\t.run();\n\t}\n\n\tasync getGroup(_groupId: string): Promise<CoordinatorGroup | null> {\n\t\tconst row = await firstRow<CoordinatorGroup>(\n\t\t\tthis.db\n\t\t\t\t.prepare(\"SELECT group_id, display_name, created_at FROM groups WHERE group_id = ?\")\n\t\t\t\t.bind(_groupId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorGroup>(row) : null;\n\t}\n\n\tasync listGroups(): Promise<CoordinatorGroup[]> {\n\t\treturn (\n\t\t\tawait allRows<CoordinatorGroup>(\n\t\t\t\tthis.db.prepare(\n\t\t\t\t\t\"SELECT group_id, display_name, created_at FROM groups ORDER BY created_at ASC\",\n\t\t\t\t),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorGroup>(row));\n\t}\n\n\tasync enrollDevice(_groupId: string, _opts: CoordinatorEnrollDeviceInput): Promise<void> {\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO enrolled_devices(\n\t\t\t\tgroup_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n\t\t\t) VALUES (?, ?, ?, ?, ?, 1, ?)\n\t\t\tON CONFLICT(group_id, device_id) DO UPDATE SET\n\t\t\t\tpublic_key = excluded.public_key,\n\t\t\t\tfingerprint = excluded.fingerprint,\n\t\t\t\tdisplay_name = excluded.display_name,\n\t\t\t\tenabled = 1`)\n\t\t\t.bind(\n\t\t\t\t_groupId,\n\t\t\t\t_opts.deviceId,\n\t\t\t\t_opts.publicKey,\n\t\t\t\t_opts.fingerprint,\n\t\t\t\t_opts.displayName ?? null,\n\t\t\t\tnowISO(),\n\t\t\t)\n\t\t\t.run();\n\t}\n\n\tasync listEnrolledDevices(\n\t\t_groupId: string,\n\t\t_includeDisabled?: boolean,\n\t): Promise<CoordinatorEnrollment[]> {\n\t\tconst where = _includeDisabled ? \"\" : \"AND enabled = 1\";\n\t\treturn (\n\t\t\tawait allRows<CoordinatorEnrollment>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT group_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n\t\t\t\t\t\t FROM enrolled_devices\n\t\t\t\t\t\t WHERE group_id = ? ${where}\n\t\t\t\t\t\t ORDER BY created_at ASC, device_id ASC`)\n\t\t\t\t\t.bind(_groupId),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorEnrollment>(row));\n\t}\n\n\tasync getEnrollment(_groupId: string, _deviceId: string): Promise<CoordinatorEnrollment | null> {\n\t\tconst row = await firstRow<CoordinatorEnrollment>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT group_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n\t\t\t\t\t FROM enrolled_devices\n\t\t\t\t\t WHERE group_id = ? AND device_id = ? AND enabled = 1`)\n\t\t\t\t.bind(_groupId, _deviceId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorEnrollment>(row) : null;\n\t}\n\n\tasync renameDevice(_groupId: string, _deviceId: string, _displayName: string): Promise<boolean> {\n\t\treturn (\n\t\t\t(await runChanges(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`UPDATE enrolled_devices SET display_name = ?\n\t\t\t\t\t WHERE group_id = ? AND device_id = ?`)\n\t\t\t\t\t.bind(_displayName, _groupId, _deviceId),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync setDeviceEnabled(_groupId: string, _deviceId: string, _enabled: boolean): Promise<boolean> {\n\t\treturn (\n\t\t\t(await runChanges(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`UPDATE enrolled_devices SET enabled = ?\n\t\t\t\t\t WHERE group_id = ? AND device_id = ?`)\n\t\t\t\t\t.bind(_enabled ? 1 : 0, _groupId, _deviceId),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync removeDevice(_groupId: string, _deviceId: string): Promise<boolean> {\n\t\tawait this.db\n\t\t\t.prepare(\"DELETE FROM presence_records WHERE group_id = ? AND device_id = ?\")\n\t\t\t.bind(_groupId, _deviceId)\n\t\t\t.run();\n\t\tawait this.db\n\t\t\t.prepare(\n\t\t\t\t\"DELETE FROM coordinator_reciprocal_approvals WHERE group_id = ? AND (requesting_device_id = ? OR requested_device_id = ?)\",\n\t\t\t)\n\t\t\t.bind(_groupId, _deviceId, _deviceId)\n\t\t\t.run();\n\t\treturn (\n\t\t\t(await runChanges(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(\"DELETE FROM enrolled_devices WHERE group_id = ? AND device_id = ?\")\n\t\t\t\t\t.bind(_groupId, _deviceId),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync recordNonce(_deviceId: string, _nonce: string, _createdAt: string): Promise<boolean> {\n\t\treturn (\n\t\t\t(await runChanges(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\"INSERT OR IGNORE INTO request_nonces(device_id, nonce, created_at) VALUES (?, ?, ?)\",\n\t\t\t\t\t)\n\t\t\t\t\t.bind(_deviceId, _nonce, _createdAt),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync cleanupNonces(_cutoff: string): Promise<void> {\n\t\tawait this.db.prepare(\"DELETE FROM request_nonces WHERE created_at < ?\").bind(_cutoff).run();\n\t}\n\n\tasync createInvite(_opts: CoordinatorCreateInviteInput): Promise<CoordinatorInvite> {\n\t\tconst now = nowISO();\n\t\tconst inviteId = tokenUrlSafe(12);\n\t\tconst token = tokenUrlSafe(24);\n\t\tconst group = await this.getGroup(_opts.groupId);\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO coordinator_invites(\n\t\t\t\tinvite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL)`)\n\t\t\t.bind(\n\t\t\t\tinviteId,\n\t\t\t\t_opts.groupId,\n\t\t\t\ttoken,\n\t\t\t\t_opts.policy,\n\t\t\t\t_opts.expiresAt,\n\t\t\t\tnow,\n\t\t\t\t_opts.createdBy ?? null,\n\t\t\t\tgroup?.display_name ?? null,\n\t\t\t)\n\t\t\t.run();\n\t\tconst row = await firstRow<CoordinatorInvite>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t\t FROM coordinator_invites WHERE invite_id = ?`)\n\t\t\t\t.bind(inviteId),\n\t\t);\n\t\treturn rowToRecord<CoordinatorInvite>(row);\n\t}\n\n\tasync getInviteByToken(_token: string): Promise<CoordinatorInvite | null> {\n\t\tconst row = await firstRow<CoordinatorInvite>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t\t FROM coordinator_invites\n\t\t\t\t\t WHERE token = ?\n\t\t\t\t\t AND revoked_at IS NULL\n\t\t\t\t\t AND expires_at > ?`)\n\t\t\t\t.bind(_token, nowISO()),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorInvite>(row) : null;\n\t}\n\n\tasync listInvites(_groupId: string): Promise<CoordinatorInvite[]> {\n\t\treturn (\n\t\t\tawait allRows<CoordinatorInvite>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t\t FROM coordinator_invites WHERE group_id = ?\n\t\t\t\t\t ORDER BY created_at DESC`)\n\t\t\t\t\t.bind(_groupId),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorInvite>(row));\n\t}\n\n\tasync createJoinRequest(\n\t\t_opts: CoordinatorCreateJoinRequestInput,\n\t): Promise<CoordinatorJoinRequest> {\n\t\tconst now = nowISO();\n\t\tconst requestId = tokenUrlSafe(12);\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO coordinator_join_requests(\n\t\t\t\trequest_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, NULL, NULL)`)\n\t\t\t.bind(\n\t\t\t\trequestId,\n\t\t\t\t_opts.groupId,\n\t\t\t\t_opts.deviceId,\n\t\t\t\t_opts.publicKey,\n\t\t\t\t_opts.fingerprint,\n\t\t\t\t_opts.displayName ?? null,\n\t\t\t\t_opts.token,\n\t\t\t\tnow,\n\t\t\t)\n\t\t\t.run();\n\t\tconst row = await firstRow<CoordinatorJoinRequest>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`)\n\t\t\t\t.bind(requestId),\n\t\t);\n\t\treturn rowToRecord<CoordinatorJoinRequest>(row);\n\t}\n\n\tasync listJoinRequests(_groupId: string, _status?: string): Promise<CoordinatorJoinRequest[]> {\n\t\tconst status = _status ?? \"pending\";\n\t\treturn (\n\t\t\tawait allRows<CoordinatorJoinRequest>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t\t\t FROM coordinator_join_requests\n\t\t\t\t\t\t WHERE group_id = ? AND status = ?\n\t\t\t\t\t\t ORDER BY created_at ASC, device_id ASC`)\n\t\t\t\t\t.bind(_groupId, status),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorJoinRequest>(row));\n\t}\n\n\tasync reviewJoinRequest(\n\t\t_opts: CoordinatorReviewJoinRequestInput,\n\t): Promise<CoordinatorJoinRequestReviewResult | null> {\n\t\tconst row = await firstRow<CoordinatorJoinRequest & { public_key: string }>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status,\n\t\t\t\t\t created_at, reviewed_at, reviewed_by\n\t\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`)\n\t\t\t\t.bind(_opts.requestId),\n\t\t);\n\t\tif (!row) return null;\n\t\tif (row.status !== \"pending\") {\n\t\t\treturn { ...rowToRecord<CoordinatorJoinRequest>(row), _no_transition: true };\n\t\t}\n\t\tconst reviewedAt = nowISO();\n\t\tconst nextStatus = _opts.approved ? \"approved\" : \"denied\";\n\t\tif (this.db.batch) {\n\t\t\tconst statements: D1PreparedStatementLike[] = [];\n\t\t\tstatements.push(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`UPDATE coordinator_join_requests\n\t\t\t\t\t\t SET status = ?, reviewed_at = ?, reviewed_by = ?\n\t\t\t\t\t\t WHERE request_id = ? AND status = 'pending'`)\n\t\t\t\t\t.bind(nextStatus, reviewedAt, _opts.reviewedBy ?? null, _opts.requestId),\n\t\t\t);\n\t\t\tif (_opts.approved) {\n\t\t\t\tstatements.push(\n\t\t\t\t\tthis.db\n\t\t\t\t\t\t.prepare(`INSERT INTO enrolled_devices(\n\t\t\t\t\t\t\tgroup_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n\t\t\t\t\t\t)\n\t\t\t\t\t\tSELECT group_id, device_id, public_key, fingerprint, display_name, 1, ?\n\t\t\t\t\t\tFROM coordinator_join_requests\n\t\t\t\t\t\tWHERE request_id = ?\n\t\t\t\t\t\t AND status = 'approved'\n\t\t\t\t\t\t AND reviewed_at = ?\n\t\t\t\t\t\tON CONFLICT(group_id, device_id) DO UPDATE SET\n\t\t\t\t\t\t\tpublic_key = excluded.public_key,\n\t\t\t\t\t\t\tfingerprint = excluded.fingerprint,\n\t\t\t\t\t\t\tdisplay_name = excluded.display_name,\n\t\t\t\t\t\t\tenabled = 1`)\n\t\t\t\t\t\t.bind(nowISO(), _opts.requestId, reviewedAt),\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait this.db.batch(statements);\n\t\t} else {\n\t\t\tconst changes = await runChanges(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`UPDATE coordinator_join_requests\n\t\t\t\t\t\t SET status = ?, reviewed_at = ?, reviewed_by = ?\n\t\t\t\t\t\t WHERE request_id = ? AND status = 'pending'`)\n\t\t\t\t\t.bind(nextStatus, reviewedAt, _opts.reviewedBy ?? null, _opts.requestId),\n\t\t\t);\n\t\t\tif (changes === 0) {\n\t\t\t\tconst latest = await firstRow<CoordinatorJoinRequestReviewResult>(\n\t\t\t\t\tthis.db\n\t\t\t\t\t\t.prepare(`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`)\n\t\t\t\t\t\t.bind(_opts.requestId),\n\t\t\t\t);\n\t\t\t\treturn latest\n\t\t\t\t\t? { ...rowToRecord<CoordinatorJoinRequestReviewResult>(latest), _no_transition: true }\n\t\t\t\t\t: null;\n\t\t\t}\n\t\t\tif (_opts.approved) {\n\t\t\t\tawait this.enrollDevice(row.group_id, {\n\t\t\t\t\tdeviceId: row.device_id,\n\t\t\t\t\tfingerprint: row.fingerprint,\n\t\t\t\t\tpublicKey: row.public_key,\n\t\t\t\t\tdisplayName: (row.display_name ?? \"\").trim() || null,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tconst updated = await firstRow<CoordinatorJoinRequestReviewResult>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`)\n\t\t\t\t.bind(_opts.requestId),\n\t\t);\n\t\treturn updated ? rowToRecord<CoordinatorJoinRequestReviewResult>(updated) : null;\n\t}\n\n\tasync createReciprocalApproval(\n\t\topts: CoordinatorCreateReciprocalApprovalInput,\n\t): Promise<CoordinatorReciprocalApproval> {\n\t\tconst groupId = opts.groupId.trim();\n\t\tconst requestingDeviceId = opts.requestingDeviceId.trim();\n\t\tconst requestedDeviceId = opts.requestedDeviceId.trim();\n\t\tif (!groupId || !requestingDeviceId || !requestedDeviceId) {\n\t\t\tthrow new Error(\"groupId, requestingDeviceId, and requestedDeviceId are required.\");\n\t\t}\n\t\tif (requestingDeviceId === requestedDeviceId) {\n\t\t\tthrow new Error(\"requesting and requested device ids must differ.\");\n\t\t}\n\t\tconst pendingPair = reciprocalPendingPair(requestingDeviceId, requestedDeviceId);\n\t\tconst existing = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t FROM coordinator_reciprocal_approvals\n\t\t\t\t\t WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n\t\t\t\t\t ORDER BY created_at DESC LIMIT 1`)\n\t\t\t\t.bind(groupId, requestingDeviceId, requestedDeviceId),\n\t\t);\n\t\tif (existing) return rowToRecord<CoordinatorReciprocalApproval>(existing);\n\t\tconst reverse = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t FROM coordinator_reciprocal_approvals\n\t\t\t\t\t WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n\t\t\t\t\t ORDER BY created_at DESC LIMIT 1`)\n\t\t\t\t.bind(groupId, requestedDeviceId, requestingDeviceId),\n\t\t);\n\t\tif (reverse) {\n\t\t\tconst resolvedAt = nowISO();\n\t\t\tawait this.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`UPDATE coordinator_reciprocal_approvals SET status = 'completed', resolved_at = ? WHERE request_id = ?`,\n\t\t\t\t)\n\t\t\t\t.bind(resolvedAt, reverse.request_id)\n\t\t\t\t.run();\n\t\t\tconst completed = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t\t FROM coordinator_reciprocal_approvals WHERE request_id = ?`)\n\t\t\t\t\t.bind(reverse.request_id),\n\t\t\t);\n\t\t\treturn rowToRecord<CoordinatorReciprocalApproval>(completed);\n\t\t}\n\t\tconst requestId = tokenUrlSafe(12);\n\t\tconst createdAt = nowISO();\n\t\ttry {\n\t\t\tawait this.db\n\t\t\t\t.prepare(`INSERT INTO coordinator_reciprocal_approvals(\n\t\t\t\t\t\trequest_id,\n\t\t\t\t\t\tgroup_id,\n\t\t\t\t\t\trequesting_device_id,\n\t\t\t\t\t\trequested_device_id,\n\t\t\t\t\t\tpending_pair_low_device_id,\n\t\t\t\t\t\tpending_pair_high_device_id,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\tcreated_at,\n\t\t\t\t\t\tresolved_at\n\t\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, NULL)`)\n\t\t\t\t.bind(\n\t\t\t\t\trequestId,\n\t\t\t\t\tgroupId,\n\t\t\t\t\trequestingDeviceId,\n\t\t\t\t\trequestedDeviceId,\n\t\t\t\t\tpendingPair.low,\n\t\t\t\t\tpendingPair.high,\n\t\t\t\t\tcreatedAt,\n\t\t\t\t)\n\t\t\t\t.run();\n\t\t} catch (error) {\n\t\t\tif (!isUniqueConstraintError(error)) throw error;\n\t\t\tconst sameDirection = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t\t FROM coordinator_reciprocal_approvals\n\t\t\t\t\t\t WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n\t\t\t\t\t\t ORDER BY created_at DESC LIMIT 1`)\n\t\t\t\t\t.bind(groupId, requestingDeviceId, requestedDeviceId),\n\t\t\t);\n\t\t\tif (sameDirection) return rowToRecord<CoordinatorReciprocalApproval>(sameDirection);\n\t\t\tconst reverseAfterConflict = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t\t FROM coordinator_reciprocal_approvals\n\t\t\t\t\t\t WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n\t\t\t\t\t\t ORDER BY created_at DESC LIMIT 1`)\n\t\t\t\t\t.bind(groupId, requestedDeviceId, requestingDeviceId),\n\t\t\t);\n\t\t\tif (reverseAfterConflict) {\n\t\t\t\tconst resolvedAt = nowISO();\n\t\t\t\tawait this.db\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t`UPDATE coordinator_reciprocal_approvals SET status = 'completed', resolved_at = ? WHERE request_id = ?`,\n\t\t\t\t\t)\n\t\t\t\t\t.bind(resolvedAt, reverseAfterConflict.request_id)\n\t\t\t\t\t.run();\n\t\t\t\tconst completed = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\t\t\tthis.db\n\t\t\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t\t\t FROM coordinator_reciprocal_approvals WHERE request_id = ?`)\n\t\t\t\t\t\t.bind(reverseAfterConflict.request_id),\n\t\t\t\t);\n\t\t\t\treturn rowToRecord<CoordinatorReciprocalApproval>(completed);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tconst created = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t FROM coordinator_reciprocal_approvals WHERE request_id = ?`)\n\t\t\t\t.bind(requestId),\n\t\t);\n\t\treturn rowToRecord<CoordinatorReciprocalApproval>(created);\n\t}\n\n\tasync listReciprocalApprovals(\n\t\topts: CoordinatorListReciprocalApprovalsInput,\n\t): Promise<CoordinatorReciprocalApproval[]> {\n\t\tconst directionColumn =\n\t\t\topts.direction === \"incoming\" ? \"requested_device_id\" : \"requesting_device_id\";\n\t\tconst status = opts.status?.trim() || \"pending\";\n\t\treturn (\n\t\t\tawait allRows<CoordinatorReciprocalApproval>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t\t FROM coordinator_reciprocal_approvals\n\t\t\t\t\t\t WHERE group_id = ? AND ${directionColumn} = ? AND status = ?\n\t\t\t\t\t\t ORDER BY created_at ASC, request_id ASC`)\n\t\t\t\t\t.bind(opts.groupId, opts.deviceId, status),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorReciprocalApproval>(row));\n\t}\n\n\tasync upsertPresence(_opts: CoordinatorUpsertPresenceInput): Promise<CoordinatorPresenceRecord> {\n\t\tconst now = new Date();\n\t\tconst expiresAt = new Date(now.getTime() + _opts.ttlS * 1000).toISOString();\n\t\tconst normalized = mergeAddresses([], _opts.addresses);\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO presence_records(group_id, device_id, addresses_json, last_seen_at, expires_at, capabilities_json)\n\t\t\t\t VALUES (?, ?, ?, ?, ?, ?)\n\t\t\t\t ON CONFLICT(group_id, device_id) DO UPDATE SET\n\t\t\t\t\taddresses_json = excluded.addresses_json,\n\t\t\t\t\tlast_seen_at = excluded.last_seen_at,\n\t\t\t\t\texpires_at = excluded.expires_at,\n\t\t\t\t\tcapabilities_json = excluded.capabilities_json`)\n\t\t\t.bind(\n\t\t\t\t_opts.groupId,\n\t\t\t\t_opts.deviceId,\n\t\t\t\tJSON.stringify(normalized),\n\t\t\t\tnow.toISOString(),\n\t\t\t\texpiresAt,\n\t\t\t\tJSON.stringify(_opts.capabilities ?? {}),\n\t\t\t)\n\t\t\t.run();\n\t\treturn {\n\t\t\tgroup_id: _opts.groupId,\n\t\t\tdevice_id: _opts.deviceId,\n\t\t\taddresses: normalized,\n\t\t\texpires_at: expiresAt,\n\t\t};\n\t}\n\n\tasync listGroupPeers(\n\t\t_groupId: string,\n\t\t_requestingDeviceId: string,\n\t): Promise<CoordinatorPeerRecord[]> {\n\t\tconst now = nowISO();\n\t\tconst rows = await allRows<Record<string, unknown>>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT enrolled_devices.device_id, enrolled_devices.public_key, enrolled_devices.fingerprint, enrolled_devices.display_name,\n\t\t\t\t\t\tpresence_records.addresses_json, presence_records.last_seen_at, presence_records.expires_at,\n\t\t\t\t\t\tpresence_records.capabilities_json\n\t\t\t\t\t FROM enrolled_devices\n\t\t\t\t\t LEFT JOIN presence_records\n\t\t\t\t\t ON presence_records.group_id = enrolled_devices.group_id\n\t\t\t\t\t AND presence_records.device_id = enrolled_devices.device_id\n\t\t\t\t\t WHERE enrolled_devices.group_id = ?\n\t\t\t\t\t AND enrolled_devices.enabled = 1\n\t\t\t\t\t AND enrolled_devices.device_id != ?\n\t\t\t\t\t ORDER BY enrolled_devices.device_id ASC`)\n\t\t\t\t.bind(_groupId, _requestingDeviceId),\n\t\t);\n\t\treturn rows.map((row) => {\n\t\t\tconst expiresRaw = String(row.expires_at ?? \"\").trim();\n\t\t\tlet stale = true;\n\t\t\tif (expiresRaw) {\n\t\t\t\tconst expiresAt = new Date(expiresRaw);\n\t\t\t\tstale = Number.isNaN(expiresAt.getTime()) || expiresAt.toISOString() <= now;\n\t\t\t}\n\t\t\tconst addresses = stale\n\t\t\t\t? []\n\t\t\t\t: mergeAddresses([], JSON.parse(String(row.addresses_json ?? \"[]\")) as string[]);\n\t\t\treturn {\n\t\t\t\tdevice_id: String(row.device_id ?? \"\"),\n\t\t\t\tpublic_key: String(row.public_key ?? \"\"),\n\t\t\t\tfingerprint: String(row.fingerprint ?? \"\"),\n\t\t\t\tdisplay_name: (row.display_name as string | null) ?? null,\n\t\t\t\taddresses,\n\t\t\t\tlast_seen_at: (row.last_seen_at as string | null) ?? null,\n\t\t\t\texpires_at: (row.expires_at as string | null) ?? null,\n\t\t\t\tstale,\n\t\t\t\tcapabilities: JSON.parse(String(row.capabilities_json ?? \"{}\")) as Record<string, unknown>,\n\t\t\t} satisfies CoordinatorPeerRecord;\n\t\t});\n\t}\n}\n","import { type CoordinatorRuntimeDeps, createCoordinatorApp } from \"./coordinator-api.js\";\nimport { D1CoordinatorStore, type D1DatabaseLike } from \"./d1-coordinator-store.js\";\n\nexport interface CreateD1CoordinatorAppOptions {\n\tdb: D1DatabaseLike;\n\tadminSecret?: string | null;\n\tnow?: () => string;\n}\n\nexport function createD1CoordinatorApp(\n\topts: CreateD1CoordinatorAppOptions,\n): ReturnType<typeof createCoordinatorApp> {\n\tconst runtime: CoordinatorRuntimeDeps = {\n\t\tadminSecret: () => String(opts.adminSecret ?? \"\").trim() || null,\n\t\tnow: opts.now ?? (() => new Date().toISOString()),\n\t};\n\treturn createCoordinatorApp({\n\t\tstoreFactory: () => new D1CoordinatorStore(opts.db),\n\t\truntime,\n\t});\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cloudflare-coordinator.js","names":[],"sources":["../../src/d1-coordinator-store.ts","../../src/d1-coordinator-runtime.ts"],"sourcesContent":["import type {\n\tCoordinatorCreateInviteInput,\n\tCoordinatorCreateJoinRequestInput,\n\tCoordinatorCreateReciprocalApprovalInput,\n\tCoordinatorEnrollDeviceInput,\n\tCoordinatorEnrollment,\n\tCoordinatorGroup,\n\tCoordinatorInvite,\n\tCoordinatorJoinRequest,\n\tCoordinatorJoinRequestReviewResult,\n\tCoordinatorListReciprocalApprovalsInput,\n\tCoordinatorPeerRecord,\n\tCoordinatorPresenceRecord,\n\tCoordinatorReciprocalApproval,\n\tCoordinatorReviewJoinRequestInput,\n\tCoordinatorStore,\n\tCoordinatorUpsertPresenceInput,\n} from \"./coordinator-store-contract.js\";\n\ninterface D1RunResultLike {\n\tmeta?: {\n\t\tchanges?: number;\n\t};\n}\n\n/**\n * Experimental D1 adapter scaffold.\n *\n * This is intentionally internal for now while the sync-only store contract\n * is still being validated against an async backend shape.\n */\n\nexport interface D1PreparedStatementLike {\n\tbind(...values: unknown[]): D1PreparedStatementLike;\n\tfirst<T = unknown>(): Promise<T | null>;\n\trun(): Promise<unknown>;\n\tall<T = unknown>(): Promise<{ results?: T[] }>;\n\traw<T = unknown>(): Promise<T[]>;\n}\n\nexport interface D1DatabaseLike {\n\tprepare(query: string): D1PreparedStatementLike;\n\tbatch?(statements: D1PreparedStatementLike[]): Promise<unknown[]>;\n\texec?(query: string): Promise<unknown>;\n}\n\nfunction notImplemented(method: string): never {\n\tthrow new Error(`D1CoordinatorStore.${method} is not implemented yet.`);\n}\n\nfunction rowToRecord<T>(row: unknown): T {\n\tif (row == null) throw new Error(\"expected row\");\n\treturn row as T;\n}\n\nfunction normalizeAddress(address: string): string {\n\tconst value = address.trim();\n\tif (!value) return \"\";\n\tconst withScheme = value.includes(\"://\") ? value : `http://${value}`;\n\ttry {\n\t\tconst url = new URL(withScheme);\n\t\tif (!url.hostname) return \"\";\n\t\tif (url.port && (Number(url.port) <= 0 || Number(url.port) > 65535)) return \"\";\n\t\treturn url.origin + url.pathname.replace(/\\/+$/, \"\");\n\t} catch {\n\t\treturn \"\";\n\t}\n}\n\nfunction addressDedupeKey(address: string): string {\n\tif (!address) return \"\";\n\ttry {\n\t\tconst parsed = new URL(address);\n\t\tconst host = parsed.hostname.toLowerCase();\n\t\tif (\n\t\t\t(parsed.protocol === \"http:\" || parsed.protocol === \"\") &&\n\t\t\thost &&\n\t\t\tparsed.port &&\n\t\t\tparsed.pathname === \"/\"\n\t\t) {\n\t\t\treturn `${host}:${parsed.port}`;\n\t\t}\n\t} catch {}\n\treturn address;\n}\n\nfunction mergeAddresses(existing: string[], candidates: string[]): string[] {\n\tconst normalized: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const address of [...existing, ...candidates]) {\n\t\tconst cleaned = normalizeAddress(address);\n\t\tconst key = addressDedupeKey(cleaned);\n\t\tif (!cleaned || seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tnormalized.push(cleaned);\n\t}\n\treturn normalized;\n}\n\nfunction nowISO(): string {\n\treturn new Date().toISOString();\n}\n\nfunction reciprocalPendingPair(\n\trequestingDeviceId: string,\n\trequestedDeviceId: string,\n): {\n\tlow: string;\n\thigh: string;\n} {\n\treturn requestingDeviceId <= requestedDeviceId\n\t\t? { low: requestingDeviceId, high: requestedDeviceId }\n\t\t: { low: requestedDeviceId, high: requestingDeviceId };\n}\n\nfunction isUniqueConstraintError(error: unknown): boolean {\n\treturn error instanceof Error && /unique|constraint/i.test(error.message);\n}\n\nfunction tokenUrlSafe(bytes: number): string {\n\tconst alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\tconst random = new Uint8Array(bytes);\n\tglobalThis.crypto.getRandomValues(random);\n\tconst output: string[] = [];\n\tfor (const byte of random) {\n\t\toutput.push(alphabet[byte % alphabet.length] ?? \"A\");\n\t}\n\treturn output.join(\"\");\n}\n\nasync function allRows<T>(statement: D1PreparedStatementLike): Promise<T[]> {\n\tconst result = await statement.all<T>();\n\treturn Array.isArray(result?.results) ? result.results : [];\n}\n\nasync function firstRow<T>(statement: D1PreparedStatementLike): Promise<T | null> {\n\treturn await statement.first<T>();\n}\n\nasync function runChanges(statement: D1PreparedStatementLike): Promise<number> {\n\tconst result = (await statement.run()) as D1RunResultLike | undefined;\n\treturn Number(result?.meta?.changes ?? 0);\n}\n\nexport class D1CoordinatorStore implements CoordinatorStore {\n\treadonly db: D1DatabaseLike;\n\n\tconstructor(db: D1DatabaseLike) {\n\t\tthis.db = db;\n\t}\n\n\tasync close(): Promise<void> {\n\t\t// No-op for D1 bindings.\n\t}\n\n\tasync createGroup(_groupId: string, _displayName?: string | null): Promise<void> {\n\t\tawait this.db\n\t\t\t.prepare(\"INSERT OR IGNORE INTO groups(group_id, display_name, created_at) VALUES (?, ?, ?)\")\n\t\t\t.bind(_groupId, _displayName ?? null, nowISO())\n\t\t\t.run();\n\t}\n\n\tasync getGroup(_groupId: string): Promise<CoordinatorGroup | null> {\n\t\tconst row = await firstRow<CoordinatorGroup>(\n\t\t\tthis.db\n\t\t\t\t.prepare(\"SELECT group_id, display_name, created_at FROM groups WHERE group_id = ?\")\n\t\t\t\t.bind(_groupId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorGroup>(row) : null;\n\t}\n\n\tasync listGroups(): Promise<CoordinatorGroup[]> {\n\t\treturn (\n\t\t\tawait allRows<CoordinatorGroup>(\n\t\t\t\tthis.db.prepare(\n\t\t\t\t\t\"SELECT group_id, display_name, created_at FROM groups ORDER BY created_at ASC\",\n\t\t\t\t),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorGroup>(row));\n\t}\n\n\tasync enrollDevice(_groupId: string, _opts: CoordinatorEnrollDeviceInput): Promise<void> {\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO enrolled_devices(\n\t\t\t\tgroup_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n\t\t\t) VALUES (?, ?, ?, ?, ?, 1, ?)\n\t\t\tON CONFLICT(group_id, device_id) DO UPDATE SET\n\t\t\t\tpublic_key = excluded.public_key,\n\t\t\t\tfingerprint = excluded.fingerprint,\n\t\t\t\tdisplay_name = excluded.display_name,\n\t\t\t\tenabled = 1`)\n\t\t\t.bind(\n\t\t\t\t_groupId,\n\t\t\t\t_opts.deviceId,\n\t\t\t\t_opts.publicKey,\n\t\t\t\t_opts.fingerprint,\n\t\t\t\t_opts.displayName ?? null,\n\t\t\t\tnowISO(),\n\t\t\t)\n\t\t\t.run();\n\t}\n\n\tasync listEnrolledDevices(\n\t\t_groupId: string,\n\t\t_includeDisabled?: boolean,\n\t): Promise<CoordinatorEnrollment[]> {\n\t\tconst where = _includeDisabled ? \"\" : \"AND enabled = 1\";\n\t\treturn (\n\t\t\tawait allRows<CoordinatorEnrollment>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT group_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n\t\t\t\t\t\t FROM enrolled_devices\n\t\t\t\t\t\t WHERE group_id = ? ${where}\n\t\t\t\t\t\t ORDER BY created_at ASC, device_id ASC`)\n\t\t\t\t\t.bind(_groupId),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorEnrollment>(row));\n\t}\n\n\tasync getEnrollment(_groupId: string, _deviceId: string): Promise<CoordinatorEnrollment | null> {\n\t\tconst row = await firstRow<CoordinatorEnrollment>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT group_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n\t\t\t\t\t FROM enrolled_devices\n\t\t\t\t\t WHERE group_id = ? AND device_id = ? AND enabled = 1`)\n\t\t\t\t.bind(_groupId, _deviceId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorEnrollment>(row) : null;\n\t}\n\n\tasync renameDevice(_groupId: string, _deviceId: string, _displayName: string): Promise<boolean> {\n\t\treturn (\n\t\t\t(await runChanges(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`UPDATE enrolled_devices SET display_name = ?\n\t\t\t\t\t WHERE group_id = ? AND device_id = ?`)\n\t\t\t\t\t.bind(_displayName, _groupId, _deviceId),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync setDeviceEnabled(_groupId: string, _deviceId: string, _enabled: boolean): Promise<boolean> {\n\t\treturn (\n\t\t\t(await runChanges(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`UPDATE enrolled_devices SET enabled = ?\n\t\t\t\t\t WHERE group_id = ? AND device_id = ?`)\n\t\t\t\t\t.bind(_enabled ? 1 : 0, _groupId, _deviceId),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync removeDevice(_groupId: string, _deviceId: string): Promise<boolean> {\n\t\tawait this.db\n\t\t\t.prepare(\"DELETE FROM presence_records WHERE group_id = ? AND device_id = ?\")\n\t\t\t.bind(_groupId, _deviceId)\n\t\t\t.run();\n\t\tawait this.db\n\t\t\t.prepare(\n\t\t\t\t\"DELETE FROM coordinator_reciprocal_approvals WHERE group_id = ? AND (requesting_device_id = ? OR requested_device_id = ?)\",\n\t\t\t)\n\t\t\t.bind(_groupId, _deviceId, _deviceId)\n\t\t\t.run();\n\t\treturn (\n\t\t\t(await runChanges(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(\"DELETE FROM enrolled_devices WHERE group_id = ? AND device_id = ?\")\n\t\t\t\t\t.bind(_groupId, _deviceId),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync recordNonce(_deviceId: string, _nonce: string, _createdAt: string): Promise<boolean> {\n\t\treturn (\n\t\t\t(await runChanges(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\"INSERT OR IGNORE INTO request_nonces(device_id, nonce, created_at) VALUES (?, ?, ?)\",\n\t\t\t\t\t)\n\t\t\t\t\t.bind(_deviceId, _nonce, _createdAt),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync cleanupNonces(_cutoff: string): Promise<void> {\n\t\tawait this.db.prepare(\"DELETE FROM request_nonces WHERE created_at < ?\").bind(_cutoff).run();\n\t}\n\n\tasync createInvite(_opts: CoordinatorCreateInviteInput): Promise<CoordinatorInvite> {\n\t\tconst now = nowISO();\n\t\tconst inviteId = tokenUrlSafe(12);\n\t\tconst token = tokenUrlSafe(24);\n\t\tconst group = await this.getGroup(_opts.groupId);\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO coordinator_invites(\n\t\t\t\tinvite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL)`)\n\t\t\t.bind(\n\t\t\t\tinviteId,\n\t\t\t\t_opts.groupId,\n\t\t\t\ttoken,\n\t\t\t\t_opts.policy,\n\t\t\t\t_opts.expiresAt,\n\t\t\t\tnow,\n\t\t\t\t_opts.createdBy ?? null,\n\t\t\t\tgroup?.display_name ?? null,\n\t\t\t)\n\t\t\t.run();\n\t\tconst row = await firstRow<CoordinatorInvite>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t\t FROM coordinator_invites WHERE invite_id = ?`)\n\t\t\t\t.bind(inviteId),\n\t\t);\n\t\treturn rowToRecord<CoordinatorInvite>(row);\n\t}\n\n\tasync getInviteByToken(_token: string): Promise<CoordinatorInvite | null> {\n\t\tconst row = await firstRow<CoordinatorInvite>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t\t FROM coordinator_invites\n\t\t\t\t\t WHERE token = ?\n\t\t\t\t\t AND revoked_at IS NULL\n\t\t\t\t\t AND expires_at > ?`)\n\t\t\t\t.bind(_token, nowISO()),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorInvite>(row) : null;\n\t}\n\n\tasync listInvites(_groupId: string): Promise<CoordinatorInvite[]> {\n\t\treturn (\n\t\t\tawait allRows<CoordinatorInvite>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t\t FROM coordinator_invites WHERE group_id = ?\n\t\t\t\t\t ORDER BY created_at DESC`)\n\t\t\t\t\t.bind(_groupId),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorInvite>(row));\n\t}\n\n\tasync createJoinRequest(\n\t\t_opts: CoordinatorCreateJoinRequestInput,\n\t): Promise<CoordinatorJoinRequest> {\n\t\tconst now = nowISO();\n\t\tconst requestId = tokenUrlSafe(12);\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO coordinator_join_requests(\n\t\t\t\trequest_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, NULL, NULL)`)\n\t\t\t.bind(\n\t\t\t\trequestId,\n\t\t\t\t_opts.groupId,\n\t\t\t\t_opts.deviceId,\n\t\t\t\t_opts.publicKey,\n\t\t\t\t_opts.fingerprint,\n\t\t\t\t_opts.displayName ?? null,\n\t\t\t\t_opts.token,\n\t\t\t\tnow,\n\t\t\t)\n\t\t\t.run();\n\t\tconst row = await firstRow<CoordinatorJoinRequest>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`)\n\t\t\t\t.bind(requestId),\n\t\t);\n\t\treturn rowToRecord<CoordinatorJoinRequest>(row);\n\t}\n\n\tasync listJoinRequests(_groupId: string, _status?: string): Promise<CoordinatorJoinRequest[]> {\n\t\tconst status = _status ?? \"pending\";\n\t\treturn (\n\t\t\tawait allRows<CoordinatorJoinRequest>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t\t\t FROM coordinator_join_requests\n\t\t\t\t\t\t WHERE group_id = ? AND status = ?\n\t\t\t\t\t\t ORDER BY created_at ASC, device_id ASC`)\n\t\t\t\t\t.bind(_groupId, status),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorJoinRequest>(row));\n\t}\n\n\tasync reviewJoinRequest(\n\t\t_opts: CoordinatorReviewJoinRequestInput,\n\t): Promise<CoordinatorJoinRequestReviewResult | null> {\n\t\tconst row = await firstRow<CoordinatorJoinRequest & { public_key: string }>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status,\n\t\t\t\t\t created_at, reviewed_at, reviewed_by\n\t\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`)\n\t\t\t\t.bind(_opts.requestId),\n\t\t);\n\t\tif (!row) return null;\n\t\tif (row.status !== \"pending\") {\n\t\t\treturn { ...rowToRecord<CoordinatorJoinRequest>(row), _no_transition: true };\n\t\t}\n\t\tconst reviewedAt = nowISO();\n\t\tconst nextStatus = _opts.approved ? \"approved\" : \"denied\";\n\t\tif (this.db.batch) {\n\t\t\tconst statements: D1PreparedStatementLike[] = [];\n\t\t\tstatements.push(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`UPDATE coordinator_join_requests\n\t\t\t\t\t\t SET status = ?, reviewed_at = ?, reviewed_by = ?\n\t\t\t\t\t\t WHERE request_id = ? AND status = 'pending'`)\n\t\t\t\t\t.bind(nextStatus, reviewedAt, _opts.reviewedBy ?? null, _opts.requestId),\n\t\t\t);\n\t\t\tif (_opts.approved) {\n\t\t\t\tstatements.push(\n\t\t\t\t\tthis.db\n\t\t\t\t\t\t.prepare(`INSERT INTO enrolled_devices(\n\t\t\t\t\t\t\tgroup_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n\t\t\t\t\t\t)\n\t\t\t\t\t\tSELECT group_id, device_id, public_key, fingerprint, display_name, 1, ?\n\t\t\t\t\t\tFROM coordinator_join_requests\n\t\t\t\t\t\tWHERE request_id = ?\n\t\t\t\t\t\t AND status = 'approved'\n\t\t\t\t\t\t AND reviewed_at = ?\n\t\t\t\t\t\tON CONFLICT(group_id, device_id) DO UPDATE SET\n\t\t\t\t\t\t\tpublic_key = excluded.public_key,\n\t\t\t\t\t\t\tfingerprint = excluded.fingerprint,\n\t\t\t\t\t\t\tdisplay_name = excluded.display_name,\n\t\t\t\t\t\t\tenabled = 1`)\n\t\t\t\t\t\t.bind(nowISO(), _opts.requestId, reviewedAt),\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait this.db.batch(statements);\n\t\t} else {\n\t\t\tconst changes = await runChanges(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`UPDATE coordinator_join_requests\n\t\t\t\t\t\t SET status = ?, reviewed_at = ?, reviewed_by = ?\n\t\t\t\t\t\t WHERE request_id = ? AND status = 'pending'`)\n\t\t\t\t\t.bind(nextStatus, reviewedAt, _opts.reviewedBy ?? null, _opts.requestId),\n\t\t\t);\n\t\t\tif (changes === 0) {\n\t\t\t\tconst latest = await firstRow<CoordinatorJoinRequestReviewResult>(\n\t\t\t\t\tthis.db\n\t\t\t\t\t\t.prepare(`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`)\n\t\t\t\t\t\t.bind(_opts.requestId),\n\t\t\t\t);\n\t\t\t\treturn latest\n\t\t\t\t\t? { ...rowToRecord<CoordinatorJoinRequestReviewResult>(latest), _no_transition: true }\n\t\t\t\t\t: null;\n\t\t\t}\n\t\t\tif (_opts.approved) {\n\t\t\t\tawait this.enrollDevice(row.group_id, {\n\t\t\t\t\tdeviceId: row.device_id,\n\t\t\t\t\tfingerprint: row.fingerprint,\n\t\t\t\t\tpublicKey: row.public_key,\n\t\t\t\t\tdisplayName: (row.display_name ?? \"\").trim() || null,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tconst updated = await firstRow<CoordinatorJoinRequestReviewResult>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`)\n\t\t\t\t.bind(_opts.requestId),\n\t\t);\n\t\treturn updated ? rowToRecord<CoordinatorJoinRequestReviewResult>(updated) : null;\n\t}\n\n\tasync createReciprocalApproval(\n\t\topts: CoordinatorCreateReciprocalApprovalInput,\n\t): Promise<CoordinatorReciprocalApproval> {\n\t\tconst groupId = opts.groupId.trim();\n\t\tconst requestingDeviceId = opts.requestingDeviceId.trim();\n\t\tconst requestedDeviceId = opts.requestedDeviceId.trim();\n\t\tif (!groupId || !requestingDeviceId || !requestedDeviceId) {\n\t\t\tthrow new Error(\"groupId, requestingDeviceId, and requestedDeviceId are required.\");\n\t\t}\n\t\tif (requestingDeviceId === requestedDeviceId) {\n\t\t\tthrow new Error(\"requesting and requested device ids must differ.\");\n\t\t}\n\t\tconst pendingPair = reciprocalPendingPair(requestingDeviceId, requestedDeviceId);\n\t\tconst existing = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t FROM coordinator_reciprocal_approvals\n\t\t\t\t\t WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n\t\t\t\t\t ORDER BY created_at DESC LIMIT 1`)\n\t\t\t\t.bind(groupId, requestingDeviceId, requestedDeviceId),\n\t\t);\n\t\tif (existing) return rowToRecord<CoordinatorReciprocalApproval>(existing);\n\t\tconst reverse = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t FROM coordinator_reciprocal_approvals\n\t\t\t\t\t WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n\t\t\t\t\t ORDER BY created_at DESC LIMIT 1`)\n\t\t\t\t.bind(groupId, requestedDeviceId, requestingDeviceId),\n\t\t);\n\t\tif (reverse) {\n\t\t\tconst resolvedAt = nowISO();\n\t\t\tawait this.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`UPDATE coordinator_reciprocal_approvals SET status = 'completed', resolved_at = ? WHERE request_id = ?`,\n\t\t\t\t)\n\t\t\t\t.bind(resolvedAt, reverse.request_id)\n\t\t\t\t.run();\n\t\t\tconst completed = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t\t FROM coordinator_reciprocal_approvals WHERE request_id = ?`)\n\t\t\t\t\t.bind(reverse.request_id),\n\t\t\t);\n\t\t\treturn rowToRecord<CoordinatorReciprocalApproval>(completed);\n\t\t}\n\t\tconst requestId = tokenUrlSafe(12);\n\t\tconst createdAt = nowISO();\n\t\ttry {\n\t\t\tawait this.db\n\t\t\t\t.prepare(`INSERT INTO coordinator_reciprocal_approvals(\n\t\t\t\t\t\trequest_id,\n\t\t\t\t\t\tgroup_id,\n\t\t\t\t\t\trequesting_device_id,\n\t\t\t\t\t\trequested_device_id,\n\t\t\t\t\t\tpending_pair_low_device_id,\n\t\t\t\t\t\tpending_pair_high_device_id,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\tcreated_at,\n\t\t\t\t\t\tresolved_at\n\t\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, NULL)`)\n\t\t\t\t.bind(\n\t\t\t\t\trequestId,\n\t\t\t\t\tgroupId,\n\t\t\t\t\trequestingDeviceId,\n\t\t\t\t\trequestedDeviceId,\n\t\t\t\t\tpendingPair.low,\n\t\t\t\t\tpendingPair.high,\n\t\t\t\t\tcreatedAt,\n\t\t\t\t)\n\t\t\t\t.run();\n\t\t} catch (error) {\n\t\t\tif (!isUniqueConstraintError(error)) throw error;\n\t\t\tconst sameDirection = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t\t FROM coordinator_reciprocal_approvals\n\t\t\t\t\t\t WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n\t\t\t\t\t\t ORDER BY created_at DESC LIMIT 1`)\n\t\t\t\t\t.bind(groupId, requestingDeviceId, requestedDeviceId),\n\t\t\t);\n\t\t\tif (sameDirection) return rowToRecord<CoordinatorReciprocalApproval>(sameDirection);\n\t\t\tconst reverseAfterConflict = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t\t FROM coordinator_reciprocal_approvals\n\t\t\t\t\t\t WHERE group_id = ? AND requesting_device_id = ? AND requested_device_id = ? AND status = 'pending'\n\t\t\t\t\t\t ORDER BY created_at DESC LIMIT 1`)\n\t\t\t\t\t.bind(groupId, requestedDeviceId, requestingDeviceId),\n\t\t\t);\n\t\t\tif (reverseAfterConflict) {\n\t\t\t\tconst resolvedAt = nowISO();\n\t\t\t\tawait this.db\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t`UPDATE coordinator_reciprocal_approvals SET status = 'completed', resolved_at = ? WHERE request_id = ?`,\n\t\t\t\t\t)\n\t\t\t\t\t.bind(resolvedAt, reverseAfterConflict.request_id)\n\t\t\t\t\t.run();\n\t\t\t\tconst completed = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\t\t\tthis.db\n\t\t\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t\t\t FROM coordinator_reciprocal_approvals WHERE request_id = ?`)\n\t\t\t\t\t\t.bind(reverseAfterConflict.request_id),\n\t\t\t\t);\n\t\t\t\treturn rowToRecord<CoordinatorReciprocalApproval>(completed);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tconst created = await firstRow<CoordinatorReciprocalApproval>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t FROM coordinator_reciprocal_approvals WHERE request_id = ?`)\n\t\t\t\t.bind(requestId),\n\t\t);\n\t\treturn rowToRecord<CoordinatorReciprocalApproval>(created);\n\t}\n\n\tasync listReciprocalApprovals(\n\t\topts: CoordinatorListReciprocalApprovalsInput,\n\t): Promise<CoordinatorReciprocalApproval[]> {\n\t\tconst directionColumn =\n\t\t\topts.direction === \"incoming\" ? \"requested_device_id\" : \"requesting_device_id\";\n\t\tconst status = opts.status?.trim() || \"pending\";\n\t\treturn (\n\t\t\tawait allRows<CoordinatorReciprocalApproval>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at\n\t\t\t\t\t\t FROM coordinator_reciprocal_approvals\n\t\t\t\t\t\t WHERE group_id = ? AND ${directionColumn} = ? AND status = ?\n\t\t\t\t\t\t ORDER BY created_at ASC, request_id ASC`)\n\t\t\t\t\t.bind(opts.groupId, opts.deviceId, status),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorReciprocalApproval>(row));\n\t}\n\n\tasync upsertPresence(_opts: CoordinatorUpsertPresenceInput): Promise<CoordinatorPresenceRecord> {\n\t\tconst now = new Date();\n\t\tconst expiresAt = new Date(now.getTime() + _opts.ttlS * 1000).toISOString();\n\t\tconst normalized = mergeAddresses([], _opts.addresses);\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO presence_records(group_id, device_id, addresses_json, last_seen_at, expires_at, capabilities_json)\n\t\t\t\t VALUES (?, ?, ?, ?, ?, ?)\n\t\t\t\t ON CONFLICT(group_id, device_id) DO UPDATE SET\n\t\t\t\t\taddresses_json = excluded.addresses_json,\n\t\t\t\t\tlast_seen_at = excluded.last_seen_at,\n\t\t\t\t\texpires_at = excluded.expires_at,\n\t\t\t\t\tcapabilities_json = excluded.capabilities_json`)\n\t\t\t.bind(\n\t\t\t\t_opts.groupId,\n\t\t\t\t_opts.deviceId,\n\t\t\t\tJSON.stringify(normalized),\n\t\t\t\tnow.toISOString(),\n\t\t\t\texpiresAt,\n\t\t\t\tJSON.stringify(_opts.capabilities ?? {}),\n\t\t\t)\n\t\t\t.run();\n\t\treturn {\n\t\t\tgroup_id: _opts.groupId,\n\t\t\tdevice_id: _opts.deviceId,\n\t\t\taddresses: normalized,\n\t\t\texpires_at: expiresAt,\n\t\t};\n\t}\n\n\tasync listGroupPeers(\n\t\t_groupId: string,\n\t\t_requestingDeviceId: string,\n\t): Promise<CoordinatorPeerRecord[]> {\n\t\tconst now = nowISO();\n\t\tconst rows = await allRows<Record<string, unknown>>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT enrolled_devices.device_id, enrolled_devices.public_key, enrolled_devices.fingerprint, enrolled_devices.display_name,\n\t\t\t\t\t\tpresence_records.addresses_json, presence_records.last_seen_at, presence_records.expires_at,\n\t\t\t\t\t\tpresence_records.capabilities_json\n\t\t\t\t\t FROM enrolled_devices\n\t\t\t\t\t LEFT JOIN presence_records\n\t\t\t\t\t ON presence_records.group_id = enrolled_devices.group_id\n\t\t\t\t\t AND presence_records.device_id = enrolled_devices.device_id\n\t\t\t\t\t WHERE enrolled_devices.group_id = ?\n\t\t\t\t\t AND enrolled_devices.enabled = 1\n\t\t\t\t\t AND enrolled_devices.device_id != ?\n\t\t\t\t\t ORDER BY enrolled_devices.device_id ASC`)\n\t\t\t\t.bind(_groupId, _requestingDeviceId),\n\t\t);\n\t\treturn rows.map((row) => {\n\t\t\tconst expiresRaw = String(row.expires_at ?? \"\").trim();\n\t\t\tlet stale = true;\n\t\t\tif (expiresRaw) {\n\t\t\t\tconst expiresAt = new Date(expiresRaw);\n\t\t\t\tstale = Number.isNaN(expiresAt.getTime()) || expiresAt.toISOString() <= now;\n\t\t\t}\n\t\t\tconst addresses = stale\n\t\t\t\t? []\n\t\t\t\t: mergeAddresses([], JSON.parse(String(row.addresses_json ?? \"[]\")) as string[]);\n\t\t\treturn {\n\t\t\t\tdevice_id: String(row.device_id ?? \"\"),\n\t\t\t\tpublic_key: String(row.public_key ?? \"\"),\n\t\t\t\tfingerprint: String(row.fingerprint ?? \"\"),\n\t\t\t\tdisplay_name: (row.display_name as string | null) ?? null,\n\t\t\t\taddresses,\n\t\t\t\tlast_seen_at: (row.last_seen_at as string | null) ?? null,\n\t\t\t\texpires_at: (row.expires_at as string | null) ?? null,\n\t\t\t\tstale,\n\t\t\t\tcapabilities: JSON.parse(String(row.capabilities_json ?? \"{}\")) as Record<string, unknown>,\n\t\t\t} satisfies CoordinatorPeerRecord;\n\t\t});\n\t}\n}\n","import {\n\ttype CoordinatorRequestVerifier,\n\ttype CoordinatorRuntimeDeps,\n\tcreateCoordinatorApp,\n} from \"./coordinator-api.js\";\nimport { D1CoordinatorStore, type D1DatabaseLike } from \"./d1-coordinator-store.js\";\n\nexport interface CreateD1CoordinatorAppOptions {\n\tdb: D1DatabaseLike;\n\tadminSecret?: string | null;\n\tnow?: () => string;\n\trequestVerifier: CoordinatorRequestVerifier;\n}\n\nexport function createD1CoordinatorApp(\n\topts: CreateD1CoordinatorAppOptions,\n): ReturnType<typeof createCoordinatorApp> {\n\tconst runtime: CoordinatorRuntimeDeps = {\n\t\tadminSecret: () => String(opts.adminSecret ?? \"\").trim() || null,\n\t\tnow: opts.now ?? (() => new Date().toISOString()),\n\t};\n\treturn createCoordinatorApp({\n\t\tstoreFactory: () => new D1CoordinatorStore(opts.db),\n\t\truntime,\n\t\trequestVerifier: opts.requestVerifier,\n\t});\n}\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface InMemoryRequestRateLimiter {
2
+ check(key: string, limit: number): {
3
+ allowed: boolean;
4
+ retryAfterS: number;
5
+ };
6
+ }
7
+ export interface CreateInMemoryRequestRateLimiterOptions {
8
+ windowMs?: number;
9
+ now?: () => number;
10
+ }
11
+ export declare function createInMemoryRequestRateLimiter(options?: CreateInMemoryRequestRateLimiterOptions): InMemoryRequestRateLimiter;
12
+ //# sourceMappingURL=request-rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-rate-limit.d.ts","sourceRoot":"","sources":["../src/request-rate-limit.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,0BAA0B;IAC1C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E;AAED,MAAM,WAAW,uCAAuC;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,wBAAgB,gCAAgC,CAC/C,OAAO,GAAE,uCAA4C,GACnD,0BAA0B,CA4C5B"}
@@ -0,0 +1,2 @@
1
+ export declare const DEFAULT_TIME_WINDOW_S = 300;
2
+ //# sourceMappingURL=sync-auth-constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-auth-constants.d.ts","sourceRoot":"","sources":["../src/sync-auth-constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,MAAM,CAAC"}
@@ -13,7 +13,6 @@ import type { Database } from "./db.js";
13
13
  * be removed. During migration, the verifier accepts both versions.
14
14
  */
15
15
  export declare const SIGNATURE_VERSION = "v2";
16
- export declare const DEFAULT_TIME_WINDOW_S = 300;
17
16
  /**
18
17
  * Build a canonical request buffer for signing/verification.
19
18
  *
@@ -1 +1 @@
1
- {"version":3,"file":"sync-auth.d.ts","sourceRoot":"","sources":["../src/sync-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQxC;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAMzC;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GACf,MAAM,CAIR;AAMD,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgC/E;AAMD,MAAM,WAAW,sBAAsB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CA4CxE;AAwCD,MAAM,WAAW,uBAAuB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYzF;AAMD;;;;;GAKG;AACH,wBAAgB,WAAW,CAC1B,EAAE,EAAE,QAAQ,EACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GACf,OAAO,CAWT;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAGhE"}
1
+ {"version":3,"file":"sync-auth.d.ts","sourceRoot":"","sources":["../src/sync-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AASxC;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAMtC;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GACf,MAAM,CAIR;AAMD,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgC/E;AAMD,MAAM,WAAW,sBAAsB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CA4CxE;AAwCD,MAAM,WAAW,uBAAuB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYzF;AAMD;;;;;GAKG;AACH,wBAAgB,WAAW,CAC1B,EAAE,EAAE,QAAQ,EACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GACf,OAAO,CAWT;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAGhE"}
@@ -42,6 +42,7 @@ export declare function getSyncDaemonPhase(db: Database): SyncDaemonPhase;
42
42
  * Set the sync daemon phase. Pass null to clear.
43
43
  */
44
44
  export declare function setSyncDaemonPhase(db: Database, phase: SyncDaemonPhase): void;
45
+ export declare function refreshCoordinatorPresenceForDaemon(db: Database, dbPath: string, keysDir?: string): Promise<boolean>;
45
46
  /**
46
47
  * Run one sync tick: iterate over all enabled peers and sync each.
47
48
  *
@@ -59,4 +60,10 @@ export declare function syncDaemonTick(db: Database, keysDir?: string): Promise<
59
60
  * 6. Cleans up on exit
60
61
  */
61
62
  export declare function runSyncDaemon(options?: SyncDaemonOptions): Promise<void>;
63
+ /**
64
+ * Run a single tick, opening and closing a DB connection.
65
+ *
66
+ * Errors are caught and recorded in sync_daemon_state.
67
+ */
68
+ export declare function runTickOnce(dbPath: string, keysDir?: string): Promise<void>;
62
69
  //# sourceMappingURL=sync-daemon.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync-daemon.d.ts","sourceRoot":"","sources":["../src/sync-daemon.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAWxC,MAAM,WAAW,iBAAiB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,KAAK,IAAI,CAAC;CACrE;AAED,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAUlD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAmBxF;AAED,gEAAgE;AAChE,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,IAAI,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,GAAG,eAAe,CAUhE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAS7E;AAMD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAkC9F;AAMD;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgE9E"}
1
+ {"version":3,"file":"sync-daemon.d.ts","sourceRoot":"","sources":["../src/sync-daemon.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAWxC,MAAM,WAAW,iBAAiB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,UAAU,KAAK,IAAI,CAAC;CACrE;AAED,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAUlD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAmBxF;AAED,gEAAgE;AAChE,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,IAAI,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,GAAG,eAAe,CAUhE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAS7E;AAED,wBAAsB,mCAAmC,CACxD,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CAKlB;AAMD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAkC9F;AAMD;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgE9E;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBjF"}
@@ -0,0 +1,3 @@
1
+ /** SHA-256 hex digest of a public key string. */
2
+ export declare function fingerprintPublicKey(publicKey: string): string;
3
+ //# sourceMappingURL=sync-fingerprint.d.ts.map