@secondlayer/sdk 3.3.2 → 3.4.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.
@@ -0,0 +1,378 @@
1
+ // src/streams/consumer.ts
2
+ async function defaultSleep(ms, signal) {
3
+ if (signal?.aborted)
4
+ return;
5
+ await new Promise((resolve) => {
6
+ const timeout = setTimeout(resolve, ms);
7
+ if (!signal)
8
+ return;
9
+ signal.addEventListener("abort", () => {
10
+ clearTimeout(timeout);
11
+ resolve();
12
+ }, { once: true });
13
+ });
14
+ }
15
+ async function consumeStreamsEvents(opts) {
16
+ const sleep = opts.sleep ?? defaultSleep;
17
+ const mode = opts.mode ?? "tail";
18
+ const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
19
+ const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
20
+ const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
21
+ let cursor = opts.fromCursor ?? null;
22
+ let pages = 0;
23
+ let emptyPolls = 0;
24
+ while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
25
+ const envelope = await opts.fetchEvents({
26
+ cursor,
27
+ limit: opts.batchSize,
28
+ types: opts.types
29
+ });
30
+ pages++;
31
+ const returnedCursor = await opts.onBatch(envelope.events, envelope);
32
+ const nextCursor = returnedCursor ?? envelope.next_cursor;
33
+ if (nextCursor && nextCursor !== cursor) {
34
+ cursor = nextCursor;
35
+ emptyPolls = 0;
36
+ continue;
37
+ }
38
+ if (envelope.events.length === 0) {
39
+ emptyPolls++;
40
+ if (mode === "bounded") {
41
+ return { cursor, pages, emptyPolls };
42
+ }
43
+ await sleep(emptyBackoffMs, opts.signal);
44
+ continue;
45
+ }
46
+ return { cursor, pages, emptyPolls };
47
+ }
48
+ return { cursor, pages, emptyPolls };
49
+ }
50
+ async function* streamStreamsEvents(opts) {
51
+ const sleep = opts.sleep ?? defaultSleep;
52
+ const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
53
+ const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
54
+ const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
55
+ let cursor = opts.fromCursor ?? null;
56
+ let pages = 0;
57
+ let emptyPolls = 0;
58
+ while (pages < maxPages && emptyPolls < maxEmptyPolls && !opts.signal?.aborted) {
59
+ const envelope = await opts.fetchEvents({
60
+ cursor,
61
+ limit: opts.batchSize,
62
+ types: opts.types
63
+ });
64
+ pages++;
65
+ for (const event of envelope.events) {
66
+ if (opts.signal?.aborted)
67
+ return;
68
+ yield event;
69
+ }
70
+ const nextCursor = envelope.next_cursor;
71
+ if (nextCursor && nextCursor !== cursor) {
72
+ cursor = nextCursor;
73
+ emptyPolls = 0;
74
+ continue;
75
+ }
76
+ if (envelope.events.length === 0) {
77
+ emptyPolls++;
78
+ if (emptyPolls >= maxEmptyPolls || pages >= maxPages)
79
+ return;
80
+ await sleep(emptyBackoffMs, opts.signal);
81
+ continue;
82
+ }
83
+ return;
84
+ }
85
+ }
86
+
87
+ // src/streams/errors.ts
88
+ class AuthError extends Error {
89
+ status = 401;
90
+ constructor(message = "API key invalid or expired.") {
91
+ super(message);
92
+ this.name = "AuthError";
93
+ }
94
+ }
95
+
96
+ class RateLimitError extends Error {
97
+ retryAfter;
98
+ status = 429;
99
+ constructor(message = "Rate limited. Try again later.", retryAfter) {
100
+ super(message);
101
+ this.retryAfter = retryAfter;
102
+ this.name = "RateLimitError";
103
+ }
104
+ }
105
+
106
+ class ValidationError extends Error {
107
+ status;
108
+ body;
109
+ constructor(message, status, body) {
110
+ super(message);
111
+ this.status = status;
112
+ this.body = body;
113
+ this.name = "ValidationError";
114
+ }
115
+ }
116
+
117
+ class StreamsServerError extends Error {
118
+ status;
119
+ body;
120
+ constructor(message, status, body) {
121
+ super(message);
122
+ this.status = status;
123
+ this.body = body;
124
+ this.name = "StreamsServerError";
125
+ }
126
+ }
127
+
128
+ // src/streams/client.ts
129
+ var DEFAULT_STREAMS_BASE_URL = "https://api.secondlayer.tools";
130
+ function normalizeBaseUrl(baseUrl) {
131
+ return baseUrl.replace(/\/+$/, "");
132
+ }
133
+ function appendSearchParam(params, name, value) {
134
+ if (value === undefined || value === null)
135
+ return;
136
+ params.set(name, String(value));
137
+ }
138
+ async function responseBody(response) {
139
+ const text = await response.text();
140
+ if (text.length === 0)
141
+ return;
142
+ try {
143
+ return JSON.parse(text);
144
+ } catch {
145
+ return text;
146
+ }
147
+ }
148
+ function errorMessage(body, fallback) {
149
+ if (body && typeof body === "object") {
150
+ const record = body;
151
+ const message = record.error ?? record.message;
152
+ if (typeof message === "string" && message.length > 0)
153
+ return message;
154
+ }
155
+ if (typeof body === "string" && body.length > 0)
156
+ return body;
157
+ return fallback;
158
+ }
159
+ async function mapStreamsError(response) {
160
+ const body = await responseBody(response);
161
+ if (response.status === 401) {
162
+ throw new AuthError(errorMessage(body, "API key invalid or expired."));
163
+ }
164
+ if (response.status === 429) {
165
+ const retryAfter = response.headers.get("Retry-After") ?? undefined;
166
+ throw new RateLimitError(errorMessage(body, "Rate limited. Try again later."), retryAfter);
167
+ }
168
+ if (response.status >= 500) {
169
+ throw new StreamsServerError(errorMessage(body, `Streams server returned ${response.status}.`), response.status, body);
170
+ }
171
+ throw new ValidationError(errorMessage(body, `Streams request returned ${response.status}.`), response.status, body);
172
+ }
173
+ function createStreamsClient(options) {
174
+ const baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_STREAMS_BASE_URL);
175
+ const fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));
176
+ async function request(path) {
177
+ const response = await fetchImpl(`${baseUrl}${path}`, {
178
+ headers: { Authorization: `Bearer ${options.apiKey}` }
179
+ });
180
+ if (!response.ok)
181
+ await mapStreamsError(response);
182
+ return await response.json();
183
+ }
184
+ const fetchEvents = async ({
185
+ cursor,
186
+ limit,
187
+ types
188
+ }) => {
189
+ return listEvents({ cursor, limit, types });
190
+ };
191
+ async function listEvents(params = {}) {
192
+ const searchParams = new URLSearchParams;
193
+ appendSearchParam(searchParams, "cursor", params.cursor);
194
+ appendSearchParam(searchParams, "from_height", params.fromHeight);
195
+ appendSearchParam(searchParams, "to_height", params.toHeight);
196
+ appendSearchParam(searchParams, "limit", params.limit);
197
+ if (params.types?.length) {
198
+ searchParams.set("types", params.types.join(","));
199
+ }
200
+ const query = searchParams.toString();
201
+ return request(`/v1/streams/events${query ? `?${query}` : ""}`);
202
+ }
203
+ return {
204
+ events: {
205
+ list: listEvents,
206
+ consume(params) {
207
+ return consumeStreamsEvents({
208
+ fromCursor: params.fromCursor,
209
+ mode: params.mode,
210
+ types: params.types,
211
+ batchSize: params.batchSize ?? 100,
212
+ fetchEvents,
213
+ onBatch: params.onBatch,
214
+ emptyBackoffMs: params.emptyBackoffMs,
215
+ maxPages: params.maxPages,
216
+ maxEmptyPolls: params.maxEmptyPolls,
217
+ signal: params.signal
218
+ });
219
+ },
220
+ stream(params = {}) {
221
+ return streamStreamsEvents({
222
+ fromCursor: params.fromCursor,
223
+ types: params.types,
224
+ batchSize: params.batchSize ?? 100,
225
+ emptyBackoffMs: params.emptyBackoffMs,
226
+ maxPages: params.maxPages,
227
+ maxEmptyPolls: params.maxEmptyPolls,
228
+ signal: params.signal,
229
+ fetchEvents
230
+ });
231
+ }
232
+ },
233
+ tip() {
234
+ return request("/v1/streams/tip");
235
+ }
236
+ };
237
+ }
238
+ // src/streams/ft-transfer.ts
239
+ function requireString(payload, field) {
240
+ const value = payload[field];
241
+ if (typeof value !== "string" || value.length === 0) {
242
+ throw new Error(`ft_transfer payload missing ${field}`);
243
+ }
244
+ return value;
245
+ }
246
+ function parseAssetIdentifier(assetIdentifier) {
247
+ const [contractId, tokenName] = assetIdentifier.split("::");
248
+ if (!contractId) {
249
+ throw new Error("ft_transfer payload has malformed asset_identifier");
250
+ }
251
+ return {
252
+ contract_id: contractId,
253
+ token_name: tokenName && tokenName.length > 0 ? tokenName : null
254
+ };
255
+ }
256
+ function isFtTransfer(event) {
257
+ return event.event_type === "ft_transfer";
258
+ }
259
+ function decodeFtTransfer(event) {
260
+ if (!isFtTransfer(event)) {
261
+ throw new Error(`Expected ft_transfer event, got ${event.event_type}`);
262
+ }
263
+ const payload = event.payload;
264
+ const assetIdentifier = requireString(payload, "asset_identifier");
265
+ const sender = requireString(payload, "sender");
266
+ const recipient = requireString(payload, "recipient");
267
+ const amount = requireString(payload, "amount");
268
+ if (!/^(0|[1-9]\d*)$/.test(amount)) {
269
+ throw new Error("ft_transfer payload has malformed amount");
270
+ }
271
+ const { contract_id, token_name } = parseAssetIdentifier(assetIdentifier);
272
+ return {
273
+ cursor: event.cursor,
274
+ block_height: event.block_height,
275
+ tx_id: event.tx_id,
276
+ tx_index: event.tx_index,
277
+ event_index: event.event_index,
278
+ event_type: event.event_type,
279
+ decoded_payload: {
280
+ asset_identifier: assetIdentifier,
281
+ contract_id: event.contract_id ?? contract_id,
282
+ token_name,
283
+ sender,
284
+ recipient,
285
+ amount
286
+ },
287
+ source_cursor: event.cursor
288
+ };
289
+ }
290
+ // src/streams/nft-transfer.ts
291
+ function requireString2(payload, field) {
292
+ const value = payload[field];
293
+ if (typeof value !== "string" || value.length === 0) {
294
+ throw new Error(`nft_transfer payload missing ${field}`);
295
+ }
296
+ return value;
297
+ }
298
+ function requireHexValue(payload) {
299
+ const value = payload.value;
300
+ const hex = typeof value === "string" ? value : value && typeof value === "object" && typeof value.hex === "string" ? value.hex : null;
301
+ if (!hex) {
302
+ throw new Error("nft_transfer payload missing value");
303
+ }
304
+ if (!/^0x[0-9a-fA-F]*$/.test(hex)) {
305
+ throw new Error("nft_transfer payload has malformed value");
306
+ }
307
+ return hex;
308
+ }
309
+ function parseAssetIdentifier2(assetIdentifier) {
310
+ const [contractId, tokenName] = assetIdentifier.split("::");
311
+ if (!contractId) {
312
+ throw new Error("nft_transfer payload has malformed asset_identifier");
313
+ }
314
+ return {
315
+ contract_id: contractId,
316
+ token_name: tokenName && tokenName.length > 0 ? tokenName : null
317
+ };
318
+ }
319
+ function isNftTransfer(event) {
320
+ return event.event_type === "nft_transfer";
321
+ }
322
+ function decodeNftTransfer(event) {
323
+ if (!isNftTransfer(event)) {
324
+ throw new Error(`Expected nft_transfer event, got ${event.event_type}`);
325
+ }
326
+ const payload = event.payload;
327
+ const assetIdentifier = requireString2(payload, "asset_identifier");
328
+ const sender = requireString2(payload, "sender");
329
+ const recipient = requireString2(payload, "recipient");
330
+ const value = requireHexValue(payload);
331
+ const { contract_id, token_name } = parseAssetIdentifier2(assetIdentifier);
332
+ return {
333
+ cursor: event.cursor,
334
+ block_height: event.block_height,
335
+ tx_id: event.tx_id,
336
+ tx_index: event.tx_index,
337
+ event_index: event.event_index,
338
+ event_type: event.event_type,
339
+ decoded_payload: {
340
+ asset_identifier: assetIdentifier,
341
+ contract_id: event.contract_id ?? contract_id,
342
+ token_name,
343
+ sender,
344
+ recipient,
345
+ value
346
+ },
347
+ source_cursor: event.cursor
348
+ };
349
+ }
350
+ // src/streams/types.ts
351
+ var STREAMS_EVENT_TYPES = [
352
+ "stx_transfer",
353
+ "stx_mint",
354
+ "stx_burn",
355
+ "stx_lock",
356
+ "ft_transfer",
357
+ "ft_mint",
358
+ "ft_burn",
359
+ "nft_transfer",
360
+ "nft_mint",
361
+ "nft_burn",
362
+ "print"
363
+ ];
364
+ export {
365
+ isNftTransfer,
366
+ isFtTransfer,
367
+ decodeNftTransfer,
368
+ decodeFtTransfer,
369
+ createStreamsClient,
370
+ ValidationError,
371
+ StreamsServerError,
372
+ STREAMS_EVENT_TYPES,
373
+ RateLimitError,
374
+ AuthError
375
+ };
376
+
377
+ //# debugId=1F9A85058EBF8AD064756E2164756E21
378
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/streams/consumer.ts", "../src/streams/errors.ts", "../src/streams/client.ts", "../src/streams/ft-transfer.ts", "../src/streams/nft-transfer.ts", "../src/streams/types.ts"],
4
+ "sourcesContent": [
5
+ "import type {\n\tStreamsEvent,\n\tStreamsEventsEnvelope,\n\tStreamsEventType,\n} from \"./types.ts\";\n\ntype StreamsEventsFetchParams = {\n\tcursor?: string | null;\n\tlimit: number;\n\ttypes?: readonly StreamsEventType[];\n};\n\nexport type StreamsEventsFetcher = (\n\tparams: StreamsEventsFetchParams,\n) => Promise<StreamsEventsEnvelope>;\n\nexport type Sleep = (ms: number, signal?: AbortSignal) => Promise<void>;\n\nexport async function defaultSleep(ms: number, signal?: AbortSignal): Promise<void> {\n\tif (signal?.aborted) return;\n\n\tawait new Promise<void>((resolve) => {\n\t\tconst timeout = setTimeout(resolve, ms);\n\t\tif (!signal) return;\n\t\tsignal.addEventListener(\n\t\t\t\"abort\",\n\t\t\t() => {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\tresolve();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t});\n}\n\nexport async function consumeStreamsEvents(opts: {\n\tfromCursor?: string | null;\n\tmode?: \"tail\" | \"bounded\";\n\tbatchSize: number;\n\ttypes?: readonly StreamsEventType[];\n\tfetchEvents: StreamsEventsFetcher;\n\tonBatch: (\n\t\tevents: StreamsEvent[],\n\t\tenvelope: StreamsEventsEnvelope,\n\t) => Promise<string | null | undefined> | string | null | undefined;\n\tsleep?: Sleep;\n\temptyBackoffMs?: number;\n\tmaxPages?: number;\n\tmaxEmptyPolls?: number;\n\tsignal?: AbortSignal;\n}): Promise<{ cursor: string | null; pages: number; emptyPolls: number }> {\n\tconst sleep = opts.sleep ?? defaultSleep;\n\tconst mode = opts.mode ?? \"tail\";\n\tconst emptyBackoffMs = opts.emptyBackoffMs ?? 500;\n\tconst maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;\n\tconst maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;\n\tlet cursor = opts.fromCursor ?? null;\n\tlet pages = 0;\n\tlet emptyPolls = 0;\n\n\twhile (\n\t\tpages < maxPages &&\n\t\temptyPolls < maxEmptyPolls &&\n\t\t!opts.signal?.aborted\n\t) {\n\t\tconst envelope = await opts.fetchEvents({\n\t\t\tcursor,\n\t\t\tlimit: opts.batchSize,\n\t\t\ttypes: opts.types,\n\t\t});\n\t\tpages++;\n\n\t\tconst returnedCursor = await opts.onBatch(envelope.events, envelope);\n\t\tconst nextCursor = returnedCursor ?? envelope.next_cursor;\n\n\t\tif (nextCursor && nextCursor !== cursor) {\n\t\t\tcursor = nextCursor;\n\t\t\temptyPolls = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (envelope.events.length === 0) {\n\t\t\temptyPolls++;\n\t\t\tif (mode === \"bounded\") {\n\t\t\t\treturn { cursor, pages, emptyPolls };\n\t\t\t}\n\t\t\tawait sleep(emptyBackoffMs, opts.signal);\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn { cursor, pages, emptyPolls };\n\t}\n\n\treturn { cursor, pages, emptyPolls };\n}\n\nexport async function* streamStreamsEvents(opts: {\n\tfromCursor?: string | null;\n\tbatchSize: number;\n\ttypes?: readonly StreamsEventType[];\n\tfetchEvents: StreamsEventsFetcher;\n\tsleep?: Sleep;\n\temptyBackoffMs?: number;\n\tmaxPages?: number;\n\tmaxEmptyPolls?: number;\n\tsignal?: AbortSignal;\n}): AsyncGenerator<StreamsEvent> {\n\tconst sleep = opts.sleep ?? defaultSleep;\n\tconst emptyBackoffMs = opts.emptyBackoffMs ?? 500;\n\tconst maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;\n\tconst maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;\n\tlet cursor = opts.fromCursor ?? null;\n\tlet pages = 0;\n\tlet emptyPolls = 0;\n\n\twhile (\n\t\tpages < maxPages &&\n\t\temptyPolls < maxEmptyPolls &&\n\t\t!opts.signal?.aborted\n\t) {\n\t\tconst envelope = await opts.fetchEvents({\n\t\t\tcursor,\n\t\t\tlimit: opts.batchSize,\n\t\t\ttypes: opts.types,\n\t\t});\n\t\tpages++;\n\n\t\tfor (const event of envelope.events) {\n\t\t\tif (opts.signal?.aborted) return;\n\t\t\tyield event;\n\t\t}\n\n\t\tconst nextCursor = envelope.next_cursor;\n\t\tif (nextCursor && nextCursor !== cursor) {\n\t\t\tcursor = nextCursor;\n\t\t\temptyPolls = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (envelope.events.length === 0) {\n\t\t\temptyPolls++;\n\t\t\tif (emptyPolls >= maxEmptyPolls || pages >= maxPages) return;\n\t\t\tawait sleep(emptyBackoffMs, opts.signal);\n\t\t\tcontinue;\n\t\t}\n\n\t\treturn;\n\t}\n}\n",
6
+ "export class AuthError extends Error {\n\treadonly status = 401;\n\n\tconstructor(message = \"API key invalid or expired.\") {\n\t\tsuper(message);\n\t\tthis.name = \"AuthError\";\n\t}\n}\n\nexport class RateLimitError extends Error {\n\treadonly status = 429;\n\n\tconstructor(\n\t\tmessage = \"Rate limited. Try again later.\",\n\t\treadonly retryAfter?: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"RateLimitError\";\n\t}\n}\n\nexport class ValidationError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\treadonly status: number,\n\t\treadonly body?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"ValidationError\";\n\t}\n}\n\nexport class StreamsServerError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\treadonly status: number,\n\t\treadonly body?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"StreamsServerError\";\n\t}\n}\n",
7
+ "import {\n\tconsumeStreamsEvents,\n\tstreamStreamsEvents,\n\ttype StreamsEventsFetcher,\n} from \"./consumer.ts\";\nimport {\n\tAuthError,\n\tRateLimitError,\n\tStreamsServerError,\n\tValidationError,\n} from \"./errors.ts\";\nimport type {\n\tFetchLike,\n\tStreamsClient,\n\tStreamsEventsEnvelope,\n\tStreamsEventsConsumeParams,\n\tStreamsEventsListParams,\n\tStreamsEventsStreamParams,\n\tStreamsTip,\n} from \"./types.ts\";\n\nconst DEFAULT_STREAMS_BASE_URL = \"https://api.secondlayer.tools\";\n\nexport type CreateStreamsClientOptions = {\n\tapiKey: string;\n\tbaseUrl?: string;\n\tfetchImpl?: FetchLike;\n};\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n\treturn baseUrl.replace(/\\/+$/, \"\");\n}\n\nfunction appendSearchParam(\n\tparams: URLSearchParams,\n\tname: string,\n\tvalue: number | string | null | undefined,\n): void {\n\tif (value === undefined || value === null) return;\n\tparams.set(name, String(value));\n}\n\nasync function responseBody(response: Response): Promise<unknown> {\n\tconst text = await response.text();\n\tif (text.length === 0) return undefined;\n\ttry {\n\t\treturn JSON.parse(text);\n\t} catch {\n\t\treturn text;\n\t}\n}\n\nfunction errorMessage(body: unknown, fallback: string): string {\n\tif (body && typeof body === \"object\") {\n\t\tconst record = body as Record<string, unknown>;\n\t\tconst message = record.error ?? record.message;\n\t\tif (typeof message === \"string\" && message.length > 0) return message;\n\t}\n\tif (typeof body === \"string\" && body.length > 0) return body;\n\treturn fallback;\n}\n\nasync function mapStreamsError(response: Response): Promise<never> {\n\tconst body = await responseBody(response);\n\n\tif (response.status === 401) {\n\t\tthrow new AuthError(errorMessage(body, \"API key invalid or expired.\"));\n\t}\n\n\tif (response.status === 429) {\n\t\tconst retryAfter = response.headers.get(\"Retry-After\") ?? undefined;\n\t\tthrow new RateLimitError(\n\t\t\terrorMessage(body, \"Rate limited. Try again later.\"),\n\t\t\tretryAfter,\n\t\t);\n\t}\n\n\tif (response.status >= 500) {\n\t\tthrow new StreamsServerError(\n\t\t\terrorMessage(body, `Streams server returned ${response.status}.`),\n\t\t\tresponse.status,\n\t\t\tbody,\n\t\t);\n\t}\n\n\tthrow new ValidationError(\n\t\terrorMessage(body, `Streams request returned ${response.status}.`),\n\t\tresponse.status,\n\t\tbody,\n\t);\n}\n\nexport function createStreamsClient(\n\toptions: CreateStreamsClientOptions,\n): StreamsClient {\n\tconst baseUrl = normalizeBaseUrl(options.baseUrl ?? DEFAULT_STREAMS_BASE_URL);\n\tconst fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));\n\n\tasync function request<T>(path: string): Promise<T> {\n\t\tconst response = await fetchImpl(`${baseUrl}${path}`, {\n\t\t\theaders: { Authorization: `Bearer ${options.apiKey}` },\n\t\t});\n\t\tif (!response.ok) await mapStreamsError(response);\n\t\treturn (await response.json()) as T;\n\t}\n\n\tconst fetchEvents: StreamsEventsFetcher = async ({\n\t\tcursor,\n\t\tlimit,\n\t\ttypes,\n\t}) => {\n\t\treturn listEvents({ cursor, limit, types });\n\t};\n\n\tasync function listEvents(\n\t\tparams: StreamsEventsListParams = {},\n\t): Promise<StreamsEventsEnvelope> {\n\t\tconst searchParams = new URLSearchParams();\n\t\tappendSearchParam(searchParams, \"cursor\", params.cursor);\n\t\tappendSearchParam(searchParams, \"from_height\", params.fromHeight);\n\t\tappendSearchParam(searchParams, \"to_height\", params.toHeight);\n\t\tappendSearchParam(searchParams, \"limit\", params.limit);\n\t\tif (params.types?.length) {\n\t\t\tsearchParams.set(\"types\", params.types.join(\",\"));\n\t\t}\n\n\t\tconst query = searchParams.toString();\n\t\treturn request<StreamsEventsEnvelope>(\n\t\t\t`/v1/streams/events${query ? `?${query}` : \"\"}`,\n\t\t);\n\t}\n\n\treturn {\n\t\tevents: {\n\t\t\tlist: listEvents,\n\t\t\tconsume(params: StreamsEventsConsumeParams) {\n\t\t\t\treturn consumeStreamsEvents({\n\t\t\t\t\tfromCursor: params.fromCursor,\n\t\t\t\t\tmode: params.mode,\n\t\t\t\t\ttypes: params.types,\n\t\t\t\t\tbatchSize: params.batchSize ?? 100,\n\t\t\t\t\tfetchEvents,\n\t\t\t\t\tonBatch: params.onBatch,\n\t\t\t\t\temptyBackoffMs: params.emptyBackoffMs,\n\t\t\t\t\tmaxPages: params.maxPages,\n\t\t\t\t\tmaxEmptyPolls: params.maxEmptyPolls,\n\t\t\t\t\tsignal: params.signal,\n\t\t\t\t});\n\t\t\t},\n\t\t\tstream(params: StreamsEventsStreamParams = {}) {\n\t\t\t\treturn streamStreamsEvents({\n\t\t\t\t\tfromCursor: params.fromCursor,\n\t\t\t\t\ttypes: params.types,\n\t\t\t\t\tbatchSize: params.batchSize ?? 100,\n\t\t\t\t\temptyBackoffMs: params.emptyBackoffMs,\n\t\t\t\t\tmaxPages: params.maxPages,\n\t\t\t\t\tmaxEmptyPolls: params.maxEmptyPolls,\n\t\t\t\t\tsignal: params.signal,\n\t\t\t\t\tfetchEvents,\n\t\t\t\t});\n\t\t\t},\n\t\t},\n\t\ttip() {\n\t\t\treturn request<StreamsTip>(\"/v1/streams/tip\");\n\t\t},\n\t};\n}\n",
8
+ "import type { StreamsEvent } from \"./types.ts\";\n\nexport type FtTransferPayload = {\n\tasset_identifier: string;\n\tsender: string;\n\trecipient: string;\n\tamount: string;\n};\n\nexport type FtTransferEvent = StreamsEvent & {\n\tevent_type: \"ft_transfer\";\n\tpayload: FtTransferPayload;\n};\n\nexport type DecodedFtTransferPayload = {\n\tasset_identifier: string;\n\tcontract_id: string;\n\ttoken_name: string | null;\n\tsender: string;\n\trecipient: string;\n\tamount: string;\n};\n\nexport type DecodedFtTransfer = {\n\tcursor: string;\n\tblock_height: number;\n\ttx_id: string;\n\ttx_index: number;\n\tevent_index: number;\n\tevent_type: \"ft_transfer\";\n\tdecoded_payload: DecodedFtTransferPayload;\n\tsource_cursor: string;\n};\n\nfunction requireString(\n\tpayload: Record<string, unknown>,\n\tfield: keyof FtTransferPayload,\n): string {\n\tconst value = payload[field];\n\tif (typeof value !== \"string\" || value.length === 0) {\n\t\tthrow new Error(`ft_transfer payload missing ${field}`);\n\t}\n\treturn value;\n}\n\nfunction parseAssetIdentifier(assetIdentifier: string): {\n\tcontract_id: string;\n\ttoken_name: string | null;\n} {\n\tconst [contractId, tokenName] = assetIdentifier.split(\"::\");\n\tif (!contractId) {\n\t\tthrow new Error(\"ft_transfer payload has malformed asset_identifier\");\n\t}\n\treturn {\n\t\tcontract_id: contractId,\n\t\ttoken_name: tokenName && tokenName.length > 0 ? tokenName : null,\n\t};\n}\n\nexport function isFtTransfer(event: StreamsEvent): event is FtTransferEvent {\n\treturn event.event_type === \"ft_transfer\";\n}\n\nexport function decodeFtTransfer(event: StreamsEvent): DecodedFtTransfer {\n\tif (!isFtTransfer(event)) {\n\t\tthrow new Error(`Expected ft_transfer event, got ${event.event_type}`);\n\t}\n\n\tconst payload = event.payload;\n\tconst assetIdentifier = requireString(payload, \"asset_identifier\");\n\tconst sender = requireString(payload, \"sender\");\n\tconst recipient = requireString(payload, \"recipient\");\n\tconst amount = requireString(payload, \"amount\");\n\tif (!/^(0|[1-9]\\d*)$/.test(amount)) {\n\t\tthrow new Error(\"ft_transfer payload has malformed amount\");\n\t}\n\n\tconst { contract_id, token_name } = parseAssetIdentifier(assetIdentifier);\n\n\treturn {\n\t\tcursor: event.cursor,\n\t\tblock_height: event.block_height,\n\t\ttx_id: event.tx_id,\n\t\ttx_index: event.tx_index,\n\t\tevent_index: event.event_index,\n\t\tevent_type: event.event_type,\n\t\tdecoded_payload: {\n\t\t\tasset_identifier: assetIdentifier,\n\t\t\tcontract_id: event.contract_id ?? contract_id,\n\t\t\ttoken_name,\n\t\t\tsender,\n\t\t\trecipient,\n\t\t\tamount,\n\t\t},\n\t\tsource_cursor: event.cursor,\n\t};\n}\n",
9
+ "import type { StreamsEvent } from \"./types.ts\";\n\nexport type NftTransferPayload = {\n\tasset_identifier: string;\n\tsender: string;\n\trecipient: string;\n\tvalue: string | { hex: string };\n};\n\nexport type NftTransferEvent = StreamsEvent & {\n\tevent_type: \"nft_transfer\";\n\tpayload: NftTransferPayload;\n};\n\nexport type DecodedNftTransferPayload = {\n\tasset_identifier: string;\n\tcontract_id: string;\n\ttoken_name: string | null;\n\tsender: string;\n\trecipient: string;\n\tvalue: string;\n};\n\nexport type DecodedNftTransfer = {\n\tcursor: string;\n\tblock_height: number;\n\ttx_id: string;\n\ttx_index: number;\n\tevent_index: number;\n\tevent_type: \"nft_transfer\";\n\tdecoded_payload: DecodedNftTransferPayload;\n\tsource_cursor: string;\n};\n\nfunction requireString(\n\tpayload: Record<string, unknown>,\n\tfield: \"asset_identifier\" | \"sender\" | \"recipient\",\n): string {\n\tconst value = payload[field];\n\tif (typeof value !== \"string\" || value.length === 0) {\n\t\tthrow new Error(`nft_transfer payload missing ${field}`);\n\t}\n\treturn value;\n}\n\nfunction requireHexValue(payload: Record<string, unknown>): string {\n\tconst value = payload.value;\n\tconst hex =\n\t\ttypeof value === \"string\"\n\t\t\t? value\n\t\t\t: value &&\n\t\t\t\t\ttypeof value === \"object\" &&\n\t\t\t\t\ttypeof (value as { hex?: unknown }).hex === \"string\"\n\t\t\t\t? (value as { hex: string }).hex\n\t\t\t\t: null;\n\n\tif (!hex) {\n\t\tthrow new Error(\"nft_transfer payload missing value\");\n\t}\n\tif (!/^0x[0-9a-fA-F]*$/.test(hex)) {\n\t\tthrow new Error(\"nft_transfer payload has malformed value\");\n\t}\n\treturn hex;\n}\n\nfunction parseAssetIdentifier(assetIdentifier: string): {\n\tcontract_id: string;\n\ttoken_name: string | null;\n} {\n\tconst [contractId, tokenName] = assetIdentifier.split(\"::\");\n\tif (!contractId) {\n\t\tthrow new Error(\"nft_transfer payload has malformed asset_identifier\");\n\t}\n\treturn {\n\t\tcontract_id: contractId,\n\t\ttoken_name: tokenName && tokenName.length > 0 ? tokenName : null,\n\t};\n}\n\nexport function isNftTransfer(event: StreamsEvent): event is NftTransferEvent {\n\treturn event.event_type === \"nft_transfer\";\n}\n\nexport function decodeNftTransfer(event: StreamsEvent): DecodedNftTransfer {\n\tif (!isNftTransfer(event)) {\n\t\tthrow new Error(`Expected nft_transfer event, got ${event.event_type}`);\n\t}\n\n\tconst payload = event.payload;\n\tconst assetIdentifier = requireString(payload, \"asset_identifier\");\n\tconst sender = requireString(payload, \"sender\");\n\tconst recipient = requireString(payload, \"recipient\");\n\tconst value = requireHexValue(payload);\n\tconst { contract_id, token_name } = parseAssetIdentifier(assetIdentifier);\n\n\treturn {\n\t\tcursor: event.cursor,\n\t\tblock_height: event.block_height,\n\t\ttx_id: event.tx_id,\n\t\ttx_index: event.tx_index,\n\t\tevent_index: event.event_index,\n\t\tevent_type: event.event_type,\n\t\tdecoded_payload: {\n\t\t\tasset_identifier: assetIdentifier,\n\t\t\tcontract_id: event.contract_id ?? contract_id,\n\t\t\ttoken_name,\n\t\t\tsender,\n\t\t\trecipient,\n\t\t\tvalue,\n\t\t},\n\t\tsource_cursor: event.cursor,\n\t};\n}\n",
10
+ "export const STREAMS_EVENT_TYPES = [\n\t\"stx_transfer\",\n\t\"stx_mint\",\n\t\"stx_burn\",\n\t\"stx_lock\",\n\t\"ft_transfer\",\n\t\"ft_mint\",\n\t\"ft_burn\",\n\t\"nft_transfer\",\n\t\"nft_mint\",\n\t\"nft_burn\",\n\t\"print\",\n] as const;\n\nexport type StreamsEventType = (typeof STREAMS_EVENT_TYPES)[number];\n\nexport type StreamsEventPayload = Record<string, unknown>;\n\nexport type StreamsEvent = {\n\tcursor: string;\n\tblock_height: number;\n\tindex_block_hash: string;\n\tburn_block_height: number;\n\ttx_id: string;\n\ttx_index: number;\n\tevent_index: number;\n\tevent_type: StreamsEventType;\n\tcontract_id: string | null;\n\tpayload: StreamsEventPayload;\n\tts: string;\n};\n\nexport type StreamsTip = {\n\tblock_height: number;\n\tindex_block_hash: string;\n\tburn_block_height: number;\n\tlag_seconds: number;\n};\n\nexport type StreamsReorg = {\n\tdetected_at: string;\n\tfork_point_height: number;\n\torphaned_range: { from: string; to: string };\n\tnew_canonical_tip: string;\n};\n\nexport type StreamsEventsEnvelope = {\n\tevents: StreamsEvent[];\n\tnext_cursor: string | null;\n\ttip: StreamsTip;\n\treorgs: StreamsReorg[];\n};\n\nexport type StreamsEventsListParams = {\n\tcursor?: string | null;\n\tfromHeight?: number;\n\ttoHeight?: number;\n\ttypes?: readonly StreamsEventType[];\n\tlimit?: number;\n};\n\nexport type StreamsEventsStreamParams = {\n\tfromCursor?: string | null;\n\ttypes?: readonly StreamsEventType[];\n\tbatchSize?: number;\n\temptyBackoffMs?: number;\n\tmaxPages?: number;\n\tmaxEmptyPolls?: number;\n\tsignal?: AbortSignal;\n};\n\nexport type StreamsEventsConsumeParams = {\n\tfromCursor?: string | null;\n\tmode?: \"tail\" | \"bounded\";\n\ttypes?: readonly StreamsEventType[];\n\tbatchSize?: number;\n\tonBatch: (\n\t\tevents: StreamsEvent[],\n\t\tenvelope: StreamsEventsEnvelope,\n\t) => Promise<string | null | undefined> | string | null | undefined;\n\temptyBackoffMs?: number;\n\tmaxPages?: number;\n\tmaxEmptyPolls?: number;\n\tsignal?: AbortSignal;\n};\n\nexport type StreamsEventsConsumeResult = {\n\tcursor: string | null;\n\tpages: number;\n\temptyPolls: number;\n};\n\nexport type FetchLike = (\n\tinput: string | URL | Request,\n\tinit?: RequestInit,\n) => Promise<Response>;\n\nexport type StreamsClient = {\n\tevents: {\n\t\tlist(params?: StreamsEventsListParams): Promise<StreamsEventsEnvelope>;\n\t\t/**\n\t\t * Pull pages from Streams and call `onBatch` after each page.\n\t\t *\n\t\t * Use `consume` for indexers and ETL jobs that own checkpointing. Return\n\t\t * the checkpoint cursor from `onBatch`. Default `mode: \"tail\"` keeps\n\t\t * polling when caught up; `mode: \"bounded\"` exits on the first empty page.\n\t\t * The consumer also exits when `maxPages`, `maxEmptyPolls`, or `signal`\n\t\t * stops it.\n\t\t */\n\t\tconsume(\n\t\t\tparams: StreamsEventsConsumeParams,\n\t\t): Promise<StreamsEventsConsumeResult>;\n\t\t/**\n\t\t * Follow Streams as an async iterator.\n\t\t *\n\t\t * Use `stream` for live processors and watch-style apps. It tails\n\t\t * indefinitely by default and stops when its `AbortSignal`, `maxPages`, or\n\t\t * `maxEmptyPolls` stops it.\n\t\t */\n\t\tstream(params?: StreamsEventsStreamParams): AsyncIterable<StreamsEvent>;\n\t};\n\ttip(): Promise<StreamsTip>;\n};\n"
11
+ ],
12
+ "mappings": "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",
13
+ "debugId": "1F9A85058EBF8AD064756E2164756E21",
14
+ "names": []
15
+ }
@@ -102,6 +102,92 @@ declare class Subgraphs extends BaseClient {
102
102
  private createTableClient;
103
103
  }
104
104
  import { InferSubgraphClient as InferSubgraphClient2 } from "@secondlayer/subgraphs";
105
+ type IndexTip = {
106
+ block_height: number
107
+ lag_seconds: number
108
+ };
109
+ type FtTransfer = {
110
+ cursor: string
111
+ block_height: number
112
+ tx_id: string
113
+ tx_index: number
114
+ event_index: number
115
+ event_type: "ft_transfer"
116
+ contract_id: string
117
+ asset_identifier: string
118
+ sender: string
119
+ recipient: string
120
+ amount: string
121
+ };
122
+ type FtTransfersEnvelope = {
123
+ events: FtTransfer[]
124
+ next_cursor: string | null
125
+ tip: IndexTip
126
+ reorgs: never[]
127
+ };
128
+ type FtTransfersListParams = {
129
+ cursor?: string | null
130
+ fromCursor?: string | null
131
+ limit?: number
132
+ contractId?: string
133
+ sender?: string
134
+ recipient?: string
135
+ fromHeight?: number
136
+ toHeight?: number
137
+ };
138
+ type FtTransfersWalkParams = Omit<FtTransfersListParams, "limit"> & {
139
+ batchSize?: number
140
+ signal?: AbortSignal
141
+ };
142
+ type NftTransfer = {
143
+ cursor: string
144
+ block_height: number
145
+ tx_id: string
146
+ tx_index: number
147
+ event_index: number
148
+ event_type: "nft_transfer"
149
+ contract_id: string
150
+ asset_identifier: string
151
+ sender: string
152
+ recipient: string
153
+ value: string
154
+ };
155
+ type NftTransfersEnvelope = {
156
+ events: NftTransfer[]
157
+ next_cursor: string | null
158
+ tip: IndexTip
159
+ reorgs: never[]
160
+ };
161
+ type NftTransfersListParams = {
162
+ cursor?: string | null
163
+ fromCursor?: string | null
164
+ limit?: number
165
+ contractId?: string
166
+ assetIdentifier?: string
167
+ sender?: string
168
+ recipient?: string
169
+ fromHeight?: number
170
+ toHeight?: number
171
+ };
172
+ type NftTransfersWalkParams = Omit<NftTransfersListParams, "limit"> & {
173
+ batchSize?: number
174
+ signal?: AbortSignal
175
+ };
176
+ declare class Index extends BaseClient {
177
+ constructor(options?: Partial<SecondLayerOptions>);
178
+ readonly ftTransfers: {
179
+ list: (params?: FtTransfersListParams) => Promise<FtTransfersEnvelope>
180
+ walk: (params?: FtTransfersWalkParams) => AsyncIterable<FtTransfer>
181
+ };
182
+ readonly nftTransfers: {
183
+ list: (params?: NftTransfersListParams) => Promise<NftTransfersEnvelope>
184
+ walk: (params?: NftTransfersWalkParams) => AsyncIterable<NftTransfer>
185
+ };
186
+ private listFtTransfers;
187
+ private listNftTransfers;
188
+ private walkFtTransfers;
189
+ private walkNftTransfers;
190
+ }
105
191
  import { CreateSubscriptionRequest, CreateSubscriptionResponse, DeadRow, DeliveryRow, ReplayResult, RotateSecretResponse, SubscriptionDetail, SubscriptionSummary, UpdateSubscriptionRequest } from "@secondlayer/shared/schemas/subscriptions";
106
192
  declare class Subscriptions extends BaseClient {
107
193
  list(): Promise<{
@@ -131,6 +217,7 @@ declare class Subscriptions extends BaseClient {
131
217
  }>;
132
218
  }
133
219
  declare class SecondLayer extends BaseClient {
220
+ readonly index: Index;
134
221
  readonly subgraphs: Subgraphs;
135
222
  readonly subscriptions: Subscriptions;
136
223
  constructor(options?: Partial<SecondLayerOptions>);
@@ -276,6 +276,111 @@ class Subgraphs extends BaseClient {
276
276
  };
277
277
  }
278
278
  }
279
+ // src/index-api/client.ts
280
+ function appendSearchParam(params, name, value) {
281
+ if (value === undefined || value === null)
282
+ return;
283
+ params.set(name, String(value));
284
+ }
285
+ function firstWalkFromHeight(params) {
286
+ if (params.fromHeight !== undefined)
287
+ return params.fromHeight;
288
+ if (params.cursor || params.fromCursor)
289
+ return;
290
+ return 0;
291
+ }
292
+
293
+ class Index extends BaseClient {
294
+ constructor(options = {}) {
295
+ super(options);
296
+ }
297
+ ftTransfers = {
298
+ list: (params = {}) => this.listFtTransfers(params),
299
+ walk: (params = {}) => this.walkFtTransfers(params)
300
+ };
301
+ nftTransfers = {
302
+ list: (params = {}) => this.listNftTransfers(params),
303
+ walk: (params = {}) => this.walkNftTransfers(params)
304
+ };
305
+ async listFtTransfers(params = {}) {
306
+ const searchParams = new URLSearchParams;
307
+ appendSearchParam(searchParams, "cursor", params.cursor);
308
+ appendSearchParam(searchParams, "from_cursor", params.fromCursor);
309
+ appendSearchParam(searchParams, "limit", params.limit);
310
+ appendSearchParam(searchParams, "contract_id", params.contractId);
311
+ appendSearchParam(searchParams, "sender", params.sender);
312
+ appendSearchParam(searchParams, "recipient", params.recipient);
313
+ appendSearchParam(searchParams, "from_height", params.fromHeight);
314
+ appendSearchParam(searchParams, "to_height", params.toHeight);
315
+ const query = searchParams.toString();
316
+ return this.request("GET", `/v1/index/ft-transfers${query ? `?${query}` : ""}`);
317
+ }
318
+ async listNftTransfers(params = {}) {
319
+ const searchParams = new URLSearchParams;
320
+ appendSearchParam(searchParams, "cursor", params.cursor);
321
+ appendSearchParam(searchParams, "from_cursor", params.fromCursor);
322
+ appendSearchParam(searchParams, "limit", params.limit);
323
+ appendSearchParam(searchParams, "contract_id", params.contractId);
324
+ appendSearchParam(searchParams, "asset_identifier", params.assetIdentifier);
325
+ appendSearchParam(searchParams, "sender", params.sender);
326
+ appendSearchParam(searchParams, "recipient", params.recipient);
327
+ appendSearchParam(searchParams, "from_height", params.fromHeight);
328
+ appendSearchParam(searchParams, "to_height", params.toHeight);
329
+ const query = searchParams.toString();
330
+ return this.request("GET", `/v1/index/nft-transfers${query ? `?${query}` : ""}`);
331
+ }
332
+ async* walkFtTransfers(params = {}) {
333
+ const batchSize = params.batchSize ?? 200;
334
+ let cursor = params.cursor ?? params.fromCursor ?? null;
335
+ let firstPage = true;
336
+ while (!params.signal?.aborted) {
337
+ const envelope = await this.listFtTransfers({
338
+ ...params,
339
+ limit: batchSize,
340
+ cursor: firstPage ? params.cursor : cursor,
341
+ fromCursor: firstPage ? params.fromCursor : undefined,
342
+ fromHeight: firstPage ? firstWalkFromHeight(params) : undefined
343
+ });
344
+ for (const event of envelope.events) {
345
+ if (params.signal?.aborted)
346
+ return;
347
+ yield event;
348
+ }
349
+ const nextCursor = envelope.next_cursor;
350
+ if (!nextCursor || nextCursor === cursor || envelope.events.length < batchSize) {
351
+ return;
352
+ }
353
+ cursor = nextCursor;
354
+ firstPage = false;
355
+ }
356
+ }
357
+ async* walkNftTransfers(params = {}) {
358
+ const batchSize = params.batchSize ?? 200;
359
+ let cursor = params.cursor ?? params.fromCursor ?? null;
360
+ let firstPage = true;
361
+ while (!params.signal?.aborted) {
362
+ const envelope = await this.listNftTransfers({
363
+ ...params,
364
+ limit: batchSize,
365
+ cursor: firstPage ? params.cursor : cursor,
366
+ fromCursor: firstPage ? params.fromCursor : undefined,
367
+ fromHeight: firstPage ? firstWalkFromHeight(params) : undefined
368
+ });
369
+ for (const event of envelope.events) {
370
+ if (params.signal?.aborted)
371
+ return;
372
+ yield event;
373
+ }
374
+ const nextCursor = envelope.next_cursor;
375
+ if (!nextCursor || nextCursor === cursor || envelope.events.length < batchSize) {
376
+ return;
377
+ }
378
+ cursor = nextCursor;
379
+ firstPage = false;
380
+ }
381
+ }
382
+ }
383
+
279
384
  // src/subscriptions/client.ts
280
385
  class Subscriptions extends BaseClient {
281
386
  async list() {
@@ -318,10 +423,12 @@ class Subscriptions extends BaseClient {
318
423
 
319
424
  // src/client.ts
320
425
  class SecondLayer extends BaseClient {
426
+ index;
321
427
  subgraphs;
322
428
  subscriptions;
323
429
  constructor(options = {}) {
324
430
  super(options);
431
+ this.index = new Index(options);
325
432
  this.subgraphs = new Subgraphs(options);
326
433
  this.subscriptions = new Subscriptions(options);
327
434
  }
@@ -342,5 +449,5 @@ export {
342
449
  Subgraphs
343
450
  };
344
451
 
345
- //# debugId=ADD697A31D92DD7264756E2164756E21
452
+ //# debugId=5DD669105B05F13064756E2164756E21
346
453
  //# sourceMappingURL=index.js.map