@dopamint-fun/open-sdk 0.2.0-dev.1 → 0.2.0-dev.3
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/agentHttp.js +11 -14
- package/dist/claim.js +37 -21
- package/dist/cli.js +11 -3
- package/dist/identity.js +12 -31
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/openTournament.d.ts +22 -0
- package/dist/openTournament.js +31 -0
- package/package.json +1 -1
package/dist/agentHttp.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* server reconstructs them from the request it actually received, so there is
|
|
14
14
|
* no restated copy to compare and therefore no comparison to forget.
|
|
15
15
|
*/
|
|
16
|
-
import { ByteWriter, frameSigningBytes, fromHex, textBytes, toHex } from "./bytes.js";
|
|
16
|
+
import { ByteReader, ByteWriter, frameSigningBytes, fromHex, textBytes, toHex, } from "./bytes.js";
|
|
17
17
|
import { blake2b256 } from "./crypto.js";
|
|
18
18
|
import { signRaw } from "./keypair.js";
|
|
19
19
|
const AGENT_HTTP_CAPABILITY_DOMAIN = textBytes("dopa_open::agent_http_capability::v1");
|
|
@@ -94,20 +94,17 @@ export function decodeAgentHttpHeader(value) {
|
|
|
94
94
|
const bytes = fromHex(value);
|
|
95
95
|
if (bytes.length !== HEADER_BYTES)
|
|
96
96
|
throw new Error(`capability header must be ${HEADER_BYTES} bytes, got ${bytes.length}`);
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
agentPublicKey: bytes.slice(32, 64),
|
|
106
|
-
issuedAtMs: readU64(64),
|
|
107
|
-
expiresAtMs: readU64(72),
|
|
108
|
-
nonce: bytes.slice(80, 80 + AGENT_HTTP_CAPABILITY_NONCE_BYTES),
|
|
109
|
-
signature: bytes.slice(80 + AGENT_HTTP_CAPABILITY_NONCE_BYTES),
|
|
97
|
+
const reader = new ByteReader(bytes);
|
|
98
|
+
const capability = {
|
|
99
|
+
agentId: reader.readFixed(32, "agent id"),
|
|
100
|
+
agentPublicKey: reader.readFixed(32, "agent public key"),
|
|
101
|
+
issuedAtMs: reader.readU64("issued_at_ms"),
|
|
102
|
+
expiresAtMs: reader.readU64("expires_at_ms"),
|
|
103
|
+
nonce: reader.readFixed(AGENT_HTTP_CAPABILITY_NONCE_BYTES, "nonce"),
|
|
104
|
+
signature: reader.readFixed(SIGNATURE_BYTES, "signature"),
|
|
110
105
|
};
|
|
106
|
+
reader.finish();
|
|
107
|
+
return capability;
|
|
111
108
|
}
|
|
112
109
|
/** Mint and sign one capability, and return the header to send with it.
|
|
113
110
|
*
|
package/dist/claim.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { signRaw } from "./keypair.js";
|
|
3
|
-
import {
|
|
3
|
+
import { ByteReader, ByteWriter, equalBytes, frameSigningBytes, fromHex, textBytes, toHex0x, } from "./bytes.js";
|
|
4
4
|
/* The agent's half of a claim: an invitation its own key signs.
|
|
5
5
|
*
|
|
6
6
|
* A wallet claims an agent by signing for it in the arena's UI. That proves
|
|
@@ -22,16 +22,6 @@ export const AGENT_CLAIM_INVITE_MAX_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
|
22
22
|
* nonce || signature`, every field fixed-width. */
|
|
23
23
|
export const AGENT_CLAIM_INVITE_TOKEN_BYTES = 32 + 32 + 32 + 8 + 8 + 16 + 64;
|
|
24
24
|
const ZERO_OWNER = new Uint8Array(32);
|
|
25
|
-
function u64be(value) {
|
|
26
|
-
const out = new Uint8Array(8);
|
|
27
|
-
new DataView(out.buffer).setBigUint64(0, value, false);
|
|
28
|
-
return out;
|
|
29
|
-
}
|
|
30
|
-
function fixed(bytes, length, what) {
|
|
31
|
-
if (bytes.length !== length)
|
|
32
|
-
throw new Error(`${what} must be ${length} bytes, got ${bytes.length}`);
|
|
33
|
-
return bytes;
|
|
34
|
-
}
|
|
35
25
|
/** `AgentClaimInvite::canonical_payload` -- the framed fields. */
|
|
36
26
|
export function claimInviteCanonicalPayload(invite) {
|
|
37
27
|
if (invite.expiresAtMs <= invite.issuedAtMs)
|
|
@@ -41,7 +31,18 @@ export function claimInviteCanonicalPayload(invite) {
|
|
|
41
31
|
throw new Error("an invitation may stay open for at most a week");
|
|
42
32
|
if (invite.nonce.every((byte) => byte === 0))
|
|
43
33
|
throw new Error("the invitation's nonce must not be all zero");
|
|
44
|
-
return
|
|
34
|
+
return new ByteWriter()
|
|
35
|
+
.pushBytes(AGENT_CLAIM_INVITE_DOMAIN)
|
|
36
|
+
.pushByte(0)
|
|
37
|
+
.pushByte(CANONICAL_WIRE_VERSION)
|
|
38
|
+
.pushByte(INVITE_OPERATION)
|
|
39
|
+
.pushFixed(invite.agentId, 32, "agent id")
|
|
40
|
+
.pushFixed(invite.owner ?? ZERO_OWNER, 32, "owner")
|
|
41
|
+
.pushFixed(invite.agentPublicKey, 32, "agent public key")
|
|
42
|
+
.pushU64(invite.issuedAtMs)
|
|
43
|
+
.pushU64(invite.expiresAtMs)
|
|
44
|
+
.pushFixed(invite.nonce, AGENT_CLAIM_INVITE_NONCE_BYTES, "nonce")
|
|
45
|
+
.bytes();
|
|
45
46
|
}
|
|
46
47
|
/** The bytes the agent's key signs, raw ed25519. */
|
|
47
48
|
export function claimInviteSigningBytes(invite) {
|
|
@@ -66,7 +67,15 @@ export async function mintClaimInvite(agent, agentId, options = {}) {
|
|
|
66
67
|
function claimInviteBytes(invite) {
|
|
67
68
|
if (invite.signature.length !== 64)
|
|
68
69
|
throw new Error("an invitation is encoded only once it is signed");
|
|
69
|
-
return
|
|
70
|
+
return new ByteWriter()
|
|
71
|
+
.pushFixed(invite.agentId, 32, "agent id")
|
|
72
|
+
.pushFixed(invite.owner ?? ZERO_OWNER, 32, "owner")
|
|
73
|
+
.pushFixed(invite.agentPublicKey, 32, "agent public key")
|
|
74
|
+
.pushU64(invite.issuedAtMs)
|
|
75
|
+
.pushU64(invite.expiresAtMs)
|
|
76
|
+
.pushFixed(invite.nonce, AGENT_CLAIM_INVITE_NONCE_BYTES, "nonce")
|
|
77
|
+
.pushBytes(invite.signature)
|
|
78
|
+
.bytes();
|
|
70
79
|
}
|
|
71
80
|
/** The token the link carries and the claim body posts back: 0x-hex.
|
|
72
81
|
*
|
|
@@ -115,16 +124,23 @@ export function decodeClaimInvite(token) {
|
|
|
115
124
|
const bytes = claimInviteTokenBytes(token.trim());
|
|
116
125
|
if (bytes.length !== AGENT_CLAIM_INVITE_TOKEN_BYTES)
|
|
117
126
|
throw new Error(`an invitation token is ${AGENT_CLAIM_INVITE_TOKEN_BYTES} bytes, got ${bytes.length}`);
|
|
118
|
-
const
|
|
119
|
-
const
|
|
127
|
+
const reader = new ByteReader(bytes);
|
|
128
|
+
const agentId = reader.readFixed(32, "agent id");
|
|
129
|
+
const owner = reader.readFixed(32, "owner");
|
|
130
|
+
const agentPublicKey = reader.readFixed(32, "agent public key");
|
|
131
|
+
const issuedAtMs = reader.readU64("issued");
|
|
132
|
+
const expiresAtMs = reader.readU64("expires");
|
|
133
|
+
const nonce = reader.readFixed(AGENT_CLAIM_INVITE_NONCE_BYTES, "nonce");
|
|
134
|
+
const signature = reader.readFixed(64, "signature");
|
|
135
|
+
reader.finish();
|
|
120
136
|
return {
|
|
121
|
-
agentId
|
|
137
|
+
agentId,
|
|
122
138
|
owner: equalBytes(owner, ZERO_OWNER) ? null : owner,
|
|
123
|
-
agentPublicKey
|
|
124
|
-
issuedAtMs
|
|
125
|
-
expiresAtMs
|
|
126
|
-
nonce
|
|
127
|
-
signature
|
|
139
|
+
agentPublicKey,
|
|
140
|
+
issuedAtMs,
|
|
141
|
+
expiresAtMs,
|
|
142
|
+
nonce,
|
|
143
|
+
signature,
|
|
128
144
|
};
|
|
129
145
|
}
|
|
130
146
|
/** Where the owner goes to accept: the agent's claim page with the token. */
|
package/dist/cli.js
CHANGED
|
@@ -23,7 +23,7 @@ import { claimInviteLink, encodeClaimInvite, encodeClaimInviteCompact, mintClaim
|
|
|
23
23
|
import { acceptAndAwaitAdmission } from "./offer.js";
|
|
24
24
|
import { playTour, queueUntilSeated } from "./tour.js";
|
|
25
25
|
import { joinRoomWhenComposed, MIN_ROOM_SEATS, openRoom, roomInvitePrompt, } from "./room.js";
|
|
26
|
-
import { disputeHolding, JoinRefused, joinTransaction, leaveTransaction, listTournaments, matchmakingOverLine, planJoin, presentToTournament, readAgentEntry, playsHeldBy, giveBackTransaction, readTournament, sponsorAndExecute, tournamentIdArg, } from "./openTournament.js";
|
|
26
|
+
import { disputeHolding, JoinRefused, joinTransaction, leaveTransaction, listTournaments, matchmakingOverLine, planJoin, presentToTournament, readAgentEntry, readAgentEntrySettled, playsHeldBy, giveBackTransaction, readTournament, sponsorAndExecute, tournamentIdArg, } from "./openTournament.js";
|
|
27
27
|
import { authorityOriginFromSessionBase, buildConsentRequest, digestForPrompt, settlementConsentPath, verifyConsentDisclosure, } from "./settlement.js";
|
|
28
28
|
import { actionSigningBytes, joinSigningBytes, requireSessionVersion, resumeSigningBytes, SESSION_VERSION, } from "./sessionWire.js";
|
|
29
29
|
import { describeNext, refusalMessageFromText } from "./refusal.js";
|
|
@@ -1471,8 +1471,16 @@ async function commandTournament(args) {
|
|
|
1471
1471
|
return;
|
|
1472
1472
|
}
|
|
1473
1473
|
if (verb === "leave") {
|
|
1474
|
-
|
|
1475
|
-
|
|
1474
|
+
/* An agent that queued a moment ago is on chain and not yet in the book,
|
|
1475
|
+
which is read a checkpoint behind it. Refusing on the first read told an
|
|
1476
|
+
agent that had just been printed `queued waiting` that it was not in the
|
|
1477
|
+
book at all, and the refusal's own `next` sent it to join again — which
|
|
1478
|
+
would queue an agent that is already queued. So an absent entry is
|
|
1479
|
+
waited out, briefly, before it is believed. */
|
|
1480
|
+
const settled = entry ??
|
|
1481
|
+
(await readAgentEntrySettled(productUrl, tournamentId, chip));
|
|
1482
|
+
if (settled?.state !== "queued")
|
|
1483
|
+
fail(`this agent is ${settled?.state ?? "not in the book"}; only a queued agent can leave, and a seated one plays its table out`);
|
|
1476
1484
|
const executed = await sponsorAndExecute(client, leaveTransaction(overview, chip));
|
|
1477
1485
|
console.log(`left ${executed.digest} ${executed.status}`);
|
|
1478
1486
|
if (executed.status !== "success")
|
package/dist/identity.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* bytes. The product parses before it verifies -- it trims each field and
|
|
13
13
|
* lower-cases the handle -- so this signs the parsed form, or the signature
|
|
14
14
|
* is over bytes the store never holds. */
|
|
15
|
+
import { ByteWriter, fromHex, textBytes } from "./bytes.js";
|
|
15
16
|
import { signOwnerAuthenticator } from "./keypair.js";
|
|
16
17
|
import { refusalMessageFromText } from "./refusal.js";
|
|
17
18
|
const IDENTITY_DOMAIN = "dopa_open::agent_identity::v1";
|
|
@@ -25,40 +26,20 @@ export function parseIdentityFields(fields) {
|
|
|
25
26
|
bio: fields.bio?.trim() || null,
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
|
-
function u64be(value) {
|
|
29
|
-
const out = new Uint8Array(8);
|
|
30
|
-
new DataView(out.buffer).setBigUint64(0, BigInt(value));
|
|
31
|
-
return out;
|
|
32
|
-
}
|
|
33
|
-
function fromHex(value) {
|
|
34
|
-
const hex = value.replace(/^0x/i, "");
|
|
35
|
-
const out = new Uint8Array(hex.length / 2);
|
|
36
|
-
for (let index = 0; index < out.length; index++)
|
|
37
|
-
out[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
|
|
38
|
-
return out;
|
|
39
|
-
}
|
|
40
29
|
/** The exact bytes the owning address signs to name an agent. */
|
|
41
30
|
export function canonicalIdentityPayload(edit) {
|
|
42
|
-
const
|
|
43
|
-
const domain = encoder.encode(IDENTITY_DOMAIN);
|
|
31
|
+
const domain = textBytes(IDENTITY_DOMAIN);
|
|
44
32
|
const parsed = parseIdentityFields(edit.fields);
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
fromHex(edit.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
u64be(edit.expiresAtMs),
|
|
54
|
-
];
|
|
55
|
-
const out = new Uint8Array(chunks.reduce((total, chunk) => total + chunk.length, 0));
|
|
56
|
-
let offset = 0;
|
|
57
|
-
for (const chunk of chunks) {
|
|
58
|
-
out.set(chunk, offset);
|
|
59
|
-
offset += chunk.length;
|
|
33
|
+
const writer = new ByteWriter()
|
|
34
|
+
.pushU64(domain.length)
|
|
35
|
+
.pushBytes(domain)
|
|
36
|
+
.pushBytes(fromHex(edit.agentId))
|
|
37
|
+
.pushBytes(fromHex(edit.owner));
|
|
38
|
+
for (const field of [parsed.name ?? "", parsed.handle ?? "", parsed.bio ?? ""]) {
|
|
39
|
+
const bytes = textBytes(field);
|
|
40
|
+
writer.pushU64(bytes.length).pushBytes(bytes);
|
|
60
41
|
}
|
|
61
|
-
return
|
|
42
|
+
return writer.pushU64(edit.issuedAtMs).pushU64(edit.expiresAtMs).bytes();
|
|
62
43
|
}
|
|
63
44
|
/** Name an agent, as the address that owns it. Answers what the arena stored. */
|
|
64
45
|
export async function nameAgent(args) {
|
|
@@ -73,7 +54,7 @@ export async function nameAgent(args) {
|
|
|
73
54
|
issuedAtMs,
|
|
74
55
|
expiresAtMs,
|
|
75
56
|
}));
|
|
76
|
-
const agentId = `0x${args.agentId.replace(/^0x
|
|
57
|
+
const agentId = `0x${args.agentId.replace(/^0x/, "")}`;
|
|
77
58
|
const response = await fetchImpl(`${args.productUrl.replace(/\/$/, "")}/open/v1/agents/${agentId}/identity`, {
|
|
78
59
|
method: "PUT",
|
|
79
60
|
headers: { "content-type": "application/json" },
|
package/dist/index.d.ts
CHANGED
|
@@ -17,5 +17,5 @@ export { handEquity, requiredEquity, cardCode, type HandEquity, type HandEquityO
|
|
|
17
17
|
export { cardIndex, handCategory, scoreHand, HAND_CATEGORIES, RANK_ORDER, SUIT_ORDER, type BestFive, type HandCategory, } from "./handRank.js";
|
|
18
18
|
export { DEFAULT_SEAT_STATE_FILE, loadSeatState, saveSeatState, type SeatSessionState, } from "./seatState.js";
|
|
19
19
|
export { newSeatState, openTurn, submitTurn, type SeatStateCheckpoint, type SeatTurnOutcome, type SeatTurnPosition, } from "./seatTurn.js";
|
|
20
|
-
export { chipAddressOf, joinTransaction, leaveTransaction, listTournaments, planJoin, presentToTournament, readAgentEntry, readPass, readOwner, playsHeldBy, giveBackTransaction, readTournament, sponsorAndExecute, tournamentIdArg, tournamentPossessionSignature, JoinRefused, TournamentRefusal, TOURNAMENT_POSSESSION_SEAT, type JoinPlan, type SponsoredExecution, type TournamentAgentEntry, type TournamentPass, type TournamentPlay, type TournamentOwnerView, type TournamentOwnerAgent, type TournamentChainObjects, type TournamentClient, type TournamentOverview, type TournamentQueueEntry, } from "./openTournament.js";
|
|
20
|
+
export { chipAddressOf, joinTransaction, leaveTransaction, listTournaments, planJoin, presentToTournament, readAgentEntry, readAgentEntrySettled, BOOK_CATCHES_UP_MS, readPass, readOwner, playsHeldBy, giveBackTransaction, readTournament, sponsorAndExecute, tournamentIdArg, tournamentPossessionSignature, JoinRefused, TournamentRefusal, TOURNAMENT_POSSESSION_SEAT, type JoinPlan, type SponsoredExecution, type TournamentAgentEntry, type TournamentPass, type TournamentPlay, type TournamentOwnerView, type TournamentOwnerAgent, type TournamentChainObjects, type TournamentClient, type TournamentOverview, type TournamentQueueEntry, } from "./openTournament.js";
|
|
21
21
|
export { ClientRefusal, describeNext, refusalLines, refusalMessage, refusalMessageFromText, type RefusalBody, type RefusalNext, } from "./refusal.js";
|
package/dist/index.js
CHANGED
|
@@ -29,5 +29,5 @@ export { handEquity, requiredEquity, cardCode, } from "./equity.js";
|
|
|
29
29
|
export { cardIndex, handCategory, scoreHand, HAND_CATEGORIES, RANK_ORDER, SUIT_ORDER, } from "./handRank.js";
|
|
30
30
|
export { DEFAULT_SEAT_STATE_FILE, loadSeatState, saveSeatState, } from "./seatState.js";
|
|
31
31
|
export { newSeatState, openTurn, submitTurn, } from "./seatTurn.js";
|
|
32
|
-
export { chipAddressOf, joinTransaction, leaveTransaction, listTournaments, planJoin, presentToTournament, readAgentEntry, readPass, readOwner, playsHeldBy, giveBackTransaction, readTournament, sponsorAndExecute, tournamentIdArg, tournamentPossessionSignature, JoinRefused, TournamentRefusal, TOURNAMENT_POSSESSION_SEAT, } from "./openTournament.js";
|
|
32
|
+
export { chipAddressOf, joinTransaction, leaveTransaction, listTournaments, planJoin, presentToTournament, readAgentEntry, readAgentEntrySettled, BOOK_CATCHES_UP_MS, readPass, readOwner, playsHeldBy, giveBackTransaction, readTournament, sponsorAndExecute, tournamentIdArg, tournamentPossessionSignature, JoinRefused, TournamentRefusal, TOURNAMENT_POSSESSION_SEAT, } from "./openTournament.js";
|
|
33
33
|
export { ClientRefusal, describeNext, refusalLines, refusalMessage, refusalMessageFromText, } from "./refusal.js";
|
package/dist/openTournament.d.ts
CHANGED
|
@@ -143,6 +143,28 @@ export declare function readTournamentMatches(productUrl: string, tournamentId:
|
|
|
143
143
|
export declare function disputeHolding(productUrl: string, tournamentId: string, chipAddress: string, fetchImpl?: typeof fetch): Promise<number | undefined>;
|
|
144
144
|
/** The agent's entry in the chip book, or `null` before its first redeem. */
|
|
145
145
|
export declare function readAgentEntry(productUrl: string, tournamentId: string, chipAddress: string, fetchImpl?: typeof fetch): Promise<TournamentAgentEntry | null>;
|
|
146
|
+
/** How long a book read waits for a join that has just landed.
|
|
147
|
+
*
|
|
148
|
+
* The book is the chain's, read a checkpoint behind it: an agent whose
|
|
149
|
+
* `queue_join` succeeded a moment ago is on chain and not yet in the book.
|
|
150
|
+
* Measured at about a second on a local stack; this is generous enough to
|
|
151
|
+
* cover a slower one and short enough that an agent that really is absent is
|
|
152
|
+
* told so rather than left waiting. */
|
|
153
|
+
export declare const BOOK_CATCHES_UP_MS = 8000;
|
|
154
|
+
/** The agent's entry, waited for while the book catches up with the chain.
|
|
155
|
+
*
|
|
156
|
+
* For a caller that has just been told its join landed. A plain
|
|
157
|
+
* `readAgentEntry` answering `null` in that window is not "this agent never
|
|
158
|
+
* queued" — it is "the book has not seen the queue join yet", and the two are
|
|
159
|
+
* worth telling apart before refusing somebody. Absent for the whole bound is
|
|
160
|
+
* the first answer, and this returns `null` for it. */
|
|
161
|
+
export declare function readAgentEntrySettled(productUrl: string, tournamentId: string, chipAddress: string, options?: {
|
|
162
|
+
boundMs?: number;
|
|
163
|
+
pollMs?: number;
|
|
164
|
+
fetchImpl?: typeof fetch;
|
|
165
|
+
sleep?: (ms: number) => Promise<void>;
|
|
166
|
+
now?: () => number;
|
|
167
|
+
}): Promise<TournamentAgentEntry | null>;
|
|
146
168
|
/** An owner's pass, or `null` when the owner has claimed none. */
|
|
147
169
|
export declare function readPass(productUrl: string, tournamentId: string, owner: string, fetchImpl?: typeof fetch): Promise<TournamentPass | null>;
|
|
148
170
|
/** An owner's side: its pass and every agent it has claimed, each with the
|
package/dist/openTournament.js
CHANGED
|
@@ -100,6 +100,37 @@ export async function readAgentEntry(productUrl, tournamentId, chipAddress, fetc
|
|
|
100
100
|
throw new TournamentRefusal(status, json, "chip book read");
|
|
101
101
|
return json;
|
|
102
102
|
}
|
|
103
|
+
/** How long a book read waits for a join that has just landed.
|
|
104
|
+
*
|
|
105
|
+
* The book is the chain's, read a checkpoint behind it: an agent whose
|
|
106
|
+
* `queue_join` succeeded a moment ago is on chain and not yet in the book.
|
|
107
|
+
* Measured at about a second on a local stack; this is generous enough to
|
|
108
|
+
* cover a slower one and short enough that an agent that really is absent is
|
|
109
|
+
* told so rather than left waiting. */
|
|
110
|
+
export const BOOK_CATCHES_UP_MS = 8_000;
|
|
111
|
+
/** The agent's entry, waited for while the book catches up with the chain.
|
|
112
|
+
*
|
|
113
|
+
* For a caller that has just been told its join landed. A plain
|
|
114
|
+
* `readAgentEntry` answering `null` in that window is not "this agent never
|
|
115
|
+
* queued" — it is "the book has not seen the queue join yet", and the two are
|
|
116
|
+
* worth telling apart before refusing somebody. Absent for the whole bound is
|
|
117
|
+
* the first answer, and this returns `null` for it. */
|
|
118
|
+
export async function readAgentEntrySettled(productUrl, tournamentId, chipAddress, options = {}) {
|
|
119
|
+
const boundMs = options.boundMs ?? BOOK_CATCHES_UP_MS;
|
|
120
|
+
const pollMs = options.pollMs ?? 1_000;
|
|
121
|
+
const now = options.now ?? Date.now;
|
|
122
|
+
const sleep = options.sleep ??
|
|
123
|
+
((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
124
|
+
const deadline = now() + boundMs;
|
|
125
|
+
for (;;) {
|
|
126
|
+
const entry = await readAgentEntry(productUrl, tournamentId, chipAddress, options.fetchImpl ?? fetch);
|
|
127
|
+
if (entry)
|
|
128
|
+
return entry;
|
|
129
|
+
if (now() >= deadline)
|
|
130
|
+
return null;
|
|
131
|
+
await sleep(pollMs);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
103
134
|
/** An owner's pass, or `null` when the owner has claimed none. */
|
|
104
135
|
export async function readPass(productUrl, tournamentId, owner, fetchImpl = fetch) {
|
|
105
136
|
const { status, json } = await readJson(fetchImpl, `${base(productUrl)}${tournamentPath(tournamentId, `/passes/${owner}`)}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dopamint-fun/open-sdk",
|
|
3
|
-
"version": "0.2.0-dev.
|
|
3
|
+
"version": "0.2.0-dev.3",
|
|
4
4
|
"description": "DOPA-OPEN client SDK: generate and hold your own agent key, sign registrations, and play a Participant Session",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|