@secondlayer/subgraphs 0.5.7 → 0.7.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/src/index.d.ts +17 -5
- package/dist/src/index.js +467 -191
- package/dist/src/index.js.map +16 -15
- package/dist/src/runtime/block-processor.d.ts +9 -1
- package/dist/src/runtime/block-processor.js +216 -144
- package/dist/src/runtime/block-processor.js.map +9 -9
- package/dist/src/runtime/catchup.d.ts +2 -2
- package/dist/src/runtime/catchup.js +366 -160
- package/dist/src/runtime/catchup.js.map +12 -11
- package/dist/src/runtime/clarity.js.map +2 -2
- package/dist/src/runtime/context.d.ts +4 -2
- package/dist/src/runtime/context.js +79 -36
- package/dist/src/runtime/context.js.map +3 -3
- package/dist/src/runtime/processor.js +384 -166
- package/dist/src/runtime/processor.js.map +14 -13
- package/dist/src/runtime/reindex.d.ts +13 -1
- package/dist/src/runtime/reindex.js +459 -189
- package/dist/src/runtime/reindex.js.map +13 -12
- package/dist/src/runtime/reorg.js +229 -148
- package/dist/src/runtime/reorg.js.map +10 -10
- package/dist/src/runtime/runner.d.ts +4 -2
- package/dist/src/runtime/runner.js +7 -3
- package/dist/src/runtime/runner.js.map +4 -4
- package/dist/src/runtime/source-matcher.js.map +3 -3
- package/dist/src/runtime/stats.d.ts +1 -1
- package/dist/src/runtime/stats.js +2 -2
- package/dist/src/runtime/stats.js.map +3 -3
- package/dist/src/schema/index.d.ts +1 -1
- package/dist/src/schema/index.js +9 -3
- package/dist/src/schema/index.js.map +5 -5
- package/dist/src/service.js +385 -167
- package/dist/src/service.js.map +15 -14
- package/dist/src/templates.js.map +2 -2
- package/dist/src/types.js.map +2 -2
- package/dist/src/validate.js +2 -2
- package/dist/src/validate.js.map +3 -3
- package/package.json +51 -51
package/dist/src/index.js
CHANGED
|
@@ -53,15 +53,15 @@ var SubgraphDefinitionSchema = z.object({
|
|
|
53
53
|
description: z.string().optional(),
|
|
54
54
|
sources: z.array(SubgraphSourceSchema).min(1, "Must have at least one source"),
|
|
55
55
|
schema: SubgraphSchemaSchema,
|
|
56
|
-
handlers: z.record(z.string(), z.
|
|
56
|
+
handlers: z.record(z.string(), z.any())
|
|
57
57
|
});
|
|
58
58
|
function validateSubgraphDefinition(def) {
|
|
59
59
|
return SubgraphDefinitionSchema.parse(def);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// src/runtime/context.ts
|
|
63
|
-
import { sql } from "kysely";
|
|
64
63
|
import { logger } from "@secondlayer/shared/logger";
|
|
64
|
+
import { sql } from "kysely";
|
|
65
65
|
function validateColumnName(name) {
|
|
66
66
|
if (!/^[a-z_][a-z0-9_]*$/i.test(name)) {
|
|
67
67
|
throw new Error(`Invalid column name: ${name}`);
|
|
@@ -102,13 +102,26 @@ class SubgraphContext {
|
|
|
102
102
|
const keyColumns = Object.keys(key);
|
|
103
103
|
const hasUniqueConstraint = tableDef.uniqueKeys?.some((uk) => uk.length === keyColumns.length && uk.every((c) => keyColumns.includes(c)));
|
|
104
104
|
if (hasUniqueConstraint) {
|
|
105
|
-
this.ops.push({
|
|
105
|
+
this.ops.push({
|
|
106
|
+
kind: "insert",
|
|
107
|
+
table,
|
|
108
|
+
data: { ...key, ...row, _upsert_keys: keyColumns }
|
|
109
|
+
});
|
|
106
110
|
} else {
|
|
107
111
|
logger.warn("upsert called without matching uniqueKeys constraint, using fallback", {
|
|
108
112
|
table,
|
|
109
113
|
keys: keyColumns
|
|
110
114
|
});
|
|
111
|
-
this.ops.push({
|
|
115
|
+
this.ops.push({
|
|
116
|
+
kind: "insert",
|
|
117
|
+
table,
|
|
118
|
+
data: {
|
|
119
|
+
...key,
|
|
120
|
+
...row,
|
|
121
|
+
_upsert_fallback_keys: keyColumns,
|
|
122
|
+
_upsert_fallback_set: row
|
|
123
|
+
}
|
|
124
|
+
});
|
|
112
125
|
}
|
|
113
126
|
}
|
|
114
127
|
delete(table, where) {
|
|
@@ -166,53 +179,83 @@ class SubgraphContext {
|
|
|
166
179
|
}
|
|
167
180
|
return opsToFlush.length;
|
|
168
181
|
}
|
|
182
|
+
prepareInsert(op) {
|
|
183
|
+
const upsertKeys = op.data._upsert_keys;
|
|
184
|
+
const data = { ...op.data };
|
|
185
|
+
delete data._upsert_keys;
|
|
186
|
+
delete data._upsert_fallback_keys;
|
|
187
|
+
delete data._upsert_fallback_set;
|
|
188
|
+
data._block_height = this.block.height;
|
|
189
|
+
data._tx_id = this._tx.txId;
|
|
190
|
+
data._created_at = "NOW()";
|
|
191
|
+
const cols = Object.keys(data);
|
|
192
|
+
cols.forEach(validateColumnName);
|
|
193
|
+
const vals = cols.map((c) => data[c] === "NOW()" ? "NOW()" : escapeLiteral(data[c]));
|
|
194
|
+
const batchKey = `${op.table}:${[...cols].sort().join(",")}:${upsertKeys ? [...upsertKeys].sort().join(",") : ""}`;
|
|
195
|
+
return { data, cols, vals, upsertKeys, batchKey };
|
|
196
|
+
}
|
|
169
197
|
buildStatements(ops) {
|
|
170
198
|
const statements = [];
|
|
199
|
+
let currentBatch = null;
|
|
200
|
+
let currentBatchKey = "";
|
|
201
|
+
const flushInsertBatch = () => {
|
|
202
|
+
if (!currentBatch)
|
|
203
|
+
return;
|
|
204
|
+
const qualifiedTable = `"${this.pgSchemaName}"."${currentBatch.table}"`;
|
|
205
|
+
const colList = currentBatch.cols.map((c) => `"${c}"`).join(", ");
|
|
206
|
+
let rows = currentBatch.rows;
|
|
207
|
+
if (currentBatch.upsertKeys && currentBatch.upsertKeys.length > 0) {
|
|
208
|
+
const keyIndices = currentBatch.upsertKeys.map((k) => currentBatch.cols.indexOf(k));
|
|
209
|
+
const seen = new Map;
|
|
210
|
+
for (let i = 0;i < rows.length; i++) {
|
|
211
|
+
const key = keyIndices.map((ki) => rows[i][ki]).join("\x00");
|
|
212
|
+
seen.set(key, i);
|
|
213
|
+
}
|
|
214
|
+
if (seen.size < rows.length) {
|
|
215
|
+
rows = Array.from(seen.values()).map((i) => rows[i]);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const valuesList = rows.map((r) => `(${r.join(", ")})`).join(", ");
|
|
219
|
+
let stmt = `INSERT INTO ${qualifiedTable} (${colList}) VALUES ${valuesList}`;
|
|
220
|
+
if (currentBatch.upsertKeys && currentBatch.upsertKeys.length > 0) {
|
|
221
|
+
const updateCols = currentBatch.cols.filter((c) => !currentBatch.upsertKeys.includes(c) && !c.startsWith("_"));
|
|
222
|
+
if (updateCols.length > 0) {
|
|
223
|
+
const setClauses = updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`);
|
|
224
|
+
stmt += ` ON CONFLICT (${currentBatch.upsertKeys.map((k) => `"${k}"`).join(", ")}) DO UPDATE SET ${setClauses.join(", ")}`;
|
|
225
|
+
} else {
|
|
226
|
+
stmt += ` ON CONFLICT (${currentBatch.upsertKeys.map((k) => `"${k}"`).join(", ")}) DO NOTHING`;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
statements.push(stmt);
|
|
230
|
+
currentBatch = null;
|
|
231
|
+
currentBatchKey = "";
|
|
232
|
+
};
|
|
171
233
|
for (const op of ops) {
|
|
172
234
|
const qualifiedTable = `"${this.pgSchemaName}"."${op.table}"`;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
delete data._upsert_fallback_set;
|
|
182
|
-
data._block_height = this.block.height;
|
|
183
|
-
data._tx_id = this._tx.txId;
|
|
184
|
-
data._created_at = "NOW()";
|
|
185
|
-
const cols = Object.keys(data);
|
|
186
|
-
cols.forEach(validateColumnName);
|
|
187
|
-
const vals = cols.map((c) => data[c] === "NOW()" ? "NOW()" : escapeLiteral(data[c]));
|
|
188
|
-
let stmt = `INSERT INTO ${qualifiedTable} (${cols.map((c) => `"${c}"`).join(", ")}) VALUES (${vals.join(", ")})`;
|
|
189
|
-
if (upsertKeys && upsertKeys.length > 0) {
|
|
190
|
-
const updateCols = cols.filter((c) => !upsertKeys.includes(c) && !c.startsWith("_"));
|
|
191
|
-
if (updateCols.length > 0) {
|
|
192
|
-
const setClauses = updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`);
|
|
193
|
-
stmt += ` ON CONFLICT (${upsertKeys.map((k) => `"${k}"`).join(", ")}) DO UPDATE SET ${setClauses.join(", ")}`;
|
|
194
|
-
} else {
|
|
195
|
-
stmt += ` ON CONFLICT (${upsertKeys.map((k) => `"${k}"`).join(", ")}) DO NOTHING`;
|
|
196
|
-
}
|
|
197
|
-
} else if (fallbackKeys && fallbackSet) {}
|
|
198
|
-
statements.push(stmt);
|
|
199
|
-
break;
|
|
235
|
+
if (op.kind === "insert") {
|
|
236
|
+
const { cols, vals, upsertKeys, batchKey } = this.prepareInsert(op);
|
|
237
|
+
if (batchKey === currentBatchKey && currentBatch) {
|
|
238
|
+
currentBatch.rows.push(vals);
|
|
239
|
+
} else {
|
|
240
|
+
flushInsertBatch();
|
|
241
|
+
currentBatch = { table: op.table, cols, rows: [vals], upsertKeys };
|
|
242
|
+
currentBatchKey = batchKey;
|
|
200
243
|
}
|
|
201
|
-
|
|
244
|
+
} else {
|
|
245
|
+
flushInsertBatch();
|
|
246
|
+
if (op.kind === "update") {
|
|
202
247
|
const setEntries = Object.entries(op.set);
|
|
203
248
|
setEntries.forEach(([k]) => validateColumnName(k));
|
|
204
249
|
const setClauses = setEntries.map(([k, v]) => `"${k}" = ${escapeLiteral(v)}`);
|
|
205
250
|
const { clause } = buildWhereClause(op.data);
|
|
206
251
|
statements.push(`UPDATE ${qualifiedTable} SET ${setClauses.join(", ")} WHERE ${clause}`);
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
case "delete": {
|
|
252
|
+
} else if (op.kind === "delete") {
|
|
210
253
|
const { clause } = buildWhereClause(op.data);
|
|
211
254
|
statements.push(`DELETE FROM ${qualifiedTable} WHERE ${clause}`);
|
|
212
|
-
break;
|
|
213
255
|
}
|
|
214
256
|
}
|
|
215
257
|
}
|
|
258
|
+
flushInsertBatch();
|
|
216
259
|
return statements;
|
|
217
260
|
}
|
|
218
261
|
validateTable(table) {
|
|
@@ -241,95 +284,6 @@ function buildWhereClause(where) {
|
|
|
241
284
|
return { clause: parts.join(" AND "), values: [] };
|
|
242
285
|
}
|
|
243
286
|
|
|
244
|
-
// src/runtime/source-matcher.ts
|
|
245
|
-
function matchPattern(value, pattern) {
|
|
246
|
-
if (!pattern.includes("*"))
|
|
247
|
-
return value === pattern;
|
|
248
|
-
const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
249
|
-
return new RegExp(`^${regex}$`).test(value);
|
|
250
|
-
}
|
|
251
|
-
function matchSource(source, transactions, eventsByTx) {
|
|
252
|
-
const results = [];
|
|
253
|
-
const key = sourceKey(source);
|
|
254
|
-
for (const tx of transactions) {
|
|
255
|
-
if (source.type) {
|
|
256
|
-
if (!matchPattern(tx.type, source.type))
|
|
257
|
-
continue;
|
|
258
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
259
|
-
let matchedEvents = txEvents;
|
|
260
|
-
if (source.minAmount !== undefined) {
|
|
261
|
-
const amountEvents = matchedEvents.filter((e) => {
|
|
262
|
-
const data = e.data;
|
|
263
|
-
const rawAmount = data?.amount;
|
|
264
|
-
if (rawAmount === undefined)
|
|
265
|
-
return false;
|
|
266
|
-
const amount = BigInt(rawAmount);
|
|
267
|
-
return amount >= source.minAmount;
|
|
268
|
-
});
|
|
269
|
-
if (amountEvents.length === 0)
|
|
270
|
-
continue;
|
|
271
|
-
matchedEvents = amountEvents;
|
|
272
|
-
}
|
|
273
|
-
results.push({ tx, events: matchedEvents, sourceKey: key });
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
if (source.contract) {
|
|
277
|
-
const txContractMatch = tx.contract_id && matchPattern(tx.contract_id, source.contract);
|
|
278
|
-
if (source.function && tx.function_name) {
|
|
279
|
-
if (!matchPattern(tx.function_name, source.function))
|
|
280
|
-
continue;
|
|
281
|
-
} else if (source.function && !tx.function_name) {
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
285
|
-
let matchedEvents = txEvents;
|
|
286
|
-
if (!txContractMatch) {
|
|
287
|
-
matchedEvents = txEvents.filter((e) => {
|
|
288
|
-
const data = e.data;
|
|
289
|
-
const contractIdentifier = data?.contract_identifier;
|
|
290
|
-
return contractIdentifier && matchPattern(contractIdentifier, source.contract);
|
|
291
|
-
});
|
|
292
|
-
if (matchedEvents.length === 0)
|
|
293
|
-
continue;
|
|
294
|
-
}
|
|
295
|
-
if (source.event) {
|
|
296
|
-
matchedEvents = matchedEvents.filter((e) => {
|
|
297
|
-
if (matchPattern(e.type, source.event))
|
|
298
|
-
return true;
|
|
299
|
-
const data = e.data;
|
|
300
|
-
const topic = data?.topic;
|
|
301
|
-
return topic ? matchPattern(topic, source.event) : false;
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
if (txContractMatch || matchedEvents.length > 0) {
|
|
305
|
-
results.push({ tx, events: matchedEvents, sourceKey: key });
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
return results;
|
|
310
|
-
}
|
|
311
|
-
function matchSources(sources, transactions, events) {
|
|
312
|
-
const eventsByTx = new Map;
|
|
313
|
-
for (const event of events) {
|
|
314
|
-
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
315
|
-
list.push(event);
|
|
316
|
-
eventsByTx.set(event.tx_id, list);
|
|
317
|
-
}
|
|
318
|
-
const seen = new Set;
|
|
319
|
-
const results = [];
|
|
320
|
-
for (const source of sources) {
|
|
321
|
-
const matches = matchSource(source, transactions, eventsByTx);
|
|
322
|
-
for (const match of matches) {
|
|
323
|
-
const dedupeKey = `${match.tx.tx_id}:${match.sourceKey}`;
|
|
324
|
-
if (!seen.has(dedupeKey)) {
|
|
325
|
-
seen.add(dedupeKey);
|
|
326
|
-
results.push(match);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return results;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
287
|
// src/runtime/clarity.ts
|
|
334
288
|
import { cvToJSON, deserializeCV } from "@secondlayer/stacks/clarity";
|
|
335
289
|
function decodeClarityValue(hex) {
|
|
@@ -362,8 +316,8 @@ function decodeFunctionArgs(args) {
|
|
|
362
316
|
}
|
|
363
317
|
|
|
364
318
|
// src/runtime/runner.ts
|
|
365
|
-
import { logger as logger2 } from "@secondlayer/shared/logger";
|
|
366
319
|
import { getErrorMessage } from "@secondlayer/shared";
|
|
320
|
+
import { logger as logger2 } from "@secondlayer/shared/logger";
|
|
367
321
|
var DEFAULT_ERROR_THRESHOLD = 50;
|
|
368
322
|
function resolveHandler(handlers, key) {
|
|
369
323
|
return handlers[key] ?? handlers["*"] ?? null;
|
|
@@ -375,7 +329,11 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
375
329
|
for (const { tx, events, sourceKey: sourceKey2 } of matched) {
|
|
376
330
|
const handler = resolveHandler(subgraph.handlers, sourceKey2);
|
|
377
331
|
if (!handler) {
|
|
378
|
-
logger2.warn("No handler found for source key", {
|
|
332
|
+
logger2.warn("No handler found for source key", {
|
|
333
|
+
subgraph: subgraph.name,
|
|
334
|
+
sourceKey: sourceKey2,
|
|
335
|
+
txId: tx.tx_id
|
|
336
|
+
});
|
|
379
337
|
continue;
|
|
380
338
|
}
|
|
381
339
|
ctx.setTx({
|
|
@@ -452,16 +410,109 @@ async function runHandlers(subgraph, matched, ctx, opts) {
|
|
|
452
410
|
return { processed, errors };
|
|
453
411
|
}
|
|
454
412
|
|
|
413
|
+
// src/runtime/source-matcher.ts
|
|
414
|
+
function matchPattern(value, pattern) {
|
|
415
|
+
if (!pattern.includes("*"))
|
|
416
|
+
return value === pattern;
|
|
417
|
+
const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
418
|
+
return new RegExp(`^${regex}$`).test(value);
|
|
419
|
+
}
|
|
420
|
+
function matchSource(source, transactions, eventsByTx) {
|
|
421
|
+
const results = [];
|
|
422
|
+
const key = sourceKey(source);
|
|
423
|
+
for (const tx of transactions) {
|
|
424
|
+
if (source.type) {
|
|
425
|
+
if (!matchPattern(tx.type, source.type))
|
|
426
|
+
continue;
|
|
427
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
428
|
+
let matchedEvents = txEvents;
|
|
429
|
+
if (source.minAmount !== undefined) {
|
|
430
|
+
const amountEvents = matchedEvents.filter((e) => {
|
|
431
|
+
const data = e.data;
|
|
432
|
+
const rawAmount = data?.amount;
|
|
433
|
+
if (rawAmount === undefined)
|
|
434
|
+
return false;
|
|
435
|
+
const amount = BigInt(rawAmount);
|
|
436
|
+
return amount >= source.minAmount;
|
|
437
|
+
});
|
|
438
|
+
if (amountEvents.length === 0)
|
|
439
|
+
continue;
|
|
440
|
+
matchedEvents = amountEvents;
|
|
441
|
+
}
|
|
442
|
+
results.push({ tx, events: matchedEvents, sourceKey: key });
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
if (source.contract) {
|
|
446
|
+
const txContractMatch = tx.contract_id && matchPattern(tx.contract_id, source.contract);
|
|
447
|
+
if (source.function && tx.function_name) {
|
|
448
|
+
if (!matchPattern(tx.function_name, source.function))
|
|
449
|
+
continue;
|
|
450
|
+
} else if (source.function && !tx.function_name) {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
454
|
+
let matchedEvents = txEvents;
|
|
455
|
+
if (!txContractMatch) {
|
|
456
|
+
matchedEvents = txEvents.filter((e) => {
|
|
457
|
+
const data = e.data;
|
|
458
|
+
const contractIdentifier = data?.contract_identifier;
|
|
459
|
+
return contractIdentifier && matchPattern(contractIdentifier, source.contract);
|
|
460
|
+
});
|
|
461
|
+
if (matchedEvents.length === 0)
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
if (source.event) {
|
|
465
|
+
matchedEvents = matchedEvents.filter((e) => {
|
|
466
|
+
if (matchPattern(e.type, source.event))
|
|
467
|
+
return true;
|
|
468
|
+
const data = e.data;
|
|
469
|
+
const topic = data?.topic;
|
|
470
|
+
return topic ? matchPattern(topic, source.event) : false;
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
if (txContractMatch || matchedEvents.length > 0) {
|
|
474
|
+
results.push({ tx, events: matchedEvents, sourceKey: key });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return results;
|
|
479
|
+
}
|
|
480
|
+
function matchSources(sources, transactions, events) {
|
|
481
|
+
const eventsByTx = new Map;
|
|
482
|
+
for (const event of events) {
|
|
483
|
+
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
484
|
+
list.push(event);
|
|
485
|
+
eventsByTx.set(event.tx_id, list);
|
|
486
|
+
}
|
|
487
|
+
const seen = new Set;
|
|
488
|
+
const results = [];
|
|
489
|
+
for (const source of sources) {
|
|
490
|
+
const matches = matchSource(source, transactions, eventsByTx);
|
|
491
|
+
for (const match of matches) {
|
|
492
|
+
const dedupeKey = `${match.tx.tx_id}:${match.sourceKey}`;
|
|
493
|
+
if (!seen.has(dedupeKey)) {
|
|
494
|
+
seen.add(dedupeKey);
|
|
495
|
+
results.push(match);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return results;
|
|
500
|
+
}
|
|
501
|
+
|
|
455
502
|
// src/runtime/block-processor.ts
|
|
456
|
-
import { sql as sql2 } from "kysely";
|
|
457
503
|
import { getDb } from "@secondlayer/shared/db";
|
|
504
|
+
import {
|
|
505
|
+
recordSubgraphProcessed,
|
|
506
|
+
updateSubgraphStatus
|
|
507
|
+
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
458
508
|
import { logger as logger3 } from "@secondlayer/shared/logger";
|
|
459
|
-
import {
|
|
509
|
+
import { sql as sql2 } from "kysely";
|
|
460
510
|
|
|
461
511
|
// src/schema/utils.ts
|
|
462
512
|
import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
|
|
463
513
|
|
|
464
514
|
// src/runtime/block-processor.ts
|
|
515
|
+
var schemaNameCache = new Map;
|
|
465
516
|
async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
466
517
|
const db = getDb();
|
|
467
518
|
const blockStart = performance.now();
|
|
@@ -472,19 +523,32 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
472
523
|
errors: 0,
|
|
473
524
|
skipped: false
|
|
474
525
|
};
|
|
475
|
-
|
|
476
|
-
if (
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
526
|
+
let block, txs, evts;
|
|
527
|
+
if (opts?.preloaded) {
|
|
528
|
+
block = opts.preloaded.block;
|
|
529
|
+
txs = opts.preloaded.txs;
|
|
530
|
+
evts = opts.preloaded.events;
|
|
531
|
+
} else {
|
|
532
|
+
block = await db.selectFrom("blocks").selectAll().where("height", "=", blockHeight).executeTakeFirst();
|
|
533
|
+
if (!block) {
|
|
534
|
+
logger3.warn("Block not found for subgraph processing", {
|
|
535
|
+
subgraph: subgraphName,
|
|
536
|
+
blockHeight
|
|
537
|
+
});
|
|
538
|
+
result.skipped = true;
|
|
539
|
+
return result;
|
|
540
|
+
}
|
|
541
|
+
if (!block.canonical) {
|
|
542
|
+
logger3.debug("Skipping non-canonical block", {
|
|
543
|
+
subgraph: subgraphName,
|
|
544
|
+
blockHeight
|
|
545
|
+
});
|
|
546
|
+
result.skipped = true;
|
|
547
|
+
return result;
|
|
548
|
+
}
|
|
549
|
+
txs = await db.selectFrom("transactions").selectAll().where("block_height", "=", blockHeight).execute();
|
|
550
|
+
evts = await db.selectFrom("events").selectAll().where("block_height", "=", blockHeight).execute();
|
|
485
551
|
}
|
|
486
|
-
const txs = await db.selectFrom("transactions").selectAll().where("block_height", "=", blockHeight).execute();
|
|
487
|
-
const evts = await db.selectFrom("events").selectAll().where("block_height", "=", blockHeight).execute();
|
|
488
552
|
const matched = matchSources(subgraph.sources, txs, evts);
|
|
489
553
|
result.matched = matched.length;
|
|
490
554
|
if (matched.length === 0) {
|
|
@@ -493,8 +557,12 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
493
557
|
}
|
|
494
558
|
return result;
|
|
495
559
|
}
|
|
496
|
-
|
|
497
|
-
|
|
560
|
+
let schemaName = schemaNameCache.get(subgraphName);
|
|
561
|
+
if (!schemaName) {
|
|
562
|
+
const subgraphRecord = await db.selectFrom("subgraphs").select("schema_name").where("name", "=", subgraphName).executeTakeFirst();
|
|
563
|
+
schemaName = subgraphRecord?.schema_name ?? pgSchemaName(subgraphName);
|
|
564
|
+
schemaNameCache.set(subgraphName, schemaName);
|
|
565
|
+
}
|
|
498
566
|
const blockMeta = {
|
|
499
567
|
height: block.height,
|
|
500
568
|
hash: block.hash,
|
|
@@ -543,7 +611,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
543
611
|
const { rows } = await sql2.raw(`SELECT COUNT(*) as count FROM "${schemaName}"."${table}"`).execute(db);
|
|
544
612
|
const count = Number(rows[0].count);
|
|
545
613
|
if (count >= 1e7) {
|
|
546
|
-
logger3.warn("Subgraph table exceeds 10M rows", {
|
|
614
|
+
logger3.warn("Subgraph table exceeds 10M rows", {
|
|
615
|
+
subgraph: subgraphName,
|
|
616
|
+
table,
|
|
617
|
+
count
|
|
618
|
+
});
|
|
547
619
|
}
|
|
548
620
|
}
|
|
549
621
|
} catch {}
|
|
@@ -597,7 +669,7 @@ class StatsAccumulator {
|
|
|
597
669
|
flush_time_ms: Math.round(entry.flushTimeMs),
|
|
598
670
|
max_block_time_ms: Math.round(entry.maxBlockTimeMs),
|
|
599
671
|
max_handler_time_ms: Math.round(entry.maxHandlerTimeMs),
|
|
600
|
-
avg_ops_per_block: parseFloat(avgOpsPerBlock.toFixed(2)),
|
|
672
|
+
avg_ops_per_block: Number.parseFloat(avgOpsPerBlock.toFixed(2)),
|
|
601
673
|
is_catchup: entry.isCatchup
|
|
602
674
|
}).execute();
|
|
603
675
|
}
|
|
@@ -619,10 +691,14 @@ class StatsAccumulator {
|
|
|
619
691
|
}
|
|
620
692
|
|
|
621
693
|
// src/runtime/reindex.ts
|
|
694
|
+
import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
|
|
622
695
|
import { getDb as getDb2, getRawClient } from "@secondlayer/shared/db";
|
|
696
|
+
import {
|
|
697
|
+
recordGapBatch,
|
|
698
|
+
resolveGaps
|
|
699
|
+
} from "@secondlayer/shared/db/queries/subgraph-gaps";
|
|
623
700
|
import { updateSubgraphStatus as updateSubgraphStatus2 } from "@secondlayer/shared/db/queries/subgraphs";
|
|
624
701
|
import { logger as logger4 } from "@secondlayer/shared/logger";
|
|
625
|
-
import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
|
|
626
702
|
|
|
627
703
|
// src/schema/generator.ts
|
|
628
704
|
var TYPE_MAP = {
|
|
@@ -709,45 +785,121 @@ function generateSubgraphSQL(def, schemaNameOverride) {
|
|
|
709
785
|
return { statements, hash };
|
|
710
786
|
}
|
|
711
787
|
|
|
788
|
+
// src/runtime/batch-loader.ts
|
|
789
|
+
async function loadBlockRange(db, fromHeight, toHeight) {
|
|
790
|
+
const [blocks, txs, events] = await Promise.all([
|
|
791
|
+
db.selectFrom("blocks").selectAll().where("height", ">=", fromHeight).where("height", "<=", toHeight).where("canonical", "=", true).execute(),
|
|
792
|
+
db.selectFrom("transactions").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
|
|
793
|
+
db.selectFrom("events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute()
|
|
794
|
+
]);
|
|
795
|
+
const txsByHeight = new Map;
|
|
796
|
+
for (const tx of txs) {
|
|
797
|
+
const h = Number(tx.block_height);
|
|
798
|
+
const list = txsByHeight.get(h) ?? [];
|
|
799
|
+
list.push(tx);
|
|
800
|
+
txsByHeight.set(h, list);
|
|
801
|
+
}
|
|
802
|
+
const eventsByHeight = new Map;
|
|
803
|
+
for (const evt of events) {
|
|
804
|
+
const h = Number(evt.block_height);
|
|
805
|
+
const list = eventsByHeight.get(h) ?? [];
|
|
806
|
+
list.push(evt);
|
|
807
|
+
eventsByHeight.set(h, list);
|
|
808
|
+
}
|
|
809
|
+
const result = new Map;
|
|
810
|
+
for (const block of blocks) {
|
|
811
|
+
const h = Number(block.height);
|
|
812
|
+
result.set(h, {
|
|
813
|
+
block,
|
|
814
|
+
txs: txsByHeight.get(h) ?? [],
|
|
815
|
+
events: eventsByHeight.get(h) ?? []
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
return result;
|
|
819
|
+
}
|
|
820
|
+
function avgEventsPerBlock(batch) {
|
|
821
|
+
if (batch.size === 0)
|
|
822
|
+
return 0;
|
|
823
|
+
let totalEvents = 0;
|
|
824
|
+
for (const data of batch.values()) {
|
|
825
|
+
totalEvents += data.events.length;
|
|
826
|
+
}
|
|
827
|
+
return totalEvents / batch.size;
|
|
828
|
+
}
|
|
829
|
+
|
|
712
830
|
// src/runtime/reindex.ts
|
|
713
831
|
var LOG_INTERVAL = 1000;
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
let toBlock;
|
|
730
|
-
if (opts?.toBlock != null) {
|
|
731
|
-
toBlock = opts.toBlock;
|
|
832
|
+
var DEFAULT_BATCH_SIZE = 500;
|
|
833
|
+
var MIN_BATCH_SIZE = 100;
|
|
834
|
+
var MAX_BATCH_SIZE = 1000;
|
|
835
|
+
function coalesceFailedBlocks(blocks) {
|
|
836
|
+
if (blocks.length === 0)
|
|
837
|
+
return [];
|
|
838
|
+
blocks.sort((a, b) => a.height - b.height);
|
|
839
|
+
const ranges = [];
|
|
840
|
+
let start = blocks[0].height;
|
|
841
|
+
let end = blocks[0].height;
|
|
842
|
+
let reason = blocks[0].reason;
|
|
843
|
+
for (let i = 1;i < blocks.length; i++) {
|
|
844
|
+
const b = blocks[i];
|
|
845
|
+
if (b.height === end + 1 && b.reason === reason) {
|
|
846
|
+
end = b.height;
|
|
732
847
|
} else {
|
|
733
|
-
|
|
734
|
-
|
|
848
|
+
ranges.push({ start, end, reason });
|
|
849
|
+
start = b.height;
|
|
850
|
+
end = b.height;
|
|
851
|
+
reason = b.reason;
|
|
735
852
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
853
|
+
}
|
|
854
|
+
ranges.push({ start, end, reason });
|
|
855
|
+
return ranges;
|
|
856
|
+
}
|
|
857
|
+
async function processBlockRange(def, opts) {
|
|
858
|
+
const db = getDb2();
|
|
859
|
+
const subgraphName = def.name;
|
|
860
|
+
const { fromBlock, toBlock, status } = opts;
|
|
861
|
+
const totalBlocks = toBlock - fromBlock + 1;
|
|
862
|
+
const stats = new StatsAccumulator(subgraphName, opts.apiKeyId, opts.isCatchup);
|
|
863
|
+
let blocksProcessed = 0;
|
|
864
|
+
let totalEventsProcessed = 0;
|
|
865
|
+
let totalErrors = 0;
|
|
866
|
+
let batchSize = DEFAULT_BATCH_SIZE;
|
|
867
|
+
let currentHeight = fromBlock;
|
|
868
|
+
let nextBatchPromise = loadBlockRange(db, currentHeight, Math.min(currentHeight + batchSize - 1, toBlock));
|
|
869
|
+
while (currentHeight <= toBlock) {
|
|
870
|
+
const batch = await nextBatchPromise;
|
|
871
|
+
const batchEnd = Math.min(currentHeight + batchSize - 1, toBlock);
|
|
872
|
+
const nextStart = batchEnd + 1;
|
|
873
|
+
if (nextStart <= toBlock) {
|
|
874
|
+
const nextEnd = Math.min(nextStart + batchSize - 1, toBlock);
|
|
875
|
+
nextBatchPromise = loadBlockRange(db, nextStart, nextEnd);
|
|
740
876
|
}
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
877
|
+
const batchFailedBlocks = [];
|
|
878
|
+
for (let height = currentHeight;height <= batchEnd; height++) {
|
|
879
|
+
const blockData = batch.get(height);
|
|
880
|
+
if (!blockData) {
|
|
881
|
+
batchFailedBlocks.push({ height, reason: "block_missing" });
|
|
882
|
+
blocksProcessed++;
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
let result;
|
|
886
|
+
try {
|
|
887
|
+
result = await processBlock(def, subgraphName, height, {
|
|
888
|
+
skipProgressUpdate: true,
|
|
889
|
+
preloaded: blockData
|
|
890
|
+
});
|
|
891
|
+
} catch (err) {
|
|
892
|
+
logger4.error("Block processing error", {
|
|
893
|
+
subgraph: subgraphName,
|
|
894
|
+
blockHeight: height,
|
|
895
|
+
error: err instanceof Error ? err.message : String(err)
|
|
896
|
+
});
|
|
897
|
+
batchFailedBlocks.push({ height, reason: "processing_error" });
|
|
898
|
+
await updateSubgraphStatus2(db, subgraphName, status, height).catch(() => {});
|
|
899
|
+
blocksProcessed++;
|
|
900
|
+
totalErrors++;
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
751
903
|
blocksProcessed++;
|
|
752
904
|
totalEventsProcessed += result.processed;
|
|
753
905
|
totalErrors += result.errors;
|
|
@@ -757,11 +909,11 @@ async function reindexSubgraph(def, opts) {
|
|
|
757
909
|
await stats.flush(db);
|
|
758
910
|
}
|
|
759
911
|
}
|
|
760
|
-
if (blocksProcessed %
|
|
761
|
-
await updateSubgraphStatus2(db, subgraphName,
|
|
912
|
+
if (blocksProcessed % 100 === 0) {
|
|
913
|
+
await updateSubgraphStatus2(db, subgraphName, status, height);
|
|
762
914
|
}
|
|
763
915
|
if (blocksProcessed % LOG_INTERVAL === 0) {
|
|
764
|
-
logger4.info("Reindex
|
|
916
|
+
logger4.info(`${status === "reindexing" ? "Reindex" : "Backfill"} progress`, {
|
|
765
917
|
subgraph: subgraphName,
|
|
766
918
|
processed: blocksProcessed,
|
|
767
919
|
total: totalBlocks,
|
|
@@ -770,14 +922,87 @@ async function reindexSubgraph(def, opts) {
|
|
|
770
922
|
});
|
|
771
923
|
}
|
|
772
924
|
}
|
|
773
|
-
|
|
925
|
+
if (batchFailedBlocks.length > 0 && opts.subgraphId) {
|
|
926
|
+
const gaps = coalesceFailedBlocks(batchFailedBlocks);
|
|
927
|
+
await recordGapBatch(db, opts.subgraphId, subgraphName, gaps).catch((err) => {
|
|
928
|
+
logger4.warn("Failed to record subgraph gaps", {
|
|
929
|
+
subgraph: subgraphName,
|
|
930
|
+
error: err instanceof Error ? err.message : String(err)
|
|
931
|
+
});
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
const avg = avgEventsPerBlock(batch);
|
|
935
|
+
if (avg > 50)
|
|
936
|
+
batchSize = Math.max(Math.round(batchSize * 0.5), MIN_BATCH_SIZE);
|
|
937
|
+
else if (avg < 10)
|
|
938
|
+
batchSize = Math.min(Math.round(batchSize * 1.5), MAX_BATCH_SIZE);
|
|
939
|
+
currentHeight = batchEnd + 1;
|
|
940
|
+
}
|
|
941
|
+
await stats.flush(db);
|
|
942
|
+
return { blocksProcessed, totalEventsProcessed, totalErrors };
|
|
943
|
+
}
|
|
944
|
+
async function resolveBlockRange(db, opts) {
|
|
945
|
+
const fromBlock = opts?.fromBlock ?? 1;
|
|
946
|
+
let toBlock;
|
|
947
|
+
if (opts?.toBlock != null) {
|
|
948
|
+
toBlock = opts.toBlock;
|
|
949
|
+
} else {
|
|
950
|
+
const progress = await db.selectFrom("index_progress").selectAll().where("network", "=", process.env.NETWORK ?? "mainnet").executeTakeFirst();
|
|
951
|
+
toBlock = progress?.last_indexed_block ?? progress?.last_contiguous_block ?? 0;
|
|
952
|
+
}
|
|
953
|
+
return { fromBlock, toBlock };
|
|
954
|
+
}
|
|
955
|
+
async function reindexSubgraph(def, opts) {
|
|
956
|
+
const db = getDb2();
|
|
957
|
+
const client = getRawClient();
|
|
958
|
+
const subgraphName = def.name;
|
|
959
|
+
const schemaName = opts?.schemaName ?? pgSchemaName(subgraphName);
|
|
960
|
+
await updateSubgraphStatus2(db, subgraphName, "reindexing");
|
|
961
|
+
logger4.info("Reindex starting", { subgraph: subgraphName });
|
|
962
|
+
try {
|
|
963
|
+
await client.unsafe(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`);
|
|
964
|
+
const { statements } = generateSubgraphSQL(def, schemaName);
|
|
965
|
+
for (const stmt of statements) {
|
|
966
|
+
await client.unsafe(stmt);
|
|
967
|
+
}
|
|
968
|
+
logger4.info("Schema recreated for reindex", { subgraph: subgraphName });
|
|
969
|
+
const { fromBlock, toBlock } = await resolveBlockRange(db, opts);
|
|
970
|
+
if (fromBlock > toBlock) {
|
|
971
|
+
logger4.info("No blocks to reindex", {
|
|
972
|
+
subgraph: subgraphName,
|
|
973
|
+
fromBlock,
|
|
974
|
+
toBlock
|
|
975
|
+
});
|
|
976
|
+
await updateSubgraphStatus2(db, subgraphName, "active", 0);
|
|
977
|
+
return { processed: 0 };
|
|
978
|
+
}
|
|
979
|
+
logger4.info("Reindexing blocks", {
|
|
980
|
+
subgraph: subgraphName,
|
|
981
|
+
fromBlock,
|
|
982
|
+
toBlock,
|
|
983
|
+
totalBlocks: toBlock - fromBlock + 1
|
|
984
|
+
});
|
|
985
|
+
const subgraphRow = await db.selectFrom("subgraphs").select(["id", "api_key_id"]).where("name", "=", subgraphName).executeTakeFirst();
|
|
986
|
+
const result = await processBlockRange(def, {
|
|
987
|
+
fromBlock,
|
|
988
|
+
toBlock,
|
|
989
|
+
status: "reindexing",
|
|
990
|
+
isCatchup: false,
|
|
991
|
+
apiKeyId: subgraphRow?.api_key_id ?? null,
|
|
992
|
+
subgraphId: subgraphRow?.id
|
|
993
|
+
});
|
|
774
994
|
const { recordSubgraphProcessed: recordSubgraphProcessed2 } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
775
|
-
if (totalEventsProcessed > 0 || totalErrors > 0) {
|
|
776
|
-
await recordSubgraphProcessed2(db, subgraphName, totalEventsProcessed, totalErrors, totalErrors > 0 ? `${totalErrors} error(s) during reindex` : undefined);
|
|
995
|
+
if (result.totalEventsProcessed > 0 || result.totalErrors > 0) {
|
|
996
|
+
await recordSubgraphProcessed2(db, subgraphName, result.totalEventsProcessed, result.totalErrors, result.totalErrors > 0 ? `${result.totalErrors} error(s) during reindex` : undefined);
|
|
777
997
|
}
|
|
778
998
|
await updateSubgraphStatus2(db, subgraphName, "active", toBlock);
|
|
779
|
-
logger4.info("Reindex complete", {
|
|
780
|
-
|
|
999
|
+
logger4.info("Reindex complete", {
|
|
1000
|
+
subgraph: subgraphName,
|
|
1001
|
+
blocks: result.blocksProcessed,
|
|
1002
|
+
events: result.totalEventsProcessed,
|
|
1003
|
+
errors: result.totalErrors
|
|
1004
|
+
});
|
|
1005
|
+
return { processed: result.blocksProcessed };
|
|
781
1006
|
} catch (err) {
|
|
782
1007
|
logger4.error("Reindex failed", {
|
|
783
1008
|
subgraph: subgraphName,
|
|
@@ -787,6 +1012,50 @@ async function reindexSubgraph(def, opts) {
|
|
|
787
1012
|
throw err;
|
|
788
1013
|
}
|
|
789
1014
|
}
|
|
1015
|
+
async function backfillSubgraph(def, opts) {
|
|
1016
|
+
const db = getDb2();
|
|
1017
|
+
const subgraphName = def.name;
|
|
1018
|
+
logger4.info("Backfill starting", {
|
|
1019
|
+
subgraph: subgraphName,
|
|
1020
|
+
from: opts.fromBlock,
|
|
1021
|
+
to: opts.toBlock
|
|
1022
|
+
});
|
|
1023
|
+
try {
|
|
1024
|
+
const subgraphRow = await db.selectFrom("subgraphs").select(["id", "api_key_id"]).where("name", "=", subgraphName).executeTakeFirst();
|
|
1025
|
+
const result = await processBlockRange(def, {
|
|
1026
|
+
fromBlock: opts.fromBlock,
|
|
1027
|
+
toBlock: opts.toBlock,
|
|
1028
|
+
status: "active",
|
|
1029
|
+
isCatchup: false,
|
|
1030
|
+
apiKeyId: subgraphRow?.api_key_id ?? null,
|
|
1031
|
+
subgraphId: subgraphRow?.id
|
|
1032
|
+
});
|
|
1033
|
+
const resolved = await resolveGaps(db, subgraphName, opts.fromBlock, opts.toBlock).catch(() => 0);
|
|
1034
|
+
if (resolved > 0) {
|
|
1035
|
+
logger4.info("Resolved subgraph gaps via backfill", {
|
|
1036
|
+
subgraph: subgraphName,
|
|
1037
|
+
resolved
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
const { recordSubgraphProcessed: recordSubgraphProcessed2 } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
1041
|
+
if (result.totalEventsProcessed > 0 || result.totalErrors > 0) {
|
|
1042
|
+
await recordSubgraphProcessed2(db, subgraphName, result.totalEventsProcessed, result.totalErrors, result.totalErrors > 0 ? `${result.totalErrors} error(s) during backfill` : undefined);
|
|
1043
|
+
}
|
|
1044
|
+
logger4.info("Backfill complete", {
|
|
1045
|
+
subgraph: subgraphName,
|
|
1046
|
+
blocks: result.blocksProcessed,
|
|
1047
|
+
events: result.totalEventsProcessed,
|
|
1048
|
+
errors: result.totalErrors
|
|
1049
|
+
});
|
|
1050
|
+
return { processed: result.blocksProcessed };
|
|
1051
|
+
} catch (err) {
|
|
1052
|
+
logger4.error("Backfill failed", {
|
|
1053
|
+
subgraph: subgraphName,
|
|
1054
|
+
error: getErrorMessage2(err)
|
|
1055
|
+
});
|
|
1056
|
+
throw err;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
790
1059
|
// src/define.ts
|
|
791
1060
|
function defineSubgraph(def) {
|
|
792
1061
|
return def;
|
|
@@ -845,7 +1114,13 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
845
1114
|
const regData = {
|
|
846
1115
|
name: def.name,
|
|
847
1116
|
version: def.version || "1.0.0",
|
|
848
|
-
definition: toJsonSafe({
|
|
1117
|
+
definition: toJsonSafe({
|
|
1118
|
+
name: def.name,
|
|
1119
|
+
version: def.version,
|
|
1120
|
+
description: def.description,
|
|
1121
|
+
sources: def.sources,
|
|
1122
|
+
schema: def.schema
|
|
1123
|
+
}),
|
|
849
1124
|
schemaHash: hash,
|
|
850
1125
|
handlerPath,
|
|
851
1126
|
apiKeyId: opts?.apiKeyId,
|
|
@@ -961,8 +1236,9 @@ export {
|
|
|
961
1236
|
generateSubgraphSQL,
|
|
962
1237
|
diffSchema,
|
|
963
1238
|
deploySchema,
|
|
964
|
-
defineSubgraph
|
|
1239
|
+
defineSubgraph,
|
|
1240
|
+
backfillSubgraph
|
|
965
1241
|
};
|
|
966
1242
|
|
|
967
|
-
//# debugId=
|
|
1243
|
+
//# debugId=5A0D359BE0D619B264756E2164756E21
|
|
968
1244
|
//# sourceMappingURL=index.js.map
|