@secondlayer/subgraphs 4.4.1 → 5.0.1

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.
Files changed (65) hide show
  1. package/README.md +13 -21
  2. package/dist/{src/index.d.ts → index.d.ts} +59 -189
  3. package/dist/index.js +2151 -0
  4. package/dist/index.js.map +24 -0
  5. package/dist/{src/runtime → runtime}/emitter.d.ts +17 -11
  6. package/dist/{src/runtime → runtime}/emitter.js +58 -18
  7. package/dist/runtime/emitter.js.map +19 -0
  8. package/dist/{src/runtime → runtime}/replay.d.ts +15 -1
  9. package/dist/{src/runtime → runtime}/replay.js +418 -723
  10. package/dist/{src/runtime → runtime}/replay.js.map +6 -6
  11. package/dist/{src/schema → schema}/index.d.ts +10 -69
  12. package/dist/{src/schema → schema}/index.js +188 -281
  13. package/dist/schema/index.js.map +13 -0
  14. package/dist/{src/testing → testing}/index.d.ts +65 -115
  15. package/dist/{src/testing → testing}/index.js +554 -968
  16. package/dist/testing/index.js.map +21 -0
  17. package/dist/{src/types.d.ts → types.d.ts} +10 -66
  18. package/dist/{src/validate.d.ts → validate.d.ts} +11 -69
  19. package/dist/{src/validate.js → validate.js} +18 -98
  20. package/dist/validate.js.map +10 -0
  21. package/package.json +18 -26
  22. package/dist/src/index.js +0 -6081
  23. package/dist/src/index.js.map +0 -44
  24. package/dist/src/runtime/block-processor.d.ts +0 -559
  25. package/dist/src/runtime/block-processor.js +0 -3810
  26. package/dist/src/runtime/block-processor.js.map +0 -31
  27. package/dist/src/runtime/catchup.d.ts +0 -473
  28. package/dist/src/runtime/catchup.js +0 -4228
  29. package/dist/src/runtime/catchup.js.map +0 -35
  30. package/dist/src/runtime/clarity.d.ts +0 -15
  31. package/dist/src/runtime/clarity.js +0 -41
  32. package/dist/src/runtime/clarity.js.map +0 -10
  33. package/dist/src/runtime/context.d.ts +0 -325
  34. package/dist/src/runtime/context.js +0 -936
  35. package/dist/src/runtime/context.js.map +0 -13
  36. package/dist/src/runtime/emitter.js.map +0 -19
  37. package/dist/src/runtime/processor.d.ts +0 -11
  38. package/dist/src/runtime/processor.js +0 -5372
  39. package/dist/src/runtime/processor.js.map +0 -40
  40. package/dist/src/runtime/reindex.d.ts +0 -539
  41. package/dist/src/runtime/reindex.js +0 -4802
  42. package/dist/src/runtime/reindex.js.map +0 -37
  43. package/dist/src/runtime/reorg.d.ts +0 -432
  44. package/dist/src/runtime/reorg.js +0 -4355
  45. package/dist/src/runtime/reorg.js.map +0 -36
  46. package/dist/src/runtime/runner.d.ts +0 -719
  47. package/dist/src/runtime/runner.js +0 -1332
  48. package/dist/src/runtime/runner.js.map +0 -16
  49. package/dist/src/runtime/source-matcher.d.ts +0 -304
  50. package/dist/src/runtime/source-matcher.js +0 -335
  51. package/dist/src/runtime/source-matcher.js.map +0 -10
  52. package/dist/src/runtime/stats.d.ts +0 -23
  53. package/dist/src/runtime/stats.js +0 -71
  54. package/dist/src/runtime/stats.js.map +0 -10
  55. package/dist/src/schema/index.js.map +0 -13
  56. package/dist/src/service.d.ts +0 -0
  57. package/dist/src/service.js +0 -5398
  58. package/dist/src/service.js.map +0 -41
  59. package/dist/src/testing/index.js.map +0 -22
  60. package/dist/src/triggers/index.d.ts +0 -381
  61. package/dist/src/triggers/index.js +0 -31
  62. package/dist/src/triggers/index.js.map +0 -10
  63. package/dist/src/validate.js.map +0 -10
  64. /package/dist/{src/types.js → types.js} +0 -0
  65. /package/dist/{src/types.js.map → types.js.map} +0 -0
package/dist/index.js ADDED
@@ -0,0 +1,2151 @@
1
+ // src/define.ts
2
+ function defineSubgraph(def) {
3
+ return def;
4
+ }
5
+ function defineSchema(schema) {
6
+ return schema;
7
+ }
8
+ // src/validate.ts
9
+ import { z } from "zod";
10
+ var SubgraphNameSchema = z.string().min(1).max(63).regex(/^[a-z][a-z0-9-]*$/, "Must start with lowercase letter, contain only lowercase alphanumeric and hyphens");
11
+ var SqlIdentifierSchema = z.string().min(1).max(63).regex(/^[a-z_][a-z0-9_]*$/i, "Must be a valid SQL identifier: start with a letter or underscore, then letters/digits/underscores only");
12
+ var ColumnTypeSchema = z.enum([
13
+ "text",
14
+ "uint",
15
+ "int",
16
+ "principal",
17
+ "boolean",
18
+ "timestamp",
19
+ "jsonb"
20
+ ]);
21
+ var PrintFieldSchema = z.lazy(() => z.union([
22
+ ColumnTypeSchema,
23
+ z.object({ type: PrintFieldSchema, optional: z.literal(true) }),
24
+ z.object({ tuple: z.record(z.string(), PrintFieldSchema) }),
25
+ z.object({ list: PrintFieldSchema })
26
+ ]));
27
+ var AbiTypeSchema = z.lazy(() => z.union([
28
+ z.string(),
29
+ z.object({ buff: z.object({ length: z.number() }) }),
30
+ z.object({ "string-ascii": z.object({ length: z.number() }) }),
31
+ z.object({ "string-utf8": z.object({ length: z.number() }) }),
32
+ z.object({ optional: AbiTypeSchema }),
33
+ z.object({
34
+ list: z.object({ type: AbiTypeSchema, length: z.number() })
35
+ }),
36
+ z.object({
37
+ tuple: z.array(z.object({ name: z.string(), type: AbiTypeSchema }))
38
+ }),
39
+ z.object({
40
+ response: z.object({ ok: AbiTypeSchema, error: AbiTypeSchema })
41
+ })
42
+ ]));
43
+ var AbiContractSchema = z.object({
44
+ functions: z.array(z.object({
45
+ name: z.string(),
46
+ access: z.enum(["public", "read-only", "private"], {
47
+ message: "ABI access must be public | read-only | private — a raw Hiro/Clarinet ABI uses `read_only`. Normalize it first: normalizeAbi() from @secondlayer/stacks/clarity."
48
+ }),
49
+ args: z.array(z.object({ name: z.string(), type: AbiTypeSchema })),
50
+ outputs: AbiTypeSchema
51
+ })),
52
+ maps: z.array(z.unknown()).optional(),
53
+ variables: z.array(z.unknown()).optional(),
54
+ fungible_tokens: z.array(z.unknown()).optional(),
55
+ non_fungible_tokens: z.array(z.unknown()).optional(),
56
+ implemented_traits: z.array(z.unknown()).optional(),
57
+ defined_traits: z.array(z.unknown()).optional()
58
+ });
59
+ var SubgraphColumnSchema = z.object({
60
+ type: ColumnTypeSchema,
61
+ nullable: z.boolean().optional(),
62
+ indexed: z.boolean().optional(),
63
+ search: z.boolean().optional(),
64
+ default: z.union([z.string(), z.number(), z.boolean()]).optional()
65
+ });
66
+ var SubgraphTableSchema = z.object({
67
+ columns: z.record(SqlIdentifierSchema, SubgraphColumnSchema).refine((c) => Object.keys(c).length > 0, "Table must have at least one column"),
68
+ indexes: z.array(z.array(SqlIdentifierSchema)).optional(),
69
+ uniqueKeys: z.array(z.array(SqlIdentifierSchema)).optional()
70
+ });
71
+ var SubgraphSchemaSchema = z.record(SqlIdentifierSchema, SubgraphTableSchema).refine((s) => Object.keys(s).length > 0, "Schema must have at least one table");
72
+ var VALID_FILTER_TYPES = [
73
+ "stx_transfer",
74
+ "stx_mint",
75
+ "stx_burn",
76
+ "stx_lock",
77
+ "ft_transfer",
78
+ "ft_mint",
79
+ "ft_burn",
80
+ "nft_transfer",
81
+ "nft_mint",
82
+ "nft_burn",
83
+ "contract_call",
84
+ "contract_deploy",
85
+ "print_event",
86
+ "nested_contract_call",
87
+ "var_set",
88
+ "map_set",
89
+ "map_insert",
90
+ "map_delete"
91
+ ];
92
+ var contractIdField = z.union([
93
+ z.string(),
94
+ z.array(z.string()).min(1).max(20)
95
+ ]);
96
+ var traitScope = { trait: z.string().optional() };
97
+ var factoryScope = {
98
+ factory: z.object({ from: z.string().min(1), field: z.string().min(1) }).strict().optional()
99
+ };
100
+ var amountRange = {
101
+ minAmount: z.bigint().optional(),
102
+ maxAmount: z.bigint().optional()
103
+ };
104
+ var SubgraphFilterUnion = z.discriminatedUnion("type", [
105
+ z.object({
106
+ type: z.literal("stx_transfer"),
107
+ sender: z.string().optional(),
108
+ recipient: z.string().optional(),
109
+ ...amountRange
110
+ }).strict(),
111
+ z.object({
112
+ type: z.literal("stx_mint"),
113
+ recipient: z.string().optional(),
114
+ minAmount: z.bigint().optional()
115
+ }).strict(),
116
+ z.object({
117
+ type: z.literal("stx_burn"),
118
+ sender: z.string().optional(),
119
+ minAmount: z.bigint().optional()
120
+ }).strict(),
121
+ z.object({
122
+ type: z.literal("stx_lock"),
123
+ lockedAddress: z.string().optional(),
124
+ minAmount: z.bigint().optional()
125
+ }).strict(),
126
+ z.object({
127
+ type: z.literal("ft_transfer"),
128
+ assetIdentifier: z.string().optional(),
129
+ sender: z.string().optional(),
130
+ recipient: z.string().optional(),
131
+ minAmount: z.bigint().optional(),
132
+ ...traitScope
133
+ }).strict(),
134
+ z.object({
135
+ type: z.literal("ft_mint"),
136
+ assetIdentifier: z.string().optional(),
137
+ recipient: z.string().optional(),
138
+ minAmount: z.bigint().optional(),
139
+ ...traitScope
140
+ }).strict(),
141
+ z.object({
142
+ type: z.literal("ft_burn"),
143
+ assetIdentifier: z.string().optional(),
144
+ sender: z.string().optional(),
145
+ minAmount: z.bigint().optional(),
146
+ ...traitScope
147
+ }).strict(),
148
+ z.object({
149
+ type: z.literal("nft_transfer"),
150
+ assetIdentifier: z.string().optional(),
151
+ sender: z.string().optional(),
152
+ recipient: z.string().optional(),
153
+ ...traitScope
154
+ }).strict(),
155
+ z.object({
156
+ type: z.literal("nft_mint"),
157
+ assetIdentifier: z.string().optional(),
158
+ recipient: z.string().optional(),
159
+ ...traitScope
160
+ }).strict(),
161
+ z.object({
162
+ type: z.literal("nft_burn"),
163
+ assetIdentifier: z.string().optional(),
164
+ sender: z.string().optional(),
165
+ ...traitScope
166
+ }).strict(),
167
+ z.object({
168
+ type: z.literal("contract_call"),
169
+ contractId: contractIdField.optional(),
170
+ functionName: z.string().optional(),
171
+ caller: z.string().optional(),
172
+ abi: AbiContractSchema.optional(),
173
+ ...traitScope,
174
+ ...factoryScope
175
+ }).strict(),
176
+ z.object({
177
+ type: z.literal("contract_deploy"),
178
+ deployer: z.string().optional(),
179
+ contractName: z.string().optional()
180
+ }).strict(),
181
+ z.object({
182
+ type: z.literal("print_event"),
183
+ contractId: contractIdField.optional(),
184
+ topic: z.string().optional(),
185
+ prints: z.record(z.string(), z.record(z.string(), PrintFieldSchema)).optional(),
186
+ ...traitScope,
187
+ ...factoryScope
188
+ }).strict(),
189
+ z.object({
190
+ type: z.literal("nested_contract_call"),
191
+ contractId: contractIdField.optional(),
192
+ functionName: z.string().optional(),
193
+ caller: z.string().optional(),
194
+ sender: z.string().optional(),
195
+ ...traitScope,
196
+ ...factoryScope
197
+ }).strict(),
198
+ z.object({
199
+ type: z.literal("var_set"),
200
+ contractId: contractIdField.optional(),
201
+ varName: z.string().optional(),
202
+ ...traitScope,
203
+ ...factoryScope
204
+ }).strict(),
205
+ z.object({
206
+ type: z.literal("map_set"),
207
+ contractId: contractIdField.optional(),
208
+ map: z.string().optional(),
209
+ ...traitScope,
210
+ ...factoryScope
211
+ }).strict(),
212
+ z.object({
213
+ type: z.literal("map_insert"),
214
+ contractId: contractIdField.optional(),
215
+ map: z.string().optional(),
216
+ ...traitScope,
217
+ ...factoryScope
218
+ }).strict(),
219
+ z.object({
220
+ type: z.literal("map_delete"),
221
+ contractId: contractIdField.optional(),
222
+ map: z.string().optional(),
223
+ ...traitScope,
224
+ ...factoryScope
225
+ }).strict()
226
+ ]);
227
+ var SubgraphFilterSchema = SubgraphFilterUnion;
228
+ function hasPinnedContractId(contractId) {
229
+ if (typeof contractId === "string")
230
+ return contractId.length > 0;
231
+ if (Array.isArray(contractId))
232
+ return contractId.length > 0;
233
+ return false;
234
+ }
235
+ function refineDeployGates(def, ctx) {
236
+ for (const [name, filter] of Object.entries(def.sources)) {
237
+ if (filter.type === "print_event") {
238
+ const pinned = hasPinnedContractId(filter.contractId) && !filter.trait;
239
+ if (pinned) {
240
+ const prints = filter.prints;
241
+ if (prints === undefined || typeof prints !== "object" || Object.keys(prints).length === 0) {
242
+ ctx.addIssue({
243
+ code: "custom",
244
+ path: ["sources", name, "prints"],
245
+ message: `print_event source "${name}" has no prints map — field names would be untyped and empty tables would typecheck. Declare prints from index_print_schema / create --from-contract.`
246
+ });
247
+ }
248
+ }
249
+ }
250
+ if (filter.type === "contract_call" && filter.functionName !== undefined) {
251
+ if (filter.abi === undefined) {
252
+ ctx.addIssue({
253
+ code: "custom",
254
+ path: ["sources", name, "abi"],
255
+ message: `contract_call source "${name}" has functionName but no abi. \`secondlayer subgraphs deploy\` fills it in from the deployed contract; otherwise pass abi (normalizeAbi() from @secondlayer/stacks/clarity).`
256
+ });
257
+ }
258
+ }
259
+ }
260
+ for (const name of Object.keys(def.sources)) {
261
+ if (def.handlers[name] == null) {
262
+ ctx.addIssue({
263
+ code: "custom",
264
+ path: ["handlers", name],
265
+ message: `source "${name}" has no handler. Add handlers.${name}, or its events are skipped and the table stays empty.`
266
+ });
267
+ }
268
+ }
269
+ }
270
+ var SubgraphDefinitionSchema = z.object({
271
+ name: SubgraphNameSchema,
272
+ description: z.string().optional(),
273
+ startBlock: z.number().int().nonnegative().optional(),
274
+ backfillMode: z.enum(["blocking", "concurrent"]).optional(),
275
+ sources: z.record(z.string(), SubgraphFilterSchema).refine((s) => Object.keys(s).length > 0, "Must have at least one source"),
276
+ schema: SubgraphSchemaSchema,
277
+ handlers: z.record(z.string(), z.any())
278
+ }).superRefine((def, ctx) => {
279
+ refineDeployGates(def, ctx);
280
+ });
281
+ function validateSubgraphDefinition(def) {
282
+ return SubgraphDefinitionSchema.parse(def);
283
+ }
284
+ function filterFieldsByType() {
285
+ const options = SubgraphFilterUnion?._def?.options ?? [];
286
+ const out = [];
287
+ for (const option of options) {
288
+ const shape = option?.shape ?? option?._def?.shape;
289
+ if (!shape)
290
+ continue;
291
+ const literal = shape.type?._def?.values?.[0] ?? shape.type?._def?.value ?? undefined;
292
+ if (typeof literal !== "string")
293
+ continue;
294
+ out.push({
295
+ type: literal,
296
+ fields: Object.keys(shape).filter((k) => k !== "type")
297
+ });
298
+ }
299
+ return out;
300
+ }
301
+ // src/print-schema.ts
302
+ import {
303
+ deserializeCV
304
+ } from "@secondlayer/stacks/clarity";
305
+ function camelizeDataKey(str) {
306
+ return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
307
+ }
308
+ function camelizeKeys(obj) {
309
+ if (obj === null || obj === undefined)
310
+ return obj;
311
+ if (typeof obj !== "object")
312
+ return obj;
313
+ if (Array.isArray(obj))
314
+ return obj.map(camelizeKeys);
315
+ const result = {};
316
+ for (const [k, v] of Object.entries(obj)) {
317
+ result[camelizeDataKey(k)] = camelizeKeys(v);
318
+ }
319
+ return result;
320
+ }
321
+ function cvToTree(cv) {
322
+ switch (cv.type) {
323
+ case "uint":
324
+ case "int":
325
+ return { kind: cv.type };
326
+ case "true":
327
+ case "false":
328
+ return { kind: "bool" };
329
+ case "address":
330
+ case "contract":
331
+ return { kind: "principal" };
332
+ case "buffer":
333
+ return { kind: "buffer", len: cv.value.length / 2 };
334
+ case "ascii":
335
+ return { kind: "ascii", len: cv.value.length };
336
+ case "utf8":
337
+ return { kind: "utf8", len: new TextEncoder().encode(cv.value).length };
338
+ case "none":
339
+ return { kind: "optional", inner: null };
340
+ case "some":
341
+ return { kind: "optional", inner: cvToTree(cv.value) };
342
+ case "ok":
343
+ return { kind: "response", ok: cvToTree(cv.value), err: null };
344
+ case "err":
345
+ return { kind: "response", ok: null, err: cvToTree(cv.value) };
346
+ case "list": {
347
+ let inner = null;
348
+ for (const el of cv.value) {
349
+ const t = cvToTree(el);
350
+ inner = inner ? unify(inner, t) : t;
351
+ }
352
+ return { kind: "list", inner };
353
+ }
354
+ case "tuple": {
355
+ const entries = new Map;
356
+ for (const [k, v] of Object.entries(cv.value)) {
357
+ entries.set(k, { tree: cvToTree(v), present: 1 });
358
+ }
359
+ return { kind: "tuple", count: 1, entries };
360
+ }
361
+ }
362
+ }
363
+ function unifyNullable(a, b) {
364
+ if (!a)
365
+ return b;
366
+ if (!b)
367
+ return a;
368
+ return unify(a, b);
369
+ }
370
+ var UNION_KIND_ORDER = {
371
+ uint: 0,
372
+ int: 1,
373
+ bool: 2,
374
+ principal: 3,
375
+ buffer: 4,
376
+ ascii: 5,
377
+ utf8: 6,
378
+ list: 7,
379
+ tuple: 8,
380
+ response: 9
381
+ };
382
+ function makeUnion(members) {
383
+ const sorted = [...members].sort((a, b) => (UNION_KIND_ORDER[a.kind] ?? 99) - (UNION_KIND_ORDER[b.kind] ?? 99));
384
+ return { kind: "union", members: sorted };
385
+ }
386
+ function unify(a, b) {
387
+ if (a.kind === "optional" || b.kind === "optional") {
388
+ const ai = a.kind === "optional" ? a.inner : a;
389
+ const bi = b.kind === "optional" ? b.inner : b;
390
+ return { kind: "optional", inner: unifyNullable(ai, bi) };
391
+ }
392
+ if (a.kind === "union")
393
+ return unionAdd(a.members, b);
394
+ if (b.kind === "union")
395
+ return unionAdd(b.members, a);
396
+ if (a.kind !== b.kind)
397
+ return makeUnion([a, b]);
398
+ switch (a.kind) {
399
+ case "uint":
400
+ case "int":
401
+ case "bool":
402
+ case "principal":
403
+ return a;
404
+ case "buffer":
405
+ case "ascii":
406
+ case "utf8":
407
+ return { kind: a.kind, len: Math.max(a.len, b.len) };
408
+ case "list":
409
+ return {
410
+ kind: "list",
411
+ inner: unifyNullable(a.inner, b.inner)
412
+ };
413
+ case "response": {
414
+ const rb = b;
415
+ return {
416
+ kind: "response",
417
+ ok: unifyNullable(a.ok, rb.ok),
418
+ err: unifyNullable(a.err, rb.err)
419
+ };
420
+ }
421
+ case "tuple": {
422
+ const tb = b;
423
+ const entries = new Map([...a.entries].map(([k, e]) => [k, { ...e }]));
424
+ for (const [k, e] of tb.entries) {
425
+ const existing = entries.get(k);
426
+ entries.set(k, existing ? {
427
+ tree: unify(existing.tree, e.tree),
428
+ present: existing.present + e.present
429
+ } : { ...e });
430
+ }
431
+ return { kind: "tuple", count: a.count + tb.count, entries };
432
+ }
433
+ }
434
+ }
435
+ function unionAdd(members, t) {
436
+ if (t.kind === "union") {
437
+ let acc = { kind: "union", members };
438
+ for (const m of t.members)
439
+ acc = unify(acc, m);
440
+ return acc;
441
+ }
442
+ const next = [...members];
443
+ for (let i = 0;i < next.length; i++) {
444
+ const member = next[i];
445
+ if (!member)
446
+ continue;
447
+ const merged = unify(member, t);
448
+ if (merged.kind !== "union") {
449
+ next[i] = merged;
450
+ return makeUnion(next);
451
+ }
452
+ }
453
+ next.push(t);
454
+ return makeUnion(next);
455
+ }
456
+ function wrapOptional(t) {
457
+ return t.kind === "optional" ? t : { kind: "optional", inner: t };
458
+ }
459
+ function renderClarity(t) {
460
+ if (!t)
461
+ return "?";
462
+ switch (t.kind) {
463
+ case "uint":
464
+ case "int":
465
+ case "bool":
466
+ case "principal":
467
+ return t.kind;
468
+ case "buffer":
469
+ return `(buff ${t.len})`;
470
+ case "ascii":
471
+ return `(string-ascii ${t.len})`;
472
+ case "utf8":
473
+ return `(string-utf8 ${t.len})`;
474
+ case "optional":
475
+ return `(optional ${renderClarity(t.inner)})`;
476
+ case "list":
477
+ return `(list ${renderClarity(t.inner)})`;
478
+ case "response":
479
+ return `(response ${renderClarity(t.ok)} ${renderClarity(t.err)})`;
480
+ case "tuple": {
481
+ const parts = [...t.entries].map(([k, e]) => {
482
+ const tree = e.present < t.count ? wrapOptional(e.tree) : e.tree;
483
+ return `(${k} ${renderClarity(tree)})`;
484
+ });
485
+ return `(tuple ${parts.join(" ")})`;
486
+ }
487
+ case "union":
488
+ return t.members.map(renderClarity).join(" | ");
489
+ }
490
+ }
491
+ function renderTs(t) {
492
+ if (!t)
493
+ return "unknown";
494
+ switch (t.kind) {
495
+ case "uint":
496
+ case "int":
497
+ return "bigint";
498
+ case "bool":
499
+ return "boolean";
500
+ case "principal":
501
+ case "buffer":
502
+ case "ascii":
503
+ case "utf8":
504
+ return "string";
505
+ case "optional":
506
+ return t.inner ? `${renderTs(t.inner)} | null` : "unknown | null";
507
+ case "list": {
508
+ const inner = renderTs(t.inner);
509
+ return inner.includes(" | ") ? `(${inner})[]` : `${inner}[]`;
510
+ }
511
+ case "response": {
512
+ const sides = [...new Set([renderTs(t.ok), renderTs(t.err)])];
513
+ return sides.join(" | ");
514
+ }
515
+ case "tuple": {
516
+ const parts = [...t.entries].map(([k, e]) => {
517
+ const opt = e.present < t.count ? "?" : "";
518
+ return `${camelizeDataKey(k)}${opt}: ${renderTs(e.tree)}`;
519
+ });
520
+ return `{ ${parts.join("; ")} }`;
521
+ }
522
+ case "union":
523
+ return [...new Set(t.members.map((m) => renderTs(m)))].join(" | ");
524
+ }
525
+ }
526
+ function toColumnType(t) {
527
+ if (!t)
528
+ return "jsonb";
529
+ switch (t.kind) {
530
+ case "uint":
531
+ return "uint";
532
+ case "int":
533
+ return "int";
534
+ case "bool":
535
+ return "boolean";
536
+ case "principal":
537
+ return "principal";
538
+ case "buffer":
539
+ case "ascii":
540
+ case "utf8":
541
+ return "text";
542
+ case "list":
543
+ case "tuple":
544
+ return "jsonb";
545
+ case "optional":
546
+ return toColumnType(t.inner);
547
+ case "response":
548
+ return t.ok ? toColumnType(t.ok) : "jsonb";
549
+ case "union":
550
+ return "jsonb";
551
+ }
552
+ }
553
+ var MAX_DECODED_PER_TOPIC_NEWEST = 75;
554
+ var MAX_DECODED_PER_TOPIC_OLDEST = 25;
555
+ function inferPrintTopics(samples) {
556
+ const groups = new Map;
557
+ for (const s of samples) {
558
+ const group = groups.get(s.topic);
559
+ if (group)
560
+ group.push(s);
561
+ else
562
+ groups.set(s.topic, [s]);
563
+ }
564
+ const out = [];
565
+ for (const [topic, rows] of groups) {
566
+ let first = Number.POSITIVE_INFINITY;
567
+ let last = Number.NEGATIVE_INFINITY;
568
+ for (const r of rows) {
569
+ if (r.blockHeight < first)
570
+ first = r.blockHeight;
571
+ if (r.blockHeight > last)
572
+ last = r.blockHeight;
573
+ }
574
+ const withHex = [...rows].filter((r) => r.rawHex !== null).sort((a, b) => b.blockHeight - a.blockHeight);
575
+ const budget = MAX_DECODED_PER_TOPIC_NEWEST + MAX_DECODED_PER_TOPIC_OLDEST;
576
+ const picked = withHex.length <= budget ? withHex : [
577
+ ...withHex.slice(0, MAX_DECODED_PER_TOPIC_NEWEST),
578
+ ...withHex.slice(-MAX_DECODED_PER_TOPIC_OLDEST)
579
+ ];
580
+ const tuples = [];
581
+ let decoded = 0;
582
+ for (const p of picked) {
583
+ try {
584
+ const cv = deserializeCV(p.rawHex);
585
+ decoded++;
586
+ if (cv.type === "tuple")
587
+ tuples.push(cv);
588
+ } catch {}
589
+ }
590
+ const nonTuple = decoded > 0 && tuples.length === 0;
591
+ const fields = [];
592
+ if (!nonTuple) {
593
+ const stats = new Map;
594
+ for (const t of tuples) {
595
+ for (const [key, value] of Object.entries(t.value)) {
596
+ if (key === "topic")
597
+ continue;
598
+ const tree = cvToTree(value);
599
+ const existing = stats.get(key);
600
+ if (existing) {
601
+ existing.present++;
602
+ existing.tree = unify(existing.tree, tree);
603
+ if (value.type === "none")
604
+ existing.noneCount++;
605
+ if (value.type === "none" || value.type === "some") {
606
+ existing.optionalSeen = true;
607
+ }
608
+ } else {
609
+ stats.set(key, {
610
+ tree,
611
+ present: 1,
612
+ noneCount: value.type === "none" ? 1 : 0,
613
+ optionalSeen: value.type === "none" || value.type === "some"
614
+ });
615
+ }
616
+ }
617
+ }
618
+ for (const [name, st] of [...stats].sort(([a], [b]) => a.localeCompare(b))) {
619
+ const field = {
620
+ name,
621
+ camel_name: camelizeDataKey(name),
622
+ clarity_type: renderClarity(st.tree),
623
+ ts_type: renderTs(st.tree),
624
+ column_type: toColumnType(st.tree),
625
+ always_present: st.present === tuples.length
626
+ };
627
+ if (st.optionalSeen) {
628
+ field.optional_some_rate = (st.present - st.noneCount) / st.present;
629
+ }
630
+ fields.push(field);
631
+ }
632
+ }
633
+ out.push({
634
+ topic,
635
+ count: rows.length,
636
+ first_height: first,
637
+ last_height: last,
638
+ non_tuple: nonTuple,
639
+ fields
640
+ });
641
+ }
642
+ return out.sort((a, b) => b.count - a.count);
643
+ }
644
+ // src/runtime/chain-read.ts
645
+ import { getSourceDb } from "@secondlayer/shared/db";
646
+ import { logger } from "@secondlayer/shared/logger";
647
+ import { http, createPublicClient } from "@secondlayer/stacks";
648
+ import {
649
+ ContractResponseError,
650
+ buildFunctionArgs,
651
+ isResponseOutput,
652
+ readContract
653
+ } from "@secondlayer/stacks/actions";
654
+ import {
655
+ clarityValueToJSUntyped,
656
+ deserializeCV as deserializeCV2,
657
+ normalizeAbi,
658
+ serializeCV,
659
+ toCamelCase
660
+ } from "@secondlayer/stacks/clarity";
661
+ function readContractAt(ctx, contractId, abi, options) {
662
+ return ctx.client.contract(contractId, abi, options);
663
+ }
664
+
665
+ class ChainReadError extends Error {
666
+ name = "ChainReadError";
667
+ }
668
+ function nodeClient(rpcUrl) {
669
+ return createPublicClient({
670
+ transport: http(rpcUrl, { retryCount: 0, timeout: 1e4 })
671
+ });
672
+ }
673
+
674
+ class ConcurrencyGate {
675
+ limit;
676
+ active = 0;
677
+ queue = [];
678
+ constructor(limit) {
679
+ this.limit = limit;
680
+ }
681
+ async run(fn) {
682
+ if (this.active >= this.limit) {
683
+ await new Promise((resolve) => this.queue.push(resolve));
684
+ }
685
+ this.active++;
686
+ try {
687
+ return await fn();
688
+ } finally {
689
+ this.active--;
690
+ this.queue.shift()?.();
691
+ }
692
+ }
693
+ }
694
+ var DEFAULT_MAX_CONCURRENT_READS = 4;
695
+ function maxConcurrentReads() {
696
+ const raw = process.env.SUBGRAPH_CHAIN_READ_CONCURRENCY;
697
+ const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
698
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_CONCURRENT_READS;
699
+ }
700
+ var gate = new ConcurrencyGate(maxConcurrentReads());
701
+ function hashArgs(args) {
702
+ const serialized = args.map((arg) => serializeCV(arg)).join(",");
703
+ return Bun.hash(serialized).toString(16);
704
+ }
705
+ function findFunction(abi, name) {
706
+ const fn = abi.functions.find((f) => f.name === name);
707
+ if (!fn) {
708
+ throw new ChainReadError(`No function "${name}" in the supplied ABI.`);
709
+ }
710
+ if (fn.access !== "read-only") {
711
+ throw new ChainReadError(`"${name}" is ${fn.access}, not read-only — a handler cannot call it.`);
712
+ }
713
+ return fn;
714
+ }
715
+ function createChainReadClient(ctx) {
716
+ const readValue = async (contractId, abi, functionName, args, options) => {
717
+ const rpcUrl = ctx.rpcUrl ?? process.env.STACKS_NODE_RPC_URL;
718
+ if (!rpcUrl) {
719
+ throw new ChainReadError("ctx.client needs a Stacks node: set STACKS_NODE_RPC_URL on the subgraph processor.");
720
+ }
721
+ const constant = options.cache === "contract-constant";
722
+ if (!constant && !ctx.indexBlockHash) {
723
+ throw new ChainReadError(`Block ${ctx.blockHeight} has no index_block_hash, so a read cannot be pinned to it. Reading at the node's tip would make this handler non-deterministic (the same reindex would produce different rows). Re-ingest the block, or pass cache: "contract-constant" if the value genuinely cannot change.`);
724
+ }
725
+ const fn = findFunction(abi, functionName);
726
+ const clarityArgs = buildFunctionArgs(fn, args);
727
+ const db = ctx.db ?? getSourceDb();
728
+ const argsHash = hashArgs(clarityArgs);
729
+ const pinnedTo = constant ? null : ctx.indexBlockHash;
730
+ const cached = await db.selectFrom("chain_read_cache").select("result_hex").where("contract_id", "=", contractId).where("function_name", "=", fn.name).where("args_hash", "=", argsHash).where((eb) => pinnedTo === null ? eb("index_block_hash", "is", null) : eb("index_block_hash", "=", pinnedTo)).executeTakeFirst();
731
+ const resultHex = cached?.result_hex ?? await gate.run(async () => {
732
+ if (!ctx.indexBlockHash) {
733
+ logger.warn("Chain read is not pinned to a block (no index_block_hash)", {
734
+ contractId,
735
+ functionName: fn.name,
736
+ blockHeight: ctx.blockHeight,
737
+ note: 'cache: "contract-constant" asserts this value cannot change; the node answered at its own tip'
738
+ });
739
+ }
740
+ const value = await readContract(nodeClient(rpcUrl), {
741
+ contract: contractId,
742
+ functionName: fn.name,
743
+ args: clarityArgs,
744
+ tip: ctx.indexBlockHash ?? undefined
745
+ });
746
+ return serializeCV(value);
747
+ });
748
+ if (!cached) {
749
+ await db.insertInto("chain_read_cache").values({
750
+ contract_id: contractId,
751
+ function_name: fn.name,
752
+ args_hash: argsHash,
753
+ index_block_hash: pinnedTo,
754
+ block_height: constant ? null : ctx.blockHeight,
755
+ result_hex: resultHex
756
+ }).onConflict((oc) => oc.doNothing()).execute();
757
+ }
758
+ const value = deserializeCV2(resultHex);
759
+ const js = clarityValueToJSUntyped(fn.outputs, value);
760
+ if (isResponseOutput(fn.outputs)) {
761
+ const response = js;
762
+ if ("ok" in response)
763
+ return response.ok;
764
+ throw new ContractResponseError(`${contractId}::${fn.name} returned an err response.`, response.err);
765
+ }
766
+ return js;
767
+ };
768
+ return {
769
+ contract(contractId, rawAbi, options = {}) {
770
+ const abi = normalizeAbi(rawAbi);
771
+ const camelToKebab = new Map;
772
+ for (const fn of abi.functions) {
773
+ camelToKebab.set(toCamelCase(fn.name), fn.name);
774
+ }
775
+ const read = new Proxy({}, {
776
+ get(_target, prop) {
777
+ const fnName = camelToKebab.get(prop) ?? prop;
778
+ return (args = {}) => readValue(contractId, abi, fnName, args, options);
779
+ }
780
+ });
781
+ return { read };
782
+ }
783
+ };
784
+ }
785
+ // src/schema/generator.ts
786
+ import { createHash } from "node:crypto";
787
+
788
+ // src/schema/utils.ts
789
+ import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
790
+ function quotePgIdent(name) {
791
+ if (/^[a-z_][a-z0-9_]*$/i.test(name))
792
+ return name;
793
+ return `"${name.replace(/"/g, '""')}"`;
794
+ }
795
+ function snakeToCamel(name) {
796
+ return name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
797
+ }
798
+ function pascalCase(name) {
799
+ const camel = snakeToCamel(name);
800
+ return camel.charAt(0).toUpperCase() + camel.slice(1);
801
+ }
802
+
803
+ // src/schema/generator.ts
804
+ var TYPE_MAP = {
805
+ text: "TEXT",
806
+ uint: "NUMERIC",
807
+ int: "NUMERIC",
808
+ principal: "TEXT",
809
+ boolean: "BOOLEAN",
810
+ timestamp: "TIMESTAMPTZ",
811
+ jsonb: "JSONB"
812
+ };
813
+ var MAX_IDENT_BYTES = 63;
814
+ function safeIndexName(name) {
815
+ if (name.length <= MAX_IDENT_BYTES)
816
+ return name;
817
+ const hash = createHash("sha256").update(name).digest("hex").slice(0, 8);
818
+ return `${name.slice(0, MAX_IDENT_BYTES - hash.length - 1)}_${hash}`;
819
+ }
820
+ function emitIndexedColumnIndexDDL(schemaName, tableName, colName, opts) {
821
+ const concurrently = opts?.concurrently ? " CONCURRENTLY" : "";
822
+ const qualified = `${quotePgIdent(schemaName)}.${quotePgIdent(tableName)}`;
823
+ const plain = `idx_${schemaName}_${tableName}_${colName}`;
824
+ const composite = safeIndexName(`idx_${schemaName}_${tableName}_${colName}_id`);
825
+ const col = quotePgIdent(colName);
826
+ const id = quotePgIdent("_id");
827
+ return {
828
+ create: [
829
+ `CREATE INDEX${concurrently} IF NOT EXISTS ${quotePgIdent(plain)} ON ${qualified} (${col})`,
830
+ `CREATE INDEX${concurrently} IF NOT EXISTS ${quotePgIdent(composite)} ON ${qualified} (${col}, ${id})`
831
+ ],
832
+ drop: [
833
+ `DROP INDEX${concurrently} IF EXISTS ${quotePgIdent(schemaName)}.${quotePgIdent(plain)}`,
834
+ `DROP INDEX${concurrently} IF EXISTS ${quotePgIdent(schemaName)}.${quotePgIdent(composite)}`
835
+ ]
836
+ };
837
+ }
838
+ function escapeLiteralDefault(value) {
839
+ if (value === null || value === undefined)
840
+ return "NULL";
841
+ if (typeof value === "number" || typeof value === "bigint")
842
+ return String(value);
843
+ if (typeof value === "boolean")
844
+ return value ? "TRUE" : "FALSE";
845
+ return `'${String(value).replace(/'/g, "''")}'`;
846
+ }
847
+ function tableNeedsTrgm(tableDef) {
848
+ return Object.values(tableDef.columns).some((col) => col.search);
849
+ }
850
+ function emitTableDDL(schemaName, tableName, tableDef) {
851
+ const qualifiedName = `${quotePgIdent(schemaName)}.${quotePgIdent(tableName)}`;
852
+ const statements = [];
853
+ const columnDefs = [
854
+ "_id BIGSERIAL PRIMARY KEY",
855
+ "_block_height BIGINT NOT NULL",
856
+ "_tx_id TEXT NOT NULL",
857
+ "_created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()"
858
+ ];
859
+ for (const [colName, col] of Object.entries(tableDef.columns)) {
860
+ const sqlType = TYPE_MAP[col.type];
861
+ const nullable = col.nullable ? "" : " NOT NULL";
862
+ let colDef = `${quotePgIdent(colName)} ${sqlType}${nullable}`;
863
+ if (col.default !== undefined) {
864
+ colDef += ` DEFAULT ${escapeLiteralDefault(col.default)}`;
865
+ }
866
+ if (col.type === "uint") {
867
+ colDef += ` CHECK (${quotePgIdent(colName)} >= 0)`;
868
+ }
869
+ columnDefs.push(colDef);
870
+ }
871
+ statements.push(`CREATE TABLE IF NOT EXISTS ${qualifiedName} (
872
+ ${columnDefs.join(`,
873
+ `)}
874
+ )`);
875
+ statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_block_height`)} ON ${qualifiedName} (_block_height)`);
876
+ statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_tx_id`)} ON ${qualifiedName} (_tx_id)`);
877
+ for (const [colName, col] of Object.entries(tableDef.columns)) {
878
+ if (col.indexed) {
879
+ statements.push(...emitIndexedColumnIndexDDL(schemaName, tableName, colName).create);
880
+ }
881
+ }
882
+ for (const [colName, col] of Object.entries(tableDef.columns)) {
883
+ if (col.search) {
884
+ statements.push(`CREATE INDEX IF NOT EXISTS ${quotePgIdent(`idx_${schemaName}_${tableName}_${colName}_trgm`)} ON ${qualifiedName} USING gin (${quotePgIdent(colName)} gin_trgm_ops)`);
885
+ }
886
+ }
887
+ if (tableDef.indexes) {
888
+ for (let i = 0;i < tableDef.indexes.length; i++) {
889
+ const cols = tableDef.indexes[i];
890
+ const idxName = quotePgIdent(`idx_${schemaName}_${tableName}_composite_${i}`);
891
+ statements.push(`CREATE INDEX IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.map(quotePgIdent).join(", ")})`);
892
+ }
893
+ }
894
+ if (tableDef.uniqueKeys) {
895
+ for (let i = 0;i < tableDef.uniqueKeys.length; i++) {
896
+ const cols = tableDef.uniqueKeys[i];
897
+ const constraintName = quotePgIdent(`uq_${schemaName}_${tableName}_${cols.join("_")}`);
898
+ statements.push(`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.map(quotePgIdent).join(", ")})`);
899
+ }
900
+ }
901
+ return statements;
902
+ }
903
+ function emitJournalDDL(schemaName) {
904
+ return [
905
+ `CREATE TABLE IF NOT EXISTS ${schemaName}._journal (
906
+ _jid BIGSERIAL PRIMARY KEY,
907
+ block_height BIGINT NOT NULL,
908
+ table_name TEXT NOT NULL,
909
+ row_key JSONB NOT NULL,
910
+ prev_row JSONB,
911
+ _created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
912
+ )`,
913
+ `CREATE INDEX IF NOT EXISTS idx_${schemaName}_journal_height ON ${schemaName}._journal (block_height)`
914
+ ];
915
+ }
916
+ function emitFactoryDDL(schemaName) {
917
+ return [
918
+ `CREATE TABLE IF NOT EXISTS ${schemaName}._factory_addresses (
919
+ source_name TEXT NOT NULL,
920
+ address TEXT NOT NULL,
921
+ block_height BIGINT NOT NULL,
922
+ _created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
923
+ PRIMARY KEY (source_name, address)
924
+ )`,
925
+ `CREATE INDEX IF NOT EXISTS idx_${schemaName}_factory_height ON ${schemaName}._factory_addresses (block_height)`
926
+ ];
927
+ }
928
+ function generateSubgraphSQL(def, schemaNameOverride) {
929
+ const schemaName = schemaNameOverride ?? pgSchemaName(def.name);
930
+ const statements = [];
931
+ const needsTrgm = Object.values(def.schema).some((table) => Object.values(table.columns).some((col) => col.search));
932
+ if (needsTrgm) {
933
+ statements.push("CREATE EXTENSION IF NOT EXISTS pg_trgm");
934
+ }
935
+ statements.push(`CREATE SCHEMA IF NOT EXISTS ${schemaName}`);
936
+ for (const [tableName, tableDef] of Object.entries(def.schema)) {
937
+ statements.push(...emitTableDDL(schemaName, tableName, tableDef));
938
+ }
939
+ statements.push(...emitJournalDDL(schemaName));
940
+ if (Object.values(def.sources ?? {}).some((src) => src.factory !== undefined)) {
941
+ statements.push(...emitFactoryDDL(schemaName));
942
+ }
943
+ const hashInput = JSON.stringify({
944
+ name: def.name,
945
+ schema: def.schema,
946
+ sources: def.sources
947
+ }, (_key, value) => typeof value === "bigint" ? value.toString() : value);
948
+ const hash = createHash("sha256").update(hashInput).digest("hex");
949
+ return { statements, hash };
950
+ }
951
+ // src/schema/prisma.ts
952
+ var PRISMA_TYPE = {
953
+ uint: { type: "Decimal", db: "@db.Numeric" },
954
+ int: { type: "Decimal", db: "@db.Numeric" },
955
+ text: { type: "String" },
956
+ principal: { type: "String" },
957
+ boolean: { type: "Boolean" },
958
+ timestamp: { type: "DateTime", db: "@db.Timestamptz" },
959
+ jsonb: { type: "Json" }
960
+ };
961
+ var SYSTEM_FIELDS = [
962
+ { field: "id", col: "_id", line: "BigInt @id @default(autoincrement())" },
963
+ { field: "blockHeight", col: "_block_height", line: "BigInt" },
964
+ { field: "txId", col: "_tx_id", line: "String" },
965
+ {
966
+ field: "createdAt",
967
+ col: "_created_at",
968
+ line: "DateTime @default(now()) @db.Timestamptz"
969
+ }
970
+ ];
971
+ function quotePrismaDefault(value) {
972
+ if (typeof value === "string")
973
+ return `@default("${value.replace(/"/g, "\\\"")}")`;
974
+ return `@default(${value})`;
975
+ }
976
+ function renderTable(tableName, table) {
977
+ const lines = [];
978
+ for (const sys of SYSTEM_FIELDS) {
979
+ lines.push(` ${sys.field} ${sys.line} @map("${sys.col}")`);
980
+ }
981
+ for (const [colName, col] of Object.entries(table.columns)) {
982
+ const field = snakeToCamel(colName);
983
+ const mapped = PRISMA_TYPE[col.type];
984
+ const optional = col.nullable ? "?" : "";
985
+ const attrs = [];
986
+ if (field !== colName)
987
+ attrs.push(`@map("${colName}")`);
988
+ if (col.default !== undefined)
989
+ attrs.push(quotePrismaDefault(col.default));
990
+ if (mapped.db)
991
+ attrs.push(mapped.db);
992
+ lines.push(` ${field} ${mapped.type}${optional}${attrs.length ? ` ${attrs.join(" ")}` : ""}`);
993
+ }
994
+ const block = [];
995
+ for (const [colName, col] of Object.entries(table.columns)) {
996
+ if (col.indexed)
997
+ block.push(` @@index([${snakeToCamel(colName)}])`);
998
+ }
999
+ for (const cols of table.indexes ?? []) {
1000
+ block.push(` @@index([${cols.map(snakeToCamel).join(", ")}])`);
1001
+ }
1002
+ for (const cols of table.uniqueKeys ?? []) {
1003
+ block.push(` @@unique([${cols.map(snakeToCamel).join(", ")}])`);
1004
+ }
1005
+ block.push(` @@map("${tableName}")`);
1006
+ return `model ${pascalCase(tableName)} {
1007
+ ${lines.join(`
1008
+ `)}
1009
+
1010
+ ${block.join(`
1011
+ `)}
1012
+ }`;
1013
+ }
1014
+ function generatePrismaSchema(def, opts = {}) {
1015
+ const schemaName = opts.schemaName ?? pgSchemaName(def.name);
1016
+ const env = opts.datasourceEnv ?? "DATABASE_URL";
1017
+ const header = [
1018
+ `// Generated by \`sl codegen subgraph --target prisma\` from subgraph "${def.name}". Do not edit by hand.`,
1019
+ "",
1020
+ "datasource db {",
1021
+ ' provider = "postgresql"',
1022
+ ` url = env("${env}")`,
1023
+ ` schemas = ["${schemaName}"]`,
1024
+ "}",
1025
+ "",
1026
+ "generator client {",
1027
+ ' provider = "prisma-client-js"',
1028
+ ' previewFeatures = ["multiSchema"]',
1029
+ "}"
1030
+ ].join(`
1031
+ `);
1032
+ const modelBlocks = Object.entries(def.schema).map(([tableName, table]) => {
1033
+ const body = renderTable(tableName, table);
1034
+ return body.replace(/\n}$/, `
1035
+ @@schema("${schemaName}")
1036
+ }`);
1037
+ });
1038
+ if (opts.modelsOnly)
1039
+ return `${modelBlocks.join(`
1040
+
1041
+ `)}
1042
+ `;
1043
+ return `${header}
1044
+
1045
+ ${modelBlocks.join(`
1046
+
1047
+ `)}
1048
+ `;
1049
+ }
1050
+ // src/schema/drizzle.ts
1051
+ var DRIZZLE_BUILDER = {
1052
+ uint: (c) => `numeric("${c}")`,
1053
+ int: (c) => `numeric("${c}")`,
1054
+ text: (c) => `text("${c}")`,
1055
+ principal: (c) => `text("${c}")`,
1056
+ boolean: (c) => `boolean("${c}")`,
1057
+ timestamp: (c) => `timestamp("${c}", { withTimezone: true })`,
1058
+ jsonb: (c) => `jsonb("${c}")`
1059
+ };
1060
+ var BUILDERS_USED = {
1061
+ uint: "numeric",
1062
+ int: "numeric",
1063
+ text: "text",
1064
+ principal: "text",
1065
+ boolean: "boolean",
1066
+ timestamp: "timestamp",
1067
+ jsonb: "jsonb"
1068
+ };
1069
+ function literalDefault(value) {
1070
+ return typeof value === "string" ? `"${value.replace(/"/g, "\\\"")}"` : `${value}`;
1071
+ }
1072
+ function renderTable2(varName, tableName, table) {
1073
+ const cols = [
1074
+ ` id: bigserial("_id", { mode: "bigint" }).primaryKey(),`,
1075
+ ` blockHeight: bigint("_block_height", { mode: "bigint" }).notNull(),`,
1076
+ ` txId: text("_tx_id").notNull(),`,
1077
+ ` createdAt: timestamp("_created_at", { withTimezone: true }).notNull().defaultNow(),`
1078
+ ];
1079
+ for (const [colName, col] of Object.entries(table.columns)) {
1080
+ let line = ` ${snakeToCamel(colName)}: ${DRIZZLE_BUILDER[col.type](colName)}`;
1081
+ if (!col.nullable)
1082
+ line += ".notNull()";
1083
+ if (col.default !== undefined)
1084
+ line += `.default(${literalDefault(col.default)})`;
1085
+ cols.push(`${line},`);
1086
+ }
1087
+ const extras = [];
1088
+ for (const [colName, col] of Object.entries(table.columns)) {
1089
+ if (col.indexed) {
1090
+ const f = snakeToCamel(colName);
1091
+ extras.push(` ${f}Idx: index("idx_${tableName}_${colName}").on(t.${f})`);
1092
+ }
1093
+ }
1094
+ table.indexes?.forEach((idxCols, i) => {
1095
+ const on = idxCols.map((c) => `t.${snakeToCamel(c)}`).join(", ");
1096
+ extras.push(` idx${i}: index("idx_${tableName}_${i}").on(${on})`);
1097
+ });
1098
+ table.uniqueKeys?.forEach((uqCols, i) => {
1099
+ const on = uqCols.map((c) => `t.${snakeToCamel(c)}`).join(", ");
1100
+ extras.push(` uq${i}: uniqueIndex("uq_${tableName}_${i}").on(${on})`);
1101
+ });
1102
+ const extrasBlock = extras.length ? `, (t) => ({
1103
+ ${extras.join(`,
1104
+ `)},
1105
+ })` : "";
1106
+ return `export const ${varName} = sg.table("${tableName}", {
1107
+ ${cols.join(`
1108
+ `)}
1109
+ }${extrasBlock});`;
1110
+ }
1111
+ function generateDrizzleSchema(def, opts = {}) {
1112
+ const schemaName = opts.schemaName ?? pgSchemaName(def.name);
1113
+ const used = new Set(["bigserial", "bigint", "text", "timestamp"]);
1114
+ let needsIndex = false;
1115
+ let needsUnique = false;
1116
+ for (const table of Object.values(def.schema)) {
1117
+ for (const col of Object.values(table.columns))
1118
+ used.add(BUILDERS_USED[col.type]);
1119
+ if (Object.values(table.columns).some((c) => c.indexed) || table.indexes?.length)
1120
+ needsIndex = true;
1121
+ if (table.uniqueKeys?.length)
1122
+ needsUnique = true;
1123
+ }
1124
+ if (needsIndex)
1125
+ used.add("index");
1126
+ if (needsUnique)
1127
+ used.add("uniqueIndex");
1128
+ used.add("pgSchema");
1129
+ const imports = `import { ${[...used].sort().join(", ")} } from "drizzle-orm/pg-core";`;
1130
+ const tables = Object.entries(def.schema).map(([name, table]) => renderTable2(snakeToCamel(name), name, table));
1131
+ const typeExports = Object.keys(def.schema).map((name) => `export type ${pascalCase(name)} = typeof ${snakeToCamel(name)}.$inferSelect;`);
1132
+ return [
1133
+ `// Generated by \`sl codegen subgraph --target drizzle\` from subgraph "${def.name}". Do not edit by hand.`,
1134
+ imports,
1135
+ "",
1136
+ `export const sg = pgSchema("${schemaName}");`,
1137
+ "",
1138
+ tables.join(`
1139
+
1140
+ `),
1141
+ "",
1142
+ typeExports.join(`
1143
+ `),
1144
+ ""
1145
+ ].join(`
1146
+ `);
1147
+ }
1148
+ // src/schema/kysely.ts
1149
+ var KYSELY_TYPE = {
1150
+ uint: "string",
1151
+ int: "string",
1152
+ text: "string",
1153
+ principal: "string",
1154
+ boolean: "boolean",
1155
+ timestamp: "Date",
1156
+ jsonb: "unknown"
1157
+ };
1158
+ var SYSTEM_FIELDS2 = [
1159
+ { col: "_id", type: "Generated<string>" },
1160
+ { col: "_block_height", type: "string" },
1161
+ { col: "_tx_id", type: "string" },
1162
+ { col: "_created_at", type: "Generated<Date>" }
1163
+ ];
1164
+ function renderTable3(tableName, table) {
1165
+ const lines = SYSTEM_FIELDS2.map((f) => ` ${f.col}: ${f.type};`);
1166
+ for (const [colName, col] of Object.entries(table.columns)) {
1167
+ let type = KYSELY_TYPE[col.type];
1168
+ if (col.nullable)
1169
+ type = `${type} | null`;
1170
+ if (col.default !== undefined)
1171
+ type = `Generated<${type}>`;
1172
+ lines.push(` ${colName}: ${type};`);
1173
+ }
1174
+ return `export interface ${pascalCase(tableName)} {
1175
+ ${lines.join(`
1176
+ `)}
1177
+ }`;
1178
+ }
1179
+ function generateKyselySchema(def, opts = {}) {
1180
+ const schemaName = opts.schemaName ?? pgSchemaName(def.name);
1181
+ const interfaces = Object.entries(def.schema).map(([name, table]) => renderTable3(name, table));
1182
+ const dbEntries = Object.keys(def.schema).map((name) => ` "${schemaName}.${name}": ${pascalCase(name)};`);
1183
+ const dbRegistry = `export interface DB {
1184
+ ${dbEntries.join(`
1185
+ `)}
1186
+ }`;
1187
+ const rowExports = Object.keys(def.schema).map((name) => `export type ${pascalCase(name)}Row = Selectable<${pascalCase(name)}>;`);
1188
+ return [
1189
+ `// Generated by \`sl codegen subgraph --target kysely\` from subgraph "${def.name}". Do not edit by hand.`,
1190
+ `import type { Generated, Selectable } from "kysely";`,
1191
+ "",
1192
+ interfaces.join(`
1193
+
1194
+ `),
1195
+ "",
1196
+ dbRegistry,
1197
+ "",
1198
+ rowExports.join(`
1199
+ `),
1200
+ ""
1201
+ ].join(`
1202
+ `);
1203
+ }
1204
+ // src/schema/index-codegen.ts
1205
+ import {
1206
+ SOURCE_READ_PKS,
1207
+ SOURCE_READ_TYPES
1208
+ } from "@secondlayer/shared/db";
1209
+ function selectTables(opts) {
1210
+ const all = SOURCE_READ_TYPES;
1211
+ const names = opts.tables ?? Object.keys(all);
1212
+ return names.map((name) => {
1213
+ const cols = all[name];
1214
+ if (!cols) {
1215
+ throw new Error(`Unknown Index table "${name}". Known: ${Object.keys(all).sort().join(", ")}`);
1216
+ }
1217
+ return [name, cols];
1218
+ });
1219
+ }
1220
+ var KYSELY_TYPE2 = {
1221
+ text: "string",
1222
+ int: "number",
1223
+ boolean: "boolean",
1224
+ timestamp: "Date",
1225
+ jsonb: "unknown"
1226
+ };
1227
+ function generateKysely(entries, schemaName) {
1228
+ const interfaces = entries.map(([table, cols]) => {
1229
+ const lines = Object.entries(cols).map(([col, def]) => {
1230
+ const type = def.nullable ? `${KYSELY_TYPE2[def.type]} | null` : KYSELY_TYPE2[def.type];
1231
+ return ` ${col}: ${type};`;
1232
+ });
1233
+ return `export interface ${pascalCase(table)} {
1234
+ ${lines.join(`
1235
+ `)}
1236
+ }`;
1237
+ });
1238
+ const dbEntries = entries.map(([table]) => {
1239
+ const key = schemaName ? `${schemaName}.${table}` : table;
1240
+ return ` "${key}": ${pascalCase(table)};`;
1241
+ });
1242
+ const rows = entries.map(([table]) => `export type ${pascalCase(table)}Row = Selectable<${pascalCase(table)}>;`);
1243
+ return [
1244
+ "// Generated by `sl codegen index --target kysely`. Do not edit by hand.",
1245
+ `import type { Selectable } from "kysely";`,
1246
+ "",
1247
+ interfaces.join(`
1248
+
1249
+ `),
1250
+ "",
1251
+ `export interface IndexDB {
1252
+ ${dbEntries.join(`
1253
+ `)}
1254
+ }`,
1255
+ "",
1256
+ rows.join(`
1257
+ `),
1258
+ ""
1259
+ ].join(`
1260
+ `);
1261
+ }
1262
+ var DRIZZLE_BUILDER2 = {
1263
+ text: (c) => `text("${c}")`,
1264
+ int: (c) => `integer("${c}")`,
1265
+ boolean: (c) => `boolean("${c}")`,
1266
+ timestamp: (c) => `timestamp("${c}", { withTimezone: true })`,
1267
+ jsonb: (c) => `jsonb("${c}")`
1268
+ };
1269
+ var DRIZZLE_BUILDER_NAME = {
1270
+ text: "text",
1271
+ int: "integer",
1272
+ boolean: "boolean",
1273
+ timestamp: "timestamp",
1274
+ jsonb: "jsonb"
1275
+ };
1276
+ function generateDrizzle(entries, schemaName) {
1277
+ const used = new Set;
1278
+ for (const [, cols] of entries)
1279
+ for (const def of Object.values(cols))
1280
+ used.add(DRIZZLE_BUILDER_NAME[def.type]);
1281
+ if (schemaName)
1282
+ used.add("pgSchema");
1283
+ else
1284
+ used.add("pgTable");
1285
+ const tableFn = schemaName ? "sg.table" : "pgTable";
1286
+ const tables = entries.map(([table, cols]) => {
1287
+ const lines = Object.entries(cols).map(([col, def]) => {
1288
+ let line = ` ${snakeToCamel(col)}: ${DRIZZLE_BUILDER2[def.type](col)}`;
1289
+ if (!def.nullable)
1290
+ line += ".notNull()";
1291
+ return `${line},`;
1292
+ });
1293
+ return `export const ${snakeToCamel(table)} = ${tableFn}("${table}", {
1294
+ ${lines.join(`
1295
+ `)}
1296
+ });`;
1297
+ });
1298
+ const rows = entries.map(([table]) => `export type ${pascalCase(table)} = typeof ${snakeToCamel(table)}.$inferSelect;`);
1299
+ return [
1300
+ "// Generated by `sl codegen index --target drizzle`. Do not edit by hand.",
1301
+ `import { ${[...used].sort().join(", ")} } from "drizzle-orm/pg-core";`,
1302
+ "",
1303
+ ...schemaName ? [`export const sg = pgSchema("${schemaName}");`, ""] : [],
1304
+ tables.join(`
1305
+
1306
+ `),
1307
+ "",
1308
+ rows.join(`
1309
+ `),
1310
+ ""
1311
+ ].join(`
1312
+ `);
1313
+ }
1314
+ var JSON_SCHEMA_TYPE = {
1315
+ text: { type: "string" },
1316
+ int: { type: "integer" },
1317
+ boolean: { type: "boolean" },
1318
+ timestamp: { type: "string", format: "date-time" },
1319
+ jsonb: {}
1320
+ };
1321
+ function generateJsonSchema(entries) {
1322
+ const defs = {};
1323
+ for (const [table, cols] of entries) {
1324
+ const properties = {};
1325
+ const required = [];
1326
+ for (const [col, def] of Object.entries(cols)) {
1327
+ const base = JSON_SCHEMA_TYPE[def.type];
1328
+ if (def.nullable && "type" in base) {
1329
+ properties[col] = { ...base, type: [base.type, "null"] };
1330
+ } else {
1331
+ properties[col] = { ...base };
1332
+ if (!def.nullable)
1333
+ required.push(col);
1334
+ }
1335
+ }
1336
+ defs[pascalCase(table)] = {
1337
+ type: "object",
1338
+ properties,
1339
+ required,
1340
+ additionalProperties: false
1341
+ };
1342
+ }
1343
+ return `${JSON.stringify({
1344
+ $schema: "https://json-schema.org/draft/2020-12/schema",
1345
+ title: "Secondlayer Index domain",
1346
+ $defs: defs
1347
+ }, null, 2)}
1348
+ `;
1349
+ }
1350
+ var PRISMA_TYPE2 = {
1351
+ text: { type: "String" },
1352
+ int: { type: "Int" },
1353
+ boolean: { type: "Boolean" },
1354
+ timestamp: { type: "DateTime", db: "@db.Timestamptz" },
1355
+ jsonb: { type: "Json" }
1356
+ };
1357
+ function generatePrisma(entries, schemaName, datasourceEnv) {
1358
+ const pks = SOURCE_READ_PKS;
1359
+ const skipped = [];
1360
+ const models = [];
1361
+ for (const [table, cols] of entries) {
1362
+ const pk = pks[table];
1363
+ if (!pk) {
1364
+ skipped.push(table);
1365
+ continue;
1366
+ }
1367
+ const single = pk.length === 1 ? pk[0] : null;
1368
+ const lines = Object.entries(cols).map(([col, def]) => {
1369
+ const field = snakeToCamel(col);
1370
+ const mapped = PRISMA_TYPE2[def.type];
1371
+ const attrs = [];
1372
+ if (single === col)
1373
+ attrs.push("@id");
1374
+ if (field !== col)
1375
+ attrs.push(`@map("${col}")`);
1376
+ if (mapped.db)
1377
+ attrs.push(mapped.db);
1378
+ const optional = def.nullable ? "?" : "";
1379
+ return ` ${field} ${mapped.type}${optional}${attrs.length ? ` ${attrs.join(" ")}` : ""}`;
1380
+ });
1381
+ const block = [];
1382
+ if (!single) {
1383
+ block.push(` @@id([${pk.map(snakeToCamel).join(", ")}])`);
1384
+ }
1385
+ block.push(` @@map("${table}")`);
1386
+ if (schemaName)
1387
+ block.push(` @@schema("${schemaName}")`);
1388
+ models.push(`model ${pascalCase(table)} {
1389
+ ${lines.join(`
1390
+ `)}
1391
+
1392
+ ${block.join(`
1393
+ `)}
1394
+ }`);
1395
+ }
1396
+ const header = [
1397
+ "// Generated by `sl codegen index --target prisma`. Do not edit by hand.",
1398
+ ...skipped.length ? [
1399
+ `// Omitted (no read-set primary key, query via another target): ${skipped.join(", ")}`
1400
+ ] : [],
1401
+ "",
1402
+ "datasource db {",
1403
+ ' provider = "postgresql"',
1404
+ ` url = env("${datasourceEnv}")`,
1405
+ ...schemaName ? [` schemas = ["${schemaName}"]`] : [],
1406
+ "}",
1407
+ "",
1408
+ "generator client {",
1409
+ ' provider = "prisma-client-js"',
1410
+ ...schemaName ? [' previewFeatures = ["multiSchema"]'] : [],
1411
+ "}"
1412
+ ].join(`
1413
+ `);
1414
+ return `${header}
1415
+
1416
+ ${models.join(`
1417
+
1418
+ `)}
1419
+ `;
1420
+ }
1421
+ function generateIndexSchema(target, opts = {}) {
1422
+ const entries = selectTables(opts);
1423
+ switch (target) {
1424
+ case "prisma":
1425
+ return generatePrisma(entries, opts.schemaName, opts.datasourceEnv ?? "DATABASE_URL");
1426
+ case "kysely":
1427
+ return generateKysely(entries, opts.schemaName);
1428
+ case "drizzle":
1429
+ return generateDrizzle(entries, opts.schemaName);
1430
+ case "json-schema":
1431
+ return generateJsonSchema(entries);
1432
+ default:
1433
+ throw new Error(`Unsupported Index codegen target "${target}".`);
1434
+ }
1435
+ }
1436
+ var INDEX_CODEGEN_TABLES = Object.keys(SOURCE_READ_TYPES);
1437
+ // src/schema/deployer.ts
1438
+ import { sql } from "kysely";
1439
+ function toJsonSafe(obj) {
1440
+ return JSON.parse(JSON.stringify(obj, (_key, value) => typeof value === "bigint" ? value.toString() : value));
1441
+ }
1442
+ function columnDataShape(col) {
1443
+ const { indexed, search, ...rest } = col ?? {};
1444
+ return JSON.stringify(rest, Object.keys(rest).sort());
1445
+ }
1446
+ function diffKeyLists(existing, incoming) {
1447
+ const key = (cols) => JSON.stringify(cols);
1448
+ const existingSet = new Set((existing ?? []).map(key));
1449
+ const incomingSet = new Set((incoming ?? []).map(key));
1450
+ return {
1451
+ added: (incoming ?? []).filter((cols) => !existingSet.has(key(cols))).map((cols) => [...cols]),
1452
+ removed: (existing ?? []).filter((cols) => !incomingSet.has(key(cols))).map((cols) => [...cols])
1453
+ };
1454
+ }
1455
+ function diffSchema(existing, incoming) {
1456
+ const existingTables = new Set(Object.keys(existing));
1457
+ const incomingTables = new Set(Object.keys(incoming));
1458
+ const addedTables = [...incomingTables].filter((t) => !existingTables.has(t));
1459
+ const removedTables = [...existingTables].filter((t) => !incomingTables.has(t));
1460
+ const tables = {};
1461
+ const constraints = {};
1462
+ for (const tableName of incomingTables) {
1463
+ if (!existingTables.has(tableName))
1464
+ continue;
1465
+ const existingTable = existing[tableName];
1466
+ const incomingTable = incoming[tableName];
1467
+ const existingCols = existingTable?.columns;
1468
+ const incomingCols = incomingTable?.columns;
1469
+ const existingKeys = new Set(Object.keys(existingCols));
1470
+ const incomingKeys = new Set(Object.keys(incomingCols));
1471
+ const common = [...incomingKeys].filter((k) => existingKeys.has(k));
1472
+ const sortedStringify = (o) => JSON.stringify(o, Object.keys(o).sort());
1473
+ tables[tableName] = {
1474
+ added: [...incomingKeys].filter((k) => !existingKeys.has(k)),
1475
+ removed: [...existingKeys].filter((k) => !incomingKeys.has(k)),
1476
+ changed: common.filter((k) => columnDataShape(existingCols[k]) !== columnDataShape(incomingCols[k])),
1477
+ indexChanged: common.filter((k) => columnDataShape(existingCols[k]) === columnDataShape(incomingCols[k]) && sortedStringify(existingCols[k]) !== sortedStringify(incomingCols[k]))
1478
+ };
1479
+ const uq = diffKeyLists(existingTable?.uniqueKeys, incomingTable?.uniqueKeys);
1480
+ const ix = diffKeyLists(existingTable?.indexes, incomingTable?.indexes);
1481
+ constraints[tableName] = {
1482
+ addedUniqueKeys: uq.added,
1483
+ removedUniqueKeys: uq.removed,
1484
+ addedIndexes: ix.added,
1485
+ removedIndexes: ix.removed
1486
+ };
1487
+ }
1488
+ return { addedTables, removedTables, tables, constraints };
1489
+ }
1490
+ function hasBreakingChanges(diff) {
1491
+ const reasons = [];
1492
+ if (diff.removedTables.length > 0) {
1493
+ reasons.push(`removed tables: [${diff.removedTables.join(", ")}]`);
1494
+ }
1495
+ for (const [table, colDiff] of Object.entries(diff.tables)) {
1496
+ if (colDiff.removed.length > 0) {
1497
+ reasons.push(`${table}: removed columns [${colDiff.removed.join(", ")}]`);
1498
+ }
1499
+ if (colDiff.changed.length > 0) {
1500
+ reasons.push(`${table}: changed columns [${colDiff.changed.join(", ")}]`);
1501
+ }
1502
+ }
1503
+ for (const [table, conDiff] of Object.entries(diff.constraints ?? {})) {
1504
+ if (conDiff.removedUniqueKeys.length > 0) {
1505
+ reasons.push(`${table}: removed uniqueKeys [${conDiff.removedUniqueKeys.map((cols) => `(${cols.join(", ")})`).join(", ")}]`);
1506
+ }
1507
+ }
1508
+ return { breaking: reasons.length > 0, reasons };
1509
+ }
1510
+ function bumpPatch(version) {
1511
+ const parts = version.split(".");
1512
+ if (parts.length !== 3)
1513
+ return "1.0.1";
1514
+ const patch = Number.parseInt(parts[2] ?? "0", 10);
1515
+ return `${parts[0]}.${parts[1]}.${Number.isNaN(patch) ? 1 : patch + 1}`;
1516
+ }
1517
+ function toDeployDiff(diff, reasons) {
1518
+ return {
1519
+ addedTables: diff?.addedTables ?? [],
1520
+ removedTables: diff?.removedTables ?? [],
1521
+ addedColumns: diff ? Object.fromEntries(Object.entries(diff.tables).filter(([, c]) => c.added.length > 0).map(([t, c]) => [t, c.added])) : {},
1522
+ breakingChanges: reasons
1523
+ };
1524
+ }
1525
+ function renderDeployPlan(def, schemaName) {
1526
+ validateSubgraphDefinition(def);
1527
+ const { statements } = generateSubgraphSQL(def, schemaName);
1528
+ const schema = schemaName ?? pgSchemaName(def.name);
1529
+ const dropStatement = `DROP SCHEMA IF EXISTS "${schema}" CASCADE;`;
1530
+ return { schemaName: schema, dropStatement, statements };
1531
+ }
1532
+ async function deploySchema(db, def, handlerPath, opts) {
1533
+ validateSubgraphDefinition(def);
1534
+ const { statements, hash } = generateSubgraphSQL(def, opts?.schemaName);
1535
+ const { getSubgraph, registerSubgraph } = await import("@secondlayer/shared/db/queries/subgraphs");
1536
+ const ddlDb = db;
1537
+ const existing = await getSubgraph(db, def.name, opts?.accountId);
1538
+ const schemaName = opts?.schemaName ?? pgSchemaName(def.name);
1539
+ const newVersion = existing ? bumpPatch(existing.version) : "1.0.0";
1540
+ const regData = {
1541
+ name: def.name,
1542
+ version: newVersion,
1543
+ definition: toJsonSafe({
1544
+ name: def.name,
1545
+ description: def.description,
1546
+ startBlock: def.startBlock,
1547
+ sources: def.sources,
1548
+ schema: def.schema
1549
+ }),
1550
+ schemaHash: hash,
1551
+ handlerPath,
1552
+ apiKeyId: opts?.apiKeyId,
1553
+ accountId: opts?.accountId,
1554
+ handlerCode: opts?.handlerCode,
1555
+ sourceCode: opts?.sourceCode,
1556
+ schemaName,
1557
+ startBlock: def.startBlock
1558
+ };
1559
+ if (existing) {
1560
+ const schemaExists = await sql`
1561
+ SELECT EXISTS (
1562
+ SELECT 1 FROM information_schema.schemata
1563
+ WHERE schema_name = ${schemaName}
1564
+ ) AS "exists"
1565
+ `.execute(ddlDb).then((r) => r.rows[0]?.exists ?? false);
1566
+ if (!schemaExists) {
1567
+ for (const stmt of statements) {
1568
+ await sql.raw(stmt).execute(ddlDb);
1569
+ }
1570
+ const sg = await registerSubgraph(db, regData);
1571
+ return { action: "reindexed", subgraphId: sg.id, version: newVersion };
1572
+ }
1573
+ if (existing.schema_hash === hash && !opts?.forceReindex) {
1574
+ const handlerChanged = opts?.handlerCode != null && opts.handlerCode !== existing.handler_code;
1575
+ const { updateSubgraphHandlerPath } = await import("@secondlayer/shared/db/queries/subgraphs");
1576
+ await updateSubgraphHandlerPath(db, def.name, handlerPath, {
1577
+ handlerCode: opts?.handlerCode,
1578
+ sourceCode: opts?.sourceCode
1579
+ });
1580
+ return {
1581
+ action: handlerChanged ? "handler_updated" : "unchanged",
1582
+ subgraphId: existing.id,
1583
+ version: existing.version,
1584
+ ...handlerChanged ? {
1585
+ warnings: [
1586
+ "handler changed but schema did not: new logic applies from the current tip only. Rows already indexed were computed by the previous handler. To recompute history, redeploy with --reindex (or backfill a range)."
1587
+ ]
1588
+ } : {}
1589
+ };
1590
+ }
1591
+ if (existing.schema_hash === hash && opts?.forceReindex) {
1592
+ await sql.raw(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`).execute(ddlDb);
1593
+ for (const stmt of statements) {
1594
+ await sql.raw(stmt).execute(ddlDb);
1595
+ }
1596
+ const sg = await registerSubgraph(db, regData);
1597
+ return { action: "reindexed", subgraphId: sg.id, version: newVersion };
1598
+ }
1599
+ if (existing.definition.schema) {
1600
+ const diff = diffSchema(existing.definition.schema, def.schema);
1601
+ const { breaking, reasons } = hasBreakingChanges(diff);
1602
+ if (breaking || opts?.forceReindex) {
1603
+ await sql.raw(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`).execute(ddlDb);
1604
+ for (const stmt of statements) {
1605
+ await sql.raw(stmt).execute(ddlDb);
1606
+ }
1607
+ const sg = await registerSubgraph(db, regData);
1608
+ const deployDiff = toDeployDiff(diff, reasons);
1609
+ return {
1610
+ action: "reindexed",
1611
+ subgraphId: sg.id,
1612
+ version: newVersion,
1613
+ diff: deployDiff
1614
+ };
1615
+ }
1616
+ const addedDefs = diff.addedTables.map((tableName) => ({ tableName, tableDef: def.schema[tableName] })).filter((t) => t.tableDef !== undefined);
1617
+ if (addedDefs.some(({ tableDef }) => tableNeedsTrgm(tableDef))) {
1618
+ await sql.raw("CREATE EXTENSION IF NOT EXISTS pg_trgm").execute(ddlDb);
1619
+ }
1620
+ for (const { tableName, tableDef } of addedDefs) {
1621
+ for (const stmt of emitTableDDL(schemaName, tableName, tableDef)) {
1622
+ await sql.raw(stmt).execute(ddlDb);
1623
+ }
1624
+ }
1625
+ for (const [tableName, colDiff] of Object.entries(diff.tables)) {
1626
+ if (colDiff.added.length === 0)
1627
+ continue;
1628
+ const qualifiedName = `${schemaName}.${tableName}`;
1629
+ const tableDef = def.schema[tableName];
1630
+ if (!tableDef)
1631
+ continue;
1632
+ for (const colName of colDiff.added) {
1633
+ const col = tableDef.columns[colName];
1634
+ if (!col)
1635
+ continue;
1636
+ const sqlType = TYPE_MAP[col.type];
1637
+ if (!sqlType)
1638
+ continue;
1639
+ const nullable = col.nullable ? "" : ` NOT NULL DEFAULT ${getDefault(col.type)}`;
1640
+ await sql.raw(`ALTER TABLE ${qualifiedName} ADD COLUMN ${colName} ${sqlType}${nullable}`).execute(ddlDb);
1641
+ if (col.indexed) {
1642
+ for (const stmt of emitIndexedColumnIndexDDL(schemaName, tableName, colName).create) {
1643
+ await sql.raw(stmt).execute(ddlDb);
1644
+ }
1645
+ }
1646
+ if (col.search) {
1647
+ await sql.raw(`CREATE INDEX IF NOT EXISTS idx_${schemaName}_${tableName}_${colName}_trgm ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
1648
+ }
1649
+ }
1650
+ }
1651
+ const existingSchema = existing.definition.schema;
1652
+ const flagFlips = {};
1653
+ const needsTrgmForFlips = Object.entries(diff.tables).some(([tableName, colDiff]) => colDiff.indexChanged.some((colName) => def.schema[tableName]?.columns[colName]?.search && !existingSchema[tableName]?.columns[colName]?.search));
1654
+ if (needsTrgmForFlips) {
1655
+ await sql.raw("CREATE EXTENSION IF NOT EXISTS pg_trgm").execute(ddlDb);
1656
+ }
1657
+ for (const [tableName, colDiff] of Object.entries(diff.tables)) {
1658
+ if (colDiff.indexChanged.length === 0)
1659
+ continue;
1660
+ flagFlips[tableName] = colDiff.indexChanged;
1661
+ const qualifiedName = `${schemaName}.${tableName}`;
1662
+ for (const colName of colDiff.indexChanged) {
1663
+ const was = existingSchema[tableName]?.columns[colName];
1664
+ const now = def.schema[tableName]?.columns[colName];
1665
+ if (!now)
1666
+ continue;
1667
+ const trgmIdx = `idx_${schemaName}_${tableName}_${colName}_trgm`;
1668
+ const indexedIdx = emitIndexedColumnIndexDDL(schemaName, tableName, colName, { concurrently: true });
1669
+ if (now.indexed && !was?.indexed) {
1670
+ for (const stmt of indexedIdx.create) {
1671
+ await sql.raw(stmt).execute(ddlDb);
1672
+ }
1673
+ } else if (!now.indexed && was?.indexed) {
1674
+ for (const stmt of indexedIdx.drop) {
1675
+ await sql.raw(stmt).execute(ddlDb);
1676
+ }
1677
+ }
1678
+ if (now.search && !was?.search) {
1679
+ await sql.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${trgmIdx} ON ${qualifiedName} USING gin (${colName} gin_trgm_ops)`).execute(ddlDb);
1680
+ } else if (!now.search && was?.search) {
1681
+ await sql.raw(`DROP INDEX CONCURRENTLY IF EXISTS ${schemaName}.${trgmIdx}`).execute(ddlDb);
1682
+ }
1683
+ }
1684
+ }
1685
+ const addedUq = {};
1686
+ for (const [tableName, conDiff] of Object.entries(diff.constraints)) {
1687
+ const qualifiedName = `${schemaName}.${tableName}`;
1688
+ for (const cols of conDiff.addedUniqueKeys) {
1689
+ const constraintName = `uq_${schemaName}_${tableName}_${cols.join("_")}`;
1690
+ const exists = await sql`
1691
+ SELECT EXISTS (
1692
+ SELECT 1 FROM pg_constraint
1693
+ WHERE conname = ${constraintName}
1694
+ ) AS "exists"
1695
+ `.execute(ddlDb).then((r) => r.rows[0]?.exists ?? false);
1696
+ if (!exists) {
1697
+ await sql.raw(`ALTER TABLE ${qualifiedName} ADD CONSTRAINT ${constraintName} UNIQUE (${cols.join(", ")})`).execute(ddlDb);
1698
+ }
1699
+ addedUq[tableName] = [...addedUq[tableName] ?? [], cols];
1700
+ }
1701
+ for (const cols of conDiff.addedIndexes) {
1702
+ const i = (def.schema[tableName]?.indexes ?? []).findIndex((entry) => JSON.stringify(entry) === JSON.stringify(cols));
1703
+ const idxName = `idx_${schemaName}_${tableName}_composite_${i}`;
1704
+ await sql.raw(`CREATE INDEX CONCURRENTLY IF NOT EXISTS ${idxName} ON ${qualifiedName} (${cols.join(", ")})`).execute(ddlDb);
1705
+ }
1706
+ for (const cols of conDiff.removedIndexes) {
1707
+ const i = (existingSchema[tableName]?.indexes ?? []).findIndex((entry) => JSON.stringify(entry) === JSON.stringify(cols));
1708
+ if (i < 0)
1709
+ continue;
1710
+ const idxName = `idx_${schemaName}_${tableName}_composite_${i}`;
1711
+ await sql.raw(`DROP INDEX CONCURRENTLY IF EXISTS ${schemaName}.${idxName}`).execute(ddlDb);
1712
+ }
1713
+ }
1714
+ const sg = await registerSubgraph(db, regData);
1715
+ const addedCols = {};
1716
+ for (const [t, colDiff] of Object.entries(diff.tables)) {
1717
+ if (colDiff.added.length > 0)
1718
+ addedCols[t] = colDiff.added;
1719
+ }
1720
+ const deployDiff = {
1721
+ addedTables: diff.addedTables,
1722
+ removedTables: [],
1723
+ addedColumns: addedCols,
1724
+ breakingChanges: [],
1725
+ ...Object.keys(flagFlips).length > 0 ? { indexChanges: flagFlips } : {},
1726
+ ...Object.keys(addedUq).length > 0 ? { addedUniqueKeys: addedUq } : {}
1727
+ };
1728
+ return {
1729
+ action: "updated",
1730
+ subgraphId: sg.id,
1731
+ version: newVersion,
1732
+ diff: deployDiff
1733
+ };
1734
+ }
1735
+ }
1736
+ for (const stmt of statements) {
1737
+ await sql.raw(stmt).execute(ddlDb);
1738
+ }
1739
+ const sg = await registerSubgraph(db, regData);
1740
+ return { action: "created", subgraphId: sg.id, version: newVersion };
1741
+ }
1742
+ function getDefault(type) {
1743
+ switch (type) {
1744
+ case "text":
1745
+ case "principal":
1746
+ return "''";
1747
+ case "uint":
1748
+ case "int":
1749
+ return "0";
1750
+ case "boolean":
1751
+ return "false";
1752
+ case "timestamp":
1753
+ return "NOW()";
1754
+ case "jsonb":
1755
+ return "'{}'";
1756
+ default:
1757
+ return "''";
1758
+ }
1759
+ }
1760
+ // src/runtime/block-source.ts
1761
+ import { getSourceDb as getSourceDb2 } from "@secondlayer/shared/db";
1762
+ import {
1763
+ createInternalIndexHttpClient
1764
+ } from "@secondlayer/shared/index-http";
1765
+ import { logger as logger2 } from "@secondlayer/shared/logger";
1766
+
1767
+ // src/runtime/batch-loader.ts
1768
+ function vmEventId(txId, vmEventIndex) {
1769
+ return `${txId}#vm:${vmEventIndex}`;
1770
+ }
1771
+ async function loadBlockRange(db, fromHeight, toHeight) {
1772
+ const [blocks, txs, events, vmRows] = await Promise.all([
1773
+ db.selectFrom("blocks").selectAll().where("height", ">=", fromHeight).where("height", "<=", toHeight).where("canonical", "=", true).execute(),
1774
+ db.selectFrom("transactions").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
1775
+ db.selectFrom("events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).execute(),
1776
+ db.selectFrom("vm_events").selectAll().where("block_height", ">=", fromHeight).where("block_height", "<=", toHeight).orderBy("ordinal", "asc").execute()
1777
+ ]);
1778
+ const txsByHeight = new Map;
1779
+ for (const tx of txs) {
1780
+ const h = Number(tx.block_height);
1781
+ const list = txsByHeight.get(h) ?? [];
1782
+ list.push(tx);
1783
+ txsByHeight.set(h, list);
1784
+ }
1785
+ const eventsByHeight = new Map;
1786
+ for (const evt of events) {
1787
+ const h = Number(evt.block_height);
1788
+ const list = eventsByHeight.get(h) ?? [];
1789
+ list.push(evt);
1790
+ eventsByHeight.set(h, list);
1791
+ }
1792
+ const vmByHeight = new Map;
1793
+ for (const row of vmRows) {
1794
+ const h = Number(row.block_height);
1795
+ const list = vmByHeight.get(h) ?? [];
1796
+ list.push({
1797
+ id: vmEventId(row.tx_id, Number(row.ordinal)),
1798
+ tx_id: row.tx_id,
1799
+ block_height: h,
1800
+ event_index: Number(row.ordinal),
1801
+ type: row.type,
1802
+ data: row.data,
1803
+ created_at: row.created_at,
1804
+ clock: "vm"
1805
+ });
1806
+ vmByHeight.set(h, list);
1807
+ }
1808
+ const result = new Map;
1809
+ for (const block of blocks) {
1810
+ const h = Number(block.height);
1811
+ result.set(h, {
1812
+ block,
1813
+ txs: txsByHeight.get(h) ?? [],
1814
+ events: eventsByHeight.get(h) ?? [],
1815
+ vmEvents: vmByHeight.get(h) ?? []
1816
+ });
1817
+ }
1818
+ return result;
1819
+ }
1820
+
1821
+ // src/runtime/observer-http-source.ts
1822
+ import {
1823
+ VM_NODE_TO_STORED_TYPE
1824
+ } from "@secondlayer/shared";
1825
+
1826
+ // src/runtime/reconstruct.ts
1827
+ function isoToUnixSeconds(iso) {
1828
+ if (!iso)
1829
+ return 0;
1830
+ return Math.floor(new Date(iso).getTime() / 1000);
1831
+ }
1832
+ function reconstructBlock(b) {
1833
+ return {
1834
+ height: b.block_height,
1835
+ hash: b.block_hash,
1836
+ parent_hash: b.parent_hash,
1837
+ burn_block_height: b.burn_block_height,
1838
+ burn_block_hash: b.burn_block_hash,
1839
+ index_block_hash: b.index_block_hash ?? null,
1840
+ timestamp: isoToUnixSeconds(b.block_time),
1841
+ canonical: true,
1842
+ created_at: new Date(0)
1843
+ };
1844
+ }
1845
+ function reconstructTransaction(t) {
1846
+ return {
1847
+ tx_id: t.tx_id,
1848
+ block_height: t.block_height,
1849
+ tx_index: t.tx_index,
1850
+ type: t.tx_type,
1851
+ sender: t.sender,
1852
+ status: t.status,
1853
+ contract_id: t.contract_call?.contract_id ?? t.smart_contract?.contract_id ?? null,
1854
+ function_name: t.contract_call?.function_name ?? null,
1855
+ function_args: t.contract_call?.function_args_hex ?? [],
1856
+ raw_result: t.contract_call?.result_hex ?? null,
1857
+ raw_tx: "",
1858
+ created_at: new Date(0)
1859
+ };
1860
+ }
1861
+ function reconstructTxFromEventRow(e) {
1862
+ return {
1863
+ tx_id: e.tx_id,
1864
+ block_height: e.block_height,
1865
+ tx_index: e.tx_index ?? 0,
1866
+ type: e.tx_type ?? "",
1867
+ sender: e.tx_sender ?? "",
1868
+ status: e.tx_status ?? "success",
1869
+ contract_id: e.tx_contract_id ?? null,
1870
+ function_name: e.tx_function_name ?? null,
1871
+ function_args: [],
1872
+ raw_result: null,
1873
+ raw_tx: "",
1874
+ created_at: new Date(0)
1875
+ };
1876
+ }
1877
+ function reconstructEvent(e) {
1878
+ const base = {
1879
+ id: `${e.tx_id}#${e.event_index}`,
1880
+ tx_id: e.tx_id,
1881
+ block_height: e.block_height,
1882
+ event_index: e.event_index,
1883
+ created_at: new Date(0)
1884
+ };
1885
+ switch (e.event_type) {
1886
+ case "ft_transfer":
1887
+ case "ft_mint":
1888
+ case "ft_burn":
1889
+ return {
1890
+ ...base,
1891
+ type: `${e.event_type}_event`,
1892
+ data: {
1893
+ asset_identifier: e.asset_identifier,
1894
+ sender: e.sender,
1895
+ recipient: e.recipient,
1896
+ amount: e.amount
1897
+ }
1898
+ };
1899
+ case "nft_transfer":
1900
+ case "nft_mint":
1901
+ case "nft_burn":
1902
+ return {
1903
+ ...base,
1904
+ type: `${e.event_type}_event`,
1905
+ data: {
1906
+ asset_identifier: e.asset_identifier,
1907
+ sender: e.sender,
1908
+ recipient: e.recipient,
1909
+ raw_value: e.value
1910
+ }
1911
+ };
1912
+ case "stx_transfer":
1913
+ case "stx_mint":
1914
+ case "stx_burn":
1915
+ return {
1916
+ ...base,
1917
+ type: `${e.event_type}_event`,
1918
+ data: {
1919
+ sender: e.sender,
1920
+ recipient: e.recipient,
1921
+ amount: e.amount,
1922
+ ...e.event_type === "stx_transfer" ? { memo: e.memo ?? "" } : {}
1923
+ }
1924
+ };
1925
+ case "stx_lock":
1926
+ return {
1927
+ ...base,
1928
+ type: "stx_lock_event",
1929
+ data: {
1930
+ locked_address: e.sender,
1931
+ locked_amount: e.amount,
1932
+ unlock_height: e.payload.unlock_height
1933
+ }
1934
+ };
1935
+ case "print":
1936
+ return {
1937
+ ...base,
1938
+ type: "contract_event",
1939
+ data: {
1940
+ topic: e.payload.topic,
1941
+ contract_id: e.contract_id,
1942
+ value: e.payload.value,
1943
+ raw_value: e.payload.raw_value
1944
+ }
1945
+ };
1946
+ case "nested_contract_call":
1947
+ case "var_set":
1948
+ case "map_set":
1949
+ case "map_insert":
1950
+ case "map_delete":
1951
+ return {
1952
+ ...base,
1953
+ id: vmEventId(e.tx_id, e.event_index),
1954
+ clock: "vm",
1955
+ type: e.event_type,
1956
+ data: {
1957
+ contract_identifier: e.contract_id,
1958
+ ..."payload" in e && e.payload && typeof e.payload === "object" ? e.payload : {},
1959
+ sender: "sender" in e ? e.sender : undefined,
1960
+ caller: "caller" in e ? e.caller : undefined,
1961
+ function_name: "function_name" in e ? e.function_name : undefined,
1962
+ function_args: "function_args" in e ? e.function_args : undefined,
1963
+ raw_result: "raw_result" in e ? e.raw_result : undefined,
1964
+ map_name: "map" in e ? e.map : undefined,
1965
+ var_name: "var_name" in e ? e.var_name : undefined,
1966
+ raw_key: "raw_key" in e ? e.raw_key : undefined,
1967
+ raw_value: "raw_value" in e ? e.raw_value : undefined
1968
+ }
1969
+ };
1970
+ }
1971
+ }
1972
+
1973
+ // src/runtime/block-source.ts
1974
+ function canSparseScan(subgraph) {
1975
+ if (Array.isArray(subgraph.sources))
1976
+ return false;
1977
+ const filters = sourceFilters(subgraph);
1978
+ if (filters.length === 0)
1979
+ return false;
1980
+ return filters.every((f) => Boolean(EVENT_FILTER_TO_INDEX_TYPE[f.type]));
1981
+ }
1982
+ function sparseProbeTargets(subgraph) {
1983
+ const targets = new Map;
1984
+ for (const f of sourceFilters(subgraph)) {
1985
+ const eventType = EVENT_FILTER_TO_INDEX_TYPE[f.type];
1986
+ if (!eventType)
1987
+ continue;
1988
+ const scoped = f;
1989
+ const contractId = scoped.contractId ?? scoped.assetIdentifier?.split("::")[0];
1990
+ const key = `${eventType}|${contractId ?? ""}`;
1991
+ targets.set(key, { eventType, ...contractId ? { contractId } : {} });
1992
+ }
1993
+ return [...targets.values()];
1994
+ }
1995
+
1996
+ class PostgresBlockSource {
1997
+ async getTip() {
1998
+ const progress = await getSourceDb2().selectFrom("index_progress").selectAll().where("network", "=", process.env.NETWORK ?? "mainnet").executeTakeFirst();
1999
+ return progress ? Number(progress.highest_seen_block) : 0;
2000
+ }
2001
+ loadBlockRange(fromHeight, toHeight) {
2002
+ return loadBlockRange(getSourceDb2(), fromHeight, toHeight);
2003
+ }
2004
+ }
2005
+ var EVENT_FILTER_TO_INDEX_TYPE = {
2006
+ stx_transfer: "stx_transfer",
2007
+ stx_mint: "stx_mint",
2008
+ stx_burn: "stx_burn",
2009
+ stx_lock: "stx_lock",
2010
+ ft_transfer: "ft_transfer",
2011
+ ft_mint: "ft_mint",
2012
+ ft_burn: "ft_burn",
2013
+ nft_transfer: "nft_transfer",
2014
+ nft_mint: "nft_mint",
2015
+ nft_burn: "nft_burn",
2016
+ print_event: "print",
2017
+ nested_contract_call: "nested_contract_call",
2018
+ var_set: "var_set",
2019
+ map_set: "map_set",
2020
+ map_insert: "map_insert",
2021
+ map_delete: "map_delete"
2022
+ };
2023
+ var TX_SOURCE_TYPES = new Set(["contract_call", "contract_deploy"]);
2024
+ var VM_INDEX_EVENT_TYPES = new Set([
2025
+ "nested_contract_call",
2026
+ "var_set",
2027
+ "map_set",
2028
+ "map_insert",
2029
+ "map_delete"
2030
+ ]);
2031
+ var CLASSIC_INDEX_EVENT_TYPES = [
2032
+ ...new Set(Object.values(EVENT_FILTER_TO_INDEX_TYPE))
2033
+ ].filter((t) => !VM_INDEX_EVENT_TYPES.has(t));
2034
+ function sourceFilters(subgraph) {
2035
+ const sources = subgraph.sources;
2036
+ return Array.isArray(sources) ? sources : Object.values(sources);
2037
+ }
2038
+ function synthesizeTxsFromEvents(events) {
2039
+ const byId = new Map;
2040
+ for (const e of events) {
2041
+ if (!byId.has(e.tx_id))
2042
+ byId.set(e.tx_id, reconstructTxFromEventRow(e));
2043
+ }
2044
+ return [...byId.values()];
2045
+ }
2046
+ function indexEventTypesForFilterTypes(filterTypes) {
2047
+ const types = new Set;
2048
+ if (filterTypes.some((t) => TX_SOURCE_TYPES.has(t))) {
2049
+ for (const t of CLASSIC_INDEX_EVENT_TYPES)
2050
+ types.add(t);
2051
+ }
2052
+ for (const t of filterTypes) {
2053
+ const indexType = EVENT_FILTER_TO_INDEX_TYPE[t];
2054
+ if (indexType)
2055
+ types.add(indexType);
2056
+ }
2057
+ return [...types];
2058
+ }
2059
+ class PublicApiBlockSource {
2060
+ http;
2061
+ eventTypes;
2062
+ probeTargets;
2063
+ needsTransactions;
2064
+ constructor(http, eventTypes, probeTargets, needsTransactions = true) {
2065
+ this.http = http;
2066
+ this.eventTypes = eventTypes;
2067
+ this.probeTargets = probeTargets;
2068
+ this.needsTransactions = needsTransactions;
2069
+ }
2070
+ async nextDataHeight(afterHeight, untilHeight) {
2071
+ if (!this.probeTargets?.length)
2072
+ return afterHeight + 1;
2073
+ const hits = await Promise.all(this.probeTargets.map((t) => this.http.firstEventHeight(t.eventType, afterHeight + 1, untilHeight, t.contractId)));
2074
+ const found = hits.filter((h) => h !== null);
2075
+ return found.length ? Math.min(...found) : null;
2076
+ }
2077
+ getTip() {
2078
+ if (this.eventTypes.some((t) => VM_INDEX_EVENT_TYPES.has(t))) {
2079
+ return this.http.getIndexSourceTip();
2080
+ }
2081
+ return this.http.getIndexTip();
2082
+ }
2083
+ async loadBlockRange(fromHeight, toHeight) {
2084
+ const withTx = !this.needsTransactions;
2085
+ const [blocks, txRows, eventLists] = await Promise.all([
2086
+ this.http.walkBlocks(fromHeight, toHeight),
2087
+ this.needsTransactions ? this.http.walkTransactions(fromHeight, toHeight) : Promise.resolve([]),
2088
+ Promise.all(this.eventTypes.map((t) => this.http.walkEvents(t, fromHeight, toHeight, withTx)))
2089
+ ]);
2090
+ const map = new Map;
2091
+ for (const b of blocks) {
2092
+ map.set(b.block_height, {
2093
+ block: reconstructBlock(b),
2094
+ txs: [],
2095
+ events: []
2096
+ });
2097
+ }
2098
+ const txs = this.needsTransactions ? txRows.map(reconstructTransaction) : synthesizeTxsFromEvents(eventLists.flat());
2099
+ for (const t of txs) {
2100
+ map.get(t.block_height)?.txs.push(t);
2101
+ }
2102
+ for (const list of eventLists) {
2103
+ for (const e of list) {
2104
+ const bd = map.get(e.block_height);
2105
+ if (!bd)
2106
+ continue;
2107
+ if (VM_INDEX_EVENT_TYPES.has(e.event_type)) {
2108
+ bd.vmEvents ??= [];
2109
+ bd.vmEvents.push(reconstructEvent(e));
2110
+ } else {
2111
+ bd.events.push(reconstructEvent(e));
2112
+ }
2113
+ }
2114
+ }
2115
+ for (const bd of map.values()) {
2116
+ bd.txs.sort((a, b) => a.tx_index - b.tx_index);
2117
+ bd.events.sort((a, b) => a.event_index - b.event_index);
2118
+ bd.vmEvents?.sort((a, b) => a.event_index - b.event_index);
2119
+ }
2120
+ return map;
2121
+ }
2122
+ }
2123
+ var postgresBlockSource = new PostgresBlockSource;
2124
+ function buildHttpClient() {
2125
+ return createInternalIndexHttpClient();
2126
+ }
2127
+ export {
2128
+ ChainReadError,
2129
+ camelizeKeys,
2130
+ canSparseScan,
2131
+ defineSchema,
2132
+ defineSubgraph,
2133
+ deploySchema,
2134
+ diffSchema,
2135
+ filterFieldsByType,
2136
+ generateDrizzleSchema,
2137
+ generateIndexSchema,
2138
+ generateKyselySchema,
2139
+ generatePrismaSchema,
2140
+ generateSubgraphSQL,
2141
+ hasBreakingChanges,
2142
+ inferPrintTopics,
2143
+ pgSchemaName,
2144
+ readContractAt,
2145
+ renderDeployPlan,
2146
+ sparseProbeTargets,
2147
+ validateSubgraphDefinition
2148
+ };
2149
+
2150
+ //# debugId=70F01B0F3E28D11B64756E2164756E21
2151
+ //# sourceMappingURL=index.js.map