@secondlayer/shared 13.14.0 → 14.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.
- package/README.md +1 -1
- package/dist/src/archive/key-registry.js +1 -1
- package/dist/src/archive/key-registry.js.map +1 -1
- package/dist/src/archive/reference.js +8 -6
- package/dist/src/archive/reference.js.map +2 -2
- package/dist/src/crypto/ed25519.js +1 -1
- package/dist/src/crypto/ed25519.js.map +1 -1
- package/dist/src/crypto/hmac.js +1 -1
- package/dist/src/crypto/hmac.js.map +1 -1
- package/dist/src/crypto/secondlayer-webhook.js +13 -11
- package/dist/src/crypto/secondlayer-webhook.js.map +2 -2
- package/dist/src/db/queries/webhooks.js +6 -4
- package/dist/src/db/queries/webhooks.js.map +2 -2
- package/dist/src/errors.js +2 -2
- package/dist/src/errors.js.map +2 -2
- package/dist/src/index.d.ts +6 -6
- package/dist/src/index.js +24 -20
- package/dist/src/index.js.map +3 -3
- package/dist/src/node/hiro-pg-client.d.ts +9 -1
- package/dist/src/node/hiro-pg-client.js +7 -4
- package/dist/src/node/hiro-pg-client.js.map +3 -3
- package/dist/src/node/local-client.js +8 -7
- package/dist/src/node/local-client.js.map +3 -3
- package/dist/src/streams-bulk-manifest.js +1 -1
- package/dist/src/streams-bulk-manifest.js.map +1 -1
- package/package.json +2 -6
- package/dist/src/node/archive-client.d.ts +0 -39
- package/dist/src/node/archive-client.js +0 -399
- package/dist/src/node/archive-client.js.map +0 -12
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/node/archive-client.ts", "../src/env.ts", "../src/logger.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"/**\n * Archive Replay Client — backfills blocks from Hiro's daily event observer archive.\n *\n * The archive at archive.hiro.so contains zstd-compressed TSV files of raw\n * /new_block payloads (the exact NewBlockPayload JSON our indexer expects).\n * This client downloads the archive, streams + filters for specific block\n * heights, and POSTs matching payloads directly to the indexer.\n *\n * Caches the archive locally for up to 24h to avoid redundant ~25GB downloads.\n * Zero external API dependency — only needs the static archive file.\n */\n\nimport {\n\texistsSync,\n\treadFileSync,\n\trenameSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { logger } from \"../logger.ts\";\n\nconst DEFAULT_ARCHIVE_URL =\n\t\"https://archive.hiro.so/mainnet/stacks-blockchain-api/mainnet-stacks-blockchain-api-latest.zst\";\n\nconst HEIGHT_REGEX = /\"block_height\":\\s*(\\d+)/;\nconst CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours\n\ninterface ArchiveMeta {\n\tlastModified: string | null;\n\tdownloadedAt: string;\n}\n\nexport interface ReplayResult {\n\treplayed: number;\n\terrors: number;\n}\n\n/** Options for replaying archive blocks, with a progress callback. */\nexport interface ReplayOptions {\n\tonProgress?: (count: number, height: number) => void;\n}\n\nexport class ArchiveReplayClient {\n\tprivate archiveUrl: string;\n\tprivate archiveDir: string;\n\n\tconstructor(opts?: { archiveUrl?: string; archiveDir?: string }) {\n\t\tthis.archiveUrl =\n\t\t\topts?.archiveUrl || process.env.ARCHIVE_URL || DEFAULT_ARCHIVE_URL;\n\t\tthis.archiveDir = opts?.archiveDir || process.env.ARCHIVE_DIR || \"/tmp\";\n\t}\n\n\tprivate get archivePath() {\n\t\treturn `${this.archiveDir}/secondlayer-archive.zst`;\n\t}\n\tprivate get metaPath() {\n\t\treturn `${this.archiveDir}/secondlayer-archive.meta.json`;\n\t}\n\tprivate get partialPath() {\n\t\treturn `${this.archiveDir}/secondlayer-archive.zst.partial`;\n\t}\n\n\t/** HEAD request to archive URL — verify reachable and has content */\n\tasync isAvailable(): Promise<boolean> {\n\t\ttry {\n\t\t\tconst res = await fetch(this.archiveUrl, {\n\t\t\t\tmethod: \"HEAD\",\n\t\t\t\tsignal: AbortSignal.timeout(15_000),\n\t\t\t});\n\t\t\tconst contentLength = Number(res.headers.get(\"content-length\") || 0);\n\t\t\treturn res.ok && contentLength > 0;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Download archive, stream-decompress, replay blocks matching gapHeights\n\t * to the indexer's /new_block endpoint.\n\t */\n\tasync replayGaps(\n\t\tgapHeights: Set<number>,\n\t\tindexerUrl: string,\n\t\topts?: ReplayOptions,\n\t): Promise<ReplayResult> {\n\t\tif (gapHeights.size === 0) return { replayed: 0, errors: 0 };\n\n\t\t// Iterate, don't spread: a full-history gap set is millions of heights,\n\t\t// and `Math.max(...set)` spreads them all as call arguments → instant\n\t\t// RangeError (max call-stack) on any large backfill.\n\t\tlet maxHeight = 0;\n\t\tfor (const h of gapHeights) if (h > maxHeight) maxHeight = h;\n\t\tlet replayed = 0;\n\t\tlet errors = 0;\n\n\t\ttry {\n\t\t\tawait this.ensureArchive();\n\n\t\t\tlogger.info(\"Archive replay: starting decompression + replay\", {\n\t\t\t\ttargetHeights: gapHeights.size,\n\t\t\t\tmaxHeight,\n\t\t\t});\n\n\t\t\t// Decompress via zstd subprocess\n\t\t\tconst proc = Bun.spawn([\"zstd\", \"-d\", this.archivePath, \"--stdout\"], {\n\t\t\t\tstdout: \"pipe\",\n\t\t\t\tstderr: \"ignore\",\n\t\t\t});\n\n\t\t\tconst reader = proc.stdout.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\t\t\tconst remaining = new Set(gapHeights);\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (remaining.size === 0) break;\n\n\t\t\t\t\t// Quick height check via regex (avoid full JSON parse)\n\t\t\t\t\tconst match = HEIGHT_REGEX.exec(line);\n\t\t\t\t\tif (!match) continue;\n\n\t\t\t\t\tconst height = Number.parseInt(match[1]);\n\n\t\t\t\t\t// Skip blocks we don't need\n\t\t\t\t\tif (!remaining.has(height)) {\n\t\t\t\t\t\t// Early exit if past all gap heights\n\t\t\t\t\t\tif (height > maxHeight) break;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Extract payload from TSV (id \\t timestamp \\t path \\t payload)\n\t\t\t\t\tconst tabIdx3 = nthIndex(line, \"\\t\", 3);\n\t\t\t\t\tif (tabIdx3 === -1) continue;\n\n\t\t\t\t\tconst path = line.substring(nthIndex(line, \"\\t\", 2) + 1, tabIdx3);\n\t\t\t\t\tif (path !== \"/new_block\") continue;\n\n\t\t\t\t\tconst payload = line.substring(tabIdx3 + 1);\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst res = await fetch(`${indexerUrl}/new_block`, {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t\t\"X-Source\": \"archive-replay\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tbody: payload,\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (res.ok) {\n\t\t\t\t\t\t\treplayed++;\n\t\t\t\t\t\t\tremaining.delete(height);\n\t\t\t\t\t\t\topts?.onProgress?.(replayed, height);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\terrors++;\n\t\t\t\t\t\t\tlogger.warn(\"Archive replay: indexer rejected block\", {\n\t\t\t\t\t\t\t\theight,\n\t\t\t\t\t\t\t\tstatus: res.status,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\terrors++;\n\t\t\t\t\t\tlogger.warn(\"Archive replay: POST failed\", {\n\t\t\t\t\t\t\theight,\n\t\t\t\t\t\t\terror: String(err),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Early exit if all gaps filled or past max height\n\t\t\t\tif (remaining.size === 0) {\n\t\t\t\t\tproc.kill();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Wait for process to exit\n\t\t\tawait proc.exited;\n\n\t\t\tif (remaining.size > 0) {\n\t\t\t\t// Don't spread `remaining` (can be millions) just to sample a few.\n\t\t\t\tconst sample: number[] = [];\n\t\t\t\tfor (const h of remaining) {\n\t\t\t\t\tsample.push(h);\n\t\t\t\t\tif (sample.length === 5) break;\n\t\t\t\t}\n\t\t\t\tlogger.warn(\"Archive replay: some heights not found in archive\", {\n\t\t\t\t\tmissing: remaining.size,\n\t\t\t\t\tsample,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlogger.info(\"Archive replay: complete\", {\n\t\t\t\treplayed,\n\t\t\t\terrors,\n\t\t\t\tmissing: remaining.size,\n\t\t\t});\n\t\t} catch (err) {\n\t\t\t// Clean up on error (corrupt/partial downloads)\n\t\t\tthis.cleanupFile(this.archivePath);\n\t\t\tthis.cleanupFile(this.metaPath);\n\t\t\tthis.cleanupFile(this.partialPath);\n\t\t\tthrow err;\n\t\t}\n\n\t\treturn { replayed, errors };\n\t}\n\n\t/**\n\t * Ensure a fresh-enough archive exists locally.\n\t * Uses HTTP conditional requests to avoid redundant downloads.\n\t */\n\tprivate async ensureArchive(): Promise<void> {\n\t\tthis.cleanStaleFiles();\n\n\t\tconst meta = this.readMeta();\n\t\tconst cached = existsSync(this.archivePath) && meta !== null;\n\n\t\tif (cached) {\n\t\t\tconst age = Date.now() - new Date(meta.downloadedAt).getTime();\n\t\t\tif (age < CACHE_MAX_AGE_MS) {\n\t\t\t\t// Cache is fresh enough — check if remote has a newer version\n\t\t\t\tconst headers: Record<string, string> = {};\n\t\t\t\tif (meta.lastModified) {\n\t\t\t\t\theaders[\"If-Modified-Since\"] = meta.lastModified;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tconst res = await fetch(this.archiveUrl, {\n\t\t\t\t\t\tmethod: \"HEAD\",\n\t\t\t\t\t\theaders,\n\t\t\t\t\t\tsignal: AbortSignal.timeout(15_000),\n\t\t\t\t\t});\n\n\t\t\t\t\tif (res.status === 304) {\n\t\t\t\t\t\tlogger.info(\"Archive replay: using cached archive\", {\n\t\t\t\t\t\t\tageHrs: (age / 3600000).toFixed(1),\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// 200 = remote is newer, re-download below\n\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\"Archive replay: remote archive is newer, re-downloading\",\n\t\t\t\t\t);\n\t\t\t\t} catch {\n\t\t\t\t\t// Can't reach remote — use cache anyway\n\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\"Archive replay: remote unreachable, using cached archive\",\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlogger.info(\"Archive replay: cache expired, re-downloading\");\n\t\t\t}\n\t\t}\n\n\t\t// Download fresh archive\n\t\tlogger.info(\"Archive replay: downloading archive\", {\n\t\t\turl: this.archiveUrl.split(\"/\").pop(),\n\t\t});\n\n\t\tconst lastModified = await this.download(this.partialPath);\n\n\t\t// Atomic rename: partial → final\n\t\trenameSync(this.partialPath, this.archivePath);\n\n\t\t// Write meta sidecar\n\t\tthis.writeMeta({ lastModified, downloadedAt: new Date().toISOString() });\n\n\t\tlogger.info(\"Archive replay: download complete\");\n\t}\n\n\t/** Remove stale cache (> 24h) and orphaned partial files */\n\tprivate cleanStaleFiles(): void {\n\t\ttry {\n\t\t\t// Clean orphaned partial downloads\n\t\t\tif (existsSync(this.partialPath)) {\n\t\t\t\tunlinkSync(this.partialPath);\n\t\t\t}\n\n\t\t\t// Clean stale cache\n\t\t\tconst meta = this.readMeta();\n\t\t\tif (meta) {\n\t\t\t\tconst age = Date.now() - new Date(meta.downloadedAt).getTime();\n\t\t\t\tif (age > CACHE_MAX_AGE_MS) {\n\t\t\t\t\tthis.cleanupFile(this.archivePath);\n\t\t\t\t\tthis.cleanupFile(this.metaPath);\n\t\t\t\t\tlogger.info(\"Archive replay: cleaned stale cache\");\n\t\t\t\t}\n\t\t\t} else if (existsSync(this.archivePath)) {\n\t\t\t\t// Archive without meta — orphaned, clean up\n\t\t\t\tthis.cleanupFile(this.archivePath);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Best-effort cleanup\n\t\t}\n\t}\n\n\tprivate readMeta(): ArchiveMeta | null {\n\t\ttry {\n\t\t\tif (!existsSync(this.metaPath)) return null;\n\t\t\treturn JSON.parse(readFileSync(this.metaPath, \"utf-8\")) as ArchiveMeta;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate writeMeta(meta: ArchiveMeta): void {\n\t\ttry {\n\t\t\twriteFileSync(this.metaPath, JSON.stringify(meta));\n\t\t} catch {\n\t\t\t// Best-effort\n\t\t}\n\t}\n\n\tprivate cleanupFile(path: string): void {\n\t\ttry {\n\t\t\tif (existsSync(path)) unlinkSync(path);\n\t\t} catch {\n\t\t\t// Best-effort\n\t\t}\n\t}\n\n\t/** Download archive to disk with streaming. Returns Last-Modified header. */\n\tprivate async download(destPath: string): Promise<string | null> {\n\t\tconst res = await fetch(this.archiveUrl, {\n\t\t\tsignal: AbortSignal.timeout(30 * 60 * 1000), // 30 min timeout\n\t\t});\n\n\t\tif (!res.ok || !res.body) {\n\t\t\tthrow new Error(`Archive download failed: HTTP ${res.status}`);\n\t\t}\n\n\t\tconst lastModified = res.headers.get(\"last-modified\");\n\t\tconst totalBytes = Number(res.headers.get(\"content-length\") || 0);\n\t\tconst writer = Bun.file(destPath).writer();\n\t\tlet downloaded = 0;\n\t\tlet lastLog = 0;\n\n\t\tfor await (const chunk of res.body) {\n\t\t\twriter.write(chunk);\n\t\t\tdownloaded += chunk.byteLength;\n\n\t\t\t// Log progress every 5GB\n\t\t\tif (totalBytes > 0 && downloaded - lastLog > 5_000_000_000) {\n\t\t\t\tlastLog = downloaded;\n\t\t\t\tconst pct = ((downloaded / totalBytes) * 100).toFixed(0);\n\t\t\t\tlogger.info(\"Archive replay: downloading\", { progress: `${pct}%` });\n\t\t\t}\n\t\t}\n\n\t\tawait writer.end();\n\t\treturn lastModified;\n\t}\n}\n\n/** Find the nth occurrence of a character in a string */\nfunction nthIndex(str: string, char: string, n: number): number {\n\tlet idx = -1;\n\tfor (let i = 0; i < n; i++) {\n\t\tidx = str.indexOf(char, idx + 1);\n\t\tif (idx === -1) return -1;\n\t}\n\treturn idx;\n}\n",
|
|
6
|
-
"import { z } from \"zod\";\n\n// Parse comma-separated networks\nconst networksSchema = z.string().transform((val) => {\n\tconst networks = val\n\t\t.split(\",\")\n\t\t.map((n) => n.trim())\n\t\t.filter(Boolean);\n\tconst valid = [\"mainnet\", \"testnet\"];\n\tfor (const n of networks) {\n\t\tif (!valid.includes(n)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid network: ${n}. Must be one of: ${valid.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\treturn networks as (\"mainnet\" | \"testnet\")[];\n});\n\ninterface EnvSchemaOutput {\n\tDATABASE_URL?: string;\n\t/**\n\t * Shared indexer DB (blocks/txs/events). Falls back to DATABASE_URL.\n\t * Set this alongside TARGET_DATABASE_URL to enable dual-DB mode.\n\t */\n\tSOURCE_DATABASE_URL?: string;\n\t/**\n\t * Tenant DB (subgraph schemas + subgraphs table). Falls back to DATABASE_URL.\n\t * Set this alongside SOURCE_DATABASE_URL to enable dual-DB mode.\n\t */\n\tTARGET_DATABASE_URL?: string;\n\tNETWORK?: \"mainnet\" | \"testnet\";\n\tNETWORKS?: (\"mainnet\" | \"testnet\")[];\n\tLOG_LEVEL: \"debug\" | \"info\" | \"warn\" | \"error\";\n\tNODE_ENV: \"development\" | \"production\" | \"test\";\n}\n\n// Cast needed: z.preprocess / z.default create different _input vs _output types\n// that z.ZodType<T> can't represent without explicit input type param\nconst envSchema: z.ZodType<EnvSchemaOutput> = z.object({\n\tDATABASE_URL: z.preprocess(\n\t\t(val) => (typeof val === \"string\" && val.length === 0 ? undefined : val),\n\t\tz.string().url().optional(),\n\t),\n\tSOURCE_DATABASE_URL: z.preprocess(\n\t\t(val) => (typeof val === \"string\" && val.length === 0 ? undefined : val),\n\t\tz.string().url().optional(),\n\t),\n\tTARGET_DATABASE_URL: z.preprocess(\n\t\t(val) => (typeof val === \"string\" && val.length === 0 ? undefined : val),\n\t\tz.string().url().optional(),\n\t),\n\tNETWORK: z.enum([\"mainnet\", \"testnet\"]).optional(),\n\tNETWORKS: networksSchema.optional(),\n\tLOG_LEVEL: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n\tNODE_ENV: z\n\t\t.enum([\"development\", \"production\", \"test\"])\n\t\t.default(\"development\"),\n}) as unknown as z.ZodType<EnvSchemaOutput>;\n\nexport type Env = EnvSchemaOutput & {\n\tenabledNetworks: (\"mainnet\" | \"testnet\")[];\n};\n\nlet cachedEnv: Env | null = null;\n\nexport function getEnv(): Env {\n\tif (cachedEnv) {\n\t\treturn cachedEnv;\n\t}\n\n\tconst result = envSchema.safeParse(process.env);\n\n\tif (!result.success) {\n\t\tconsole.error(\"❌ Invalid environment configuration:\");\n\t\tconsole.error(z.treeifyError(result.error));\n\t\tthrow new Error(\"Invalid environment configuration\");\n\t}\n\n\t// Compute enabled networks from NETWORKS or NETWORK\n\tlet enabledNetworks: (\"mainnet\" | \"testnet\")[];\n\tif (result.data.NETWORKS && result.data.NETWORKS.length > 0) {\n\t\tenabledNetworks = result.data.NETWORKS;\n\t} else if (result.data.NETWORK) {\n\t\tenabledNetworks = [result.data.NETWORK];\n\t} else {\n\t\tenabledNetworks = [\"mainnet\"]; // Default\n\t}\n\n\tcachedEnv = { ...result.data, enabledNetworks };\n\treturn cachedEnv;\n}\n\n/**\n * True when `NODE_ENV=production`, read at RUNTIME — the single source of truth\n * for prod-vs-dev branching in this package.\n *\n * Why a helper instead of inlining `process.env.NODE_ENV === \"production\"`:\n * bunup/esbuild constant-folds the dot-access `process.env.NODE_ENV` to its\n * BUILD-time value. `@secondlayer/shared` is consumed by other services as its\n * built `dist`, so an inlined check freezes to a literal in the shipped bundle\n * (e.g. `const isProd = false`) and silently ignores the container's real\n * NODE_ENV. The bracket access below is NOT folded, so it stays a runtime read.\n * Route every prod check through here so the footgun lives in exactly one place.\n */\nexport function isProductionEnv(): boolean {\n\t// biome-ignore lint/complexity/useLiteralKeys: bracket access is deliberate — dot-access gets constant-folded by the bundler, freezing this to a build-time literal in the shipped dist.\n\treturn process.env[\"NODE_ENV\"] === \"production\";\n}\n\ntype EnvMap = NodeJS.ProcessEnv;\n\n/**\n * PoX-4 stacking decoder is ON by default — `/v1/index/stacking` is part of the\n * public surface, so the decoder that fills `pox4_calls` runs unless explicitly\n * opted out with `POX4_DECODER_ENABLED=false` (mirrors the sBTC decoder policy).\n */\nexport function isPox4DecoderEnabled(env: EnvMap = process.env): boolean {\n\treturn env.POX4_DECODER_ENABLED !== \"false\";\n}\n\n/**\n * PoX-5 print-event decoder is ON by default (mirrors pox-4/sBTC policy).\n * No activation gating needed: pox-5 events cannot exist before the Epoch 4.0\n * hard fork (Bitcoin block 960,230), so pre-activation the decoder idles at\n * tip. Opt out with `POX5_DECODER_ENABLED=false`.\n */\nexport function isPox5DecoderEnabled(env: EnvMap = process.env): boolean {\n\treturn env.POX5_DECODER_ENABLED !== \"false\";\n}\n\n/** Default ON. Opt out with `SBTC_DECODER_ENABLED=false`. */\nexport function isSbtcDecoderEnabled(env: EnvMap = process.env): boolean {\n\treturn env.SBTC_DECODER_ENABLED !== \"false\";\n}\n\n/** Default OFF. Opt in with `BNS_DECODER_ENABLED=true`. */\nexport function isBnsDecoderEnabled(env: EnvMap = process.env): boolean {\n\treturn env.BNS_DECODER_ENABLED === \"true\";\n}\n\n// Export for testing\nexport { envSchema };\n",
|
|
7
|
-
"import { getEnv } from \"./env.ts\";\n\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n\tdebug: 0,\n\tinfo: 1,\n\twarn: 2,\n\terror: 3,\n};\n\nclass Logger {\n\tprivate _level?: LogLevel;\n\tprivate _isProduction?: boolean;\n\tprivate _initialized = false;\n\n\tprivate init() {\n\t\tif (this._initialized) return;\n\t\tthis._initialized = true;\n\t\ttry {\n\t\t\tconst env = getEnv();\n\t\t\tthis._level = env.LOG_LEVEL;\n\t\t\tthis._isProduction = env.NODE_ENV === \"production\";\n\t\t} catch {\n\t\t\t// Fallback when env is unavailable (e.g. tests without DATABASE_URL)\n\t\t\tthis._level = \"info\";\n\t\t\tthis._isProduction = false;\n\t\t}\n\t}\n\n\tprivate get level(): LogLevel {\n\t\tthis.init();\n\t\t// biome-ignore lint/style/noNonNullAssertion: value is non-null after preceding check or by construction; TS narrowing limitation\n\t\treturn this._level!;\n\t}\n\n\tprivate get isProduction(): boolean {\n\t\tthis.init();\n\t\t// biome-ignore lint/style/noNonNullAssertion: value is non-null after preceding check or by construction; TS narrowing limitation\n\t\treturn this._isProduction!;\n\t}\n\n\tprivate shouldLog(level: LogLevel): boolean {\n\t\treturn LOG_LEVELS[level] >= LOG_LEVELS[this.level];\n\t}\n\n\tprivate formatMessage(\n\t\tlevel: LogLevel,\n\t\tmessage: string,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tmeta?: Record<string, any>,\n\t) {\n\t\tconst timestamp = new Date().toISOString();\n\n\t\tif (this.isProduction) {\n\t\t\t// JSON output for production\n\t\t\treturn JSON.stringify({\n\t\t\t\ttimestamp,\n\t\t\t\tlevel,\n\t\t\t\tmessage,\n\t\t\t\t...meta,\n\t\t\t});\n\t\t}\n\n\t\t// Human-readable output for development\n\t\tconst metaStr = meta ? ` ${JSON.stringify(meta)}` : \"\";\n\t\treturn `[${timestamp}] ${level.toUpperCase()}: ${message}${metaStr}`;\n\t}\n\n\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\tdebug(message: string, meta?: Record<string, any>): void {\n\t\tif (this.shouldLog(\"debug\")) {\n\t\t\tconsole.debug(this.formatMessage(\"debug\", message, meta));\n\t\t}\n\t}\n\n\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\tinfo(message: string, meta?: Record<string, any>): void {\n\t\tif (this.shouldLog(\"info\")) {\n\t\t\tconsole.info(this.formatMessage(\"info\", message, meta));\n\t\t}\n\t}\n\n\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\twarn(message: string, meta?: Record<string, any>): void {\n\t\tif (this.shouldLog(\"warn\")) {\n\t\t\tconsole.warn(this.formatMessage(\"warn\", message, meta));\n\t\t}\n\t}\n\n\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\terror(message: string, meta?: Record<string, any>): void {\n\t\tif (this.shouldLog(\"error\")) {\n\t\t\tconsole.error(this.formatMessage(\"error\", message, meta));\n\t\t}\n\t}\n}\n\n// Export singleton instance\nexport const logger: Logger = new Logger();\n"
|
|
8
|
-
],
|
|
9
|
-
"mappings": "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",
|
|
10
|
-
"debugId": "9E72CC1B9C56340B64756E2164756E21",
|
|
11
|
-
"names": []
|
|
12
|
-
}
|