@secondlayer/sdk 10.1.1 → 10.2.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 +30 -0
- package/dist/index.d.ts +187 -1
- package/dist/index.js +299 -6
- package/dist/index.js.map +8 -6
- package/dist/sinks/bun-sqlite.js +34 -1
- package/dist/sinks/bun-sqlite.js.map +3 -3
- package/dist/sinks/core.js +34 -1
- package/dist/sinks/core.js.map +3 -3
- package/dist/sinks/drizzle.js +34 -1
- package/dist/sinks/drizzle.js.map +3 -3
- package/dist/sinks/kysely.js +34 -1
- package/dist/sinks/kysely.js.map +3 -3
- package/dist/streams/index.js +34 -1
- package/dist/streams/index.js.map +4 -4
- package/dist/subgraphs/index.d.ts +159 -0
- package/dist/subgraphs/index.js +294 -6
- package/dist/subgraphs/index.js.map +8 -6
- package/package.json +2 -2
|
@@ -14,6 +14,12 @@ interface SecondLayerOptions {
|
|
|
14
14
|
* Required for `streams.dumps.*`; without it the dumps client falls back to
|
|
15
15
|
* its built-in default. */
|
|
16
16
|
dumpsBaseUrl?: string;
|
|
17
|
+
/** Public base URL for the signed canonical archive tree. Defaults to
|
|
18
|
+
* `https://archive.secondlayer.tools`. Reaches `sl.archive`. */
|
|
19
|
+
archiveBaseUrl?: string;
|
|
20
|
+
/** Credits and fetch-gate API. Defaults to `https://api.secondlayer.tools`,
|
|
21
|
+
* not `baseUrl`. Reaches `sl.archive` quote/fetch/credits. */
|
|
22
|
+
archiveOpsUrl?: string;
|
|
17
23
|
/** Deploy origin label sent as `x-sl-origin` (telemetry). Defaults to `cli`. */
|
|
18
24
|
origin?: "cli" | "mcp" | "session";
|
|
19
25
|
/** Check the ed25519 signature on every Streams read. Omit for lenient
|
|
@@ -207,7 +213,156 @@ declare class Subgraphs extends BaseClient {
|
|
|
207
213
|
private createTableClient;
|
|
208
214
|
}
|
|
209
215
|
import { InferSubgraphClient as InferSubgraphClient2 } from "@secondlayer/subgraphs";
|
|
216
|
+
import { InstanceDiagnosis as InstanceDiagnosis2 } from "@secondlayer/shared/archive/instance-diagnosis";
|
|
210
217
|
import { SubgraphSummary as SubgraphSummary2 } from "@secondlayer/shared/schemas";
|
|
218
|
+
import { InstanceDiagnosis, PublicStatus } from "@secondlayer/shared/archive/instance-diagnosis";
|
|
219
|
+
/** Structural `/public/status` payload. Extra fields are ignored by diagnosis. */
|
|
220
|
+
type InstanceStatus = PublicStatus;
|
|
221
|
+
type InstanceClient = {
|
|
222
|
+
status(): Promise<InstanceStatus>;
|
|
223
|
+
diagnose(): Promise<InstanceDiagnosis>;
|
|
224
|
+
};
|
|
225
|
+
type ArchiveVerifyInput = {
|
|
226
|
+
against: string;
|
|
227
|
+
target?: string;
|
|
228
|
+
fromBlock?: number;
|
|
229
|
+
toBlock?: number;
|
|
230
|
+
insecure?: boolean;
|
|
231
|
+
publicKeyPem?: string;
|
|
232
|
+
};
|
|
233
|
+
type ArchiveVerifyRangeStatus = "match" | "digest-mismatch" | "count-mismatch" | "missing";
|
|
234
|
+
type ArchiveVerifyResult = {
|
|
235
|
+
status: "clean" | "diverged" | "unanchored";
|
|
236
|
+
target: string;
|
|
237
|
+
against: string;
|
|
238
|
+
signature: {
|
|
239
|
+
verified: boolean;
|
|
240
|
+
reason?: string;
|
|
241
|
+
};
|
|
242
|
+
coverage?: {
|
|
243
|
+
from_block: number;
|
|
244
|
+
to_block: number;
|
|
245
|
+
};
|
|
246
|
+
ranges: Array<{
|
|
247
|
+
dataset: string;
|
|
248
|
+
from_block: number;
|
|
249
|
+
to_block: number;
|
|
250
|
+
status: ArchiveVerifyRangeStatus;
|
|
251
|
+
expected_digest: string | null;
|
|
252
|
+
actual_digest: string | null;
|
|
253
|
+
}>;
|
|
254
|
+
reason?: string;
|
|
255
|
+
};
|
|
256
|
+
import { RangeDigest } from "@secondlayer/shared/archive/range-digest";
|
|
257
|
+
type ArchiveDataset = "blocks" | "transactions" | "events";
|
|
258
|
+
type ArchiveFlow = "bootstrap" | "repair";
|
|
259
|
+
type ArchivePartition = {
|
|
260
|
+
dataset: string;
|
|
261
|
+
from_block: number;
|
|
262
|
+
to_block: number;
|
|
263
|
+
path: string;
|
|
264
|
+
row_count: number;
|
|
265
|
+
byte_size: number;
|
|
266
|
+
sha256: string;
|
|
267
|
+
};
|
|
268
|
+
type ArchiveManifest = {
|
|
269
|
+
network?: string;
|
|
270
|
+
coverage?: {
|
|
271
|
+
from_block: number;
|
|
272
|
+
to_block: number;
|
|
273
|
+
};
|
|
274
|
+
partition_size_blocks?: number;
|
|
275
|
+
range_digests?: RangeDigest[];
|
|
276
|
+
partitions?: ArchivePartition[];
|
|
277
|
+
signature?: string;
|
|
278
|
+
key_id?: string;
|
|
279
|
+
[key: string]: unknown;
|
|
280
|
+
};
|
|
281
|
+
type LoadedArchive = {
|
|
282
|
+
manifest: ArchiveManifest;
|
|
283
|
+
origin: string;
|
|
284
|
+
root: string;
|
|
285
|
+
isRemote: boolean;
|
|
286
|
+
signature: {
|
|
287
|
+
verified: boolean;
|
|
288
|
+
reason?: string;
|
|
289
|
+
};
|
|
290
|
+
};
|
|
291
|
+
type ArchiveQuote = {
|
|
292
|
+
partitions: number;
|
|
293
|
+
bundles: number;
|
|
294
|
+
usdMicros: number;
|
|
295
|
+
usd: string;
|
|
296
|
+
freeAllowanceAppliedMicros: number;
|
|
297
|
+
allowanceRemainingBundles: number;
|
|
298
|
+
balanceUsdMicros: number;
|
|
299
|
+
sufficient: boolean;
|
|
300
|
+
};
|
|
301
|
+
type ArchiveFetchItem = {
|
|
302
|
+
path: string;
|
|
303
|
+
url: string;
|
|
304
|
+
expiresAt: string;
|
|
305
|
+
chargedUsdMicros: number;
|
|
306
|
+
};
|
|
307
|
+
type ArchiveFetchResult = {
|
|
308
|
+
urls: ArchiveFetchItem[];
|
|
309
|
+
chargedTotalUsdMicros: number;
|
|
310
|
+
balanceAfterUsdMicros: number;
|
|
311
|
+
};
|
|
312
|
+
type ArchiveCreditsBalance = {
|
|
313
|
+
creditsUsdMicros: string;
|
|
314
|
+
refill: {
|
|
315
|
+
belowUsd: number | null;
|
|
316
|
+
packUsd: number | null;
|
|
317
|
+
lastAt: string | null;
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
type ArchiveLoadOptions = {
|
|
321
|
+
insecure?: boolean;
|
|
322
|
+
};
|
|
323
|
+
type ArchiveClient = {
|
|
324
|
+
latest(against?: string, opts?: ArchiveLoadOptions): Promise<LoadedArchive>;
|
|
325
|
+
load(against: string, opts?: ArchiveLoadOptions): Promise<LoadedArchive>;
|
|
326
|
+
status(): Promise<unknown>;
|
|
327
|
+
partitions(ref: LoadedArchive, filter?: {
|
|
328
|
+
dataset?: ArchiveDataset;
|
|
329
|
+
fromBlock?: number;
|
|
330
|
+
toBlock?: number;
|
|
331
|
+
}): ArchivePartition[];
|
|
332
|
+
quote(input: {
|
|
333
|
+
paths: string[];
|
|
334
|
+
flow: ArchiveFlow;
|
|
335
|
+
}): Promise<ArchiveQuote>;
|
|
336
|
+
fetch(input: {
|
|
337
|
+
paths: string[];
|
|
338
|
+
flow: ArchiveFlow;
|
|
339
|
+
}): Promise<ArchiveFetchResult>;
|
|
340
|
+
download(partition: ArchivePartition, opts?: {
|
|
341
|
+
url?: string;
|
|
342
|
+
}): Promise<Uint8Array>;
|
|
343
|
+
credits: {
|
|
344
|
+
balance(): Promise<ArchiveCreditsBalance>;
|
|
345
|
+
checkout(input: {
|
|
346
|
+
email: string;
|
|
347
|
+
pack: 10 | 25 | 50 | 100;
|
|
348
|
+
}): Promise<{
|
|
349
|
+
url: string;
|
|
350
|
+
}>;
|
|
351
|
+
refill(input: {
|
|
352
|
+
belowUsd: number;
|
|
353
|
+
packUsd: 10 | 25 | 50 | 100;
|
|
354
|
+
} | {
|
|
355
|
+
off: true;
|
|
356
|
+
}): Promise<{
|
|
357
|
+
belowUsd: number | null;
|
|
358
|
+
packUsd: number | null;
|
|
359
|
+
}>;
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
/** Hosted archive client plus instance `verify`. `status()` is still the public archive tree. */
|
|
363
|
+
type SecondLayerArchive = ArchiveClient & {
|
|
364
|
+
verify(input: ArchiveVerifyInput): Promise<ArchiveVerifyResult>;
|
|
365
|
+
};
|
|
211
366
|
/**
|
|
212
367
|
* Typed client for the contract-discovery API (`GET /v1/contracts`).
|
|
213
368
|
*
|
|
@@ -2386,6 +2541,8 @@ interface ContextSnapshot {
|
|
|
2386
2541
|
}>;
|
|
2387
2542
|
/** In-flight reindex operations (bounded to subgraphs reporting `reindexing`). */
|
|
2388
2543
|
activeOperations: ContextField<ActiveSubgraphOperation[]>;
|
|
2544
|
+
/** Decoder lag / empty-index from `GET /public/status`. */
|
|
2545
|
+
instance: ContextField<InstanceDiagnosis2>;
|
|
2389
2546
|
}
|
|
2390
2547
|
declare class SecondLayer extends BaseClient {
|
|
2391
2548
|
readonly streams: StreamsClient;
|
|
@@ -2393,6 +2550,8 @@ declare class SecondLayer extends BaseClient {
|
|
|
2393
2550
|
readonly contracts: Contracts;
|
|
2394
2551
|
readonly subgraphs: Subgraphs;
|
|
2395
2552
|
readonly subscriptions: Subscriptions;
|
|
2553
|
+
readonly archive: SecondLayerArchive;
|
|
2554
|
+
readonly instance: InstanceClient;
|
|
2396
2555
|
constructor(options?: Partial<SecondLayerOptions>);
|
|
2397
2556
|
/**
|
|
2398
2557
|
* Up to 10 public reads in one round trip (`POST /v1/batch`). Each item
|
package/dist/subgraphs/index.js
CHANGED
|
@@ -110,6 +110,39 @@ class ValidationError extends ApiError {
|
|
|
110
110
|
this.name = "ValidationError";
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
+
|
|
114
|
+
class ArchiveSignatureError extends SecondLayerError {
|
|
115
|
+
constructor(message = "Archive manifest signature is missing or invalid.") {
|
|
116
|
+
super(message, { code: "ARCHIVE_SIGNATURE", retryable: false });
|
|
117
|
+
this.name = "ArchiveSignatureError";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
class ArchiveAuthError extends SecondLayerError {
|
|
122
|
+
constructor(message = "Archive ops request was not authenticated.") {
|
|
123
|
+
super(message, { code: "ARCHIVE_AUTH", retryable: false });
|
|
124
|
+
this.name = "ArchiveAuthError";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
class InsufficientArchiveCreditsError extends SecondLayerError {
|
|
129
|
+
shortfallUsdMicros;
|
|
130
|
+
constructor(message = "Insufficient archive credits.", shortfallUsdMicros) {
|
|
131
|
+
super(message, {
|
|
132
|
+
code: "INSUFFICIENT_ARCHIVE_CREDITS",
|
|
133
|
+
retryable: false
|
|
134
|
+
});
|
|
135
|
+
this.name = "InsufficientArchiveCreditsError";
|
|
136
|
+
this.shortfallUsdMicros = shortfallUsdMicros;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
class ArchiveGateNotConfiguredError extends SecondLayerError {
|
|
141
|
+
constructor(message = "The archive gate is not configured on the server.") {
|
|
142
|
+
super(message, { code: "ARCHIVE_GATE_NOT_CONFIGURED", retryable: false });
|
|
143
|
+
this.name = "ArchiveGateNotConfiguredError";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
113
146
|
function parseRetryAfter(value) {
|
|
114
147
|
if (!value)
|
|
115
148
|
return;
|
|
@@ -814,6 +847,239 @@ class Subgraphs extends BaseClient {
|
|
|
814
847
|
};
|
|
815
848
|
}
|
|
816
849
|
}
|
|
850
|
+
// src/archive/client.ts
|
|
851
|
+
import { createHash } from "node:crypto";
|
|
852
|
+
import {
|
|
853
|
+
checkSignature,
|
|
854
|
+
loadReference
|
|
855
|
+
} from "@secondlayer/shared/archive/reference";
|
|
856
|
+
var OFFICIAL_ARCHIVE_HOST = "archive.secondlayer.tools";
|
|
857
|
+
var DEFAULT_ARCHIVE_BASE_URL = "https://archive.secondlayer.tools";
|
|
858
|
+
var DEFAULT_ARCHIVE_OPS_URL = "https://api.secondlayer.tools";
|
|
859
|
+
var MAX_FETCH_BATCH = 64;
|
|
860
|
+
function quoteFromBody(body) {
|
|
861
|
+
return {
|
|
862
|
+
partitions: body.partitions,
|
|
863
|
+
bundles: body.bundles,
|
|
864
|
+
usdMicros: body.usd_micros,
|
|
865
|
+
usd: body.usd,
|
|
866
|
+
freeAllowanceAppliedMicros: body.free_allowance_applied_micros,
|
|
867
|
+
allowanceRemainingBundles: body.allowance_remaining_bundles,
|
|
868
|
+
balanceUsdMicros: body.balance_usd_micros,
|
|
869
|
+
sufficient: body.sufficient
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
function stripSlash(url) {
|
|
873
|
+
return url.replace(/\/+$/, "");
|
|
874
|
+
}
|
|
875
|
+
function createArchiveClient(options = {}) {
|
|
876
|
+
const apiKey = resolveApiKey(options.apiKey);
|
|
877
|
+
const fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));
|
|
878
|
+
const archiveBaseUrl = stripSlash(options.archiveBaseUrl ?? DEFAULT_ARCHIVE_BASE_URL);
|
|
879
|
+
const archiveOpsUrl = stripSlash(options.archiveOpsUrl ?? DEFAULT_ARCHIVE_OPS_URL);
|
|
880
|
+
const verifyManifest = options.verifyManifest ?? true;
|
|
881
|
+
const pinnedPem = options.publicKeyPem;
|
|
882
|
+
let keyPromise = null;
|
|
883
|
+
async function fetchSigningKey() {
|
|
884
|
+
if (pinnedPem)
|
|
885
|
+
return pinnedPem;
|
|
886
|
+
if (keyPromise)
|
|
887
|
+
return keyPromise;
|
|
888
|
+
const pending = (async () => {
|
|
889
|
+
const res = await fetchImpl(`${archiveOpsUrl}/public/streams/signing-key`);
|
|
890
|
+
if (!res.ok) {
|
|
891
|
+
throw new ArchiveSignatureError(`Could not fetch archive signing key (${res.status}).`);
|
|
892
|
+
}
|
|
893
|
+
const body = await res.json();
|
|
894
|
+
if (typeof body.public_key_pem !== "string") {
|
|
895
|
+
throw new ArchiveSignatureError("Signing key response missing key.");
|
|
896
|
+
}
|
|
897
|
+
return body.public_key_pem;
|
|
898
|
+
})();
|
|
899
|
+
keyPromise = pending;
|
|
900
|
+
pending.catch(() => {
|
|
901
|
+
if (keyPromise === pending)
|
|
902
|
+
keyPromise = null;
|
|
903
|
+
});
|
|
904
|
+
return pending;
|
|
905
|
+
}
|
|
906
|
+
async function throwOpsError(response) {
|
|
907
|
+
const envelope = parseErrorEnvelope(await response.text());
|
|
908
|
+
if (response.status === 401) {
|
|
909
|
+
throw new ArchiveAuthError(envelope.message ?? "Archive ops request was not authenticated.");
|
|
910
|
+
}
|
|
911
|
+
if (response.status === 402) {
|
|
912
|
+
const raw = envelope.body && typeof envelope.body === "object" && "shortfall_usd_micros" in envelope.body ? Number(envelope.body.shortfall_usd_micros) : undefined;
|
|
913
|
+
throw new InsufficientArchiveCreditsError(envelope.message ?? "Insufficient archive credits.", Number.isFinite(raw) ? raw : undefined);
|
|
914
|
+
}
|
|
915
|
+
if (response.status === 503) {
|
|
916
|
+
throw new ArchiveGateNotConfiguredError(envelope.message ?? "The archive gate is not configured on the server.");
|
|
917
|
+
}
|
|
918
|
+
throw new ApiError(response.status, envelope.message ?? `Archive ops returned ${response.status}.`, envelope.body, envelope.code);
|
|
919
|
+
}
|
|
920
|
+
async function opsRequest(method, path, body, authed = true) {
|
|
921
|
+
const headers = {
|
|
922
|
+
"Content-Type": "application/json"
|
|
923
|
+
};
|
|
924
|
+
if (authed && apiKey)
|
|
925
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
926
|
+
const response = await fetchImpl(`${archiveOpsUrl}${path}`, {
|
|
927
|
+
method,
|
|
928
|
+
headers,
|
|
929
|
+
body: body !== undefined ? JSON.stringify(body) : undefined
|
|
930
|
+
});
|
|
931
|
+
if (!response.ok)
|
|
932
|
+
await throwOpsError(response);
|
|
933
|
+
return await response.json();
|
|
934
|
+
}
|
|
935
|
+
async function load(against, opts = {}) {
|
|
936
|
+
if (!/^https?:\/\//i.test(against)) {
|
|
937
|
+
throw new ValidationError("archive.load() accepts only http(s) URLs; local paths stay on the CLI.", 400);
|
|
938
|
+
}
|
|
939
|
+
const insecure = opts.insecure === true;
|
|
940
|
+
const publicKeyPem = pinnedPem ?? (verifyManifest && !insecure ? await fetchSigningKey() : undefined);
|
|
941
|
+
const ref = await loadReference(against, {
|
|
942
|
+
publicKeyPem,
|
|
943
|
+
fetchImpl
|
|
944
|
+
});
|
|
945
|
+
const signature = checkSignature(ref.manifest, publicKeyPem, insecure);
|
|
946
|
+
if (verifyManifest && !insecure && !signature.verified) {
|
|
947
|
+
throw new ArchiveSignatureError(signature.reason ?? "Archive manifest signature is missing or invalid.");
|
|
948
|
+
}
|
|
949
|
+
return { ...ref, signature };
|
|
950
|
+
}
|
|
951
|
+
async function latest(against, opts = {}) {
|
|
952
|
+
return load(against ?? `${archiveBaseUrl}/latest.json`, opts);
|
|
953
|
+
}
|
|
954
|
+
async function status() {
|
|
955
|
+
const response = await fetchImpl(`${archiveBaseUrl}/status.json`);
|
|
956
|
+
if (!response.ok) {
|
|
957
|
+
throw new ApiError(response.status, `Could not fetch archive status (${response.status}).`);
|
|
958
|
+
}
|
|
959
|
+
return response.json();
|
|
960
|
+
}
|
|
961
|
+
function partitions(ref, filter) {
|
|
962
|
+
let list = ref.manifest.partitions ?? [];
|
|
963
|
+
if (!filter)
|
|
964
|
+
return list;
|
|
965
|
+
if (filter.dataset) {
|
|
966
|
+
list = list.filter((p) => p.dataset === filter.dataset);
|
|
967
|
+
}
|
|
968
|
+
const fromBlock = filter.fromBlock;
|
|
969
|
+
if (fromBlock !== undefined) {
|
|
970
|
+
list = list.filter((p) => p.to_block >= fromBlock);
|
|
971
|
+
}
|
|
972
|
+
const toBlock = filter.toBlock;
|
|
973
|
+
if (toBlock !== undefined) {
|
|
974
|
+
list = list.filter((p) => p.from_block <= toBlock);
|
|
975
|
+
}
|
|
976
|
+
return list;
|
|
977
|
+
}
|
|
978
|
+
async function quote(input) {
|
|
979
|
+
const body = await opsRequest("POST", "/api/archive/quote", { paths: input.paths, flow: input.flow });
|
|
980
|
+
return quoteFromBody(body);
|
|
981
|
+
}
|
|
982
|
+
async function fetchPartitions(input) {
|
|
983
|
+
const urls = [];
|
|
984
|
+
let chargedTotalUsdMicros = 0;
|
|
985
|
+
let balanceAfterUsdMicros = 0;
|
|
986
|
+
for (let i = 0;i < input.paths.length; i += MAX_FETCH_BATCH) {
|
|
987
|
+
const chunk = input.paths.slice(i, i + MAX_FETCH_BATCH);
|
|
988
|
+
const body = await opsRequest("POST", "/api/archive/fetch", { paths: chunk, flow: input.flow });
|
|
989
|
+
for (const item of body.urls) {
|
|
990
|
+
urls.push({
|
|
991
|
+
path: item.path,
|
|
992
|
+
url: item.url,
|
|
993
|
+
expiresAt: item.expires_at,
|
|
994
|
+
chargedUsdMicros: item.charged_usd_micros
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
chargedTotalUsdMicros += body.charged_total_usd_micros;
|
|
998
|
+
balanceAfterUsdMicros = body.balance_after_usd_micros;
|
|
999
|
+
}
|
|
1000
|
+
return { urls, chargedTotalUsdMicros, balanceAfterUsdMicros };
|
|
1001
|
+
}
|
|
1002
|
+
async function download(partition, opts) {
|
|
1003
|
+
let url = opts?.url;
|
|
1004
|
+
if (!url) {
|
|
1005
|
+
let official = false;
|
|
1006
|
+
try {
|
|
1007
|
+
official = new URL(archiveBaseUrl).hostname === OFFICIAL_ARCHIVE_HOST;
|
|
1008
|
+
} catch {
|
|
1009
|
+
official = false;
|
|
1010
|
+
}
|
|
1011
|
+
if (official) {
|
|
1012
|
+
throw new ValidationError("Official archive host requires a presigned URL from archive.fetch() before download.", 400);
|
|
1013
|
+
}
|
|
1014
|
+
url = `${archiveBaseUrl}/${partition.path.replace(/^\/+/, "")}`;
|
|
1015
|
+
}
|
|
1016
|
+
const response = await fetchImpl(url);
|
|
1017
|
+
if (!response.ok) {
|
|
1018
|
+
throw new ApiError(response.status, `Could not download ${partition.path} (${response.status}).`);
|
|
1019
|
+
}
|
|
1020
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
1021
|
+
const digest = createHash("sha256").update(bytes).digest("hex");
|
|
1022
|
+
if (digest !== partition.sha256) {
|
|
1023
|
+
throw new ArchiveSignatureError(`${partition.path} sha256 mismatch (expected ${partition.sha256}, got ${digest}).`);
|
|
1024
|
+
}
|
|
1025
|
+
return bytes;
|
|
1026
|
+
}
|
|
1027
|
+
return {
|
|
1028
|
+
latest,
|
|
1029
|
+
load,
|
|
1030
|
+
status,
|
|
1031
|
+
partitions,
|
|
1032
|
+
quote,
|
|
1033
|
+
fetch: fetchPartitions,
|
|
1034
|
+
download,
|
|
1035
|
+
credits: {
|
|
1036
|
+
async balance() {
|
|
1037
|
+
const body = await opsRequest("GET", "/api/billing/status");
|
|
1038
|
+
return {
|
|
1039
|
+
creditsUsdMicros: body.creditsUsdMicros,
|
|
1040
|
+
refill: body.refill
|
|
1041
|
+
};
|
|
1042
|
+
},
|
|
1043
|
+
async checkout(input) {
|
|
1044
|
+
const body = await opsRequest("POST", "/api/public/credits/checkout", { email: input.email, amount: input.pack }, false);
|
|
1045
|
+
return { url: body.url };
|
|
1046
|
+
},
|
|
1047
|
+
async refill(input) {
|
|
1048
|
+
const payload = "off" in input && input.off ? { belowUsd: null } : {
|
|
1049
|
+
belowUsd: input.belowUsd,
|
|
1050
|
+
packUsd: input.packUsd
|
|
1051
|
+
};
|
|
1052
|
+
const body = await opsRequest("POST", "/api/billing/refill", payload);
|
|
1053
|
+
return { belowUsd: body.belowUsd, packUsd: body.packUsd };
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// src/archive/instance.ts
|
|
1060
|
+
import {
|
|
1061
|
+
diagnoseInstanceStatus
|
|
1062
|
+
} from "@secondlayer/shared/archive/instance-diagnosis";
|
|
1063
|
+
function createArchiveVerify(request) {
|
|
1064
|
+
return (input) => request("POST", "/v1/archive/verify", {
|
|
1065
|
+
against: input.against,
|
|
1066
|
+
target: input.target ?? "raw",
|
|
1067
|
+
from_block: input.fromBlock,
|
|
1068
|
+
to_block: input.toBlock,
|
|
1069
|
+
insecure: input.insecure,
|
|
1070
|
+
public_key_pem: input.publicKeyPem
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
function createInstanceClient(request) {
|
|
1074
|
+
async function status() {
|
|
1075
|
+
return request("GET", "/public/status");
|
|
1076
|
+
}
|
|
1077
|
+
async function diagnose() {
|
|
1078
|
+
return diagnoseInstanceStatus(await status());
|
|
1079
|
+
}
|
|
1080
|
+
return { status, diagnose };
|
|
1081
|
+
}
|
|
1082
|
+
|
|
817
1083
|
// src/contracts/client.ts
|
|
818
1084
|
class Contracts extends BaseClient {
|
|
819
1085
|
constructor(options = {}) {
|
|
@@ -1786,7 +2052,7 @@ function decode(event) {
|
|
|
1786
2052
|
}
|
|
1787
2053
|
|
|
1788
2054
|
// src/streams/dumps.ts
|
|
1789
|
-
import { createHash } from "node:crypto";
|
|
2055
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
1790
2056
|
import { verifyStreamsBulkManifestSignature } from "@secondlayer/shared/streams-bulk-manifest";
|
|
1791
2057
|
function createStreamsDumps(opts) {
|
|
1792
2058
|
const baseUrl = opts.baseUrl?.replace(/\/+$/, "");
|
|
@@ -1838,7 +2104,7 @@ function createStreamsDumps(opts) {
|
|
|
1838
2104
|
if (!res.body) {
|
|
1839
2105
|
throw new StreamsServerError(`Dump ${file.path} response has no body.`, 0);
|
|
1840
2106
|
}
|
|
1841
|
-
const hash =
|
|
2107
|
+
const hash = createHash2("sha256");
|
|
1842
2108
|
const chunks = [];
|
|
1843
2109
|
let total = 0;
|
|
1844
2110
|
const reader = res.body.getReader();
|
|
@@ -2232,6 +2498,8 @@ class SecondLayer extends BaseClient {
|
|
|
2232
2498
|
contracts;
|
|
2233
2499
|
subgraphs;
|
|
2234
2500
|
subscriptions;
|
|
2501
|
+
archive;
|
|
2502
|
+
instance;
|
|
2235
2503
|
constructor(options = {}) {
|
|
2236
2504
|
super(options);
|
|
2237
2505
|
this.streams = createStreamsClient({
|
|
@@ -2247,17 +2515,36 @@ class SecondLayer extends BaseClient {
|
|
|
2247
2515
|
this.contracts = new Contracts(options);
|
|
2248
2516
|
this.subgraphs = new Subgraphs(options);
|
|
2249
2517
|
this.subscriptions = new Subscriptions(options);
|
|
2518
|
+
const request = (method, path, body) => this.request(method, path, body);
|
|
2519
|
+
this.archive = {
|
|
2520
|
+
...createArchiveClient({
|
|
2521
|
+
apiKey: this.apiKey,
|
|
2522
|
+
fetchImpl: options.fetchImpl,
|
|
2523
|
+
archiveBaseUrl: options.archiveBaseUrl,
|
|
2524
|
+
archiveOpsUrl: options.archiveOpsUrl
|
|
2525
|
+
}),
|
|
2526
|
+
verify: createArchiveVerify(request)
|
|
2527
|
+
};
|
|
2528
|
+
this.instance = createInstanceClient(request);
|
|
2250
2529
|
}
|
|
2251
2530
|
async batch(requests) {
|
|
2252
2531
|
return this.request("POST", "/v1/batch", { requests });
|
|
2253
2532
|
}
|
|
2254
2533
|
async context() {
|
|
2255
|
-
const [
|
|
2534
|
+
const [
|
|
2535
|
+
account,
|
|
2536
|
+
streamsTip,
|
|
2537
|
+
indexEnv,
|
|
2538
|
+
subgraphsRes,
|
|
2539
|
+
subscriptionsRes,
|
|
2540
|
+
instance
|
|
2541
|
+
] = await Promise.all([
|
|
2256
2542
|
contextField(this.request("GET", "/api/accounts/me")),
|
|
2257
2543
|
contextField(this.streams.tip()),
|
|
2258
2544
|
contextField(this.index.canonical.list({ limit: 1 })),
|
|
2259
2545
|
contextField(this.subgraphs.list()),
|
|
2260
|
-
contextField(this.subscriptions.list())
|
|
2546
|
+
contextField(this.subscriptions.list()),
|
|
2547
|
+
contextField(this.instance.diagnose())
|
|
2261
2548
|
]);
|
|
2262
2549
|
const subgraphs = {
|
|
2263
2550
|
value: subgraphsRes.value?.data ?? null,
|
|
@@ -2309,7 +2596,8 @@ class SecondLayer extends BaseClient {
|
|
|
2309
2596
|
},
|
|
2310
2597
|
subgraphs,
|
|
2311
2598
|
subscriptions,
|
|
2312
|
-
activeOperations
|
|
2599
|
+
activeOperations,
|
|
2600
|
+
instance
|
|
2313
2601
|
};
|
|
2314
2602
|
}
|
|
2315
2603
|
}
|
|
@@ -2329,5 +2617,5 @@ export {
|
|
|
2329
2617
|
Subgraphs
|
|
2330
2618
|
};
|
|
2331
2619
|
|
|
2332
|
-
//# debugId=
|
|
2620
|
+
//# debugId=59EE80259E1C9AEC64756E2164756E21
|
|
2333
2621
|
//# sourceMappingURL=index.js.map
|