@astroscope/node 2.1.0 → 3.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 (70) hide show
  1. package/README.md +8 -5
  2. package/dist/boot.d.ts +2 -2
  3. package/dist/boot.d.ts.map +1 -1
  4. package/dist/csrf-middleware-entrypoint.d.ts +1 -2
  5. package/dist/csrf-middleware-entrypoint.d.ts.map +1 -1
  6. package/dist/csrf-middleware-entrypoint.js +1 -1
  7. package/dist/dev-middleware-entrypoint.d.ts +1 -2
  8. package/dist/dev-middleware-entrypoint.d.ts.map +1 -1
  9. package/dist/{prepare-CXZsyAVk.js → duplicate-slashes-C6hmOLl9.js} +94 -38
  10. package/dist/duplicate-slashes-C6hmOLl9.js.map +1 -0
  11. package/dist/{excludes-pE23EbmQ.js → excludes-10eDopVB.js} +18 -4
  12. package/dist/excludes-10eDopVB.js.map +1 -0
  13. package/dist/{excludes-BDiE3eyp.d.ts → excludes-rvkVGg6B.d.ts} +11 -3
  14. package/dist/excludes-rvkVGg6B.d.ts.map +1 -0
  15. package/dist/excludes.d.ts +2 -2
  16. package/dist/excludes.js +2 -2
  17. package/dist/graph-DdAxiaxa.js +35 -0
  18. package/dist/graph-DdAxiaxa.js.map +1 -0
  19. package/dist/health.d.ts +3 -4
  20. package/dist/health.d.ts.map +1 -1
  21. package/dist/image-endpoint.d.ts +1 -2
  22. package/dist/image-endpoint.d.ts.map +1 -1
  23. package/dist/index.d.ts +4 -4
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +38 -122
  26. package/dist/index.js.map +1 -1
  27. package/dist/islands-middleware-entrypoint.d.ts +1 -2
  28. package/dist/islands-middleware-entrypoint.d.ts.map +1 -1
  29. package/dist/islands-middleware-entrypoint.js +3 -1
  30. package/dist/islands-middleware-entrypoint.js.map +1 -1
  31. package/dist/islands-runtime.iife.js +1 -1
  32. package/dist/islands.d.ts +57 -2
  33. package/dist/islands.d.ts.map +1 -0
  34. package/dist/islands.js +3 -1
  35. package/dist/log/index.d.ts +5 -5
  36. package/dist/log/index.d.ts.map +1 -1
  37. package/dist/native.d.ts +5 -6
  38. package/dist/native.d.ts.map +1 -1
  39. package/dist/{prerendered-DVjzWNbW.js → prerendered-Z-Qi-FFK.js} +4 -4
  40. package/dist/prerendered-Z-Qi-FFK.js.map +1 -0
  41. package/dist/request-route-DcnZOOM4.js.map +1 -1
  42. package/dist/route-islands-B53rE5MH.js +73 -0
  43. package/dist/route-islands-B53rE5MH.js.map +1 -0
  44. package/dist/route-middleware-entrypoint.d.ts +1 -2
  45. package/dist/route-middleware-entrypoint.d.ts.map +1 -1
  46. package/dist/{route-store-CSYR4G5e.d.ts → route-store-neUA2nBb.d.ts} +10 -14
  47. package/dist/route-store-neUA2nBb.d.ts.map +1 -0
  48. package/dist/routes-D1o_WrJ5.js +39 -0
  49. package/dist/routes-D1o_WrJ5.js.map +1 -0
  50. package/dist/server.d.ts +2 -3
  51. package/dist/server.d.ts.map +1 -1
  52. package/dist/server.js +68 -45
  53. package/dist/server.js.map +1 -1
  54. package/dist/telemetry-B9aziFyQ.js +202 -0
  55. package/dist/telemetry-B9aziFyQ.js.map +1 -0
  56. package/dist/telemetry.d.ts +68 -0
  57. package/dist/telemetry.d.ts.map +1 -0
  58. package/dist/telemetry.js +2 -0
  59. package/dist/transform-Bz5z5w2Y.js +594 -0
  60. package/dist/transform-Bz5z5w2Y.js.map +1 -0
  61. package/package.json +13 -11
  62. package/dist/excludes-BDiE3eyp.d.ts.map +0 -1
  63. package/dist/excludes-pE23EbmQ.js.map +0 -1
  64. package/dist/prepare-CXZsyAVk.js.map +0 -1
  65. package/dist/prerendered-DVjzWNbW.js.map +0 -1
  66. package/dist/route-store-CSYR4G5e.d.ts.map +0 -1
  67. package/dist/strip-build-paths-C6eb1QdG.js +0 -63
  68. package/dist/strip-build-paths-C6eb1QdG.js.map +0 -1
  69. package/dist/transform-BqV8SOEm.js +0 -195
  70. package/dist/transform-BqV8SOEm.js.map +0 -1
@@ -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';\nimport { setRequestRouteData } from './route-store.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 const matched = routeData && !(routeData.type === 'page' && routeData.prerender) ? routeData : undefined;\n\n if (matched) {\n setRequestRouteData(request, matched);\n }\n\n const response = matched\n ? await app.render(request, { addCookieHeader: true, routeData: matched, 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
+ {"version":3,"file":"server.js","names":["healthServer","server"],"sources":["../src/server/client-dir.ts","../src/server/serve-app.ts","../src/server/serve-static.ts","../src/server/server.ts"],"sourcesContent":["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, OutgoingHttpHeaders, ServerResponse } from 'node:http';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { BaseApp } from 'astro/app';\nimport { createRequestFromNodeRequest, getAbortControllerCleanup } from 'astro/app/node';\nimport { log } from '../observability/log/index.js';\nimport { getRequestRecord } from '../observability/log/store.js';\nimport type { RuntimeOptions } from '../types.js';\nimport { setRequestRouteData } from './route-store.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\nfunction createOutgoingHttpHeaders(headers: Headers): OutgoingHttpHeaders | undefined {\n const nodeHeaders: OutgoingHttpHeaders = Object.fromEntries(headers.entries());\n\n if (Object.keys(nodeHeaders).length === 0) {\n return undefined;\n }\n\n // the entries iterator joins set-cookie values with a comma; node needs them as an array\n const cookies = headers.getSetCookie();\n\n if (cookies.length > 1) {\n nodeHeaders['set-cookie'] = cookies;\n }\n\n return nodeHeaders;\n}\n\n/**\n * Streams the web response into the node response. A render failing after the\n * first chunk cannot change the status anymore, so it is logged through the\n * request logger and marked on the request record — the completion line, the\n * span and `astro.render.failures` reflect it. On the wire it behaves like\n * astro's own writer: an `Internal server error` marker, then the socket is\n * destroyed.\n */\nexport async function writeResponse(response: Response, res: ServerResponse): Promise<void> {\n res.statusMessage = response.statusText;\n res.writeHead(response.status, createOutgoingHttpHeaders(response.headers));\n\n // astro parks the socket listener behind the request's abort signal on the node\n // request; releasing it once the response is done keeps keep-alive sockets from\n // accumulating one listener per request\n const cleanupAbort = getAbortControllerCleanup(res.req);\n\n if (cleanupAbort) {\n const runCleanup = (): void => {\n cleanupAbort();\n res.off('finish', runCleanup);\n res.off('close', runCleanup);\n };\n\n res.on('finish', runCleanup);\n res.on('close', runCleanup);\n }\n\n if (!response.body) {\n res.end();\n\n return;\n }\n\n const reader = response.body.getReader();\n\n // a client going away stops the render; on a failed stream the cancel rejects\n // with the render error, which the catch below has already reported\n res.on('close', () => {\n reader.cancel().catch(() => undefined);\n });\n\n try {\n for (let result = await reader.read(); !result.done; result = await reader.read()) {\n res.write(result.value);\n }\n\n res.end();\n } catch (err) {\n const record = getRequestRecord();\n\n if (record) {\n record.truncated = true;\n }\n\n log.error(\n {\n ...(err instanceof Error ? { err } : { reason: err }),\n ...(record?.route && { route: record.route }),\n ...(!record?.logger && { url: record?.url ?? res.req.url }),\n },\n 'render failed after the response started, response truncated',\n );\n\n res.write('Internal server error', () => {\n res.destroy(err instanceof Error ? err : undefined);\n });\n }\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 const matched = routeData && !(routeData.type === 'page' && routeData.prerender) ? routeData : undefined;\n\n if (matched) {\n setRequestRouteData(request, matched);\n }\n\n const response = matched\n ? await app.render(request, { addCookieHeader: true, routeData: matched, 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 { defined } from '@entwico/dash';\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 { shutdownTelemetry } from '../observability/telemetry/sdk.js';\nimport { type StartedSpan, startSpan, withSpan } from '../observability/telemetry/telemetry.js';\nimport { preparePlatform } from '../platform/prepare.js';\nimport type { RuntimeOptions } from '../types.js';\nimport { resolveClientDir } from './client-dir.js';\nimport { redirectDuplicateSlashes } from './duplicate-slashes.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(defined);\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 = startSpan('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 = startSpan('warmup', { parent: 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 (shutdownContext?: StartedSpan['context']): Promise<void> => {\n try {\n if (shutdownContext) {\n await withSpan('onShutdown', { parent: 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 withSpan('boot', { parent: 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 (redirectDuplicateSlashes(req, res)) return;\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 withSpan('listen', { parent: 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 = startSpan('shutdown');\n\n await withSpan('drain', { parent: 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,SAAgB,iBAAiB,SAA6C,eAA+B;CAC3G,MAAM,aAAa,IAAI,cAAc,IAAI,IAAI,QAAQ,MAAM,CAAC;CAC5D,MAAM,aAAa,IAAI,cAAc,IAAI,IAAI,QAAQ,MAAM,CAAC;CAC5D,MAAM,MAAM,KAAK,SAAS,YAAY,UAAU;CAChD,MAAM,eAAe,KAAK,SAAS,UAAU;CAE7C,IAAI,uBAAuB,KAAK,QAAQ,aAAa;CACrD,IAAI,WAAW;CAEf,OAAO,CAAC,qBAAqB,SAAS,YAAY,GAAG;EACnD,IAAI,yBAAyB,UAC3B,MAAM,IAAI,MACR,2DAA2D,aAAa,wBAAwB,cAAc,EAChH;EAGF,WAAW;EACX,uBAAuB,KAAK,QAAQ,oBAAoB;CAC1D;CAEA,MAAM,YAAY,IAAI,IAAI,IAAI,SAAS,GAAG,IAAI,MAAM,GAAG,IAAI,IAAI,GAAG,qBAAqB,WAAW;CAElG,OAAO,IAAI,cAAc,SAAS;AACpC;;;ACvBA,eAAe,gBAAgB,QAAgB,QAA+C;CAC5F,MAAM,YAAY,CAAC,GAAG,OAAO,QAAQ,GAAG,OAAO,YAAY;CAE3D,KAAK,MAAM,YAAY,WAAW;EAChC,MAAM,WAAW,KAAK,KAAK,QAAQ,QAAQ;EAC3C,IAAI;EAEJ,IAAI;GACF,SAAS,iBAAiB,QAAQ;GAElC,MAAM,IAAI,SAAe,SAAS,WAAW;IAC3C,OAAQ,KAAK,cAAc,QAAQ,CAAC;IACpC,OAAQ,KAAK,SAAS,MAAM;GAC9B,CAAC;GAED,OAAO,IAAI,SAAS,SAAS,MAAM,MAAM,GAAqB,EAC5D,SAAS,EAAE,gBAAgB,2BAA2B,EACxD,CAAC;EACH,QAAQ;GACN,QAAQ,QAAQ;EAClB;CACF;AAGF;AAEA,SAAS,0BAA0B,SAAmD;CACpF,MAAM,cAAmC,OAAO,YAAY,QAAQ,QAAQ,CAAC;CAE7E,IAAI,OAAO,KAAK,WAAW,CAAC,CAAC,WAAW,GACtC;CAIF,MAAM,UAAU,QAAQ,aAAa;CAErC,IAAI,QAAQ,SAAS,GACnB,YAAY,gBAAgB;CAG9B,OAAO;AACT;;;;;;;;;AAUA,eAAsB,cAAc,UAAoB,KAAoC;CAC1F,IAAI,gBAAgB,SAAS;CAC7B,IAAI,UAAU,SAAS,QAAQ,0BAA0B,SAAS,OAAO,CAAC;CAK1E,MAAM,eAAe,0BAA0B,IAAI,GAAG;CAEtD,IAAI,cAAc;EAChB,MAAM,mBAAyB;GAC7B,aAAa;GACb,IAAI,IAAI,UAAU,UAAU;GAC5B,IAAI,IAAI,SAAS,UAAU;EAC7B;EAEA,IAAI,GAAG,UAAU,UAAU;EAC3B,IAAI,GAAG,SAAS,UAAU;CAC5B;CAEA,IAAI,CAAC,SAAS,MAAM;EAClB,IAAI,IAAI;EAER;CACF;CAEA,MAAM,SAAS,SAAS,KAAK,UAAU;CAIvC,IAAI,GAAG,eAAe;EACpB,OAAO,OAAO,CAAC,CAAC,YAAY,KAAA,CAAS;CACvC,CAAC;CAED,IAAI;EACF,KAAK,IAAI,SAAS,MAAM,OAAO,KAAK,GAAG,CAAC,OAAO,MAAM,SAAS,MAAM,OAAO,KAAK,GAC9E,IAAI,MAAM,OAAO,KAAK;EAGxB,IAAI,IAAI;CACV,SAAS,KAAK;EACZ,MAAM,SAAS,iBAAiB;EAEhC,IAAI,QACF,OAAO,YAAY;EAGrB,IAAI,MACF;GACE,GAAI,eAAe,QAAQ,EAAE,IAAI,IAAI,EAAE,QAAQ,IAAI;GACnD,GAAI,QAAQ,SAAS,EAAE,OAAO,OAAO,MAAM;GAC3C,GAAI,CAAC,QAAQ,UAAU,EAAE,KAAK,QAAQ,OAAO,IAAI,IAAI,IAAI;EAC3D,GACA,8DACF;EAEA,IAAI,MAAM,+BAA+B;GACvC,IAAI,QAAQ,eAAe,QAAQ,MAAM,KAAA,CAAS;EACpD,CAAC;CACH;AACF;;;;;;AAOA,SAAgB,iBAAiB,KAAc,SAAyB,QAAgB;CACtF,QAAQ,GAAG,uBAAuB,WAAW;EAC3C,MAAM,aAAa,iBAAiB,CAAC,EAAE;EAEvC,IAAI,MACF;GACE,GAAI,kBAAkB,QAAQ,EAAE,KAAK,OAAO,IAAI,EAAE,OAAO;GACzD,GAAI,cAAc,EAAE,KAAK,WAAW;EACtC,GACA,aAAa,wCAAwC,qBACvD;CACF,CAAC;CAED,MAAM,4BAA4B,OAAO,QAAmC;EAC1E,MAAM,EAAE,aAAa,IAAI,IAAI,GAAG;EAEhC,KAAK,MAAM,UAAU,CAAC,KAAK,GAAG,GAC5B,IAAI,SAAS,SAAS,IAAI,OAAO,MAAM,KAAK,SAAS,SAAS,IAAI,OAAO,YAAY,GAAG;GACtF,MAAM,WAAW,MAAM,gBAAgB,QAAQ,MAAM;GAErD,IAAI,UAAU,OAAO;EACvB;EAGF,OAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;CAC3C;CAEA,MAAM,gBACJ,QAAQ,kBAAkB,KAAK,QAAQ,kBAAkB,OAAO,oBAC5D,KAAA,IACA,QAAQ;CAEd,OAAO,OAAO,KAAsB,QAAuC;EACzE,IAAI;EAEJ,IAAI;GACF,UAAU,6BAA6B,KAAK;IAC1C,gBAAgB,IAAI,oBAAoB,KAAK,CAAC;IAC9C,GAAI,kBAAkB,KAAA,KAAa,EAAE,cAAc;IACnD,MAAM,QAAQ;GAChB,CAAC;EACH,SAAS,KAAK;GACZ,IAAI,MAAM,eAAe,QAAQ;IAAE;IAAK,KAAK,IAAI;GAAI,IAAI;IAAE,QAAQ;IAAK,KAAK,IAAI;GAAI,GAAG,kBAAkB;GAE1G,IAAI,aAAa;GACjB,IAAI,IAAI,uBAAuB;GAE/B;EACF;EAEA,MAAM,YAAY,IAAI,MAAM,SAAS,IAAI;EACzC,MAAM,UAAU,aAAa,EAAE,UAAU,SAAS,UAAU,UAAU,aAAa,YAAY,KAAA;EAE/F,IAAI,SACF,oBAAoB,SAAS,OAAO;EAOtC,MAAM,cAJW,UACb,MAAM,IAAI,OAAO,SAAS;GAAE,iBAAiB;GAAM,WAAW;GAAS;EAA0B,CAAC,IAClG,MAAM,IAAI,OAAO,SAAS;GAAE,iBAAiB;GAAM;EAA0B,CAAC,GAEpD,GAAG;CACnC;AACF;;;ACzLA,MAAM,WAAW,CACf;CAAE,UAAU;CAAM,QAAQ;AAAM,GAChC;CAAE,UAAU;CAAQ,QAAQ;AAAM,CACpC;AAEA,SAAS,iBACP,KACA,QACA,UACoD;CACpD,MAAM,SAAS,IAAI,QAAQ;CAE3B,IAAI,OAAO,WAAW,UAAU,OAAO,KAAA;CAEvC,KAAK,MAAM,EAAE,UAAU,YAAY,UAAU;EAC3C,IAAI,CAAC,OAAO,SAAS,QAAQ,GAAG;EAEhC,IAAI,GAAG,WAAW,KAAK,KAAK,QAAQ,GAAG,WAAW,QAAQ,CAAC,GACzD,OAAO;GAAE,UAAU,GAAG,WAAW;GAAU;EAAS;CAExD;AAGF;AAEA,SAAS,iBAAiB,UAA2B;CACnD,MAAM,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC,IAAI;CAErC,OAAO,CAAC,CAAC,QAAQ,KAAK,SAAS,GAAG;AACpC;AAEA,SAAS,oBAAoB,UAA0B;CACrD,OAAO,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI;AACnD;AAEA,SAAS,YAAY,QAAgB,SAA0B;CAC7D,MAAM,WAAW,KAAK,KAAK,QAAQ,OAAO;CAC1C,MAAM,WAAW,KAAK,QAAQ,QAAQ;CACtC,MAAM,iBAAiB,KAAK,QAAQ,MAAM;CAG1C,IAAI,aAAa,kBAAkB,CAAC,SAAS,WAAW,iBAAiB,KAAK,GAAG,GAC/E,OAAO;CAGT,IAAI;EACF,OAAO,GAAG,UAAU,QAAQ,CAAC,CAAC,YAAY;CAC5C,QAAQ;EACN,OAAO;CACT;AACF;;;;;;AAOA,SAAgB,oBAAoB,KAAc,QAAgB;CAChE,QAAQ,KAAsB,KAAqB,QAA0B;EAC3E,IAAI,CAAC,IAAI,KAAK;GACZ,IAAI;GAEJ;EACF;EAEA,IAAI,UAAU,IAAI;EAElB,IAAI,QAAQ,SAAS,GAAG,GACtB,UAAU,QAAQ,MAAM,GAAG,QAAQ,QAAQ,GAAG,CAAC;EAGjD,MAAM,CAAC,UAAU,IAAI,YAAY,QAAQ,MAAM,GAAG;EAClD,IAAI,SAAS,IAAI,WAAW,OAAO;EAEnC,IAAI;GACF,SAAS,UAAU,MAAM;EAC3B,QAAQ,CAER;EAEA,MAAM,MAAM,YAAY,QAAQ,MAAM;EACtC,MAAM,WAAW,QAAQ,SAAS,GAAG;EACrC,IAAI,WAAW;EAEf,QAAQ,IAAI,SAAS,eAArB;GACE,KAAK;IACH,IAAI,OAAO,YAAY,OAAO,UAAU;KACtC,IAAI,aAAa;KACjB,IAAI,UAAU,YAAY,QAAQ,MAAM,GAAG,EAAE,KAAK,WAAW,IAAI,aAAa,GAAG;KACjF,IAAI,IAAI;KAER;IACF;IAEA,IAAI,OAAO,CAAC,UACV,WAAW,GAAG,QAAQ;IAGxB;GAEF,KAAK;IACH,IAAI,OAAO,CAAC,UACV,WAAW,GAAG,QAAQ;IAGxB;GAEF,KAAK,UACH,IAAI,CAAC,YAAY,CAAC,iBAAiB,OAAO,KAAK,CAAC,QAAQ,WAAW,IAAI,GAAG;IACxE,IAAI,aAAa;IACjB,IAAI,UAAU,YAAY,GAAG,QAAQ,GAAG,WAAW,IAAI,aAAa,IAAI;IACxE,IAAI,IAAI;IAER;GACF;EAIJ;EAEA,WAAW,oBAAoB,IAAI,WAAW,QAAQ,CAAC;EAEvD,MAAM,qBAAqB,KAAK,MAAM,UAAU,QAAQ;EACxD,MAAM,eAAe,aAAa,IAAI,KAAK,MAAM,QAAQ,kBAAkB,CAAC;EAC5E,MAAM,UAAU,eAAe,iBAAiB,KAAK,QAAQ,kBAAkB,IAAI,KAAA;EAEnF,MAAM,SAAS,KAAK,KAAK,SAAS,YAAY,oBAAoB;GAChE,MAAM;GACN,UAAU,mBAAmB,WAAW,eAAe,IAAI,UAAU;GAGrE,YAAY,IAAI,SAAS,gBAAgB,UAAU,IAAI,SAAS,gBAAgB,aAAa,CAAC,MAAM,IAAI,CAAC;EAC3G,CAAC;EAED,IAAI,eAAe;EAEnB,OAAO,GAAG,UAAU,QAAyD;GAC3E,IAAI,cAAc;IAChB,MAAM,SAAS,IAAI,cAAc;IAEjC,IAAI,UAAU,KACZ,QAAQ,MAAM,IAAI,SAAS,CAAC;IAG9B,IAAI,UAAU,MAAM;IACpB,IAAI,IAAI,UAAU,MAAM,0BAA0B,EAAE;IAEpD;GACF;GAEA,IAAI;EACN,CAAC;EAED,OAAO,GAAG,cAAc;GACtB,eAAe;EACjB,CAAC;EAID,OAAO,GAAG,YAAY,eAA+B;GACnD,IAAI,cACF,WAAW,UAAU,QAAQ,iBAAiB;GAGhD,IAAI,SAAS;IACX,WAAW,UAAU,oBAAoB,QAAQ,QAAQ;IACzD,WAAW,UACT,gBACA,WAAW,IAAI,KAAK,MAAM,QAAQ,kBAAkB,CAAC,KAAK,0BAC5D;GACF;GAEA,IAAI,mBAAmB,WAAW,IAAI,IAAI,SAAS,UAAU,EAAE,GAC7D,WAAW,UAAU,iBAAiB,qCAAqC;EAE/E,CAAC;EAED,OAAO,KAAK,GAAG;CACjB;AACF;;;AC/JA,WAAW,QAAQ,QAAQ,IAAI,IAAI;AAEnC,MAAM,iBAAiB;AACvB,MAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAEzC,MAAM,WAAW,MAAc,KAAK,MAAM,IAAI,GAAG,IAAI;;;;;;AAOrD,eAAe,gBAA+B;CAC5C,MAAM,UAAU;EACd,GAAI,IAAI,SAAS,SAAS,OAAO,KAAK,CAAC;EACvC,IAAI,SAAS;EACb,IAAI,SAAS;EACb,IAAI,SAAS;EACb,IAAI,SAAS;CACf,CAAC,CAAC,OAAO,OAAO;CAGhB,MAAM,YAAW,MADK,QAAQ,WAAW,QAAQ,KAAK,SAAS,KAAK,CAAC,CAAC,EAAA,CAC7C,QAAQ,WAAW,OAAO,WAAW,UAAU;CAExE,IAAI,SAAS,WAAW,GAAG;CAE3B,KAAK,MAAM,WAAW,UACpB,IAAI,MACF,QAAQ,kBAAkB,QAAQ,EAAE,KAAK,QAAQ,OAAO,IAAI,EAAE,QAAQ,QAAQ,OAAO,GACrF,sBACF;CAMF,MAAM,IAAI,eACR,SAAS,KAAK,YAAY,QAAQ,MAAM,GACxC,sBACF;AACF;;;;;AAMA,SAAS,iBAA4D;CACnE,MAAM,WAAW,QAAQ,IAAI;CAC7B,MAAM,UAAU,QAAQ,IAAI;CAE5B,IAAI,CAAC,YAAY,CAAC,SAAS,OAAO,KAAA;CAElC,IAAI,CAAC,YAAY,CAAC,SAChB,MAAM,IAAI,MAAM,sEAAsE;CAGxF,OAAO;EAAE,MAAM,GAAG,aAAa,QAAQ;EAAG,KAAK,GAAG,aAAa,OAAO;CAAE;AAC1E;AASA,eAAsB,YAAY,WAGR;CACxB,MAAM,YAAY,YAAY,IAAI;CAClC,MAAM,OAAO,WAAW,QAAQ,QAAQ,IAAI,WAAW,eAAe;CACtE,MAAM,OAAO,WAAW,SAAS,QAAQ,IAAI,UAAU,OAAO,QAAQ,IAAI,OAAO,IAAI,eAAe;CACpG,MAAM,UAAuB;EAAE,KAAK;EAAO;EAAM;CAAK;CACtD,MAAM,SAAS,eAAe;CAE9B,eAAe,OAAO;CAEtB,IAAI;EACF,MAAM,gBAAgB;GACpB,KAAK;GACL,WAAW,eAAe,YAAY,EAAE,YAAY,eAAe,UAAU,WAAW,IAAI;GAC5F,OAAO;IACL,cAAc,OAAO;IACrB,uBAAuB,OAAO;IAC9B,WAAW,OAAO;GACpB;EACF,CAAC;CACH,SAAS,KAAK;EAEZ,cAAc;EACd,QAAQ,MAAM,GAAG;EACjB,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,QAAQ;EACV,OAAa,MAAM;GAAE,GAAG;GAAQ,MAAM,OAAO,QAAQ,QAAQ,IAAI,kBAAkB;EAAU,CAAC;EAC9F,OAAO,KAAK,OAAO;EACnB,qBAAqB,MAAM;EAC3B,IAAI,MAAM,yBAAyB;CACrC;CAEA,MAAM,UAAU,UAAU,SAAS;CAEnC,IAAI,KAAK;EAAE;EAAM;CAAK,GAAG,UAAU;CAInC,IAAI,aAAyB,CAAC;CAC9B,IAAI,SAAS;CACb,IAAI,WAAW;CAKf,MAAM,kBAAkB,YAAY,IAAI;CACxC,MAAM,aAAa,UAAU,UAAU,EAAE,QAAQ,QAAQ,QAAQ,CAAC;CAClE,MAAM,SAAS,cAAc,CAAC,CAAC,WACvB;EACJ,WAAW,QAAQ,YAAY,IAAI,IAAI,eAAe;EACtD,WAAW,KAAK,IAAI;CAGtB,IACC,UAAmB;EAClB,WAAW,QAAQ,YAAY,IAAI,IAAI,eAAe;EACtD,WAAW,KAAK,UAAU;GAAE,MAAM,eAAe;GAAO,SAAS;EAAuB,CAAC;EACzF,WAAW,KAAK,IAAI;EAEpB,OAAO;CACT,CACF;CAEA,MAAM,oBAAoB,OAAO,oBAA4D;EAC3F,IAAI;GACF,IAAI,iBACF,MAAM,SAAS,cAAc,EAAE,QAAQ,gBAAgB,SAAS,YAAY,YAAY,OAAO,CAAC;QAEhG,MAAM,YAAY,YAAY,OAAO;EAEzC,SAAS,KAAK;GACZ,IAAI,MAAM,eAAe,QAAQ,EAAE,IAAI,IAAI,EAAE,QAAQ,IAAI,GAAG,iBAAiB;EAC/E;EAEA,kBAAkB;EAElB,IAAI,QAAQ;GACV,uBAAuB;GAEvB,IAAI;IACF,MAAMA,OAAa,KAAK;GAC1B,SAAS,KAAK;IAGZ,IAAI,MAAM,eAAe,QAAQ,EAAE,IAAI,IAAI,EAAE,QAAQ,IAAI,GAAG,iCAAiC;GAC/F;EACF;CACF;CAEA,MAAM,cAAc,OAAO,KAAc,YAAoC;EAC3E,IAAI,MAAM,eAAe,QAAQ,EAAE,IAAI,IAAI,EAAE,QAAQ,IAAI,GAAG,OAAO;EACnE,QAAQ,KAAK,UAAU;GAAE,MAAM,eAAe;GAAO;EAAQ,CAAC;EAC9D,QAAQ,KAAK,IAAI;EAEjB,MAAM,kBAAkB;EACxB,MAAM,kBAAkB;EACxB,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI;EACF,MAAM,gBAAgB,YAAY,IAAI;EAGtC,aAAc,MAAM,OAAO;EAE3B,MAAM,SAAS,QAAQ,EAAE,QAAQ,QAAQ,QAAQ,SAAS,WAAW,YAAY,OAAO,CAAC;EAEzF,SAAS,QAAQ,YAAY,IAAI,IAAI,aAAa;CACpD,SAAS,KAAK;EACZ,MAAM,YAAY,KAAK,gBAAgB;CACzC;CAEA,MAAM,cAAc,MAAM;CAE1B,IAAI,gBAAgB,KAAA,GAClB,MAAM,YAAY,aAAa,sBAAsB;CAGvD,IAAI,QAAQ,OAAO,QAAQ,OAAO;CAElC,MAAM,SAAS,iBAAiB,gBAAgB,YAAY,GAAG;CAC/D,MAAM,aAAa,iBAAiB,KAAK,gBAAgB,MAAM;CAC/D,MAAM,gBAAgB,oBAAoB,KAAK,MAAM;CACrD,MAAM,aAAa,6BAA6B;EAC9C,SAAS,eAAe;EACxB,WAAW,eAAe,YAAY,EAAE,SAAS,eAAe,UAAU,QAAQ,IAAI;CACxF,CAAC;CAED,IAAI;CAEJ,IAAI;EACF,MAAM,eAAe;CACvB,SAAS,KAAK;EACZ,MAAM,YAAY,KAAK,4BAA4B;CACrD;CAEA,MAAM,YAAkC,KAAK,QAAQ;EACnD,IAAI;GACF,UAAU,IAAI,OAAO,EAAE;EACzB,QAAQ;GACN,IAAI,UAAU,GAAG;GACjB,IAAI,IAAI,cAAc;GAEtB;EACF;EAEA,WAAW,KAAK,WAAW;GACzB,IAAI,yBAAyB,KAAK,GAAG,GAAG;GACxC,IAAI,oBAAoB,KAAK,GAAG,GAAG;GAEnC,cAAc,KAAK,WAAW,KAAK,WAAW,KAAK,GAAG,CAAC;EACzD,CAAC;CACH;CAEA,MAAMC,WAAS,MAAM,MAAM,aAAa,KAAK,QAAQ,IAAI,KAAK,aAAa,QAAQ;CAEnF,IAAI;EACF,MAAM,SAAS,UAAU,EAAE,QAAQ,QAAQ,QAAQ,SAAS;GAC1D,OAAO,IAAI,SAAe,SAAS,WAAW;IAC5C,SAAO,KAAK,SAAS,MAAM;IAC3B,SAAO,OAAO,MAAM,MAAM,OAAO;GACnC,CAAC;EACH,CAAC;CACH,SAAS,KAAK;EACZ,MAAM,YAAY,KAAK,uBAAuB,KAAK,GAAG,MAAM;CAC9D;CAEA,IAAI,QAAQ,OAAO,MAAM,OAAO;CAEhC,QAAQ,KAAK,UAAU,EAAE,MAAM,eAAe,GAAG,CAAC;CAClD,QAAQ,KAAK,IAAI;CAEjB,IAAI,KACF;EACE;EACA;EACA,GAAI,OAAO,EAAE,OAAO,KAAK;EACzB,QAAQ,CAAC,CAAC;EACV;EACA;EACA,SAAS,QAAQ,YAAY,IAAI,IAAI,SAAS;CAChD,GACA,cACF;CAEA,IAAI;CACJ,IAAI;CAEJ,MAAM,gBAAgB,IAAI,SAAe,YAAY;EACnD,gBAAgB;CAClB,CAAC;CAED,MAAM,SAAS,YAA2B;EACxC,IAAI,QAAQ,OAAO,MAAM,QAAQ;EAEjC,IAAI,KAAK,oBAAoB;EAE7B,MAAM,iBAAiB,YAAY,IAAI;EACvC,MAAM,WAAW,UAAU,UAAU;EAErC,MAAM,SAAS,SAAS,EAAE,QAAQ,SAAS,QAAQ,GAAG,YAAY;GAChE,MAAM,SAAS,IAAI,SAAe,YAAYA,SAAO,YAAY,QAAQ,CAAC,CAAC;GAE3E,SAAO,qBAAqB;GAE5B,MAAM,aAAa,iBAAiBA,SAAO,oBAAoB,GAAG,eAAe,eAAe;GAEhG,MAAM;GAEN,aAAa,UAAU;EACzB,CAAC;EAED,MAAM,UAAU,QAAQ,YAAY,IAAI,IAAI,cAAc;EAE1D,MAAM,kBAAkB,SAAS,OAAO;EAExC,SAAS,KAAK,IAAI;EAElB,IAAI,KAAK,EAAE,QAAQ,GAAG,mBAAmB;EAEzC,MAAM,kBAAkB;EACxB,cAAc;CAChB;CAEA,MAAM,aAA6B,gBAAgB,OAAO;CAE1D,QAAQ,KAAK,iBAAiB,KAAK,KAAK,CAAC,CAAC,WAAW,QAAQ,KAAK,CAAC,CAAC,CAAC;CACrE,QAAQ,KAAK,gBAAgB,KAAK,KAAK,CAAC,CAAC,WAAW,QAAQ,KAAK,CAAC,CAAC,CAAC;CAEpE,OAAO;EAAE;EAAM;EAAM;EAAM,cAAc;CAAc;AACzD;AAEA,IAAI,QAAQ,IAAI,iCAAiC,YAC/C,MAAM,YAAY"}
@@ -0,0 +1,202 @@
1
+ import { maybeThen } from "@entwico/dash";
2
+ import { SpanStatusCode, ValueType, context, createNoopMeter, metrics, trace } from "@opentelemetry/api";
3
+ //#region src/observability/telemetry/gauges.ts
4
+ /**
5
+ * Gauges have no "first use" to bind on: their callback must be registered on
6
+ * a real meter, and a meter obtained before the SDK started is a no-op
7
+ * forever. A creation that lands on the no-op meter is parked here and retried
8
+ * by the SDK right after `sdk.start()`. Keyed on `globalThis` so the
9
+ * vite-runner and native module instances share one list.
10
+ */
11
+ const STORE_KEY = Symbol.for("@astroscope/node/telemetry.gauges");
12
+ function getPending() {
13
+ const g = globalThis;
14
+ let pending = g[STORE_KEY];
15
+ if (!pending) {
16
+ pending = [];
17
+ g[STORE_KEY] = pending;
18
+ }
19
+ return pending;
20
+ }
21
+ function registerGauge(bind) {
22
+ if (!bind()) getPending().push(bind);
23
+ }
24
+ function bindGauges() {
25
+ const pending = getPending();
26
+ for (const bind of pending.splice(0)) if (!bind()) pending.push(bind);
27
+ }
28
+ //#endregion
29
+ //#region src/observability/telemetry/telemetry.ts
30
+ /**
31
+ * The telemetry seam for the astroscope packages — internal api, not for app
32
+ * code (which uses `@opentelemetry/api` against the same SDK). Instruments
33
+ * bind to the SDK meter lazily (a meter obtained before `startTelemetry()` is
34
+ * a no-op forever, so a handle keeps trying until a real meter answers),
35
+ * names, units and buckets have one owner, and no package needs its own
36
+ * `@opentelemetry/api` copy. Without a started SDK — dev by default, or
37
+ * `telemetry: false` — every call resolves to the api's no-op implementation.
38
+ * @internal
39
+ */
40
+ const DEFAULT_SCOPE = "@astroscope/node";
41
+ const noopMeter = createNoopMeter();
42
+ const NOOP_HISTOGRAM = noopMeter.createHistogram("noop");
43
+ const NOOP_COUNTER = noopMeter.createCounter("noop");
44
+ const NOOP_UP_DOWN_COUNTER = noopMeter.createUpDownCounter("noop");
45
+ const NOOP_OBSERVABLE_GAUGE = noopMeter.createObservableGauge("noop");
46
+ /**
47
+ * Bucket boundaries in seconds for the three kinds of durations the platform
48
+ * measures: `http` is the semconv set for server requests, `io` covers one
49
+ * outbound call (1 ms – 2.5 s), `compute` a synchronous render (10 µs – 50 ms).
50
+ */
51
+ const DURATION_BUCKETS = {
52
+ http: [
53
+ .005,
54
+ .01,
55
+ .025,
56
+ .05,
57
+ .075,
58
+ .1,
59
+ .25,
60
+ .5,
61
+ .75,
62
+ 1,
63
+ 2.5,
64
+ 5,
65
+ 7.5,
66
+ 10
67
+ ],
68
+ io: [
69
+ .001,
70
+ .0025,
71
+ .005,
72
+ .01,
73
+ .025,
74
+ .05,
75
+ .1,
76
+ .25,
77
+ .5,
78
+ 1,
79
+ 2.5
80
+ ],
81
+ compute: [
82
+ 1e-5,
83
+ 5e-5,
84
+ 1e-4,
85
+ 25e-5,
86
+ 5e-4,
87
+ .001,
88
+ .0025,
89
+ .005,
90
+ .01,
91
+ .05
92
+ ]
93
+ };
94
+ function getMeter(options) {
95
+ return metrics.getMeter(options.scope ?? DEFAULT_SCOPE);
96
+ }
97
+ function describe(options) {
98
+ return {
99
+ ...options.description && { description: options.description },
100
+ ...options.unit && { unit: options.unit }
101
+ };
102
+ }
103
+ function bindLazily(create, noop) {
104
+ let instrument;
105
+ return () => {
106
+ if (instrument === void 0) {
107
+ const created = create();
108
+ if (created === noop) return;
109
+ instrument = created;
110
+ }
111
+ return instrument;
112
+ };
113
+ }
114
+ function createHistogram(name, options) {
115
+ const bound = bindLazily(() => getMeter(options).createHistogram(name, {
116
+ ...describe(options),
117
+ valueType: ValueType.DOUBLE,
118
+ advice: { explicitBucketBoundaries: [...options.buckets] }
119
+ }), NOOP_HISTOGRAM);
120
+ return { record: (value, attributes) => bound()?.record(value, attributes) };
121
+ }
122
+ function createCounter(name, options = {}) {
123
+ const bound = bindLazily(() => getMeter(options).createCounter(name, {
124
+ ...describe(options),
125
+ valueType: ValueType.INT
126
+ }), NOOP_COUNTER);
127
+ return { add: (value, attributes) => bound()?.add(value, attributes) };
128
+ }
129
+ function createUpDownCounter(name, options = {}) {
130
+ const bound = bindLazily(() => getMeter(options).createUpDownCounter(name, {
131
+ ...describe(options),
132
+ valueType: ValueType.INT
133
+ }), NOOP_UP_DOWN_COUNTER);
134
+ return { add: (value, attributes) => bound()?.add(value, attributes) };
135
+ }
136
+ /**
137
+ * An asynchronous gauge: `observe` runs at every collection (the Prometheus
138
+ * scrape) and reports the current values. Registered with the SDK when it
139
+ * starts, so calling this at module level is fine.
140
+ */
141
+ function createObservableGauge(name, options, observe) {
142
+ registerGauge(() => {
143
+ const gauge = getMeter(options).createObservableGauge(name, {
144
+ ...describe(options),
145
+ valueType: ValueType.DOUBLE
146
+ });
147
+ if (gauge === NOOP_OBSERVABLE_GAUGE) return false;
148
+ gauge.addCallback((result) => observe({ observe: (value, attributes) => result.observe(value, attributes) }));
149
+ return true;
150
+ });
151
+ }
152
+ /** the `error.type` attribute value for a thrown value: the error's class, else its type */
153
+ function errorType(error) {
154
+ if (error instanceof Error) return error.constructor.name || error.name || "Error";
155
+ return typeof error;
156
+ }
157
+ /** starts a span the caller ends itself; `withSpan` for work that is one function */
158
+ function startSpan(name, options = {}) {
159
+ const parent = options.parent ?? context.active();
160
+ const span = trace.getTracer(options.scope ?? DEFAULT_SCOPE).startSpan(name, {
161
+ ...options.attributes && { attributes: options.attributes },
162
+ ...options.kind !== void 0 && { kind: options.kind }
163
+ }, parent);
164
+ return {
165
+ span,
166
+ context: trace.setSpan(parent, span)
167
+ };
168
+ }
169
+ function endSpan(span, error, failed) {
170
+ if (failed) span.setStatus({
171
+ code: SpanStatusCode.ERROR,
172
+ message: error instanceof Error ? error.message : "unknown error"
173
+ });
174
+ else span.setStatus({ code: SpanStatusCode.OK });
175
+ span.end();
176
+ }
177
+ /**
178
+ * Runs `fn` inside a span: status from the outcome, the span active for
179
+ * everything `fn` awaits. A synchronous `fn` stays synchronous — no promise
180
+ * is allocated on the common path.
181
+ */
182
+ function withSpan(name, options, fn) {
183
+ const { span, context: spanContext } = startSpan(name, options);
184
+ let result;
185
+ try {
186
+ result = context.with(spanContext, fn);
187
+ } catch (error) {
188
+ endSpan(span, error, true);
189
+ throw error;
190
+ }
191
+ return maybeThen(result, (value) => {
192
+ endSpan(span, void 0, false);
193
+ return value;
194
+ }, (error) => {
195
+ endSpan(span, error, true);
196
+ throw error;
197
+ });
198
+ }
199
+ //#endregion
200
+ export { createUpDownCounter as a, withSpan as c, createObservableGauge as i, bindGauges as l, createCounter as n, errorType as o, createHistogram as r, startSpan as s, DURATION_BUCKETS as t };
201
+
202
+ //# sourceMappingURL=telemetry-B9aziFyQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry-B9aziFyQ.js","names":[],"sources":["../src/observability/telemetry/gauges.ts","../src/observability/telemetry/telemetry.ts"],"sourcesContent":["/**\n * Gauges have no \"first use\" to bind on: their callback must be registered on\n * a real meter, and a meter obtained before the SDK started is a no-op\n * forever. A creation that lands on the no-op meter is parked here and retried\n * by the SDK right after `sdk.start()`. Keyed on `globalThis` so the\n * vite-runner and native module instances share one list.\n */\n\nconst STORE_KEY = Symbol.for('@astroscope/node/telemetry.gauges');\n\n/** returns whether the instrument bound to a real meter */\ntype Bind = () => boolean;\n\nfunction getPending(): Bind[] {\n const g = globalThis as Record<symbol, unknown>;\n let pending = g[STORE_KEY] as Bind[] | undefined;\n\n if (!pending) {\n pending = [];\n g[STORE_KEY] = pending;\n }\n\n return pending;\n}\n\nexport function registerGauge(bind: Bind): void {\n if (!bind()) {\n getPending().push(bind);\n }\n}\n\nexport function bindGauges(): void {\n const pending = getPending();\n\n for (const bind of pending.splice(0)) {\n if (!bind()) {\n pending.push(bind);\n }\n }\n}\n","import { type MaybePromise, maybeThen } from '@entwico/dash';\nimport {\n type Attributes,\n type Context,\n type Counter,\n type Histogram,\n type Meter,\n type Span,\n type SpanKind,\n SpanStatusCode,\n type UpDownCounter,\n ValueType,\n context,\n createNoopMeter,\n metrics,\n trace,\n} from '@opentelemetry/api';\nimport { registerGauge } from './gauges.js';\n\n/**\n * The telemetry seam for the astroscope packages — internal api, not for app\n * code (which uses `@opentelemetry/api` against the same SDK). Instruments\n * bind to the SDK meter lazily (a meter obtained before `startTelemetry()` is\n * a no-op forever, so a handle keeps trying until a real meter answers),\n * names, units and buckets have one owner, and no package needs its own\n * `@opentelemetry/api` copy. Without a started SDK — dev by default, or\n * `telemetry: false` — every call resolves to the api's no-op implementation.\n * @internal\n */\n\nconst DEFAULT_SCOPE = '@astroscope/node';\n\n// the no-op meter hands out singletons — the tell for \"no SDK registered yet\"\nconst noopMeter = createNoopMeter();\nconst NOOP_HISTOGRAM = noopMeter.createHistogram('noop');\nconst NOOP_COUNTER = noopMeter.createCounter('noop');\nconst NOOP_UP_DOWN_COUNTER = noopMeter.createUpDownCounter('noop');\nconst NOOP_OBSERVABLE_GAUGE = noopMeter.createObservableGauge('noop');\n\nexport interface InstrumentOptions {\n /** instrumentation scope, `@astroscope/<package>`; defaults to `@astroscope/node` */\n scope?: string | undefined;\n description?: string | undefined;\n /** UCUM unit, e.g. `s`, `{request}` */\n unit?: string | undefined;\n}\n\nexport interface HistogramOptions extends InstrumentOptions {\n /** explicit bucket boundaries; the SDK default set starts at 5, useless for seconds */\n buckets: readonly number[];\n}\n\nexport interface HistogramHandle {\n record(value: number, attributes?: Attributes | undefined): void;\n}\n\nexport interface CounterHandle {\n add(value: number, attributes?: Attributes | undefined): void;\n}\n\n/** what an observable gauge callback reports: one value per attribute set */\nexport interface ObservableHandle {\n observe(value: number, attributes?: Attributes | undefined): void;\n}\n\n/**\n * Bucket boundaries in seconds for the three kinds of durations the platform\n * measures: `http` is the semconv set for server requests, `io` covers one\n * outbound call (1 ms – 2.5 s), `compute` a synchronous render (10 µs – 50 ms).\n */\nexport const DURATION_BUCKETS = {\n http: [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10],\n io: [0.001, 0.0025, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5],\n compute: [0.00001, 0.00005, 0.0001, 0.00025, 0.0005, 0.001, 0.0025, 0.005, 0.01, 0.05],\n} as const satisfies Record<string, readonly number[]>;\n\nfunction getMeter(options: InstrumentOptions): Meter {\n return metrics.getMeter(options.scope ?? DEFAULT_SCOPE);\n}\n\nfunction describe(options: InstrumentOptions): { description?: string; unit?: string } {\n return {\n ...(options.description && { description: options.description }),\n ...(options.unit && { unit: options.unit }),\n };\n}\n\n// binds on first use after a real meter is registered; until then every use\n// creates a no-op instrument and drops the value\nfunction bindLazily<T>(create: () => T, noop: T): () => T | undefined {\n let instrument: T | undefined;\n\n return () => {\n if (instrument === undefined) {\n const created = create();\n\n if (created === noop) {\n return undefined;\n }\n\n instrument = created;\n }\n\n return instrument;\n };\n}\n\nexport function createHistogram(name: string, options: HistogramOptions): HistogramHandle {\n const bound = bindLazily<Histogram>(\n () =>\n getMeter(options).createHistogram(name, {\n ...describe(options),\n valueType: ValueType.DOUBLE,\n advice: { explicitBucketBoundaries: [...options.buckets] },\n }),\n NOOP_HISTOGRAM,\n );\n\n return { record: (value, attributes) => bound()?.record(value, attributes) };\n}\n\nexport function createCounter(name: string, options: InstrumentOptions = {}): CounterHandle {\n const bound = bindLazily<Counter>(\n () => getMeter(options).createCounter(name, { ...describe(options), valueType: ValueType.INT }),\n NOOP_COUNTER,\n );\n\n return { add: (value, attributes) => bound()?.add(value, attributes) };\n}\n\nexport function createUpDownCounter(name: string, options: InstrumentOptions = {}): CounterHandle {\n const bound = bindLazily<UpDownCounter>(\n () => getMeter(options).createUpDownCounter(name, { ...describe(options), valueType: ValueType.INT }),\n NOOP_UP_DOWN_COUNTER,\n );\n\n return { add: (value, attributes) => bound()?.add(value, attributes) };\n}\n\n/**\n * An asynchronous gauge: `observe` runs at every collection (the Prometheus\n * scrape) and reports the current values. Registered with the SDK when it\n * starts, so calling this at module level is fine.\n */\nexport function createObservableGauge(\n name: string,\n options: InstrumentOptions,\n observe: (result: ObservableHandle) => void,\n): void {\n registerGauge(() => {\n const gauge = getMeter(options).createObservableGauge(name, { ...describe(options), valueType: ValueType.DOUBLE });\n\n if (gauge === NOOP_OBSERVABLE_GAUGE) {\n return false;\n }\n\n gauge.addCallback((result) => observe({ observe: (value, attributes) => result.observe(value, attributes) }));\n\n return true;\n });\n}\n\n/** the `error.type` attribute value for a thrown value: the error's class, else its type */\nexport function errorType(error: unknown): string {\n if (error instanceof Error) {\n return error.constructor.name || error.name || 'Error';\n }\n\n return typeof error;\n}\n\nexport interface SpanOptions {\n /** instrumentation scope, `@astroscope/<package>`; defaults to `@astroscope/node` */\n scope?: string | undefined;\n attributes?: Attributes | undefined;\n kind?: SpanKind | undefined;\n /** parent context; defaults to the active one (the request span inside a request) */\n parent?: Context | undefined;\n}\n\nexport interface StartedSpan {\n span: Span;\n /** the context with `span` active — pass as `parent` to nest children */\n context: Context;\n}\n\n/** starts a span the caller ends itself; `withSpan` for work that is one function */\nexport function startSpan(name: string, options: SpanOptions = {}): StartedSpan {\n const parent = options.parent ?? context.active();\n const span = trace.getTracer(options.scope ?? DEFAULT_SCOPE).startSpan(\n name,\n {\n ...(options.attributes && { attributes: options.attributes }),\n ...(options.kind !== undefined && { kind: options.kind }),\n },\n parent,\n );\n\n return { span, context: trace.setSpan(parent, span) };\n}\n\nfunction endSpan(span: Span, error: unknown, failed: boolean): void {\n if (failed) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: error instanceof Error ? error.message : 'unknown error' });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n\n span.end();\n}\n\n/**\n * Runs `fn` inside a span: status from the outcome, the span active for\n * everything `fn` awaits. A synchronous `fn` stays synchronous — no promise\n * is allocated on the common path.\n */\nexport function withSpan<T>(name: string, options: SpanOptions, fn: () => MaybePromise<T>): MaybePromise<T> {\n const { span, context: spanContext } = startSpan(name, options);\n let result: MaybePromise<T>;\n\n try {\n result = context.with(spanContext, fn);\n } catch (error) {\n endSpan(span, error, true);\n\n throw error;\n }\n\n return maybeThen(\n result,\n (value) => {\n endSpan(span, undefined, false);\n\n return value;\n },\n (error: unknown) => {\n endSpan(span, error, true);\n\n throw error;\n },\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { MaybePromise } from "@entwico/dash";
2
+ import { Attributes, Context, Span, SpanKind } from "@opentelemetry/api";
3
+ //#region src/observability/telemetry/telemetry.d.ts
4
+ export interface InstrumentOptions {
5
+ /** instrumentation scope, `@astroscope/<package>`; defaults to `@astroscope/node` */
6
+ scope?: string | undefined;
7
+ description?: string | undefined;
8
+ /** UCUM unit, e.g. `s`, `{request}` */
9
+ unit?: string | undefined;
10
+ }
11
+ export interface HistogramOptions extends InstrumentOptions {
12
+ /** explicit bucket boundaries; the SDK default set starts at 5, useless for seconds */
13
+ buckets: readonly number[];
14
+ }
15
+ export interface HistogramHandle {
16
+ record(value: number, attributes?: Attributes | undefined): void;
17
+ }
18
+ export interface CounterHandle {
19
+ add(value: number, attributes?: Attributes | undefined): void;
20
+ }
21
+ /** what an observable gauge callback reports: one value per attribute set */
22
+ export interface ObservableHandle {
23
+ observe(value: number, attributes?: Attributes | undefined): void;
24
+ }
25
+ /**
26
+ * Bucket boundaries in seconds for the three kinds of durations the platform
27
+ * measures: `http` is the semconv set for server requests, `io` covers one
28
+ * outbound call (1 ms – 2.5 s), `compute` a synchronous render (10 µs – 50 ms).
29
+ */
30
+ export declare const DURATION_BUCKETS: {
31
+ readonly http: readonly [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10];
32
+ readonly io: readonly [0.001, 0.0025, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5];
33
+ readonly compute: readonly [0.00001, 0.00005, 0.0001, 0.00025, 0.0005, 0.001, 0.0025, 0.005, 0.01, 0.05];
34
+ };
35
+ export declare function createHistogram(name: string, options: HistogramOptions): HistogramHandle;
36
+ export declare function createCounter(name: string, options?: InstrumentOptions): CounterHandle;
37
+ export declare function createUpDownCounter(name: string, options?: InstrumentOptions): CounterHandle;
38
+ /**
39
+ * An asynchronous gauge: `observe` runs at every collection (the Prometheus
40
+ * scrape) and reports the current values. Registered with the SDK when it
41
+ * starts, so calling this at module level is fine.
42
+ */
43
+ export declare function createObservableGauge(name: string, options: InstrumentOptions, observe: (result: ObservableHandle) => void): void;
44
+ /** the `error.type` attribute value for a thrown value: the error's class, else its type */
45
+ export declare function errorType(error: unknown): string;
46
+ export interface SpanOptions {
47
+ /** instrumentation scope, `@astroscope/<package>`; defaults to `@astroscope/node` */
48
+ scope?: string | undefined;
49
+ attributes?: Attributes | undefined;
50
+ kind?: SpanKind | undefined;
51
+ /** parent context; defaults to the active one (the request span inside a request) */
52
+ parent?: Context | undefined;
53
+ }
54
+ export interface StartedSpan {
55
+ span: Span;
56
+ /** the context with `span` active — pass as `parent` to nest children */
57
+ context: Context;
58
+ }
59
+ /** starts a span the caller ends itself; `withSpan` for work that is one function */
60
+ export declare function startSpan(name: string, options?: SpanOptions): StartedSpan;
61
+ /**
62
+ * Runs `fn` inside a span: status from the outcome, the span active for
63
+ * everything `fn` awaits. A synchronous `fn` stays synchronous — no promise
64
+ * is allocated on the common path.
65
+ */
66
+ export declare function withSpan<T>(name: string, options: SpanOptions, fn: () => MaybePromise<T>): MaybePromise<T>;
67
+ //#endregion
68
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","names":[],"sources":["../src/observability/telemetry/telemetry.ts"],"mappings":";;;iBAuCiB;;EAEf;EACA;;EAEA;;iBAGe,yBAAyB;;EAExC;;iBAGe;EACf,OAAO,eAAe,aAAa;;iBAGpB;EACf,IAAI,eAAe,aAAa;;;iBAIjB;EACf,QAAQ,eAAe,aAAa;;;;;;;qBAQzB;;;;;wBAqCG,gBAAgB,cAAc,SAAS,mBAAmB;wBAc1D,cAAc,cAAc,UAAS,oBAAyB;wBAS9D,oBAAoB,cAAc,UAAS,oBAAyB;;;;;;wBAcpE,sBACd,cACA,SAAS,mBACT,UAAU,QAAQ;;wBAgBJ,UAAU;iBAQT;;EAEf;EACA,aAAa;EACb,OAAO;;EAEP,SAAS;;iBAGM;EACf,MAAM;;EAEN,SAAS;;;wBAIK,UAAU,cAAc,UAAS,cAAmB;;;;;;wBA6BpD,SAAS,GAAG,cAAc,SAAS,aAAa,UAAU,aAAa,KAAK,aAAa"}
@@ -0,0 +1,2 @@
1
+ import { a as createUpDownCounter, c as withSpan, i as createObservableGauge, n as createCounter, o as errorType, r as createHistogram, s as startSpan, t as DURATION_BUCKETS } from "./telemetry-B9aziFyQ.js";
2
+ export { DURATION_BUCKETS, createCounter, createHistogram, createObservableGauge, createUpDownCounter, errorType, startSpan, withSpan };