@secondlayer/sdk 5.9.0 → 6.1.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/README.md +62 -6
- package/dist/index.d.ts +165 -36
- package/dist/index.js +285 -302
- package/dist/index.js.map +15 -14
- package/dist/streams/index.d.ts +162 -35
- package/dist/streams/index.js +343 -226
- package/dist/streams/index.js.map +12 -9
- package/dist/subgraphs/index.d.ts +135 -14
- package/dist/subgraphs/index.js +204 -141
- package/dist/subgraphs/index.js.map +10 -9
- package/package.json +2 -2
package/dist/streams/index.js
CHANGED
|
@@ -1,7 +1,205 @@
|
|
|
1
1
|
// src/streams/client.ts
|
|
2
2
|
import { ed25519 } from "@secondlayer/shared";
|
|
3
3
|
|
|
4
|
+
// src/errors.ts
|
|
5
|
+
class ApiError extends Error {
|
|
6
|
+
status;
|
|
7
|
+
body;
|
|
8
|
+
code;
|
|
9
|
+
constructor(status, message, body, code) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.body = body;
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.name = "ApiError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class VersionConflictError extends ApiError {
|
|
19
|
+
currentVersion;
|
|
20
|
+
expectedVersion;
|
|
21
|
+
constructor(currentVersion, expectedVersion, message = `Version conflict: expected ${expectedVersion}, current ${currentVersion}`) {
|
|
22
|
+
super(409, message, { currentVersion, expectedVersion });
|
|
23
|
+
this.currentVersion = currentVersion;
|
|
24
|
+
this.expectedVersion = expectedVersion;
|
|
25
|
+
this.name = "VersionConflictError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/base.ts
|
|
30
|
+
var DEFAULT_BASE_URL = "https://api.secondlayer.tools";
|
|
31
|
+
function buildQuery(params) {
|
|
32
|
+
const search = new URLSearchParams;
|
|
33
|
+
for (const [name, value] of Object.entries(params)) {
|
|
34
|
+
if (value === undefined || value === null)
|
|
35
|
+
continue;
|
|
36
|
+
const serialized = Array.isArray(value) ? value.join(",") : String(value);
|
|
37
|
+
if (serialized.length === 0)
|
|
38
|
+
continue;
|
|
39
|
+
search.set(name, serialized);
|
|
40
|
+
}
|
|
41
|
+
const query = search.toString();
|
|
42
|
+
return query ? `?${query}` : "";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class BaseClient {
|
|
46
|
+
baseUrl;
|
|
47
|
+
apiKey;
|
|
48
|
+
origin;
|
|
49
|
+
constructor(options = {}) {
|
|
50
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
51
|
+
this.apiKey = options.apiKey;
|
|
52
|
+
this.origin = options.origin ?? "cli";
|
|
53
|
+
}
|
|
54
|
+
static authHeaders(apiKey) {
|
|
55
|
+
const headers = {
|
|
56
|
+
"Content-Type": "application/json"
|
|
57
|
+
};
|
|
58
|
+
if (apiKey) {
|
|
59
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
60
|
+
}
|
|
61
|
+
return headers;
|
|
62
|
+
}
|
|
63
|
+
async request(method, path, body) {
|
|
64
|
+
const response = await this.fetchResponse(method, path, body);
|
|
65
|
+
if (response.status === 204) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
return response.json();
|
|
69
|
+
}
|
|
70
|
+
async requestText(method, path, body) {
|
|
71
|
+
const response = await this.fetchResponse(method, path, body);
|
|
72
|
+
return response.text();
|
|
73
|
+
}
|
|
74
|
+
async fetchResponse(method, path, body) {
|
|
75
|
+
const url = `${this.baseUrl}${path}`;
|
|
76
|
+
const headers = BaseClient.authHeaders(this.apiKey);
|
|
77
|
+
headers["x-sl-origin"] = this.origin;
|
|
78
|
+
let serializedBody;
|
|
79
|
+
if (body !== undefined && body !== null) {
|
|
80
|
+
try {
|
|
81
|
+
serializedBody = JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
84
|
+
throw new ApiError(0, `Failed to serialize request body: ${detail}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let response;
|
|
88
|
+
try {
|
|
89
|
+
response = await fetch(url, {
|
|
90
|
+
method,
|
|
91
|
+
headers,
|
|
92
|
+
body: serializedBody
|
|
93
|
+
});
|
|
94
|
+
} catch {
|
|
95
|
+
throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`);
|
|
96
|
+
}
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
if (response.status === 401) {
|
|
99
|
+
throw new ApiError(401, "API key invalid or expired.");
|
|
100
|
+
}
|
|
101
|
+
if (response.status === 429) {
|
|
102
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
103
|
+
const msg = retryAfter ? `Rate limited. Wait ${retryAfter} seconds.` : "Rate limited. Try again later.";
|
|
104
|
+
throw new ApiError(429, msg);
|
|
105
|
+
}
|
|
106
|
+
if (response.status >= 500) {
|
|
107
|
+
throw new ApiError(response.status, `Server error. Try again or check status at ${this.baseUrl}/health`);
|
|
108
|
+
}
|
|
109
|
+
const errorBody = await response.text();
|
|
110
|
+
let message = `HTTP ${response.status}`;
|
|
111
|
+
let parsedBody = errorBody;
|
|
112
|
+
let code;
|
|
113
|
+
try {
|
|
114
|
+
const json = JSON.parse(errorBody);
|
|
115
|
+
parsedBody = json;
|
|
116
|
+
const err = json.error ?? json.message;
|
|
117
|
+
if (typeof err === "string") {
|
|
118
|
+
message = err;
|
|
119
|
+
} else if (err && typeof err === "object") {
|
|
120
|
+
message = JSON.stringify(err);
|
|
121
|
+
}
|
|
122
|
+
if (typeof json.code === "string") {
|
|
123
|
+
code = json.code;
|
|
124
|
+
}
|
|
125
|
+
} catch {
|
|
126
|
+
if (errorBody)
|
|
127
|
+
message = errorBody;
|
|
128
|
+
}
|
|
129
|
+
throw new ApiError(response.status, message, parsedBody, code);
|
|
130
|
+
}
|
|
131
|
+
return response;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/streams/errors.ts
|
|
136
|
+
class AuthError extends Error {
|
|
137
|
+
status = 401;
|
|
138
|
+
constructor(message = "API key invalid or expired.") {
|
|
139
|
+
super(message);
|
|
140
|
+
this.name = "AuthError";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
class RateLimitError extends Error {
|
|
145
|
+
retryAfter;
|
|
146
|
+
status = 429;
|
|
147
|
+
constructor(message = "Rate limited. Try again later.", retryAfter) {
|
|
148
|
+
super(message);
|
|
149
|
+
this.retryAfter = retryAfter;
|
|
150
|
+
this.name = "RateLimitError";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
class ValidationError extends Error {
|
|
155
|
+
status;
|
|
156
|
+
body;
|
|
157
|
+
constructor(message, status, body) {
|
|
158
|
+
super(message);
|
|
159
|
+
this.status = status;
|
|
160
|
+
this.body = body;
|
|
161
|
+
this.name = "ValidationError";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
class StreamsServerError extends Error {
|
|
166
|
+
status;
|
|
167
|
+
body;
|
|
168
|
+
constructor(message, status, body) {
|
|
169
|
+
super(message);
|
|
170
|
+
this.status = status;
|
|
171
|
+
this.body = body;
|
|
172
|
+
this.name = "StreamsServerError";
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
class StreamsSignatureError extends Error {
|
|
177
|
+
constructor(message = "Streams response signature verification failed.") {
|
|
178
|
+
super(message);
|
|
179
|
+
this.name = "StreamsSignatureError";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/streams/cursor.ts
|
|
184
|
+
var Cursor = {
|
|
185
|
+
atHeight(height) {
|
|
186
|
+
return `${height}:0`;
|
|
187
|
+
},
|
|
188
|
+
parse(cursor) {
|
|
189
|
+
const parts = cursor.split(":");
|
|
190
|
+
const blockHeight = Number(parts[0]);
|
|
191
|
+
const eventIndex = Number(parts[1]);
|
|
192
|
+
if (parts.length !== 2 || !Number.isInteger(blockHeight) || !Number.isInteger(eventIndex)) {
|
|
193
|
+
throw new ValidationError(`Invalid stream cursor "${cursor}"; expected "<block>:<index>" (e.g. "951475:3").`, 400);
|
|
194
|
+
}
|
|
195
|
+
return { blockHeight, eventIndex };
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
4
199
|
// src/streams/consumer.ts
|
|
200
|
+
function reorgKey(reorg) {
|
|
201
|
+
return `${reorg.detected_at}|${reorg.fork_point_height}|${reorg.new_canonical_tip}`;
|
|
202
|
+
}
|
|
5
203
|
async function defaultSleep(ms, signal) {
|
|
6
204
|
if (signal?.aborted)
|
|
7
205
|
return;
|
|
@@ -18,10 +216,12 @@ async function defaultSleep(ms, signal) {
|
|
|
18
216
|
async function consumeStreamsEvents(opts) {
|
|
19
217
|
const sleep = opts.sleep ?? defaultSleep;
|
|
20
218
|
const mode = opts.mode ?? "tail";
|
|
219
|
+
const finalizedOnly = opts.finalizedOnly ?? false;
|
|
21
220
|
const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
|
|
22
221
|
const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
|
|
23
222
|
const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
|
|
24
223
|
let cursor = opts.fromCursor ?? null;
|
|
224
|
+
const handledReorgs = new Set;
|
|
25
225
|
let pages = 0;
|
|
26
226
|
let emptyPolls = 0;
|
|
27
227
|
while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
|
|
@@ -29,20 +229,39 @@ async function consumeStreamsEvents(opts) {
|
|
|
29
229
|
cursor,
|
|
30
230
|
limit: opts.batchSize,
|
|
31
231
|
types: opts.types,
|
|
232
|
+
notTypes: opts.notTypes,
|
|
32
233
|
contractId: opts.contractId,
|
|
33
234
|
sender: opts.sender,
|
|
34
235
|
recipient: opts.recipient,
|
|
35
236
|
assetIdentifier: opts.assetIdentifier
|
|
36
237
|
});
|
|
37
238
|
pages++;
|
|
38
|
-
|
|
39
|
-
|
|
239
|
+
if (!finalizedOnly && opts.onReorg) {
|
|
240
|
+
const fresh = envelope.reorgs.filter((reorg) => !handledReorgs.has(reorgKey(reorg))).sort((a, b) => a.fork_point_height - b.fork_point_height);
|
|
241
|
+
if (fresh.length > 0) {
|
|
242
|
+
const forkPoint = Math.min(...fresh.map((reorg) => reorg.fork_point_height));
|
|
243
|
+
const rewind = Cursor.atHeight(forkPoint);
|
|
244
|
+
for (const reorg of fresh) {
|
|
245
|
+
await opts.onReorg(reorg, { cursor: rewind });
|
|
246
|
+
handledReorgs.add(reorgKey(reorg));
|
|
247
|
+
}
|
|
248
|
+
cursor = rewind;
|
|
249
|
+
emptyPolls = 0;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const emitted = finalizedOnly ? envelope.events.filter((event) => event.finalized) : envelope.events;
|
|
254
|
+
const checkpoint = finalizedOnly ? emitted.at(-1)?.cursor ?? cursor : envelope.next_cursor;
|
|
255
|
+
const returnedCursor = await opts.onBatch(emitted, envelope, {
|
|
256
|
+
cursor: checkpoint
|
|
257
|
+
});
|
|
258
|
+
const nextCursor = returnedCursor ?? checkpoint;
|
|
40
259
|
if (nextCursor && nextCursor !== cursor) {
|
|
41
260
|
cursor = nextCursor;
|
|
42
261
|
emptyPolls = 0;
|
|
43
262
|
continue;
|
|
44
263
|
}
|
|
45
|
-
if (
|
|
264
|
+
if (emitted.length === 0) {
|
|
46
265
|
emptyPolls++;
|
|
47
266
|
if (mode === "bounded") {
|
|
48
267
|
return { cursor, pages, emptyPolls };
|
|
@@ -67,6 +286,7 @@ async function* streamStreamsEvents(opts) {
|
|
|
67
286
|
cursor,
|
|
68
287
|
limit: opts.batchSize,
|
|
69
288
|
types: opts.types,
|
|
289
|
+
notTypes: opts.notTypes,
|
|
70
290
|
contractId: opts.contractId,
|
|
71
291
|
sender: opts.sender,
|
|
72
292
|
recipient: opts.recipient,
|
|
@@ -97,56 +317,6 @@ async function* streamStreamsEvents(opts) {
|
|
|
97
317
|
|
|
98
318
|
// src/streams/dumps.ts
|
|
99
319
|
import { createHash } from "node:crypto";
|
|
100
|
-
|
|
101
|
-
// src/streams/errors.ts
|
|
102
|
-
class AuthError extends Error {
|
|
103
|
-
status = 401;
|
|
104
|
-
constructor(message = "API key invalid or expired.") {
|
|
105
|
-
super(message);
|
|
106
|
-
this.name = "AuthError";
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
class RateLimitError extends Error {
|
|
111
|
-
retryAfter;
|
|
112
|
-
status = 429;
|
|
113
|
-
constructor(message = "Rate limited. Try again later.", retryAfter) {
|
|
114
|
-
super(message);
|
|
115
|
-
this.retryAfter = retryAfter;
|
|
116
|
-
this.name = "RateLimitError";
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
class ValidationError extends Error {
|
|
121
|
-
status;
|
|
122
|
-
body;
|
|
123
|
-
constructor(message, status, body) {
|
|
124
|
-
super(message);
|
|
125
|
-
this.status = status;
|
|
126
|
-
this.body = body;
|
|
127
|
-
this.name = "ValidationError";
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
class StreamsServerError extends Error {
|
|
132
|
-
status;
|
|
133
|
-
body;
|
|
134
|
-
constructor(message, status, body) {
|
|
135
|
-
super(message);
|
|
136
|
-
this.status = status;
|
|
137
|
-
this.body = body;
|
|
138
|
-
this.name = "StreamsServerError";
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
class StreamsSignatureError extends Error {
|
|
143
|
-
constructor(message = "Streams response signature verification failed.") {
|
|
144
|
-
super(message);
|
|
145
|
-
this.name = "StreamsSignatureError";
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// src/streams/dumps.ts
|
|
150
320
|
function createStreamsDumps(opts) {
|
|
151
321
|
const baseUrl = opts.baseUrl?.replace(/\/+$/, "");
|
|
152
322
|
function requireBaseUrl() {
|
|
@@ -185,8 +355,12 @@ function createStreamsDumps(opts) {
|
|
|
185
355
|
function cursorTuple(cursor) {
|
|
186
356
|
if (!cursor)
|
|
187
357
|
return [-1, -1];
|
|
188
|
-
const
|
|
189
|
-
|
|
358
|
+
const parts = cursor.split(":");
|
|
359
|
+
const [block, index] = parts.map(Number);
|
|
360
|
+
if (parts.length !== 2 || !Number.isInteger(block) || !Number.isInteger(index)) {
|
|
361
|
+
throw new ValidationError(`Invalid stream cursor "${cursor}"; expected "<block>:<index>" (e.g. "951475:3").`, 400);
|
|
362
|
+
}
|
|
363
|
+
return [block, index];
|
|
190
364
|
}
|
|
191
365
|
function maxCursor(a, b) {
|
|
192
366
|
const [ah, ai] = cursorTuple(a);
|
|
@@ -197,11 +371,6 @@ var DEFAULT_STREAMS_BASE_URL = "https://api.secondlayer.tools";
|
|
|
197
371
|
function normalizeBaseUrl(baseUrl) {
|
|
198
372
|
return baseUrl.replace(/\/+$/, "");
|
|
199
373
|
}
|
|
200
|
-
function appendSearchParam(params, name, value) {
|
|
201
|
-
if (value === undefined || value === null)
|
|
202
|
-
return;
|
|
203
|
-
params.set(name, String(value));
|
|
204
|
-
}
|
|
205
374
|
async function responseBody(response) {
|
|
206
375
|
const text = await response.text();
|
|
207
376
|
if (text.length === 0)
|
|
@@ -245,13 +414,16 @@ function createStreamsClient(options) {
|
|
|
245
414
|
baseUrl: options.dumpsBaseUrl,
|
|
246
415
|
fetchImpl
|
|
247
416
|
});
|
|
248
|
-
let
|
|
249
|
-
function
|
|
250
|
-
if (
|
|
251
|
-
return
|
|
252
|
-
|
|
417
|
+
let keyPromise = null;
|
|
418
|
+
function loadKey() {
|
|
419
|
+
if (keyPromise)
|
|
420
|
+
return keyPromise;
|
|
421
|
+
keyPromise = (async () => {
|
|
253
422
|
if (typeof verify === "object") {
|
|
254
|
-
return
|
|
423
|
+
return {
|
|
424
|
+
keyId: ed25519.ed25519KeyId(verify.publicKey),
|
|
425
|
+
publicKey: ed25519.loadEd25519PublicKey(verify.publicKey)
|
|
426
|
+
};
|
|
255
427
|
}
|
|
256
428
|
const res = await fetchImpl(`${baseUrl}/public/streams/signing-key`);
|
|
257
429
|
if (!res.ok) {
|
|
@@ -261,9 +433,12 @@ function createStreamsClient(options) {
|
|
|
261
433
|
if (!body.public_key_pem) {
|
|
262
434
|
throw new StreamsSignatureError("Signing key response missing key.");
|
|
263
435
|
}
|
|
264
|
-
return
|
|
436
|
+
return {
|
|
437
|
+
keyId: body.key_id ?? ed25519.ed25519KeyId(body.public_key_pem),
|
|
438
|
+
publicKey: ed25519.loadEd25519PublicKey(body.public_key_pem)
|
|
439
|
+
};
|
|
265
440
|
})();
|
|
266
|
-
return
|
|
441
|
+
return keyPromise;
|
|
267
442
|
}
|
|
268
443
|
async function request(path) {
|
|
269
444
|
const response = await fetchImpl(`${baseUrl}${path}`, {
|
|
@@ -277,8 +452,19 @@ function createStreamsClient(options) {
|
|
|
277
452
|
if (!signature) {
|
|
278
453
|
throw new StreamsSignatureError("Response is missing X-Signature.");
|
|
279
454
|
}
|
|
280
|
-
const
|
|
281
|
-
|
|
455
|
+
const responseKeyId = response.headers.get("X-Signature-KeyId");
|
|
456
|
+
let key = await loadKey();
|
|
457
|
+
if (responseKeyId && responseKeyId !== key.keyId) {
|
|
458
|
+
if (typeof verify === "object") {
|
|
459
|
+
throw new StreamsSignatureError(`Response signed with key '${responseKeyId}', expected pinned key '${key.keyId}'.`);
|
|
460
|
+
}
|
|
461
|
+
keyPromise = null;
|
|
462
|
+
key = await loadKey();
|
|
463
|
+
if (responseKeyId !== key.keyId) {
|
|
464
|
+
throw new StreamsSignatureError(`Response signed with key '${responseKeyId}' not served by the signing-key endpoint.`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
if (!ed25519.verifyEd25519(text, signature, key.publicKey)) {
|
|
282
468
|
throw new StreamsSignatureError;
|
|
283
469
|
}
|
|
284
470
|
}
|
|
@@ -288,6 +474,7 @@ function createStreamsClient(options) {
|
|
|
288
474
|
cursor,
|
|
289
475
|
limit,
|
|
290
476
|
types,
|
|
477
|
+
notTypes,
|
|
291
478
|
contractId,
|
|
292
479
|
sender,
|
|
293
480
|
recipient,
|
|
@@ -297,6 +484,7 @@ function createStreamsClient(options) {
|
|
|
297
484
|
cursor,
|
|
298
485
|
limit,
|
|
299
486
|
types,
|
|
487
|
+
notTypes,
|
|
300
488
|
contractId,
|
|
301
489
|
sender,
|
|
302
490
|
recipient,
|
|
@@ -304,20 +492,18 @@ function createStreamsClient(options) {
|
|
|
304
492
|
});
|
|
305
493
|
};
|
|
306
494
|
async function listEvents(params = {}) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
}
|
|
319
|
-
const query = searchParams.toString();
|
|
320
|
-
return request(`/v1/streams/events${query ? `?${query}` : ""}`);
|
|
495
|
+
return request(`/v1/streams/events${buildQuery({
|
|
496
|
+
cursor: params.cursor,
|
|
497
|
+
from_height: params.fromHeight,
|
|
498
|
+
to_height: params.toHeight,
|
|
499
|
+
limit: params.limit,
|
|
500
|
+
contract_id: params.contractId,
|
|
501
|
+
sender: params.sender,
|
|
502
|
+
recipient: params.recipient,
|
|
503
|
+
asset_identifier: params.assetIdentifier,
|
|
504
|
+
types: params.types,
|
|
505
|
+
not_types: params.notTypes
|
|
506
|
+
})}`);
|
|
321
507
|
}
|
|
322
508
|
return {
|
|
323
509
|
events: {
|
|
@@ -329,7 +515,9 @@ function createStreamsClient(options) {
|
|
|
329
515
|
return consumeStreamsEvents({
|
|
330
516
|
fromCursor: params.fromCursor,
|
|
331
517
|
mode: params.mode,
|
|
518
|
+
finalizedOnly: params.finalizedOnly,
|
|
332
519
|
types: params.types,
|
|
520
|
+
notTypes: params.notTypes,
|
|
333
521
|
contractId: params.contractId,
|
|
334
522
|
sender: params.sender,
|
|
335
523
|
recipient: params.recipient,
|
|
@@ -337,6 +525,7 @@ function createStreamsClient(options) {
|
|
|
337
525
|
batchSize: params.batchSize ?? 100,
|
|
338
526
|
fetchEvents,
|
|
339
527
|
onBatch: params.onBatch,
|
|
528
|
+
onReorg: params.onReorg,
|
|
340
529
|
emptyBackoffMs: params.emptyBackoffMs,
|
|
341
530
|
maxPages: params.maxPages,
|
|
342
531
|
maxEmptyPolls: params.maxEmptyPolls,
|
|
@@ -347,6 +536,7 @@ function createStreamsClient(options) {
|
|
|
347
536
|
return streamStreamsEvents({
|
|
348
537
|
fromCursor: params.fromCursor,
|
|
349
538
|
types: params.types,
|
|
539
|
+
notTypes: params.notTypes,
|
|
350
540
|
contractId: params.contractId,
|
|
351
541
|
sender: params.sender,
|
|
352
542
|
recipient: params.recipient,
|
|
@@ -390,11 +580,10 @@ function createStreamsClient(options) {
|
|
|
390
580
|
},
|
|
391
581
|
reorgs: {
|
|
392
582
|
list(params) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
return request(`/v1/streams/reorgs${query ? `?${query}` : ""}`);
|
|
583
|
+
return request(`/v1/streams/reorgs${buildQuery({
|
|
584
|
+
since: params.since,
|
|
585
|
+
limit: params.limit
|
|
586
|
+
})}`);
|
|
398
587
|
}
|
|
399
588
|
},
|
|
400
589
|
dumps,
|
|
@@ -406,127 +595,8 @@ function createStreamsClient(options) {
|
|
|
406
595
|
}
|
|
407
596
|
};
|
|
408
597
|
}
|
|
409
|
-
// src/streams/ft-transfer.ts
|
|
410
|
-
function requireString(payload, field) {
|
|
411
|
-
const value = payload[field];
|
|
412
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
413
|
-
throw new Error(`ft_transfer payload missing ${field}`);
|
|
414
|
-
}
|
|
415
|
-
return value;
|
|
416
|
-
}
|
|
417
|
-
function parseAssetIdentifier(assetIdentifier) {
|
|
418
|
-
const [contractId, tokenName] = assetIdentifier.split("::");
|
|
419
|
-
if (!contractId) {
|
|
420
|
-
throw new Error("ft_transfer payload has malformed asset_identifier");
|
|
421
|
-
}
|
|
422
|
-
return {
|
|
423
|
-
contract_id: contractId,
|
|
424
|
-
token_name: tokenName && tokenName.length > 0 ? tokenName : null
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
function isFtTransfer(event) {
|
|
428
|
-
return event.event_type === "ft_transfer";
|
|
429
|
-
}
|
|
430
|
-
function decodeFtTransfer(event) {
|
|
431
|
-
if (!isFtTransfer(event)) {
|
|
432
|
-
throw new Error(`Expected ft_transfer event, got ${event.event_type}`);
|
|
433
|
-
}
|
|
434
|
-
const payload = event.payload;
|
|
435
|
-
const assetIdentifier = requireString(payload, "asset_identifier");
|
|
436
|
-
const sender = requireString(payload, "sender");
|
|
437
|
-
const recipient = requireString(payload, "recipient");
|
|
438
|
-
const amount = requireString(payload, "amount");
|
|
439
|
-
if (!/^(0|[1-9]\d*)$/.test(amount)) {
|
|
440
|
-
throw new Error("ft_transfer payload has malformed amount");
|
|
441
|
-
}
|
|
442
|
-
const { contract_id, token_name } = parseAssetIdentifier(assetIdentifier);
|
|
443
|
-
return {
|
|
444
|
-
cursor: event.cursor,
|
|
445
|
-
block_height: event.block_height,
|
|
446
|
-
tx_id: event.tx_id,
|
|
447
|
-
tx_index: event.tx_index,
|
|
448
|
-
event_index: event.event_index,
|
|
449
|
-
event_type: event.event_type,
|
|
450
|
-
decoded_payload: {
|
|
451
|
-
asset_identifier: assetIdentifier,
|
|
452
|
-
contract_id: event.contract_id ?? contract_id,
|
|
453
|
-
token_name,
|
|
454
|
-
sender,
|
|
455
|
-
recipient,
|
|
456
|
-
amount
|
|
457
|
-
},
|
|
458
|
-
source_cursor: event.cursor
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
// src/streams/nft-transfer.ts
|
|
462
|
-
function requireString2(payload, field) {
|
|
463
|
-
const value = payload[field];
|
|
464
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
465
|
-
throw new Error(`nft_transfer payload missing ${field}`);
|
|
466
|
-
}
|
|
467
|
-
return value;
|
|
468
|
-
}
|
|
469
|
-
function requireHexValue(payload) {
|
|
470
|
-
const rawValue = payload.raw_value;
|
|
471
|
-
if (typeof rawValue === "string") {
|
|
472
|
-
if (!/^0x[0-9a-fA-F]*$/.test(rawValue)) {
|
|
473
|
-
throw new Error("nft_transfer payload has malformed value");
|
|
474
|
-
}
|
|
475
|
-
return rawValue;
|
|
476
|
-
}
|
|
477
|
-
const value = payload.value;
|
|
478
|
-
const hex = typeof value === "string" ? value : value && typeof value === "object" && typeof value.hex === "string" ? value.hex : null;
|
|
479
|
-
if (!hex) {
|
|
480
|
-
throw new Error("nft_transfer payload missing value");
|
|
481
|
-
}
|
|
482
|
-
if (!/^0x[0-9a-fA-F]*$/.test(hex)) {
|
|
483
|
-
throw new Error("nft_transfer payload has malformed value");
|
|
484
|
-
}
|
|
485
|
-
return hex;
|
|
486
|
-
}
|
|
487
|
-
function parseAssetIdentifier2(assetIdentifier) {
|
|
488
|
-
const [contractId, tokenName] = assetIdentifier.split("::");
|
|
489
|
-
if (!contractId) {
|
|
490
|
-
throw new Error("nft_transfer payload has malformed asset_identifier");
|
|
491
|
-
}
|
|
492
|
-
return {
|
|
493
|
-
contract_id: contractId,
|
|
494
|
-
token_name: tokenName && tokenName.length > 0 ? tokenName : null
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
function isNftTransfer(event) {
|
|
498
|
-
return event.event_type === "nft_transfer";
|
|
499
|
-
}
|
|
500
|
-
function decodeNftTransfer(event) {
|
|
501
|
-
if (!isNftTransfer(event)) {
|
|
502
|
-
throw new Error(`Expected nft_transfer event, got ${event.event_type}`);
|
|
503
|
-
}
|
|
504
|
-
const payload = event.payload;
|
|
505
|
-
const assetIdentifier = requireString2(payload, "asset_identifier");
|
|
506
|
-
const sender = requireString2(payload, "sender");
|
|
507
|
-
const recipient = requireString2(payload, "recipient");
|
|
508
|
-
const value = requireHexValue(payload);
|
|
509
|
-
const { contract_id, token_name } = parseAssetIdentifier2(assetIdentifier);
|
|
510
|
-
return {
|
|
511
|
-
cursor: event.cursor,
|
|
512
|
-
block_height: event.block_height,
|
|
513
|
-
tx_id: event.tx_id,
|
|
514
|
-
tx_index: event.tx_index,
|
|
515
|
-
event_index: event.event_index,
|
|
516
|
-
event_type: event.event_type,
|
|
517
|
-
decoded_payload: {
|
|
518
|
-
asset_identifier: assetIdentifier,
|
|
519
|
-
contract_id: event.contract_id ?? contract_id,
|
|
520
|
-
token_name,
|
|
521
|
-
sender,
|
|
522
|
-
recipient,
|
|
523
|
-
value
|
|
524
|
-
},
|
|
525
|
-
source_cursor: event.cursor
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
598
|
// src/streams/_payload.ts
|
|
529
|
-
function
|
|
599
|
+
function requireString(payload, field, eventType) {
|
|
530
600
|
const value = payload[field];
|
|
531
601
|
if (typeof value !== "string" || value.length === 0) {
|
|
532
602
|
throw new Error(`${eventType} payload missing ${field}`);
|
|
@@ -537,7 +607,7 @@ function optionalString(value) {
|
|
|
537
607
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
538
608
|
}
|
|
539
609
|
function requireAmountField(payload, field, eventType) {
|
|
540
|
-
const amount =
|
|
610
|
+
const amount = requireString(payload, field, eventType);
|
|
541
611
|
if (!/^(0|[1-9]\d*)$/.test(amount)) {
|
|
542
612
|
throw new Error(`${eventType} payload has malformed ${field}`);
|
|
543
613
|
}
|
|
@@ -546,7 +616,7 @@ function requireAmountField(payload, field, eventType) {
|
|
|
546
616
|
function requireAmount(payload, eventType) {
|
|
547
617
|
return requireAmountField(payload, "amount", eventType);
|
|
548
618
|
}
|
|
549
|
-
function
|
|
619
|
+
function parseAssetIdentifier(assetIdentifier, eventType) {
|
|
550
620
|
const [contractId, tokenName] = assetIdentifier.split("::");
|
|
551
621
|
if (!contractId) {
|
|
552
622
|
throw new Error(`${eventType} payload has malformed asset_identifier`);
|
|
@@ -556,7 +626,7 @@ function parseAssetIdentifier3(assetIdentifier, eventType) {
|
|
|
556
626
|
token_name: tokenName && tokenName.length > 0 ? tokenName : null
|
|
557
627
|
};
|
|
558
628
|
}
|
|
559
|
-
function
|
|
629
|
+
function requireHexValue(payload, eventType) {
|
|
560
630
|
const rawValue = payload.raw_value;
|
|
561
631
|
if (typeof rawValue === "string") {
|
|
562
632
|
if (!/^0x[0-9a-fA-F]*$/.test(rawValue)) {
|
|
@@ -587,6 +657,52 @@ function decodedRow(event, eventType, decoded_payload) {
|
|
|
587
657
|
};
|
|
588
658
|
}
|
|
589
659
|
|
|
660
|
+
// src/streams/ft-transfer.ts
|
|
661
|
+
function isFtTransfer(event) {
|
|
662
|
+
return event.event_type === "ft_transfer";
|
|
663
|
+
}
|
|
664
|
+
function decodeFtTransfer(event) {
|
|
665
|
+
if (!isFtTransfer(event)) {
|
|
666
|
+
throw new Error(`Expected ft_transfer event, got ${event.event_type}`);
|
|
667
|
+
}
|
|
668
|
+
const payload = event.payload;
|
|
669
|
+
const assetIdentifier = requireString(payload, "asset_identifier", "ft_transfer");
|
|
670
|
+
const sender = requireString(payload, "sender", "ft_transfer");
|
|
671
|
+
const recipient = requireString(payload, "recipient", "ft_transfer");
|
|
672
|
+
const amount = requireAmount(payload, "ft_transfer");
|
|
673
|
+
const { contract_id, token_name } = parseAssetIdentifier(assetIdentifier, "ft_transfer");
|
|
674
|
+
return decodedRow(event, "ft_transfer", {
|
|
675
|
+
asset_identifier: assetIdentifier,
|
|
676
|
+
contract_id: event.contract_id ?? contract_id,
|
|
677
|
+
token_name,
|
|
678
|
+
sender,
|
|
679
|
+
recipient,
|
|
680
|
+
amount
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
// src/streams/nft-transfer.ts
|
|
684
|
+
function isNftTransfer(event) {
|
|
685
|
+
return event.event_type === "nft_transfer";
|
|
686
|
+
}
|
|
687
|
+
function decodeNftTransfer(event) {
|
|
688
|
+
if (!isNftTransfer(event)) {
|
|
689
|
+
throw new Error(`Expected nft_transfer event, got ${event.event_type}`);
|
|
690
|
+
}
|
|
691
|
+
const payload = event.payload;
|
|
692
|
+
const assetIdentifier = requireString(payload, "asset_identifier", "nft_transfer");
|
|
693
|
+
const sender = requireString(payload, "sender", "nft_transfer");
|
|
694
|
+
const recipient = requireString(payload, "recipient", "nft_transfer");
|
|
695
|
+
const value = requireHexValue(payload, "nft_transfer");
|
|
696
|
+
const { contract_id, token_name } = parseAssetIdentifier(assetIdentifier, "nft_transfer");
|
|
697
|
+
return decodedRow(event, "nft_transfer", {
|
|
698
|
+
asset_identifier: assetIdentifier,
|
|
699
|
+
contract_id: event.contract_id ?? contract_id,
|
|
700
|
+
token_name,
|
|
701
|
+
sender,
|
|
702
|
+
recipient,
|
|
703
|
+
value
|
|
704
|
+
});
|
|
705
|
+
}
|
|
590
706
|
// src/streams/stx-events.ts
|
|
591
707
|
function isStxTransfer(event) {
|
|
592
708
|
return event.event_type === "stx_transfer";
|
|
@@ -597,8 +713,8 @@ function decodeStxTransfer(event) {
|
|
|
597
713
|
}
|
|
598
714
|
const payload = event.payload;
|
|
599
715
|
return decodedRow(event, "stx_transfer", {
|
|
600
|
-
sender:
|
|
601
|
-
recipient:
|
|
716
|
+
sender: requireString(payload, "sender", "stx_transfer"),
|
|
717
|
+
recipient: requireString(payload, "recipient", "stx_transfer"),
|
|
602
718
|
amount: requireAmount(payload, "stx_transfer"),
|
|
603
719
|
memo: optionalString(payload.memo)
|
|
604
720
|
});
|
|
@@ -612,7 +728,7 @@ function decodeStxMint(event) {
|
|
|
612
728
|
}
|
|
613
729
|
const payload = event.payload;
|
|
614
730
|
return decodedRow(event, "stx_mint", {
|
|
615
|
-
recipient:
|
|
731
|
+
recipient: requireString(payload, "recipient", "stx_mint"),
|
|
616
732
|
amount: requireAmount(payload, "stx_mint")
|
|
617
733
|
});
|
|
618
734
|
}
|
|
@@ -625,7 +741,7 @@ function decodeStxBurn(event) {
|
|
|
625
741
|
}
|
|
626
742
|
const payload = event.payload;
|
|
627
743
|
return decodedRow(event, "stx_burn", {
|
|
628
|
-
sender:
|
|
744
|
+
sender: requireString(payload, "sender", "stx_burn"),
|
|
629
745
|
amount: requireAmount(payload, "stx_burn")
|
|
630
746
|
});
|
|
631
747
|
}
|
|
@@ -638,15 +754,15 @@ function decodeStxLock(event) {
|
|
|
638
754
|
}
|
|
639
755
|
const payload = event.payload;
|
|
640
756
|
return decodedRow(event, "stx_lock", {
|
|
641
|
-
sender:
|
|
757
|
+
sender: requireString(payload, "locked_address", "stx_lock"),
|
|
642
758
|
amount: requireAmountField(payload, "locked_amount", "stx_lock"),
|
|
643
759
|
payload: { unlock_height: optionalString(payload.unlock_height) }
|
|
644
760
|
});
|
|
645
761
|
}
|
|
646
762
|
// src/streams/token-mint-burn.ts
|
|
647
763
|
function assetFields(event, eventType) {
|
|
648
|
-
const assetIdentifier =
|
|
649
|
-
const { contract_id, token_name } =
|
|
764
|
+
const assetIdentifier = requireString(event.payload, "asset_identifier", eventType);
|
|
765
|
+
const { contract_id, token_name } = parseAssetIdentifier(assetIdentifier, eventType);
|
|
650
766
|
return {
|
|
651
767
|
asset_identifier: assetIdentifier,
|
|
652
768
|
contract_id: event.contract_id ?? contract_id,
|
|
@@ -662,7 +778,7 @@ function decodeFtMint(event) {
|
|
|
662
778
|
}
|
|
663
779
|
return decodedRow(event, "ft_mint", {
|
|
664
780
|
...assetFields(event, "ft_mint"),
|
|
665
|
-
recipient:
|
|
781
|
+
recipient: requireString(event.payload, "recipient", "ft_mint"),
|
|
666
782
|
amount: requireAmount(event.payload, "ft_mint")
|
|
667
783
|
});
|
|
668
784
|
}
|
|
@@ -675,7 +791,7 @@ function decodeFtBurn(event) {
|
|
|
675
791
|
}
|
|
676
792
|
return decodedRow(event, "ft_burn", {
|
|
677
793
|
...assetFields(event, "ft_burn"),
|
|
678
|
-
sender:
|
|
794
|
+
sender: requireString(event.payload, "sender", "ft_burn"),
|
|
679
795
|
amount: requireAmount(event.payload, "ft_burn")
|
|
680
796
|
});
|
|
681
797
|
}
|
|
@@ -688,8 +804,8 @@ function decodeNftMint(event) {
|
|
|
688
804
|
}
|
|
689
805
|
return decodedRow(event, "nft_mint", {
|
|
690
806
|
...assetFields(event, "nft_mint"),
|
|
691
|
-
recipient:
|
|
692
|
-
value:
|
|
807
|
+
recipient: requireString(event.payload, "recipient", "nft_mint"),
|
|
808
|
+
value: requireHexValue(event.payload, "nft_mint")
|
|
693
809
|
});
|
|
694
810
|
}
|
|
695
811
|
function isNftBurn(event) {
|
|
@@ -701,8 +817,8 @@ function decodeNftBurn(event) {
|
|
|
701
817
|
}
|
|
702
818
|
return decodedRow(event, "nft_burn", {
|
|
703
819
|
...assetFields(event, "nft_burn"),
|
|
704
|
-
sender:
|
|
705
|
-
value:
|
|
820
|
+
sender: requireString(event.payload, "sender", "nft_burn"),
|
|
821
|
+
value: requireHexValue(event.payload, "nft_burn")
|
|
706
822
|
});
|
|
707
823
|
}
|
|
708
824
|
// src/clarity.ts
|
|
@@ -801,8 +917,9 @@ export {
|
|
|
801
917
|
StreamsServerError,
|
|
802
918
|
STREAMS_EVENT_TYPES,
|
|
803
919
|
RateLimitError,
|
|
920
|
+
Cursor,
|
|
804
921
|
AuthError
|
|
805
922
|
};
|
|
806
923
|
|
|
807
|
-
//# debugId=
|
|
924
|
+
//# debugId=CA2A679BA194AF3B64756E2164756E21
|
|
808
925
|
//# sourceMappingURL=index.js.map
|