@secondlayer/shared 11.10.1 → 11.11.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.
@@ -1,10 +1,969 @@
1
1
  // src/queue/listener.ts
2
+ import postgres2 from "postgres";
3
+
4
+ // src/db/index.ts
5
+ import { Kysely } from "kysely";
6
+ import { PostgresJSDialect } from "kysely-postgres-js";
2
7
  import postgres from "postgres";
8
+
9
+ // src/env.ts
10
+ import { z } from "zod";
11
+ var networksSchema = z.string().transform((val) => {
12
+ const networks = val.split(",").map((n) => n.trim()).filter(Boolean);
13
+ const valid = ["mainnet", "testnet"];
14
+ for (const n of networks) {
15
+ if (!valid.includes(n)) {
16
+ throw new Error(`Invalid network: ${n}. Must be one of: ${valid.join(", ")}`);
17
+ }
18
+ }
19
+ return networks;
20
+ });
21
+ var envSchema = z.object({
22
+ DATABASE_URL: z.preprocess((val) => typeof val === "string" && val.length === 0 ? undefined : val, z.string().url().optional()),
23
+ SOURCE_DATABASE_URL: z.preprocess((val) => typeof val === "string" && val.length === 0 ? undefined : val, z.string().url().optional()),
24
+ TARGET_DATABASE_URL: z.preprocess((val) => typeof val === "string" && val.length === 0 ? undefined : val, z.string().url().optional()),
25
+ NETWORK: z.enum(["mainnet", "testnet"]).optional(),
26
+ NETWORKS: networksSchema.optional(),
27
+ LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
28
+ NODE_ENV: z.enum(["development", "production", "test"]).default("development")
29
+ });
30
+ var cachedEnv = null;
31
+ function getEnv() {
32
+ if (cachedEnv) {
33
+ return cachedEnv;
34
+ }
35
+ const result = envSchema.safeParse(process.env);
36
+ if (!result.success) {
37
+ console.error("❌ Invalid environment configuration:");
38
+ console.error(z.treeifyError(result.error));
39
+ throw new Error("Invalid environment configuration");
40
+ }
41
+ let enabledNetworks;
42
+ if (result.data.NETWORKS && result.data.NETWORKS.length > 0) {
43
+ enabledNetworks = result.data.NETWORKS;
44
+ } else if (result.data.NETWORK) {
45
+ enabledNetworks = [result.data.NETWORK];
46
+ } else {
47
+ enabledNetworks = ["mainnet"];
48
+ }
49
+ cachedEnv = { ...result.data, enabledNetworks };
50
+ return cachedEnv;
51
+ }
52
+ function isProductionEnv() {
53
+ return process.env["NODE_ENV"] === "production";
54
+ }
55
+ function isPox4DecoderEnabled(env = process.env) {
56
+ return env.POX4_DECODER_ENABLED !== "false";
57
+ }
58
+ function isPox5DecoderEnabled(env = process.env) {
59
+ return env.POX5_DECODER_ENABLED !== "false";
60
+ }
61
+ function isSbtcDecoderEnabled(env = process.env) {
62
+ return env.SBTC_DECODER_ENABLED !== "false";
63
+ }
64
+ function isBnsDecoderEnabled(env = process.env) {
65
+ return env.BNS_DECODER_ENABLED === "true";
66
+ }
67
+ // src/logger.ts
68
+ var LOG_LEVELS = {
69
+ debug: 0,
70
+ info: 1,
71
+ warn: 2,
72
+ error: 3
73
+ };
74
+
75
+ class Logger {
76
+ _level;
77
+ _isProduction;
78
+ _initialized = false;
79
+ init() {
80
+ if (this._initialized)
81
+ return;
82
+ this._initialized = true;
83
+ try {
84
+ const env = getEnv();
85
+ this._level = env.LOG_LEVEL;
86
+ this._isProduction = env.NODE_ENV === "production";
87
+ } catch {
88
+ this._level = "info";
89
+ this._isProduction = false;
90
+ }
91
+ }
92
+ get level() {
93
+ this.init();
94
+ return this._level;
95
+ }
96
+ get isProduction() {
97
+ this.init();
98
+ return this._isProduction;
99
+ }
100
+ shouldLog(level) {
101
+ return LOG_LEVELS[level] >= LOG_LEVELS[this.level];
102
+ }
103
+ formatMessage(level, message, meta) {
104
+ const timestamp = new Date().toISOString();
105
+ if (this.isProduction) {
106
+ return JSON.stringify({
107
+ timestamp,
108
+ level,
109
+ message,
110
+ ...meta
111
+ });
112
+ }
113
+ const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
114
+ return `[${timestamp}] ${level.toUpperCase()}: ${message}${metaStr}`;
115
+ }
116
+ debug(message, meta) {
117
+ if (this.shouldLog("debug")) {
118
+ console.debug(this.formatMessage("debug", message, meta));
119
+ }
120
+ }
121
+ info(message, meta) {
122
+ if (this.shouldLog("info")) {
123
+ console.info(this.formatMessage("info", message, meta));
124
+ }
125
+ }
126
+ warn(message, meta) {
127
+ if (this.shouldLog("warn")) {
128
+ console.warn(this.formatMessage("warn", message, meta));
129
+ }
130
+ }
131
+ error(message, meta) {
132
+ if (this.shouldLog("error")) {
133
+ console.error(this.formatMessage("error", message, meta));
134
+ }
135
+ }
136
+ }
137
+ var logger = new Logger;
138
+
139
+ // src/mode.ts
140
+ var VALID_MODES = [
141
+ "oss",
142
+ "platform",
143
+ "archive"
144
+ ];
145
+ function declaredMode() {
146
+ const raw = process.env.INSTANCE_MODE?.trim().toLowerCase();
147
+ if (raw && VALID_MODES.includes(raw)) {
148
+ return raw;
149
+ }
150
+ return "oss";
151
+ }
152
+ function getInstanceMode() {
153
+ const declared = declaredMode();
154
+ return declared === "archive" ? "platform" : declared;
155
+ }
156
+ function getDeclaredInstanceMode() {
157
+ return declaredMode();
158
+ }
159
+ function isMeteredReads() {
160
+ const override = process.env.METERED_READS?.trim().toLowerCase();
161
+ if (override === "true")
162
+ return true;
163
+ if (override === "false")
164
+ return false;
165
+ return getInstanceMode() === "platform";
166
+ }
167
+ function isPlatformMode() {
168
+ return getInstanceMode() === "platform";
169
+ }
170
+ function isOssMode() {
171
+ return getInstanceMode() === "oss";
172
+ }
173
+
174
+ // src/db/index.ts
175
+ import { sql as sql2 } from "kysely";
176
+
177
+ // src/db/source-read-columns.ts
178
+ var SOURCE_READ_COLUMNS = {
179
+ blocks: [
180
+ "burn_block_hash",
181
+ "burn_block_height",
182
+ "canonical",
183
+ "hash",
184
+ "height",
185
+ "parent_hash",
186
+ "timestamp"
187
+ ],
188
+ decoded_events: [
189
+ "amount",
190
+ "asset_identifier",
191
+ "block_height",
192
+ "canonical",
193
+ "contract_id",
194
+ "cursor",
195
+ "event_index",
196
+ "event_type",
197
+ "memo",
198
+ "payload",
199
+ "recipient",
200
+ "sender",
201
+ "tx_id",
202
+ "tx_index",
203
+ "value"
204
+ ],
205
+ transactions: [
206
+ "block_height",
207
+ "contract_id",
208
+ "function_args",
209
+ "function_name",
210
+ "raw_result",
211
+ "raw_tx",
212
+ "sender",
213
+ "status",
214
+ "tx_id",
215
+ "tx_index",
216
+ "type"
217
+ ],
218
+ mempool_transactions: [
219
+ "contract_id",
220
+ "function_args",
221
+ "function_name",
222
+ "raw_tx",
223
+ "received_at",
224
+ "sender",
225
+ "seq",
226
+ "tx_id",
227
+ "type"
228
+ ],
229
+ pox4_calls: [
230
+ "aggregated_amount_ustx",
231
+ "aggregated_signer_index",
232
+ "amount_ustx",
233
+ "auth_allowed",
234
+ "auth_id",
235
+ "auth_period",
236
+ "auth_topic",
237
+ "block_height",
238
+ "block_time",
239
+ "burn_block_height",
240
+ "caller",
241
+ "canonical",
242
+ "cursor",
243
+ "delegate_to",
244
+ "end_cycle",
245
+ "function_name",
246
+ "lock_period",
247
+ "max_amount",
248
+ "pox_addr_btc",
249
+ "pox_addr_hashbytes",
250
+ "pox_addr_version",
251
+ "result_ok",
252
+ "reward_cycle",
253
+ "signer_key",
254
+ "signer_signature",
255
+ "source_cursor",
256
+ "stacker",
257
+ "start_cycle",
258
+ "tx_id",
259
+ "tx_index"
260
+ ],
261
+ pox5_events: [
262
+ "amount_sats",
263
+ "amount_ustx",
264
+ "block_height",
265
+ "block_time",
266
+ "bond_index",
267
+ "canonical",
268
+ "cursor",
269
+ "data",
270
+ "event_index",
271
+ "first_reward_cycle",
272
+ "is_l1_lock",
273
+ "reward_cycle",
274
+ "signer",
275
+ "signer_key",
276
+ "signer_manager",
277
+ "staker",
278
+ "topic",
279
+ "tx_id",
280
+ "tx_index",
281
+ "unlock_burn_height",
282
+ "unlock_cycle"
283
+ ],
284
+ sbtc_events: [
285
+ "amount",
286
+ "bitcoin_txid",
287
+ "block_height",
288
+ "block_time",
289
+ "burn_hash",
290
+ "burn_height",
291
+ "canonical",
292
+ "cursor",
293
+ "event_index",
294
+ "fee",
295
+ "governance_contract_type",
296
+ "governance_new_contract",
297
+ "max_fee",
298
+ "output_index",
299
+ "recipient_btc_hashbytes",
300
+ "recipient_btc_version",
301
+ "request_id",
302
+ "sender",
303
+ "signer_address",
304
+ "signer_aggregate_pubkey",
305
+ "signer_bitmap",
306
+ "signer_keys_count",
307
+ "signer_threshold",
308
+ "sweep_txid",
309
+ "topic",
310
+ "tx_id",
311
+ "tx_index"
312
+ ],
313
+ sbtc_token_events: [
314
+ "amount",
315
+ "block_height",
316
+ "block_time",
317
+ "canonical",
318
+ "cursor",
319
+ "event_index",
320
+ "event_type",
321
+ "memo",
322
+ "recipient",
323
+ "sender",
324
+ "tx_id",
325
+ "tx_index"
326
+ ],
327
+ bns_name_events: [
328
+ "block_height",
329
+ "block_time",
330
+ "bns_id",
331
+ "canonical",
332
+ "cursor",
333
+ "event_index",
334
+ "fqn",
335
+ "hashed_salted_fqn_preorder",
336
+ "imported_at",
337
+ "name",
338
+ "namespace",
339
+ "owner",
340
+ "preordered_by",
341
+ "registered_at",
342
+ "renewal_height",
343
+ "stx_burn",
344
+ "topic",
345
+ "tx_id",
346
+ "tx_index"
347
+ ],
348
+ bns_namespace_events: [
349
+ "block_height",
350
+ "block_time",
351
+ "canonical",
352
+ "cursor",
353
+ "event_index",
354
+ "launched_at",
355
+ "lifetime",
356
+ "manager",
357
+ "manager_frozen",
358
+ "manager_transfers_disabled",
359
+ "namespace",
360
+ "price_frozen",
361
+ "price_function",
362
+ "revealed_at",
363
+ "status",
364
+ "tx_id",
365
+ "tx_index"
366
+ ],
367
+ bns_marketplace_events: [
368
+ "action",
369
+ "block_height",
370
+ "block_time",
371
+ "bns_id",
372
+ "canonical",
373
+ "commission",
374
+ "cursor",
375
+ "event_index",
376
+ "price_ustx",
377
+ "tx_id",
378
+ "tx_index"
379
+ ],
380
+ bns_names: [
381
+ "bns_id",
382
+ "fqn",
383
+ "last_event_at",
384
+ "last_event_cursor",
385
+ "name",
386
+ "namespace",
387
+ "owner",
388
+ "registered_at",
389
+ "renewal_height"
390
+ ],
391
+ bns_namespaces: [
392
+ "last_event_at",
393
+ "last_event_cursor",
394
+ "launched_at",
395
+ "lifetime",
396
+ "manager",
397
+ "manager_frozen",
398
+ "name_count",
399
+ "namespace",
400
+ "price_frozen"
401
+ ],
402
+ burn_block_rewards: [
403
+ "amount_sats",
404
+ "burn_amount",
405
+ "burn_block_hash",
406
+ "burn_block_height",
407
+ "cursor",
408
+ "recipient_btc",
409
+ "reward_index"
410
+ ],
411
+ burn_block_reward_slots: [
412
+ "burn_block_hash",
413
+ "burn_block_height",
414
+ "cursor",
415
+ "holder_btc",
416
+ "slot_index"
417
+ ],
418
+ events: ["block_height", "data", "event_index", "tx_id", "type"],
419
+ chain_reorgs: ["detected_at"]
420
+ };
421
+ var SOURCE_READ_TYPES = {
422
+ blocks: {
423
+ burn_block_hash: { type: "text", nullable: true },
424
+ burn_block_height: { type: "int" },
425
+ canonical: { type: "boolean" },
426
+ hash: { type: "text" },
427
+ height: { type: "int" },
428
+ parent_hash: { type: "text" },
429
+ timestamp: { type: "int" }
430
+ },
431
+ decoded_events: {
432
+ amount: { type: "text", nullable: true },
433
+ asset_identifier: { type: "text", nullable: true },
434
+ block_height: { type: "int" },
435
+ canonical: { type: "boolean" },
436
+ contract_id: { type: "text", nullable: true },
437
+ cursor: { type: "text" },
438
+ event_index: { type: "int" },
439
+ event_type: { type: "text" },
440
+ memo: { type: "text", nullable: true },
441
+ payload: { type: "jsonb", nullable: true },
442
+ recipient: { type: "text", nullable: true },
443
+ sender: { type: "text", nullable: true },
444
+ tx_id: { type: "text" },
445
+ tx_index: { type: "int" },
446
+ value: { type: "text", nullable: true }
447
+ },
448
+ transactions: {
449
+ block_height: { type: "int" },
450
+ contract_id: { type: "text", nullable: true },
451
+ function_args: { type: "jsonb", nullable: true },
452
+ function_name: { type: "text", nullable: true },
453
+ raw_result: { type: "text", nullable: true },
454
+ raw_tx: { type: "text" },
455
+ sender: { type: "text" },
456
+ status: { type: "text" },
457
+ tx_id: { type: "text" },
458
+ tx_index: { type: "int" },
459
+ type: { type: "text" }
460
+ },
461
+ mempool_transactions: {
462
+ contract_id: { type: "text", nullable: true },
463
+ function_args: { type: "jsonb", nullable: true },
464
+ function_name: { type: "text", nullable: true },
465
+ raw_tx: { type: "text" },
466
+ received_at: { type: "timestamp" },
467
+ sender: { type: "text" },
468
+ seq: { type: "text" },
469
+ tx_id: { type: "text" },
470
+ type: { type: "text" }
471
+ },
472
+ pox4_calls: {
473
+ aggregated_amount_ustx: { type: "text", nullable: true },
474
+ aggregated_signer_index: { type: "int", nullable: true },
475
+ amount_ustx: { type: "text", nullable: true },
476
+ auth_allowed: { type: "boolean", nullable: true },
477
+ auth_id: { type: "text", nullable: true },
478
+ auth_period: { type: "int", nullable: true },
479
+ auth_topic: { type: "text", nullable: true },
480
+ block_height: { type: "int" },
481
+ block_time: { type: "timestamp" },
482
+ burn_block_height: { type: "int" },
483
+ caller: { type: "text" },
484
+ canonical: { type: "boolean" },
485
+ cursor: { type: "text" },
486
+ delegate_to: { type: "text", nullable: true },
487
+ end_cycle: { type: "int", nullable: true },
488
+ function_name: { type: "text" },
489
+ lock_period: { type: "int", nullable: true },
490
+ max_amount: { type: "text", nullable: true },
491
+ pox_addr_btc: { type: "text", nullable: true },
492
+ pox_addr_hashbytes: { type: "text", nullable: true },
493
+ pox_addr_version: { type: "int", nullable: true },
494
+ result_ok: { type: "boolean" },
495
+ reward_cycle: { type: "int", nullable: true },
496
+ signer_key: { type: "text", nullable: true },
497
+ signer_signature: { type: "text", nullable: true },
498
+ source_cursor: { type: "text" },
499
+ stacker: { type: "text", nullable: true },
500
+ start_cycle: { type: "int", nullable: true },
501
+ tx_id: { type: "text" },
502
+ tx_index: { type: "int" }
503
+ },
504
+ pox5_events: {
505
+ amount_sats: { type: "text", nullable: true },
506
+ amount_ustx: { type: "text", nullable: true },
507
+ block_height: { type: "int" },
508
+ block_time: { type: "timestamp" },
509
+ bond_index: { type: "int", nullable: true },
510
+ canonical: { type: "boolean" },
511
+ cursor: { type: "text" },
512
+ data: { type: "jsonb", nullable: true },
513
+ event_index: { type: "int" },
514
+ first_reward_cycle: { type: "int", nullable: true },
515
+ is_l1_lock: { type: "boolean", nullable: true },
516
+ reward_cycle: { type: "int", nullable: true },
517
+ signer: { type: "text", nullable: true },
518
+ signer_key: { type: "text", nullable: true },
519
+ signer_manager: { type: "text", nullable: true },
520
+ staker: { type: "text", nullable: true },
521
+ topic: { type: "text" },
522
+ tx_id: { type: "text" },
523
+ tx_index: { type: "int" },
524
+ unlock_burn_height: { type: "int", nullable: true },
525
+ unlock_cycle: { type: "int", nullable: true }
526
+ },
527
+ sbtc_events: {
528
+ amount: { type: "text", nullable: true },
529
+ bitcoin_txid: { type: "text", nullable: true },
530
+ block_height: { type: "int" },
531
+ block_time: { type: "timestamp" },
532
+ burn_hash: { type: "text", nullable: true },
533
+ burn_height: { type: "int", nullable: true },
534
+ canonical: { type: "boolean" },
535
+ cursor: { type: "text" },
536
+ event_index: { type: "int" },
537
+ fee: { type: "text", nullable: true },
538
+ governance_contract_type: { type: "int", nullable: true },
539
+ governance_new_contract: { type: "text", nullable: true },
540
+ max_fee: { type: "text", nullable: true },
541
+ output_index: { type: "int", nullable: true },
542
+ recipient_btc_hashbytes: { type: "text", nullable: true },
543
+ recipient_btc_version: { type: "int", nullable: true },
544
+ request_id: { type: "int", nullable: true },
545
+ sender: { type: "text", nullable: true },
546
+ signer_address: { type: "text", nullable: true },
547
+ signer_aggregate_pubkey: { type: "text", nullable: true },
548
+ signer_bitmap: { type: "text", nullable: true },
549
+ signer_keys_count: { type: "int", nullable: true },
550
+ signer_threshold: { type: "int", nullable: true },
551
+ sweep_txid: { type: "text", nullable: true },
552
+ topic: { type: "text" },
553
+ tx_id: { type: "text" },
554
+ tx_index: { type: "int" }
555
+ },
556
+ sbtc_token_events: {
557
+ amount: { type: "text" },
558
+ block_height: { type: "int" },
559
+ block_time: { type: "timestamp" },
560
+ canonical: { type: "boolean" },
561
+ cursor: { type: "text" },
562
+ event_index: { type: "int" },
563
+ event_type: { type: "text" },
564
+ memo: { type: "text", nullable: true },
565
+ recipient: { type: "text", nullable: true },
566
+ sender: { type: "text", nullable: true },
567
+ tx_id: { type: "text" },
568
+ tx_index: { type: "int" }
569
+ },
570
+ bns_name_events: {
571
+ block_height: { type: "int" },
572
+ block_time: { type: "timestamp" },
573
+ bns_id: { type: "text" },
574
+ canonical: { type: "boolean" },
575
+ cursor: { type: "text" },
576
+ event_index: { type: "int" },
577
+ fqn: { type: "text" },
578
+ hashed_salted_fqn_preorder: { type: "text", nullable: true },
579
+ imported_at: { type: "int", nullable: true },
580
+ name: { type: "text" },
581
+ namespace: { type: "text" },
582
+ owner: { type: "text", nullable: true },
583
+ preordered_by: { type: "text", nullable: true },
584
+ registered_at: { type: "int", nullable: true },
585
+ renewal_height: { type: "int", nullable: true },
586
+ stx_burn: { type: "text", nullable: true },
587
+ topic: { type: "text" },
588
+ tx_id: { type: "text" },
589
+ tx_index: { type: "int" }
590
+ },
591
+ bns_namespace_events: {
592
+ block_height: { type: "int" },
593
+ block_time: { type: "timestamp" },
594
+ canonical: { type: "boolean" },
595
+ cursor: { type: "text" },
596
+ event_index: { type: "int" },
597
+ launched_at: { type: "int", nullable: true },
598
+ lifetime: { type: "int", nullable: true },
599
+ manager: { type: "text", nullable: true },
600
+ manager_frozen: { type: "boolean", nullable: true },
601
+ manager_transfers_disabled: { type: "boolean", nullable: true },
602
+ namespace: { type: "text" },
603
+ price_frozen: { type: "boolean", nullable: true },
604
+ price_function: { type: "text", nullable: true },
605
+ revealed_at: { type: "int", nullable: true },
606
+ status: { type: "text" },
607
+ tx_id: { type: "text" },
608
+ tx_index: { type: "int" }
609
+ },
610
+ bns_marketplace_events: {
611
+ action: { type: "text" },
612
+ block_height: { type: "int" },
613
+ block_time: { type: "timestamp" },
614
+ bns_id: { type: "text" },
615
+ canonical: { type: "boolean" },
616
+ commission: { type: "text", nullable: true },
617
+ cursor: { type: "text" },
618
+ event_index: { type: "int" },
619
+ price_ustx: { type: "text", nullable: true },
620
+ tx_id: { type: "text" },
621
+ tx_index: { type: "int" }
622
+ },
623
+ bns_names: {
624
+ bns_id: { type: "text" },
625
+ fqn: { type: "text" },
626
+ last_event_at: { type: "timestamp" },
627
+ last_event_cursor: { type: "text" },
628
+ name: { type: "text" },
629
+ namespace: { type: "text" },
630
+ owner: { type: "text" },
631
+ registered_at: { type: "int", nullable: true },
632
+ renewal_height: { type: "int", nullable: true }
633
+ },
634
+ bns_namespaces: {
635
+ last_event_at: { type: "timestamp" },
636
+ last_event_cursor: { type: "text" },
637
+ launched_at: { type: "int", nullable: true },
638
+ lifetime: { type: "int", nullable: true },
639
+ manager: { type: "text", nullable: true },
640
+ manager_frozen: { type: "boolean" },
641
+ name_count: { type: "int" },
642
+ namespace: { type: "text" },
643
+ price_frozen: { type: "boolean" }
644
+ },
645
+ burn_block_rewards: {
646
+ amount_sats: { type: "text" },
647
+ burn_amount: { type: "text" },
648
+ burn_block_hash: { type: "text" },
649
+ burn_block_height: { type: "int" },
650
+ cursor: { type: "text" },
651
+ recipient_btc: { type: "text" },
652
+ reward_index: { type: "int" }
653
+ },
654
+ burn_block_reward_slots: {
655
+ burn_block_hash: { type: "text" },
656
+ burn_block_height: { type: "int" },
657
+ cursor: { type: "text" },
658
+ holder_btc: { type: "text" },
659
+ slot_index: { type: "int" }
660
+ },
661
+ events: {
662
+ block_height: { type: "int" },
663
+ data: { type: "jsonb" },
664
+ event_index: { type: "int" },
665
+ tx_id: { type: "text" },
666
+ type: { type: "text" }
667
+ },
668
+ chain_reorgs: {
669
+ detected_at: { type: "timestamp" }
670
+ }
671
+ };
672
+ var SOURCE_READ_PKS = {
673
+ blocks: ["height"],
674
+ decoded_events: ["cursor"],
675
+ transactions: ["tx_id"],
676
+ mempool_transactions: ["seq"],
677
+ pox4_calls: ["cursor"],
678
+ pox5_events: ["cursor"],
679
+ sbtc_events: ["cursor"],
680
+ sbtc_token_events: ["cursor"],
681
+ bns_name_events: ["cursor"],
682
+ bns_namespace_events: ["cursor"],
683
+ bns_marketplace_events: ["cursor"],
684
+ bns_names: ["fqn"],
685
+ bns_namespaces: ["namespace"],
686
+ burn_block_rewards: ["cursor"],
687
+ burn_block_reward_slots: ["cursor"],
688
+ events: ["tx_id", "event_index"],
689
+ chain_reorgs: null
690
+ };
691
+ // src/db/jsonb.ts
692
+ import { sql } from "kysely";
693
+ function jsonb(value) {
694
+ const escaped = JSON.stringify(value, (_k, v) => typeof v === "bigint" ? v.toString() : v).replace(/'/g, "''");
695
+ return sql`${sql.raw(`'${escaped}'::jsonb`)}`;
696
+ }
697
+ function parseJsonb(value) {
698
+ if (typeof value === "string") {
699
+ try {
700
+ return JSON.parse(value);
701
+ } catch {
702
+ return value;
703
+ }
704
+ }
705
+ return value ?? {};
706
+ }
707
+ // src/db/migration-role.ts
708
+ var currentRole = "both";
709
+ function setMigrationRole(role) {
710
+ currentRole = role;
711
+ }
712
+ function getMigrationRole() {
713
+ return currentRole;
714
+ }
715
+ async function onControlPlane(fn) {
716
+ if (currentRole === "target" || currentRole === "both")
717
+ await fn();
718
+ }
719
+ async function onChainPlane(fn) {
720
+ if (currentRole === "source" || currentRole === "both")
721
+ await fn();
722
+ }
723
+ // src/db/table-plane.ts
724
+ var TABLE_TO_DB = {
725
+ blocks: "source",
726
+ transactions: "source",
727
+ events: "source",
728
+ vm_events: "source",
729
+ transactions_archive: "source",
730
+ events_archive: "source",
731
+ vm_events_archive: "source",
732
+ dead_letter_events: "source",
733
+ mempool_transactions: "source",
734
+ index_progress: "source",
735
+ contracts: "source",
736
+ chain_reorgs: "source",
737
+ chain_read_cache: "source",
738
+ pending_fork_blocks: "source",
739
+ observer_journal: "source",
740
+ sync_scopes: "source",
741
+ stage_registry: "source",
742
+ stage_runs: "source",
743
+ stage_block_receipts: "source",
744
+ coverage_segments: "source",
745
+ stage_failures: "source",
746
+ decoded_events: "source",
747
+ decoder_checkpoints: "source",
748
+ pox4_calls: "source",
749
+ pox5_events: "source",
750
+ burn_block_rewards: "source",
751
+ burn_block_reward_slots: "source",
752
+ sbtc_events: "source",
753
+ sbtc_token_events: "source",
754
+ sbtc_settlements: "source",
755
+ bns_name_events: "source",
756
+ bns_namespace_events: "source",
757
+ bns_marketplace_events: "source",
758
+ bns_names: "source",
759
+ bns_namespaces: "source",
760
+ instances: "target",
761
+ accounts: "target",
762
+ api_keys: "target",
763
+ sessions: "target",
764
+ magic_links: "target",
765
+ account_spend_caps: "target",
766
+ processed_stripe_events: "target",
767
+ webhooks: "target",
768
+ webhook_outbox: "target",
769
+ webhook_deliveries: "target",
770
+ trigger_evaluator_state: "target",
771
+ subgraphs: "target",
772
+ subgraph_operations: "target",
773
+ subgraph_health_snapshots: "target",
774
+ subgraph_gaps: "target",
775
+ subgraph_violations: "target",
776
+ subgraph_processing_stats: "target",
777
+ account_credits: "target",
778
+ archive_fetches: "target",
779
+ usage_ledger: "target",
780
+ waitlists: "target",
781
+ waitlist_signups: "target",
782
+ service_heartbeats: "both"
783
+ };
784
+
785
+ // src/db/index.ts
786
+ var DEFAULT_URL = "postgres://postgres:postgres@localhost:5432/secondlayer_dev";
787
+ var pools = new Map;
788
+ var poolSeq = 0;
789
+ function maxPools() {
790
+ return Number.parseInt(process.env.DATABASE_MAX_POOLS ?? "25", 10);
791
+ }
792
+ function evictIfNeeded() {
793
+ if (pools.size <= maxPools())
794
+ return;
795
+ const protectedUrls = new Set([resolveSourceUrl(), resolveTargetUrl()]);
796
+ let lruUrl;
797
+ let lruEntry;
798
+ for (const [url, entry] of pools) {
799
+ if (protectedUrls.has(url))
800
+ continue;
801
+ if (!lruEntry || entry.lastUsed < lruEntry.lastUsed || entry.lastUsed === lruEntry.lastUsed && entry.seq < lruEntry.seq) {
802
+ lruUrl = url;
803
+ lruEntry = entry;
804
+ }
805
+ }
806
+ if (!lruUrl || !lruEntry)
807
+ return;
808
+ pools.delete(lruUrl);
809
+ const evicted = lruEntry;
810
+ evicted.db.destroy().catch(() => {}).then(() => evicted.rawClient.end({ timeout: 5 })).catch(() => {});
811
+ }
812
+ function applicationDatabaseUrl() {
813
+ return process.env.DATABASE_URL || DEFAULT_URL;
814
+ }
815
+ function resolveSourceUrl() {
816
+ if (!isPlatformMode())
817
+ return applicationDatabaseUrl();
818
+ return process.env.SOURCE_DATABASE_URL || process.env.DATABASE_URL || DEFAULT_URL;
819
+ }
820
+ function resolveTargetUrl() {
821
+ if (!isPlatformMode())
822
+ return applicationDatabaseUrl();
823
+ return process.env.TARGET_DATABASE_URL || process.env.DATABASE_URL || DEFAULT_URL;
824
+ }
825
+ function describeDbUrl(url) {
826
+ try {
827
+ const u = new URL(url);
828
+ return `${u.hostname}${u.port ? `:${u.port}` : ""}${u.pathname}`;
829
+ } catch {
830
+ return "invalid-url";
831
+ }
832
+ }
833
+ var warnedDefaultUrlFallthrough = false;
834
+ function warnIfTestRunFallsThroughToDefault() {
835
+ if (warnedDefaultUrlFallthrough)
836
+ return;
837
+ if (process.env["NODE_ENV"] !== "test")
838
+ return;
839
+ if (process.env.DATABASE_URL || process.env.SOURCE_DATABASE_URL || process.env.TARGET_DATABASE_URL) {
840
+ return;
841
+ }
842
+ warnedDefaultUrlFallthrough = true;
843
+ console.warn(`⚠️ DATABASE_URL unset — falling through to the built-in DEFAULT_URL (${describeDbUrl(DEFAULT_URL)}). Database-backed tests will skip or hit the wrong Postgres. Copy .env.example to .env in this package (see \`bun run db\`).`);
844
+ }
845
+ warnIfTestRunFallsThroughToDefault();
846
+ function getDbSplitStatus() {
847
+ const source = resolveSourceUrl();
848
+ const target = resolveTargetUrl();
849
+ const active = source !== target;
850
+ return {
851
+ mode: active ? "split" : "single",
852
+ active,
853
+ sourceDb: describeDbUrl(source),
854
+ targetDb: describeDbUrl(target)
855
+ };
856
+ }
857
+ function assertDbSplit() {
858
+ const isProd = isProductionEnv();
859
+ const wantsSplit = !!(process.env.SOURCE_DATABASE_URL || process.env.TARGET_DATABASE_URL);
860
+ const databaseUrlSet = !!process.env.DATABASE_URL;
861
+ const source = resolveSourceUrl();
862
+ const target = resolveTargetUrl();
863
+ if (wantsSplit && !databaseUrlSet && (source === DEFAULT_URL || target === DEFAULT_URL)) {
864
+ const which = source === DEFAULT_URL ? "SOURCE_DATABASE_URL" : "TARGET_DATABASE_URL";
865
+ const msg = `${which} unset and DATABASE_URL absent — resolving to built-in DEFAULT_URL; refusing to silently use the wrong database`;
866
+ if (isProd)
867
+ console.error(`❌ ${msg}`);
868
+ else
869
+ console.warn(`⚠️ ${msg}`);
870
+ return;
871
+ }
872
+ if (!wantsSplit) {
873
+ if (isProd) {
874
+ console.warn("⚠️ DB split dormant — all services share one Postgres failure domain (SOURCE_/TARGET_DATABASE_URL unset)");
875
+ }
876
+ return;
877
+ }
878
+ if (source === target) {
879
+ const msg = "DB split requested but SOURCE_DATABASE_URL === TARGET_DATABASE_URL (check for a typo or a stray DATABASE_URL fallback)";
880
+ if (isProd)
881
+ console.error(`❌ ${msg}`);
882
+ else
883
+ console.warn(`⚠️ ${msg}`);
884
+ }
885
+ }
886
+ function getOrCreatePool(url) {
887
+ const existing = pools.get(url);
888
+ if (existing) {
889
+ existing.lastUsed = Date.now();
890
+ existing.seq = poolSeq++;
891
+ return existing;
892
+ }
893
+ const host = (() => {
894
+ try {
895
+ return new URL(url).hostname;
896
+ } catch {
897
+ return "";
898
+ }
899
+ })();
900
+ const isLocal = host === "localhost" || host === "127.0.0.1" || !host.includes(".");
901
+ const poolMax = Number.parseInt(process.env.DATABASE_POOL_MAX ?? "20", 10);
902
+ const idleTimeout = Number.parseInt(process.env.DATABASE_IDLE_TIMEOUT ?? "300", 10);
903
+ const rawClient = postgres(url, {
904
+ max: poolMax,
905
+ idle_timeout: idleTimeout,
906
+ ssl: isLocal ? undefined : {
907
+ rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0"
908
+ }
909
+ });
910
+ const db = new Kysely({
911
+ dialect: new PostgresJSDialect({ postgres: rawClient }),
912
+ log: (event) => {
913
+ if (event.level !== "error")
914
+ return;
915
+ const err = event.error;
916
+ if (err?.code !== "42P10")
917
+ return;
918
+ logger.warn("db.on_conflict_constraint_missing", {
919
+ code: err.code,
920
+ message: err.message,
921
+ sql: event.query.sql,
922
+ params: event.query.parameters
923
+ });
924
+ }
925
+ });
926
+ const entry = {
927
+ db,
928
+ rawClient,
929
+ lastUsed: Date.now(),
930
+ seq: poolSeq++
931
+ };
932
+ pools.set(url, entry);
933
+ evictIfNeeded();
934
+ return entry;
935
+ }
936
+ function getSourceDb() {
937
+ return getOrCreatePool(resolveSourceUrl()).db;
938
+ }
939
+ function getTargetDb() {
940
+ return getOrCreatePool(resolveTargetUrl()).db;
941
+ }
942
+ function getDb(connectionString) {
943
+ if (connectionString)
944
+ return getOrCreatePool(connectionString).db;
945
+ return getTargetDb();
946
+ }
947
+ function getRawClient(role = "target") {
948
+ const url = role === "source" ? resolveSourceUrl() : resolveTargetUrl();
949
+ return getOrCreatePool(url).rawClient;
950
+ }
951
+ function getRawClientFor(url) {
952
+ return getOrCreatePool(url).rawClient;
953
+ }
954
+ async function closeDb() {
955
+ for (const entry of pools.values()) {
956
+ await entry.db.destroy();
957
+ await entry.rawClient.end();
958
+ }
959
+ pools.clear();
960
+ }
961
+ // src/queue/listener.ts
3
962
  function sourceListenerUrl() {
4
- return process.env.SOURCE_DATABASE_URL || process.env.DATABASE_URL;
963
+ return resolveSourceUrl();
5
964
  }
6
965
  function targetListenerUrl() {
7
- return process.env.TARGET_DATABASE_URL || process.env.DATABASE_URL;
966
+ return resolveTargetUrl();
8
967
  }
9
968
  function resolveUrl(opts) {
10
969
  const url = opts?.connectionString || process.env.DATABASE_URL;
@@ -14,7 +973,7 @@ function resolveUrl(opts) {
14
973
  return url;
15
974
  }
16
975
  async function listen(channel, callback, opts) {
17
- const client = postgres(resolveUrl(opts), {
976
+ const client = postgres2(resolveUrl(opts), {
18
977
  max: 1,
19
978
  onnotice: () => {}
20
979
  });
@@ -25,8 +984,27 @@ async function listen(channel, callback, opts) {
25
984
  await client.end();
26
985
  };
27
986
  }
987
+ async function createWakeBus(channel, opts) {
988
+ let waiters = new Set;
989
+ const stopListening = await listen(channel, () => {
990
+ const pending = waiters;
991
+ waiters = new Set;
992
+ for (const resolve of pending)
993
+ resolve();
994
+ }, opts);
995
+ return {
996
+ wait: () => new Promise((resolve) => waiters.add(resolve)),
997
+ stop: async () => {
998
+ const pending = waiters;
999
+ waiters = new Set;
1000
+ for (const resolve of pending)
1001
+ resolve();
1002
+ await stopListening();
1003
+ }
1004
+ };
1005
+ }
28
1006
  async function notify(channel, payload, opts) {
29
- const client = postgres(resolveUrl(opts), { max: 1 });
1007
+ const client = postgres2(resolveUrl(opts), { max: 1 });
30
1008
  try {
31
1009
  if (payload) {
32
1010
  await client`SELECT pg_notify(${channel}, ${payload})`;
@@ -38,11 +1016,12 @@ async function notify(channel, payload, opts) {
38
1016
  }
39
1017
  }
40
1018
  export {
1019
+ createWakeBus,
41
1020
  listen,
42
1021
  notify,
43
1022
  sourceListenerUrl,
44
1023
  targetListenerUrl
45
1024
  };
46
1025
 
47
- //# debugId=572FCF166DDCA86A64756E2164756E21
1026
+ //# debugId=3C6355491C0B100364756E2164756E21
48
1027
  //# sourceMappingURL=listener.js.map