@fulmenhq/tsfulmen 0.3.3 → 0.4.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/CHANGELOG.md +76 -1
- package/README.md +30 -1
- package/config/crucible-ts/taxonomy/metrics.yaml +1 -1
- package/dist/appidentity/index.js +9 -2209
- package/dist/appidentity/index.js.map +1 -1
- package/dist/assets/index.d.ts +184 -0
- package/dist/assets/index.js +4 -0
- package/dist/assets/index.js.map +1 -0
- package/dist/bin/prometheus-cli.js +15 -9139
- package/dist/bin/prometheus-cli.js.map +1 -1
- package/dist/bin/schema-cli.js +4 -6098
- package/dist/bin/schema-cli.js.map +1 -1
- package/dist/bin/signals-cli.js +15 -1910
- package/dist/bin/signals-cli.js.map +1 -1
- package/dist/chunk-24BAF63H.js +194 -0
- package/dist/chunk-24BAF63H.js.map +1 -0
- package/dist/chunk-3HISR2XI.js +36 -0
- package/dist/chunk-3HISR2XI.js.map +1 -0
- package/dist/chunk-56F2EEAG.js +328 -0
- package/dist/chunk-56F2EEAG.js.map +1 -0
- package/dist/chunk-5HZEJGZ5.js +725 -0
- package/dist/chunk-5HZEJGZ5.js.map +1 -0
- package/dist/chunk-7YWACZEJ.js +837 -0
- package/dist/chunk-7YWACZEJ.js.map +1 -0
- package/dist/chunk-BCO3FUUO.js +682 -0
- package/dist/chunk-BCO3FUUO.js.map +1 -0
- package/dist/chunk-GOTEQYAD.js +410 -0
- package/dist/chunk-GOTEQYAD.js.map +1 -0
- package/dist/chunk-HY6JQOFN.js +32 -0
- package/dist/chunk-HY6JQOFN.js.map +1 -0
- package/dist/chunk-LMNAUTW7.js +43 -0
- package/dist/chunk-LMNAUTW7.js.map +1 -0
- package/dist/chunk-LWAH2UG7.js +103 -0
- package/dist/chunk-LWAH2UG7.js.map +1 -0
- package/dist/chunk-OHE2AOH4.js +273 -0
- package/dist/chunk-OHE2AOH4.js.map +1 -0
- package/dist/chunk-P3DNOZOG.js +1488 -0
- package/dist/chunk-P3DNOZOG.js.map +1 -0
- package/dist/chunk-PQ6TKPUN.js +27 -0
- package/dist/chunk-PQ6TKPUN.js.map +1 -0
- package/dist/chunk-QCCN6POA.js +211 -0
- package/dist/chunk-QCCN6POA.js.map +1 -0
- package/dist/chunk-QGTNV4U2.js +985 -0
- package/dist/chunk-QGTNV4U2.js.map +1 -0
- package/dist/chunk-RSLFR4TO.js +631 -0
- package/dist/chunk-RSLFR4TO.js.map +1 -0
- package/dist/chunk-SNV5WG5V.js +9412 -0
- package/dist/chunk-SNV5WG5V.js.map +1 -0
- package/dist/chunk-UP2VWCW5.js +33 -0
- package/dist/chunk-UP2VWCW5.js.map +1 -0
- package/dist/chunk-VJIGX7MH.js +208 -0
- package/dist/chunk-VJIGX7MH.js.map +1 -0
- package/dist/chunk-WCCMRVZF.js +130 -0
- package/dist/chunk-WCCMRVZF.js.map +1 -0
- package/dist/chunk-WSMVLWLB.js +269 -0
- package/dist/chunk-WSMVLWLB.js.map +1 -0
- package/dist/config/index.js +6 -1220
- package/dist/config/index.js.map +1 -1
- package/dist/crucible/fulpack/index.js +2 -25
- package/dist/crucible/fulpack/index.js.map +1 -1
- package/dist/crucible/index.js +13 -1129
- package/dist/crucible/index.js.map +1 -1
- package/dist/docscribe/index.js +2 -680
- package/dist/docscribe/index.js.map +1 -1
- package/dist/errors/index.js +5 -1831
- package/dist/errors/index.js.map +1 -1
- package/dist/errors-D7KXV3QW.js +4 -0
- package/dist/errors-D7KXV3QW.js.map +1 -0
- package/dist/errors-R7ZODJXS.js +8 -0
- package/dist/errors-R7ZODJXS.js.map +1 -0
- package/dist/export-EA3RNQNS.js +6 -0
- package/dist/export-EA3RNQNS.js.map +1 -0
- package/dist/foundry/index.js +340 -3949
- package/dist/foundry/index.js.map +1 -1
- package/dist/foundry/similarity/index.js +3 -134
- package/dist/foundry/similarity/index.js.map +1 -1
- package/dist/fulencode/index.js +3 -319
- package/dist/fulencode/index.js.map +1 -1
- package/dist/fulhash/index.js +3 -611
- package/dist/fulhash/index.js.map +1 -1
- package/dist/fulpack/index.js +3 -1515
- package/dist/fulpack/index.js.map +1 -1
- package/dist/hash-YOJYVOWH.js +4 -0
- package/dist/hash-YOJYVOWH.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +14 -4395
- package/dist/index.js.map +1 -1
- package/dist/loader-VP2ZIC4O.js +9 -0
- package/dist/loader-VP2ZIC4O.js.map +1 -0
- package/dist/logging/index.js +2 -723
- package/dist/logging/index.js.map +1 -1
- package/dist/pathfinder/index.js +18 -2341
- package/dist/pathfinder/index.js.map +1 -1
- package/dist/prom-client-PNEBGD3G.js +4801 -0
- package/dist/prom-client-PNEBGD3G.js.map +1 -0
- package/dist/reports/license-inventory.csv +1 -1
- package/dist/schema/index.d.ts +19 -6
- package/dist/schema/index.js +4 -6148
- package/dist/schema/index.js.map +1 -1
- package/dist/signals/index.js +8 -2767
- package/dist/signals/index.js.map +1 -1
- package/dist/similarity/index.js +3 -134
- package/dist/similarity/index.js.map +1 -1
- package/dist/telemetry/http/index.js +9 -930
- package/dist/telemetry/http/index.js.map +1 -1
- package/dist/telemetry/index.js +4 -1561
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/prometheus/index.js +7 -8422
- package/dist/telemetry/prometheus/index.js.map +1 -1
- package/dist/validator-I3ASDVDB.js +6 -0
- package/dist/validator-I3ASDVDB.js.map +1 -0
- package/package.json +5 -1
- package/schemas/crucible-ts/library/module-manifest/v1.0.0/module-manifest.schema.json +1 -1
- package/schemas/crucible-ts/observability/logging/v1.0.0/definitions.schema.json +1 -1
- package/schemas/crucible-ts/observability/logging/v1.0.0/log-event.schema.json +13 -13
- package/schemas/crucible-ts/observability/logging/v1.0.0/logger-config.schema.json +2 -2
- package/schemas/crucible-ts/observability/logging/v1.0.0/logging-policy.schema.json +1 -1
- package/schemas/crucible-ts/observability/logging/v1.0.0/middleware-config.schema.json +2 -2
- package/schemas/crucible-ts/observability/logging/v1.0.0/severity-filter.schema.json +3 -3
- package/schemas/crucible-ts/upstream/3leaps/crucible/PROVENANCE.md +3 -3
- package/schemas/crucible-ts/upstream/3leaps/crucible/docs/catalog/classifiers/README.md +1 -1
- package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/classifiers-framework.md +1 -1
- package/schemas/crucible-ts/upstream/3leaps/crucible/docs/standards/data-sensitivity-classification.md +1 -1
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
import { hostname } from 'os';
|
|
2
|
+
import pino from 'pino';
|
|
3
|
+
import { readFileSync, appendFileSync } from 'fs';
|
|
4
|
+
import { parse } from 'yaml';
|
|
5
|
+
|
|
6
|
+
// src/logging/logger.ts
|
|
7
|
+
|
|
8
|
+
// src/logging/types.ts
|
|
9
|
+
var LoggingProfile = /* @__PURE__ */ ((LoggingProfile2) => {
|
|
10
|
+
LoggingProfile2["SIMPLE"] = "simple";
|
|
11
|
+
LoggingProfile2["STRUCTURED"] = "structured";
|
|
12
|
+
LoggingProfile2["ENTERPRISE"] = "enterprise";
|
|
13
|
+
LoggingProfile2["CUSTOM"] = "custom";
|
|
14
|
+
return LoggingProfile2;
|
|
15
|
+
})(LoggingProfile || {});
|
|
16
|
+
var PolicyError = class extends Error {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "PolicyError";
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/logging/policy.ts
|
|
24
|
+
var PolicyEnforcer = class {
|
|
25
|
+
policy;
|
|
26
|
+
policyFile;
|
|
27
|
+
/**
|
|
28
|
+
* Create a new policy enforcer
|
|
29
|
+
*
|
|
30
|
+
* @param policyFile - Path to YAML policy file
|
|
31
|
+
* @throws {PolicyError} If policy file cannot be loaded or is invalid
|
|
32
|
+
*/
|
|
33
|
+
constructor(policyFile) {
|
|
34
|
+
this.policyFile = policyFile;
|
|
35
|
+
this.policy = this.loadPolicy(policyFile);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validate if a profile is allowed under the current policy
|
|
39
|
+
*
|
|
40
|
+
* @param profile - The logging profile to validate
|
|
41
|
+
* @param options - Optional validation context (appType, environment)
|
|
42
|
+
* @returns true if profile is allowed, false otherwise
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* const enforcer = new PolicyEnforcer('/org/logging-policy.yaml');
|
|
46
|
+
* if (enforcer.validateProfile(LoggingProfile.ENTERPRISE, { environment: 'production' })) {
|
|
47
|
+
* // Profile is allowed
|
|
48
|
+
* }
|
|
49
|
+
*/
|
|
50
|
+
validateProfile(profile, options) {
|
|
51
|
+
if (!this.policy.allowedProfiles.includes(profile)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (options?.environment && this.policy.environmentRules) {
|
|
55
|
+
const envRules = this.policy.environmentRules[options.environment];
|
|
56
|
+
if (envRules && !envRules.includes(profile)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (options?.appType && this.policy.requiredProfiles) {
|
|
61
|
+
const appTypeRules = this.policy.requiredProfiles[options.appType];
|
|
62
|
+
if (appTypeRules && !appTypeRules.includes(profile)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get the required profile for a given app type and environment
|
|
70
|
+
*
|
|
71
|
+
* @param appType - The application type
|
|
72
|
+
* @param environment - The deployment environment
|
|
73
|
+
* @returns The required logging profile
|
|
74
|
+
* @throws {PolicyError} If no required profile found for app type
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* const enforcer = new PolicyEnforcer('/org/logging-policy.yaml');
|
|
78
|
+
* const profile = enforcer.getRequiredProfile('api-server', 'production');
|
|
79
|
+
* // Returns LoggingProfile.ENTERPRISE
|
|
80
|
+
*/
|
|
81
|
+
getRequiredProfile(appType, environment) {
|
|
82
|
+
if (this.policy.requiredProfiles?.[appType]) {
|
|
83
|
+
const profiles = this.policy.requiredProfiles[appType];
|
|
84
|
+
if (this.policy.environmentRules?.[environment]) {
|
|
85
|
+
const envProfiles = this.policy.environmentRules[environment];
|
|
86
|
+
const validProfiles = profiles.filter((p) => envProfiles.includes(p));
|
|
87
|
+
if (validProfiles.length > 0) {
|
|
88
|
+
return this.getMostRestrictiveProfile(validProfiles);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return this.getMostRestrictiveProfile(profiles);
|
|
92
|
+
}
|
|
93
|
+
throw new PolicyError(
|
|
94
|
+
`No required profile found for app type "${appType}" in policy ${this.policyFile}`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get a helpful error message for policy validation failure
|
|
99
|
+
*
|
|
100
|
+
* @param profile - The profile that failed validation
|
|
101
|
+
* @param options - The validation options used
|
|
102
|
+
* @returns A descriptive error message
|
|
103
|
+
*/
|
|
104
|
+
getValidationErrorMessage(profile, options) {
|
|
105
|
+
const parts = [`Profile "${profile}" not allowed by policy "${this.policyFile}".`];
|
|
106
|
+
if (options?.environment) {
|
|
107
|
+
parts.push(`Environment: ${options.environment}`);
|
|
108
|
+
}
|
|
109
|
+
if (options?.appType) {
|
|
110
|
+
parts.push(`App Type: ${options.appType}`);
|
|
111
|
+
}
|
|
112
|
+
parts.push(`Allowed profiles: ${this.policy.allowedProfiles.join(", ")}`);
|
|
113
|
+
if (options?.environment && this.policy.environmentRules?.[options.environment]) {
|
|
114
|
+
const envProfiles = this.policy.environmentRules[options.environment];
|
|
115
|
+
parts.push(`Environment "${options.environment}" allows: ${envProfiles.join(", ")}`);
|
|
116
|
+
}
|
|
117
|
+
if (options?.appType && this.policy.requiredProfiles?.[options.appType]) {
|
|
118
|
+
const appProfiles = this.policy.requiredProfiles[options.appType];
|
|
119
|
+
parts.push(`App type "${options.appType}" requires: ${appProfiles.join(", ")}`);
|
|
120
|
+
}
|
|
121
|
+
return parts.join(" | ");
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Load and parse the policy file
|
|
125
|
+
*
|
|
126
|
+
* @param policyFile - Path to YAML policy file
|
|
127
|
+
* @returns Parsed logging policy
|
|
128
|
+
* @throws {PolicyError} If file cannot be read or parsed
|
|
129
|
+
*/
|
|
130
|
+
loadPolicy(policyFile) {
|
|
131
|
+
try {
|
|
132
|
+
const content = readFileSync(policyFile, "utf-8");
|
|
133
|
+
const parsed = parse(content);
|
|
134
|
+
if (!parsed.allowedProfiles || !Array.isArray(parsed.allowedProfiles)) {
|
|
135
|
+
throw new PolicyError(
|
|
136
|
+
`Invalid policy file ${policyFile}: missing or invalid "allowedProfiles" field`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
const validProfiles = Object.values(LoggingProfile);
|
|
140
|
+
for (const profile of parsed.allowedProfiles) {
|
|
141
|
+
if (!validProfiles.includes(profile)) {
|
|
142
|
+
throw new PolicyError(`Invalid policy file ${policyFile}: unknown profile "${profile}"`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (parsed.requiredProfiles) {
|
|
146
|
+
if (typeof parsed.requiredProfiles !== "object") {
|
|
147
|
+
throw new PolicyError(
|
|
148
|
+
`Invalid policy file ${policyFile}: "requiredProfiles" must be an object`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
for (const [appType, profiles] of Object.entries(parsed.requiredProfiles)) {
|
|
152
|
+
if (!Array.isArray(profiles)) {
|
|
153
|
+
throw new PolicyError(
|
|
154
|
+
`Invalid policy file ${policyFile}: requiredProfiles["${appType}"] must be an array, got ${typeof profiles}. Check YAML formatting - each profile should be on a new line with a hyphen.`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
for (const profile of profiles) {
|
|
158
|
+
if (!validProfiles.includes(profile)) {
|
|
159
|
+
throw new PolicyError(
|
|
160
|
+
`Invalid policy file ${policyFile}: requiredProfiles["${appType}"] contains unknown profile "${profile}"`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (parsed.environmentRules) {
|
|
167
|
+
if (typeof parsed.environmentRules !== "object") {
|
|
168
|
+
throw new PolicyError(
|
|
169
|
+
`Invalid policy file ${policyFile}: "environmentRules" must be an object`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
for (const [environment, profiles] of Object.entries(parsed.environmentRules)) {
|
|
173
|
+
if (!Array.isArray(profiles)) {
|
|
174
|
+
throw new PolicyError(
|
|
175
|
+
`Invalid policy file ${policyFile}: environmentRules["${environment}"] must be an array, got ${typeof profiles}. Check YAML formatting - each profile should be on a new line with a hyphen.`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
for (const profile of profiles) {
|
|
179
|
+
if (!validProfiles.includes(profile)) {
|
|
180
|
+
throw new PolicyError(
|
|
181
|
+
`Invalid policy file ${policyFile}: environmentRules["${environment}"] contains unknown profile "${profile}"`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return parsed;
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (error instanceof PolicyError) {
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
if (error.code === "ENOENT") {
|
|
193
|
+
throw new PolicyError(`Policy file not found: ${policyFile}`);
|
|
194
|
+
}
|
|
195
|
+
throw new PolicyError(
|
|
196
|
+
`Failed to load policy file ${policyFile}: ${error.message}`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Get the most restrictive profile from a list
|
|
202
|
+
* Order: CUSTOM > ENTERPRISE > STRUCTURED > SIMPLE
|
|
203
|
+
*
|
|
204
|
+
* @param profiles - List of profiles
|
|
205
|
+
* @returns The most restrictive profile
|
|
206
|
+
*/
|
|
207
|
+
getMostRestrictiveProfile(profiles) {
|
|
208
|
+
const order = [
|
|
209
|
+
"custom" /* CUSTOM */,
|
|
210
|
+
"enterprise" /* ENTERPRISE */,
|
|
211
|
+
"structured" /* STRUCTURED */,
|
|
212
|
+
"simple" /* SIMPLE */
|
|
213
|
+
];
|
|
214
|
+
for (const profile of order) {
|
|
215
|
+
if (profiles.includes(profile)) {
|
|
216
|
+
return profile;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return profiles[0];
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
var ConsoleSink = class {
|
|
223
|
+
write(event) {
|
|
224
|
+
console.log(JSON.stringify(event));
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
var FileSink = class {
|
|
228
|
+
constructor(filePath) {
|
|
229
|
+
this.filePath = filePath;
|
|
230
|
+
}
|
|
231
|
+
write(event) {
|
|
232
|
+
try {
|
|
233
|
+
appendFileSync(this.filePath, `${JSON.stringify(event)}
|
|
234
|
+
`);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(`FileSink: Failed to write to ${this.filePath}:`, error.message);
|
|
237
|
+
console.log(JSON.stringify(event));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
var NullSink = class {
|
|
242
|
+
write(_event) {
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// src/logging/logger.ts
|
|
247
|
+
var Logger = class {
|
|
248
|
+
service;
|
|
249
|
+
profile;
|
|
250
|
+
impl;
|
|
251
|
+
constructor(config) {
|
|
252
|
+
this.service = config.service;
|
|
253
|
+
this.profile = config.profile;
|
|
254
|
+
if (config.policyFile) {
|
|
255
|
+
this.validatePolicy(config.profile, config.policyFile);
|
|
256
|
+
}
|
|
257
|
+
this.impl = this.createImplementation(config);
|
|
258
|
+
}
|
|
259
|
+
debug(message, context) {
|
|
260
|
+
this.impl.debug(message, context);
|
|
261
|
+
}
|
|
262
|
+
info(message, context) {
|
|
263
|
+
this.impl.info(message, context);
|
|
264
|
+
}
|
|
265
|
+
warn(message, context) {
|
|
266
|
+
this.impl.warn(message, context);
|
|
267
|
+
}
|
|
268
|
+
error(message, error, context) {
|
|
269
|
+
this.impl.error(message, error, context);
|
|
270
|
+
}
|
|
271
|
+
child(bindings) {
|
|
272
|
+
const childLogger = Object.create(this);
|
|
273
|
+
childLogger.impl = this.impl.child(bindings);
|
|
274
|
+
return childLogger;
|
|
275
|
+
}
|
|
276
|
+
validatePolicy(profile, policyFile) {
|
|
277
|
+
const enforcer = new PolicyEnforcer(policyFile);
|
|
278
|
+
const appType = process.env.APP_TYPE;
|
|
279
|
+
const environment = process.env.NODE_ENV;
|
|
280
|
+
if (!enforcer.validateProfile(profile, { appType, environment })) {
|
|
281
|
+
throw new PolicyError(enforcer.getValidationErrorMessage(profile, { appType, environment }));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
createImplementation(config) {
|
|
285
|
+
switch (config.profile) {
|
|
286
|
+
case "simple" /* SIMPLE */:
|
|
287
|
+
return new SimpleLogger(config.service);
|
|
288
|
+
case "structured" /* STRUCTURED */: {
|
|
289
|
+
const structuredConfig = config;
|
|
290
|
+
return new StructuredLogger(
|
|
291
|
+
structuredConfig.service,
|
|
292
|
+
structuredConfig.filePath,
|
|
293
|
+
structuredConfig.middleware
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
case "enterprise" /* ENTERPRISE */:
|
|
297
|
+
return new EnterpriseLogger(config.service, {
|
|
298
|
+
// biome-ignore lint/suspicious/noExplicitAny: Phase 1 - proper discriminated union handling in Phase 2
|
|
299
|
+
sinks: config.sinks,
|
|
300
|
+
// biome-ignore lint/suspicious/noExplicitAny: Phase 1 - proper discriminated union handling in Phase 2
|
|
301
|
+
middleware: config.middleware
|
|
302
|
+
});
|
|
303
|
+
case "custom" /* CUSTOM */:
|
|
304
|
+
return new EnterpriseLogger(config.service, config.customConfig);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
var SimpleLogger = class {
|
|
309
|
+
constructor(service) {
|
|
310
|
+
this.service = service;
|
|
311
|
+
this.pino = pino({
|
|
312
|
+
name: service,
|
|
313
|
+
level: "debug",
|
|
314
|
+
messageKey: "message",
|
|
315
|
+
base: void 0,
|
|
316
|
+
timestamp: false,
|
|
317
|
+
formatters: {
|
|
318
|
+
level: (label) => ({ severity: label.toUpperCase() }),
|
|
319
|
+
// biome-ignore lint/suspicious/noExplicitAny: Pino formatter requires any type
|
|
320
|
+
log: (object) => {
|
|
321
|
+
return {
|
|
322
|
+
service: this.service,
|
|
323
|
+
...object
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
pino;
|
|
330
|
+
debug(message, context) {
|
|
331
|
+
this.pino.debug(context, message);
|
|
332
|
+
}
|
|
333
|
+
info(message, context) {
|
|
334
|
+
this.pino.info(context, message);
|
|
335
|
+
}
|
|
336
|
+
warn(message, context) {
|
|
337
|
+
this.pino.warn(context, message);
|
|
338
|
+
}
|
|
339
|
+
error(message, error, context) {
|
|
340
|
+
const errorContext = {
|
|
341
|
+
...context,
|
|
342
|
+
...error && { error: error.message, stack: error.stack }
|
|
343
|
+
};
|
|
344
|
+
this.pino.error(errorContext, message);
|
|
345
|
+
}
|
|
346
|
+
child(bindings) {
|
|
347
|
+
const childPino = this.pino.child(bindings);
|
|
348
|
+
const childLogger = Object.create(this);
|
|
349
|
+
childLogger.pino = childPino;
|
|
350
|
+
return childLogger;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
var StructuredLogger = class _StructuredLogger {
|
|
354
|
+
constructor(service, filePath, middleware, bindings, existingPino) {
|
|
355
|
+
this.service = service;
|
|
356
|
+
this.filePath = filePath;
|
|
357
|
+
this.middleware = middleware ?? [];
|
|
358
|
+
this.bindings = bindings ?? {};
|
|
359
|
+
const config = {
|
|
360
|
+
name: service,
|
|
361
|
+
level: "debug",
|
|
362
|
+
messageKey: "message",
|
|
363
|
+
base: void 0,
|
|
364
|
+
timestamp: () => `,"timestamp":"${(/* @__PURE__ */ new Date()).toISOString()}"`,
|
|
365
|
+
formatters: {
|
|
366
|
+
level: (label) => ({ severity: label.toUpperCase() }),
|
|
367
|
+
// biome-ignore lint/suspicious/noExplicitAny: Pino formatter requires any type
|
|
368
|
+
log: (object) => {
|
|
369
|
+
return {
|
|
370
|
+
service: this.service,
|
|
371
|
+
...object
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
this.pino = existingPino ?? (filePath ? pino(
|
|
377
|
+
config,
|
|
378
|
+
pino.multistream([{ stream: process.stdout }, { stream: pino.destination(filePath) }])
|
|
379
|
+
) : pino(config));
|
|
380
|
+
}
|
|
381
|
+
pino;
|
|
382
|
+
middleware;
|
|
383
|
+
bindings;
|
|
384
|
+
debug(message, context) {
|
|
385
|
+
if (this.middleware.length > 0) {
|
|
386
|
+
this.logWithMiddleware("DEBUG", message, context);
|
|
387
|
+
} else {
|
|
388
|
+
this.pino.debug(context, message);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
info(message, context) {
|
|
392
|
+
if (this.middleware.length > 0) {
|
|
393
|
+
this.logWithMiddleware("INFO", message, context);
|
|
394
|
+
} else {
|
|
395
|
+
this.pino.info(context, message);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
warn(message, context) {
|
|
399
|
+
if (this.middleware.length > 0) {
|
|
400
|
+
this.logWithMiddleware("WARN", message, context);
|
|
401
|
+
} else {
|
|
402
|
+
this.pino.warn(context, message);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
error(message, error, context) {
|
|
406
|
+
const errorContext = {
|
|
407
|
+
...context,
|
|
408
|
+
...error && { error: error.message, stack: error.stack }
|
|
409
|
+
};
|
|
410
|
+
if (this.middleware.length > 0) {
|
|
411
|
+
this.logWithMiddleware("ERROR", message, errorContext);
|
|
412
|
+
} else {
|
|
413
|
+
this.pino.error(errorContext, message);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
logWithMiddleware(severity, message, context) {
|
|
417
|
+
let event = {
|
|
418
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
419
|
+
service: this.service,
|
|
420
|
+
severity,
|
|
421
|
+
message,
|
|
422
|
+
...this.bindings,
|
|
423
|
+
...context
|
|
424
|
+
};
|
|
425
|
+
for (const mw of this.middleware) {
|
|
426
|
+
event = mw.process(event);
|
|
427
|
+
}
|
|
428
|
+
const { message: finalMessage, severity: finalSeverity, ...finalContext } = event;
|
|
429
|
+
const level = (finalSeverity || severity).toLowerCase();
|
|
430
|
+
switch (level) {
|
|
431
|
+
case "debug":
|
|
432
|
+
this.pino.debug(finalContext, finalMessage);
|
|
433
|
+
break;
|
|
434
|
+
case "info":
|
|
435
|
+
this.pino.info(finalContext, finalMessage);
|
|
436
|
+
break;
|
|
437
|
+
case "warn":
|
|
438
|
+
this.pino.warn(finalContext, finalMessage);
|
|
439
|
+
break;
|
|
440
|
+
case "error":
|
|
441
|
+
this.pino.error(finalContext, finalMessage);
|
|
442
|
+
break;
|
|
443
|
+
default:
|
|
444
|
+
this.pino.info(finalContext, finalMessage);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
child(bindings) {
|
|
448
|
+
const mergedBindings = { ...this.bindings, ...bindings };
|
|
449
|
+
const childPino = this.pino.child(bindings);
|
|
450
|
+
return new _StructuredLogger(
|
|
451
|
+
this.service,
|
|
452
|
+
this.filePath,
|
|
453
|
+
this.middleware,
|
|
454
|
+
mergedBindings,
|
|
455
|
+
childPino
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
var EnterpriseLogger = class _EnterpriseLogger {
|
|
460
|
+
constructor(service, config, bindings) {
|
|
461
|
+
this.service = service;
|
|
462
|
+
this.sinks = config?.sinks ?? [new ConsoleSink()];
|
|
463
|
+
this.middleware = config?.middleware ?? [];
|
|
464
|
+
this.bindings = bindings ?? {};
|
|
465
|
+
}
|
|
466
|
+
sinks;
|
|
467
|
+
middleware;
|
|
468
|
+
bindings;
|
|
469
|
+
debug(message, context) {
|
|
470
|
+
this.logWithPipeline("DEBUG", message, context);
|
|
471
|
+
}
|
|
472
|
+
info(message, context) {
|
|
473
|
+
this.logWithPipeline("INFO", message, context);
|
|
474
|
+
}
|
|
475
|
+
warn(message, context) {
|
|
476
|
+
this.logWithPipeline("WARN", message, context);
|
|
477
|
+
}
|
|
478
|
+
error(message, error, context) {
|
|
479
|
+
const errorContext = {
|
|
480
|
+
...context,
|
|
481
|
+
...error && {
|
|
482
|
+
error: error.message,
|
|
483
|
+
stack: error.stack,
|
|
484
|
+
errorType: error.constructor.name
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
this.logWithPipeline("ERROR", message, errorContext);
|
|
488
|
+
}
|
|
489
|
+
logWithPipeline(severity, message, context) {
|
|
490
|
+
let event = {
|
|
491
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
492
|
+
service: this.service,
|
|
493
|
+
severity,
|
|
494
|
+
message,
|
|
495
|
+
correlationId: context?.correlationId ?? this.generateCorrelationId(),
|
|
496
|
+
host: this.getHostname(),
|
|
497
|
+
pid: process.pid,
|
|
498
|
+
...this.bindings,
|
|
499
|
+
...context
|
|
500
|
+
};
|
|
501
|
+
for (const mw of this.middleware) {
|
|
502
|
+
event = mw.process(event);
|
|
503
|
+
}
|
|
504
|
+
for (const sink of this.sinks) {
|
|
505
|
+
sink.write(event);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
child(bindings) {
|
|
509
|
+
const mergedBindings = { ...this.bindings, ...bindings };
|
|
510
|
+
return new _EnterpriseLogger(
|
|
511
|
+
this.service,
|
|
512
|
+
{
|
|
513
|
+
sinks: this.sinks,
|
|
514
|
+
middleware: this.middleware
|
|
515
|
+
},
|
|
516
|
+
mergedBindings
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
generateCorrelationId() {
|
|
520
|
+
return `corr-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
521
|
+
}
|
|
522
|
+
getHostname() {
|
|
523
|
+
try {
|
|
524
|
+
return hostname();
|
|
525
|
+
} catch {
|
|
526
|
+
return process.env.HOSTNAME || "unknown";
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
// src/logging/middleware.ts
|
|
532
|
+
var DEFAULT_SECRET_KEYS = [
|
|
533
|
+
"password",
|
|
534
|
+
"token",
|
|
535
|
+
"apikey",
|
|
536
|
+
"api_key",
|
|
537
|
+
"authorization",
|
|
538
|
+
"secret",
|
|
539
|
+
"cardnumber",
|
|
540
|
+
"card_number",
|
|
541
|
+
"cvv",
|
|
542
|
+
"ssn",
|
|
543
|
+
"accesstoken",
|
|
544
|
+
"access_token",
|
|
545
|
+
"refreshtoken",
|
|
546
|
+
"refresh_token"
|
|
547
|
+
];
|
|
548
|
+
var DEFAULT_SECRET_PATTERNS = [
|
|
549
|
+
/SECRET_[A-Z0-9_]+/g,
|
|
550
|
+
// Environment variable secrets
|
|
551
|
+
/[A-Z0-9_]*TOKEN[A-Z0-9_]*/g,
|
|
552
|
+
// Token variants
|
|
553
|
+
/[A-Z0-9_]*KEY[A-Z0-9_]*/g,
|
|
554
|
+
// Key variants
|
|
555
|
+
/[A-Za-z0-9+/]{40,}={0,2}/g,
|
|
556
|
+
// Base64 blobs (~40+ chars)
|
|
557
|
+
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
558
|
+
// Email addresses
|
|
559
|
+
/\b\d{13,19}\b/g
|
|
560
|
+
// Credit card numbers (13-19 digits)
|
|
561
|
+
];
|
|
562
|
+
var MAX_PATTERN_SCAN_SIZE = 10240;
|
|
563
|
+
var RedactSecretsMiddleware = class {
|
|
564
|
+
secretKeys;
|
|
565
|
+
secretKeysLower;
|
|
566
|
+
patterns;
|
|
567
|
+
constructor(optionsOrKeys) {
|
|
568
|
+
let secretKeys;
|
|
569
|
+
let patterns = [];
|
|
570
|
+
let useDefaultPatterns = true;
|
|
571
|
+
if (Array.isArray(optionsOrKeys)) {
|
|
572
|
+
secretKeys = optionsOrKeys;
|
|
573
|
+
useDefaultPatterns = false;
|
|
574
|
+
} else {
|
|
575
|
+
secretKeys = optionsOrKeys?.secretKeys ?? DEFAULT_SECRET_KEYS;
|
|
576
|
+
patterns = optionsOrKeys?.patterns ?? [];
|
|
577
|
+
useDefaultPatterns = optionsOrKeys?.useDefaultPatterns ?? true;
|
|
578
|
+
}
|
|
579
|
+
this.secretKeys = secretKeys;
|
|
580
|
+
this.secretKeysLower = secretKeys.map((k) => k.toLowerCase());
|
|
581
|
+
this.patterns = [...useDefaultPatterns ? DEFAULT_SECRET_PATTERNS : [], ...patterns];
|
|
582
|
+
}
|
|
583
|
+
process(event) {
|
|
584
|
+
const redacted = { ...event };
|
|
585
|
+
for (const key of Object.keys(redacted)) {
|
|
586
|
+
if (this.isSecretKey(key)) {
|
|
587
|
+
redacted[key] = "[REDACTED]";
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
this.redactNested(redacted);
|
|
591
|
+
return redacted;
|
|
592
|
+
}
|
|
593
|
+
isSecretKey(key) {
|
|
594
|
+
return this.secretKeysLower.includes(key.toLowerCase());
|
|
595
|
+
}
|
|
596
|
+
redactNested(obj) {
|
|
597
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
598
|
+
if (this.isSecretKey(key)) {
|
|
599
|
+
obj[key] = "[REDACTED]";
|
|
600
|
+
} else if (typeof value === "string") {
|
|
601
|
+
obj[key] = this.redactPatterns(value);
|
|
602
|
+
} else if (value && typeof value === "object") {
|
|
603
|
+
if (this.isPlainObject(value)) {
|
|
604
|
+
if (Array.isArray(value)) {
|
|
605
|
+
this.redactArray(value);
|
|
606
|
+
} else {
|
|
607
|
+
this.redactNested(value);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
redactArray(arr) {
|
|
614
|
+
for (let i = 0; i < arr.length; i++) {
|
|
615
|
+
const item = arr[i];
|
|
616
|
+
if (typeof item === "string") {
|
|
617
|
+
arr[i] = this.redactPatterns(item);
|
|
618
|
+
} else if (item && typeof item === "object") {
|
|
619
|
+
if (this.isPlainObject(item)) {
|
|
620
|
+
if (Array.isArray(item)) {
|
|
621
|
+
this.redactArray(item);
|
|
622
|
+
} else {
|
|
623
|
+
this.redactNested(item);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Apply regex patterns to redact secrets from string values
|
|
631
|
+
* Skips strings larger than MAX_PATTERN_SCAN_SIZE for performance
|
|
632
|
+
*/
|
|
633
|
+
redactPatterns(value) {
|
|
634
|
+
if (value.length > MAX_PATTERN_SCAN_SIZE) {
|
|
635
|
+
return value;
|
|
636
|
+
}
|
|
637
|
+
let result = value;
|
|
638
|
+
for (const pattern of this.patterns) {
|
|
639
|
+
pattern.lastIndex = 0;
|
|
640
|
+
result = result.replace(pattern, "[REDACTED]");
|
|
641
|
+
}
|
|
642
|
+
return result;
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Check if value is a plain object (not Date, Buffer, Error, etc.)
|
|
646
|
+
*/
|
|
647
|
+
isPlainObject(value) {
|
|
648
|
+
if (!value || typeof value !== "object") {
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
if (value instanceof Date || value instanceof Error || value instanceof RegExp || ArrayBuffer.isView(value) || // Covers Buffer, TypedArray, DataView
|
|
652
|
+
value instanceof ArrayBuffer) {
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
if (Array.isArray(value)) {
|
|
656
|
+
return true;
|
|
657
|
+
}
|
|
658
|
+
return true;
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
var AddFieldsMiddleware = class {
|
|
662
|
+
constructor(fields) {
|
|
663
|
+
this.fields = fields;
|
|
664
|
+
}
|
|
665
|
+
process(event) {
|
|
666
|
+
return {
|
|
667
|
+
...event,
|
|
668
|
+
...this.fields
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
var TransformMiddleware = class {
|
|
673
|
+
constructor(transformer) {
|
|
674
|
+
this.transformer = transformer;
|
|
675
|
+
}
|
|
676
|
+
process(event) {
|
|
677
|
+
return this.transformer(event);
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
// src/logging/create-logger.ts
|
|
682
|
+
function createLogger(config) {
|
|
683
|
+
return new Logger(config);
|
|
684
|
+
}
|
|
685
|
+
function createSimpleLogger(service) {
|
|
686
|
+
return new Logger({
|
|
687
|
+
service,
|
|
688
|
+
profile: "simple" /* SIMPLE */
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
function createStructuredLogger(service, filePath) {
|
|
692
|
+
return new Logger({
|
|
693
|
+
service,
|
|
694
|
+
profile: "structured" /* STRUCTURED */,
|
|
695
|
+
filePath
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
function createEnterpriseLogger(service, options) {
|
|
699
|
+
return new Logger({
|
|
700
|
+
service,
|
|
701
|
+
profile: "enterprise" /* ENTERPRISE */,
|
|
702
|
+
...options
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
function createStructuredLoggerWithRedaction(service, options) {
|
|
706
|
+
return new Logger({
|
|
707
|
+
service,
|
|
708
|
+
profile: "structured" /* STRUCTURED */,
|
|
709
|
+
filePath: options?.filePath,
|
|
710
|
+
middleware: [
|
|
711
|
+
new RedactSecretsMiddleware({
|
|
712
|
+
secretKeys: options?.customFields,
|
|
713
|
+
patterns: options?.customPatterns,
|
|
714
|
+
useDefaultPatterns: options?.useDefaultPatterns
|
|
715
|
+
})
|
|
716
|
+
]
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// src/logging/index.ts
|
|
721
|
+
var VERSION = "0.1.0";
|
|
722
|
+
|
|
723
|
+
export { AddFieldsMiddleware, ConsoleSink, FileSink, Logger, LoggingProfile, NullSink, PolicyEnforcer, PolicyError, RedactSecretsMiddleware, TransformMiddleware, VERSION, createEnterpriseLogger, createLogger, createSimpleLogger, createStructuredLogger, createStructuredLoggerWithRedaction };
|
|
724
|
+
//# sourceMappingURL=chunk-5HZEJGZ5.js.map
|
|
725
|
+
//# sourceMappingURL=chunk-5HZEJGZ5.js.map
|