@absolutejs/voice 0.0.22-beta.432 → 0.0.22-beta.433
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/dist/incidentTimeline.d.ts +31 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +137 -2
- package/package.json +1 -1
|
@@ -10,6 +10,30 @@ export type VoiceIncidentTimelineAction = {
|
|
|
10
10
|
label: string;
|
|
11
11
|
method?: 'GET' | 'POST';
|
|
12
12
|
};
|
|
13
|
+
export type VoiceIncidentRecoveryAction = {
|
|
14
|
+
detail?: string;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
eventId?: string;
|
|
17
|
+
href?: string;
|
|
18
|
+
id: string;
|
|
19
|
+
label: string;
|
|
20
|
+
method?: 'GET' | 'POST';
|
|
21
|
+
sessionId?: string;
|
|
22
|
+
};
|
|
23
|
+
export type VoiceIncidentRecoveryActionResult = {
|
|
24
|
+
actionId: string;
|
|
25
|
+
detail?: string;
|
|
26
|
+
href?: string;
|
|
27
|
+
ok: boolean;
|
|
28
|
+
status?: string;
|
|
29
|
+
};
|
|
30
|
+
export type VoiceIncidentRecoveryActionHandlerInput = {
|
|
31
|
+
action: VoiceIncidentRecoveryAction;
|
|
32
|
+
actionId: string;
|
|
33
|
+
report: VoiceIncidentTimelineReport;
|
|
34
|
+
request: Request;
|
|
35
|
+
};
|
|
36
|
+
export type VoiceIncidentRecoveryActionHandler = (input: VoiceIncidentRecoveryActionHandlerInput) => Promise<VoiceIncidentRecoveryActionResult> | VoiceIncidentRecoveryActionResult;
|
|
13
37
|
export type VoiceIncidentTimelineEvent = {
|
|
14
38
|
action?: VoiceIncidentTimelineAction;
|
|
15
39
|
at: number;
|
|
@@ -24,6 +48,7 @@ export type VoiceIncidentTimelineEvent = {
|
|
|
24
48
|
value?: number | string;
|
|
25
49
|
};
|
|
26
50
|
export type VoiceIncidentTimelineReport = {
|
|
51
|
+
actions: VoiceIncidentRecoveryAction[];
|
|
27
52
|
events: VoiceIncidentTimelineEvent[];
|
|
28
53
|
generatedAt: number;
|
|
29
54
|
links: VoiceIncidentTimelineLinks;
|
|
@@ -57,9 +82,15 @@ export type VoiceIncidentTimelineOptions = {
|
|
|
57
82
|
operationalStatus?: VoiceIncidentTimelineValue<VoiceOperationalStatusReport>;
|
|
58
83
|
operationsRecords?: VoiceIncidentTimelineValue<readonly VoiceOperationsRecord[]>;
|
|
59
84
|
opsRecovery?: VoiceIncidentTimelineValue<VoiceOpsRecoveryReport>;
|
|
85
|
+
recoveryActions?: readonly VoiceIncidentRecoveryAction[] | ((input: {
|
|
86
|
+
events: readonly VoiceIncidentTimelineEvent[];
|
|
87
|
+
report: Omit<VoiceIncidentTimelineReport, 'actions'>;
|
|
88
|
+
}) => Promise<readonly VoiceIncidentRecoveryAction[]> | readonly VoiceIncidentRecoveryAction[]);
|
|
60
89
|
windowMs?: number;
|
|
61
90
|
};
|
|
62
91
|
export type VoiceIncidentTimelineRoutesOptions = VoiceIncidentTimelineOptions & {
|
|
92
|
+
actionHandlers?: Record<string, VoiceIncidentRecoveryActionHandler>;
|
|
93
|
+
actionPath?: false | string;
|
|
63
94
|
headers?: HeadersInit;
|
|
64
95
|
htmlPath?: false | string;
|
|
65
96
|
markdownPath?: false | string;
|
package/dist/index.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export type { VoiceDeliverySinkDescriptor, VoiceDeliverySinkDescriptorInput, Voi
|
|
|
60
60
|
export type { VoiceOpsActionAuditRecord, VoiceOpsActionAuditRoutesOptions, VoiceOpsActionHistoryEntry, VoiceOpsActionHistoryReport } from './opsActionAuditRoutes';
|
|
61
61
|
export type { VoiceDeliveryRuntime, VoiceDeliveryRuntimeAuditConfig, VoiceDeliveryRuntimeConfig, VoiceDeliveryRuntimeFilePresetOptions, VoiceDeliveryRuntimePresetLeaseConfig, VoiceDeliveryRuntimePresetMode, VoiceDeliveryRuntimePresetOptions, VoiceDeliveryRuntimeReport, VoiceDeliveryRuntimeRoutesOptions, VoiceDeliveryRuntimeS3PresetOptions, VoiceDeliveryRuntimeSummary, VoiceDeliveryRuntimeTickResult, VoiceDeliveryRuntimeTraceConfig, VoiceDeliveryRuntimeWebhookPresetOptions } from './deliveryRuntime';
|
|
62
62
|
export type { VoiceOperationalStatus, VoiceOperationalStatusCheck, VoiceOperationalStatusOptions, VoiceOperationalStatusReport, VoiceOperationalStatusRoutesOptions, VoiceOperationalStatusValue } from './operationalStatus';
|
|
63
|
-
export type { VoiceIncidentTimelineAction, VoiceIncidentTimelineEvent, VoiceIncidentTimelineLinks, VoiceIncidentTimelineOptions, VoiceIncidentTimelineReport, VoiceIncidentTimelineRoutesOptions, VoiceIncidentTimelineSeverity, VoiceIncidentTimelineStatus, VoiceIncidentTimelineValue } from './incidentTimeline';
|
|
63
|
+
export type { VoiceIncidentRecoveryAction, VoiceIncidentRecoveryActionHandler, VoiceIncidentRecoveryActionHandlerInput, VoiceIncidentRecoveryActionResult, VoiceIncidentTimelineAction, VoiceIncidentTimelineEvent, VoiceIncidentTimelineLinks, VoiceIncidentTimelineOptions, VoiceIncidentTimelineReport, VoiceIncidentTimelineRoutesOptions, VoiceIncidentTimelineSeverity, VoiceIncidentTimelineStatus, VoiceIncidentTimelineValue } from './incidentTimeline';
|
|
64
64
|
export { compareVoiceEvalBaseline, createVoiceFileEvalBaselineStore, createVoiceFileScenarioFixtureStore, createVoiceEvalRoutes, renderVoiceEvalBaselineHTML, renderVoiceEvalHTML, renderVoiceScenarioEvalHTML, renderVoiceScenarioFixtureEvalHTML, runVoiceScenarioEvals, runVoiceScenarioFixtureEvals, runVoiceSessionEvals } from './evalRoutes';
|
|
65
65
|
export { assertVoiceSimulationSuiteEvidence, createVoiceSimulationSuiteRoutes, evaluateVoiceSimulationSuiteEvidence, renderVoiceSimulationSuiteHTML, runVoiceSimulationSuite } from './simulationSuite';
|
|
66
66
|
export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, createVoiceWorkflowContractPreset, createVoiceWorkflowScenario, recordVoiceWorkflowContractTrace, validateVoiceWorkflowRouteResult } from './workflowContract';
|
package/dist/index.js
CHANGED
|
@@ -30127,6 +30127,61 @@ var statusToSeverity = (status) => status === "fail" || status === "failed" ? "c
|
|
|
30127
30127
|
var failureReplayStatusToSeverity = (status) => status === "failed" ? "critical" : status === "healthy" ? "info" : "warn";
|
|
30128
30128
|
var withinWindow = (event, now, windowMs) => !windowMs || event.at >= now - windowMs;
|
|
30129
30129
|
var eventStatus2 = (event) => event.severity === "critical" ? "fail" : event.severity === "warn" ? "warn" : "pass";
|
|
30130
|
+
var defaultIncidentRecoveryActions = (events, links) => {
|
|
30131
|
+
const actions = [];
|
|
30132
|
+
const add = (action) => {
|
|
30133
|
+
const key = `${action.id}:${action.sessionId ?? ""}:${action.href ?? ""}`;
|
|
30134
|
+
if (actions.some((existing) => `${existing.id}:${existing.sessionId ?? ""}:${existing.href ?? ""}` === key)) {
|
|
30135
|
+
return;
|
|
30136
|
+
}
|
|
30137
|
+
actions.push(action);
|
|
30138
|
+
};
|
|
30139
|
+
for (const event of events) {
|
|
30140
|
+
if (event.category === "delivery") {
|
|
30141
|
+
add({
|
|
30142
|
+
detail: "Ask the app to tick delivery workers or retry failed delivery queue work.",
|
|
30143
|
+
eventId: event.id,
|
|
30144
|
+
href: links.deliveryRuntime,
|
|
30145
|
+
id: "delivery.retry",
|
|
30146
|
+
label: "Retry delivery work",
|
|
30147
|
+
method: "POST",
|
|
30148
|
+
sessionId: event.sessionId
|
|
30149
|
+
});
|
|
30150
|
+
}
|
|
30151
|
+
if (event.category === "readiness" || event.category === "operational-status") {
|
|
30152
|
+
add({
|
|
30153
|
+
detail: "Refresh production readiness and proof freshness before declaring the incident resolved.",
|
|
30154
|
+
eventId: event.id,
|
|
30155
|
+
href: links.productionReadiness ?? links.operationalStatus,
|
|
30156
|
+
id: "readiness.refresh",
|
|
30157
|
+
label: "Refresh readiness proof",
|
|
30158
|
+
method: "POST",
|
|
30159
|
+
sessionId: event.sessionId
|
|
30160
|
+
});
|
|
30161
|
+
}
|
|
30162
|
+
if (event.sessionId) {
|
|
30163
|
+
add({
|
|
30164
|
+
detail: "Generate or open a support/debug artifact for the affected call.",
|
|
30165
|
+
eventId: event.id,
|
|
30166
|
+
href: linkForSession(links.supportBundle, event.sessionId) ?? linkForSession(links.callDebugger, event.sessionId),
|
|
30167
|
+
id: "support.bundle",
|
|
30168
|
+
label: "Generate support bundle",
|
|
30169
|
+
method: "POST",
|
|
30170
|
+
sessionId: event.sessionId
|
|
30171
|
+
});
|
|
30172
|
+
}
|
|
30173
|
+
}
|
|
30174
|
+
if (events.some((event) => event.severity !== "info")) {
|
|
30175
|
+
add({
|
|
30176
|
+
detail: "Rerun the app proof pack to confirm the current release evidence is fresh.",
|
|
30177
|
+
href: links.proofPack,
|
|
30178
|
+
id: "proof.rerun",
|
|
30179
|
+
label: "Rerun proof pack",
|
|
30180
|
+
method: "POST"
|
|
30181
|
+
});
|
|
30182
|
+
}
|
|
30183
|
+
return actions;
|
|
30184
|
+
};
|
|
30130
30185
|
var worstStatus3 = (statuses) => statuses.includes("fail") ? "fail" : statuses.includes("warn") ? "warn" : "pass";
|
|
30131
30186
|
var pushOperationalStatusEvents = (events, report, links) => {
|
|
30132
30187
|
if (!report) {
|
|
@@ -30304,7 +30359,7 @@ var buildVoiceIncidentTimelineReport = async (options) => {
|
|
|
30304
30359
|
total: filtered.length,
|
|
30305
30360
|
warn: filtered.filter((event) => event.severity === "warn").length
|
|
30306
30361
|
};
|
|
30307
|
-
|
|
30362
|
+
const baseReport = {
|
|
30308
30363
|
events: filtered,
|
|
30309
30364
|
generatedAt: now,
|
|
30310
30365
|
links,
|
|
@@ -30312,6 +30367,14 @@ var buildVoiceIncidentTimelineReport = async (options) => {
|
|
|
30312
30367
|
summary,
|
|
30313
30368
|
windowMs: options.windowMs
|
|
30314
30369
|
};
|
|
30370
|
+
const configuredActions = typeof options.recoveryActions === "function" ? await options.recoveryActions({
|
|
30371
|
+
events: filtered,
|
|
30372
|
+
report: baseReport
|
|
30373
|
+
}) : options.recoveryActions;
|
|
30374
|
+
return {
|
|
30375
|
+
...baseReport,
|
|
30376
|
+
actions: configuredActions === undefined ? defaultIncidentRecoveryActions(filtered, links) : [...configuredActions]
|
|
30377
|
+
};
|
|
30315
30378
|
};
|
|
30316
30379
|
var renderVoiceIncidentTimelineMarkdown = (report, options = {}) => {
|
|
30317
30380
|
const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
|
|
@@ -30334,6 +30397,11 @@ Summary: ${report.summary.critical} critical, ${report.summary.warn} warn, ${rep
|
|
|
30334
30397
|
## Events
|
|
30335
30398
|
|
|
30336
30399
|
${rows || "- No incident timeline events."}
|
|
30400
|
+
|
|
30401
|
+
## Recovery Actions
|
|
30402
|
+
|
|
30403
|
+
${report.actions.map((action) => `- ${action.method ?? "GET"} ${action.id}: ${action.label}${action.href ? ` (${action.href})` : ""}${action.detail ? ` - ${action.detail}` : ""}`).join(`
|
|
30404
|
+
`) || "- No recovery actions."}
|
|
30337
30405
|
`;
|
|
30338
30406
|
};
|
|
30339
30407
|
var renderVoiceIncidentTimelineHTML = (report, options = {}) => {
|
|
@@ -30346,12 +30414,20 @@ var renderVoiceIncidentTimelineHTML = (report, options = {}) => {
|
|
|
30346
30414
|
${event.detail ? `<p>${escapeHtml43(event.detail)}</p>` : ""}
|
|
30347
30415
|
<div>${event.href ? `<a href="${escapeHtml43(event.href)}">Open source</a>` : ""}${event.action?.href ? `<a href="${escapeHtml43(event.action.href)}">${escapeHtml43(event.action.label)}</a>` : ""}</div>
|
|
30348
30416
|
</article>`).join("");
|
|
30349
|
-
|
|
30417
|
+
const actions = report.actions.map((action) => {
|
|
30418
|
+
const label = escapeHtml43(action.label);
|
|
30419
|
+
const detail = action.detail ? `<p>${escapeHtml43(action.detail)}</p>` : "";
|
|
30420
|
+
const href = action.href ? `<a href="${escapeHtml43(action.href)}">Open target</a>` : "";
|
|
30421
|
+
const control = action.method === "POST" ? `<button type="button" data-voice-incident-action="${escapeHtml43(action.id)}" ${action.disabled ? "disabled" : ""}>${label}</button>` : href;
|
|
30422
|
+
return `<article class="action"><span>${escapeHtml43(action.method ?? "GET")}</span><h2>${label}</h2>${detail}<div>${control}${href && action.method === "POST" ? href : ""}</div></article>`;
|
|
30423
|
+
}).join("");
|
|
30424
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml43(title)}</title><style>body{background:#11110d;color:#faf4df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero{background:linear-gradient(135deg,rgba(248,113,113,.2),rgba(245,158,11,.13),rgba(34,197,94,.12));border:1px solid #39301d;border-radius:30px;margin-bottom:18px;padding:28px}.eyebrow{color:#fcd34d;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #575030;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.status.pass{border-color:rgba(34,197,94,.65)}.status.warn{border-color:rgba(245,158,11,.75)}.status.fail{border-color:rgba(239,68,68,.85)}.grid{display:grid;gap:14px}.actions{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:0 0 18px}.summary{display:flex;flex-wrap:wrap;gap:10px}.summary span{background:#181711;border:1px solid #39301d;border-radius:999px;padding:8px 12px}article{background:#181711;border:1px solid #39301d;border-radius:22px;padding:18px}article.critical{border-color:rgba(239,68,68,.85)}article.warn{border-color:rgba(245,158,11,.75)}article.info{border-color:rgba(34,197,94,.55)}article.action{border-color:#5b4a22}article span{color:#fcd34d;font-size:.78rem;font-weight:900;letter-spacing:.08em}article h2{margin:.35rem 0}.muted,article p{color:#cfc5a8}article strong{display:block;font-size:1.3rem;margin:.5rem 0}a{color:#fde68a;margin-right:12px}button{background:#fcd34d;border:0;border-radius:999px;color:#171307;cursor:pointer;font-weight:900;padding:10px 14px}button:disabled{cursor:not-allowed;opacity:.55}</style></head><body><main><section class="hero"><p class="eyebrow">Operational triage</p><h1>${escapeHtml43(title)}</h1><p class="status ${escapeHtml43(report.status)}">Overall: ${escapeHtml43(report.status.toUpperCase())}</p><p class="muted">Generated ${escapeHtml43(new Date(report.generatedAt).toLocaleString())}</p><div class="summary"><span>${String(report.summary.critical)} critical</span><span>${String(report.summary.warn)} warn</span><span>${String(report.summary.info)} info</span><span>${String(report.summary.total)} total</span></div></section><h2>Recovery actions</h2><section class="actions">${actions || '<article class="action"><span>NONE</span><h2>No recovery actions</h2><p>No executable actions are available for this report.</p></article>'}</section><h2>Timeline</h2><section class="grid">${events || '<article class="info"><span>INFO</span><h2>No incident events</h2><p>No non-pass operational events were found in this window.</p></article>'}</section></main><script>document.querySelectorAll("[data-voice-incident-action]").forEach((button)=>{button.addEventListener("click",async()=>{const id=button.getAttribute("data-voice-incident-action");if(!id)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch("/api/voice/incident-timeline/actions/"+encodeURIComponent(id),{method:"POST"});button.textContent=response.ok?"Done":"Failed";if(response.ok)setTimeout(()=>location.reload(),700)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1600)}})});</script></body></html>`;
|
|
30350
30425
|
};
|
|
30351
30426
|
var createVoiceIncidentTimelineRoutes = (options) => {
|
|
30352
30427
|
const path = options.path ?? "/api/voice/incident-timeline";
|
|
30353
30428
|
const htmlPath = options.htmlPath === undefined ? "/voice/incident-timeline" : options.htmlPath;
|
|
30354
30429
|
const markdownPath = options.markdownPath === undefined ? "/voice/incident-timeline.md" : options.markdownPath;
|
|
30430
|
+
const actionPath = options.actionPath === undefined ? "/api/voice/incident-timeline/actions" : options.actionPath;
|
|
30355
30431
|
const routes = new Elysia46({
|
|
30356
30432
|
name: options.name ?? "absolutejs-voice-incident-timeline"
|
|
30357
30433
|
}).get(path, async () => {
|
|
@@ -30389,6 +30465,65 @@ var createVoiceIncidentTimelineRoutes = (options) => {
|
|
|
30389
30465
|
});
|
|
30390
30466
|
});
|
|
30391
30467
|
}
|
|
30468
|
+
if (actionPath !== false) {
|
|
30469
|
+
routes.get(actionPath, async () => {
|
|
30470
|
+
const report = await buildVoiceIncidentTimelineReport(options);
|
|
30471
|
+
return new Response(JSON.stringify({
|
|
30472
|
+
actions: report.actions,
|
|
30473
|
+
generatedAt: report.generatedAt,
|
|
30474
|
+
status: report.status
|
|
30475
|
+
}), {
|
|
30476
|
+
headers: {
|
|
30477
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
30478
|
+
...options.headers
|
|
30479
|
+
}
|
|
30480
|
+
});
|
|
30481
|
+
}).post(`${actionPath}/:actionId`, async ({ params, request }) => {
|
|
30482
|
+
const actionId = params.actionId;
|
|
30483
|
+
const report = await buildVoiceIncidentTimelineReport(options);
|
|
30484
|
+
const action = report.actions.find((item) => item.id === actionId);
|
|
30485
|
+
const handler = options.actionHandlers?.[actionId];
|
|
30486
|
+
if (!action) {
|
|
30487
|
+
return new Response(JSON.stringify({
|
|
30488
|
+
actionId,
|
|
30489
|
+
ok: false,
|
|
30490
|
+
status: "not_found"
|
|
30491
|
+
}), {
|
|
30492
|
+
headers: {
|
|
30493
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
30494
|
+
...options.headers
|
|
30495
|
+
},
|
|
30496
|
+
status: 404
|
|
30497
|
+
});
|
|
30498
|
+
}
|
|
30499
|
+
if (action.disabled || action.method !== "POST" || !handler) {
|
|
30500
|
+
return new Response(JSON.stringify({
|
|
30501
|
+
actionId,
|
|
30502
|
+
ok: false,
|
|
30503
|
+
status: action.disabled ? "disabled" : "not_executable"
|
|
30504
|
+
}), {
|
|
30505
|
+
headers: {
|
|
30506
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
30507
|
+
...options.headers
|
|
30508
|
+
},
|
|
30509
|
+
status: 409
|
|
30510
|
+
});
|
|
30511
|
+
}
|
|
30512
|
+
const result = await handler({
|
|
30513
|
+
action,
|
|
30514
|
+
actionId,
|
|
30515
|
+
report,
|
|
30516
|
+
request
|
|
30517
|
+
});
|
|
30518
|
+
return new Response(JSON.stringify(result), {
|
|
30519
|
+
headers: {
|
|
30520
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
30521
|
+
...options.headers
|
|
30522
|
+
},
|
|
30523
|
+
status: result.ok ? 200 : 500
|
|
30524
|
+
});
|
|
30525
|
+
});
|
|
30526
|
+
}
|
|
30392
30527
|
return routes;
|
|
30393
30528
|
};
|
|
30394
30529
|
// src/dataControl.ts
|