@codraoss/core 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +625 -0
- package/README.md +15 -0
- package/dist/chunk-FGXA7JNR.js +57 -0
- package/dist/chunk-FGXA7JNR.js.map +1 -0
- package/dist/chunk-ILZUCIZH.js +117 -0
- package/dist/chunk-ILZUCIZH.js.map +1 -0
- package/dist/chunk-JC74XH7Q.js +67 -0
- package/dist/chunk-JC74XH7Q.js.map +1 -0
- package/dist/chunk-MMVQPGK3.js +341 -0
- package/dist/chunk-MMVQPGK3.js.map +1 -0
- package/dist/chunk-NSTV4TRP.js +39 -0
- package/dist/chunk-NSTV4TRP.js.map +1 -0
- package/dist/chunk-OIQRTDYR.js +337 -0
- package/dist/chunk-OIQRTDYR.js.map +1 -0
- package/dist/chunk-PVXFURDR.js +90 -0
- package/dist/chunk-PVXFURDR.js.map +1 -0
- package/dist/chunk-R2HRWFRH.js +108 -0
- package/dist/chunk-R2HRWFRH.js.map +1 -0
- package/dist/chunk-RQ5S67XB.js +82 -0
- package/dist/chunk-RQ5S67XB.js.map +1 -0
- package/dist/chunk-U3VKVDZ7.js +266 -0
- package/dist/chunk-U3VKVDZ7.js.map +1 -0
- package/dist/chunk-V3RF6GQW.js +137 -0
- package/dist/chunk-V3RF6GQW.js.map +1 -0
- package/dist/chunk-WSK45HW4.js +79 -0
- package/dist/chunk-WSK45HW4.js.map +1 -0
- package/dist/chunk-XD6CGYHO.js +893 -0
- package/dist/chunk-XD6CGYHO.js.map +1 -0
- package/dist/claim-checks.d.ts +54 -0
- package/dist/claim-checks.js +20 -0
- package/dist/claim-checks.js.map +1 -0
- package/dist/diff/index.d.ts +22 -0
- package/dist/diff/index.js +25 -0
- package/dist/diff/index.js.map +1 -0
- package/dist/fingerprint.d.ts +9 -0
- package/dist/fingerprint.js +19 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/index-BTDWXDck.d.ts +93 -0
- package/dist/index.d.ts +138 -0
- package/dist/index.js +1755 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +32 -0
- package/dist/logger.js +17 -0
- package/dist/logger.js.map +1 -0
- package/dist/model-gRDTk8yk.d.ts +117 -0
- package/dist/model-output/index.d.ts +4 -0
- package/dist/model-output/index.js +32 -0
- package/dist/model-output/index.js.map +1 -0
- package/dist/ports/index.d.ts +512 -0
- package/dist/ports/index.js +13 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/position-h_jdn6ZH.d.ts +30 -0
- package/dist/prompts/file-review.d.ts +6 -0
- package/dist/prompts/file-review.js +25 -0
- package/dist/prompts/file-review.js.map +1 -0
- package/dist/prompts/languages.d.ts +9 -0
- package/dist/prompts/languages.js +7 -0
- package/dist/prompts/languages.js.map +1 -0
- package/dist/prompts/summary.d.ts +12 -0
- package/dist/prompts/summary.js +50 -0
- package/dist/prompts/summary.js.map +1 -0
- package/dist/prompts/verify.d.ts +71 -0
- package/dist/prompts/verify.js +15 -0
- package/dist/prompts/verify.js.map +1 -0
- package/dist/rules/detect.d.ts +32 -0
- package/dist/rules/detect.js +13 -0
- package/dist/rules/detect.js.map +1 -0
- package/dist/rules/table.d.ts +18 -0
- package/dist/rules/table.js +7 -0
- package/dist/rules/table.js.map +1 -0
- package/dist/timeout.d.ts +6 -0
- package/dist/timeout.js +26 -0
- package/dist/timeout.js.map +1 -0
- package/dist/token-tracker.d.ts +38 -0
- package/dist/token-tracker.js +8 -0
- package/dist/token-tracker.js.map +1 -0
- package/dist/verify.d.ts +3 -0
- package/dist/verify.js +21 -0
- package/dist/verify.js.map +1 -0
- package/package.json +181 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/prompts/verify.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { jsonrepair } from "jsonrepair";
|
|
4
|
+
var verifyResultSchema = z.object({
|
|
5
|
+
results: z.array(
|
|
6
|
+
z.object({
|
|
7
|
+
index: z.number().int(),
|
|
8
|
+
reason: z.string().optional(),
|
|
9
|
+
// decidable" -- only an explicit `false` costs a finding. See the note on the prompt below.
|
|
10
|
+
decidable: z.boolean().optional(),
|
|
11
|
+
verdict: z.enum(["keep", "drop"]),
|
|
12
|
+
confidence: z.number().min(0).max(1).optional()
|
|
13
|
+
})
|
|
14
|
+
).default([])
|
|
15
|
+
});
|
|
16
|
+
var VERIFY_RESPONSE_SCHEMA = {
|
|
17
|
+
name: "codra_verify_findings",
|
|
18
|
+
schema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
additionalProperties: false,
|
|
21
|
+
required: ["results"],
|
|
22
|
+
properties: {
|
|
23
|
+
results: {
|
|
24
|
+
type: "array",
|
|
25
|
+
items: {
|
|
26
|
+
type: "object",
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
required: ["index", "reason", "decidable", "verdict"],
|
|
29
|
+
properties: {
|
|
30
|
+
index: { type: "integer", minimum: 0 },
|
|
31
|
+
reason: { type: "string", maxLength: 300 },
|
|
32
|
+
decidable: { type: "boolean" },
|
|
33
|
+
verdict: { type: "string", enum: ["keep", "drop"] },
|
|
34
|
+
confidence: { type: "number", minimum: 0, maximum: 1 }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var VERIFY_SYSTEM_PROMPT = `You are a meticulous senior engineer checking whether each candidate code-review finding is actually supported by the code it points at.
|
|
42
|
+
|
|
43
|
+
For EACH finding you are given the claim and a SHORT WINDOW of diff context around the line it was anchored to. That window is all you have: you cannot see the rest of the file, any other file, the project's dependencies and their versions, its build target, or its runtime.
|
|
44
|
+
|
|
45
|
+
Answer two questions per finding, in this order.
|
|
46
|
+
|
|
47
|
+
1. "decidable": can this claim be settled from the window you were given?
|
|
48
|
+
- true - the window contains everything needed to say whether the claim holds.
|
|
49
|
+
- false - settling it would need something outside the window: which files import this one, what a function defined elsewhere does, which version of a dependency is installed, what engine or renderer the code runs on, or how a caller uses the result.
|
|
50
|
+
Watch for claims that assert a CONSEQUENCE somewhere you cannot see: "this breaks importers", "this throws on older runtimes", "this fails during server rendering", "the caller will not await this". The anchored line can be exactly as quoted and the consequence still be unverifiable - confirming that the quote is real is NOT confirming the claim.
|
|
51
|
+
When "decidable" is false, say in "reason" what you would have to look at, e.g. "would need the importers of this module".
|
|
52
|
+
|
|
53
|
+
Two rules, because both have been got wrong on real reviews:
|
|
54
|
+
|
|
55
|
+
a) A claim of the form "if X() fails / rejects / throws, this is unhandled" is NOT decidable unless the
|
|
56
|
+
BODY of X is inside your window. A function whose body you cannot see may well handle its own
|
|
57
|
+
errors, in which case there is nothing to report. Seeing the CALL is not seeing the body. Mark it
|
|
58
|
+
not decidable and say you would need that function's implementation.
|
|
59
|
+
|
|
60
|
+
b) Read the diff markers before you agree that something was removed or changed. A line prefixed "-"
|
|
61
|
+
is the OLD code and a line prefixed "+" is the NEW code. A claim that says "X was replaced by Y" is
|
|
62
|
+
false if the diff shows Y being replaced by X, and a claim that a safeguard was "removed" is false
|
|
63
|
+
if the "+" line still carries an equivalent one under a different name. State the direction in your
|
|
64
|
+
reason: "the + line adds strict validation, so the claim is backwards".
|
|
65
|
+
|
|
66
|
+
2. "verdict":
|
|
67
|
+
- "keep": the code in the window genuinely exhibits the problem the claim describes.
|
|
68
|
+
- "drop": the claim is not supported by the code shown - it describes something that isn't there, it is speculative, it is a subjective style preference, or it is not decidable from this window.
|
|
69
|
+
A claim you marked not decidable is always a "drop".
|
|
70
|
+
|
|
71
|
+
Judge the CLAIM against the CODE. Do not defer to the claim's confidence or phrasing; a well-written claim about code that doesn't do what it says is still a drop.
|
|
72
|
+
Be strict: when in doubt, "drop". It is better to drop a borderline finding than to keep a wrong one.
|
|
73
|
+
|
|
74
|
+
Output MUST be valid JSON, exactly one object, no prose before or after:
|
|
75
|
+
{
|
|
76
|
+
"results": [
|
|
77
|
+
{ "index": <number>, "reason": "<why, and if not decidable what you would need to see>", "decidable": true | false, "verdict": "keep" | "drop", "confidence": <float 0.0-1.0> }
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
Include exactly one result object for every finding index provided, and use the same index numbers you were given.`;
|
|
81
|
+
function buildVerifyPrompt(candidates) {
|
|
82
|
+
const blocks = candidates.map((c) => {
|
|
83
|
+
const location = c.line != null ? `${c.path}:${c.line}` : c.path;
|
|
84
|
+
return [
|
|
85
|
+
`### Finding index ${c.index}`,
|
|
86
|
+
`Location: ${location}`,
|
|
87
|
+
`Title: ${c.title}`,
|
|
88
|
+
`Claim: ${c.body}`,
|
|
89
|
+
...c.evidence ? [`Code the claim cites: ${c.evidence}`] : [],
|
|
90
|
+
"Relevant diff:",
|
|
91
|
+
c.snippet || "(no diff context available for this location)"
|
|
92
|
+
].join("\n");
|
|
93
|
+
});
|
|
94
|
+
return [
|
|
95
|
+
"Validate each finding below against its diff context. Return a verdict for every index.",
|
|
96
|
+
"",
|
|
97
|
+
blocks.join("\n\n")
|
|
98
|
+
].join("\n");
|
|
99
|
+
}
|
|
100
|
+
function renderDiffSnippet(file, line, radius = 12) {
|
|
101
|
+
if (!file) return "";
|
|
102
|
+
const flat = file.hunks.flatMap((hunk) => hunk.lines);
|
|
103
|
+
if (flat.length === 0) return "";
|
|
104
|
+
if (line == null) return "";
|
|
105
|
+
const byNewLine = flat.findIndex((l) => l.newLineNumber === line);
|
|
106
|
+
const anchor = byNewLine !== -1 ? byNewLine : flat.findIndex((l) => l.oldLineNumber === line);
|
|
107
|
+
if (anchor === -1) return "";
|
|
108
|
+
const start = Math.max(0, anchor - radius);
|
|
109
|
+
const end = Math.min(flat.length, anchor + radius + 1);
|
|
110
|
+
return flat.slice(start, end).map((l) => {
|
|
111
|
+
const prefix = l.kind === "add" ? "+" : l.kind === "del" ? "-" : " ";
|
|
112
|
+
const gutter = String(l.newLineNumber ?? l.oldLineNumber ?? "").padStart(4, " ");
|
|
113
|
+
return `${gutter} ${prefix}${l.content}`;
|
|
114
|
+
}).join("\n");
|
|
115
|
+
}
|
|
116
|
+
function parseVerifyResponse(raw) {
|
|
117
|
+
const trimmed = raw.trim();
|
|
118
|
+
const start = trimmed.indexOf("{");
|
|
119
|
+
const end = trimmed.lastIndexOf("}");
|
|
120
|
+
const candidate = start !== -1 && end !== -1 && end > start ? trimmed.slice(start, end + 1) : trimmed;
|
|
121
|
+
let json;
|
|
122
|
+
try {
|
|
123
|
+
json = JSON.parse(candidate);
|
|
124
|
+
} catch {
|
|
125
|
+
json = JSON.parse(jsonrepair(candidate));
|
|
126
|
+
}
|
|
127
|
+
return verifyResultSchema.parse(json).results;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
VERIFY_RESPONSE_SCHEMA,
|
|
132
|
+
VERIFY_SYSTEM_PROMPT,
|
|
133
|
+
buildVerifyPrompt,
|
|
134
|
+
renderDiffSnippet,
|
|
135
|
+
parseVerifyResponse
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=chunk-V3RF6GQW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/prompts/verify.ts"],"sourcesContent":["import { z } from 'zod';\r\nimport { jsonrepair } from 'jsonrepair';\r\nimport type { FileDiff } from '../diff';\r\n\r\nexport type VerifyCandidate = {\r\n index: number;\r\n path: string;\r\n line: number | null;\r\n title: string;\r\n body: string;\r\n snippet: string;\r\n evidence?: string | null;\r\n};\r\n\r\nconst verifyResultSchema = z.object({\r\n results: z\r\n .array(\r\n z.object({\r\n index: z.number().int(),\r\n reason: z.string().optional(),\r\n // decidable\" -- only an explicit `false` costs a finding. See the note on the prompt below.\r\n decidable: z.boolean().optional(),\r\n verdict: z.enum(['keep', 'drop']),\r\n confidence: z.number().min(0).max(1).optional(),\r\n }),\r\n )\r\n .default([]),\r\n});\r\n\r\nexport type VerifyResult = z.infer<typeof verifyResultSchema>['results'][number];\r\n\r\nexport const VERIFY_RESPONSE_SCHEMA = {\r\n name: 'codra_verify_findings',\r\n schema: {\r\n type: 'object',\r\n additionalProperties: false,\r\n required: ['results'],\r\n properties: {\r\n results: {\r\n type: 'array',\r\n items: {\r\n type: 'object',\r\n additionalProperties: false,\r\n required: ['index', 'reason', 'decidable', 'verdict'],\r\n properties: {\r\n index: { type: 'integer', minimum: 0 },\r\n reason: { type: 'string', maxLength: 300 },\r\n decidable: { type: 'boolean' },\r\n verdict: { type: 'string', enum: ['keep', 'drop'] },\r\n confidence: { type: 'number', minimum: 0, maximum: 1 },\r\n },\r\n },\r\n },\r\n },\r\n },\r\n} as const;\r\n\r\nexport const VERIFY_SYSTEM_PROMPT = `You are a meticulous senior engineer checking whether each candidate code-review finding is actually supported by the code it points at.\r\n\r\nFor EACH finding you are given the claim and a SHORT WINDOW of diff context around the line it was anchored to. That window is all you have: you cannot see the rest of the file, any other file, the project's dependencies and their versions, its build target, or its runtime.\r\n\r\nAnswer two questions per finding, in this order.\r\n\r\n1. \"decidable\": can this claim be settled from the window you were given?\r\n - true - the window contains everything needed to say whether the claim holds.\r\n - false - settling it would need something outside the window: which files import this one, what a function defined elsewhere does, which version of a dependency is installed, what engine or renderer the code runs on, or how a caller uses the result.\r\n Watch for claims that assert a CONSEQUENCE somewhere you cannot see: \"this breaks importers\", \"this throws on older runtimes\", \"this fails during server rendering\", \"the caller will not await this\". The anchored line can be exactly as quoted and the consequence still be unverifiable - confirming that the quote is real is NOT confirming the claim.\r\n When \"decidable\" is false, say in \"reason\" what you would have to look at, e.g. \"would need the importers of this module\".\r\n\r\n Two rules, because both have been got wrong on real reviews:\r\n\r\n a) A claim of the form \"if X() fails / rejects / throws, this is unhandled\" is NOT decidable unless the\r\n BODY of X is inside your window. A function whose body you cannot see may well handle its own\r\n errors, in which case there is nothing to report. Seeing the CALL is not seeing the body. Mark it\r\n not decidable and say you would need that function's implementation.\r\n\r\n b) Read the diff markers before you agree that something was removed or changed. A line prefixed \"-\"\r\n is the OLD code and a line prefixed \"+\" is the NEW code. A claim that says \"X was replaced by Y\" is\r\n false if the diff shows Y being replaced by X, and a claim that a safeguard was \"removed\" is false\r\n if the \"+\" line still carries an equivalent one under a different name. State the direction in your\r\n reason: \"the + line adds strict validation, so the claim is backwards\".\r\n\r\n2. \"verdict\":\r\n - \"keep\": the code in the window genuinely exhibits the problem the claim describes.\r\n - \"drop\": the claim is not supported by the code shown - it describes something that isn't there, it is speculative, it is a subjective style preference, or it is not decidable from this window.\r\n A claim you marked not decidable is always a \"drop\".\r\n\r\nJudge the CLAIM against the CODE. Do not defer to the claim's confidence or phrasing; a well-written claim about code that doesn't do what it says is still a drop.\r\nBe strict: when in doubt, \"drop\". It is better to drop a borderline finding than to keep a wrong one.\r\n\r\nOutput MUST be valid JSON, exactly one object, no prose before or after:\r\n{\r\n \"results\": [\r\n { \"index\": <number>, \"reason\": \"<why, and if not decidable what you would need to see>\", \"decidable\": true | false, \"verdict\": \"keep\" | \"drop\", \"confidence\": <float 0.0-1.0> }\r\n ]\r\n}\r\nInclude exactly one result object for every finding index provided, and use the same index numbers you were given.`;\r\n\r\nexport function buildVerifyPrompt(candidates: VerifyCandidate[]): string {\r\n const blocks = candidates.map((c) => {\r\n const location = c.line != null ? `${c.path}:${c.line}` : c.path;\r\n return [\r\n `### Finding index ${c.index}`,\r\n `Location: ${location}`,\r\n `Title: ${c.title}`,\r\n `Claim: ${c.body}`,\r\n ...(c.evidence ? [`Code the claim cites: ${c.evidence}`] : []),\r\n 'Relevant diff:',\r\n c.snippet || '(no diff context available for this location)',\r\n ].join('\\n');\r\n });\r\n\r\n return [\r\n 'Validate each finding below against its diff context. Return a verdict for every index.',\r\n '',\r\n blocks.join('\\n\\n'),\r\n ].join('\\n');\r\n}\r\n\r\nexport function renderDiffSnippet(file: FileDiff | undefined, line: number | undefined, radius = 12): string {\r\n if (!file) return '';\r\n const flat = file.hunks.flatMap((hunk) => hunk.lines);\r\n if (flat.length === 0) return '';\r\n\r\n if (line == null) return '';\r\n\r\n const byNewLine = flat.findIndex((l) => l.newLineNumber === line);\r\n const anchor = byNewLine !== -1 ? byNewLine : flat.findIndex((l) => l.oldLineNumber === line);\r\n if (anchor === -1) return '';\r\n\r\n const start = Math.max(0, anchor - radius);\r\n const end = Math.min(flat.length, anchor + radius + 1);\r\n\r\n return flat\r\n .slice(start, end)\r\n .map((l) => {\r\n const prefix = l.kind === 'add' ? '+' : l.kind === 'del' ? '-' : ' ';\r\n const gutter = String(l.newLineNumber ?? l.oldLineNumber ?? '').padStart(4, ' ');\r\n return `${gutter} ${prefix}${l.content}`;\r\n })\r\n .join('\\n');\r\n}\r\n\r\nexport function parseVerifyResponse(raw: string): VerifyResult[] {\r\n const trimmed = raw.trim();\r\n const start = trimmed.indexOf('{');\r\n const end = trimmed.lastIndexOf('}');\r\n const candidate = start !== -1 && end !== -1 && end > start ? trimmed.slice(start, end + 1) : trimmed;\r\n\r\n let json: unknown;\r\n try {\r\n json = JSON.parse(candidate);\r\n } catch {\r\n json = JSON.parse(jsonrepair(candidate));\r\n }\r\n\r\n return verifyResultSchema.parse(json).results;\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/logger.ts
|
|
2
|
+
var SENSITIVE_KEYS = [
|
|
3
|
+
"api_key",
|
|
4
|
+
"api-key",
|
|
5
|
+
"apikey",
|
|
6
|
+
"secret",
|
|
7
|
+
"password",
|
|
8
|
+
"token",
|
|
9
|
+
"private_key",
|
|
10
|
+
"private-key",
|
|
11
|
+
"database_url",
|
|
12
|
+
"authorization",
|
|
13
|
+
"session",
|
|
14
|
+
"cookie"
|
|
15
|
+
];
|
|
16
|
+
var JWT = /\beyJ[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]{4,}\.[A-Za-z0-9_-]*/g;
|
|
17
|
+
var BEARER = /\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]{8,}/gi;
|
|
18
|
+
function scrubString(value) {
|
|
19
|
+
return value.replace(JWT, "[REDACTED_JWT]").replace(BEARER, (m) => `${m.split(/\s+/)[0]} [REDACTED]`);
|
|
20
|
+
}
|
|
21
|
+
function redact(obj) {
|
|
22
|
+
if (obj === null || obj === void 0) return obj;
|
|
23
|
+
if (typeof obj !== "object") {
|
|
24
|
+
return typeof obj === "string" ? scrubString(obj) : obj;
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(obj)) return obj.map(redact);
|
|
27
|
+
if (obj instanceof Error) {
|
|
28
|
+
return {
|
|
29
|
+
name: obj.name,
|
|
30
|
+
message: scrubString(obj.message),
|
|
31
|
+
...obj.stack ? { stack: scrubString(obj.stack) } : {}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const redacted = {};
|
|
35
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
36
|
+
const lowerKey = key.toLowerCase();
|
|
37
|
+
if (SENSITIVE_KEYS.some((sk) => lowerKey.includes(sk))) {
|
|
38
|
+
redacted[key] = "[REDACTED]";
|
|
39
|
+
} else {
|
|
40
|
+
redacted[key] = redact(value);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return redacted;
|
|
44
|
+
}
|
|
45
|
+
function formatLogRecord(level, message, contexts, data) {
|
|
46
|
+
return {
|
|
47
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
48
|
+
level,
|
|
49
|
+
message: scrubString(message),
|
|
50
|
+
...contexts.reduce((merged, context) => Object.assign(merged, redact(context)), {}),
|
|
51
|
+
...data ? { data: redact(data) } : {}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
var consoleLogger = {
|
|
55
|
+
info: (message, data) => console.log(JSON.stringify(formatLogRecord("info", message, [], data))),
|
|
56
|
+
warn: (message, data) => console.warn(JSON.stringify(formatLogRecord("warn", message, [], data))),
|
|
57
|
+
error: (message, data) => console.error(JSON.stringify(formatLogRecord("error", message, [], data))),
|
|
58
|
+
debug: (message, data) => console.log(JSON.stringify(formatLogRecord("debug", message, [], data)))
|
|
59
|
+
};
|
|
60
|
+
var sink = consoleLogger;
|
|
61
|
+
function setLoggerSink(next) {
|
|
62
|
+
sink = next;
|
|
63
|
+
}
|
|
64
|
+
var logger = {
|
|
65
|
+
info: (message, data) => sink.info(message, data),
|
|
66
|
+
warn: (message, data) => sink.warn(message, data),
|
|
67
|
+
error: (message, data) => sink.error(message, data),
|
|
68
|
+
debug: (message, data) => sink.debug(message, data)
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
scrubString,
|
|
73
|
+
redact,
|
|
74
|
+
formatLogRecord,
|
|
75
|
+
consoleLogger,
|
|
76
|
+
setLoggerSink,
|
|
77
|
+
logger
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=chunk-WSK45HW4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/logger.ts"],"sourcesContent":["\r\n/**\r\n * The logging port. A correct implementation must:\r\n * - never throw, for any input, including circular objects (callers log on failure paths, so a\r\n * throwing logger converts a handled error into an unhandled one);\r\n * - never block the caller on I/O;\r\n * - scrub secrets before emitting, using `scrubString`/`redact` below rather than its own rules.\r\n * Ordering between calls is not guaranteed and callers must not rely on it.\r\n */\r\nexport interface Logger {\r\n info(message: string, data?: unknown): void;\r\n warn(message: string, data?: unknown): void;\r\n error(message: string, data?: unknown): void;\r\n debug(message: string, data?: unknown): void;\r\n}\r\n\r\nconst SENSITIVE_KEYS = [\r\n 'api_key',\r\n 'api-key',\r\n 'apikey',\r\n 'secret',\r\n 'password',\r\n 'token',\r\n 'private_key',\r\n 'private-key',\r\n 'database_url',\r\n 'authorization',\r\n 'session',\r\n 'cookie',\r\n];\r\n\r\nconst JWT = /\\beyJ[A-Za-z0-9_-]{4,}\\.[A-Za-z0-9_-]{4,}\\.[A-Za-z0-9_-]*/g;\r\nconst BEARER = /\\b(Bearer|Basic)\\s+[A-Za-z0-9._~+/=-]{8,}/gi;\r\n\r\nexport function scrubString(value: string): string {\r\n return value.replace(JWT, '[REDACTED_JWT]').replace(BEARER, (m) => `${m.split(/\\s+/)[0]} [REDACTED]`);\r\n}\r\n\r\nexport function redact(obj: any): any {\r\n if (obj === null || obj === undefined) return obj;\r\n if (typeof obj !== 'object') {\r\n return typeof obj === 'string' ? scrubString(obj) : obj;\r\n }\r\n if (Array.isArray(obj)) return obj.map(redact);\r\n if (obj instanceof Error) {\r\n return {\r\n name: obj.name,\r\n message: scrubString(obj.message),\r\n ...(obj.stack ? { stack: scrubString(obj.stack) } : {}),\r\n };\r\n }\r\n\r\n const redacted: any = {};\r\n for (const [key, value] of Object.entries(obj)) {\r\n const lowerKey = key.toLowerCase();\r\n if (SENSITIVE_KEYS.some((sk) => lowerKey.includes(sk))) {\r\n redacted[key] = '[REDACTED]';\r\n } else {\r\n redacted[key] = redact(value);\r\n }\r\n }\r\n return redacted;\r\n}\r\n\r\nexport function formatLogRecord(\r\n level: string,\r\n message: string,\r\n contexts: Array<Record<string, any>>,\r\n data?: any,\r\n): Record<string, unknown> {\r\n return {\r\n timestamp: new Date().toISOString(),\r\n level,\r\n message: scrubString(message),\r\n ...contexts.reduce<Record<string, unknown>>((merged, context) => Object.assign(merged, redact(context)), {}),\r\n ...(data ? { data: redact(data) } : {}),\r\n };\r\n}\r\n\r\nexport const consoleLogger: Logger = {\r\n info: (message, data) => console.log(JSON.stringify(formatLogRecord('info', message, [], data))),\r\n warn: (message, data) => console.warn(JSON.stringify(formatLogRecord('warn', message, [], data))),\r\n error: (message, data) => console.error(JSON.stringify(formatLogRecord('error', message, [], data))),\r\n debug: (message, data) => console.log(JSON.stringify(formatLogRecord('debug', message, [], data))),\r\n};\r\n\r\nlet sink: Logger = consoleLogger;\r\n\r\n/**\r\n * Installs the host's logger. Called once at import scope by src/server/core/logger.ts, and by tests\r\n * that want to capture output.\r\n *\r\n * This is the one piece of module-level mutable state in this package, and it is deliberate: `logger`\r\n * below is used at import scope by fifteen modules here, several of them (model-output/*, rules/*,\r\n * finding-gates.ts) pure functions with no runtime parameter to hang a port off. Threading a Logger\r\n * argument through all of them would be by far the largest and least mechanical part of the\r\n * extraction, for no behavioural gain.\r\n */\r\nexport function setLoggerSink(next: Logger) {\r\n sink = next;\r\n}\r\n\r\nexport const logger: Logger = {\r\n info: (message, data) => sink.info(message, data),\r\n warn: (message, data) => sink.warn(message, data),\r\n error: (message, data) => sink.error(message, data),\r\n debug: (message, data) => sink.debug(message, data),\r\n};\r\n"],"mappings":";AAgBA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,MAAM;AACZ,IAAM,SAAS;AAER,SAAS,YAAY,OAAuB;AACjD,SAAO,MAAM,QAAQ,KAAK,gBAAgB,EAAE,QAAQ,QAAQ,CAAC,MAAM,GAAG,EAAE,MAAM,KAAK,EAAE,CAAC,CAAC,aAAa;AACtG;AAEO,SAAS,OAAO,KAAe;AACpC,MAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAC9C,MAAI,OAAO,QAAQ,UAAU;AAC3B,WAAO,OAAO,QAAQ,WAAW,YAAY,GAAG,IAAI;AAAA,EACtD;AACA,MAAI,MAAM,QAAQ,GAAG,EAAG,QAAO,IAAI,IAAI,MAAM;AAC7C,MAAI,eAAe,OAAO;AACxB,WAAO;AAAA,MACL,MAAM,IAAI;AAAA,MACV,SAAS,YAAY,IAAI,OAAO;AAAA,MAChC,GAAI,IAAI,QAAQ,EAAE,OAAO,YAAY,IAAI,KAAK,EAAE,IAAI,CAAC;AAAA,IACvD;AAAA,EACF;AAEA,QAAM,WAAgB,CAAC;AACvB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,UAAM,WAAW,IAAI,YAAY;AACjC,QAAI,eAAe,KAAK,CAAC,OAAO,SAAS,SAAS,EAAE,CAAC,GAAG;AACtD,eAAS,GAAG,IAAI;AAAA,IAClB,OAAO;AACL,eAAS,GAAG,IAAI,OAAO,KAAK;AAAA,IAC9B;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,gBACd,OACA,SACA,UACA,MACyB;AACzB,SAAO;AAAA,IACL,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC;AAAA,IACA,SAAS,YAAY,OAAO;AAAA,IAC5B,GAAG,SAAS,OAAgC,CAAC,QAAQ,YAAY,OAAO,OAAO,QAAQ,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;AAAA,IAC3G,GAAI,OAAO,EAAE,MAAM,OAAO,IAAI,EAAE,IAAI,CAAC;AAAA,EACvC;AACF;AAEO,IAAM,gBAAwB;AAAA,EACnC,MAAM,CAAC,SAAS,SAAS,QAAQ,IAAI,KAAK,UAAU,gBAAgB,QAAQ,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AAAA,EAC/F,MAAM,CAAC,SAAS,SAAS,QAAQ,KAAK,KAAK,UAAU,gBAAgB,QAAQ,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AAAA,EAChG,OAAO,CAAC,SAAS,SAAS,QAAQ,MAAM,KAAK,UAAU,gBAAgB,SAAS,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AAAA,EACnG,OAAO,CAAC,SAAS,SAAS,QAAQ,IAAI,KAAK,UAAU,gBAAgB,SAAS,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AACnG;AAEA,IAAI,OAAe;AAYZ,SAAS,cAAc,MAAc;AAC1C,SAAO;AACT;AAEO,IAAM,SAAiB;AAAA,EAC5B,MAAM,CAAC,SAAS,SAAS,KAAK,KAAK,SAAS,IAAI;AAAA,EAChD,MAAM,CAAC,SAAS,SAAS,KAAK,KAAK,SAAS,IAAI;AAAA,EAChD,OAAO,CAAC,SAAS,SAAS,KAAK,MAAM,SAAS,IAAI;AAAA,EAClD,OAAO,CAAC,SAAS,SAAS,KAAK,MAAM,SAAS,IAAI;AACpD;","names":[]}
|