@open-mercato/shared 0.7.1-develop.7151.1.00d0391847 → 0.7.1-develop.7153.1.7145d295e6
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/.turbo/turbo-build.log +1 -1
- package/dist/lib/dev-runtime/layout.js +22 -0
- package/dist/lib/dev-runtime/layout.js.map +7 -0
- package/dist/lib/dev-runtime/redaction.js +25 -0
- package/dist/lib/dev-runtime/redaction.js.map +7 -0
- package/dist/lib/dev-runtime/report.js +92 -0
- package/dist/lib/dev-runtime/report.js.map +7 -0
- package/dist/lib/dev-runtime/routes.js +164 -0
- package/dist/lib/dev-runtime/routes.js.map +7 -0
- package/dist/lib/dev-runtime/server.js +111 -0
- package/dist/lib/dev-runtime/server.js.map +7 -0
- package/dist/lib/dev-runtime/types.js +23 -0
- package/dist/lib/dev-runtime/types.js.map +7 -0
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/package.json +2 -2
- package/src/lib/dev-runtime/__tests__/routes.test.ts +473 -0
- package/src/lib/dev-runtime/layout.ts +39 -0
- package/src/lib/dev-runtime/redaction.ts +29 -0
- package/src/lib/dev-runtime/report.ts +116 -0
- package/src/lib/dev-runtime/routes.ts +219 -0
- package/src/lib/dev-runtime/server.ts +174 -0
- package/src/lib/dev-runtime/types.ts +101 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
[build:shared] found
|
|
1
|
+
[build:shared] found 278 entry points
|
|
2
2
|
[build:shared] built successfully
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { resolveDevRuntimeServerConfig } from "./server.js";
|
|
2
|
+
import {
|
|
3
|
+
DEV_RUNTIME_BANNER_META_NAME,
|
|
4
|
+
DEV_RUNTIME_LOGS_URL_META_NAME,
|
|
5
|
+
DEV_RUNTIME_TOKEN_META_NAME
|
|
6
|
+
} from "./types.js";
|
|
7
|
+
const DISABLED = { enabled: false, bannerEnabled: false, meta: [] };
|
|
8
|
+
function resolveDevRuntimeLayoutConfig(env = process.env) {
|
|
9
|
+
const config = resolveDevRuntimeServerConfig(env);
|
|
10
|
+
if (!config.enabled || !config.token) return DISABLED;
|
|
11
|
+
const meta = [
|
|
12
|
+
{ name: DEV_RUNTIME_TOKEN_META_NAME, content: config.token },
|
|
13
|
+
{ name: DEV_RUNTIME_BANNER_META_NAME, content: config.bannerEnabled ? "1" : "0" }
|
|
14
|
+
];
|
|
15
|
+
const logsUrl = typeof env.OM_DEV_RUNTIME_SPLASH_URL === "string" ? env.OM_DEV_RUNTIME_SPLASH_URL.trim() : "";
|
|
16
|
+
if (logsUrl) meta.push({ name: DEV_RUNTIME_LOGS_URL_META_NAME, content: logsUrl });
|
|
17
|
+
return { enabled: true, bannerEnabled: config.bannerEnabled, meta };
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
resolveDevRuntimeLayoutConfig
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/dev-runtime/layout.ts"],
|
|
4
|
+
"sourcesContent": ["import { resolveDevRuntimeServerConfig } from './server'\nimport {\n DEV_RUNTIME_BANNER_META_NAME,\n DEV_RUNTIME_LOGS_URL_META_NAME,\n DEV_RUNTIME_TOKEN_META_NAME,\n} from './types'\n\nexport type DevRuntimeLayoutMeta = {\n name: string\n content: string\n}\n\nexport type DevRuntimeLayoutConfig = {\n enabled: boolean\n bannerEnabled: boolean\n meta: DevRuntimeLayoutMeta[]\n}\n\nconst DISABLED: DevRuntimeLayoutConfig = { enabled: false, bannerEnabled: false, meta: [] }\n\n/**\n * Server-side helper for the app layout. It exposes the per-run token to the\n * local dev browser through dev-only `<meta>` elements without adding a context\n * provider, and returns nothing at all outside a supervised dev runtime.\n */\nexport function resolveDevRuntimeLayoutConfig(env: NodeJS.ProcessEnv = process.env): DevRuntimeLayoutConfig {\n const config = resolveDevRuntimeServerConfig(env)\n if (!config.enabled || !config.token) return DISABLED\n\n const meta: DevRuntimeLayoutMeta[] = [\n { name: DEV_RUNTIME_TOKEN_META_NAME, content: config.token },\n { name: DEV_RUNTIME_BANNER_META_NAME, content: config.bannerEnabled ? '1' : '0' },\n ]\n\n const logsUrl = typeof env.OM_DEV_RUNTIME_SPLASH_URL === 'string' ? env.OM_DEV_RUNTIME_SPLASH_URL.trim() : ''\n if (logsUrl) meta.push({ name: DEV_RUNTIME_LOGS_URL_META_NAME, content: logsUrl })\n\n return { enabled: true, bannerEnabled: config.bannerEnabled, meta }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,qCAAqC;AAC9C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAaP,MAAM,WAAmC,EAAE,SAAS,OAAO,eAAe,OAAO,MAAM,CAAC,EAAE;AAOnF,SAAS,8BAA8B,MAAyB,QAAQ,KAA6B;AAC1G,QAAM,SAAS,8BAA8B,GAAG;AAChD,MAAI,CAAC,OAAO,WAAW,CAAC,OAAO,MAAO,QAAO;AAE7C,QAAM,OAA+B;AAAA,IACnC,EAAE,MAAM,6BAA6B,SAAS,OAAO,MAAM;AAAA,IAC3D,EAAE,MAAM,8BAA8B,SAAS,OAAO,gBAAgB,MAAM,IAAI;AAAA,EAClF;AAEA,QAAM,UAAU,OAAO,IAAI,8BAA8B,WAAW,IAAI,0BAA0B,KAAK,IAAI;AAC3G,MAAI,QAAS,MAAK,KAAK,EAAE,MAAM,gCAAgC,SAAS,QAAQ,CAAC;AAEjF,SAAO,EAAE,SAAS,MAAM,eAAe,OAAO,eAAe,KAAK;AACpE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const REDACTION_RULES = [
|
|
2
|
+
[/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, "***"],
|
|
3
|
+
[/\b(postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?)::?\/\/[^\s'"`<>)]+/gi, "$1://***"],
|
|
4
|
+
[/\bBearer\s+[A-Za-z0-9._~+/=-]{8,}/gi, "Bearer ***"],
|
|
5
|
+
[/\b(authorization|proxy-authorization|x-api-key|x-auth-token)(\s*[:=]\s*)(?:\w+\s+)?\S+/gi, "$1$2***"],
|
|
6
|
+
[/\b(set-cookie|cookie)(\s*[:=]\s*)[^\n]+/gi, "$1$2***"],
|
|
7
|
+
[/\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]*/g, "***"],
|
|
8
|
+
[/\b(sk|pk|rk)_(live|test)_[A-Za-z0-9]{8,}/g, "***"],
|
|
9
|
+
[/\bgh[pousr]_[A-Za-z0-9]{16,}/g, "***"],
|
|
10
|
+
[/\b(password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|client[_-]?secret|session[_-]?id)("?\s*[:=]\s*"?)([^\s"',;)}]+)/gi, "$1$2***"]
|
|
11
|
+
];
|
|
12
|
+
function redactDevRuntimeText(value, maxLength = 400) {
|
|
13
|
+
if (value == null) return void 0;
|
|
14
|
+
let text = String(value);
|
|
15
|
+
for (const [pattern, replacement] of REDACTION_RULES) {
|
|
16
|
+
text = text.replace(pattern, replacement);
|
|
17
|
+
}
|
|
18
|
+
text = text.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "").replace(/\s+$/g, "");
|
|
19
|
+
if (!text) return void 0;
|
|
20
|
+
return text.length > maxLength ? `${text.slice(0, maxLength - 1)}\u2026` : text;
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
redactDevRuntimeText
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=redaction.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/dev-runtime/redaction.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Mirror of the supervisor-side rules in `scripts/dev-runtime-state.mjs`.\n * The supervisor re-redacts everything it ingests, but the dev-only app route\n * writes reports to a local file first, so the same rules must apply here.\n * `scripts/__tests__/dev-runtime-redaction-parity.test.mjs` keeps the two lists\n * from drifting.\n */\nconst REDACTION_RULES: Array<[RegExp, string]> = [\n [/-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, '***'],\n [/\\b(postgres(?:ql)?|mysql|mariadb|mongodb(?:\\+srv)?|rediss?|amqps?)::?\\/\\/[^\\s'\"`<>)]+/gi, '$1://***'],\n [/\\bBearer\\s+[A-Za-z0-9._~+/=-]{8,}/gi, 'Bearer ***'],\n [/\\b(authorization|proxy-authorization|x-api-key|x-auth-token)(\\s*[:=]\\s*)(?:\\w+\\s+)?\\S+/gi, '$1$2***'],\n [/\\b(set-cookie|cookie)(\\s*[:=]\\s*)[^\\n]+/gi, '$1$2***'],\n [/\\beyJ[A-Za-z0-9_-]{6,}\\.[A-Za-z0-9_-]{6,}\\.[A-Za-z0-9_-]*/g, '***'],\n [/\\b(sk|pk|rk)_(live|test)_[A-Za-z0-9]{8,}/g, '***'],\n [/\\bgh[pousr]_[A-Za-z0-9]{16,}/g, '***'],\n [/\\b(password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|client[_-]?secret|session[_-]?id)(\"?\\s*[:=]\\s*\"?)([^\\s\"',;)}]+)/gi, '$1$2***'],\n]\n\nexport function redactDevRuntimeText(value: unknown, maxLength = 400): string | undefined {\n if (value == null) return undefined\n let text = String(value)\n for (const [pattern, replacement] of REDACTION_RULES) {\n text = text.replace(pattern, replacement)\n }\n text = text.replace(/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f]/g, '').replace(/\\s+$/g, '')\n if (!text) return undefined\n return text.length > maxLength ? `${text.slice(0, maxLength - 1)}\u2026` : text\n}\n"],
|
|
5
|
+
"mappings": "AAOA,MAAM,kBAA2C;AAAA,EAC/C,CAAC,+EAA+E,KAAK;AAAA,EACrF,CAAC,2FAA2F,UAAU;AAAA,EACtG,CAAC,uCAAuC,YAAY;AAAA,EACpD,CAAC,4FAA4F,SAAS;AAAA,EACtG,CAAC,6CAA6C,SAAS;AAAA,EACvD,CAAC,8DAA8D,KAAK;AAAA,EACpE,CAAC,6CAA6C,KAAK;AAAA,EACnD,CAAC,iCAAiC,KAAK;AAAA,EACvC,CAAC,oJAAoJ,SAAS;AAChK;AAEO,SAAS,qBAAqB,OAAgB,YAAY,KAAyB;AACxF,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,OAAO,KAAK;AACvB,aAAW,CAAC,SAAS,WAAW,KAAK,iBAAiB;AACpD,WAAO,KAAK,QAAQ,SAAS,WAAW;AAAA,EAC1C;AACA,SAAO,KAAK,QAAQ,mDAAmD,EAAE,EAAE,QAAQ,SAAS,EAAE;AAC9F,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,SAAS,YAAY,GAAG,KAAK,MAAM,GAAG,YAAY,CAAC,CAAC,WAAM;AACxE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEV_RUNTIME_BANNER_META_NAME,
|
|
3
|
+
DEV_RUNTIME_DIAGNOSTICS_PATH,
|
|
4
|
+
DEV_RUNTIME_LOGS_URL_META_NAME,
|
|
5
|
+
DEV_RUNTIME_TOKEN_HEADER,
|
|
6
|
+
DEV_RUNTIME_TOKEN_META_NAME
|
|
7
|
+
} from "./types.js";
|
|
8
|
+
const MAX_MESSAGE_LENGTH = 500;
|
|
9
|
+
const MAX_STACK_LENGTH = 2e3;
|
|
10
|
+
const MAX_REPORTS_PER_PAGE = 20;
|
|
11
|
+
let sentReports = 0;
|
|
12
|
+
const seenFingerprints = /* @__PURE__ */ new Set();
|
|
13
|
+
function readMeta(name) {
|
|
14
|
+
if (typeof document === "undefined") return null;
|
|
15
|
+
const element = document.querySelector(`meta[name="${name}"]`);
|
|
16
|
+
const content = element?.getAttribute("content")?.trim();
|
|
17
|
+
return content ? content : null;
|
|
18
|
+
}
|
|
19
|
+
function readDevRuntimeToken() {
|
|
20
|
+
return readMeta(DEV_RUNTIME_TOKEN_META_NAME);
|
|
21
|
+
}
|
|
22
|
+
function isDevRuntimeBannerEnabled() {
|
|
23
|
+
return readMeta(DEV_RUNTIME_BANNER_META_NAME) === "1";
|
|
24
|
+
}
|
|
25
|
+
function readDevRuntimeLogsUrl() {
|
|
26
|
+
return readMeta(DEV_RUNTIME_LOGS_URL_META_NAME);
|
|
27
|
+
}
|
|
28
|
+
function truncate(value, maxLength) {
|
|
29
|
+
if (typeof value !== "string") return void 0;
|
|
30
|
+
const trimmed = value.trim();
|
|
31
|
+
if (!trimmed) return void 0;
|
|
32
|
+
return trimmed.length > maxLength ? trimmed.slice(0, maxLength) : trimmed;
|
|
33
|
+
}
|
|
34
|
+
function describeDevRuntimeError(error) {
|
|
35
|
+
if (error instanceof Error) {
|
|
36
|
+
return {
|
|
37
|
+
message: `${error.name}: ${error.message}`,
|
|
38
|
+
stack: truncate(error.stack, MAX_STACK_LENGTH),
|
|
39
|
+
digest: truncate(error.digest, 64)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (typeof error === "string") return { message: error };
|
|
43
|
+
return { message: "Unknown browser error" };
|
|
44
|
+
}
|
|
45
|
+
function reportDevRuntimeError(input) {
|
|
46
|
+
if (typeof window === "undefined") return;
|
|
47
|
+
if (sentReports >= MAX_REPORTS_PER_PAGE) return;
|
|
48
|
+
const token = readDevRuntimeToken();
|
|
49
|
+
if (!token) return;
|
|
50
|
+
const described = input.error !== void 0 ? describeDevRuntimeError(input.error) : {};
|
|
51
|
+
const message = truncate(input.message ?? described.message, MAX_MESSAGE_LENGTH);
|
|
52
|
+
if (!message) return;
|
|
53
|
+
const digest = truncate(input.digest ?? described.digest, 64);
|
|
54
|
+
const stack = truncate(input.stack ?? described.stack, MAX_STACK_LENGTH);
|
|
55
|
+
const path = truncate(window.location?.pathname, 300);
|
|
56
|
+
const fingerprint = `${input.kind}|${digest ?? message}|${path ?? ""}`;
|
|
57
|
+
if (seenFingerprints.has(fingerprint)) return;
|
|
58
|
+
seenFingerprints.add(fingerprint);
|
|
59
|
+
sentReports += 1;
|
|
60
|
+
const report = {
|
|
61
|
+
kind: input.kind,
|
|
62
|
+
message,
|
|
63
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
64
|
+
};
|
|
65
|
+
if (digest) report.digest = digest;
|
|
66
|
+
if (stack) report.stack = stack;
|
|
67
|
+
if (path) report.path = path;
|
|
68
|
+
try {
|
|
69
|
+
void fetch(DEV_RUNTIME_DIAGNOSTICS_PATH, {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: { "content-type": "application/json", [DEV_RUNTIME_TOKEN_HEADER]: token },
|
|
72
|
+
body: JSON.stringify(report),
|
|
73
|
+
cache: "no-store",
|
|
74
|
+
keepalive: true
|
|
75
|
+
}).catch(() => {
|
|
76
|
+
});
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function resetDevRuntimeReporterForTests() {
|
|
81
|
+
sentReports = 0;
|
|
82
|
+
seenFingerprints.clear();
|
|
83
|
+
}
|
|
84
|
+
export {
|
|
85
|
+
describeDevRuntimeError,
|
|
86
|
+
isDevRuntimeBannerEnabled,
|
|
87
|
+
readDevRuntimeLogsUrl,
|
|
88
|
+
readDevRuntimeToken,
|
|
89
|
+
reportDevRuntimeError,
|
|
90
|
+
resetDevRuntimeReporterForTests
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/dev-runtime/report.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n DEV_RUNTIME_BANNER_META_NAME,\n DEV_RUNTIME_DIAGNOSTICS_PATH,\n DEV_RUNTIME_LOGS_URL_META_NAME,\n DEV_RUNTIME_TOKEN_HEADER,\n DEV_RUNTIME_TOKEN_META_NAME,\n type DevRuntimeReport,\n type DevRuntimeReportKind,\n} from './types'\n\nconst MAX_MESSAGE_LENGTH = 500\nconst MAX_STACK_LENGTH = 2000\nconst MAX_REPORTS_PER_PAGE = 20\n\nlet sentReports = 0\nconst seenFingerprints = new Set<string>()\n\nfunction readMeta(name: string): string | null {\n if (typeof document === 'undefined') return null\n const element = document.querySelector(`meta[name=\"${name}\"]`)\n const content = element?.getAttribute('content')?.trim()\n return content ? content : null\n}\n\nexport function readDevRuntimeToken(): string | null {\n return readMeta(DEV_RUNTIME_TOKEN_META_NAME)\n}\n\nexport function isDevRuntimeBannerEnabled(): boolean {\n return readMeta(DEV_RUNTIME_BANNER_META_NAME) === '1'\n}\n\nexport function readDevRuntimeLogsUrl(): string | null {\n return readMeta(DEV_RUNTIME_LOGS_URL_META_NAME)\n}\n\nfunction truncate(value: unknown, maxLength: number): string | undefined {\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n if (!trimmed) return undefined\n return trimmed.length > maxLength ? trimmed.slice(0, maxLength) : trimmed\n}\n\nexport function describeDevRuntimeError(error: unknown): { message: string; stack?: string; digest?: string } {\n if (error instanceof Error) {\n return {\n message: `${error.name}: ${error.message}`,\n stack: truncate(error.stack, MAX_STACK_LENGTH),\n digest: truncate((error as { digest?: unknown }).digest, 64),\n }\n }\n if (typeof error === 'string') return { message: error }\n return { message: 'Unknown browser error' }\n}\n\n/**\n * Best-effort, fire-and-forget browser report. It never blocks rendering, never\n * retries, and silently gives up when the collector is unavailable \u2014 a broken\n * runtime must still show its own rendered error state.\n */\nexport function reportDevRuntimeError(input: {\n kind: DevRuntimeReportKind\n error?: unknown\n message?: string\n digest?: string\n stack?: string\n}): void {\n if (typeof window === 'undefined') return\n if (sentReports >= MAX_REPORTS_PER_PAGE) return\n\n const token = readDevRuntimeToken()\n if (!token) return\n\n const described: Partial<ReturnType<typeof describeDevRuntimeError>> = input.error !== undefined\n ? describeDevRuntimeError(input.error)\n : {}\n const message = truncate(input.message ?? described.message, MAX_MESSAGE_LENGTH)\n if (!message) return\n\n const digest = truncate(input.digest ?? described.digest, 64)\n const stack = truncate(input.stack ?? described.stack, MAX_STACK_LENGTH)\n const path = truncate(window.location?.pathname, 300)\n\n // One report per distinct failure per page: a render loop must not turn into\n // a request loop.\n const fingerprint = `${input.kind}|${digest ?? message}|${path ?? ''}`\n if (seenFingerprints.has(fingerprint)) return\n seenFingerprints.add(fingerprint)\n sentReports += 1\n\n const report: DevRuntimeReport = {\n kind: input.kind,\n message,\n timestamp: new Date().toISOString(),\n }\n if (digest) report.digest = digest\n if (stack) report.stack = stack\n if (path) report.path = path\n\n try {\n void fetch(DEV_RUNTIME_DIAGNOSTICS_PATH, {\n method: 'POST',\n headers: { 'content-type': 'application/json', [DEV_RUNTIME_TOKEN_HEADER]: token },\n body: JSON.stringify(report),\n cache: 'no-store',\n keepalive: true,\n }).catch(() => {})\n } catch {\n // Reporting is optional; the rendered fallback stays the source of truth.\n }\n}\n\nexport function resetDevRuntimeReporterForTests(): void {\n sentReports = 0\n seenFingerprints.clear()\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAEP,MAAM,qBAAqB;AAC3B,MAAM,mBAAmB;AACzB,MAAM,uBAAuB;AAE7B,IAAI,cAAc;AAClB,MAAM,mBAAmB,oBAAI,IAAY;AAEzC,SAAS,SAAS,MAA6B;AAC7C,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,QAAM,UAAU,SAAS,cAAc,cAAc,IAAI,IAAI;AAC7D,QAAM,UAAU,SAAS,aAAa,SAAS,GAAG,KAAK;AACvD,SAAO,UAAU,UAAU;AAC7B;AAEO,SAAS,sBAAqC;AACnD,SAAO,SAAS,2BAA2B;AAC7C;AAEO,SAAS,4BAAqC;AACnD,SAAO,SAAS,4BAA4B,MAAM;AACpD;AAEO,SAAS,wBAAuC;AACrD,SAAO,SAAS,8BAA8B;AAChD;AAEA,SAAS,SAAS,OAAgB,WAAuC;AACvE,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,QAAQ,SAAS,YAAY,QAAQ,MAAM,GAAG,SAAS,IAAI;AACpE;AAEO,SAAS,wBAAwB,OAAsE;AAC5G,MAAI,iBAAiB,OAAO;AAC1B,WAAO;AAAA,MACL,SAAS,GAAG,MAAM,IAAI,KAAK,MAAM,OAAO;AAAA,MACxC,OAAO,SAAS,MAAM,OAAO,gBAAgB;AAAA,MAC7C,QAAQ,SAAU,MAA+B,QAAQ,EAAE;AAAA,IAC7D;AAAA,EACF;AACA,MAAI,OAAO,UAAU,SAAU,QAAO,EAAE,SAAS,MAAM;AACvD,SAAO,EAAE,SAAS,wBAAwB;AAC5C;AAOO,SAAS,sBAAsB,OAM7B;AACP,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI,eAAe,qBAAsB;AAEzC,QAAM,QAAQ,oBAAoB;AAClC,MAAI,CAAC,MAAO;AAEZ,QAAM,YAAiE,MAAM,UAAU,SACnF,wBAAwB,MAAM,KAAK,IACnC,CAAC;AACL,QAAM,UAAU,SAAS,MAAM,WAAW,UAAU,SAAS,kBAAkB;AAC/E,MAAI,CAAC,QAAS;AAEd,QAAM,SAAS,SAAS,MAAM,UAAU,UAAU,QAAQ,EAAE;AAC5D,QAAM,QAAQ,SAAS,MAAM,SAAS,UAAU,OAAO,gBAAgB;AACvE,QAAM,OAAO,SAAS,OAAO,UAAU,UAAU,GAAG;AAIpD,QAAM,cAAc,GAAG,MAAM,IAAI,IAAI,UAAU,OAAO,IAAI,QAAQ,EAAE;AACpE,MAAI,iBAAiB,IAAI,WAAW,EAAG;AACvC,mBAAiB,IAAI,WAAW;AAChC,iBAAe;AAEf,QAAM,SAA2B;AAAA,IAC/B,MAAM,MAAM;AAAA,IACZ;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACA,MAAI,OAAQ,QAAO,SAAS;AAC5B,MAAI,MAAO,QAAO,QAAQ;AAC1B,MAAI,KAAM,QAAO,OAAO;AAExB,MAAI;AACF,SAAK,MAAM,8BAA8B;AAAA,MACvC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,CAAC,wBAAwB,GAAG,MAAM;AAAA,MACjF,MAAM,KAAK,UAAU,MAAM;AAAA,MAC3B,OAAO;AAAA,MACP,WAAW;AAAA,IACb,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnB,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,kCAAwC;AACtD,gBAAc;AACd,mBAAiB,MAAM;AACzB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { redactDevRuntimeText } from "./redaction.js";
|
|
3
|
+
import {
|
|
4
|
+
appendDevRuntimeActionRequest,
|
|
5
|
+
appendDevRuntimeReport,
|
|
6
|
+
isMatchingDevRuntimeToken,
|
|
7
|
+
readDevRuntimeLogs,
|
|
8
|
+
readDevRuntimeStatus,
|
|
9
|
+
resolveDevRuntimeServerConfig
|
|
10
|
+
} from "./server.js";
|
|
11
|
+
import {
|
|
12
|
+
DEV_RUNTIME_RECOVERY_ACTIONS,
|
|
13
|
+
DEV_RUNTIME_TOKEN_HEADER
|
|
14
|
+
} from "./types.js";
|
|
15
|
+
const MAX_DEV_RUNTIME_REPORT_BYTES = 8192;
|
|
16
|
+
const MAX_REPORTS_PER_WINDOW = 30;
|
|
17
|
+
const RATE_LIMIT_WINDOW_MS = 1e4;
|
|
18
|
+
const reportSchema = z.object({
|
|
19
|
+
kind: z.enum(["global-error", "window-error", "unhandled-rejection", "chunk-load-error", "request-error"]),
|
|
20
|
+
message: z.string().trim().min(1).max(2e3),
|
|
21
|
+
digest: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/).optional(),
|
|
22
|
+
path: z.string().max(300).optional(),
|
|
23
|
+
stack: z.string().max(2e4).optional(),
|
|
24
|
+
timestamp: z.string().datetime().optional()
|
|
25
|
+
});
|
|
26
|
+
function createRateLimiter(now = () => Date.now()) {
|
|
27
|
+
let windowStart = now();
|
|
28
|
+
let count = 0;
|
|
29
|
+
return {
|
|
30
|
+
tryConsume() {
|
|
31
|
+
const timestamp = now();
|
|
32
|
+
if (timestamp - windowStart >= RATE_LIMIT_WINDOW_MS) {
|
|
33
|
+
windowStart = timestamp;
|
|
34
|
+
count = 0;
|
|
35
|
+
}
|
|
36
|
+
if (count >= MAX_REPORTS_PER_WINDOW) return false;
|
|
37
|
+
count += 1;
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function jsonError(status, code, message) {
|
|
43
|
+
return Response.json({ error: { code, message } }, { status });
|
|
44
|
+
}
|
|
45
|
+
const NOT_FOUND = () => jsonError(404, "not_found", "Not found.");
|
|
46
|
+
function isAuthorized(request, config) {
|
|
47
|
+
return isMatchingDevRuntimeToken(config.token, request.headers.get(DEV_RUNTIME_TOKEN_HEADER));
|
|
48
|
+
}
|
|
49
|
+
function isAcceptableOrigin(request) {
|
|
50
|
+
const origin = request.headers.get("origin");
|
|
51
|
+
if (!origin) return true;
|
|
52
|
+
try {
|
|
53
|
+
return new URL(origin).host === new URL(request.url).host;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function createDevRuntimeStatusRoute(options = {}) {
|
|
59
|
+
const resolveConfig = options.resolveConfig ?? (() => resolveDevRuntimeServerConfig());
|
|
60
|
+
return async function GET(request) {
|
|
61
|
+
const config = resolveConfig();
|
|
62
|
+
if (!config.enabled) return NOT_FOUND();
|
|
63
|
+
if (!isAcceptableOrigin(request)) return jsonError(403, "forbidden", "Origin is not allowed.");
|
|
64
|
+
if (!isAuthorized(request, config)) return jsonError(403, "forbidden", "Invalid dev runtime token.");
|
|
65
|
+
const status = readDevRuntimeStatus(config);
|
|
66
|
+
if (!status) return jsonError(404, "not_found", "Runtime status is not available.");
|
|
67
|
+
return Response.json(status, { headers: { "cache-control": "no-store" } });
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function createDevRuntimeDiagnosticsRoute(options = {}) {
|
|
71
|
+
const resolveConfig = options.resolveConfig ?? (() => resolveDevRuntimeServerConfig());
|
|
72
|
+
const limiter = createRateLimiter();
|
|
73
|
+
return async function POST(request) {
|
|
74
|
+
const config = resolveConfig();
|
|
75
|
+
if (!config.enabled) return NOT_FOUND();
|
|
76
|
+
if (!isAcceptableOrigin(request)) return jsonError(403, "forbidden", "Origin is not allowed.");
|
|
77
|
+
if (!isAuthorized(request, config)) return jsonError(403, "forbidden", "Invalid dev runtime token.");
|
|
78
|
+
const contentType = request.headers.get("content-type") ?? "";
|
|
79
|
+
if (!contentType.toLowerCase().includes("application/json")) {
|
|
80
|
+
return jsonError(400, "invalid_report", "Diagnostic report must be JSON.");
|
|
81
|
+
}
|
|
82
|
+
const body = await request.text();
|
|
83
|
+
if (Buffer.byteLength(body, "utf8") > MAX_DEV_RUNTIME_REPORT_BYTES) {
|
|
84
|
+
return jsonError(400, "report_too_large", "Diagnostic report exceeds the size limit.");
|
|
85
|
+
}
|
|
86
|
+
let parsedBody;
|
|
87
|
+
try {
|
|
88
|
+
parsedBody = JSON.parse(body);
|
|
89
|
+
} catch {
|
|
90
|
+
return jsonError(400, "invalid_report", "Diagnostic report is not valid JSON.");
|
|
91
|
+
}
|
|
92
|
+
const parsed = reportSchema.safeParse(parsedBody);
|
|
93
|
+
if (!parsed.success) {
|
|
94
|
+
return jsonError(400, "invalid_report", "Diagnostic report failed validation.");
|
|
95
|
+
}
|
|
96
|
+
if (!limiter.tryConsume()) {
|
|
97
|
+
return jsonError(429, "rate_limited", "Too many diagnostic reports.");
|
|
98
|
+
}
|
|
99
|
+
const message = redactDevRuntimeText(parsed.data.message, 500);
|
|
100
|
+
if (!message) return jsonError(400, "invalid_report", "Diagnostic report message is empty after redaction.");
|
|
101
|
+
const report = { kind: parsed.data.kind, message };
|
|
102
|
+
if (parsed.data.digest) report.digest = parsed.data.digest;
|
|
103
|
+
const sanitizedPath = redactDevRuntimeText(parsed.data.path, 300);
|
|
104
|
+
if (sanitizedPath) report.path = sanitizedPath.startsWith("/") ? sanitizedPath : `/${sanitizedPath}`;
|
|
105
|
+
const sanitizedStack = redactDevRuntimeText(parsed.data.stack, 2e3);
|
|
106
|
+
if (sanitizedStack) report.stack = sanitizedStack;
|
|
107
|
+
report.timestamp = parsed.data.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
108
|
+
if (!appendDevRuntimeReport(config, report)) {
|
|
109
|
+
return jsonError(503, "collector_unavailable", "Diagnostic collector is unavailable.");
|
|
110
|
+
}
|
|
111
|
+
return Response.json({ accepted: true, issueId: `${report.kind}:${report.timestamp}` }, { status: 202 });
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createDevRuntimeActionsRoute(options = {}) {
|
|
115
|
+
const resolveConfig = options.resolveConfig ?? (() => resolveDevRuntimeServerConfig());
|
|
116
|
+
return async function POST(request, context) {
|
|
117
|
+
const config = resolveConfig();
|
|
118
|
+
if (!config.enabled) return NOT_FOUND();
|
|
119
|
+
if (!isAcceptableOrigin(request)) return jsonError(403, "forbidden", "Origin is not allowed.");
|
|
120
|
+
if (!isAuthorized(request, config)) return jsonError(403, "forbidden", "Invalid dev runtime token.");
|
|
121
|
+
const { action } = await context.params;
|
|
122
|
+
if (!DEV_RUNTIME_RECOVERY_ACTIONS.includes(action)) {
|
|
123
|
+
return jsonError(400, "unknown_action", "Unknown recovery action.");
|
|
124
|
+
}
|
|
125
|
+
const status = readDevRuntimeStatus(config);
|
|
126
|
+
if (!status) return jsonError(503, "supervisor_unavailable", "The supervisor is not available.");
|
|
127
|
+
if (status.recovery?.busy) {
|
|
128
|
+
return jsonError(409, "action_busy", `The "${status.recovery.action}" action is still running.`);
|
|
129
|
+
}
|
|
130
|
+
const requestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
131
|
+
const queued = appendDevRuntimeActionRequest(config, {
|
|
132
|
+
action,
|
|
133
|
+
generation: status.generation,
|
|
134
|
+
requestedAt
|
|
135
|
+
});
|
|
136
|
+
if (!queued) return jsonError(503, "supervisor_unavailable", "The supervisor cannot accept recovery actions.");
|
|
137
|
+
return Response.json(
|
|
138
|
+
{ accepted: true, actionId: `${status.generation}:${action}:${requestedAt}`, generation: status.generation },
|
|
139
|
+
{ status: 202 }
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function createDevRuntimeLogsRoute(options = {}) {
|
|
144
|
+
const resolveConfig = options.resolveConfig ?? (() => resolveDevRuntimeServerConfig());
|
|
145
|
+
return async function GET(request) {
|
|
146
|
+
const config = resolveConfig();
|
|
147
|
+
if (!config.enabled) return NOT_FOUND();
|
|
148
|
+
if (!isAcceptableOrigin(request)) return jsonError(403, "forbidden", "Origin is not allowed.");
|
|
149
|
+
if (!isAuthorized(request, config)) return jsonError(403, "forbidden", "Invalid dev runtime token.");
|
|
150
|
+
const raw = new URL(request.url).searchParams.get("cursor");
|
|
151
|
+
const parsed = Number.parseInt(raw ?? "", 10);
|
|
152
|
+
const snapshot = readDevRuntimeLogs(config, Number.isInteger(parsed) && parsed >= 0 ? parsed : 0);
|
|
153
|
+
if (!snapshot) return jsonError(404, "not_found", "Runtime logs are not available.");
|
|
154
|
+
return Response.json(snapshot, { headers: { "cache-control": "no-store" } });
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export {
|
|
158
|
+
MAX_DEV_RUNTIME_REPORT_BYTES,
|
|
159
|
+
createDevRuntimeActionsRoute,
|
|
160
|
+
createDevRuntimeDiagnosticsRoute,
|
|
161
|
+
createDevRuntimeLogsRoute,
|
|
162
|
+
createDevRuntimeStatusRoute
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/dev-runtime/routes.ts"],
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\n\nimport { redactDevRuntimeText } from './redaction'\nimport {\n appendDevRuntimeActionRequest,\n appendDevRuntimeReport,\n isMatchingDevRuntimeToken,\n readDevRuntimeLogs,\n readDevRuntimeStatus,\n resolveDevRuntimeServerConfig,\n type DevRuntimeServerConfig,\n} from './server'\nimport {\n DEV_RUNTIME_RECOVERY_ACTIONS,\n DEV_RUNTIME_TOKEN_HEADER,\n type DevRuntimeReport,\n type RuntimeRecoveryAction,\n} from './types'\n\nexport const MAX_DEV_RUNTIME_REPORT_BYTES = 8192\nconst MAX_REPORTS_PER_WINDOW = 30\nconst RATE_LIMIT_WINDOW_MS = 10_000\n\nconst reportSchema = z.object({\n kind: z.enum(['global-error', 'window-error', 'unhandled-rejection', 'chunk-load-error', 'request-error']),\n message: z.string().trim().min(1).max(2000),\n digest: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/).optional(),\n path: z.string().max(300).optional(),\n stack: z.string().max(20_000).optional(),\n timestamp: z.string().datetime().optional(),\n})\n\ntype RateLimiter = { tryConsume: () => boolean }\n\nfunction createRateLimiter(now: () => number = () => Date.now()): RateLimiter {\n let windowStart = now()\n let count = 0\n return {\n tryConsume() {\n const timestamp = now()\n if (timestamp - windowStart >= RATE_LIMIT_WINDOW_MS) {\n windowStart = timestamp\n count = 0\n }\n if (count >= MAX_REPORTS_PER_WINDOW) return false\n count += 1\n return true\n },\n }\n}\n\nfunction jsonError(status: number, code: string, message: string): Response {\n return Response.json({ error: { code, message } }, { status })\n}\n\nconst NOT_FOUND = () => jsonError(404, 'not_found', 'Not found.')\n\nfunction isAuthorized(request: Request, config: DevRuntimeServerConfig): boolean {\n return isMatchingDevRuntimeToken(config.token, request.headers.get(DEV_RUNTIME_TOKEN_HEADER))\n}\n\n// A browser sends `Origin` on cross-origin requests; anything that does not\n// match the request's own host is rejected outright. Non-browser callers that\n// omit `Origin` still have to present the token.\nfunction isAcceptableOrigin(request: Request): boolean {\n const origin = request.headers.get('origin')\n if (!origin) return true\n try {\n return new URL(origin).host === new URL(request.url).host\n } catch {\n return false\n }\n}\n\nexport type DevRuntimeRouteOptions = {\n resolveConfig?: () => DevRuntimeServerConfig\n}\n\n/**\n * Dev-only status bridge for the in-app banner. It exposes only the\n * supervisor's local runtime state and returns 404 whenever diagnostics are off\n * or the supervisor is not running.\n */\nexport function createDevRuntimeStatusRoute(options: DevRuntimeRouteOptions = {}) {\n const resolveConfig = options.resolveConfig ?? (() => resolveDevRuntimeServerConfig())\n\n return async function GET(request: Request): Promise<Response> {\n const config = resolveConfig()\n if (!config.enabled) return NOT_FOUND()\n if (!isAcceptableOrigin(request)) return jsonError(403, 'forbidden', 'Origin is not allowed.')\n if (!isAuthorized(request, config)) return jsonError(403, 'forbidden', 'Invalid dev runtime token.')\n\n const status = readDevRuntimeStatus(config)\n if (!status) return jsonError(404, 'not_found', 'Runtime status is not available.')\n\n return Response.json(status, { headers: { 'cache-control': 'no-store' } })\n }\n}\n\nexport function createDevRuntimeDiagnosticsRoute(options: DevRuntimeRouteOptions = {}) {\n const resolveConfig = options.resolveConfig ?? (() => resolveDevRuntimeServerConfig())\n const limiter = createRateLimiter()\n\n return async function POST(request: Request): Promise<Response> {\n const config = resolveConfig()\n if (!config.enabled) return NOT_FOUND()\n if (!isAcceptableOrigin(request)) return jsonError(403, 'forbidden', 'Origin is not allowed.')\n if (!isAuthorized(request, config)) return jsonError(403, 'forbidden', 'Invalid dev runtime token.')\n\n const contentType = request.headers.get('content-type') ?? ''\n if (!contentType.toLowerCase().includes('application/json')) {\n return jsonError(400, 'invalid_report', 'Diagnostic report must be JSON.')\n }\n\n const body = await request.text()\n if (Buffer.byteLength(body, 'utf8') > MAX_DEV_RUNTIME_REPORT_BYTES) {\n return jsonError(400, 'report_too_large', 'Diagnostic report exceeds the size limit.')\n }\n\n let parsedBody: unknown\n try {\n parsedBody = JSON.parse(body)\n } catch {\n return jsonError(400, 'invalid_report', 'Diagnostic report is not valid JSON.')\n }\n\n const parsed = reportSchema.safeParse(parsedBody)\n if (!parsed.success) {\n return jsonError(400, 'invalid_report', 'Diagnostic report failed validation.')\n }\n\n if (!limiter.tryConsume()) {\n return jsonError(429, 'rate_limited', 'Too many diagnostic reports.')\n }\n\n const message = redactDevRuntimeText(parsed.data.message, 500)\n if (!message) return jsonError(400, 'invalid_report', 'Diagnostic report message is empty after redaction.')\n\n const report: DevRuntimeReport = { kind: parsed.data.kind, message }\n if (parsed.data.digest) report.digest = parsed.data.digest\n const sanitizedPath = redactDevRuntimeText(parsed.data.path, 300)\n if (sanitizedPath) report.path = sanitizedPath.startsWith('/') ? sanitizedPath : `/${sanitizedPath}`\n const sanitizedStack = redactDevRuntimeText(parsed.data.stack, 2000)\n if (sanitizedStack) report.stack = sanitizedStack\n report.timestamp = parsed.data.timestamp ?? new Date().toISOString()\n\n if (!appendDevRuntimeReport(config, report)) {\n return jsonError(503, 'collector_unavailable', 'Diagnostic collector is unavailable.')\n }\n\n return Response.json({ accepted: true, issueId: `${report.kind}:${report.timestamp}` }, { status: 202 })\n }\n}\n\n/**\n * Dev-only recovery bridge for the in-app banner. The action is matched against\n * the fixed allowlist and queued for the supervisor, which owns the actual\n * lifecycle step \u2014 this route never spawns a process itself.\n */\nexport function createDevRuntimeActionsRoute(options: DevRuntimeRouteOptions = {}) {\n const resolveConfig = options.resolveConfig ?? (() => resolveDevRuntimeServerConfig())\n\n return async function POST(request: Request, context: { params: Promise<{ action: string }> }): Promise<Response> {\n const config = resolveConfig()\n if (!config.enabled) return NOT_FOUND()\n if (!isAcceptableOrigin(request)) return jsonError(403, 'forbidden', 'Origin is not allowed.')\n if (!isAuthorized(request, config)) return jsonError(403, 'forbidden', 'Invalid dev runtime token.')\n\n const { action } = await context.params\n if (!DEV_RUNTIME_RECOVERY_ACTIONS.includes(action as RuntimeRecoveryAction)) {\n return jsonError(400, 'unknown_action', 'Unknown recovery action.')\n }\n\n const status = readDevRuntimeStatus(config)\n if (!status) return jsonError(503, 'supervisor_unavailable', 'The supervisor is not available.')\n // Serialization is enforced again by the runner; rejecting here just gives\n // the banner an immediate, accurate answer instead of a silent queue.\n if (status.recovery?.busy) {\n return jsonError(409, 'action_busy', `The \"${status.recovery.action}\" action is still running.`)\n }\n\n const requestedAt = new Date().toISOString()\n const queued = appendDevRuntimeActionRequest(config, {\n action: action as RuntimeRecoveryAction,\n generation: status.generation,\n requestedAt,\n })\n if (!queued) return jsonError(503, 'supervisor_unavailable', 'The supervisor cannot accept recovery actions.')\n\n return Response.json(\n { accepted: true, actionId: `${status.generation}:${action}:${requestedAt}`, generation: status.generation },\n { status: 202 },\n )\n }\n}\n\n/**\n * Dev-only bounded log tail for the in-app logs view. Serving it from the app\n * keeps the developer on the page they are debugging instead of bouncing them\n * to the standalone splash on another port.\n */\nexport function createDevRuntimeLogsRoute(options: DevRuntimeRouteOptions = {}) {\n const resolveConfig = options.resolveConfig ?? (() => resolveDevRuntimeServerConfig())\n\n return async function GET(request: Request): Promise<Response> {\n const config = resolveConfig()\n if (!config.enabled) return NOT_FOUND()\n if (!isAcceptableOrigin(request)) return jsonError(403, 'forbidden', 'Origin is not allowed.')\n if (!isAuthorized(request, config)) return jsonError(403, 'forbidden', 'Invalid dev runtime token.')\n\n // A malformed cursor restarts the snapshot rather than failing the view.\n const raw = new URL(request.url).searchParams.get('cursor')\n const parsed = Number.parseInt(raw ?? '', 10)\n const snapshot = readDevRuntimeLogs(config, Number.isInteger(parsed) && parsed >= 0 ? parsed : 0)\n if (!snapshot) return jsonError(404, 'not_found', 'Runtime logs are not available.')\n\n return Response.json(snapshot, { headers: { 'cache-control': 'no-store' } })\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { appendFileSync, readFileSync } from "node:fs";
|
|
2
|
+
import { timingSafeEqual } from "node:crypto";
|
|
3
|
+
const DISABLED_CONFIG = {
|
|
4
|
+
enabled: false,
|
|
5
|
+
bannerEnabled: false,
|
|
6
|
+
token: null,
|
|
7
|
+
statusFilePath: null,
|
|
8
|
+
diagnosticsFilePath: null,
|
|
9
|
+
actionsFilePath: null,
|
|
10
|
+
logsFilePath: null
|
|
11
|
+
};
|
|
12
|
+
function readFlag(value, fallback) {
|
|
13
|
+
if (typeof value !== "string" || value.trim() === "") return fallback;
|
|
14
|
+
const normalized = value.trim().toLowerCase();
|
|
15
|
+
if (["1", "true", "on", "yes", "enabled"].includes(normalized)) return true;
|
|
16
|
+
if (["0", "false", "off", "no", "disabled"].includes(normalized)) return false;
|
|
17
|
+
return fallback;
|
|
18
|
+
}
|
|
19
|
+
function readNonEmpty(value) {
|
|
20
|
+
if (typeof value !== "string") return null;
|
|
21
|
+
const trimmed = value.trim();
|
|
22
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
23
|
+
}
|
|
24
|
+
function resolveDevRuntimeServerConfig(env = process.env) {
|
|
25
|
+
if (!readFlag(env.OM_DEV_RUNTIME_DIAGNOSTICS, false)) return DISABLED_CONFIG;
|
|
26
|
+
const token = readNonEmpty(env.OM_DEV_RUNTIME_TOKEN);
|
|
27
|
+
const statusFilePath = readNonEmpty(env.OM_DEV_RUNTIME_STATUS_FILE);
|
|
28
|
+
const diagnosticsFilePath = readNonEmpty(env.OM_DEV_RUNTIME_DIAGNOSTICS_FILE);
|
|
29
|
+
if (!token || !statusFilePath || !diagnosticsFilePath) return DISABLED_CONFIG;
|
|
30
|
+
return {
|
|
31
|
+
enabled: true,
|
|
32
|
+
bannerEnabled: readFlag(env.OM_DEV_RUNTIME_BANNER, true),
|
|
33
|
+
token,
|
|
34
|
+
statusFilePath,
|
|
35
|
+
diagnosticsFilePath,
|
|
36
|
+
// Absent when the supervisor predates the action channel; the actions route
|
|
37
|
+
// then reports itself unavailable rather than silently dropping requests.
|
|
38
|
+
actionsFilePath: readNonEmpty(env.OM_DEV_RUNTIME_ACTIONS_FILE),
|
|
39
|
+
logsFilePath: readNonEmpty(env.OM_DEV_RUNTIME_LOGS_FILE)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function isMatchingDevRuntimeToken(expected, provided) {
|
|
43
|
+
if (!expected || !provided) return false;
|
|
44
|
+
const expectedBuffer = Buffer.from(expected);
|
|
45
|
+
const providedBuffer = Buffer.from(provided);
|
|
46
|
+
if (expectedBuffer.length !== providedBuffer.length) return false;
|
|
47
|
+
return timingSafeEqual(expectedBuffer, providedBuffer);
|
|
48
|
+
}
|
|
49
|
+
function isRuntimeStatus(value) {
|
|
50
|
+
if (!value || typeof value !== "object") return false;
|
|
51
|
+
const candidate = value;
|
|
52
|
+
return typeof candidate.health === "string" && typeof candidate.generation === "number" && Array.isArray(candidate.incidents);
|
|
53
|
+
}
|
|
54
|
+
function readDevRuntimeStatus(config) {
|
|
55
|
+
if (!config.enabled || !config.statusFilePath) return null;
|
|
56
|
+
let parsed;
|
|
57
|
+
try {
|
|
58
|
+
parsed = JSON.parse(readFileSync(config.statusFilePath, "utf8"));
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (typeof parsed.token !== "string" || !isMatchingDevRuntimeToken(config.token, parsed.token)) return null;
|
|
63
|
+
return isRuntimeStatus(parsed.status) ? parsed.status : null;
|
|
64
|
+
}
|
|
65
|
+
function appendDevRuntimeReport(config, report) {
|
|
66
|
+
if (!config.enabled || !config.diagnosticsFilePath) return false;
|
|
67
|
+
try {
|
|
68
|
+
appendFileSync(config.diagnosticsFilePath, `${JSON.stringify(report)}
|
|
69
|
+
`, "utf8");
|
|
70
|
+
return true;
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function appendDevRuntimeActionRequest(config, request) {
|
|
76
|
+
if (!config.enabled || !config.actionsFilePath) return false;
|
|
77
|
+
try {
|
|
78
|
+
appendFileSync(config.actionsFilePath, `${JSON.stringify(request)}
|
|
79
|
+
`, "utf8");
|
|
80
|
+
return true;
|
|
81
|
+
} catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function readDevRuntimeLogs(config, cursor = 0) {
|
|
86
|
+
if (!config.enabled || !config.logsFilePath) return null;
|
|
87
|
+
let parsed;
|
|
88
|
+
try {
|
|
89
|
+
parsed = JSON.parse(readFileSync(config.logsFilePath, "utf8"));
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (typeof parsed.token !== "string" || !isMatchingDevRuntimeToken(config.token, parsed.token)) return null;
|
|
94
|
+
if (!Array.isArray(parsed.lines)) return null;
|
|
95
|
+
const from = Number.isInteger(cursor) && cursor >= 0 ? cursor : 0;
|
|
96
|
+
const lines = parsed.lines.filter((line) => line && typeof line === "object" && typeof line.text === "string" && Number(line.seq) > from);
|
|
97
|
+
return {
|
|
98
|
+
generation: typeof parsed.generation === "number" ? parsed.generation : 0,
|
|
99
|
+
lines,
|
|
100
|
+
nextCursor: lines.length > 0 ? Number(lines[lines.length - 1].seq) : from
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
appendDevRuntimeActionRequest,
|
|
105
|
+
appendDevRuntimeReport,
|
|
106
|
+
isMatchingDevRuntimeToken,
|
|
107
|
+
readDevRuntimeLogs,
|
|
108
|
+
readDevRuntimeStatus,
|
|
109
|
+
resolveDevRuntimeServerConfig
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/dev-runtime/server.ts"],
|
|
4
|
+
"sourcesContent": ["import { appendFileSync, readFileSync } from 'node:fs'\nimport { timingSafeEqual } from 'node:crypto'\n\nimport type {\n DevRuntimeLogLine,\n DevRuntimeLogSnapshot,\n DevRuntimeReport,\n DevRuntimeRecoveryAction,\n RuntimeStatus,\n} from './types'\n\nexport type DevRuntimeServerConfig = {\n enabled: boolean\n bannerEnabled: boolean\n token: string | null\n statusFilePath: string | null\n diagnosticsFilePath: string | null\n actionsFilePath: string | null\n logsFilePath: string | null\n}\n\nconst DISABLED_CONFIG: DevRuntimeServerConfig = {\n enabled: false,\n bannerEnabled: false,\n token: null,\n statusFilePath: null,\n diagnosticsFilePath: null,\n actionsFilePath: null,\n logsFilePath: null,\n}\n\nfunction readFlag(value: string | undefined, fallback: boolean): boolean {\n if (typeof value !== 'string' || value.trim() === '') return fallback\n const normalized = value.trim().toLowerCase()\n if (['1', 'true', 'on', 'yes', 'enabled'].includes(normalized)) return true\n if (['0', 'false', 'off', 'no', 'disabled'].includes(normalized)) return false\n return fallback\n}\n\nfunction readNonEmpty(value: string | undefined): string | null {\n if (typeof value !== 'string') return null\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\n/**\n * Dev diagnostics exist only while a local `yarn dev` supervisor is managing\n * this process.\n *\n * NODE_ENV is deliberately NOT the guard: `mercato dev` spawns the Next.js dev\n * server through `buildServerProcessEnvironment`, which forces\n * `NODE_ENV=production` (the same helper already forces the logging facade to\n * re-apply its dev defaults by hand). Keying off it would disable diagnostics\n * in exactly the environment they exist for.\n *\n * The real discriminator is the supervisor handshake: the per-run token and the\n * two state-file paths are injected by `createDevRuntimeSupervisor().childEnv()`\n * and exist nowhere else. A deployed `mercato server` has no supervisor, so it\n * has none of them and every route stays closed \u2014 even if someone sets\n * `OM_DEV_RUNTIME_DIAGNOSTICS` by hand.\n */\nexport function resolveDevRuntimeServerConfig(env: NodeJS.ProcessEnv = process.env): DevRuntimeServerConfig {\n if (!readFlag(env.OM_DEV_RUNTIME_DIAGNOSTICS, false)) return DISABLED_CONFIG\n\n const token = readNonEmpty(env.OM_DEV_RUNTIME_TOKEN)\n const statusFilePath = readNonEmpty(env.OM_DEV_RUNTIME_STATUS_FILE)\n const diagnosticsFilePath = readNonEmpty(env.OM_DEV_RUNTIME_DIAGNOSTICS_FILE)\n if (!token || !statusFilePath || !diagnosticsFilePath) return DISABLED_CONFIG\n\n return {\n enabled: true,\n bannerEnabled: readFlag(env.OM_DEV_RUNTIME_BANNER, true),\n token,\n statusFilePath,\n diagnosticsFilePath,\n // Absent when the supervisor predates the action channel; the actions route\n // then reports itself unavailable rather than silently dropping requests.\n actionsFilePath: readNonEmpty(env.OM_DEV_RUNTIME_ACTIONS_FILE),\n logsFilePath: readNonEmpty(env.OM_DEV_RUNTIME_LOGS_FILE),\n }\n}\n\nexport function isMatchingDevRuntimeToken(expected: string | null, provided: string | null): boolean {\n if (!expected || !provided) return false\n const expectedBuffer = Buffer.from(expected)\n const providedBuffer = Buffer.from(provided)\n if (expectedBuffer.length !== providedBuffer.length) return false\n return timingSafeEqual(expectedBuffer, providedBuffer)\n}\n\ntype SupervisorStatusFile = {\n token?: unknown\n pid?: unknown\n status?: unknown\n}\n\nfunction isRuntimeStatus(value: unknown): value is RuntimeStatus {\n if (!value || typeof value !== 'object') return false\n const candidate = value as Partial<RuntimeStatus>\n return typeof candidate.health === 'string'\n && typeof candidate.generation === 'number'\n && Array.isArray(candidate.incidents)\n}\n\n/**\n * Reads the supervisor-owned status file. The route never derives status from\n * application state, so it cannot leak module, tenant, or organization data.\n */\nexport function readDevRuntimeStatus(config: DevRuntimeServerConfig): RuntimeStatus | null {\n if (!config.enabled || !config.statusFilePath) return null\n let parsed: SupervisorStatusFile\n try {\n parsed = JSON.parse(readFileSync(config.statusFilePath, 'utf8')) as SupervisorStatusFile\n } catch {\n return null\n }\n // A status file written by a different run must not be served: its token is\n // stale and its generation would confuse the banner.\n if (typeof parsed.token !== 'string' || !isMatchingDevRuntimeToken(config.token, parsed.token)) return null\n return isRuntimeStatus(parsed.status) ? parsed.status : null\n}\n\nexport function appendDevRuntimeReport(config: DevRuntimeServerConfig, report: DevRuntimeReport): boolean {\n if (!config.enabled || !config.diagnosticsFilePath) return false\n try {\n appendFileSync(config.diagnosticsFilePath, `${JSON.stringify(report)}\\n`, 'utf8')\n return true\n } catch {\n return false\n }\n}\n\nexport function appendDevRuntimeActionRequest(\n config: DevRuntimeServerConfig,\n request: { action: DevRuntimeRecoveryAction; generation?: number; requestedAt: string },\n): boolean {\n if (!config.enabled || !config.actionsFilePath) return false\n try {\n appendFileSync(config.actionsFilePath, `${JSON.stringify(request)}\\n`, 'utf8')\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Reads the supervisor-published log tail. Lines are already bounded and\n * redacted by the collector; this only filters by cursor so the logs view can\n * poll incrementally.\n */\nexport function readDevRuntimeLogs(\n config: DevRuntimeServerConfig,\n cursor = 0,\n): DevRuntimeLogSnapshot | null {\n if (!config.enabled || !config.logsFilePath) return null\n let parsed: { token?: unknown; generation?: unknown; lines?: unknown }\n try {\n parsed = JSON.parse(readFileSync(config.logsFilePath, 'utf8'))\n } catch {\n return null\n }\n if (typeof parsed.token !== 'string' || !isMatchingDevRuntimeToken(config.token, parsed.token)) return null\n if (!Array.isArray(parsed.lines)) return null\n\n const from = Number.isInteger(cursor) && cursor >= 0 ? cursor : 0\n const lines = (parsed.lines as DevRuntimeLogLine[]).filter((line) => (\n line && typeof line === 'object' && typeof line.text === 'string' && Number(line.seq) > from\n ))\n return {\n generation: typeof parsed.generation === 'number' ? parsed.generation : 0,\n lines,\n nextCursor: lines.length > 0 ? Number(lines[lines.length - 1].seq) : from,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const RUNTIME_STATUS_SCHEMA_VERSION = 1;
|
|
2
|
+
const DEV_RUNTIME_RECOVERY_ACTIONS = ["generate", "migrate", "restart"];
|
|
3
|
+
const DEV_RUNTIME_TOKEN_HEADER = "x-om-dev-runtime-token";
|
|
4
|
+
const DEV_RUNTIME_TOKEN_META_NAME = "om-dev-runtime-token";
|
|
5
|
+
const DEV_RUNTIME_BANNER_META_NAME = "om-dev-runtime-banner";
|
|
6
|
+
const DEV_RUNTIME_LOGS_URL_META_NAME = "om-dev-runtime-logs-url";
|
|
7
|
+
const DEV_RUNTIME_STATUS_PATH = "/api/dev-runtime/status";
|
|
8
|
+
const DEV_RUNTIME_DIAGNOSTICS_PATH = "/api/dev-runtime/diagnostics";
|
|
9
|
+
const DEV_RUNTIME_ACTIONS_PATH = "/api/dev-runtime/actions";
|
|
10
|
+
const DEV_RUNTIME_LOGS_PATH = "/api/dev-runtime/logs";
|
|
11
|
+
export {
|
|
12
|
+
DEV_RUNTIME_ACTIONS_PATH,
|
|
13
|
+
DEV_RUNTIME_BANNER_META_NAME,
|
|
14
|
+
DEV_RUNTIME_DIAGNOSTICS_PATH,
|
|
15
|
+
DEV_RUNTIME_LOGS_PATH,
|
|
16
|
+
DEV_RUNTIME_LOGS_URL_META_NAME,
|
|
17
|
+
DEV_RUNTIME_RECOVERY_ACTIONS,
|
|
18
|
+
DEV_RUNTIME_STATUS_PATH,
|
|
19
|
+
DEV_RUNTIME_TOKEN_HEADER,
|
|
20
|
+
DEV_RUNTIME_TOKEN_META_NAME,
|
|
21
|
+
RUNTIME_STATUS_SCHEMA_VERSION
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/dev-runtime/types.ts"],
|
|
4
|
+
"sourcesContent": ["export const RUNTIME_STATUS_SCHEMA_VERSION = 1 as const\n\nexport type RuntimeHealth =\n | 'starting'\n | 'ready'\n | 'degraded'\n | 'recovering'\n | 'unavailable'\n\nexport type RuntimeIssueSource = 'process' | 'log' | 'warmup' | 'probe' | 'browser'\nexport type RuntimeIssueSeverity = 'warning' | 'error'\nexport type RuntimeRecoveryAction = 'generate' | 'migrate' | 'restart'\nexport type DevRuntimeRecoveryAction = RuntimeRecoveryAction\nexport const DEV_RUNTIME_RECOVERY_ACTIONS: RuntimeRecoveryAction[] = ['generate', 'migrate', 'restart']\n\nexport type RuntimeIssue = {\n id: string\n fingerprint: string\n code: string\n source: RuntimeIssueSource\n severity: RuntimeIssueSeverity\n title: string\n detail?: string\n firstSeenAt: string\n lastSeenAt: string\n occurrences: number\n generation: number\n path?: string\n digest?: string\n recovery?: RuntimeRecoveryAction\n}\n\nexport type RuntimeStatus = {\n schemaVersion: typeof RUNTIME_STATUS_SCHEMA_VERSION\n generation: number\n health: RuntimeHealth\n ready: boolean\n failed: boolean\n updatedAt: string\n upstream: {\n configuredPort: number\n actualPort?: number\n publicUrl: string\n }\n issueSummary?: RuntimeIssue\n incidents: RuntimeIssue[]\n recovery?: {\n action: RuntimeRecoveryAction\n startedAt: string\n busy: boolean\n lastExitCode?: number\n }\n legacy: {\n failureLines: string[]\n failureCommand?: string\n failureStage?: string\n }\n}\n\nexport type DevRuntimeReportKind =\n | 'global-error'\n | 'window-error'\n | 'unhandled-rejection'\n | 'chunk-load-error'\n | 'request-error'\n\nexport type DevRuntimeReport = {\n kind: DevRuntimeReportKind\n message: string\n digest?: string\n path?: string\n stack?: string\n timestamp?: string\n}\n\nexport const DEV_RUNTIME_TOKEN_HEADER = 'x-om-dev-runtime-token'\nexport const DEV_RUNTIME_TOKEN_META_NAME = 'om-dev-runtime-token'\nexport const DEV_RUNTIME_BANNER_META_NAME = 'om-dev-runtime-banner'\nexport const DEV_RUNTIME_LOGS_URL_META_NAME = 'om-dev-runtime-logs-url'\n// Kebab-case on purpose: module ids are snake_case, so this segment can never\n// collide with a module's `/api/<module_id>/...` routes. A leading-underscore\n// path is not an option \u2014 Next.js treats `_folder` as a private folder and drops\n// it from the route tree entirely.\nexport const DEV_RUNTIME_STATUS_PATH = '/api/dev-runtime/status'\nexport const DEV_RUNTIME_DIAGNOSTICS_PATH = '/api/dev-runtime/diagnostics'\nexport const DEV_RUNTIME_ACTIONS_PATH = '/api/dev-runtime/actions'\nexport const DEV_RUNTIME_LOGS_PATH = '/api/dev-runtime/logs'\n\nexport type DevRuntimeLogLine = {\n seq: number\n at: string\n generation: number\n source: string\n text: string\n}\n\nexport type DevRuntimeLogSnapshot = {\n generation: number\n lines: DevRuntimeLogLine[]\n nextCursor: number\n}\n"],
|
|
5
|
+
"mappings": "AAAO,MAAM,gCAAgC;AAatC,MAAM,+BAAwD,CAAC,YAAY,WAAW,SAAS;AA8D/F,MAAM,2BAA2B;AACjC,MAAM,8BAA8B;AACpC,MAAM,+BAA+B;AACrC,MAAM,iCAAiC;AAKvC,MAAM,0BAA0B;AAChC,MAAM,+BAA+B;AACrC,MAAM,2BAA2B;AACjC,MAAM,wBAAwB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/lib/version.js
CHANGED
package/dist/lib/version.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/version.ts"],
|
|
4
|
-
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.7.1-develop.
|
|
4
|
+
"sourcesContent": ["// Build-time generated version\nexport const APP_VERSION = '0.7.1-develop.7153.1.7145d295e6';\nexport const appVersion = APP_VERSION;\n"],
|
|
5
5
|
"mappings": "AACO,MAAM,cAAc;AACpB,MAAM,aAAa;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/shared",
|
|
3
|
-
"version": "0.7.1-develop.
|
|
3
|
+
"version": "0.7.1-develop.7153.1.7145d295e6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"@mikro-orm/core": "^7.1.8",
|
|
114
114
|
"@mikro-orm/decorators": "^7.1.8",
|
|
115
115
|
"@mikro-orm/postgresql": "^7.1.8",
|
|
116
|
-
"@open-mercato/cache": "0.7.1-develop.
|
|
116
|
+
"@open-mercato/cache": "0.7.1-develop.7153.1.7145d295e6",
|
|
117
117
|
"@types/html-to-text": "^9.0.4",
|
|
118
118
|
"@types/sanitize-html": "^2.16.1",
|
|
119
119
|
"dotenv": "^17.4.2",
|