@devopsplaybook.io/common-utils 1.0.0-beta.5.9c5ecde

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.github/workflows/main-build.yml +17 -0
  2. package/.github/workflows/npm-upgrade.yml +16 -0
  3. package/.github/workflows/pr-check.yml +26 -0
  4. package/.github/workflows/reusable-merge-build.yml +141 -0
  5. package/.github/workflows/reusable-npm-merge.yml +135 -0
  6. package/.github/workflows/reusable-npm-pr.yml +153 -0
  7. package/.github/workflows/reusable-npm-upgrade.yml +92 -0
  8. package/.github/workflows/reusable-pr-verify.yml +135 -0
  9. package/README.md +1 -0
  10. package/dist/index.d.ts +8 -0
  11. package/dist/index.js +24 -0
  12. package/dist/src/ConfigBase.d.ts +104 -0
  13. package/dist/src/ConfigBase.js +201 -0
  14. package/dist/src/DbUtils.d.ts +50 -0
  15. package/dist/src/DbUtils.js +117 -0
  16. package/dist/src/DbUtilsNoTelemetry.d.ts +27 -0
  17. package/dist/src/DbUtilsNoTelemetry.js +89 -0
  18. package/dist/src/OTelContext.d.ts +35 -0
  19. package/dist/src/OTelContext.js +42 -0
  20. package/dist/src/PostgresDbUtils.d.ts +52 -0
  21. package/dist/src/PostgresDbUtils.js +217 -0
  22. package/dist/src/SqlDbUtils.d.ts +40 -0
  23. package/dist/src/SqlDbUtils.js +156 -0
  24. package/dist/src/SystemCommand.d.ts +9 -0
  25. package/dist/src/SystemCommand.js +56 -0
  26. package/dist/src/Timeout.d.ts +6 -0
  27. package/dist/src/Timeout.js +15 -0
  28. package/eslint.config.mjs +10 -0
  29. package/index.ts +8 -0
  30. package/jest.config.js +13 -0
  31. package/package.json +50 -0
  32. package/prettierrc.json +5 -0
  33. package/src/ConfigBase.spec.ts +108 -0
  34. package/src/ConfigBase.ts +213 -0
  35. package/src/DbUtils.spec.ts +23 -0
  36. package/src/DbUtils.ts +118 -0
  37. package/src/DbUtilsNoTelemetry.spec.ts +174 -0
  38. package/src/DbUtilsNoTelemetry.ts +121 -0
  39. package/src/OTelContext.spec.ts +58 -0
  40. package/src/OTelContext.ts +65 -0
  41. package/src/PostgresDbUtils.spec.ts +155 -0
  42. package/src/PostgresDbUtils.ts +233 -0
  43. package/src/SqlDbUtils.spec.ts +111 -0
  44. package/src/SqlDbUtils.ts +153 -0
  45. package/src/SystemCommand.spec.ts +18 -0
  46. package/src/SystemCommand.ts +23 -0
  47. package/src/Timeout.spec.ts +18 -0
  48. package/src/Timeout.ts +12 -0
  49. package/tsconfig.json +14 -0
  50. package/tsconfig.spec.json +7 -0
@@ -0,0 +1,135 @@
1
+ name: "Reusable: PR Verify"
2
+
3
+ on:
4
+ workflow_call:
5
+ inputs:
6
+ docker_platforms:
7
+ description: "Docker platforms to build for"
8
+ required: false
9
+ type: string
10
+ default: "linux/arm64/v8,linux/amd64"
11
+ node_app_directories:
12
+ description: "JSON array of Node.js app directories to build, lint, and test"
13
+ required: false
14
+ type: string
15
+ default: ""
16
+ node_version:
17
+ description: "Node.js version to use"
18
+ required: false
19
+ type: string
20
+ default: "22"
21
+ secrets:
22
+ DOCKER_HUB_USERNAME:
23
+ required: true
24
+ DOCKER_HUB_ACCESS_TOKEN:
25
+ required: true
26
+ QUALITY_DASHBOARD_TOKEN:
27
+ required: false
28
+ QUALITY_DASHBOARD_URL:
29
+ required: false
30
+
31
+ jobs:
32
+ node-build:
33
+ if: inputs.node_app_directories != ''
34
+ strategy:
35
+ matrix:
36
+ app: ${{ fromJSON(inputs.node_app_directories) }}
37
+ runs-on: ubuntu-latest
38
+ defaults:
39
+ run:
40
+ working-directory: ${{ matrix.app }}
41
+ steps:
42
+ - uses: actions/checkout@v6
43
+
44
+ - name: Setup Node.js
45
+ uses: actions/setup-node@v4
46
+ with:
47
+ node-version: ${{ inputs.node_version }}
48
+ cache: "npm"
49
+ cache-dependency-path: ${{ matrix.app }}/package-lock.json
50
+
51
+ - name: Install dependencies
52
+ run: npm ci
53
+
54
+ - name: Build
55
+ run: npm run build
56
+
57
+ - name: Lint
58
+ run: npm run lint
59
+
60
+ - name: Test
61
+ run: npm run test
62
+
63
+ - name: Zip coverage and upload to Quality Dashboard
64
+ if: always()
65
+ env:
66
+ QUALITY_DASHBOARD_URL: ${{ secrets.QUALITY_DASHBOARD_URL }}
67
+ QUALITY_DASHBOARD_TOKEN: ${{ secrets.QUALITY_DASHBOARD_TOKEN }}
68
+ run: |
69
+ APP_NAME="${{ matrix.app }}"
70
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
71
+ REPORT_KEY="$(echo "$APP_NAME" | sed 's/[^a-zA-Z0-9._:\-\/]/_/g')_coverage"
72
+
73
+ if [ -d coverage ]; then
74
+ if [ -z "$QUALITY_DASHBOARD_URL" ] || [ -z "$QUALITY_DASHBOARD_TOKEN" ]; then
75
+ echo "Quality Dashboard URL or token not set, skipping upload"
76
+ exit 0
77
+ fi
78
+ # Zip coverage contents at root level so the jest processor
79
+ # finds coverage-final.json / clover.xml directly
80
+ (cd coverage && zip -r "${{ runner.temp }}/coverage.zip" .)
81
+
82
+ META=$(jq -n \
83
+ --arg key "$REPORT_KEY" \
84
+ --arg pkg "$PACKAGE_NAME" \
85
+ '{
86
+ key: $key,
87
+ displayName: "Coverage: \($pkg)",
88
+ processor: "jest"
89
+ }')
90
+
91
+ echo "Uploading coverage: $REPORT_KEY"
92
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
93
+ -X POST "$QUALITY_DASHBOARD_URL/api/reports/" \
94
+ -H "x-upload-token: $QUALITY_DASHBOARD_TOKEN" \
95
+ -F "meta=$META" \
96
+ -F "file=@${{ runner.temp }}/coverage.zip" \
97
+ --max-time 30)
98
+
99
+ if [ "$HTTP_CODE" = "201" ]; then
100
+ echo "\u2713 Coverage uploaded ($HTTP_CODE)"
101
+ else
102
+ echo "\u26a0 Upload returned HTTP $HTTP_CODE"
103
+ fi
104
+ else
105
+ echo "No coverage/ directory found, skipping upload"
106
+ fi
107
+
108
+ docker-build:
109
+ runs-on: ubuntu-latest
110
+ steps:
111
+ - uses: actions/checkout@v6
112
+
113
+ - name: Set up QEMU
114
+ uses: docker/setup-qemu-action@v4
115
+
116
+ - name: Set up Docker Buildx
117
+ uses: docker/setup-buildx-action@v4
118
+
119
+ - name: Login to Docker Hub
120
+ uses: docker/login-action@v4
121
+ with:
122
+ username: ${{ secrets.DOCKER_HUB_USERNAME }}
123
+ password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
124
+
125
+ - name: Build and Push Docker Image
126
+ run: |
127
+ set -e
128
+ SERVICE_NAME=$(cat package.json | jq -r '.name')
129
+ echo "Building ${SERVICE_NAME}:beta"
130
+ docker buildx build \
131
+ --platform ${{ inputs.docker_platforms }} \
132
+ --push \
133
+ -f Dockerfile \
134
+ -t ${{ secrets.DOCKER_HUB_USERNAME }}/${SERVICE_NAME}:beta \
135
+ .
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # common-utils
@@ -0,0 +1,8 @@
1
+ export * from "./src/OTelContext";
2
+ export * from "./src/ConfigBase";
3
+ export * from "./src/DbUtils";
4
+ export * from "./src/DbUtilsNoTelemetry";
5
+ export * from "./src/SqlDbUtils";
6
+ export * from "./src/PostgresDbUtils";
7
+ export * from "./src/SystemCommand";
8
+ export * from "./src/Timeout";
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./src/OTelContext"), exports);
18
+ __exportStar(require("./src/ConfigBase"), exports);
19
+ __exportStar(require("./src/DbUtils"), exports);
20
+ __exportStar(require("./src/DbUtilsNoTelemetry"), exports);
21
+ __exportStar(require("./src/SqlDbUtils"), exports);
22
+ __exportStar(require("./src/PostgresDbUtils"), exports);
23
+ __exportStar(require("./src/SystemCommand"), exports);
24
+ __exportStar(require("./src/Timeout"), exports);
@@ -0,0 +1,104 @@
1
+ import { ConfigOTelInterface } from "@devopsplaybook.io/otel-utils";
2
+ /**
3
+ * Configuration field descriptor used by {@link ConfigBase.addConfigField}.
4
+ */
5
+ export interface ConfigFieldDef {
6
+ /** Property name on the config instance. */
7
+ field: string;
8
+ /** When `true` the value is masked in log output. */
9
+ sensitive?: boolean;
10
+ }
11
+ /**
12
+ * Database-specific configuration fields shared by every project that
13
+ * supports both SQLite and PostgreSQL backends.
14
+ */
15
+ export interface ConfigDatabaseInterface {
16
+ DATABASE_TYPE: "sqlite" | "postgres";
17
+ DATABASE_POSTGRES_HOST: string;
18
+ DATABASE_POSTGRES_PORT: number;
19
+ DATABASE_POSTGRES_USER: string;
20
+ DATABASE_POSTGRES_PASSWORD: string;
21
+ DATABASE_POSTGRES_DATABASE: string;
22
+ }
23
+ /**
24
+ * Common server configuration fields shared across projects.
25
+ */
26
+ export interface ConfigCommonInterface extends ConfigOTelInterface, ConfigDatabaseInterface {
27
+ CONFIG_FILE: string;
28
+ API_PORT: number;
29
+ JWT_VALIDITY_DURATION: number;
30
+ CORS_POLICY_ORIGIN: string;
31
+ DATA_DIR: string;
32
+ JWT_KEY: string;
33
+ LOG_LEVEL: string;
34
+ }
35
+ /**
36
+ * Abstract base class for project configuration.
37
+ *
38
+ * Implements the three-layer override strategy used across all
39
+ * devopsplaybook.io server projects:
40
+ * 1. **Environment variable** (highest priority)
41
+ * 2. **config.json** file value
42
+ * 3. **Default** declared on the class property
43
+ *
44
+ * Subclasses add project-specific fields and call {@link addConfigField}
45
+ * inside their constructor so that {@link reload} picks them up.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * class MyConfig extends ConfigBase {
50
+ * public MY_SETTING = "default";
51
+ * constructor() {
52
+ * super("my-service");
53
+ * this.addConfigField({ field: "MY_SETTING" });
54
+ * }
55
+ * }
56
+ * ```
57
+ */
58
+ export declare abstract class ConfigBase implements ConfigCommonInterface {
59
+ SERVICE_ID: string;
60
+ VERSION: string;
61
+ OPENTELEMETRY_COLLECTOR_HTTP_TRACES: string;
62
+ OPENTELEMETRY_COLLECTOR_HTTP_METRICS: string;
63
+ OPENTELEMETRY_COLLECTOR_HTTP_LOGS: string;
64
+ OPENTELEMETRY_COLLECTOR_AWS: boolean;
65
+ OPENTELEMETRY_COLLECTOR_EXPORT_LOGS_INTERVAL_SECONDS: number;
66
+ OPENTELEMETRY_COLLECTOR_EXPORT_METRICS_INTERVAL_SECONDS: number;
67
+ OPENTELEMETRY_COLLECT_AUTHORIZATION_HEADER: string;
68
+ CONFIG_FILE: string;
69
+ API_PORT: number;
70
+ JWT_VALIDITY_DURATION: number;
71
+ CORS_POLICY_ORIGIN: string;
72
+ DATA_DIR: string;
73
+ JWT_KEY: string;
74
+ LOG_LEVEL: string;
75
+ DATABASE_TYPE: "sqlite" | "postgres";
76
+ DATABASE_POSTGRES_HOST: string;
77
+ DATABASE_POSTGRES_PORT: number;
78
+ DATABASE_POSTGRES_USER: string;
79
+ DATABASE_POSTGRES_PASSWORD: string;
80
+ DATABASE_POSTGRES_DATABASE: string;
81
+ /**
82
+ * Fields registered by subclasses (or the base) that {@link reload}
83
+ * should process. Common / DB / OTel fields are pre-registered.
84
+ */
85
+ private _fields;
86
+ /**
87
+ * @param serviceId Unique service identifier (e.g. `"cryptotrader-server"`).
88
+ * @param configFile Optional path to the JSON config file. Defaults to `"config.json"`.
89
+ */
90
+ constructor(serviceId: string, configFile?: string);
91
+ /**
92
+ * Register a configuration field so that {@link reload} processes it.
93
+ * Call this in your subclass constructor for every project-specific field.
94
+ */
95
+ addConfigField(def: ConfigFieldDef): void;
96
+ /**
97
+ * Load (or reload) configuration from the JSON file and environment variables.
98
+ * Environment variables always take precedence over file values.
99
+ *
100
+ * @param logger Optional log callback `(message: string) => void`.
101
+ * When omitted nothing is logged (useful in tests).
102
+ */
103
+ reload(logger?: (message: string) => void): Promise<void>;
104
+ }
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.ConfigBase = void 0;
40
+ const fse = __importStar(require("fs-extra"));
41
+ const uuid_1 = require("uuid");
42
+ const path_1 = __importDefault(require("path"));
43
+ /**
44
+ * Abstract base class for project configuration.
45
+ *
46
+ * Implements the three-layer override strategy used across all
47
+ * devopsplaybook.io server projects:
48
+ * 1. **Environment variable** (highest priority)
49
+ * 2. **config.json** file value
50
+ * 3. **Default** declared on the class property
51
+ *
52
+ * Subclasses add project-specific fields and call {@link addConfigField}
53
+ * inside their constructor so that {@link reload} picks them up.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * class MyConfig extends ConfigBase {
58
+ * public MY_SETTING = "default";
59
+ * constructor() {
60
+ * super("my-service");
61
+ * this.addConfigField({ field: "MY_SETTING" });
62
+ * }
63
+ * }
64
+ * ```
65
+ */
66
+ class ConfigBase {
67
+ /**
68
+ * @param serviceId Unique service identifier (e.g. `"cryptotrader-server"`).
69
+ * @param configFile Optional path to the JSON config file. Defaults to `"config.json"`.
70
+ */
71
+ constructor(serviceId, configFile) {
72
+ this.VERSION = "1";
73
+ this.OPENTELEMETRY_COLLECTOR_HTTP_TRACES = "";
74
+ this.OPENTELEMETRY_COLLECTOR_HTTP_METRICS = "";
75
+ this.OPENTELEMETRY_COLLECTOR_HTTP_LOGS = "";
76
+ this.OPENTELEMETRY_COLLECTOR_AWS = false;
77
+ this.OPENTELEMETRY_COLLECTOR_EXPORT_LOGS_INTERVAL_SECONDS = 60;
78
+ this.OPENTELEMETRY_COLLECTOR_EXPORT_METRICS_INTERVAL_SECONDS = 60;
79
+ this.OPENTELEMETRY_COLLECT_AUTHORIZATION_HEADER = "";
80
+ this.API_PORT = 8080;
81
+ this.JWT_VALIDITY_DURATION = 3 * 31 * 24 * 3600;
82
+ this.CORS_POLICY_ORIGIN = "";
83
+ this.DATA_DIR = process.env.DATA_DIR || "/data";
84
+ this.JWT_KEY = (0, uuid_1.v4)();
85
+ this.LOG_LEVEL = "info";
86
+ // -- Database fields --
87
+ this.DATABASE_TYPE = "sqlite";
88
+ this.DATABASE_POSTGRES_HOST = "";
89
+ this.DATABASE_POSTGRES_PORT = 5432;
90
+ this.DATABASE_POSTGRES_USER = "";
91
+ this.DATABASE_POSTGRES_PASSWORD = "";
92
+ this.DATABASE_POSTGRES_DATABASE = "";
93
+ /**
94
+ * Fields registered by subclasses (or the base) that {@link reload}
95
+ * should process. Common / DB / OTel fields are pre-registered.
96
+ */
97
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
+ this._fields = [];
99
+ this.SERVICE_ID = serviceId;
100
+ this.CONFIG_FILE = configFile || process.env.CONFIG_FILE || "config.json";
101
+ // Auto-detect version from nearest package.json
102
+ try {
103
+ const pkg = fse.readJsonSync(path_1.default.resolve(__dirname, "../package.json"));
104
+ if (pkg && pkg.version) {
105
+ this.VERSION = pkg.version;
106
+ }
107
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
108
+ }
109
+ catch (_e) {
110
+ // fallback to "1"
111
+ }
112
+ // Pre-register base + DB + OTel fields so reload() handles them
113
+ const baseFields = [
114
+ { field: "JWT_VALIDITY_DURATION" },
115
+ { field: "CORS_POLICY_ORIGIN" },
116
+ { field: "DATA_DIR" },
117
+ { field: "JWT_KEY", sensitive: true },
118
+ { field: "LOG_LEVEL" },
119
+ { field: "DATABASE_TYPE" },
120
+ { field: "DATABASE_POSTGRES_HOST" },
121
+ { field: "DATABASE_POSTGRES_PORT" },
122
+ { field: "DATABASE_POSTGRES_USER" },
123
+ { field: "DATABASE_POSTGRES_PASSWORD", sensitive: true },
124
+ { field: "DATABASE_POSTGRES_DATABASE" },
125
+ { field: "OPENTELEMETRY_COLLECTOR_HTTP_TRACES" },
126
+ { field: "OPENTELEMETRY_COLLECTOR_HTTP_METRICS" },
127
+ { field: "OPENTELEMETRY_COLLECTOR_HTTP_LOGS" },
128
+ { field: "OPENTELEMETRY_COLLECTOR_AWS" },
129
+ {
130
+ field: "OPENTELEMETRY_COLLECTOR_EXPORT_LOGS_INTERVAL_SECONDS",
131
+ },
132
+ {
133
+ field: "OPENTELEMETRY_COLLECTOR_EXPORT_METRICS_INTERVAL_SECONDS",
134
+ },
135
+ {
136
+ field: "OPENTELEMETRY_COLLECT_AUTHORIZATION_HEADER",
137
+ sensitive: true,
138
+ },
139
+ ];
140
+ for (const f of baseFields) {
141
+ this.addConfigField(f);
142
+ }
143
+ }
144
+ /**
145
+ * Register a configuration field so that {@link reload} processes it.
146
+ * Call this in your subclass constructor for every project-specific field.
147
+ */
148
+ addConfigField(def) {
149
+ var _a;
150
+ this._fields.push({
151
+ field: def.field,
152
+ sensitive: (_a = def.sensitive) !== null && _a !== void 0 ? _a : false,
153
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
154
+ defaultValue: this[def.field],
155
+ });
156
+ }
157
+ /**
158
+ * Load (or reload) configuration from the JSON file and environment variables.
159
+ * Environment variables always take precedence over file values.
160
+ *
161
+ * @param logger Optional log callback `(message: string) => void`.
162
+ * When omitted nothing is logged (useful in tests).
163
+ */
164
+ async reload(logger) {
165
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
166
+ const log = logger !== null && logger !== void 0 ? logger : (() => { });
167
+ let content = {};
168
+ try {
169
+ content = await fse.readJson(this.CONFIG_FILE);
170
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
171
+ }
172
+ catch (_e) {
173
+ // config file is optional – fall back to env + defaults
174
+ }
175
+ log(`Configuration Value: CONFIG_FILE: ${this.CONFIG_FILE}`);
176
+ log(`Configuration Value: SERVICE_ID: ${this.SERVICE_ID}`);
177
+ log(`Configuration Value: VERSION: ${this.VERSION}`);
178
+ for (const { field, sensitive } of this._fields) {
179
+ let from = "defaults";
180
+ if (process.env[field] !== undefined) {
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ this[field] = process.env[field];
183
+ from = "environment";
184
+ }
185
+ else if (content[field] !== undefined) {
186
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
187
+ this[field] = content[field];
188
+ from = "config";
189
+ }
190
+ if (sensitive) {
191
+ log(`Configuration Value: ${field}: ******************** (from ${from})`);
192
+ }
193
+ else {
194
+ log(
195
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
196
+ `Configuration Value: ${field}: ${this[field]} (from ${from})`);
197
+ }
198
+ }
199
+ }
200
+ }
201
+ exports.ConfigBase = ConfigBase;
@@ -0,0 +1,50 @@
1
+ import { Span } from "@opentelemetry/sdk-trace-base";
2
+ import { StandardTracer, StandardLogger } from "@devopsplaybook.io/otel-utils";
3
+ import * as SqlDbUtils from "./SqlDbUtils";
4
+ import * as PostgresDbUtils from "./PostgresDbUtils";
5
+ /**
6
+ * Configuration subset required by the unified DB facade.
7
+ */
8
+ export interface DbUtilsConfig extends SqlDbUtils.SqlDbConfig, PostgresDbUtils.PostgresDbConfig {
9
+ DATABASE_TYPE: "sqlite" | "postgres";
10
+ }
11
+ /**
12
+ * Injects the OTel tracer and logger instances used by the DB layer.
13
+ * Must be called once at startup, before {@link DbUtilsInit}.
14
+ */
15
+ export declare function DbUtilsSetOTel(tracer: StandardTracer, logger: StandardLogger): void;
16
+ /**
17
+ * Initialise the database layer.
18
+ *
19
+ * Dispatches to the SQLite or Postgres backend depending on
20
+ * `config.DATABASE_TYPE` and runs pending migration files from `sqlDir`.
21
+ *
22
+ * @param context Parent OTel span.
23
+ * @param config Server configuration.
24
+ * @param sqlDir Absolute path to the directory containing SQL migration files.
25
+ */
26
+ export declare function DbUtilsInit(context: Span, config: DbUtilsConfig, sqlDir: string): Promise<void>;
27
+ /**
28
+ * Returns the native database handle.
29
+ * - SQLite: `better-sqlite3` `Database` instance
30
+ * - Postgres: `pg` `Pool` instance
31
+ */
32
+ export declare function DbUtilsGetDatabase(): any;
33
+ /** Convert SQLite `?` placeholders to PostgreSQL `$1, $2, ...` numbering. */
34
+ export declare function convertToPostgresPlaceholders(sql: string): string;
35
+ /**
36
+ * Execute a write SQL statement with OTel tracing.
37
+ * Automatically converts `?` placeholders to `$N` when using Postgres.
38
+ *
39
+ * @returns Number of rows changed.
40
+ */
41
+ export declare function DbUtilsExecSQL(context: Span, sql: string, params?: unknown[]): number | Promise<number>;
42
+ /**
43
+ * Execute a read SQL query with OTel tracing.
44
+ * Automatically converts `?` placeholders to `$N` when using Postgres.
45
+ *
46
+ * @returns Array of row objects.
47
+ */
48
+ export declare function DbUtilsQuerySQL(context: Span, sql: string, params?: unknown[], debug?: boolean): any[] | Promise<any[]>;
49
+ /** Returns the active database type (`"sqlite"` or `"postgres"`). */
50
+ export declare function DbUtilsGetType(): "sqlite" | "postgres";
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DbUtilsSetOTel = DbUtilsSetOTel;
37
+ exports.DbUtilsInit = DbUtilsInit;
38
+ exports.DbUtilsGetDatabase = DbUtilsGetDatabase;
39
+ exports.convertToPostgresPlaceholders = convertToPostgresPlaceholders;
40
+ exports.DbUtilsExecSQL = DbUtilsExecSQL;
41
+ exports.DbUtilsQuerySQL = DbUtilsQuerySQL;
42
+ exports.DbUtilsGetType = DbUtilsGetType;
43
+ const SqlDbUtils = __importStar(require("./SqlDbUtils"));
44
+ const PostgresDbUtils = __importStar(require("./PostgresDbUtils"));
45
+ let databaseType = "sqlite";
46
+ /**
47
+ * Injects the OTel tracer and logger instances used by the DB layer.
48
+ * Must be called once at startup, before {@link DbUtilsInit}.
49
+ */
50
+ function DbUtilsSetOTel(tracer, logger) {
51
+ SqlDbUtils.SqlDbUtilsSetOTel(tracer, logger);
52
+ PostgresDbUtils.PostgresDbUtilsSetOTel(tracer, logger);
53
+ }
54
+ /**
55
+ * Initialise the database layer.
56
+ *
57
+ * Dispatches to the SQLite or Postgres backend depending on
58
+ * `config.DATABASE_TYPE` and runs pending migration files from `sqlDir`.
59
+ *
60
+ * @param context Parent OTel span.
61
+ * @param config Server configuration.
62
+ * @param sqlDir Absolute path to the directory containing SQL migration files.
63
+ */
64
+ async function DbUtilsInit(context, config, sqlDir) {
65
+ databaseType = config.DATABASE_TYPE;
66
+ if (databaseType === "postgres") {
67
+ await PostgresDbUtils.PostgresDbUtilsInit(context, config, sqlDir);
68
+ }
69
+ else {
70
+ await SqlDbUtils.SqlDbUtilsInit(context, config, sqlDir);
71
+ }
72
+ }
73
+ /**
74
+ * Returns the native database handle.
75
+ * - SQLite: `better-sqlite3` `Database` instance
76
+ * - Postgres: `pg` `Pool` instance
77
+ */
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ function DbUtilsGetDatabase() {
80
+ if (databaseType === "postgres") {
81
+ return PostgresDbUtils.PostgresDbUtilsGetPool();
82
+ }
83
+ return SqlDbUtils.SqlDbUtilsGetDatabase();
84
+ }
85
+ /** Convert SQLite `?` placeholders to PostgreSQL `$1, $2, ...` numbering. */
86
+ function convertToPostgresPlaceholders(sql) {
87
+ let paramIndex = 1;
88
+ return sql.replace(/\?/g, () => `$${paramIndex++}`);
89
+ }
90
+ /**
91
+ * Execute a write SQL statement with OTel tracing.
92
+ * Automatically converts `?` placeholders to `$N` when using Postgres.
93
+ *
94
+ * @returns Number of rows changed.
95
+ */
96
+ function DbUtilsExecSQL(context, sql, params = []) {
97
+ if (databaseType === "postgres") {
98
+ return PostgresDbUtils.PostgresDbUtilsExecSQL(context, convertToPostgresPlaceholders(sql), params);
99
+ }
100
+ return SqlDbUtils.SqlDbUtilsExecSQL(context, sql, params);
101
+ }
102
+ /**
103
+ * Execute a read SQL query with OTel tracing.
104
+ * Automatically converts `?` placeholders to `$N` when using Postgres.
105
+ *
106
+ * @returns Array of row objects.
107
+ */
108
+ function DbUtilsQuerySQL(context, sql, params = [], debug = false) {
109
+ if (databaseType === "postgres") {
110
+ return PostgresDbUtils.PostgresDbUtilsQuerySQL(context, convertToPostgresPlaceholders(sql), params, debug);
111
+ }
112
+ return SqlDbUtils.SqlDbUtilsQuerySQL(context, sql, params, debug);
113
+ }
114
+ /** Returns the active database type (`"sqlite"` or `"postgres"`). */
115
+ function DbUtilsGetType() {
116
+ return databaseType;
117
+ }
@@ -0,0 +1,27 @@
1
+ import { StandardLogger } from "@devopsplaybook.io/otel-utils";
2
+ /**
3
+ * Injects the OTel logger instance used by no-telemetry DB operations.
4
+ * Must be called once at startup.
5
+ */
6
+ export declare function DbUtilsNoTelemetrySetLogger(loggerIn: StandardLogger): void;
7
+ /**
8
+ * Execute a multi-row INSERT with a flat parameter array.
9
+ * Builds: INSERT INTO <tableCols> VALUES (?,?...),(?,?...),...
10
+ *
11
+ * @returns Number of rows inserted.
12
+ */
13
+ export declare function DbUtilsNoTelemetryBatchInsert(tableCols: string, numCols: number, rows: any[][]): number | Promise<number>;
14
+ /**
15
+ * Execute a write SQL statement **without** creating an OTel span.
16
+ * Use this on high-throughput paths where span overhead matters.
17
+ *
18
+ * @returns Number of rows changed.
19
+ */
20
+ export declare function DbUtilsNoTelemetryExecSQL(sql: string, params?: unknown[]): number | Promise<number>;
21
+ /**
22
+ * Execute a read SQL query **without** creating an OTel span.
23
+ * Use this on high-throughput paths where span overhead matters.
24
+ *
25
+ * @returns Array of row objects.
26
+ */
27
+ export declare function DbUtilsNoTelemetryQuerySQL(sql: string, params?: unknown[], debug?: boolean): any[] | Promise<any[]>;