@dvmkit/sdk 0.4.0-rc.1 → 0.6.0-rc.1

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.
Files changed (43) hide show
  1. package/README.md +100 -0
  2. package/dist/{chunk-TXX4GOUB.js → chunk-7ZUKL4IV.js} +1 -1
  3. package/dist/chunk-AFV77S4C.js +258 -0
  4. package/dist/{chunk-2VGWYG4P.js → chunk-B2IROPQF.js} +446 -81
  5. package/dist/chunk-BQJIFRAZ.js +36 -0
  6. package/dist/{chunk-PB4FYZET.js → chunk-EQLA4FFY.js} +54 -4
  7. package/dist/{chunk-74PUEZJ4.js → chunk-GFY5FRX3.js} +1 -1
  8. package/dist/{chunk-O3QDFPKM.js → chunk-HCLVYZUZ.js} +2 -2
  9. package/dist/{chunk-K2YX2VRA.js → chunk-HHBDBMEO.js} +170 -43
  10. package/dist/{chunk-O5AT6S5T.js → chunk-IV6Q7NRW.js} +25 -195
  11. package/dist/{chunk-W2EQGDF5.js → chunk-LE74UEV7.js} +49 -8
  12. package/dist/{chunk-CE7XEHYC.js → chunk-N5RZ5HVB.js} +1 -1
  13. package/dist/chunk-OFVV37XC.js +57 -0
  14. package/dist/{chunk-5LR6P7WN.js → chunk-OQA7IHI4.js} +133 -213
  15. package/dist/{chunk-3MHE725Z.js → chunk-PAIM66YA.js} +90 -1
  16. package/dist/{chunk-6DOGTAHA.js → chunk-QR2EF2NI.js} +9 -43
  17. package/dist/{chunk-KXZUCCEY.js → chunk-RCM3P2HE.js} +5 -56
  18. package/dist/{chunk-R4DQZFAL.js → chunk-UOCZXO2Z.js} +3 -3
  19. package/dist/content-retention-BTGSQRfP.d.ts +78 -0
  20. package/dist/{credit-menu-B8_p_vUz.d.ts → credit-menu-BAsrRu3z.d.ts} +98 -5
  21. package/dist/{fx-DuaYHvVn.d.ts → fx-BgDmyTdd.d.ts} +1 -1
  22. package/dist/index.d.ts +5 -5
  23. package/dist/index.js +4 -3
  24. package/dist/internal/caller.d.ts +90 -22
  25. package/dist/internal/caller.js +40 -40
  26. package/dist/internal/index.js +1 -1
  27. package/dist/internal/server.d.ts +8 -99
  28. package/dist/internal/server.js +15 -11
  29. package/dist/{job-store-D6Rjj5Oi.d.ts → job-store-BVeF5_Jq.d.ts} +1 -1
  30. package/dist/{lightning-backend-BaeCPmx7.d.ts → lightning-backend-40fIzz1W.d.ts} +1 -1
  31. package/dist/{postgres-consumed-credential-store-CUQJKRSL.js → postgres-consumed-credential-store-BUVCAPLS.js} +13 -0
  32. package/dist/{postgres-job-store-VLXLWXUQ.js → postgres-job-store-VHLQBJZC.js} +3 -1
  33. package/dist/server/index.d.ts +28 -10
  34. package/dist/server/index.js +24 -10
  35. package/dist/{step-cache-Bi43A0JN.d.ts → step-cache-CPdH1hei.d.ts} +3 -3
  36. package/dist/{tempo-lifecycle-OTHV757Y.js → tempo-lifecycle-ZBBLFBMH.js} +5 -4
  37. package/dist/{tempo-wallet-32JWQZKQ.js → tempo-wallet-B5XXV3YH.js} +1 -1
  38. package/dist/testing/index.d.ts +9 -2
  39. package/dist/testing/index.js +6 -3
  40. package/dist/{usd-AZPVq4eU.d.ts → usd-AWlrwlM5.d.ts} +1 -1
  41. package/dist/{x402-CEDHGZDI.js → x402-R6HPBY5D.js} +5 -4
  42. package/package.json +3 -2
  43. package/recipes/postgres/financial-export-v1.sql +137 -0
@@ -1,191 +1,15 @@
1
1
  import {
2
- StepCache,
2
+ ContentHoldError,
3
+ applyContentHoldMutation,
4
+ isTerminal
5
+ } from "./chunk-AFV77S4C.js";
6
+ import {
3
7
  loggers
4
- } from "./chunk-6DOGTAHA.js";
8
+ } from "./chunk-QR2EF2NI.js";
5
9
  import {
6
10
  redactUrl
7
11
  } from "./chunk-FUJ36YDV.js";
8
12
 
9
- // src/sdk/server/job.ts
10
- function toJobRecord(job) {
11
- return {
12
- id: job.id,
13
- tags: job.tags,
14
- capability: job.capability,
15
- input: job.input,
16
- params: job.params,
17
- requesterId: job.requesterId,
18
- requesterTokenHash: job.requesterTokenHash,
19
- requesterToken: job.requesterToken,
20
- requestFingerprint: job.requestFingerprint,
21
- requesterPubkey: job.requesterPubkey,
22
- requestId: job.requestId,
23
- authRequestPath: job.authRequestPath,
24
- status: job.status,
25
- summary: job.summary,
26
- timingKnown: job.timingKnown,
27
- endedBy: job.endedBy,
28
- terminalAtMs: job.terminalAtMs,
29
- callerWaitMs: job.callerWaitMs,
30
- awaitingInputAtMs: job.awaitingInputAtMs,
31
- messages: job.messages,
32
- seq: job.seq,
33
- paidMsats: job.paidMsats,
34
- paymentMint: job.paymentMint,
35
- paymentRail: job.paymentRail,
36
- paymentTxHash: job.paymentTxHash,
37
- paymentTransactionHash: job.paymentTransactionHash,
38
- nativeAmount: job.nativeAmount,
39
- nativeAsset: job.nativeAsset,
40
- cashuFlow: job.cashuFlow,
41
- costAmountMicro: job.costAmountMicro,
42
- costCurrency: job.costCurrency,
43
- costRevision: job.costRevision,
44
- creditId: job.creditId,
45
- drawId: job.drawId,
46
- fundingReceipt: job.fundingReceipt,
47
- fundingCredit: job.fundingCredit,
48
- receivedProofs: job.receivedProofs,
49
- pendingPaymentMsats: job.pendingPaymentMsats,
50
- pendingMppChallengeIds: job.pendingMppChallengeIds,
51
- pendingX402Nonce: job.pendingX402Nonce,
52
- pendingX402AmountUsdcMicro: job.pendingX402AmountUsdcMicro,
53
- pendingPaymentFiatMicro: job.pendingPaymentFiatMicro,
54
- pendingPaymentFiatCurrency: job.pendingPaymentFiatCurrency,
55
- askedTopUpMicro: job.askedTopUpMicro,
56
- askedTopUpCurrency: job.askedTopUpCurrency,
57
- topUpCapUnenforcedReason: job.topUpCapUnenforcedReason,
58
- requiredMsats: job.requiredMsats,
59
- receipt: job.receipt,
60
- stepCache: job.stepCache.serialize(),
61
- state: job.state,
62
- createdAt: job.createdAt,
63
- lastActivityAt: job.lastActivityAt
64
- };
65
- }
66
- function fromJobRecord(record) {
67
- return {
68
- id: record.id,
69
- tags: record.tags,
70
- capability: record.capability,
71
- input: record.input,
72
- params: record.params,
73
- requesterId: record.requesterId,
74
- requesterTokenHash: record.requesterTokenHash,
75
- status: record.status,
76
- summary: record.summary,
77
- timingKnown: record.timingKnown,
78
- endedBy: record.endedBy,
79
- terminalAtMs: record.terminalAtMs,
80
- callerWaitMs: record.callerWaitMs,
81
- awaitingInputAtMs: record.awaitingInputAtMs,
82
- messages: [...record.messages],
83
- seq: record.seq,
84
- paidMsats: record.paidMsats,
85
- paymentMint: record.paymentMint,
86
- paymentRail: record.paymentRail,
87
- paymentTxHash: record.paymentTxHash,
88
- paymentTransactionHash: record.paymentTransactionHash,
89
- nativeAmount: record.nativeAmount,
90
- nativeAsset: record.nativeAsset,
91
- cashuFlow: record.cashuFlow,
92
- costAmountMicro: record.costAmountMicro,
93
- costCurrency: record.costCurrency,
94
- costRevision: record.costRevision,
95
- creditId: record.creditId,
96
- drawId: record.drawId,
97
- fundingReceipt: record.fundingReceipt,
98
- fundingCredit: record.fundingCredit,
99
- receivedProofs: [...record.receivedProofs],
100
- pendingPaymentMsats: record.pendingPaymentMsats,
101
- pendingMppChallengeIds: record.pendingMppChallengeIds ? [...record.pendingMppChallengeIds] : void 0,
102
- requesterToken: record.requesterToken,
103
- requestFingerprint: record.requestFingerprint,
104
- requesterPubkey: record.requesterPubkey,
105
- requestId: record.requestId,
106
- authRequestPath: record.authRequestPath,
107
- pendingX402Nonce: record.pendingX402Nonce,
108
- pendingX402AmountUsdcMicro: record.pendingX402AmountUsdcMicro,
109
- pendingPaymentFiatMicro: record.pendingPaymentFiatMicro,
110
- pendingPaymentFiatCurrency: record.pendingPaymentFiatCurrency,
111
- askedTopUpMicro: record.askedTopUpMicro,
112
- askedTopUpCurrency: record.askedTopUpCurrency,
113
- topUpCapUnenforcedReason: record.topUpCapUnenforcedReason,
114
- requiredMsats: record.requiredMsats,
115
- receipt: record.receipt,
116
- listeners: /* @__PURE__ */ new Set(),
117
- abort: new AbortController(),
118
- messageAppender: void 0,
119
- messageAppenderTail: void 0,
120
- stepCache: StepCache.deserialize(record.stepCache),
121
- state: record.state,
122
- pendingPrompts: /* @__PURE__ */ new Map(),
123
- pendingPayment: null,
124
- replayHighSeq: null,
125
- replayProviderSkip: 0,
126
- replayPaymentIndex: 0,
127
- createdAt: record.createdAt,
128
- lastActivityAt: record.lastActivityAt
129
- };
130
- }
131
- function abortJob(job, reason) {
132
- job.abort.abort(new JobCancelledError(reason));
133
- }
134
- var JobCancelledError = class extends Error {
135
- constructor(message = "Job cancelled") {
136
- super(message);
137
- this.name = "JobCancelledError";
138
- }
139
- };
140
- function providerMessage(job, type, content) {
141
- if (job.replayProviderSkip > 0) {
142
- job.replayProviderSkip--;
143
- return;
144
- }
145
- const msg = localMessage(job, "provider", type, content);
146
- if (type === "payment-request") {
147
- const paymentRequest = content;
148
- const pendingPaymentDelta = typeof paymentRequest.amount_msats === "number" ? paymentRequest.amount_msats : void 0;
149
- const pendingMppChallengeIds = paymentRequest.tempo?.challenges?.map((challenge) => challenge.id).filter((id) => typeof id === "string");
150
- job.messageAppender?.(msg, {
151
- awaitingInput: job.status === "awaiting-input",
152
- pendingPaymentDelta,
153
- pendingMppChallengeIds,
154
- pendingX402Nonce: typeof paymentRequest.x402?.nonce === "string" ? paymentRequest.x402.nonce : void 0,
155
- pendingX402AmountUsdcMicro: typeof paymentRequest.x402?.required_usdc_micro === "string" ? paymentRequest.x402.required_usdc_micro : void 0,
156
- // read off the job rather than the content — the fiat envelope
157
- // is ledger state, not part of the wire shape, and `requestPayment` has
158
- // already pinned it on the job by the time this fires (same ordering the
159
- // `pendingPaymentMsats` comment above describes).
160
- pendingPaymentFiatMicro: job.pendingPaymentFiatMicro,
161
- pendingPaymentFiatCurrency: job.pendingPaymentFiatCurrency
162
- });
163
- return;
164
- }
165
- job.messageAppender?.(msg, type === "prompt" ? { awaitingInput: job.status === "awaiting-input" } : void 0);
166
- }
167
- function localMessage(job, from, type, content) {
168
- job.seq++;
169
- const msg = {
170
- seq: job.seq,
171
- from,
172
- timestamp: Math.floor(Date.now() / 1e3),
173
- type,
174
- content
175
- };
176
- job.messages.push(msg);
177
- for (const listener of job.listeners) {
178
- listener(msg);
179
- }
180
- return msg;
181
- }
182
- function isTerminal(status) {
183
- return status === "completed" || status === "failed" || status === "cancelled";
184
- }
185
- function isYieldMessage(msg) {
186
- return msg.type === "prompt" || msg.type === "payment-request" || msg.type === "working" || msg.type === "complete" || msg.type === "cancel";
187
- }
188
-
189
13
  // src/sdk/server/job-store.ts
190
14
  function accumulateAskedTopUp(prior, ask) {
191
15
  const poisoned = (reason) => ({
@@ -219,6 +43,7 @@ function isStaleJobReapable(store) {
219
43
 
220
44
  // src/sdk/server/memory-job-store.ts
221
45
  var MemoryJobStore = class {
46
+ contentHolds = /* @__PURE__ */ new Map();
222
47
  data = /* @__PURE__ */ new Map();
223
48
  messages = /* @__PURE__ */ new Map();
224
49
  nextSeq = /* @__PURE__ */ new Map();
@@ -290,6 +115,7 @@ var MemoryJobStore = class {
290
115
  }
291
116
  const snapshot = structuredClone(record);
292
117
  if (existing) snapshot.createdAt = existing.createdAt;
118
+ if (existing && isTerminal(existing.status)) snapshot.lastActivityAt = existing.lastActivityAt;
293
119
  if (existing?.receipt) snapshot.receipt = existing.receipt;
294
120
  snapshot.timingKnown = existing ? existing.timingKnown ?? false : record.timingKnown ?? true;
295
121
  applyTiming(snapshot, existing, Date.now());
@@ -302,6 +128,7 @@ var MemoryJobStore = class {
302
128
  return Promise.resolve();
303
129
  }
304
130
  delete(id) {
131
+ this.contentHolds.delete(id);
305
132
  this.data.delete(id);
306
133
  this.messages.delete(id);
307
134
  this.nextSeq.delete(id);
@@ -317,7 +144,7 @@ var MemoryJobStore = class {
317
144
  }
318
145
  redactJob(args) {
319
146
  const record = this.data.get(args.jobId);
320
- if (!record || record.redactedAt !== void 0 || !isTerminal(record.status) || record.lastActivityAt !== args.expectedLastActivityAt) {
147
+ if (!record || record.redactedAt !== void 0 || !isTerminal(record.status) || this.contentHolds.get(args.jobId)?.holds.some((hold) => hold.cleared_at === null) || record.lastActivityAt !== args.expectedLastActivityAt) {
321
148
  return Promise.resolve(false);
322
149
  }
323
150
  record.input = "";
@@ -340,6 +167,17 @@ var MemoryJobStore = class {
340
167
  this.messages.delete(args.jobId);
341
168
  return Promise.resolve(true);
342
169
  }
170
+ getContentSnapshot(jobId) {
171
+ const job = this.data.get(jobId);
172
+ return Promise.resolve(job ? structuredClone({ job, history: this.contentHolds.get(jobId) ?? { holds: [], events: [] } }) : void 0);
173
+ }
174
+ mutateContentHold(jobId, mutation) {
175
+ const job = this.data.get(jobId);
176
+ if (!job) return Promise.reject(new ContentHoldError("job_not_found"));
177
+ const history = applyContentHoldMutation(job, this.contentHolds.get(jobId) ?? { holds: [], events: [] }, mutation);
178
+ this.contentHolds.set(jobId, history);
179
+ return Promise.resolve(structuredClone(history));
180
+ }
343
181
  claimReceiptSeq(jobId) {
344
182
  if (!this.data.has(jobId)) {
345
183
  return Promise.reject(new Error(`claimReceiptSeq: job ${jobId} not found`));
@@ -394,7 +232,7 @@ var MemoryJobStore = class {
394
232
  record.askedTopUpCurrency = asked.currency;
395
233
  record.topUpCapUnenforcedReason = asked.reason;
396
234
  }
397
- record.lastActivityAt = Date.now();
235
+ if (!isTerminal(record.status)) record.lastActivityAt = Date.now();
398
236
  if (seq > record.seq) record.seq = seq;
399
237
  this.notify(jobId, fullMessage);
400
238
  return Promise.resolve(seq);
@@ -407,7 +245,7 @@ var MemoryJobStore = class {
407
245
  const seq = this.allocateSeq(jobId);
408
246
  const fullMessage = { ...message, seq };
409
247
  this.messagesFor(jobId).push({ message: fullMessage, status: "pending-verification" });
410
- record.lastActivityAt = Date.now();
248
+ if (!isTerminal(record.status)) record.lastActivityAt = Date.now();
411
249
  if (seq > record.seq) record.seq = seq;
412
250
  this.fireNotifyOnly(jobId);
413
251
  return Promise.resolve(seq);
@@ -456,7 +294,7 @@ var MemoryJobStore = class {
456
294
  record.pendingX402AmountUsdcMicro = void 0;
457
295
  }
458
296
  }
459
- record.lastActivityAt = Date.now();
297
+ if (!isTerminal(record.status)) record.lastActivityAt = Date.now();
460
298
  this.notify(jobId, stored.message);
461
299
  return Promise.resolve({
462
300
  counters: snapshotCounters(record),
@@ -521,7 +359,7 @@ var MemoryJobStore = class {
521
359
  if (record?.status !== "awaiting-input") return Promise.resolve(false);
522
360
  applyTiming(record, structuredClone(record), Date.now(), "processing");
523
361
  record.status = "processing";
524
- record.lastActivityAt = Date.now();
362
+ if (!isTerminal(record.status)) record.lastActivityAt = Date.now();
525
363
  return Promise.resolve(true);
526
364
  }
527
365
  async tryReactivationLock(jobId, fn) {
@@ -704,14 +542,6 @@ function mergeSignals(a, b) {
704
542
  }
705
543
 
706
544
  export {
707
- toJobRecord,
708
- fromJobRecord,
709
- abortJob,
710
- JobCancelledError,
711
- providerMessage,
712
- localMessage,
713
- isTerminal,
714
- isYieldMessage,
715
545
  accumulateAskedTopUp,
716
546
  UNCAPPABLE_ASK_TOTAL,
717
547
  isJobRetentionStore,
@@ -1,10 +1,9 @@
1
1
  import {
2
2
  fundingReachableAt,
3
- info,
4
3
  listCredits,
5
4
  maxAdvertisedMicro,
6
5
  withinAdvertisedTolerance
7
- } from "./chunk-2VGWYG4P.js";
6
+ } from "./chunk-B2IROPQF.js";
8
7
  import {
9
8
  authorizeProtectedRemoval,
10
9
  isValidEvmPrivateKey,
@@ -31,7 +30,7 @@ import {
31
30
  getLastKnownBtcRate,
32
31
  msatsToUsdc,
33
32
  usdcToMsats
34
- } from "./chunk-3MHE725Z.js";
33
+ } from "./chunk-PAIM66YA.js";
35
34
  import {
36
35
  X402_CHANNELS_FILE,
37
36
  ensureConfigDir,
@@ -72,6 +71,42 @@ function x402WrongAssetHint(asset) {
72
71
  return `This rail reads only ${asset.label} (${asset.address}) on ${asset.networkLabel}. ${wrongToken} USDC on a different chain is invisible here too \u2014 this wallet reads ${asset.networkLabel} alone. If you funded the wrong one, swap it to ${asset.label} on ${asset.networkLabel}, or bridge the funds across to ${asset.networkLabel}; either way they land where this rail can spend them.`;
73
72
  }
74
73
 
74
+ // src/lib/output.ts
75
+ import { writeSync } from "fs";
76
+ var _traceId = null;
77
+ function setOutputTraceId(id) {
78
+ _traceId = id;
79
+ }
80
+ function getOutputTraceId() {
81
+ return _traceId;
82
+ }
83
+ function json(data) {
84
+ const out = _traceId !== null && typeof data === "object" && data !== null && !Array.isArray(data) && !("trace_id" in data) ? { trace_id: _traceId, ...data } : data;
85
+ writeAllSync(1, JSON.stringify(out) + "\n");
86
+ }
87
+ function info(msg) {
88
+ writeAllSync(2, msg + "\n");
89
+ }
90
+ function raw(msg) {
91
+ writeAllSync(1, msg + "\n");
92
+ }
93
+ function progress(event) {
94
+ writeAllSync(2, JSON.stringify(event) + "\n");
95
+ }
96
+ function writeAllSync(fd, text) {
97
+ const buf = Buffer.from(text, "utf8");
98
+ let offset = 0;
99
+ while (offset < buf.length) {
100
+ try {
101
+ offset += writeSync(fd, buf, offset, buf.length - offset);
102
+ } catch (err) {
103
+ const code = err.code;
104
+ if (code === "EAGAIN" || code === "EINTR") continue;
105
+ throw err;
106
+ }
107
+ }
108
+ }
109
+
75
110
  // src/lib/btc-rate.ts
76
111
  async function resolveBtcUsdRate(purpose) {
77
112
  try {
@@ -600,18 +635,18 @@ function ensureX402ChannelFile() {
600
635
  function loadFile() {
601
636
  if (!existsSync(X402_CHANNELS_FILE)) return { ...EMPTY, channels: {}, pending: [], exact: [] };
602
637
  try {
603
- const raw = JSON.parse(readFileSync(X402_CHANNELS_FILE, "utf8"));
604
- if (raw.version !== 1 || !raw.channels || typeof raw.channels !== "object" || Array.isArray(raw.channels) || !Array.isArray(raw.pending)) {
638
+ const raw2 = JSON.parse(readFileSync(X402_CHANNELS_FILE, "utf8"));
639
+ if (raw2.version !== 1 || !raw2.channels || typeof raw2.channels !== "object" || Array.isArray(raw2.channels) || !Array.isArray(raw2.pending)) {
605
640
  throw new Error("unsupported x402 channel store shape");
606
641
  }
607
642
  return {
608
643
  version: 1,
609
- channels: raw.channels,
610
- pending: raw.pending,
644
+ channels: raw2.channels,
645
+ pending: raw2.pending,
611
646
  // Tolerated rather than required, unlike its two siblings: every file
612
647
  // written before lacks the key, and failing closed on it would
613
648
  // read as a corrupt journal on a store that is perfectly intact.
614
- exact: Array.isArray(raw.exact) ? raw.exact : []
649
+ exact: Array.isArray(raw2.exact) ? raw2.exact : []
615
650
  };
616
651
  } catch (cause) {
617
652
  throw new DvmError(
@@ -986,6 +1021,12 @@ async function priceableRate(btcRate) {
986
1021
  export {
987
1022
  resolveX402Asset,
988
1023
  x402WrongAssetHint,
1024
+ setOutputTraceId,
1025
+ getOutputTraceId,
1026
+ json,
1027
+ info,
1028
+ raw,
1029
+ progress,
989
1030
  resolveBtcUsdRate,
990
1031
  tryResolveBtcUsdRate,
991
1032
  staleRateNotice,
@@ -4,7 +4,7 @@ import {
4
4
  signedRequestHeaderMatchesDomain,
5
5
  signedRequestStatementHeader,
6
6
  testOnlySignedRequestDomain
7
- } from "./chunk-KXZUCCEY.js";
7
+ } from "./chunk-RCM3P2HE.js";
8
8
  import {
9
9
  diagnosticConsole
10
10
  } from "./chunk-ETXDTUJO.js";
@@ -0,0 +1,57 @@
1
+ // src/lib/cast-auth/canonical-json.ts
2
+ function canonicalize(value) {
3
+ return JSON.stringify(sortDeep(value));
4
+ }
5
+ function canonicaliseForSigning(value) {
6
+ return new TextEncoder().encode(canonicalize(value));
7
+ }
8
+ function sortDeep(value) {
9
+ if (value === null) return null;
10
+ if (typeof value === "string" || typeof value === "boolean") return value;
11
+ if (typeof value === "number") {
12
+ if (!Number.isFinite(value)) {
13
+ throw new TypeError(`canonicalize: non-finite number is not JSON-serialisable`);
14
+ }
15
+ return value;
16
+ }
17
+ if (Array.isArray(value)) return value.map(sortDeep);
18
+ if (typeof value === "object") {
19
+ const sorted = {};
20
+ for (const key of Object.keys(value).sort()) {
21
+ const v = value[key];
22
+ if (v === void 0) continue;
23
+ sorted[key] = sortDeep(v);
24
+ }
25
+ return sorted;
26
+ }
27
+ throw new TypeError(`canonicalize: unsupported value of type ${typeof value}`);
28
+ }
29
+
30
+ // src/lib/cast-auth/verify.ts
31
+ import { schnorr } from "@noble/curves/secp256k1.js";
32
+ function verifyCanonicalSignedRequest(payload, signatureHex, pubkeyHex) {
33
+ if (!/^[0-9a-f]{64}$/i.test(pubkeyHex) || !/^[0-9a-f]{128}$/i.test(signatureHex)) {
34
+ return false;
35
+ }
36
+ const data = canonicaliseForSigning(payload);
37
+ const signature = hexToBytes(signatureHex);
38
+ const pubkey = hexToBytes(pubkeyHex);
39
+ try {
40
+ return schnorr.verify(signature, data, pubkey);
41
+ } catch {
42
+ return false;
43
+ }
44
+ }
45
+ function hexToBytes(hex) {
46
+ const out = new Uint8Array(hex.length / 2);
47
+ for (let i = 0; i < out.length; i++) {
48
+ out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
49
+ }
50
+ return out;
51
+ }
52
+
53
+ export {
54
+ canonicalize,
55
+ canonicaliseForSigning,
56
+ verifyCanonicalSignedRequest
57
+ };