@secondlayer/subgraphs 0.6.0 → 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.
@@ -2,12 +2,12 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/validate.ts", "../src/schema/utils.ts", "../src/schema/generator.ts", "../src/schema/deployer.ts"],
4
4
  "sourcesContent": [
5
- "import { z } from \"zod/v4\";\nimport type { ColumnType, SubgraphColumn, SubgraphTable, SubgraphSource, SubgraphDefinition } from \"./types.ts\";\n\nexport const SubgraphNameSchema: z.ZodType<string> = z\n .string()\n .min(1)\n .max(63)\n .regex(\n /^[a-z][a-z0-9-]*$/,\n \"Must start with lowercase letter, contain only lowercase alphanumeric and hyphens\",\n );\n\nexport const ColumnTypeSchema: z.ZodType<ColumnType> = z.enum([\n \"text\",\n \"uint\",\n \"int\",\n \"principal\",\n \"boolean\",\n \"timestamp\",\n \"jsonb\",\n]);\n\nexport const SubgraphColumnSchema: z.ZodType<SubgraphColumn> = z.object({\n type: ColumnTypeSchema,\n nullable: z.boolean().optional(),\n indexed: z.boolean().optional(),\n search: z.boolean().optional(),\n default: z.union([z.string(), z.number(), z.boolean()]).optional(),\n}) as z.ZodType<SubgraphColumn>;\n\nexport const SubgraphTableSchema: z.ZodType<SubgraphTable> = z.object({\n columns: z\n .record(z.string(), SubgraphColumnSchema)\n .refine((c) => Object.keys(c).length > 0, \"Table must have at least one column\"),\n indexes: z.array(z.array(z.string())).optional(),\n uniqueKeys: z.array(z.array(z.string())).optional(),\n}) as z.ZodType<SubgraphTable>;\n\nexport const SubgraphSchemaSchema: z.ZodType<Record<string, SubgraphTable>> = z\n .record(z.string(), SubgraphTableSchema)\n .refine((s) => Object.keys(s).length > 0, \"Schema must have at least one table\") as z.ZodType<Record<string, SubgraphTable>>;\n\nexport const SubgraphSourceSchema: z.ZodType<SubgraphSource> = z\n .object({\n contract: z.string().optional(),\n event: z.string().optional(),\n function: z.string().optional(),\n type: z.string().optional(),\n minAmount: z.bigint().optional(),\n })\n .refine(\n (s) => s.contract || s.type,\n \"Source must specify at least 'contract' or 'type'\",\n ) as z.ZodType<SubgraphSource>;\n\nexport const SubgraphDefinitionSchema: z.ZodType<SubgraphDefinition> = z.object({\n name: SubgraphNameSchema,\n version: z.string().optional(),\n description: z.string().optional(),\n sources: z.array(SubgraphSourceSchema).min(1, \"Must have at least one source\"),\n schema: SubgraphSchemaSchema,\n handlers: z.record(z.string(), z.any()),\n}) as unknown as z.ZodType<SubgraphDefinition>;\n\n/**\n * Validates a subgraph definition, returning the parsed result or throwing on failure.\n */\nexport function validateSubgraphDefinition(def: unknown): SubgraphDefinition {\n return SubgraphDefinitionSchema.parse(def);\n}\n",
5
+ "import { z } from \"zod/v4\";\nimport type {\n\tColumnType,\n\tSubgraphColumn,\n\tSubgraphDefinition,\n\tSubgraphSource,\n\tSubgraphTable,\n} from \"./types.ts\";\n\nexport const SubgraphNameSchema: z.ZodType<string> = z\n\t.string()\n\t.min(1)\n\t.max(63)\n\t.regex(\n\t\t/^[a-z][a-z0-9-]*$/,\n\t\t\"Must start with lowercase letter, contain only lowercase alphanumeric and hyphens\",\n\t);\n\nexport const ColumnTypeSchema: z.ZodType<ColumnType> = z.enum([\n\t\"text\",\n\t\"uint\",\n\t\"int\",\n\t\"principal\",\n\t\"boolean\",\n\t\"timestamp\",\n\t\"jsonb\",\n]);\n\nexport const SubgraphColumnSchema: z.ZodType<SubgraphColumn> = z.object({\n\ttype: ColumnTypeSchema,\n\tnullable: z.boolean().optional(),\n\tindexed: z.boolean().optional(),\n\tsearch: z.boolean().optional(),\n\tdefault: z.union([z.string(), z.number(), z.boolean()]).optional(),\n}) as z.ZodType<SubgraphColumn>;\n\nexport const SubgraphTableSchema: z.ZodType<SubgraphTable> = z.object({\n\tcolumns: z\n\t\t.record(z.string(), SubgraphColumnSchema)\n\t\t.refine(\n\t\t\t(c) => Object.keys(c).length > 0,\n\t\t\t\"Table must have at least one column\",\n\t\t),\n\tindexes: z.array(z.array(z.string())).optional(),\n\tuniqueKeys: z.array(z.array(z.string())).optional(),\n}) as z.ZodType<SubgraphTable>;\n\nexport const SubgraphSchemaSchema: z.ZodType<Record<string, SubgraphTable>> = z\n\t.record(z.string(), SubgraphTableSchema)\n\t.refine(\n\t\t(s) => Object.keys(s).length > 0,\n\t\t\"Schema must have at least one table\",\n\t) as z.ZodType<Record<string, SubgraphTable>>;\n\nexport const SubgraphSourceSchema: z.ZodType<SubgraphSource> = z\n\t.object({\n\t\tcontract: z.string().optional(),\n\t\tevent: z.string().optional(),\n\t\tfunction: z.string().optional(),\n\t\ttype: z.string().optional(),\n\t\tminAmount: z.bigint().optional(),\n\t})\n\t.refine(\n\t\t(s) => s.contract || s.type,\n\t\t\"Source must specify at least 'contract' or 'type'\",\n\t) as z.ZodType<SubgraphSource>;\n\nexport const SubgraphDefinitionSchema: z.ZodType<SubgraphDefinition> = z.object(\n\t{\n\t\tname: SubgraphNameSchema,\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.array(SubgraphSourceSchema)\n\t\t\t.min(1, \"Must have at least one source\"),\n\t\tschema: SubgraphSchemaSchema,\n\t\thandlers: z.record(z.string(), z.any()),\n\t},\n) as unknown as z.ZodType<SubgraphDefinition>;\n\n/**\n * Validates a subgraph definition, returning the parsed result or throwing on failure.\n */\nexport function validateSubgraphDefinition(def: unknown): SubgraphDefinition {\n\treturn SubgraphDefinitionSchema.parse(def);\n}\n",
6
6
  "// Re-export canonical pgSchemaName from shared\nexport { pgSchemaName } from \"@secondlayer/shared/db/queries/subgraphs\";\n",
7
- "import type { ColumnType, SubgraphDefinition } from \"../types.ts\";\nimport { pgSchemaName } from \"./utils.ts\";\n\nexport const TYPE_MAP: Record<ColumnType, string> = {\n text: \"TEXT\",\n uint: \"NUMERIC\",\n int: \"NUMERIC\",\n principal: \"TEXT\",\n boolean: \"BOOLEAN\",\n timestamp: \"TIMESTAMPTZ\",\n jsonb: \"JSONB\",\n};\n\nexport interface GeneratedSQL {\n statements: string[];\n hash: string;\n}\n\nfunction escapeLiteralDefault(value: unknown): string {\n if (value === null || value === undefined) return \"NULL\";\n if (typeof value === \"number\" || typeof value === \"bigint\") return String(value);\n if (typeof value === \"boolean\") return value ? \"TRUE\" : \"FALSE\";\n return `'${String(value).replace(/'/g, \"''\")}'`;\n}\n\n/**\n * Generates PostgreSQL DDL statements for a subgraph definition.\n * Creates a dedicated schema `subgraph_<name>` with one table per schema entry,\n * each with auto-columns and indexes.\n */\nexport function generateSubgraphSQL(def: SubgraphDefinition, schemaNameOverride?: string): GeneratedSQL {\n const schemaName = schemaNameOverride ?? pgSchemaName(def.name);\n const statements: string[] = [];\n\n // Check if any column uses search (trigram)\n const needsTrgm = Object.values(def.schema).some((table) =>\n Object.values(table.columns).some((col) => col.search),\n );\n\n if (needsTrgm) {\n statements.push(`CREATE EXTENSION IF NOT EXISTS pg_trgm`);\n }\n\n // Schema namespace\n statements.push(`CREATE SCHEMA IF NOT EXISTS ${schemaName}`);\n\n // One table per schema entry\n for (const [tableName, tableDef] of Object.entries(def.schema)) {\n const qualifiedName = `${schemaName}.${tableName}`;\n\n // Auto-columns + user columns\n const columnDefs: string[] = [\n `_id BIGSERIAL PRIMARY KEY`,\n `_block_height BIGINT NOT NULL`,\n `_tx_id TEXT NOT NULL`,\n `_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`,\n ];\n\n for (const [colName, col] of Object.entries(tableDef.columns)) {\n const sqlType = TYPE_MAP[col.type];\n const nullable = col.nullable ? \"\" : \" NOT NULL\";\n let colDef = `${colName} ${sqlType}${nullable}`;\n if (col.default !== undefined) {\n colDef += ` DEFAULT ${escapeLiteralDefault(col.default)}`;\n }\n columnDefs.push(colDef);\n }\n\n statements.push(\n `CREATE TABLE IF NOT EXISTS ${qualifiedName} (\\n ${columnDefs.join(\",\\n \")}\\n)`,\n );\n\n // Auto-indexes on meta columns\n statements.push(\n `CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`,\n );\n statements.push(\n `CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`,\n );\n\n // Single-column indexes\n for (const [colName, col] of Object.entries(tableDef.columns)) {\n if (col.indexed) {\n statements.push(\n `CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`,\n );\n }\n }\n\n // Trigram GIN indexes for search columns\n for (const [colName, col] of Object.entries(tableDef.columns)) {\n if (col.search) {\n statements.push(\n `CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`,\n );\n }\n }\n\n // Composite indexes\n if (tableDef.indexes) {\n for (let i = 0; i < tableDef.indexes.length; i++) {\n const cols = tableDef.indexes[i]!;\n const idxName = `idx_${schemaName}_${tableName}_composite_${i}`;\n statements.push(\n `CREATE INDEX IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.join(\", \")})`,\n );\n }\n }\n\n // Unique constraints (required for upsert ON CONFLICT)\n if (tableDef.uniqueKeys) {\n for (let i = 0; i < tableDef.uniqueKeys.length; i++) {\n const cols = tableDef.uniqueKeys[i]!;\n const constraintName = `uq_${schemaName}_${tableName}_${cols.join(\"_\")}`;\n statements.push(\n `ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.join(\", \")})`,\n );\n }\n }\n }\n\n // Hash based on schema structure (excludes handler)\n const hashInput = JSON.stringify({\n name: def.name,\n version: def.version,\n schema: def.schema,\n sources: def.sources,\n }, (_key, value) => typeof value === \"bigint\" ? value.toString() : value);\n const hash = String(Bun.hash(hashInput));\n\n return { statements, hash };\n}\n",
8
- "import { sql, type Kysely } from \"kysely\";\nimport type { Database } from \"@secondlayer/shared/db\";\nimport { validateSubgraphDefinition } from \"../validate.ts\";\nimport { generateSubgraphSQL, TYPE_MAP } from \"./generator.ts\";\nimport { pgSchemaName } from \"./utils.ts\";\nimport type { SubgraphDefinition, SubgraphSchema } from \"../types.ts\";\n\ntype AnyDb = Kysely<Database>;\n\n/** Deep-clone an object, converting BigInts to strings for JSON serialization. */\nfunction toJsonSafe(obj: unknown): unknown {\n return JSON.parse(JSON.stringify(obj, (_key, value) =>\n typeof value === \"bigint\" ? value.toString() : value\n ));\n}\n\nexport interface TableDiff {\n /** Tables added to the schema */\n addedTables: string[];\n /** Tables removed from the schema */\n removedTables: string[];\n /** Per-table column diffs (only for tables present in both) */\n tables: Record<string, ColumnDiff>;\n}\n\nexport interface ColumnDiff {\n added: string[];\n removed: string[];\n changed: string[];\n}\n\n/**\n * Compare two multi-table subgraph schemas and return differences.\n */\nexport function diffSchema(existing: SubgraphSchema, incoming: SubgraphSchema): TableDiff {\n const existingTables = new Set(Object.keys(existing));\n const incomingTables = new Set(Object.keys(incoming));\n\n const addedTables = [...incomingTables].filter((t) => !existingTables.has(t));\n const removedTables = [...existingTables].filter((t) => !incomingTables.has(t));\n\n const tables: Record<string, ColumnDiff> = {};\n for (const tableName of incomingTables) {\n if (!existingTables.has(tableName)) continue;\n const existingCols = existing[tableName]!.columns;\n const incomingCols = incoming[tableName]!.columns;\n\n const existingKeys = new Set(Object.keys(existingCols));\n const incomingKeys = new Set(Object.keys(incomingCols));\n\n tables[tableName] = {\n added: [...incomingKeys].filter((k) => !existingKeys.has(k)),\n removed: [...existingKeys].filter((k) => !incomingKeys.has(k)),\n changed: [...incomingKeys].filter((k) => {\n if (!existingKeys.has(k)) return false;\n return JSON.stringify(existingCols[k]) !== JSON.stringify(incomingCols[k]);\n }),\n };\n }\n\n return { addedTables, removedTables, tables };\n}\n\n/**\n * Returns true if the diff contains any breaking changes\n * (removed tables, removed columns, or changed column types).\n */\nfunction hasBreakingChanges(diff: TableDiff): { breaking: boolean; reasons: string[] } {\n const reasons: string[] = [];\n if (diff.removedTables.length > 0) {\n reasons.push(`removed tables: [${diff.removedTables.join(\", \")}]`);\n }\n for (const [table, colDiff] of Object.entries(diff.tables)) {\n if (colDiff.removed.length > 0) {\n reasons.push(`${table}: removed columns [${colDiff.removed.join(\", \")}]`);\n }\n if (colDiff.changed.length > 0) {\n reasons.push(`${table}: changed columns [${colDiff.changed.join(\", \")}]`);\n }\n }\n return { breaking: reasons.length > 0, reasons };\n}\n\n/**\n * Deploy a subgraph schema to the database.\n * - New subgraph → CREATE SCHEMA + tables + register\n * - Same hash → no-op\n * - Additive change → ALTER TABLE ADD COLUMN / CREATE TABLE for new tables\n * - Breaking change → throws error\n */\nexport async function deploySchema(\n db: AnyDb,\n def: SubgraphDefinition,\n handlerPath: string,\n opts?: { forceReindex?: boolean; apiKeyId?: string; schemaName?: string },\n): Promise<{ action: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\"; subgraphId: string }> {\n validateSubgraphDefinition(def);\n\n const { statements, hash } = generateSubgraphSQL(def, opts?.schemaName);\n const { getSubgraph, registerSubgraph } = await import(\"@secondlayer/shared/db/queries/subgraphs\");\n\n const existing = await getSubgraph(db, def.name, opts?.apiKeyId);\n\n const schemaName = opts?.schemaName ?? pgSchemaName(def.name);\n const regData = {\n name: def.name,\n version: def.version || \"1.0.0\",\n definition: toJsonSafe({ name: def.name, version: def.version, description: def.description, sources: def.sources, schema: def.schema }) as Record<string, unknown>,\n schemaHash: hash,\n handlerPath,\n apiKeyId: opts?.apiKeyId,\n schemaName,\n };\n\n if (existing) {\n if (existing.schema_hash === hash && !opts?.forceReindex) {\n // Update handler path in case file moved\n const { updateSubgraphHandlerPath } = await import(\"@secondlayer/shared/db/queries/subgraphs\");\n await updateSubgraphHandlerPath(db, def.name, handlerPath);\n return { action: \"unchanged\", subgraphId: existing.id };\n }\n\n if (existing.schema_hash === hash && opts?.forceReindex) {\n // Same schema but force reindex requested — drop and recreate\n await sql.raw(`DROP SCHEMA IF EXISTS \"${schemaName}\" CASCADE`).execute(db);\n for (const stmt of statements) {\n await sql.raw(stmt).execute(db);\n }\n const sg = await registerSubgraph(db, regData);\n return { action: \"reindexed\", subgraphId: sg.id };\n }\n\n if (existing.definition.schema) {\n const diff = diffSchema(existing.definition.schema as SubgraphSchema, def.schema);\n const { breaking, reasons } = hasBreakingChanges(diff);\n\n if (breaking) {\n if (!opts?.forceReindex) {\n throw new Error(\n `Breaking schema change detected (${reasons.join(\"; \")}). ` +\n `Use --reindex to force rebuild, or delete the subgraph first.`,\n );\n }\n\n // Force reindex: drop schema, recreate, register, caller triggers reindex\n await sql.raw(`DROP SCHEMA IF EXISTS \"${schemaName}\" CASCADE`).execute(db);\n for (const stmt of statements) {\n await sql.raw(stmt).execute(db);\n }\n const sg = await registerSubgraph(db, regData);\n return { action: \"reindexed\", subgraphId: sg.id };\n }\n\n // Create new tables\n for (const tableName of diff.addedTables) {\n const tableDef = def.schema[tableName]!;\n const qualifiedName = `${schemaName}.${tableName}`;\n const colDefs = [\n `_id BIGSERIAL PRIMARY KEY`,\n `_block_height BIGINT NOT NULL`,\n `_tx_id TEXT NOT NULL`,\n `_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`,\n ];\n for (const [colName, col] of Object.entries(tableDef.columns)) {\n const nullable = col.nullable ? \"\" : \" NOT NULL\";\n colDefs.push(`${colName} ${TYPE_MAP[col.type]!}${nullable}`);\n }\n await sql.raw(\n `CREATE TABLE IF NOT EXISTS ${qualifiedName} (\\n ${colDefs.join(\",\\n \")}\\n)`,\n ).execute(db);\n await sql.raw(\n `CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`,\n ).execute(db);\n await sql.raw(\n `CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`,\n ).execute(db);\n for (const [colName, col] of Object.entries(tableDef.columns)) {\n if (col.indexed) {\n await sql.raw(\n `CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`,\n ).execute(db);\n }\n if (col.search) {\n await sql.raw(\n `CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`,\n ).execute(db);\n }\n }\n }\n\n // Add columns to existing tables\n for (const [tableName, colDiff] of Object.entries(diff.tables)) {\n if (colDiff.added.length === 0) continue;\n const qualifiedName = `${schemaName}.${tableName}`;\n const tableDef = def.schema[tableName]!;\n for (const colName of colDiff.added) {\n const col = tableDef.columns[colName]!;\n const sqlType = TYPE_MAP[col.type]!;\n const nullable = col.nullable ? \"\" : \" NOT NULL DEFAULT \" + getDefault(col.type);\n await sql.raw(\n `ALTER TABLE ${qualifiedName} ADD COLUMN ${colName} ${sqlType}${nullable}`,\n ).execute(db);\n if (col.indexed) {\n await sql.raw(\n `CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`,\n ).execute(db);\n }\n if (col.search) {\n await sql.raw(\n `CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`,\n ).execute(db);\n }\n }\n }\n }\n\n const sg = await registerSubgraph(db, regData);\n return { action: \"updated\", subgraphId: sg.id };\n }\n\n // New subgraph — execute all DDL\n for (const stmt of statements) {\n await sql.raw(stmt).execute(db);\n }\n\n const sg = await registerSubgraph(db, regData);\n return { action: \"created\", subgraphId: sg.id };\n}\n\nfunction getDefault(type: string): string {\n switch (type) {\n case \"text\":\n case \"principal\":\n return \"''\";\n case \"uint\":\n case \"int\":\n return \"0\";\n case \"boolean\":\n return \"false\";\n case \"timestamp\":\n return \"NOW()\";\n case \"jsonb\":\n return \"'{}'\";\n default:\n return \"''\";\n }\n}\n"
7
+ "import type { ColumnType, SubgraphDefinition } from \"../types.ts\";\nimport { pgSchemaName } from \"./utils.ts\";\n\nexport const TYPE_MAP: Record<ColumnType, string> = {\n\ttext: \"TEXT\",\n\tuint: \"NUMERIC\",\n\tint: \"NUMERIC\",\n\tprincipal: \"TEXT\",\n\tboolean: \"BOOLEAN\",\n\ttimestamp: \"TIMESTAMPTZ\",\n\tjsonb: \"JSONB\",\n};\n\nexport interface GeneratedSQL {\n\tstatements: string[];\n\thash: string;\n}\n\nfunction escapeLiteralDefault(value: unknown): string {\n\tif (value === null || value === undefined) return \"NULL\";\n\tif (typeof value === \"number\" || typeof value === \"bigint\")\n\t\treturn String(value);\n\tif (typeof value === \"boolean\") return value ? \"TRUE\" : \"FALSE\";\n\treturn `'${String(value).replace(/'/g, \"''\")}'`;\n}\n\n/**\n * Generates PostgreSQL DDL statements for a subgraph definition.\n * Creates a dedicated schema `subgraph_<name>` with one table per schema entry,\n * each with auto-columns and indexes.\n */\nexport function generateSubgraphSQL(\n\tdef: SubgraphDefinition,\n\tschemaNameOverride?: string,\n): GeneratedSQL {\n\tconst schemaName = schemaNameOverride ?? pgSchemaName(def.name);\n\tconst statements: string[] = [];\n\n\t// Check if any column uses search (trigram)\n\tconst needsTrgm = Object.values(def.schema).some((table) =>\n\t\tObject.values(table.columns).some((col) => col.search),\n\t);\n\n\tif (needsTrgm) {\n\t\tstatements.push(`CREATE EXTENSION IF NOT EXISTS pg_trgm`);\n\t}\n\n\t// Schema namespace\n\tstatements.push(`CREATE SCHEMA IF NOT EXISTS ${schemaName}`);\n\n\t// One table per schema entry\n\tfor (const [tableName, tableDef] of Object.entries(def.schema)) {\n\t\tconst qualifiedName = `${schemaName}.${tableName}`;\n\n\t\t// Auto-columns + user columns\n\t\tconst columnDefs: string[] = [\n\t\t\t`_id BIGSERIAL PRIMARY KEY`,\n\t\t\t`_block_height BIGINT NOT NULL`,\n\t\t\t`_tx_id TEXT NOT NULL`,\n\t\t\t`_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`,\n\t\t];\n\n\t\tfor (const [colName, col] of Object.entries(tableDef.columns)) {\n\t\t\tconst sqlType = TYPE_MAP[col.type];\n\t\t\tconst nullable = col.nullable ? \"\" : \" NOT NULL\";\n\t\t\tlet colDef = `${colName} ${sqlType}${nullable}`;\n\t\t\tif (col.default !== undefined) {\n\t\t\t\tcolDef += ` DEFAULT ${escapeLiteralDefault(col.default)}`;\n\t\t\t}\n\t\t\tcolumnDefs.push(colDef);\n\t\t}\n\n\t\tstatements.push(\n\t\t\t`CREATE TABLE IF NOT EXISTS ${qualifiedName} (\\n ${columnDefs.join(\",\\n \")}\\n)`,\n\t\t);\n\n\t\t// Auto-indexes on meta columns\n\t\tstatements.push(\n\t\t\t`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`,\n\t\t);\n\t\tstatements.push(\n\t\t\t`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`,\n\t\t);\n\n\t\t// Single-column indexes\n\t\tfor (const [colName, col] of Object.entries(tableDef.columns)) {\n\t\t\tif (col.indexed) {\n\t\t\t\tstatements.push(\n\t\t\t\t\t`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Trigram GIN indexes for search columns\n\t\tfor (const [colName, col] of Object.entries(tableDef.columns)) {\n\t\t\tif (col.search) {\n\t\t\t\tstatements.push(\n\t\t\t\t\t`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Composite indexes\n\t\tif (tableDef.indexes) {\n\t\t\tfor (let i = 0; i < tableDef.indexes.length; i++) {\n\t\t\t\tconst cols = tableDef.indexes[i]!;\n\t\t\t\tconst idxName = `idx_${schemaName}_${tableName}_composite_${i}`;\n\t\t\t\tstatements.push(\n\t\t\t\t\t`CREATE INDEX IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.join(\", \")})`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Unique constraints (required for upsert ON CONFLICT)\n\t\tif (tableDef.uniqueKeys) {\n\t\t\tfor (let i = 0; i < tableDef.uniqueKeys.length; i++) {\n\t\t\t\tconst cols = tableDef.uniqueKeys[i]!;\n\t\t\t\tconst constraintName = `uq_${schemaName}_${tableName}_${cols.join(\"_\")}`;\n\t\t\t\tstatements.push(\n\t\t\t\t\t`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.join(\", \")})`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Hash based on schema structure (excludes handler)\n\tconst hashInput = JSON.stringify(\n\t\t{\n\t\t\tname: def.name,\n\t\t\tversion: def.version,\n\t\t\tschema: def.schema,\n\t\t\tsources: def.sources,\n\t\t},\n\t\t(_key, value) => (typeof value === \"bigint\" ? value.toString() : value),\n\t);\n\tconst hash = String(Bun.hash(hashInput));\n\n\treturn { statements, hash };\n}\n",
8
+ "import type { Database } from \"@secondlayer/shared/db\";\nimport { type Kysely, sql } from \"kysely\";\nimport type { SubgraphDefinition, SubgraphSchema } from \"../types.ts\";\nimport { validateSubgraphDefinition } from \"../validate.ts\";\nimport { TYPE_MAP, generateSubgraphSQL } from \"./generator.ts\";\nimport { pgSchemaName } from \"./utils.ts\";\n\ntype AnyDb = Kysely<Database>;\n\n/** Deep-clone an object, converting BigInts to strings for JSON serialization. */\nfunction toJsonSafe(obj: unknown): unknown {\n\treturn JSON.parse(\n\t\tJSON.stringify(obj, (_key, value) =>\n\t\t\ttypeof value === \"bigint\" ? value.toString() : value,\n\t\t),\n\t);\n}\n\nexport interface TableDiff {\n\t/** Tables added to the schema */\n\taddedTables: string[];\n\t/** Tables removed from the schema */\n\tremovedTables: string[];\n\t/** Per-table column diffs (only for tables present in both) */\n\ttables: Record<string, ColumnDiff>;\n}\n\nexport interface ColumnDiff {\n\tadded: string[];\n\tremoved: string[];\n\tchanged: string[];\n}\n\n/**\n * Compare two multi-table subgraph schemas and return differences.\n */\nexport function diffSchema(\n\texisting: SubgraphSchema,\n\tincoming: SubgraphSchema,\n): TableDiff {\n\tconst existingTables = new Set(Object.keys(existing));\n\tconst incomingTables = new Set(Object.keys(incoming));\n\n\tconst addedTables = [...incomingTables].filter((t) => !existingTables.has(t));\n\tconst removedTables = [...existingTables].filter(\n\t\t(t) => !incomingTables.has(t),\n\t);\n\n\tconst tables: Record<string, ColumnDiff> = {};\n\tfor (const tableName of incomingTables) {\n\t\tif (!existingTables.has(tableName)) continue;\n\t\tconst existingCols = existing[tableName]!.columns;\n\t\tconst incomingCols = incoming[tableName]!.columns;\n\n\t\tconst existingKeys = new Set(Object.keys(existingCols));\n\t\tconst incomingKeys = new Set(Object.keys(incomingCols));\n\n\t\ttables[tableName] = {\n\t\t\tadded: [...incomingKeys].filter((k) => !existingKeys.has(k)),\n\t\t\tremoved: [...existingKeys].filter((k) => !incomingKeys.has(k)),\n\t\t\tchanged: [...incomingKeys].filter((k) => {\n\t\t\t\tif (!existingKeys.has(k)) return false;\n\t\t\t\treturn (\n\t\t\t\t\tJSON.stringify(existingCols[k]) !== JSON.stringify(incomingCols[k])\n\t\t\t\t);\n\t\t\t}),\n\t\t};\n\t}\n\n\treturn { addedTables, removedTables, tables };\n}\n\n/**\n * Returns true if the diff contains any breaking changes\n * (removed tables, removed columns, or changed column types).\n */\nfunction hasBreakingChanges(diff: TableDiff): {\n\tbreaking: boolean;\n\treasons: string[];\n} {\n\tconst reasons: string[] = [];\n\tif (diff.removedTables.length > 0) {\n\t\treasons.push(`removed tables: [${diff.removedTables.join(\", \")}]`);\n\t}\n\tfor (const [table, colDiff] of Object.entries(diff.tables)) {\n\t\tif (colDiff.removed.length > 0) {\n\t\t\treasons.push(`${table}: removed columns [${colDiff.removed.join(\", \")}]`);\n\t\t}\n\t\tif (colDiff.changed.length > 0) {\n\t\t\treasons.push(`${table}: changed columns [${colDiff.changed.join(\", \")}]`);\n\t\t}\n\t}\n\treturn { breaking: reasons.length > 0, reasons };\n}\n\n/**\n * Deploy a subgraph schema to the database.\n * - New subgraph → CREATE SCHEMA + tables + register\n * - Same hash → no-op\n * - Additive change → ALTER TABLE ADD COLUMN / CREATE TABLE for new tables\n * - Breaking change → throws error\n */\nexport async function deploySchema(\n\tdb: AnyDb,\n\tdef: SubgraphDefinition,\n\thandlerPath: string,\n\topts?: { forceReindex?: boolean; apiKeyId?: string; schemaName?: string },\n): Promise<{\n\taction: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n}> {\n\tvalidateSubgraphDefinition(def);\n\n\tconst { statements, hash } = generateSubgraphSQL(def, opts?.schemaName);\n\tconst { getSubgraph, registerSubgraph } = await import(\n\t\t\"@secondlayer/shared/db/queries/subgraphs\"\n\t);\n\n\tconst existing = await getSubgraph(db, def.name, opts?.apiKeyId);\n\n\tconst schemaName = opts?.schemaName ?? pgSchemaName(def.name);\n\tconst regData = {\n\t\tname: def.name,\n\t\tversion: def.version || \"1.0.0\",\n\t\tdefinition: toJsonSafe({\n\t\t\tname: def.name,\n\t\t\tversion: def.version,\n\t\t\tdescription: def.description,\n\t\t\tsources: def.sources,\n\t\t\tschema: def.schema,\n\t\t}) as Record<string, unknown>,\n\t\tschemaHash: hash,\n\t\thandlerPath,\n\t\tapiKeyId: opts?.apiKeyId,\n\t\tschemaName,\n\t};\n\n\tif (existing) {\n\t\tif (existing.schema_hash === hash && !opts?.forceReindex) {\n\t\t\t// Update handler path in case file moved\n\t\t\tconst { updateSubgraphHandlerPath } = await import(\n\t\t\t\t\"@secondlayer/shared/db/queries/subgraphs\"\n\t\t\t);\n\t\t\tawait updateSubgraphHandlerPath(db, def.name, handlerPath);\n\t\t\treturn { action: \"unchanged\", subgraphId: existing.id };\n\t\t}\n\n\t\tif (existing.schema_hash === hash && opts?.forceReindex) {\n\t\t\t// Same schema but force reindex requested — drop and recreate\n\t\t\tawait sql\n\t\t\t\t.raw(`DROP SCHEMA IF EXISTS \"${schemaName}\" CASCADE`)\n\t\t\t\t.execute(db);\n\t\t\tfor (const stmt of statements) {\n\t\t\t\tawait sql.raw(stmt).execute(db);\n\t\t\t}\n\t\t\tconst sg = await registerSubgraph(db, regData);\n\t\t\treturn { action: \"reindexed\", subgraphId: sg.id };\n\t\t}\n\n\t\tif (existing.definition.schema) {\n\t\t\tconst diff = diffSchema(\n\t\t\t\texisting.definition.schema as SubgraphSchema,\n\t\t\t\tdef.schema,\n\t\t\t);\n\t\t\tconst { breaking, reasons } = hasBreakingChanges(diff);\n\n\t\t\tif (breaking) {\n\t\t\t\tif (!opts?.forceReindex) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Breaking schema change detected (${reasons.join(\"; \")}). ` +\n\t\t\t\t\t\t\t`Use --reindex to force rebuild, or delete the subgraph first.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Force reindex: drop schema, recreate, register, caller triggers reindex\n\t\t\t\tawait sql\n\t\t\t\t\t.raw(`DROP SCHEMA IF EXISTS \"${schemaName}\" CASCADE`)\n\t\t\t\t\t.execute(db);\n\t\t\t\tfor (const stmt of statements) {\n\t\t\t\t\tawait sql.raw(stmt).execute(db);\n\t\t\t\t}\n\t\t\t\tconst sg = await registerSubgraph(db, regData);\n\t\t\t\treturn { action: \"reindexed\", subgraphId: sg.id };\n\t\t\t}\n\n\t\t\t// Create new tables\n\t\t\tfor (const tableName of diff.addedTables) {\n\t\t\t\tconst tableDef = def.schema[tableName]!;\n\t\t\t\tconst qualifiedName = `${schemaName}.${tableName}`;\n\t\t\t\tconst colDefs = [\n\t\t\t\t\t`_id BIGSERIAL PRIMARY KEY`,\n\t\t\t\t\t`_block_height BIGINT NOT NULL`,\n\t\t\t\t\t`_tx_id TEXT NOT NULL`,\n\t\t\t\t\t`_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()`,\n\t\t\t\t];\n\t\t\t\tfor (const [colName, col] of Object.entries(tableDef.columns)) {\n\t\t\t\t\tconst nullable = col.nullable ? \"\" : \" NOT NULL\";\n\t\t\t\t\tcolDefs.push(`${colName} ${TYPE_MAP[col.type]!}${nullable}`);\n\t\t\t\t}\n\t\t\t\tawait sql\n\t\t\t\t\t.raw(\n\t\t\t\t\t\t`CREATE TABLE IF NOT EXISTS ${qualifiedName} (\\n ${colDefs.join(\",\\n \")}\\n)`,\n\t\t\t\t\t)\n\t\t\t\t\t.execute(db);\n\t\t\t\tawait sql\n\t\t\t\t\t.raw(\n\t\t\t\t\t\t`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`,\n\t\t\t\t\t)\n\t\t\t\t\t.execute(db);\n\t\t\t\tawait sql\n\t\t\t\t\t.raw(\n\t\t\t\t\t\t`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`,\n\t\t\t\t\t)\n\t\t\t\t\t.execute(db);\n\t\t\t\tfor (const [colName, col] of Object.entries(tableDef.columns)) {\n\t\t\t\t\tif (col.indexed) {\n\t\t\t\t\t\tawait sql\n\t\t\t\t\t\t\t.raw(\n\t\t\t\t\t\t\t\t`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.execute(db);\n\t\t\t\t\t}\n\t\t\t\t\tif (col.search) {\n\t\t\t\t\t\tawait sql\n\t\t\t\t\t\t\t.raw(\n\t\t\t\t\t\t\t\t`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.execute(db);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add columns to existing tables\n\t\t\tfor (const [tableName, colDiff] of Object.entries(diff.tables)) {\n\t\t\t\tif (colDiff.added.length === 0) continue;\n\t\t\t\tconst qualifiedName = `${schemaName}.${tableName}`;\n\t\t\t\tconst tableDef = def.schema[tableName]!;\n\t\t\t\tfor (const colName of colDiff.added) {\n\t\t\t\t\tconst col = tableDef.columns[colName]!;\n\t\t\t\t\tconst sqlType = TYPE_MAP[col.type]!;\n\t\t\t\t\tconst nullable = col.nullable\n\t\t\t\t\t\t? \"\"\n\t\t\t\t\t\t: \" NOT NULL DEFAULT \" + getDefault(col.type);\n\t\t\t\t\tawait sql\n\t\t\t\t\t\t.raw(\n\t\t\t\t\t\t\t`ALTER TABLE ${qualifiedName} ADD COLUMN ${colName} ${sqlType}${nullable}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.execute(db);\n\t\t\t\t\tif (col.indexed) {\n\t\t\t\t\t\tawait sql\n\t\t\t\t\t\t\t.raw(\n\t\t\t\t\t\t\t\t`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.execute(db);\n\t\t\t\t\t}\n\t\t\t\t\tif (col.search) {\n\t\t\t\t\t\tawait sql\n\t\t\t\t\t\t\t.raw(\n\t\t\t\t\t\t\t\t`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.execute(db);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst sg = await registerSubgraph(db, regData);\n\t\treturn { action: \"updated\", subgraphId: sg.id };\n\t}\n\n\t// New subgraph — execute all DDL\n\tfor (const stmt of statements) {\n\t\tawait sql.raw(stmt).execute(db);\n\t}\n\n\tconst sg = await registerSubgraph(db, regData);\n\treturn { action: \"created\", subgraphId: sg.id };\n}\n\nfunction getDefault(type: string): string {\n\tswitch (type) {\n\t\tcase \"text\":\n\t\tcase \"principal\":\n\t\t\treturn \"''\";\n\t\tcase \"uint\":\n\t\tcase \"int\":\n\t\t\treturn \"0\";\n\t\tcase \"boolean\":\n\t\t\treturn \"false\";\n\t\tcase \"timestamp\":\n\t\t\treturn \"NOW()\";\n\t\tcase \"jsonb\":\n\t\t\treturn \"'{}'\";\n\t\tdefault:\n\t\t\treturn \"''\";\n\t}\n}\n"
9
9
  ],
10
- "mappings": "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",
11
- "debugId": "F409930E6906982D64756E2164756E21",
10
+ "mappings": "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",
11
+ "debugId": "30F66900C450EBD564756E2164756E21",
12
12
  "names": []
13
13
  }
@@ -16,8 +16,8 @@ function sourceKey(source) {
16
16
  }
17
17
 
18
18
  // src/runtime/context.ts
19
- import { sql } from "kysely";
20
19
  import { logger } from "@secondlayer/shared/logger";
20
+ import { sql } from "kysely";
21
21
  function validateColumnName(name) {
22
22
  if (!/^[a-z_][a-z0-9_]*$/i.test(name)) {
23
23
  throw new Error(`Invalid column name: ${name}`);
@@ -58,13 +58,26 @@ class SubgraphContext {
58
58
  const keyColumns = Object.keys(key);
59
59
  const hasUniqueConstraint = tableDef.uniqueKeys?.some((uk) => uk.length === keyColumns.length && uk.every((c) => keyColumns.includes(c)));
60
60
  if (hasUniqueConstraint) {
61
- this.ops.push({ kind: "insert", table, data: { ...key, ...row, _upsert_keys: keyColumns } });
61
+ this.ops.push({
62
+ kind: "insert",
63
+ table,
64
+ data: { ...key, ...row, _upsert_keys: keyColumns }
65
+ });
62
66
  } else {
63
67
  logger.warn("upsert called without matching uniqueKeys constraint, using fallback", {
64
68
  table,
65
69
  keys: keyColumns
66
70
  });
67
- this.ops.push({ kind: "insert", table, data: { ...key, ...row, _upsert_fallback_keys: keyColumns, _upsert_fallback_set: row } });
71
+ this.ops.push({
72
+ kind: "insert",
73
+ table,
74
+ data: {
75
+ ...key,
76
+ ...row,
77
+ _upsert_fallback_keys: keyColumns,
78
+ _upsert_fallback_set: row
79
+ }
80
+ });
68
81
  }
69
82
  }
70
83
  delete(table, where) {
@@ -227,95 +240,6 @@ function buildWhereClause(where) {
227
240
  return { clause: parts.join(" AND "), values: [] };
228
241
  }
229
242
 
230
- // src/runtime/source-matcher.ts
231
- function matchPattern(value, pattern) {
232
- if (!pattern.includes("*"))
233
- return value === pattern;
234
- const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
235
- return new RegExp(`^${regex}$`).test(value);
236
- }
237
- function matchSource(source, transactions, eventsByTx) {
238
- const results = [];
239
- const key = sourceKey(source);
240
- for (const tx of transactions) {
241
- if (source.type) {
242
- if (!matchPattern(tx.type, source.type))
243
- continue;
244
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
245
- let matchedEvents = txEvents;
246
- if (source.minAmount !== undefined) {
247
- const amountEvents = matchedEvents.filter((e) => {
248
- const data = e.data;
249
- const rawAmount = data?.amount;
250
- if (rawAmount === undefined)
251
- return false;
252
- const amount = BigInt(rawAmount);
253
- return amount >= source.minAmount;
254
- });
255
- if (amountEvents.length === 0)
256
- continue;
257
- matchedEvents = amountEvents;
258
- }
259
- results.push({ tx, events: matchedEvents, sourceKey: key });
260
- continue;
261
- }
262
- if (source.contract) {
263
- const txContractMatch = tx.contract_id && matchPattern(tx.contract_id, source.contract);
264
- if (source.function && tx.function_name) {
265
- if (!matchPattern(tx.function_name, source.function))
266
- continue;
267
- } else if (source.function && !tx.function_name) {
268
- continue;
269
- }
270
- const txEvents = eventsByTx.get(tx.tx_id) ?? [];
271
- let matchedEvents = txEvents;
272
- if (!txContractMatch) {
273
- matchedEvents = txEvents.filter((e) => {
274
- const data = e.data;
275
- const contractIdentifier = data?.contract_identifier;
276
- return contractIdentifier && matchPattern(contractIdentifier, source.contract);
277
- });
278
- if (matchedEvents.length === 0)
279
- continue;
280
- }
281
- if (source.event) {
282
- matchedEvents = matchedEvents.filter((e) => {
283
- if (matchPattern(e.type, source.event))
284
- return true;
285
- const data = e.data;
286
- const topic = data?.topic;
287
- return topic ? matchPattern(topic, source.event) : false;
288
- });
289
- }
290
- if (txContractMatch || matchedEvents.length > 0) {
291
- results.push({ tx, events: matchedEvents, sourceKey: key });
292
- }
293
- }
294
- }
295
- return results;
296
- }
297
- function matchSources(sources, transactions, events) {
298
- const eventsByTx = new Map;
299
- for (const event of events) {
300
- const list = eventsByTx.get(event.tx_id) ?? [];
301
- list.push(event);
302
- eventsByTx.set(event.tx_id, list);
303
- }
304
- const seen = new Set;
305
- const results = [];
306
- for (const source of sources) {
307
- const matches = matchSource(source, transactions, eventsByTx);
308
- for (const match of matches) {
309
- const dedupeKey = `${match.tx.tx_id}:${match.sourceKey}`;
310
- if (!seen.has(dedupeKey)) {
311
- seen.add(dedupeKey);
312
- results.push(match);
313
- }
314
- }
315
- }
316
- return results;
317
- }
318
-
319
243
  // src/runtime/clarity.ts
320
244
  import { cvToJSON, deserializeCV } from "@secondlayer/stacks/clarity";
321
245
  function decodeClarityValue(hex) {
@@ -348,8 +272,8 @@ function decodeFunctionArgs(args) {
348
272
  }
349
273
 
350
274
  // src/runtime/runner.ts
351
- import { logger as logger2 } from "@secondlayer/shared/logger";
352
275
  import { getErrorMessage } from "@secondlayer/shared";
276
+ import { logger as logger2 } from "@secondlayer/shared/logger";
353
277
  var DEFAULT_ERROR_THRESHOLD = 50;
354
278
  function resolveHandler(handlers, key) {
355
279
  return handlers[key] ?? handlers["*"] ?? null;
@@ -361,7 +285,11 @@ async function runHandlers(subgraph, matched, ctx, opts) {
361
285
  for (const { tx, events, sourceKey: sourceKey2 } of matched) {
362
286
  const handler = resolveHandler(subgraph.handlers, sourceKey2);
363
287
  if (!handler) {
364
- logger2.warn("No handler found for source key", { subgraph: subgraph.name, sourceKey: sourceKey2, txId: tx.tx_id });
288
+ logger2.warn("No handler found for source key", {
289
+ subgraph: subgraph.name,
290
+ sourceKey: sourceKey2,
291
+ txId: tx.tx_id
292
+ });
365
293
  continue;
366
294
  }
367
295
  ctx.setTx({
@@ -438,11 +366,103 @@ async function runHandlers(subgraph, matched, ctx, opts) {
438
366
  return { processed, errors };
439
367
  }
440
368
 
369
+ // src/runtime/source-matcher.ts
370
+ function matchPattern(value, pattern) {
371
+ if (!pattern.includes("*"))
372
+ return value === pattern;
373
+ const regex = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
374
+ return new RegExp(`^${regex}$`).test(value);
375
+ }
376
+ function matchSource(source, transactions, eventsByTx) {
377
+ const results = [];
378
+ const key = sourceKey(source);
379
+ for (const tx of transactions) {
380
+ if (source.type) {
381
+ if (!matchPattern(tx.type, source.type))
382
+ continue;
383
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
384
+ let matchedEvents = txEvents;
385
+ if (source.minAmount !== undefined) {
386
+ const amountEvents = matchedEvents.filter((e) => {
387
+ const data = e.data;
388
+ const rawAmount = data?.amount;
389
+ if (rawAmount === undefined)
390
+ return false;
391
+ const amount = BigInt(rawAmount);
392
+ return amount >= source.minAmount;
393
+ });
394
+ if (amountEvents.length === 0)
395
+ continue;
396
+ matchedEvents = amountEvents;
397
+ }
398
+ results.push({ tx, events: matchedEvents, sourceKey: key });
399
+ continue;
400
+ }
401
+ if (source.contract) {
402
+ const txContractMatch = tx.contract_id && matchPattern(tx.contract_id, source.contract);
403
+ if (source.function && tx.function_name) {
404
+ if (!matchPattern(tx.function_name, source.function))
405
+ continue;
406
+ } else if (source.function && !tx.function_name) {
407
+ continue;
408
+ }
409
+ const txEvents = eventsByTx.get(tx.tx_id) ?? [];
410
+ let matchedEvents = txEvents;
411
+ if (!txContractMatch) {
412
+ matchedEvents = txEvents.filter((e) => {
413
+ const data = e.data;
414
+ const contractIdentifier = data?.contract_identifier;
415
+ return contractIdentifier && matchPattern(contractIdentifier, source.contract);
416
+ });
417
+ if (matchedEvents.length === 0)
418
+ continue;
419
+ }
420
+ if (source.event) {
421
+ matchedEvents = matchedEvents.filter((e) => {
422
+ if (matchPattern(e.type, source.event))
423
+ return true;
424
+ const data = e.data;
425
+ const topic = data?.topic;
426
+ return topic ? matchPattern(topic, source.event) : false;
427
+ });
428
+ }
429
+ if (txContractMatch || matchedEvents.length > 0) {
430
+ results.push({ tx, events: matchedEvents, sourceKey: key });
431
+ }
432
+ }
433
+ }
434
+ return results;
435
+ }
436
+ function matchSources(sources, transactions, events) {
437
+ const eventsByTx = new Map;
438
+ for (const event of events) {
439
+ const list = eventsByTx.get(event.tx_id) ?? [];
440
+ list.push(event);
441
+ eventsByTx.set(event.tx_id, list);
442
+ }
443
+ const seen = new Set;
444
+ const results = [];
445
+ for (const source of sources) {
446
+ const matches = matchSource(source, transactions, eventsByTx);
447
+ for (const match of matches) {
448
+ const dedupeKey = `${match.tx.tx_id}:${match.sourceKey}`;
449
+ if (!seen.has(dedupeKey)) {
450
+ seen.add(dedupeKey);
451
+ results.push(match);
452
+ }
453
+ }
454
+ }
455
+ return results;
456
+ }
457
+
441
458
  // src/runtime/block-processor.ts
442
- import { sql as sql2 } from "kysely";
443
459
  import { getDb } from "@secondlayer/shared/db";
460
+ import {
461
+ recordSubgraphProcessed,
462
+ updateSubgraphStatus
463
+ } from "@secondlayer/shared/db/queries/subgraphs";
444
464
  import { logger as logger3 } from "@secondlayer/shared/logger";
445
- import { updateSubgraphStatus, recordSubgraphProcessed } from "@secondlayer/shared/db/queries/subgraphs";
465
+ import { sql as sql2 } from "kysely";
446
466
 
447
467
  // src/schema/utils.ts
448
468
  import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
@@ -467,12 +487,18 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
467
487
  } else {
468
488
  block = await db.selectFrom("blocks").selectAll().where("height", "=", blockHeight).executeTakeFirst();
469
489
  if (!block) {
470
- logger3.warn("Block not found for subgraph processing", { subgraph: subgraphName, blockHeight });
490
+ logger3.warn("Block not found for subgraph processing", {
491
+ subgraph: subgraphName,
492
+ blockHeight
493
+ });
471
494
  result.skipped = true;
472
495
  return result;
473
496
  }
474
497
  if (!block.canonical) {
475
- logger3.debug("Skipping non-canonical block", { subgraph: subgraphName, blockHeight });
498
+ logger3.debug("Skipping non-canonical block", {
499
+ subgraph: subgraphName,
500
+ blockHeight
501
+ });
476
502
  result.skipped = true;
477
503
  return result;
478
504
  }
@@ -541,7 +567,11 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
541
567
  const { rows } = await sql2.raw(`SELECT COUNT(*) as count FROM "${schemaName}"."${table}"`).execute(db);
542
568
  const count = Number(rows[0].count);
543
569
  if (count >= 1e7) {
544
- logger3.warn("Subgraph table exceeds 10M rows", { subgraph: subgraphName, table, count });
570
+ logger3.warn("Subgraph table exceeds 10M rows", {
571
+ subgraph: subgraphName,
572
+ table,
573
+ count
574
+ });
545
575
  }
546
576
  }
547
577
  } catch {}
@@ -595,7 +625,7 @@ class StatsAccumulator {
595
625
  flush_time_ms: Math.round(entry.flushTimeMs),
596
626
  max_block_time_ms: Math.round(entry.maxBlockTimeMs),
597
627
  max_handler_time_ms: Math.round(entry.maxHandlerTimeMs),
598
- avg_ops_per_block: parseFloat(avgOpsPerBlock.toFixed(2)),
628
+ avg_ops_per_block: Number.parseFloat(avgOpsPerBlock.toFixed(2)),
599
629
  is_catchup: entry.isCatchup
600
630
  }).execute();
601
631
  }
@@ -618,8 +648,11 @@ class StatsAccumulator {
618
648
 
619
649
  // src/runtime/catchup.ts
620
650
  import { getDb as getDb2 } from "@secondlayer/shared/db";
621
- import { logger as logger4 } from "@secondlayer/shared/logger";
651
+ import {
652
+ recordGapBatch
653
+ } from "@secondlayer/shared/db/queries/subgraph-gaps";
622
654
  import { getSubgraph } from "@secondlayer/shared/db/queries/subgraphs";
655
+ import { logger as logger4 } from "@secondlayer/shared/logger";
623
656
 
624
657
  // src/runtime/batch-loader.ts
625
658
  async function loadBlockRange(db, fromHeight, toHeight) {
@@ -669,6 +702,28 @@ var DEFAULT_BATCH_SIZE = 500;
669
702
  var MIN_BATCH_SIZE = 100;
670
703
  var MAX_BATCH_SIZE = 1000;
671
704
  var catchingUp = new Set;
705
+ function coalesceGaps(blocks) {
706
+ if (blocks.length === 0)
707
+ return [];
708
+ blocks.sort((a, b) => a.height - b.height);
709
+ const ranges = [];
710
+ let start = blocks[0].height;
711
+ let end = blocks[0].height;
712
+ let reason = blocks[0].reason;
713
+ for (let i = 1;i < blocks.length; i++) {
714
+ const b = blocks[i];
715
+ if (b.height === end + 1 && b.reason === reason) {
716
+ end = b.height;
717
+ } else {
718
+ ranges.push({ start, end, reason });
719
+ start = b.height;
720
+ end = b.height;
721
+ reason = b.reason;
722
+ }
723
+ }
724
+ ranges.push({ start, end, reason });
725
+ return ranges;
726
+ }
672
727
  function adjustBatchSize(current, avgEvents) {
673
728
  if (avgEvents > 50)
674
729
  return Math.max(Math.round(current * 0.5), MIN_BATCH_SIZE);
@@ -706,6 +761,14 @@ async function catchUpSubgraph(subgraph, subgraphName) {
706
761
  let currentHeight = startBlock;
707
762
  let nextBatchPromise = loadBlockRange(db, currentHeight, Math.min(currentHeight + batchSize - 1, chainTip));
708
763
  while (currentHeight <= chainTip) {
764
+ const currentRow = await getSubgraph(db, subgraphName);
765
+ if (!currentRow || currentRow.status !== "active") {
766
+ logger4.info("Subgraph status changed, stopping catch-up", {
767
+ subgraph: subgraphName,
768
+ status: currentRow?.status ?? "deleted"
769
+ });
770
+ break;
771
+ }
709
772
  const batch = await nextBatchPromise;
710
773
  const batchEnd = Math.min(currentHeight + batchSize - 1, chainTip);
711
774
  const nextStart = batchEnd + 1;
@@ -713,9 +776,11 @@ async function catchUpSubgraph(subgraph, subgraphName) {
713
776
  const nextEnd = Math.min(nextStart + batchSize - 1, chainTip);
714
777
  nextBatchPromise = loadBlockRange(db, nextStart, nextEnd);
715
778
  }
779
+ const batchFailedBlocks = [];
716
780
  for (let height = currentHeight;height <= batchEnd; height++) {
717
781
  const blockData = batch.get(height);
718
782
  if (!blockData) {
783
+ batchFailedBlocks.push({ height, reason: "block_missing" });
719
784
  processed++;
720
785
  continue;
721
786
  }
@@ -730,6 +795,7 @@ async function catchUpSubgraph(subgraph, subgraphName) {
730
795
  blockHeight: height,
731
796
  error: err instanceof Error ? err.message : String(err)
732
797
  });
798
+ batchFailedBlocks.push({ height, reason: "processing_error" });
733
799
  const { updateSubgraphStatus: updateSubgraphStatus2 } = await import("@secondlayer/shared/db/queries/subgraphs");
734
800
  await updateSubgraphStatus2(db, subgraphName, "active", height).catch(() => {});
735
801
  processed++;
@@ -752,6 +818,15 @@ async function catchUpSubgraph(subgraph, subgraphName) {
752
818
  });
753
819
  }
754
820
  }
821
+ if (batchFailedBlocks.length > 0) {
822
+ const gaps = coalesceGaps(batchFailedBlocks);
823
+ await recordGapBatch(db, subgraphRow.id, subgraphName, gaps).catch((err) => {
824
+ logger4.warn("Failed to record subgraph gaps", {
825
+ subgraph: subgraphName,
826
+ error: err instanceof Error ? err.message : String(err)
827
+ });
828
+ });
829
+ }
755
830
  const avg = avgEventsPerBlock(batch);
756
831
  batchSize = adjustBatchSize(batchSize, avg);
757
832
  currentHeight = batchEnd + 1;
@@ -768,17 +843,20 @@ async function catchUpSubgraph(subgraph, subgraphName) {
768
843
  }
769
844
 
770
845
  // src/runtime/reorg.ts
846
+ import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
771
847
  import { getDb as getDb3, getRawClient } from "@secondlayer/shared/db";
772
848
  import { listSubgraphs } from "@secondlayer/shared/db/queries/subgraphs";
773
849
  import { logger as logger5 } from "@secondlayer/shared/logger";
774
- import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
775
850
  async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
776
851
  const db = getDb3();
777
852
  const client = getRawClient();
778
853
  const activeSubgraphs = (await listSubgraphs(db)).filter((v) => v.status === "active");
779
854
  if (activeSubgraphs.length === 0)
780
855
  return;
781
- logger5.info("Propagating reorg to subgraphs", { blockHeight, subgraphCount: activeSubgraphs.length });
856
+ logger5.info("Propagating reorg to subgraphs", {
857
+ blockHeight,
858
+ subgraphCount: activeSubgraphs.length
859
+ });
782
860
  for (const sg of activeSubgraphs) {
783
861
  try {
784
862
  const schema = sg.definition.schema;
@@ -788,10 +866,16 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
788
866
  for (const tableName of Object.keys(schema)) {
789
867
  await client.unsafe(`DELETE FROM "${schemaName}"."${tableName}" WHERE "_block_height" = $1`, [blockHeight]);
790
868
  }
791
- logger5.info("Subgraph reorg cleanup done", { subgraph: sg.name, blockHeight });
869
+ logger5.info("Subgraph reorg cleanup done", {
870
+ subgraph: sg.name,
871
+ blockHeight
872
+ });
792
873
  const def = await loadSubgraphDef(sg.handler_path);
793
874
  await processBlock(def, sg.name, blockHeight);
794
- logger5.info("Subgraph reorg reprocessed", { subgraph: sg.name, blockHeight });
875
+ logger5.info("Subgraph reorg reprocessed", {
876
+ subgraph: sg.name,
877
+ blockHeight
878
+ });
795
879
  } catch (err) {
796
880
  logger5.error("Subgraph reorg handling failed", {
797
881
  subgraph: sg.name,
@@ -803,11 +887,14 @@ async function handleSubgraphReorg(blockHeight, loadSubgraphDef) {
803
887
  }
804
888
 
805
889
  // src/runtime/processor.ts
890
+ import { getErrorMessage as getErrorMessage3 } from "@secondlayer/shared";
806
891
  import { getDb as getDb4 } from "@secondlayer/shared/db";
892
+ import {
893
+ listSubgraphs as listSubgraphs2,
894
+ updateSubgraphStatus as updateSubgraphStatus2
895
+ } from "@secondlayer/shared/db/queries/subgraphs";
807
896
  import { logger as logger6 } from "@secondlayer/shared/logger";
808
- import { getErrorMessage as getErrorMessage3 } from "@secondlayer/shared";
809
897
  import { listen } from "@secondlayer/shared/queue/listener";
810
- import { listSubgraphs as listSubgraphs2, updateSubgraphStatus as updateSubgraphStatus2 } from "@secondlayer/shared/db/queries/subgraphs";
811
898
  var CHANNEL_NEW_BLOCK = "streams:new_job";
812
899
  var DEFAULT_CONCURRENCY = 5;
813
900
  var POLL_INTERVAL_MS = 5000;
@@ -910,7 +997,7 @@ async function startSubgraphProcessor(opts) {
910
997
  // src/service.ts
911
998
  import { logger as logger7 } from "@secondlayer/shared/logger";
912
999
  var processor = await startSubgraphProcessor({
913
- concurrency: parseInt(process.env.SUBGRAPH_CONCURRENCY ?? "5")
1000
+ concurrency: Number.parseInt(process.env.SUBGRAPH_CONCURRENCY ?? "5")
914
1001
  });
915
1002
  var shutdown = async () => {
916
1003
  logger7.info("Shutting down subgraph processor...");
@@ -920,5 +1007,5 @@ var shutdown = async () => {
920
1007
  process.on("SIGINT", shutdown);
921
1008
  process.on("SIGTERM", shutdown);
922
1009
 
923
- //# debugId=4CDF75EDA895AA0964756E2164756E21
1010
+ //# debugId=BC466B3425B1559464756E2164756E21
924
1011
  //# sourceMappingURL=service.js.map