@compose-market/sdk 0.1.1 → 0.3.0
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/CHANGELOG.md +64 -0
- package/LICENSE +21 -0
- package/README.md +158 -35
- package/dist/errors.d.ts +145 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +152 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +75 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +68 -0
- package/dist/events.js.map +1 -0
- package/dist/http.d.ts +98 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +350 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +206 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +340 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/inference.d.ts +175 -0
- package/dist/resources/inference.d.ts.map +1 -0
- package/dist/resources/inference.js +740 -0
- package/dist/resources/inference.js.map +1 -0
- package/dist/resources/instrumentation.d.ts +28 -0
- package/dist/resources/instrumentation.d.ts.map +1 -0
- package/dist/resources/instrumentation.js +43 -0
- package/dist/resources/instrumentation.js.map +1 -0
- package/dist/resources/keys.d.ts +54 -0
- package/dist/resources/keys.d.ts.map +1 -0
- package/dist/resources/keys.js +164 -0
- package/dist/resources/keys.js.map +1 -0
- package/dist/resources/models.d.ts +26 -0
- package/dist/resources/models.d.ts.map +1 -0
- package/dist/resources/models.js +52 -0
- package/dist/resources/models.js.map +1 -0
- package/dist/resources/session-events.d.ts +46 -0
- package/dist/resources/session-events.d.ts.map +1 -0
- package/dist/resources/session-events.js +199 -0
- package/dist/resources/session-events.js.map +1 -0
- package/dist/resources/webhooks.d.ts +27 -0
- package/dist/resources/webhooks.d.ts.map +1 -0
- package/dist/resources/webhooks.js +78 -0
- package/dist/resources/webhooks.js.map +1 -0
- package/dist/resources/x402.d.ts +37 -0
- package/dist/resources/x402.d.ts.map +1 -0
- package/dist/resources/x402.js +72 -0
- package/dist/resources/x402.js.map +1 -0
- package/dist/storage.d.ts +27 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +46 -0
- package/dist/storage.js.map +1 -0
- package/dist/streaming/budget.d.ts +22 -0
- package/dist/streaming/budget.d.ts.map +1 -0
- package/dist/streaming/budget.js +40 -0
- package/dist/streaming/budget.js.map +1 -0
- package/dist/streaming/receipt.d.ts +25 -0
- package/dist/streaming/receipt.d.ts.map +1 -0
- package/dist/streaming/receipt.js +92 -0
- package/dist/streaming/receipt.js.map +1 -0
- package/dist/streaming/sse.d.ts +29 -0
- package/dist/streaming/sse.d.ts.map +1 -0
- package/dist/streaming/sse.js +125 -0
- package/dist/streaming/sse.js.map +1 -0
- package/dist/types/index.d.ts +540 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +9 -0
- package/dist/version.js.map +1 -0
- package/package.json +32 -21
- package/index.d.ts +0 -244
- package/index.js +0 -397
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-events SSE resource.
|
|
3
|
+
*
|
|
4
|
+
* Subscribes to `/api/session/events?userAddress=<address>&chainId=<chainId>`
|
|
5
|
+
* and yields typed `session-active` / `session-expired` events as they arrive.
|
|
6
|
+
*
|
|
7
|
+
* Each event is additionally dispatched to the SDK event bus so application
|
|
8
|
+
* code can centralise listeners on `sdk.events.on("sessionActive", ...)` and
|
|
9
|
+
* `sdk.events.on("sessionExpired", ...)` without plumbing the iterator
|
|
10
|
+
* manually.
|
|
11
|
+
*
|
|
12
|
+
* The iterator auto-reconnects transparently on network errors using the
|
|
13
|
+
* retry policy configured on the HTTP client; callers cancel via the
|
|
14
|
+
* provided `AbortSignal`.
|
|
15
|
+
*/
|
|
16
|
+
import { ComposeError } from "../errors.js";
|
|
17
|
+
import { parseSSEStream } from "../streaming/sse.js";
|
|
18
|
+
/**
|
|
19
|
+
* Drive a raw SSE subscription to `/api/session/events` and yield one event
|
|
20
|
+
* at a time. The generator terminates only when the caller aborts the signal
|
|
21
|
+
* or when a terminal `session-expired` event is emitted for the requested
|
|
22
|
+
* `(userAddress, chainId)` tuple (a terminal signal from the server side).
|
|
23
|
+
*/
|
|
24
|
+
export class SessionEventsResource {
|
|
25
|
+
client;
|
|
26
|
+
events;
|
|
27
|
+
constructor(client, events) {
|
|
28
|
+
this.client = client;
|
|
29
|
+
this.events = events;
|
|
30
|
+
}
|
|
31
|
+
subscribe(opts) {
|
|
32
|
+
const self = this;
|
|
33
|
+
return {
|
|
34
|
+
[Symbol.asyncIterator]() {
|
|
35
|
+
return self.createIterator(opts);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
createIterator(opts) {
|
|
40
|
+
const reconnectMaxDelayMs = Math.max(500, opts.reconnectMaxDelayMs ?? 10_000);
|
|
41
|
+
let closed = false;
|
|
42
|
+
let attempt = 0;
|
|
43
|
+
let pending = null;
|
|
44
|
+
// Cancellation is routed through `opts.signal` into `parseSSEStream`;
|
|
45
|
+
// we don't hold a reader handle here, so the iterator just flips the
|
|
46
|
+
// `closed` flag and lets the in-flight `asResponse()` / parseSSEStream
|
|
47
|
+
// receive the abort.
|
|
48
|
+
const abortHandler = () => { closed = true; };
|
|
49
|
+
opts.signal?.addEventListener("abort", abortHandler, { once: true });
|
|
50
|
+
const poll = async () => {
|
|
51
|
+
while (!closed) {
|
|
52
|
+
try {
|
|
53
|
+
const response = await this.client.request({
|
|
54
|
+
method: "GET",
|
|
55
|
+
path: "/api/session/events",
|
|
56
|
+
query: {
|
|
57
|
+
userAddress: opts.userAddress,
|
|
58
|
+
chainId: opts.chainId,
|
|
59
|
+
},
|
|
60
|
+
signal: opts.signal,
|
|
61
|
+
expectStream: true,
|
|
62
|
+
doNotRetry: true,
|
|
63
|
+
headers: {
|
|
64
|
+
extra: { Accept: "text/event-stream" },
|
|
65
|
+
},
|
|
66
|
+
}).asResponse();
|
|
67
|
+
if (!response.body) {
|
|
68
|
+
throw new ComposeError({
|
|
69
|
+
code: "upstream_error",
|
|
70
|
+
message: "session events stream had no body",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
attempt = 0;
|
|
74
|
+
return await this.consumeStream(response.body, opts);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
if (closed)
|
|
78
|
+
break;
|
|
79
|
+
if (opts.signal?.aborted) {
|
|
80
|
+
closed = true;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
attempt += 1;
|
|
84
|
+
const delay = Math.min(500 * (2 ** (attempt - 1)), reconnectMaxDelayMs);
|
|
85
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
86
|
+
if (closed || opts.signal?.aborted)
|
|
87
|
+
break;
|
|
88
|
+
// fall through and reconnect
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
opts.signal?.removeEventListener("abort", abortHandler);
|
|
92
|
+
return { done: true, value: undefined };
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
next: async () => {
|
|
96
|
+
if (pending) {
|
|
97
|
+
const result = pending;
|
|
98
|
+
pending = null;
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
return poll();
|
|
102
|
+
},
|
|
103
|
+
return: async () => {
|
|
104
|
+
closed = true;
|
|
105
|
+
opts.signal?.removeEventListener("abort", abortHandler);
|
|
106
|
+
return { done: true, value: undefined };
|
|
107
|
+
},
|
|
108
|
+
throw: async (err) => {
|
|
109
|
+
closed = true;
|
|
110
|
+
opts.signal?.removeEventListener("abort", abortHandler);
|
|
111
|
+
throw err;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Read until a named event arrives, yield it, emit on the bus, and
|
|
117
|
+
* return. The outer iterator loops back to re-open the stream, preserving
|
|
118
|
+
* the read-one-then-reconnect cadence we want for cross-tab event
|
|
119
|
+
* semantics (no orphaned bytes when the consumer pauses).
|
|
120
|
+
*/
|
|
121
|
+
async consumeStream(body, opts) {
|
|
122
|
+
const parser = parseSSEStream(body, { signal: opts.signal });
|
|
123
|
+
let nextResult = null;
|
|
124
|
+
try {
|
|
125
|
+
for await (const frame of parser) {
|
|
126
|
+
const parsed = parseSessionEventFrame(frame);
|
|
127
|
+
if (!parsed)
|
|
128
|
+
continue;
|
|
129
|
+
if (parsed.type === "session-active") {
|
|
130
|
+
this.events.emit("sessionActive", parsed);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
this.events.emit("sessionExpired", parsed);
|
|
134
|
+
}
|
|
135
|
+
if (nextResult === null) {
|
|
136
|
+
nextResult = { done: false, value: parsed };
|
|
137
|
+
return nextResult;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
try {
|
|
143
|
+
body.cancel();
|
|
144
|
+
}
|
|
145
|
+
catch { /* best-effort */ }
|
|
146
|
+
}
|
|
147
|
+
// Stream ended without yielding (e.g. server closed). Signal upstream
|
|
148
|
+
// to reconnect rather than terminate.
|
|
149
|
+
throw new ComposeError({ code: "upstream_error", message: "session events stream closed without emitting a frame" });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function parseSessionEventFrame(frame) {
|
|
153
|
+
if (frame.event === "session-active") {
|
|
154
|
+
try {
|
|
155
|
+
const data = JSON.parse(frame.data);
|
|
156
|
+
return {
|
|
157
|
+
type: "session-active",
|
|
158
|
+
userAddress: String(data.userAddress ?? ""),
|
|
159
|
+
chainId: Number(data.chainId ?? 0),
|
|
160
|
+
expiresAt: typeof data.expiresAt === "number" ? data.expiresAt : undefined,
|
|
161
|
+
budgetLimit: coerceWeiString(data.budgetLimit),
|
|
162
|
+
budgetUsed: coerceWeiString(data.budgetUsed),
|
|
163
|
+
budgetLocked: coerceWeiString(data.budgetLocked),
|
|
164
|
+
budgetRemaining: coerceWeiString(data.budgetRemaining),
|
|
165
|
+
timestamp: typeof data.timestamp === "number" ? data.timestamp : undefined,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (frame.event === "session-expired") {
|
|
173
|
+
try {
|
|
174
|
+
const data = JSON.parse(frame.data);
|
|
175
|
+
return {
|
|
176
|
+
type: "session-expired",
|
|
177
|
+
userAddress: String(data.userAddress ?? ""),
|
|
178
|
+
chainId: Number(data.chainId ?? 0),
|
|
179
|
+
reason: typeof data.reason === "string" ? data.reason : undefined,
|
|
180
|
+
message: typeof data.message === "string" ? data.message : undefined,
|
|
181
|
+
action: typeof data.action === "string" ? data.action : undefined,
|
|
182
|
+
expiresAt: typeof data.expiresAt === "number" ? data.expiresAt : undefined,
|
|
183
|
+
timestamp: typeof data.timestamp === "number" ? data.timestamp : undefined,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
function coerceWeiString(value) {
|
|
193
|
+
if (typeof value === "string" && value.length > 0)
|
|
194
|
+
return value;
|
|
195
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
196
|
+
return String(value);
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=session-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-events.js","sourceRoot":"","sources":["../../src/resources/session-events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAiB,MAAM,qBAAqB,CAAC;AAUpE;;;;;GAKG;AACH,MAAM,OAAO,qBAAqB;IAET;IACA;IAFrB,YACqB,MAAkB,EAClB,MAAuB;QADvB,WAAM,GAAN,MAAM,CAAY;QAClB,WAAM,GAAN,MAAM,CAAiB;IACzC,CAAC;IAEJ,SAAS,CAAC,IAA0B;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,OAAO;YACH,CAAC,MAAM,CAAC,aAAa,CAAC;gBAClB,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC;SACJ,CAAC;IACN,CAAC;IAEO,cAAc,CAAC,IAA0B;QAC7C,MAAM,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,mBAAmB,IAAI,MAAM,CAAC,CAAC;QAC9E,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,IAAI,OAAO,GAAwC,IAAI,CAAC;QACxD,sEAAsE;QACtE,qEAAqE;QACrE,uEAAuE;QACvE,qBAAqB;QACrB,MAAM,YAAY,GAAG,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAErE,MAAM,IAAI,GAAG,KAAK,IAA2C,EAAE;YAC3D,OAAO,CAAC,MAAM,EAAE,CAAC;gBACb,IAAI,CAAC;oBACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAU;wBAChD,MAAM,EAAE,KAAK;wBACb,IAAI,EAAE,qBAAqB;wBAC3B,KAAK,EAAE;4BACH,WAAW,EAAE,IAAI,CAAC,WAAW;4BAC7B,OAAO,EAAE,IAAI,CAAC,OAAO;yBACxB;wBACD,MAAM,EAAE,IAAI,CAAC,MAAM;wBACnB,YAAY,EAAE,IAAI;wBAClB,UAAU,EAAE,IAAI;wBAChB,OAAO,EAAE;4BACL,KAAK,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE;yBACzC;qBACJ,CAAC,CAAC,UAAU,EAAE,CAAC;oBAEhB,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;wBACjB,MAAM,IAAI,YAAY,CAAC;4BACnB,IAAI,EAAE,gBAAgB;4BACtB,OAAO,EAAE,mCAAmC;yBAC/C,CAAC,CAAC;oBACP,CAAC;oBAED,OAAO,GAAG,CAAC,CAAC;oBACZ,OAAO,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACzD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACX,IAAI,MAAM;wBAAE,MAAM;oBAClB,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;wBACvB,MAAM,GAAG,IAAI,CAAC;wBACd,MAAM;oBACV,CAAC;oBACD,OAAO,IAAI,CAAC,CAAC;oBACb,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC;oBACxE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;oBACjE,IAAI,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;wBAAE,MAAM;oBAC1C,6BAA6B;gBACjC,CAAC;YACL,CAAC;YACD,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YACxD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAC5C,CAAC,CAAC;QAEF,OAAO;YACH,IAAI,EAAE,KAAK,IAA2C,EAAE;gBACpD,IAAI,OAAO,EAAE,CAAC;oBACV,MAAM,MAAM,GAAG,OAAO,CAAC;oBACvB,OAAO,GAAG,IAAI,CAAC;oBACf,OAAO,MAAM,CAAC;gBAClB,CAAC;gBACD,OAAO,IAAI,EAAE,CAAC;YAClB,CAAC;YACD,MAAM,EAAE,KAAK,IAA2C,EAAE;gBACtD,MAAM,GAAG,IAAI,CAAC;gBACd,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;gBACxD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;YAC5C,CAAC;YACD,KAAK,EAAE,KAAK,EAAE,GAAG,EAAyC,EAAE;gBACxD,MAAM,GAAG,IAAI,CAAC;gBACd,IAAI,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;gBACxD,MAAM,GAAG,CAAC;YACd,CAAC;SACJ,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,aAAa,CACvB,IAAgC,EAChC,IAA0B;QAE1B,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,IAAI,UAAU,GAAwC,IAAI,CAAC;QAE3D,IAAI,CAAC;YACD,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC/B,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;gBAC7C,IAAI,CAAC,MAAM;oBAAE,SAAS;gBAEtB,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;oBACnC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;gBAC9C,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;gBAC/C,CAAC;gBAED,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;oBACtB,UAAU,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;oBAC5C,OAAO,UAAU,CAAC;gBACtB,CAAC;YACL,CAAC;QACL,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC;gBAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;QACtD,CAAC;QAED,sEAAsE;QACtE,sCAAsC;QACtC,MAAM,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,uDAAuD,EAAE,CAAC,CAAC;IACzH,CAAC;CACJ;AAED,SAAS,sBAAsB,CAAC,KAAe;IAC3C,IAAI,KAAK,CAAC,KAAK,KAAK,gBAAgB,EAAE,CAAC;QACnC,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;YAC/D,OAAO;gBACH,IAAI,EAAE,gBAAgB;gBACtB,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;gBAC3C,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;gBAClC,SAAS,EAAE,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;gBAC1E,WAAW,EAAE,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC;gBAC9C,UAAU,EAAE,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC;gBAC5C,YAAY,EAAE,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC;gBAChD,eAAe,EAAE,eAAe,CAAC,IAAI,CAAC,eAAe,CAAC;gBACtD,SAAS,EAAE,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;aAChD,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,KAAK,iBAAiB,EAAE,CAAC;QACpC,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;YAC/D,OAAO;gBACH,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;gBAC3C,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;gBAClC,MAAM,EAAE,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;gBACjE,OAAO,EAAE,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;gBACpE,MAAM,EAAE,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;gBACjE,SAAS,EAAE,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;gBAC1E,SAAS,EAAE,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;aAC/C,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACL,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAChE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9E,OAAO,SAAS,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook signature verification.
|
|
3
|
+
*
|
|
4
|
+
* Compose delivery uses a Stripe-style signature header:
|
|
5
|
+
* `X-Compose-Signature: t=<unix>,v1=<hex>`
|
|
6
|
+
* where `v1` is `HMAC-SHA256(timestamp + "." + payload, secret)`.
|
|
7
|
+
*
|
|
8
|
+
* `verify()` is a constant-time comparison.
|
|
9
|
+
*/
|
|
10
|
+
export interface ComposeWebhookEvent<T = unknown> {
|
|
11
|
+
id?: string;
|
|
12
|
+
type: string;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
data: T;
|
|
15
|
+
}
|
|
16
|
+
export interface VerifyWebhookInput {
|
|
17
|
+
body: string;
|
|
18
|
+
signature: string;
|
|
19
|
+
secret: string;
|
|
20
|
+
/** Max allowed skew between `t` and now, in seconds. Default 300 (5 min). */
|
|
21
|
+
toleranceSeconds?: number;
|
|
22
|
+
}
|
|
23
|
+
export declare class WebhooksResource {
|
|
24
|
+
verify(input: VerifyWebhookInput): Promise<boolean>;
|
|
25
|
+
constructEvent<T = unknown>(input: VerifyWebhookInput): Promise<ComposeWebhookEvent<T>>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/resources/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,mBAAmB,CAAC,CAAC,GAAG,OAAO;IAC5C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,CAAC,CAAC;CACX;AAED,MAAM,WAAW,kBAAkB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,qBAAa,gBAAgB;IACnB,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBnD,cAAc,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;CAShG"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook signature verification.
|
|
3
|
+
*
|
|
4
|
+
* Compose delivery uses a Stripe-style signature header:
|
|
5
|
+
* `X-Compose-Signature: t=<unix>,v1=<hex>`
|
|
6
|
+
* where `v1` is `HMAC-SHA256(timestamp + "." + payload, secret)`.
|
|
7
|
+
*
|
|
8
|
+
* `verify()` is a constant-time comparison.
|
|
9
|
+
*/
|
|
10
|
+
export class WebhooksResource {
|
|
11
|
+
async verify(input) {
|
|
12
|
+
const parsed = parseSignatureHeader(input.signature);
|
|
13
|
+
if (!parsed)
|
|
14
|
+
return false;
|
|
15
|
+
const now = Math.floor(Date.now() / 1000);
|
|
16
|
+
const tolerance = input.toleranceSeconds ?? 300;
|
|
17
|
+
if (Math.abs(now - parsed.timestamp) > tolerance)
|
|
18
|
+
return false;
|
|
19
|
+
const expected = await hmacSha256Hex(input.secret, `${parsed.timestamp}.${input.body}`);
|
|
20
|
+
return timingSafeEqualHex(expected, parsed.v1);
|
|
21
|
+
}
|
|
22
|
+
async constructEvent(input) {
|
|
23
|
+
const ok = await this.verify(input);
|
|
24
|
+
if (!ok) {
|
|
25
|
+
const error = new Error("Invalid webhook signature");
|
|
26
|
+
error.name = "ComposeWebhookSignatureError";
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
return JSON.parse(input.body);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function parseSignatureHeader(header) {
|
|
33
|
+
const parts = header.split(",");
|
|
34
|
+
let timestamp = null;
|
|
35
|
+
let v1 = null;
|
|
36
|
+
for (const part of parts) {
|
|
37
|
+
const [rawKey, ...rawValue] = part.split("=");
|
|
38
|
+
const key = rawKey.trim();
|
|
39
|
+
const value = rawValue.join("=").trim();
|
|
40
|
+
if (key === "t") {
|
|
41
|
+
timestamp = parseInt(value, 10);
|
|
42
|
+
}
|
|
43
|
+
else if (key === "v1") {
|
|
44
|
+
v1 = value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (timestamp === null || !Number.isFinite(timestamp) || !v1)
|
|
48
|
+
return null;
|
|
49
|
+
return { timestamp, v1 };
|
|
50
|
+
}
|
|
51
|
+
async function hmacSha256Hex(secret, message) {
|
|
52
|
+
const cryptoObj = globalThis.crypto;
|
|
53
|
+
const subtle = cryptoObj?.subtle;
|
|
54
|
+
if (!subtle) {
|
|
55
|
+
throw new Error("WebCrypto SubtleCrypto is required for webhook verification");
|
|
56
|
+
}
|
|
57
|
+
const encoder = new TextEncoder();
|
|
58
|
+
const key = await subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
59
|
+
const signature = await subtle.sign("HMAC", key, encoder.encode(message));
|
|
60
|
+
return toHex(new Uint8Array(signature));
|
|
61
|
+
}
|
|
62
|
+
function toHex(bytes) {
|
|
63
|
+
const out = [];
|
|
64
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
65
|
+
out.push(bytes[i].toString(16).padStart(2, "0"));
|
|
66
|
+
}
|
|
67
|
+
return out.join("");
|
|
68
|
+
}
|
|
69
|
+
function timingSafeEqualHex(a, b) {
|
|
70
|
+
if (a.length !== b.length)
|
|
71
|
+
return false;
|
|
72
|
+
let result = 0;
|
|
73
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
74
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
75
|
+
}
|
|
76
|
+
return result === 0;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/resources/webhooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { APIPromise, HttpClient } from "../http.js";
|
|
2
|
+
import type { ComposeReceipt, FacilitatorChainsResponse, FacilitatorSupportedResponse, PaymentPayload, PaymentRequired, PaymentRequirements, SettleResponse, VerifyResponse } from "../types/index.js";
|
|
3
|
+
export declare class FacilitatorResource {
|
|
4
|
+
private readonly client;
|
|
5
|
+
constructor(client: HttpClient);
|
|
6
|
+
supported(): APIPromise<FacilitatorSupportedResponse>;
|
|
7
|
+
chains(): APIPromise<FacilitatorChainsResponse>;
|
|
8
|
+
verify(body: {
|
|
9
|
+
x402Version: 2;
|
|
10
|
+
paymentPayload: PaymentPayload;
|
|
11
|
+
paymentRequirements: PaymentRequirements;
|
|
12
|
+
}): APIPromise<VerifyResponse>;
|
|
13
|
+
settle(body: {
|
|
14
|
+
x402Version: 2;
|
|
15
|
+
paymentPayload: PaymentPayload;
|
|
16
|
+
paymentRequirements: PaymentRequirements;
|
|
17
|
+
}): APIPromise<SettleResponse>;
|
|
18
|
+
}
|
|
19
|
+
export declare class X402Resource {
|
|
20
|
+
readonly facilitator: FacilitatorResource;
|
|
21
|
+
constructor(client: HttpClient);
|
|
22
|
+
/**
|
|
23
|
+
* Decode the base64-url PAYMENT-REQUIRED header into a typed
|
|
24
|
+
* `PaymentRequired` body. Throws on malformed input.
|
|
25
|
+
*/
|
|
26
|
+
decodePaymentRequired(headerValue: string): PaymentRequired;
|
|
27
|
+
/**
|
|
28
|
+
* Decode the base64-url PAYMENT-RESPONSE header into a typed
|
|
29
|
+
* `SettleResponse` body. Throws on malformed input.
|
|
30
|
+
*/
|
|
31
|
+
decodePaymentResponse(headerValue: string): SettleResponse;
|
|
32
|
+
/**
|
|
33
|
+
* Decode the base64-url X-Compose-Receipt header. Throws on malformed input.
|
|
34
|
+
*/
|
|
35
|
+
decodeReceipt(headerValue: string): ComposeReceipt;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=x402.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x402.d.ts","sourceRoot":"","sources":["../../src/resources/x402.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,KAAK,EACR,cAAc,EACd,yBAAyB,EACzB,4BAA4B,EAC5B,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,cAAc,EACjB,MAAM,mBAAmB,CAAC;AAe3B,qBAAa,mBAAmB;IAChB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,UAAU;IAE/C,SAAS,IAAI,UAAU,CAAC,4BAA4B,CAAC;IAOrD,MAAM,IAAI,UAAU,CAAC,yBAAyB,CAAC;IAO/C,MAAM,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,CAAC,CAAC;QAAC,cAAc,EAAE,cAAc,CAAC;QAAC,mBAAmB,EAAE,mBAAmB,CAAA;KAAE,GAAG,UAAU,CAAC,cAAc,CAAC;IAQtI,MAAM,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,CAAC,CAAC;QAAC,cAAc,EAAE,cAAc,CAAC;QAAC,mBAAmB,EAAE,mBAAmB,CAAA;KAAE,GAAG,UAAU,CAAC,cAAc,CAAC;CAOzI;AAED,qBAAa,YAAY;IACrB,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;gBAE9B,MAAM,EAAE,UAAU;IAI9B;;;OAGG;IACH,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe;IAI3D;;;OAGG;IACH,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc;IAI1D;;OAEG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc;CAGrD"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { decodeReceiptHeader } from "../streaming/receipt.js";
|
|
2
|
+
function base64UrlDecode(value) {
|
|
3
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
4
|
+
const padding = normalized.length % 4 === 0 ? "" : "=".repeat(4 - (normalized.length % 4));
|
|
5
|
+
if (typeof globalThis.atob === "function") {
|
|
6
|
+
const binary = globalThis.atob(normalized + padding);
|
|
7
|
+
const bytes = new Uint8Array(binary.length);
|
|
8
|
+
for (let i = 0; i < binary.length; i += 1)
|
|
9
|
+
bytes[i] = binary.charCodeAt(i);
|
|
10
|
+
return new TextDecoder("utf-8").decode(bytes);
|
|
11
|
+
}
|
|
12
|
+
return Buffer.from(normalized + padding, "base64").toString("utf-8");
|
|
13
|
+
}
|
|
14
|
+
export class FacilitatorResource {
|
|
15
|
+
client;
|
|
16
|
+
constructor(client) {
|
|
17
|
+
this.client = client;
|
|
18
|
+
}
|
|
19
|
+
supported() {
|
|
20
|
+
return this.client.request({
|
|
21
|
+
method: "GET",
|
|
22
|
+
path: "/api/x402/facilitator/supported",
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
chains() {
|
|
26
|
+
return this.client.request({
|
|
27
|
+
method: "GET",
|
|
28
|
+
path: "/api/x402/facilitator/chains",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
verify(body) {
|
|
32
|
+
return this.client.request({
|
|
33
|
+
method: "POST",
|
|
34
|
+
path: "/api/x402/facilitator/verify",
|
|
35
|
+
body,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
settle(body) {
|
|
39
|
+
return this.client.request({
|
|
40
|
+
method: "POST",
|
|
41
|
+
path: "/api/x402/facilitator/settle",
|
|
42
|
+
body,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export class X402Resource {
|
|
47
|
+
facilitator;
|
|
48
|
+
constructor(client) {
|
|
49
|
+
this.facilitator = new FacilitatorResource(client);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Decode the base64-url PAYMENT-REQUIRED header into a typed
|
|
53
|
+
* `PaymentRequired` body. Throws on malformed input.
|
|
54
|
+
*/
|
|
55
|
+
decodePaymentRequired(headerValue) {
|
|
56
|
+
return JSON.parse(base64UrlDecode(headerValue));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Decode the base64-url PAYMENT-RESPONSE header into a typed
|
|
60
|
+
* `SettleResponse` body. Throws on malformed input.
|
|
61
|
+
*/
|
|
62
|
+
decodePaymentResponse(headerValue) {
|
|
63
|
+
return JSON.parse(base64UrlDecode(headerValue));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Decode the base64-url X-Compose-Receipt header. Throws on malformed input.
|
|
67
|
+
*/
|
|
68
|
+
decodeReceipt(headerValue) {
|
|
69
|
+
return decodeReceiptHeader(headerValue);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=x402.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x402.js","sourceRoot":"","sources":["../../src/resources/x402.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,SAAS,eAAe,CAAC,KAAa;IAClC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAC3F,IAAI,OAAO,UAAU,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC;YAAE,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC3E,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,OAAO,mBAAmB;IACC;IAA7B,YAA6B,MAAkB;QAAlB,WAAM,GAAN,MAAM,CAAY;IAAG,CAAC;IAEnD,SAAS;QACL,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAA+B;YACrD,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,iCAAiC;SAC1C,CAAC,CAAC;IACP,CAAC;IAED,MAAM;QACF,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAA4B;YAClD,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,8BAA8B;SACvC,CAAC,CAAC;IACP,CAAC;IAED,MAAM,CAAC,IAAkG;QACrG,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAiB;YACvC,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,8BAA8B;YACpC,IAAI;SACP,CAAC,CAAC;IACP,CAAC;IAED,MAAM,CAAC,IAAkG;QACrG,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAiB;YACvC,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,8BAA8B;YACpC,IAAI;SACP,CAAC,CAAC;IACP,CAAC;CACJ;AAED,MAAM,OAAO,YAAY;IACZ,WAAW,CAAsB;IAE1C,YAAY,MAAkB;QAC1B,IAAI,CAAC,WAAW,GAAG,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACvD,CAAC;IAED;;;OAGG;IACH,qBAAqB,CAAC,WAAmB;QACrC,OAAO,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,WAAW,CAAC,CAAoB,CAAC;IACvE,CAAC;IAED;;;OAGG;IACH,qBAAqB,CAAC,WAAmB;QACrC,OAAO,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,WAAW,CAAC,CAAmB,CAAC;IACtE,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,WAAmB;QAC7B,OAAO,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC;CACJ"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable storage for persisting the Compose Key JWT across process
|
|
3
|
+
* restarts (browser reload, serverless cold start, etc).
|
|
4
|
+
*
|
|
5
|
+
* The SDK stays framework-agnostic by not importing any browser or Node
|
|
6
|
+
* storage API directly. Integrators pass a minimal adapter; the SDK
|
|
7
|
+
* auto-detects `globalThis.localStorage` when none is provided but runs in a
|
|
8
|
+
* browser, and falls back to an in-memory Map everywhere else.
|
|
9
|
+
*
|
|
10
|
+
* Storage is scoped per `(userAddress, chainId)` tuple so multiple wallets /
|
|
11
|
+
* chains on the same device never collide.
|
|
12
|
+
*/
|
|
13
|
+
export interface ComposeStorage {
|
|
14
|
+
getItem(key: string): string | null;
|
|
15
|
+
setItem(key: string, value: string): void;
|
|
16
|
+
removeItem(key: string): void;
|
|
17
|
+
}
|
|
18
|
+
export declare function resolveStorage(explicit: ComposeStorage | undefined): ComposeStorage | null;
|
|
19
|
+
export declare function createMemoryStorage(): ComposeStorage;
|
|
20
|
+
/**
|
|
21
|
+
* Build the scoped storage key for a persisted Compose Key token. The shape
|
|
22
|
+
* `compose.sdk.token:<lower-address>:<chainId>` mirrors the convention already
|
|
23
|
+
* in use by web/ so multiple SDK instances + the bare web app never collide.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildTokenStorageKey(tokenScope: string, userAddress: string, chainId: number): string;
|
|
26
|
+
export declare const DEFAULT_TOKEN_SCOPE = "compose.sdk.token";
|
|
27
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,cAAc;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,GAAG,cAAc,GAAG,IAAI,CAgB1F;AAED,wBAAgB,mBAAmB,IAAI,cAAc,CAOpD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAErG;AAED,eAAO,MAAM,mBAAmB,sBAAsB,CAAC"}
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pluggable storage for persisting the Compose Key JWT across process
|
|
3
|
+
* restarts (browser reload, serverless cold start, etc).
|
|
4
|
+
*
|
|
5
|
+
* The SDK stays framework-agnostic by not importing any browser or Node
|
|
6
|
+
* storage API directly. Integrators pass a minimal adapter; the SDK
|
|
7
|
+
* auto-detects `globalThis.localStorage` when none is provided but runs in a
|
|
8
|
+
* browser, and falls back to an in-memory Map everywhere else.
|
|
9
|
+
*
|
|
10
|
+
* Storage is scoped per `(userAddress, chainId)` tuple so multiple wallets /
|
|
11
|
+
* chains on the same device never collide.
|
|
12
|
+
*/
|
|
13
|
+
export function resolveStorage(explicit) {
|
|
14
|
+
if (explicit)
|
|
15
|
+
return explicit;
|
|
16
|
+
// Browser / Deno / Cloudflare Workers with localStorage polyfills.
|
|
17
|
+
const candidate = globalThis.localStorage;
|
|
18
|
+
if (candidate && typeof candidate === "object") {
|
|
19
|
+
const ls = candidate;
|
|
20
|
+
if (typeof ls.getItem === "function" && typeof ls.setItem === "function" && typeof ls.removeItem === "function") {
|
|
21
|
+
return ls;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Node / server runtimes without a persistent store: return null so the
|
|
25
|
+
// SDK falls back to in-memory only. Integrators who want cross-restart
|
|
26
|
+
// persistence in those environments pass an explicit adapter.
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
export function createMemoryStorage() {
|
|
30
|
+
const map = new Map();
|
|
31
|
+
return {
|
|
32
|
+
getItem: (key) => map.get(key) ?? null,
|
|
33
|
+
setItem: (key, value) => { map.set(key, value); },
|
|
34
|
+
removeItem: (key) => { map.delete(key); },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the scoped storage key for a persisted Compose Key token. The shape
|
|
39
|
+
* `compose.sdk.token:<lower-address>:<chainId>` mirrors the convention already
|
|
40
|
+
* in use by web/ so multiple SDK instances + the bare web app never collide.
|
|
41
|
+
*/
|
|
42
|
+
export function buildTokenStorageKey(tokenScope, userAddress, chainId) {
|
|
43
|
+
return `${tokenScope}:${userAddress.toLowerCase()}:${chainId}`;
|
|
44
|
+
}
|
|
45
|
+
export const DEFAULT_TOKEN_SCOPE = "compose.sdk.token";
|
|
46
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH,MAAM,UAAU,cAAc,CAAC,QAAoC;IAC/D,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,mEAAmE;IACnE,MAAM,SAAS,GAAI,UAAyC,CAAC,YAAY,CAAC;IAC1E,IAAI,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC7C,MAAM,EAAE,GAAG,SAA2B,CAAC;QACvC,IAAI,OAAO,EAAE,CAAC,OAAO,KAAK,UAAU,IAAI,OAAO,EAAE,CAAC,OAAO,KAAK,UAAU,IAAI,OAAO,EAAE,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YAC9G,OAAO,EAAE,CAAC;QACd,CAAC;IACL,CAAC;IAED,wEAAwE;IACxE,uEAAuE;IACvE,8DAA8D;IAC9D,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,mBAAmB;IAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,OAAO;QACH,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI;QACtC,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QACjD,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;KAC5C,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAkB,EAAE,WAAmB,EAAE,OAAe;IACzF,OAAO,GAAG,UAAU,IAAI,WAAW,CAAC,WAAW,EAAE,IAAI,OAAO,EAAE,CAAC;AACnE,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract session-budget state from response headers.
|
|
3
|
+
*
|
|
4
|
+
* The api.compose.market gateway emits the live session-budget ledger on every
|
|
5
|
+
* billable response via these headers:
|
|
6
|
+
* - `x-session-budget-limit` total budget (wei, string)
|
|
7
|
+
* - `x-session-budget-used` spent (wei, string)
|
|
8
|
+
* - `x-session-budget-locked` reserved (wei, string)
|
|
9
|
+
* - `x-session-budget-remaining` remaining (wei, string)
|
|
10
|
+
* - `x-compose-session-invalid` truthy string when the session must be torn
|
|
11
|
+
* down (e.g. "budget-depleted", "expired")
|
|
12
|
+
*
|
|
13
|
+
* This helper converts them into a typed object for the SDK event bus.
|
|
14
|
+
*/
|
|
15
|
+
import type { SessionBudgetSnapshot, SessionInvalidReason } from "../types/index.js";
|
|
16
|
+
export declare function extractSessionBudgetFromResponse(response: {
|
|
17
|
+
headers: Headers;
|
|
18
|
+
}): {
|
|
19
|
+
budget: SessionBudgetSnapshot | null;
|
|
20
|
+
sessionInvalidReason: SessionInvalidReason | null;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../src/streaming/budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAErF,wBAAgB,gCAAgC,CAC5C,QAAQ,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GAC/B;IAAE,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAAC,oBAAoB,EAAE,oBAAoB,GAAG,IAAI,CAAA;CAAE,CA2B7F"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract session-budget state from response headers.
|
|
3
|
+
*
|
|
4
|
+
* The api.compose.market gateway emits the live session-budget ledger on every
|
|
5
|
+
* billable response via these headers:
|
|
6
|
+
* - `x-session-budget-limit` total budget (wei, string)
|
|
7
|
+
* - `x-session-budget-used` spent (wei, string)
|
|
8
|
+
* - `x-session-budget-locked` reserved (wei, string)
|
|
9
|
+
* - `x-session-budget-remaining` remaining (wei, string)
|
|
10
|
+
* - `x-compose-session-invalid` truthy string when the session must be torn
|
|
11
|
+
* down (e.g. "budget-depleted", "expired")
|
|
12
|
+
*
|
|
13
|
+
* This helper converts them into a typed object for the SDK event bus.
|
|
14
|
+
*/
|
|
15
|
+
export function extractSessionBudgetFromResponse(response) {
|
|
16
|
+
const limit = response.headers.get("x-session-budget-limit");
|
|
17
|
+
const used = response.headers.get("x-session-budget-used");
|
|
18
|
+
const locked = response.headers.get("x-session-budget-locked");
|
|
19
|
+
const remaining = response.headers.get("x-session-budget-remaining");
|
|
20
|
+
const invalidRaw = response.headers.get("x-compose-session-invalid");
|
|
21
|
+
const sessionInvalidReason = typeof invalidRaw === "string" && invalidRaw.length > 0
|
|
22
|
+
? invalidRaw
|
|
23
|
+
: null;
|
|
24
|
+
// Emit the budget snapshot only when at least one numeric header is
|
|
25
|
+
// present. Any non-parseable value falls back to null so downstream
|
|
26
|
+
// consumers see `null` rather than NaN.
|
|
27
|
+
if (limit === null && used === null && locked === null && remaining === null) {
|
|
28
|
+
return { budget: null, sessionInvalidReason };
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
budget: {
|
|
32
|
+
limitWei: limit ?? null,
|
|
33
|
+
usedWei: used ?? null,
|
|
34
|
+
lockedWei: locked ?? null,
|
|
35
|
+
remainingWei: remaining ?? null,
|
|
36
|
+
},
|
|
37
|
+
sessionInvalidReason,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/streaming/budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,MAAM,UAAU,gCAAgC,CAC5C,QAA8B;IAE9B,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAErE,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IACrE,MAAM,oBAAoB,GAAG,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAChF,CAAC,CAAE,UAAmC;QACtC,CAAC,CAAC,IAAI,CAAC;IAEX,oEAAoE;IACpE,oEAAoE;IACpE,wCAAwC;IACxC,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QAC3E,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC;IAClD,CAAC;IAED,OAAO;QACH,MAAM,EAAE;YACJ,QAAQ,EAAE,KAAK,IAAI,IAAI;YACvB,OAAO,EAAE,IAAI,IAAI,IAAI;YACrB,SAAS,EAAE,MAAM,IAAI,IAAI;YACzB,YAAY,EAAE,SAAS,IAAI,IAAI;SAClC;QACD,oBAAoB;KACvB,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receipt helpers.
|
|
3
|
+
*
|
|
4
|
+
* `X-Compose-Receipt` is a url-safe base64 blob carrying an authoritative
|
|
5
|
+
* settlement breakdown. The same shape is also emitted in streams as an SSE
|
|
6
|
+
* `event: compose.receipt` frame and echoed as `compose_receipt` in JSON
|
|
7
|
+
* response bodies.
|
|
8
|
+
*/
|
|
9
|
+
import type { ComposeReceipt } from "../types/index.js";
|
|
10
|
+
export declare const RECEIPT_HEADER_NAMES: readonly ["x-compose-receipt", "X-Compose-Receipt"];
|
|
11
|
+
export declare function decodeReceiptHeader(value: string): ComposeReceipt;
|
|
12
|
+
/**
|
|
13
|
+
* Extract a receipt from a Fetch `Response`. Preference order:
|
|
14
|
+
* 1. `X-Compose-Receipt` header (binary truth)
|
|
15
|
+
* 2. `compose_receipt` field on the JSON body (fallback mirror)
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractReceiptFromResponse(response: {
|
|
18
|
+
headers: Headers;
|
|
19
|
+
}, body?: Record<string, unknown> | null): ComposeReceipt | null;
|
|
20
|
+
/**
|
|
21
|
+
* Parse the payload of an SSE `event: compose.receipt` frame. Throws on
|
|
22
|
+
* malformed JSON so callers can abort the stream consumer.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseReceiptEvent(data: string): ComposeReceipt;
|
|
25
|
+
//# sourceMappingURL=receipt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../../src/streaming/receipt.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,eAAO,MAAM,oBAAoB,qDAAsD,CAAC;AAmBxF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAEjE;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACtC,QAAQ,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,EAC9B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACtC,cAAc,GAAG,IAAI,CAevB;AAuBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAoB9D"}
|