@saga-sync/producer 0.0.0 → 0.1.2
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/chunk-builder/accumulator.d.ts +29 -0
- package/dist/chunk-builder/accumulator.d.ts.map +1 -0
- package/dist/chunk-builder/accumulator.js +72 -0
- package/dist/chunk-builder/accumulator.js.map +1 -0
- package/dist/chunk-builder/archive.d.ts +18 -0
- package/dist/chunk-builder/archive.d.ts.map +1 -0
- package/dist/chunk-builder/archive.js +68 -0
- package/dist/chunk-builder/archive.js.map +1 -0
- package/dist/chunk-builder/cli.d.ts +28 -0
- package/dist/chunk-builder/cli.d.ts.map +1 -0
- package/dist/chunk-builder/cli.js +167 -0
- package/dist/chunk-builder/cli.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/keygen.d.ts +3 -0
- package/dist/keygen.d.ts.map +1 -0
- package/dist/keygen.js +10 -0
- package/dist/keygen.js.map +1 -0
- package/dist/orchestrator/cli.d.ts +37 -0
- package/dist/orchestrator/cli.d.ts.map +1 -0
- package/dist/orchestrator/cli.js +336 -0
- package/dist/orchestrator/cli.js.map +1 -0
- package/dist/orchestrator/pipeline.d.ts +32 -0
- package/dist/orchestrator/pipeline.d.ts.map +1 -0
- package/dist/orchestrator/pipeline.js +32 -0
- package/dist/orchestrator/pipeline.js.map +1 -0
- package/dist/scraper/cli.d.ts +5 -0
- package/dist/scraper/cli.d.ts.map +1 -0
- package/dist/scraper/cli.js +153 -0
- package/dist/scraper/cli.js.map +1 -0
- package/dist/scraper/config.d.ts +15 -0
- package/dist/scraper/config.d.ts.map +1 -0
- package/dist/scraper/config.js +87 -0
- package/dist/scraper/config.js.map +1 -0
- package/dist/scraper/cursor.d.ts +12 -0
- package/dist/scraper/cursor.d.ts.map +1 -0
- package/dist/scraper/cursor.js +38 -0
- package/dist/scraper/cursor.js.map +1 -0
- package/dist/scraper/normalize.d.ts +5 -0
- package/dist/scraper/normalize.d.ts.map +1 -0
- package/dist/scraper/normalize.js +19 -0
- package/dist/scraper/normalize.js.map +1 -0
- package/dist/scraper/scrape.d.ts +10 -0
- package/dist/scraper/scrape.d.ts.map +1 -0
- package/dist/scraper/scrape.js +70 -0
- package/dist/scraper/scrape.js.map +1 -0
- package/dist/storage/dry-run-store.d.ts +10 -0
- package/dist/storage/dry-run-store.d.ts.map +1 -0
- package/dist/storage/dry-run-store.js +22 -0
- package/dist/storage/dry-run-store.js.map +1 -0
- package/dist/storage/gcs-store.d.ts +44 -0
- package/dist/storage/gcs-store.d.ts.map +1 -0
- package/dist/storage/gcs-store.js +85 -0
- package/dist/storage/gcs-store.js.map +1 -0
- package/dist/storage/index.d.ts +15 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +52 -0
- package/dist/storage/index.js.map +1 -0
- package/package.json +38 -16
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
const address = z
|
|
4
|
+
.string()
|
|
5
|
+
.regex(/^0x[0-9a-fA-F]{40}$/, "expected a 20-byte (0x + 40 hex) address");
|
|
6
|
+
const topic = z
|
|
7
|
+
.string()
|
|
8
|
+
.regex(/^0x[0-9a-fA-F]{64}$/, "expected a 32-byte (0x + 64 hex) topic");
|
|
9
|
+
const quantity = z
|
|
10
|
+
.string()
|
|
11
|
+
.regex(/^0x[0-9a-fA-F]+$/, "expected a 0x-prefixed hex quantity");
|
|
12
|
+
const eventFilterSchema = z.object({
|
|
13
|
+
contractAddress: address,
|
|
14
|
+
eventTopic: topic,
|
|
15
|
+
filter: z.array(topic).optional(),
|
|
16
|
+
});
|
|
17
|
+
// `chunkSettings.maxSizeBytes` (optional, number or 0x-hex) tells the
|
|
18
|
+
// chunk-builder how big each chunk may grow. Other chunkSettings fields are
|
|
19
|
+
// ignored — passthrough is set so they don't cause a validation error.
|
|
20
|
+
const chunkSettingsSchema = z
|
|
21
|
+
.object({
|
|
22
|
+
maxSizeBytes: z.union([z.number().int().positive(), quantity]).optional(),
|
|
23
|
+
})
|
|
24
|
+
.passthrough()
|
|
25
|
+
.optional();
|
|
26
|
+
const targetSchema = z.object({
|
|
27
|
+
chainId: quantity,
|
|
28
|
+
fromBlock: quantity,
|
|
29
|
+
events: z.array(eventFilterSchema).min(1, "at least one event filter is required"),
|
|
30
|
+
chunkSettings: chunkSettingsSchema,
|
|
31
|
+
});
|
|
32
|
+
function readAndParse(path) {
|
|
33
|
+
let raw;
|
|
34
|
+
try {
|
|
35
|
+
raw = JSON.parse(readFileSync(path, "utf8"));
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
throw new Error(`config ${path}: ${err.message}`);
|
|
39
|
+
}
|
|
40
|
+
const protocols = raw?.protocols;
|
|
41
|
+
if (!protocols || typeof protocols !== "object") {
|
|
42
|
+
throw new Error(`config ${path}: missing top-level "protocols" object`);
|
|
43
|
+
}
|
|
44
|
+
return protocols;
|
|
45
|
+
}
|
|
46
|
+
function parseTarget(path, protocolId, raw) {
|
|
47
|
+
const parsed = targetSchema.safeParse(raw);
|
|
48
|
+
if (!parsed.success) {
|
|
49
|
+
const issues = parsed.error.issues
|
|
50
|
+
.map((i) => ` - ${["protocols", protocolId, ...i.path].join(".")}: ${i.message}`)
|
|
51
|
+
.join("\n");
|
|
52
|
+
throw new Error(`config ${path} is invalid:\n${issues}`);
|
|
53
|
+
}
|
|
54
|
+
const maxSizeRaw = parsed.data.chunkSettings?.maxSizeBytes;
|
|
55
|
+
const maxSizeBytes = maxSizeRaw === undefined
|
|
56
|
+
? undefined
|
|
57
|
+
: typeof maxSizeRaw === "number"
|
|
58
|
+
? maxSizeRaw
|
|
59
|
+
: Number(BigInt(maxSizeRaw));
|
|
60
|
+
const target = {
|
|
61
|
+
chainId: parsed.data.chainId,
|
|
62
|
+
fromBlock: parsed.data.fromBlock,
|
|
63
|
+
events: parsed.data.events,
|
|
64
|
+
};
|
|
65
|
+
if (maxSizeBytes !== undefined)
|
|
66
|
+
target.maxSizeBytes = maxSizeBytes;
|
|
67
|
+
return target;
|
|
68
|
+
}
|
|
69
|
+
export function loadConfig(path, protocolId) {
|
|
70
|
+
const protocols = readAndParse(path);
|
|
71
|
+
if (!(protocolId in protocols)) {
|
|
72
|
+
const known = Object.keys(protocols).join(", ") || "(none)";
|
|
73
|
+
throw new Error(`config ${path}: no protocol "${protocolId}". Known: ${known}`);
|
|
74
|
+
}
|
|
75
|
+
return parseTarget(path, protocolId, protocols[protocolId]);
|
|
76
|
+
}
|
|
77
|
+
// Orchestrator-side: load every protocol at once. Each is validated independently
|
|
78
|
+
// so a single bad protocol entry doesn't poison the dict.
|
|
79
|
+
export function loadAllProtocols(path) {
|
|
80
|
+
const protocols = readAndParse(path);
|
|
81
|
+
const out = {};
|
|
82
|
+
for (const [id, raw] of Object.entries(protocols)) {
|
|
83
|
+
out[id] = parseTarget(path, id, raw);
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/scraper/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Hex } from "viem";
|
|
2
|
+
import type { Store } from "@saga-sync/core";
|
|
3
|
+
export declare class Cursor {
|
|
4
|
+
private readonly store;
|
|
5
|
+
private readonly key;
|
|
6
|
+
private data;
|
|
7
|
+
private constructor();
|
|
8
|
+
static load(store: Store, key?: string): Promise<Cursor>;
|
|
9
|
+
lastScrapedBlock(protocolId: string): Hex | undefined;
|
|
10
|
+
set(protocolId: string, lastScrapedBlock: Hex): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/scraper/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAChC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAQ7C,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,IAAI;IAHd,OAAO;WAMM,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;IAgB1E,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS;IAK/C,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;CAOpE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const CURSOR_KEY = "cursor.json";
|
|
2
|
+
// The standalone scraper's resume pointer, keyed by the opaque protocol-instance
|
|
3
|
+
// id. `lastScrapedBlock` is the last block covered by a successful run; the next
|
|
4
|
+
// run resumes at lastScrapedBlock + 1. Only used by direct scraper CLI runs —
|
|
5
|
+
// the orchestrator derives resume points from the manifest instead.
|
|
6
|
+
export class Cursor {
|
|
7
|
+
store;
|
|
8
|
+
key;
|
|
9
|
+
data;
|
|
10
|
+
constructor(store, key, data) {
|
|
11
|
+
this.store = store;
|
|
12
|
+
this.key = key;
|
|
13
|
+
this.data = data;
|
|
14
|
+
}
|
|
15
|
+
static async load(store, key = CURSOR_KEY) {
|
|
16
|
+
const raw = await store.get(key);
|
|
17
|
+
if (!raw)
|
|
18
|
+
return new Cursor(store, key, {});
|
|
19
|
+
try {
|
|
20
|
+
const data = JSON.parse(new TextDecoder().decode(raw));
|
|
21
|
+
return new Cursor(store, key, data);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
// A corrupt cursor is a hard error — silently treating it as empty would
|
|
25
|
+
// re-scrape from the cold-start block and flood the downstream consumer.
|
|
26
|
+
throw new Error(`cursor ${key}: ${err.message}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
lastScrapedBlock(protocolId) {
|
|
30
|
+
return this.data[protocolId]?.lastScrapedBlock;
|
|
31
|
+
}
|
|
32
|
+
// Atomic via the Store (DiskStore: temp-file + rename).
|
|
33
|
+
async set(protocolId, lastScrapedBlock) {
|
|
34
|
+
this.data[protocolId] = { lastScrapedBlock };
|
|
35
|
+
await this.store.put(this.key, Buffer.from(JSON.stringify(this.data, null, 2) + "\n", "utf8"));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/scraper/cursor.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,GAAG,aAAa,CAAC;AAEjC,iFAAiF;AACjF,iFAAiF;AACjF,8EAA8E;AAC9E,oEAAoE;AACpE,MAAM,OAAO,MAAM;IAEE;IACA;IACT;IAHV,YACmB,KAAY,EACZ,GAAW,EACpB,IAA+C;QAFtC,UAAK,GAAL,KAAK,CAAO;QACZ,QAAG,GAAH,GAAG,CAAQ;QACpB,SAAI,GAAJ,IAAI,CAA2C;IACtD,CAAC;IAEJ,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAY,EAAE,MAAc,UAAU;QACtD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAGpD,CAAC;YACF,OAAO,IAAI,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,yEAAyE;YACzE,yEAAyE;YACzE,MAAM,IAAI,KAAK,CAAC,UAAU,GAAG,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,gBAAgB,CAAC,UAAkB;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;IACjD,CAAC;IAED,wDAAwD;IACxD,KAAK,CAAC,GAAG,CAAC,UAAkB,EAAE,gBAAqB;QACjD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,gBAAgB,EAAE,CAAC;QAC7C,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAClB,IAAI,CAAC,GAAG,EACR,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAC/D,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/scraper/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,cAAc,EAAO,MAAM,iBAAiB,CAAC;AAM3D,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAItD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAoBrD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const lower = (hex) => hex.toLowerCase();
|
|
2
|
+
export function normalize(log) {
|
|
3
|
+
const eventTopic = log.topics[0];
|
|
4
|
+
if (!eventTopic) {
|
|
5
|
+
throw new Error(`log without topics (tx ${log.transactionHash ?? "?"}, logIndex ${log.logIndex ?? "?"})`);
|
|
6
|
+
}
|
|
7
|
+
if (log.blockNumber == null || log.logIndex == null) {
|
|
8
|
+
throw new Error(`log missing block/index fields — pending logs are not scrapable (tx ${log.transactionHash ?? "?"})`);
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
contractAddress: lower(log.address),
|
|
12
|
+
eventTopic: lower(eventTopic),
|
|
13
|
+
topics: log.topics.map(lower),
|
|
14
|
+
data: lower(log.data),
|
|
15
|
+
blockNumber: lower(log.blockNumber),
|
|
16
|
+
logIndex: lower(log.logIndex),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/scraper/normalize.ts"],"names":[],"mappings":"AASA,MAAM,KAAK,GAAG,CAAC,GAAW,EAAO,EAAE,CAAC,GAAG,CAAC,WAAW,EAAS,CAAC;AAE7D,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACb,0BAA0B,GAAG,CAAC,eAAe,IAAI,GAAG,cAAc,GAAG,CAAC,QAAQ,IAAI,GAAG,GAAG,CACzF,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,WAAW,IAAI,IAAI,IAAI,GAAG,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CACb,uEAAuE,GAAG,CAAC,eAAe,IAAI,GAAG,GAAG,CACrG,CAAC;IACJ,CAAC;IACD,OAAO;QACL,eAAe,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;QACnC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC;QAC7B,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;QAC7B,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;QACrB,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC;QACnC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;KAC9B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PublicClient, RpcLog } from "viem";
|
|
2
|
+
import type { EventFilter } from "./config.js";
|
|
3
|
+
export type ScrapeOptions = {
|
|
4
|
+
fromBlock: bigint;
|
|
5
|
+
toBlock: bigint;
|
|
6
|
+
events: EventFilter[];
|
|
7
|
+
window: number;
|
|
8
|
+
};
|
|
9
|
+
export declare function scrape(client: PublicClient, { fromBlock, toBlock, events, window }: ScrapeOptions): AsyncGenerator<RpcLog>;
|
|
10
|
+
//# sourceMappingURL=scrape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrape.d.ts","sourceRoot":"","sources":["../../src/scraper/scrape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,YAAY,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAOF,wBAAuB,MAAM,CAC3B,MAAM,EAAE,YAAY,EACpB,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,aAAa,GACpD,cAAc,CAAC,MAAM,CAAC,CAsBxB"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { numberToHex } from "viem";
|
|
2
|
+
// Streams raw RPC logs for [fromBlock, toBlock]. The range is sliced into
|
|
3
|
+
// `window`-sized sub-ranges so a single eth_getLogs call never spans more blocks
|
|
4
|
+
// than a provider will accept. On a range/result-size error the window is halved
|
|
5
|
+
// and the same sub-range retried — that adaptive split is what makes this work
|
|
6
|
+
// against real RPCs whose limits vary.
|
|
7
|
+
export async function* scrape(client, { fromBlock, toBlock, events, window }) {
|
|
8
|
+
let windowSize = BigInt(Math.max(1, Math.floor(window)));
|
|
9
|
+
let start = fromBlock;
|
|
10
|
+
while (start <= toBlock) {
|
|
11
|
+
let end = start + windowSize - 1n;
|
|
12
|
+
if (end > toBlock)
|
|
13
|
+
end = toBlock;
|
|
14
|
+
let logs;
|
|
15
|
+
try {
|
|
16
|
+
logs = await getLogsForWindow(client, start, end, events);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
if (isRangeError(err) && windowSize > 1n) {
|
|
20
|
+
windowSize = windowSize / 2n;
|
|
21
|
+
continue; // retry the same `start` with a smaller window
|
|
22
|
+
}
|
|
23
|
+
throw err;
|
|
24
|
+
}
|
|
25
|
+
for (const log of logs)
|
|
26
|
+
yield log;
|
|
27
|
+
start = end + 1n;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function getLogsForWindow(client, fromBlock, toBlock, events) {
|
|
31
|
+
const out = [];
|
|
32
|
+
for (const filter of events) {
|
|
33
|
+
const topics = [filter.eventTopic, ...(filter.filter ?? [])];
|
|
34
|
+
const logs = (await client.request({
|
|
35
|
+
method: "eth_getLogs",
|
|
36
|
+
params: [
|
|
37
|
+
{
|
|
38
|
+
address: filter.contractAddress,
|
|
39
|
+
topics,
|
|
40
|
+
fromBlock: numberToHex(fromBlock),
|
|
41
|
+
toBlock: numberToHex(toBlock),
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
}));
|
|
45
|
+
out.push(...logs);
|
|
46
|
+
}
|
|
47
|
+
// Windows are scanned in ascending order; sorting within the window makes the
|
|
48
|
+
// whole NDJSON stream globally ordered by (blockNumber, logIndex).
|
|
49
|
+
out.sort((a, b) => {
|
|
50
|
+
const ab = BigInt(a.blockNumber ?? 0);
|
|
51
|
+
const bb = BigInt(b.blockNumber ?? 0);
|
|
52
|
+
if (ab !== bb)
|
|
53
|
+
return ab < bb ? -1 : 1;
|
|
54
|
+
const ai = BigInt(a.logIndex ?? 0);
|
|
55
|
+
const bi = BigInt(b.logIndex ?? 0);
|
|
56
|
+
return ai < bi ? -1 : ai > bi ? 1 : 0;
|
|
57
|
+
});
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
function isRangeError(err) {
|
|
61
|
+
const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
62
|
+
return (msg.includes("range") ||
|
|
63
|
+
msg.includes("too many results") ||
|
|
64
|
+
msg.includes("too large") ||
|
|
65
|
+
msg.includes("limit exceeded") ||
|
|
66
|
+
msg.includes("more than") ||
|
|
67
|
+
msg.includes("response size") ||
|
|
68
|
+
msg.includes("query timeout"));
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=scrape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrape.js","sourceRoot":"","sources":["../../src/scraper/scrape.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Store } from "@saga-sync/core";
|
|
2
|
+
export declare class DryRunStore implements Store {
|
|
3
|
+
private readonly inner;
|
|
4
|
+
constructor(inner: Store);
|
|
5
|
+
put(_key: string, _data: Uint8Array): Promise<void>;
|
|
6
|
+
delete(_key: string): Promise<void>;
|
|
7
|
+
get(key: string): Promise<Uint8Array | null>;
|
|
8
|
+
list(prefix: string): Promise<string[]>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=dry-run-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dry-run-store.d.ts","sourceRoot":"","sources":["../../src/storage/dry-run-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAK7C,qBAAa,WAAY,YAAW,KAAK;IAC3B,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,KAAK;IAEnC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAI5C,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAGxC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Wraps another Store so that writes (put, delete) become no-ops while reads
|
|
2
|
+
// (get, list) pass through. Lets `--dry-run` be expressed entirely at the
|
|
3
|
+
// storage layer — every other class (ChunkArchive, Manifest) stays oblivious.
|
|
4
|
+
export class DryRunStore {
|
|
5
|
+
inner;
|
|
6
|
+
constructor(inner) {
|
|
7
|
+
this.inner = inner;
|
|
8
|
+
}
|
|
9
|
+
async put(_key, _data) {
|
|
10
|
+
/* dry-run: discard writes */
|
|
11
|
+
}
|
|
12
|
+
async delete(_key) {
|
|
13
|
+
/* dry-run: discard deletes */
|
|
14
|
+
}
|
|
15
|
+
get(key) {
|
|
16
|
+
return this.inner.get(key);
|
|
17
|
+
}
|
|
18
|
+
list(prefix) {
|
|
19
|
+
return this.inner.list(prefix);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=dry-run-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dry-run-store.js","sourceRoot":"","sources":["../../src/storage/dry-run-store.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,0EAA0E;AAC1E,8EAA8E;AAC9E,MAAM,OAAO,WAAW;IACO;IAA7B,YAA6B,KAAY;QAAZ,UAAK,GAAL,KAAK,CAAO;IAAG,CAAC;IAE7C,KAAK,CAAC,GAAG,CAAC,IAAY,EAAE,KAAiB;QACvC,6BAA6B;IAC/B,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,8BAA8B;IAChC,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,CAAC,MAAc;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;CACF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Store } from "@saga-sync/core";
|
|
2
|
+
export type SaveOptions = {
|
|
3
|
+
contentType?: string;
|
|
4
|
+
metadata?: {
|
|
5
|
+
cacheControl?: string;
|
|
6
|
+
};
|
|
7
|
+
resumable?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export interface GcsFile {
|
|
10
|
+
save(data: Uint8Array, opts: SaveOptions): Promise<void>;
|
|
11
|
+
download(): Promise<[Uint8Array]>;
|
|
12
|
+
delete(opts: {
|
|
13
|
+
ignoreNotFound: boolean;
|
|
14
|
+
}): Promise<unknown>;
|
|
15
|
+
}
|
|
16
|
+
export interface GcsBucket {
|
|
17
|
+
file(name: string): GcsFile;
|
|
18
|
+
getFiles(opts: {
|
|
19
|
+
prefix: string;
|
|
20
|
+
}): Promise<[Array<{
|
|
21
|
+
name: string;
|
|
22
|
+
}>]>;
|
|
23
|
+
}
|
|
24
|
+
export type BucketProvider = (bucket: string) => Promise<GcsBucket>;
|
|
25
|
+
export declare function cacheControlFor(key: string): string;
|
|
26
|
+
export declare function contentTypeFor(key: string): string;
|
|
27
|
+
export type GcsStoreOptions = {
|
|
28
|
+
prefix?: string;
|
|
29
|
+
bucketProvider?: BucketProvider;
|
|
30
|
+
};
|
|
31
|
+
export declare class GcsStore implements Store {
|
|
32
|
+
private readonly bucketName;
|
|
33
|
+
private readonly prefix;
|
|
34
|
+
private readonly provider;
|
|
35
|
+
private bucketHandle?;
|
|
36
|
+
constructor(bucketName: string, opts?: GcsStoreOptions);
|
|
37
|
+
private bucket;
|
|
38
|
+
private obj;
|
|
39
|
+
put(key: string, data: Uint8Array): Promise<void>;
|
|
40
|
+
get(key: string): Promise<Uint8Array | null>;
|
|
41
|
+
delete(key: string): Promise<void>;
|
|
42
|
+
list(prefix: string): Promise<string[]>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=gcs-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcs-store.d.ts","sourceRoot":"","sources":["../../src/storage/gcs-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAO7C,MAAM,MAAM,WAAW,GAAG;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AACF,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,QAAQ,IAAI,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAClC,MAAM,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7D;AACD,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC,CAAC;CACxE;AACD,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;AAUpE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIlD;AAkBD,MAAM,MAAM,eAAe,GAAG;IAG5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC,CAAC;AAKF,qBAAa,QAAS,YAAW,KAAK;IAMlC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAL7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,YAAY,CAAC,CAAqB;gBAGvB,UAAU,EAAE,MAAM,EACnC,IAAI,GAAE,eAAoB;IAM5B,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,GAAG;IAIL,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IASjD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAW5C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAI9C"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const SEALED = /\.jsonl\.gz$/;
|
|
2
|
+
const HOT = /\.hot\.jsonl\.gz$/;
|
|
3
|
+
// Cache-Control by key. Sealed chunks are immutable + content-addressed, so they
|
|
4
|
+
// cache forever; the manifest and the hot head change every publish, so they get
|
|
5
|
+
// a short TTL that keeps a CDN / browser re-validating. The GcsStore sets this on
|
|
6
|
+
// every put — useful even without a CDN (browser/proxy caching), and exactly what
|
|
7
|
+
// a CDN in front of the bucket needs (cache-mode USE_ORIGIN_HEADERS).
|
|
8
|
+
export function cacheControlFor(key) {
|
|
9
|
+
if (SEALED.test(key) && !HOT.test(key))
|
|
10
|
+
return "public, max-age=31536000, immutable";
|
|
11
|
+
return "public, max-age=30";
|
|
12
|
+
}
|
|
13
|
+
export function contentTypeFor(key) {
|
|
14
|
+
if (key.endsWith(".gz"))
|
|
15
|
+
return "application/gzip";
|
|
16
|
+
if (key.endsWith(".json"))
|
|
17
|
+
return "application/json";
|
|
18
|
+
return "application/octet-stream";
|
|
19
|
+
}
|
|
20
|
+
// Lazily import the real SDK so neither the consumer nor a disk/http run pays for
|
|
21
|
+
// it. The specifier is held in a variable so the type checker does not resolve
|
|
22
|
+
// the (optional) module at compile time.
|
|
23
|
+
const defaultBucketProvider = async (bucket) => {
|
|
24
|
+
const spec = "@google-cloud/storage";
|
|
25
|
+
let mod;
|
|
26
|
+
try {
|
|
27
|
+
mod = (await import(spec));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new Error(`GcsStore needs the "@google-cloud/storage" package — run: npm install @google-cloud/storage`);
|
|
31
|
+
}
|
|
32
|
+
return new mod.Storage().bucket(bucket);
|
|
33
|
+
};
|
|
34
|
+
// Store backed by a Google Cloud Storage bucket (producer write-side; consumers
|
|
35
|
+
// read the same objects over plain HTTP via HttpStore). GCS object writes are
|
|
36
|
+
// atomic and strongly consistent, so no temp-file+rename dance is needed.
|
|
37
|
+
export class GcsStore {
|
|
38
|
+
bucketName;
|
|
39
|
+
prefix;
|
|
40
|
+
provider;
|
|
41
|
+
bucketHandle;
|
|
42
|
+
constructor(bucketName, opts = {}) {
|
|
43
|
+
this.bucketName = bucketName;
|
|
44
|
+
this.prefix = opts.prefix ? opts.prefix.replace(/\/+$/, "") + "/" : "";
|
|
45
|
+
this.provider = opts.bucketProvider ?? defaultBucketProvider;
|
|
46
|
+
}
|
|
47
|
+
bucket() {
|
|
48
|
+
return (this.bucketHandle ??= this.provider(this.bucketName));
|
|
49
|
+
}
|
|
50
|
+
obj(key) {
|
|
51
|
+
return this.prefix + key;
|
|
52
|
+
}
|
|
53
|
+
async put(key, data) {
|
|
54
|
+
const file = (await this.bucket()).file(this.obj(key));
|
|
55
|
+
await file.save(data, {
|
|
56
|
+
contentType: contentTypeFor(key),
|
|
57
|
+
metadata: { cacheControl: cacheControlFor(key) },
|
|
58
|
+
resumable: false,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async get(key) {
|
|
62
|
+
const file = (await this.bucket()).file(this.obj(key));
|
|
63
|
+
try {
|
|
64
|
+
const [buf] = await file.download();
|
|
65
|
+
return buf;
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (isNotFound(err))
|
|
69
|
+
return null;
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async delete(key) {
|
|
74
|
+
const file = (await this.bucket()).file(this.obj(key));
|
|
75
|
+
await file.delete({ ignoreNotFound: true });
|
|
76
|
+
}
|
|
77
|
+
async list(prefix) {
|
|
78
|
+
const [files] = await (await this.bucket()).getFiles({ prefix: this.obj(prefix) });
|
|
79
|
+
return files.map((f) => f.name.slice(this.prefix.length));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function isNotFound(err) {
|
|
83
|
+
return typeof err === "object" && err !== null && err.code === 404;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=gcs-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcs-store.js","sourceRoot":"","sources":["../../src/storage/gcs-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Store } from "@saga-sync/core";
|
|
2
|
+
export type { Store } from "@saga-sync/core";
|
|
3
|
+
export { DiskStore } from "@saga-sync/core/node";
|
|
4
|
+
export { DryRunStore } from "./dry-run-store.js";
|
|
5
|
+
export { HttpStore } from "@saga-sync/core";
|
|
6
|
+
export { GcsStore } from "./gcs-store.js";
|
|
7
|
+
export type StoreConfig = {
|
|
8
|
+
protocol: "disk" | "s3" | "http" | "ftp" | "gcs";
|
|
9
|
+
baseDir?: string;
|
|
10
|
+
settings?: unknown;
|
|
11
|
+
dryRun?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function createStore(cfg: StoreConfig): Store;
|
|
14
|
+
export declare function parseStoreTarget(target: string): Omit<StoreConfig, "dryRun">;
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAM7C,YAAY,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAU1C,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,GAAG,KAAK,CAuBnD;AAMD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAU5E"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { DiskStore } from "@saga-sync/core/node";
|
|
3
|
+
import { DryRunStore } from "./dry-run-store.js";
|
|
4
|
+
import { HttpStore } from "@saga-sync/core";
|
|
5
|
+
import { GcsStore } from "./gcs-store.js";
|
|
6
|
+
export { DiskStore } from "@saga-sync/core/node";
|
|
7
|
+
export { DryRunStore } from "./dry-run-store.js";
|
|
8
|
+
export { HttpStore } from "@saga-sync/core";
|
|
9
|
+
export { GcsStore } from "./gcs-store.js";
|
|
10
|
+
export function createStore(cfg) {
|
|
11
|
+
let store;
|
|
12
|
+
switch (cfg.protocol) {
|
|
13
|
+
case "disk":
|
|
14
|
+
store = new DiskStore(cfg.baseDir ?? ".");
|
|
15
|
+
break;
|
|
16
|
+
case "http": {
|
|
17
|
+
const settings = cfg.settings;
|
|
18
|
+
const baseUrl = settings?.baseUrl ?? cfg.baseDir;
|
|
19
|
+
if (!baseUrl)
|
|
20
|
+
throw new Error(`http store requires settings.baseUrl or baseDir`);
|
|
21
|
+
store = new HttpStore(baseUrl);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
case "gcs": {
|
|
25
|
+
const settings = cfg.settings;
|
|
26
|
+
if (!settings?.bucket)
|
|
27
|
+
throw new Error(`gcs store requires settings.bucket`);
|
|
28
|
+
store = new GcsStore(settings.bucket, { prefix: settings.prefix });
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(`store protocol "${cfg.protocol}" not implemented yet`);
|
|
33
|
+
}
|
|
34
|
+
return cfg.dryRun ? new DryRunStore(store) : store;
|
|
35
|
+
}
|
|
36
|
+
// Resolve a producer `--output-dir` argument to a StoreConfig (minus dryRun).
|
|
37
|
+
// A `gs://bucket[/prefix]` target selects the GCS store; anything else is a local
|
|
38
|
+
// filesystem directory. The single place the gs:// convention is parsed, shared
|
|
39
|
+
// by the orchestrator and chunk-builder CLIs.
|
|
40
|
+
export function parseStoreTarget(target) {
|
|
41
|
+
if (target.startsWith("gs://")) {
|
|
42
|
+
const rest = target.slice("gs://".length);
|
|
43
|
+
const slash = rest.indexOf("/");
|
|
44
|
+
const bucket = slash === -1 ? rest : rest.slice(0, slash);
|
|
45
|
+
const prefix = slash === -1 ? undefined : rest.slice(slash + 1) || undefined;
|
|
46
|
+
if (!bucket)
|
|
47
|
+
throw new Error(`invalid gs:// target: ${target}`);
|
|
48
|
+
return { protocol: "gcs", settings: { bucket, prefix } };
|
|
49
|
+
}
|
|
50
|
+
return { protocol: "disk", baseDir: resolve(target) };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,24 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saga-sync/producer",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"packageManager": {
|
|
11
|
-
"name": "pnpm",
|
|
12
|
-
"version": "^11.3.0",
|
|
13
|
-
"onFail": "download"
|
|
14
|
-
}
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Producer side of saga-sync: scraper, chunk-builder, orchestrator, and cloud stores",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/fatlabsxyz/saga-sync.git",
|
|
9
|
+
"directory": "packages/producer"
|
|
15
10
|
},
|
|
16
|
-
"
|
|
11
|
+
"homepage": "https://github.com/fatlabsxyz/saga-sync/tree/master/packages/producer#readme",
|
|
12
|
+
"bugs": "https://github.com/fatlabsxyz/saga-sync/issues",
|
|
17
13
|
"publishConfig": {
|
|
18
|
-
"access": "public"
|
|
19
|
-
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./chunk-builder/archive": {
|
|
23
|
+
"types": "./dist/chunk-builder/archive.d.ts",
|
|
24
|
+
"default": "./dist/chunk-builder/archive.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"bin": {
|
|
28
|
+
"scraper": "dist/scraper/cli.js",
|
|
29
|
+
"chunk-builder": "dist/chunk-builder/cli.js",
|
|
30
|
+
"orchestrator": "dist/orchestrator/cli.js"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"viem": "^2.21.0",
|
|
37
|
+
"zod": "^3.23.8",
|
|
38
|
+
"@saga-sync/core": "0.1.2"
|
|
39
|
+
},
|
|
40
|
+
"optionalDependencies": {
|
|
41
|
+
"@google-cloud/storage": "^7.19.0"
|
|
20
42
|
},
|
|
21
43
|
"scripts": {
|
|
22
|
-
"
|
|
44
|
+
"build": "tsc -b"
|
|
23
45
|
}
|
|
24
46
|
}
|