@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,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DbUtilsNoTelemetrySetLogger = DbUtilsNoTelemetrySetLogger;
4
+ exports.DbUtilsNoTelemetryBatchInsert = DbUtilsNoTelemetryBatchInsert;
5
+ exports.DbUtilsNoTelemetryExecSQL = DbUtilsNoTelemetryExecSQL;
6
+ exports.DbUtilsNoTelemetryQuerySQL = DbUtilsNoTelemetryQuerySQL;
7
+ const DbUtils_1 = require("./DbUtils");
8
+ let logger;
9
+ /**
10
+ * Injects the OTel logger instance used by no-telemetry DB operations.
11
+ * Must be called once at startup.
12
+ */
13
+ function DbUtilsNoTelemetrySetLogger(loggerIn) {
14
+ logger = loggerIn.createModuleLogger("DbUtilsNoTelemetry");
15
+ }
16
+ /**
17
+ * Execute a multi-row INSERT with a flat parameter array.
18
+ * Builds: INSERT INTO <tableCols> VALUES (?,?...),(?,?...),...
19
+ *
20
+ * @returns Number of rows inserted.
21
+ */
22
+ function DbUtilsNoTelemetryBatchInsert(tableCols, numCols,
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
+ rows) {
25
+ if (rows.length === 0)
26
+ return 0;
27
+ const rowSQL = `(${Array.from({ length: numCols }, () => "?").join(",")})`;
28
+ const multiValues = Array.from({ length: rows.length }, () => rowSQL).join(",");
29
+ const sql = `INSERT ${tableCols} VALUES ${multiValues}`;
30
+ return DbUtilsNoTelemetryExecSQL(sql, rows.flat());
31
+ }
32
+ /**
33
+ * Execute a write SQL statement **without** creating an OTel span.
34
+ * Use this on high-throughput paths where span overhead matters.
35
+ *
36
+ * @returns Number of rows changed.
37
+ */
38
+ function DbUtilsNoTelemetryExecSQL(sql, params = []) {
39
+ const dbType = (0, DbUtils_1.DbUtilsGetType)();
40
+ if (dbType === "postgres") {
41
+ const pgSql = (0, DbUtils_1.convertToPostgresPlaceholders)(sql);
42
+ return new Promise((resolve, reject) => {
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ (0, DbUtils_1.DbUtilsGetDatabase)().query(pgSql, params, (error, result) => {
45
+ if (error) {
46
+ logger.error(`SQL INSERT ERROR: ${sql.substring(0, 200)}`, error);
47
+ reject(error);
48
+ }
49
+ else {
50
+ resolve(result.rowCount || 0);
51
+ }
52
+ });
53
+ });
54
+ }
55
+ // SQLite (better-sqlite3) – synchronous
56
+ const stmt = (0, DbUtils_1.DbUtilsGetDatabase)().prepare(sql);
57
+ const result = stmt.run(params);
58
+ return result.changes;
59
+ }
60
+ /**
61
+ * Execute a read SQL query **without** creating an OTel span.
62
+ * Use this on high-throughput paths where span overhead matters.
63
+ *
64
+ * @returns Array of row objects.
65
+ */
66
+ function DbUtilsNoTelemetryQuerySQL(sql, params = [], debug = false) {
67
+ if (debug) {
68
+ console.log(sql);
69
+ }
70
+ const dbType = (0, DbUtils_1.DbUtilsGetType)();
71
+ if (dbType === "postgres") {
72
+ const pgSql = (0, DbUtils_1.convertToPostgresPlaceholders)(sql);
73
+ return new Promise((resolve, reject) => {
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ (0, DbUtils_1.DbUtilsGetDatabase)().query(pgSql, params, (error, result) => {
76
+ if (error) {
77
+ logger.error(`SQL ERROR: ${sql}`, error);
78
+ reject(error);
79
+ }
80
+ else {
81
+ resolve(result.rows);
82
+ }
83
+ });
84
+ });
85
+ }
86
+ // SQLite (better-sqlite3) – synchronous
87
+ const stmt = (0, DbUtils_1.DbUtilsGetDatabase)().prepare(sql);
88
+ return stmt.all(params);
89
+ }
@@ -0,0 +1,35 @@
1
+ import { StandardLogger, StandardMeter, StandardTracer } from "@devopsplaybook.io/otel-utils";
2
+ import { Span } from "@opentelemetry/sdk-trace-base";
3
+ /**
4
+ * Result of {@link createOTelContext}.
5
+ * Holds module-level singletons for the OpenTelemetry tracer, meter and logger
6
+ * used throughout a server process.
7
+ */
8
+ export interface OTelContext {
9
+ OTelTracer: () => StandardTracer;
10
+ OTelSetTracer: (tracer: StandardTracer) => void;
11
+ OTelMeter: () => StandardMeter;
12
+ OTelSetMeter: (meter: StandardMeter) => void;
13
+ OTelLogger: () => StandardLogger;
14
+ /**
15
+ * Retrieves the span previously attached to a request object.
16
+ * Equivalent to `req.tracerSpanApi`.
17
+ */
18
+ OTelRequestSpan: (req: any) => Span | undefined;
19
+ }
20
+ /**
21
+ * Creates an isolated OTel context (tracer / meter / logger singletons).
22
+ *
23
+ * Each server process should call this once at startup and pass the returned
24
+ * object to modules that need telemetry access. This avoids polluting the
25
+ * global module scope and makes unit-testing straightforward.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const otel = createOTelContext();
30
+ * otel.OTelSetTracer(new StandardTracer(config));
31
+ * otel.OTelSetMeter(new StandardMeter(config));
32
+ * otel.OTelLogger().initOTel(config);
33
+ * ```
34
+ */
35
+ export declare function createOTelContext(): OTelContext;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createOTelContext = createOTelContext;
4
+ const otel_utils_1 = require("@devopsplaybook.io/otel-utils");
5
+ /**
6
+ * Creates an isolated OTel context (tracer / meter / logger singletons).
7
+ *
8
+ * Each server process should call this once at startup and pass the returned
9
+ * object to modules that need telemetry access. This avoids polluting the
10
+ * global module scope and makes unit-testing straightforward.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const otel = createOTelContext();
15
+ * otel.OTelSetTracer(new StandardTracer(config));
16
+ * otel.OTelSetMeter(new StandardMeter(config));
17
+ * otel.OTelLogger().initOTel(config);
18
+ * ```
19
+ */
20
+ function createOTelContext() {
21
+ let tracer;
22
+ let meter;
23
+ let logger;
24
+ return {
25
+ OTelTracer: () => tracer,
26
+ OTelSetTracer: (t) => {
27
+ tracer = t;
28
+ },
29
+ OTelMeter: () => meter,
30
+ OTelSetMeter: (m) => {
31
+ meter = m;
32
+ },
33
+ OTelLogger: () => {
34
+ if (!logger) {
35
+ logger = new otel_utils_1.StandardLogger();
36
+ }
37
+ return logger;
38
+ },
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ OTelRequestSpan: (req) => req === null || req === void 0 ? void 0 : req.tracerSpanApi,
41
+ };
42
+ }
@@ -0,0 +1,52 @@
1
+ import { Pool } from "pg";
2
+ import { Span } from "@opentelemetry/sdk-trace-base";
3
+ import { StandardTracer, StandardLogger } from "@devopsplaybook.io/otel-utils";
4
+ /**
5
+ * Configuration subset required by the Postgres module.
6
+ */
7
+ export interface PostgresDbConfig {
8
+ DATABASE_POSTGRES_HOST: string;
9
+ DATABASE_POSTGRES_PORT: number;
10
+ DATABASE_POSTGRES_USER: string;
11
+ DATABASE_POSTGRES_PASSWORD: string;
12
+ DATABASE_POSTGRES_DATABASE: string;
13
+ }
14
+ /**
15
+ * Injects the OTel tracer and logger instances used by all Postgres operations.
16
+ * Must be called once at startup, before {@link PostgresDbUtilsInit}.
17
+ */
18
+ export declare function PostgresDbUtilsSetOTel(tracerIn: StandardTracer, loggerIn: StandardLogger): void;
19
+ /**
20
+ * Creates the Postgres connection pool and applies pending migration files
21
+ * from `sqlDir`.
22
+ *
23
+ * Migration files must follow the naming convention `init-NNNN.sql` and are
24
+ * applied in lexicographic order. A `metadata` table tracks which migrations
25
+ * have already been applied so they are idempotent.
26
+ *
27
+ * @param context Parent OTel span.
28
+ * @param config Configuration with Postgres connection fields.
29
+ * @param sqlDir Absolute path to the directory containing SQL migration files
30
+ * written for Postgres (with `$1,$2...` placeholders).
31
+ * If migrations are SQLite-first, use `convertToPostgresPlaceholders`
32
+ * before passing them.
33
+ */
34
+ export declare function PostgresDbUtilsInit(context: Span, config: PostgresDbConfig, sqlDir: string): Promise<void>;
35
+ /** Returns the underlying `pg.Pool` instance. */
36
+ export declare function PostgresDbUtilsGetPool(): Pool;
37
+ /**
38
+ * Execute a write SQL statement with OTel tracing.
39
+ * @returns Number of rows changed.
40
+ */
41
+ export declare function PostgresDbUtilsExecSQL(context: Span, sql: string, params?: unknown[]): Promise<number>;
42
+ /** Execute an entire SQL file (used for migrations). */
43
+ export declare function PostgresDbUtilsExecSQLFile(context: Span, filename: string): Promise<void>;
44
+ /**
45
+ * Execute a read SQL query with OTel tracing.
46
+ * @returns Array of row objects.
47
+ */
48
+ export declare function PostgresDbUtilsQuerySQL(context: Span, sql: string, params?: unknown[], debug?: boolean): Promise<any[]>;
49
+ /** Start a transaction. */
50
+ export declare function PostgresDbUtilsTransactionStart(context: Span): Promise<void>;
51
+ /** Commit a transaction. */
52
+ export declare function PostgresDbUtilsTransactionCommit(context: Span): Promise<void>;
@@ -0,0 +1,217 @@
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.PostgresDbUtilsSetOTel = PostgresDbUtilsSetOTel;
37
+ exports.PostgresDbUtilsInit = PostgresDbUtilsInit;
38
+ exports.PostgresDbUtilsGetPool = PostgresDbUtilsGetPool;
39
+ exports.PostgresDbUtilsExecSQL = PostgresDbUtilsExecSQL;
40
+ exports.PostgresDbUtilsExecSQLFile = PostgresDbUtilsExecSQLFile;
41
+ exports.PostgresDbUtilsQuerySQL = PostgresDbUtilsQuerySQL;
42
+ exports.PostgresDbUtilsTransactionStart = PostgresDbUtilsTransactionStart;
43
+ exports.PostgresDbUtilsTransactionCommit = PostgresDbUtilsTransactionCommit;
44
+ const pg_1 = require("pg");
45
+ const fs = __importStar(require("fs-extra"));
46
+ const api_1 = require("@opentelemetry/api");
47
+ let pool;
48
+ let tracer;
49
+ let logger;
50
+ /**
51
+ * Injects the OTel tracer and logger instances used by all Postgres operations.
52
+ * Must be called once at startup, before {@link PostgresDbUtilsInit}.
53
+ */
54
+ function PostgresDbUtilsSetOTel(tracerIn, loggerIn) {
55
+ tracer = tracerIn;
56
+ logger = loggerIn.createModuleLogger("PostgresDbUtils");
57
+ }
58
+ /**
59
+ * Creates the Postgres connection pool and applies pending migration files
60
+ * from `sqlDir`.
61
+ *
62
+ * Migration files must follow the naming convention `init-NNNN.sql` and are
63
+ * applied in lexicographic order. A `metadata` table tracks which migrations
64
+ * have already been applied so they are idempotent.
65
+ *
66
+ * @param context Parent OTel span.
67
+ * @param config Configuration with Postgres connection fields.
68
+ * @param sqlDir Absolute path to the directory containing SQL migration files
69
+ * written for Postgres (with `$1,$2...` placeholders).
70
+ * If migrations are SQLite-first, use `convertToPostgresPlaceholders`
71
+ * before passing them.
72
+ */
73
+ async function PostgresDbUtilsInit(context, config, sqlDir) {
74
+ const span = tracer.startSpan("PostgresDbUtilsInit", context);
75
+ pool = new pg_1.Pool({
76
+ host: config.DATABASE_POSTGRES_HOST,
77
+ port: config.DATABASE_POSTGRES_PORT || 5432,
78
+ user: config.DATABASE_POSTGRES_USER,
79
+ password: config.DATABASE_POSTGRES_PASSWORD,
80
+ database: config.DATABASE_POSTGRES_DATABASE,
81
+ max: 20,
82
+ idleTimeoutMillis: 30000,
83
+ connectionTimeoutMillis: 10000,
84
+ keepAlive: true,
85
+ });
86
+ pool.on("error", (err) => {
87
+ logger.error("PostgreSQL pool connection error", err);
88
+ });
89
+ await PostgresDbUtilsExecSQLFile(span, `${sqlDir}/init-0000.sql`);
90
+ const initFiles = (await fs.readdir(sqlDir)).sort();
91
+ let dbVersionApplied = 0;
92
+ const dbVersionQuery = await PostgresDbUtilsQuerySQL(span, "SELECT MAX(value) as version FROM metadata WHERE \"type\" = 'db_version'");
93
+ if (dbVersionQuery.length > 0 && dbVersionQuery[0].version) {
94
+ dbVersionApplied = Number(dbVersionQuery[0].version);
95
+ }
96
+ logger.info(`Current DB Version: ${dbVersionApplied}`, span);
97
+ for (const initFile of initFiles) {
98
+ const regex = /init-(\d+).sql/g;
99
+ const match = regex.exec(initFile);
100
+ if (match) {
101
+ const dbVersionInitFile = Number(match[1]);
102
+ if (dbVersionInitFile > dbVersionApplied) {
103
+ logger.info(`Loading init file: ${initFile}`, span);
104
+ await PostgresDbUtilsExecSQLFile(span, `${sqlDir}/${initFile}`);
105
+ await PostgresDbUtilsQuerySQL(span, 'INSERT INTO metadata ("type", "value", "dateCreated") VALUES ($1, $2, $3)', ["db_version", dbVersionInitFile, new Date().toISOString()]);
106
+ }
107
+ }
108
+ }
109
+ span.end();
110
+ }
111
+ /** Returns the underlying `pg.Pool` instance. */
112
+ function PostgresDbUtilsGetPool() {
113
+ return pool;
114
+ }
115
+ /**
116
+ * Execute a write SQL statement with OTel tracing.
117
+ * @returns Number of rows changed.
118
+ */
119
+ function PostgresDbUtilsExecSQL(context, sql, params = []) {
120
+ const span = tracer.startSpan("PostgresDbUtilsExecSQL", context);
121
+ return new Promise((resolve, reject) => {
122
+ pool.query(sql, params, (error, result) => {
123
+ if (error) {
124
+ span.setStatus({
125
+ code: api_1.SpanStatusCode.ERROR,
126
+ message: error.message,
127
+ });
128
+ span.end();
129
+ reject(error);
130
+ }
131
+ else {
132
+ span.addEvent(`Impacted Rows: ${result.rowCount || 0}`);
133
+ span.end();
134
+ resolve(result.rowCount || 0);
135
+ }
136
+ });
137
+ });
138
+ }
139
+ /** Execute an entire SQL file (used for migrations). */
140
+ async function PostgresDbUtilsExecSQLFile(context, filename) {
141
+ const span = tracer.startSpan("PostgresDbUtilsExecSQLFile", context);
142
+ const sql = (await fs.readFile(filename)).toString();
143
+ return new Promise((resolve, reject) => {
144
+ pool.query(sql, (error) => {
145
+ if (error) {
146
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message });
147
+ span.end();
148
+ reject(error);
149
+ }
150
+ else {
151
+ span.end();
152
+ resolve();
153
+ }
154
+ });
155
+ });
156
+ }
157
+ /**
158
+ * Execute a read SQL query with OTel tracing.
159
+ * @returns Array of row objects.
160
+ */
161
+ function PostgresDbUtilsQuerySQL(context, sql, params = [], debug = false) {
162
+ const span = tracer.startSpan("PostgresDbUtilsQuerySQL", context);
163
+ if (debug) {
164
+ console.log(sql);
165
+ }
166
+ return new Promise((resolve, reject) => {
167
+ pool.query(sql, params, (error, result) => {
168
+ if (error) {
169
+ span.setStatus({
170
+ code: api_1.SpanStatusCode.ERROR,
171
+ message: error.message,
172
+ });
173
+ logger.error(`SQL ERROR: ${sql}`, error, span);
174
+ span.end();
175
+ reject(error);
176
+ }
177
+ else {
178
+ span.end();
179
+ resolve(result.rows);
180
+ }
181
+ });
182
+ });
183
+ }
184
+ /** Start a transaction. */
185
+ function PostgresDbUtilsTransactionStart(context) {
186
+ const span = tracer.startSpan("PostgresDbUtilsTransactionStart", context);
187
+ return new Promise((resolve, reject) => {
188
+ pool.query("BEGIN", (error) => {
189
+ if (error) {
190
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message });
191
+ span.end();
192
+ reject(error);
193
+ }
194
+ else {
195
+ span.end();
196
+ resolve();
197
+ }
198
+ });
199
+ });
200
+ }
201
+ /** Commit a transaction. */
202
+ function PostgresDbUtilsTransactionCommit(context) {
203
+ const span = tracer.startSpan("PostgresDbUtilsTransactionCommit", context);
204
+ return new Promise((resolve, reject) => {
205
+ pool.query("COMMIT", (error) => {
206
+ if (error) {
207
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message });
208
+ span.end();
209
+ reject(error);
210
+ }
211
+ else {
212
+ span.end();
213
+ resolve();
214
+ }
215
+ });
216
+ });
217
+ }
@@ -0,0 +1,40 @@
1
+ import Database from "better-sqlite3";
2
+ import { Span } from "@opentelemetry/sdk-trace-base";
3
+ import { StandardTracer, StandardLogger } from "@devopsplaybook.io/otel-utils";
4
+ /**
5
+ * Configuration subset required by the SQLite module.
6
+ */
7
+ export interface SqlDbConfig {
8
+ DATA_DIR: string;
9
+ }
10
+ /**
11
+ * Injects the OTel tracer and logger instances used by all SQL operations.
12
+ * Must be called once at startup, before {@link SqlDbUtilsInit}.
13
+ */
14
+ export declare function SqlDbUtilsSetOTel(tracerIn: StandardTracer, loggerIn: StandardLogger): void;
15
+ /**
16
+ * Opens the SQLite database and applies pending migration files from `sqlDir`.
17
+ *
18
+ * Migration files must follow the naming convention `init-NNNN.sql` and are
19
+ * applied in lexicographic order. A `metadata` table tracks which migrations
20
+ * have already been applied so they are idempotent.
21
+ *
22
+ * @param context Parent OTel span.
23
+ * @param config Configuration with `DATA_DIR`.
24
+ * @param sqlDir Absolute path to the directory containing SQL migration files.
25
+ */
26
+ export declare function SqlDbUtilsInit(context: Span, config: SqlDbConfig, sqlDir: string): Promise<void>;
27
+ /** Returns the underlying `better-sqlite3` Database instance. */
28
+ export declare function SqlDbUtilsGetDatabase(): Database.Database;
29
+ /**
30
+ * Execute a write SQL statement with OTel tracing.
31
+ * @returns Number of rows changed.
32
+ */
33
+ export declare function SqlDbUtilsExecSQL(context: Span, sql: string, params?: unknown[]): number;
34
+ /** Execute an entire SQL file (used for migrations). */
35
+ export declare function SqlDbUtilsExecSQLFile(context: Span, filename: string): void;
36
+ /**
37
+ * Execute a read SQL query with OTel tracing.
38
+ * @returns Array of row objects.
39
+ */
40
+ export declare function SqlDbUtilsQuerySQL(context: Span, sql: string, params?: unknown[], debug?: boolean): any[];
@@ -0,0 +1,156 @@
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.SqlDbUtilsSetOTel = SqlDbUtilsSetOTel;
40
+ exports.SqlDbUtilsInit = SqlDbUtilsInit;
41
+ exports.SqlDbUtilsGetDatabase = SqlDbUtilsGetDatabase;
42
+ exports.SqlDbUtilsExecSQL = SqlDbUtilsExecSQL;
43
+ exports.SqlDbUtilsExecSQLFile = SqlDbUtilsExecSQLFile;
44
+ exports.SqlDbUtilsQuerySQL = SqlDbUtilsQuerySQL;
45
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
46
+ const fs = __importStar(require("fs-extra"));
47
+ const api_1 = require("@opentelemetry/api");
48
+ let database;
49
+ let tracer;
50
+ let logger;
51
+ /**
52
+ * Injects the OTel tracer and logger instances used by all SQL operations.
53
+ * Must be called once at startup, before {@link SqlDbUtilsInit}.
54
+ */
55
+ function SqlDbUtilsSetOTel(tracerIn, loggerIn) {
56
+ tracer = tracerIn;
57
+ logger = loggerIn.createModuleLogger("SqlDbUtils");
58
+ }
59
+ /**
60
+ * Opens the SQLite database and applies pending migration files from `sqlDir`.
61
+ *
62
+ * Migration files must follow the naming convention `init-NNNN.sql` and are
63
+ * applied in lexicographic order. A `metadata` table tracks which migrations
64
+ * have already been applied so they are idempotent.
65
+ *
66
+ * @param context Parent OTel span.
67
+ * @param config Configuration with `DATA_DIR`.
68
+ * @param sqlDir Absolute path to the directory containing SQL migration files.
69
+ */
70
+ async function SqlDbUtilsInit(context, config, sqlDir) {
71
+ const span = tracer.startSpan("SqlDbUtilsInit", context);
72
+ await fs.ensureDir(config.DATA_DIR);
73
+ database = new better_sqlite3_1.default(`${config.DATA_DIR}/database.db`);
74
+ SqlDbUtilsExecSQLFile(span, `${sqlDir}/init-0000.sql`);
75
+ const initFiles = (await fs.readdir(sqlDir)).sort();
76
+ let dbVersionApplied = 0;
77
+ const rows = SqlDbUtilsQuerySQL(span, "SELECT MAX(value) as maxVersion FROM metadata WHERE type='db_version'");
78
+ if (rows.length > 0 && rows[0].maxVersion) {
79
+ dbVersionApplied = Number(rows[0].maxVersion);
80
+ }
81
+ logger.info(`Current DB Version: ${dbVersionApplied}`, span);
82
+ for (const initFile of initFiles) {
83
+ const regex = /init-(\d+).sql/g;
84
+ const match = regex.exec(initFile);
85
+ if (match) {
86
+ const dbVersionInitFile = Number(match[1]);
87
+ if (dbVersionInitFile > dbVersionApplied) {
88
+ logger.info(`Loading init file: ${initFile}`, span);
89
+ SqlDbUtilsExecSQLFile(span, `${sqlDir}/${initFile}`);
90
+ SqlDbUtilsExecSQL(span, "INSERT INTO metadata (type, value, dateCreated) VALUES ('db_version',?,?)", [dbVersionInitFile, new Date().toISOString()]);
91
+ }
92
+ }
93
+ }
94
+ span.end();
95
+ }
96
+ /** Returns the underlying `better-sqlite3` Database instance. */
97
+ function SqlDbUtilsGetDatabase() {
98
+ return database;
99
+ }
100
+ /**
101
+ * Execute a write SQL statement with OTel tracing.
102
+ * @returns Number of rows changed.
103
+ */
104
+ function SqlDbUtilsExecSQL(context, sql, params = []) {
105
+ const span = tracer.startSpan("SqlDbUtilsExecSQL", context);
106
+ try {
107
+ const stmt = database.prepare(sql);
108
+ const result = stmt.run(params);
109
+ span.addEvent(`Impacted Rows: ${result.changes}`);
110
+ span.end();
111
+ return result.changes;
112
+ }
113
+ catch (error) {
114
+ const err = error;
115
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
116
+ span.end();
117
+ throw error;
118
+ }
119
+ }
120
+ /** Execute an entire SQL file (used for migrations). */
121
+ function SqlDbUtilsExecSQLFile(context, filename) {
122
+ const span = tracer.startSpan("SqlDbUtilsExecSQLFile", context);
123
+ try {
124
+ const sql = fs.readFileSync(filename).toString();
125
+ database.exec(sql);
126
+ span.end();
127
+ }
128
+ catch (error) {
129
+ const err = error;
130
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
131
+ span.end();
132
+ throw error;
133
+ }
134
+ }
135
+ /**
136
+ * Execute a read SQL query with OTel tracing.
137
+ * @returns Array of row objects.
138
+ */
139
+ function SqlDbUtilsQuerySQL(context, sql, params = [], debug = false) {
140
+ const span = tracer.startSpan("SqlDbUtilsQuerySQL", context);
141
+ if (debug) {
142
+ console.log(sql);
143
+ }
144
+ try {
145
+ const stmt = database.prepare(sql);
146
+ const rows = stmt.all(params);
147
+ span.end();
148
+ return rows;
149
+ }
150
+ catch (error) {
151
+ const err = error;
152
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message });
153
+ span.end();
154
+ throw error;
155
+ }
156
+ }
@@ -0,0 +1,9 @@
1
+ import * as childProcess from "child_process";
2
+ /**
3
+ * Execute a shell command and return its stdout.
4
+ *
5
+ * @param command The command string to execute.
6
+ * @param options Optional `child_process.exec` options.
7
+ * @returns Resolves with stdout on success, rejects on error.
8
+ */
9
+ export declare function SystemCommandExecute(command: string, options?: childProcess.ExecOptions): Promise<string>;