@secondlayer/subgraphs 3.7.4 → 3.8.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.
@@ -628,6 +628,35 @@ interface DrizzleGenOptions {
628
628
  schemaName?: string;
629
629
  }
630
630
  declare function generateDrizzleSchema(def: SubgraphDefinition, opts?: DrizzleGenOptions): string;
631
+ interface KyselyGenOptions {
632
+ /** Postgres schema the tables live in (account-scoped). */
633
+ schemaName?: string;
634
+ }
635
+ declare function generateKyselySchema(def: SubgraphDefinition, opts?: KyselyGenOptions): string;
636
+ /**
637
+ * Emit a portable, typed schema for the public Index read domain (blocks,
638
+ * decoded events, transactions, stacking, sBTC, BNS, …) from the single-sourced
639
+ * `SOURCE_READ_TYPES` registry. This is the Index arm of the "typed Stacks data in
640
+ * your own stack" story: a BYO-database consumer mirrors Index rows and gets a
641
+ * fully-typed ORM / JSON-Schema that can't drift from the API (the registry is
642
+ * drift-tested against the producer's read contract).
643
+ *
644
+ * Prisma uses the `SOURCE_READ_PKS` registry for model identity. Tables whose only
645
+ * physical PK is a synthetic id excluded from the read contract (chain_reorgs) have
646
+ * no read-set key and are omitted from Prisma output (other targets still emit them).
647
+ */
648
+ type IndexCodegenTarget = "prisma" | "kysely" | "drizzle" | "json-schema";
649
+ interface IndexCodegenOptions {
650
+ /** Restrict output to these Index tables (default: all). */
651
+ tables?: string[];
652
+ /** Postgres schema to qualify table names with (default: bare names). */
653
+ schemaName?: string;
654
+ /** Prisma only: datasource url env var (default DATABASE_URL). */
655
+ datasourceEnv?: string;
656
+ }
657
+ declare function generateIndexSchema(target: IndexCodegenTarget, opts?: IndexCodegenOptions): string;
658
+ /** Index tables available to codegen, for help text and validation. */
659
+ declare const INDEX_CODEGEN_TABLES: readonly string[];
631
660
  import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
632
661
  import { Database } from "@secondlayer/shared/db";
633
662
  import { Kysely } from "kysely";
@@ -696,4 +725,4 @@ declare function deploySchema(db: AnyDb, def: SubgraphDefinition, handlerPath: s
696
725
  version: string
697
726
  diff?: DeployDiff
698
727
  }>;
699
- export { validateSubgraphDefinition, resumeReindex, renderDeployPlan, reindexSubgraph, pgSchemaName, generateSubgraphSQL, generatePrismaSchema, generateDrizzleSchema, diffSchema, deploySchema, defineSubgraph, 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, RowValue, ReindexOptions, PrismaGenOptions, PrintEventPayload, PrintEventFor, PrintEventFilter, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, InferTableRow, InferSubgraphClient, InferColumnType, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, EventForFilter, DrizzleGenOptions, DeployPlan, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, AnyEvent };
728
+ export { validateSubgraphDefinition, resumeReindex, renderDeployPlan, reindexSubgraph, pgSchemaName, generateSubgraphSQL, generatePrismaSchema, generateKyselySchema, generateIndexSchema, generateDrizzleSchema, diffSchema, deploySchema, defineSubgraph, 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, 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, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, AnyEvent };
package/dist/src/index.js CHANGED
@@ -1293,6 +1293,37 @@ class PublicApiBlockSource {
1293
1293
  return map;
1294
1294
  }
1295
1295
  }
1296
+
1297
+ class FallbackBlockSource {
1298
+ primary;
1299
+ fallback;
1300
+ constructor(primary, fallback) {
1301
+ this.primary = primary;
1302
+ this.fallback = fallback;
1303
+ }
1304
+ async getTip() {
1305
+ try {
1306
+ return await this.primary.getTip();
1307
+ } catch (err) {
1308
+ logger3.warn("block source primary getTip failed — using DB tap", {
1309
+ error: err instanceof Error ? err.message : String(err)
1310
+ });
1311
+ return this.fallback.getTip();
1312
+ }
1313
+ }
1314
+ async loadBlockRange(fromHeight, toHeight) {
1315
+ try {
1316
+ return await this.primary.loadBlockRange(fromHeight, toHeight);
1317
+ } catch (err) {
1318
+ logger3.warn("block source primary loadBlockRange failed — using DB tap", {
1319
+ from: fromHeight,
1320
+ to: toHeight,
1321
+ error: err instanceof Error ? err.message : String(err)
1322
+ });
1323
+ return this.fallback.loadBlockRange(fromHeight, toHeight);
1324
+ }
1325
+ }
1326
+ }
1296
1327
  var postgresBlockSource = new PostgresBlockSource;
1297
1328
  function buildHttpClient() {
1298
1329
  const baseUrl = process.env.SUBGRAPH_INDEX_API_URL ?? process.env.STREAMS_API_URL ?? "http://api:3800";
@@ -1304,7 +1335,7 @@ function buildHttpClient() {
1304
1335
  }
1305
1336
  function resolveBlockSource(subgraph) {
1306
1337
  if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph && isStreamsIndexEligible(subgraph)) {
1307
- return new PublicApiBlockSource(buildHttpClient(), referencedIndexEventTypes(subgraph));
1338
+ return new FallbackBlockSource(new PublicApiBlockSource(buildHttpClient(), referencedIndexEventTypes(subgraph)), postgresBlockSource);
1308
1339
  }
1309
1340
  if (process.env.SUBGRAPH_SOURCE === "streams-index" && subgraph) {
1310
1341
  logger3.debug("Subgraph not streams-index eligible, using DB tap", {
@@ -1539,6 +1570,13 @@ class SubscriptionMatcher {
1539
1570
 
1540
1571
  // src/runtime/subscription-state.ts
1541
1572
  var matcher = new SubscriptionMatcher;
1573
+ async function refreshMatcher(db) {
1574
+ const rows = await sql2`
1575
+ SELECT * FROM subscriptions WHERE status = 'active'
1576
+ `.execute(db);
1577
+ matcher.setAll(rows.rows);
1578
+ return matcher.size();
1579
+ }
1542
1580
 
1543
1581
  // src/runtime/block-processor.ts
1544
1582
  var routeCache = new Map;
@@ -1806,67 +1844,78 @@ function escapeLiteralDefault(value) {
1806
1844
  return value ? "TRUE" : "FALSE";
1807
1845
  return `'${String(value).replace(/'/g, "''")}'`;
1808
1846
  }
1809
- function generateSubgraphSQL(def, schemaNameOverride) {
1810
- const schemaName = schemaNameOverride ?? pgSchemaName(def.name);
1847
+ function tableNeedsTrgm(tableDef) {
1848
+ return Object.values(tableDef.columns).some((col) => col.search);
1849
+ }
1850
+ function emitTableDDL(schemaName, tableName, tableDef) {
1851
+ const qualifiedName = `${schemaName}.${tableName}`;
1811
1852
  const statements = [];
1812
- const needsTrgm = Object.values(def.schema).some((table) => Object.values(table.columns).some((col) => col.search));
1813
- if (needsTrgm) {
1814
- statements.push("CREATE EXTENSION IF NOT EXISTS pg_trgm");
1815
- }
1816
- statements.push(`CREATE SCHEMA IF NOT EXISTS ${schemaName}`);
1817
- for (const [tableName, tableDef] of Object.entries(def.schema)) {
1818
- const qualifiedName = `${schemaName}.${tableName}`;
1819
- const columnDefs = [
1820
- "_id BIGSERIAL PRIMARY KEY",
1821
- "_block_height BIGINT NOT NULL",
1822
- "_tx_id TEXT NOT NULL",
1823
- "_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()"
1824
- ];
1825
- for (const [colName, col] of Object.entries(tableDef.columns)) {
1826
- const sqlType = TYPE_MAP[col.type];
1827
- const nullable = col.nullable ? "" : " NOT NULL";
1828
- let colDef = `${colName} ${sqlType}${nullable}`;
1829
- if (col.default !== undefined) {
1830
- colDef += ` DEFAULT ${escapeLiteralDefault(col.default)}`;
1831
- }
1832
- columnDefs.push(colDef);
1853
+ const columnDefs = [
1854
+ "_id BIGSERIAL PRIMARY KEY",
1855
+ "_block_height BIGINT NOT NULL",
1856
+ "_tx_id TEXT NOT NULL",
1857
+ "_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()"
1858
+ ];
1859
+ for (const [colName, col] of Object.entries(tableDef.columns)) {
1860
+ const sqlType = TYPE_MAP[col.type];
1861
+ const nullable = col.nullable ? "" : " NOT NULL";
1862
+ let colDef = `${colName} ${sqlType}${nullable}`;
1863
+ if (col.default !== undefined) {
1864
+ colDef += ` DEFAULT ${escapeLiteralDefault(col.default)}`;
1833
1865
  }
1834
- statements.push(`CREATE TABLE IF NOT EXISTS ${qualifiedName} (
1866
+ columnDefs.push(colDef);
1867
+ }
1868
+ statements.push(`CREATE TABLE IF NOT EXISTS ${qualifiedName} (
1835
1869
  ${columnDefs.join(`,
1836
1870
  `)}
1837
1871
  )`);
1838
- statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`);
1839
- statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`);
1840
- for (const [colName, col] of Object.entries(tableDef.columns)) {
1841
- if (col.indexed) {
1842
- statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`);
1843
- }
1872
+ statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`);
1873
+ statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`);
1874
+ for (const [colName, col] of Object.entries(tableDef.columns)) {
1875
+ if (col.indexed) {
1876
+ statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`);
1844
1877
  }
1845
- for (const [colName, col] of Object.entries(tableDef.columns)) {
1846
- if (col.search) {
1847
- statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`);
1848
- }
1878
+ }
1879
+ for (const [colName, col] of Object.entries(tableDef.columns)) {
1880
+ if (col.search) {
1881
+ statements.push(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`);
1849
1882
  }
1850
- if (tableDef.indexes) {
1851
- for (let i = 0;i < tableDef.indexes.length; i++) {
1852
- const cols = tableDef.indexes[i];
1853
- const idxName = `idx_${schemaName}_${tableName}_composite_${i}`;
1854
- statements.push(`CREATE INDEX IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.join(", ")})`);
1855
- }
1883
+ }
1884
+ if (tableDef.indexes) {
1885
+ for (let i = 0;i < tableDef.indexes.length; i++) {
1886
+ const cols = tableDef.indexes[i];
1887
+ const idxName = `idx_${schemaName}_${tableName}_composite_${i}`;
1888
+ statements.push(`CREATE INDEX IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.join(", ")})`);
1856
1889
  }
1857
- if (tableDef.uniqueKeys) {
1858
- for (let i = 0;i < tableDef.uniqueKeys.length; i++) {
1859
- const cols = tableDef.uniqueKeys[i];
1860
- const constraintName = `uq_${schemaName}_${tableName}_${cols.join("_")}`;
1861
- statements.push(`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.join(", ")})`);
1862
- }
1890
+ }
1891
+ if (tableDef.uniqueKeys) {
1892
+ for (let i = 0;i < tableDef.uniqueKeys.length; i++) {
1893
+ const cols = tableDef.uniqueKeys[i];
1894
+ const constraintName = `uq_${schemaName}_${tableName}_${cols.join("_")}`;
1895
+ statements.push(`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.join(", ")})`);
1863
1896
  }
1864
1897
  }
1898
+ return statements;
1899
+ }
1900
+ function emitForeignKeyDDL(schemaName, tableName, tableDef) {
1901
+ return (tableDef.relations ?? []).map((rel) => {
1902
+ const constraintName = `fk_${schemaName}_${tableName}_${rel.name}`;
1903
+ return `ALTER TABLE ${schemaName}.${tableName} ADD CONSTRAINT ${constraintName} ` + `FOREIGN KEY (${rel.fields.join(", ")}) ` + `REFERENCES ${schemaName}.${rel.references} (${rel.referencedColumns.join(", ")})`;
1904
+ });
1905
+ }
1906
+ function generateSubgraphSQL(def, schemaNameOverride) {
1907
+ const schemaName = schemaNameOverride ?? pgSchemaName(def.name);
1908
+ const statements = [];
1909
+ const needsTrgm = Object.values(def.schema).some((table) => Object.values(table.columns).some((col) => col.search));
1910
+ if (needsTrgm) {
1911
+ statements.push("CREATE EXTENSION IF NOT EXISTS pg_trgm");
1912
+ }
1913
+ statements.push(`CREATE SCHEMA IF NOT EXISTS ${schemaName}`);
1865
1914
  for (const [tableName, tableDef] of Object.entries(def.schema)) {
1866
- for (const rel of tableDef.relations ?? []) {
1867
- const constraintName = `fk_${schemaName}_${tableName}_${rel.name}`;
1868
- statements.push(`ALTER TABLE ${schemaName}.${tableName} ADD CONSTRAINT ${constraintName} ` + `FOREIGN KEY (${rel.fields.join(", ")}) ` + `REFERENCES ${schemaName}.${rel.references} (${rel.referencedColumns.join(", ")})`);
1869
- }
1915
+ statements.push(...emitTableDDL(schemaName, tableName, tableDef));
1916
+ }
1917
+ for (const [tableName, tableDef] of Object.entries(def.schema)) {
1918
+ statements.push(...emitForeignKeyDDL(schemaName, tableName, tableDef));
1870
1919
  }
1871
1920
  const hashInput = JSON.stringify({
1872
1921
  name: def.name,
@@ -2561,6 +2610,304 @@ function generateDrizzleSchema(def, opts = {}) {
2561
2610
  ].join(`
2562
2611
  `);
2563
2612
  }
2613
+ // src/schema/kysely.ts
2614
+ var KYSELY_TYPE = {
2615
+ uint: "string",
2616
+ int: "string",
2617
+ text: "string",
2618
+ principal: "string",
2619
+ boolean: "boolean",
2620
+ timestamp: "Date",
2621
+ jsonb: "unknown"
2622
+ };
2623
+ var SYSTEM_FIELDS2 = [
2624
+ { col: "_id", type: "Generated<string>" },
2625
+ { col: "_block_height", type: "string" },
2626
+ { col: "_tx_id", type: "string" },
2627
+ { col: "_created_at", type: "Generated<Date>" }
2628
+ ];
2629
+ function pascalCase3(name) {
2630
+ return name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase()).replace(/^[a-z]/, (c) => c.toUpperCase());
2631
+ }
2632
+ function renderTable3(tableName, table) {
2633
+ const lines = SYSTEM_FIELDS2.map((f) => ` ${f.col}: ${f.type};`);
2634
+ for (const [colName, col] of Object.entries(table.columns)) {
2635
+ let type = KYSELY_TYPE[col.type];
2636
+ if (col.nullable)
2637
+ type = `${type} | null`;
2638
+ if (col.default !== undefined)
2639
+ type = `Generated<${type}>`;
2640
+ lines.push(` ${colName}: ${type};`);
2641
+ }
2642
+ return `export interface ${pascalCase3(tableName)} {
2643
+ ${lines.join(`
2644
+ `)}
2645
+ }`;
2646
+ }
2647
+ function generateKyselySchema(def, opts = {}) {
2648
+ const schemaName = opts.schemaName ?? pgSchemaName(def.name);
2649
+ const interfaces = Object.entries(def.schema).map(([name, table]) => renderTable3(name, table));
2650
+ const dbEntries = Object.keys(def.schema).map((name) => ` "${schemaName}.${name}": ${pascalCase3(name)};`);
2651
+ const dbRegistry = `export interface DB {
2652
+ ${dbEntries.join(`
2653
+ `)}
2654
+ }`;
2655
+ const rowExports = Object.keys(def.schema).map((name) => `export type ${pascalCase3(name)}Row = Selectable<${pascalCase3(name)}>;`);
2656
+ return [
2657
+ `// Generated by \`sl subgraphs codegen --target kysely\` from subgraph "${def.name}". Do not edit by hand.`,
2658
+ `import type { Generated, Selectable } from "kysely";`,
2659
+ "",
2660
+ interfaces.join(`
2661
+
2662
+ `),
2663
+ "",
2664
+ dbRegistry,
2665
+ "",
2666
+ rowExports.join(`
2667
+ `),
2668
+ ""
2669
+ ].join(`
2670
+ `);
2671
+ }
2672
+ // src/schema/index-codegen.ts
2673
+ import {
2674
+ SOURCE_READ_PKS,
2675
+ SOURCE_READ_TYPES
2676
+ } from "@secondlayer/shared/db";
2677
+ function pascalCase4(name) {
2678
+ return name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase()).replace(/^[a-z]/, (c) => c.toUpperCase());
2679
+ }
2680
+ function snakeToCamel3(name) {
2681
+ return name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
2682
+ }
2683
+ function selectTables(opts) {
2684
+ const all = SOURCE_READ_TYPES;
2685
+ const names = opts.tables ?? Object.keys(all);
2686
+ return names.map((name) => {
2687
+ const cols = all[name];
2688
+ if (!cols) {
2689
+ throw new Error(`Unknown Index table "${name}". Known: ${Object.keys(all).sort().join(", ")}`);
2690
+ }
2691
+ return [name, cols];
2692
+ });
2693
+ }
2694
+ var KYSELY_TYPE2 = {
2695
+ text: "string",
2696
+ int: "number",
2697
+ boolean: "boolean",
2698
+ timestamp: "Date",
2699
+ jsonb: "unknown"
2700
+ };
2701
+ function generateKysely(entries, schemaName) {
2702
+ const interfaces = entries.map(([table, cols]) => {
2703
+ const lines = Object.entries(cols).map(([col, def]) => {
2704
+ const type = def.nullable ? `${KYSELY_TYPE2[def.type]} | null` : KYSELY_TYPE2[def.type];
2705
+ return ` ${col}: ${type};`;
2706
+ });
2707
+ return `export interface ${pascalCase4(table)} {
2708
+ ${lines.join(`
2709
+ `)}
2710
+ }`;
2711
+ });
2712
+ const dbEntries = entries.map(([table]) => {
2713
+ const key2 = schemaName ? `${schemaName}.${table}` : table;
2714
+ return ` "${key2}": ${pascalCase4(table)};`;
2715
+ });
2716
+ const rows = entries.map(([table]) => `export type ${pascalCase4(table)}Row = Selectable<${pascalCase4(table)}>;`);
2717
+ return [
2718
+ "// Generated by `sl index codegen --target kysely`. Do not edit by hand.",
2719
+ `import type { Selectable } from "kysely";`,
2720
+ "",
2721
+ interfaces.join(`
2722
+
2723
+ `),
2724
+ "",
2725
+ `export interface IndexDB {
2726
+ ${dbEntries.join(`
2727
+ `)}
2728
+ }`,
2729
+ "",
2730
+ rows.join(`
2731
+ `),
2732
+ ""
2733
+ ].join(`
2734
+ `);
2735
+ }
2736
+ var DRIZZLE_BUILDER2 = {
2737
+ text: (c) => `text("${c}")`,
2738
+ int: (c) => `integer("${c}")`,
2739
+ boolean: (c) => `boolean("${c}")`,
2740
+ timestamp: (c) => `timestamp("${c}", { withTimezone: true })`,
2741
+ jsonb: (c) => `jsonb("${c}")`
2742
+ };
2743
+ var DRIZZLE_BUILDER_NAME = {
2744
+ text: "text",
2745
+ int: "integer",
2746
+ boolean: "boolean",
2747
+ timestamp: "timestamp",
2748
+ jsonb: "jsonb"
2749
+ };
2750
+ function generateDrizzle(entries, schemaName) {
2751
+ const used = new Set;
2752
+ for (const [, cols] of entries)
2753
+ for (const def of Object.values(cols))
2754
+ used.add(DRIZZLE_BUILDER_NAME[def.type]);
2755
+ if (schemaName)
2756
+ used.add("pgSchema");
2757
+ else
2758
+ used.add("pgTable");
2759
+ const tableFn = schemaName ? "sg.table" : "pgTable";
2760
+ const tables = entries.map(([table, cols]) => {
2761
+ const lines = Object.entries(cols).map(([col, def]) => {
2762
+ let line = ` ${snakeToCamel3(col)}: ${DRIZZLE_BUILDER2[def.type](col)}`;
2763
+ if (!def.nullable)
2764
+ line += ".notNull()";
2765
+ return `${line},`;
2766
+ });
2767
+ return `export const ${snakeToCamel3(table)} = ${tableFn}("${table}", {
2768
+ ${lines.join(`
2769
+ `)}
2770
+ });`;
2771
+ });
2772
+ const rows = entries.map(([table]) => `export type ${pascalCase4(table)} = typeof ${snakeToCamel3(table)}.$inferSelect;`);
2773
+ return [
2774
+ "// Generated by `sl index codegen --target drizzle`. Do not edit by hand.",
2775
+ `import { ${[...used].sort().join(", ")} } from "drizzle-orm/pg-core";`,
2776
+ "",
2777
+ ...schemaName ? [`export const sg = pgSchema("${schemaName}");`, ""] : [],
2778
+ tables.join(`
2779
+
2780
+ `),
2781
+ "",
2782
+ rows.join(`
2783
+ `),
2784
+ ""
2785
+ ].join(`
2786
+ `);
2787
+ }
2788
+ var JSON_SCHEMA_TYPE = {
2789
+ text: { type: "string" },
2790
+ int: { type: "integer" },
2791
+ boolean: { type: "boolean" },
2792
+ timestamp: { type: "string", format: "date-time" },
2793
+ jsonb: {}
2794
+ };
2795
+ function generateJsonSchema(entries) {
2796
+ const defs = {};
2797
+ for (const [table, cols] of entries) {
2798
+ const properties = {};
2799
+ const required = [];
2800
+ for (const [col, def] of Object.entries(cols)) {
2801
+ const base = JSON_SCHEMA_TYPE[def.type];
2802
+ if (def.nullable && "type" in base) {
2803
+ properties[col] = { ...base, type: [base.type, "null"] };
2804
+ } else {
2805
+ properties[col] = { ...base };
2806
+ if (!def.nullable)
2807
+ required.push(col);
2808
+ }
2809
+ }
2810
+ defs[pascalCase4(table)] = {
2811
+ type: "object",
2812
+ properties,
2813
+ required,
2814
+ additionalProperties: false
2815
+ };
2816
+ }
2817
+ return `${JSON.stringify({
2818
+ $schema: "https://json-schema.org/draft/2020-12/schema",
2819
+ title: "Secondlayer Index domain",
2820
+ $defs: defs
2821
+ }, null, 2)}
2822
+ `;
2823
+ }
2824
+ var PRISMA_TYPE2 = {
2825
+ text: { type: "String" },
2826
+ int: { type: "Int" },
2827
+ boolean: { type: "Boolean" },
2828
+ timestamp: { type: "DateTime", db: "@db.Timestamptz" },
2829
+ jsonb: { type: "Json" }
2830
+ };
2831
+ function generatePrisma(entries, schemaName, datasourceEnv) {
2832
+ const pks = SOURCE_READ_PKS;
2833
+ const skipped = [];
2834
+ const models = [];
2835
+ for (const [table, cols] of entries) {
2836
+ const pk = pks[table];
2837
+ if (!pk) {
2838
+ skipped.push(table);
2839
+ continue;
2840
+ }
2841
+ const single = pk.length === 1 ? pk[0] : null;
2842
+ const lines = Object.entries(cols).map(([col, def]) => {
2843
+ const field = snakeToCamel3(col);
2844
+ const mapped = PRISMA_TYPE2[def.type];
2845
+ const attrs = [];
2846
+ if (single === col)
2847
+ attrs.push("@id");
2848
+ if (field !== col)
2849
+ attrs.push(`@map("${col}")`);
2850
+ if (mapped.db)
2851
+ attrs.push(mapped.db);
2852
+ const optional = def.nullable ? "?" : "";
2853
+ return ` ${field} ${mapped.type}${optional}${attrs.length ? ` ${attrs.join(" ")}` : ""}`;
2854
+ });
2855
+ const block = [];
2856
+ if (!single) {
2857
+ block.push(` @@id([${pk.map(snakeToCamel3).join(", ")}])`);
2858
+ }
2859
+ block.push(` @@map("${table}")`);
2860
+ if (schemaName)
2861
+ block.push(` @@schema("${schemaName}")`);
2862
+ models.push(`model ${pascalCase4(table)} {
2863
+ ${lines.join(`
2864
+ `)}
2865
+
2866
+ ${block.join(`
2867
+ `)}
2868
+ }`);
2869
+ }
2870
+ const header = [
2871
+ "// Generated by `sl index codegen --target prisma`. Do not edit by hand.",
2872
+ ...skipped.length ? [
2873
+ `// Omitted (no read-set primary key, query via another target): ${skipped.join(", ")}`
2874
+ ] : [],
2875
+ "",
2876
+ "datasource db {",
2877
+ ' provider = "postgresql"',
2878
+ ` url = env("${datasourceEnv}")`,
2879
+ ...schemaName ? [` schemas = ["${schemaName}"]`] : [],
2880
+ "}",
2881
+ "",
2882
+ "generator client {",
2883
+ ' provider = "prisma-client-js"',
2884
+ ...schemaName ? [' previewFeatures = ["multiSchema"]'] : [],
2885
+ "}"
2886
+ ].join(`
2887
+ `);
2888
+ return `${header}
2889
+
2890
+ ${models.join(`
2891
+
2892
+ `)}
2893
+ `;
2894
+ }
2895
+ function generateIndexSchema(target, opts = {}) {
2896
+ const entries = selectTables(opts);
2897
+ switch (target) {
2898
+ case "prisma":
2899
+ return generatePrisma(entries, opts.schemaName, opts.datasourceEnv ?? "DATABASE_URL");
2900
+ case "kysely":
2901
+ return generateKysely(entries, opts.schemaName);
2902
+ case "drizzle":
2903
+ return generateDrizzle(entries, opts.schemaName);
2904
+ case "json-schema":
2905
+ return generateJsonSchema(entries);
2906
+ default:
2907
+ throw new Error(`Unsupported Index codegen target "${target}".`);
2908
+ }
2909
+ }
2910
+ var INDEX_CODEGEN_TABLES = Object.keys(SOURCE_READ_TYPES);
2564
2911
  // src/schema/deployer.ts
2565
2912
  import { sql as sql4 } from "kysely";
2566
2913
  function toJsonSafe(obj) {
@@ -2723,37 +3070,18 @@ async function deploySchema(db, def, handlerPath, opts) {
2723
3070
  diff: deployDiff2
2724
3071
  };
2725
3072
  }
2726
- for (const tableName of diff.addedTables) {
2727
- const tableDef = def.schema[tableName];
2728
- if (!tableDef)
2729
- continue;
2730
- const qualifiedName = `${schemaName}.${tableName}`;
2731
- const colDefs = [
2732
- "_id BIGSERIAL PRIMARY KEY",
2733
- "_block_height BIGINT NOT NULL",
2734
- "_tx_id TEXT NOT NULL",
2735
- "_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()"
2736
- ];
2737
- for (const [colName, col] of Object.entries(tableDef.columns)) {
2738
- const nullable = col.nullable ? "" : " NOT NULL";
2739
- const sqlType = TYPE_MAP[col.type];
2740
- if (!sqlType)
2741
- continue;
2742
- colDefs.push(`${colName} ${sqlType}${nullable}`);
3073
+ const addedDefs = diff.addedTables.map((tableName) => ({ tableName, tableDef: def.schema[tableName] })).filter((t) => t.tableDef !== undefined);
3074
+ if (addedDefs.some(({ tableDef }) => tableNeedsTrgm(tableDef))) {
3075
+ await sql4.raw("CREATE EXTENSION IF NOT EXISTS pg_trgm").execute(ddlDb);
3076
+ }
3077
+ for (const { tableName, tableDef } of addedDefs) {
3078
+ for (const stmt of emitTableDDL(schemaName, tableName, tableDef)) {
3079
+ await sql4.raw(stmt).execute(ddlDb);
2743
3080
  }
2744
- await sql4.raw(`CREATE TABLE IF NOT EXISTS ${qualifiedName} (
2745
- ${colDefs.join(`,
2746
- `)}
2747
- )`).execute(ddlDb);
2748
- await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`).execute(ddlDb);
2749
- await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`).execute(ddlDb);
2750
- for (const [colName, col] of Object.entries(tableDef.columns)) {
2751
- if (col.indexed) {
2752
- await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`).execute(ddlDb);
2753
- }
2754
- if (col.search) {
2755
- await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
2756
- }
3081
+ }
3082
+ for (const { tableName, tableDef } of addedDefs) {
3083
+ for (const stmt of emitForeignKeyDDL(schemaName, tableName, tableDef)) {
3084
+ await sql4.raw(stmt).execute(ddlDb);
2757
3085
  }
2758
3086
  }
2759
3087
  for (const [tableName, colDiff] of Object.entries(diff.tables)) {
@@ -2832,12 +3160,15 @@ export {
2832
3160
  pgSchemaName,
2833
3161
  generateSubgraphSQL,
2834
3162
  generatePrismaSchema,
3163
+ generateKyselySchema,
3164
+ generateIndexSchema,
2835
3165
  generateDrizzleSchema,
2836
3166
  diffSchema,
2837
3167
  deploySchema,
2838
3168
  defineSubgraph,
2839
- backfillSubgraph
3169
+ backfillSubgraph,
3170
+ INDEX_CODEGEN_TABLES
2840
3171
  };
2841
3172
 
2842
- //# debugId=B9164957A8A3E4E364756E2164756E21
3173
+ //# debugId=822BF312943996F964756E2164756E21
2843
3174
  //# sourceMappingURL=index.js.map