@secondlayer/subgraphs 3.7.3 → 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.
- package/dist/src/index.d.ts +30 -1
- package/dist/src/index.js +407 -83
- package/dist/src/index.js.map +9 -7
- package/dist/src/runtime/block-processor.js +33 -2
- package/dist/src/runtime/block-processor.js.map +3 -3
- package/dist/src/runtime/catchup.js +33 -2
- package/dist/src/runtime/catchup.js.map +3 -3
- package/dist/src/runtime/emitter.d.ts +18 -0
- package/dist/src/runtime/emitter.js +773 -0
- package/dist/src/runtime/emitter.js.map +19 -0
- package/dist/src/runtime/processor.js +129 -943
- package/dist/src/runtime/processor.js.map +7 -20
- package/dist/src/runtime/reindex.js +94 -52
- package/dist/src/runtime/reindex.js.map +4 -4
- package/dist/src/runtime/reorg.js +33 -2
- package/dist/src/runtime/reorg.js.map +3 -3
- package/dist/src/runtime/replay.js +33 -2
- package/dist/src/runtime/replay.js.map +4 -4
- package/dist/src/schema/index.d.ts +2 -1
- package/dist/src/schema/index.js +75 -82
- package/dist/src/schema/index.js.map +5 -5
- package/dist/src/service.js +134 -948
- package/dist/src/service.js.map +6 -19
- package/dist/src/validate.d.ts +2 -1
- package/dist/src/validate.js +2 -1
- package/dist/src/validate.js.map +3 -3
- package/package.json +6 -2
package/dist/src/index.d.ts
CHANGED
|
@@ -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", {
|
|
@@ -1813,67 +1844,78 @@ function escapeLiteralDefault(value) {
|
|
|
1813
1844
|
return value ? "TRUE" : "FALSE";
|
|
1814
1845
|
return `'${String(value).replace(/'/g, "''")}'`;
|
|
1815
1846
|
}
|
|
1816
|
-
function
|
|
1817
|
-
|
|
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}`;
|
|
1818
1852
|
const statements = [];
|
|
1819
|
-
const
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
const
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
];
|
|
1832
|
-
for (const [colName, col] of Object.entries(tableDef.columns)) {
|
|
1833
|
-
const sqlType = TYPE_MAP[col.type];
|
|
1834
|
-
const nullable = col.nullable ? "" : " NOT NULL";
|
|
1835
|
-
let colDef = `${colName} ${sqlType}${nullable}`;
|
|
1836
|
-
if (col.default !== undefined) {
|
|
1837
|
-
colDef += ` DEFAULT ${escapeLiteralDefault(col.default)}`;
|
|
1838
|
-
}
|
|
1839
|
-
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)}`;
|
|
1840
1865
|
}
|
|
1841
|
-
|
|
1866
|
+
columnDefs.push(colDef);
|
|
1867
|
+
}
|
|
1868
|
+
statements.push(`CREATE TABLE IF NOT EXISTS ${qualifiedName} (
|
|
1842
1869
|
${columnDefs.join(`,
|
|
1843
1870
|
`)}
|
|
1844
1871
|
)`);
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
}
|
|
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})`);
|
|
1851
1877
|
}
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
}
|
|
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)`);
|
|
1856
1882
|
}
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
}
|
|
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(", ")})`);
|
|
1863
1889
|
}
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
}
|
|
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(", ")})`);
|
|
1870
1896
|
}
|
|
1871
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}`);
|
|
1872
1914
|
for (const [tableName, tableDef] of Object.entries(def.schema)) {
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
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));
|
|
1877
1919
|
}
|
|
1878
1920
|
const hashInput = JSON.stringify({
|
|
1879
1921
|
name: def.name,
|
|
@@ -2568,6 +2610,304 @@ function generateDrizzleSchema(def, opts = {}) {
|
|
|
2568
2610
|
].join(`
|
|
2569
2611
|
`);
|
|
2570
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);
|
|
2571
2911
|
// src/schema/deployer.ts
|
|
2572
2912
|
import { sql as sql4 } from "kysely";
|
|
2573
2913
|
function toJsonSafe(obj) {
|
|
@@ -2730,37 +3070,18 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
2730
3070
|
diff: deployDiff2
|
|
2731
3071
|
};
|
|
2732
3072
|
}
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
const
|
|
2739
|
-
|
|
2740
|
-
"_block_height BIGINT NOT NULL",
|
|
2741
|
-
"_tx_id TEXT NOT NULL",
|
|
2742
|
-
"_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()"
|
|
2743
|
-
];
|
|
2744
|
-
for (const [colName, col] of Object.entries(tableDef.columns)) {
|
|
2745
|
-
const nullable = col.nullable ? "" : " NOT NULL";
|
|
2746
|
-
const sqlType = TYPE_MAP[col.type];
|
|
2747
|
-
if (!sqlType)
|
|
2748
|
-
continue;
|
|
2749
|
-
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);
|
|
2750
3080
|
}
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
)
|
|
2755
|
-
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`).execute(ddlDb);
|
|
2756
|
-
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`).execute(ddlDb);
|
|
2757
|
-
for (const [colName, col] of Object.entries(tableDef.columns)) {
|
|
2758
|
-
if (col.indexed) {
|
|
2759
|
-
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`).execute(ddlDb);
|
|
2760
|
-
}
|
|
2761
|
-
if (col.search) {
|
|
2762
|
-
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
|
|
2763
|
-
}
|
|
3081
|
+
}
|
|
3082
|
+
for (const { tableName, tableDef } of addedDefs) {
|
|
3083
|
+
for (const stmt of emitForeignKeyDDL(schemaName, tableName, tableDef)) {
|
|
3084
|
+
await sql4.raw(stmt).execute(ddlDb);
|
|
2764
3085
|
}
|
|
2765
3086
|
}
|
|
2766
3087
|
for (const [tableName, colDiff] of Object.entries(diff.tables)) {
|
|
@@ -2839,12 +3160,15 @@ export {
|
|
|
2839
3160
|
pgSchemaName,
|
|
2840
3161
|
generateSubgraphSQL,
|
|
2841
3162
|
generatePrismaSchema,
|
|
3163
|
+
generateKyselySchema,
|
|
3164
|
+
generateIndexSchema,
|
|
2842
3165
|
generateDrizzleSchema,
|
|
2843
3166
|
diffSchema,
|
|
2844
3167
|
deploySchema,
|
|
2845
3168
|
defineSubgraph,
|
|
2846
|
-
backfillSubgraph
|
|
3169
|
+
backfillSubgraph,
|
|
3170
|
+
INDEX_CODEGEN_TABLES
|
|
2847
3171
|
};
|
|
2848
3172
|
|
|
2849
|
-
//# debugId=
|
|
3173
|
+
//# debugId=822BF312943996F964756E2164756E21
|
|
2850
3174
|
//# sourceMappingURL=index.js.map
|