@codemem/core 0.36.0 → 0.37.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.
- package/dist/better-sqlite-coordinator-store.d.ts.map +1 -1
- package/dist/{coordinator-api-CbONcz8J.js → coordinator-api-hZ1RVJUN.js} +132 -124
- package/dist/coordinator-api-hZ1RVJUN.js.map +1 -0
- package/dist/coordinator-api.d.ts.map +1 -1
- package/dist/coordinator-runtime.d.ts +5 -0
- package/dist/coordinator-runtime.d.ts.map +1 -1
- package/dist/coordinator-store-contract.d.ts +8 -0
- package/dist/coordinator-store-contract.d.ts.map +1 -1
- package/dist/d1-coordinator-store.d.ts.map +1 -1
- package/dist/db.d.ts +10 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/filters.d.ts +11 -0
- package/dist/filters.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4645 -4336
- package/dist/index.js.map +1 -1
- package/dist/internal/cloudflare-coordinator.js +193 -188
- package/dist/internal/cloudflare-coordinator.js.map +1 -1
- package/dist/observer-config.d.ts.map +1 -1
- package/dist/pack.d.ts.map +1 -1
- package/dist/raw-event-sweeper.d.ts.map +1 -1
- package/dist/request-rate-limit.d.ts +11 -0
- package/dist/request-rate-limit.d.ts.map +1 -1
- package/dist/scope-backfill.d.ts +2 -1
- package/dist/scope-backfill.d.ts.map +1 -1
- package/dist/scope-resolution.d.ts +34 -0
- package/dist/scope-resolution.d.ts.map +1 -1
- package/dist/search.d.ts +3 -0
- package/dist/search.d.ts.map +1 -1
- package/dist/store.d.ts +41 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/sync-bootstrap.d.ts +10 -0
- package/dist/sync-bootstrap.d.ts.map +1 -1
- package/dist/sync-pass.d.ts.map +1 -1
- package/dist/sync-replication.d.ts +1 -0
- package/dist/sync-replication.d.ts.map +1 -1
- package/dist/sync-retention-runner.d.ts +1 -0
- package/dist/sync-retention-runner.d.ts.map +1 -1
- package/dist/vectors.d.ts +2 -4
- package/dist/vectors.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/coordinator-api-CbONcz8J.js.map +0 -1
|
@@ -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\tCoordinatorBootstrapGrant,\n\tCoordinatorCreateBootstrapGrantInput,\n\tCoordinatorCreateInviteInput,\n\tCoordinatorCreateJoinRequestInput,\n\tCoordinatorCreateReciprocalApprovalInput,\n\tCoordinatorCreateScopeInput,\n\tCoordinatorEnrollDeviceInput,\n\tCoordinatorEnrollment,\n\tCoordinatorGrantScopeMembershipInput,\n\tCoordinatorGroup,\n\tCoordinatorInvite,\n\tCoordinatorJoinRequest,\n\tCoordinatorJoinRequestReviewResult,\n\tCoordinatorListReciprocalApprovalsInput,\n\tCoordinatorListScopeMembershipAuditInput,\n\tCoordinatorListScopesInput,\n\tCoordinatorPeerRecord,\n\tCoordinatorPresenceRecord,\n\tCoordinatorReciprocalApproval,\n\tCoordinatorReviewJoinRequestBootstrapGrantInput,\n\tCoordinatorReviewJoinRequestInput,\n\tCoordinatorRevokeScopeMembershipInput,\n\tCoordinatorScope,\n\tCoordinatorScopeMembership,\n\tCoordinatorScopeMembershipAuditEvent,\n\tCoordinatorStore,\n\tCoordinatorUpdateScopeInput,\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 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 clean(value: string | null | undefined): string | null {\n\tconst trimmed = value?.trim();\n\treturn trimmed ? trimmed : null;\n}\n\nfunction normalizeEpoch(value: number | null | undefined, fallback = 0): number {\n\tif (value == null) return fallback;\n\tif (!Number.isFinite(value) || value < 0)\n\t\tthrow new Error(\"membershipEpoch must be non-negative.\");\n\treturn Math.trunc(value);\n}\n\nfunction normalizeCreateScopeInput(opts: CoordinatorCreateScopeInput) {\n\tconst scopeId = clean(opts.scopeId);\n\tconst label = clean(opts.label);\n\tif (!scopeId || !label) throw new Error(\"scopeId and label are required.\");\n\treturn {\n\t\tscopeId,\n\t\tlabel,\n\t\tkind: clean(opts.kind) ?? \"user\",\n\t\tauthorityType: clean(opts.authorityType) ?? \"coordinator\",\n\t\tcoordinatorId: clean(opts.coordinatorId),\n\t\tgroupId: clean(opts.groupId),\n\t\tmanifestIssuerDeviceId: clean(opts.manifestIssuerDeviceId),\n\t\tmembershipEpoch: normalizeEpoch(opts.membershipEpoch),\n\t\tmanifestHash: clean(opts.manifestHash),\n\t\tstatus: clean(opts.status) ?? \"active\",\n\t};\n}\n\nfunction cleanRequiredUpdate(\n\tvalue: string | null | undefined,\n\tcurrent: string,\n\tfieldName: string,\n): string {\n\tif (value === undefined) return current;\n\tconst cleaned = clean(value);\n\tif (!cleaned) throw new Error(`${fieldName} must not be empty.`);\n\treturn cleaned;\n}\n\nfunction cleanNullableUpdate(\n\tvalue: string | null | undefined,\n\tcurrent: string | null,\n): string | null {\n\treturn value === undefined ? current : clean(value);\n}\n\nfunction normalizeUpdateScopeInput(opts: CoordinatorUpdateScopeInput, existing: CoordinatorScope) {\n\tconst scopeId = clean(opts.scopeId);\n\tif (!scopeId) throw new Error(\"scopeId is required.\");\n\tconst requestedEpoch = opts.membershipEpoch == null ? null : normalizeEpoch(opts.membershipEpoch);\n\tif (requestedEpoch != null && requestedEpoch < existing.membership_epoch) {\n\t\tthrow new Error(\"membershipEpoch must not move backwards.\");\n\t}\n\treturn {\n\t\tscopeId,\n\t\tlabel: cleanRequiredUpdate(opts.label, existing.label, \"label\"),\n\t\tkind: cleanRequiredUpdate(opts.kind, existing.kind, \"kind\"),\n\t\tauthorityType: cleanRequiredUpdate(\n\t\t\topts.authorityType,\n\t\t\texisting.authority_type,\n\t\t\t\"authorityType\",\n\t\t),\n\t\tcoordinatorId: cleanNullableUpdate(opts.coordinatorId, existing.coordinator_id),\n\t\tgroupId: cleanNullableUpdate(opts.groupId, existing.group_id),\n\t\tmanifestIssuerDeviceId: cleanNullableUpdate(\n\t\t\topts.manifestIssuerDeviceId,\n\t\t\texisting.manifest_issuer_device_id,\n\t\t),\n\t\tmembershipEpoch: requestedEpoch ?? existing.membership_epoch,\n\t\tmanifestHash: cleanNullableUpdate(opts.manifestHash, existing.manifest_hash),\n\t\tstatus: cleanRequiredUpdate(opts.status, existing.status, \"status\"),\n\t};\n}\n\nfunction normalizeGrantInput(\n\topts: CoordinatorGrantScopeMembershipInput,\n\tscope: CoordinatorScope | null,\n\texisting: CoordinatorScopeMembership | null,\n) {\n\tconst scopeId = clean(opts.scopeId);\n\tconst deviceId = clean(opts.deviceId);\n\tif (!scopeId || !deviceId) throw new Error(\"scopeId and deviceId are required.\");\n\tconst coordinatorId = clean(opts.coordinatorId);\n\tconst groupId = clean(opts.groupId);\n\tif (coordinatorId && coordinatorId !== scope?.coordinator_id) {\n\t\tthrow new Error(\"membership coordinatorId must match the scope coordinatorId.\");\n\t}\n\tif (groupId && groupId !== scope?.group_id) {\n\t\tthrow new Error(\"membership groupId must match the scope groupId.\");\n\t}\n\tconst requestedEpoch = opts.membershipEpoch == null ? null : normalizeEpoch(opts.membershipEpoch);\n\tconst inheritedEpoch = scope?.membership_epoch ?? 0;\n\tif (requestedEpoch != null && requestedEpoch < inheritedEpoch) {\n\t\tthrow new Error(\"membershipEpoch must not be lower than the scope membershipEpoch.\");\n\t}\n\tif (requestedEpoch != null && existing) {\n\t\tconst minimumEpoch =\n\t\t\texisting.status === \"revoked\" ? existing.membership_epoch + 1 : existing.membership_epoch;\n\t\tif (requestedEpoch < minimumEpoch) {\n\t\t\tthrow new Error(\"membershipEpoch must not move backwards.\");\n\t\t}\n\t}\n\tconst membershipEpoch =\n\t\trequestedEpoch ??\n\t\t(existing\n\t\t\t? Math.max(\n\t\t\t\t\tinheritedEpoch,\n\t\t\t\t\texisting.membership_epoch + (existing.status === \"revoked\" ? 1 : 0),\n\t\t\t\t)\n\t\t\t: inheritedEpoch);\n\treturn {\n\t\tscopeId,\n\t\tdeviceId,\n\t\trole: clean(opts.role) ?? \"member\",\n\t\tmembershipEpoch,\n\t\tcoordinatorId: scope?.coordinator_id ?? null,\n\t\tgroupId: scope?.group_id ?? null,\n\t\tmanifestIssuerDeviceId:\n\t\t\tclean(opts.manifestIssuerDeviceId) ?? scope?.manifest_issuer_device_id ?? null,\n\t\tmanifestHash: clean(opts.manifestHash) ?? scope?.manifest_hash ?? null,\n\t\tsignedManifestJson: clean(opts.signedManifestJson),\n\t\tactorType: clean(opts.actorType),\n\t\tactorId: clean(opts.actorId),\n\t};\n}\n\nfunction normalizeAuditLimit(limit: number | null | undefined): number {\n\tif (limit == null) return 100;\n\tif (!Number.isFinite(limit)) return 100;\n\treturn Math.max(1, Math.min(1000, Math.trunc(limit)));\n}\n\nfunction prepareMembershipAuditFromCurrentRow(\n\tdb: D1DatabaseLike,\n\tinput: {\n\t\taction: \"grant\" | \"revoke\";\n\t\tprevious: CoordinatorScopeMembership | null;\n\t\tscopeId: string;\n\t\tdeviceId: string;\n\t\tstatus: string;\n\t\tmembershipEpoch: number;\n\t\tupdatedAt: string;\n\t\tactorType: string | null;\n\t\tactorId: string | null;\n\t\tcreatedAt: string;\n\t},\n): D1PreparedStatementLike {\n\t// D1 batch() is the transaction boundary for audited membership changes.\n\t// changes() ties the audit insert to the immediately preceding mutation in\n\t// the same batch. Losing guarded updates insert no audit row, while winning\n\t// mutations still emit one required row; if the post-mutation membership row\n\t// is then missing, NOT NULL audit columns make the statement fail and roll\n\t// back the batch instead of committing an unaudited grant/revoke.\n\treturn db\n\t\t.prepare(`INSERT INTO coordinator_scope_membership_audit_log(\n\t\t\taction, scope_id, device_id, role, status, membership_epoch,\n\t\t\tprevious_role, previous_status, previous_membership_epoch,\n\t\t\tcoordinator_id, group_id, actor_type, actor_id, manifest_hash, created_at\n\t\t)\n\t\tSELECT ?, membership.scope_id, membership.device_id, membership.role, membership.status,\n\t\t\tmembership.membership_epoch, ?, ?, ?, membership.coordinator_id, membership.group_id,\n\t\t\t?, ?, membership.manifest_hash, ?\n\t\tFROM (SELECT 1 WHERE changes() > 0) AS required\n\t\tLEFT JOIN coordinator_scope_memberships AS membership\n\t\t\tON membership.scope_id = ?\n\t\t\tAND membership.device_id = ?\n\t\t\tAND membership.status = ?\n\t\t\tAND membership.membership_epoch = ?\n\t\t\tAND membership.updated_at = ?`)\n\t\t.bind(\n\t\t\tinput.action,\n\t\t\tinput.previous?.role ?? null,\n\t\t\tinput.previous?.status ?? null,\n\t\t\tinput.previous?.membership_epoch ?? null,\n\t\t\tinput.actorType,\n\t\t\tinput.actorId,\n\t\t\tinput.createdAt,\n\t\t\tinput.scopeId,\n\t\t\tinput.deviceId,\n\t\t\tinput.status,\n\t\t\tinput.membershipEpoch,\n\t\t\tinput.updatedAt,\n\t\t);\n}\n\nasync function runAuditedBatch(\n\tdb: D1DatabaseLike,\n\tstatements: D1PreparedStatementLike[],\n): Promise<unknown[]> {\n\tif (!db.batch) {\n\t\tthrow new Error(\"D1 batch support is required for audited scope membership changes.\");\n\t}\n\treturn await db.batch(statements);\n}\n\nfunction batchResultChanges(result: unknown): number {\n\treturn Number((result as D1RunResultLike | undefined)?.meta?.changes ?? 0);\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(\n\t\t\t\t\"INSERT OR IGNORE INTO groups(group_id, display_name, archived_at, created_at) VALUES (?, ?, NULL, ?)\",\n\t\t\t)\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(\n\t\t\t\t\t\"SELECT group_id, display_name, archived_at, created_at FROM groups WHERE group_id = ?\",\n\t\t\t\t)\n\t\t\t\t.bind(_groupId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorGroup>(row) : null;\n\t}\n\n\tasync listGroups(_includeArchived = false): Promise<CoordinatorGroup[]> {\n\t\tconst where = _includeArchived ? \"\" : \"WHERE archived_at IS NULL\";\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, archived_at, created_at FROM groups ${where} 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 renameGroup(_groupId: 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 groups SET display_name = ? WHERE group_id = ?\")\n\t\t\t\t\t.bind(_displayName, _groupId),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync archiveGroup(_groupId: string, _archivedAt = 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 groups SET archived_at = ? WHERE group_id = ? AND archived_at IS NULL\")\n\t\t\t\t\t.bind(_archivedAt, _groupId),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync unarchiveGroup(_groupId: 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\"UPDATE groups SET archived_at = NULL WHERE group_id = ? AND archived_at IS NOT NULL\",\n\t\t\t\t\t)\n\t\t\t\t\t.bind(_groupId),\n\t\t\t)) > 0\n\t\t);\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\tprivate async getScope(scopeId: string): Promise<CoordinatorScope | null> {\n\t\tconst row = await firstRow<CoordinatorScope>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT scope_id, label, kind, authority_type, coordinator_id, group_id,\n\t\t\t\t\t\tmanifest_issuer_device_id, membership_epoch, manifest_hash, status, created_at, updated_at\n\t\t\t\t\t FROM coordinator_scopes WHERE scope_id = ?`)\n\t\t\t\t.bind(scopeId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorScope>(row) : null;\n\t}\n\n\tprivate async getScopeMembership(\n\t\tscopeId: string,\n\t\tdeviceId: string,\n\t): Promise<CoordinatorScopeMembership | null> {\n\t\tconst row = await firstRow<CoordinatorScopeMembership>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT scope_id, device_id, role, status, membership_epoch, coordinator_id, group_id,\n\t\t\t\t\t\tmanifest_issuer_device_id, manifest_hash, signed_manifest_json, updated_at\n\t\t\t\t\t FROM coordinator_scope_memberships\n\t\t\t\t\t WHERE scope_id = ? AND device_id = ?`)\n\t\t\t\t.bind(scopeId, deviceId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorScopeMembership>(row) : null;\n\t}\n\n\tasync createScope(opts: CoordinatorCreateScopeInput): Promise<CoordinatorScope> {\n\t\tconst normalized = normalizeCreateScopeInput(opts);\n\t\tif (await this.getScope(normalized.scopeId)) throw new Error(\"scopeId already exists.\");\n\t\tconst now = nowISO();\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO coordinator_scopes(\n\t\t\t\t\tscope_id, label, kind, authority_type, coordinator_id, group_id,\n\t\t\t\t\tmanifest_issuer_device_id, membership_epoch, manifest_hash, status, created_at, updated_at\n\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)\n\t\t\t.bind(\n\t\t\t\tnormalized.scopeId,\n\t\t\t\tnormalized.label,\n\t\t\t\tnormalized.kind,\n\t\t\t\tnormalized.authorityType,\n\t\t\t\tnormalized.coordinatorId,\n\t\t\t\tnormalized.groupId,\n\t\t\t\tnormalized.manifestIssuerDeviceId,\n\t\t\t\tnormalized.membershipEpoch,\n\t\t\t\tnormalized.manifestHash,\n\t\t\t\tnormalized.status,\n\t\t\t\tnow,\n\t\t\t\tnow,\n\t\t\t)\n\t\t\t.run();\n\t\tconst scope = await this.getScope(normalized.scopeId);\n\t\tif (!scope) throw new Error(\"scope insert returned no row\");\n\t\treturn scope;\n\t}\n\n\tasync updateScope(opts: CoordinatorUpdateScopeInput): Promise<CoordinatorScope | null> {\n\t\tconst scopeId = clean(opts.scopeId);\n\t\tconst existing = scopeId ? await this.getScope(scopeId) : null;\n\t\tif (!existing) return null;\n\t\tconst normalized = normalizeUpdateScopeInput(opts, existing);\n\t\tawait this.db\n\t\t\t.prepare(`UPDATE coordinator_scopes\n\t\t\t\t SET label = ?,\n\t\t\t\t\t kind = ?,\n\t\t\t\t\t authority_type = ?,\n\t\t\t\t\t coordinator_id = ?,\n\t\t\t\t\t group_id = ?,\n\t\t\t\t\t manifest_issuer_device_id = ?,\n\t\t\t\t\t membership_epoch = ?,\n\t\t\t\t\t manifest_hash = ?,\n\t\t\t\t\t status = ?,\n\t\t\t\t\t updated_at = ?\n\t\t\t\t WHERE scope_id = ?`)\n\t\t\t.bind(\n\t\t\t\tnormalized.label,\n\t\t\t\tnormalized.kind,\n\t\t\t\tnormalized.authorityType,\n\t\t\t\tnormalized.coordinatorId,\n\t\t\t\tnormalized.groupId,\n\t\t\t\tnormalized.manifestIssuerDeviceId,\n\t\t\t\tnormalized.membershipEpoch,\n\t\t\t\tnormalized.manifestHash,\n\t\t\t\tnormalized.status,\n\t\t\t\tnowISO(),\n\t\t\t\tnormalized.scopeId,\n\t\t\t)\n\t\t\t.run();\n\t\treturn await this.getScope(normalized.scopeId);\n\t}\n\n\tasync listScopes(opts: CoordinatorListScopesInput = {}): Promise<CoordinatorScope[]> {\n\t\tconst where: string[] = [];\n\t\tconst params: unknown[] = [];\n\t\tconst coordinatorId = clean(opts.coordinatorId);\n\t\tconst groupId = clean(opts.groupId);\n\t\tconst status = clean(opts.status);\n\t\tif (coordinatorId) {\n\t\t\twhere.push(\"coordinator_id = ?\");\n\t\t\tparams.push(coordinatorId);\n\t\t}\n\t\tif (groupId) {\n\t\t\twhere.push(\"group_id = ?\");\n\t\t\tparams.push(groupId);\n\t\t}\n\t\tif (status) {\n\t\t\twhere.push(\"status = ?\");\n\t\t\tparams.push(status);\n\t\t} else if (!opts.includeInactive) {\n\t\t\twhere.push(\"status = 'active'\");\n\t\t}\n\t\tconst whereSql = where.length ? `WHERE ${where.join(\" AND \")}` : \"\";\n\t\tconst statement =\n\t\t\tthis.db.prepare(`SELECT scope_id, label, kind, authority_type, coordinator_id, group_id,\n\t\t\t\tmanifest_issuer_device_id, membership_epoch, manifest_hash, status, created_at, updated_at\n\t\t\t FROM coordinator_scopes ${whereSql}\n\t\t\t ORDER BY coordinator_id ASC, group_id ASC, scope_id ASC`);\n\t\treturn (\n\t\t\tawait allRows<CoordinatorScope>(params.length ? statement.bind(...params) : statement)\n\t\t).map((row) => rowToRecord<CoordinatorScope>(row));\n\t}\n\n\tasync grantScopeMembership(\n\t\topts: CoordinatorGrantScopeMembershipInput,\n\t): Promise<CoordinatorScopeMembership> {\n\t\tconst scopeId = clean(opts.scopeId);\n\t\tconst deviceId = clean(opts.deviceId);\n\t\tconst scope = scopeId ? await this.getScope(scopeId) : null;\n\t\tif (!scope) throw new Error(\"scopeId must reference an existing scope.\");\n\t\tconst existing = scopeId && deviceId ? await this.getScopeMembership(scopeId, deviceId) : null;\n\t\tconst normalized = normalizeGrantInput(opts, scope, existing);\n\t\tif (normalized.groupId) {\n\t\t\tconst enrollment = await this.getEnrollment(normalized.groupId, normalized.deviceId);\n\t\t\tif (!enrollment) {\n\t\t\t\tthrow new Error(\"device must be enrolled and enabled in the scope group.\");\n\t\t\t}\n\t\t}\n\t\tconst now = nowISO();\n\t\tconst batchResults = await runAuditedBatch(this.db, [\n\t\t\tthis.db\n\t\t\t\t.prepare(`INSERT INTO coordinator_scope_memberships(\n\t\t\t\t\tscope_id, device_id, role, status, membership_epoch, coordinator_id, group_id,\n\t\t\t\t\tmanifest_issuer_device_id, manifest_hash, signed_manifest_json, updated_at\n\t\t\t\t) VALUES (?, ?, ?, 'active', ?, ?, ?, ?, ?, ?, ?)\n\t\t\t\tON CONFLICT(scope_id, device_id) DO UPDATE SET\n\t\t\t\t\trole = excluded.role,\n\t\t\t\t\tstatus = 'active',\n\t\t\t\t\tmembership_epoch = excluded.membership_epoch,\n\t\t\t\t\tcoordinator_id = excluded.coordinator_id,\n\t\t\t\t\tgroup_id = excluded.group_id,\n\t\t\t\t\tmanifest_issuer_device_id = excluded.manifest_issuer_device_id,\n\t\t\t\t\tmanifest_hash = excluded.manifest_hash,\n\t\t\t\t\tsigned_manifest_json = excluded.signed_manifest_json,\n\t\t\t\t\tupdated_at = excluded.updated_at\n\t\t\t\tWHERE excluded.membership_epoch > coordinator_scope_memberships.membership_epoch\n\t\t\t\t OR (\n\t\t\t\t\texcluded.membership_epoch = coordinator_scope_memberships.membership_epoch\n\t\t\t\t\tAND coordinator_scope_memberships.status != 'revoked'\n\t\t\t\t )`)\n\t\t\t\t.bind(\n\t\t\t\t\tnormalized.scopeId,\n\t\t\t\t\tnormalized.deviceId,\n\t\t\t\t\tnormalized.role,\n\t\t\t\t\tnormalized.membershipEpoch,\n\t\t\t\t\tnormalized.coordinatorId,\n\t\t\t\t\tnormalized.groupId,\n\t\t\t\t\tnormalized.manifestIssuerDeviceId,\n\t\t\t\t\tnormalized.manifestHash,\n\t\t\t\t\tnormalized.signedManifestJson,\n\t\t\t\t\tnow,\n\t\t\t\t),\n\t\t\tprepareMembershipAuditFromCurrentRow(this.db, {\n\t\t\t\taction: \"grant\",\n\t\t\t\tprevious: existing,\n\t\t\t\tscopeId: normalized.scopeId,\n\t\t\t\tdeviceId: normalized.deviceId,\n\t\t\t\tstatus: \"active\",\n\t\t\t\tmembershipEpoch: normalized.membershipEpoch,\n\t\t\t\tupdatedAt: now,\n\t\t\t\tactorType: normalized.actorType,\n\t\t\t\tactorId: normalized.actorId,\n\t\t\t\tcreatedAt: now,\n\t\t\t}),\n\t\t]);\n\t\tif (batchResultChanges(batchResults[0]) <= 0) {\n\t\t\tthrow new Error(\"scope membership grant was not applied.\");\n\t\t}\n\t\tconst row = await firstRow<CoordinatorScopeMembership>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT scope_id, device_id, role, status, membership_epoch, coordinator_id, group_id,\n\t\t\t\t\t\tmanifest_issuer_device_id, manifest_hash, signed_manifest_json, updated_at\n\t\t\t\t\t FROM coordinator_scope_memberships\n\t\t\t\t\t WHERE scope_id = ? AND device_id = ?`)\n\t\t\t\t.bind(normalized.scopeId, normalized.deviceId),\n\t\t);\n\t\treturn rowToRecord<CoordinatorScopeMembership>(row);\n\t}\n\n\tasync revokeScopeMembership(opts: CoordinatorRevokeScopeMembershipInput): Promise<boolean> {\n\t\tconst scopeId = clean(opts.scopeId);\n\t\tconst deviceId = clean(opts.deviceId);\n\t\tif (!scopeId || !deviceId) throw new Error(\"scopeId and deviceId are required.\");\n\t\tconst membershipEpoch =\n\t\t\topts.membershipEpoch == null ? null : normalizeEpoch(opts.membershipEpoch);\n\t\tconst existing = await this.getScopeMembership(scopeId, deviceId);\n\t\tif (!existing) return false;\n\t\tif (membershipEpoch != null && existing && membershipEpoch <= existing.membership_epoch) {\n\t\t\tthrow new Error(\"membershipEpoch must increase on revoke.\");\n\t\t}\n\t\tconst now = nowISO();\n\t\tconst revokedEpoch = membershipEpoch ?? existing.membership_epoch + 1;\n\t\tconst updateStatement = this.db\n\t\t\t.prepare(`UPDATE coordinator_scope_memberships\n\t\t\t\t\t\t SET status = 'revoked',\n\t\t\t\t\t\t\t membership_epoch = CASE WHEN ? IS NULL THEN membership_epoch + 1 ELSE ? END,\n\t\t\t\t\t\t\t manifest_hash = COALESCE(?, manifest_hash),\n\t\t\t\t\t\t\t signed_manifest_json = COALESCE(?, signed_manifest_json),\n\t\t\t\t\t\t\t updated_at = ?\n\t\t\t\t\t\t WHERE scope_id = ? AND device_id = ? AND membership_epoch = ? AND status = ?`)\n\t\t\t.bind(\n\t\t\t\tmembershipEpoch,\n\t\t\t\tmembershipEpoch,\n\t\t\t\tclean(opts.manifestHash),\n\t\t\t\tclean(opts.signedManifestJson),\n\t\t\t\tnow,\n\t\t\t\tscopeId,\n\t\t\t\tdeviceId,\n\t\t\t\texisting.membership_epoch,\n\t\t\t\texisting.status,\n\t\t\t);\n\t\tconst auditStatement = prepareMembershipAuditFromCurrentRow(this.db, {\n\t\t\taction: \"revoke\",\n\t\t\tprevious: existing,\n\t\t\tscopeId,\n\t\t\tdeviceId,\n\t\t\tstatus: \"revoked\",\n\t\t\tmembershipEpoch: revokedEpoch,\n\t\t\tupdatedAt: now,\n\t\t\tactorType: clean(opts.actorType),\n\t\t\tactorId: clean(opts.actorId),\n\t\t\tcreatedAt: now,\n\t\t});\n\t\tlet batchResults: unknown[];\n\t\ttry {\n\t\t\tbatchResults = await runAuditedBatch(this.db, [updateStatement, auditStatement]);\n\t\t} catch (err) {\n\t\t\tconst current = await this.getScopeMembership(scopeId, deviceId);\n\t\t\tif (\n\t\t\t\t!current ||\n\t\t\t\tcurrent.membership_epoch !== existing.membership_epoch ||\n\t\t\t\tcurrent.status !== existing.status\n\t\t\t) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t\tif (batchResultChanges(batchResults[0]) <= 0) return false;\n\t\treturn true;\n\t}\n\n\tasync listScopeMemberships(\n\t\tscopeId: string,\n\t\tincludeRevoked = false,\n\t): Promise<CoordinatorScopeMembership[]> {\n\t\tconst normalizedScopeId = clean(scopeId);\n\t\tif (!normalizedScopeId) throw new Error(\"scopeId is required.\");\n\t\tconst statusWhere = includeRevoked ? \"\" : \"AND status = 'active'\";\n\t\treturn (\n\t\t\tawait allRows<CoordinatorScopeMembership>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT scope_id, device_id, role, status, membership_epoch, coordinator_id, group_id,\n\t\t\t\t\t\t\tmanifest_issuer_device_id, manifest_hash, signed_manifest_json, updated_at\n\t\t\t\t\t\t FROM coordinator_scope_memberships\n\t\t\t\t\t\t WHERE scope_id = ? ${statusWhere}\n\t\t\t\t\t\t ORDER BY device_id ASC`)\n\t\t\t\t\t.bind(normalizedScopeId),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorScopeMembership>(row));\n\t}\n\n\tasync listScopeMembershipAuditEvents(\n\t\topts: CoordinatorListScopeMembershipAuditInput,\n\t): Promise<CoordinatorScopeMembershipAuditEvent[]> {\n\t\tconst scopeId = clean(opts.scopeId);\n\t\tif (!scopeId) throw new Error(\"scopeId is required.\");\n\t\tconst deviceId = clean(opts.deviceId);\n\t\tconst limit = normalizeAuditLimit(opts.limit);\n\t\tconst where = deviceId ? \"scope_id = ? AND device_id = ?\" : \"scope_id = ?\";\n\t\tconst params = deviceId ? [scopeId, deviceId, limit] : [scopeId, limit];\n\t\treturn (\n\t\t\tawait allRows<CoordinatorScopeMembershipAuditEvent>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT event_id, action, scope_id, device_id, role, status, membership_epoch,\n\t\t\t\t\t\t\tprevious_role, previous_status, previous_membership_epoch,\n\t\t\t\t\t\t\tcoordinator_id, group_id, actor_type, actor_id, manifest_hash, created_at\n\t\t\t\t\t\t FROM coordinator_scope_membership_audit_log\n\t\t\t\t\t\t WHERE ${where}\n\t\t\t\t\t\t ORDER BY event_id ASC\n\t\t\t\t\t\t LIMIT ?`)\n\t\t\t\t\t.bind(...params),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorScopeMembershipAuditEvent>(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":";;AA0DA,SAAS,EAAe,GAAiB;CACxC,IAAI,KAAO,MAAM,MAAU,MAAM,cAAc;CAC/C,OAAO;AACR;AAEA,SAAS,EAAiB,GAAyB;CAClD,IAAM,IAAQ,EAAQ,KAAK;CAC3B,IAAI,CAAC,GAAO,OAAO;CACnB,IAAM,IAAa,EAAM,SAAS,KAAK,IAAI,IAAQ,UAAU;CAC7D,IAAI;EACH,IAAM,IAAM,IAAI,IAAI,CAAU;EAG9B,OAFI,CAAC,EAAI,YACL,EAAI,SAAS,OAAO,EAAI,IAAI,KAAK,KAAK,OAAO,EAAI,IAAI,IAAI,SAAe,KACrE,EAAI,SAAS,EAAI,SAAS,QAAQ,QAAQ,EAAE;CACpD,QAAQ;EACP,OAAO;CACR;AACD;AAEA,SAAS,EAAiB,GAAyB;CAClD,IAAI,CAAC,GAAS,OAAO;CACrB,IAAI;EACH,IAAM,IAAS,IAAI,IAAI,CAAO,GACxB,IAAO,EAAO,SAAS,YAAY;EACzC,KACE,EAAO,aAAa,WAAW,EAAO,aAAa,OACpD,KACA,EAAO,QACP,EAAO,aAAa,KAEpB,OAAO,GAAG,EAAK,GAAG,EAAO;CAE3B,QAAQ,CAAC;CACT,OAAO;AACR;AAEA,SAAS,EAAe,GAAoB,GAAgC;CAC3E,IAAM,IAAuB,CAAC,GACxB,oBAAO,IAAI,IAAY;CAC7B,KAAK,IAAM,KAAW,CAAC,GAAG,GAAU,GAAG,CAAU,GAAG;EACnD,IAAM,IAAU,EAAiB,CAAO,GAClC,IAAM,EAAiB,CAAO;EAChC,CAAC,KAAW,EAAK,IAAI,CAAG,MAC5B,EAAK,IAAI,CAAG,GACZ,EAAW,KAAK,CAAO;CACxB;CACA,OAAO;AACR;AAEA,SAAS,IAAiB;CACzB,wBAAO,IAAI,KAAK,GAAE,YAAY;AAC/B;AAEA,SAAS,EACR,GACA,GAIC;CACD,OAAO,KAAsB,IAC1B;EAAE,KAAK;EAAoB,MAAM;CAAkB,IACnD;EAAE,KAAK;EAAmB,MAAM;CAAmB;AACvD;AAEA,SAAS,EAAwB,GAAyB;CACzD,OAAO,aAAiB,SAAS,qBAAqB,KAAK,EAAM,OAAO;AACzE;AAEA,SAAS,EAAa,GAAuB;CAC5C,IACM,IAAS,IAAI,WAAW,CAAK;CACnC,WAAW,OAAO,gBAAgB,CAAM;CACxC,IAAM,IAAmB,CAAC;CAC1B,KAAK,IAAM,KAAQ,GAClB,EAAO,KAAK,mEAAS,IAAO,OAAoB,GAAG;CAEpD,OAAO,EAAO,KAAK,EAAE;AACtB;AAEA,SAAS,EAAkC,GAMzC;CACD,IAAM,IAAU,OAAO,EAAK,WAAW,EAAE,EAAE,KAAK,GAC1C,IAAe,OAAO,EAAK,gBAAgB,EAAE,EAAE,KAAK,GACpD,IAAiB,OAAO,EAAK,kBAAkB,EAAE,EAAE,KAAK,GACxD,IAAY,OAAO,EAAK,aAAa,EAAE,EAAE,KAAK,GAC9C,IAAY,OAAO,EAAK,aAAa,EAAE,EAAE,KAAK,KAAK;CACzD,IAAI,CAAC,KAAW,CAAC,KAAgB,CAAC,KAAkB,CAAC,GACpD,MAAU,MAAM,oEAAoE;CAErF,OAAO;EAAE;EAAS;EAAc;EAAgB;EAAW;CAAU;AACtE;AAEA,SAAS,EAAM,GAAiD;CAE/D,OADgB,GAAO,KAAK,KACD;AAC5B;AAEA,SAAS,EAAe,GAAkC,IAAW,GAAW;CAC/E,IAAI,KAAS,MAAM,OAAO;CAC1B,IAAI,CAAC,OAAO,SAAS,CAAK,KAAK,IAAQ,GACtC,MAAU,MAAM,uCAAuC;CACxD,OAAO,KAAK,MAAM,CAAK;AACxB;AAEA,SAAS,EAA0B,GAAmC;CACrE,IAAM,IAAU,EAAM,EAAK,OAAO,GAC5B,IAAQ,EAAM,EAAK,KAAK;CAC9B,IAAI,CAAC,KAAW,CAAC,GAAO,MAAU,MAAM,iCAAiC;CACzE,OAAO;EACN;EACA;EACA,MAAM,EAAM,EAAK,IAAI,KAAK;EAC1B,eAAe,EAAM,EAAK,aAAa,KAAK;EAC5C,eAAe,EAAM,EAAK,aAAa;EACvC,SAAS,EAAM,EAAK,OAAO;EAC3B,wBAAwB,EAAM,EAAK,sBAAsB;EACzD,iBAAiB,EAAe,EAAK,eAAe;EACpD,cAAc,EAAM,EAAK,YAAY;EACrC,QAAQ,EAAM,EAAK,MAAM,KAAK;CAC/B;AACD;AAEA,SAAS,EACR,GACA,GACA,GACS;CACT,IAAI,MAAU,KAAA,GAAW,OAAO;CAChC,IAAM,IAAU,EAAM,CAAK;CAC3B,IAAI,CAAC,GAAS,MAAU,MAAM,GAAG,EAAU,oBAAoB;CAC/D,OAAO;AACR;AAEA,SAAS,EACR,GACA,GACgB;CAChB,OAAO,MAAU,KAAA,IAAY,IAAU,EAAM,CAAK;AACnD;AAEA,SAAS,EAA0B,GAAmC,GAA4B;CACjG,IAAM,IAAU,EAAM,EAAK,OAAO;CAClC,IAAI,CAAC,GAAS,MAAU,MAAM,sBAAsB;CACpD,IAAM,IAAiB,EAAK,mBAAmB,OAAO,OAAO,EAAe,EAAK,eAAe;CAChG,IAAI,KAAkB,QAAQ,IAAiB,EAAS,kBACvD,MAAU,MAAM,0CAA0C;CAE3D,OAAO;EACN;EACA,OAAO,EAAoB,EAAK,OAAO,EAAS,OAAO,OAAO;EAC9D,MAAM,EAAoB,EAAK,MAAM,EAAS,MAAM,MAAM;EAC1D,eAAe,EACd,EAAK,eACL,EAAS,gBACT,eACD;EACA,eAAe,EAAoB,EAAK,eAAe,EAAS,cAAc;EAC9E,SAAS,EAAoB,EAAK,SAAS,EAAS,QAAQ;EAC5D,wBAAwB,EACvB,EAAK,wBACL,EAAS,yBACV;EACA,iBAAiB,KAAkB,EAAS;EAC5C,cAAc,EAAoB,EAAK,cAAc,EAAS,aAAa;EAC3E,QAAQ,EAAoB,EAAK,QAAQ,EAAS,QAAQ,QAAQ;CACnE;AACD;AAEA,SAAS,EACR,GACA,GACA,GACC;CACD,IAAM,IAAU,EAAM,EAAK,OAAO,GAC5B,IAAW,EAAM,EAAK,QAAQ;CACpC,IAAI,CAAC,KAAW,CAAC,GAAU,MAAU,MAAM,oCAAoC;CAC/E,IAAM,IAAgB,EAAM,EAAK,aAAa,GACxC,IAAU,EAAM,EAAK,OAAO;CAClC,IAAI,KAAiB,MAAkB,GAAO,gBAC7C,MAAU,MAAM,8DAA8D;CAE/E,IAAI,KAAW,MAAY,GAAO,UACjC,MAAU,MAAM,kDAAkD;CAEnE,IAAM,IAAiB,EAAK,mBAAmB,OAAO,OAAO,EAAe,EAAK,eAAe,GAC1F,IAAiB,GAAO,oBAAoB;CAClD,IAAI,KAAkB,QAAQ,IAAiB,GAC9C,MAAU,MAAM,mEAAmE;CAEpF,IAAI,KAAkB,QAAQ,KAGzB,KADH,EAAS,WAAW,YAAY,EAAS,mBAAmB,IAAI,EAAS,mBAEzE,MAAU,MAAM,0CAA0C;CAG5D,IAAM,IACL,MACC,IACE,KAAK,IACL,GACA,EAAS,mBAAoB,IAAS,WAAW,UAClD,IACC;CACJ,OAAO;EACN;EACA;EACA,MAAM,EAAM,EAAK,IAAI,KAAK;EAC1B;EACA,eAAe,GAAO,kBAAkB;EACxC,SAAS,GAAO,YAAY;EAC5B,wBACC,EAAM,EAAK,sBAAsB,KAAK,GAAO,6BAA6B;EAC3E,cAAc,EAAM,EAAK,YAAY,KAAK,GAAO,iBAAiB;EAClE,oBAAoB,EAAM,EAAK,kBAAkB;EACjD,WAAW,EAAM,EAAK,SAAS;EAC/B,SAAS,EAAM,EAAK,OAAO;CAC5B;AACD;AAEA,SAAS,EAAoB,GAA0C;CAGtE,OAFI,KAAS,QACT,CAAC,OAAO,SAAS,CAAK,IAAU,MAC7B,KAAK,IAAI,GAAG,KAAK,IAAI,KAAM,KAAK,MAAM,CAAK,CAAC,CAAC;AACrD;AAEA,SAAS,EACR,GACA,GAY0B;CAO1B,OAAO,EACL,QAAQ,wvBAcsB,EAC9B,KACA,EAAM,QACN,EAAM,UAAU,QAAQ,MACxB,EAAM,UAAU,UAAU,MAC1B,EAAM,UAAU,oBAAoB,MACpC,EAAM,WACN,EAAM,SACN,EAAM,WACN,EAAM,SACN,EAAM,UACN,EAAM,QACN,EAAM,iBACN,EAAM,SACP;AACF;AAEA,eAAe,EACd,GACA,GACqB;CACrB,IAAI,CAAC,EAAG,OACP,MAAU,MAAM,oEAAoE;CAErF,OAAO,MAAM,EAAG,MAAM,CAAU;AACjC;AAEA,SAAS,EAAmB,GAAyB;CACpD,OAAO,OAAQ,GAAwC,MAAM,WAAW,CAAC;AAC1E;AAEA,SAAS,EACR,GAC8C;CAC9C,IAAI,CAAC,GAAO,OAAO;CACnB,IAAM,IAAe,OAAO,EAAM,gBAAgB,EAAE,EAAE,KAAK,GACrD,IAAY,OAAO,EAAM,aAAa,EAAE,EAAE,KAAK,GAC/C,IAAY,OAAO,EAAM,aAAa,EAAE,EAAE,KAAK,KAAK;CAC1D,IAAI,CAAC,KAAgB,CAAC,GACrB,MAAU,MAAM,yDAAyD;CAE1E,OAAO;EACN,SAAS;EACT;EACA,gBAAgB;EAChB;EACA;CACD;AACD;AAEA,eAAe,EAAW,GAAkD;CAC3E,IAAM,IAAS,MAAM,EAAU,IAAO;CACtC,OAAO,MAAM,QAAQ,GAAQ,OAAO,IAAI,EAAO,UAAU,CAAC;AAC3D;AAEA,eAAe,EAAY,GAAuD;CACjF,OAAO,MAAM,EAAU,MAAS;AACjC;AAEA,eAAe,EAAW,GAAqD;CAC9E,IAAM,IAAU,MAAM,EAAU,IAAI;CACpC,OAAO,OAAO,GAAQ,MAAM,WAAW,CAAC;AACzC;AAEA,IAAa,IAAb,MAA4D;CAC3D;CAEA,YAAY,GAAoB;EAC/B,KAAK,KAAK;CACX;CAEA,MAAM,QAAuB,CAE7B;CAEA,MAAM,YAAY,GAAkB,GAA6C;EAChF,MAAM,KAAK,GACT,QACA,sGACD,EACC,KAAK,GAAU,KAAgB,MAAM,EAAO,CAAC,EAC7C,IAAI;CACP;CAEA,MAAM,SAAS,GAAoD;EAClE,IAAM,IAAM,MAAM,EACjB,KAAK,GACH,QACA,uFACD,EACC,KAAK,CAAQ,CAChB;EACA,OAAO,IAAM,EAA8B,CAAG,IAAI;CACnD;CAEA,MAAM,WAAW,IAAmB,IAAoC;EACvE,IAAM,IAAQ,IAAmB,KAAK;EACtC,QACC,MAAM,EACL,KAAK,GAAG,QACP,sEAAsE,EAAM,yBAC7E,CACD,GACC,KAAK,MAAQ,EAA8B,CAAG,CAAC;CAClD;CAEA,MAAM,YAAY,GAAkB,GAAwC;EAC3E,OACE,MAAM,EACN,KAAK,GACH,QAAQ,uDAAuD,EAC/D,KAAK,GAAc,CAAQ,CAC9B,IAAK;CAEP;CAEA,MAAM,aAAa,GAAkB,IAAc,EAAO,GAAqB;EAC9E,OACE,MAAM,EACN,KAAK,GACH,QAAQ,8EAA8E,EACtF,KAAK,GAAa,CAAQ,CAC7B,IAAK;CAEP;CAEA,MAAM,eAAe,GAAoC;EACxD,OACE,MAAM,EACN,KAAK,GACH,QACA,qFACD,EACC,KAAK,CAAQ,CAChB,IAAK;CAEP;CAEA,MAAM,aAAa,GAAkB,GAAoD;EACxF,MAAM,KAAK,GACT,QAAQ,sVAOI,EACZ,KACA,GACA,EAAM,UACN,EAAM,WACN,EAAM,aACN,EAAM,eAAe,MACrB,EAAO,CACR,EACC,IAAI;CACP;CAEA,MAAM,oBACL,GACA,GACmC;EACnC,IAAM,IAAQ,IAAmB,KAAK;EACtC,QACC,MAAM,EACL,KAAK,GACH,QAAQ;;4BAEc,EAAM;8CACY,EACxC,KAAK,CAAQ,CAChB,GACC,KAAK,MAAQ,EAAmC,CAAG,CAAC;CACvD;CAEA,MAAM,cAAc,GAAkB,GAA0D;EAC/F,IAAM,IAAM,MAAM,EACjB,KAAK,GACH,QAAQ,iLAE8C,EACtD,KAAK,GAAU,CAAS,CAC3B;EACA,OAAO,IAAM,EAAmC,CAAG,IAAI;CACxD;CAEA,MAAM,aAAa,GAAkB,GAAmB,GAAwC;EAC/F,OACE,MAAM,EACN,KAAK,GACH,QAAQ,0FAC6B,EACrC,KAAK,GAAc,GAAU,CAAS,CACzC,IAAK;CAEP;CAEA,MAAM,iBAAiB,GAAkB,GAAmB,GAAqC;EAChG,OACE,MAAM,EACN,KAAK,GACH,QAAQ,qFAC6B,EACrC,KAAK,MAAkB,GAAU,CAAS,CAC7C,IAAK;CAEP;CAEA,MAAM,aAAa,GAAkB,GAAqC;EAWzE,OAVA,MAAM,KAAK,GACT,QAAQ,mEAAmE,EAC3E,KAAK,GAAU,CAAS,EACxB,IAAI,GACN,MAAM,KAAK,GACT,QACA,2HACD,EACC,KAAK,GAAU,GAAW,CAAS,EACnC,IAAI,GAEJ,MAAM,EACN,KAAK,GACH,QAAQ,mEAAmE,EAC3E,KAAK,GAAU,CAAS,CAC3B,IAAK;CAEP;CAEA,MAAM,YAAY,GAAmB,GAAgB,GAAsC;EAC1F,OACE,MAAM,EACN,KAAK,GACH,QACA,qFACD,EACC,KAAK,GAAW,GAAQ,CAAU,CACrC,IAAK;CAEP;CAEA,MAAM,cAAc,GAAgC;EACnD,MAAM,KAAK,GAAG,QAAQ,iDAAiD,EAAE,KAAK,CAAO,EAAE,IAAI;CAC5F;CAEA,MAAM,aAAa,GAAiE;EACnF,IAAM,IAAM,EAAO,GACb,IAAW,EAAa,EAAE,GAC1B,IAAQ,EAAa,EAAE,GACvB,IAAQ,MAAM,KAAK,SAAS,EAAM,OAAO;EAsB/C,OArBA,MAAM,KAAK,GACT,QAAQ,0LAE+B,EACvC,KACA,GACA,EAAM,SACN,GACA,EAAM,QACN,EAAM,WACN,GACA,EAAM,aAAa,MACnB,GAAO,gBAAgB,IACxB,EACC,IAAI,GAOC,EAA+B,MANpB,EACjB,KAAK,GACH,QAAQ,mKACsC,EAC9C,KAAK,CAAQ,CAChB,CACyC;CAC1C;CAEA,MAAM,iBAAiB,GAAmD;EACzE,IAAM,IAAM,MAAM,EACjB,KAAK,GACH,QAAQ,kOAIc,EACtB,KAAK,GAAQ,EAAO,CAAC,CACxB;EACA,OAAO,IAAM,EAA+B,CAAG,IAAI;CACpD;CAEA,MAAM,YAAY,GAAgD;EACjE,QACC,MAAM,EACL,KAAK,GACH,QAAQ,kMAEiB,EACzB,KAAK,CAAQ,CAChB,GACC,KAAK,MAAQ,EAA+B,CAAG,CAAC;CACnD;CAEA,MAAM,kBACL,GACkC;EAClC,IAAM,IAAM,EAAO,GACb,IAAY,EAAa,EAAE;EAsBjC,OArBA,MAAM,KAAK,GACT,QAAQ,sOAEgD,EACxD,KACA,GACA,EAAM,SACN,EAAM,UACN,EAAM,WACN,EAAM,aACN,EAAM,eAAe,MACrB,EAAM,OACN,CACD,EACC,IAAI,GAOC,EAAoC,MANzB,EACjB,KAAK,GACH,QAAQ,+LAC6C,EACrD,KAAK,CAAS,CACjB,CAC8C;CAC/C;CAEA,MAAM,iBAAiB,GAAkB,GAAqD;EAC7F,IAAM,IAAS,KAAW;EAC1B,QACC,MAAM,EACL,KAAK,GACH,QAAQ,oQAGgC,EACxC,KAAK,GAAU,CAAM,CACxB,GACC,KAAK,MAAQ,EAAoC,CAAG,CAAC;CACxD;CAEA,MAAM,kBACL,GACqD;EACrD,IAAM,IAAM,MAAM,EACjB,KAAK,GACH,QAAQ,6MAE6C,EACrD,KAAK,EAAM,SAAS,CACvB;EACA,IAAI,CAAC,GAAK,OAAO;EACjB,IAAI,EAAI,WAAW,WAClB,OAAO;GAAE,GAAG,EAAoC,CAAG;GAAG,gBAAgB;EAAK;EAE5E,IAAM,IAAwB,EAA+B,EAAM,cAAc,GAC3E,IAAa,EAAO,GACpB,IAAa,EAAM,WAAW,aAAa,UAC7C,IAAmE,MACnE,IAAkC;EACtC,IAAI,EAAM,YAAY,GAAuB;GAQ5C,IAAI,CAAC,MAPwB,EAC5B,KAAK,GACH,QACA,6FACD,EACC,KAAK,EAAI,UAAU,EAAsB,YAAY,CACxD,GAEC,MAAU,MAAM,2DAA2D;GAE5E,IAAI,EAAsB,iBAAiB,EAAI,WAC9C,MAAU,MAAM,yDAAyD;GAE1E,IAAsB;IACrB,GAAG;IACH,SAAS,EAAI;IACb,gBAAgB,EAAI;GACrB;EACD;EACA,IAAI,KAAK,GAAG,OAAO;GAClB,IAAM,IAAwC,CAAC;GAQ/C,IAPA,EAAW,KACV,KAAK,GACH,QAAQ,+IAEqC,EAC7C,KAAK,GAAY,GAAY,EAAM,cAAc,MAAM,EAAM,SAAS,CACzE,GACI,EAAM,aACT,EAAW,KACV,KAAK,GACH,QAAQ,6hBAYI,EACZ,KAAK,EAAO,GAAG,EAAM,WAAW,CAAU,CAC7C,GACI,IAAqB;IACxB,IAAmB,EAAa,EAAE;IAClC,IAAM,IAAY,EAAO;IAIzB,EAAW,KACV,KAAK,GACH,QAAQ,0VAMR,EACA,KACA,GACA,EAAoB,SACpB,EAAoB,cACpB,EAAoB,gBACpB,EAAoB,WACpB,GACA,EAAoB,aAAa,MACjC,EAAM,WACN,CACD,CACF;GACD;GAED,MAAM,KAAK,GAAG,MAAM,CAAU;EAC/B,OAAO;GACN,IAAI,IAAgC;GACpC,IAAI,EAAM,aACT,MAAM,KAAK,aAAa,EAAI,UAAU;IACrC,UAAU,EAAI;IACd,aAAa,EAAI;IACjB,WAAW,EAAI;IACf,cAAc,EAAI,gBAAgB,IAAI,KAAK,KAAK;GACjD,CAAC,GACG,IAAqB;IACxB,IAAM,IAAQ,MAAM,KAAK,qBAAqB,CAAmB;IAEjE,AADA,IAAiB,EAAM,UACvB,IAAmB,EAAM;GAC1B;GASD,IAAI,MAPkB,EACrB,KAAK,GACH,QAAQ,+IAEqC,EAC7C,KAAK,GAAY,GAAY,EAAM,cAAc,MAAM,EAAM,SAAS,CACzE,MACgB,GAAG;IAClB,AAAI,KACH,MAAM,KAAK,qBAAqB,CAAc;IAE/C,IAAM,IAAS,MAAM,EACpB,KAAK,GACH,QAAQ,iMAC6C,EACrD,KAAK,EAAM,SAAS,CACvB;IACA,OAAO,IACJ;KAAE,GAAG,EAAgD,CAAM;KAAG,gBAAgB;IAAK,IACnF;GACJ;EACD;EACA,IAAM,IAAU,MAAM,EACrB,KAAK,GACH,QAAQ,+LAC6C,EACrD,KAAK,EAAM,SAAS,CACvB;EACA,IAAI,CAAC,GAAS,OAAO;EACrB,IAAM,IAAiB,IACpB,IACC,MAAM,KAAK,kBAAkB,CAAgB,IAC7C,MAAM,KAAK,qBAAqB,CAAmB,IACpD;EACH,OAAO;GACN,GAAG,EAAgD,CAAO;GAC1D,iBAAiB;EAClB;CACD;CAEA,MAAM,yBACL,GACyC;EACzC,IAAM,IAAU,EAAK,QAAQ,KAAK,GAC5B,IAAqB,EAAK,mBAAmB,KAAK,GAClD,IAAoB,EAAK,kBAAkB,KAAK;EACtD,IAAI,CAAC,KAAW,CAAC,KAAsB,CAAC,GACvC,MAAU,MAAM,kEAAkE;EAEnF,IAAI,MAAuB,GAC1B,MAAU,MAAM,kDAAkD;EAEnE,IAAM,IAAc,EAAsB,GAAoB,CAAiB,GACzE,IAAW,MAAM,EACtB,KAAK,GACH,QAAQ,wSAG0B,EAClC,KAAK,GAAS,GAAoB,CAAiB,CACtD;EACA,IAAI,GAAU,OAAO,EAA2C,CAAQ;EACxE,IAAM,IAAU,MAAM,EACrB,KAAK,GACH,QAAQ,wSAG0B,EAClC,KAAK,GAAS,GAAmB,CAAkB,CACtD;EACA,IAAI,GAAS;GACZ,IAAM,IAAa,EAAO;GAa1B,OAZA,MAAM,KAAK,GACT,QACA,wGACD,EACC,KAAK,GAAY,EAAQ,UAAU,EACnC,IAAI,GAOC,EAA2C,MAN1B,EACvB,KAAK,GACH,QAAQ,4KACoD,EAC5D,KAAK,EAAQ,UAAU,CAC1B,CAC2D;EAC5D;EACA,IAAM,IAAY,EAAa,EAAE,GAC3B,IAAY,EAAO;EACzB,IAAI;GACH,MAAM,KAAK,GACT,QAAQ,8TAUwC,EAChD,KACA,GACA,GACA,GACA,GACA,EAAY,KACZ,EAAY,MACZ,CACD,EACC,IAAI;EACP,SAAS,GAAO;GACf,IAAI,CAAC,EAAwB,CAAK,GAAG,MAAM;GAC3C,IAAM,IAAgB,MAAM,EAC3B,KAAK,GACH,QAAQ,2SAG0B,EAClC,KAAK,GAAS,GAAoB,CAAiB,CACtD;GACA,IAAI,GAAe,OAAO,EAA2C,CAAa;GAClF,IAAM,IAAuB,MAAM,EAClC,KAAK,GACH,QAAQ,2SAG0B,EAClC,KAAK,GAAS,GAAmB,CAAkB,CACtD;GACA,IAAI,GAAsB;IACzB,IAAM,IAAa,EAAO;IAa1B,OAZA,MAAM,KAAK,GACT,QACA,wGACD,EACC,KAAK,GAAY,EAAqB,UAAU,EAChD,IAAI,GAOC,EAA2C,MAN1B,EACvB,KAAK,GACH,QAAQ,6KACoD,EAC5D,KAAK,EAAqB,UAAU,CACvC,CAC2D;GAC5D;GACA,MAAM;EACP;EAOA,OAAO,EAA2C,MAN5B,EACrB,KAAK,GACH,QAAQ,2KACoD,EAC5D,KAAK,CAAS,CACjB,CACyD;CAC1D;CAEA,MAAM,qBACL,GACqC;EACrC,IAAM,IAAa,EAAkC,CAAI,GACnD,IAAU,EAAa,EAAE,GACzB,IAAY,EAAO;EAqBzB,OApBA,MAAM,KAAK,GACT,QAAQ,8LAE4B,EACpC,KACA,GACA,EAAW,SACX,EAAW,cACX,EAAW,gBACX,EAAW,WACX,GACA,EAAW,SACZ,EACC,IAAI,GAOC,EAAuC,MAN5B,EACjB,KAAK,GACH,QAAQ,yKAC8C,EACtD,KAAK,CAAO,CACf,CACiD;CAClD;CAEA,MAAc,SAAS,GAAmD;EACzE,IAAM,IAAM,MAAM,EACjB,KAAK,GACH,QAAQ,6NAEoC,EAC5C,KAAK,CAAO,CACf;EACA,OAAO,IAAM,EAA8B,CAAG,IAAI;CACnD;CAEA,MAAc,mBACb,GACA,GAC6C;EAC7C,IAAM,IAAM,MAAM,EACjB,KAAK,GACH,QAAQ,+PAG8B,EACtC,KAAK,GAAS,CAAQ,CACzB;EACA,OAAO,IAAM,EAAwC,CAAG,IAAI;CAC7D;CAEA,MAAM,YAAY,GAA8D;EAC/E,IAAM,IAAa,EAA0B,CAAI;EACjD,IAAI,MAAM,KAAK,SAAS,EAAW,OAAO,GAAG,MAAU,MAAM,yBAAyB;EACtF,IAAM,IAAM,EAAO;EACnB,MAAM,KAAK,GACT,QAAQ,4PAGsC,EAC9C,KACA,EAAW,SACX,EAAW,OACX,EAAW,MACX,EAAW,eACX,EAAW,eACX,EAAW,SACX,EAAW,wBACX,EAAW,iBACX,EAAW,cACX,EAAW,QACX,GACA,CACD,EACC,IAAI;EACN,IAAM,IAAQ,MAAM,KAAK,SAAS,EAAW,OAAO;EACpD,IAAI,CAAC,GAAO,MAAU,MAAM,8BAA8B;EAC1D,OAAO;CACR;CAEA,MAAM,YAAY,GAAqE;EACtF,IAAM,IAAU,EAAM,EAAK,OAAO,GAC5B,IAAW,IAAU,MAAM,KAAK,SAAS,CAAO,IAAI;EAC1D,IAAI,CAAC,GAAU,OAAO;EACtB,IAAM,IAAa,EAA0B,GAAM,CAAQ;EA4B3D,OA3BA,MAAM,KAAK,GACT,QAAQ,2SAWY,EACpB,KACA,EAAW,OACX,EAAW,MACX,EAAW,eACX,EAAW,eACX,EAAW,SACX,EAAW,wBACX,EAAW,iBACX,EAAW,cACX,EAAW,QACX,EAAO,GACP,EAAW,OACZ,EACC,IAAI,GACC,MAAM,KAAK,SAAS,EAAW,OAAO;CAC9C;CAEA,MAAM,WAAW,IAAmC,CAAC,GAAgC;EACpF,IAAM,IAAkB,CAAC,GACnB,IAAoB,CAAC,GACrB,IAAgB,EAAM,EAAK,aAAa,GACxC,IAAU,EAAM,EAAK,OAAO,GAC5B,IAAS,EAAM,EAAK,MAAM;EAShC,AARI,MACH,EAAM,KAAK,oBAAoB,GAC/B,EAAO,KAAK,CAAa,IAEtB,MACH,EAAM,KAAK,cAAc,GACzB,EAAO,KAAK,CAAO,IAEhB,KACH,EAAM,KAAK,YAAY,GACvB,EAAO,KAAK,CAAM,KACP,EAAK,mBAChB,EAAM,KAAK,mBAAmB;EAE/B,IAAM,IAAW,EAAM,SAAS,SAAS,EAAM,KAAK,OAAO,MAAM,IAC3D,IACL,KAAK,GAAG,QAAQ;;8BAEW,EAAS;4DACqB;EAC1D,QACC,MAAM,EAA0B,EAAO,SAAS,EAAU,KAAK,GAAG,CAAM,IAAI,CAAS,GACpF,KAAK,MAAQ,EAA8B,CAAG,CAAC;CAClD;CAEA,MAAM,qBACL,GACsC;EACtC,IAAM,IAAU,EAAM,EAAK,OAAO,GAC5B,IAAW,EAAM,EAAK,QAAQ,GAC9B,IAAQ,IAAU,MAAM,KAAK,SAAS,CAAO,IAAI;EACvD,IAAI,CAAC,GAAO,MAAU,MAAM,2CAA2C;EACvE,IAAM,IAAW,KAAW,IAAW,MAAM,KAAK,mBAAmB,GAAS,CAAQ,IAAI,MACpF,IAAa,EAAoB,GAAM,GAAO,CAAQ;EAC5D,IAAI,EAAW,WAEV,CAAC,MADoB,KAAK,cAAc,EAAW,SAAS,EAAW,QAAQ,GAElF,MAAU,MAAM,yDAAyD;EAG3E,IAAM,IAAM,EAAO;EA+CnB,IAAI,GAAmB,MA9CI,EAAgB,KAAK,IAAI,CACnD,KAAK,GACH,QAAQ,28BAkBJ,EACJ,KACA,EAAW,SACX,EAAW,UACX,EAAW,MACX,EAAW,iBACX,EAAW,eACX,EAAW,SACX,EAAW,wBACX,EAAW,cACX,EAAW,oBACX,CACD,GACD,EAAqC,KAAK,IAAI;GAC7C,QAAQ;GACR,UAAU;GACV,SAAS,EAAW;GACpB,UAAU,EAAW;GACrB,QAAQ;GACR,iBAAiB,EAAW;GAC5B,WAAW;GACX,WAAW,EAAW;GACtB,SAAS,EAAW;GACpB,WAAW;EACZ,CAAC,CACF,CAAC,GACmC,EAAE,KAAK,GAC1C,MAAU,MAAM,yCAAyC;EAU1D,OAAO,EAAwC,MAR7B,EACjB,KAAK,GACH,QAAQ,+PAG8B,EACtC,KAAK,EAAW,SAAS,EAAW,QAAQ,CAC/C,CACkD;CACnD;CAEA,MAAM,sBAAsB,GAA+D;EAC1F,IAAM,IAAU,EAAM,EAAK,OAAO,GAC5B,IAAW,EAAM,EAAK,QAAQ;EACpC,IAAI,CAAC,KAAW,CAAC,GAAU,MAAU,MAAM,oCAAoC;EAC/E,IAAM,IACL,EAAK,mBAAmB,OAAO,OAAO,EAAe,EAAK,eAAe,GACpE,IAAW,MAAM,KAAK,mBAAmB,GAAS,CAAQ;EAChE,IAAI,CAAC,GAAU,OAAO;EACtB,IAAI,KAAmB,QAAQ,KAAY,KAAmB,EAAS,kBACtE,MAAU,MAAM,0CAA0C;EAE3D,IAAM,IAAM,EAAO,GACb,IAAe,KAAmB,EAAS,mBAAmB,GAC9D,IAAkB,KAAK,GAC3B,QAAQ,iYAMwE,EAChF,KACA,GACA,GACA,EAAM,EAAK,YAAY,GACvB,EAAM,EAAK,kBAAkB,GAC7B,GACA,GACA,GACA,EAAS,kBACT,EAAS,MACV,GACK,IAAiB,EAAqC,KAAK,IAAI;GACpE,QAAQ;GACR,UAAU;GACV;GACA;GACA,QAAQ;GACR,iBAAiB;GACjB,WAAW;GACX,WAAW,EAAM,EAAK,SAAS;GAC/B,SAAS,EAAM,EAAK,OAAO;GAC3B,WAAW;EACZ,CAAC,GACG;EACJ,IAAI;GACH,IAAe,MAAM,EAAgB,KAAK,IAAI,CAAC,GAAiB,CAAc,CAAC;EAChF,SAAS,GAAK;GACb,IAAM,IAAU,MAAM,KAAK,mBAAmB,GAAS,CAAQ;GAC/D,IACC,CAAC,KACD,EAAQ,qBAAqB,EAAS,oBACtC,EAAQ,WAAW,EAAS,QAE5B,OAAO;GAER,MAAM;EACP;EAEA,OADA,EAAI,EAAmB,EAAa,EAAE,KAAK;CAE5C;CAEA,MAAM,qBACL,GACA,IAAiB,IACuB;EACxC,IAAM,IAAoB,EAAM,CAAO;EACvC,IAAI,CAAC,GAAmB,MAAU,MAAM,sBAAsB;EAC9D,IAAM,IAAc,IAAiB,KAAK;EAC1C,QACC,MAAM,EACL,KAAK,GACH,QAAQ;;;4BAGc,EAAY;8BACV,EACxB,KAAK,CAAiB,CACzB,GACC,KAAK,MAAQ,EAAwC,CAAG,CAAC;CAC5D;CAEA,MAAM,+BACL,GACkD;EAClD,IAAM,IAAU,EAAM,EAAK,OAAO;EAClC,IAAI,CAAC,GAAS,MAAU,MAAM,sBAAsB;EACpD,IAAM,IAAW,EAAM,EAAK,QAAQ,GAC9B,IAAQ,EAAoB,EAAK,KAAK,GACtC,IAAQ,IAAW,mCAAmC,gBACtD,IAAS,IAAW;GAAC;GAAS;GAAU;EAAK,IAAI,CAAC,GAAS,CAAK;EACtE,QACC,MAAM,EACL,KAAK,GACH,QAAQ;;;;eAIC,EAAM;;eAEN,EACT,KAAK,GAAG,CAAM,CACjB,GACC,KAAK,MAAQ,EAAkD,CAAG,CAAC;CACtE;CAEA,MAAM,kBAAkB,GAA4D;EACnF,IAAM,IAAM,MAAM,EACjB,KAAK,GACH,QAAQ,yKAC8C,EACtD,KAAK,CAAO,CACf;EACA,OAAO,IAAM,EAAuC,CAAG,IAAI;CAC5D;CAEA,MAAM,oBAAoB,GAAuD;EAChF,QACC,MAAM,EACL,KAAK,GACH,QAAQ,0NAEiC,EACzC,KAAK,CAAO,CACf,GACC,KAAK,MAAQ,EAAuC,CAAG,CAAC;CAC3D;CAEA,MAAM,qBAAqB,GAAiB,IAAY,EAAO,GAAqB;EACnF,OACE,MAAM,EACN,KAAK,GACH,QAAQ,iHAEY,EACpB,KAAK,GAAW,CAAO,CAC1B,IAAK;CAEP;CAEA,MAAM,wBACL,GAC2C;EAC3C,IAAM,IACL,EAAK,cAAc,aAAa,wBAAwB,wBACnD,IAAS,EAAK,QAAQ,KAAK,KAAK;EACtC,QACC,MAAM,EACL,KAAK,GACH,QAAQ;;gCAEkB,EAAgB;+CACD,EACzC,KAAK,EAAK,SAAS,EAAK,UAAU,CAAM,CAC3C,GACC,KAAK,MAAQ,EAA2C,CAAG,CAAC;CAC/D;CAEA,MAAM,eAAe,GAA2E;EAC/F,IAAM,oBAAM,IAAI,KAAK,GACf,IAAY,IAAI,KAAK,EAAI,QAAQ,IAAI,EAAM,OAAO,GAAI,EAAE,YAAY,GACpE,IAAa,EAAe,CAAC,GAAG,EAAM,SAAS;EAkBrD,OAjBA,MAAM,KAAK,GACT,QAAQ,8XAMwC,EAChD,KACA,EAAM,SACN,EAAM,UACN,KAAK,UAAU,CAAU,GACzB,EAAI,YAAY,GAChB,GACA,KAAK,UAAU,EAAM,gBAAgB,CAAC,CAAC,CACxC,EACC,IAAI,GACC;GACN,UAAU,EAAM;GAChB,WAAW,EAAM;GACjB,WAAW;GACX,YAAY;EACb;CACD;CAEA,MAAM,eACL,GACA,GACmC;EACnC,IAAM,IAAM,EAAO;EAgBnB,QAAO,MAfY,EAClB,KAAK,GACH,QAAQ,koBAUiC,EACzC,KAAK,GAAU,CAAmB,CACrC,GACY,KAAK,MAAQ;GACxB,IAAM,IAAa,OAAO,EAAI,cAAc,EAAE,EAAE,KAAK,GACjD,IAAQ;GACZ,IAAI,GAAY;IACf,IAAM,IAAY,IAAI,KAAK,CAAU;IACrC,IAAQ,OAAO,MAAM,EAAU,QAAQ,CAAC,KAAK,EAAU,YAAY,KAAK;GACzE;GACA,IAAM,IAAY,IACf,CAAC,IACD,EAAe,CAAC,GAAG,KAAK,MAAM,OAAO,EAAI,kBAAkB,IAAI,CAAC,CAAa;GAChF,OAAO;IACN,WAAW,OAAO,EAAI,aAAa,EAAE;IACrC,YAAY,OAAO,EAAI,cAAc,EAAE;IACvC,aAAa,OAAO,EAAI,eAAe,EAAE;IACzC,cAAe,EAAI,gBAAkC;IACrD;IACA,cAAe,EAAI,gBAAkC;IACrD,YAAa,EAAI,cAAgC;IACjD;IACA,cAAc,KAAK,MAAM,OAAO,EAAI,qBAAqB,IAAI,CAAC;GAC/D;EACD,CAAC;CACF;AACD;;;ACl2CA,SAAgB,EACf,GAC0C;CAK1C,OAAO,EAAqB;EAC3B,oBAAoB,IAAI,EAAmB,EAAK,EAAE;EAClD,SAAA;GALA,mBAAmB,OAAO,EAAK,eAAe,EAAE,EAAE,KAAK,KAAK;GAC5D,KAAK,EAAK,+BAAc,IAAI,KAAK,GAAE,YAAY;EAI/C;EACA,iBAAiB,EAAK;CACvB,CAAC;AACF"}
|
|
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\tCoordinatorCreateScopeInput,\n\tCoordinatorEnrollDeviceInput,\n\tCoordinatorEnrollment,\n\tCoordinatorGrantScopeMembershipInput,\n\tCoordinatorGroup,\n\tCoordinatorInvite,\n\tCoordinatorJoinRequest,\n\tCoordinatorJoinRequestReviewResult,\n\tCoordinatorListReciprocalApprovalsInput,\n\tCoordinatorListScopeMembershipAuditInput,\n\tCoordinatorListScopesInput,\n\tCoordinatorPeerRecord,\n\tCoordinatorPresenceRecord,\n\tCoordinatorReciprocalApproval,\n\tCoordinatorReviewJoinRequestBootstrapGrantInput,\n\tCoordinatorReviewJoinRequestInput,\n\tCoordinatorRevokeScopeMembershipInput,\n\tCoordinatorScope,\n\tCoordinatorScopeMembership,\n\tCoordinatorScopeMembershipAuditEvent,\n\tCoordinatorStore,\n\tCoordinatorUpdateScopeInput,\n\tCoordinatorUpsertPresenceInput,\n} from \"./coordinator-store-contract.js\";\nimport { normalizeInviteExpiresAt } 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 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 clean(value: string | null | undefined): string | null {\n\tconst trimmed = value?.trim();\n\treturn trimmed ? trimmed : null;\n}\n\nfunction normalizeEpoch(value: number | null | undefined, fallback = 0): number {\n\tif (value == null) return fallback;\n\tif (!Number.isFinite(value) || value < 0)\n\t\tthrow new Error(\"membershipEpoch must be non-negative.\");\n\treturn Math.trunc(value);\n}\n\nfunction normalizeCreateScopeInput(opts: CoordinatorCreateScopeInput) {\n\tconst scopeId = clean(opts.scopeId);\n\tconst label = clean(opts.label);\n\tif (!scopeId || !label) throw new Error(\"scopeId and label are required.\");\n\treturn {\n\t\tscopeId,\n\t\tlabel,\n\t\tkind: clean(opts.kind) ?? \"user\",\n\t\tauthorityType: clean(opts.authorityType) ?? \"coordinator\",\n\t\tcoordinatorId: clean(opts.coordinatorId),\n\t\tgroupId: clean(opts.groupId),\n\t\tmanifestIssuerDeviceId: clean(opts.manifestIssuerDeviceId),\n\t\tmembershipEpoch: normalizeEpoch(opts.membershipEpoch),\n\t\tmanifestHash: clean(opts.manifestHash),\n\t\tstatus: clean(opts.status) ?? \"active\",\n\t};\n}\n\nfunction cleanRequiredUpdate(\n\tvalue: string | null | undefined,\n\tcurrent: string,\n\tfieldName: string,\n): string {\n\tif (value === undefined) return current;\n\tconst cleaned = clean(value);\n\tif (!cleaned) throw new Error(`${fieldName} must not be empty.`);\n\treturn cleaned;\n}\n\nfunction cleanNullableUpdate(\n\tvalue: string | null | undefined,\n\tcurrent: string | null,\n): string | null {\n\treturn value === undefined ? current : clean(value);\n}\n\nfunction normalizeUpdateScopeInput(opts: CoordinatorUpdateScopeInput, existing: CoordinatorScope) {\n\tconst scopeId = clean(opts.scopeId);\n\tif (!scopeId) throw new Error(\"scopeId is required.\");\n\tconst requestedEpoch = opts.membershipEpoch == null ? null : normalizeEpoch(opts.membershipEpoch);\n\tif (requestedEpoch != null && requestedEpoch < existing.membership_epoch) {\n\t\tthrow new Error(\"membershipEpoch must not move backwards.\");\n\t}\n\treturn {\n\t\tscopeId,\n\t\tlabel: cleanRequiredUpdate(opts.label, existing.label, \"label\"),\n\t\tkind: cleanRequiredUpdate(opts.kind, existing.kind, \"kind\"),\n\t\tauthorityType: cleanRequiredUpdate(\n\t\t\topts.authorityType,\n\t\t\texisting.authority_type,\n\t\t\t\"authorityType\",\n\t\t),\n\t\tcoordinatorId: cleanNullableUpdate(opts.coordinatorId, existing.coordinator_id),\n\t\tgroupId: cleanNullableUpdate(opts.groupId, existing.group_id),\n\t\tmanifestIssuerDeviceId: cleanNullableUpdate(\n\t\t\topts.manifestIssuerDeviceId,\n\t\t\texisting.manifest_issuer_device_id,\n\t\t),\n\t\tmembershipEpoch: requestedEpoch ?? existing.membership_epoch,\n\t\tmanifestHash: cleanNullableUpdate(opts.manifestHash, existing.manifest_hash),\n\t\tstatus: cleanRequiredUpdate(opts.status, existing.status, \"status\"),\n\t};\n}\n\nfunction normalizeGrantInput(\n\topts: CoordinatorGrantScopeMembershipInput,\n\tscope: CoordinatorScope | null,\n\texisting: CoordinatorScopeMembership | null,\n) {\n\tconst scopeId = clean(opts.scopeId);\n\tconst deviceId = clean(opts.deviceId);\n\tif (!scopeId || !deviceId) throw new Error(\"scopeId and deviceId are required.\");\n\tconst coordinatorId = clean(opts.coordinatorId);\n\tconst groupId = clean(opts.groupId);\n\tif (coordinatorId && coordinatorId !== scope?.coordinator_id) {\n\t\tthrow new Error(\"membership coordinatorId must match the scope coordinatorId.\");\n\t}\n\tif (groupId && groupId !== scope?.group_id) {\n\t\tthrow new Error(\"membership groupId must match the scope groupId.\");\n\t}\n\tconst requestedEpoch = opts.membershipEpoch == null ? null : normalizeEpoch(opts.membershipEpoch);\n\tconst inheritedEpoch = scope?.membership_epoch ?? 0;\n\tif (requestedEpoch != null && requestedEpoch < inheritedEpoch) {\n\t\tthrow new Error(\"membershipEpoch must not be lower than the scope membershipEpoch.\");\n\t}\n\tif (requestedEpoch != null && existing) {\n\t\tconst minimumEpoch =\n\t\t\texisting.status === \"revoked\" ? existing.membership_epoch + 1 : existing.membership_epoch;\n\t\tif (requestedEpoch < minimumEpoch) {\n\t\t\tthrow new Error(\"membershipEpoch must not move backwards.\");\n\t\t}\n\t}\n\tconst membershipEpoch =\n\t\trequestedEpoch ??\n\t\t(existing\n\t\t\t? Math.max(\n\t\t\t\t\tinheritedEpoch,\n\t\t\t\t\texisting.membership_epoch + (existing.status === \"revoked\" ? 1 : 0),\n\t\t\t\t)\n\t\t\t: inheritedEpoch);\n\treturn {\n\t\tscopeId,\n\t\tdeviceId,\n\t\trole: clean(opts.role) ?? \"member\",\n\t\tmembershipEpoch,\n\t\tcoordinatorId: scope?.coordinator_id ?? null,\n\t\tgroupId: scope?.group_id ?? null,\n\t\tmanifestIssuerDeviceId:\n\t\t\tclean(opts.manifestIssuerDeviceId) ?? scope?.manifest_issuer_device_id ?? null,\n\t\tmanifestHash: clean(opts.manifestHash) ?? scope?.manifest_hash ?? null,\n\t\tsignedManifestJson: clean(opts.signedManifestJson),\n\t\tactorType: clean(opts.actorType),\n\t\tactorId: clean(opts.actorId),\n\t};\n}\n\nfunction normalizeAuditLimit(limit: number | null | undefined): number {\n\tif (limit == null) return 100;\n\tif (!Number.isFinite(limit)) return 100;\n\treturn Math.max(1, Math.min(1000, Math.trunc(limit)));\n}\n\nfunction prepareMembershipAuditFromCurrentRow(\n\tdb: D1DatabaseLike,\n\tinput: {\n\t\taction: \"grant\" | \"revoke\";\n\t\tprevious: CoordinatorScopeMembership | null;\n\t\tscopeId: string;\n\t\tdeviceId: string;\n\t\tstatus: string;\n\t\tmembershipEpoch: number;\n\t\tupdatedAt: string;\n\t\tactorType: string | null;\n\t\tactorId: string | null;\n\t\tcreatedAt: string;\n\t},\n): D1PreparedStatementLike {\n\t// D1 batch() is the transaction boundary for audited membership changes.\n\t// changes() ties the audit insert to the immediately preceding mutation in\n\t// the same batch. Losing guarded updates insert no audit row, while winning\n\t// mutations still emit one required row; if the post-mutation membership row\n\t// is then missing, NOT NULL audit columns make the statement fail and roll\n\t// back the batch instead of committing an unaudited grant/revoke.\n\treturn db\n\t\t.prepare(`INSERT INTO coordinator_scope_membership_audit_log(\n\t\t\taction, scope_id, device_id, role, status, membership_epoch,\n\t\t\tprevious_role, previous_status, previous_membership_epoch,\n\t\t\tcoordinator_id, group_id, actor_type, actor_id, manifest_hash, created_at\n\t\t)\n\t\tSELECT ?, membership.scope_id, membership.device_id, membership.role, membership.status,\n\t\t\tmembership.membership_epoch, ?, ?, ?, membership.coordinator_id, membership.group_id,\n\t\t\t?, ?, membership.manifest_hash, ?\n\t\tFROM (SELECT 1 WHERE changes() > 0) AS required\n\t\tLEFT JOIN coordinator_scope_memberships AS membership\n\t\t\tON membership.scope_id = ?\n\t\t\tAND membership.device_id = ?\n\t\t\tAND membership.status = ?\n\t\t\tAND membership.membership_epoch = ?\n\t\t\tAND membership.updated_at = ?`)\n\t\t.bind(\n\t\t\tinput.action,\n\t\t\tinput.previous?.role ?? null,\n\t\t\tinput.previous?.status ?? null,\n\t\t\tinput.previous?.membership_epoch ?? null,\n\t\t\tinput.actorType,\n\t\t\tinput.actorId,\n\t\t\tinput.createdAt,\n\t\t\tinput.scopeId,\n\t\t\tinput.deviceId,\n\t\t\tinput.status,\n\t\t\tinput.membershipEpoch,\n\t\t\tinput.updatedAt,\n\t\t);\n}\n\nasync function runAuditedBatch(\n\tdb: D1DatabaseLike,\n\tstatements: D1PreparedStatementLike[],\n): Promise<unknown[]> {\n\tif (!db.batch) {\n\t\tthrow new Error(\"D1 batch support is required for audited scope membership changes.\");\n\t}\n\treturn await db.batch(statements);\n}\n\nfunction batchResultChanges(result: unknown): number {\n\treturn Number((result as D1RunResultLike | undefined)?.meta?.changes ?? 0);\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(\n\t\t\t\t\"INSERT OR IGNORE INTO groups(group_id, display_name, archived_at, created_at) VALUES (?, ?, NULL, ?)\",\n\t\t\t)\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(\n\t\t\t\t\t\"SELECT group_id, display_name, archived_at, created_at FROM groups WHERE group_id = ?\",\n\t\t\t\t)\n\t\t\t\t.bind(_groupId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorGroup>(row) : null;\n\t}\n\n\tasync listGroups(_includeArchived = false): Promise<CoordinatorGroup[]> {\n\t\tconst where = _includeArchived ? \"\" : \"WHERE archived_at IS NULL\";\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, archived_at, created_at FROM groups ${where} 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 renameGroup(_groupId: 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 groups SET display_name = ? WHERE group_id = ?\")\n\t\t\t\t\t.bind(_displayName, _groupId),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync archiveGroup(_groupId: string, _archivedAt = 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 groups SET archived_at = ? WHERE group_id = ? AND archived_at IS NULL\")\n\t\t\t\t\t.bind(_archivedAt, _groupId),\n\t\t\t)) > 0\n\t\t);\n\t}\n\n\tasync unarchiveGroup(_groupId: 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\"UPDATE groups SET archived_at = NULL WHERE group_id = ? AND archived_at IS NOT NULL\",\n\t\t\t\t\t)\n\t\t\t\t\t.bind(_groupId),\n\t\t\t)) > 0\n\t\t);\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\t// Presence, reciprocal approvals, and the enrollment row must be removed\n\t\t// all-or-nothing. Running them as independent statements can orphan rows\n\t\t// (an enrolled device with no presence/approvals, or vice versa) if any\n\t\t// one statement fails. D1 batch() is the transaction boundary here, the\n\t\t// same convention used for audited membership changes via runAuditedBatch.\n\t\tif (!this.db.batch) {\n\t\t\tthrow new Error(\"D1 batch support is required for atomic device removal.\");\n\t\t}\n\t\tconst results = await this.db.batch([\n\t\t\tthis.db\n\t\t\t\t.prepare(\"DELETE FROM presence_records WHERE group_id = ? AND device_id = ?\")\n\t\t\t\t.bind(_groupId, _deviceId),\n\t\t\tthis.db\n\t\t\t\t.prepare(\n\t\t\t\t\t\"DELETE FROM coordinator_reciprocal_approvals WHERE group_id = ? AND (requesting_device_id = ? OR requested_device_id = ?)\",\n\t\t\t\t)\n\t\t\t\t.bind(_groupId, _deviceId, _deviceId),\n\t\t\tthis.db\n\t\t\t\t.prepare(\"DELETE FROM enrolled_devices WHERE group_id = ? AND device_id = ?\")\n\t\t\t\t.bind(_groupId, _deviceId),\n\t\t]);\n\t\t// The enrolled_devices delete is the last statement; its changes() tells us\n\t\t// whether the device existed, preserving the boolean return contract.\n\t\treturn batchResultChanges(results[2]) > 0;\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 expiresAt = normalizeInviteExpiresAt(_opts.expiresAt);\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\texpiresAt,\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\tprivate async getScope(scopeId: string): Promise<CoordinatorScope | null> {\n\t\tconst row = await firstRow<CoordinatorScope>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT scope_id, label, kind, authority_type, coordinator_id, group_id,\n\t\t\t\t\t\tmanifest_issuer_device_id, membership_epoch, manifest_hash, status, created_at, updated_at\n\t\t\t\t\t FROM coordinator_scopes WHERE scope_id = ?`)\n\t\t\t\t.bind(scopeId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorScope>(row) : null;\n\t}\n\n\tprivate async getScopeMembership(\n\t\tscopeId: string,\n\t\tdeviceId: string,\n\t): Promise<CoordinatorScopeMembership | null> {\n\t\tconst row = await firstRow<CoordinatorScopeMembership>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT scope_id, device_id, role, status, membership_epoch, coordinator_id, group_id,\n\t\t\t\t\t\tmanifest_issuer_device_id, manifest_hash, signed_manifest_json, updated_at\n\t\t\t\t\t FROM coordinator_scope_memberships\n\t\t\t\t\t WHERE scope_id = ? AND device_id = ?`)\n\t\t\t\t.bind(scopeId, deviceId),\n\t\t);\n\t\treturn row ? rowToRecord<CoordinatorScopeMembership>(row) : null;\n\t}\n\n\tasync createScope(opts: CoordinatorCreateScopeInput): Promise<CoordinatorScope> {\n\t\tconst normalized = normalizeCreateScopeInput(opts);\n\t\tif (await this.getScope(normalized.scopeId)) throw new Error(\"scopeId already exists.\");\n\t\tconst now = nowISO();\n\t\tawait this.db\n\t\t\t.prepare(`INSERT INTO coordinator_scopes(\n\t\t\t\t\tscope_id, label, kind, authority_type, coordinator_id, group_id,\n\t\t\t\t\tmanifest_issuer_device_id, membership_epoch, manifest_hash, status, created_at, updated_at\n\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)\n\t\t\t.bind(\n\t\t\t\tnormalized.scopeId,\n\t\t\t\tnormalized.label,\n\t\t\t\tnormalized.kind,\n\t\t\t\tnormalized.authorityType,\n\t\t\t\tnormalized.coordinatorId,\n\t\t\t\tnormalized.groupId,\n\t\t\t\tnormalized.manifestIssuerDeviceId,\n\t\t\t\tnormalized.membershipEpoch,\n\t\t\t\tnormalized.manifestHash,\n\t\t\t\tnormalized.status,\n\t\t\t\tnow,\n\t\t\t\tnow,\n\t\t\t)\n\t\t\t.run();\n\t\tconst scope = await this.getScope(normalized.scopeId);\n\t\tif (!scope) throw new Error(\"scope insert returned no row\");\n\t\treturn scope;\n\t}\n\n\tasync updateScope(opts: CoordinatorUpdateScopeInput): Promise<CoordinatorScope | null> {\n\t\tconst scopeId = clean(opts.scopeId);\n\t\tconst existing = scopeId ? await this.getScope(scopeId) : null;\n\t\tif (!existing) return null;\n\t\tconst normalized = normalizeUpdateScopeInput(opts, existing);\n\t\tawait this.db\n\t\t\t.prepare(`UPDATE coordinator_scopes\n\t\t\t\t SET label = ?,\n\t\t\t\t\t kind = ?,\n\t\t\t\t\t authority_type = ?,\n\t\t\t\t\t coordinator_id = ?,\n\t\t\t\t\t group_id = ?,\n\t\t\t\t\t manifest_issuer_device_id = ?,\n\t\t\t\t\t membership_epoch = ?,\n\t\t\t\t\t manifest_hash = ?,\n\t\t\t\t\t status = ?,\n\t\t\t\t\t updated_at = ?\n\t\t\t\t WHERE scope_id = ?`)\n\t\t\t.bind(\n\t\t\t\tnormalized.label,\n\t\t\t\tnormalized.kind,\n\t\t\t\tnormalized.authorityType,\n\t\t\t\tnormalized.coordinatorId,\n\t\t\t\tnormalized.groupId,\n\t\t\t\tnormalized.manifestIssuerDeviceId,\n\t\t\t\tnormalized.membershipEpoch,\n\t\t\t\tnormalized.manifestHash,\n\t\t\t\tnormalized.status,\n\t\t\t\tnowISO(),\n\t\t\t\tnormalized.scopeId,\n\t\t\t)\n\t\t\t.run();\n\t\treturn await this.getScope(normalized.scopeId);\n\t}\n\n\tasync listScopes(opts: CoordinatorListScopesInput = {}): Promise<CoordinatorScope[]> {\n\t\tconst where: string[] = [];\n\t\tconst params: unknown[] = [];\n\t\tconst coordinatorId = clean(opts.coordinatorId);\n\t\tconst groupId = clean(opts.groupId);\n\t\tconst status = clean(opts.status);\n\t\tif (coordinatorId) {\n\t\t\twhere.push(\"coordinator_id = ?\");\n\t\t\tparams.push(coordinatorId);\n\t\t}\n\t\tif (groupId) {\n\t\t\twhere.push(\"group_id = ?\");\n\t\t\tparams.push(groupId);\n\t\t}\n\t\tif (status) {\n\t\t\twhere.push(\"status = ?\");\n\t\t\tparams.push(status);\n\t\t} else if (!opts.includeInactive) {\n\t\t\twhere.push(\"status = 'active'\");\n\t\t}\n\t\tconst whereSql = where.length ? `WHERE ${where.join(\" AND \")}` : \"\";\n\t\tconst statement =\n\t\t\tthis.db.prepare(`SELECT scope_id, label, kind, authority_type, coordinator_id, group_id,\n\t\t\t\tmanifest_issuer_device_id, membership_epoch, manifest_hash, status, created_at, updated_at\n\t\t\t FROM coordinator_scopes ${whereSql}\n\t\t\t ORDER BY coordinator_id ASC, group_id ASC, scope_id ASC`);\n\t\treturn (\n\t\t\tawait allRows<CoordinatorScope>(params.length ? statement.bind(...params) : statement)\n\t\t).map((row) => rowToRecord<CoordinatorScope>(row));\n\t}\n\n\tasync grantScopeMembership(\n\t\topts: CoordinatorGrantScopeMembershipInput,\n\t): Promise<CoordinatorScopeMembership> {\n\t\tconst scopeId = clean(opts.scopeId);\n\t\tconst deviceId = clean(opts.deviceId);\n\t\tconst scope = scopeId ? await this.getScope(scopeId) : null;\n\t\tif (!scope) throw new Error(\"scopeId must reference an existing scope.\");\n\t\tconst existing = scopeId && deviceId ? await this.getScopeMembership(scopeId, deviceId) : null;\n\t\tconst normalized = normalizeGrantInput(opts, scope, existing);\n\t\tif (normalized.groupId) {\n\t\t\tconst enrollment = await this.getEnrollment(normalized.groupId, normalized.deviceId);\n\t\t\tif (!enrollment) {\n\t\t\t\tthrow new Error(\"device must be enrolled and enabled in the scope group.\");\n\t\t\t}\n\t\t}\n\t\tconst now = nowISO();\n\t\tconst batchResults = await runAuditedBatch(this.db, [\n\t\t\tthis.db\n\t\t\t\t.prepare(`INSERT INTO coordinator_scope_memberships(\n\t\t\t\t\tscope_id, device_id, role, status, membership_epoch, coordinator_id, group_id,\n\t\t\t\t\tmanifest_issuer_device_id, manifest_hash, signed_manifest_json, updated_at\n\t\t\t\t) VALUES (?, ?, ?, 'active', ?, ?, ?, ?, ?, ?, ?)\n\t\t\t\tON CONFLICT(scope_id, device_id) DO UPDATE SET\n\t\t\t\t\trole = excluded.role,\n\t\t\t\t\tstatus = 'active',\n\t\t\t\t\tmembership_epoch = excluded.membership_epoch,\n\t\t\t\t\tcoordinator_id = excluded.coordinator_id,\n\t\t\t\t\tgroup_id = excluded.group_id,\n\t\t\t\t\tmanifest_issuer_device_id = excluded.manifest_issuer_device_id,\n\t\t\t\t\tmanifest_hash = excluded.manifest_hash,\n\t\t\t\t\tsigned_manifest_json = excluded.signed_manifest_json,\n\t\t\t\t\tupdated_at = excluded.updated_at\n\t\t\t\tWHERE excluded.membership_epoch > coordinator_scope_memberships.membership_epoch\n\t\t\t\t OR (\n\t\t\t\t\texcluded.membership_epoch = coordinator_scope_memberships.membership_epoch\n\t\t\t\t\tAND coordinator_scope_memberships.status != 'revoked'\n\t\t\t\t )`)\n\t\t\t\t.bind(\n\t\t\t\t\tnormalized.scopeId,\n\t\t\t\t\tnormalized.deviceId,\n\t\t\t\t\tnormalized.role,\n\t\t\t\t\tnormalized.membershipEpoch,\n\t\t\t\t\tnormalized.coordinatorId,\n\t\t\t\t\tnormalized.groupId,\n\t\t\t\t\tnormalized.manifestIssuerDeviceId,\n\t\t\t\t\tnormalized.manifestHash,\n\t\t\t\t\tnormalized.signedManifestJson,\n\t\t\t\t\tnow,\n\t\t\t\t),\n\t\t\tprepareMembershipAuditFromCurrentRow(this.db, {\n\t\t\t\taction: \"grant\",\n\t\t\t\tprevious: existing,\n\t\t\t\tscopeId: normalized.scopeId,\n\t\t\t\tdeviceId: normalized.deviceId,\n\t\t\t\tstatus: \"active\",\n\t\t\t\tmembershipEpoch: normalized.membershipEpoch,\n\t\t\t\tupdatedAt: now,\n\t\t\t\tactorType: normalized.actorType,\n\t\t\t\tactorId: normalized.actorId,\n\t\t\t\tcreatedAt: now,\n\t\t\t}),\n\t\t]);\n\t\tif (batchResultChanges(batchResults[0]) <= 0) {\n\t\t\tthrow new Error(\"scope membership grant was not applied.\");\n\t\t}\n\t\tconst row = await firstRow<CoordinatorScopeMembership>(\n\t\t\tthis.db\n\t\t\t\t.prepare(`SELECT scope_id, device_id, role, status, membership_epoch, coordinator_id, group_id,\n\t\t\t\t\t\tmanifest_issuer_device_id, manifest_hash, signed_manifest_json, updated_at\n\t\t\t\t\t FROM coordinator_scope_memberships\n\t\t\t\t\t WHERE scope_id = ? AND device_id = ?`)\n\t\t\t\t.bind(normalized.scopeId, normalized.deviceId),\n\t\t);\n\t\treturn rowToRecord<CoordinatorScopeMembership>(row);\n\t}\n\n\tasync revokeScopeMembership(opts: CoordinatorRevokeScopeMembershipInput): Promise<boolean> {\n\t\tconst scopeId = clean(opts.scopeId);\n\t\tconst deviceId = clean(opts.deviceId);\n\t\tif (!scopeId || !deviceId) throw new Error(\"scopeId and deviceId are required.\");\n\t\tconst membershipEpoch =\n\t\t\topts.membershipEpoch == null ? null : normalizeEpoch(opts.membershipEpoch);\n\t\tconst existing = await this.getScopeMembership(scopeId, deviceId);\n\t\tif (!existing) return false;\n\t\tif (membershipEpoch != null && existing && membershipEpoch <= existing.membership_epoch) {\n\t\t\tthrow new Error(\"membershipEpoch must increase on revoke.\");\n\t\t}\n\t\tconst now = nowISO();\n\t\tconst revokedEpoch = membershipEpoch ?? existing.membership_epoch + 1;\n\t\tconst updateStatement = this.db\n\t\t\t.prepare(`UPDATE coordinator_scope_memberships\n\t\t\t\t\t\t SET status = 'revoked',\n\t\t\t\t\t\t\t membership_epoch = CASE WHEN ? IS NULL THEN membership_epoch + 1 ELSE ? END,\n\t\t\t\t\t\t\t manifest_hash = COALESCE(?, manifest_hash),\n\t\t\t\t\t\t\t signed_manifest_json = COALESCE(?, signed_manifest_json),\n\t\t\t\t\t\t\t updated_at = ?\n\t\t\t\t\t\t WHERE scope_id = ? AND device_id = ? AND membership_epoch = ? AND status = ?`)\n\t\t\t.bind(\n\t\t\t\tmembershipEpoch,\n\t\t\t\tmembershipEpoch,\n\t\t\t\tclean(opts.manifestHash),\n\t\t\t\tclean(opts.signedManifestJson),\n\t\t\t\tnow,\n\t\t\t\tscopeId,\n\t\t\t\tdeviceId,\n\t\t\t\texisting.membership_epoch,\n\t\t\t\texisting.status,\n\t\t\t);\n\t\tconst auditStatement = prepareMembershipAuditFromCurrentRow(this.db, {\n\t\t\taction: \"revoke\",\n\t\t\tprevious: existing,\n\t\t\tscopeId,\n\t\t\tdeviceId,\n\t\t\tstatus: \"revoked\",\n\t\t\tmembershipEpoch: revokedEpoch,\n\t\t\tupdatedAt: now,\n\t\t\tactorType: clean(opts.actorType),\n\t\t\tactorId: clean(opts.actorId),\n\t\t\tcreatedAt: now,\n\t\t});\n\t\tlet batchResults: unknown[];\n\t\ttry {\n\t\t\tbatchResults = await runAuditedBatch(this.db, [updateStatement, auditStatement]);\n\t\t} catch (err) {\n\t\t\tconst current = await this.getScopeMembership(scopeId, deviceId);\n\t\t\tif (\n\t\t\t\t!current ||\n\t\t\t\tcurrent.membership_epoch !== existing.membership_epoch ||\n\t\t\t\tcurrent.status !== existing.status\n\t\t\t) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t\tif (batchResultChanges(batchResults[0]) <= 0) return false;\n\t\treturn true;\n\t}\n\n\tasync listScopeMemberships(\n\t\tscopeId: string,\n\t\tincludeRevoked = false,\n\t): Promise<CoordinatorScopeMembership[]> {\n\t\tconst normalizedScopeId = clean(scopeId);\n\t\tif (!normalizedScopeId) throw new Error(\"scopeId is required.\");\n\t\tconst statusWhere = includeRevoked ? \"\" : \"AND status = 'active'\";\n\t\treturn (\n\t\t\tawait allRows<CoordinatorScopeMembership>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT scope_id, device_id, role, status, membership_epoch, coordinator_id, group_id,\n\t\t\t\t\t\t\tmanifest_issuer_device_id, manifest_hash, signed_manifest_json, updated_at\n\t\t\t\t\t\t FROM coordinator_scope_memberships\n\t\t\t\t\t\t WHERE scope_id = ? ${statusWhere}\n\t\t\t\t\t\t ORDER BY device_id ASC`)\n\t\t\t\t\t.bind(normalizedScopeId),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorScopeMembership>(row));\n\t}\n\n\tasync listScopeMembershipAuditEvents(\n\t\topts: CoordinatorListScopeMembershipAuditInput,\n\t): Promise<CoordinatorScopeMembershipAuditEvent[]> {\n\t\tconst scopeId = clean(opts.scopeId);\n\t\tif (!scopeId) throw new Error(\"scopeId is required.\");\n\t\tconst deviceId = clean(opts.deviceId);\n\t\tconst limit = normalizeAuditLimit(opts.limit);\n\t\tconst where = deviceId ? \"scope_id = ? AND device_id = ?\" : \"scope_id = ?\";\n\t\tconst params = deviceId ? [scopeId, deviceId, limit] : [scopeId, limit];\n\t\treturn (\n\t\t\tawait allRows<CoordinatorScopeMembershipAuditEvent>(\n\t\t\t\tthis.db\n\t\t\t\t\t.prepare(`SELECT event_id, action, scope_id, device_id, role, status, membership_epoch,\n\t\t\t\t\t\t\tprevious_role, previous_status, previous_membership_epoch,\n\t\t\t\t\t\t\tcoordinator_id, group_id, actor_type, actor_id, manifest_hash, created_at\n\t\t\t\t\t\t FROM coordinator_scope_membership_audit_log\n\t\t\t\t\t\t WHERE ${where}\n\t\t\t\t\t\t ORDER BY event_id ASC\n\t\t\t\t\t\t LIMIT ?`)\n\t\t\t\t\t.bind(...params),\n\t\t\t)\n\t\t).map((row) => rowToRecord<CoordinatorScopeMembershipAuditEvent>(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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observer-config.d.ts","sourceRoot":"","sources":["../src/observer-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA+CtD;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgCxD;AAMD,uEAAuE;AACvE,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2B5D;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAMD,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAMzE;AAED,wBAAgB,mCAAmC,IAAI,MAAM,GAAG,IAAI,CAcnE;AAYD,wBAAgB,8BAA8B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE3F;AAED,gEAAgE;AAChE,eAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"observer-config.d.ts","sourceRoot":"","sources":["../src/observer-config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA+CtD;AAED,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgCxD;AAMD,uEAAuE;AACvE,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2B5D;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAMD,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAMzE;AAED,wBAAgB,mCAAmC,IAAI,MAAM,GAAG,IAAI,CAcnE;AAYD,wBAAgB,8BAA8B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE3F;AAED,gEAAgE;AAChE,eAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAkD/D,CAAC;AAMF,MAAM,MAAM,gBAAgB,GACzB,UAAU,GACV,oBAAoB,GACpB,kBAAkB,GAClB,kBAAkB,GAClB,eAAe,CAAC;AAEnB,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,6FAA6F;IAC7F,KAAK,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,aAAa,EAAE,oBAAoB,EAAE,CAAC;CACtC;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACvC,aAAa,CAAC,EAAE,MAAM,EACtB,IAAI,GAAE,MAAM,GAAG,OAAgB,GAC7B,sBAAsB,CA2HxB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAG/D;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+BvF;AAED,0DAA0D;AAC1D,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAKjG;AAED,wBAAgB,+BAA+B,CAC9C,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAER;AAED,2DAA2D;AAC3D,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO/D;AAMD,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAQzC,mEAAmE;AACnE,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAMrE;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,IAAI,GAAG,CAAC,MAAM,CAAC,CAKjD;AAUD,wBAAgB,+BAA+B,IAAI,GAAG,CAAC,MAAM,CAAC,CAQ7D;AAED,wBAAgB,2BAA2B,IAAI,MAAM,EAAE,CAItD;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG5E;AAED,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKlF;AAED,wBAAgB,2BAA2B,CAC1C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GACf,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAQnF;AAED,kFAAkF;AAClF,wBAAgB,8BAA8B,CAC7C,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACpB,MAAM,GAAG,IAAI,CAIf;AAMD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGxD;AAkFD,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,SAAS,GAAG,SAAS,CAGvE;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAK3E;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUpF;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAa1E;AAMD,oDAAoD;AACpD,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBjF;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GACf,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA2BnF"}
|
package/dist/pack.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,SAAS,CAAC;AAOlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAQ/C,OAAO,KAAK,EACX,aAAa,EAEb,YAAY,EAEZ,iBAAiB,EACjB,YAAY,EACZ,SAAS,EAMT,MAAM,YAAY,CAAC;AAoCpB,gDAAgD;AAChD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;
|
|
1
|
+
{"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,SAAS,CAAC;AAOlD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAQ/C,OAAO,KAAK,EACX,aAAa,EAEb,YAAY,EAEZ,iBAAiB,EACjB,YAAY,EACZ,SAAS,EAMT,MAAM,YAAY,CAAC;AAoCpB,gDAAgD;AAChD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AA4zDD,wBAAgB,eAAe,CAC9B,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,SAAK,EACV,WAAW,GAAE,MAAM,GAAG,IAAW,EACjC,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,YAAY,EAAE,EAChC,aAAa,CAAC,EAAE,iBAAiB,GAC/B,YAAY,CAOd;AAED,wBAAgB,oBAAoB,CACnC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,SAAK,EACV,WAAW,GAAE,MAAM,GAAG,IAAW,EACjC,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,YAAY,EAAE,EAChC,aAAa,CAAC,EAAE,iBAAiB,GAC/B,SAAS,CAOX;AAMD;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CACzC,KAAK,EAAE,WAAW,GAAG;IAAE,EAAE,EAAE,QAAQ,CAAA;CAAE,EACrC,OAAO,EAAE,MAAM,EACf,KAAK,SAAK,EACV,WAAW,GAAE,MAAM,GAAG,IAAW,EACjC,OAAO,CAAC,EAAE,aAAa,EACvB,aAAa,CAAC,EAAE,iBAAiB,GAC/B,OAAO,CAAC,YAAY,CAAC,CAuBvB;AAED,wBAAsB,yBAAyB,CAC9C,KAAK,EAAE,WAAW,GAAG;IAAE,EAAE,EAAE,QAAQ,CAAA;CAAE,EACrC,OAAO,EAAE,MAAM,EACf,KAAK,SAAK,EACV,WAAW,GAAE,MAAM,GAAG,IAAW,EACjC,OAAO,CAAC,EAAE,aAAa,EACvB,aAAa,CAAC,EAAE,iBAAiB,GAC/B,OAAO,CAAC,SAAS,CAAC,CAsBpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw-event-sweeper.d.ts","sourceRoot":"","sources":["../src/raw-event-sweeper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"raw-event-sweeper.d.ts","sourceRoot":"","sources":["../src/raw-event-sweeper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AA4B9C,qBAAa,eAAe;IAC3B,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,eAAe,CAAoD;IAC3E,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,eAAe,CAAS;gBAEpB,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa;IASzD,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,eAAe;IAYvB;;;OAGG;IACH,gBAAgB,IAAI,IAAI;IAMxB;;OAEG;IACH,iBAAiB,IAAI;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;IAU5D;;;;;;OAMG;IACH,KAAK,IAAI,IAAI;IAOb;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;OAGG;IACH,mBAAmB,IAAI,IAAI;IAI3B;;;OAGG;IACH,KAAK,CAAC,iBAAiB,EAAE,MAAM,EAAE,MAAM,SAAa,GAAG,IAAI;IA0B3D,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,iBAAiB;YAqBX,QAAQ;IAwCtB,4DAA4D;IAC5D,OAAO,CAAC,YAAY;IAYpB,iDAAiD;YACnC,OAAO;IAgBrB,OAAO,CAAC,IAAI;IAmBZ;;;;;;;;OAQG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAkG3B"}
|
|
@@ -8,5 +8,16 @@ export interface CreateInMemoryRequestRateLimiterOptions {
|
|
|
8
8
|
windowMs?: number;
|
|
9
9
|
now?: () => number;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Best-effort, per-isolate request rate limiter.
|
|
13
|
+
*
|
|
14
|
+
* Buckets live in a module-local Map, so on Cloudflare Workers the limits are
|
|
15
|
+
* enforced per isolate, not fleet-wide: the effective ceiling is multiplied by
|
|
16
|
+
* the number of live isolates and resets whenever an isolate is evicted. This
|
|
17
|
+
* is acceptable as a coarse abuse brake but is NOT a hard global limit. The
|
|
18
|
+
* unauthenticated /v1/join surface is the most exposed to this gap. Real
|
|
19
|
+
* fleet-wide enforcement needs a shared counter backed by a Durable Object or
|
|
20
|
+
* D1/KV.
|
|
21
|
+
*/
|
|
11
22
|
export declare function createInMemoryRequestRateLimiter(options?: CreateInMemoryRequestRateLimiterOptions): InMemoryRequestRateLimiter;
|
|
12
23
|
//# sourceMappingURL=request-rate-limit.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-rate-limit.d.ts","sourceRoot":"","sources":["../src/request-rate-limit.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,0BAA0B;IAC1C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E;AAED,MAAM,WAAW,uCAAuC;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,wBAAgB,gCAAgC,CAC/C,OAAO,GAAE,uCAA4C,GACnD,0BAA0B,CA4C5B"}
|
|
1
|
+
{"version":3,"file":"request-rate-limit.d.ts","sourceRoot":"","sources":["../src/request-rate-limit.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,0BAA0B;IAC1C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E;AAED,MAAM,WAAW,uCAAuC;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gCAAgC,CAC/C,OAAO,GAAE,uCAA4C,GACnD,0BAA0B,CA4C5B"}
|
package/dist/scope-backfill.d.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* legacy shared memories go to a review scope rather than an org/team scope.
|
|
7
7
|
*/
|
|
8
8
|
import type { Database as SqliteDatabase } from "better-sqlite3";
|
|
9
|
-
|
|
9
|
+
import { LEGACY_SHARED_REVIEW_SCOPE_ID } from "./scope-resolution.js";
|
|
10
|
+
export { LEGACY_SHARED_REVIEW_SCOPE_ID };
|
|
10
11
|
export declare const SCOPE_BACKFILL_JOB = "scope_id_backfill";
|
|
11
12
|
export type ScopeBackfillReason = "private_or_personal" | "shared_with_canonical_workspace_review" | "shared_ambiguous_review";
|
|
12
13
|
export interface LegacyMemoryScopeInput {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-backfill.d.ts","sourceRoot":"","sources":["../src/scope-backfill.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,IAAI,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"scope-backfill.d.ts","sourceRoot":"","sources":["../src/scope-backfill.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,IAAI,cAAc,EAAE,MAAM,gBAAgB,CAAC;AASjE,OAAO,EAAE,6BAA6B,EAA0B,MAAM,uBAAuB,CAAC;AAI9F,OAAO,EAAE,6BAA6B,EAAE,CAAC;AACzC,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAC5B,qBAAqB,GACrB,wCAAwC,GACxC,yBAAyB,CAAC;AAE7B,MAAM,WAAW,sBAAsB;IACtC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,+BAA+B;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,uBAAuB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,0BAA0B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAiED,wBAAgB,yBAAyB,CACxC,KAAK,EAAE,sBAAsB,GAC3B,+BAA+B,CAyBjC;AAeD,wBAAgB,yBAAyB,CACxC,EAAE,EAAE,cAAc,EAClB,GAAG,SAA2B,GAC5B,MAAM,CAiBR;AA4CD;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CA6DnE;AAkFD,wBAAgB,gBAAgB,CAC/B,EAAE,EAAE,cAAc,EAClB,OAAO,GAAE,oBAAyB,GAChC,mBAAmB,CAoFrB;AAcD,wBAAsB,oBAAoB,CACzC,EAAE,EAAE,cAAc,EAClB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,OAAO,CAAC,OAAO,CAAC,CA8KlB;AAED,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,UAAU,CAA8B;gBAEpC,OAAO,GAAE,0BAA+B;IAOpD,KAAK,IAAI,IAAI;IAMP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B,OAAO,CAAC,QAAQ;YAgBF,OAAO;CA0BrB"}
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
+
import type { Database } from "better-sqlite3";
|
|
1
2
|
export declare const LOCAL_DEFAULT_SCOPE_ID = "local-default";
|
|
3
|
+
/**
|
|
4
|
+
* Scope id used by the conservative migration backfill for legacy shared
|
|
5
|
+
* memories that cannot yet be assigned to a concrete team/org scope. Single
|
|
6
|
+
* source of truth: filters.ts and scope-backfill.ts re-import this so the
|
|
7
|
+
* read-visibility predicate and the backfill agree on one literal.
|
|
8
|
+
*/
|
|
9
|
+
export declare const LEGACY_SHARED_REVIEW_SCOPE_ID = "legacy-shared-review";
|
|
10
|
+
/**
|
|
11
|
+
* Upper bound on how many scope ids we will inline into the index-eligible
|
|
12
|
+
* `scope_id IN (...)` fast path (one bound parameter each). Kept well under
|
|
13
|
+
* SQLite's compiled `SQLITE_MAX_VARIABLE_NUMBER` so a device with an
|
|
14
|
+
* unrealistically large visible set falls back to the fixed-param EXISTS
|
|
15
|
+
* predicate instead of throwing "too many SQL variables" at prepare time.
|
|
16
|
+
*/
|
|
17
|
+
export declare const MAX_SCOPE_IN_PARAMS = 500;
|
|
2
18
|
export type WorkspaceIdentitySource = "git_remote" | "git_remote_branch" | "cwd" | "workspace_id" | "unmapped";
|
|
3
19
|
export interface WorkspaceIdentityInput {
|
|
4
20
|
gitRemote?: string | null;
|
|
@@ -37,4 +53,22 @@ export interface ResolveProjectScopeInput extends WorkspaceIdentityInput {
|
|
|
37
53
|
}
|
|
38
54
|
export declare function canonicalWorkspaceIdentity(input: WorkspaceIdentityInput): CanonicalWorkspaceIdentity;
|
|
39
55
|
export declare function resolveProjectScope(input: ResolveProjectScopeInput): ScopeResolution;
|
|
56
|
+
/**
|
|
57
|
+
* Resolve, once per request, the full set of scope_ids that `deviceId` may read.
|
|
58
|
+
*
|
|
59
|
+
* This is the index-eligible equivalent of the per-row EXISTS predicate in
|
|
60
|
+
* `addScopeVisibilityFilter`'s fallback branch (filters.ts): instead of
|
|
61
|
+
* evaluating the replication_scopes / scope_memberships subqueries for every
|
|
62
|
+
* candidate row, we compute the visible set up front and let the SQL filter use
|
|
63
|
+
* a plain `scope_id IN (...)`. The membership predicate (active membership with
|
|
64
|
+
* `membership_epoch >= scope.membership_epoch`) is preserved exactly.
|
|
65
|
+
*
|
|
66
|
+
* Lives in this leaf module (it imports nothing from filters/search/store/
|
|
67
|
+
* vectors) so all three OwnershipFilterContext builders can share it without an
|
|
68
|
+
* import cycle.
|
|
69
|
+
*
|
|
70
|
+
* NULL scope_ids are skipped here — a NULL scope_id is handled by the filter's
|
|
71
|
+
* dedicated `IS NULL` branch, not by membership in this set.
|
|
72
|
+
*/
|
|
73
|
+
export declare function resolveVisibleScopeIds(db: Database, deviceId: string): string[];
|
|
40
74
|
//# sourceMappingURL=scope-resolution.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-resolution.d.ts","sourceRoot":"","sources":["../src/scope-resolution.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scope-resolution.d.ts","sourceRoot":"","sources":["../src/scope-resolution.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,eAAO,MAAM,sBAAsB,kBAAkB,CAAC;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,yBAAyB,CAAC;AAEpE;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,MAAM,MAAM,uBAAuB,GAChC,YAAY,GACZ,mBAAmB,GACnB,KAAK,GACL,cAAc,GACd,UAAU,CAAC;AAEd,MAAM,WAAW,sBAAsB;IACtC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,0BAA0B;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,uBAAuB,CAAC;IAChC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC5B,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,MAAM,qBAAqB,GAC9B,mBAAmB,GACnB,eAAe,GACf,iBAAiB,GACjB,eAAe,CAAC;AAEnB,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,iBAAiB,EAAE,0BAA0B,CAAC;IAC9C,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAyB,SAAQ,sBAAsB;IACvE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAuCD,wBAAgB,0BAA0B,CACzC,KAAK,EAAE,sBAAsB,GAC3B,0BAA0B,CA4B5B;AAiDD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,eAAe,CA4EpF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CA2B/E"}
|
package/dist/search.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Simple queries (anchor lookup, batch ID fetch, session projects) use Drizzle typed queries.
|
|
6
6
|
* Dynamic filter queries use raw SQL since the filter builder returns SQL strings.
|
|
7
7
|
*/
|
|
8
|
+
import { type OwnershipFilterContext } from "./filters.js";
|
|
8
9
|
import type { ExplainResponse, MemoryFilters, MemoryItem, MemoryItemResponse, MemoryResult, PackTraceCandidateScores, TimelineItemResponse } from "./types.js";
|
|
9
10
|
export interface ExplainOptions {
|
|
10
11
|
includePackContext?: boolean;
|
|
@@ -16,10 +17,12 @@ export interface StoreHandle {
|
|
|
16
17
|
readonly deviceId: string;
|
|
17
18
|
get(memoryId: number): MemoryItemResponse | null;
|
|
18
19
|
memoryOwnedBySelf(item: MemoryItem | MemoryResult | Record<string, unknown>): boolean;
|
|
20
|
+
sameActorPeerIds?(): string[];
|
|
19
21
|
buildOwnershipPredicate?(): (item: MemoryItem | MemoryResult | Record<string, unknown>) => boolean;
|
|
20
22
|
recent(limit?: number, filters?: MemoryFilters | null, offset?: number): MemoryItemResponse[];
|
|
21
23
|
recentByKinds(kinds: string[], limit?: number, filters?: MemoryFilters | null, offset?: number): MemoryItemResponse[];
|
|
22
24
|
}
|
|
25
|
+
export declare function ownershipFilterContext(store: StoreHandle): OwnershipFilterContext;
|
|
23
26
|
/**
|
|
24
27
|
* Expand a user query string into an FTS5 MATCH expression.
|
|
25
28
|
*
|
package/dist/search.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* `codemem db init` invocation first.
|
|
10
10
|
*/
|
|
11
11
|
import type { Database } from "./db.js";
|
|
12
|
+
import { type OwnershipFilterContext } from "./filters.js";
|
|
12
13
|
import type { RefQueryOptions, RefQueryResult } from "./ref-queries.js";
|
|
13
14
|
import { type ExplainOptions } from "./search.js";
|
|
14
15
|
import { SecretScanner } from "./secret-scanner.js";
|
|
@@ -45,6 +46,14 @@ export declare class MemoryStore {
|
|
|
45
46
|
private assertSharedMutationAllowed;
|
|
46
47
|
private enqueueVectorWrite;
|
|
47
48
|
flushPendingVectorWrites(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Build the ownership/scope-visibility filter context for SQL filter
|
|
51
|
+
* builders. Snapshots claimed same-actor peers (and their legacy-sync actor
|
|
52
|
+
* ids) once so `ownership_scope=mine/theirs` SQL matches
|
|
53
|
+
* {@link buildOwnershipPredicate}. Shared by core read paths and viewer
|
|
54
|
+
* routes to keep a single source of truth for "is this mine?".
|
|
55
|
+
*/
|
|
56
|
+
ownershipFilterContext(): OwnershipFilterContext;
|
|
48
57
|
private scopeVisibleFilterContext;
|
|
49
58
|
/**
|
|
50
59
|
* Fetch a single memory item by ID.
|
|
@@ -137,6 +146,23 @@ export declare class MemoryStore {
|
|
|
137
146
|
* Return recent active memories filtered to specific kinds, newest first.
|
|
138
147
|
*/
|
|
139
148
|
recentByKinds(kinds: string[], limit?: number, filters?: MemoryFilters | null, offset?: number): MemoryItemResponse[];
|
|
149
|
+
/**
|
|
150
|
+
* Neutral, unfiltered token/event aggregate over usage_events, grouped by
|
|
151
|
+
* event kind. This is the shared SQL aggregate used by both stats() and the
|
|
152
|
+
* viewer /api/usage route so neither has to scan the (potentially large)
|
|
153
|
+
* usage_events table into JS to sum it. The COALESCE on tokens_saved matches
|
|
154
|
+
* the historical stats() semantics (treat NULL saved as 0). When
|
|
155
|
+
* projectFilter is a non-empty string, rows are restricted to the given
|
|
156
|
+
* project via the sessions join; otherwise every row is aggregated.
|
|
157
|
+
* Callers sort the returned rows as needed.
|
|
158
|
+
*/
|
|
159
|
+
usageAggregate(projectFilter?: string | null): Array<{
|
|
160
|
+
event: string;
|
|
161
|
+
count: number;
|
|
162
|
+
tokens_read: number;
|
|
163
|
+
tokens_written: number;
|
|
164
|
+
tokens_saved: number;
|
|
165
|
+
}>;
|
|
140
166
|
/**
|
|
141
167
|
* Return database statistics matching the Python stats() output shape.
|
|
142
168
|
*/
|
|
@@ -239,6 +265,21 @@ export declare class MemoryStore {
|
|
|
239
265
|
* Port of purge_raw_events() + purge_raw_events_before().
|
|
240
266
|
*/
|
|
241
267
|
purgeRawEvents(maxAgeMs: number): number;
|
|
268
|
+
/**
|
|
269
|
+
* Report raw_events storage usage and how many rows fall before an age cutoff.
|
|
270
|
+
*
|
|
271
|
+
* Age-based only (mirrors planReplicationOpsAgePrune / estimateReplicationOps
|
|
272
|
+
* style): counts total rows, estimates the on-disk bytes for the raw_events
|
|
273
|
+
* table and its indexes, and counts rows with ts_wall_ms older than the
|
|
274
|
+
* cutoff implied by maxAgeMs. Used by the prune-raw-events dry-run report.
|
|
275
|
+
*/
|
|
276
|
+
rawEventsRetentionStatus(maxAgeMs: number): {
|
|
277
|
+
total_rows: number;
|
|
278
|
+
estimated_bytes: number;
|
|
279
|
+
candidate_rows: number;
|
|
280
|
+
cutoff_ts_wall_ms: number | null;
|
|
281
|
+
};
|
|
282
|
+
private estimateRawEventsBytes;
|
|
242
283
|
/**
|
|
243
284
|
* Mark stuck flush batches (started/running/pending/claimed) as failed.
|
|
244
285
|
* Port of mark_stuck_raw_event_batches_as_error().
|