@astroscope/node 1.2.2 → 1.4.0
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/README.md +21 -0
- package/dist/boot.d.ts +0 -1
- package/dist/boot.d.ts.map +1 -1
- package/dist/{construct-DgB-jR0a.d.ts → construct-BGlPfWMF.d.ts} +1 -2
- package/dist/construct-BGlPfWMF.d.ts.map +1 -0
- package/dist/csrf-middleware-entrypoint.d.ts.map +1 -1
- package/dist/dev-middleware-entrypoint.d.ts +0 -1
- package/dist/dev-middleware-entrypoint.d.ts.map +1 -1
- package/dist/{events-CUzQ2_cp.d.ts → events-u7J3ezJR.d.ts} +1 -2
- package/dist/events-u7J3ezJR.d.ts.map +1 -0
- package/dist/{excludes-DLF3A_Cf.d.ts → excludes-BDiE3eyp.d.ts} +1 -2
- package/dist/excludes-BDiE3eyp.d.ts.map +1 -0
- package/dist/excludes.d.ts +1 -1
- package/dist/health.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +170 -2
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/events.d.ts +1 -1
- package/dist/log/index.d.ts +33 -3
- package/dist/log/index.d.ts.map +1 -1
- package/dist/log/index.js +3 -2
- package/dist/{log-CSJlKaxY.js → log-B69HEBvg.js} +2 -2
- package/dist/{log-CSJlKaxY.js.map → log-B69HEBvg.js.map} +1 -1
- package/dist/{native-mount-hhwWdLtL.js → native-mount-DjYEnO4X.js} +4 -13
- package/dist/native-mount-DjYEnO4X.js.map +1 -0
- package/dist/native.d.ts +0 -1
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +1 -1
- package/dist/{prepare-DQEf2Bnt.js → prepare-CXZsyAVk.js} +6 -4
- package/dist/prepare-CXZsyAVk.js.map +1 -0
- package/dist/preview.d.ts +0 -1
- package/dist/preview.d.ts.map +1 -1
- package/dist/request-route-DcnZOOM4.js +92 -0
- package/dist/request-route-DcnZOOM4.js.map +1 -0
- package/dist/route-middleware-entrypoint.d.ts +3 -2
- package/dist/route-middleware-entrypoint.d.ts.map +1 -1
- package/dist/route-middleware-entrypoint.js +5 -11
- package/dist/route-middleware-entrypoint.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +23 -8
- package/dist/server.js.map +1 -1
- package/dist/types-D0uMBi2M.d.ts.map +1 -1
- package/package.json +8 -7
- package/dist/construct-DgB-jR0a.d.ts.map +0 -1
- package/dist/events-CUzQ2_cp.d.ts.map +0 -1
- package/dist/excludes-DLF3A_Cf.d.ts.map +0 -1
- package/dist/native-mount-hhwWdLtL.js.map +0 -1
- package/dist/prepare-DQEf2Bnt.js.map +0 -1
- package/dist/store-BIUF4lqk.js +0 -29
- package/dist/store-BIUF4lqk.js.map +0 -1
package/dist/preview.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.d.ts","names":[],"sources":["../src/server/preview.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"preview.d.ts","names":[],"sources":["../src/server/preview.ts"],"mappings":";;;;;;;cAQM,qBAAqB"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { trace } from "@opentelemetry/api";
|
|
3
|
+
//#region src/observability/log/store.ts
|
|
4
|
+
/**
|
|
5
|
+
* Shared state between the public `log` proxy and the server runtime. Keyed on
|
|
6
|
+
* `globalThis` because the two sides may live in different module instances
|
|
7
|
+
* (bundled app vs vite module runner in dev).
|
|
8
|
+
*/
|
|
9
|
+
const STORE_KEY = Symbol.for("@astroscope/node/log");
|
|
10
|
+
function getLogStore() {
|
|
11
|
+
const g = globalThis;
|
|
12
|
+
let store = g[STORE_KEY];
|
|
13
|
+
if (!store) {
|
|
14
|
+
store = {
|
|
15
|
+
root: void 0,
|
|
16
|
+
requestStorage: new AsyncLocalStorage(),
|
|
17
|
+
buffer: [],
|
|
18
|
+
dropped: 0
|
|
19
|
+
};
|
|
20
|
+
g[STORE_KEY] = store;
|
|
21
|
+
}
|
|
22
|
+
return store;
|
|
23
|
+
}
|
|
24
|
+
function getRequestRecord() {
|
|
25
|
+
return getLogStore().requestStorage.getStore();
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/observability/request-route.ts
|
|
29
|
+
/**
|
|
30
|
+
* Apply a route label to the request record (final log line, request duration
|
|
31
|
+
* metric) and the active server span.
|
|
32
|
+
*
|
|
33
|
+
* Last write wins — astro's routing stamps every pass, so a rewrite lands the
|
|
34
|
+
* route that rendered. An override comes from the middleware that knows what it
|
|
35
|
+
* served and outranks routing, whatever the middleware order.
|
|
36
|
+
*/
|
|
37
|
+
function applyRoute(route, method, override) {
|
|
38
|
+
const record = getRequestRecord();
|
|
39
|
+
if (record) {
|
|
40
|
+
if (!override && record.routeOverride) return;
|
|
41
|
+
record.route = route;
|
|
42
|
+
if (override) record.routeOverride = true;
|
|
43
|
+
}
|
|
44
|
+
const span = trace.getActiveSpan();
|
|
45
|
+
if (span?.isRecording()) {
|
|
46
|
+
span.setAttribute("http.route", route);
|
|
47
|
+
if (!record?.actionName) span.updateName(`${method} ${route}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Report the route that actually served the current request, overriding the one
|
|
52
|
+
* astro's routing matched.
|
|
53
|
+
*
|
|
54
|
+
* A middleware that rewrites (`next(url)`) or answers with its own response
|
|
55
|
+
* serves a request astro has no page for, so routing matches `/404` and that is
|
|
56
|
+
* what the request is logged and measured as — every such request collapsing
|
|
57
|
+
* into one `/404` bucket in the request metrics. Calling this corrects the log
|
|
58
|
+
* line, the metric and the server span name together.
|
|
59
|
+
*
|
|
60
|
+
* Pass a templated label rather than a concrete path, so metric cardinality
|
|
61
|
+
* stays bounded. No-op outside instrumented requests.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { overrideRequestRoute } from '@astroscope/node/log';
|
|
66
|
+
*
|
|
67
|
+
* export const onRequest: MiddlewareHandler = (ctx, next) => {
|
|
68
|
+
* const page = lookupPage(ctx.url.pathname);
|
|
69
|
+
*
|
|
70
|
+
* if (!page) return next();
|
|
71
|
+
*
|
|
72
|
+
* overrideRequestRoute('/cms/pages/[id]');
|
|
73
|
+
*
|
|
74
|
+
* return next(`/cms/pages/${page.id}`);
|
|
75
|
+
* };
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
function overrideRequestRoute(route) {
|
|
79
|
+
applyRoute(route, getRequestRecord()?.method ?? "GET", true);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Stamp the route astro's routing matched, unless it was overridden by the
|
|
83
|
+
* middleware that served the request.
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
function setRequestRoute(route, method) {
|
|
87
|
+
applyRoute(route, method, false);
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { getRequestRecord as i, setRequestRoute as n, getLogStore as r, overrideRequestRoute as t };
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=request-route-DcnZOOM4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-route-DcnZOOM4.js","names":[],"sources":["../src/observability/log/store.ts","../src/observability/request-route.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Logger } from 'pino';\n\n/**\n * Shared state between the public `log` proxy and the server runtime. Keyed on\n * `globalThis` because the two sides may live in different module instances\n * (bundled app vs vite module runner in dev).\n */\n\nconst STORE_KEY = Symbol.for('@astroscope/node/log');\n\nexport const EARLY_LOG_BUFFER_CAP = 100;\n\nexport interface BufferedEntry {\n level: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n bindings: Record<string, unknown>[];\n args: unknown[];\n time: number;\n}\n\nexport interface RequestRecord {\n logger: Logger | undefined;\n url: string;\n method: string;\n route: string | undefined;\n routeOverride: boolean;\n actionName: string | undefined;\n}\n\nexport interface LogStore {\n root: Logger | undefined;\n requestStorage: AsyncLocalStorage<RequestRecord>;\n buffer: BufferedEntry[];\n dropped: number;\n}\n\nexport function getLogStore(): LogStore {\n const g = globalThis as Record<symbol, unknown>;\n let store = g[STORE_KEY] as LogStore | undefined;\n\n if (!store) {\n store = { root: undefined, requestStorage: new AsyncLocalStorage(), buffer: [], dropped: 0 };\n g[STORE_KEY] = store;\n }\n\n return store;\n}\n\nexport function getRequestRecord(): RequestRecord | undefined {\n return getLogStore().requestStorage.getStore();\n}\n","import { trace } from '@opentelemetry/api';\nimport { getRequestRecord } from './log/store.js';\n\n/**\n * Apply a route label to the request record (final log line, request duration\n * metric) and the active server span.\n *\n * Last write wins — astro's routing stamps every pass, so a rewrite lands the\n * route that rendered. An override comes from the middleware that knows what it\n * served and outranks routing, whatever the middleware order.\n */\nfunction applyRoute(route: string, method: string, override: boolean): void {\n const record = getRequestRecord();\n\n if (record) {\n if (!override && record.routeOverride) return;\n\n record.route = route;\n\n if (override) {\n record.routeOverride = true;\n }\n }\n\n const span = trace.getActiveSpan();\n\n if (span?.isRecording()) {\n span.setAttribute('http.route', route);\n\n if (!record?.actionName) {\n span.updateName(`${method} ${route}`);\n }\n }\n}\n\n/**\n * Report the route that actually served the current request, overriding the one\n * astro's routing matched.\n *\n * A middleware that rewrites (`next(url)`) or answers with its own response\n * serves a request astro has no page for, so routing matches `/404` and that is\n * what the request is logged and measured as — every such request collapsing\n * into one `/404` bucket in the request metrics. Calling this corrects the log\n * line, the metric and the server span name together.\n *\n * Pass a templated label rather than a concrete path, so metric cardinality\n * stays bounded. No-op outside instrumented requests.\n *\n * @example\n * ```ts\n * import { overrideRequestRoute } from '@astroscope/node/log';\n *\n * export const onRequest: MiddlewareHandler = (ctx, next) => {\n * const page = lookupPage(ctx.url.pathname);\n *\n * if (!page) return next();\n *\n * overrideRequestRoute('/cms/pages/[id]');\n *\n * return next(`/cms/pages/${page.id}`);\n * };\n * ```\n */\nexport function overrideRequestRoute(route: string): void {\n applyRoute(route, getRequestRecord()?.method ?? 'GET', true);\n}\n\n/**\n * Stamp the route astro's routing matched, unless it was overridden by the\n * middleware that served the request.\n * @internal\n */\nexport function setRequestRoute(route: string, method: string): void {\n applyRoute(route, method, false);\n}\n"],"mappings":";;;;;;;;AASA,MAAM,YAAY,OAAO,IAAI,sBAAsB;AA2BnD,SAAgB,cAAwB;CACtC,MAAM,IAAI;CACV,IAAI,QAAQ,EAAE;CAEd,IAAI,CAAC,OAAO;EACV,QAAQ;GAAE,MAAM,KAAA;GAAW,gBAAgB,IAAI,kBAAkB;GAAG,QAAQ,CAAC;GAAG,SAAS;EAAE;EAC3F,EAAE,aAAa;CACjB;CAEA,OAAO;AACT;AAEA,SAAgB,mBAA8C;CAC5D,OAAO,YAAY,CAAC,CAAC,eAAe,SAAS;AAC/C;;;;;;;;;;;ACvCA,SAAS,WAAW,OAAe,QAAgB,UAAyB;CAC1E,MAAM,SAAS,iBAAiB;CAEhC,IAAI,QAAQ;EACV,IAAI,CAAC,YAAY,OAAO,eAAe;EAEvC,OAAO,QAAQ;EAEf,IAAI,UACF,OAAO,gBAAgB;CAE3B;CAEA,MAAM,OAAO,MAAM,cAAc;CAEjC,IAAI,MAAM,YAAY,GAAG;EACvB,KAAK,aAAa,cAAc,KAAK;EAErC,IAAI,CAAC,QAAQ,YACX,KAAK,WAAW,GAAG,OAAO,GAAG,OAAO;CAExC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,qBAAqB,OAAqB;CACxD,WAAW,OAAO,iBAAiB,CAAC,EAAE,UAAU,OAAO,IAAI;AAC7D;;;;;;AAOA,SAAgB,gBAAgB,OAAe,QAAsB;CACnE,WAAW,OAAO,QAAQ,KAAK;AACjC"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { MiddlewareHandler } from "astro";
|
|
2
|
-
|
|
3
2
|
//#region src/observability/route-middleware-entrypoint.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Route enrichment: the native handler starts spans and request logging
|
|
6
5
|
* before routing, so the route pattern is the one thing only astro knows.
|
|
7
6
|
* Feeds it back into the request record (final log line, metrics) and the
|
|
8
|
-
* active server span.
|
|
7
|
+
* active server span. A middleware that serves a route astro never matched
|
|
8
|
+
* reports it itself via `overrideRequestRoute`, which wins over this. No-op
|
|
9
|
+
* outside instrumented requests.
|
|
9
10
|
*/
|
|
10
11
|
declare const onRequest: MiddlewareHandler;
|
|
11
12
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-middleware-entrypoint.d.ts","names":[],"sources":["../src/observability/route-middleware-entrypoint.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"route-middleware-entrypoint.d.ts","names":[],"sources":["../src/observability/route-middleware-entrypoint.ts"],"mappings":";;;;;;;;;;cAWa,WAAW"}
|
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import { trace } from "@opentelemetry/api";
|
|
1
|
+
import { n as setRequestRoute } from "./request-route-DcnZOOM4.js";
|
|
3
2
|
//#region src/observability/route-middleware-entrypoint.ts
|
|
4
3
|
/**
|
|
5
4
|
* Route enrichment: the native handler starts spans and request logging
|
|
6
5
|
* before routing, so the route pattern is the one thing only astro knows.
|
|
7
6
|
* Feeds it back into the request record (final log line, metrics) and the
|
|
8
|
-
* active server span.
|
|
7
|
+
* active server span. A middleware that serves a route astro never matched
|
|
8
|
+
* reports it itself via `overrideRequestRoute`, which wins over this. No-op
|
|
9
|
+
* outside instrumented requests.
|
|
9
10
|
*/
|
|
10
11
|
const onRequest = (ctx, next) => {
|
|
11
|
-
|
|
12
|
-
const route = ctx.routePattern;
|
|
13
|
-
if (record && !record.route) record.route = route;
|
|
14
|
-
const span = trace.getActiveSpan();
|
|
15
|
-
if (span?.isRecording() && route) {
|
|
16
|
-
span.setAttribute("http.route", route);
|
|
17
|
-
if (!record?.actionName) span.updateName(`${ctx.request.method} ${route}`);
|
|
18
|
-
}
|
|
12
|
+
if (ctx.routePattern) setRequestRoute(ctx.routePattern, ctx.request.method);
|
|
19
13
|
return next();
|
|
20
14
|
};
|
|
21
15
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-middleware-entrypoint.js","names":[],"sources":["../src/observability/route-middleware-entrypoint.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"route-middleware-entrypoint.js","names":[],"sources":["../src/observability/route-middleware-entrypoint.ts"],"sourcesContent":["import type { MiddlewareHandler } from 'astro';\nimport { setRequestRoute } from './request-route.js';\n\n/**\n * Route enrichment: the native handler starts spans and request logging\n * before routing, so the route pattern is the one thing only astro knows.\n * Feeds it back into the request record (final log line, metrics) and the\n * active server span. A middleware that serves a route astro never matched\n * reports it itself via `overrideRequestRoute`, which wins over this. No-op\n * outside instrumented requests.\n */\nexport const onRequest: MiddlewareHandler = (ctx, next) => {\n if (ctx.routePattern) {\n setRequestRoute(ctx.routePattern, ctx.request.method);\n }\n\n return next();\n};\n"],"mappings":";;;;;;;;;;AAWA,MAAa,aAAgC,KAAK,SAAS;CACzD,IAAI,IAAI,cACN,gBAAgB,IAAI,cAAc,IAAI,QAAQ,MAAM;CAGtD,OAAO,KAAK;AACd"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","names":[],"sources":["../src/server/server.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"server.d.ts","names":[],"sources":["../src/server/server.ts"],"mappings":";UAoFiB;EACf;EACA;EACA,QAAQ;EACR,UAAU;;iBAGU,YAAY;EAChC;EACA;IACE,QAAQ"}
|
package/dist/server.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { n as setBootContext } from "./context-Bkg-FnnQ.js";
|
|
2
|
-
import { a as runShutdown, i as createRequestInstrumentation, n as shutdownTelemetry, o as runStartup, r as dumpEarlyLogs, t as preparePlatform } from "./prepare-
|
|
3
|
-
import {
|
|
4
|
-
import { n as log } from "./log-
|
|
5
|
-
import { n as dispatchNativeMount, t as clearNativeMounts } from "./native-mount-
|
|
2
|
+
import { a as runShutdown, i as createRequestInstrumentation, n as shutdownTelemetry, o as runStartup, r as dumpEarlyLogs, t as preparePlatform } from "./prepare-CXZsyAVk.js";
|
|
3
|
+
import { i as getRequestRecord } from "./request-route-DcnZOOM4.js";
|
|
4
|
+
import { n as log } from "./log-B69HEBvg.js";
|
|
5
|
+
import { n as dispatchNativeMount, t as clearNativeMounts } from "./native-mount-DjYEnO4X.js";
|
|
6
6
|
import { n as deactivateHealthChecks, t as activateHealthChecks } from "./store-8pnTxM1x.js";
|
|
7
7
|
import { n as MIME_TYPES, t as COMPRESSIBLE } from "./mime-C_GwZovh.js";
|
|
8
8
|
import fs, { createReadStream } from "node:fs";
|
|
@@ -279,8 +279,10 @@ async function warmupModules() {
|
|
|
279
279
|
app.manifest.sessionDriver,
|
|
280
280
|
app.manifest.serverIslandMappings
|
|
281
281
|
].filter((load) => load !== void 0);
|
|
282
|
-
const
|
|
283
|
-
|
|
282
|
+
const failures = (await Promise.allSettled(loaders.map((load) => load()))).filter((result) => result.status === "rejected");
|
|
283
|
+
if (failures.length === 0) return;
|
|
284
|
+
for (const failure of failures) log.error(failure.reason instanceof Error ? { err: failure.reason } : { reason: failure.reason }, "warmup import failed");
|
|
285
|
+
throw new AggregateError(failures.map((failure) => failure.reason), "warmup import failed");
|
|
284
286
|
}
|
|
285
287
|
/**
|
|
286
288
|
* TLS tokens from `SERVER_CERT_PATH` / `SERVER_KEY_PATH` (same contract as
|
|
@@ -344,6 +346,14 @@ async function startServer(overrides) {
|
|
|
344
346
|
const warmup = warmupModules().then(() => {
|
|
345
347
|
warmupMs = roundMs(performance.now() - warmupStartedAt);
|
|
346
348
|
warmupSpan.span.end();
|
|
349
|
+
}, (error) => {
|
|
350
|
+
warmupMs = roundMs(performance.now() - warmupStartedAt);
|
|
351
|
+
warmupSpan.span.setStatus({
|
|
352
|
+
code: SpanStatusCode.ERROR,
|
|
353
|
+
message: "warmup import failed"
|
|
354
|
+
});
|
|
355
|
+
warmupSpan.span.end();
|
|
356
|
+
return error;
|
|
347
357
|
});
|
|
348
358
|
const shutdownLifecycle = async (shutdownContext) => {
|
|
349
359
|
try {
|
|
@@ -355,7 +365,11 @@ async function startServer(overrides) {
|
|
|
355
365
|
clearNativeMounts();
|
|
356
366
|
if (health) {
|
|
357
367
|
deactivateHealthChecks();
|
|
358
|
-
|
|
368
|
+
try {
|
|
369
|
+
await server.stop();
|
|
370
|
+
} catch (err) {
|
|
371
|
+
log.debug(err instanceof Error ? { err } : { reason: err }, "health probe server stop failed");
|
|
372
|
+
}
|
|
359
373
|
}
|
|
360
374
|
};
|
|
361
375
|
const failStartup = async (err, message) => {
|
|
@@ -377,7 +391,8 @@ async function startServer(overrides) {
|
|
|
377
391
|
} catch (err) {
|
|
378
392
|
await failStartup(err, "startup failed");
|
|
379
393
|
}
|
|
380
|
-
await warmup;
|
|
394
|
+
const warmupError = await warmup;
|
|
395
|
+
if (warmupError !== void 0) await failStartup(warmupError, "warmup import failed");
|
|
381
396
|
if (health) probes.startup.enable();
|
|
382
397
|
const client = resolveClientDir(runtimeOptions, import.meta.url);
|
|
383
398
|
const appHandler = createAppHandler(app, runtimeOptions, client);
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":["healthServer","server"],"sources":["../src/observability/telemetry/lifecycle.ts","../src/server/client-dir.ts","../src/server/serve-app.ts","../src/server/serve-static.ts","../src/server/server.ts"],"sourcesContent":["import { type Context, type Span, SpanStatusCode, context, trace } from '@opentelemetry/api';\n\nconst LIB_NAME = '@astroscope/node';\n\n/**\n * Lifecycle spans (`startup` / `shutdown` with phase children). No-op when no\n * SDK is registered — `trace.getTracer` returns the no-op tracer.\n */\n\nexport function startLifecycleSpan(name: string, parent?: Context): { span: Span; context: Context } {\n const parentContext = parent ?? context.active();\n const span = trace.getTracer(LIB_NAME).startSpan(name, undefined, parentContext);\n\n return { span, context: trace.setSpan(parentContext, span) };\n}\n\nexport async function withLifecycleSpan<T>(name: string, parent: Context, fn: () => Promise<T> | T): Promise<T> {\n const { span, context: spanContext } = startLifecycleSpan(name, parent);\n\n try {\n const result = await context.with(spanContext, fn);\n\n span.setStatus({ code: SpanStatusCode.OK });\n\n return result;\n } catch (err) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: err instanceof Error ? err.message : 'unknown error' });\n\n throw err;\n } finally {\n span.end();\n }\n}\n","import path from 'node:path';\nimport url from 'node:url';\n\n/**\n * Resolve the client directory at runtime relative to the built server entry.\n *\n * The build-time client/server URLs are only valid on the build machine; in a\n * container the deploy path differs. Walk up from `import.meta.url` of the\n * bundled server code until the server directory is found, then apply the\n * build-time server→client relative path.\n */\nexport function resolveClientDir(options: { client: string; server: string }, importMetaUrl: string): string {\n const clientPath = url.fileURLToPath(new URL(options.client));\n const serverPath = url.fileURLToPath(new URL(options.server));\n const rel = path.relative(serverPath, clientPath);\n const serverFolder = path.basename(serverPath);\n\n let serverEntryFolderURL = path.dirname(importMetaUrl);\n let previous = '';\n\n while (!serverEntryFolderURL.endsWith(serverFolder)) {\n if (serverEntryFolderURL === previous) {\n throw new Error(\n `[@astroscope/node] could not find the server directory \"${serverFolder}\" by walking up from \"${importMetaUrl}\"`,\n );\n }\n\n previous = serverEntryFolderURL;\n serverEntryFolderURL = path.dirname(serverEntryFolderURL);\n }\n\n const clientURL = new URL(rel.endsWith('/') ? rel : `${rel}/`, `${serverEntryFolderURL}/entry.mjs`);\n\n return url.fileURLToPath(clientURL);\n}\n","import { createReadStream } from 'node:fs';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { BaseApp } from 'astro/app';\nimport { createRequestFromNodeRequest, writeResponse } from 'astro/app/node';\nimport { log } from '../observability/log/index.js';\nimport { getRequestRecord } from '../observability/log/store.js';\nimport type { RuntimeOptions } from '../types.js';\n\nasync function readFSErrorPage(client: string, status: number): Promise<Response | undefined> {\n const filePaths = [`${status}.html`, `${status}/index.html`];\n\n for (const filePath of filePaths) {\n const fullPath = path.join(client, filePath);\n let stream: ReturnType<typeof createReadStream> | undefined;\n\n try {\n stream = createReadStream(fullPath);\n\n await new Promise<void>((resolve, reject) => {\n stream!.once('open', () => resolve());\n stream!.once('error', reject);\n });\n\n return new Response(Readable.toWeb(stream) as ReadableStream, {\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n });\n } catch {\n stream?.destroy();\n }\n }\n\n return undefined;\n}\n\n/**\n * Render on-demand routes: node req → web Request → `app.render()` → node res.\n * Prerendered pages never reach this handler (the static handler serves them);\n * requests for them landing here render the 404 route.\n */\nexport function createAppHandler(app: BaseApp, options: RuntimeOptions, client: string) {\n process.on('unhandledRejection', (reason) => {\n const requestUrl = getRequestRecord()?.url;\n\n log.error(\n {\n ...(reason instanceof Error ? { err: reason } : { reason }),\n ...(requestUrl && { url: requestUrl }),\n },\n requestUrl ? 'unhandled rejection while rendering' : 'unhandled rejection',\n );\n });\n\n const prerenderedErrorPageFetch = async (url: string): Promise<Response> => {\n const { pathname } = new URL(url);\n\n for (const status of [404, 500]) {\n if (pathname.endsWith(`/${status}.html`) || pathname.endsWith(`/${status}/index.html`)) {\n const response = await readFSErrorPage(client, status);\n\n if (response) return response;\n }\n }\n\n return new Response(null, { status: 404 });\n };\n\n const bodySizeLimit =\n options.bodySizeLimit === 0 || options.bodySizeLimit === Number.POSITIVE_INFINITY\n ? undefined\n : options.bodySizeLimit;\n\n return async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n let request: Request;\n\n try {\n request = createRequestFromNodeRequest(req, {\n allowedDomains: app.getAllowedDomains?.() ?? [],\n ...(bodySizeLimit !== undefined && { bodySizeLimit }),\n port: options.port,\n });\n } catch (err) {\n log.error(err instanceof Error ? { err, url: req.url } : { reason: err, url: req.url }, 'could not render');\n\n res.statusCode = 500;\n res.end('Internal Server Error');\n\n return;\n }\n\n const routeData = app.match(request, true);\n\n const response =\n routeData && !(routeData.type === 'page' && routeData.prerender)\n ? await app.render(request, { addCookieHeader: true, routeData, prerenderedErrorPageFetch })\n : await app.render(request, { addCookieHeader: true, prerenderedErrorPageFetch });\n\n await writeResponse(response, res);\n };\n}\n","import fs from 'node:fs';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport path from 'node:path';\nimport type { BaseApp } from 'astro/app';\nimport send from 'send';\nimport { COMPRESSIBLE, MIME_TYPES } from './mime.js';\n\nconst VARIANTS = [\n { encoding: 'br', suffix: '.br' },\n { encoding: 'gzip', suffix: '.gz' },\n] as const;\n\nfunction negotiateVariant(\n req: IncomingMessage,\n client: string,\n pathname: string,\n): { pathname: string; encoding: string } | undefined {\n const accept = req.headers['accept-encoding'];\n\n if (typeof accept !== 'string') return undefined;\n\n for (const { encoding, suffix } of VARIANTS) {\n if (!accept.includes(encoding)) continue;\n\n if (fs.existsSync(path.join(client, `${pathname}${suffix}`))) {\n return { pathname: `${pathname}${suffix}`, encoding };\n }\n }\n\n return undefined;\n}\n\nfunction hasFileExtension(pathname: string): boolean {\n const last = pathname.split('/').pop();\n\n return !!last && last.includes('.');\n}\n\nfunction prependForwardSlash(pathname: string): string {\n return pathname.startsWith('/') ? pathname : `/${pathname}`;\n}\n\nfunction isDirectory(client: string, urlPath: string): boolean {\n const filePath = path.join(client, urlPath);\n const resolved = path.resolve(filePath);\n const resolvedClient = path.resolve(client);\n\n // path traversal guard\n if (resolved !== resolvedClient && !resolved.startsWith(resolvedClient + path.sep)) {\n return false;\n }\n\n try {\n return fs.lstatSync(filePath).isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Serve files from the client build directory, falling through to `ssr` when\n * no file matches. Handles trailing-slash redirects per the manifest config\n * and marks hashed assets as immutable.\n */\nexport function createStaticHandler(app: BaseApp, client: string) {\n return (req: IncomingMessage, res: ServerResponse, ssr: () => void): void => {\n if (!req.url) {\n ssr();\n\n return;\n }\n\n let fullUrl = req.url;\n\n if (fullUrl.includes('#')) {\n fullUrl = fullUrl.slice(0, fullUrl.indexOf('#'));\n }\n\n const [urlPath = '', urlQuery] = fullUrl.split('?');\n let fsPath = app.removeBase(urlPath);\n\n try {\n fsPath = decodeURI(fsPath);\n } catch {\n // fall through with the raw path; send() rejects malformed paths itself\n }\n\n const dir = isDirectory(client, fsPath);\n const hasSlash = urlPath.endsWith('/');\n let pathname = urlPath;\n\n switch (app.manifest.trailingSlash) {\n case 'never': {\n if (dir && urlPath !== '/' && hasSlash) {\n res.statusCode = 301;\n res.setHeader('Location', urlPath.slice(0, -1) + (urlQuery ? `?${urlQuery}` : ''));\n res.end();\n\n return;\n }\n\n if (dir && !hasSlash) {\n pathname = `${urlPath}/index.html`;\n }\n\n break;\n }\n case 'ignore': {\n if (dir && !hasSlash) {\n pathname = `${urlPath}/index.html`;\n }\n\n break;\n }\n case 'always': {\n if (!hasSlash && !hasFileExtension(urlPath) && !urlPath.startsWith('/_')) {\n res.statusCode = 301;\n res.setHeader('Location', `${urlPath}/${urlQuery ? `?${urlQuery}` : ''}`);\n res.end();\n\n return;\n }\n\n break;\n }\n }\n\n pathname = prependForwardSlash(app.removeBase(pathname));\n\n const normalizedPathname = path.posix.normalize(pathname);\n const compressible = COMPRESSIBLE.has(path.posix.extname(normalizedPathname));\n const variant = compressible ? negotiateVariant(req, client, normalizedPathname) : undefined;\n\n const stream = send(req, variant?.pathname ?? normalizedPathname, {\n root: client,\n dotfiles: normalizedPathname.startsWith('/.well-known/') ? 'allow' : 'deny',\n // with build.format 'file' or 'preserve', pages are output as `page.html`\n // instead of `page/index.html` — let send() try appending `.html`\n extensions: app.manifest.buildFormat === 'file' || app.manifest.buildFormat === 'preserve' ? ['html'] : [],\n });\n\n let forwardError = false;\n\n stream.on('error', (err: NodeJS.ErrnoException & { statusCode?: number }) => {\n if (forwardError) {\n const status = err.statusCode ?? 500;\n\n if (status >= 500) {\n console.error(err.toString());\n }\n\n res.writeHead(status);\n res.end(status >= 500 ? 'Internal server error' : '');\n\n return;\n }\n\n ssr();\n });\n\n stream.on('file', () => {\n forwardError = true;\n });\n\n // fires before the body and before conditional-GET handling, so these\n // headers also land on 304 responses\n stream.on('headers', (headersRes: ServerResponse) => {\n if (compressible) {\n headersRes.setHeader('Vary', 'Accept-Encoding');\n }\n\n if (variant) {\n headersRes.setHeader('Content-Encoding', variant.encoding);\n headersRes.setHeader(\n 'Content-Type',\n MIME_TYPES.get(path.posix.extname(normalizedPathname)) ?? 'application/octet-stream',\n );\n }\n\n if (normalizedPathname.startsWith(`/${app.manifest.assetsDir}/`)) {\n headersRes.setHeader('Cache-Control', 'public, max-age=31536000, immutable');\n }\n });\n\n stream.pipe(res);\n };\n}\n","import fs from 'node:fs';\nimport http from 'node:http';\nimport https from 'node:https';\nimport { checks, server as healthServer, probes } from '@entwico/health-probes';\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { createApp } from 'astro/app/entrypoint';\nimport { setGetEnv } from 'astro/env/setup';\n// @ts-expect-error virtual module provided by the integration\nimport { options } from 'virtual:@astroscope/node/config';\nimport { activateHealthChecks, deactivateHealthChecks } from '../health/store.js';\nimport { setBootContext } from '../lifecycle/context.js';\nimport { type BootModule, runShutdown, runStartup } from '../lifecycle/lifecycle.js';\nimport type { BootContext } from '../lifecycle/types.js';\nimport { createRequestInstrumentation } from '../observability/instrument.js';\nimport { dumpEarlyLogs } from '../observability/log/construct.js';\nimport { log } from '../observability/log/index.js';\nimport { startLifecycleSpan, withLifecycleSpan } from '../observability/telemetry/lifecycle.js';\nimport { shutdownTelemetry } from '../observability/telemetry/sdk.js';\nimport { preparePlatform } from '../platform/prepare.js';\nimport type { RuntimeOptions } from '../types.js';\nimport { resolveClientDir } from './client-dir.js';\nimport { clearNativeMounts, dispatchNativeMount } from './native-mount.js';\nimport { createAppHandler } from './serve-app.js';\nimport { createStaticHandler } from './serve-static.js';\n\nsetGetEnv((key) => process.env[key]);\n\nconst runtimeOptions = options as RuntimeOptions;\nconst app = createApp({ streaming: true });\n\nconst roundMs = (n: number) => Math.round(n * 100) / 100;\n\n/**\n * Pre-import every lazily loaded server module (pages, middleware, actions,\n * session driver) so the first request pays no import cost. Uses the\n * manifest's own loaders — exactly what the runtime calls per request.\n */\nasync function warmupModules(): Promise<void> {\n const loaders = [\n ...(app.manifest.pageMap?.values() ?? []),\n app.manifest.middleware,\n app.manifest.actions,\n app.manifest.sessionDriver,\n app.manifest.serverIslandMappings,\n ].filter((load) => load !== undefined);\n\n const results = await Promise.allSettled(loaders.map((load) => load()));\n\n for (const result of results) {\n if (result.status === 'rejected') {\n log.error(\n result.reason instanceof Error ? { err: result.reason } : { reason: result.reason },\n 'warmup import failed',\n );\n }\n }\n}\n\n/**\n * TLS tokens from `SERVER_CERT_PATH` / `SERVER_KEY_PATH` (same contract as\n * `@astrojs/node`). Read after env loading, so the paths may come from `.env`.\n */\nfunction loadTlsOptions(): { cert: Buffer; key: Buffer } | undefined {\n const certPath = process.env['SERVER_CERT_PATH'];\n const keyPath = process.env['SERVER_KEY_PATH'];\n\n if (!certPath && !keyPath) return undefined;\n\n if (!certPath || !keyPath) {\n throw new Error('SERVER_CERT_PATH and SERVER_KEY_PATH must both be set to serve HTTPS');\n }\n\n return { cert: fs.readFileSync(certPath), key: fs.readFileSync(keyPath) };\n}\n\nexport interface ServerHandle {\n host: string;\n port: number;\n stop(): Promise<void>;\n closed(): Promise<void>;\n}\n\nexport async function startServer(overrides?: {\n host?: string | undefined;\n port?: number | undefined;\n}): Promise<ServerHandle> {\n const startedAt = performance.now();\n const host = overrides?.host ?? process.env['HOST'] ?? runtimeOptions.host;\n const port = overrides?.port ?? (process.env['PORT'] ? Number(process.env['PORT']) : runtimeOptions.port);\n const context: BootContext = { dev: false, host, port };\n const health = runtimeOptions.health;\n\n setBootContext(context);\n\n try {\n await preparePlatform({\n dev: false,\n telemetry: runtimeOptions.telemetry ? { prometheus: runtimeOptions.telemetry.prometheus } : false,\n seams: {\n config: () => import('virtual:@astroscope/node/config-entry'),\n instrumentation: () => import('virtual:@astroscope/node/instrumentation-entry'),\n log: () => import('virtual:@astroscope/node/log-entry'),\n },\n });\n } catch (err) {\n // the logger never came up — no silent phase, dump the buffer and die\n dumpEarlyLogs();\n console.error(err);\n process.exit(1);\n }\n\n if (health) {\n healthServer.start({ ...health, host: health.host ?? process.env['HEALTH_HOST'] ?? '0.0.0.0' });\n probes.live.enable();\n activateHealthChecks(checks);\n log.debug('health probes listening');\n }\n\n const startup = startLifecycleSpan('startup');\n\n log.info({ host, port }, 'starting');\n\n // the boot module graph may read config at import time, so it must only be\n // evaluated after preparePlatform() has loaded env and config\n let bootModule: BootModule = {};\n let bootMs = 0;\n let warmupMs = 0;\n\n // starts in parallel with the boot startup, awaited before listen\n const warmupStartedAt = performance.now();\n const warmupSpan = startLifecycleSpan('warmup', startup.context);\n const warmup = warmupModules().then(() => {\n warmupMs = roundMs(performance.now() - warmupStartedAt);\n warmupSpan.span.end();\n });\n\n const shutdownLifecycle = async (\n shutdownContext?: ReturnType<typeof startLifecycleSpan>['context'],\n ): Promise<void> => {\n try {\n if (shutdownContext) {\n await withLifecycleSpan('onShutdown', shutdownContext, () => runShutdown(bootModule, context));\n } else {\n await runShutdown(bootModule, context);\n }\n } catch (err) {\n log.error(err instanceof Error ? { err } : { reason: err }, 'shutdown failed');\n }\n\n clearNativeMounts();\n\n if (health) {\n deactivateHealthChecks();\n await healthServer.stop();\n }\n };\n\n const failStartup = async (err: unknown, message: string): Promise<never> => {\n log.error(err instanceof Error ? { err } : { reason: err }, message);\n startup.span.setStatus({ code: SpanStatusCode.ERROR, message });\n startup.span.end();\n\n await shutdownLifecycle();\n await shutdownTelemetry();\n process.exit(1);\n };\n\n try {\n const bootStartedAt = performance.now();\n\n // @ts-expect-error virtual module provided by the integration\n bootModule = (await import('virtual:@astroscope/node/boot')) as BootModule;\n\n await withLifecycleSpan('boot', startup.context, () => runStartup(bootModule, context));\n\n bootMs = roundMs(performance.now() - bootStartedAt);\n } catch (err) {\n await failStartup(err, 'startup failed');\n }\n\n await warmup;\n\n if (health) probes.startup.enable();\n\n const client = resolveClientDir(runtimeOptions, import.meta.url);\n const appHandler = createAppHandler(app, runtimeOptions, client);\n const staticHandler = createStaticHandler(app, client);\n const instrument = createRequestInstrumentation({\n logging: runtimeOptions.logging,\n telemetry: runtimeOptions.telemetry ? { exclude: runtimeOptions.telemetry.exclude } : false,\n });\n\n let tls: { cert: Buffer; key: Buffer } | undefined;\n\n try {\n tls = loadTlsOptions();\n } catch (err) {\n await failStartup(err, 'failed to load TLS options');\n }\n\n const listener: http.RequestListener = (req, res) => {\n try {\n decodeURI(req.url ?? '');\n } catch {\n res.writeHead(400);\n res.end('Bad request.');\n\n return;\n }\n\n instrument(req, res, () => {\n if (dispatchNativeMount(req, res)) return;\n\n staticHandler(req, res, () => void appHandler(req, res));\n });\n };\n\n const server = tls ? https.createServer(tls, listener) : http.createServer(listener);\n\n try {\n await withLifecycleSpan('listen', startup.context, () => {\n return new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(port, host, resolve);\n });\n });\n } catch (err) {\n await failStartup(err, `failed to listen on ${host}:${port}`);\n }\n\n if (health) probes.ready.enable();\n\n startup.span.setStatus({ code: SpanStatusCode.OK });\n startup.span.end();\n\n log.info(\n {\n host,\n port,\n ...(tls && { https: true }),\n health: !!health,\n bootMs,\n warmupMs,\n totalMs: roundMs(performance.now() - startedAt),\n },\n 'server ready',\n );\n\n let stopPromise: Promise<void> | undefined;\n let resolveClosed!: () => void;\n\n const closedPromise = new Promise<void>((resolve) => {\n resolveClosed = resolve;\n });\n\n const doStop = async (): Promise<void> => {\n if (health) probes.ready.disable();\n\n log.info('shutdown initiated');\n\n const drainStartedAt = performance.now();\n const shutdown = startLifecycleSpan('shutdown');\n\n await withLifecycleSpan('drain', shutdown.context, async () => {\n const closed = new Promise<void>((resolve) => server.close(() => resolve()));\n\n server.closeIdleConnections();\n\n const forceTimer = setTimeout(() => server.closeAllConnections(), runtimeOptions.shutdownTimeout);\n\n await closed;\n\n clearTimeout(forceTimer);\n });\n\n const drainMs = roundMs(performance.now() - drainStartedAt);\n\n await shutdownLifecycle(shutdown.context);\n\n shutdown.span.end();\n\n log.info({ drainMs }, 'shutdown complete');\n\n await shutdownTelemetry();\n resolveClosed();\n };\n\n const stop = (): Promise<void> => (stopPromise ??= doStop());\n\n process.once('SIGTERM', () => void stop().then(() => process.exit(0)));\n process.once('SIGINT', () => void stop().then(() => process.exit(0)));\n\n return { host, port, stop, closed: () => closedPromise };\n}\n\nif (process.env['ASTROSCOPE_NODE_AUTOSTART'] !== 'disabled') {\n await startServer();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","names":["healthServer","server"],"sources":["../src/observability/telemetry/lifecycle.ts","../src/server/client-dir.ts","../src/server/serve-app.ts","../src/server/serve-static.ts","../src/server/server.ts"],"sourcesContent":["import { type Context, type Span, SpanStatusCode, context, trace } from '@opentelemetry/api';\n\nconst LIB_NAME = '@astroscope/node';\n\n/**\n * Lifecycle spans (`startup` / `shutdown` with phase children). No-op when no\n * SDK is registered — `trace.getTracer` returns the no-op tracer.\n */\n\nexport function startLifecycleSpan(name: string, parent?: Context): { span: Span; context: Context } {\n const parentContext = parent ?? context.active();\n const span = trace.getTracer(LIB_NAME).startSpan(name, undefined, parentContext);\n\n return { span, context: trace.setSpan(parentContext, span) };\n}\n\nexport async function withLifecycleSpan<T>(name: string, parent: Context, fn: () => Promise<T> | T): Promise<T> {\n const { span, context: spanContext } = startLifecycleSpan(name, parent);\n\n try {\n const result = await context.with(spanContext, fn);\n\n span.setStatus({ code: SpanStatusCode.OK });\n\n return result;\n } catch (err) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: err instanceof Error ? err.message : 'unknown error' });\n\n throw err;\n } finally {\n span.end();\n }\n}\n","import path from 'node:path';\nimport url from 'node:url';\n\n/**\n * Resolve the client directory at runtime relative to the built server entry.\n *\n * The build-time client/server URLs are only valid on the build machine; in a\n * container the deploy path differs. Walk up from `import.meta.url` of the\n * bundled server code until the server directory is found, then apply the\n * build-time server→client relative path.\n */\nexport function resolveClientDir(options: { client: string; server: string }, importMetaUrl: string): string {\n const clientPath = url.fileURLToPath(new URL(options.client));\n const serverPath = url.fileURLToPath(new URL(options.server));\n const rel = path.relative(serverPath, clientPath);\n const serverFolder = path.basename(serverPath);\n\n let serverEntryFolderURL = path.dirname(importMetaUrl);\n let previous = '';\n\n while (!serverEntryFolderURL.endsWith(serverFolder)) {\n if (serverEntryFolderURL === previous) {\n throw new Error(\n `[@astroscope/node] could not find the server directory \"${serverFolder}\" by walking up from \"${importMetaUrl}\"`,\n );\n }\n\n previous = serverEntryFolderURL;\n serverEntryFolderURL = path.dirname(serverEntryFolderURL);\n }\n\n const clientURL = new URL(rel.endsWith('/') ? rel : `${rel}/`, `${serverEntryFolderURL}/entry.mjs`);\n\n return url.fileURLToPath(clientURL);\n}\n","import { createReadStream } from 'node:fs';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { BaseApp } from 'astro/app';\nimport { createRequestFromNodeRequest, writeResponse } from 'astro/app/node';\nimport { log } from '../observability/log/index.js';\nimport { getRequestRecord } from '../observability/log/store.js';\nimport type { RuntimeOptions } from '../types.js';\n\nasync function readFSErrorPage(client: string, status: number): Promise<Response | undefined> {\n const filePaths = [`${status}.html`, `${status}/index.html`];\n\n for (const filePath of filePaths) {\n const fullPath = path.join(client, filePath);\n let stream: ReturnType<typeof createReadStream> | undefined;\n\n try {\n stream = createReadStream(fullPath);\n\n await new Promise<void>((resolve, reject) => {\n stream!.once('open', () => resolve());\n stream!.once('error', reject);\n });\n\n return new Response(Readable.toWeb(stream) as ReadableStream, {\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n });\n } catch {\n stream?.destroy();\n }\n }\n\n return undefined;\n}\n\n/**\n * Render on-demand routes: node req → web Request → `app.render()` → node res.\n * Prerendered pages never reach this handler (the static handler serves them);\n * requests for them landing here render the 404 route.\n */\nexport function createAppHandler(app: BaseApp, options: RuntimeOptions, client: string) {\n process.on('unhandledRejection', (reason) => {\n const requestUrl = getRequestRecord()?.url;\n\n log.error(\n {\n ...(reason instanceof Error ? { err: reason } : { reason }),\n ...(requestUrl && { url: requestUrl }),\n },\n requestUrl ? 'unhandled rejection while rendering' : 'unhandled rejection',\n );\n });\n\n const prerenderedErrorPageFetch = async (url: string): Promise<Response> => {\n const { pathname } = new URL(url);\n\n for (const status of [404, 500]) {\n if (pathname.endsWith(`/${status}.html`) || pathname.endsWith(`/${status}/index.html`)) {\n const response = await readFSErrorPage(client, status);\n\n if (response) return response;\n }\n }\n\n return new Response(null, { status: 404 });\n };\n\n const bodySizeLimit =\n options.bodySizeLimit === 0 || options.bodySizeLimit === Number.POSITIVE_INFINITY\n ? undefined\n : options.bodySizeLimit;\n\n return async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n let request: Request;\n\n try {\n request = createRequestFromNodeRequest(req, {\n allowedDomains: app.getAllowedDomains?.() ?? [],\n ...(bodySizeLimit !== undefined && { bodySizeLimit }),\n port: options.port,\n });\n } catch (err) {\n log.error(err instanceof Error ? { err, url: req.url } : { reason: err, url: req.url }, 'could not render');\n\n res.statusCode = 500;\n res.end('Internal Server Error');\n\n return;\n }\n\n const routeData = app.match(request, true);\n\n const response =\n routeData && !(routeData.type === 'page' && routeData.prerender)\n ? await app.render(request, { addCookieHeader: true, routeData, prerenderedErrorPageFetch })\n : await app.render(request, { addCookieHeader: true, prerenderedErrorPageFetch });\n\n await writeResponse(response, res);\n };\n}\n","import fs from 'node:fs';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport path from 'node:path';\nimport type { BaseApp } from 'astro/app';\nimport send from 'send';\nimport { COMPRESSIBLE, MIME_TYPES } from './mime.js';\n\nconst VARIANTS = [\n { encoding: 'br', suffix: '.br' },\n { encoding: 'gzip', suffix: '.gz' },\n] as const;\n\nfunction negotiateVariant(\n req: IncomingMessage,\n client: string,\n pathname: string,\n): { pathname: string; encoding: string } | undefined {\n const accept = req.headers['accept-encoding'];\n\n if (typeof accept !== 'string') return undefined;\n\n for (const { encoding, suffix } of VARIANTS) {\n if (!accept.includes(encoding)) continue;\n\n if (fs.existsSync(path.join(client, `${pathname}${suffix}`))) {\n return { pathname: `${pathname}${suffix}`, encoding };\n }\n }\n\n return undefined;\n}\n\nfunction hasFileExtension(pathname: string): boolean {\n const last = pathname.split('/').pop();\n\n return !!last && last.includes('.');\n}\n\nfunction prependForwardSlash(pathname: string): string {\n return pathname.startsWith('/') ? pathname : `/${pathname}`;\n}\n\nfunction isDirectory(client: string, urlPath: string): boolean {\n const filePath = path.join(client, urlPath);\n const resolved = path.resolve(filePath);\n const resolvedClient = path.resolve(client);\n\n // path traversal guard\n if (resolved !== resolvedClient && !resolved.startsWith(resolvedClient + path.sep)) {\n return false;\n }\n\n try {\n return fs.lstatSync(filePath).isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Serve files from the client build directory, falling through to `ssr` when\n * no file matches. Handles trailing-slash redirects per the manifest config\n * and marks hashed assets as immutable.\n */\nexport function createStaticHandler(app: BaseApp, client: string) {\n return (req: IncomingMessage, res: ServerResponse, ssr: () => void): void => {\n if (!req.url) {\n ssr();\n\n return;\n }\n\n let fullUrl = req.url;\n\n if (fullUrl.includes('#')) {\n fullUrl = fullUrl.slice(0, fullUrl.indexOf('#'));\n }\n\n const [urlPath = '', urlQuery] = fullUrl.split('?');\n let fsPath = app.removeBase(urlPath);\n\n try {\n fsPath = decodeURI(fsPath);\n } catch {\n // fall through with the raw path; send() rejects malformed paths itself\n }\n\n const dir = isDirectory(client, fsPath);\n const hasSlash = urlPath.endsWith('/');\n let pathname = urlPath;\n\n switch (app.manifest.trailingSlash) {\n case 'never': {\n if (dir && urlPath !== '/' && hasSlash) {\n res.statusCode = 301;\n res.setHeader('Location', urlPath.slice(0, -1) + (urlQuery ? `?${urlQuery}` : ''));\n res.end();\n\n return;\n }\n\n if (dir && !hasSlash) {\n pathname = `${urlPath}/index.html`;\n }\n\n break;\n }\n case 'ignore': {\n if (dir && !hasSlash) {\n pathname = `${urlPath}/index.html`;\n }\n\n break;\n }\n case 'always': {\n if (!hasSlash && !hasFileExtension(urlPath) && !urlPath.startsWith('/_')) {\n res.statusCode = 301;\n res.setHeader('Location', `${urlPath}/${urlQuery ? `?${urlQuery}` : ''}`);\n res.end();\n\n return;\n }\n\n break;\n }\n }\n\n pathname = prependForwardSlash(app.removeBase(pathname));\n\n const normalizedPathname = path.posix.normalize(pathname);\n const compressible = COMPRESSIBLE.has(path.posix.extname(normalizedPathname));\n const variant = compressible ? negotiateVariant(req, client, normalizedPathname) : undefined;\n\n const stream = send(req, variant?.pathname ?? normalizedPathname, {\n root: client,\n dotfiles: normalizedPathname.startsWith('/.well-known/') ? 'allow' : 'deny',\n // with build.format 'file' or 'preserve', pages are output as `page.html`\n // instead of `page/index.html` — let send() try appending `.html`\n extensions: app.manifest.buildFormat === 'file' || app.manifest.buildFormat === 'preserve' ? ['html'] : [],\n });\n\n let forwardError = false;\n\n stream.on('error', (err: NodeJS.ErrnoException & { statusCode?: number }) => {\n if (forwardError) {\n const status = err.statusCode ?? 500;\n\n if (status >= 500) {\n console.error(err.toString());\n }\n\n res.writeHead(status);\n res.end(status >= 500 ? 'Internal server error' : '');\n\n return;\n }\n\n ssr();\n });\n\n stream.on('file', () => {\n forwardError = true;\n });\n\n // fires before the body and before conditional-GET handling, so these\n // headers also land on 304 responses\n stream.on('headers', (headersRes: ServerResponse) => {\n if (compressible) {\n headersRes.setHeader('Vary', 'Accept-Encoding');\n }\n\n if (variant) {\n headersRes.setHeader('Content-Encoding', variant.encoding);\n headersRes.setHeader(\n 'Content-Type',\n MIME_TYPES.get(path.posix.extname(normalizedPathname)) ?? 'application/octet-stream',\n );\n }\n\n if (normalizedPathname.startsWith(`/${app.manifest.assetsDir}/`)) {\n headersRes.setHeader('Cache-Control', 'public, max-age=31536000, immutable');\n }\n });\n\n stream.pipe(res);\n };\n}\n","import fs from 'node:fs';\nimport http from 'node:http';\nimport https from 'node:https';\nimport { checks, server as healthServer, probes } from '@entwico/health-probes';\nimport { SpanStatusCode } from '@opentelemetry/api';\nimport { createApp } from 'astro/app/entrypoint';\nimport { setGetEnv } from 'astro/env/setup';\n// @ts-expect-error virtual module provided by the integration\nimport { options } from 'virtual:@astroscope/node/config';\nimport { activateHealthChecks, deactivateHealthChecks } from '../health/store.js';\nimport { setBootContext } from '../lifecycle/context.js';\nimport { type BootModule, runShutdown, runStartup } from '../lifecycle/lifecycle.js';\nimport type { BootContext } from '../lifecycle/types.js';\nimport { createRequestInstrumentation } from '../observability/instrument.js';\nimport { dumpEarlyLogs } from '../observability/log/construct.js';\nimport { log } from '../observability/log/index.js';\nimport { startLifecycleSpan, withLifecycleSpan } from '../observability/telemetry/lifecycle.js';\nimport { shutdownTelemetry } from '../observability/telemetry/sdk.js';\nimport { preparePlatform } from '../platform/prepare.js';\nimport type { RuntimeOptions } from '../types.js';\nimport { resolveClientDir } from './client-dir.js';\nimport { clearNativeMounts, dispatchNativeMount } from './native-mount.js';\nimport { createAppHandler } from './serve-app.js';\nimport { createStaticHandler } from './serve-static.js';\n\nsetGetEnv((key) => process.env[key]);\n\nconst runtimeOptions = options as RuntimeOptions;\nconst app = createApp({ streaming: true });\n\nconst roundMs = (n: number) => Math.round(n * 100) / 100;\n\n/**\n * Pre-import every lazily loaded server module (pages, middleware, actions,\n * session driver) so the first request pays no import cost. Uses the\n * manifest's own loaders — exactly what the runtime calls per request.\n */\nasync function warmupModules(): Promise<void> {\n const loaders = [\n ...(app.manifest.pageMap?.values() ?? []),\n app.manifest.middleware,\n app.manifest.actions,\n app.manifest.sessionDriver,\n app.manifest.serverIslandMappings,\n ].filter((load) => load !== undefined);\n\n const results = await Promise.allSettled(loaders.map((load) => load()));\n const failures = results.filter((result) => result.status === 'rejected');\n\n if (failures.length === 0) return;\n\n for (const failure of failures) {\n log.error(\n failure.reason instanceof Error ? { err: failure.reason } : { reason: failure.reason },\n 'warmup import failed',\n );\n }\n\n // a module that cannot even be imported would throw on its first request;\n // failing the boot turns a silently-degraded deploy into a crash the health\n // checks catch before it takes traffic\n throw new AggregateError(\n failures.map((failure) => failure.reason),\n 'warmup import failed',\n );\n}\n\n/**\n * TLS tokens from `SERVER_CERT_PATH` / `SERVER_KEY_PATH` (same contract as\n * `@astrojs/node`). Read after env loading, so the paths may come from `.env`.\n */\nfunction loadTlsOptions(): { cert: Buffer; key: Buffer } | undefined {\n const certPath = process.env['SERVER_CERT_PATH'];\n const keyPath = process.env['SERVER_KEY_PATH'];\n\n if (!certPath && !keyPath) return undefined;\n\n if (!certPath || !keyPath) {\n throw new Error('SERVER_CERT_PATH and SERVER_KEY_PATH must both be set to serve HTTPS');\n }\n\n return { cert: fs.readFileSync(certPath), key: fs.readFileSync(keyPath) };\n}\n\nexport interface ServerHandle {\n host: string;\n port: number;\n stop(): Promise<void>;\n closed(): Promise<void>;\n}\n\nexport async function startServer(overrides?: {\n host?: string | undefined;\n port?: number | undefined;\n}): Promise<ServerHandle> {\n const startedAt = performance.now();\n const host = overrides?.host ?? process.env['HOST'] ?? runtimeOptions.host;\n const port = overrides?.port ?? (process.env['PORT'] ? Number(process.env['PORT']) : runtimeOptions.port);\n const context: BootContext = { dev: false, host, port };\n const health = runtimeOptions.health;\n\n setBootContext(context);\n\n try {\n await preparePlatform({\n dev: false,\n telemetry: runtimeOptions.telemetry ? { prometheus: runtimeOptions.telemetry.prometheus } : false,\n seams: {\n config: () => import('virtual:@astroscope/node/config-entry'),\n instrumentation: () => import('virtual:@astroscope/node/instrumentation-entry'),\n log: () => import('virtual:@astroscope/node/log-entry'),\n },\n });\n } catch (err) {\n // the logger never came up — no silent phase, dump the buffer and die\n dumpEarlyLogs();\n console.error(err);\n process.exit(1);\n }\n\n if (health) {\n healthServer.start({ ...health, host: health.host ?? process.env['HEALTH_HOST'] ?? '0.0.0.0' });\n probes.live.enable();\n activateHealthChecks(checks);\n log.debug('health probes listening');\n }\n\n const startup = startLifecycleSpan('startup');\n\n log.info({ host, port }, 'starting');\n\n // the boot module graph may read config at import time, so it must only be\n // evaluated after preparePlatform() has loaded env and config\n let bootModule: BootModule = {};\n let bootMs = 0;\n let warmupMs = 0;\n\n // starts in parallel with the boot startup, awaited before listen. resolves\n // to the failure (if any) instead of rejecting, so a warmup error landing\n // before the join below is never seen as an unhandled rejection\n const warmupStartedAt = performance.now();\n const warmupSpan = startLifecycleSpan('warmup', startup.context);\n const warmup = warmupModules().then(\n () => {\n warmupMs = roundMs(performance.now() - warmupStartedAt);\n warmupSpan.span.end();\n\n return undefined;\n },\n (error: unknown) => {\n warmupMs = roundMs(performance.now() - warmupStartedAt);\n warmupSpan.span.setStatus({ code: SpanStatusCode.ERROR, message: 'warmup import failed' });\n warmupSpan.span.end();\n\n return error;\n },\n );\n\n const shutdownLifecycle = async (\n shutdownContext?: ReturnType<typeof startLifecycleSpan>['context'],\n ): Promise<void> => {\n try {\n if (shutdownContext) {\n await withLifecycleSpan('onShutdown', shutdownContext, () => runShutdown(bootModule, context));\n } else {\n await runShutdown(bootModule, context);\n }\n } catch (err) {\n log.error(err instanceof Error ? { err } : { reason: err }, 'shutdown failed');\n }\n\n clearNativeMounts();\n\n if (health) {\n deactivateHealthChecks();\n\n try {\n await healthServer.stop();\n } catch (err) {\n // a startup failure can tear the health server down before it has\n // finished binding; stopping it is best-effort\n log.debug(err instanceof Error ? { err } : { reason: err }, 'health probe server stop failed');\n }\n }\n };\n\n const failStartup = async (err: unknown, message: string): Promise<never> => {\n log.error(err instanceof Error ? { err } : { reason: err }, message);\n startup.span.setStatus({ code: SpanStatusCode.ERROR, message });\n startup.span.end();\n\n await shutdownLifecycle();\n await shutdownTelemetry();\n process.exit(1);\n };\n\n try {\n const bootStartedAt = performance.now();\n\n // @ts-expect-error virtual module provided by the integration\n bootModule = (await import('virtual:@astroscope/node/boot')) as BootModule;\n\n await withLifecycleSpan('boot', startup.context, () => runStartup(bootModule, context));\n\n bootMs = roundMs(performance.now() - bootStartedAt);\n } catch (err) {\n await failStartup(err, 'startup failed');\n }\n\n const warmupError = await warmup;\n\n if (warmupError !== undefined) {\n await failStartup(warmupError, 'warmup import failed');\n }\n\n if (health) probes.startup.enable();\n\n const client = resolveClientDir(runtimeOptions, import.meta.url);\n const appHandler = createAppHandler(app, runtimeOptions, client);\n const staticHandler = createStaticHandler(app, client);\n const instrument = createRequestInstrumentation({\n logging: runtimeOptions.logging,\n telemetry: runtimeOptions.telemetry ? { exclude: runtimeOptions.telemetry.exclude } : false,\n });\n\n let tls: { cert: Buffer; key: Buffer } | undefined;\n\n try {\n tls = loadTlsOptions();\n } catch (err) {\n await failStartup(err, 'failed to load TLS options');\n }\n\n const listener: http.RequestListener = (req, res) => {\n try {\n decodeURI(req.url ?? '');\n } catch {\n res.writeHead(400);\n res.end('Bad request.');\n\n return;\n }\n\n instrument(req, res, () => {\n if (dispatchNativeMount(req, res)) return;\n\n staticHandler(req, res, () => void appHandler(req, res));\n });\n };\n\n const server = tls ? https.createServer(tls, listener) : http.createServer(listener);\n\n try {\n await withLifecycleSpan('listen', startup.context, () => {\n return new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(port, host, resolve);\n });\n });\n } catch (err) {\n await failStartup(err, `failed to listen on ${host}:${port}`);\n }\n\n if (health) probes.ready.enable();\n\n startup.span.setStatus({ code: SpanStatusCode.OK });\n startup.span.end();\n\n log.info(\n {\n host,\n port,\n ...(tls && { https: true }),\n health: !!health,\n bootMs,\n warmupMs,\n totalMs: roundMs(performance.now() - startedAt),\n },\n 'server ready',\n );\n\n let stopPromise: Promise<void> | undefined;\n let resolveClosed!: () => void;\n\n const closedPromise = new Promise<void>((resolve) => {\n resolveClosed = resolve;\n });\n\n const doStop = async (): Promise<void> => {\n if (health) probes.ready.disable();\n\n log.info('shutdown initiated');\n\n const drainStartedAt = performance.now();\n const shutdown = startLifecycleSpan('shutdown');\n\n await withLifecycleSpan('drain', shutdown.context, async () => {\n const closed = new Promise<void>((resolve) => server.close(() => resolve()));\n\n server.closeIdleConnections();\n\n const forceTimer = setTimeout(() => server.closeAllConnections(), runtimeOptions.shutdownTimeout);\n\n await closed;\n\n clearTimeout(forceTimer);\n });\n\n const drainMs = roundMs(performance.now() - drainStartedAt);\n\n await shutdownLifecycle(shutdown.context);\n\n shutdown.span.end();\n\n log.info({ drainMs }, 'shutdown complete');\n\n await shutdownTelemetry();\n resolveClosed();\n };\n\n const stop = (): Promise<void> => (stopPromise ??= doStop());\n\n process.once('SIGTERM', () => void stop().then(() => process.exit(0)));\n process.once('SIGINT', () => void stop().then(() => process.exit(0)));\n\n return { host, port, stop, closed: () => closedPromise };\n}\n\nif (process.env['ASTROSCOPE_NODE_AUTOSTART'] !== 'disabled') {\n await startServer();\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types-D0uMBi2M.d.ts","names":[],"sources":["../src/lifecycle/types.ts"],"mappings":";UAAiB
|
|
1
|
+
{"version":3,"file":"types-D0uMBi2M.d.ts","names":[],"sources":["../src/lifecycle/types.ts"],"mappings":";UAAiB;;EAEf;;EAEA;;EAEA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astroscope/node",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Opinionated, cloud-friendly Node adapter for Astro: boot lifecycle, health probes, request logging, telemetry, CSRF and static serving run as plain code around server.listen()",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -82,14 +82,15 @@
|
|
|
82
82
|
},
|
|
83
83
|
"homepage": "https://github.com/entwico/astroscope/tree/main/packages/node#readme",
|
|
84
84
|
"dependencies": {
|
|
85
|
-
"@
|
|
86
|
-
"@entwico/
|
|
85
|
+
"@astrojs/compiler-rs": "^0.3.1",
|
|
86
|
+
"@entwico/dash": "^1.3.0",
|
|
87
|
+
"@entwico/health-probes": "^1.1.2",
|
|
87
88
|
"@opentelemetry/api": "^1.9.1",
|
|
88
|
-
"@opentelemetry/exporter-prometheus": "^0.
|
|
89
|
+
"@opentelemetry/exporter-prometheus": "^0.220.0",
|
|
89
90
|
"@opentelemetry/host-metrics": "^0.39.0",
|
|
90
91
|
"@opentelemetry/instrumentation-runtime-node": "^0.33.0",
|
|
91
92
|
"@opentelemetry/instrumentation-undici": "^0.30.0",
|
|
92
|
-
"@opentelemetry/sdk-node": "^0.
|
|
93
|
+
"@opentelemetry/sdk-node": "^0.220.0",
|
|
93
94
|
"acorn": "^8.17.0",
|
|
94
95
|
"magic-string": "^0.30.21",
|
|
95
96
|
"pino": "^10.3.1",
|
|
@@ -97,8 +98,8 @@
|
|
|
97
98
|
},
|
|
98
99
|
"devDependencies": {
|
|
99
100
|
"@types/send": "^1.2.1",
|
|
100
|
-
"astro": "^7.0.
|
|
101
|
-
"tsdown": "^0.22.
|
|
101
|
+
"astro": "^7.0.9",
|
|
102
|
+
"tsdown": "^0.22.8",
|
|
102
103
|
"typescript": "^6.0.3"
|
|
103
104
|
},
|
|
104
105
|
"peerDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"construct-DgB-jR0a.d.ts","names":[],"sources":["../src/observability/log/construct.ts"],"mappings":";;;;;AASA;;;KAAY,oBAAA,GAAuB,aAAA,KAAkB,GAAA;EAAO,GAAA;AAAA,MAAmB,aAAA,GAAgB,OAAA,CAAQ,aAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"events-CUzQ2_cp.d.ts","names":[],"sources":["../src/lifecycle/events.ts"],"mappings":";;;KAEY,aAAA;AAAA,KAEA,gBAAA,IAAoB,OAAA,EAAS,WAAA,KAAgB,OAAO;AAFhE;;;AAAA,iBAyBgB,EAAA,CAAG,KAAA,EAAO,aAAA,EAAe,OAAA,EAAS,gBAAgB;AAzBzC;AAEzB;;AAFyB,iBAwCT,GAAA,CAAI,KAAA,EAAO,aAAA,EAAe,OAAA,EAAS,gBAAgB;;;;iBAS7C,IAAA,CAAK,KAAA,EAAO,aAAA,EAAe,OAAA,EAAS,WAAA,GAAc,OAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"excludes-DLF3A_Cf.d.ts","names":[],"sources":["../src/excludes/excludes.ts"],"mappings":";;;;;;AAQA;;;KAAY,cAAA,GAAiB,OAAO,CAAC,aAAA,GAAgB,KAAA;;;;cAKxC,YAAA,EAAc,cAAc;AALyB;AAKlE;;AALkE,cAgBrD,qBAAA,EAAuB,cAAc;;AAXT;AAWzC;cAKa,eAAA,EAAiB,cAAc;;;AALM;AAKlD;;;;AAA4C;AAwB5C;;;;AAAiD;AAKjD;;cALa,oBAAA,EAAsB,cAAc;;;;iBAKjC,aAAA,CACd,GAAA,EAAK,UAAA,EACL,OAAA,WAAkB,aAAA,OAAoB,OAAA,EAAS,UAAA;;;;;;;;;AAAmC;AA4BpF;;;;;;;;iBAAgB,YAAA,CACd,UAAA,EAAY,iBAAA,EACZ,OAAA,WAAkB,aAAA,OAAoB,OAAA,EAAS,UAAA,gBAC9C,iBAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"native-mount-hhwWdLtL.js","names":[],"sources":["../src/server/native-mount.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http';\nimport { trace } from '@opentelemetry/api';\nimport { log } from '../observability/log/index.js';\nimport { getRequestRecord } from '../observability/log/store.js';\n\n/**\n * Native mounts: raw `(req, res)` handlers dispatched before static/astro,\n * for Node libraries that need the real request and response (e.g.\n * `oidc-provider`'s `callback()`). Mounted requests bypass astro middleware\n * entirely but stay inside request logging and tracing.\n *\n * Keyed on `globalThis` because registration (boot file, vite runner in dev)\n * and dispatch (server runtime) may live in different module instances.\n */\n\nconst STORE_KEY = Symbol.for('@astroscope/node/native-mounts');\n\nexport type NativeHandler = (req: IncomingMessage, res: ServerResponse) => void | Promise<void>;\n\nexport interface NativeMountMatcher {\n /** match requests whose pathname starts with this prefix */\n prefix?: string | undefined;\n\n /** predicate over the native request; evaluated when no prefix mount matches */\n match?: ((req: IncomingMessage) => boolean) | undefined;\n\n /** route label for logs, metrics and span names; defaults to the prefix */\n name?: string | undefined;\n}\n\ninterface Mount {\n prefix: string | undefined;\n match: ((req: IncomingMessage) => boolean) | undefined;\n name: string | undefined;\n handler: NativeHandler;\n}\n\ninterface Store {\n mounts: Mount[];\n}\n\nfunction getStore(): Store {\n const g = globalThis as Record<symbol, unknown>;\n let store = g[STORE_KEY] as Store | undefined;\n\n if (!store) {\n store = { mounts: [] };\n g[STORE_KEY] = store;\n }\n\n return store;\n}\n\nfunction matchesPrefix(pathname: string, prefix: string): boolean {\n if (!pathname.startsWith(prefix)) return false;\n\n const rest = pathname.slice(prefix.length);\n\n return rest === '' || rest.startsWith('/') || rest.startsWith('?');\n}\n\n/**\n * Mount a native `(req, res)` handler on the adapter's server. The handler\n * owns the response completely — matched requests never reach astro\n * middleware or rendering. Dispatch happens before static file serving, in\n * production and dev alike.\n *\n * Call from `onStartup`. Returns an unregister function; mounts still\n * registered after `onShutdown` are removed automatically.\n *\n * When several prefix mounts match, the longest prefix wins; predicate\n * mounts are consulted afterwards in registration order.\n *\n * @example\n * ```ts\n * // src/boot.ts\n * import { mountNativeHandler } from '@astroscope/node/native';\n *\n * export function onStartup() {\n * mountNativeHandler({ prefix: '/oidc', name: 'oidc' }, getOidcProvider().callback());\n * }\n * ```\n */\nexport function mountNativeHandler(matcher: NativeMountMatcher, handler: NativeHandler): () => void {\n if (!matcher.prefix && !matcher.match) {\n throw new Error('[@astroscope/node] mountNativeHandler requires a prefix or a match predicate');\n }\n\n const mount: Mount = {\n prefix: matcher.prefix,\n match: matcher.match,\n name: matcher.name ?? matcher.prefix,\n handler,\n };\n\n const store = getStore();\n\n store.mounts.push(mount);\n\n return () => {\n const index = store.mounts.indexOf(mount);\n\n if (index !== -1) store.mounts.splice(index, 1);\n };\n}\n\n/**\n * Remove every registered mount. Runs after `onShutdown` (prod) and between\n * dev generations, so re-running `onStartup` never stacks duplicates.\n */\nexport function clearNativeMounts(): void {\n getStore().mounts.length = 0;\n}\n\nfunction findMount(req: IncomingMessage): Mount | undefined {\n const url = req.url ?? '';\n const queryIndex = url.indexOf('?');\n const pathname = queryIndex === -1 ? url : url.slice(0, queryIndex);\n\n let best: Mount | undefined;\n\n for (const mount of getStore().mounts) {\n if (mount.prefix && matchesPrefix(pathname, mount.prefix)) {\n if (!best?.prefix || mount.prefix.length > best.prefix.length) {\n best = mount;\n }\n }\n }\n\n if (best) return best;\n\n return getStore().mounts.find((mount) => mount.match?.(req));\n}\n\nfunction failResponse(res: ServerResponse): void {\n if (res.writableEnded) return;\n\n if (!res.headersSent) {\n res.writeHead(500, { 'content-type': 'text/plain' });\n }\n\n res.end('Internal Server Error');\n}\n\n/**\n * Dispatch a request to a matching mount. Returns `false` when no mount\n * matches — the caller continues with static/astro handling.\n */\nexport function dispatchNativeMount(req: IncomingMessage, res: ServerResponse): boolean {\n const mount = findMount(req);\n\n if (!mount) return false;\n\n if (mount.name) {\n const record = getRequestRecord();\n\n if (record && !record.route) {\n record.route = mount.name;\n }\n\n const span = trace.getActiveSpan();\n\n if (span?.isRecording()) {\n span.setAttribute('http.route', mount.name);\n span.updateName(`${req.method ?? 'GET'} ${mount.name}`);\n }\n }\n\n try {\n const result = mount.handler(req, res);\n\n if (result instanceof Promise) {\n result.catch((err: unknown) => {\n log.error(err instanceof Error ? { err } : { reason: err }, 'native mount handler failed');\n failResponse(res);\n });\n }\n } catch (err) {\n log.error(err instanceof Error ? { err } : { reason: err }, 'native mount handler failed');\n failResponse(res);\n }\n\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,MAAM,YAAY,OAAO,IAAI,gCAAgC;AA0B7D,SAAS,WAAkB;CACzB,MAAM,IAAI;CACV,IAAI,QAAQ,EAAE;CAEd,IAAI,CAAC,OAAO;EACV,QAAQ,EAAE,QAAQ,CAAC,EAAE;EACrB,EAAE,aAAa;CACjB;CAEA,OAAO;AACT;AAEA,SAAS,cAAc,UAAkB,QAAyB;CAChE,IAAI,CAAC,SAAS,WAAW,MAAM,GAAG,OAAO;CAEzC,MAAM,OAAO,SAAS,MAAM,OAAO,MAAM;CAEzC,OAAO,SAAS,MAAM,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG;AACnE;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,mBAAmB,SAA6B,SAAoC;CAClG,IAAI,CAAC,QAAQ,UAAU,CAAC,QAAQ,OAC9B,MAAM,IAAI,MAAM,8EAA8E;CAGhG,MAAM,QAAe;EACnB,QAAQ,QAAQ;EAChB,OAAO,QAAQ;EACf,MAAM,QAAQ,QAAQ,QAAQ;EAC9B;CACF;CAEA,MAAM,QAAQ,SAAS;CAEvB,MAAM,OAAO,KAAK,KAAK;CAEvB,aAAa;EACX,MAAM,QAAQ,MAAM,OAAO,QAAQ,KAAK;EAExC,IAAI,UAAU,IAAI,MAAM,OAAO,OAAO,OAAO,CAAC;CAChD;AACF;;;;;AAMA,SAAgB,oBAA0B;CACxC,SAAS,CAAC,CAAC,OAAO,SAAS;AAC7B;AAEA,SAAS,UAAU,KAAyC;CAC1D,MAAM,MAAM,IAAI,OAAO;CACvB,MAAM,aAAa,IAAI,QAAQ,GAAG;CAClC,MAAM,WAAW,eAAe,KAAK,MAAM,IAAI,MAAM,GAAG,UAAU;CAElE,IAAI;CAEJ,KAAK,MAAM,SAAS,SAAS,CAAC,CAAC,QAC7B,IAAI,MAAM,UAAU,cAAc,UAAU,MAAM,MAAM;MAClD,CAAC,MAAM,UAAU,MAAM,OAAO,SAAS,KAAK,OAAO,QACrD,OAAO;CAAA;CAKb,IAAI,MAAM,OAAO;CAEjB,OAAO,SAAS,CAAC,CAAC,OAAO,MAAM,UAAU,MAAM,QAAQ,GAAG,CAAC;AAC7D;AAEA,SAAS,aAAa,KAA2B;CAC/C,IAAI,IAAI,eAAe;CAEvB,IAAI,CAAC,IAAI,aACP,IAAI,UAAU,KAAK,EAAE,gBAAgB,aAAa,CAAC;CAGrD,IAAI,IAAI,uBAAuB;AACjC;;;;;AAMA,SAAgB,oBAAoB,KAAsB,KAA8B;CACtF,MAAM,QAAQ,UAAU,GAAG;CAE3B,IAAI,CAAC,OAAO,OAAO;CAEnB,IAAI,MAAM,MAAM;EACd,MAAM,SAAS,iBAAiB;EAEhC,IAAI,UAAU,CAAC,OAAO,OACpB,OAAO,QAAQ,MAAM;EAGvB,MAAM,OAAO,MAAM,cAAc;EAEjC,IAAI,MAAM,YAAY,GAAG;GACvB,KAAK,aAAa,cAAc,MAAM,IAAI;GAC1C,KAAK,WAAW,GAAG,IAAI,UAAU,MAAM,GAAG,MAAM,MAAM;EACxD;CACF;CAEA,IAAI;EACF,MAAM,SAAS,MAAM,QAAQ,KAAK,GAAG;EAErC,IAAI,kBAAkB,SACpB,OAAO,OAAO,QAAiB;GAC7B,IAAI,MAAM,eAAe,QAAQ,EAAE,IAAI,IAAI,EAAE,QAAQ,IAAI,GAAG,6BAA6B;GACzF,aAAa,GAAG;EAClB,CAAC;CAEL,SAAS,KAAK;EACZ,IAAI,MAAM,eAAe,QAAQ,EAAE,IAAI,IAAI,EAAE,QAAQ,IAAI,GAAG,6BAA6B;EACzF,aAAa,GAAG;CAClB;CAEA,OAAO;AACT"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"prepare-DQEf2Bnt.js","names":["LIB_NAME"],"sources":["../src/lifecycle/lifecycle.ts","../src/observability/telemetry/metrics.ts","../src/observability/instrument.ts","../src/observability/log/construct.ts","../src/observability/telemetry/sdk.ts","../src/platform/env.ts","../src/platform/prepare.ts"],"sourcesContent":["import { emit } from './events.js';\nimport type { BootContext } from './types.js';\n\nexport interface BootModule {\n onStartup?: ((context: BootContext) => Promise<void> | void) | undefined;\n onShutdown?: ((context: BootContext) => Promise<void> | void) | undefined;\n}\n\nexport async function runStartup(boot: BootModule, context: BootContext): Promise<void> {\n await emit('beforeOnStartup', context);\n await boot.onStartup?.(context);\n await emit('afterOnStartup', context);\n}\n\nexport async function runShutdown(boot: BootModule, context: BootContext): Promise<void> {\n try {\n await emit('beforeOnShutdown', context);\n await boot.onShutdown?.(context);\n } finally {\n await emit('afterOnShutdown', context);\n }\n}\n","import { type Histogram, type UpDownCounter, ValueType, metrics } from '@opentelemetry/api';\n\nconst LIB_NAME = '@astroscope/node';\n\n// lazy initialization so instruments bind to the SDK meter provider\nlet httpRequestDuration: Histogram | null = null;\nlet httpActiveRequests: UpDownCounter | null = null;\nlet actionDuration: Histogram | null = null;\n\nfunction getHttpRequestDuration(): Histogram {\n return (httpRequestDuration ??= metrics.getMeter(LIB_NAME).createHistogram('http.server.request.duration', {\n description: 'Duration of HTTP server requests',\n unit: 's',\n valueType: ValueType.DOUBLE,\n }));\n}\n\nfunction getHttpActiveRequests(): UpDownCounter {\n return (httpActiveRequests ??= metrics.getMeter(LIB_NAME).createUpDownCounter('http.server.active_requests', {\n description: 'Number of active HTTP server requests',\n unit: '{request}',\n valueType: ValueType.INT,\n }));\n}\n\nfunction getActionDuration(): Histogram {\n return (actionDuration ??= metrics.getMeter(LIB_NAME).createHistogram('astro.action.duration', {\n description: 'Duration of Astro action executions',\n unit: 's',\n valueType: ValueType.DOUBLE,\n }));\n}\n\n/**\n * Record the start of an HTTP request. Returns a function to call when the\n * request ends. Route is unknown at the native-handler level, so active\n * requests carry only the method.\n */\nexport function recordHttpRequestStart(method: string): () => void {\n getHttpActiveRequests().add(1, { 'http.request.method': method });\n\n return () => {\n getHttpActiveRequests().add(-1, { 'http.request.method': method });\n };\n}\n\nexport function recordHttpRequestDuration(\n attributes: { method: string; route: string | undefined; status: number },\n durationMs: number,\n): void {\n getHttpRequestDuration().record(durationMs / 1000, {\n 'http.request.method': attributes.method,\n 'http.route': attributes.route ?? '',\n 'http.response.status_code': attributes.status,\n });\n}\n\nexport function recordActionDuration(attributes: { name: string; status: number }, durationMs: number): void {\n getActionDuration().record(durationMs / 1000, {\n 'astro.action.name': attributes.name,\n 'http.response.status_code': attributes.status,\n });\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http';\nimport { createMatcher } from '@entwico/dash/match';\nimport { ROOT_CONTEXT, SpanKind, SpanStatusCode, context, propagation, trace } from '@opentelemetry/api';\nimport type { Logger } from 'pino';\nimport type { ExcludePattern } from '../excludes/excludes.js';\nimport { generateReqId } from './log/index.js';\nimport { type RequestRecord, getLogStore } from './log/store.js';\nimport { recordActionDuration, recordHttpRequestDuration, recordHttpRequestStart } from './telemetry/metrics.js';\n\nconst LIB_NAME = '@astroscope/node';\nconst ACTIONS_PREFIX = '/_actions/';\nconst REQUEST_ID_PATTERN = /^[\\w.-]{1,64}$/;\n\nconst roundTime = (n: number) => Math.round(n * 100) / 100;\n\nexport interface RequestLoggingConfig {\n exclude: ExcludePattern[];\n extended: boolean;\n}\n\nexport interface RequestTelemetryConfig {\n exclude: ExcludePattern[];\n}\n\nexport interface RequestInstrumentationConfig {\n logging: RequestLoggingConfig | false;\n telemetry: RequestTelemetryConfig | false;\n}\n\nfunction getClientIp(req: IncomingMessage): string | undefined {\n const forwarded = req.headers['x-forwarded-for'];\n const first = Array.isArray(forwarded) ? forwarded[0] : forwarded;\n\n return (\n first?.split(',')[0]?.trim() ??\n (req.headers['x-real-ip'] as string | undefined) ??\n (req.headers['cf-connecting-ip'] as string | undefined)\n );\n}\n\nfunction resolveReqId(req: IncomingMessage): string {\n const incoming = req.headers['x-request-id'];\n const value = Array.isArray(incoming) ? incoming[0] : incoming;\n\n return value && REQUEST_ID_PATTERN.test(value) ? value : generateReqId();\n}\n\nfunction chunkSize(chunk: unknown): number {\n if (chunk == null) return 0;\n if (ArrayBuffer.isView(chunk)) return chunk.byteLength;\n if (typeof chunk === 'string') return Buffer.byteLength(chunk);\n\n return 0;\n}\n\n/**\n * Wraps the native request/response with logging and telemetry: a request\n * logger in async context (real status, response size, aborted-vs-completed\n * on `finish`/`close`), a SERVER span with propagation extraction, and\n * request metrics. Both concerns honor their own exclude patterns; when both\n * are excluded the request passes through untouched.\n */\nexport function createRequestInstrumentation(config: RequestInstrumentationConfig) {\n const tracer = trace.getTracer(LIB_NAME);\n const store = getLogStore();\n const loggingExcluded = config.logging ? createMatcher(config.logging.exclude) : () => true;\n const telemetryExcluded = config.telemetry ? createMatcher(config.telemetry.exclude) : () => true;\n\n return (req: IncomingMessage, res: ServerResponse, inner: () => void): void => {\n const url = req.url ?? '';\n const queryIndex = url.indexOf('?');\n const pathname = queryIndex === -1 ? url : url.slice(0, queryIndex);\n const method = req.method ?? 'GET';\n\n const logging = config.logging && !loggingExcluded(pathname) ? config.logging : false;\n const telemetry = config.telemetry && !telemetryExcluded(pathname) ? config.telemetry : false;\n\n if (!logging && !telemetry) {\n inner();\n\n return;\n }\n\n const startTime = performance.now();\n const isAction = pathname.startsWith(ACTIONS_PREFIX);\n\n let requestLogger: Logger | undefined;\n\n if (logging && store.root) {\n const reqId = resolveReqId(req);\n const reqData: Record<string, unknown> = { method, url: pathname };\n\n // extended logging includes potentially sensitive data\n if (logging.extended) {\n reqData['query'] = queryIndex === -1 ? '' : url.slice(queryIndex + 1);\n reqData['headers'] = req.headers;\n reqData['remoteAddress'] = getClientIp(req) ?? req.socket.remoteAddress;\n }\n\n requestLogger = store.root.child({ reqId, req: reqData });\n\n res.setHeader('x-request-id', reqId);\n }\n\n const record: RequestRecord = {\n logger: requestLogger,\n url,\n route: undefined,\n actionName: isAction ? pathname.slice(ACTIONS_PREFIX.length).replace(/\\/$/, '') : undefined,\n };\n\n let span: ReturnType<typeof tracer.startSpan> | undefined;\n let firstByteSpan: ReturnType<typeof tracer.startSpan> | undefined;\n let endActiveRequest: (() => void) | undefined;\n\n if (telemetry) {\n const parentContext = propagation.extract(ROOT_CONTEXT, req.headers);\n const contentLength = req.headers['content-length'];\n const clientIp = getClientIp(req);\n const host = req.headers['host'];\n\n span = tracer.startSpan(\n isAction ? `ACTION ${record.actionName}` : method,\n {\n kind: SpanKind.SERVER,\n attributes: {\n 'http.request.method': method,\n 'url.path': pathname,\n 'url.query': queryIndex === -1 ? '' : url.slice(queryIndex + 1),\n 'url.scheme': 'http',\n 'user_agent.original': req.headers['user-agent'] ?? '',\n ...(host && { 'server.address': host }),\n ...(contentLength && { 'http.request.body.size': parseInt(contentLength) }),\n ...(clientIp && { 'client.address': clientIp }),\n },\n },\n parentContext,\n );\n\n firstByteSpan = tracer.startSpan('response:first-byte', undefined, trace.setSpan(parentContext, span));\n\n endActiveRequest = recordHttpRequestStart(method);\n }\n\n let responseSize = 0;\n let firstByteTime: number | undefined;\n\n const originalWrite = res.write.bind(res);\n const originalEnd = res.end.bind(res);\n\n const markFirstByte = (): void => {\n if (firstByteTime !== undefined) return;\n\n firstByteTime = performance.now();\n\n if (firstByteSpan) {\n firstByteSpan.setAttribute('http.response.status_code', res.statusCode);\n firstByteSpan.end();\n }\n };\n\n res.write = ((chunk: unknown, ...rest: unknown[]) => {\n markFirstByte();\n responseSize += chunkSize(chunk);\n\n return (originalWrite as (...args: unknown[]) => boolean)(chunk, ...rest);\n }) as typeof res.write;\n\n res.end = ((chunk: unknown, ...rest: unknown[]) => {\n markFirstByte();\n responseSize += chunkSize(chunk);\n\n return (originalEnd as (...args: unknown[]) => ServerResponse)(chunk, ...rest);\n }) as typeof res.end;\n\n let finalized = false;\n\n const finalize = (aborted: boolean): void => {\n if (finalized) return;\n\n finalized = true;\n\n const status = res.statusCode;\n const responseTime = performance.now() - startTime;\n const ttfb = roundTime((firstByteTime ?? performance.now()) - startTime);\n\n if (requestLogger) {\n const level = status >= 500 ? 'error' : status >= 400 ? 'warn' : 'info';\n\n requestLogger[level](\n {\n res: { statusCode: status },\n responseTime: roundTime(responseTime),\n ttfb,\n responseSize,\n ...(record.route && { route: record.route }),\n ...(aborted && { aborted: true }),\n },\n aborted ? 'request aborted' : 'request completed',\n );\n }\n\n if (firstByteSpan && firstByteTime === undefined) {\n firstByteSpan.setStatus({ code: SpanStatusCode.ERROR, message: 'request aborted' });\n firstByteSpan.end();\n }\n\n if (span) {\n span.setAttribute('http.response.status_code', status);\n span.setAttribute('http.response.body.size', responseSize);\n span.setAttribute('ttfb', ttfb);\n\n if (aborted || status >= 400) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: aborted ? 'request aborted' : `HTTP ${status}` });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n\n span.end();\n }\n\n if (telemetry) {\n endActiveRequest?.();\n recordHttpRequestDuration({ method, route: record.route, status }, responseTime);\n\n if (record.actionName) {\n recordActionDuration({ name: record.actionName, status }, responseTime);\n }\n }\n };\n\n res.once('finish', () => finalize(false));\n res.once('close', () => finalize(!res.writableFinished));\n\n const run = (): void => store.requestStorage.run(record, inner);\n\n if (span) {\n context.with(trace.setSpan(context.active(), span), run);\n } else {\n run();\n }\n };\n}\n","import { isSpanContextValid, trace } from '@opentelemetry/api';\nimport pino, { type Bindings, type Logger, type LoggerOptions } from 'pino';\nimport { getLogStore } from './store.js';\n\n/**\n * Contract of the `src/log.ts` entry seam: pino logger options, or a factory\n * producing them. Never a logger instance — the platform constructs the\n * logger itself (after instrumentation, so trace correlation works).\n */\nexport type LoggerOptionsFactory = LoggerOptions | ((ctx: { dev: boolean }) => LoggerOptions | Promise<LoggerOptions>);\n\n/**\n * Compose the user mixin (if any) with platform trace correlation: when a\n * span is active, every entry carries `trace_id` / `span_id` / `trace_flags`.\n */\nfunction composeMixin(userMixin: LoggerOptions['mixin']): NonNullable<LoggerOptions['mixin']> {\n return (mergeObject, level, logger) => {\n const user = userMixin ? userMixin(mergeObject, level, logger) : {};\n const spanContext = trace.getActiveSpan()?.spanContext();\n\n if (!spanContext || !isSpanContextValid(spanContext)) return user;\n\n return {\n ...user,\n trace_id: spanContext.traceId,\n span_id: spanContext.spanId,\n trace_flags: `0${spanContext.traceFlags.toString(16)}`,\n };\n };\n}\n\n/**\n * Construct the root logger from the app's options seam and replay any logs\n * buffered before construction (original timestamps kept as `bufferedTime`).\n * In dev this runs once per generation; the buffer only exists the first time.\n */\nexport async function constructRootLogger(\n factory: LoggerOptionsFactory | undefined,\n ctx: { dev: boolean },\n): Promise<Logger> {\n const store = getLogStore();\n const options = (typeof factory === 'function' ? await factory(ctx) : factory) ?? {};\n const root = pino({ level: 'info', ...options, mixin: composeMixin(options.mixin) });\n\n store.root = root;\n\n for (const entry of store.buffer.splice(0)) {\n const bindings = entry.bindings.length ? (Object.assign({}, ...entry.bindings) as Bindings) : {};\n const target = root.child({ ...bindings, bufferedTime: new Date(entry.time).toISOString() });\n\n (target[entry.level] as (...args: unknown[]) => void)(...entry.args);\n }\n\n if (store.dropped > 0) {\n root.warn({ dropped: store.dropped }, 'early log buffer overflowed, entries dropped');\n store.dropped = 0;\n }\n\n return root;\n}\n\n/**\n * Failure path for startups that die before the logger exists: dump the\n * buffered entries to the console so no phase is silent.\n */\nexport function dumpEarlyLogs(): void {\n const store = getLogStore();\n\n if (store.root) return;\n\n for (const entry of store.buffer.splice(0)) {\n const bindings = entry.bindings.length ? Object.assign({}, ...entry.bindings) : undefined;\n\n console.error(\n new Date(entry.time).toISOString(),\n entry.level.toUpperCase(),\n ...(bindings ? [bindings] : []),\n ...entry.args,\n );\n }\n\n if (store.dropped > 0) {\n console.error(`(${store.dropped} early log entries dropped)`);\n store.dropped = 0;\n }\n}\n","import { log } from '../log/index.js';\n\n/**\n * Platform-owned telemetry bundle: NodeSDK with undici (fetch) and node\n * runtime instrumentation, host metrics, and a Prometheus reader. Trace\n * exporters are driven by standard `OTEL_*` env vars; without any of them\n * traces stay off (no failing localhost OTLP exports).\n *\n * Guarded per process (dev restarts are in-process; a re-created NodeSDK\n * would double-register instrumentations and leak the Prometheus port).\n */\n\nconst TELEMETRY_KEY = Symbol.for('@astroscope/node/telemetry');\n\ninterface TelemetryHandle {\n shutdown: () => Promise<void>;\n}\n\nexport interface TelemetrySdkOptions {\n prometheus: { host?: string | undefined; port?: number | undefined } | false;\n}\n\nfunction getHandle(): TelemetryHandle | undefined {\n return (globalThis as Record<symbol, unknown>)[TELEMETRY_KEY] as TelemetryHandle | undefined;\n}\n\nfunction defaultEnv(key: string, value: string): void {\n if (!process.env[key]) process.env[key] = value;\n}\n\nexport async function startTelemetry(options: TelemetrySdkOptions): Promise<void> {\n const g = globalThis as Record<symbol, unknown>;\n\n if (g[TELEMETRY_KEY]) return;\n\n if (process.env['OTEL_SDK_DISABLED'] === 'true') {\n log.debug('telemetry disabled via OTEL_SDK_DISABLED');\n\n return;\n }\n\n // without an explicitly configured exporter target, exporting traces to the\n // default localhost OTLP endpoint would fail on every flush\n if (!process.env['OTEL_EXPORTER_OTLP_ENDPOINT'] && !process.env['OTEL_EXPORTER_OTLP_TRACES_ENDPOINT']) {\n defaultEnv('OTEL_TRACES_EXPORTER', 'none');\n }\n\n defaultEnv('OTEL_METRICS_EXPORTER', 'none');\n defaultEnv('OTEL_LOGS_EXPORTER', 'none');\n\n const [\n { NodeSDK },\n { UndiciInstrumentation },\n { RuntimeNodeInstrumentation },\n { PrometheusExporter },\n { HostMetrics },\n ] = await Promise.all([\n import('@opentelemetry/sdk-node'),\n import('@opentelemetry/instrumentation-undici'),\n import('@opentelemetry/instrumentation-runtime-node'),\n import('@opentelemetry/exporter-prometheus'),\n import('@opentelemetry/host-metrics'),\n ]);\n\n const prometheus = options.prometheus\n ? {\n host: process.env['OTEL_EXPORTER_PROMETHEUS_HOST'] ?? options.prometheus.host ?? '0.0.0.0',\n port: process.env['OTEL_EXPORTER_PROMETHEUS_PORT']\n ? Number(process.env['OTEL_EXPORTER_PROMETHEUS_PORT'])\n : (options.prometheus.port ?? 9464),\n }\n : false;\n\n const sdk = new NodeSDK({\n instrumentations: [new UndiciInstrumentation(), new RuntimeNodeInstrumentation()],\n ...(prometheus && { metricReaders: [new PrometheusExporter(prometheus)] }),\n });\n\n sdk.start();\n\n const hostMetrics = new HostMetrics();\n\n hostMetrics.start();\n\n g[TELEMETRY_KEY] = {\n shutdown: () => sdk.shutdown(),\n } satisfies TelemetryHandle;\n\n if (prometheus) {\n log.debug({ host: prometheus.host, port: prometheus.port }, 'prometheus metrics listening');\n }\n}\n\n/**\n * Flush and shut the SDK down. Prod-only (dev keeps the SDK for the process\n * lifetime across generations).\n */\nexport async function shutdownTelemetry(): Promise<void> {\n const handle = getHandle();\n\n if (!handle) return;\n\n delete (globalThis as Record<symbol, unknown>)[TELEMETRY_KEY];\n\n await handle.shutdown();\n}\n","import fs from 'node:fs';\nimport { log } from '../observability/log/index.js';\n\n/**\n * Platform env loading (position −1, before the config seam):\n * `CONFIG_PATH` → `./.env` → none. Existing process env vars win\n */\nexport function loadEnvFiles(): void {\n const configPath = process.env['CONFIG_PATH'];\n\n if (configPath) {\n process.loadEnvFile(configPath);\n\n log.debug({ path: configPath }, 'loaded env file from CONFIG_PATH');\n\n return;\n }\n\n if (fs.existsSync('.env')) {\n process.loadEnvFile('.env');\n log.debug({ path: '.env' }, 'loaded env file');\n\n return;\n }\n\n log.debug('no env file loaded');\n}\n","import { type LoggerOptionsFactory, constructRootLogger } from '../observability/log/construct.js';\nimport { type TelemetrySdkOptions, startTelemetry } from '../observability/telemetry/sdk.js';\nimport { loadEnvFiles } from './env.js';\n\nconst INSTRUMENTATION_KEY = Symbol.for('@astroscope/node/instrumentation');\n\nexport interface InstrumentationContext {\n dev: boolean;\n}\n\ninterface InstrumentationSeam {\n register?: ((ctx: InstrumentationContext) => void | Promise<void>) | undefined;\n}\n\ninterface LogSeam {\n default?: LoggerOptionsFactory | undefined;\n}\n\nexport interface PlatformSeams {\n /** `src/config.ts` — validation runs at import; a throw fails the startup */\n config?: (() => Promise<unknown>) | undefined;\n /** `src/instrumentation.ts` — extra instrumentation, once per process */\n instrumentation?: (() => Promise<InstrumentationSeam>) | undefined;\n /** `src/log.ts` — pino logger options (or a factory), never an instance */\n log?: (() => Promise<LogSeam>) | undefined;\n}\n\nexport interface PreparePlatformOptions {\n dev: boolean;\n telemetry: TelemetrySdkOptions | false;\n seams: PlatformSeams;\n}\n\n/**\n * The platform sequence in front of the boot lifecycle:\n * env → config → instrumentation (platform SDK + `register`, once per\n * process) → logger construction (after instrumentation, so entries carry\n * trace correlation). Prod runs it once in `startServer()`; dev re-runs it\n * per generation with the once-per-process parts guarded.\n */\nexport async function preparePlatform(options: PreparePlatformOptions): Promise<void> {\n loadEnvFiles();\n\n await options.seams.config?.();\n\n const g = globalThis as Record<symbol, unknown>;\n\n if (!g[INSTRUMENTATION_KEY]) {\n g[INSTRUMENTATION_KEY] = true;\n\n if (options.telemetry) {\n await startTelemetry(options.telemetry);\n }\n\n const instrumentation = await options.seams.instrumentation?.();\n\n await instrumentation?.register?.({ dev: options.dev });\n }\n\n const logSeam = await options.seams.log?.();\n\n await constructRootLogger(logSeam?.default, { dev: options.dev });\n}\n"],"mappings":"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"}
|
package/dist/store-BIUF4lqk.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
//#region src/observability/log/store.ts
|
|
3
|
-
/**
|
|
4
|
-
* Shared state between the public `log` proxy and the server runtime. Keyed on
|
|
5
|
-
* `globalThis` because the two sides may live in different module instances
|
|
6
|
-
* (bundled app vs vite module runner in dev).
|
|
7
|
-
*/
|
|
8
|
-
const STORE_KEY = Symbol.for("@astroscope/node/log");
|
|
9
|
-
function getLogStore() {
|
|
10
|
-
const g = globalThis;
|
|
11
|
-
let store = g[STORE_KEY];
|
|
12
|
-
if (!store) {
|
|
13
|
-
store = {
|
|
14
|
-
root: void 0,
|
|
15
|
-
requestStorage: new AsyncLocalStorage(),
|
|
16
|
-
buffer: [],
|
|
17
|
-
dropped: 0
|
|
18
|
-
};
|
|
19
|
-
g[STORE_KEY] = store;
|
|
20
|
-
}
|
|
21
|
-
return store;
|
|
22
|
-
}
|
|
23
|
-
function getRequestRecord() {
|
|
24
|
-
return getLogStore().requestStorage.getStore();
|
|
25
|
-
}
|
|
26
|
-
//#endregion
|
|
27
|
-
export { getRequestRecord as n, getLogStore as t };
|
|
28
|
-
|
|
29
|
-
//# sourceMappingURL=store-BIUF4lqk.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"store-BIUF4lqk.js","names":[],"sources":["../src/observability/log/store.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport type { Logger } from 'pino';\n\n/**\n * Shared state between the public `log` proxy and the server runtime. Keyed on\n * `globalThis` because the two sides may live in different module instances\n * (bundled app vs vite module runner in dev).\n */\n\nconst STORE_KEY = Symbol.for('@astroscope/node/log');\n\nexport const EARLY_LOG_BUFFER_CAP = 100;\n\nexport interface BufferedEntry {\n level: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n bindings: Record<string, unknown>[];\n args: unknown[];\n time: number;\n}\n\nexport interface RequestRecord {\n logger: Logger | undefined;\n url: string;\n route: string | undefined;\n actionName: string | undefined;\n}\n\nexport interface LogStore {\n root: Logger | undefined;\n requestStorage: AsyncLocalStorage<RequestRecord>;\n buffer: BufferedEntry[];\n dropped: number;\n}\n\nexport function getLogStore(): LogStore {\n const g = globalThis as Record<symbol, unknown>;\n let store = g[STORE_KEY] as LogStore | undefined;\n\n if (!store) {\n store = { root: undefined, requestStorage: new AsyncLocalStorage(), buffer: [], dropped: 0 };\n g[STORE_KEY] = store;\n }\n\n return store;\n}\n\nexport function getRequestRecord(): RequestRecord | undefined {\n return getLogStore().requestStorage.getStore();\n}\n"],"mappings":";;;;;;;AASA,MAAM,YAAY,OAAO,IAAI,sBAAsB;AAyBnD,SAAgB,cAAwB;CACtC,MAAM,IAAI;CACV,IAAI,QAAQ,EAAE;CAEd,IAAI,CAAC,OAAO;EACV,QAAQ;GAAE,MAAM,KAAA;GAAW,gBAAgB,IAAI,kBAAkB;GAAG,QAAQ,CAAC;GAAG,SAAS;EAAE;EAC3F,EAAE,aAAa;CACjB;CAEA,OAAO;AACT;AAEA,SAAgB,mBAA8C;CAC5D,OAAO,YAAY,CAAC,CAAC,eAAe,SAAS;AAC/C"}
|