@secondlayer/sdk 10.4.3 → 10.6.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/dist/index.d.ts +423 -398
- package/dist/index.js +770 -722
- package/dist/index.js.map +11 -11
- package/dist/sinks/bun-sqlite.d.ts +2 -2
- package/dist/sinks/core.d.ts +1 -1
- package/dist/sinks/core.js +4 -4
- package/dist/sinks/core.js.map +1 -1
- package/dist/sinks/drizzle.d.ts +2 -2
- package/dist/sinks/drizzle.js +6 -4
- package/dist/sinks/drizzle.js.map +5 -5
- package/dist/sinks/kysely.d.ts +2 -2
- package/dist/sinks/kysely.js +4 -2
- package/dist/sinks/kysely.js.map +5 -5
- package/dist/sinks/testing.d.ts +1 -1
- package/dist/sinks/testing.js +3 -3
- package/dist/sinks/testing.js.map +2 -2
- package/dist/streams/index.d.ts +1 -1
- package/dist/streams/index.js +80 -35
- package/dist/streams/index.js.map +3 -3
- package/dist/streams/rows.d.ts +1 -1
- package/dist/streams/rows.js +22 -22
- package/dist/streams/rows.js.map +1 -1
- package/dist/subgraphs/index.d.ts +19 -3
- package/dist/subgraphs/index.js +63 -18
- package/dist/subgraphs/index.js.map +4 -4
- package/package.json +3 -3
|
@@ -39,6 +39,22 @@ interface SecondLayerOptions {
|
|
|
39
39
|
* retry policy instead of stalling a walk or consume loop forever. `0`
|
|
40
40
|
* disables the timeout. */
|
|
41
41
|
requestTimeoutMs?: number;
|
|
42
|
+
/** Where GET responses carrying an `ETag` are kept, so a repeat read sends
|
|
43
|
+
* `If-None-Match` and a finalized page comes back as a free `304`.
|
|
44
|
+
* Defaults to an in-memory store of the last 64 pages. Pass your own to
|
|
45
|
+
* keep it across processes (a file, a KV), or `false` to turn it off. */
|
|
46
|
+
etagCache?: EtagCache | false;
|
|
47
|
+
}
|
|
48
|
+
/** One cached GET response: its validator and the body it validated. */
|
|
49
|
+
type EtagEntry = {
|
|
50
|
+
etag: string;
|
|
51
|
+
body: string;
|
|
52
|
+
};
|
|
53
|
+
/** Storage for `etagCache`. Keys are full request URLs. Either method may be
|
|
54
|
+
* async, so a disk or KV store works as well as a Map. */
|
|
55
|
+
interface EtagCache {
|
|
56
|
+
get(key: string): EtagEntry | undefined | Promise<EtagEntry | undefined>;
|
|
57
|
+
set(key: string, entry: EtagEntry): void | Promise<void>;
|
|
42
58
|
}
|
|
43
59
|
/** Per-call options every `request*` method accepts. */
|
|
44
60
|
type RequestOptions = {
|
|
@@ -52,6 +68,7 @@ declare abstract class BaseClient {
|
|
|
52
68
|
protected origin: "cli" | "mcp" | "session";
|
|
53
69
|
protected fetchImpl: FetchLike;
|
|
54
70
|
protected requestTimeoutMs: number;
|
|
71
|
+
protected etagCache: EtagCache | null;
|
|
55
72
|
constructor(options?: Partial<SecondLayerOptions>);
|
|
56
73
|
static authHeaders(apiKey?: string): Record<string, string>;
|
|
57
74
|
protected request<T>(method: string, path: string, body?: unknown, opts?: RequestOptions): Promise<T>;
|
|
@@ -68,7 +85,7 @@ declare abstract class BaseClient {
|
|
|
68
85
|
/** Issue the HTTP request and map non-2xx statuses onto the error family.
|
|
69
86
|
* `signal` is the combined caller-plus-timeout signal from `request*`;
|
|
70
87
|
* subclasses that stream a body call this directly and read it themselves. */
|
|
71
|
-
protected fetchResponse(method: string, path: string, body?: unknown, signal?: AbortSignal): Promise<Response>;
|
|
88
|
+
protected fetchResponse(method: string, path: string, body?: unknown, signal?: AbortSignal, extraHeaders?: Record<string, string>): Promise<Response>;
|
|
72
89
|
}
|
|
73
90
|
interface SubgraphSource {
|
|
74
91
|
name: string;
|
|
@@ -108,7 +125,6 @@ interface SubgraphRowsEnvelope<T = unknown> {
|
|
|
108
125
|
interface BundleSubgraphResponse {
|
|
109
126
|
ok: true;
|
|
110
127
|
name: string;
|
|
111
|
-
version: string | null;
|
|
112
128
|
description: string | null;
|
|
113
129
|
sources: Record<string, Record<string, unknown>>;
|
|
114
130
|
schema: Record<string, unknown>;
|
|
@@ -2651,4 +2667,4 @@ declare function getSubgraph<T extends {
|
|
|
2651
2667
|
schema: Record<string, unknown>;
|
|
2652
2668
|
}>(def: T, options?: Partial<SecondLayerOptions> | SecondLayer | Subgraphs): InferSubgraphClient2<T>;
|
|
2653
2669
|
import { SubgraphAgentSchema as SubgraphAgentSchema2, SubgraphSpecFormat, SubgraphSpecOptions as SubgraphSpecOptions2 } from "@secondlayer/shared/subgraphs/spec";
|
|
2654
|
-
export {
|
|
2670
|
+
export { SecondLayerOptions, SubgraphAgentSchema2 as SubgraphAgentSchema, SubgraphSpecFormat, SubgraphSpecOptions2 as SubgraphSpecOptions, Subgraphs, getSubgraph };
|
package/dist/subgraphs/index.js
CHANGED
|
@@ -156,6 +156,30 @@ function parseRetryAfter(value) {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
// src/base.ts
|
|
159
|
+
class MemoryEtagCache {
|
|
160
|
+
limit;
|
|
161
|
+
entries = new Map;
|
|
162
|
+
constructor(limit = 64) {
|
|
163
|
+
this.limit = limit;
|
|
164
|
+
}
|
|
165
|
+
get(key) {
|
|
166
|
+
const entry = this.entries.get(key);
|
|
167
|
+
if (entry) {
|
|
168
|
+
this.entries.delete(key);
|
|
169
|
+
this.entries.set(key, entry);
|
|
170
|
+
}
|
|
171
|
+
return entry;
|
|
172
|
+
}
|
|
173
|
+
set(key, entry) {
|
|
174
|
+
this.entries.delete(key);
|
|
175
|
+
this.entries.set(key, entry);
|
|
176
|
+
if (this.entries.size > this.limit) {
|
|
177
|
+
const oldest = this.entries.keys().next().value;
|
|
178
|
+
if (oldest !== undefined)
|
|
179
|
+
this.entries.delete(oldest);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
159
183
|
var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
160
184
|
function abortError() {
|
|
161
185
|
return new DOMException("This operation was aborted", "AbortError");
|
|
@@ -266,12 +290,14 @@ class BaseClient {
|
|
|
266
290
|
origin;
|
|
267
291
|
fetchImpl;
|
|
268
292
|
requestTimeoutMs;
|
|
293
|
+
etagCache;
|
|
269
294
|
constructor(options = {}) {
|
|
270
295
|
this.baseUrl = resolveBaseUrl(options.baseUrl);
|
|
271
296
|
this.apiKey = resolveApiKey(options.apiKey);
|
|
272
297
|
this.origin = options.origin ?? "cli";
|
|
273
298
|
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
274
299
|
this.fetchImpl = options.fetchImpl ?? ((...args) => fetch(...args));
|
|
300
|
+
this.etagCache = options.etagCache === false ? null : options.etagCache ?? new MemoryEtagCache;
|
|
275
301
|
}
|
|
276
302
|
static authHeaders(apiKey) {
|
|
277
303
|
const headers = {
|
|
@@ -284,11 +310,25 @@ class BaseClient {
|
|
|
284
310
|
}
|
|
285
311
|
async request(method, path, body, opts = {}) {
|
|
286
312
|
return this.withRequestBudget(opts.signal, async (signal) => {
|
|
287
|
-
const
|
|
313
|
+
const cacheKey = method === "GET" && this.etagCache ? `${this.baseUrl}${path}` : null;
|
|
314
|
+
const cached = cacheKey ? await this.etagCache?.get(cacheKey) : undefined;
|
|
315
|
+
const response = await this.fetchResponse(method, path, body, signal, cached ? { "If-None-Match": cached.etag } : undefined);
|
|
288
316
|
if (response.status === 204) {
|
|
289
317
|
return;
|
|
290
318
|
}
|
|
291
|
-
|
|
319
|
+
if (response.status === 304) {
|
|
320
|
+
if (!cached) {
|
|
321
|
+
throw new ApiError(304, "Not Modified without a cached copy");
|
|
322
|
+
}
|
|
323
|
+
return JSON.parse(cached.body);
|
|
324
|
+
}
|
|
325
|
+
const etag = cacheKey ? response.headers.get("ETag") : null;
|
|
326
|
+
if (!cacheKey || !etag) {
|
|
327
|
+
return await raceAbort(response.json(), signal);
|
|
328
|
+
}
|
|
329
|
+
const text = await raceAbort(response.text(), signal);
|
|
330
|
+
await this.etagCache?.set(cacheKey, { etag, body: text });
|
|
331
|
+
return JSON.parse(text);
|
|
292
332
|
});
|
|
293
333
|
}
|
|
294
334
|
async requestOrNull(method, path, body, opts = {}) {
|
|
@@ -331,9 +371,12 @@ class BaseClient {
|
|
|
331
371
|
callerSignal?.removeEventListener("abort", forward);
|
|
332
372
|
}
|
|
333
373
|
}
|
|
334
|
-
async fetchResponse(method, path, body, signal) {
|
|
374
|
+
async fetchResponse(method, path, body, signal, extraHeaders) {
|
|
335
375
|
const url = `${this.baseUrl}${path}`;
|
|
336
|
-
const headers =
|
|
376
|
+
const headers = {
|
|
377
|
+
...BaseClient.authHeaders(this.apiKey),
|
|
378
|
+
...extraHeaders
|
|
379
|
+
};
|
|
337
380
|
headers["x-sl-origin"] = this.origin;
|
|
338
381
|
let serializedBody;
|
|
339
382
|
if (body !== undefined && body !== null) {
|
|
@@ -358,6 +401,8 @@ class BaseClient {
|
|
|
358
401
|
throw err;
|
|
359
402
|
throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`, undefined, undefined, { retryable: true, cause: err instanceof Error ? err : undefined });
|
|
360
403
|
}
|
|
404
|
+
if (response.status === 304)
|
|
405
|
+
return response;
|
|
361
406
|
if (!response.ok) {
|
|
362
407
|
const errorBody = signal ? await raceAbort(response.text(), signal) : await response.text();
|
|
363
408
|
const envelope = parseErrorEnvelope(errorBody);
|
|
@@ -1280,7 +1325,7 @@ async function defaultSleep(ms, signal) {
|
|
|
1280
1325
|
});
|
|
1281
1326
|
}
|
|
1282
1327
|
async function consumeStreamsEvents(opts) {
|
|
1283
|
-
const
|
|
1328
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
1284
1329
|
const mode = opts.mode ?? "tail";
|
|
1285
1330
|
const finalizedOnly = opts.finalizedOnly ?? false;
|
|
1286
1331
|
const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
|
|
@@ -1310,7 +1355,7 @@ async function consumeStreamsEvents(opts) {
|
|
|
1310
1355
|
retryCount: opts.retryCount,
|
|
1311
1356
|
retryDelay: opts.retryDelay,
|
|
1312
1357
|
onError: opts.onError,
|
|
1313
|
-
sleep
|
|
1358
|
+
sleep,
|
|
1314
1359
|
signal: opts.signal
|
|
1315
1360
|
});
|
|
1316
1361
|
pages++;
|
|
@@ -1342,7 +1387,7 @@ async function consumeStreamsEvents(opts) {
|
|
|
1342
1387
|
retryCount: opts.retryCount,
|
|
1343
1388
|
retryDelay: opts.retryDelay,
|
|
1344
1389
|
onError: opts.onError,
|
|
1345
|
-
sleep
|
|
1390
|
+
sleep,
|
|
1346
1391
|
signal: opts.signal
|
|
1347
1392
|
});
|
|
1348
1393
|
if (listed.next_since !== null)
|
|
@@ -1405,7 +1450,7 @@ async function consumeStreamsEvents(opts) {
|
|
|
1405
1450
|
if (mode === "bounded") {
|
|
1406
1451
|
return { cursor, pages, emptyPolls };
|
|
1407
1452
|
}
|
|
1408
|
-
await
|
|
1453
|
+
await sleep(emptyBackoffMs, opts.signal);
|
|
1409
1454
|
continue;
|
|
1410
1455
|
}
|
|
1411
1456
|
return { cursor, pages, emptyPolls };
|
|
@@ -1413,7 +1458,7 @@ async function consumeStreamsEvents(opts) {
|
|
|
1413
1458
|
return { cursor, pages, emptyPolls };
|
|
1414
1459
|
}
|
|
1415
1460
|
async function* iterateStreamsBatches(opts) {
|
|
1416
|
-
const
|
|
1461
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
1417
1462
|
let cursor = opts.fromCursor ?? null;
|
|
1418
1463
|
while (!opts.signal?.aborted) {
|
|
1419
1464
|
const envelope = await opts.fetchEvents({
|
|
@@ -1441,12 +1486,12 @@ async function* iterateStreamsBatches(opts) {
|
|
|
1441
1486
|
if (!advanced && envelope.events.length === 0) {
|
|
1442
1487
|
if (opts.signal?.aborted)
|
|
1443
1488
|
return;
|
|
1444
|
-
await
|
|
1489
|
+
await sleep(opts.intervalMs, opts.signal);
|
|
1445
1490
|
}
|
|
1446
1491
|
}
|
|
1447
1492
|
}
|
|
1448
1493
|
async function* streamStreamsEvents(opts) {
|
|
1449
|
-
const
|
|
1494
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
1450
1495
|
const emptyBackoffMs = opts.emptyBackoffMs ?? 500;
|
|
1451
1496
|
const maxPages = opts.maxPages ?? Number.POSITIVE_INFINITY;
|
|
1452
1497
|
const maxEmptyPolls = opts.maxEmptyPolls ?? Number.POSITIVE_INFINITY;
|
|
@@ -1481,7 +1526,7 @@ async function* streamStreamsEvents(opts) {
|
|
|
1481
1526
|
emptyPolls++;
|
|
1482
1527
|
if (emptyPolls >= maxEmptyPolls || pages >= maxPages)
|
|
1483
1528
|
return;
|
|
1484
|
-
await
|
|
1529
|
+
await sleep(emptyBackoffMs, opts.signal);
|
|
1485
1530
|
continue;
|
|
1486
1531
|
}
|
|
1487
1532
|
return;
|
|
@@ -1490,7 +1535,7 @@ async function* streamStreamsEvents(opts) {
|
|
|
1490
1535
|
|
|
1491
1536
|
// src/index-api/consumer.ts
|
|
1492
1537
|
async function consumeIndexFeed(opts) {
|
|
1493
|
-
const
|
|
1538
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
1494
1539
|
const mode = opts.mode ?? "tail";
|
|
1495
1540
|
const finalizedOnly = opts.finalizedOnly ?? false;
|
|
1496
1541
|
const batchSize = opts.batchSize ?? 200;
|
|
@@ -1514,7 +1559,7 @@ async function consumeIndexFeed(opts) {
|
|
|
1514
1559
|
retryCount: opts.retryCount,
|
|
1515
1560
|
retryDelay: opts.retryDelay,
|
|
1516
1561
|
onError: opts.onError,
|
|
1517
|
-
sleep
|
|
1562
|
+
sleep,
|
|
1518
1563
|
signal: opts.signal
|
|
1519
1564
|
});
|
|
1520
1565
|
pages++;
|
|
@@ -1577,7 +1622,7 @@ async function consumeIndexFeed(opts) {
|
|
|
1577
1622
|
if (mode === "bounded") {
|
|
1578
1623
|
return { cursor, pages, emptyPolls };
|
|
1579
1624
|
}
|
|
1580
|
-
await
|
|
1625
|
+
await sleep(emptyBackoffMs, opts.signal);
|
|
1581
1626
|
continue;
|
|
1582
1627
|
}
|
|
1583
1628
|
return { cursor, pages, emptyPolls };
|
|
@@ -2639,9 +2684,9 @@ function getSubgraph(def, options = {}) {
|
|
|
2639
2684
|
return new Subgraphs(options).typed(def);
|
|
2640
2685
|
}
|
|
2641
2686
|
export {
|
|
2642
|
-
|
|
2643
|
-
|
|
2687
|
+
Subgraphs,
|
|
2688
|
+
getSubgraph
|
|
2644
2689
|
};
|
|
2645
2690
|
|
|
2646
|
-
//# debugId=
|
|
2691
|
+
//# debugId=8E4B27D4BE8A894F64756E2164756E21
|
|
2647
2692
|
//# sourceMappingURL=index.js.map
|