@kici-dev/shared 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db-admin.d.ts +25 -3
- package/dist/db-admin.js +20 -11
- package/dist/db-collation.d.ts +60 -0
- package/dist/db-collation.js +52 -0
- package/dist/db-collation.test.d.ts +2 -0
- package/dist/env/allowlist.d.ts +6 -6
- package/dist/env/allowlist.js +7 -7
- package/dist/env/logger-env.d.ts +6 -1
- package/dist/env/logger-env.js +21 -0
- package/dist/idempotency-env-diff.d.ts +83 -0
- package/dist/idempotency-env-diff.js +163 -0
- package/dist/idempotency-env-diff.test.d.ts +2 -0
- package/dist/idempotency-files.d.ts +71 -0
- package/dist/idempotency-files.js +73 -6
- package/dist/idempotency-yaml-diff.d.ts +74 -0
- package/dist/idempotency-yaml-diff.js +241 -0
- package/dist/idempotency-yaml-diff.test.d.ts +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/logger.d.ts +4 -1
- package/dist/logger.js +23 -17
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/telemetry/index.js +2 -2
- package/dist/telemetry/init.d.ts +10 -0
- package/dist/telemetry/init.js +32 -1
- package/dist/tool-check.js +31 -11
- package/package.json +14 -1
- package/sbom.spdx.json +10 -5
package/dist/logger.js
CHANGED
|
@@ -17,9 +17,10 @@ let _serviceName;
|
|
|
17
17
|
*/
|
|
18
18
|
const _pendingFileTransportLoggers = /* @__PURE__ */ new Set();
|
|
19
19
|
function buildFileTransport() {
|
|
20
|
-
|
|
20
|
+
const dir = process.env.KICI_LOG_DIR;
|
|
21
|
+
if (!dir || dir === "undefined") return void 0;
|
|
21
22
|
return new DailyRotateFile({
|
|
22
|
-
dirname:
|
|
23
|
+
dirname: dir,
|
|
23
24
|
filename: buildLogFilename(_serviceName),
|
|
24
25
|
datePattern: "YYYY-MM-DD",
|
|
25
26
|
maxSize: process.env.KICI_LOG_MAX_SIZE ?? "500m",
|
|
@@ -66,13 +67,26 @@ function buildLogFilename(serviceName) {
|
|
|
66
67
|
return suffix ? `${base}-${suffix}-%DATE%.log` : `${base}-%DATE%.log`;
|
|
67
68
|
}
|
|
68
69
|
/**
|
|
70
|
+
* Pick the default JSON-vs-plain selection for `createLogger` callers that do
|
|
71
|
+
* not pass an explicit `json` option. Honours the operator-controlled
|
|
72
|
+
* `KICI_LOG_FORMAT` env var (`json` / `plain` / `auto`); anything else
|
|
73
|
+
* (including typos and unset) falls back to TTY detection so a piped CLI
|
|
74
|
+
* still produces machine-readable JSON.
|
|
75
|
+
*/
|
|
76
|
+
function pickJsonDefault() {
|
|
77
|
+
const envFormat = process.env.KICI_LOG_FORMAT;
|
|
78
|
+
if (envFormat === "json") return true;
|
|
79
|
+
if (envFormat === "plain") return false;
|
|
80
|
+
return !process.stdout.isTTY;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
69
83
|
* Create a winston logger instance.
|
|
70
84
|
*
|
|
71
85
|
* @param options - Logger configuration options
|
|
72
86
|
* @returns Configured winston logger
|
|
73
87
|
*/
|
|
74
88
|
function createLogger(options = {}) {
|
|
75
|
-
const { json =
|
|
89
|
+
const { json = pickJsonDefault(), level = "info", prefix } = options;
|
|
76
90
|
const TOKEN_MASK_RE = /kat_[0-9a-f]{64}/gi;
|
|
77
91
|
const maskTokens = (value) => {
|
|
78
92
|
if (typeof value === "string") return value.replace(TOKEN_MASK_RE, "kat_***");
|
|
@@ -103,13 +117,10 @@ function createLogger(options = {}) {
|
|
|
103
117
|
if (ctx.spanId) info["spanId"] = ctx.spanId;
|
|
104
118
|
return info;
|
|
105
119
|
});
|
|
106
|
-
const prettyFormat = winston.format.printf(({ level, message,
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
minute: "2-digit",
|
|
111
|
-
second: "2-digit"
|
|
112
|
-
});
|
|
120
|
+
const prettyFormat = winston.format.printf(({ level, message, requestId }) => {
|
|
121
|
+
const traceStr = typeof requestId === "string" ? `${pc.dim(`[${requestId.slice(0, 8)}]`)} ` : "";
|
|
122
|
+
const prefixStr = prefix ? `${prefix} ` : "";
|
|
123
|
+
if (level === "info") return `${traceStr}${prefixStr}${message}`;
|
|
113
124
|
let coloredLevel;
|
|
114
125
|
switch (level) {
|
|
115
126
|
case "error":
|
|
@@ -118,20 +129,15 @@ function createLogger(options = {}) {
|
|
|
118
129
|
case "warn":
|
|
119
130
|
coloredLevel = pc.yellow(level);
|
|
120
131
|
break;
|
|
121
|
-
case "info":
|
|
122
|
-
coloredLevel = pc.cyan(level);
|
|
123
|
-
break;
|
|
124
132
|
case "debug":
|
|
125
133
|
coloredLevel = pc.gray(level);
|
|
126
134
|
break;
|
|
127
135
|
default: coloredLevel = level;
|
|
128
136
|
}
|
|
129
|
-
|
|
130
|
-
const prefixStr = prefix ? `${prefix} ` : "";
|
|
131
|
-
return `${pc.dim(`[${time}]`)}${traceStr} ${coloredLevel}: ${prefixStr}${message}`;
|
|
137
|
+
return `${traceStr}${coloredLevel}: ${prefixStr}${message}`;
|
|
132
138
|
});
|
|
133
139
|
const jsonFormat = winston.format.combine(winston.format.timestamp(), traceContextFormat(), tokenMaskFormat(), winston.format.json());
|
|
134
|
-
const prettyPipeline = winston.format.combine(
|
|
140
|
+
const prettyPipeline = winston.format.combine(traceContextFormat(), tokenMaskFormat(), prettyFormat);
|
|
135
141
|
const loggerInstance = winston.createLogger({
|
|
136
142
|
level,
|
|
137
143
|
format: json ? jsonFormat : prettyPipeline,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { initTelemetry, getPrometheusExporter, type TelemetryConfig } from './init.js';
|
|
1
|
+
export { initTelemetry, getPrometheusExporter, collectRuntimeMetricNames, type TelemetryConfig, } from './init.js';
|
|
2
2
|
export { createMeter } from './metrics.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/telemetry/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import "../chunk-gOLHoazu.js";
|
|
2
|
-
import { getPrometheusExporter, initTelemetry } from "./init.js";
|
|
2
|
+
import { collectRuntimeMetricNames, getPrometheusExporter, initTelemetry } from "./init.js";
|
|
3
3
|
import { createMeter } from "./metrics.js";
|
|
4
|
-
export { createMeter, getPrometheusExporter, initTelemetry };
|
|
4
|
+
export { collectRuntimeMetricNames, createMeter, getPrometheusExporter, initTelemetry };
|
package/dist/telemetry/init.d.ts
CHANGED
|
@@ -17,4 +17,14 @@ export interface TelemetryConfig {
|
|
|
17
17
|
export declare function initTelemetry(config: TelemetryConfig): NodeSDK;
|
|
18
18
|
/** Get the PrometheusExporter instance created by initTelemetry(). */
|
|
19
19
|
export declare function getPrometheusExporter(): PrometheusExporter | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Boot `RuntimeNodeInstrumentation` standalone, exercise the event loop and
|
|
22
|
+
* garbage collector, collect once, and return the set of dotted instrument
|
|
23
|
+
* names it emitted. This is the ground truth for the curated runtime-metrics
|
|
24
|
+
* catalog drift guard (`scripts/generate-prometheus.ts`). It deliberately
|
|
25
|
+
* does NOT touch the singleton `_prometheusExporter` — it spins up an
|
|
26
|
+
* isolated SDK so it can be called from tooling without affecting a running
|
|
27
|
+
* service's telemetry.
|
|
28
|
+
*/
|
|
29
|
+
export declare function collectRuntimeMetricNames(): Promise<string[]>;
|
|
20
30
|
//# sourceMappingURL=init.d.ts.map
|
package/dist/telemetry/init.js
CHANGED
|
@@ -36,7 +36,38 @@ function initTelemetry(config) {
|
|
|
36
36
|
function getPrometheusExporter() {
|
|
37
37
|
return _prometheusExporter;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Boot `RuntimeNodeInstrumentation` standalone, exercise the event loop and
|
|
41
|
+
* garbage collector, collect once, and return the set of dotted instrument
|
|
42
|
+
* names it emitted. This is the ground truth for the curated runtime-metrics
|
|
43
|
+
* catalog drift guard (`scripts/generate-prometheus.ts`). It deliberately
|
|
44
|
+
* does NOT touch the singleton `_prometheusExporter` — it spins up an
|
|
45
|
+
* isolated SDK so it can be called from tooling without affecting a running
|
|
46
|
+
* service's telemetry.
|
|
47
|
+
*/
|
|
48
|
+
async function collectRuntimeMetricNames() {
|
|
49
|
+
const exporter = new PrometheusExporter({ preventServerStart: true });
|
|
50
|
+
const sdk = new NodeSDK({
|
|
51
|
+
resource: resourceFromAttributes({ [ATTR_SERVICE_NAME]: "runtime-metrics-drift-guard" }),
|
|
52
|
+
metricReader: exporter,
|
|
53
|
+
instrumentations: [new RuntimeNodeInstrumentation({ monitoringPrecision: 100 })]
|
|
54
|
+
});
|
|
55
|
+
sdk.start();
|
|
56
|
+
for (let round = 0; round < 5; round++) {
|
|
57
|
+
const junk = [];
|
|
58
|
+
for (let i = 0; i < 100; i++) junk.push(new Array(1e4).fill(i));
|
|
59
|
+
junk.length;
|
|
60
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
61
|
+
}
|
|
62
|
+
if (typeof global.gc === "function") global.gc();
|
|
63
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
64
|
+
const names = /* @__PURE__ */ new Set();
|
|
65
|
+
const { resourceMetrics } = await exporter.collect();
|
|
66
|
+
for (const scopeMetrics of resourceMetrics.scopeMetrics) for (const metric of scopeMetrics.metrics) names.add(metric.descriptor.name);
|
|
67
|
+
await sdk.shutdown();
|
|
68
|
+
return [...names].sort();
|
|
69
|
+
}
|
|
39
70
|
//#endregion
|
|
40
|
-
export { getPrometheusExporter, initTelemetry };
|
|
71
|
+
export { collectRuntimeMetricNames, getPrometheusExporter, initTelemetry };
|
|
41
72
|
|
|
42
73
|
//# sourceMappingURL=init.js.map
|
package/dist/tool-check.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./chunk-gOLHoazu.js";
|
|
2
|
-
import { execFileSync } from "node:child_process";
|
|
3
2
|
import { accessSync, constants } from "node:fs";
|
|
3
|
+
import { delimiter, join } from "node:path";
|
|
4
4
|
//#region src/tool-check.ts
|
|
5
5
|
/**
|
|
6
6
|
* Startup validation for required external tools and binaries.
|
|
@@ -40,19 +40,39 @@ function validateRequiredTools(requirements) {
|
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
42
|
* Check if a binary is available on PATH.
|
|
43
|
-
*
|
|
43
|
+
*
|
|
44
|
+
* Walks the directories in `process.env.PATH` and tests each candidate via
|
|
45
|
+
* `accessSync(..., X_OK)`. Pure-Node implementation — no `which` / `where`
|
|
46
|
+
* dependency, so the check works on minimal base images (Docker Hardened
|
|
47
|
+
* Images, distroless, alpine variants) that strip the `which` binary even
|
|
48
|
+
* when `debianutils` is technically installed.
|
|
49
|
+
*
|
|
50
|
+
* Windows: tries `name` + `name.exe`/`.cmd`/`.bat`/`.com` extensions (mirrors
|
|
51
|
+
* PATHEXT defaults) so `git` resolves to `git.exe` without the caller having
|
|
52
|
+
* to pre-suffix.
|
|
44
53
|
*/
|
|
45
54
|
function isBinaryOnPath(name) {
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
const pathEnv = process.env.PATH;
|
|
56
|
+
if (!pathEnv) return false;
|
|
57
|
+
const dirs = pathEnv.split(delimiter);
|
|
58
|
+
const exts = process.platform === "win32" ? [
|
|
59
|
+
"",
|
|
60
|
+
".exe",
|
|
61
|
+
".cmd",
|
|
62
|
+
".bat",
|
|
63
|
+
".com"
|
|
64
|
+
] : [""];
|
|
65
|
+
for (const dir of dirs) {
|
|
66
|
+
if (!dir) continue;
|
|
67
|
+
for (const ext of exts) {
|
|
68
|
+
const candidate = join(dir, name + ext);
|
|
69
|
+
try {
|
|
70
|
+
accessSync(candidate, constants.X_OK);
|
|
71
|
+
return true;
|
|
72
|
+
} catch {}
|
|
73
|
+
}
|
|
55
74
|
}
|
|
75
|
+
return false;
|
|
56
76
|
}
|
|
57
77
|
//#endregion
|
|
58
78
|
export { validateRequiredTools };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kici-dev/shared",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Shared utilities for the KiCI CI/CD stack — logging, zx setup, crypto, telemetry, health and metrics routes. No business logic.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kici",
|
|
@@ -53,6 +53,18 @@
|
|
|
53
53
|
"./idempotency-files": {
|
|
54
54
|
"import": "./dist/idempotency-files.js",
|
|
55
55
|
"types": "./dist/idempotency-files.d.ts"
|
|
56
|
+
},
|
|
57
|
+
"./idempotency-env-diff": {
|
|
58
|
+
"import": "./dist/idempotency-env-diff.js",
|
|
59
|
+
"types": "./dist/idempotency-env-diff.d.ts"
|
|
60
|
+
},
|
|
61
|
+
"./idempotency-yaml-diff": {
|
|
62
|
+
"import": "./dist/idempotency-yaml-diff.js",
|
|
63
|
+
"types": "./dist/idempotency-yaml-diff.d.ts"
|
|
64
|
+
},
|
|
65
|
+
"./db-collation": {
|
|
66
|
+
"import": "./dist/db-collation.js",
|
|
67
|
+
"types": "./dist/db-collation.d.ts"
|
|
56
68
|
}
|
|
57
69
|
},
|
|
58
70
|
"dependencies": {
|
|
@@ -73,6 +85,7 @@
|
|
|
73
85
|
"picocolors": "^1.1.1",
|
|
74
86
|
"winston": "^3.19.0",
|
|
75
87
|
"winston-daily-rotate-file": "^5.0.0",
|
|
88
|
+
"yaml": "^2.8.3",
|
|
76
89
|
"zod": "^4.3.6",
|
|
77
90
|
"zx": "^8.8.5"
|
|
78
91
|
},
|
package/sbom.spdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"spdxVersion": "SPDX-2.3",
|
|
3
3
|
"dataLicense": "CC0-1.0",
|
|
4
4
|
"SPDXID": "SPDXRef-DOCUMENT",
|
|
5
|
-
"name": "@kici-dev/shared@0.1.
|
|
6
|
-
"documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Fshared/0.1.
|
|
5
|
+
"name": "@kici-dev/shared@0.1.5",
|
|
6
|
+
"documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Fshared/0.1.5/85ac4622-fd3d-43e9-9c60-b957c319d477",
|
|
7
7
|
"creationInfo": {
|
|
8
|
-
"created": "2026-05-
|
|
8
|
+
"created": "2026-05-25T13:20:21Z",
|
|
9
9
|
"creators": [
|
|
10
10
|
"Tool: kici-sbom-generator"
|
|
11
11
|
]
|
|
@@ -947,7 +947,7 @@
|
|
|
947
947
|
{
|
|
948
948
|
"SPDXID": "SPDXRef-RootPackage",
|
|
949
949
|
"name": "@kici-dev/shared",
|
|
950
|
-
"versionInfo": "0.1.
|
|
950
|
+
"versionInfo": "0.1.5",
|
|
951
951
|
"downloadLocation": "NOASSERTION",
|
|
952
952
|
"filesAnalyzed": false,
|
|
953
953
|
"licenseConcluded": "NOASSERTION",
|
|
@@ -958,7 +958,7 @@
|
|
|
958
958
|
{
|
|
959
959
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
960
960
|
"referenceType": "purl",
|
|
961
|
-
"referenceLocator": "pkg:npm/%40kici-dev/shared@0.1.
|
|
961
|
+
"referenceLocator": "pkg:npm/%40kici-dev/shared@0.1.5"
|
|
962
962
|
}
|
|
963
963
|
],
|
|
964
964
|
"description": "Shared utilities for the KiCI CI/CD stack — logging, zx setup, crypto, telemetry, health and metrics routes. No business logic.",
|
|
@@ -6506,6 +6506,11 @@
|
|
|
6506
6506
|
"relatedSpdxElement": "SPDXRef-Package-winston-3.19.0",
|
|
6507
6507
|
"relationshipType": "DEPENDS_ON"
|
|
6508
6508
|
},
|
|
6509
|
+
{
|
|
6510
|
+
"spdxElementId": "SPDXRef-RootPackage",
|
|
6511
|
+
"relatedSpdxElement": "SPDXRef-Package-yaml-2.8.3",
|
|
6512
|
+
"relationshipType": "DEPENDS_ON"
|
|
6513
|
+
},
|
|
6509
6514
|
{
|
|
6510
6515
|
"spdxElementId": "SPDXRef-RootPackage",
|
|
6511
6516
|
"relatedSpdxElement": "SPDXRef-Package-zod-4.3.6",
|