@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.
- package/dist/src/coverage/index.js +3 -1
- package/dist/src/coverage/index.js.map +3 -3
- package/dist/src/db/index.d.ts +18 -1
- package/dist/src/db/index.js +4 -1
- package/dist/src/db/index.js.map +3 -3
- package/dist/src/db/queries/chain-reorgs.js.map +2 -2
- package/dist/src/index-http.d.ts +42 -4
- package/dist/src/index-http.js +45 -10
- package/dist/src/index-http.js.map +3 -3
- package/dist/src/index.d.ts +18 -1
- package/dist/src/index.js +4 -1
- package/dist/src/index.js.map +3 -3
- package/dist/src/queue/listener.d.ts +33 -7
- package/dist/src/queue/listener.js +984 -5
- package/dist/src/queue/listener.js.map +12 -4
- package/package.json +1 -1
|
@@ -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
|
|
963
|
+
return resolveSourceUrl();
|
|
5
964
|
}
|
|
6
965
|
function targetListenerUrl() {
|
|
7
|
-
return
|
|
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 =
|
|
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 =
|
|
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=
|
|
1026
|
+
//# debugId=3C6355491C0B100364756E2164756E21
|
|
48
1027
|
//# sourceMappingURL=listener.js.map
|