@secondlayer/subgraphs 3.13.0 → 3.14.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/dist/src/index.d.ts +47 -1
- package/dist/src/index.js +443 -37
- package/dist/src/index.js.map +7 -6
- package/dist/src/runtime/block-processor.d.ts +16 -11
- package/dist/src/runtime/block-processor.js +83 -30
- package/dist/src/runtime/block-processor.js.map +3 -3
- package/dist/src/runtime/catchup.js +83 -30
- package/dist/src/runtime/catchup.js.map +3 -3
- package/dist/src/runtime/processor.js +111 -37
- package/dist/src/runtime/processor.js.map +5 -5
- package/dist/src/runtime/reindex.js +109 -36
- package/dist/src/runtime/reindex.js.map +4 -4
- package/dist/src/runtime/reorg.js +83 -30
- package/dist/src/runtime/reorg.js.map +3 -3
- package/dist/src/schema/index.js +3 -2
- package/dist/src/schema/index.js.map +3 -3
- package/dist/src/service.js +111 -37
- package/dist/src/service.js.map +5 -5
- package/dist/src/validate.js +3 -2
- package/dist/src/validate.js.map +3 -3
- package/package.json +2 -2
package/dist/src/index.d.ts
CHANGED
|
@@ -664,6 +664,52 @@ declare function defineSubgraph<
|
|
|
664
664
|
const Sources extends Record<string, SubgraphFilter>,
|
|
665
665
|
const S extends SubgraphSchema
|
|
666
666
|
>(def: TypedSubgraphDefinition<Sources, S>): TypedSubgraphDefinition<Sources, S>;
|
|
667
|
+
/**
|
|
668
|
+
* Empirical print-payload schema inference. Callers (the index print-schema
|
|
669
|
+
* endpoint) extract topic + raw Clarity hex per row; this module deserializes
|
|
670
|
+
* a bounded sample per topic, unifies the observed value shapes into a single
|
|
671
|
+
* structural tree, and renders Clarity/TS/ColumnType views of each field.
|
|
672
|
+
*/
|
|
673
|
+
interface PrintSample {
|
|
674
|
+
blockHeight: number;
|
|
675
|
+
topic: string;
|
|
676
|
+
rawHex: string | null;
|
|
677
|
+
}
|
|
678
|
+
interface InferredPrintField {
|
|
679
|
+
/** Original kebab-case tuple key (the `topic` discriminant is excluded) */
|
|
680
|
+
name: string;
|
|
681
|
+
/** What handlers see on `e.data` — exact runner camelization */
|
|
682
|
+
camel_name: string;
|
|
683
|
+
/** Rendered Clarity type; buff/string lengths are the max observed */
|
|
684
|
+
clarity_type: string;
|
|
685
|
+
/** Decoded handler value type (uint→bigint, buffer→string, …) */
|
|
686
|
+
ts_type: string;
|
|
687
|
+
column_type: ColumnType;
|
|
688
|
+
/** Present in 100% of this topic's decoded samples */
|
|
689
|
+
always_present: boolean;
|
|
690
|
+
/** Only when optional observed: share of present samples that were some */
|
|
691
|
+
optional_some_rate?: number;
|
|
692
|
+
}
|
|
693
|
+
interface InferredTopicSchema {
|
|
694
|
+
topic: string;
|
|
695
|
+
count: number;
|
|
696
|
+
first_height: number;
|
|
697
|
+
last_height: number;
|
|
698
|
+
non_tuple: boolean;
|
|
699
|
+
fields: InferredPrintField[];
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Kebab-case → camelCase using the exact runner regex, so `camel_name`
|
|
703
|
+
* matches what handlers see on `e.data` (runner.ts camelizeKeys).
|
|
704
|
+
*/
|
|
705
|
+
declare function camelizeDataKey(str: string): string;
|
|
706
|
+
/**
|
|
707
|
+
* Infers per-topic field schemas from sampled print events. Counts and
|
|
708
|
+
* height bounds cover ALL rows of a topic (cheap); only a bounded subset is
|
|
709
|
+
* deserialized for typing. Rows with missing/undecodable hex still count but
|
|
710
|
+
* contribute nothing to typing.
|
|
711
|
+
*/
|
|
712
|
+
declare function inferPrintTopics(samples: PrintSample[]): InferredTopicSchema[];
|
|
667
713
|
interface GeneratedSQL {
|
|
668
714
|
statements: string[];
|
|
669
715
|
hash: string;
|
|
@@ -829,4 +875,4 @@ type SparseProbeTarget = {
|
|
|
829
875
|
declare function canSparseScan(subgraph: SubgraphDefinition): boolean;
|
|
830
876
|
/** Probe targets for a subgraph's filters: decoded type + contract scope when\\n* the filter pins one (assetIdentifier "SP….contract::asset" or contractId). */
|
|
831
877
|
declare function sparseProbeTargets(subgraph: SubgraphDefinition): SparseProbeTarget[];
|
|
832
|
-
export { validateSubgraphDefinition, sparseProbeTargets, resumeReindex, renderDeployPlan, reindexSubgraph, pgSchemaName, hasBreakingChanges, generateSubgraphSQL, generatePrismaSchema, generateKyselySchema, generateIndexSchema, generateDrizzleSchema, diffSchema, deploySchema, defineSubgraph, canSparseScan, backfillSubgraph, WriteRow, WhereInput, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TableDiff, SystemRow, SubscribeOptions, SubgraphTableClient, SubgraphTable, SubgraphSchema, SubgraphHandler, SubgraphFilter, SubgraphDefinition, SubgraphContext, SubgraphColumn, StxTransferPayload, StxTransferFilter, StxMintPayload, StxMintFilter, StxLockPayload, StxLockFilter, StxBurnPayload, StxBurnFilter, SparseProbeTarget, RowValue, ReindexOptions, PrismaGenOptions, PrintEventPayload, PrintEventFor, PrintEventFilter, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, KyselyGenOptions, InferTableRow, InferSubgraphClient, InferColumnType, IndexCodegenTarget, IndexCodegenOptions, INDEX_CODEGEN_TABLES, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, EventForFilter, DrizzleGenOptions, DeployPlan, DeployDiff, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, ByoMigrationPlan, ByoBreakingChangeError, AnyEvent, AggregateSpec, AggregateResult };
|
|
878
|
+
export { validateSubgraphDefinition, sparseProbeTargets, resumeReindex, renderDeployPlan, reindexSubgraph, pgSchemaName, inferPrintTopics, hasBreakingChanges, generateSubgraphSQL, generatePrismaSchema, generateKyselySchema, generateIndexSchema, generateDrizzleSchema, diffSchema, deploySchema, defineSubgraph, canSparseScan, camelizeDataKey, backfillSubgraph, WriteRow, WhereInput, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TableDiff, SystemRow, SubscribeOptions, SubgraphTableClient, SubgraphTable, SubgraphSchema, SubgraphHandler, SubgraphFilter, SubgraphDefinition, SubgraphContext, SubgraphColumn, StxTransferPayload, StxTransferFilter, StxMintPayload, StxMintFilter, StxLockPayload, StxLockFilter, StxBurnPayload, StxBurnFilter, SparseProbeTarget, RowValue, ReindexOptions, PrismaGenOptions, PrintSample, PrintEventPayload, PrintEventFor, PrintEventFilter, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, KyselyGenOptions, InferredTopicSchema, InferredPrintField, InferTableRow, InferSubgraphClient, InferColumnType, IndexCodegenTarget, IndexCodegenOptions, INDEX_CODEGEN_TABLES, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, EventForFilter, DrizzleGenOptions, DeployPlan, DeployDiff, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, ByoMigrationPlan, ByoBreakingChangeError, AnyEvent, AggregateSpec, AggregateResult };
|
package/dist/src/index.js
CHANGED
|
@@ -62,7 +62,8 @@ var SubgraphFilterSchema = z.object({
|
|
|
62
62
|
topic: z.string().optional(),
|
|
63
63
|
lockedAddress: z.string().optional(),
|
|
64
64
|
abi: z.record(z.string(), z.any()).optional(),
|
|
65
|
-
trait: z.string().optional()
|
|
65
|
+
trait: z.string().optional(),
|
|
66
|
+
prints: z.record(z.string(), z.record(z.string(), ColumnTypeSchema)).optional()
|
|
66
67
|
}).strict();
|
|
67
68
|
var SubgraphDefinitionSchema = z.object({
|
|
68
69
|
name: SubgraphNameSchema,
|
|
@@ -1409,6 +1410,7 @@ function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
|
1409
1410
|
// src/runtime/block-processor.ts
|
|
1410
1411
|
import { getTargetDb } from "@secondlayer/shared/db";
|
|
1411
1412
|
import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
|
|
1413
|
+
import { advanceOperationCursor } from "@secondlayer/shared/db/queries/subgraph-operations";
|
|
1412
1414
|
import {
|
|
1413
1415
|
isByoSubgraph,
|
|
1414
1416
|
recordSubgraphProcessed,
|
|
@@ -2028,6 +2030,21 @@ async function resolveTraitContracts(subgraph, blockHeight, db) {
|
|
|
2028
2030
|
}
|
|
2029
2031
|
return resolved;
|
|
2030
2032
|
}
|
|
2033
|
+
|
|
2034
|
+
class CursorRaceLostError extends Error {
|
|
2035
|
+
constructor(operationId, height) {
|
|
2036
|
+
super(`op ${operationId} lost cursor race at block ${height}`);
|
|
2037
|
+
this.name = "CursorRaceLostError";
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
function opCursorMode(opts) {
|
|
2041
|
+
const ap = opts?.atomicProgress;
|
|
2042
|
+
return ap && "operationId" in ap ? ap : undefined;
|
|
2043
|
+
}
|
|
2044
|
+
function statusMode(opts) {
|
|
2045
|
+
const ap = opts?.atomicProgress;
|
|
2046
|
+
return ap && "status" in ap ? ap : undefined;
|
|
2047
|
+
}
|
|
2031
2048
|
var BLOCK_RETRY_DELAYS_MS = [500, 2000, 5000];
|
|
2032
2049
|
function journalEnabled(opts) {
|
|
2033
2050
|
return !opts?.skipProgressUpdate;
|
|
@@ -2121,12 +2138,19 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2121
2138
|
}
|
|
2122
2139
|
};
|
|
2123
2140
|
if (route.byo) {
|
|
2124
|
-
if (opts
|
|
2141
|
+
if (statusMode(opts)) {
|
|
2125
2142
|
const row = await targetDb.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
|
|
2126
2143
|
if (row && Number(row.last_processed_block) >= blockHeight) {
|
|
2127
2144
|
result.skipped = true;
|
|
2128
2145
|
return result;
|
|
2129
2146
|
}
|
|
2147
|
+
} else if (opCursorMode(opts)) {
|
|
2148
|
+
const om = opCursorMode(opts);
|
|
2149
|
+
const row = await targetDb.selectFrom("subgraph_operations").select("cursor_block").where("id", "=", om.operationId).executeTakeFirst();
|
|
2150
|
+
if (row?.cursor_block != null && Number(row.cursor_block) >= blockHeight) {
|
|
2151
|
+
result.skipped = true;
|
|
2152
|
+
return result;
|
|
2153
|
+
}
|
|
2130
2154
|
}
|
|
2131
2155
|
let runResult = { processed: 0, errors: 0 };
|
|
2132
2156
|
let manifest;
|
|
@@ -2147,41 +2171,71 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2147
2171
|
if (manifest && manifest.count > 0) {
|
|
2148
2172
|
await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
|
|
2149
2173
|
}
|
|
2150
|
-
|
|
2151
|
-
|
|
2174
|
+
const byoSm = statusMode(opts);
|
|
2175
|
+
const byoOm = opCursorMode(opts);
|
|
2176
|
+
if (byoSm && manifest && manifest.count > 0) {
|
|
2177
|
+
await updateSubgraphStatus(tx, subgraphName, byoSm.status, blockHeight);
|
|
2178
|
+
} else if (byoOm && manifest && manifest.count > 0) {
|
|
2179
|
+
await advanceOperationCursor(tx, byoOm.operationId, blockHeight);
|
|
2152
2180
|
}
|
|
2153
2181
|
await applyProgress(tx, runResult);
|
|
2154
2182
|
});
|
|
2155
2183
|
} else {
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
const
|
|
2159
|
-
if (
|
|
2160
|
-
|
|
2161
|
-
|
|
2184
|
+
try {
|
|
2185
|
+
await targetDb.transaction().execute(async (tx) => {
|
|
2186
|
+
const opMode = opCursorMode(opts);
|
|
2187
|
+
if (statusMode(opts)) {
|
|
2188
|
+
const row = await tx.selectFrom("subgraphs").select("last_processed_block").where("name", "=", subgraphName).executeTakeFirst();
|
|
2189
|
+
if (row && Number(row.last_processed_block) >= blockHeight) {
|
|
2190
|
+
result.skipped = true;
|
|
2191
|
+
return;
|
|
2192
|
+
}
|
|
2193
|
+
} else if (opMode) {
|
|
2194
|
+
const row = await tx.selectFrom("subgraph_operations").select("cursor_block").where("id", "=", opMode.operationId).executeTakeFirst();
|
|
2195
|
+
if (row?.cursor_block != null && Number(row.cursor_block) >= blockHeight) {
|
|
2196
|
+
result.skipped = true;
|
|
2197
|
+
return;
|
|
2198
|
+
}
|
|
2162
2199
|
}
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2200
|
+
const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx, false, journalEnabled(opts));
|
|
2201
|
+
const handlerStart = performance.now();
|
|
2202
|
+
const runResult = await runHandlers(subgraph, matched, ctx);
|
|
2203
|
+
handlerMs = performance.now() - handlerStart;
|
|
2204
|
+
result.processed = runResult.processed;
|
|
2205
|
+
result.errors = runResult.errors;
|
|
2206
|
+
let flushedWrites = false;
|
|
2207
|
+
if (ctx.pendingOps > 0) {
|
|
2208
|
+
const flushStart = performance.now();
|
|
2209
|
+
const manifest = await ctx.flush();
|
|
2210
|
+
flushedWrites = manifest.count > 0;
|
|
2211
|
+
if (manifest.count > 0) {
|
|
2212
|
+
await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
|
|
2213
|
+
}
|
|
2214
|
+
flushMs = performance.now() - flushStart;
|
|
2177
2215
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2216
|
+
const sm = statusMode(opts);
|
|
2217
|
+
if (sm && flushedWrites) {
|
|
2218
|
+
await updateSubgraphStatus(tx, subgraphName, sm.status, blockHeight);
|
|
2219
|
+
} else if (opMode && flushedWrites) {
|
|
2220
|
+
const advanced = await advanceOperationCursor(tx, opMode.operationId, blockHeight);
|
|
2221
|
+
if (!advanced) {
|
|
2222
|
+
throw new CursorRaceLostError(opMode.operationId, blockHeight);
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
await applyProgress(tx, runResult);
|
|
2226
|
+
});
|
|
2227
|
+
} catch (err) {
|
|
2228
|
+
if (err instanceof CursorRaceLostError) {
|
|
2229
|
+
logger5.warn("cursor race lost — block already covered", {
|
|
2230
|
+
subgraph: subgraphName,
|
|
2231
|
+
blockHeight,
|
|
2232
|
+
error: err.message
|
|
2233
|
+
});
|
|
2234
|
+
result.skipped = true;
|
|
2235
|
+
return result;
|
|
2182
2236
|
}
|
|
2183
|
-
|
|
2184
|
-
}
|
|
2237
|
+
throw err;
|
|
2238
|
+
}
|
|
2185
2239
|
}
|
|
2186
2240
|
const totalMs = performance.now() - blockStart;
|
|
2187
2241
|
result.timing = {
|
|
@@ -2286,7 +2340,10 @@ import {
|
|
|
2286
2340
|
recordGapBatch,
|
|
2287
2341
|
resolveGaps
|
|
2288
2342
|
} from "@secondlayer/shared/db/queries/subgraph-gaps";
|
|
2289
|
-
import {
|
|
2343
|
+
import {
|
|
2344
|
+
advanceOperationCursor as advanceOperationCursor2,
|
|
2345
|
+
updateOperationProcessedEvents
|
|
2346
|
+
} from "@secondlayer/shared/db/queries/subgraph-operations";
|
|
2290
2347
|
import {
|
|
2291
2348
|
recordSubgraphProcessed as recordSubgraphProcessed2,
|
|
2292
2349
|
updateSubgraphStatus as updateSubgraphStatus2
|
|
@@ -2359,6 +2416,7 @@ async function processBlockRange(def, opts) {
|
|
|
2359
2416
|
const totalBlocks = toBlock - fromBlock + 1;
|
|
2360
2417
|
const stats = new StatsAccumulator(subgraphName, opts.isCatchup);
|
|
2361
2418
|
let blocksProcessed = 0;
|
|
2419
|
+
let blocksSkippedByCursor = 0;
|
|
2362
2420
|
let totalEventsProcessed = 0;
|
|
2363
2421
|
let totalErrors = 0;
|
|
2364
2422
|
let pendingEventsProcessed = 0;
|
|
@@ -2419,7 +2477,8 @@ async function processBlockRange(def, opts) {
|
|
|
2419
2477
|
}
|
|
2420
2478
|
const batchFailedBlocks = [];
|
|
2421
2479
|
let batchMatched = 0;
|
|
2422
|
-
const
|
|
2480
|
+
const opCursor = status === "active" && opts.operationId ? { operationId: opts.operationId } : undefined;
|
|
2481
|
+
const atomicProgress = status === "reindexing" ? { status } : opCursor;
|
|
2423
2482
|
for (let height = currentHeight;height <= batchEnd; height++) {
|
|
2424
2483
|
let blockData = batch.get(height);
|
|
2425
2484
|
if (!blockData) {
|
|
@@ -2459,6 +2518,8 @@ async function processBlockRange(def, opts) {
|
|
|
2459
2518
|
continue;
|
|
2460
2519
|
}
|
|
2461
2520
|
blocksProcessed++;
|
|
2521
|
+
if (result.skipped)
|
|
2522
|
+
blocksSkippedByCursor++;
|
|
2462
2523
|
batchMatched += result.matched;
|
|
2463
2524
|
totalEventsProcessed += result.processed;
|
|
2464
2525
|
totalErrors += result.errors;
|
|
@@ -2476,7 +2537,11 @@ async function processBlockRange(def, opts) {
|
|
|
2476
2537
|
const now = Date.now();
|
|
2477
2538
|
const shouldFlushProgress = blocksProcessed % 100 === 0 || now - lastProgressFlushAt >= PROGRESS_FLUSH_INTERVAL_MS;
|
|
2478
2539
|
if (shouldFlushProgress) {
|
|
2479
|
-
|
|
2540
|
+
if (opCursor) {
|
|
2541
|
+
await advanceOperationCursor2(targetDb, opCursor.operationId, height);
|
|
2542
|
+
} else {
|
|
2543
|
+
await updateSubgraphStatus2(targetDb, subgraphName, status, height);
|
|
2544
|
+
}
|
|
2480
2545
|
if (opts.operationId) {
|
|
2481
2546
|
await updateOperationProcessedEvents(targetDb, opts.operationId, totalEventsProcessed).catch(() => {});
|
|
2482
2547
|
}
|
|
@@ -2488,7 +2553,8 @@ async function processBlockRange(def, opts) {
|
|
|
2488
2553
|
processed: blocksProcessed,
|
|
2489
2554
|
total: totalBlocks,
|
|
2490
2555
|
currentBlock: height,
|
|
2491
|
-
pct: Math.round(blocksProcessed / totalBlocks * 100)
|
|
2556
|
+
pct: Math.round(blocksProcessed / totalBlocks * 100),
|
|
2557
|
+
...blocksSkippedByCursor > 0 ? { skippedByCursor: blocksSkippedByCursor } : {}
|
|
2492
2558
|
});
|
|
2493
2559
|
}
|
|
2494
2560
|
}
|
|
@@ -2514,7 +2580,11 @@ async function processBlockRange(def, opts) {
|
|
|
2514
2580
|
if (jumpTo > batchEnd + 1) {
|
|
2515
2581
|
const skipped = Math.min(jumpTo, toBlock + 1) - (batchEnd + 1);
|
|
2516
2582
|
blocksProcessed += skipped;
|
|
2517
|
-
|
|
2583
|
+
if (opCursor) {
|
|
2584
|
+
await advanceOperationCursor2(targetDb, opCursor.operationId, jumpTo - 1);
|
|
2585
|
+
} else {
|
|
2586
|
+
await updateSubgraphStatus2(targetDb, subgraphName, status, jumpTo - 1);
|
|
2587
|
+
}
|
|
2518
2588
|
logger6.info("Sparse skip", {
|
|
2519
2589
|
subgraph: subgraphName,
|
|
2520
2590
|
from: batchEnd + 1,
|
|
@@ -2572,7 +2642,11 @@ async function reindexSubgraph(def, opts) {
|
|
|
2572
2642
|
for (const stmt of statements) {
|
|
2573
2643
|
await client.unsafe(stmt);
|
|
2574
2644
|
}
|
|
2575
|
-
|
|
2645
|
+
await updateSubgraphStatus2(targetDb, subgraphName, "reindexing", Math.max(0, fromBlock - 1));
|
|
2646
|
+
logger6.info("Schema recreated for reindex", {
|
|
2647
|
+
subgraph: subgraphName,
|
|
2648
|
+
cursorResetTo: Math.max(0, fromBlock - 1)
|
|
2649
|
+
});
|
|
2576
2650
|
await targetDb.updateTable("subgraphs").set({
|
|
2577
2651
|
last_processed_block: initialReindexProgressBlock(fromBlock),
|
|
2578
2652
|
reindex_from_block: fromBlock,
|
|
@@ -2756,6 +2830,336 @@ async function backfillSubgraph(def, opts) {
|
|
|
2756
2830
|
function defineSubgraph(def) {
|
|
2757
2831
|
return def;
|
|
2758
2832
|
}
|
|
2833
|
+
// src/print-schema.ts
|
|
2834
|
+
import {
|
|
2835
|
+
deserializeCV as deserializeCV3
|
|
2836
|
+
} from "@secondlayer/stacks/clarity";
|
|
2837
|
+
function camelizeDataKey(str) {
|
|
2838
|
+
return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
2839
|
+
}
|
|
2840
|
+
function cvToTree(cv) {
|
|
2841
|
+
switch (cv.type) {
|
|
2842
|
+
case "uint":
|
|
2843
|
+
case "int":
|
|
2844
|
+
return { kind: cv.type };
|
|
2845
|
+
case "true":
|
|
2846
|
+
case "false":
|
|
2847
|
+
return { kind: "bool" };
|
|
2848
|
+
case "address":
|
|
2849
|
+
case "contract":
|
|
2850
|
+
return { kind: "principal" };
|
|
2851
|
+
case "buffer":
|
|
2852
|
+
return { kind: "buffer", len: cv.value.length / 2 };
|
|
2853
|
+
case "ascii":
|
|
2854
|
+
return { kind: "ascii", len: cv.value.length };
|
|
2855
|
+
case "utf8":
|
|
2856
|
+
return { kind: "utf8", len: new TextEncoder().encode(cv.value).length };
|
|
2857
|
+
case "none":
|
|
2858
|
+
return { kind: "optional", inner: null };
|
|
2859
|
+
case "some":
|
|
2860
|
+
return { kind: "optional", inner: cvToTree(cv.value) };
|
|
2861
|
+
case "ok":
|
|
2862
|
+
return { kind: "response", ok: cvToTree(cv.value), err: null };
|
|
2863
|
+
case "err":
|
|
2864
|
+
return { kind: "response", ok: null, err: cvToTree(cv.value) };
|
|
2865
|
+
case "list": {
|
|
2866
|
+
let inner = null;
|
|
2867
|
+
for (const el of cv.value) {
|
|
2868
|
+
const t = cvToTree(el);
|
|
2869
|
+
inner = inner ? unify(inner, t) : t;
|
|
2870
|
+
}
|
|
2871
|
+
return { kind: "list", inner };
|
|
2872
|
+
}
|
|
2873
|
+
case "tuple": {
|
|
2874
|
+
const entries = new Map;
|
|
2875
|
+
for (const [k, v] of Object.entries(cv.value)) {
|
|
2876
|
+
entries.set(k, { tree: cvToTree(v), present: 1 });
|
|
2877
|
+
}
|
|
2878
|
+
return { kind: "tuple", count: 1, entries };
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
function unifyNullable(a, b) {
|
|
2883
|
+
if (!a)
|
|
2884
|
+
return b;
|
|
2885
|
+
if (!b)
|
|
2886
|
+
return a;
|
|
2887
|
+
return unify(a, b);
|
|
2888
|
+
}
|
|
2889
|
+
var UNION_KIND_ORDER = {
|
|
2890
|
+
uint: 0,
|
|
2891
|
+
int: 1,
|
|
2892
|
+
bool: 2,
|
|
2893
|
+
principal: 3,
|
|
2894
|
+
buffer: 4,
|
|
2895
|
+
ascii: 5,
|
|
2896
|
+
utf8: 6,
|
|
2897
|
+
list: 7,
|
|
2898
|
+
tuple: 8,
|
|
2899
|
+
response: 9
|
|
2900
|
+
};
|
|
2901
|
+
function makeUnion(members) {
|
|
2902
|
+
const sorted = [...members].sort((a, b) => (UNION_KIND_ORDER[a.kind] ?? 99) - (UNION_KIND_ORDER[b.kind] ?? 99));
|
|
2903
|
+
return { kind: "union", members: sorted };
|
|
2904
|
+
}
|
|
2905
|
+
function unify(a, b) {
|
|
2906
|
+
if (a.kind === "optional" || b.kind === "optional") {
|
|
2907
|
+
const ai = a.kind === "optional" ? a.inner : a;
|
|
2908
|
+
const bi = b.kind === "optional" ? b.inner : b;
|
|
2909
|
+
return { kind: "optional", inner: unifyNullable(ai, bi) };
|
|
2910
|
+
}
|
|
2911
|
+
if (a.kind === "union")
|
|
2912
|
+
return unionAdd(a.members, b);
|
|
2913
|
+
if (b.kind === "union")
|
|
2914
|
+
return unionAdd(b.members, a);
|
|
2915
|
+
if (a.kind !== b.kind)
|
|
2916
|
+
return makeUnion([a, b]);
|
|
2917
|
+
switch (a.kind) {
|
|
2918
|
+
case "uint":
|
|
2919
|
+
case "int":
|
|
2920
|
+
case "bool":
|
|
2921
|
+
case "principal":
|
|
2922
|
+
return a;
|
|
2923
|
+
case "buffer":
|
|
2924
|
+
case "ascii":
|
|
2925
|
+
case "utf8":
|
|
2926
|
+
return { kind: a.kind, len: Math.max(a.len, b.len) };
|
|
2927
|
+
case "list":
|
|
2928
|
+
return {
|
|
2929
|
+
kind: "list",
|
|
2930
|
+
inner: unifyNullable(a.inner, b.inner)
|
|
2931
|
+
};
|
|
2932
|
+
case "response": {
|
|
2933
|
+
const rb = b;
|
|
2934
|
+
return {
|
|
2935
|
+
kind: "response",
|
|
2936
|
+
ok: unifyNullable(a.ok, rb.ok),
|
|
2937
|
+
err: unifyNullable(a.err, rb.err)
|
|
2938
|
+
};
|
|
2939
|
+
}
|
|
2940
|
+
case "tuple": {
|
|
2941
|
+
const tb = b;
|
|
2942
|
+
const entries = new Map([...a.entries].map(([k, e]) => [k, { ...e }]));
|
|
2943
|
+
for (const [k, e] of tb.entries) {
|
|
2944
|
+
const existing = entries.get(k);
|
|
2945
|
+
entries.set(k, existing ? {
|
|
2946
|
+
tree: unify(existing.tree, e.tree),
|
|
2947
|
+
present: existing.present + e.present
|
|
2948
|
+
} : { ...e });
|
|
2949
|
+
}
|
|
2950
|
+
return { kind: "tuple", count: a.count + tb.count, entries };
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
function unionAdd(members, t) {
|
|
2955
|
+
if (t.kind === "union") {
|
|
2956
|
+
let acc = { kind: "union", members };
|
|
2957
|
+
for (const m of t.members)
|
|
2958
|
+
acc = unify(acc, m);
|
|
2959
|
+
return acc;
|
|
2960
|
+
}
|
|
2961
|
+
const next = [...members];
|
|
2962
|
+
for (let i = 0;i < next.length; i++) {
|
|
2963
|
+
const member = next[i];
|
|
2964
|
+
if (!member)
|
|
2965
|
+
continue;
|
|
2966
|
+
const merged = unify(member, t);
|
|
2967
|
+
if (merged.kind !== "union") {
|
|
2968
|
+
next[i] = merged;
|
|
2969
|
+
return makeUnion(next);
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
next.push(t);
|
|
2973
|
+
return makeUnion(next);
|
|
2974
|
+
}
|
|
2975
|
+
function wrapOptional(t) {
|
|
2976
|
+
return t.kind === "optional" ? t : { kind: "optional", inner: t };
|
|
2977
|
+
}
|
|
2978
|
+
function renderClarity(t) {
|
|
2979
|
+
if (!t)
|
|
2980
|
+
return "?";
|
|
2981
|
+
switch (t.kind) {
|
|
2982
|
+
case "uint":
|
|
2983
|
+
case "int":
|
|
2984
|
+
case "bool":
|
|
2985
|
+
case "principal":
|
|
2986
|
+
return t.kind;
|
|
2987
|
+
case "buffer":
|
|
2988
|
+
return `(buff ${t.len})`;
|
|
2989
|
+
case "ascii":
|
|
2990
|
+
return `(string-ascii ${t.len})`;
|
|
2991
|
+
case "utf8":
|
|
2992
|
+
return `(string-utf8 ${t.len})`;
|
|
2993
|
+
case "optional":
|
|
2994
|
+
return `(optional ${renderClarity(t.inner)})`;
|
|
2995
|
+
case "list":
|
|
2996
|
+
return `(list ${renderClarity(t.inner)})`;
|
|
2997
|
+
case "response":
|
|
2998
|
+
return `(response ${renderClarity(t.ok)} ${renderClarity(t.err)})`;
|
|
2999
|
+
case "tuple": {
|
|
3000
|
+
const parts = [...t.entries].map(([k, e]) => {
|
|
3001
|
+
const tree = e.present < t.count ? wrapOptional(e.tree) : e.tree;
|
|
3002
|
+
return `(${k} ${renderClarity(tree)})`;
|
|
3003
|
+
});
|
|
3004
|
+
return `(tuple ${parts.join(" ")})`;
|
|
3005
|
+
}
|
|
3006
|
+
case "union":
|
|
3007
|
+
return t.members.map(renderClarity).join(" | ");
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
function renderTs(t) {
|
|
3011
|
+
if (!t)
|
|
3012
|
+
return "unknown";
|
|
3013
|
+
switch (t.kind) {
|
|
3014
|
+
case "uint":
|
|
3015
|
+
case "int":
|
|
3016
|
+
return "bigint";
|
|
3017
|
+
case "bool":
|
|
3018
|
+
return "boolean";
|
|
3019
|
+
case "principal":
|
|
3020
|
+
case "buffer":
|
|
3021
|
+
case "ascii":
|
|
3022
|
+
case "utf8":
|
|
3023
|
+
return "string";
|
|
3024
|
+
case "optional":
|
|
3025
|
+
return t.inner ? `${renderTs(t.inner)} | null` : "unknown | null";
|
|
3026
|
+
case "list": {
|
|
3027
|
+
const inner = renderTs(t.inner);
|
|
3028
|
+
return inner.includes(" | ") ? `(${inner})[]` : `${inner}[]`;
|
|
3029
|
+
}
|
|
3030
|
+
case "response": {
|
|
3031
|
+
const sides = [...new Set([renderTs(t.ok), renderTs(t.err)])];
|
|
3032
|
+
return sides.join(" | ");
|
|
3033
|
+
}
|
|
3034
|
+
case "tuple": {
|
|
3035
|
+
const parts = [...t.entries].map(([k, e]) => {
|
|
3036
|
+
const opt = e.present < t.count ? "?" : "";
|
|
3037
|
+
return `${camelizeDataKey(k)}${opt}: ${renderTs(e.tree)}`;
|
|
3038
|
+
});
|
|
3039
|
+
return `{ ${parts.join("; ")} }`;
|
|
3040
|
+
}
|
|
3041
|
+
case "union":
|
|
3042
|
+
return [...new Set(t.members.map((m) => renderTs(m)))].join(" | ");
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
function toColumnType(t) {
|
|
3046
|
+
if (!t)
|
|
3047
|
+
return "jsonb";
|
|
3048
|
+
switch (t.kind) {
|
|
3049
|
+
case "uint":
|
|
3050
|
+
return "uint";
|
|
3051
|
+
case "int":
|
|
3052
|
+
return "int";
|
|
3053
|
+
case "bool":
|
|
3054
|
+
return "boolean";
|
|
3055
|
+
case "principal":
|
|
3056
|
+
return "principal";
|
|
3057
|
+
case "buffer":
|
|
3058
|
+
case "ascii":
|
|
3059
|
+
case "utf8":
|
|
3060
|
+
return "text";
|
|
3061
|
+
case "list":
|
|
3062
|
+
case "tuple":
|
|
3063
|
+
return "jsonb";
|
|
3064
|
+
case "optional":
|
|
3065
|
+
return toColumnType(t.inner);
|
|
3066
|
+
case "response":
|
|
3067
|
+
return t.ok ? toColumnType(t.ok) : "jsonb";
|
|
3068
|
+
case "union":
|
|
3069
|
+
return "jsonb";
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
var MAX_DECODED_PER_TOPIC_NEWEST = 75;
|
|
3073
|
+
var MAX_DECODED_PER_TOPIC_OLDEST = 25;
|
|
3074
|
+
function inferPrintTopics(samples) {
|
|
3075
|
+
const groups = new Map;
|
|
3076
|
+
for (const s of samples) {
|
|
3077
|
+
const group = groups.get(s.topic);
|
|
3078
|
+
if (group)
|
|
3079
|
+
group.push(s);
|
|
3080
|
+
else
|
|
3081
|
+
groups.set(s.topic, [s]);
|
|
3082
|
+
}
|
|
3083
|
+
const out = [];
|
|
3084
|
+
for (const [topic, rows] of groups) {
|
|
3085
|
+
let first = Number.POSITIVE_INFINITY;
|
|
3086
|
+
let last = Number.NEGATIVE_INFINITY;
|
|
3087
|
+
for (const r of rows) {
|
|
3088
|
+
if (r.blockHeight < first)
|
|
3089
|
+
first = r.blockHeight;
|
|
3090
|
+
if (r.blockHeight > last)
|
|
3091
|
+
last = r.blockHeight;
|
|
3092
|
+
}
|
|
3093
|
+
const withHex = [...rows].filter((r) => r.rawHex !== null).sort((a, b) => b.blockHeight - a.blockHeight);
|
|
3094
|
+
const budget = MAX_DECODED_PER_TOPIC_NEWEST + MAX_DECODED_PER_TOPIC_OLDEST;
|
|
3095
|
+
const picked = withHex.length <= budget ? withHex : [
|
|
3096
|
+
...withHex.slice(0, MAX_DECODED_PER_TOPIC_NEWEST),
|
|
3097
|
+
...withHex.slice(-MAX_DECODED_PER_TOPIC_OLDEST)
|
|
3098
|
+
];
|
|
3099
|
+
const tuples = [];
|
|
3100
|
+
let decoded = 0;
|
|
3101
|
+
for (const p of picked) {
|
|
3102
|
+
try {
|
|
3103
|
+
const cv = deserializeCV3(p.rawHex);
|
|
3104
|
+
decoded++;
|
|
3105
|
+
if (cv.type === "tuple")
|
|
3106
|
+
tuples.push(cv);
|
|
3107
|
+
} catch {}
|
|
3108
|
+
}
|
|
3109
|
+
const nonTuple = decoded > 0 && tuples.length === 0;
|
|
3110
|
+
const fields = [];
|
|
3111
|
+
if (!nonTuple) {
|
|
3112
|
+
const stats = new Map;
|
|
3113
|
+
for (const t of tuples) {
|
|
3114
|
+
for (const [key2, value] of Object.entries(t.value)) {
|
|
3115
|
+
if (key2 === "topic")
|
|
3116
|
+
continue;
|
|
3117
|
+
const tree = cvToTree(value);
|
|
3118
|
+
const existing = stats.get(key2);
|
|
3119
|
+
if (existing) {
|
|
3120
|
+
existing.present++;
|
|
3121
|
+
existing.tree = unify(existing.tree, tree);
|
|
3122
|
+
if (value.type === "none")
|
|
3123
|
+
existing.noneCount++;
|
|
3124
|
+
if (value.type === "none" || value.type === "some") {
|
|
3125
|
+
existing.optionalSeen = true;
|
|
3126
|
+
}
|
|
3127
|
+
} else {
|
|
3128
|
+
stats.set(key2, {
|
|
3129
|
+
tree,
|
|
3130
|
+
present: 1,
|
|
3131
|
+
noneCount: value.type === "none" ? 1 : 0,
|
|
3132
|
+
optionalSeen: value.type === "none" || value.type === "some"
|
|
3133
|
+
});
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
for (const [name, st] of [...stats].sort(([a], [b]) => a.localeCompare(b))) {
|
|
3138
|
+
const field = {
|
|
3139
|
+
name,
|
|
3140
|
+
camel_name: camelizeDataKey(name),
|
|
3141
|
+
clarity_type: renderClarity(st.tree),
|
|
3142
|
+
ts_type: renderTs(st.tree),
|
|
3143
|
+
column_type: toColumnType(st.tree),
|
|
3144
|
+
always_present: st.present === tuples.length
|
|
3145
|
+
};
|
|
3146
|
+
if (st.optionalSeen) {
|
|
3147
|
+
field.optional_some_rate = (st.present - st.noneCount) / st.present;
|
|
3148
|
+
}
|
|
3149
|
+
fields.push(field);
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
out.push({
|
|
3153
|
+
topic,
|
|
3154
|
+
count: rows.length,
|
|
3155
|
+
first_height: first,
|
|
3156
|
+
last_height: last,
|
|
3157
|
+
non_tuple: nonTuple,
|
|
3158
|
+
fields
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
return out.sort((a, b) => b.count - a.count);
|
|
3162
|
+
}
|
|
2759
3163
|
// src/schema/prisma.ts
|
|
2760
3164
|
var PRISMA_TYPE = {
|
|
2761
3165
|
uint: { type: "Decimal", db: "@db.Numeric" },
|
|
@@ -3602,6 +4006,7 @@ export {
|
|
|
3602
4006
|
renderDeployPlan,
|
|
3603
4007
|
reindexSubgraph,
|
|
3604
4008
|
pgSchemaName,
|
|
4009
|
+
inferPrintTopics,
|
|
3605
4010
|
hasBreakingChanges,
|
|
3606
4011
|
generateSubgraphSQL,
|
|
3607
4012
|
generatePrismaSchema,
|
|
@@ -3612,10 +4017,11 @@ export {
|
|
|
3612
4017
|
deploySchema,
|
|
3613
4018
|
defineSubgraph,
|
|
3614
4019
|
canSparseScan,
|
|
4020
|
+
camelizeDataKey,
|
|
3615
4021
|
backfillSubgraph,
|
|
3616
4022
|
INDEX_CODEGEN_TABLES,
|
|
3617
4023
|
ByoBreakingChangeError
|
|
3618
4024
|
};
|
|
3619
4025
|
|
|
3620
|
-
//# debugId=
|
|
4026
|
+
//# debugId=9ABB4400810C6A0464756E2164756E21
|
|
3621
4027
|
//# sourceMappingURL=index.js.map
|