@ckirg/corelib 0.1.22

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Costas Kirgoussios
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # @ckirg/corelib
2
+
3
+ The foundational package of the Corelib monorepo, providing essential utilities, resilient HTTP, structured logging, and database abstractions.
4
+
5
+ > [!IMPORTANT]
6
+ > Not on the public npm registry — install from GitHub Releases. See the [root install guide](../README.md#-installation-for-external-projects).
7
+
8
+ ## Features
9
+
10
+ - **Multi-Runtime Support**: Compatible with Node.js, Bun, and Deno.
11
+ - **Resilient HTTP**: `RequestUnlimited` (based on `ky`) with automatic retries and consistent error serialization.
12
+ - **Structured Logging**: Strict Logger API with telemetry and structured data support.
13
+ - **Database Abstraction**: Unified interface for SQLite (via LibSQL) and PostgreSQL.
14
+ - **Configuration Management**: `ConfigManager` with support for CLI, Environment Variables, and Local/Remote encrypted files.
15
+ - **Native Core (FFI)**: High-performance logic implemented in Rust and exposed via N-API.
16
+ - **Runtime Utilities**: Platform-agnostic helpers for environment, file system, and telemetry.
17
+
18
+ ## Installation
19
+
20
+ Install from the GitHub Release — see the [root install guide](../README.md#-installation-for-external-projects) for package manager overrides.
21
+
22
+ ```bash
23
+ # pnpm
24
+ pnpm add https://github.com/ckir/corelib/releases/download/v0.1.17/ckirg-corelib-0.1.17.tgz
25
+ ```
26
+
27
+ ## Usage Examples
28
+
29
+ ### 1. Resilient HTTP Requests
30
+ Use `endPoint` for single requests or `endPoints` for parallel requests.
31
+
32
+ ```typescript
33
+ import { endPoint } from '@ckirg/corelib';
34
+
35
+ const result = await endPoint('https://api.github.com/repos/ckir/corelib');
36
+
37
+ if (result.status === 'success') {
38
+ // result.value is a SerializedResponse object
39
+ console.log('Body:', result.value.body);
40
+ }
41
+ ```
42
+
43
+ ### 2. Proxied HTTP Requests (Self-Healing, Rotating Pool)
44
+ `RequestProxied` provides an identical API to `RequestUnlimited` but adds automatic rotation, full fallback, and automatic removal of dead proxies. The pool is self-healing — unhealthy endpoints are pruned on first failure and do not re-enter rotation.
45
+
46
+ ```typescript
47
+ import { RequestProxied } from '@ckirg/corelib';
48
+
49
+ const proxies = [
50
+ "https://proxy-us.example.com",
51
+ "https://proxy-eu.example.com",
52
+ "https://proxy-as.example.com"
53
+ ];
54
+
55
+ const client = new RequestProxied(proxies);
56
+
57
+ // Single request with automatic rotation and fallback
58
+ const result = await client.endPoint("https://api.nasdaq.com/api/market-info");
59
+
60
+ // Parallel requests with round-robin load balancing
61
+ const results = await client.endPoints([
62
+ "https://api.nasdaq.com/api/quote/AAPL/info",
63
+ "https://api.nasdaq.com/api/quote/TSLA/info"
64
+ ]);
65
+ ```
66
+
67
+ ### 3. Structured Logging
68
+ The logger follows a strict `(msg: string, extras?: object)` signature and handles runtime differences automatically.
69
+
70
+ ```typescript
71
+ import { logger } from '@ckirg/corelib';
72
+
73
+ // Basic logging
74
+ logger.info("Application started");
75
+
76
+ // Logging with structured metadata
77
+ logger.error("Database connection failed", {
78
+ host: "localhost",
79
+ port: 5432,
80
+ error: "Connection timeout"
81
+ });
82
+
83
+ // Telemetry support (if configured)
84
+ logger.setTelemetry('on');
85
+ logger.info("Critical event", { telemetry: true });
86
+ ```
87
+
88
+ ### 4. Unified Database API
89
+ Switch between SQLite and PostgreSQL with minimal configuration changes.
90
+
91
+ ```typescript
92
+ import { createDatabase } from '@ckirg/corelib';
93
+
94
+ // SQLite
95
+ const db = await createDatabase({
96
+ dialect: 'sqlite',
97
+ url: 'file:./app.db'
98
+ });
99
+
100
+ // PostgreSQL
101
+ // const db = await createDatabase({
102
+ // dialect: 'postgres',
103
+ // url: 'postgres://user:pass@localhost:5432/dbname'
104
+ // });
105
+
106
+ // Querying (Returns a DatabaseResult discriminated union)
107
+ const result = await db.query('SELECT * FROM users WHERE active = ?', [true]);
108
+
109
+ if (result.status === 'success') {
110
+ // result.value is a QueryResponse object containing .rows
111
+ const users = result.value.rows;
112
+ console.log(`Found ${users.length} users`);
113
+ } else {
114
+ console.error('Query failed:', result.reason.message);
115
+ }
116
+
117
+ // Transactions
118
+ await db.transaction(async () => {
119
+ await db.query('INSERT INTO logs (msg) VALUES (?)', ['Transaction step 1']);
120
+ await db.query('UPDATE status SET value = ?', ['processed']);
121
+ // Return success from the callback to commit the transaction
122
+ return { status: 'success', value: true };
123
+ });
124
+ ```
125
+
126
+
127
+ ### 5. Configuration Management
128
+ Manage complex configuration hierarchies with ease.
129
+
130
+ Both the **static** and **instance** forms of `get()` are supported by design — use whichever fits your call site. They read from the same singleton store.
131
+
132
+ ```typescript
133
+ import { ConfigManager } from '@ckirg/corelib';
134
+
135
+ // ── Instance form (explicit singleton) ────────────────────────────────────
136
+ const config = ConfigManager.getInstance();
137
+
138
+ // Initialize (parses CLI, Env, and local defaults). Idempotent under
139
+ // concurrency: simultaneous calls share one in-flight run.
140
+ await config.initialize();
141
+
142
+ // Instance get — nested value with dot-notation
143
+ const dbPort = config.get('database.port');
144
+
145
+ // ── Static form (convenience shorthand — same singleton, same data) ────────
146
+ // Both of these are equivalent after the first initialize() settles:
147
+ const port = ConfigManager.get('database.port');
148
+
149
+ // Load external encrypted configuration
150
+ await config.loadExternalConfig('https://remote-server.com/prod.json.enc');
151
+
152
+ // Reactive updates
153
+ config.on('change:database.port', (newPort) => {
154
+ console.log(`Port changed to ${newPort}`);
155
+ });
156
+ ```
157
+
158
+ #### Readiness
159
+
160
+ `get()` is synchronous and never throws — bundled defaults are seeded in the
161
+ constructor, so reads are safe even before `initialize()` resolves (a one-time
162
+ dev warning fires if you read pre-init, suppressed in production). Long-lived
163
+ orchestrators (DB connections, stream wrappers) that need *resolved* config
164
+ should gate on readiness:
165
+
166
+ ```typescript
167
+ if (!config.isInitialized) {
168
+ await config.whenReady(); // resolves when the first initialize() settles
169
+ }
170
+ ```
171
+
172
+ > **Anti-pattern — don't cache a sub-section at module scope.** Read at the hot
173
+ > point instead. `this._config`'s object identity is stable (mutation is in
174
+ > place), but caching a nested slice can still mask a later override:
175
+ >
176
+ > ```typescript
177
+ > // ❌ captured once at import; may miss overrides applied during initialize()
178
+ > const db = ConfigManager.getInstance().get('database');
179
+ >
180
+ > // ✅ read at the use site (static or instance — both work)
181
+ > const port = ConfigManager.get('database.port');
182
+ > ```
183
+
184
+ ### 6. Native Core FFI
185
+ Access high-performance Rust logic directly from TypeScript. The implementation is resilient and will disable FFI features if the native binary is missing rather than crashing the module.
186
+
187
+ ```typescript
188
+ import { Core } from '@ckirg/corelib';
189
+
190
+ // Check if FFI is available in current runtime
191
+ if (Core.isFfiAvailable()) {
192
+ console.log('Rust Version:', Core.getVersion());
193
+ const doubled = Core.logAndDouble("Double me", 21);
194
+ console.log('Result:', doubled); // 42
195
+ }
196
+
197
+ // Safe run method that includes runtime info
198
+ Core.run("maintenance-task", { verbose: true });
199
+ ```
200
+
201
+ ### 7. Runtime Utilities
202
+ Platform-agnostic helpers for common tasks.
203
+
204
+ **Key Helpers**:
205
+ - `detectRuntime()` — returns current runtime (`"node" | "bun" | "deno" | ...`)
206
+ - `sleep(ms)` — cross-runtime delay
207
+ - `getEnv(key)`, `getAllEnv()` — safe environment access
208
+ - `getSysInfo()` — detailed system info with automatic secret redaction
209
+ - `includeExcludeCron` — helpers for complex cron include/exclude patterns
210
+ - File system helpers with graceful fallbacks across runtimes
211
+
212
+ ```typescript
213
+ import { detectRuntime, sleep, getEnv, getSysInfo } from '@ckirg/corelib';
214
+
215
+ // Detect where we are (node, bun, deno, cloudflare, aws-lambda, etc.)
216
+ const runtime = detectRuntime();
217
+
218
+ // Resilient sleep
219
+ await sleep(1000);
220
+
221
+ // Platform-agnostic env access
222
+ const apiKey = getEnv('API_KEY');
223
+
224
+ // Detailed system and process telemetry (auto-redacts secrets)
225
+ const sysInfo = getSysInfo();
226
+ console.log('Memory Usage:', sysInfo.memory.rss);
227
+ ```
228
+
@@ -0,0 +1,27 @@
1
+ import {
2
+ DEFAULT_REQUEST_OPTIONS,
3
+ MAX_BACKOFF_LIMIT_MS,
4
+ MAX_RETRY_LIMIT,
5
+ MAX_TIMEOUT_MS,
6
+ MIN_TIMEOUT_MS,
7
+ RequestUnlimited,
8
+ clampNumber,
9
+ endPoint,
10
+ endPoints,
11
+ fullJitterDelay
12
+ } from "./chunk-PESRDNPD.js";
13
+ import "./chunk-BAVE2JXI.js";
14
+ import "./chunk-2DF4ADYX.js";
15
+ import "./chunk-HPE2XSTW.js";
16
+ export {
17
+ DEFAULT_REQUEST_OPTIONS,
18
+ MAX_BACKOFF_LIMIT_MS,
19
+ MAX_RETRY_LIMIT,
20
+ MAX_TIMEOUT_MS,
21
+ MIN_TIMEOUT_MS,
22
+ RequestUnlimited,
23
+ clampNumber,
24
+ endPoint,
25
+ endPoints,
26
+ fullJitterDelay
27
+ };
@@ -0,0 +1,6 @@
1
+ import { S as StrictLogger } from './flight-recorder-BCSVZvWQ.js';
2
+ export { F as FlightRecorderExtras, L as LogMethod, n as nextCid } from './flight-recorder-BCSVZvWQ.js';
3
+
4
+ declare const logger: StrictLogger;
5
+
6
+ export { StrictLogger, logger };
@@ -0,0 +1,75 @@
1
+ import {
2
+ nextCid
3
+ } from "./chunk-2DF4ADYX.js";
4
+
5
+ // src/loggers/implementations/browser.ts
6
+ var LEVEL_MAP = {
7
+ trace: 10,
8
+ debug: 20,
9
+ info: 30,
10
+ warn: 40,
11
+ error: 50,
12
+ fatal: 60,
13
+ silent: Infinity
14
+ };
15
+ var envLevel = (typeof process !== "undefined" ? process.env?.LOG_LEVEL : void 0) || "info";
16
+ var BrowserLogger = class _BrowserLogger {
17
+ ctx;
18
+ // Shared state object so children reflect parent level changes (non-independent).
19
+ state;
20
+ constructor(ctx = {}, state = { level: envLevel }) {
21
+ this.ctx = ctx;
22
+ this.state = state;
23
+ }
24
+ get level() {
25
+ return this.state.level;
26
+ }
27
+ set level(val) {
28
+ this.state.level = val;
29
+ }
30
+ get levelVal() {
31
+ return LEVEL_MAP[this.state.level] ?? 30;
32
+ }
33
+ emit(lvl, consoleFn, msg, extras) {
34
+ if ((LEVEL_MAP[lvl] ?? 30) < this.levelVal) return;
35
+ consoleFn(`[${lvl.toUpperCase()}]`, msg, { ...this.ctx, ...extras });
36
+ }
37
+ trace(msg, extras) {
38
+ this.emit("trace", console.debug.bind(console), msg, extras);
39
+ }
40
+ debug(msg, extras) {
41
+ this.emit("debug", console.debug.bind(console), msg, extras);
42
+ }
43
+ info(msg, extras) {
44
+ this.emit("info", console.info.bind(console), msg, extras);
45
+ }
46
+ warn(msg, extras) {
47
+ this.emit("warn", console.warn.bind(console), msg, extras);
48
+ }
49
+ error(msg, extras) {
50
+ this.emit("error", console.error.bind(console), msg, extras);
51
+ }
52
+ fatal(msg, extras) {
53
+ this.emit("fatal", console.error.bind(console), msg, extras);
54
+ }
55
+ child(bindings) {
56
+ return new _BrowserLogger({ ...this.ctx, ...bindings }, this.state);
57
+ }
58
+ setTelemetry(_mode) {
59
+ }
60
+ bindings() {
61
+ return { ...this.ctx };
62
+ }
63
+ silent() {
64
+ this.state.level = "silent";
65
+ }
66
+ flush(cb) {
67
+ cb?.();
68
+ }
69
+ };
70
+ var logger = new BrowserLogger();
71
+ var browser_default = logger;
72
+ export {
73
+ browser_default as logger,
74
+ nextCid
75
+ };
@@ -0,0 +1,41 @@
1
+ import {
2
+ StrictLoggerWrapper
3
+ } from "./chunk-HOOAMOFY.js";
4
+ import "./chunk-HPE2XSTW.js";
5
+
6
+ // src/loggers/implementations/bun.ts
7
+ import { Writable } from "stream";
8
+ import pino from "pino";
9
+ import pretty from "pino-pretty";
10
+ var isPretty = process.env.LOG_PRETTY === "true" || process.env.NODE_ENV !== "production" && process.env.LOG_PRETTY !== "false";
11
+ var level = process.env.LOG_LEVEL || "info";
12
+ var wsProxy = new Writable({
13
+ write(chunk, _encoding, cb) {
14
+ const fn = globalThis.__wsLogWrite;
15
+ if (fn) fn(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
16
+ cb();
17
+ }
18
+ });
19
+ var dest = pino.multistream([
20
+ {
21
+ level,
22
+ stream: isPretty ? pretty({
23
+ colorize: true,
24
+ translateTime: "SYS:standard",
25
+ ignore: "pid,hostname"
26
+ }) : process.stdout
27
+ },
28
+ { level, stream: wsProxy }
29
+ ]);
30
+ var pinoInstance = pino(
31
+ {
32
+ level,
33
+ redact: ["password", "secret", "token", "authorization", "apiKey"]
34
+ },
35
+ dest
36
+ );
37
+ var logger = new StrictLoggerWrapper(pinoInstance);
38
+ var bun_default = logger;
39
+ export {
40
+ bun_default as default
41
+ };
@@ -0,0 +1,9 @@
1
+ // src/utils/flight-recorder.ts
2
+ var _cid = 0;
3
+ function nextCid() {
4
+ return ++_cid;
5
+ }
6
+
7
+ export {
8
+ nextCid
9
+ };
@@ -0,0 +1,207 @@
1
+ import {
2
+ detectRuntime
3
+ } from "./chunk-HPE2XSTW.js";
4
+
5
+ // src/loggers/index.ts
6
+ var runtime = detectRuntime();
7
+ async function loadLogger() {
8
+ let impl;
9
+ switch (runtime) {
10
+ case "cloudflare":
11
+ impl = await import("./cloudflare-IOVZ3QEK.js");
12
+ break;
13
+ case "aws-lambda":
14
+ impl = await import("./lambda-CQXJKGN5.js");
15
+ break;
16
+ case "gcp-cloudrun":
17
+ impl = await import("./gcp-TRX5BADQ.js");
18
+ break;
19
+ case "bun":
20
+ impl = await import("./bun-LDGTNQBK.js");
21
+ break;
22
+ case "deno":
23
+ impl = await import("./deno-MVIUW5GX.js");
24
+ break;
25
+ default:
26
+ impl = await import("./node-6V4FDE5Z.js");
27
+ break;
28
+ }
29
+ const loggerRaw = impl.default;
30
+ const logger = typeof loggerRaw === "function" ? loggerRaw() : loggerRaw;
31
+ globalThis.logger = logger;
32
+ return logger;
33
+ }
34
+ var loggers_default = await loadLogger();
35
+
36
+ // src/utils/cron.ts
37
+ import { Cron } from "croner";
38
+ function includeExcludeCron(includeExprs, excludeExprs, handler, timezone) {
39
+ const opts = timezone ? { timezone } : {};
40
+ const job = new Cron("* * * * * *", () => {
41
+ const now = /* @__PURE__ */ new Date();
42
+ const included = includeExprs.some((expr) => {
43
+ const c = new Cron(expr, opts);
44
+ return c.nextRun(now) === null;
45
+ });
46
+ if (!included) return;
47
+ const excluded = excludeExprs.some((expr) => {
48
+ const c = new Cron(expr, opts);
49
+ return c.nextRun(now) === null;
50
+ });
51
+ if (!excluded) {
52
+ handler();
53
+ }
54
+ });
55
+ return job;
56
+ }
57
+
58
+ // src/utils/index.ts
59
+ var _createRequire;
60
+ if (typeof __EDGE_RUNTIME__ === "undefined" || !__EDGE_RUNTIME__) {
61
+ _createRequire = (await import("module")).createRequire;
62
+ }
63
+ var utilsLogger = loggers_default.child({ section: "Utils" });
64
+ var _require;
65
+ var getRequire = () => {
66
+ if (!_require) {
67
+ const runtime2 = detectRuntime();
68
+ if (_createRequire && typeof import.meta !== "undefined" && import.meta.url) {
69
+ try {
70
+ _require = _createRequire(import.meta.url);
71
+ } catch (_e) {
72
+ }
73
+ }
74
+ if (!_require && typeof globalThis.require === "function") {
75
+ _require = globalThis.require;
76
+ }
77
+ if (!_require) {
78
+ _require = (path) => {
79
+ throw new Error(
80
+ `require("${path}") is not available in this runtime (${runtime2}).`
81
+ );
82
+ };
83
+ }
84
+ }
85
+ return _require;
86
+ };
87
+ var Utils = {
88
+ /**
89
+ * Logs the current runtime information.
90
+ */
91
+ run: () => utilsLogger.info(`Running on ${detectRuntime()}`)
92
+ };
93
+ var getEnv = (key) => {
94
+ try {
95
+ if (typeof Deno !== "undefined" && Deno.env) {
96
+ return Deno.env.get(key);
97
+ }
98
+ } catch {
99
+ }
100
+ try {
101
+ if (typeof process !== "undefined" && process.env) {
102
+ return process.env[key];
103
+ }
104
+ } catch {
105
+ }
106
+ return void 0;
107
+ };
108
+ var getAllEnv = () => {
109
+ try {
110
+ if (typeof Deno !== "undefined" && Deno.env) {
111
+ return Deno.env.toObject();
112
+ }
113
+ } catch {
114
+ }
115
+ try {
116
+ if (typeof process !== "undefined" && process.env) {
117
+ return { ...process.env };
118
+ }
119
+ } catch {
120
+ }
121
+ return {};
122
+ };
123
+ var readTextFileSync = (file) => {
124
+ if (typeof Deno !== "undefined" && Deno.readTextFileSync) {
125
+ return Deno.readTextFileSync(file);
126
+ }
127
+ const { readFileSync } = getRequire()("node:fs");
128
+ return readFileSync(file, "utf8");
129
+ };
130
+ var existsSync = (file) => {
131
+ if (typeof Deno !== "undefined" && Deno.statSync) {
132
+ try {
133
+ Deno.statSync(file);
134
+ return true;
135
+ } catch {
136
+ return false;
137
+ }
138
+ }
139
+ const { existsSync: existsSync2 } = getRequire()("node:fs");
140
+ return existsSync2(file);
141
+ };
142
+ var getCwd = () => {
143
+ if (typeof Deno !== "undefined" && Deno.cwd) {
144
+ return Deno.cwd();
145
+ }
146
+ if (typeof process !== "undefined" && process.cwd) {
147
+ return process.cwd();
148
+ }
149
+ return "/";
150
+ };
151
+ var getDirname = () => {
152
+ const runtime2 = detectRuntime();
153
+ if (runtime2 === "deno") {
154
+ return new URL(".", import.meta.url).pathname;
155
+ }
156
+ if (typeof import.meta !== "undefined" && import.meta.url) {
157
+ const url = new URL(import.meta.url);
158
+ if (url.protocol === "file:") {
159
+ const { dirname } = getRequire()("node:path");
160
+ const { fileURLToPath } = getRequire()("node:url");
161
+ return dirname(fileURLToPath(import.meta.url));
162
+ }
163
+ const path = url.pathname;
164
+ return path.substring(0, path.lastIndexOf("/"));
165
+ }
166
+ return "";
167
+ };
168
+ var getPlatform = () => {
169
+ const runtime2 = detectRuntime();
170
+ let plat;
171
+ if (runtime2 === "deno") {
172
+ plat = Deno.build.os;
173
+ } else {
174
+ plat = process.platform;
175
+ }
176
+ return plat === "win32" || plat === "windows" ? "windows" : "linux";
177
+ };
178
+ var getMode = () => {
179
+ const env = getEnv("NODE_ENV")?.toLowerCase();
180
+ return env === "production" ? "production" : "development";
181
+ };
182
+ var getTempDir = () => {
183
+ if (detectRuntime() === "deno") {
184
+ return Deno.env.get("TMPDIR") || Deno.env.get("TEMP") || "/tmp";
185
+ } else {
186
+ const os = getRequire()("node:os");
187
+ return os.tmpdir();
188
+ }
189
+ };
190
+ var sleep = (ms) => new Promise((res) => setTimeout(res, ms));
191
+
192
+ export {
193
+ loggers_default,
194
+ includeExcludeCron,
195
+ getRequire,
196
+ Utils,
197
+ getEnv,
198
+ getAllEnv,
199
+ readTextFileSync,
200
+ existsSync,
201
+ getCwd,
202
+ getDirname,
203
+ getPlatform,
204
+ getMode,
205
+ getTempDir,
206
+ sleep
207
+ };