🌳
pt0/deployF/claimLockF/claimLockAI.mts
2import { isClaimAcquirable, type Claim } from './claimCoreAI.mts'
4export type { Claim }
6// swappable store iface (cfTxtClaimStore now) — claimKey is opaque to the protocol.
7export type ClaimStore = {
8 readClaim(claimKey: string): Promise<Claim | null>
9 putClaim(claimKey: string, claim: Claim): Promise<void>
10 listClaims(): Promise<Array<{claimKey: string, claim: Claim}>>
13const nowSec = () => Math.floor(Date.now() / 1000)
15// fail-closed per item: store-unreachable → notify + return fallback (caller skips the gated action).
16async function claimOnStoreErr<T>(fallback: T, debugH: Record<string, unknown>, fn: () => Promise<T>): Promise<T> {
17 try { return await fn() }
18 catch (err) { await noThrowNotifErr('claimStoreUnreachable', {...debugH, err: String(err)}); return fallback }
21export const acquireClaim = async ({store, claimKey, owner, ttlSec = 600}: {
22 store: ClaimStore, claimKey: string, owner: string, ttlSec?: number
23}): Promise<{claimed: boolean}> => {
24 const debugH = {claimKey, owner}
25 return claimOnStoreErr({claimed: false}, debugH, async () => {
26 const claim = await store.readClaim(claimKey)
27 const now = nowSec()
28 if (isClaimAcquirable(claim, owner, now)) {
29 await store.putClaim(claimKey, {owner, hb: now, ttlSec})
30 return {claimed: true}
31 }
32 return {claimed: false}
33 })
36export const checkClaim = async ({store, claimKey, owner}: {
37 store: ClaimStore, claimKey: string, owner: string
38}): Promise<boolean> => {
39 return claimOnStoreErr(false, {claimKey, owner}, async () => {
40 const claim = await store.readClaim(claimKey)
41 return claim?.owner === owner
42 })
45// best-effort maintenance: failure only notifies (never blocks the gated actions above).
46export const renewAllMyClaims = async ({store, owner}: {store: ClaimStore, owner: string}): Promise<{renewed: number}> => {
47 return claimOnStoreErr({renewed: 0}, {owner}, async () => {
48 const all = await store.listClaims()
49 const mine = all.filter(({claim}) => claim.owner === owner)
50 const now = nowSec()
51 for (const {claimKey, claim} of mine) await store.putClaim(claimKey, {owner, hb: now, ttlSec: claim.ttlSec})
52 return {renewed: mine.length}
53 })