@codemem/core 0.22.4 → 0.24.0

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 (63) hide show
  1. package/dist/better-sqlite-coordinator-store.d.ts +5 -1
  2. package/dist/better-sqlite-coordinator-store.d.ts.map +1 -1
  3. package/dist/coordinator-actions.d.ts +13 -1
  4. package/dist/coordinator-actions.d.ts.map +1 -1
  5. package/dist/{coordinator-api-CEbQ6LHE.js → coordinator-api-tVUvKt0u.js} +70 -9
  6. package/dist/coordinator-api-tVUvKt0u.js.map +1 -0
  7. package/dist/coordinator-api.d.ts.map +1 -1
  8. package/dist/coordinator-store-contract.d.ts +32 -0
  9. package/dist/coordinator-store-contract.d.ts.map +1 -1
  10. package/dist/coordinator-store-test-harness.d.ts.map +1 -1
  11. package/dist/coordinator-store.d.ts +1 -1
  12. package/dist/coordinator-store.d.ts.map +1 -1
  13. package/dist/d1-coordinator-store.d.ts +5 -1
  14. package/dist/d1-coordinator-store.d.ts.map +1 -1
  15. package/dist/eval-scenarios.d.ts +20 -0
  16. package/dist/eval-scenarios.d.ts.map +1 -0
  17. package/dist/extraction-benchmarks.d.ts +31 -0
  18. package/dist/extraction-benchmarks.d.ts.map +1 -0
  19. package/dist/extraction-eval.d.ts +104 -0
  20. package/dist/extraction-eval.d.ts.map +1 -0
  21. package/dist/extraction-replay.d.ts +68 -0
  22. package/dist/extraction-replay.d.ts.map +1 -0
  23. package/dist/extraction-tier-routing.d.ts +19 -0
  24. package/dist/extraction-tier-routing.d.ts.map +1 -0
  25. package/dist/index.d.ts +18 -6
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +8761 -6474
  28. package/dist/index.js.map +1 -1
  29. package/dist/ingest-pipeline.d.ts +3 -1
  30. package/dist/ingest-pipeline.d.ts.map +1 -1
  31. package/dist/ingest-prompts.d.ts +1 -0
  32. package/dist/ingest-prompts.d.ts.map +1 -1
  33. package/dist/ingest-types.d.ts +1 -0
  34. package/dist/ingest-types.d.ts.map +1 -1
  35. package/dist/internal/cloudflare-coordinator.js +107 -46
  36. package/dist/internal/cloudflare-coordinator.js.map +1 -1
  37. package/dist/maintenance.d.ts +224 -0
  38. package/dist/maintenance.d.ts.map +1 -1
  39. package/dist/observer-client.d.ts +34 -0
  40. package/dist/observer-client.d.ts.map +1 -1
  41. package/dist/observer-config.d.ts +35 -1
  42. package/dist/observer-config.d.ts.map +1 -1
  43. package/dist/pack-eval-fixtures.d.ts +19 -0
  44. package/dist/pack-eval-fixtures.d.ts.map +1 -0
  45. package/dist/pack.d.ts.map +1 -1
  46. package/dist/raw-event-sweeper.d.ts.map +1 -1
  47. package/dist/recap-policy.d.ts +7 -0
  48. package/dist/recap-policy.d.ts.map +1 -0
  49. package/dist/search.d.ts +1 -1
  50. package/dist/search.d.ts.map +1 -1
  51. package/dist/session-policy.d.ts +16 -0
  52. package/dist/session-policy.d.ts.map +1 -0
  53. package/dist/store.d.ts +10 -0
  54. package/dist/store.d.ts.map +1 -1
  55. package/dist/summary-memory.d.ts +12 -0
  56. package/dist/summary-memory.d.ts.map +1 -0
  57. package/dist/sync-auth.d.ts +1 -0
  58. package/dist/sync-auth.d.ts.map +1 -1
  59. package/dist/sync-bootstrap.d.ts +1 -0
  60. package/dist/sync-bootstrap.d.ts.map +1 -1
  61. package/dist/sync-replication.d.ts.map +1 -1
  62. package/package.json +10 -7
  63. package/dist/coordinator-api-CEbQ6LHE.js.map +0 -1
@@ -19,11 +19,13 @@
19
19
  * 12. End session
20
20
  */
21
21
  import type { IngestPayload } from "./ingest-types.js";
22
- import type { ObserverClient } from "./observer-client.js";
22
+ import { type ObserverClient } from "./observer-client.js";
23
23
  import type { MemoryStore } from "./store.js";
24
24
  export interface IngestOptions {
25
25
  /** Observer LLM client. */
26
26
  observer: ObserverClient;
27
+ /** Optional hook to create a routed observer during tests or controlled integrations. */
28
+ createTierObserver?: (config: ReturnType<ObserverClient["toConfig"]>) => ObserverClient;
27
29
  /** Maximum chars per tool event payload (from config). Default 12000. */
28
30
  maxChars?: number;
29
31
  /** Maximum chars for observer total budget. Default 12000. */
@@ -1 +1 @@
1
- {"version":3,"file":"ingest-pipeline.d.ts","sourceRoot":"","sources":["../src/ingest-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAyBH,OAAO,KAAK,EACX,aAAa,EAMb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA8F9C,MAAM,WAAW,aAAa;IAC7B,2BAA2B;IAC3B,QAAQ,EAAE,cAAc,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yDAAyD;IACzD,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAwCD;;;;;;GAMG;AACH,wBAAsB,MAAM,CAC3B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,aAAa,GACpB,OAAO,CAAC,IAAI,CAAC,CAyUf;AA0BD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,SAAK,GAAG,MAAM,CAShF;AAMD;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBtF"}
1
+ {"version":3,"file":"ingest-pipeline.d.ts","sourceRoot":"","sources":["../src/ingest-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA6BH,OAAO,KAAK,EACX,aAAa,EAMb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,KAAK,cAAc,EAAwC,MAAM,sBAAsB,CAAC;AAIjG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA8F9C,MAAM,WAAW,aAAa;IAC7B,2BAA2B;IAC3B,QAAQ,EAAE,cAAc,CAAC;IACzB,yFAAyF;IACzF,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,KAAK,cAAc,CAAC;IACxF,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yDAAyD;IACzD,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAwCD;;;;;;GAMG;AACH,wBAAsB,MAAM,CAC3B,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,aAAa,GACpB,OAAO,CAAC,IAAI,CAAC,CA+cf;AA2BD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,SAAK,GAAG,MAAM,CAShF;AAMD;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBtF"}
@@ -19,4 +19,5 @@ export declare function buildObserverPrompt(context: ObserverContext): {
19
19
  system: string;
20
20
  user: string;
21
21
  };
22
+ export declare function truncateObserverTranscript(transcript: string, maxChars: number): string;
22
23
  //# sourceMappingURL=ingest-prompts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ingest-prompts.d.ts","sourceRoot":"","sources":["../src/ingest-prompts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAa,MAAM,mBAAmB,CAAC;AA6MpE;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb,CA+DA"}
1
+ {"version":3,"file":"ingest-prompts.d.ts","sourceRoot":"","sources":["../src/ingest-prompts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAa,MAAM,mBAAmB,CAAC;AAoPpE;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACb,CAuEA;AAED,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIvF"}
@@ -16,6 +16,7 @@ export interface ObserverContext {
16
16
  project: string | null;
17
17
  userPrompt: string;
18
18
  promptNumber: number | null;
19
+ transcript: string;
19
20
  toolEvents: ToolEvent[];
20
21
  lastAssistantMessage: string | null;
21
22
  includeSummary: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"ingest-types.d.ts","sourceRoot":"","sources":["../src/ingest-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACnB;AAMD,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACpB;AAMD,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC5B,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAMD,MAAM,WAAW,cAAc;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,cAAc,CAAC;CAChC"}
1
+ {"version":3,"file":"ingest-types.d.ts","sourceRoot":"","sources":["../src/ingest-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,SAAS;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACnB;AAMD,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACpB;AAMD,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC5B,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAMD,MAAM,WAAW,cAAc;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,cAAc,CAAC;CAChC"}
@@ -1,4 +1,4 @@
1
- import { r as e, t } from "../coordinator-api-CEbQ6LHE.js";
1
+ import { r as e, t } from "../coordinator-api-tVUvKt0u.js";
2
2
  //#region src/d1-coordinator-store.ts
3
3
  function n(e) {
4
4
  if (e == null) throw Error("expected row");
@@ -53,18 +53,41 @@ function l(e) {
53
53
  for (let e of t) n.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"[e % 64] ?? "A");
54
54
  return n.join("");
55
55
  }
56
- async function u(e) {
56
+ function u(e) {
57
+ let t = String(e.groupId ?? "").trim(), n = String(e.seedDeviceId ?? "").trim(), r = String(e.workerDeviceId ?? "").trim(), i = String(e.expiresAt ?? "").trim(), a = String(e.createdBy ?? "").trim() || null;
58
+ if (!t || !n || !r || !i) throw Error("groupId, seedDeviceId, workerDeviceId, and expiresAt are required.");
59
+ return {
60
+ groupId: t,
61
+ seedDeviceId: n,
62
+ workerDeviceId: r,
63
+ expiresAt: i,
64
+ createdBy: a
65
+ };
66
+ }
67
+ function d(e) {
68
+ if (!e) return null;
69
+ let t = String(e.seedDeviceId ?? "").trim(), n = String(e.expiresAt ?? "").trim(), r = String(e.createdBy ?? "").trim() || null;
70
+ if (!t || !n) throw Error("bootstrapGrant.seedDeviceId and expiresAt are required.");
71
+ return {
72
+ groupId: "",
73
+ seedDeviceId: t,
74
+ workerDeviceId: "",
75
+ expiresAt: n,
76
+ createdBy: r
77
+ };
78
+ }
79
+ async function f(e) {
57
80
  let t = await e.all();
58
81
  return Array.isArray(t?.results) ? t.results : [];
59
82
  }
60
- async function d(e) {
83
+ async function p(e) {
61
84
  return await e.first();
62
85
  }
63
- async function f(e) {
86
+ async function m(e) {
64
87
  let t = await e.run();
65
88
  return Number(t?.meta?.changes ?? 0);
66
89
  }
67
- var p = class {
90
+ var h = class {
68
91
  db;
69
92
  constructor(e) {
70
93
  this.db = e;
@@ -74,119 +97,157 @@ var p = class {
74
97
  await this.db.prepare("INSERT OR IGNORE INTO groups(group_id, display_name, created_at) VALUES (?, ?, ?)").bind(e, t ?? null, o()).run();
75
98
  }
76
99
  async getGroup(e) {
77
- let t = await d(this.db.prepare("SELECT group_id, display_name, created_at FROM groups WHERE group_id = ?").bind(e));
100
+ let t = await p(this.db.prepare("SELECT group_id, display_name, created_at FROM groups WHERE group_id = ?").bind(e));
78
101
  return t ? n(t) : null;
79
102
  }
80
103
  async listGroups() {
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));
104
+ return (await f(this.db.prepare("SELECT group_id, display_name, created_at FROM groups ORDER BY created_at ASC"))).map((e) => n(e));
82
105
  }
83
106
  async enrollDevice(e, t) {
84
107
  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
108
  }
86
109
  async listEnrolledDevices(e, t) {
87
110
  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
111
+ return (await f(this.db.prepare(`SELECT group_id, device_id, public_key, fingerprint, display_name, enabled, created_at
89
112
  FROM enrolled_devices
90
113
  WHERE group_id = ? ${r}
91
114
  ORDER BY created_at ASC, device_id ASC`).bind(e))).map((e) => n(e));
92
115
  }
93
116
  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));
117
+ let r = await p(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
118
  return r ? n(r) : null;
96
119
  }
97
120
  async renameDevice(e, t, n) {
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;
121
+ return await m(this.db.prepare("UPDATE enrolled_devices SET display_name = ?\n WHERE group_id = ? AND device_id = ?").bind(n, e, t)) > 0;
99
122
  }
100
123
  async setDeviceEnabled(e, t, n) {
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;
124
+ return await m(this.db.prepare("UPDATE enrolled_devices SET enabled = ?\n WHERE group_id = ? AND device_id = ?").bind(n ? 1 : 0, e, t)) > 0;
102
125
  }
103
126
  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 f(this.db.prepare("DELETE FROM enrolled_devices WHERE group_id = ? AND device_id = ?").bind(e, t)) > 0;
127
+ 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 m(this.db.prepare("DELETE FROM enrolled_devices WHERE group_id = ? AND device_id = ?").bind(e, t)) > 0;
105
128
  }
106
129
  async recordNonce(e, t, n) {
107
- return await f(this.db.prepare("INSERT OR IGNORE INTO request_nonces(device_id, nonce, created_at) VALUES (?, ?, ?)").bind(e, t, n)) > 0;
130
+ return await m(this.db.prepare("INSERT OR IGNORE INTO request_nonces(device_id, nonce, created_at) VALUES (?, ?, ?)").bind(e, t, n)) > 0;
108
131
  }
109
132
  async cleanupNonces(e) {
110
133
  await this.db.prepare("DELETE FROM request_nonces WHERE created_at < ?").bind(e).run();
111
134
  }
112
135
  async createInvite(e) {
113
136
  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)));
137
+ 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 p(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
138
  }
116
139
  async getInviteByToken(e) {
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()));
140
+ let t = await p(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
141
  return t ? n(t) : null;
119
142
  }
120
143
  async listInvites(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));
144
+ return (await f(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
145
  }
123
146
  async createJoinRequest(e) {
124
147
  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)));
148
+ 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 p(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
149
  }
127
150
  async listJoinRequests(e, t) {
128
151
  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));
152
+ return (await f(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
153
  }
131
154
  async reviewJoinRequest(e) {
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));
155
+ let t = await p(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
156
  if (!t) return null;
134
157
  if (t.status !== "pending") return {
135
158
  ...n(t),
136
159
  _no_transition: !0
137
160
  };
138
- let r = o(), i = e.approved ? "approved" : "denied";
161
+ let r = d(e.bootstrapGrant), i = o(), a = e.approved ? "approved" : "denied", s = null, c = null;
162
+ if (e.approved && r) {
163
+ if (!await p(this.db.prepare("SELECT device_id FROM enrolled_devices WHERE group_id = ? AND device_id = ? AND enabled = 1").bind(t.group_id, r.seedDeviceId))) throw Error("bootstrap grant seed device is not enrolled in the group.");
164
+ if (r.seedDeviceId === t.device_id) throw Error("bootstrap grant seed and worker device ids must differ.");
165
+ s = {
166
+ ...r,
167
+ groupId: t.group_id,
168
+ workerDeviceId: t.device_id
169
+ };
170
+ }
139
171
  if (this.db.batch) {
140
172
  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(o(), e.requestId, r)), await this.db.batch(t);
173
+ if (t.push(this.db.prepare("UPDATE coordinator_join_requests\n SET status = ?, reviewed_at = ?, reviewed_by = ?\n WHERE request_id = ? AND status = 'pending'").bind(a, i, 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, i)), s)) {
174
+ c = l(12);
175
+ let n = o();
176
+ t.push(this.db.prepare("INSERT INTO coordinator_bootstrap_grants(\n grant_id, group_id, seed_device_id, worker_device_id, expires_at, created_at, created_by, revoked_at\n ) SELECT ?, ?, ?, ?, ?, ?, ?, NULL\n WHERE EXISTS (\n SELECT 1 FROM coordinator_join_requests\n WHERE request_id = ? AND status = 'approved' AND reviewed_at = ?\n )").bind(c, s.groupId, s.seedDeviceId, s.workerDeviceId, s.expiresAt, n, s.createdBy ?? null, e.requestId, i));
177
+ }
178
+ await this.db.batch(t);
142
179
  } else {
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));
180
+ let r = null;
181
+ if (e.approved && (await this.enrollDevice(t.group_id, {
182
+ deviceId: t.device_id,
183
+ fingerprint: t.fingerprint,
184
+ publicKey: t.public_key,
185
+ displayName: (t.display_name ?? "").trim() || null
186
+ }), s)) {
187
+ let e = await this.createBootstrapGrant(s);
188
+ r = e.grant_id, c = e.grant_id;
189
+ }
190
+ if (await m(this.db.prepare("UPDATE coordinator_join_requests\n SET status = ?, reviewed_at = ?, reviewed_by = ?\n WHERE request_id = ? AND status = 'pending'").bind(a, i, e.reviewedBy ?? null, e.requestId)) === 0) {
191
+ r && await this.revokeBootstrapGrant(r);
192
+ let t = await p(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
193
  return t ? {
146
194
  ...n(t),
147
195
  _no_transition: !0
148
196
  } : null;
149
197
  }
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
- });
156
198
  }
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;
199
+ let u = await p(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));
200
+ if (!u) return null;
201
+ let f = s ? c ? await this.getBootstrapGrant(c) : await this.createBootstrapGrant(s) : null;
202
+ return {
203
+ ...n(u),
204
+ bootstrap_grant: f
205
+ };
159
206
  }
160
207
  async createReciprocalApproval(e) {
161
208
  let t = e.groupId.trim(), r = e.requestingDeviceId.trim(), i = e.requestedDeviceId.trim();
162
209
  if (!t || !r || !i) throw Error("groupId, requestingDeviceId, and requestedDeviceId are required.");
163
210
  if (r === i) throw Error("requesting and requested device ids must differ.");
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));
211
+ let a = s(r, i), u = await p(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
212
  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
- if (f) {
213
+ let d = await p(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));
214
+ if (d) {
168
215
  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)));
216
+ return await this.db.prepare("UPDATE coordinator_reciprocal_approvals SET status = 'completed', resolved_at = ? WHERE request_id = ?").bind(e, d.request_id).run(), n(await p(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(d.request_id)));
170
217
  }
171
- let p = l(12), m = o();
218
+ let f = l(12), m = o();
172
219
  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, t, r, i, a.low, a.high, m).run();
220
+ 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(f, t, r, i, a.low, a.high, m).run();
174
221
  } catch (e) {
175
222
  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));
223
+ let a = await p(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
224
  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));
225
+ let s = await p(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
226
  if (s) {
180
227
  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)));
228
+ 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 p(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
229
  }
183
230
  throw e;
184
231
  }
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)));
232
+ return n(await p(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)));
233
+ }
234
+ async createBootstrapGrant(e) {
235
+ let t = u(e), r = l(12), i = o();
236
+ return await this.db.prepare("INSERT INTO coordinator_bootstrap_grants(\n grant_id, group_id, seed_device_id, worker_device_id, expires_at, created_at, created_by, revoked_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, NULL)").bind(r, t.groupId, t.seedDeviceId, t.workerDeviceId, t.expiresAt, i, t.createdBy).run(), n(await p(this.db.prepare("SELECT grant_id, group_id, seed_device_id, worker_device_id, expires_at, created_at, created_by, revoked_at\n FROM coordinator_bootstrap_grants WHERE grant_id = ?").bind(r)));
237
+ }
238
+ async getBootstrapGrant(e) {
239
+ let t = await p(this.db.prepare("SELECT grant_id, group_id, seed_device_id, worker_device_id, expires_at, created_at, created_by, revoked_at\n FROM coordinator_bootstrap_grants WHERE grant_id = ?").bind(e));
240
+ return t ? n(t) : null;
241
+ }
242
+ async listBootstrapGrants(e) {
243
+ return (await f(this.db.prepare("SELECT grant_id, group_id, seed_device_id, worker_device_id, expires_at, created_at, created_by, revoked_at\n FROM coordinator_bootstrap_grants WHERE group_id = ?\n ORDER BY created_at DESC, grant_id DESC").bind(e))).map((e) => n(e));
244
+ }
245
+ async revokeBootstrapGrant(e, t = o()) {
246
+ return await m(this.db.prepare("UPDATE coordinator_bootstrap_grants\n SET revoked_at = COALESCE(revoked_at, ?)\n WHERE grant_id = ?").bind(t, e)) > 0;
186
247
  }
187
248
  async listReciprocalApprovals(e) {
188
249
  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
250
+ return (await f(this.db.prepare(`SELECT request_id, group_id, requesting_device_id, requested_device_id, status, created_at, resolved_at
190
251
  FROM coordinator_reciprocal_approvals
191
252
  WHERE group_id = ? AND ${t} = ? AND status = ?
192
253
  ORDER BY created_at ASC, request_id ASC`).bind(e.groupId, e.deviceId, r))).map((e) => n(e));
@@ -202,7 +263,7 @@ var p = class {
202
263
  }
203
264
  async listGroupPeers(e, t) {
204
265
  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) => {
266
+ return (await f(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
267
  let t = String(e.expires_at ?? "").trim(), r = !0;
207
268
  if (t) {
208
269
  let e = new Date(t);
@@ -225,9 +286,9 @@ var p = class {
225
286
  };
226
287
  //#endregion
227
288
  //#region src/d1-coordinator-runtime.ts
228
- function m(e) {
289
+ function g(e) {
229
290
  return t({
230
- storeFactory: () => new p(e.db),
291
+ storeFactory: () => new h(e.db),
231
292
  runtime: {
232
293
  adminSecret: () => String(e.adminSecret ?? "").trim() || null,
233
294
  now: e.now ?? (() => (/* @__PURE__ */ new Date()).toISOString())
@@ -236,6 +297,6 @@ function m(e) {
236
297
  });
237
298
  }
238
299
  //#endregion
239
- export { p as D1CoordinatorStore, e as DEFAULT_TIME_WINDOW_S, m as createD1CoordinatorApp };
300
+ export { h as D1CoordinatorStore, e as DEFAULT_TIME_WINDOW_S, g as createD1CoordinatorApp };
240
301
 
241
302
  //# 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 {\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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"}
1
+ {"version":3,"file":"cloudflare-coordinator.js","names":[],"sources":["../../src/d1-coordinator-store.ts","../../src/d1-coordinator-runtime.ts"],"sourcesContent":["import type {\n\tCoordinatorBootstrapGrant,\n\tCoordinatorCreateBootstrapGrantInput,\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\tCoordinatorReviewJoinRequestBootstrapGrantInput,\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\nfunction requireTrimmedBootstrapGrantInput(opts: CoordinatorCreateBootstrapGrantInput): {\n\tgroupId: string;\n\tseedDeviceId: string;\n\tworkerDeviceId: string;\n\texpiresAt: string;\n\tcreatedBy: string | null;\n} {\n\tconst groupId = String(opts.groupId ?? \"\").trim();\n\tconst seedDeviceId = String(opts.seedDeviceId ?? \"\").trim();\n\tconst workerDeviceId = String(opts.workerDeviceId ?? \"\").trim();\n\tconst expiresAt = String(opts.expiresAt ?? \"\").trim();\n\tconst createdBy = String(opts.createdBy ?? \"\").trim() || null;\n\tif (!groupId || !seedDeviceId || !workerDeviceId || !expiresAt) {\n\t\tthrow new Error(\"groupId, seedDeviceId, workerDeviceId, and expiresAt are required.\");\n\t}\n\treturn { groupId, seedDeviceId, workerDeviceId, expiresAt, createdBy };\n}\n\nfunction normalizeBootstrapGrantRequest(\n\tinput: CoordinatorReviewJoinRequestBootstrapGrantInput | null | undefined,\n): CoordinatorCreateBootstrapGrantInput | null {\n\tif (!input) return null;\n\tconst seedDeviceId = String(input.seedDeviceId ?? \"\").trim();\n\tconst expiresAt = String(input.expiresAt ?? \"\").trim();\n\tconst createdBy = String(input.createdBy ?? \"\").trim() || null;\n\tif (!seedDeviceId || !expiresAt) {\n\t\tthrow new Error(\"bootstrapGrant.seedDeviceId and expiresAt are required.\");\n\t}\n\treturn {\n\t\tgroupId: \"\",\n\t\tseedDeviceId,\n\t\tworkerDeviceId: \"\",\n\t\texpiresAt,\n\t\tcreatedBy,\n\t};\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 bootstrapGrantRequest = normalizeBootstrapGrantRequest(_opts.bootstrapGrant);\n\t\tconst reviewedAt = nowISO();\n\t\tconst nextStatus = _opts.approved ? \"approved\" : \"denied\";\n\t\tlet bootstrapGrantInput: CoordinatorCreateBootstrapGrantInput | null = null;\n\t\tlet bootstrapGrantId: string | null = null;\n\t\tif (_opts.approved && bootstrapGrantRequest) {\n\t\t\tconst seedEnrollment = await firstRow<{ device_id: string }>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(\n\t\t\t\t\t\t`SELECT device_id FROM enrolled_devices WHERE group_id = ? AND device_id = ? AND enabled = 1`,\n\t\t\t\t\t)\n\t\t\t\t\t.bind(row.group_id, bootstrapGrantRequest.seedDeviceId),\n\t\t\t);\n\t\t\tif (!seedEnrollment) {\n\t\t\t\tthrow new Error(\"bootstrap grant seed device is not enrolled in the group.\");\n\t\t\t}\n\t\t\tif (bootstrapGrantRequest.seedDeviceId === row.device_id) {\n\t\t\t\tthrow new Error(\"bootstrap grant seed and worker device ids must differ.\");\n\t\t\t}\n\t\t\tbootstrapGrantInput = {\n\t\t\t\t...bootstrapGrantRequest,\n\t\t\t\tgroupId: row.group_id,\n\t\t\t\tworkerDeviceId: row.device_id,\n\t\t\t};\n\t\t}\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\tif (bootstrapGrantInput) {\n\t\t\t\t\tbootstrapGrantId = tokenUrlSafe(12);\n\t\t\t\t\tconst createdAt = nowISO();\n\t\t\t\t\t// Conditional INSERT: only mint a grant if the join request was\n\t\t\t\t\t// actually transitioned to 'approved' by the UPDATE in this batch.\n\t\t\t\t\t// This prevents issuing extra grants under concurrent review races.\n\t\t\t\t\tstatements.push(\n\t\t\t\t\t\tthis.db\n\t\t\t\t\t\t\t.prepare(`INSERT INTO coordinator_bootstrap_grants(\n\t\t\t\t\t\t\tgrant_id, group_id, seed_device_id, worker_device_id, expires_at, created_at, created_by, revoked_at\n\t\t\t\t\t\t) SELECT ?, ?, ?, ?, ?, ?, ?, NULL\n\t\t\t\t\t\tWHERE EXISTS (\n\t\t\t\t\t\t\tSELECT 1 FROM coordinator_join_requests\n\t\t\t\t\t\t\tWHERE request_id = ? AND status = 'approved' AND reviewed_at = ?\n\t\t\t\t\t\t)`)\n\t\t\t\t\t\t\t.bind(\n\t\t\t\t\t\t\t\tbootstrapGrantId,\n\t\t\t\t\t\t\t\tbootstrapGrantInput.groupId,\n\t\t\t\t\t\t\t\tbootstrapGrantInput.seedDeviceId,\n\t\t\t\t\t\t\t\tbootstrapGrantInput.workerDeviceId,\n\t\t\t\t\t\t\t\tbootstrapGrantInput.expiresAt,\n\t\t\t\t\t\t\t\tcreatedAt,\n\t\t\t\t\t\t\t\tbootstrapGrantInput.createdBy ?? null,\n\t\t\t\t\t\t\t\t_opts.requestId,\n\t\t\t\t\t\t\t\treviewedAt,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait this.db.batch(statements);\n\t\t} else {\n\t\t\tlet createdGrantId: string | null = null;\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\tif (bootstrapGrantInput) {\n\t\t\t\t\tconst grant = await this.createBootstrapGrant(bootstrapGrantInput);\n\t\t\t\t\tcreatedGrantId = grant.grant_id;\n\t\t\t\t\tbootstrapGrantId = grant.grant_id;\n\t\t\t\t}\n\t\t\t}\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\tif (createdGrantId) {\n\t\t\t\t\tawait this.revokeBootstrapGrant(createdGrantId);\n\t\t\t\t}\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}\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\tif (!updated) return null;\n\t\tconst bootstrapGrant = bootstrapGrantInput\n\t\t\t? bootstrapGrantId\n\t\t\t\t? await this.getBootstrapGrant(bootstrapGrantId)\n\t\t\t\t: await this.createBootstrapGrant(bootstrapGrantInput)\n\t\t\t: null;\n\t\treturn {\n\t\t\t...rowToRecord<CoordinatorJoinRequestReviewResult>(updated),\n\t\t\tbootstrap_grant: bootstrapGrant,\n\t\t};\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 createBootstrapGrant(\n\t\topts: CoordinatorCreateBootstrapGrantInput,\n\t): Promise<CoordinatorBootstrapGrant> {\n\t\tconst normalized = requireTrimmedBootstrapGrantInput(opts);\n\t\tconst grantId = tokenUrlSafe(12);\n\t\tconst createdAt = nowISO();\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO coordinator_bootstrap_grants(\n\t\t\t\tgrant_id, group_id, seed_device_id, worker_device_id, expires_at, created_at, created_by, revoked_at\n\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, NULL)`)\n\t\t\t.bind(\n\t\t\t\tgrantId,\n\t\t\t\tnormalized.groupId,\n\t\t\t\tnormalized.seedDeviceId,\n\t\t\t\tnormalized.workerDeviceId,\n\t\t\t\tnormalized.expiresAt,\n\t\t\t\tcreatedAt,\n\t\t\t\tnormalized.createdBy,\n\t\t\t)\n\t\t\t.run();\n\t\tconst row = await firstRow<CoordinatorBootstrapGrant>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT grant_id, group_id, seed_device_id, worker_device_id, expires_at, created_at, created_by, revoked_at\n\t\t\t\t\t FROM coordinator_bootstrap_grants WHERE grant_id = ?`)\n\t\t\t\t.bind(grantId),\n\t\t);\n\t\treturn rowToRecord<CoordinatorBootstrapGrant>(row);\n\t}\n\n\tasync getBootstrapGrant(grantId: string): Promise<CoordinatorBootstrapGrant | null> {\n\t\tconst row = await firstRow<CoordinatorBootstrapGrant>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT grant_id, group_id, seed_device_id, worker_device_id, expires_at, created_at, created_by, revoked_at\n\t\t\t\t\t FROM coordinator_bootstrap_grants WHERE grant_id = ?`)\n\t\t\t\t.bind(grantId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorBootstrapGrant>(row) : null;\n\t}\n\n\tasync listBootstrapGrants(groupId: string): Promise<CoordinatorBootstrapGrant[]> {\n\t\treturn (\n\t\t\tawait allRows<CoordinatorBootstrapGrant>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT grant_id, group_id, seed_device_id, worker_device_id, expires_at, created_at, created_by, revoked_at\n\t\t\t\t\t\t FROM coordinator_bootstrap_grants WHERE group_id = ?\n\t\t\t\t\t\t ORDER BY created_at DESC, grant_id DESC`)\n\t\t\t\t\t.bind(groupId),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorBootstrapGrant>(row));\n\t}\n\n\tasync revokeBootstrapGrant(grantId: string, revokedAt = nowISO()): 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 coordinator_bootstrap_grants\n\t\t\t\t\t\t SET revoked_at = COALESCE(revoked_at, ?)\n\t\t\t\t\t\t WHERE grant_id = ?`)\n\t\t\t\t\t.bind(revokedAt, grantId),\n\t\t\t)) > 0\n\t\t);\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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"}