Files
HealthLog/src/lib/logging/context.ts
T
Marc-André Bombeck fea1d1e0b5 chore: strip planning tags from comments and enable the rule
Output of `node scripts/strip-planning-tags.mjs --write`: 3,157 comment
lines in 1,308 files lose the work-item identifier at their head. Release
versions and issue references stay; nothing outside comment text
changes, which the script checks per file against a comment-free
printout. healthlog/no-planning-tags is enabled at error level with this
commit, so the tree it lands on is clean.
2026-10-20 09:08:40 +02:00

67 lines
2.4 KiB
TypeScript

import { AsyncLocalStorage } from "node:async_hooks";
import type { WideEvent } from "./types";
import { WideEventBuilder } from "./event-builder";
import { registerSourcePriorityParseObserver } from "@/lib/validations/source-priority";
import { registerRecurrenceParseErrorObserver } from "@/lib/medications/scheduling/recurrence";
import {
DATETIME_NO_SECONDS_COUNTER,
registerDatetimeNoSecondsObserver,
} from "@/lib/validations/datetime";
/** AsyncLocalStorage fuer den aktuellen Request-Kontext */
export const eventStorage = new AsyncLocalStorage<WideEventBuilder>();
/** Aktuellen EventBuilder abrufen (null falls ausserhalb eines Kontexts) */
export function getEvent(): WideEventBuilder | null {
return eventStorage.getStore() ?? null;
}
/**
* Convenience: Felder im aktuellen Event annotieren.
* Safe to call anywhere — no-op wenn kein Kontext aktiv.
*/
export function annotate(fields: {
action?: WideEvent["action"];
meta?: Record<string, unknown>;
}): void {
const evt = getEvent();
if (!evt) return;
if (fields.action) evt.setAction(fields.action);
if (fields.meta) {
for (const [key, value] of Object.entries(fields.meta)) {
evt.addMeta(key, value);
}
}
}
// v1.4.25 — wire the schema-parser breadcrumb into the
// AsyncLocalStorage-backed `annotate()` only on the server. The
// validations module stays bundle-safe for client imports; this side
// effect runs the first time any server module imports the logging
// context.
registerSourcePriorityParseObserver((fields) => {
annotate(fields);
});
// A medication recurrence rule that does not parse. Same bundle-safe
// registration: the scheduling module also runs in the browser.
registerRecurrenceParseErrorObserver((rrule) => {
annotate({
action: { name: "medication.recurrence.parse_error" },
meta: { rrule },
});
});
// v1.43 — count every request instant refused for lacking seconds, per wide
// event, so a client still on a minute-precision encoder is visible in the
// logs (`meta["validation.datetime.no_seconds"]`) before its users report
// the 422. Same bundle-safe registration as the breadcrumb above.
const noSecondsCounts = new WeakMap<WideEventBuilder, number>();
registerDatetimeNoSecondsObserver(() => {
const evt = getEvent();
if (!evt) return;
const next = (noSecondsCounts.get(evt) ?? 0) + 1;
noSecondsCounts.set(evt, next);
evt.addMeta(DATETIME_NO_SECONDS_COUNTER, next);
});