@alxia/telemetry 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ function requestAttributes(url, method, ip) {
23
23
  // src/telemetry.ts
24
24
  import {
25
25
  defineMiddleware,
26
+ markFactory,
26
27
  settle
27
28
  } from "@alxia/core";
28
29
  import {
@@ -78,7 +79,7 @@ function telemetry(options) {
78
79
  const instance = options.instance ?? createTelemetry(options.service, options).install();
79
80
  const traced = guarded(options.traced ?? (() => true), () => true);
80
81
  const spanName = guarded(options.spanName ?? defaultName, defaultName);
81
- const middleware = defineMiddleware(async (ctx, next) => {
82
+ const middleware = defineMiddleware(async function telemetry(ctx, next) {
82
83
  const untraced = { span: undefined, telemetry: instance };
83
84
  const upgrade = ctx.request.headers.get("upgrade")?.toLowerCase() === "websocket";
84
85
  if (upgrade || !traced(ctx))
@@ -156,6 +157,7 @@ function guarded(hook, fallback) {
156
157
  }
157
158
  };
158
159
  }
160
+ markFactory(telemetry);
159
161
  export {
160
162
  CLIENT_ADDRESS,
161
163
  HTTP_METHOD,
@@ -168,5 +170,5 @@ export {
168
170
  telemetry
169
171
  };
170
172
 
171
- //# debugId=E98136B2AF2AC44B64756E2164756E21
173
+ //# debugId=160D011B4C9DB2B964756E2164756E21
172
174
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -3,10 +3,10 @@
3
3
  "sources": ["../src/attributes.ts", "../src/telemetry.ts", "../src/body.ts"],
4
4
  "sourcesContent": [
5
5
  "import type { Attributes } from '@nxgt/telemetry';\n\n/**\n * The semantic conventions a server span carries, by name: the ones a\n * backend's HTTP dashboards look for. They are `@nxgt/telemetry-hono`'s\n * names, kept twice on purpose — importing them would make this package\n * depend on Hono — and a server span here and a client span from\n * `@nxgt/telemetry-httpyz` must agree on them.\n */\nexport const HTTP_METHOD = 'http.request.method';\nexport const URL_PATH = 'url.path';\nexport const URL_SCHEME = 'url.scheme';\nexport const HTTP_ROUTE = 'http.route';\nexport const HTTP_STATUS = 'http.response.status_code';\nexport const SERVER_ADDRESS = 'server.address';\nexport const SERVER_PORT = 'server.port';\nexport const CLIENT_ADDRESS = 'client.address';\n\n/** A 4xx is the server working: only a 5xx marks a span. */\nexport function serverFailed(status: number): boolean {\n\treturn status >= 500;\n}\n\n/** What is known of a request before routing: the route is not, yet. */\nexport function requestAttributes(\n\turl: URL,\n\tmethod: string,\n\tip: string | undefined,\n): Attributes {\n\treturn {\n\t\t[HTTP_METHOD]: method,\n\t\t[URL_PATH]: url.pathname,\n\t\t[URL_SCHEME]: url.protocol.replace(':', ''),\n\t\t[SERVER_ADDRESS]: url.hostname,\n\t\t...(url.port === '' ? {} : { [SERVER_PORT]: Number(url.port) }),\n\t\t...(ip === undefined ? {} : { [CLIENT_ADDRESS]: ip }),\n\t};\n}\n",
6
- "import {\n\tdefineMiddleware,\n\ttype Empty,\n\ttype Middleware,\n\ttype MiddlewareMark,\n\ttype Next,\n\ttype RequestContext,\n\tsettle,\n} from '@alxia/core';\nimport {\n\tcontinuing,\n\tcreateTelemetry,\n\ttype SpanScope,\n\ttype Telemetry,\n\ttype TelemetryOptions,\n\twithTelemetry,\n} from '@nxgt/telemetry';\nimport {\n\tHTTP_ROUTE,\n\tHTTP_STATUS,\n\trequestAttributes,\n\tserverFailed,\n} from './attributes';\nimport { type Outcome, settled, watched } from './body';\n\ninterface Hooks {\n\t/**\n\t * Whether a request gets a span. Every one does by default: a library\n\t * that decides which requests do not matter hides the one that did.\n\t */\n\treadonly traced?: (ctx: RequestContext) => boolean;\n\t/** The name the span opens with. `\"<METHOD> <path>\"` by default; renamed `\"<METHOD> <route>\"` once answered, when a route matched. */\n\treadonly spanName?: (ctx: RequestContext) => string;\n\t/** Whether the response says `traceparent` back, so a caller can find the trace. Off by default. */\n\treadonly traceResponse?: boolean;\n}\n\n/**\n * A telemetry built from `service` and `@nxgt/telemetry`'s options, and\n * installed; or one handed over as `instance`, adopted and not closed.\n */\nexport type TelemetryPluginOptions =\n\t| (Hooks &\n\t\t\tTelemetryOptions & {\n\t\t\t\t/** The service name: everything groups by it. */\n\t\t\t\treadonly service: string;\n\t\t\t\treadonly instance?: undefined;\n\t\t\t})\n\t| (Hooks & {\n\t\t\treadonly instance: Telemetry;\n\t\t\treadonly service?: undefined;\n\t });\n\n/** What the routes after `telemetry()` read. */\nexport interface TelemetryContext {\n\t/** The server span around this request; `undefined` when `traced` said no. */\n\treadonly span: SpanScope | undefined;\n\treadonly telemetry: Telemetry;\n}\n\n/**\n * What `telemetry()` makes: a middleware that gives `span` and\n * `telemetry`, with the telemetry on it, to close on stop.\n */\nexport type TelemetryMiddleware = Middleware<\n\tEmpty,\n\tPromise<Next<TelemetryContext>>\n> &\n\tMiddlewareMark & { telemetry: Telemetry };\n\n/**\n * One server span per request, with [`@nxgt/telemetry`](https://www.npmjs.com/package/@nxgt/telemetry),\n * as a middleware.\n *\n * Give it to `use` first: the span then holds everything the request\n * runs after it — the middlewares, the handler, what they await, the\n * answer to an error — and every log written with `createLogger` inside\n * it carries its trace id. A request no route matches gets one too. An\n * inbound `traceparent` continues its trace; an unusable one starts a\n * fresh trace. The span is named for the route, `GET /users/:id`, once\n * routing has matched. Only a 5xx marks it an error. A WebSocket upgrade\n * gets no span: there is no response to time.\n *\n * A streamed body (a page rendered as it goes, an event stream) keeps the\n * span open until it has been sent: a body that fails midway marks it an\n * error, and a client that leaves midway adds an `http.response.aborted`\n * event. A body of known length, or none, ends the span with the response.\n *\n * Routes declared after it read the span as `span`, and the telemetry as\n * `telemetry`.\n *\n * ```ts\n * const tracing = telemetry({ service: 'checkout', exporters: [otlpExporter({ endpoint })] });\n * const app = alxia().use(tracing).get(...);\n * app.onStop(() => tracing.telemetry.close());\n * ```\n */\nexport function telemetry(\n\toptions: TelemetryPluginOptions,\n): TelemetryMiddleware {\n\tconst instance =\n\t\toptions.instance ?? createTelemetry(options.service, options).install();\n\tconst traced = guarded(options.traced ?? (() => true), () => true);\n\tconst spanName = guarded(options.spanName ?? defaultName, defaultName);\n\n\tconst middleware = defineMiddleware(async (ctx, next) => {\n\t\tconst untraced: TelemetryContext = { span: undefined, telemetry: instance };\n\t\tconst upgrade =\n\t\t\tctx.request.headers.get('upgrade')?.toLowerCase() === 'websocket';\n\t\tif (upgrade || !traced(ctx)) return next(untraced);\n\t\t// Resolved with the response as soon as there is one; the span itself\n\t\t// stays open until a streamed body has been sent, or has stopped.\n\t\tconst { promise, resolve, reject } =\n\t\t\tPromise.withResolvers<Next<TelemetryContext>>();\n\t\tconst answer = (response: Response) =>\n\t\t\tresolve(response as Next<TelemetryContext>);\n\t\twithTelemetry(instance, () =>\n\t\t\tcontinuing(\n\t\t\t\tctx.request.headers.get('traceparent'),\n\t\t\t\tspanName(ctx),\n\t\t\t\t{ kind: 'server' },\n\t\t\t\tasync (scope) => {\n\t\t\t\t\t// The span's own, not `SpanOptions.attributes`: those every span\n\t\t\t\t\t// and log inside inherits, and a database call is not the request.\n\t\t\t\t\tscope.attributes(\n\t\t\t\t\t\trequestAttributes(ctx.url, ctx.request.method, ctx.ip),\n\t\t\t\t\t);\n\t\t\t\t\tconst added: TelemetryContext = { span: scope, telemetry: instance };\n\t\t\t\t\tconst response = await settle(ctx, next(added));\n\t\t\t\t\tif (ctx.route !== undefined) {\n\t\t\t\t\t\tscope.name = `${ctx.request.method} ${ctx.route}`;\n\t\t\t\t\t\tscope.attribute(HTTP_ROUTE, ctx.route);\n\t\t\t\t\t}\n\t\t\t\t\trecord(scope, response.status, ctx.error);\n\t\t\t\t\tif (options.traceResponse) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tresponse.headers.set('traceparent', scope.traceparent());\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// An immutable response keeps its headers; the span is what matters.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn handOver(scope, response, answer);\n\t\t\t\t},\n\t\t\t),\n\t\t).then(answer, reject);\n\t\treturn promise;\n\t});\n\n\treturn Object.assign(middleware, { telemetry: instance });\n}\n\n/**\n * The status, and the failure. A route's error is recorded as the span's\n * exception, but only a 5xx makes it an error: a 401 a guard answered is\n * the server working.\n */\nfunction record(scope: SpanScope, status: number, error: unknown): void {\n\tif (error !== undefined) {\n\t\tconst before = scope.status;\n\t\tscope.fail(error);\n\t\tif (!serverFailed(status)) scope.status = before;\n\t}\n\tscope.attribute(HTTP_STATUS, status);\n\tif (serverFailed(status) && scope.status === 'ok') scope.status = 'error';\n}\n\n/**\n * The response the span ends with. A body with nothing left to time, or a\n * span never exported, is returned as it is: the span ends, then the\n * response is handed over. A streamed body is handed over at once through\n * `resolve`, watched, and returned once it has ended, so the span ends then.\n */\nasync function handOver(\n\tscope: SpanScope,\n\tresponse: Response,\n\tresolve: (response: Response) => void,\n): Promise<Response> {\n\tif (settled(response) || !scope.context.sampled) return response;\n\tconst { promise: sent, resolve: end } = Promise.withResolvers<void>();\n\tconst body = watched(\n\t\tresponse.body as ReadableStream<Uint8Array>,\n\t\t(outcome, error) => {\n\t\t\ttry {\n\t\t\t\tended(scope, outcome, error);\n\t\t\t} finally {\n\t\t\t\tend();\n\t\t\t}\n\t\t},\n\t);\n\tconst streamed = new Response(body, {\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t\theaders: response.headers,\n\t});\n\tresolve(streamed);\n\tawait sent;\n\treturn streamed;\n}\n\n/**\n * How a streamed body ended, on its span: a body that failed fails the\n * span, as a 5xx does; a client that left is an event, the server having\n * done nothing wrong.\n */\nfunction ended(scope: SpanScope, outcome: Outcome, error: unknown): void {\n\tif (outcome === 'errored') {\n\t\tscope.fail(error);\n\t\t// `fail` keeps a failure recorded before, and its status with it.\n\t\tscope.status = 'error';\n\t} else if (outcome === 'aborted') scope.event(RESPONSE_ABORTED);\n}\n\n/** The event of a server span whose client left before the body was sent. */\nconst RESPONSE_ABORTED = 'http.response.aborted';\n\nfunction defaultName(ctx: RequestContext): string {\n\treturn `${ctx.request.method} ${ctx.url.pathname}`;\n}\n\n/** A hook, and what to answer when it throws: observability never costs the request. */\nfunction guarded<T>(\n\thook: (ctx: RequestContext) => T,\n\tfallback: (ctx: RequestContext) => T,\n): (ctx: RequestContext) => T {\n\treturn (ctx) => {\n\t\ttry {\n\t\t\treturn hook(ctx);\n\t\t} catch {\n\t\t\treturn fallback(ctx);\n\t\t}\n\t};\n}\n",
6
+ "import {\n\tdefineMiddleware,\n\ttype Empty,\n\ttype Middleware,\n\tmarkFactory,\n\ttype Next,\n\ttype RequestContext,\n\tsettle,\n} from '@alxia/core';\nimport {\n\tcontinuing,\n\tcreateTelemetry,\n\ttype SpanScope,\n\ttype Telemetry,\n\ttype TelemetryOptions,\n\twithTelemetry,\n} from '@nxgt/telemetry';\nimport {\n\tHTTP_ROUTE,\n\tHTTP_STATUS,\n\trequestAttributes,\n\tserverFailed,\n} from './attributes';\nimport { type Outcome, settled, watched } from './body';\n\ninterface Hooks {\n\t/**\n\t * Whether a request gets a span. Every one does by default: a library\n\t * that decides which requests do not matter hides the one that did.\n\t */\n\treadonly traced?: (ctx: RequestContext) => boolean;\n\t/** The name the span opens with. `\"<METHOD> <path>\"` by default; renamed `\"<METHOD> <route>\"` once answered, when a route matched. */\n\treadonly spanName?: (ctx: RequestContext) => string;\n\t/** Whether the response says `traceparent` back, so a caller can find the trace. Off by default. */\n\treadonly traceResponse?: boolean;\n}\n\n/**\n * A telemetry built from `service` and `@nxgt/telemetry`'s options, and\n * installed; or one handed over as `instance`, adopted and not closed.\n */\nexport type TelemetryPluginOptions =\n\t| (Hooks &\n\t\t\tTelemetryOptions & {\n\t\t\t\t/** The service name: everything groups by it. */\n\t\t\t\treadonly service: string;\n\t\t\t\treadonly instance?: undefined;\n\t\t\t})\n\t| (Hooks & {\n\t\t\treadonly instance: Telemetry;\n\t\t\treadonly service?: undefined;\n\t });\n\n/** What the routes after `telemetry()` read. */\nexport interface TelemetryContext {\n\t/** The server span around this request; `undefined` when `traced` said no. */\n\treadonly span: SpanScope | undefined;\n\treadonly telemetry: Telemetry;\n}\n\n/**\n * What `telemetry()` makes: a middleware that gives `span` and\n * `telemetry`, with the telemetry on it, to close on stop.\n */\nexport type TelemetryMiddleware = Middleware<\n\tEmpty,\n\tPromise<Next<TelemetryContext>>\n> & { telemetry: Telemetry };\n\n/**\n * One server span per request, with [`@nxgt/telemetry`](https://www.npmjs.com/package/@nxgt/telemetry),\n * as a middleware.\n *\n * Give it to `use` first: the span then holds everything the request\n * runs after it — the middlewares, the handler, what they await, the\n * answer to an error — and every log written with `createLogger` inside\n * it carries its trace id. A request no route matches gets one too. An\n * inbound `traceparent` continues its trace; an unusable one starts a\n * fresh trace. The span is named for the route, `GET /users/:id`, once\n * routing has matched. Only a 5xx marks it an error. A WebSocket upgrade\n * gets no span: there is no response to time.\n *\n * A streamed body (a page rendered as it goes, an event stream) keeps the\n * span open until it has been sent: a body that fails midway marks it an\n * error, and a client that leaves midway adds an `http.response.aborted`\n * event. A body of known length, or none, ends the span with the response.\n *\n * Routes declared after it read the span as `span`, and the telemetry as\n * `telemetry`.\n *\n * ```ts\n * const tracing = telemetry({ service: 'checkout', exporters: [otlpExporter({ endpoint })] });\n * const app = alxia().use(tracing).get(...);\n * app.onStop(() => tracing.telemetry.close());\n * ```\n */\nexport function telemetry(\n\toptions: TelemetryPluginOptions,\n): TelemetryMiddleware {\n\tconst instance =\n\t\toptions.instance ?? createTelemetry(options.service, options).install();\n\tconst traced = guarded(options.traced ?? (() => true), () => true);\n\tconst spanName = guarded(options.spanName ?? defaultName, defaultName);\n\n\tconst middleware = defineMiddleware(async function telemetry(ctx, next) {\n\t\tconst untraced: TelemetryContext = { span: undefined, telemetry: instance };\n\t\tconst upgrade =\n\t\t\tctx.request.headers.get('upgrade')?.toLowerCase() === 'websocket';\n\t\tif (upgrade || !traced(ctx)) return next(untraced);\n\t\t// Resolved with the response as soon as there is one; the span itself\n\t\t// stays open until a streamed body has been sent, or has stopped.\n\t\tconst { promise, resolve, reject } =\n\t\t\tPromise.withResolvers<Next<TelemetryContext>>();\n\t\tconst answer = (response: Response) =>\n\t\t\tresolve(response as Next<TelemetryContext>);\n\t\twithTelemetry(instance, () =>\n\t\t\tcontinuing(\n\t\t\t\tctx.request.headers.get('traceparent'),\n\t\t\t\tspanName(ctx),\n\t\t\t\t{ kind: 'server' },\n\t\t\t\tasync (scope) => {\n\t\t\t\t\t// The span's own, not `SpanOptions.attributes`: those every span\n\t\t\t\t\t// and log inside inherits, and a database call is not the request.\n\t\t\t\t\tscope.attributes(\n\t\t\t\t\t\trequestAttributes(ctx.url, ctx.request.method, ctx.ip),\n\t\t\t\t\t);\n\t\t\t\t\tconst added: TelemetryContext = { span: scope, telemetry: instance };\n\t\t\t\t\tconst response = await settle(ctx, next(added));\n\t\t\t\t\tif (ctx.route !== undefined) {\n\t\t\t\t\t\tscope.name = `${ctx.request.method} ${ctx.route}`;\n\t\t\t\t\t\tscope.attribute(HTTP_ROUTE, ctx.route);\n\t\t\t\t\t}\n\t\t\t\t\trecord(scope, response.status, ctx.error);\n\t\t\t\t\tif (options.traceResponse) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tresponse.headers.set('traceparent', scope.traceparent());\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// An immutable response keeps its headers; the span is what matters.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn handOver(scope, response, answer);\n\t\t\t\t},\n\t\t\t),\n\t\t).then(answer, reject);\n\t\treturn promise;\n\t});\n\n\treturn Object.assign(middleware, { telemetry: instance });\n}\n\n/**\n * The status, and the failure. A route's error is recorded as the span's\n * exception, but only a 5xx makes it an error: a 401 a guard answered is\n * the server working.\n */\nfunction record(scope: SpanScope, status: number, error: unknown): void {\n\tif (error !== undefined) {\n\t\tconst before = scope.status;\n\t\tscope.fail(error);\n\t\tif (!serverFailed(status)) scope.status = before;\n\t}\n\tscope.attribute(HTTP_STATUS, status);\n\tif (serverFailed(status) && scope.status === 'ok') scope.status = 'error';\n}\n\n/**\n * The response the span ends with. A body with nothing left to time, or a\n * span never exported, is returned as it is: the span ends, then the\n * response is handed over. A streamed body is handed over at once through\n * `resolve`, watched, and returned once it has ended, so the span ends then.\n */\nasync function handOver(\n\tscope: SpanScope,\n\tresponse: Response,\n\tresolve: (response: Response) => void,\n): Promise<Response> {\n\tif (settled(response) || !scope.context.sampled) return response;\n\tconst { promise: sent, resolve: end } = Promise.withResolvers<void>();\n\tconst body = watched(\n\t\tresponse.body as ReadableStream<Uint8Array>,\n\t\t(outcome, error) => {\n\t\t\ttry {\n\t\t\t\tended(scope, outcome, error);\n\t\t\t} finally {\n\t\t\t\tend();\n\t\t\t}\n\t\t},\n\t);\n\tconst streamed = new Response(body, {\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t\theaders: response.headers,\n\t});\n\tresolve(streamed);\n\tawait sent;\n\treturn streamed;\n}\n\n/**\n * How a streamed body ended, on its span: a body that failed fails the\n * span, as a 5xx does; a client that left is an event, the server having\n * done nothing wrong.\n */\nfunction ended(scope: SpanScope, outcome: Outcome, error: unknown): void {\n\tif (outcome === 'errored') {\n\t\tscope.fail(error);\n\t\t// `fail` keeps a failure recorded before, and its status with it.\n\t\tscope.status = 'error';\n\t} else if (outcome === 'aborted') scope.event(RESPONSE_ABORTED);\n}\n\n/** The event of a server span whose client left before the body was sent. */\nconst RESPONSE_ABORTED = 'http.response.aborted';\n\nfunction defaultName(ctx: RequestContext): string {\n\treturn `${ctx.request.method} ${ctx.url.pathname}`;\n}\n\n/** A hook, and what to answer when it throws: observability never costs the request. */\nfunction guarded<T>(\n\thook: (ctx: RequestContext) => T,\n\tfallback: (ctx: RequestContext) => T,\n): (ctx: RequestContext) => T {\n\treturn (ctx) => {\n\t\ttry {\n\t\t\treturn hook(ctx);\n\t\t} catch {\n\t\t\treturn fallback(ctx);\n\t\t}\n\t};\n}\n\nmarkFactory(telemetry);\n",
7
7
  "// Kept twice: `logger/src/body.ts` is the same file. Change both together.\n/** How a streamed body ended: sent whole, left by its client, or failed. */\nexport type Outcome = 'completed' | 'aborted' | 'errored';\n\n/**\n * Whether `response` is sent as it is, with nothing left to time: no body,\n * or a `Content-Length` header, which `@alxia/core` sets on every reply of\n * a string, JSON, a buffer or a file. Bun sends those without JavaScript,\n * so they are not wrapped. A raw `Response` has no such header, even of a\n * string, and is watched. The header is read first: it does not make Bun\n * build the body's stream.\n */\nexport function settled(response: Response): boolean {\n\treturn response.headers.has('content-length') || response.body === null;\n}\n\n/**\n * `body`, passed through chunk by chunk, calling `end` once when it has\n * been read to its end, cancelled by its reader (the client left: Bun\n * cancels the body), or failed, with the failure. A cancel goes on to\n * `body`, so an event stream releases its generator. One chunk is read per\n * pull, never ahead.\n */\nexport function watched(\n\tbody: ReadableStream<Uint8Array>,\n\tend: (outcome: Outcome, error?: unknown) => void,\n): ReadableStream<Uint8Array> {\n\tconst reader = body.getReader();\n\tlet ended = false;\n\tconst finish = (outcome: Outcome, error?: unknown) => {\n\t\tif (ended) return;\n\t\tended = true;\n\t\ttry {\n\t\t\tend(outcome, error);\n\t\t} catch (failure) {\n\t\t\t// What watches the body never stops it: a cancel still reaches it.\n\t\t\ttry {\n\t\t\t\tconsole.error(failure);\n\t\t\t} catch {}\n\t\t}\n\t};\n\treturn new ReadableStream<Uint8Array>(\n\t\t{\n\t\t\tasync pull(controller) {\n\t\t\t\tlet next: Awaited<ReturnType<typeof reader.read>>;\n\t\t\t\ttry {\n\t\t\t\t\tnext = await reader.read();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tfinish('errored', error);\n\t\t\t\t\tcontroller.error(error);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (next.done) {\n\t\t\t\t\tfinish('completed');\n\t\t\t\t\tcontroller.close();\n\t\t\t\t} else controller.enqueue(next.value);\n\t\t\t},\n\t\t\tcancel(reason) {\n\t\t\t\tfinish('aborted');\n\t\t\t\treturn reader.cancel(reason);\n\t\t\t},\n\t\t},\n\t\t{ highWaterMark: 0 },\n\t);\n}\n"
8
8
  ],
9
- "mappings": ";AASO,IAAM,cAAc;AACpB,IAAM,WAAW;AACjB,IAAM,aAAa;AACnB,IAAM,aAAa;AACnB,IAAM,cAAc;AACpB,IAAM,iBAAiB;AACvB,IAAM,cAAc;AACpB,IAAM,iBAAiB;AAGvB,SAAS,YAAY,CAAC,QAAyB;AAAA,EACrD,OAAO,UAAU;AAAA;AAIX,SAAS,iBAAiB,CAChC,KACA,QACA,IACa;AAAA,EACb,OAAO;AAAA,KACL,cAAc;AAAA,KACd,WAAW,IAAI;AAAA,KACf,aAAa,IAAI,SAAS,QAAQ,KAAK,EAAE;AAAA,KACzC,iBAAiB,IAAI;AAAA,OAClB,IAAI,SAAS,KAAK,CAAC,IAAI,GAAG,cAAc,OAAO,IAAI,IAAI,EAAE;AAAA,OACzD,OAAO,YAAY,CAAC,IAAI,GAAG,iBAAiB,GAAG;AAAA,EACpD;AAAA;;ACpCD;AAAA;AAAA;AAAA;AASA;AAAA;AAAA;AAAA;AAAA;;;ACGO,SAAS,OAAO,CAAC,UAA6B;AAAA,EACpD,OAAO,SAAS,QAAQ,IAAI,gBAAgB,KAAK,SAAS,SAAS;AAAA;AAU7D,SAAS,OAAO,CACtB,MACA,KAC6B;AAAA,EAC7B,MAAM,SAAS,KAAK,UAAU;AAAA,EAC9B,IAAI,QAAQ;AAAA,EACZ,MAAM,SAAS,CAAC,SAAkB,UAAoB;AAAA,IACrD,IAAI;AAAA,MAAO;AAAA,IACX,QAAQ;AAAA,IACR,IAAI;AAAA,MACH,IAAI,SAAS,KAAK;AAAA,MACjB,OAAO,SAAS;AAAA,MAEjB,IAAI;AAAA,QACH,QAAQ,MAAM,OAAO;AAAA,QACpB,MAAM;AAAA;AAAA;AAAA,EAGV,OAAO,IAAI,eACV;AAAA,SACO,KAAI,CAAC,YAAY;AAAA,MACtB,IAAI;AAAA,MACJ,IAAI;AAAA,QACH,OAAO,MAAM,OAAO,KAAK;AAAA,QACxB,OAAO,OAAO;AAAA,QACf,OAAO,WAAW,KAAK;AAAA,QACvB,WAAW,MAAM,KAAK;AAAA,QACtB;AAAA;AAAA,MAED,IAAI,KAAK,MAAM;AAAA,QACd,OAAO,WAAW;AAAA,QAClB,WAAW,MAAM;AAAA,MAClB,EAAO;AAAA,mBAAW,QAAQ,KAAK,KAAK;AAAA;AAAA,IAErC,MAAM,CAAC,QAAQ;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,OAAO,OAAO,OAAO,MAAM;AAAA;AAAA,EAE7B,GACA,EAAE,eAAe,EAAE,CACpB;AAAA;;;ADkCM,SAAS,SAAS,CACxB,SACsB;AAAA,EACtB,MAAM,WACL,QAAQ,YAAY,gBAAgB,QAAQ,SAAS,OAAO,EAAE,QAAQ;AAAA,EACvE,MAAM,SAAS,QAAQ,QAAQ,WAAW,MAAM,OAAO,MAAM,IAAI;AAAA,EACjE,MAAM,WAAW,QAAQ,QAAQ,YAAY,aAAa,WAAW;AAAA,EAErE,MAAM,aAAa,iBAAiB,OAAO,KAAK,SAAS;AAAA,IACxD,MAAM,WAA6B,EAAE,MAAM,WAAW,WAAW,SAAS;AAAA,IAC1E,MAAM,UACL,IAAI,QAAQ,QAAQ,IAAI,SAAS,GAAG,YAAY,MAAM;AAAA,IACvD,IAAI,WAAW,CAAC,OAAO,GAAG;AAAA,MAAG,OAAO,KAAK,QAAQ;AAAA,IAGjD,QAAQ,SAAS,SAAS,WACzB,QAAQ,cAAsC;AAAA,IAC/C,MAAM,SAAS,CAAC,aACf,QAAQ,QAAkC;AAAA,IAC3C,cAAc,UAAU,MACvB,WACC,IAAI,QAAQ,QAAQ,IAAI,aAAa,GACrC,SAAS,GAAG,GACZ,EAAE,MAAM,SAAS,GACjB,OAAO,UAAU;AAAA,MAGhB,MAAM,WACL,kBAAkB,IAAI,KAAK,IAAI,QAAQ,QAAQ,IAAI,EAAE,CACtD;AAAA,MACA,MAAM,QAA0B,EAAE,MAAM,OAAO,WAAW,SAAS;AAAA,MACnE,MAAM,WAAW,MAAM,OAAO,KAAK,KAAK,KAAK,CAAC;AAAA,MAC9C,IAAI,IAAI,UAAU,WAAW;AAAA,QAC5B,MAAM,OAAO,GAAG,IAAI,QAAQ,UAAU,IAAI;AAAA,QAC1C,MAAM,UAAU,YAAY,IAAI,KAAK;AAAA,MACtC;AAAA,MACA,OAAO,OAAO,SAAS,QAAQ,IAAI,KAAK;AAAA,MACxC,IAAI,QAAQ,eAAe;AAAA,QAC1B,IAAI;AAAA,UACH,SAAS,QAAQ,IAAI,eAAe,MAAM,YAAY,CAAC;AAAA,UACtD,MAAM;AAAA,MAGT;AAAA,MACA,OAAO,SAAS,OAAO,UAAU,MAAM;AAAA,KAEzC,CACD,EAAE,KAAK,QAAQ,MAAM;AAAA,IACrB,OAAO;AAAA,GACP;AAAA,EAED,OAAO,OAAO,OAAO,YAAY,EAAE,WAAW,SAAS,CAAC;AAAA;AAQzD,SAAS,MAAM,CAAC,OAAkB,QAAgB,OAAsB;AAAA,EACvE,IAAI,UAAU,WAAW;AAAA,IACxB,MAAM,SAAS,MAAM;AAAA,IACrB,MAAM,KAAK,KAAK;AAAA,IAChB,IAAI,CAAC,aAAa,MAAM;AAAA,MAAG,MAAM,SAAS;AAAA,EAC3C;AAAA,EACA,MAAM,UAAU,aAAa,MAAM;AAAA,EACnC,IAAI,aAAa,MAAM,KAAK,MAAM,WAAW;AAAA,IAAM,MAAM,SAAS;AAAA;AASnE,eAAe,QAAQ,CACtB,OACA,UACA,SACoB;AAAA,EACpB,IAAI,QAAQ,QAAQ,KAAK,CAAC,MAAM,QAAQ;AAAA,IAAS,OAAO;AAAA,EACxD,QAAQ,SAAS,MAAM,SAAS,QAAQ,QAAQ,cAAoB;AAAA,EACpE,MAAM,OAAO,QACZ,SAAS,MACT,CAAC,SAAS,UAAU;AAAA,IACnB,IAAI;AAAA,MACH,MAAM,OAAO,SAAS,KAAK;AAAA,cAC1B;AAAA,MACD,IAAI;AAAA;AAAA,GAGP;AAAA,EACA,MAAM,WAAW,IAAI,SAAS,MAAM;AAAA,IACnC,QAAQ,SAAS;AAAA,IACjB,YAAY,SAAS;AAAA,IACrB,SAAS,SAAS;AAAA,EACnB,CAAC;AAAA,EACD,QAAQ,QAAQ;AAAA,EAChB,MAAM;AAAA,EACN,OAAO;AAAA;AAQR,SAAS,KAAK,CAAC,OAAkB,SAAkB,OAAsB;AAAA,EACxE,IAAI,YAAY,WAAW;AAAA,IAC1B,MAAM,KAAK,KAAK;AAAA,IAEhB,MAAM,SAAS;AAAA,EAChB,EAAO,SAAI,YAAY;AAAA,IAAW,MAAM,MAAM,gBAAgB;AAAA;AAI/D,IAAM,mBAAmB;AAEzB,SAAS,WAAW,CAAC,KAA6B;AAAA,EACjD,OAAO,GAAG,IAAI,QAAQ,UAAU,IAAI,IAAI;AAAA;AAIzC,SAAS,OAAU,CAClB,MACA,UAC6B;AAAA,EAC7B,OAAO,CAAC,QAAQ;AAAA,IACf,IAAI;AAAA,MACH,OAAO,KAAK,GAAG;AAAA,MACd,MAAM;AAAA,MACP,OAAO,SAAS,GAAG;AAAA;AAAA;AAAA;",
10
- "debugId": "E98136B2AF2AC44B64756E2164756E21",
9
+ "mappings": "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",
10
+ "debugId": "160D011B4C9DB2B964756E2164756E21",
11
11
  "names": []
12
12
  }
@@ -1,4 +1,4 @@
1
- import { type Empty, type Middleware, type MiddlewareMark, type Next, type RequestContext } from '@alxia/core';
1
+ import { type Empty, type Middleware, type Next, type RequestContext } from '@alxia/core';
2
2
  import { type SpanScope, type Telemetry, type TelemetryOptions } from '@nxgt/telemetry';
3
3
  interface Hooks {
4
4
  /**
@@ -33,7 +33,7 @@ export interface TelemetryContext {
33
33
  * What `telemetry()` makes: a middleware that gives `span` and
34
34
  * `telemetry`, with the telemetry on it, to close on stop.
35
35
  */
36
- export type TelemetryMiddleware = Middleware<Empty, Promise<Next<TelemetryContext>>> & MiddlewareMark & {
36
+ export type TelemetryMiddleware = Middleware<Empty, Promise<Next<TelemetryContext>>> & {
37
37
  telemetry: Telemetry;
38
38
  };
39
39
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,KAAK,EACV,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,IAAI,EACT,KAAK,cAAc,EAEnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAGN,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,gBAAgB,EAErB,MAAM,iBAAiB,CAAC;AASzB,UAAU,KAAK;IACd;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC;IACnD,sIAAsI;IACtI,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAC;IACpD,oGAAoG;IACpG,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAC/B,CAAC,KAAK,GACN,gBAAgB,GAAG;IAClB,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC9B,CAAC,GACF,CAAC,KAAK,GAAG;IACT,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC;CAC5B,CAAC,CAAC;AAEN,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAC3C,KAAK,EACL,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAC/B,GACA,cAAc,GAAG;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,sBAAsB,GAC7B,mBAAmB,CAkDrB"}
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,KAAK,KAAK,EACV,KAAK,UAAU,EAEf,KAAK,IAAI,EACT,KAAK,cAAc,EAEnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAGN,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,gBAAgB,EAErB,MAAM,iBAAiB,CAAC;AASzB,UAAU,KAAK;IACd;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC;IACnD,sIAAsI;IACtI,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAC;IACpD,oGAAoG;IACpG,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAC/B,CAAC,KAAK,GACN,gBAAgB,GAAG;IAClB,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC9B,CAAC,GACF,CAAC,KAAK,GAAG;IACT,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC;CAC5B,CAAC,CAAC;AAEN,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAC3C,KAAK,EACL,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAC/B,GAAG;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,sBAAsB,GAC7B,mBAAmB,CAkDrB"}
package/docs/guide.md CHANGED
@@ -68,7 +68,7 @@ One span per request, of kind `server`, opened by the middleware. It
68
68
  holds everything the request runs after it: the middlewares, validation,
69
69
  the route's own middlewares and handler, whatever they await, and the
70
70
  answer to an error: `telemetry()` settles `next()`, so the span sees the
71
- response the client gets, an `onError` reply or a 500 included. A log written with `createLogger` anywhere inside it,
71
+ response the client gets, an `HttpError`'s status or a 500 included. A log written with `createLogger` anywhere inside it,
72
72
  and a span opened with `span()`, belong to it.
73
73
 
74
74
  ```ts
@@ -149,7 +149,7 @@ unmatched request still gets its span). A
149
149
  | --- | --- | --- |
150
150
  | `2xx`, `3xx`, `4xx` replied | `ok` | none |
151
151
  | a `4xx` an error-handling middleware made of a thrown error | `ok` | none: the middleware caught it |
152
- | a `4xx` the route boundary made of a thrown error (a deprecated `onError` hook, an `HttpError`) | `ok` | the error |
152
+ | a `4xx` the route boundary made of a thrown `HttpError` | `ok` | the error |
153
153
  | `499`, the client hung up mid-request | `ok` | the `AbortError` |
154
154
  | a `5xx` from a throw | `error` | the error |
155
155
  | a `5xx` the route replied | `error` | none |
@@ -416,12 +416,7 @@ const app = alxia()
416
416
  .get('/', ({ reply }) => reply(200, 'ok'))
417
417
  .onStop(() => tracing.telemetry.close());
418
418
 
419
- app.listen(3000);
420
-
421
- process.on('SIGTERM', async () => {
422
- await app.stop();
423
- process.exit(0);
424
- });
419
+ app.listen(3000); // SIGTERM: the requests in flight finish, then onStop closes it
425
420
  ```
426
421
 
427
422
  Once closed, a telemetry takes nothing more: the app still answers, and
package/docs/roadmap.md CHANGED
@@ -7,7 +7,7 @@ number on it. Every release, with each change it made, is in
7
7
 
8
8
  ## Now
9
9
 
10
- - **A middleware, not a plugin (0.4).** `app.use(telemetry({ ... }))` opens a span for every request, a 404 or a 405 included, around everything after it, and sees the response the client gets. `app.plugin(telemetry(...))` still works, deprecated.
10
+ - **A middleware, not a plugin (0.4).** `app.use(telemetry({ ... }))` opens a span for every request, a 404 or a 405 included, around everything after it, and sees the response the client gets. `app.plugin(telemetry(...))`, the deprecated form, was removed in 0.5.
11
11
 
12
12
  ## Next
13
13
 
@@ -39,7 +39,7 @@ Nothing scheduled yet.
39
39
  ### 0.1.0
40
40
 
41
41
  - **One server span per request.** `alxia().use(telemetry({ service, exporters }))`
42
- opens a span around everything a request runs — hooks, handler, what
42
+ opens a span around everything a request runs — middlewares, handler, what
43
43
  they await — and every log written with `@nxgt/telemetry`'s
44
44
  `createLogger` inside it carries its trace id.
45
45
  - **Named for the route.** The span is renamed `GET /orders/:id` once
@@ -6,7 +6,7 @@ the server log, or, for what prints nothing, what you see in your traces.
6
6
  **Types**
7
7
 
8
8
  - [`Property 'span' does not exist on type 'Context<…>'`](#property-span-does-not-exist-on-type-context)
9
- - [`Type 'Alxia<Empty, "", never>' is not assignable to type 'TelemetryPluginOptions'`](#type-alxiaempty--never-is-not-assignable-to-type-telemetrypluginoptions)
9
+ - [`Type 'TelemetryMiddleware' is not assignable to type '"this looks like a factory given uncalled: call it, as use(cors()) and not use(cors)"'`](#type-telemetrymiddleware-is-not-assignable-to-type-this-looks-like-a-factory-given-uncalled-call-it-as-usecors-and-not-usecors)
10
10
  - [`Property 'service' is missing in type '…' but required in type '{ readonly service: string; readonly instance?: undefined; }'`](#property-service-is-missing-in-type--but-required-in-type--readonly-service-string-readonly-instance-undefined-)
11
11
  - [`Type 'Telemetry' is not assignable to type 'undefined'`](#type-telemetry-is-not-assignable-to-type-undefined)
12
12
  - [`Object literal may only specify known properties, and 'version' does not exist in type 'Hooks & { readonly instance: Telemetry; … }'`](#object-literal-may-only-specify-known-properties-and-version-does-not-exist-in-type-hooks---readonly-instance-telemetry--)
@@ -60,22 +60,27 @@ const app = alxia()
60
60
  });
61
61
  ```
62
62
 
63
- ### `Type 'Alxia<Empty, "", never>' is not assignable to type 'TelemetryPluginOptions'`
63
+ ### `Type 'TelemetryMiddleware' is not assignable to type '"this looks like a factory given uncalled: call it, as use(cors()) and not use(cors)"'`
64
64
 
65
65
  ```text
66
- error TS2769: No overload matches this call.
67
- Overload 1 of 11, '(plugin: (app: Alxia<Empty, "", never>) => AnyAlxia): AnyAlxia', gave the following error.
68
- Argument of type '(options: TelemetryPluginOptions) => Middleware<…>' is not assignable to parameter of type '(app: Alxia<Empty, "", never>) => AnyAlxia'.
69
- Types of parameters 'options' and 'app' are incompatible.
70
- Type 'Alxia<Empty, "", never>' is not assignable to type 'TelemetryPluginOptions'.
66
+ error TS2345: Argument of type '(options: TelemetryPluginOptions) => TelemetryMiddleware' is not assignable to parameter of type '…'.
67
+ …
68
+ Type 'TelemetryMiddleware' is not assignable to type '"this looks like a factory given uncalled: call it, as use(cors()) and not use(cors)"'.
71
69
  ```
72
70
 
73
- **When:** `app.use(telemetry)`, without calling it.
71
+ **When:** `app.use(telemetry)`, the factory given uncalled. The message names
72
+ `cors` as its example, whichever factory it is. It also throws where it is
73
+ declared, since alxia 0.5, rather than answering each request with a 500:
74
74
 
75
- **Why:** `telemetry` makes the middleware; it is not the middleware, and it
76
- needs a `service` or an `instance`.
75
+ ```text
76
+ TypeError: use(): argument 1 looks like a factory (telemetry): call it, use(telemetry())
77
+ ```
77
78
 
78
- **Fix:**
79
+ **Why:** `telemetry` makes the middleware; it is not the middleware. A
80
+ function that returns a function is no middleware, and `telemetry` is marked
81
+ as a factory, so `use`, a route and `plugin` refuse it.
82
+
83
+ **Fix:** call it, with a `service` or an `instance`:
79
84
 
80
85
  ```ts
81
86
  alxia().use(telemetry({ service: 'checkout', exporters: [consoleExporter()] }));
@@ -208,7 +213,7 @@ is stopped, and the last spans and logs never reach the exporter — with
208
213
  or a second after it started. A process that exits first loses it. The
209
214
  middleware never closes the telemetry, not even one it built from `service`.
210
215
 
211
- **Fix:** close it in `onStop`, await `app.stop()` on shutdown, and await
216
+ **Fix:** close it in `onStop`, which `listen`'s shutdown on `SIGTERM` awaits, and await
212
217
  `close()` in a script or a test before reading what was exported:
213
218
 
214
219
  ```ts
@@ -216,10 +221,7 @@ const app = alxia()
216
221
  .use(tracing)
217
222
  .onStop(() => tracing.telemetry.close());
218
223
 
219
- process.on('SIGTERM', async () => {
220
- await app.stop();
221
- process.exit(0);
222
- });
224
+ app.listen(3000); // on SIGTERM: the requests drain, then onStop closes the telemetry
223
225
  ```
224
226
 
225
227
  ### A log written in a route has no `traceId`, or never arrives
@@ -390,8 +392,8 @@ app.use(telemetry({ service: 'checkout', exporters, traced: (ctx) => ctx.url.pat
390
392
 
391
393
  ### A span has an exception, and its status is `ok`
392
394
 
393
- **When:** a route throws, and the route boundary (an `HttpError`, a
394
- deprecated `onError` hook) answers with a `4xx`. An error-handling middleware
395
+ **When:** a route throws, and the route boundary (an `HttpError`'s
396
+ status) answers with a `4xx`. An error-handling middleware
395
397
  that catches it leaves the span `ok` with no exception at all.
396
398
 
397
399
  **Why:** the error is recorded as the span's exception, but only a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alxia/telemetry",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Traces and logs for alxia on @nxgt/telemetry: one server span per request around everything it runs, the traceparent continued, the route as its name",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,13 +41,13 @@
41
41
  ]
42
42
  },
43
43
  "devDependencies": {
44
- "@alxia/core": "^0.4.0",
44
+ "@alxia/core": "^0.6.0",
45
45
  "@nxgt/telemetry": "^0.2.1",
46
46
  "@types/bun": "^1.4.2",
47
47
  "zod": "^4.6.5"
48
48
  },
49
49
  "peerDependencies": {
50
- "@alxia/core": "^0.4.0",
50
+ "@alxia/core": "^0.6.0",
51
51
  "@nxgt/telemetry": "^0.2.1",
52
52
  "typescript": "^6.0.3 || ^7.0.0"
53
53
  }