@secondlayer/subgraphs 3.2.1 → 3.3.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 +71 -9
- 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/README.md
CHANGED
|
@@ -93,6 +93,87 @@ Delivery bodies and response previews land in `subscription_deliveries`. Rows wh
|
|
|
93
93
|
| `SUBGRAPH_REINDEX_BATCH_SIZE` | plan-based | Override the default historical block batch size used by reindex/backfill. |
|
|
94
94
|
| `SUBGRAPH_REINDEX_MIN_BATCH_SIZE` | plan-based | Override the adaptive lower bound for reindex/backfill batches. |
|
|
95
95
|
| `SUBGRAPH_REINDEX_MAX_BATCH_SIZE` | plan-based | Override the adaptive upper bound for reindex/backfill batches. |
|
|
96
|
+
| `DATABASE_MAX_POOLS` | `25` | Max cached connection pools. With BYO subgraphs (one pool per user DB) the least-recently-used pool is evicted past this cap; the source/target pools are never evicted. |
|
|
97
|
+
| `DATABASE_IDLE_TIMEOUT` | `300` | Seconds before idle connections are closed (`0` = never). Keeps a fleet of BYO pools from pinning connections. |
|
|
98
|
+
|
|
99
|
+
## Bring your own database (BYO data plane)
|
|
100
|
+
|
|
101
|
+
There are three tiers, not two:
|
|
102
|
+
|
|
103
|
+
| Tier | Indexer / decode | Handler exec | Database | Serving API |
|
|
104
|
+
| --- | --- | --- | --- | --- |
|
|
105
|
+
| Managed (default) | ours | ours | ours | ours |
|
|
106
|
+
| **BYO data plane** | ours | ours | **yours** | **yours** |
|
|
107
|
+
| Self-host | yours | yours | yours | yours |
|
|
108
|
+
|
|
109
|
+
With BYO, the managed pipeline (ingest → decode → match → run your handler) is
|
|
110
|
+
unchanged, but your handler's rows land in **your** Postgres and the serving API
|
|
111
|
+
reads from there. Deploy with a connection string:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
sl subgraphs deploy subgraphs/my.ts --database-url "postgres://user:pass@your-host:5432/db"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The connection string is stored encrypted at rest (AES-GCM, keyed by
|
|
118
|
+
`SECONDLAYER_SECRETS_KEY`) and never returned in API responses. The server
|
|
119
|
+
verifies the connection before deploying. Once deployed, query the
|
|
120
|
+
`subgraph_…` schema directly with any ORM/GraphQL/REST — we're no longer in the
|
|
121
|
+
serving path.
|
|
122
|
+
|
|
123
|
+
**Preview before it touches your DB.** `POST /api/subgraphs` with
|
|
124
|
+
`{"dryRun": true, "databaseUrl": "…"}` returns the exact DDL plus a grant
|
|
125
|
+
script and verifies the connection — without writing anything.
|
|
126
|
+
|
|
127
|
+
**Constraints (v1):**
|
|
128
|
+
|
|
129
|
+
- **Idempotent handlers only.** A BYO block write can't share the managed
|
|
130
|
+
transaction, so a crash replays the block (at-least-once). `ctx.insert` and
|
|
131
|
+
`ctx.upsert` (with a unique key) are safe — flush is replace-per-height. A
|
|
132
|
+
deploy with non-idempotent `ctx.update` / `ctx.patchOrInsert` is rejected.
|
|
133
|
+
- **No reindex.** Reindex would drop + rebuild the schema in your DB from a
|
|
134
|
+
background job; instead, re-deploy to rebuild (or drop the schema yourself).
|
|
135
|
+
- **Delete leaves your data.** Deleting the subgraph removes our registry row
|
|
136
|
+
(and the stored connection) and pauses subscriptions, but never drops the
|
|
137
|
+
schema in your database.
|
|
138
|
+
|
|
139
|
+
**Recommended:** give Secondlayer a least-privilege role scoped to its own
|
|
140
|
+
schema, and point `--database-url` at a session-mode pooler endpoint
|
|
141
|
+
(PgBouncer/Neon/Supabase) rather than raw Postgres.
|
|
142
|
+
|
|
143
|
+
### ORM codegen
|
|
144
|
+
|
|
145
|
+
Once rows land in your DB, generate a typed schema for your ORM:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
sl subgraphs codegen subgraphs/my.ts --target prisma -o prisma/schema.prisma
|
|
149
|
+
sl subgraphs codegen subgraphs/my.ts --target drizzle -o db/schema.ts
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Prisma and Drizzle have first-class generators (`generatePrismaSchema` /
|
|
153
|
+
`generateDrizzleSchema` are exported); both emit relations/`@relation` from the
|
|
154
|
+
schema's `relations` metadata. For Kysely, run `kysely-codegen` against the DB.
|
|
155
|
+
Output mirrors the deployed DDL — `prisma db pull` should be a no-op; treat the
|
|
156
|
+
tables as read-only (the processor owns them) and never `migrate`/`push`.
|
|
157
|
+
`uint`→`Decimal`/`numeric` and the `BigInt` id need `.toString()` for JSON.
|
|
158
|
+
|
|
159
|
+
## Trait-scoped sources
|
|
160
|
+
|
|
161
|
+
A source can target a SIP standard instead of a fixed contract — it indexes
|
|
162
|
+
every contract the registry classifies as that standard (incl. ones deployed
|
|
163
|
+
later):
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
sources: {
|
|
167
|
+
tokens: { type: "ft_transfer", trait: "sip-010" }, // all SIP-010 tokens
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`trait` (`sip-009` | `sip-010` | `sip-013`) is supported on FT/NFT/`contract_call`/
|
|
172
|
+
`print_event` filters and composes (AND) with other fields. Token filters match
|
|
173
|
+
the asset-identifier's contract; `contract_call`/`print` match `contract_id`.
|
|
174
|
+
Resolution is as-of-block, so a reindex backfills a contract's full history even
|
|
175
|
+
if it was classified after deploy. Requires the contract registry to be
|
|
176
|
+
populated. Discover the set via `GET /v1/contracts?trait=sip-010`.
|
|
96
177
|
|
|
97
178
|
## Postgres + pool mode
|
|
98
179
|
|
package/dist/src/index.d.ts
CHANGED
|
@@ -9,6 +9,22 @@ interface SubgraphColumn {
|
|
|
9
9
|
search?: boolean;
|
|
10
10
|
default?: string | number | boolean;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* A foreign-key relation to another table in the same subgraph. Drives DDL FK
|
|
14
|
+
* constraints and ORM codegen (`@relation` in Prisma, `relations()` in Drizzle)
|
|
15
|
+
* so generated clients get typed joins. The referenced columns must form a
|
|
16
|
+
* `uniqueKeys` entry on the target table.
|
|
17
|
+
*/
|
|
18
|
+
interface SubgraphRelation {
|
|
19
|
+
/** Relation field name on this table's generated model (e.g. "pool"). */
|
|
20
|
+
name: string;
|
|
21
|
+
/** Target table name in this subgraph. */
|
|
22
|
+
references: string;
|
|
23
|
+
/** Local column(s) holding the foreign key. */
|
|
24
|
+
fields: string[];
|
|
25
|
+
/** Target column(s) the fields point at (a uniqueKeys entry on the target). */
|
|
26
|
+
referencedColumns: string[];
|
|
27
|
+
}
|
|
12
28
|
/** Table definition within a subgraph schema */
|
|
13
29
|
interface SubgraphTable {
|
|
14
30
|
columns: Record<string, SubgraphColumn>;
|
|
@@ -16,6 +32,8 @@ interface SubgraphTable {
|
|
|
16
32
|
indexes?: string[][];
|
|
17
33
|
/** Unique key constraints (each entry is an array of column names). Required for upsert. */
|
|
18
34
|
uniqueKeys?: string[][];
|
|
35
|
+
/** Foreign-key relations to other tables (for typed ORM joins). */
|
|
36
|
+
relations?: SubgraphRelation[];
|
|
19
37
|
}
|
|
20
38
|
/** Subgraph schema — maps table names to table definitions */
|
|
21
39
|
type SubgraphSchema = Record<string, SubgraphTable>;
|
|
@@ -42,39 +60,47 @@ interface StxLockFilter {
|
|
|
42
60
|
lockedAddress?: string;
|
|
43
61
|
minAmount?: bigint;
|
|
44
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Restrict a source to contracts conforming to a trait/standard (e.g. "sip-010")
|
|
65
|
+
* instead of a fixed contract — resolved from the contract registry at match time,
|
|
66
|
+
* as-of each processed block. Lets a source index "all SIP-010 tokens" etc.
|
|
67
|
+
*/
|
|
68
|
+
type TraitScope = {
|
|
69
|
+
trait?: string
|
|
70
|
+
};
|
|
45
71
|
/** FT event filters */
|
|
46
|
-
interface FtTransferFilter {
|
|
72
|
+
interface FtTransferFilter extends TraitScope {
|
|
47
73
|
type: "ft_transfer";
|
|
48
74
|
assetIdentifier?: string;
|
|
49
75
|
sender?: string;
|
|
50
76
|
recipient?: string;
|
|
51
77
|
minAmount?: bigint;
|
|
52
78
|
}
|
|
53
|
-
interface FtMintFilter {
|
|
79
|
+
interface FtMintFilter extends TraitScope {
|
|
54
80
|
type: "ft_mint";
|
|
55
81
|
assetIdentifier?: string;
|
|
56
82
|
recipient?: string;
|
|
57
83
|
minAmount?: bigint;
|
|
58
84
|
}
|
|
59
|
-
interface FtBurnFilter {
|
|
85
|
+
interface FtBurnFilter extends TraitScope {
|
|
60
86
|
type: "ft_burn";
|
|
61
87
|
assetIdentifier?: string;
|
|
62
88
|
sender?: string;
|
|
63
89
|
minAmount?: bigint;
|
|
64
90
|
}
|
|
65
91
|
/** NFT event filters */
|
|
66
|
-
interface NftTransferFilter {
|
|
92
|
+
interface NftTransferFilter extends TraitScope {
|
|
67
93
|
type: "nft_transfer";
|
|
68
94
|
assetIdentifier?: string;
|
|
69
95
|
sender?: string;
|
|
70
96
|
recipient?: string;
|
|
71
97
|
}
|
|
72
|
-
interface NftMintFilter {
|
|
98
|
+
interface NftMintFilter extends TraitScope {
|
|
73
99
|
type: "nft_mint";
|
|
74
100
|
assetIdentifier?: string;
|
|
75
101
|
recipient?: string;
|
|
76
102
|
}
|
|
77
|
-
interface NftBurnFilter {
|
|
103
|
+
interface NftBurnFilter extends TraitScope {
|
|
78
104
|
type: "nft_burn";
|
|
79
105
|
assetIdentifier?: string;
|
|
80
106
|
sender?: string;
|
|
@@ -121,7 +147,7 @@ interface ContractCallEvent {
|
|
|
121
147
|
};
|
|
122
148
|
}
|
|
123
149
|
/** Contract event filters */
|
|
124
|
-
interface ContractCallFilter {
|
|
150
|
+
interface ContractCallFilter extends TraitScope {
|
|
125
151
|
type: "contract_call";
|
|
126
152
|
contractId?: string;
|
|
127
153
|
functionName?: string;
|
|
@@ -139,7 +165,7 @@ interface ContractDeployFilter {
|
|
|
139
165
|
deployer?: string;
|
|
140
166
|
contractName?: string;
|
|
141
167
|
}
|
|
142
|
-
interface PrintEventFilter {
|
|
168
|
+
interface PrintEventFilter extends TraitScope {
|
|
143
169
|
type: "print_event";
|
|
144
170
|
contractId?: string;
|
|
145
171
|
topic?: string;
|
|
@@ -559,6 +585,22 @@ interface GeneratedSQL {
|
|
|
559
585
|
* each with auto-columns and indexes.
|
|
560
586
|
*/
|
|
561
587
|
declare function generateSubgraphSQL(def: SubgraphDefinition, schemaNameOverride?: string): GeneratedSQL;
|
|
588
|
+
interface PrismaGenOptions {
|
|
589
|
+
/** Postgres schema the tables live in (account-scoped). */
|
|
590
|
+
schemaName?: string;
|
|
591
|
+
/** env var the datasource url reads from. */
|
|
592
|
+
datasourceEnv?: string;
|
|
593
|
+
/**
|
|
594
|
+
* Emit only the `model` blocks (no datasource/generator). Lets users compose
|
|
595
|
+
* these models with their own schema via Prisma's `prismaSchemaFolder`.
|
|
596
|
+
*/
|
|
597
|
+
modelsOnly?: boolean;
|
|
598
|
+
}
|
|
599
|
+
declare function generatePrismaSchema(def: SubgraphDefinition, opts?: PrismaGenOptions): string;
|
|
600
|
+
interface DrizzleGenOptions {
|
|
601
|
+
schemaName?: string;
|
|
602
|
+
}
|
|
603
|
+
declare function generateDrizzleSchema(def: SubgraphDefinition, opts?: DrizzleGenOptions): string;
|
|
562
604
|
import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
|
|
563
605
|
import { Database } from "@secondlayer/shared/db";
|
|
564
606
|
import { Kysely } from "kysely";
|
|
@@ -586,6 +628,18 @@ interface DeployDiff {
|
|
|
586
628
|
addedColumns: Record<string, string[]>;
|
|
587
629
|
breakingChanges: string[];
|
|
588
630
|
}
|
|
631
|
+
interface DeployPlan {
|
|
632
|
+
schemaName: string;
|
|
633
|
+
/** DDL Secondlayer will run against your database. */
|
|
634
|
+
statements: string[];
|
|
635
|
+
/** Least-privilege grant script to run once, before deploying. */
|
|
636
|
+
grantScript: string;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Render the DDL + grant script a BYO deploy would run, without executing.
|
|
640
|
+
* Powers `--dry-run`: the user reviews exactly what touches their DB first.
|
|
641
|
+
*/
|
|
642
|
+
declare function renderDeployPlan(def: SubgraphDefinition, schemaName?: string): DeployPlan;
|
|
589
643
|
/**
|
|
590
644
|
* Deploy a subgraph schema to the database.
|
|
591
645
|
* - New subgraph → CREATE SCHEMA + tables + register
|
|
@@ -601,10 +655,18 @@ declare function deploySchema(db: AnyDb, def: SubgraphDefinition, handlerPath: s
|
|
|
601
655
|
version?: string
|
|
602
656
|
handlerCode?: string
|
|
603
657
|
sourceCode?: string
|
|
658
|
+
/**
|
|
659
|
+
* BYO data plane: when set, schema DDL (CREATE/ALTER/index) runs against
|
|
660
|
+
* the user-owned DB while the subgraphs registry row stays on `db`
|
|
661
|
+
* (managed). Defaults to `db` — managed deploys are unchanged.
|
|
662
|
+
*/
|
|
663
|
+
dataDb?: AnyDb
|
|
664
|
+
/** Encrypted user-DB connection string to persist on the registry row. */
|
|
665
|
+
databaseUrlEnc?: Buffer | null
|
|
604
666
|
}): Promise<{
|
|
605
667
|
action: "created" | "unchanged" | "handler_updated" | "updated" | "reindexed"
|
|
606
668
|
subgraphId: string
|
|
607
669
|
version: string
|
|
608
670
|
diff?: DeployDiff
|
|
609
671
|
}>;
|
|
610
|
-
export { validateSubgraphDefinition, resumeReindex, reindexSubgraph, pgSchemaName, generateSubgraphSQL, diffSchema, deploySchema, defineSubgraph, backfillSubgraph, WriteRow, WhereInput, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TableDiff, SystemRow, SubgraphTableClient, SubgraphTable, SubgraphSchema, SubgraphHandler, SubgraphFilter, SubgraphDefinition, SubgraphContext, SubgraphColumn, StxTransferPayload, StxTransferFilter, StxMintPayload, StxMintFilter, StxLockPayload, StxLockFilter, StxBurnPayload, StxBurnFilter, RowValue, ReindexOptions, PrintEventPayload, PrintEventFor, PrintEventFilter, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, InferTableRow, InferSubgraphClient, InferColumnType, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, EventForFilter, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, AnyEvent };
|
|
672
|
+
export { validateSubgraphDefinition, resumeReindex, renderDeployPlan, reindexSubgraph, pgSchemaName, generateSubgraphSQL, generatePrismaSchema, generateDrizzleSchema, diffSchema, deploySchema, defineSubgraph, backfillSubgraph, WriteRow, WhereInput, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TableDiff, SystemRow, 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 };
|