@donezone/client 0.1.50 → 0.1.55

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/index.js CHANGED
@@ -1,6 +1,1219 @@
1
- export { DoneBackendClient } from "./client";
2
- export { Done, DoneContractClient } from "./done";
3
- export { DoneRequest } from "./done";
4
- export { createPasskeyEnvelopeBuilder, createSessionEnvelopeBuilder, } from "./auth";
5
- export { CURRENT_ENVELOPE_VERSION, buildEnvelope, createDevEnvelopeBuilder, decodeSignature, encodeSignature, signDocBytes, signDocDigest, toSignDoc, } from "./envelope";
6
- export { buildQueryMessage, buildTransactionMessage } from "./messages";
1
+ // src/envelope-builder.ts
2
+ var CURRENT_ENVELOPE_VERSION = 1;
3
+ function normalizeMetadata(metadata) {
4
+ if (!metadata) return void 0;
5
+ const normalized = {};
6
+ if (metadata.trace_id) normalized.trace_id = metadata.trace_id;
7
+ if (metadata.memo) normalized.memo = metadata.memo;
8
+ if (typeof metadata.gas_limit === "number") normalized.gas_limit = metadata.gas_limit;
9
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
10
+ }
11
+ function toSignDoc(envelope) {
12
+ const metadata = normalizeMetadata(envelope.metadata);
13
+ return {
14
+ version: envelope.version ?? CURRENT_ENVELOPE_VERSION,
15
+ user_id: envelope.user_id,
16
+ session_id: envelope.session_id,
17
+ msgs: envelope.msgs.map((msg) => cloneMsg(msg)),
18
+ nonce: envelope.nonce,
19
+ expires_at: envelope.expires_at,
20
+ role: envelope.role,
21
+ agent: envelope.agent,
22
+ forwarder: envelope.forwarder,
23
+ metadata
24
+ };
25
+ }
26
+ function buildEnvelope(draft) {
27
+ if (!Array.isArray(draft.msgs) || draft.msgs.length === 0) {
28
+ throw new Error("envelope requires at least one message");
29
+ }
30
+ const version = draft.version ?? CURRENT_ENVELOPE_VERSION;
31
+ const metadata = normalizeMetadata(draft.metadata);
32
+ return {
33
+ version,
34
+ user_id: draft.user_id,
35
+ session_id: draft.session_id,
36
+ msgs: draft.msgs.map((msg) => cloneMsg(msg)),
37
+ nonce: draft.nonce,
38
+ expires_at: draft.expires_at,
39
+ role: draft.role,
40
+ agent: draft.agent,
41
+ forwarder: draft.forwarder,
42
+ signatures: draft.signatures ? { ...draft.signatures } : {},
43
+ metadata
44
+ };
45
+ }
46
+ function signDocBytes(signDoc) {
47
+ const json = canonicalStringify(signDoc);
48
+ return new TextEncoder().encode(json);
49
+ }
50
+ function cloneMsg(msg) {
51
+ return structuredClone(msg);
52
+ }
53
+ function canonicalStringify(value) {
54
+ return JSON.stringify(value, (_key, val) => {
55
+ if (val instanceof Map) {
56
+ return Object.fromEntries(Array.from(val.entries()).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
57
+ }
58
+ if (val && typeof val === "object" && !Array.isArray(val)) {
59
+ const entries = Object.entries(val).sort(
60
+ ([a], [b]) => a < b ? -1 : a > b ? 1 : 0
61
+ );
62
+ return entries.reduce((acc, [key, entry]) => {
63
+ acc[key] = entry;
64
+ return acc;
65
+ }, {});
66
+ }
67
+ return val;
68
+ });
69
+ }
70
+ function createDevEnvelopeBuilder(options = {}) {
71
+ const userId = options.userId ?? "dev";
72
+ const ttlSeconds = options.ttlSeconds ?? 300;
73
+ return (ctx) => {
74
+ const envelope = buildEnvelope({
75
+ user_id: userId,
76
+ msgs: [ctx.msg],
77
+ nonce: Date.now(),
78
+ expires_at: Math.floor(Date.now() / 1e3) + ttlSeconds,
79
+ role: "Passkey",
80
+ signatures: {}
81
+ });
82
+ return { envelope };
83
+ };
84
+ }
85
+
86
+ // src/messages.ts
87
+ function buildTransactionMessage(contractAddr, call, options = {}) {
88
+ const payload = normalizeCall(call);
89
+ const msg = { msg: payload };
90
+ const funds = normalizeFunds(options.funds);
91
+ return {
92
+ wasm: {
93
+ execute: {
94
+ contract_addr: contractAddr,
95
+ msg,
96
+ funds: funds && funds.length > 0 ? funds : void 0
97
+ }
98
+ }
99
+ };
100
+ }
101
+ function buildQueryMessage(call) {
102
+ return { msg: normalizeCall(call) };
103
+ }
104
+ function normalizeCall(call) {
105
+ if (!call.path || !call.path.startsWith("/")) {
106
+ throw new Error("contract call path must start with '/'");
107
+ }
108
+ const payload = { path: call.path };
109
+ if (call.query instanceof URLSearchParams) {
110
+ payload.query = Object.fromEntries(Array.from(call.query));
111
+ } else if (call.query) {
112
+ payload.query = call.query;
113
+ }
114
+ if (call.body !== void 0) {
115
+ payload.body = call.body;
116
+ }
117
+ return payload;
118
+ }
119
+ function normalizeFunds(funds) {
120
+ if (!funds) return void 0;
121
+ if (Array.isArray(funds)) {
122
+ if (funds.length === 0) return void 0;
123
+ return funds.map((coin) => ({ denom: coin.denom, amount: coin.amount }));
124
+ }
125
+ const entries = Object.entries(funds);
126
+ if (entries.length === 0) return void 0;
127
+ return entries.map(([denom, amount]) => ({ denom, amount: String(amount) }));
128
+ }
129
+
130
+ // src/client.ts
131
+ var DoneBackendClient = class {
132
+ baseUrl;
133
+ fetchImpl;
134
+ constructor(config) {
135
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
136
+ const candidate = config.fetch ?? fetch;
137
+ if (typeof candidate !== "function") {
138
+ throw new Error("fetch implementation is not available; configure Done with a fetch implementation via Done.config");
139
+ }
140
+ this.fetchImpl = candidate.bind(globalThis);
141
+ }
142
+ contract(address) {
143
+ return new DoneContractHandle(this, address);
144
+ }
145
+ buildEnvelope(draft) {
146
+ return buildEnvelope(draft);
147
+ }
148
+ async executeEnvelope(envelope, options = {}, init = {}) {
149
+ const body = {
150
+ envelope,
151
+ passkey: options.passkey,
152
+ memo: options.memo ?? envelope.metadata?.memo
153
+ };
154
+ const { headers: initHeaders, signal: initSignal, method: initMethod, body: _ignoredBody, ...rest } = init;
155
+ const headers = new Headers(initHeaders ?? {});
156
+ headers.set("content-type", "application/json");
157
+ const res = await this.fetchImpl(`${this.baseUrl}/tx`, {
158
+ ...rest,
159
+ method: initMethod ?? "POST",
160
+ headers,
161
+ body: JSON.stringify(body),
162
+ signal: initSignal ?? options.signal
163
+ });
164
+ if (!res.ok) {
165
+ const text = await res.text().catch(() => "");
166
+ throw new Error(`tx failed with ${res.status}: ${text}`);
167
+ }
168
+ return res.json();
169
+ }
170
+ async publishCode(request, init = {}) {
171
+ const { contract, script, msg } = request;
172
+ const { headers: initHeaders, method: initMethod, body: _ignoredBody, ...rest } = init;
173
+ const headers = new Headers(initHeaders ?? {});
174
+ headers.set("content-type", "application/json");
175
+ const payload = { script };
176
+ if (Object.prototype.hasOwnProperty.call(request, "msg")) {
177
+ payload.msg = msg;
178
+ }
179
+ const res = await this.fetchImpl(`${this.baseUrl}/contracts/${contract}/publish`, {
180
+ ...rest,
181
+ method: initMethod ?? "POST",
182
+ headers,
183
+ body: JSON.stringify(payload)
184
+ });
185
+ if (!res.ok) {
186
+ const text = await res.text().catch(() => "");
187
+ throw new Error(`publish failed with ${res.status}: ${text}`);
188
+ }
189
+ return res.json();
190
+ }
191
+ async queryContractRaw(address, call, init = {}) {
192
+ const message = buildQueryMessage(call);
193
+ const params = new URLSearchParams();
194
+ params.set("msg", JSON.stringify(message));
195
+ const { method: initMethod, ...rest } = init;
196
+ return this.fetchImpl(`${this.baseUrl}/query/${address}?${params}`, {
197
+ method: initMethod ?? "GET",
198
+ ...rest
199
+ });
200
+ }
201
+ async queryContract(address, call, init = {}) {
202
+ const res = await this.queryContractRaw(address, call, init);
203
+ if (!res.ok) {
204
+ const text = await res.text().catch(() => "");
205
+ throw new Error(`query failed with ${res.status}: ${text}`);
206
+ }
207
+ return res.json();
208
+ }
209
+ async getAgentQuota(userId, agent) {
210
+ const res = await this.fetchImpl(`${this.baseUrl}/quota/agent/${userId}/${agent}`);
211
+ if (!res.ok) {
212
+ const text = await res.text().catch(() => "");
213
+ throw new Error(`agent quota failed with ${res.status}: ${text}`);
214
+ }
215
+ return res.json();
216
+ }
217
+ async getForwarderQuota(address) {
218
+ const res = await this.fetchImpl(`${this.baseUrl}/quota/forwarder/${address}`);
219
+ if (!res.ok) {
220
+ const text = await res.text().catch(() => "");
221
+ throw new Error(`forwarder quota failed with ${res.status}: ${text}`);
222
+ }
223
+ return res.json();
224
+ }
225
+ };
226
+ var DoneContractHandle = class {
227
+ constructor(backend, address) {
228
+ this.backend = backend;
229
+ this.address = address;
230
+ }
231
+ transaction(path, options = {}) {
232
+ const call = {
233
+ path,
234
+ query: options.query,
235
+ body: options.body
236
+ };
237
+ const msg = buildTransactionMessage(this.address, call, options);
238
+ const metadata = metadataFromOptions(options);
239
+ return { msg, metadata };
240
+ }
241
+ async query(path, options = {}) {
242
+ const call = {
243
+ path,
244
+ query: options.query,
245
+ body: options.body
246
+ };
247
+ return this.backend.queryContract(this.address, call);
248
+ }
249
+ publishCode(request, init = {}) {
250
+ return this.backend.publishCode(
251
+ {
252
+ contract: this.address,
253
+ script: request.script,
254
+ msg: request.msg
255
+ },
256
+ init
257
+ );
258
+ }
259
+ };
260
+ function metadataFromOptions(options) {
261
+ const metadata = {};
262
+ if (options.traceId) metadata.trace_id = options.traceId;
263
+ if (options.memo) metadata.memo = options.memo;
264
+ if (typeof options.gasLimit === "number") metadata.gas_limit = options.gasLimit;
265
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
266
+ }
267
+
268
+ // ../../node_modules/@noble/hashes/esm/_assert.js
269
+ function bytes(b, ...lengths) {
270
+ if (!(b instanceof Uint8Array))
271
+ throw new Error("Expected Uint8Array");
272
+ if (lengths.length > 0 && !lengths.includes(b.length))
273
+ throw new Error(`Expected Uint8Array of length ${lengths}, not of length=${b.length}`);
274
+ }
275
+ function exists(instance, checkFinished = true) {
276
+ if (instance.destroyed)
277
+ throw new Error("Hash instance has been destroyed");
278
+ if (checkFinished && instance.finished)
279
+ throw new Error("Hash#digest() has already been called");
280
+ }
281
+ function output(out, instance) {
282
+ bytes(out);
283
+ const min = instance.outputLen;
284
+ if (out.length < min) {
285
+ throw new Error(`digestInto() expects output buffer of length at least ${min}`);
286
+ }
287
+ }
288
+
289
+ // ../../node_modules/@noble/hashes/esm/utils.js
290
+ var u8a = (a) => a instanceof Uint8Array;
291
+ var createView = (arr) => new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
292
+ var rotr = (word, shift) => word << 32 - shift | word >>> shift;
293
+ var isLE = new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68;
294
+ if (!isLE)
295
+ throw new Error("Non little-endian hardware is not supported");
296
+ function utf8ToBytes(str) {
297
+ if (typeof str !== "string")
298
+ throw new Error(`utf8ToBytes expected string, got ${typeof str}`);
299
+ return new Uint8Array(new TextEncoder().encode(str));
300
+ }
301
+ function toBytes(data) {
302
+ if (typeof data === "string")
303
+ data = utf8ToBytes(data);
304
+ if (!u8a(data))
305
+ throw new Error(`expected Uint8Array, got ${typeof data}`);
306
+ return data;
307
+ }
308
+ var Hash = class {
309
+ // Safe version that clones internal state
310
+ clone() {
311
+ return this._cloneInto();
312
+ }
313
+ };
314
+ var toStr = {}.toString;
315
+ function wrapConstructor(hashCons) {
316
+ const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
317
+ const tmp = hashCons();
318
+ hashC.outputLen = tmp.outputLen;
319
+ hashC.blockLen = tmp.blockLen;
320
+ hashC.create = () => hashCons();
321
+ return hashC;
322
+ }
323
+
324
+ // ../../node_modules/@noble/hashes/esm/_sha2.js
325
+ function setBigUint64(view, byteOffset, value, isLE2) {
326
+ if (typeof view.setBigUint64 === "function")
327
+ return view.setBigUint64(byteOffset, value, isLE2);
328
+ const _32n = BigInt(32);
329
+ const _u32_max = BigInt(4294967295);
330
+ const wh = Number(value >> _32n & _u32_max);
331
+ const wl = Number(value & _u32_max);
332
+ const h = isLE2 ? 4 : 0;
333
+ const l = isLE2 ? 0 : 4;
334
+ view.setUint32(byteOffset + h, wh, isLE2);
335
+ view.setUint32(byteOffset + l, wl, isLE2);
336
+ }
337
+ var SHA2 = class extends Hash {
338
+ constructor(blockLen, outputLen, padOffset, isLE2) {
339
+ super();
340
+ this.blockLen = blockLen;
341
+ this.outputLen = outputLen;
342
+ this.padOffset = padOffset;
343
+ this.isLE = isLE2;
344
+ this.finished = false;
345
+ this.length = 0;
346
+ this.pos = 0;
347
+ this.destroyed = false;
348
+ this.buffer = new Uint8Array(blockLen);
349
+ this.view = createView(this.buffer);
350
+ }
351
+ update(data) {
352
+ exists(this);
353
+ const { view, buffer, blockLen } = this;
354
+ data = toBytes(data);
355
+ const len = data.length;
356
+ for (let pos = 0; pos < len; ) {
357
+ const take = Math.min(blockLen - this.pos, len - pos);
358
+ if (take === blockLen) {
359
+ const dataView = createView(data);
360
+ for (; blockLen <= len - pos; pos += blockLen)
361
+ this.process(dataView, pos);
362
+ continue;
363
+ }
364
+ buffer.set(data.subarray(pos, pos + take), this.pos);
365
+ this.pos += take;
366
+ pos += take;
367
+ if (this.pos === blockLen) {
368
+ this.process(view, 0);
369
+ this.pos = 0;
370
+ }
371
+ }
372
+ this.length += data.length;
373
+ this.roundClean();
374
+ return this;
375
+ }
376
+ digestInto(out) {
377
+ exists(this);
378
+ output(out, this);
379
+ this.finished = true;
380
+ const { buffer, view, blockLen, isLE: isLE2 } = this;
381
+ let { pos } = this;
382
+ buffer[pos++] = 128;
383
+ this.buffer.subarray(pos).fill(0);
384
+ if (this.padOffset > blockLen - pos) {
385
+ this.process(view, 0);
386
+ pos = 0;
387
+ }
388
+ for (let i = pos; i < blockLen; i++)
389
+ buffer[i] = 0;
390
+ setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE2);
391
+ this.process(view, 0);
392
+ const oview = createView(out);
393
+ const len = this.outputLen;
394
+ if (len % 4)
395
+ throw new Error("_sha2: outputLen should be aligned to 32bit");
396
+ const outLen = len / 4;
397
+ const state = this.get();
398
+ if (outLen > state.length)
399
+ throw new Error("_sha2: outputLen bigger than state");
400
+ for (let i = 0; i < outLen; i++)
401
+ oview.setUint32(4 * i, state[i], isLE2);
402
+ }
403
+ digest() {
404
+ const { buffer, outputLen } = this;
405
+ this.digestInto(buffer);
406
+ const res = buffer.slice(0, outputLen);
407
+ this.destroy();
408
+ return res;
409
+ }
410
+ _cloneInto(to) {
411
+ to || (to = new this.constructor());
412
+ to.set(...this.get());
413
+ const { blockLen, buffer, length, finished, destroyed, pos } = this;
414
+ to.length = length;
415
+ to.pos = pos;
416
+ to.finished = finished;
417
+ to.destroyed = destroyed;
418
+ if (length % blockLen)
419
+ to.buffer.set(buffer);
420
+ return to;
421
+ }
422
+ };
423
+
424
+ // ../../node_modules/@noble/hashes/esm/sha256.js
425
+ var Chi = (a, b, c) => a & b ^ ~a & c;
426
+ var Maj = (a, b, c) => a & b ^ a & c ^ b & c;
427
+ var SHA256_K = /* @__PURE__ */ new Uint32Array([
428
+ 1116352408,
429
+ 1899447441,
430
+ 3049323471,
431
+ 3921009573,
432
+ 961987163,
433
+ 1508970993,
434
+ 2453635748,
435
+ 2870763221,
436
+ 3624381080,
437
+ 310598401,
438
+ 607225278,
439
+ 1426881987,
440
+ 1925078388,
441
+ 2162078206,
442
+ 2614888103,
443
+ 3248222580,
444
+ 3835390401,
445
+ 4022224774,
446
+ 264347078,
447
+ 604807628,
448
+ 770255983,
449
+ 1249150122,
450
+ 1555081692,
451
+ 1996064986,
452
+ 2554220882,
453
+ 2821834349,
454
+ 2952996808,
455
+ 3210313671,
456
+ 3336571891,
457
+ 3584528711,
458
+ 113926993,
459
+ 338241895,
460
+ 666307205,
461
+ 773529912,
462
+ 1294757372,
463
+ 1396182291,
464
+ 1695183700,
465
+ 1986661051,
466
+ 2177026350,
467
+ 2456956037,
468
+ 2730485921,
469
+ 2820302411,
470
+ 3259730800,
471
+ 3345764771,
472
+ 3516065817,
473
+ 3600352804,
474
+ 4094571909,
475
+ 275423344,
476
+ 430227734,
477
+ 506948616,
478
+ 659060556,
479
+ 883997877,
480
+ 958139571,
481
+ 1322822218,
482
+ 1537002063,
483
+ 1747873779,
484
+ 1955562222,
485
+ 2024104815,
486
+ 2227730452,
487
+ 2361852424,
488
+ 2428436474,
489
+ 2756734187,
490
+ 3204031479,
491
+ 3329325298
492
+ ]);
493
+ var IV = /* @__PURE__ */ new Uint32Array([
494
+ 1779033703,
495
+ 3144134277,
496
+ 1013904242,
497
+ 2773480762,
498
+ 1359893119,
499
+ 2600822924,
500
+ 528734635,
501
+ 1541459225
502
+ ]);
503
+ var SHA256_W = /* @__PURE__ */ new Uint32Array(64);
504
+ var SHA256 = class extends SHA2 {
505
+ constructor() {
506
+ super(64, 32, 8, false);
507
+ this.A = IV[0] | 0;
508
+ this.B = IV[1] | 0;
509
+ this.C = IV[2] | 0;
510
+ this.D = IV[3] | 0;
511
+ this.E = IV[4] | 0;
512
+ this.F = IV[5] | 0;
513
+ this.G = IV[6] | 0;
514
+ this.H = IV[7] | 0;
515
+ }
516
+ get() {
517
+ const { A, B, C, D, E, F, G, H } = this;
518
+ return [A, B, C, D, E, F, G, H];
519
+ }
520
+ // prettier-ignore
521
+ set(A, B, C, D, E, F, G, H) {
522
+ this.A = A | 0;
523
+ this.B = B | 0;
524
+ this.C = C | 0;
525
+ this.D = D | 0;
526
+ this.E = E | 0;
527
+ this.F = F | 0;
528
+ this.G = G | 0;
529
+ this.H = H | 0;
530
+ }
531
+ process(view, offset) {
532
+ for (let i = 0; i < 16; i++, offset += 4)
533
+ SHA256_W[i] = view.getUint32(offset, false);
534
+ for (let i = 16; i < 64; i++) {
535
+ const W15 = SHA256_W[i - 15];
536
+ const W2 = SHA256_W[i - 2];
537
+ const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ W15 >>> 3;
538
+ const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ W2 >>> 10;
539
+ SHA256_W[i] = s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16] | 0;
540
+ }
541
+ let { A, B, C, D, E, F, G, H } = this;
542
+ for (let i = 0; i < 64; i++) {
543
+ const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
544
+ const T1 = H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i] | 0;
545
+ const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
546
+ const T2 = sigma0 + Maj(A, B, C) | 0;
547
+ H = G;
548
+ G = F;
549
+ F = E;
550
+ E = D + T1 | 0;
551
+ D = C;
552
+ C = B;
553
+ B = A;
554
+ A = T1 + T2 | 0;
555
+ }
556
+ A = A + this.A | 0;
557
+ B = B + this.B | 0;
558
+ C = C + this.C | 0;
559
+ D = D + this.D | 0;
560
+ E = E + this.E | 0;
561
+ F = F + this.F | 0;
562
+ G = G + this.G | 0;
563
+ H = H + this.H | 0;
564
+ this.set(A, B, C, D, E, F, G, H);
565
+ }
566
+ roundClean() {
567
+ SHA256_W.fill(0);
568
+ }
569
+ destroy() {
570
+ this.set(0, 0, 0, 0, 0, 0, 0, 0);
571
+ this.buffer.fill(0);
572
+ }
573
+ };
574
+ var sha256 = /* @__PURE__ */ wrapConstructor(() => new SHA256());
575
+
576
+ // src/envelope-crypto.ts
577
+ function signDocDigest(signDoc) {
578
+ const bytes2 = signDocBytes(signDoc);
579
+ return sha256(bytes2);
580
+ }
581
+ function encodeSignature(signature) {
582
+ const nodeBuffer = globalThis.Buffer;
583
+ if (nodeBuffer) {
584
+ return nodeBuffer.from(signature).toString("base64");
585
+ }
586
+ let result = "";
587
+ for (const byte of signature) {
588
+ result += String.fromCharCode(byte);
589
+ }
590
+ return btoa(result);
591
+ }
592
+ function decodeSignature(signature) {
593
+ const nodeBuffer = globalThis.Buffer;
594
+ if (nodeBuffer) {
595
+ return nodeBuffer.from(signature, "base64");
596
+ }
597
+ const binary = atob(signature);
598
+ const bytes2 = new Uint8Array(binary.length);
599
+ for (let i = 0; i < binary.length; i++) {
600
+ bytes2[i] = binary.charCodeAt(i);
601
+ }
602
+ return bytes2;
603
+ }
604
+
605
+ // src/done.ts
606
+ var DoneContractClient = class {
607
+ backend;
608
+ address;
609
+ buildEnvelopeFn;
610
+ eventSourceFactory;
611
+ eventsPath;
612
+ defaultMetadata;
613
+ constructor(config) {
614
+ this.backend = new DoneBackendClient({ baseUrl: config.baseUrl, fetch: config.fetch });
615
+ this.address = config.address;
616
+ this.buildEnvelopeFn = config.buildEnvelope;
617
+ this.eventSourceFactory = config.eventSource;
618
+ this.eventsPath = config.eventsPath ?? "/events";
619
+ this.defaultMetadata = config.defaultMetadata;
620
+ }
621
+ get(path, request = {}, init = {}) {
622
+ const promise = this.fetchQuery(path, request, init);
623
+ return new DoneRequest(promise);
624
+ }
625
+ async query(path, request = {}, init = {}) {
626
+ const response = await this.fetchQuery(path, request, init);
627
+ return response.json();
628
+ }
629
+ async post(path, request = {}, init = {}) {
630
+ return this.execute(path, request, init);
631
+ }
632
+ async run(path, request = {}, init = {}) {
633
+ return this.execute(path, request, init);
634
+ }
635
+ async execute(path, request = {}, init = {}) {
636
+ if (!this.buildEnvelopeFn) {
637
+ throw new Error("execute requires a buildEnvelope function in contract config");
638
+ }
639
+ const normalizedCall = {
640
+ path,
641
+ query: request.query,
642
+ body: request.body
643
+ };
644
+ const transactionMsg = buildTransactionMessage(
645
+ this.address,
646
+ normalizedCall,
647
+ request
648
+ );
649
+ const metadata = mergeMetadata(this.defaultMetadata, request.metadata, { trace_id: request.traceId, memo: request.memo, gas_limit: request.gasLimit });
650
+ const buildContext = {
651
+ msg: transactionMsg,
652
+ metadata,
653
+ path,
654
+ request
655
+ };
656
+ const buildResult = await this.buildEnvelopeFn(buildContext);
657
+ if (metadata) {
658
+ buildResult.envelope.metadata = buildResult.envelope.metadata ?? metadata;
659
+ }
660
+ const executeOptions = mergeExecuteOptions(buildResult.options, {
661
+ memo: request.memo,
662
+ passkey: request.passkey,
663
+ signal: request.signal
664
+ });
665
+ return this.backend.executeEnvelope(buildResult.envelope, executeOptions, init);
666
+ }
667
+ transaction(path, request = {}) {
668
+ const normalizedCall = {
669
+ path,
670
+ query: request.query,
671
+ body: request.body
672
+ };
673
+ const msg = buildTransactionMessage(this.address, normalizedCall, request);
674
+ const metadata = mergeMetadata(this.defaultMetadata, request.metadata, {
675
+ trace_id: request.traceId,
676
+ memo: request.memo,
677
+ gas_limit: request.gasLimit
678
+ });
679
+ return { msg, metadata };
680
+ }
681
+ subscribe(topic, handler, options = {}) {
682
+ const factory = this.eventSourceFactory ?? defaultEventSourceFactory;
683
+ const eventsBase = buildEventsUrl(this.backend.baseUrl, this.eventsPath);
684
+ const encodedTopic = Array.isArray(topic) ? JSON.stringify(topic) : topic;
685
+ const url = `${eventsBase}?topic=${encodeURIComponent(encodedTopic)}`;
686
+ const source = factory(url);
687
+ const messageListener = (event) => {
688
+ if (options.rawEvent) {
689
+ handler(event);
690
+ return;
691
+ }
692
+ const data = event.data;
693
+ try {
694
+ handler(JSON.parse(data));
695
+ } catch {
696
+ handler(data);
697
+ }
698
+ };
699
+ if (typeof source.addEventListener === "function") {
700
+ source.addEventListener("message", messageListener);
701
+ } else {
702
+ source.onmessage = messageListener;
703
+ }
704
+ if (options.onError) {
705
+ if (typeof source.addEventListener === "function") {
706
+ source.addEventListener("error", options.onError);
707
+ } else {
708
+ source.onerror = options.onError;
709
+ }
710
+ }
711
+ const cleanup = () => {
712
+ if (typeof source.removeEventListener === "function") {
713
+ source.removeEventListener("message", messageListener);
714
+ if (options.onError) {
715
+ source.removeEventListener("error", options.onError);
716
+ }
717
+ }
718
+ source.close();
719
+ };
720
+ if (options.signal) {
721
+ if (options.signal.aborted) {
722
+ cleanup();
723
+ } else {
724
+ options.signal.addEventListener("abort", cleanup, { once: true });
725
+ }
726
+ }
727
+ return cleanup;
728
+ }
729
+ publishCode(request, init = {}) {
730
+ return this.backend.publishCode(
731
+ {
732
+ contract: this.address,
733
+ script: request.script,
734
+ msg: request.msg
735
+ },
736
+ init
737
+ );
738
+ }
739
+ buildEnvelope(draft) {
740
+ return buildEnvelope(draft);
741
+ }
742
+ signDocDigest(envelope) {
743
+ return signDocDigest(toSignDoc(envelope));
744
+ }
745
+ get baseUrl() {
746
+ return this.backend.baseUrl;
747
+ }
748
+ async fetchQuery(path, request, init = {}) {
749
+ const call = { path, query: request.query, body: request.body };
750
+ return this.backend.queryContractRaw(this.address, call, init);
751
+ }
752
+ };
753
+ var DoneRequest = class {
754
+ constructor(promise) {
755
+ this.promise = promise;
756
+ }
757
+ then(onfulfilled, onrejected) {
758
+ return this.promise.then(onfulfilled, onrejected);
759
+ }
760
+ catch(onrejected) {
761
+ return this.promise.catch(onrejected);
762
+ }
763
+ finally(onfinally) {
764
+ return this.promise.finally(onfinally);
765
+ }
766
+ json() {
767
+ return this.promise.then((response) => response.json());
768
+ }
769
+ text() {
770
+ return this.promise.then((response) => response.text());
771
+ }
772
+ arrayBuffer() {
773
+ return this.promise.then((response) => response.arrayBuffer());
774
+ }
775
+ };
776
+ function mergeMetadata(...entries) {
777
+ const merged = {};
778
+ for (const entry of entries) {
779
+ if (!entry) continue;
780
+ if (entry.trace_id) merged.trace_id = entry.trace_id;
781
+ if (entry.memo) merged.memo = entry.memo;
782
+ if (typeof entry.gas_limit === "number") merged.gas_limit = entry.gas_limit;
783
+ }
784
+ return Object.keys(merged).length > 0 ? merged : void 0;
785
+ }
786
+ function mergeExecuteOptions(base, overrides) {
787
+ if (!base && !overrides.memo && !overrides.passkey && !overrides.signal) {
788
+ return void 0;
789
+ }
790
+ return {
791
+ memo: overrides.memo ?? base?.memo,
792
+ passkey: overrides.passkey ?? base?.passkey,
793
+ signal: overrides.signal ?? base?.signal
794
+ };
795
+ }
796
+ function defaultEventSourceFactory(url) {
797
+ if (typeof EventSource === "undefined") {
798
+ throw new Error("EventSource is not available in this environment");
799
+ }
800
+ return new EventSource(url);
801
+ }
802
+ function buildEventsUrl(baseUrl, eventsPath) {
803
+ const normalized = eventsPath.startsWith("/") ? eventsPath : `/${eventsPath}`;
804
+ return `${baseUrl.replace(/\/$/, "")}${normalized}`;
805
+ }
806
+ function resolveFetch(fn) {
807
+ if (fn) {
808
+ return typeof fn === "function" ? fn : (() => {
809
+ throw new Error("provided fetch implementation is not a function");
810
+ })();
811
+ }
812
+ if (typeof fetch === "function") {
813
+ return fetch.bind(globalThis);
814
+ }
815
+ throw new Error("global fetch is not available; configure Done with a fetch implementation via Done.config");
816
+ }
817
+ function resolveEventSource(factory) {
818
+ if (factory) return factory;
819
+ if (typeof EventSource === "function") {
820
+ return (url) => new EventSource(url);
821
+ }
822
+ return void 0;
823
+ }
824
+ function normalizeConfig(input = {}, base) {
825
+ const previous = base ?? {
826
+ doneHttp: "https://doneHttp.done.zone",
827
+ doneEvents: "https://doneEvents.done.zone",
828
+ fetch: resolveFetch(),
829
+ eventSource: resolveEventSource(),
830
+ envelopeBuilder: void 0
831
+ };
832
+ return {
833
+ doneHttp: input.doneHttp ?? previous.doneHttp,
834
+ doneEvents: input.doneEvents ?? previous.doneEvents,
835
+ fetch: resolveFetch(input.fetch ?? previous.fetch),
836
+ eventSource: resolveEventSource(input.eventSource ?? previous.eventSource),
837
+ envelopeBuilder: input.envelopeBuilder ?? input.signer ?? previous.envelopeBuilder
838
+ };
839
+ }
840
+ function resolveRoute(raw, base, extraSearch) {
841
+ const url = new URL(raw, base);
842
+ if (extraSearch) {
843
+ for (const [key, value] of Object.entries(extraSearch)) {
844
+ url.searchParams.set(key, String(value));
845
+ }
846
+ }
847
+ const pathSegments = url.pathname.replace(/^\/+/, "").split("/");
848
+ const contract = pathSegments.shift();
849
+ if (!contract) {
850
+ throw new Error("route must include contract address as first path segment");
851
+ }
852
+ const path = pathSegments.length > 0 ? `/${pathSegments.join("/")}` : "/";
853
+ const queryRecord = searchParamsToRecord(url.searchParams);
854
+ return {
855
+ url,
856
+ contract,
857
+ path,
858
+ query: Object.keys(queryRecord).length > 0 ? queryRecord : void 0
859
+ };
860
+ }
861
+ function searchParamsToRecord(params) {
862
+ const record = {};
863
+ for (const [key, value] of params) {
864
+ const existing = record[key];
865
+ if (existing === void 0) {
866
+ record[key] = value;
867
+ } else if (Array.isArray(existing)) {
868
+ existing.push(value);
869
+ } else {
870
+ record[key] = [existing, value];
871
+ }
872
+ }
873
+ return record;
874
+ }
875
+ function normalizeContractBody(body) {
876
+ if (body === void 0 || body === null) {
877
+ return body;
878
+ }
879
+ if (isPlainObject(body) || Array.isArray(body)) {
880
+ return body;
881
+ }
882
+ if (typeof body === "string") {
883
+ try {
884
+ return JSON.parse(body);
885
+ } catch (err) {
886
+ throw new Error("string bodies must contain JSON payloads when invoking Done.run/query");
887
+ }
888
+ }
889
+ if (body instanceof URLSearchParams) {
890
+ return Object.fromEntries(Array.from(body));
891
+ }
892
+ throw new Error("unsupported body type for Done.run/query; provide a JSON-serialisable value");
893
+ }
894
+ function isPlainObject(value) {
895
+ if (value === null || typeof value !== "object") return false;
896
+ const proto = Object.getPrototypeOf(value);
897
+ return proto === Object.prototype || proto === null;
898
+ }
899
+ function normalizeContractSegment(value) {
900
+ if (!value) {
901
+ throw new Error("contract identifier cannot be empty");
902
+ }
903
+ return value.replace(/^\/+/u, "").replace(/\/+$/u, "");
904
+ }
905
+ var DoneInstance = class _DoneInstance {
906
+ settings;
907
+ backend;
908
+ CURRENT_ENVELOPE_VERSION = CURRENT_ENVELOPE_VERSION;
909
+ buildEnvelope = buildEnvelope;
910
+ signDocDigest = signDocDigest;
911
+ toSignDoc = toSignDoc;
912
+ constructor(input) {
913
+ this.settings = normalizeConfig(input);
914
+ this.backend = new DoneBackendClient({ baseUrl: this.settings.doneHttp, fetch: this.settings.fetch });
915
+ }
916
+ async run(url, init = {}) {
917
+ const {
918
+ body: rawBody,
919
+ funds,
920
+ memo,
921
+ traceId,
922
+ gasLimit,
923
+ passkey,
924
+ metadata,
925
+ search,
926
+ ...rest
927
+ } = init;
928
+ const fetchInit = { ...rest };
929
+ const resolved = resolveRoute(url, this.settings.doneHttp, search);
930
+ const contractBody = normalizeContractBody(rawBody);
931
+ const call = {
932
+ path: resolved.path,
933
+ query: resolved.query,
934
+ body: contractBody
935
+ };
936
+ const msg = buildTransactionMessage(resolved.contract, call, { funds });
937
+ const mergedMetadata = mergeMetadata(metadata, {
938
+ trace_id: traceId,
939
+ memo,
940
+ gas_limit: gasLimit
941
+ });
942
+ const builder = this.requireEnvelopeBuilder();
943
+ const request = {
944
+ body: contractBody,
945
+ query: resolved.query,
946
+ funds,
947
+ memo,
948
+ traceId,
949
+ gasLimit,
950
+ metadata: mergedMetadata,
951
+ passkey,
952
+ signal: fetchInit.signal
953
+ };
954
+ const result = await Promise.resolve(
955
+ builder({
956
+ msg,
957
+ metadata: mergedMetadata,
958
+ path: resolved.path,
959
+ request
960
+ })
961
+ );
962
+ if (mergedMetadata) {
963
+ result.envelope.metadata = result.envelope.metadata ?? mergedMetadata;
964
+ }
965
+ const executeOptions = mergeExecuteOptions(result.options, {
966
+ memo,
967
+ passkey,
968
+ signal: fetchInit.signal
969
+ });
970
+ return this.backend.executeEnvelope(result.envelope, executeOptions ?? {}, fetchInit);
971
+ }
972
+ async query(url, init = {}) {
973
+ const { body: rawBody, search, ...rest } = init;
974
+ const fetchInit = { ...rest };
975
+ const resolved = resolveRoute(url, this.settings.doneHttp, search);
976
+ const contractBody = normalizeContractBody(rawBody);
977
+ const call = {
978
+ path: resolved.path,
979
+ query: resolved.query,
980
+ body: contractBody
981
+ };
982
+ return this.backend.queryContract(resolved.contract, call, fetchInit);
983
+ }
984
+ subscribe(eventsUrl, topic, handler, options = {}) {
985
+ const factory = this.settings.eventSource ?? defaultEventSourceFactory;
986
+ if (!factory) {
987
+ throw new Error("EventSource is not available; configure Done with an eventSource factory");
988
+ }
989
+ const url = new URL(eventsUrl, this.settings.doneEvents);
990
+ const topicValue = Array.isArray(topic) ? JSON.stringify(topic) : topic;
991
+ url.searchParams.set("topic", topicValue);
992
+ const source = factory(url.toString());
993
+ const messageListener = (event) => {
994
+ if (options.rawEvent) {
995
+ handler(event);
996
+ return;
997
+ }
998
+ try {
999
+ handler(JSON.parse(event.data));
1000
+ } catch {
1001
+ handler(event.data);
1002
+ }
1003
+ };
1004
+ if (typeof source.addEventListener === "function") {
1005
+ source.addEventListener("message", messageListener);
1006
+ } else {
1007
+ source.onmessage = messageListener;
1008
+ }
1009
+ if (options.onError) {
1010
+ if (typeof source.addEventListener === "function") {
1011
+ source.addEventListener("error", options.onError);
1012
+ } else {
1013
+ source.onerror = options.onError;
1014
+ }
1015
+ }
1016
+ const cleanup = () => {
1017
+ if (typeof source.removeEventListener === "function") {
1018
+ source.removeEventListener("message", messageListener);
1019
+ if (options.onError) {
1020
+ source.removeEventListener("error", options.onError);
1021
+ }
1022
+ }
1023
+ source.close();
1024
+ };
1025
+ if (options.signal) {
1026
+ if (options.signal.aborted) {
1027
+ cleanup();
1028
+ } else {
1029
+ options.signal.addEventListener("abort", cleanup, { once: true });
1030
+ }
1031
+ }
1032
+ return cleanup;
1033
+ }
1034
+ config(update) {
1035
+ this.settings = normalizeConfig(update, this.settings);
1036
+ this.backend = new DoneBackendClient({ baseUrl: this.settings.doneHttp, fetch: this.settings.fetch });
1037
+ }
1038
+ create(update = {}) {
1039
+ const merged = normalizeConfig(update, this.settings);
1040
+ return new _DoneInstance({
1041
+ doneHttp: merged.doneHttp,
1042
+ doneEvents: merged.doneEvents,
1043
+ fetch: merged.fetch,
1044
+ eventSource: merged.eventSource,
1045
+ envelopeBuilder: merged.envelopeBuilder
1046
+ });
1047
+ }
1048
+ contract(config, maybeConfig) {
1049
+ if (typeof config === "string") {
1050
+ if (!maybeConfig) {
1051
+ return this.buildContractHandle(config);
1052
+ }
1053
+ return new DoneContractClient({
1054
+ baseUrl: config,
1055
+ ...maybeConfig,
1056
+ buildEnvelope: maybeConfig.buildEnvelope ?? this.settings.envelopeBuilder,
1057
+ fetch: maybeConfig.fetch ?? this.settings.fetch,
1058
+ eventSource: maybeConfig.eventSource ?? this.settings.eventSource
1059
+ });
1060
+ }
1061
+ return new DoneContractClient({
1062
+ ...config,
1063
+ baseUrl: config.baseUrl,
1064
+ buildEnvelope: config.buildEnvelope ?? this.settings.envelopeBuilder,
1065
+ fetch: config.fetch ?? this.settings.fetch,
1066
+ eventSource: config.eventSource ?? this.settings.eventSource
1067
+ });
1068
+ }
1069
+ requireEnvelopeBuilder() {
1070
+ if (!this.settings.envelopeBuilder) {
1071
+ throw new Error("Done.run requires an envelope builder; configure Done.config({ signer: ... }) first");
1072
+ }
1073
+ return this.settings.envelopeBuilder;
1074
+ }
1075
+ buildContractHandle(contract) {
1076
+ const address = normalizeContractSegment(contract);
1077
+ const client = new DoneContractClient({
1078
+ baseUrl: this.settings.doneHttp,
1079
+ address,
1080
+ buildEnvelope: this.settings.envelopeBuilder,
1081
+ fetch: this.settings.fetch,
1082
+ eventSource: this.settings.eventSource
1083
+ });
1084
+ return client;
1085
+ }
1086
+ };
1087
+ var Done = new DoneInstance();
1088
+
1089
+ // src/auth.ts
1090
+ function createPasskeyEnvelopeBuilder(options) {
1091
+ return async (ctx) => {
1092
+ const [userId, nonce, expiresAt, publicKey, agent, forwarder, metadata] = await Promise.all([
1093
+ resolve(options.userId, ctx),
1094
+ resolve(options.nonce, ctx),
1095
+ resolve(options.expiresAt, ctx),
1096
+ resolve(options.publicKey, ctx),
1097
+ resolve(options.agent, ctx),
1098
+ resolve(options.forwarder, ctx),
1099
+ resolve(options.metadata, ctx)
1100
+ ]);
1101
+ assertDefined(userId, "userId");
1102
+ assertDefined(nonce, "nonce");
1103
+ assertDefined(expiresAt, "expiresAt");
1104
+ assertDefined(publicKey, "publicKey");
1105
+ const mergedMetadata = combineMetadata(ctx.metadata, metadata);
1106
+ const draft = {
1107
+ user_id: userId,
1108
+ msgs: [ctx.msg],
1109
+ nonce,
1110
+ expires_at: expiresAt,
1111
+ role: options.role ?? "Passkey",
1112
+ agent: agent ?? void 0,
1113
+ forwarder: forwarder ?? void 0,
1114
+ metadata: mergedMetadata,
1115
+ signatures: {}
1116
+ };
1117
+ const signDoc = toSignDoc(draft);
1118
+ const signature = await options.sign(signDoc, ctx);
1119
+ draft.signatures = {
1120
+ passkey: normalizeSignature(signature)
1121
+ };
1122
+ const envelope = buildEnvelope(draft);
1123
+ envelope.metadata = envelope.metadata ?? mergedMetadata;
1124
+ const passkeyOption = { publicKey };
1125
+ const executeOptions = { passkey: passkeyOption };
1126
+ return makeResult(envelope, executeOptions);
1127
+ };
1128
+ }
1129
+ function createSessionEnvelopeBuilder(options) {
1130
+ return async (ctx) => {
1131
+ const [userId, sessionId, nonce, expiresAt, agent, forwarder, metadata] = await Promise.all([
1132
+ resolve(options.userId, ctx),
1133
+ resolve(options.sessionId, ctx),
1134
+ resolve(options.nonce, ctx),
1135
+ resolve(options.expiresAt, ctx),
1136
+ resolve(options.agent, ctx),
1137
+ resolve(options.forwarder, ctx),
1138
+ resolve(options.metadata, ctx)
1139
+ ]);
1140
+ assertDefined(userId, "userId");
1141
+ assertDefined(sessionId, "sessionId");
1142
+ assertDefined(nonce, "nonce");
1143
+ assertDefined(expiresAt, "expiresAt");
1144
+ const mergedMetadata = combineMetadata(ctx.metadata, metadata);
1145
+ const draft = {
1146
+ user_id: userId,
1147
+ session_id: sessionId,
1148
+ msgs: [ctx.msg],
1149
+ nonce,
1150
+ expires_at: expiresAt,
1151
+ role: options.role ?? "Session",
1152
+ agent: agent ?? void 0,
1153
+ forwarder: forwarder ?? void 0,
1154
+ metadata: mergedMetadata,
1155
+ signatures: {}
1156
+ };
1157
+ const signDoc = toSignDoc(draft);
1158
+ const signature = await options.sign(signDoc, ctx);
1159
+ draft.signatures = {
1160
+ session: normalizeSignature(signature)
1161
+ };
1162
+ const envelope = buildEnvelope(draft);
1163
+ envelope.metadata = envelope.metadata ?? mergedMetadata;
1164
+ return makeResult(envelope, void 0);
1165
+ };
1166
+ }
1167
+ function normalizeSignature(signature) {
1168
+ if (typeof signature === "string") {
1169
+ return signature;
1170
+ }
1171
+ return encodeSignature(signature);
1172
+ }
1173
+ function combineMetadata(base, override) {
1174
+ const merged = {};
1175
+ for (const entry of [base, override]) {
1176
+ if (!entry) continue;
1177
+ if (entry.trace_id) merged.trace_id = entry.trace_id;
1178
+ if (entry.memo) merged.memo = entry.memo;
1179
+ if (typeof entry.gas_limit === "number") merged.gas_limit = entry.gas_limit;
1180
+ }
1181
+ return Object.keys(merged).length > 0 ? merged : void 0;
1182
+ }
1183
+ function makeResult(envelope, options) {
1184
+ return options ? { envelope, options } : { envelope };
1185
+ }
1186
+ function resolve(value, ctx) {
1187
+ if (typeof value === "function") {
1188
+ return Promise.resolve(value(ctx));
1189
+ }
1190
+ return Promise.resolve(value);
1191
+ }
1192
+ function assertDefined(value, name) {
1193
+ if (value === void 0 || value === null) {
1194
+ throw new Error(`${name} must be provided for envelope builder`);
1195
+ }
1196
+ }
1197
+ export {
1198
+ CURRENT_ENVELOPE_VERSION,
1199
+ Done,
1200
+ DoneBackendClient,
1201
+ DoneContractClient,
1202
+ DoneRequest,
1203
+ buildEnvelope,
1204
+ buildQueryMessage,
1205
+ buildTransactionMessage,
1206
+ createDevEnvelopeBuilder,
1207
+ createPasskeyEnvelopeBuilder,
1208
+ createSessionEnvelopeBuilder,
1209
+ decodeSignature,
1210
+ encodeSignature,
1211
+ signDocBytes,
1212
+ signDocDigest,
1213
+ toSignDoc
1214
+ };
1215
+ /*! Bundled license information:
1216
+
1217
+ @noble/hashes/esm/utils.js:
1218
+ (*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
1219
+ */