@johannc-dev/zyrk 0.0.0-stage → 0.2.1
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 +79 -2
- package/dist/build.d.ts +48 -0
- package/dist/build.js +338 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +295 -0
- package/dist/client.d.ts +212 -0
- package/dist/client.js +909 -0
- package/dist/contracts.d.ts +312 -0
- package/dist/contracts.js +154 -0
- package/dist/errors.d.ts +99 -0
- package/dist/errors.js +63 -0
- package/dist/export.d.ts +96 -0
- package/dist/export.js +422 -0
- package/dist/globals.d.ts +59 -0
- package/dist/hourfile.d.ts +59 -0
- package/dist/hourfile.js +345 -0
- package/dist/limits.d.ts +91 -0
- package/dist/limits.js +54 -0
- package/dist/local-data.d.ts +115 -0
- package/dist/local-data.js +373 -0
- package/dist/local-replay.d.ts +40 -0
- package/dist/local-replay.js +343 -0
- package/dist/local-storage.d.ts +76 -0
- package/dist/local-storage.js +499 -0
- package/dist/local.d.ts +235 -0
- package/dist/local.js +1147 -0
- package/dist/sandbox-bootstrap.js +681 -0
- package/dist/sdk.d.ts +1152 -0
- package/dist/sdk.js +210 -0
- package/dist/stream.d.ts +84 -0
- package/dist/stream.js +167 -0
- package/dist/webhook.d.ts +43 -0
- package/dist/webhook.js +54 -0
- package/package.json +56 -4
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { batchOf, decompressHour, encodeHour, parseHour, nodeRealm, COLUMNS, MARKET_COLUMNS, ORDER_COLUMNS, TX_COLUMNS, } from "./hourfile.js";
|
|
5
|
+
import { MAX_WINDOW_MS } from "./limits.js";
|
|
6
|
+
export const HOUR_MS = 3_600_000;
|
|
7
|
+
export const READ_FORMAT = "zyrk-read/1";
|
|
8
|
+
export function spec(input) {
|
|
9
|
+
const withEvents = !!input.withEvents;
|
|
10
|
+
return {
|
|
11
|
+
scope: input.scope,
|
|
12
|
+
key: input.key,
|
|
13
|
+
side: input.side ?? "any",
|
|
14
|
+
withSignatures: !!input.withSignatures,
|
|
15
|
+
withMarket: !!input.withMarket || withEvents,
|
|
16
|
+
withEvents,
|
|
17
|
+
withOrder: !!input.withOrder,
|
|
18
|
+
withTx: !!input.withTx,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const flagsOf = (s) => (s.withSignatures ? "s" : "") +
|
|
22
|
+
(s.withMarket ? "m" : "") +
|
|
23
|
+
(s.withEvents ? "e" : "") +
|
|
24
|
+
(s.withOrder ? "o" : "") +
|
|
25
|
+
(s.withTx ? "t" : "");
|
|
26
|
+
export function readId(s, sinceMs, untilMs) {
|
|
27
|
+
const flags = flagsOf(s);
|
|
28
|
+
return `${s.scope}-${s.key}-${s.side}${flags ? "-" + flags : ""}-${sinceMs}-${untilMs}`;
|
|
29
|
+
}
|
|
30
|
+
const sameSpec = (a, b) => a.scope === b.scope &&
|
|
31
|
+
a.key === b.key &&
|
|
32
|
+
a.side === b.side &&
|
|
33
|
+
a.withSignatures === b.withSignatures &&
|
|
34
|
+
a.withMarket === b.withMarket &&
|
|
35
|
+
a.withEvents === b.withEvents &&
|
|
36
|
+
a.withOrder === b.withOrder &&
|
|
37
|
+
!!a.withTx === !!b.withTx;
|
|
38
|
+
export function exportCommand(s, sinceMs, untilMs, refresh = false) {
|
|
39
|
+
return [
|
|
40
|
+
"zyrk export",
|
|
41
|
+
`--${s.scope} ${s.key}`,
|
|
42
|
+
`--since ${new Date(sinceMs).toISOString()}`,
|
|
43
|
+
`--until ${new Date(untilMs).toISOString()}`,
|
|
44
|
+
s.side !== "any" ? `--side ${s.side}` : "",
|
|
45
|
+
s.withSignatures ? "--with-signatures" : "",
|
|
46
|
+
s.withMarket && !s.withEvents ? "--with-market" : "",
|
|
47
|
+
s.withEvents ? "--with-events" : "",
|
|
48
|
+
s.withOrder ? "--with-order" : "",
|
|
49
|
+
s.withTx ? "--with-tx" : "",
|
|
50
|
+
refresh ? "--refresh" : "",
|
|
51
|
+
]
|
|
52
|
+
.filter(Boolean)
|
|
53
|
+
.join(" ");
|
|
54
|
+
}
|
|
55
|
+
export class ReadFailed extends Error {
|
|
56
|
+
failure;
|
|
57
|
+
constructor(failure) {
|
|
58
|
+
super(failure.message);
|
|
59
|
+
this.failure = failure;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const failed = (code, message, retryAfterMs) => new ReadFailed({
|
|
63
|
+
code,
|
|
64
|
+
message,
|
|
65
|
+
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
66
|
+
});
|
|
67
|
+
const corrupt = (what, error, again) => failed("local_data_corrupt", `${what} is corrupt (${error instanceof Error ? error.message : String(error)}). Export it again: ${again}`);
|
|
68
|
+
export const sha256 = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
69
|
+
const served = (c) => {
|
|
70
|
+
const sealed = c.sealedUntilMs ?? 0;
|
|
71
|
+
return c.liveUntilMs == null ? sealed : Math.max(c.liveUntilMs, sealed);
|
|
72
|
+
};
|
|
73
|
+
const anyGap = (c, a, b) => c.gaps === undefined ||
|
|
74
|
+
c.gaps.some((g) => (g.fromMs == null || g.fromMs < b) && (g.toMs == null || g.toMs > a));
|
|
75
|
+
export function windowComplete(c, since, until) {
|
|
76
|
+
const horizon = c.retentionHorizonMs ?? Number.MAX_SAFE_INTEGER;
|
|
77
|
+
return since >= horizon && until <= served(c) && !anyGap(c, since, until);
|
|
78
|
+
}
|
|
79
|
+
export function hourComplete(c, hour, since, until) {
|
|
80
|
+
const a = Math.max(since, hour * HOUR_MS);
|
|
81
|
+
const b = Math.min(until, (hour + 1) * HOUR_MS);
|
|
82
|
+
if (a >= b)
|
|
83
|
+
return windowComplete(c, since, until);
|
|
84
|
+
return windowComplete(c, a, b);
|
|
85
|
+
}
|
|
86
|
+
function recut(raw, keep) {
|
|
87
|
+
const b = batchOf(raw);
|
|
88
|
+
const h = raw.header;
|
|
89
|
+
const order = [...keep].sort((x, y) => b.line[x] - b.line[y] || b.op[x] - b.op[y]);
|
|
90
|
+
const dict = new Map();
|
|
91
|
+
const addresses = [];
|
|
92
|
+
const map = (index) => {
|
|
93
|
+
if (index < 0)
|
|
94
|
+
return index;
|
|
95
|
+
const a = b.addresses[index];
|
|
96
|
+
let i = dict.get(a);
|
|
97
|
+
if (i === undefined) {
|
|
98
|
+
i = addresses.length;
|
|
99
|
+
addresses.push(a);
|
|
100
|
+
dict.set(a, i);
|
|
101
|
+
}
|
|
102
|
+
return i;
|
|
103
|
+
};
|
|
104
|
+
const n = keep.length;
|
|
105
|
+
const remap = new Map();
|
|
106
|
+
const fields = ["mint", "pool", "trader", "quoteMint"];
|
|
107
|
+
for (const f of fields)
|
|
108
|
+
remap.set(f, new Int32Array(b.length).fill(-1));
|
|
109
|
+
const mcols = ["creatorFeeRecipient", "signer"];
|
|
110
|
+
for (const f of mcols)
|
|
111
|
+
remap.set(f, new Int32Array(b.length).fill(-1));
|
|
112
|
+
for (const i of order) {
|
|
113
|
+
for (const f of fields)
|
|
114
|
+
remap.get(f)[i] = map(b[f][i]);
|
|
115
|
+
if (b.market)
|
|
116
|
+
for (const f of mcols)
|
|
117
|
+
remap.get(f)[i] = map(b.market[f][i]);
|
|
118
|
+
}
|
|
119
|
+
const pick = (source, name) => {
|
|
120
|
+
const Ctor = source.constructor;
|
|
121
|
+
const out = new Ctor(n);
|
|
122
|
+
const re = name ? remap.get(name) : undefined;
|
|
123
|
+
keep.forEach((i, j) => (out[j] = re ? re[i] : source[i]));
|
|
124
|
+
return out;
|
|
125
|
+
};
|
|
126
|
+
const cols = b;
|
|
127
|
+
const next = {
|
|
128
|
+
hour: h.hour,
|
|
129
|
+
source: h.source,
|
|
130
|
+
complete: h.complete,
|
|
131
|
+
skipped: h.skipped,
|
|
132
|
+
length: n,
|
|
133
|
+
addresses,
|
|
134
|
+
};
|
|
135
|
+
for (const [name] of COLUMNS)
|
|
136
|
+
next[name] = pick(cols[name], remap.has(name) ? name : undefined);
|
|
137
|
+
if (b.signatures)
|
|
138
|
+
next.signatures = keep.map((i) => b.signatures[i] ?? null);
|
|
139
|
+
if (b.market) {
|
|
140
|
+
const m = b.market;
|
|
141
|
+
next.market = Object.fromEntries(MARKET_COLUMNS.map(([name]) => [
|
|
142
|
+
name,
|
|
143
|
+
pick(m[name], remap.has(name) ? name : undefined),
|
|
144
|
+
]));
|
|
145
|
+
}
|
|
146
|
+
if (b.order) {
|
|
147
|
+
const o = b.order;
|
|
148
|
+
next.order = Object.fromEntries(ORDER_COLUMNS.map(([name]) => [name, pick(o[name])]));
|
|
149
|
+
}
|
|
150
|
+
if (b.tx) {
|
|
151
|
+
const t = b.tx;
|
|
152
|
+
next.tx = Object.fromEntries(TX_COLUMNS.map(([name]) => [name, pick(t[name])]));
|
|
153
|
+
}
|
|
154
|
+
return parseHour(encodeHour(next));
|
|
155
|
+
}
|
|
156
|
+
const withComplete = (raw, complete) => raw.header.complete === complete
|
|
157
|
+
? raw
|
|
158
|
+
: { header: { ...raw.header, complete }, bytes: raw.bytes };
|
|
159
|
+
export function batchBytes(h) {
|
|
160
|
+
const perRow = 4 +
|
|
161
|
+
2 +
|
|
162
|
+
14 * 8 +
|
|
163
|
+
4 +
|
|
164
|
+
4 * 4 +
|
|
165
|
+
(h.market ? 75 : 0) +
|
|
166
|
+
(h.order ? 15 : 0) +
|
|
167
|
+
(h.tx ? 7 : 0);
|
|
168
|
+
let strings = 0;
|
|
169
|
+
for (const a of h.addresses)
|
|
170
|
+
strings += Buffer.byteLength(a) + 16;
|
|
171
|
+
for (const s of h.signatures ?? [])
|
|
172
|
+
strings += (s?.length ?? 0) + 16;
|
|
173
|
+
return h.length * perRow + strings;
|
|
174
|
+
}
|
|
175
|
+
export class LocalData {
|
|
176
|
+
dir;
|
|
177
|
+
#reads;
|
|
178
|
+
#hours = new Map();
|
|
179
|
+
constructor(dir) {
|
|
180
|
+
this.dir = dir;
|
|
181
|
+
}
|
|
182
|
+
reads() {
|
|
183
|
+
if (this.#reads)
|
|
184
|
+
return this.#reads;
|
|
185
|
+
const root = join(this.dir, "reads");
|
|
186
|
+
const out = [];
|
|
187
|
+
if (existsSync(root))
|
|
188
|
+
for (const id of readdirSync(root).sort()) {
|
|
189
|
+
const file = join(root, id, "read.json");
|
|
190
|
+
if (!existsSync(file))
|
|
191
|
+
continue;
|
|
192
|
+
let meta;
|
|
193
|
+
try {
|
|
194
|
+
meta = JSON.parse(readFileSync(file, "utf8"));
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
throw corrupt(file, e, `zyrk export ... --refresh (or delete ${join(root, id)})`);
|
|
198
|
+
}
|
|
199
|
+
if (meta.format === READ_FORMAT)
|
|
200
|
+
out.push(meta);
|
|
201
|
+
}
|
|
202
|
+
return (this.#reads = out);
|
|
203
|
+
}
|
|
204
|
+
refresh() {
|
|
205
|
+
this.#reads = undefined;
|
|
206
|
+
this.#hours.clear();
|
|
207
|
+
}
|
|
208
|
+
find(s, since, until) {
|
|
209
|
+
return this.reads()
|
|
210
|
+
.filter((r) => sameSpec(r, s) && r.sinceMs <= since && until <= r.untilMs)
|
|
211
|
+
.sort((a, b) => a.untilMs - a.sinceMs - (b.untilMs - b.sinceMs))[0];
|
|
212
|
+
}
|
|
213
|
+
#raw(meta, hour) {
|
|
214
|
+
const id = readId(meta, meta.sinceMs, meta.untilMs);
|
|
215
|
+
const key = `${id}/${hour}`;
|
|
216
|
+
let raw = this.#hours.get(key);
|
|
217
|
+
if (!raw) {
|
|
218
|
+
const file = join(this.dir, "reads", id, `${hour}.zhb`);
|
|
219
|
+
try {
|
|
220
|
+
const bytes = new Uint8Array(readFileSync(file));
|
|
221
|
+
const want = meta.hashes?.[String(hour)];
|
|
222
|
+
if (want !== undefined && sha256(bytes) !== want)
|
|
223
|
+
throw new Error("its SHA-256 is not the one recorded by the export");
|
|
224
|
+
raw = decompressHour(bytes);
|
|
225
|
+
if (raw.header.hour !== hour)
|
|
226
|
+
throw new Error(`it holds hour ${raw.header.hour}`);
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
throw corrupt(file, e, exportCommand(meta, meta.sinceMs, meta.untilMs, true));
|
|
230
|
+
}
|
|
231
|
+
this.#hours.set(key, raw);
|
|
232
|
+
}
|
|
233
|
+
return raw;
|
|
234
|
+
}
|
|
235
|
+
readHour(s, since, until, allowPartial, afterHour, realm = nodeRealm) {
|
|
236
|
+
if (since < 0 || until <= since || until - since > MAX_WINDOW_MS)
|
|
237
|
+
throw failed("invalid_window", `window must be non-empty, start at 0 or later and span at most ${MAX_WINDOW_MS} ms`);
|
|
238
|
+
const meta = this.find(s, since, until);
|
|
239
|
+
if (!meta)
|
|
240
|
+
throw failed("local_data_missing", `No exported data covers this read. Export it with: ${exportCommand(s, since, until)}`);
|
|
241
|
+
if (!meta.done)
|
|
242
|
+
throw failed("local_data_missing", `The export of this read is not finished: run again ${exportCommand(meta, meta.sinceMs, meta.untilMs)}`);
|
|
243
|
+
const cov = meta.coverage ?? {};
|
|
244
|
+
if (!allowPartial && !windowComplete(cov, since, until)) {
|
|
245
|
+
if (cov.preparing)
|
|
246
|
+
throw failed("history_preparing", "History for the requested period is still being prepared. Please retry later, or read with allowPartial.", 30_000);
|
|
247
|
+
throw failed("partial_coverage", `the window is not fully covered; retry with allowPartial (coverage: ${JSON.stringify(cov)})`);
|
|
248
|
+
}
|
|
249
|
+
const exact = since === meta.sinceMs && until === meta.untilMs;
|
|
250
|
+
const sealedUntil = cov.sealedUntilMs ?? 0;
|
|
251
|
+
const stale = (source, h) => source === "live" || (h + 1) * HOUR_MS > sealedUntil;
|
|
252
|
+
const files = new Set(meta.hours);
|
|
253
|
+
const first = Math.max(Math.floor(since / HOUR_MS) - 1, afterHour === null ? -Infinity : afterHour + 1);
|
|
254
|
+
const last = Math.floor((until - 1) / HOUR_MS) + 1;
|
|
255
|
+
const inWindow = (h) => h >= Math.floor(since / HOUR_MS) &&
|
|
256
|
+
h <= Math.floor((until - 1) / HOUR_MS);
|
|
257
|
+
for (let hour = first; hour <= last; hour++) {
|
|
258
|
+
let raw = files.has(hour) ? this.#raw(meta, hour) : undefined;
|
|
259
|
+
let recuts = false;
|
|
260
|
+
if (raw && !exact) {
|
|
261
|
+
const times = raw.header.skippedTimes;
|
|
262
|
+
const b = batchOf(raw);
|
|
263
|
+
const keep = [];
|
|
264
|
+
for (let i = 0; i < b.length; i++) {
|
|
265
|
+
const t = b.timeMs[i];
|
|
266
|
+
if (t >= since && t < until)
|
|
267
|
+
keep.push(i);
|
|
268
|
+
}
|
|
269
|
+
if (keep.length !== b.length) {
|
|
270
|
+
raw = recut(raw, keep);
|
|
271
|
+
recuts = true;
|
|
272
|
+
}
|
|
273
|
+
if (times) {
|
|
274
|
+
const kept = times.filter((t) => t === null || (t >= since && t < until));
|
|
275
|
+
if (kept.length !== raw.header.skipped)
|
|
276
|
+
raw = {
|
|
277
|
+
header: {
|
|
278
|
+
...raw.header,
|
|
279
|
+
skipped: kept.length,
|
|
280
|
+
skippedTimes: kept,
|
|
281
|
+
},
|
|
282
|
+
bytes: raw.bytes,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
raw = withComplete(raw, raw.header.skipped === 0 && hourComplete(cov, hour, since, until));
|
|
286
|
+
}
|
|
287
|
+
if (!raw || (raw.header.length === 0 && raw.header.skipped === 0)) {
|
|
288
|
+
if (inWindow(hour) && !hourComplete(cov, hour, since, until)) {
|
|
289
|
+
const source = raw?.header.source ??
|
|
290
|
+
(hour * HOUR_MS < (cov.sealedUntilMs ?? 0) ? "sealed" : "live");
|
|
291
|
+
const empty = {
|
|
292
|
+
hour,
|
|
293
|
+
source,
|
|
294
|
+
complete: false,
|
|
295
|
+
skipped: 0,
|
|
296
|
+
length: 0,
|
|
297
|
+
addresses: [],
|
|
298
|
+
...(s.withSignatures ? { signatures: [] } : {}),
|
|
299
|
+
};
|
|
300
|
+
for (const [name] of COLUMNS)
|
|
301
|
+
empty[name] = new Uint8Array(0);
|
|
302
|
+
const parsed = parseHour(encodeHour(empty));
|
|
303
|
+
return {
|
|
304
|
+
batch: batchOf(parsed, realm),
|
|
305
|
+
bytes: 0,
|
|
306
|
+
recut: false,
|
|
307
|
+
stale: stale(source, hour),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (raw.header.skipped > 0 && !allowPartial)
|
|
313
|
+
throw failed("partial_coverage", `hour ${hour} holds ${raw.header.skipped} operation(s) of this scope that normalization rejects; retry with allowPartial`);
|
|
314
|
+
return {
|
|
315
|
+
batch: batchOf(raw, realm),
|
|
316
|
+
bytes: batchBytes(raw.header),
|
|
317
|
+
recut: recuts,
|
|
318
|
+
stale: stale(raw.header.source, hour) ||
|
|
319
|
+
(raw.header.market && !raw.header.virtualSides),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
#json(file, again) {
|
|
325
|
+
if (!existsSync(file))
|
|
326
|
+
return undefined;
|
|
327
|
+
try {
|
|
328
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
329
|
+
}
|
|
330
|
+
catch (e) {
|
|
331
|
+
throw corrupt(file, e, again);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
#failure(file, again) {
|
|
335
|
+
const saved = this.#json(join(this.dir, "errors", file), again);
|
|
336
|
+
if (saved === undefined)
|
|
337
|
+
return undefined;
|
|
338
|
+
const { code, message } = saved?.error ?? {};
|
|
339
|
+
if (typeof code !== "string" || typeof message !== "string")
|
|
340
|
+
throw corrupt(join(this.dir, "errors", file), new Error("not an error answer"), again);
|
|
341
|
+
const retry = saved.retryAfterMs;
|
|
342
|
+
return {
|
|
343
|
+
code,
|
|
344
|
+
message,
|
|
345
|
+
...(typeof retry === "number" ? { retryAfterMs: retry } : {}),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
totals(mint, quote) {
|
|
349
|
+
return this.#json(join(this.dir, "totals", totalsName(mint, quote)), `zyrk export --totals ${mint}${quote ? ` --quote ${quote}` : ""}`);
|
|
350
|
+
}
|
|
351
|
+
info(mint) {
|
|
352
|
+
return this.#json(join(this.dir, "info", infoName(mint)), `zyrk export --totals ${mint}`);
|
|
353
|
+
}
|
|
354
|
+
totalsFailure(mint, quote) {
|
|
355
|
+
return this.#failure(join("totals", totalsName(mint, quote)), `zyrk export --totals ${mint}${quote ? ` --quote ${quote}` : ""}`);
|
|
356
|
+
}
|
|
357
|
+
infoFailure(mint) {
|
|
358
|
+
return this.#failure(join("info", infoName(mint)), `zyrk export --totals ${mint}`);
|
|
359
|
+
}
|
|
360
|
+
volumeFailure(params) {
|
|
361
|
+
return this.#failure(join("volume", `${volumeName(params)}.json`), "zyrk export --volume-search '<params>'");
|
|
362
|
+
}
|
|
363
|
+
volumePage(params) {
|
|
364
|
+
const saved = this.#json(join(this.dir, "volume", `${volumeName(params)}.json`), "zyrk export --volume-search '<params>'");
|
|
365
|
+
return saved?.page;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
export const totalsName = (mint, quote) => quote ? `${mint}~${quote}.json` : `${mint}.json`;
|
|
369
|
+
export const infoName = (mint) => `${mint}.json`;
|
|
370
|
+
export function volumeName(params) {
|
|
371
|
+
const canonical = JSON.stringify([...params].sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)));
|
|
372
|
+
return createHash("sha256").update(canonical).digest("hex").slice(0, 32);
|
|
373
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type LocalData, type ReadMeta } from "./local-data.js";
|
|
2
|
+
import { type HourBatch, type Subscription } from "./sdk.js";
|
|
3
|
+
/** The first 30 s after coverage starts are not trusted (as the service). */
|
|
4
|
+
export declare const LAG_MS = 30000;
|
|
5
|
+
/** Events per delivery, as `delivery_batch` of the service. */
|
|
6
|
+
export declare const DELIVERY_BATCH = 32;
|
|
7
|
+
/** Largest `batchSize` of a replay of the service. */
|
|
8
|
+
export declare const REPLAY_MAX_BATCH = 1000;
|
|
9
|
+
/** History items per source page of the service's replays. */
|
|
10
|
+
export declare const SOURCE_PAGE = 500;
|
|
11
|
+
/**
|
|
12
|
+
* Fields of the live operation JSON that exported
|
|
13
|
+
* columns do not hold: absent from replayed operations.
|
|
14
|
+
*/
|
|
15
|
+
export declare const MISSING_OPERATION_FIELDS: readonly ["prices (provider strings: read batch.price… instead)", "usdValue", "finality", "provenance", "market (the provider's raw object)"];
|
|
16
|
+
export interface PendingEvent {
|
|
17
|
+
readonly event: Record<string, unknown>;
|
|
18
|
+
/** Time of the event (a gap: its start). */
|
|
19
|
+
readonly time: number;
|
|
20
|
+
readonly gap: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface ReplayProgress {
|
|
23
|
+
eventsRead: number;
|
|
24
|
+
eventsDelivered: number;
|
|
25
|
+
deliveries: number;
|
|
26
|
+
gaps: number;
|
|
27
|
+
orderFallbacks: number;
|
|
28
|
+
}
|
|
29
|
+
/** Decimal string of a raw amount, as the live JSON writes it. */
|
|
30
|
+
export declare function formatRaw(raw: bigint, decimals: number): string;
|
|
31
|
+
/** Row `i` as the live publication shapes it (without {@link MISSING_OPERATION_FIELDS}). */
|
|
32
|
+
export declare function operationOf(b: HourBatch, i: number): Record<string, unknown>;
|
|
33
|
+
/**
|
|
34
|
+
* Every event a subscription receives over `[since, until)`, from the
|
|
35
|
+
* export `meta` (a read of the trigger's token, or wallet), in delivery
|
|
36
|
+
* order.
|
|
37
|
+
*/
|
|
38
|
+
export declare function replayEvents(data: LocalData, meta: ReadMeta, sub: Subscription, since: number, until: number, progress: ReplayProgress): PendingEvent[];
|
|
39
|
+
/** `interval`: ticks on the simulated clock, no history read, no gap. */
|
|
40
|
+
export declare function intervalEvents(everyMs: number, since: number, until: number): PendingEvent[];
|