Spaces:
Runtime error
Runtime error
Aryan Mishra
feat: initial commit - Multilingual ABSA project setup with 6 phases, 36 requirements
a6b96c2 | ; | |
| /** | |
| * DispatchLogger interface + default implementation β issue #177 (ADR-0174 P1.3). | |
| * | |
| * Interface: | |
| * { onEvent(event: DispatchEvent): void } | |
| * | |
| * Default behaviour (createDefaultLogger): | |
| * 1. Silent on success β no stdout/stderr when result.kind === 'ok'. | |
| * 2. Structured JSON to stderr on error β one line per dispatch error. | |
| * 3. Opt-in audit file β when GSD_AUDIT=1 OR config.audit.enabled===true, | |
| * appends every event (success + error) as one JSON line to | |
| * .planning/.gsd-trace.jsonl relative to `cwd`. Creates .planning/ if absent. | |
| * 4. Args redaction β args omitted by default; included when GSD_AUDIT_ARGS=1. | |
| * | |
| * No-op logger (createNoOpLogger): | |
| * Silent on all events. Used as the Hub default when no logger is injected. | |
| * | |
| * ADR-457 build-at-publish: the hand-written bin/lib/observability/logger.cjs | |
| * collapsed to a TypeScript source of truth. Behaviour is preserved | |
| * byte-for-behaviour from the prior hand-written .cjs; only types are added. | |
| */ | |
| var __importDefault = (this && this.__importDefault) || function (mod) { | |
| return (mod && mod.__esModule) ? mod : { "default": mod }; | |
| }; | |
| const node_fs_1 = __importDefault(require("node:fs")); | |
| const node_path_1 = __importDefault(require("node:path")); | |
| const redaction_cjs_1 = require("./redaction.cjs"); | |
| const AUDIT_FILE_NAME = '.gsd-trace.jsonl'; | |
| const PLANNING_DIR = '.planning'; | |
| // βββ helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * Safely serialise a value to JSON, falling back to a placeholder on circular refs. | |
| */ | |
| function _safeStringify(value) { | |
| try { | |
| return JSON.stringify(value); | |
| } | |
| catch { | |
| return JSON.stringify({ _serializationError: true }); | |
| } | |
| } | |
| /** | |
| * Determine whether the audit file should be written to. | |
| */ | |
| function _isAuditEnabled(config) { | |
| if (process.env['GSD_AUDIT'] === '1') | |
| return true; | |
| if (config && config.audit && config.audit.enabled === true) | |
| return true; | |
| return false; | |
| } | |
| /** | |
| * Build the redacted plain object for the audit file. | |
| * Preserves the full DispatchEvent structure. | |
| */ | |
| function _toAuditRecord(event) { | |
| return (0, redaction_cjs_1.redactEvent)(event); | |
| } | |
| /** | |
| * Build the flattened stderr error line. | |
| * | |
| * Per ADR-0174 P1.3 contract: { "kind": "<variant>", "traceId": "<uuid>", ...typedPayload } | |
| * The result's kind is promoted to top-level and the typed payload fields are spread in. | |
| * The `result` wrapper is removed. | |
| */ | |
| function _toStderrRecord(event) { | |
| const redacted = (0, redaction_cjs_1.redactEvent)(event); | |
| const { result, ...eventWithoutResult } = redacted; | |
| // Flatten: top-level gets kind + typed payload fields from result | |
| const resultObj = result; | |
| const { kind, ...typedPayload } = resultObj; | |
| return Object.assign({}, eventWithoutResult, { kind }, typedPayload); | |
| } | |
| /** | |
| * Append one JSON line to the audit file. | |
| * Creates .planning/ directory if it does not exist. | |
| * | |
| * Uses synchronous fs API (crash-safe for v1 β dispatch is synchronous). | |
| */ | |
| function _appendAuditLine(cwd, event) { | |
| const planningDir = node_path_1.default.join(cwd, PLANNING_DIR); | |
| // Ensure the directory exists | |
| if (!node_fs_1.default.existsSync(planningDir)) { | |
| node_fs_1.default.mkdirSync(planningDir, { recursive: true }); | |
| } | |
| const auditPath = node_path_1.default.join(planningDir, AUDIT_FILE_NAME); | |
| node_fs_1.default.appendFileSync(auditPath, _safeStringify(event) + '\n', 'utf8'); | |
| } | |
| /** | |
| * Create a no-op logger. All events are silently dropped. | |
| * This is the Hub's default when no logger is injected by the caller. | |
| */ | |
| function createNoOpLogger() { | |
| return { | |
| onEvent(_event) { | |
| // intentionally empty | |
| }, | |
| }; | |
| } | |
| /** | |
| * Create the default DispatchLogger. | |
| */ | |
| function createDefaultLogger({ cwd = process.cwd(), config } = {}) { | |
| return { | |
| /** | |
| * @param event - A DispatchEvent from the Hub. | |
| */ | |
| onEvent(event) { | |
| const resultObj = event && event['result']; | |
| const isOk = resultObj && resultObj['kind'] === 'ok'; | |
| // ββ Audit file (both ok and error) ββββββββββββββββββββββββββββββββββββ | |
| if (_isAuditEnabled(config)) { | |
| try { | |
| const auditRecord = _toAuditRecord(event); | |
| _appendAuditLine(cwd, auditRecord); | |
| } | |
| catch (auditErr) { | |
| // Audit errors must not surface to callers | |
| process.stderr.write(_safeStringify({ | |
| level: 'warn', | |
| source: 'DispatchLogger', | |
| message: 'audit file write failed: ' + String(auditErr?.message ?? auditErr), | |
| }) + '\n'); | |
| } | |
| } | |
| // ββ Stderr on error βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if (!isOk) { | |
| try { | |
| const stderrRecord = _toStderrRecord(event); | |
| process.stderr.write(_safeStringify(stderrRecord) + '\n'); | |
| } | |
| catch (stderrErr) { | |
| // Last-resort: we cannot throw from the logger | |
| process.stderr.write(_safeStringify({ | |
| level: 'warn', | |
| source: 'DispatchLogger', | |
| message: 'stderr emit failed: ' + String(stderrErr?.message ?? stderrErr), | |
| }) + '\n'); | |
| } | |
| } | |
| // ββ Silent on success (no else branch needed) βββββββββββββββββββββββββ | |
| }, | |
| }; | |
| } | |
| module.exports = { createDefaultLogger, createNoOpLogger }; | |