@astroscope/node 1.0.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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +286 -0
  3. package/dist/boot.d.ts +15 -0
  4. package/dist/boot.d.ts.map +1 -0
  5. package/dist/boot.js +2 -0
  6. package/dist/compress-B11aeRHQ.js +58 -0
  7. package/dist/compress-B11aeRHQ.js.map +1 -0
  8. package/dist/construct-DgB-jR0a.d.ts +14 -0
  9. package/dist/construct-DgB-jR0a.d.ts.map +1 -0
  10. package/dist/context-Bkg-FnnQ.js +20 -0
  11. package/dist/context-Bkg-FnnQ.js.map +1 -0
  12. package/dist/csrf-middleware-entrypoint.d.ts +5 -0
  13. package/dist/csrf-middleware-entrypoint.d.ts.map +1 -0
  14. package/dist/csrf-middleware-entrypoint.js +30 -0
  15. package/dist/csrf-middleware-entrypoint.js.map +1 -0
  16. package/dist/dev-middleware-entrypoint.d.ts +15 -0
  17. package/dist/dev-middleware-entrypoint.d.ts.map +1 -0
  18. package/dist/dev-middleware-entrypoint.js +26 -0
  19. package/dist/dev-middleware-entrypoint.js.map +1 -0
  20. package/dist/events-CUzQ2_cp.d.ts +20 -0
  21. package/dist/events-CUzQ2_cp.d.ts.map +1 -0
  22. package/dist/events-CgoM3Fvu.js +39 -0
  23. package/dist/events-CgoM3Fvu.js.map +1 -0
  24. package/dist/excludes-DLF3A_Cf.d.ts +63 -0
  25. package/dist/excludes-DLF3A_Cf.d.ts.map +1 -0
  26. package/dist/excludes-pE23EbmQ.js +78 -0
  27. package/dist/excludes-pE23EbmQ.js.map +1 -0
  28. package/dist/excludes.d.ts +2 -0
  29. package/dist/excludes.js +2 -0
  30. package/dist/generation-Bp2IA0jf.js +45 -0
  31. package/dist/generation-Bp2IA0jf.js.map +1 -0
  32. package/dist/health.d.ts +38 -0
  33. package/dist/health.d.ts.map +1 -0
  34. package/dist/health.js +31 -0
  35. package/dist/health.js.map +1 -0
  36. package/dist/index.d.ts +167 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +732 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/lifecycle/events.d.ts +2 -0
  41. package/dist/lifecycle/events.js +2 -0
  42. package/dist/log/index.d.ts +55 -0
  43. package/dist/log/index.d.ts.map +1 -0
  44. package/dist/log/index.js +2 -0
  45. package/dist/log-CSJlKaxY.js +112 -0
  46. package/dist/log-CSJlKaxY.js.map +1 -0
  47. package/dist/mime-C_GwZovh.js +39 -0
  48. package/dist/mime-C_GwZovh.js.map +1 -0
  49. package/dist/native-mount-hhwWdLtL.js +120 -0
  50. package/dist/native-mount-hhwWdLtL.js.map +1 -0
  51. package/dist/native.d.ts +48 -0
  52. package/dist/native.d.ts.map +1 -0
  53. package/dist/native.js +2 -0
  54. package/dist/prepare-Be2GgRyf.js +390 -0
  55. package/dist/prepare-Be2GgRyf.js.map +1 -0
  56. package/dist/preview.d.ts +12 -0
  57. package/dist/preview.d.ts.map +1 -0
  58. package/dist/preview.js +23 -0
  59. package/dist/preview.js.map +1 -0
  60. package/dist/restart-page.html +107 -0
  61. package/dist/route-middleware-entrypoint.d.ts +13 -0
  62. package/dist/route-middleware-entrypoint.d.ts.map +1 -0
  63. package/dist/route-middleware-entrypoint.js +24 -0
  64. package/dist/route-middleware-entrypoint.js.map +1 -0
  65. package/dist/server.d.ts +14 -0
  66. package/dist/server.d.ts.map +1 -0
  67. package/dist/server.js +441 -0
  68. package/dist/server.js.map +1 -0
  69. package/dist/store-8pnTxM1x.js +37 -0
  70. package/dist/store-8pnTxM1x.js.map +1 -0
  71. package/dist/store-BIUF4lqk.js +29 -0
  72. package/dist/store-BIUF4lqk.js.map +1 -0
  73. package/dist/types-D0uMBi2M.d.ts +12 -0
  74. package/dist/types-D0uMBi2M.d.ts.map +1 -0
  75. package/package.json +114 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","names":[],"sources":["../src/server/server.ts"],"mappings":";UA0DiB,YAAA;EACf,IAAA;EACA,IAAA;EACA,IAAA,IAAQ,OAAA;EACR,MAAA,IAAU,OAAO;AAAA;AAAA,iBAGG,WAAA,CAAY,SAAA;EAChC,IAAA;EACA,IAAA;AAAA,IACE,OAAO,CAAC,YAAA"}
package/dist/server.js ADDED
@@ -0,0 +1,441 @@
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-Be2GgRyf.js";
3
+ import { n as getRequestRecord } from "./store-BIUF4lqk.js";
4
+ import { n as log } from "./log-CSJlKaxY.js";
5
+ import { n as dispatchNativeMount, t as clearNativeMounts } from "./native-mount-hhwWdLtL.js";
6
+ import { n as deactivateHealthChecks, t as activateHealthChecks } from "./store-8pnTxM1x.js";
7
+ import { n as MIME_TYPES, t as COMPRESSIBLE } from "./mime-C_GwZovh.js";
8
+ import fs, { createReadStream } from "node:fs";
9
+ import path from "node:path";
10
+ import url from "node:url";
11
+ import { SpanStatusCode, context, trace } from "@opentelemetry/api";
12
+ import http from "node:http";
13
+ import { checks, probes, server } from "@entwico/health-probes";
14
+ import { createApp } from "astro/app/entrypoint";
15
+ import { setGetEnv } from "astro/env/setup";
16
+ import * as bootModule from "virtual:@astroscope/node/boot";
17
+ import { options } from "virtual:@astroscope/node/config";
18
+ import { Readable } from "node:stream";
19
+ import { createRequestFromNodeRequest, writeResponse } from "astro/app/node";
20
+ import send from "send";
21
+ //#region src/observability/telemetry/lifecycle.ts
22
+ const LIB_NAME = "@astroscope/node";
23
+ /**
24
+ * Lifecycle spans (`startup` / `shutdown` with phase children). No-op when no
25
+ * SDK is registered — `trace.getTracer` returns the no-op tracer.
26
+ */
27
+ function startLifecycleSpan(name, parent) {
28
+ const parentContext = parent ?? context.active();
29
+ const span = trace.getTracer(LIB_NAME).startSpan(name, void 0, parentContext);
30
+ return {
31
+ span,
32
+ context: trace.setSpan(parentContext, span)
33
+ };
34
+ }
35
+ async function withLifecycleSpan(name, parent, fn) {
36
+ const { span, context: spanContext } = startLifecycleSpan(name, parent);
37
+ try {
38
+ const result = await context.with(spanContext, fn);
39
+ span.setStatus({ code: SpanStatusCode.OK });
40
+ return result;
41
+ } catch (err) {
42
+ span.setStatus({
43
+ code: SpanStatusCode.ERROR,
44
+ message: err instanceof Error ? err.message : "unknown error"
45
+ });
46
+ throw err;
47
+ } finally {
48
+ span.end();
49
+ }
50
+ }
51
+ //#endregion
52
+ //#region src/server/client-dir.ts
53
+ /**
54
+ * Resolve the client directory at runtime relative to the built server entry.
55
+ *
56
+ * The build-time client/server URLs are only valid on the build machine; in a
57
+ * container the deploy path differs. Walk up from `import.meta.url` of the
58
+ * bundled server code until the server directory is found, then apply the
59
+ * build-time server→client relative path.
60
+ */
61
+ function resolveClientDir(options, importMetaUrl) {
62
+ const clientPath = url.fileURLToPath(new URL(options.client));
63
+ const serverPath = url.fileURLToPath(new URL(options.server));
64
+ const rel = path.relative(serverPath, clientPath);
65
+ const serverFolder = path.basename(serverPath);
66
+ let serverEntryFolderURL = path.dirname(importMetaUrl);
67
+ let previous = "";
68
+ while (!serverEntryFolderURL.endsWith(serverFolder)) {
69
+ if (serverEntryFolderURL === previous) throw new Error(`[@astroscope/node] could not find the server directory "${serverFolder}" by walking up from "${importMetaUrl}"`);
70
+ previous = serverEntryFolderURL;
71
+ serverEntryFolderURL = path.dirname(serverEntryFolderURL);
72
+ }
73
+ const clientURL = new URL(rel.endsWith("/") ? rel : `${rel}/`, `${serverEntryFolderURL}/entry.mjs`);
74
+ return url.fileURLToPath(clientURL);
75
+ }
76
+ //#endregion
77
+ //#region src/server/serve-app.ts
78
+ async function readFSErrorPage(client, status) {
79
+ const filePaths = [`${status}.html`, `${status}/index.html`];
80
+ for (const filePath of filePaths) {
81
+ const fullPath = path.join(client, filePath);
82
+ let stream;
83
+ try {
84
+ stream = createReadStream(fullPath);
85
+ await new Promise((resolve, reject) => {
86
+ stream.once("open", () => resolve());
87
+ stream.once("error", reject);
88
+ });
89
+ return new Response(Readable.toWeb(stream), { headers: { "Content-Type": "text/html; charset=utf-8" } });
90
+ } catch {
91
+ stream?.destroy();
92
+ }
93
+ }
94
+ }
95
+ /**
96
+ * Render on-demand routes: node req → web Request → `app.render()` → node res.
97
+ * Prerendered pages never reach this handler (the static handler serves them);
98
+ * requests for them landing here render the 404 route.
99
+ */
100
+ function createAppHandler(app, options, client) {
101
+ process.on("unhandledRejection", (reason) => {
102
+ const requestUrl = getRequestRecord()?.url;
103
+ log.error({
104
+ ...reason instanceof Error ? { err: reason } : { reason },
105
+ ...requestUrl && { url: requestUrl }
106
+ }, requestUrl ? "unhandled rejection while rendering" : "unhandled rejection");
107
+ });
108
+ const prerenderedErrorPageFetch = async (url) => {
109
+ const { pathname } = new URL(url);
110
+ for (const status of [404, 500]) if (pathname.endsWith(`/${status}.html`) || pathname.endsWith(`/${status}/index.html`)) {
111
+ const response = await readFSErrorPage(client, status);
112
+ if (response) return response;
113
+ }
114
+ return new Response(null, { status: 404 });
115
+ };
116
+ const bodySizeLimit = options.bodySizeLimit === 0 || options.bodySizeLimit === Number.POSITIVE_INFINITY ? void 0 : options.bodySizeLimit;
117
+ return async (req, res) => {
118
+ let request;
119
+ try {
120
+ request = createRequestFromNodeRequest(req, {
121
+ allowedDomains: app.getAllowedDomains?.() ?? [],
122
+ ...bodySizeLimit !== void 0 && { bodySizeLimit },
123
+ port: options.port
124
+ });
125
+ } catch (err) {
126
+ log.error(err instanceof Error ? {
127
+ err,
128
+ url: req.url
129
+ } : {
130
+ reason: err,
131
+ url: req.url
132
+ }, "could not render");
133
+ res.statusCode = 500;
134
+ res.end("Internal Server Error");
135
+ return;
136
+ }
137
+ const routeData = app.match(request, true);
138
+ await writeResponse(routeData && !(routeData.type === "page" && routeData.prerender) ? await app.render(request, {
139
+ addCookieHeader: true,
140
+ routeData,
141
+ prerenderedErrorPageFetch
142
+ }) : await app.render(request, {
143
+ addCookieHeader: true,
144
+ prerenderedErrorPageFetch
145
+ }), res);
146
+ };
147
+ }
148
+ //#endregion
149
+ //#region src/server/serve-static.ts
150
+ const VARIANTS = [{
151
+ encoding: "br",
152
+ suffix: ".br"
153
+ }, {
154
+ encoding: "gzip",
155
+ suffix: ".gz"
156
+ }];
157
+ function negotiateVariant(req, client, pathname) {
158
+ const accept = req.headers["accept-encoding"];
159
+ if (typeof accept !== "string") return void 0;
160
+ for (const { encoding, suffix } of VARIANTS) {
161
+ if (!accept.includes(encoding)) continue;
162
+ if (fs.existsSync(path.join(client, `${pathname}${suffix}`))) return {
163
+ pathname: `${pathname}${suffix}`,
164
+ encoding
165
+ };
166
+ }
167
+ }
168
+ function hasFileExtension(pathname) {
169
+ const last = pathname.split("/").pop();
170
+ return !!last && last.includes(".");
171
+ }
172
+ function prependForwardSlash(pathname) {
173
+ return pathname.startsWith("/") ? pathname : `/${pathname}`;
174
+ }
175
+ function isDirectory(client, urlPath) {
176
+ const filePath = path.join(client, urlPath);
177
+ const resolved = path.resolve(filePath);
178
+ const resolvedClient = path.resolve(client);
179
+ if (resolved !== resolvedClient && !resolved.startsWith(resolvedClient + path.sep)) return false;
180
+ try {
181
+ return fs.lstatSync(filePath).isDirectory();
182
+ } catch {
183
+ return false;
184
+ }
185
+ }
186
+ /**
187
+ * Serve files from the client build directory, falling through to `ssr` when
188
+ * no file matches. Handles trailing-slash redirects per the manifest config
189
+ * and marks hashed assets as immutable.
190
+ */
191
+ function createStaticHandler(app, client) {
192
+ return (req, res, ssr) => {
193
+ if (!req.url) {
194
+ ssr();
195
+ return;
196
+ }
197
+ let fullUrl = req.url;
198
+ if (fullUrl.includes("#")) fullUrl = fullUrl.slice(0, fullUrl.indexOf("#"));
199
+ const [urlPath = "", urlQuery] = fullUrl.split("?");
200
+ let fsPath = app.removeBase(urlPath);
201
+ try {
202
+ fsPath = decodeURI(fsPath);
203
+ } catch {}
204
+ const dir = isDirectory(client, fsPath);
205
+ const hasSlash = urlPath.endsWith("/");
206
+ let pathname = urlPath;
207
+ switch (app.manifest.trailingSlash) {
208
+ case "never":
209
+ if (dir && urlPath !== "/" && hasSlash) {
210
+ res.statusCode = 301;
211
+ res.setHeader("Location", urlPath.slice(0, -1) + (urlQuery ? `?${urlQuery}` : ""));
212
+ res.end();
213
+ return;
214
+ }
215
+ if (dir && !hasSlash) pathname = `${urlPath}/index.html`;
216
+ break;
217
+ case "ignore":
218
+ if (dir && !hasSlash) pathname = `${urlPath}/index.html`;
219
+ break;
220
+ case "always":
221
+ if (!hasSlash && !hasFileExtension(urlPath) && !urlPath.startsWith("/_")) {
222
+ res.statusCode = 301;
223
+ res.setHeader("Location", `${urlPath}/${urlQuery ? `?${urlQuery}` : ""}`);
224
+ res.end();
225
+ return;
226
+ }
227
+ break;
228
+ }
229
+ pathname = prependForwardSlash(app.removeBase(pathname));
230
+ const normalizedPathname = path.posix.normalize(pathname);
231
+ const compressible = COMPRESSIBLE.has(path.posix.extname(normalizedPathname));
232
+ const variant = compressible ? negotiateVariant(req, client, normalizedPathname) : void 0;
233
+ const stream = send(req, variant?.pathname ?? normalizedPathname, {
234
+ root: client,
235
+ dotfiles: normalizedPathname.startsWith("/.well-known/") ? "allow" : "deny",
236
+ extensions: app.manifest.buildFormat === "file" || app.manifest.buildFormat === "preserve" ? ["html"] : []
237
+ });
238
+ let forwardError = false;
239
+ stream.on("error", (err) => {
240
+ if (forwardError) {
241
+ const status = err.statusCode ?? 500;
242
+ if (status >= 500) console.error(err.toString());
243
+ res.writeHead(status);
244
+ res.end(status >= 500 ? "Internal server error" : "");
245
+ return;
246
+ }
247
+ ssr();
248
+ });
249
+ stream.on("file", () => {
250
+ forwardError = true;
251
+ });
252
+ stream.on("headers", (headersRes) => {
253
+ if (compressible) headersRes.setHeader("Vary", "Accept-Encoding");
254
+ if (variant) {
255
+ headersRes.setHeader("Content-Encoding", variant.encoding);
256
+ headersRes.setHeader("Content-Type", MIME_TYPES.get(path.posix.extname(normalizedPathname)) ?? "application/octet-stream");
257
+ }
258
+ if (normalizedPathname.startsWith(`/${app.manifest.assetsDir}/`)) headersRes.setHeader("Cache-Control", "public, max-age=31536000, immutable");
259
+ });
260
+ stream.pipe(res);
261
+ };
262
+ }
263
+ //#endregion
264
+ //#region src/server/server.ts
265
+ setGetEnv((key) => process.env[key]);
266
+ const runtimeOptions = options;
267
+ const app = createApp({ streaming: true });
268
+ const roundMs = (n) => Math.round(n * 100) / 100;
269
+ /**
270
+ * Pre-import every lazily loaded server module (pages, middleware, actions,
271
+ * session driver) so the first request pays no import cost. Uses the
272
+ * manifest's own loaders — exactly what the runtime calls per request.
273
+ */
274
+ async function warmupModules() {
275
+ const loaders = [
276
+ ...app.manifest.pageMap?.values() ?? [],
277
+ app.manifest.middleware,
278
+ app.manifest.actions,
279
+ app.manifest.sessionDriver,
280
+ app.manifest.serverIslandMappings
281
+ ].filter((load) => load !== void 0);
282
+ const results = await Promise.allSettled(loaders.map((load) => load()));
283
+ for (const result of results) if (result.status === "rejected") log.error(result.reason instanceof Error ? { err: result.reason } : { reason: result.reason }, "warmup import failed");
284
+ }
285
+ async function startServer(overrides) {
286
+ const startedAt = performance.now();
287
+ const host = overrides?.host ?? process.env["HOST"] ?? runtimeOptions.host;
288
+ const port = overrides?.port ?? (process.env["PORT"] ? Number(process.env["PORT"]) : runtimeOptions.port);
289
+ const context = {
290
+ dev: false,
291
+ host,
292
+ port
293
+ };
294
+ const health = runtimeOptions.health;
295
+ setBootContext(context);
296
+ try {
297
+ await preparePlatform({
298
+ dev: false,
299
+ telemetry: runtimeOptions.telemetry ? { prometheus: runtimeOptions.telemetry.prometheus } : false,
300
+ seams: {
301
+ config: () => import("virtual:@astroscope/node/config-entry"),
302
+ instrumentation: () => import("virtual:@astroscope/node/instrumentation-entry"),
303
+ log: () => import("virtual:@astroscope/node/log-entry")
304
+ }
305
+ });
306
+ } catch (err) {
307
+ dumpEarlyLogs();
308
+ console.error(err);
309
+ process.exit(1);
310
+ }
311
+ if (health) {
312
+ server.start({
313
+ ...health,
314
+ host: health.host ?? process.env["HEALTH_HOST"] ?? "0.0.0.0"
315
+ });
316
+ probes.live.enable();
317
+ activateHealthChecks(checks);
318
+ log.debug("health probes listening");
319
+ }
320
+ const startup = startLifecycleSpan("startup");
321
+ let bootMs = 0;
322
+ let warmupMs = 0;
323
+ const warmupStartedAt = performance.now();
324
+ const warmupSpan = startLifecycleSpan("warmup", startup.context);
325
+ const warmup = warmupModules().then(() => {
326
+ warmupMs = roundMs(performance.now() - warmupStartedAt);
327
+ warmupSpan.span.end();
328
+ });
329
+ const shutdownLifecycle = async (shutdownContext) => {
330
+ try {
331
+ if (shutdownContext) await withLifecycleSpan("onShutdown", shutdownContext, () => runShutdown(bootModule, context));
332
+ else await runShutdown(bootModule, context);
333
+ } catch (err) {
334
+ log.error(err instanceof Error ? { err } : { reason: err }, "shutdown failed");
335
+ }
336
+ clearNativeMounts();
337
+ if (health) {
338
+ deactivateHealthChecks();
339
+ await server.stop();
340
+ }
341
+ };
342
+ const failStartup = async (err, message) => {
343
+ log.error(err instanceof Error ? { err } : { reason: err }, message);
344
+ startup.span.setStatus({
345
+ code: SpanStatusCode.ERROR,
346
+ message
347
+ });
348
+ startup.span.end();
349
+ await shutdownLifecycle();
350
+ await shutdownTelemetry();
351
+ process.exit(1);
352
+ };
353
+ try {
354
+ const bootStartedAt = performance.now();
355
+ await withLifecycleSpan("boot", startup.context, () => runStartup(bootModule, context));
356
+ bootMs = roundMs(performance.now() - bootStartedAt);
357
+ } catch (err) {
358
+ await failStartup(err, "startup failed");
359
+ }
360
+ await warmup;
361
+ if (health) probes.startup.enable();
362
+ const client = resolveClientDir(runtimeOptions, import.meta.url);
363
+ const appHandler = createAppHandler(app, runtimeOptions, client);
364
+ const staticHandler = createStaticHandler(app, client);
365
+ const instrument = createRequestInstrumentation({
366
+ logging: runtimeOptions.logging,
367
+ telemetry: runtimeOptions.telemetry ? { exclude: runtimeOptions.telemetry.exclude } : false
368
+ });
369
+ const server$1 = http.createServer((req, res) => {
370
+ try {
371
+ decodeURI(req.url ?? "");
372
+ } catch {
373
+ res.writeHead(400);
374
+ res.end("Bad request.");
375
+ return;
376
+ }
377
+ instrument(req, res, () => {
378
+ if (dispatchNativeMount(req, res)) return;
379
+ staticHandler(req, res, () => void appHandler(req, res));
380
+ });
381
+ });
382
+ try {
383
+ await withLifecycleSpan("listen", startup.context, () => {
384
+ return new Promise((resolve, reject) => {
385
+ server$1.once("error", reject);
386
+ server$1.listen(port, host, resolve);
387
+ });
388
+ });
389
+ } catch (err) {
390
+ await failStartup(err, `failed to listen on ${host}:${port}`);
391
+ }
392
+ if (health) probes.ready.enable();
393
+ startup.span.setStatus({ code: SpanStatusCode.OK });
394
+ startup.span.end();
395
+ log.info({
396
+ host,
397
+ port,
398
+ health: !!health,
399
+ bootMs,
400
+ warmupMs,
401
+ totalMs: roundMs(performance.now() - startedAt)
402
+ }, "server ready");
403
+ let stopPromise;
404
+ let resolveClosed;
405
+ const closedPromise = new Promise((resolve) => {
406
+ resolveClosed = resolve;
407
+ });
408
+ const doStop = async () => {
409
+ if (health) probes.ready.disable();
410
+ log.info("draining");
411
+ const drainStartedAt = performance.now();
412
+ const shutdown = startLifecycleSpan("shutdown");
413
+ await withLifecycleSpan("drain", shutdown.context, async () => {
414
+ const closed = new Promise((resolve) => server$1.close(() => resolve()));
415
+ server$1.closeIdleConnections();
416
+ const forceTimer = setTimeout(() => server$1.closeAllConnections(), runtimeOptions.shutdownTimeout);
417
+ await closed;
418
+ clearTimeout(forceTimer);
419
+ });
420
+ const drainMs = roundMs(performance.now() - drainStartedAt);
421
+ await shutdownLifecycle(shutdown.context);
422
+ shutdown.span.end();
423
+ log.info({ drainMs }, "shutdown complete");
424
+ await shutdownTelemetry();
425
+ resolveClosed();
426
+ };
427
+ const stop = () => stopPromise ??= doStop();
428
+ process.once("SIGTERM", () => void stop().then(() => process.exit(0)));
429
+ process.once("SIGINT", () => void stop().then(() => process.exit(0)));
430
+ return {
431
+ host,
432
+ port,
433
+ stop,
434
+ closed: () => closedPromise
435
+ };
436
+ }
437
+ if (process.env["ASTROSCOPE_NODE_AUTOSTART"] !== "disabled") await startServer();
438
+ //#endregion
439
+ export { startServer };
440
+
441
+ //# sourceMappingURL=server.js.map
@@ -0,0 +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 http from 'node:http';\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 * as bootModule from 'virtual:@astroscope/node/boot';\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\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 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 as BootModule, context));\n } else {\n await runShutdown(bootModule as 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 await withLifecycleSpan('boot', startup.context, () => runStartup(bootModule as 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 const server = http.createServer((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 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 { host, port, health: !!health, bootMs, warmupMs, totalMs: roundMs(performance.now() - startedAt) },\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('draining');\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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"}
@@ -0,0 +1,37 @@
1
+ //#region src/health/store.ts
2
+ /**
3
+ * Shared state between the public `registerHealthCheck` API and the server
4
+ * runtime. Keyed on `globalThis` because the two sides may live in different
5
+ * module instances (bundled app vs vite module runner in dev).
6
+ */
7
+ const STORE_KEY = Symbol.for("@astroscope/node/health");
8
+ function getHealthStore() {
9
+ const g = globalThis;
10
+ let store = g[STORE_KEY];
11
+ if (!store) {
12
+ store = {
13
+ registry: void 0,
14
+ unregisters: /* @__PURE__ */ new Map()
15
+ };
16
+ g[STORE_KEY] = store;
17
+ }
18
+ return store;
19
+ }
20
+ function activateHealthChecks(registry) {
21
+ getHealthStore().registry = registry;
22
+ }
23
+ /**
24
+ * Remove every check still registered and detach the registry. Runs after
25
+ * `onShutdown`, so consumers get first chance to unregister themselves; this
26
+ * guarantees a clean registry no matter what they forgot.
27
+ */
28
+ function deactivateHealthChecks() {
29
+ const store = getHealthStore();
30
+ for (const unregister of store.unregisters.values()) unregister();
31
+ store.unregisters.clear();
32
+ store.registry = void 0;
33
+ }
34
+ //#endregion
35
+ export { deactivateHealthChecks as n, getHealthStore as r, activateHealthChecks as t };
36
+
37
+ //# sourceMappingURL=store-8pnTxM1x.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-8pnTxM1x.js","names":[],"sources":["../src/health/store.ts"],"sourcesContent":["/**\n * Shared state between the public `registerHealthCheck` API and the server\n * runtime. Keyed on `globalThis` because the two sides may live in different\n * module instances (bundled app vs vite module runner in dev).\n */\n\nconst STORE_KEY = Symbol.for('@astroscope/node/health');\n\nexport interface RegistrableCheck {\n name: string;\n check: () => Promise<{ status: 'healthy' | 'unhealthy' } | void> | { status: 'healthy' | 'unhealthy' } | void;\n optional?: boolean;\n timeout?: number;\n}\n\nexport interface ChecksRegistry {\n register(check: RegistrableCheck): () => void;\n}\n\ninterface Store {\n registry: ChecksRegistry | undefined;\n unregisters: Map<string, () => void>;\n}\n\nexport function getHealthStore(): Store {\n const g = globalThis as Record<symbol, unknown>;\n let store = g[STORE_KEY] as Store | undefined;\n\n if (!store) {\n store = { registry: undefined, unregisters: new Map() };\n g[STORE_KEY] = store;\n }\n\n return store;\n}\n\nexport function activateHealthChecks(registry: ChecksRegistry): void {\n getHealthStore().registry = registry;\n}\n\n/**\n * Remove every check still registered and detach the registry. Runs after\n * `onShutdown`, so consumers get first chance to unregister themselves; this\n * guarantees a clean registry no matter what they forgot.\n */\nexport function deactivateHealthChecks(): void {\n const store = getHealthStore();\n\n for (const unregister of store.unregisters.values()) {\n unregister();\n }\n\n store.unregisters.clear();\n store.registry = undefined;\n}\n"],"mappings":";;;;;;AAMA,MAAM,YAAY,OAAO,IAAI,yBAAyB;AAkBtD,SAAgB,iBAAwB;CACtC,MAAM,IAAI;CACV,IAAI,QAAQ,EAAE;CAEd,IAAI,CAAC,OAAO;EACV,QAAQ;GAAE,UAAU,KAAA;GAAW,6BAAa,IAAI,IAAI;EAAE;EACtD,EAAE,aAAa;CACjB;CAEA,OAAO;AACT;AAEA,SAAgB,qBAAqB,UAAgC;CACnE,eAAe,CAAC,CAAC,WAAW;AAC9B;;;;;;AAOA,SAAgB,yBAA+B;CAC7C,MAAM,QAAQ,eAAe;CAE7B,KAAK,MAAM,cAAc,MAAM,YAAY,OAAO,GAChD,WAAW;CAGb,MAAM,YAAY,MAAM;CACxB,MAAM,WAAW,KAAA;AACnB"}
@@ -0,0 +1,29 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,12 @@
1
+ //#region src/lifecycle/types.d.ts
2
+ interface BootContext {
3
+ /** Whether running in development mode (vite dev server) */
4
+ dev: boolean;
5
+ /** Server host from Astro config */
6
+ host: string;
7
+ /** Server port from Astro config */
8
+ port: number;
9
+ }
10
+ //#endregion
11
+ export { BootContext as t };
12
+ //# sourceMappingURL=types-D0uMBi2M.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-D0uMBi2M.d.ts","names":[],"sources":["../src/lifecycle/types.ts"],"mappings":";UAAiB,WAAA;EAAA;EAEf,GAAA;;EAEA,IAAA;EAFA;EAIA,IAAA;AAAA"}