feat: add diagnostics and telemetry runtime

This commit is contained in:
donghyeon-ka
2026-07-26 16:42:27 +09:00
parent 2fa0baa577
commit 5173b6c8d6
43 changed files with 1760 additions and 116 deletions
@@ -5,7 +5,8 @@ import { getStorageDefinition } from "../../contracts/storage-keys.js";
* @param {{
* localStorage?: Storage,
* sessionStorage?: Storage,
* now?: () => number
* now?: () => number,
* diagnostics?: import("../../application/ports/diagnostics-port.js").DiagnosticsPort
* }} [dependencies]
* @returns {import("../../application/ports/storage-port.js").StoragePort}
*/
@@ -26,7 +27,7 @@ export function createBrowserStorageAdapter(dependencies = {}) {
try {
definition = getStorageDefinition(logicalName);
} catch {
return unavailable("read", logicalName);
return unavailable("read", logicalName, dependencies.diagnostics);
}
const backend = backendFor(definition.backend);
@@ -50,7 +51,7 @@ export function createBrowserStorageAdapter(dependencies = {}) {
}
return { ok: true, value: structuredClone(envelope.value) };
} catch {
return unavailable("read", logicalName);
return unavailable("read", logicalName, dependencies.diagnostics);
}
},
@@ -59,7 +60,7 @@ export function createBrowserStorageAdapter(dependencies = {}) {
try {
definition = getStorageDefinition(logicalName);
} catch {
return unavailable("write", logicalName);
return unavailable("write", logicalName, dependencies.diagnostics);
}
const expiresAt =
@@ -82,12 +83,24 @@ export function createBrowserStorageAdapter(dependencies = {}) {
if (definition.quotaFallback === "memory") {
memory.set(definition.physicalKey, structuredClone(value));
recordStorageFailure(
dependencies.diagnostics,
"write",
logicalName,
quota,
);
return {
ok: false,
error: storageFailure(quota, "write", logicalName),
fallback: "memory",
};
}
recordStorageFailure(
dependencies.diagnostics,
"write",
logicalName,
quota,
);
return {
ok: false,
error: storageFailure(quota, "write", logicalName),
@@ -101,14 +114,14 @@ export function createBrowserStorageAdapter(dependencies = {}) {
try {
definition = getStorageDefinition(logicalName);
} catch {
return unavailable("remove", logicalName);
return unavailable("remove", logicalName, dependencies.diagnostics);
}
try {
backendFor(definition.backend)?.removeItem(definition.physicalKey);
memory.delete(definition.physicalKey);
return { ok: true };
} catch {
return unavailable("remove", logicalName);
return unavailable("remove", logicalName, dependencies.diagnostics);
}
},
});
@@ -128,10 +141,38 @@ function storageFailure(quota, phase, logicalName) {
);
}
/** @param {string} phase @param {string} logicalName */
function unavailable(phase, logicalName) {
/**
* @param {string} phase
* @param {string} logicalName
* @param {import("../../application/ports/diagnostics-port.js").DiagnosticsPort | undefined} diagnostics
*/
function unavailable(phase, logicalName, diagnostics) {
recordStorageFailure(diagnostics, phase, logicalName, false);
return {
ok: /** @type {false} */ (false),
error: storageFailure(false, phase, logicalName),
};
}
/**
* @param {import("../../application/ports/diagnostics-port.js").DiagnosticsPort | undefined} diagnostics
* @param {string} phase
* @param {string} logicalName
* @param {boolean} quota
*/
function recordStorageFailure(diagnostics, phase, logicalName, quota) {
try {
diagnostics?.record({
level: "warn",
eventId: "storage.operation.failed",
context: {
operation: `${phase}:${logicalName}`,
error_kind: quota
? "STORAGE_QUOTA_EXCEEDED"
: "STORAGE_UNAVAILABLE",
},
});
} catch {
// Storage behavior remains independent from diagnostics.
}
}