@zerolith/otel 0.1.0-alpha.5 → 0.1.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@ const api = createClient<Api>(apiRoutes, { transport: tracedTransport(undefined,
12
12
  tracedHydrate(App(router), "#app", tracer);
13
13
  ```
14
14
 
15
- - `navigationObserver` opens a span when a navigation starts and ends it on commit or failure, with the route name, the path, and the mode as attributes.
15
+ - `navigationObserver` opens a span when a navigation starts and ends it on commit, failure, or cancel, with the route name, the path, and the mode as attributes, and `zerolith.navigation.canceled` on a canceled one.
16
16
  - `tracedHydrate` wraps `hydrate` in a span and records the number of mismatches.
17
17
  - `tracedTransport` wraps a client transport in a client span per request, with the method, URL, and status, and injects a `traceparent` header from the active span so the server's trace continues the browser's.
18
18
 
package/dist/index.d.ts CHANGED
@@ -11,9 +11,13 @@ import type { Transport } from "@zerolith/client";
11
11
  import type { NavigationEvent } from "@zerolith/router";
12
12
  /**
13
13
  * The `observe` function for `createRouter`: a span named `navigate`
14
- * opens when a navigation starts and ends when it commits or fails, with
15
- * `zerolith.route`, `url.path`, and `zerolith.navigation.mode` as
16
- * attributes and the loader's error recorded on failure.
14
+ * opens when a navigation starts and ends when it commits, fails, or is
15
+ * canceled by a newer navigation, with `zerolith.route`, `url.path`,
16
+ * `url.query` when the URL has a query, and `zerolith.navigation.mode` as
17
+ * attributes, the loader's error recorded on failure, and
18
+ * `zerolith.navigation.canceled` set on a canceled one, whose status stays
19
+ * unset. The values of the query keys the URL conventions redact, such as
20
+ * `sig`, are recorded as `REDACTED`.
17
21
  */
18
22
  export declare function navigationObserver(tracer?: Tracer): (event: NavigationEvent) => void;
19
23
  /** `hydrate` inside a span named `hydrate`, with the number of mismatches as `zerolith.hydration.mismatches`. */
@@ -22,9 +26,14 @@ export declare function tracedHydrate(view: unknown, container: MountTarget, tra
22
26
  export declare function traceparent(active?: Context): string | undefined;
23
27
  /**
24
28
  * Wraps a transport, or `fetch` when none is given, so every request runs
25
- * in a client span named `METHOD /path` with `http.request.method`,
26
- * `url.full`, and `http.response.status_code`, carries a `traceparent`
27
- * header from that span, and records a failed request as an error.
29
+ * in a client span named by its method, as `GET`, with
30
+ * `http.request.method`, `url.full`, and `http.response.status_code`,
31
+ * carries a `traceparent` header from that span, and records a failed
32
+ * request as an error. The span is not named by the request's path, which
33
+ * would give every note its own span name; the HTTP conventions name a
34
+ * client span by its method when no route template is known. `url.full`
35
+ * records the values of the query keys the conventions redact, such as
36
+ * `sig`, as `REDACTED`.
28
37
  */
29
38
  export declare function tracedTransport(transport: Transport | undefined, tracer?: Tracer): Transport;
30
39
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,KAAK,OAAO,EAAuC,KAAK,MAAM,EAAkB,MAAM,oBAAoB,CAAC;AACpH,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAW,MAAM,UAAU,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAQxD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,MAAwB,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAiCrG;AAED,iHAAiH;AACjH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,GAAE,MAAwB,GAAG,aAAa,CAsBpH;AAED,6FAA6F;AAC7F,wBAAgB,WAAW,CAAC,MAAM,GAAE,OAA0B,GAAG,MAAM,GAAG,SAAS,CAQlF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,GAAE,MAAwB,GAAG,SAAS,CA6B7G"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,KAAK,OAAO,EAAuC,KAAK,MAAM,EAAkB,MAAM,oBAAoB,CAAC;AACpH,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAW,MAAM,UAAU,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAqBxD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,MAAwB,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAsCrG;AAED,iHAAiH;AACjH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,GAAE,MAAwB,GAAG,aAAa,CAsBpH;AAED,6FAA6F;AAC7F,wBAAgB,WAAW,CAAC,MAAM,GAAE,OAA0B,GAAG,MAAM,GAAG,SAAS,CAQlF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,GAAE,MAAwB,GAAG,SAAS,CA8B7G"}
package/dist/index.js CHANGED
@@ -11,19 +11,33 @@ import { hydrate } from "zerolith";
11
11
  function defaultTracer() {
12
12
  return trace.getTracer("@zerolith/otel");
13
13
  }
14
+ /** The query keys whose values the URL conventions redact by default, such as a signed URL's signature. */
15
+ const SENSITIVE_QUERY = new Set(["X-Amz-Signature", "X-Amz-Credential", "X-Amz-Security-Token", "sig", "X-Goog-Signature"]);
16
+ /** A URL's query component without its `?`, with the value of each key the conventions redact replaced by `REDACTED`. */
17
+ function redactedQuery(search) {
18
+ return search.replace(/^\?/, "").split("&").map((pair) => {
19
+ const key = pair.split("=", 1)[0] ?? "";
20
+ return SENSITIVE_QUERY.has(key) ? `${key}=REDACTED` : pair;
21
+ }).join("&");
22
+ }
14
23
  /**
15
24
  * The `observe` function for `createRouter`: a span named `navigate`
16
- * opens when a navigation starts and ends when it commits or fails, with
17
- * `zerolith.route`, `url.path`, and `zerolith.navigation.mode` as
18
- * attributes and the loader's error recorded on failure.
25
+ * opens when a navigation starts and ends when it commits, fails, or is
26
+ * canceled by a newer navigation, with `zerolith.route`, `url.path`,
27
+ * `url.query` when the URL has a query, and `zerolith.navigation.mode` as
28
+ * attributes, the loader's error recorded on failure, and
29
+ * `zerolith.navigation.canceled` set on a canceled one, whose status stays
30
+ * unset. The values of the query keys the URL conventions redact, such as
31
+ * `sig`, are recorded as `REDACTED`.
19
32
  */
20
33
  export function navigationObserver(tracer = defaultTracer()) {
21
34
  const open = new Map();
22
35
  return (event) => {
23
36
  if (event.type === "start") {
37
+ const query = redactedQuery(event.url.search);
24
38
  const span = tracer.startSpan("navigate", {
25
39
  kind: SpanKind.INTERNAL,
26
- attributes: { "url.path": event.url.pathname, "url.query": event.url.search, "zerolith.navigation.mode": event.mode }
40
+ attributes: { "url.path": event.url.pathname, ...(query === "" ? {} : { "url.query": query }), "zerolith.navigation.mode": event.mode }
27
41
  });
28
42
  open.set(event.id, span);
29
43
  return;
@@ -37,6 +51,9 @@ export function navigationObserver(tracer = defaultTracer()) {
37
51
  span.setAttribute("zerolith.route", event.name ?? "");
38
52
  span.setStatus({ code: SpanStatusCode.OK });
39
53
  }
54
+ else if (event.type === "cancel") {
55
+ span.setAttribute("zerolith.navigation.canceled", true);
56
+ }
40
57
  else {
41
58
  const error = event.error;
42
59
  span.recordException(error instanceof Error ? error : new Error(String(error)));
@@ -74,15 +91,21 @@ export function traceparent(active = context.active()) {
74
91
  }
75
92
  /**
76
93
  * Wraps a transport, or `fetch` when none is given, so every request runs
77
- * in a client span named `METHOD /path` with `http.request.method`,
78
- * `url.full`, and `http.response.status_code`, carries a `traceparent`
79
- * header from that span, and records a failed request as an error.
94
+ * in a client span named by its method, as `GET`, with
95
+ * `http.request.method`, `url.full`, and `http.response.status_code`,
96
+ * carries a `traceparent` header from that span, and records a failed
97
+ * request as an error. The span is not named by the request's path, which
98
+ * would give every note its own span name; the HTTP conventions name a
99
+ * client span by its method when no route template is known. `url.full`
100
+ * records the values of the query keys the conventions redact, such as
101
+ * `sig`, as `REDACTED`.
80
102
  */
81
103
  export function tracedTransport(transport, tracer = defaultTracer()) {
82
104
  const send = transport ?? ((request) => fetch(request));
83
105
  return (request) => {
84
- const url = new URL(request.url);
85
- return tracer.startActiveSpan(`${request.method} ${url.pathname}`, { kind: SpanKind.CLIENT, attributes: { "http.request.method": request.method, "url.full": request.url } }, async (span) => {
106
+ const full = new URL(request.url);
107
+ full.search = redactedQuery(full.search);
108
+ return tracer.startActiveSpan(request.method, { kind: SpanKind.CLIENT, attributes: { "http.request.method": request.method, "url.full": full.href } }, async (span) => {
86
109
  try {
87
110
  const header = traceparent();
88
111
  const traced = header === undefined ? request : new Request(request, { headers: withHeader(request.headers, "traceparent", header) });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerolith/otel",
3
- "version": "0.1.0-alpha.5",
3
+ "version": "0.1.0-alpha.6",
4
4
  "description": "OpenTelemetry spans for zerolith applications through @opentelemetry/api: a span per navigation, per hydration, and per request of the typed client with traceparent propagation",
5
5
  "license": "Apache-2.0",
6
6
  "author": "molexxxx",
@@ -41,17 +41,17 @@
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@opentelemetry/api": ">=1.9.0 <2.0.0",
44
- "@zerolith/client": "^0.1.0-alpha.5",
45
- "@zerolith/router": "^0.1.0-alpha.5",
46
- "zerolith": "^0.1.0-alpha.5"
44
+ "@zerolith/client": "^0.1.0-alpha.6",
45
+ "@zerolith/router": "^0.1.0-alpha.6",
46
+ "zerolith": "^0.1.0-alpha.6"
47
47
  },
48
48
  "peerDependenciesMeta": {
49
49
  "@zerolith/client": { "optional": true },
50
50
  "@zerolith/router": { "optional": true }
51
51
  },
52
52
  "devDependencies": {
53
- "@zerolith/client": "^0.1.0-alpha.5",
54
- "@zerolith/router": "^0.1.0-alpha.5",
55
- "zerolith": "^0.1.0-alpha.5"
53
+ "@zerolith/client": "^0.1.0-alpha.6",
54
+ "@zerolith/router": "^0.1.0-alpha.6",
55
+ "zerolith": "^0.1.0-alpha.6"
56
56
  }
57
57
  }
package/src/index.ts CHANGED
@@ -16,11 +16,28 @@ function defaultTracer(): Tracer
16
16
  return trace.getTracer("@zerolith/otel");
17
17
  }
18
18
 
19
+ /** The query keys whose values the URL conventions redact by default, such as a signed URL's signature. */
20
+ const SENSITIVE_QUERY = new Set(["X-Amz-Signature", "X-Amz-Credential", "X-Amz-Security-Token", "sig", "X-Goog-Signature"]);
21
+
22
+ /** A URL's query component without its `?`, with the value of each key the conventions redact replaced by `REDACTED`. */
23
+ function redactedQuery(search: string): string
24
+ {
25
+ return search.replace(/^\?/, "").split("&").map((pair) =>
26
+ {
27
+ const key = pair.split("=", 1)[0] ?? "";
28
+ return SENSITIVE_QUERY.has(key) ? `${key}=REDACTED` : pair;
29
+ }).join("&");
30
+ }
31
+
19
32
  /**
20
33
  * The `observe` function for `createRouter`: a span named `navigate`
21
- * opens when a navigation starts and ends when it commits or fails, with
22
- * `zerolith.route`, `url.path`, and `zerolith.navigation.mode` as
23
- * attributes and the loader's error recorded on failure.
34
+ * opens when a navigation starts and ends when it commits, fails, or is
35
+ * canceled by a newer navigation, with `zerolith.route`, `url.path`,
36
+ * `url.query` when the URL has a query, and `zerolith.navigation.mode` as
37
+ * attributes, the loader's error recorded on failure, and
38
+ * `zerolith.navigation.canceled` set on a canceled one, whose status stays
39
+ * unset. The values of the query keys the URL conventions redact, such as
40
+ * `sig`, are recorded as `REDACTED`.
24
41
  */
25
42
  export function navigationObserver(tracer: Tracer = defaultTracer()): (event: NavigationEvent) => void
26
43
  {
@@ -29,9 +46,10 @@ export function navigationObserver(tracer: Tracer = defaultTracer()): (event: Na
29
46
  {
30
47
  if (event.type === "start")
31
48
  {
49
+ const query = redactedQuery(event.url.search);
32
50
  const span = tracer.startSpan("navigate", {
33
51
  kind: SpanKind.INTERNAL,
34
- attributes: { "url.path": event.url.pathname, "url.query": event.url.search, "zerolith.navigation.mode": event.mode }
52
+ attributes: { "url.path": event.url.pathname, ...(query === "" ? {} : { "url.query": query }), "zerolith.navigation.mode": event.mode }
35
53
  });
36
54
  open.set(event.id, span);
37
55
  return;
@@ -47,6 +65,10 @@ export function navigationObserver(tracer: Tracer = defaultTracer()): (event: Na
47
65
  span.setAttribute("zerolith.route", event.name ?? "");
48
66
  span.setStatus({ code: SpanStatusCode.OK });
49
67
  }
68
+ else if (event.type === "cancel")
69
+ {
70
+ span.setAttribute("zerolith.navigation.canceled", true);
71
+ }
50
72
  else
51
73
  {
52
74
  const error = event.error;
@@ -95,17 +117,23 @@ export function traceparent(active: Context = context.active()): string | undefi
95
117
 
96
118
  /**
97
119
  * Wraps a transport, or `fetch` when none is given, so every request runs
98
- * in a client span named `METHOD /path` with `http.request.method`,
99
- * `url.full`, and `http.response.status_code`, carries a `traceparent`
100
- * header from that span, and records a failed request as an error.
120
+ * in a client span named by its method, as `GET`, with
121
+ * `http.request.method`, `url.full`, and `http.response.status_code`,
122
+ * carries a `traceparent` header from that span, and records a failed
123
+ * request as an error. The span is not named by the request's path, which
124
+ * would give every note its own span name; the HTTP conventions name a
125
+ * client span by its method when no route template is known. `url.full`
126
+ * records the values of the query keys the conventions redact, such as
127
+ * `sig`, as `REDACTED`.
101
128
  */
102
129
  export function tracedTransport(transport: Transport | undefined, tracer: Tracer = defaultTracer()): Transport
103
130
  {
104
131
  const send: Transport = transport ?? ((request) => fetch(request));
105
132
  return (request) =>
106
133
  {
107
- const url = new URL(request.url);
108
- return tracer.startActiveSpan(`${request.method} ${url.pathname}`, { kind: SpanKind.CLIENT, attributes: { "http.request.method": request.method, "url.full": request.url } }, async (span) =>
134
+ const full = new URL(request.url);
135
+ full.search = redactedQuery(full.search);
136
+ return tracer.startActiveSpan(request.method, { kind: SpanKind.CLIENT, attributes: { "http.request.method": request.method, "url.full": full.href } }, async (span) =>
109
137
  {
110
138
  try
111
139
  {