@secondlayer/subgraphs 3.2.1 → 3.4.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/README.md +81 -0
- package/dist/src/index.d.ts +84 -10
- package/dist/src/index.js +461 -54
- package/dist/src/index.js.map +11 -9
- package/dist/src/runtime/block-processor.d.ts +37 -9
- package/dist/src/runtime/block-processor.js +127 -37
- package/dist/src/runtime/block-processor.js.map +5 -5
- package/dist/src/runtime/catchup.d.ts +34 -8
- package/dist/src/runtime/catchup.js +125 -36
- package/dist/src/runtime/catchup.js.map +5 -5
- package/dist/src/runtime/context.d.ts +27 -1
- package/dist/src/runtime/context.js +13 -2
- package/dist/src/runtime/context.js.map +3 -3
- package/dist/src/runtime/processor.js +143 -39
- package/dist/src/runtime/processor.js.map +8 -8
- package/dist/src/runtime/reindex.d.ts +34 -8
- package/dist/src/runtime/reindex.js +131 -36
- package/dist/src/runtime/reindex.js.map +6 -6
- package/dist/src/runtime/reorg.d.ts +34 -8
- package/dist/src/runtime/reorg.js +131 -39
- package/dist/src/runtime/reorg.js.map +6 -6
- package/dist/src/runtime/runner.d.ts +43 -9
- package/dist/src/runtime/source-matcher.d.ts +19 -10
- package/dist/src/runtime/source-matcher.js +26 -6
- package/dist/src/runtime/source-matcher.js.map +3 -3
- package/dist/src/schema/index.d.ts +42 -8
- package/dist/src/schema/index.js +57 -19
- package/dist/src/schema/index.js.map +5 -5
- package/dist/src/service.js +143 -39
- package/dist/src/service.js.map +8 -8
- package/dist/src/triggers/index.d.ts +16 -8
- package/dist/src/types.d.ts +35 -9
- package/dist/src/validate.d.ts +34 -8
- package/dist/src/validate.js +10 -3
- package/dist/src/validate.js.map +3 -3
- package/package.json +3 -3
package/dist/src/index.js
CHANGED
|
@@ -23,7 +23,13 @@ var SubgraphColumnSchema = z.object({
|
|
|
23
23
|
var SubgraphTableSchema = z.object({
|
|
24
24
|
columns: z.record(z.string(), SubgraphColumnSchema).refine((c) => Object.keys(c).length > 0, "Table must have at least one column"),
|
|
25
25
|
indexes: z.array(z.array(z.string())).optional(),
|
|
26
|
-
uniqueKeys: z.array(z.array(z.string())).optional()
|
|
26
|
+
uniqueKeys: z.array(z.array(z.string())).optional(),
|
|
27
|
+
relations: z.array(z.object({
|
|
28
|
+
name: z.string(),
|
|
29
|
+
references: z.string(),
|
|
30
|
+
fields: z.array(z.string()).min(1),
|
|
31
|
+
referencedColumns: z.array(z.string()).min(1)
|
|
32
|
+
})).optional()
|
|
27
33
|
});
|
|
28
34
|
var SubgraphSchemaSchema = z.record(z.string(), SubgraphTableSchema).refine((s) => Object.keys(s).length > 0, "Schema must have at least one table");
|
|
29
35
|
var VALID_FILTER_TYPES = [
|
|
@@ -55,7 +61,8 @@ var SubgraphFilterSchema = z.object({
|
|
|
55
61
|
contractName: z.string().optional(),
|
|
56
62
|
topic: z.string().optional(),
|
|
57
63
|
lockedAddress: z.string().optional(),
|
|
58
|
-
abi: z.record(z.string(), z.any()).optional()
|
|
64
|
+
abi: z.record(z.string(), z.any()).optional(),
|
|
65
|
+
trait: z.string().optional()
|
|
59
66
|
}).strict();
|
|
60
67
|
var SubgraphDefinitionSchema = z.object({
|
|
61
68
|
name: SubgraphNameSchema,
|
|
@@ -87,12 +94,14 @@ class SubgraphContext {
|
|
|
87
94
|
pgSchemaName;
|
|
88
95
|
subgraphSchema;
|
|
89
96
|
ops = [];
|
|
90
|
-
|
|
97
|
+
byo;
|
|
98
|
+
constructor(db, pgSchemaName, subgraphSchema, block, tx, byo = false) {
|
|
91
99
|
this.db = db;
|
|
92
100
|
this.pgSchemaName = pgSchemaName;
|
|
93
101
|
this.subgraphSchema = subgraphSchema;
|
|
94
102
|
this.block = block;
|
|
95
103
|
this._tx = tx;
|
|
104
|
+
this.byo = byo;
|
|
96
105
|
}
|
|
97
106
|
get tx() {
|
|
98
107
|
return this._tx;
|
|
@@ -287,6 +296,15 @@ class SubgraphContext {
|
|
|
287
296
|
}
|
|
288
297
|
buildStatements(ops) {
|
|
289
298
|
const statements = [];
|
|
299
|
+
if (this.byo) {
|
|
300
|
+
const insertTables = new Set;
|
|
301
|
+
for (const op of ops)
|
|
302
|
+
if (op.kind === "insert")
|
|
303
|
+
insertTables.add(op.table);
|
|
304
|
+
for (const table of insertTables) {
|
|
305
|
+
statements.push(`DELETE FROM "${this.pgSchemaName}"."${table}" WHERE "_block_height" = ${this.block.height}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
290
308
|
let currentBatch = null;
|
|
291
309
|
let currentBatchKey = "";
|
|
292
310
|
const flushInsertBatch = () => {
|
|
@@ -780,7 +798,19 @@ function matchPattern(value, pattern) {
|
|
|
780
798
|
}
|
|
781
799
|
return re.test(value);
|
|
782
800
|
}
|
|
783
|
-
|
|
801
|
+
var EMPTY_SET = new Set;
|
|
802
|
+
function traitAllows(filter, contractId, traitContracts) {
|
|
803
|
+
const trait = filter.trait;
|
|
804
|
+
if (!trait)
|
|
805
|
+
return true;
|
|
806
|
+
if (!contractId)
|
|
807
|
+
return false;
|
|
808
|
+
return (traitContracts.get(trait) ?? EMPTY_SET).has(contractId);
|
|
809
|
+
}
|
|
810
|
+
function assetContract(assetId) {
|
|
811
|
+
return assetId?.split("::")[0];
|
|
812
|
+
}
|
|
813
|
+
function matchFilter(filter, transactions, eventsByTx, traitContracts) {
|
|
784
814
|
const results = [];
|
|
785
815
|
switch (filter.type) {
|
|
786
816
|
case "stx_transfer":
|
|
@@ -844,6 +874,8 @@ function matchFilter(filter, transactions, eventsByTx) {
|
|
|
844
874
|
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
845
875
|
return false;
|
|
846
876
|
}
|
|
877
|
+
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
878
|
+
return false;
|
|
847
879
|
if ("sender" in filter && filter.sender) {
|
|
848
880
|
if (!matchPattern(data.sender, filter.sender))
|
|
849
881
|
return false;
|
|
@@ -882,6 +914,8 @@ function matchFilter(filter, transactions, eventsByTx) {
|
|
|
882
914
|
if (!assetId || !matchPattern(assetId, filter.assetIdentifier))
|
|
883
915
|
return false;
|
|
884
916
|
}
|
|
917
|
+
if (!traitAllows(filter, assetContract(data.asset_identifier), traitContracts))
|
|
918
|
+
return false;
|
|
885
919
|
if ("sender" in filter && filter.sender) {
|
|
886
920
|
if (!matchPattern(data.sender, filter.sender))
|
|
887
921
|
return false;
|
|
@@ -914,6 +948,8 @@ function matchFilter(filter, transactions, eventsByTx) {
|
|
|
914
948
|
if (!matchPattern(tx.sender, filter.caller))
|
|
915
949
|
continue;
|
|
916
950
|
}
|
|
951
|
+
if (!traitAllows(filter, tx.contract_id, traitContracts))
|
|
952
|
+
continue;
|
|
917
953
|
const txEvents = eventsByTx.get(tx.tx_id) ?? [];
|
|
918
954
|
results.push({ tx, events: txEvents });
|
|
919
955
|
}
|
|
@@ -948,11 +984,13 @@ function matchFilter(filter, transactions, eventsByTx) {
|
|
|
948
984
|
return false;
|
|
949
985
|
if (data.topic !== "print")
|
|
950
986
|
return false;
|
|
987
|
+
const printContractId = data.contract_identifier ?? data.contract_id;
|
|
951
988
|
if (filter.contractId) {
|
|
952
|
-
|
|
953
|
-
if (!contractId || !matchPattern(contractId, filter.contractId))
|
|
989
|
+
if (!printContractId || !matchPattern(printContractId, filter.contractId))
|
|
954
990
|
return false;
|
|
955
991
|
}
|
|
992
|
+
if (!traitAllows(filter, printContractId, traitContracts))
|
|
993
|
+
return false;
|
|
956
994
|
return true;
|
|
957
995
|
});
|
|
958
996
|
if (matched.length > 0) {
|
|
@@ -964,7 +1002,7 @@ function matchFilter(filter, transactions, eventsByTx) {
|
|
|
964
1002
|
}
|
|
965
1003
|
return results;
|
|
966
1004
|
}
|
|
967
|
-
function matchSources(sources, transactions, events) {
|
|
1005
|
+
function matchSources(sources, transactions, events, traitContracts = new Map) {
|
|
968
1006
|
const eventsByTx = new Map;
|
|
969
1007
|
for (const event of events) {
|
|
970
1008
|
const list = eventsByTx.get(event.tx_id) ?? [];
|
|
@@ -974,7 +1012,7 @@ function matchSources(sources, transactions, events) {
|
|
|
974
1012
|
const seen = new Set;
|
|
975
1013
|
const results = [];
|
|
976
1014
|
for (const [sourceName, filter] of Object.entries(sources)) {
|
|
977
|
-
const matches = matchFilter(filter, transactions, eventsByTx);
|
|
1015
|
+
const matches = matchFilter(filter, transactions, eventsByTx, traitContracts);
|
|
978
1016
|
for (const match of matches) {
|
|
979
1017
|
const dedupeKey = `${match.tx.tx_id}:${sourceName}`;
|
|
980
1018
|
if (!seen.has(dedupeKey)) {
|
|
@@ -991,8 +1029,11 @@ import {
|
|
|
991
1029
|
getSourceDb,
|
|
992
1030
|
getTargetDb
|
|
993
1031
|
} from "@secondlayer/shared/db";
|
|
1032
|
+
import { resolveTraitContractIds } from "@secondlayer/shared/db/queries/contracts";
|
|
994
1033
|
import {
|
|
1034
|
+
isByoSubgraph,
|
|
995
1035
|
recordSubgraphProcessed,
|
|
1036
|
+
resolveSubgraphDb,
|
|
996
1037
|
updateSubgraphStatus
|
|
997
1038
|
} from "@secondlayer/shared/db/queries/subgraphs";
|
|
998
1039
|
import { logger as logger4 } from "@secondlayer/shared/logger";
|
|
@@ -1230,7 +1271,38 @@ async function refreshMatcher(db) {
|
|
|
1230
1271
|
}
|
|
1231
1272
|
|
|
1232
1273
|
// src/runtime/block-processor.ts
|
|
1233
|
-
var
|
|
1274
|
+
var routeCache = new Map;
|
|
1275
|
+
async function resolveRoute(subgraphName, targetDb) {
|
|
1276
|
+
const cached = routeCache.get(subgraphName);
|
|
1277
|
+
if (cached)
|
|
1278
|
+
return cached;
|
|
1279
|
+
const row = await targetDb.selectFrom("subgraphs").selectAll().where("name", "=", subgraphName).executeTakeFirst();
|
|
1280
|
+
const byo = row ? isByoSubgraph(row) : false;
|
|
1281
|
+
const route = {
|
|
1282
|
+
schemaName: row?.schema_name ?? pgSchemaName(subgraphName),
|
|
1283
|
+
dataDb: row && byo ? resolveSubgraphDb(row) : targetDb,
|
|
1284
|
+
byo
|
|
1285
|
+
};
|
|
1286
|
+
routeCache.set(subgraphName, route);
|
|
1287
|
+
return route;
|
|
1288
|
+
}
|
|
1289
|
+
function invalidateSubgraphRoute(subgraphName) {
|
|
1290
|
+
routeCache.delete(subgraphName);
|
|
1291
|
+
}
|
|
1292
|
+
async function resolveTraitContracts(subgraph, blockHeight, db) {
|
|
1293
|
+
const traits = new Set;
|
|
1294
|
+
for (const source of Object.values(subgraph.sources)) {
|
|
1295
|
+
const trait = source.trait;
|
|
1296
|
+
if (trait)
|
|
1297
|
+
traits.add(trait);
|
|
1298
|
+
}
|
|
1299
|
+
const resolved = new Map;
|
|
1300
|
+
for (const trait of traits) {
|
|
1301
|
+
const ids = await resolveTraitContractIds(db, trait, blockHeight);
|
|
1302
|
+
resolved.set(trait, new Set(ids));
|
|
1303
|
+
}
|
|
1304
|
+
return resolved;
|
|
1305
|
+
}
|
|
1234
1306
|
async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
1235
1307
|
const sourceDb = getSourceDb();
|
|
1236
1308
|
const targetDb = getTargetDb();
|
|
@@ -1270,7 +1342,8 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
1270
1342
|
txs = await sourceDb.selectFrom("transactions").selectAll().where("block_height", "=", blockHeight).execute();
|
|
1271
1343
|
evts = await sourceDb.selectFrom("events").selectAll().where("block_height", "=", blockHeight).execute();
|
|
1272
1344
|
}
|
|
1273
|
-
const
|
|
1345
|
+
const traitContracts = await resolveTraitContracts(subgraph, blockHeight, targetDb);
|
|
1346
|
+
const matched = matchSources(subgraph.sources, txs, evts, traitContracts);
|
|
1274
1347
|
result.matched = matched.length;
|
|
1275
1348
|
if (matched.length === 0) {
|
|
1276
1349
|
if (!opts?.skipProgressUpdate) {
|
|
@@ -1278,12 +1351,8 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
1278
1351
|
}
|
|
1279
1352
|
return result;
|
|
1280
1353
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
const subgraphRecord = await targetDb.selectFrom("subgraphs").select("schema_name").where("name", "=", subgraphName).executeTakeFirst();
|
|
1284
|
-
schemaName = subgraphRecord?.schema_name ?? pgSchemaName(subgraphName);
|
|
1285
|
-
schemaNameCache.set(subgraphName, schemaName);
|
|
1286
|
-
}
|
|
1354
|
+
const route = await resolveRoute(subgraphName, targetDb);
|
|
1355
|
+
const schemaName = route.schemaName;
|
|
1287
1356
|
const blockMeta = {
|
|
1288
1357
|
height: block.height,
|
|
1289
1358
|
hash: block.hash,
|
|
@@ -1298,30 +1367,57 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
1298
1367
|
};
|
|
1299
1368
|
let handlerMs = 0;
|
|
1300
1369
|
let flushMs = 0;
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
const
|
|
1305
|
-
|
|
1370
|
+
const applyProgress = async (tx, rr) => {
|
|
1371
|
+
if (opts?.skipProgressUpdate)
|
|
1372
|
+
return;
|
|
1373
|
+
const status = rr.errors > 0 && rr.processed === 0 ? "error" : "active";
|
|
1374
|
+
await updateSubgraphStatus(tx, subgraphName, status, blockHeight);
|
|
1375
|
+
if (rr.processed > 0 || rr.errors > 0) {
|
|
1376
|
+
const lastError = rr.errors > 0 ? `${rr.errors} error(s) at block ${blockHeight}` : undefined;
|
|
1377
|
+
await recordSubgraphProcessed(tx, subgraphName, rr.processed, rr.errors, lastError);
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
if (route.byo) {
|
|
1381
|
+
let runResult = { processed: 0, errors: 0 };
|
|
1382
|
+
let manifest;
|
|
1383
|
+
await route.dataDb.transaction().execute(async (tx) => {
|
|
1384
|
+
const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx, true);
|
|
1385
|
+
const handlerStart = performance.now();
|
|
1386
|
+
runResult = await runHandlers(subgraph, matched, ctx);
|
|
1387
|
+
handlerMs = performance.now() - handlerStart;
|
|
1388
|
+
if (ctx.pendingOps > 0) {
|
|
1389
|
+
const flushStart = performance.now();
|
|
1390
|
+
manifest = await ctx.flush();
|
|
1391
|
+
flushMs = performance.now() - flushStart;
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1306
1394
|
result.processed = runResult.processed;
|
|
1307
1395
|
result.errors = runResult.errors;
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
const manifest = await ctx.flush();
|
|
1311
|
-
if (manifest.count > 0) {
|
|
1396
|
+
await targetDb.transaction().execute(async (tx) => {
|
|
1397
|
+
if (manifest && manifest.count > 0) {
|
|
1312
1398
|
await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
|
|
1313
1399
|
}
|
|
1314
|
-
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1400
|
+
await applyProgress(tx, runResult);
|
|
1401
|
+
});
|
|
1402
|
+
} else {
|
|
1403
|
+
await targetDb.transaction().execute(async (tx) => {
|
|
1404
|
+
const ctx = new SubgraphContext(tx, schemaName, subgraph.schema, blockMeta, initialTx);
|
|
1405
|
+
const handlerStart = performance.now();
|
|
1406
|
+
const runResult = await runHandlers(subgraph, matched, ctx);
|
|
1407
|
+
handlerMs = performance.now() - handlerStart;
|
|
1408
|
+
result.processed = runResult.processed;
|
|
1409
|
+
result.errors = runResult.errors;
|
|
1410
|
+
if (ctx.pendingOps > 0) {
|
|
1411
|
+
const flushStart = performance.now();
|
|
1412
|
+
const manifest = await ctx.flush();
|
|
1413
|
+
if (manifest.count > 0) {
|
|
1414
|
+
await emitSubscriptionOutbox(tx, subgraphName, manifest, matcher, block.height);
|
|
1415
|
+
}
|
|
1416
|
+
flushMs = performance.now() - flushStart;
|
|
1417
|
+
}
|
|
1418
|
+
await applyProgress(tx, runResult);
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1325
1421
|
const totalMs = performance.now() - blockStart;
|
|
1326
1422
|
result.timing = {
|
|
1327
1423
|
totalMs: Math.round(totalMs),
|
|
@@ -1332,7 +1428,7 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
1332
1428
|
try {
|
|
1333
1429
|
const tables = Object.keys(subgraph.schema);
|
|
1334
1430
|
for (const table of tables) {
|
|
1335
|
-
const { rows } = await sql3.raw(`SELECT n_live_tup AS count FROM pg_stat_user_tables WHERE schemaname = '${schemaName}' AND relname = '${table}'`).execute(
|
|
1431
|
+
const { rows } = await sql3.raw(`SELECT n_live_tup AS count FROM pg_stat_user_tables WHERE schemaname = '${schemaName}' AND relname = '${table}'`).execute(route.dataDb);
|
|
1336
1432
|
const count = Number(rows[0]?.count ?? 0);
|
|
1337
1433
|
if (count >= 1e7) {
|
|
1338
1434
|
logger4.warn("Subgraph table exceeds 10M rows (estimate)", {
|
|
@@ -1504,6 +1600,12 @@ function generateSubgraphSQL(def, schemaNameOverride) {
|
|
|
1504
1600
|
}
|
|
1505
1601
|
}
|
|
1506
1602
|
}
|
|
1603
|
+
for (const [tableName, tableDef] of Object.entries(def.schema)) {
|
|
1604
|
+
for (const rel of tableDef.relations ?? []) {
|
|
1605
|
+
const constraintName = `fk_${schemaName}_${tableName}_${rel.name}`;
|
|
1606
|
+
statements.push(`ALTER TABLE ${schemaName}.${tableName} ADD CONSTRAINT ${constraintName} ` + `FOREIGN KEY (${rel.fields.join(", ")}) ` + `REFERENCES ${schemaName}.${rel.references} (${rel.referencedColumns.join(", ")})`);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1507
1609
|
const hashInput = JSON.stringify({
|
|
1508
1610
|
name: def.name,
|
|
1509
1611
|
schema: def.schema,
|
|
@@ -1968,6 +2070,283 @@ async function backfillSubgraph(def, opts) {
|
|
|
1968
2070
|
function defineSubgraph(def) {
|
|
1969
2071
|
return def;
|
|
1970
2072
|
}
|
|
2073
|
+
// src/schema/prisma.ts
|
|
2074
|
+
var PRISMA_TYPE = {
|
|
2075
|
+
uint: { type: "Decimal", db: "@db.Numeric" },
|
|
2076
|
+
int: { type: "Decimal", db: "@db.Numeric" },
|
|
2077
|
+
text: { type: "String" },
|
|
2078
|
+
principal: { type: "String" },
|
|
2079
|
+
boolean: { type: "Boolean" },
|
|
2080
|
+
timestamp: { type: "DateTime", db: "@db.Timestamptz" },
|
|
2081
|
+
jsonb: { type: "Json" }
|
|
2082
|
+
};
|
|
2083
|
+
var SYSTEM_FIELDS = [
|
|
2084
|
+
{ field: "id", col: "_id", line: "BigInt @id @default(autoincrement())" },
|
|
2085
|
+
{ field: "blockHeight", col: "_block_height", line: "BigInt" },
|
|
2086
|
+
{ field: "txId", col: "_tx_id", line: "String" },
|
|
2087
|
+
{
|
|
2088
|
+
field: "createdAt",
|
|
2089
|
+
col: "_created_at",
|
|
2090
|
+
line: "DateTime @default(now()) @db.Timestamptz"
|
|
2091
|
+
}
|
|
2092
|
+
];
|
|
2093
|
+
function snakeToCamel(name) {
|
|
2094
|
+
return name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
2095
|
+
}
|
|
2096
|
+
function pascalCase(name) {
|
|
2097
|
+
const camel = snakeToCamel(name);
|
|
2098
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
2099
|
+
}
|
|
2100
|
+
function quotePrismaDefault(value) {
|
|
2101
|
+
if (typeof value === "string")
|
|
2102
|
+
return `@default("${value.replace(/"/g, "\\\"")}")`;
|
|
2103
|
+
return `@default(${value})`;
|
|
2104
|
+
}
|
|
2105
|
+
function renderTable(tableName, table, backRelations) {
|
|
2106
|
+
const lines = [];
|
|
2107
|
+
for (const sys of SYSTEM_FIELDS) {
|
|
2108
|
+
lines.push(` ${sys.field} ${sys.line} @map("${sys.col}")`);
|
|
2109
|
+
}
|
|
2110
|
+
for (const [colName, col] of Object.entries(table.columns)) {
|
|
2111
|
+
const field = snakeToCamel(colName);
|
|
2112
|
+
const mapped = PRISMA_TYPE[col.type];
|
|
2113
|
+
const optional = col.nullable ? "?" : "";
|
|
2114
|
+
const attrs = [];
|
|
2115
|
+
if (field !== colName)
|
|
2116
|
+
attrs.push(`@map("${colName}")`);
|
|
2117
|
+
if (col.default !== undefined)
|
|
2118
|
+
attrs.push(quotePrismaDefault(col.default));
|
|
2119
|
+
if (mapped.db)
|
|
2120
|
+
attrs.push(mapped.db);
|
|
2121
|
+
lines.push(` ${field} ${mapped.type}${optional}${attrs.length ? ` ${attrs.join(" ")}` : ""}`);
|
|
2122
|
+
}
|
|
2123
|
+
for (const rel of table.relations ?? []) {
|
|
2124
|
+
const optional = rel.fields.some((f) => table.columns[f]?.nullable) ? "?" : "";
|
|
2125
|
+
const relName = `${pascalCase(tableName)}_${rel.name}`;
|
|
2126
|
+
const fields = rel.fields.map(snakeToCamel).join(", ");
|
|
2127
|
+
const refs = rel.referencedColumns.map(snakeToCamel).join(", ");
|
|
2128
|
+
lines.push(` ${rel.name} ${pascalCase(rel.references)}${optional} @relation("${relName}", fields: [${fields}], references: [${refs}])`);
|
|
2129
|
+
}
|
|
2130
|
+
for (const back of backRelations) {
|
|
2131
|
+
lines.push(` ${back.field} ${back.model}[] @relation("${back.relationName}")`);
|
|
2132
|
+
}
|
|
2133
|
+
const block = [];
|
|
2134
|
+
for (const [colName, col] of Object.entries(table.columns)) {
|
|
2135
|
+
if (col.indexed)
|
|
2136
|
+
block.push(` @@index([${snakeToCamel(colName)}])`);
|
|
2137
|
+
}
|
|
2138
|
+
for (const cols of table.indexes ?? []) {
|
|
2139
|
+
block.push(` @@index([${cols.map(snakeToCamel).join(", ")}])`);
|
|
2140
|
+
}
|
|
2141
|
+
for (const cols of table.uniqueKeys ?? []) {
|
|
2142
|
+
block.push(` @@unique([${cols.map(snakeToCamel).join(", ")}])`);
|
|
2143
|
+
}
|
|
2144
|
+
block.push(` @@map("${tableName}")`);
|
|
2145
|
+
return `model ${pascalCase(tableName)} {
|
|
2146
|
+
${lines.join(`
|
|
2147
|
+
`)}
|
|
2148
|
+
|
|
2149
|
+
${block.join(`
|
|
2150
|
+
`)}
|
|
2151
|
+
}`;
|
|
2152
|
+
}
|
|
2153
|
+
function computeBackRelations(schema) {
|
|
2154
|
+
const map = new Map;
|
|
2155
|
+
for (const [owningTable, table] of Object.entries(schema)) {
|
|
2156
|
+
for (const rel of table.relations ?? []) {
|
|
2157
|
+
const list = map.get(rel.references) ?? [];
|
|
2158
|
+
list.push({
|
|
2159
|
+
field: `${snakeToCamel(owningTable)}${pascalCase(rel.name)}`,
|
|
2160
|
+
model: pascalCase(owningTable),
|
|
2161
|
+
relationName: `${pascalCase(owningTable)}_${rel.name}`
|
|
2162
|
+
});
|
|
2163
|
+
map.set(rel.references, list);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return map;
|
|
2167
|
+
}
|
|
2168
|
+
function generatePrismaSchema(def, opts = {}) {
|
|
2169
|
+
const schemaName = opts.schemaName ?? pgSchemaName(def.name);
|
|
2170
|
+
const env = opts.datasourceEnv ?? "DATABASE_URL";
|
|
2171
|
+
const header = [
|
|
2172
|
+
`// Generated by \`sl generate prisma\` from subgraph "${def.name}". Do not edit by hand.`,
|
|
2173
|
+
"",
|
|
2174
|
+
"datasource db {",
|
|
2175
|
+
' provider = "postgresql"',
|
|
2176
|
+
` url = env("${env}")`,
|
|
2177
|
+
` schemas = ["${schemaName}"]`,
|
|
2178
|
+
"}",
|
|
2179
|
+
"",
|
|
2180
|
+
"generator client {",
|
|
2181
|
+
' provider = "prisma-client-js"',
|
|
2182
|
+
' previewFeatures = ["multiSchema"]',
|
|
2183
|
+
"}"
|
|
2184
|
+
].join(`
|
|
2185
|
+
`);
|
|
2186
|
+
const backRelations = computeBackRelations(def.schema);
|
|
2187
|
+
const modelBlocks = Object.entries(def.schema).map(([tableName, table]) => {
|
|
2188
|
+
const body = renderTable(tableName, table, backRelations.get(tableName) ?? []);
|
|
2189
|
+
return body.replace(/\n}$/, `
|
|
2190
|
+
@@schema("${schemaName}")
|
|
2191
|
+
}`);
|
|
2192
|
+
});
|
|
2193
|
+
if (opts.modelsOnly)
|
|
2194
|
+
return `${modelBlocks.join(`
|
|
2195
|
+
|
|
2196
|
+
`)}
|
|
2197
|
+
`;
|
|
2198
|
+
return `${header}
|
|
2199
|
+
|
|
2200
|
+
${modelBlocks.join(`
|
|
2201
|
+
|
|
2202
|
+
`)}
|
|
2203
|
+
`;
|
|
2204
|
+
}
|
|
2205
|
+
// src/schema/drizzle.ts
|
|
2206
|
+
var DRIZZLE_BUILDER = {
|
|
2207
|
+
uint: (c) => `numeric("${c}")`,
|
|
2208
|
+
int: (c) => `numeric("${c}")`,
|
|
2209
|
+
text: (c) => `text("${c}")`,
|
|
2210
|
+
principal: (c) => `text("${c}")`,
|
|
2211
|
+
boolean: (c) => `boolean("${c}")`,
|
|
2212
|
+
timestamp: (c) => `timestamp("${c}", { withTimezone: true })`,
|
|
2213
|
+
jsonb: (c) => `jsonb("${c}")`
|
|
2214
|
+
};
|
|
2215
|
+
var BUILDERS_USED = {
|
|
2216
|
+
uint: "numeric",
|
|
2217
|
+
int: "numeric",
|
|
2218
|
+
text: "text",
|
|
2219
|
+
principal: "text",
|
|
2220
|
+
boolean: "boolean",
|
|
2221
|
+
timestamp: "timestamp",
|
|
2222
|
+
jsonb: "jsonb"
|
|
2223
|
+
};
|
|
2224
|
+
function snakeToCamel2(name) {
|
|
2225
|
+
return name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
2226
|
+
}
|
|
2227
|
+
function pascalCase2(name) {
|
|
2228
|
+
const camel = snakeToCamel2(name);
|
|
2229
|
+
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
2230
|
+
}
|
|
2231
|
+
function literalDefault(value) {
|
|
2232
|
+
return typeof value === "string" ? `"${value.replace(/"/g, "\\\"")}"` : `${value}`;
|
|
2233
|
+
}
|
|
2234
|
+
function renderTable2(varName, tableName, table) {
|
|
2235
|
+
const cols = [
|
|
2236
|
+
` id: bigserial("_id", { mode: "bigint" }).primaryKey(),`,
|
|
2237
|
+
` blockHeight: bigint("_block_height", { mode: "bigint" }).notNull(),`,
|
|
2238
|
+
` txId: text("_tx_id").notNull(),`,
|
|
2239
|
+
` createdAt: timestamp("_created_at", { withTimezone: true }).notNull().defaultNow(),`
|
|
2240
|
+
];
|
|
2241
|
+
for (const [colName, col] of Object.entries(table.columns)) {
|
|
2242
|
+
let line = ` ${snakeToCamel2(colName)}: ${DRIZZLE_BUILDER[col.type](colName)}`;
|
|
2243
|
+
if (!col.nullable)
|
|
2244
|
+
line += ".notNull()";
|
|
2245
|
+
if (col.default !== undefined)
|
|
2246
|
+
line += `.default(${literalDefault(col.default)})`;
|
|
2247
|
+
cols.push(`${line},`);
|
|
2248
|
+
}
|
|
2249
|
+
const extras = [];
|
|
2250
|
+
for (const [colName, col] of Object.entries(table.columns)) {
|
|
2251
|
+
if (col.indexed) {
|
|
2252
|
+
const f = snakeToCamel2(colName);
|
|
2253
|
+
extras.push(` ${f}Idx: index("idx_${tableName}_${colName}").on(t.${f})`);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
table.indexes?.forEach((idxCols, i) => {
|
|
2257
|
+
const on = idxCols.map((c) => `t.${snakeToCamel2(c)}`).join(", ");
|
|
2258
|
+
extras.push(` idx${i}: index("idx_${tableName}_${i}").on(${on})`);
|
|
2259
|
+
});
|
|
2260
|
+
table.uniqueKeys?.forEach((uqCols, i) => {
|
|
2261
|
+
const on = uqCols.map((c) => `t.${snakeToCamel2(c)}`).join(", ");
|
|
2262
|
+
extras.push(` uq${i}: uniqueIndex("uq_${tableName}_${i}").on(${on})`);
|
|
2263
|
+
});
|
|
2264
|
+
const extrasBlock = extras.length ? `, (t) => ({
|
|
2265
|
+
${extras.join(`,
|
|
2266
|
+
`)},
|
|
2267
|
+
})` : "";
|
|
2268
|
+
return `export const ${varName} = sg.table("${tableName}", {
|
|
2269
|
+
${cols.join(`
|
|
2270
|
+
`)}
|
|
2271
|
+
}${extrasBlock});`;
|
|
2272
|
+
}
|
|
2273
|
+
function renderRelations(schema) {
|
|
2274
|
+
const back = new Map;
|
|
2275
|
+
for (const [owning, table] of Object.entries(schema)) {
|
|
2276
|
+
for (const rel of table.relations ?? []) {
|
|
2277
|
+
const list = back.get(rel.references) ?? [];
|
|
2278
|
+
list.push({
|
|
2279
|
+
field: `${snakeToCamel2(owning)}${pascalCase2(rel.name)}`,
|
|
2280
|
+
from: snakeToCamel2(owning)
|
|
2281
|
+
});
|
|
2282
|
+
back.set(rel.references, list);
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
const out = [];
|
|
2286
|
+
for (const [tableName, table] of Object.entries(schema)) {
|
|
2287
|
+
const v = snakeToCamel2(tableName);
|
|
2288
|
+
const ones = (table.relations ?? []).map((rel) => {
|
|
2289
|
+
const localFields = rel.fields.map((f) => `${v}.${snakeToCamel2(f)}`).join(", ");
|
|
2290
|
+
const refFields = rel.referencedColumns.map((c) => `${snakeToCamel2(rel.references)}.${snakeToCamel2(c)}`).join(", ");
|
|
2291
|
+
return ` ${rel.name}: one(${snakeToCamel2(rel.references)}, { fields: [${localFields}], references: [${refFields}] }),`;
|
|
2292
|
+
});
|
|
2293
|
+
const manys = (back.get(tableName) ?? []).map((b) => ` ${b.field}: many(${b.from}),`);
|
|
2294
|
+
if (ones.length === 0 && manys.length === 0)
|
|
2295
|
+
continue;
|
|
2296
|
+
const helpers = [ones.length ? "one" : "", manys.length ? "many" : ""].filter(Boolean).join(", ");
|
|
2297
|
+
out.push(`export const ${v}Relations = relations(${v}, ({ ${helpers} }) => ({
|
|
2298
|
+
${[...ones, ...manys].join(`
|
|
2299
|
+
`)}
|
|
2300
|
+
}));`);
|
|
2301
|
+
}
|
|
2302
|
+
return out;
|
|
2303
|
+
}
|
|
2304
|
+
function generateDrizzleSchema(def, opts = {}) {
|
|
2305
|
+
const schemaName = opts.schemaName ?? pgSchemaName(def.name);
|
|
2306
|
+
const used = new Set(["bigserial", "bigint", "text", "timestamp"]);
|
|
2307
|
+
let needsIndex = false;
|
|
2308
|
+
let needsUnique = false;
|
|
2309
|
+
for (const table of Object.values(def.schema)) {
|
|
2310
|
+
for (const col of Object.values(table.columns))
|
|
2311
|
+
used.add(BUILDERS_USED[col.type]);
|
|
2312
|
+
if (Object.values(table.columns).some((c) => c.indexed) || table.indexes?.length)
|
|
2313
|
+
needsIndex = true;
|
|
2314
|
+
if (table.uniqueKeys?.length)
|
|
2315
|
+
needsUnique = true;
|
|
2316
|
+
}
|
|
2317
|
+
if (needsIndex)
|
|
2318
|
+
used.add("index");
|
|
2319
|
+
if (needsUnique)
|
|
2320
|
+
used.add("uniqueIndex");
|
|
2321
|
+
used.add("pgSchema");
|
|
2322
|
+
const hasRelations = Object.values(def.schema).some((t) => t.relations?.length);
|
|
2323
|
+
const imports = [
|
|
2324
|
+
`import { ${[...used].sort().join(", ")} } from "drizzle-orm/pg-core";`,
|
|
2325
|
+
...hasRelations ? [`import { relations } from "drizzle-orm";`] : []
|
|
2326
|
+
].join(`
|
|
2327
|
+
`);
|
|
2328
|
+
const tables = Object.entries(def.schema).map(([name, table]) => renderTable2(snakeToCamel2(name), name, table));
|
|
2329
|
+
const relationDecls = renderRelations(def.schema);
|
|
2330
|
+
const typeExports = Object.keys(def.schema).map((name) => `export type ${pascalCase2(name)} = typeof ${snakeToCamel2(name)}.$inferSelect;`);
|
|
2331
|
+
return [
|
|
2332
|
+
`// Generated by \`sl generate drizzle\` from subgraph "${def.name}". Do not edit by hand.`,
|
|
2333
|
+
imports,
|
|
2334
|
+
"",
|
|
2335
|
+
`export const sg = pgSchema("${schemaName}");`,
|
|
2336
|
+
"",
|
|
2337
|
+
tables.join(`
|
|
2338
|
+
|
|
2339
|
+
`),
|
|
2340
|
+
...relationDecls.length ? ["", relationDecls.join(`
|
|
2341
|
+
|
|
2342
|
+
`)] : [],
|
|
2343
|
+
"",
|
|
2344
|
+
typeExports.join(`
|
|
2345
|
+
`),
|
|
2346
|
+
""
|
|
2347
|
+
].join(`
|
|
2348
|
+
`);
|
|
2349
|
+
}
|
|
1971
2350
|
// src/schema/deployer.ts
|
|
1972
2351
|
import { sql as sql4 } from "kysely";
|
|
1973
2352
|
function toJsonSafe(obj) {
|
|
@@ -2021,10 +2400,29 @@ function bumpPatch(version) {
|
|
|
2021
2400
|
const patch = Number.parseInt(parts[2] ?? "0", 10);
|
|
2022
2401
|
return `${parts[0]}.${parts[1]}.${Number.isNaN(patch) ? 1 : patch + 1}`;
|
|
2023
2402
|
}
|
|
2403
|
+
function renderDeployPlan(def, schemaName) {
|
|
2404
|
+
validateSubgraphDefinition(def);
|
|
2405
|
+
const { statements } = generateSubgraphSQL(def, schemaName);
|
|
2406
|
+
const schema = schemaName ?? pgSchemaName(def.name);
|
|
2407
|
+
const grantScript = [
|
|
2408
|
+
"-- Run once on YOUR database as an owner/superuser, replacing <role>",
|
|
2409
|
+
"-- with the role whose credentials you give Secondlayer.",
|
|
2410
|
+
"-- Secondlayer then creates and owns only this one schema:",
|
|
2411
|
+
`GRANT CREATE ON DATABASE current_database() TO <role>;`,
|
|
2412
|
+
`-- (after first deploy <role> owns "${schema}"; no further grants needed)`
|
|
2413
|
+
].join(`
|
|
2414
|
+
`);
|
|
2415
|
+
return { schemaName: schema, statements, grantScript };
|
|
2416
|
+
}
|
|
2024
2417
|
async function deploySchema(db, def, handlerPath, opts) {
|
|
2025
2418
|
validateSubgraphDefinition(def);
|
|
2026
2419
|
const { statements, hash } = generateSubgraphSQL(def, opts?.schemaName);
|
|
2027
2420
|
const { getSubgraph, registerSubgraph } = await import("@secondlayer/shared/db/queries/subgraphs");
|
|
2421
|
+
const ddlDb = opts?.dataDb ?? db;
|
|
2422
|
+
const byo = opts?.dataDb != null;
|
|
2423
|
+
const refuseDestructiveOnByo = (reason) => {
|
|
2424
|
+
throw new Error(`Breaking schema change on a BYO subgraph (${reason}) would drop data in your database. Drop the schema "${opts?.schemaName ?? pgSchemaName(def.name)}" manually and re-deploy to rebuild.`);
|
|
2425
|
+
};
|
|
2028
2426
|
const existing = await getSubgraph(db, def.name, opts?.accountId);
|
|
2029
2427
|
const schemaName = opts?.schemaName ?? pgSchemaName(def.name);
|
|
2030
2428
|
const newVersion = opts?.version ?? (existing ? bumpPatch(existing.version) : "1.0.0");
|
|
@@ -2046,7 +2444,8 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
2046
2444
|
handlerCode: opts?.handlerCode,
|
|
2047
2445
|
sourceCode: opts?.sourceCode,
|
|
2048
2446
|
schemaName,
|
|
2049
|
-
startBlock: def.startBlock
|
|
2447
|
+
startBlock: def.startBlock,
|
|
2448
|
+
databaseUrlEnc: opts?.databaseUrlEnc ?? null
|
|
2050
2449
|
};
|
|
2051
2450
|
if (existing) {
|
|
2052
2451
|
const schemaExists = await sql4`
|
|
@@ -2054,10 +2453,10 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
2054
2453
|
SELECT 1 FROM information_schema.schemata
|
|
2055
2454
|
WHERE schema_name = ${schemaName}
|
|
2056
2455
|
) AS "exists"
|
|
2057
|
-
`.execute(
|
|
2456
|
+
`.execute(ddlDb).then((r) => r.rows[0]?.exists ?? false);
|
|
2058
2457
|
if (!schemaExists) {
|
|
2059
2458
|
for (const stmt of statements) {
|
|
2060
|
-
await sql4.raw(stmt).execute(
|
|
2459
|
+
await sql4.raw(stmt).execute(ddlDb);
|
|
2061
2460
|
}
|
|
2062
2461
|
const sg2 = await registerSubgraph(db, regData);
|
|
2063
2462
|
return { action: "reindexed", subgraphId: sg2.id, version: newVersion };
|
|
@@ -2076,9 +2475,11 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
2076
2475
|
};
|
|
2077
2476
|
}
|
|
2078
2477
|
if (existing.schema_hash === hash && opts?.forceReindex) {
|
|
2079
|
-
|
|
2478
|
+
if (byo)
|
|
2479
|
+
refuseDestructiveOnByo("force reindex");
|
|
2480
|
+
await sql4.raw(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`).execute(ddlDb);
|
|
2080
2481
|
for (const stmt of statements) {
|
|
2081
|
-
await sql4.raw(stmt).execute(
|
|
2482
|
+
await sql4.raw(stmt).execute(ddlDb);
|
|
2082
2483
|
}
|
|
2083
2484
|
const sg2 = await registerSubgraph(db, regData);
|
|
2084
2485
|
return { action: "reindexed", subgraphId: sg2.id, version: newVersion };
|
|
@@ -2087,9 +2488,12 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
2087
2488
|
const diff = diffSchema(existing.definition.schema, def.schema);
|
|
2088
2489
|
const { breaking, reasons } = hasBreakingChanges(diff);
|
|
2089
2490
|
if (breaking || opts?.forceReindex) {
|
|
2090
|
-
|
|
2491
|
+
if (byo) {
|
|
2492
|
+
refuseDestructiveOnByo(reasons.length > 0 ? reasons.join("; ") : "force reindex");
|
|
2493
|
+
}
|
|
2494
|
+
await sql4.raw(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`).execute(ddlDb);
|
|
2091
2495
|
for (const stmt of statements) {
|
|
2092
|
-
await sql4.raw(stmt).execute(
|
|
2496
|
+
await sql4.raw(stmt).execute(ddlDb);
|
|
2093
2497
|
}
|
|
2094
2498
|
const sg3 = await registerSubgraph(db, regData);
|
|
2095
2499
|
const deployDiff2 = {
|
|
@@ -2126,15 +2530,15 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
2126
2530
|
await sql4.raw(`CREATE TABLE IF NOT EXISTS ${qualifiedName} (
|
|
2127
2531
|
${colDefs.join(`,
|
|
2128
2532
|
`)}
|
|
2129
|
-
)`).execute(
|
|
2130
|
-
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`).execute(
|
|
2131
|
-
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`).execute(
|
|
2533
|
+
)`).execute(ddlDb);
|
|
2534
|
+
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_block_height ON ${qualifiedName} (_block_height)`).execute(ddlDb);
|
|
2535
|
+
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_tx_id ON ${qualifiedName} (_tx_id)`).execute(ddlDb);
|
|
2132
2536
|
for (const [colName, col] of Object.entries(tableDef.columns)) {
|
|
2133
2537
|
if (col.indexed) {
|
|
2134
|
-
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`).execute(
|
|
2538
|
+
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`).execute(ddlDb);
|
|
2135
2539
|
}
|
|
2136
2540
|
if (col.search) {
|
|
2137
|
-
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(
|
|
2541
|
+
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
|
|
2138
2542
|
}
|
|
2139
2543
|
}
|
|
2140
2544
|
}
|
|
@@ -2153,12 +2557,12 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
2153
2557
|
if (!sqlType)
|
|
2154
2558
|
continue;
|
|
2155
2559
|
const nullable = col.nullable ? "" : ` NOT NULL DEFAULT ${getDefault(col.type)}`;
|
|
2156
|
-
await sql4.raw(`ALTER TABLE ${qualifiedName} ADD COLUMN ${colName} ${sqlType}${nullable}`).execute(
|
|
2560
|
+
await sql4.raw(`ALTER TABLE ${qualifiedName} ADD COLUMN ${colName} ${sqlType}${nullable}`).execute(ddlDb);
|
|
2157
2561
|
if (col.indexed) {
|
|
2158
|
-
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`).execute(
|
|
2562
|
+
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName} ON ${qualifiedName} (${colName})`).execute(ddlDb);
|
|
2159
2563
|
}
|
|
2160
2564
|
if (col.search) {
|
|
2161
|
-
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(
|
|
2565
|
+
await sql4.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
|
|
2162
2566
|
}
|
|
2163
2567
|
}
|
|
2164
2568
|
}
|
|
@@ -2183,7 +2587,7 @@ async function deploySchema(db, def, handlerPath, opts) {
|
|
|
2183
2587
|
}
|
|
2184
2588
|
}
|
|
2185
2589
|
for (const stmt of statements) {
|
|
2186
|
-
await sql4.raw(stmt).execute(
|
|
2590
|
+
await sql4.raw(stmt).execute(ddlDb);
|
|
2187
2591
|
}
|
|
2188
2592
|
const sg = await registerSubgraph(db, regData);
|
|
2189
2593
|
return { action: "created", subgraphId: sg.id, version: newVersion };
|
|
@@ -2209,14 +2613,17 @@ function getDefault(type) {
|
|
|
2209
2613
|
export {
|
|
2210
2614
|
validateSubgraphDefinition,
|
|
2211
2615
|
resumeReindex,
|
|
2616
|
+
renderDeployPlan,
|
|
2212
2617
|
reindexSubgraph,
|
|
2213
2618
|
pgSchemaName,
|
|
2214
2619
|
generateSubgraphSQL,
|
|
2620
|
+
generatePrismaSchema,
|
|
2621
|
+
generateDrizzleSchema,
|
|
2215
2622
|
diffSchema,
|
|
2216
2623
|
deploySchema,
|
|
2217
2624
|
defineSubgraph,
|
|
2218
2625
|
backfillSubgraph
|
|
2219
2626
|
};
|
|
2220
2627
|
|
|
2221
|
-
//# debugId=
|
|
2628
|
+
//# debugId=12416214D391B0FF64756E2164756E21
|
|
2222
2629
|
//# sourceMappingURL=index.js.map
|