pending-adoption-holder.ts

3.2 KB · raw

1import type { AdoptRequest } from "../shared/vault-adopt";2import type { PairedDevice } from "../shared/sync-types";3import { storageGet, storageRemove, storageSet } from "../shared/storage";45export const PENDING_ADOPTION_HOLDER_KEY = "peach_pending_adoption_holder_v1";6export const PENDING_ADOPTION_IDENTITY_KEY = "peach_pending_adoption_identity_v2";78export type PendingAdoptionHolder = {9  request: AdoptRequest;10  createdAt: number;11};1213export type PendingAdoptionIdentity = {14  device: PairedDevice;15  createdAt: number;16};1718function isHolder(value: unknown): value is PendingAdoptionHolder {19  if (!value || typeof value !== "object") return false;20  const holder = value as Partial<PendingAdoptionHolder>;21  return typeof holder.createdAt === "number" && Number.isFinite(holder.createdAt)22    && !!holder.request && typeof holder.request === "object"23    && typeof (holder.request as Partial<AdoptRequest>).device_id === "string";24}2526export async function persistPendingAdoptionHolder(request: AdoptRequest): Promise<void> {27  await storageSet(PENDING_ADOPTION_HOLDER_KEY, JSON.stringify({ request, createdAt: Date.now() }));28}2930/** Returns null for absent or malformed data; malformed bytes remain for explicit recovery/removal. */31export async function readPendingAdoptionHolder(): Promise<PendingAdoptionHolder | null> {32  const raw = await storageGet(PENDING_ADOPTION_HOLDER_KEY);33  if (!raw) return null;34  try {35    const parsed: unknown = JSON.parse(raw);36    return isHolder(parsed) ? parsed : null;37  } catch {38    return null;39  }40}4142export async function listPendingAdoptionHolders(): Promise<Array<PendingAdoptionHolder | PendingAdoptionIdentity>> {43  const [holder, identity] = await Promise.all([44    readPendingAdoptionHolder(),45    readPendingAdoptionIdentity(),46  ]);47  return [holder, identity].filter((value): value is PendingAdoptionHolder | PendingAdoptionIdentity => value !== null);48}4950export async function removePendingAdoptionHolder(): Promise<void> {51  await Promise.all([52    storageRemove(PENDING_ADOPTION_HOLDER_KEY),53    storageRemove(PENDING_ADOPTION_IDENTITY_KEY),54  ]);55}5657export const revokePendingAdoptionHolder = removePendingAdoptionHolder;5859export async function persistPendingAdoptionIdentity(60  device: PairedDevice,61  createdAt: number,62): Promise<boolean> {63  await storageSet(PENDING_ADOPTION_IDENTITY_KEY, JSON.stringify({ device, createdAt }));64  return true;65}6667export async function readPendingAdoptionIdentity(): Promise<PendingAdoptionIdentity | null> {68  const raw = await storageGet(PENDING_ADOPTION_IDENTITY_KEY);69  if (!raw) return null;70  try {71    const value = JSON.parse(raw) as { device?: PairedDevice; createdAt?: number };72    return value.device?.deviceId && Number.isFinite(value.createdAt)73      ? { device: value.device, createdAt: value.createdAt as number }74      : null;75  } catch {76    return null;77  }78}7980export async function completePendingAdoptionIdentity(device: PairedDevice): Promise<void> {81  const pending = await readPendingAdoptionIdentity();82  if (pending?.device.deviceId === device.deviceId &&83      pending.device.longTermPublicKeyHex === device.longTermPublicKeyHex) {84    await storageRemove(PENDING_ADOPTION_IDENTITY_KEY);85  }86}