@opentelemetry/browser-instrumentation 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +115 -0
  2. package/dist/console/index.d.ts +3 -0
  3. package/dist/console/index.js +2 -0
  4. package/dist/console/instrumentation.d.ts +21 -0
  5. package/dist/console/instrumentation.js +84 -0
  6. package/dist/console/instrumentation.js.map +1 -0
  7. package/dist/console/semconv.js +14 -0
  8. package/dist/console/semconv.js.map +1 -0
  9. package/dist/console/types.d.ts +25 -0
  10. package/dist/navigation/index.d.ts +4 -0
  11. package/dist/navigation/index.js +3 -0
  12. package/dist/navigation/instrumentation.d.ts +29 -0
  13. package/dist/navigation/instrumentation.js +156 -0
  14. package/dist/navigation/instrumentation.js.map +1 -0
  15. package/dist/navigation/semconv.js +22 -0
  16. package/dist/navigation/semconv.js.map +1 -0
  17. package/dist/navigation/types.d.ts +21 -0
  18. package/dist/navigation/utils.d.ts +12 -0
  19. package/dist/navigation/utils.js +77 -0
  20. package/dist/navigation/utils.js.map +1 -0
  21. package/dist/navigation-timing/semconv.js +24 -24
  22. package/dist/navigation-timing/semconv.js.map +1 -1
  23. package/dist/package.js +1 -1
  24. package/dist/resource-timing/idle-callback-shim.js +35 -0
  25. package/dist/resource-timing/idle-callback-shim.js.map +1 -0
  26. package/dist/resource-timing/index.d.ts +3 -0
  27. package/dist/resource-timing/index.js +2 -0
  28. package/dist/resource-timing/instrumentation.d.ts +33 -0
  29. package/dist/resource-timing/instrumentation.js +160 -0
  30. package/dist/resource-timing/instrumentation.js.map +1 -0
  31. package/dist/resource-timing/semconv.js +131 -0
  32. package/dist/resource-timing/semconv.js.map +1 -0
  33. package/dist/resource-timing/types.d.ts +38 -0
  34. package/dist/utils/getElementCSSSelector.js.map +1 -1
  35. package/package.json +14 -8
@@ -0,0 +1,77 @@
1
+ //#region src/navigation/utils.ts
2
+ const SENSITIVE_PARAMS = [
3
+ "password",
4
+ "passwd",
5
+ "secret",
6
+ "api_key",
7
+ "apikey",
8
+ "auth",
9
+ "authorization",
10
+ "token",
11
+ "access_token",
12
+ "refresh_token",
13
+ "jwt",
14
+ "session",
15
+ "sessionid",
16
+ "key",
17
+ "private_key",
18
+ "client_secret",
19
+ "client_id",
20
+ "signature",
21
+ "hash"
22
+ ];
23
+ /**
24
+ * Default URL sanitization function that redacts credentials and sensitive query parameters.
25
+ * This is the default implementation used when no custom sanitizeUrl callback is provided.
26
+ *
27
+ * @param url - The URL to sanitize
28
+ * @returns The sanitized URL with credentials and sensitive parameters redacted
29
+ */
30
+ function defaultSanitizeUrl(url) {
31
+ try {
32
+ const urlObj = new URL(url);
33
+ if (urlObj.username || urlObj.password) {
34
+ urlObj.username = "REDACTED";
35
+ urlObj.password = "REDACTED";
36
+ }
37
+ for (const param of SENSITIVE_PARAMS) if (urlObj.searchParams.has(param)) urlObj.searchParams.set(param, "REDACTED");
38
+ return urlObj.toString();
39
+ } catch {
40
+ let sanitized = url.replace(/\/\/[^:/@]+:[^/@]+@/, "//REDACTED:REDACTED@");
41
+ for (const param of SENSITIVE_PARAMS) {
42
+ const regex = new RegExp(`([?&]${param}(?:%3D|=))[^&]*`, "gi");
43
+ sanitized = sanitized.replace(regex, "$1REDACTED");
44
+ }
45
+ return sanitized;
46
+ }
47
+ }
48
+ /**
49
+ * Determines if navigation between two URLs represents a hash change.
50
+ * A hash change is true if the URLs are the same except for the hash part,
51
+ * AND the hash is being added or changed (not removed).
52
+ *
53
+ * @param fromUrl - The source URL
54
+ * @param toUrl - The destination URL
55
+ * @returns true if this represents a hash change navigation
56
+ */
57
+ function isHashChange(fromUrl, toUrl) {
58
+ try {
59
+ const a = new URL(fromUrl, window.location.origin);
60
+ const b = new URL(toUrl, window.location.origin);
61
+ const sameBase = a.origin === b.origin && a.pathname === b.pathname && a.search === b.search;
62
+ const fromHasHash = a.hash !== "";
63
+ const toHasHash = b.hash !== "";
64
+ const hashesAreDifferent = a.hash !== b.hash;
65
+ return sameBase && hashesAreDifferent && (fromHasHash && toHasHash || !fromHasHash && toHasHash);
66
+ } catch {
67
+ const fromBase = fromUrl.split("#")[0];
68
+ const toBase = toUrl.split("#")[0];
69
+ const fromHash = fromUrl.split("#")[1] ?? "";
70
+ const toHash = toUrl.split("#")[1] ?? "";
71
+ return fromBase === toBase && fromHash !== toHash && toHash !== "";
72
+ }
73
+ }
74
+ //#endregion
75
+ export { defaultSanitizeUrl, isHashChange };
76
+
77
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../src/navigation/utils.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nconst SENSITIVE_PARAMS = [\n 'password',\n 'passwd',\n 'secret',\n 'api_key',\n 'apikey',\n 'auth',\n 'authorization',\n 'token',\n 'access_token',\n 'refresh_token',\n 'jwt',\n 'session',\n 'sessionid',\n 'key',\n 'private_key',\n 'client_secret',\n 'client_id',\n 'signature',\n 'hash',\n];\n\n/**\n * Default URL sanitization function that redacts credentials and sensitive query parameters.\n * This is the default implementation used when no custom sanitizeUrl callback is provided.\n *\n * @param url - The URL to sanitize\n * @returns The sanitized URL with credentials and sensitive parameters redacted\n */\nexport function defaultSanitizeUrl(url: string): string {\n try {\n const urlObj = new URL(url);\n\n if (urlObj.username || urlObj.password) {\n urlObj.username = 'REDACTED';\n urlObj.password = 'REDACTED';\n }\n\n for (const param of SENSITIVE_PARAMS) {\n if (urlObj.searchParams.has(param)) {\n urlObj.searchParams.set(param, 'REDACTED');\n }\n }\n\n return urlObj.toString();\n } catch {\n // If URL parsing fails, redact credentials and sensitive query parameters\n // using regexes. The credential regex uses a restricted character class to\n // avoid polynomial time complexity.\n let sanitized = url.replace(/\\/\\/[^:/@]+:[^/@]+@/, '//REDACTED:REDACTED@');\n\n for (const param of SENSITIVE_PARAMS) {\n // Match param=value or param%3Dvalue (URL encoded)\n const regex = new RegExp(`([?&]${param}(?:%3D|=))[^&]*`, 'gi');\n sanitized = sanitized.replace(regex, '$1REDACTED');\n }\n\n return sanitized;\n }\n}\n\n/**\n * Determines if navigation between two URLs represents a hash change.\n * A hash change is true if the URLs are the same except for the hash part,\n * AND the hash is being added or changed (not removed).\n *\n * @param fromUrl - The source URL\n * @param toUrl - The destination URL\n * @returns true if this represents a hash change navigation\n */\nexport function isHashChange(fromUrl: string, toUrl: string): boolean {\n try {\n const a = new URL(fromUrl, window.location.origin);\n const b = new URL(toUrl, window.location.origin);\n const sameBase =\n a.origin === b.origin &&\n a.pathname === b.pathname &&\n a.search === b.search;\n const fromHasHash = a.hash !== '';\n const toHasHash = b.hash !== '';\n const hashesAreDifferent = a.hash !== b.hash;\n\n return (\n sameBase &&\n hashesAreDifferent &&\n ((fromHasHash && toHasHash) || (!fromHasHash && toHasHash))\n );\n } catch {\n const fromBase = fromUrl.split('#')[0];\n const toBase = toUrl.split('#')[0];\n const fromHash = fromUrl.split('#')[1] ?? '';\n const toHash = toUrl.split('#')[1] ?? '';\n\n const sameBase = fromBase === toBase;\n const hashesAreDifferent = fromHash !== toHash;\n const notRemovingHash = toHash !== '';\n\n return sameBase && hashesAreDifferent && notRemovingHash;\n }\n}\n"],"mappings":";AAKA,MAAM,mBAAmB;CACvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;AASD,SAAgB,mBAAmB,KAAqB;AACtD,KAAI;EACF,MAAM,SAAS,IAAI,IAAI,IAAI;AAE3B,MAAI,OAAO,YAAY,OAAO,UAAU;AACtC,UAAO,WAAW;AAClB,UAAO,WAAW;;AAGpB,OAAK,MAAM,SAAS,iBAClB,KAAI,OAAO,aAAa,IAAI,MAAM,CAChC,QAAO,aAAa,IAAI,OAAO,WAAW;AAI9C,SAAO,OAAO,UAAU;SAClB;EAIN,IAAI,YAAY,IAAI,QAAQ,uBAAuB,uBAAuB;AAE1E,OAAK,MAAM,SAAS,kBAAkB;GAEpC,MAAM,QAAQ,IAAI,OAAO,QAAQ,MAAM,kBAAkB,KAAK;AAC9D,eAAY,UAAU,QAAQ,OAAO,aAAa;;AAGpD,SAAO;;;;;;;;;;;;AAaX,SAAgB,aAAa,SAAiB,OAAwB;AACpE,KAAI;EACF,MAAM,IAAI,IAAI,IAAI,SAAS,OAAO,SAAS,OAAO;EAClD,MAAM,IAAI,IAAI,IAAI,OAAO,OAAO,SAAS,OAAO;EAChD,MAAM,WACJ,EAAE,WAAW,EAAE,UACf,EAAE,aAAa,EAAE,YACjB,EAAE,WAAW,EAAE;EACjB,MAAM,cAAc,EAAE,SAAS;EAC/B,MAAM,YAAY,EAAE,SAAS;EAC7B,MAAM,qBAAqB,EAAE,SAAS,EAAE;AAExC,SACE,YACA,uBACE,eAAe,aAAe,CAAC,eAAe;SAE5C;EACN,MAAM,WAAW,QAAQ,MAAM,IAAI,CAAC;EACpC,MAAM,SAAS,MAAM,MAAM,IAAI,CAAC;EAChC,MAAM,WAAW,QAAQ,MAAM,IAAI,CAAC,MAAM;EAC1C,MAAM,SAAS,MAAM,MAAM,IAAI,CAAC,MAAM;AAMtC,SAJiB,aAAa,UACH,aAAa,UAChB,WAAW"}
@@ -3,99 +3,99 @@ const NAVIGATION_TIMING_EVENT_NAME = "browser.navigation_timing";
3
3
  /**
4
4
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
5
5
  */
6
- const ATTR_NAVIGATION_TYPE = "navigation.type";
6
+ const ATTR_NAVIGATION_TYPE = "browser.navigation_timing.type";
7
7
  /**
8
8
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
9
9
  */
10
- const ATTR_NAVIGATION_URL = "navigation.url";
10
+ const ATTR_NAVIGATION_URL = "url.full";
11
11
  /**
12
12
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
13
13
  */
14
- const ATTR_NAVIGATION_DURATION = "navigation.duration";
14
+ const ATTR_NAVIGATION_DOM_COMPLETE = "browser.navigation_timing.dom_complete";
15
15
  /**
16
16
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
17
17
  */
18
- const ATTR_NAVIGATION_DOM_COMPLETE = "navigation.dom_complete";
18
+ const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END = "browser.navigation_timing.dom_content_loaded_event_end";
19
19
  /**
20
20
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
21
21
  */
22
- const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END = "navigation.dom_content_loaded_event_end";
22
+ const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START = "browser.navigation_timing.dom_content_loaded_event_start";
23
23
  /**
24
24
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
25
25
  */
26
- const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START = "navigation.dom_content_loaded_event_start";
26
+ const ATTR_NAVIGATION_DOM_INTERACTIVE = "browser.navigation_timing.dom_interactive";
27
27
  /**
28
28
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
29
29
  */
30
- const ATTR_NAVIGATION_DOM_INTERACTIVE = "navigation.dom_interactive";
30
+ const ATTR_NAVIGATION_LOAD_EVENT_END = "browser.navigation_timing.load_event_end";
31
31
  /**
32
32
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
33
33
  */
34
- const ATTR_NAVIGATION_LOAD_EVENT_END = "navigation.load_event_end";
34
+ const ATTR_NAVIGATION_LOAD_EVENT_START = "browser.navigation_timing.load_event_start";
35
35
  /**
36
36
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
37
37
  */
38
- const ATTR_NAVIGATION_LOAD_EVENT_START = "navigation.load_event_start";
38
+ const ATTR_NAVIGATION_REDIRECT_COUNT = "browser.navigation_timing.redirect_count";
39
39
  /**
40
40
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
41
41
  */
42
- const ATTR_NAVIGATION_REDIRECT_COUNT = "navigation.redirect_count";
42
+ const ATTR_NAVIGATION_UNLOAD_EVENT_END = "browser.navigation_timing.unload_event_end";
43
43
  /**
44
44
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
45
45
  */
46
- const ATTR_NAVIGATION_UNLOAD_EVENT_END = "navigation.unload_event_end";
46
+ const ATTR_NAVIGATION_UNLOAD_EVENT_START = "browser.navigation_timing.unload_event_start";
47
47
  /**
48
48
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
49
49
  */
50
- const ATTR_NAVIGATION_UNLOAD_EVENT_START = "navigation.unload_event_start";
50
+ const ATTR_NAVIGATION_DURATION = "browser.resource_timing.duration";
51
51
  /**
52
52
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
53
53
  */
54
- const ATTR_NAVIGATION_FETCH_START = "navigation.fetch_start";
54
+ const ATTR_NAVIGATION_FETCH_START = "browser.resource_timing.fetch_start";
55
55
  /**
56
56
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
57
57
  */
58
- const ATTR_NAVIGATION_DOMAIN_LOOKUP_START = "navigation.domain_lookup_start";
58
+ const ATTR_NAVIGATION_DOMAIN_LOOKUP_START = "browser.resource_timing.domain_lookup_start";
59
59
  /**
60
60
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
61
61
  */
62
- const ATTR_NAVIGATION_DOMAIN_LOOKUP_END = "navigation.domain_lookup_end";
62
+ const ATTR_NAVIGATION_DOMAIN_LOOKUP_END = "browser.resource_timing.domain_lookup_end";
63
63
  /**
64
64
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
65
65
  */
66
- const ATTR_NAVIGATION_CONNECT_START = "navigation.connect_start";
66
+ const ATTR_NAVIGATION_CONNECT_START = "browser.resource_timing.connect_start";
67
67
  /**
68
68
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
69
69
  */
70
- const ATTR_NAVIGATION_CONNECT_END = "navigation.connect_end";
70
+ const ATTR_NAVIGATION_CONNECT_END = "browser.resource_timing.connect_end";
71
71
  /**
72
72
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
73
73
  */
74
- const ATTR_NAVIGATION_SECURE_CONNECTION_START = "navigation.secure_connection_start";
74
+ const ATTR_NAVIGATION_SECURE_CONNECTION_START = "browser.resource_timing.secure_connection_start";
75
75
  /**
76
76
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
77
77
  */
78
- const ATTR_NAVIGATION_REQUEST_START = "navigation.request_start";
78
+ const ATTR_NAVIGATION_REQUEST_START = "browser.resource_timing.request_start";
79
79
  /**
80
80
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
81
81
  */
82
- const ATTR_NAVIGATION_RESPONSE_START = "navigation.response_start";
82
+ const ATTR_NAVIGATION_RESPONSE_START = "browser.resource_timing.response_start";
83
83
  /**
84
84
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
85
85
  */
86
- const ATTR_NAVIGATION_RESPONSE_END = "navigation.response_end";
86
+ const ATTR_NAVIGATION_RESPONSE_END = "browser.resource_timing.response_end";
87
87
  /**
88
88
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
89
89
  */
90
- const ATTR_NAVIGATION_TRANSFER_SIZE = "navigation.transfer_size";
90
+ const ATTR_NAVIGATION_TRANSFER_SIZE = "browser.resource_timing.transfer_size";
91
91
  /**
92
92
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
93
93
  */
94
- const ATTR_NAVIGATION_ENCODED_BODY_SIZE = "navigation.encoded_body_size";
94
+ const ATTR_NAVIGATION_ENCODED_BODY_SIZE = "browser.resource_timing.encoded_body_size";
95
95
  /**
96
96
  * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.
97
97
  */
98
- const ATTR_NAVIGATION_DECODED_BODY_SIZE = "navigation.decoded_body_size";
98
+ const ATTR_NAVIGATION_DECODED_BODY_SIZE = "browser.resource_timing.decoded_body_size";
99
99
  //#endregion
100
100
  export { ATTR_NAVIGATION_CONNECT_END, ATTR_NAVIGATION_CONNECT_START, ATTR_NAVIGATION_DECODED_BODY_SIZE, ATTR_NAVIGATION_DOMAIN_LOOKUP_END, ATTR_NAVIGATION_DOMAIN_LOOKUP_START, ATTR_NAVIGATION_DOM_COMPLETE, ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END, ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START, ATTR_NAVIGATION_DOM_INTERACTIVE, ATTR_NAVIGATION_DURATION, ATTR_NAVIGATION_ENCODED_BODY_SIZE, ATTR_NAVIGATION_FETCH_START, ATTR_NAVIGATION_LOAD_EVENT_END, ATTR_NAVIGATION_LOAD_EVENT_START, ATTR_NAVIGATION_REDIRECT_COUNT, ATTR_NAVIGATION_REQUEST_START, ATTR_NAVIGATION_RESPONSE_END, ATTR_NAVIGATION_RESPONSE_START, ATTR_NAVIGATION_SECURE_CONNECTION_START, ATTR_NAVIGATION_TRANSFER_SIZE, ATTR_NAVIGATION_TYPE, ATTR_NAVIGATION_UNLOAD_EVENT_END, ATTR_NAVIGATION_UNLOAD_EVENT_START, ATTR_NAVIGATION_URL, NAVIGATION_TIMING_EVENT_NAME };
101
101
 
@@ -1 +1 @@
1
- {"version":3,"file":"semconv.js","names":[],"sources":["../../src/navigation-timing/semconv.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * This file contains a copy of unstable semantic convention definitions\n * used by this package.\n * @see https://github.com/open-telemetry/opentelemetry-js/tree/main/semantic-conventions#unstable-semconv\n */\n\nexport const NAVIGATION_TIMING_EVENT_NAME = 'browser.navigation_timing';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_TYPE = 'navigation.type';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_URL = 'navigation.url';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DURATION = 'navigation.duration';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_COMPLETE = 'navigation.dom_complete';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END =\n 'navigation.dom_content_loaded_event_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START =\n 'navigation.dom_content_loaded_event_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_INTERACTIVE = 'navigation.dom_interactive';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_LOAD_EVENT_END = 'navigation.load_event_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_LOAD_EVENT_START = 'navigation.load_event_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_REDIRECT_COUNT = 'navigation.redirect_count';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_UNLOAD_EVENT_END = 'navigation.unload_event_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_UNLOAD_EVENT_START =\n 'navigation.unload_event_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_FETCH_START = 'navigation.fetch_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOMAIN_LOOKUP_START =\n 'navigation.domain_lookup_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOMAIN_LOOKUP_END = 'navigation.domain_lookup_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_CONNECT_START = 'navigation.connect_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_CONNECT_END = 'navigation.connect_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_SECURE_CONNECTION_START =\n 'navigation.secure_connection_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_REQUEST_START = 'navigation.request_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_RESPONSE_START = 'navigation.response_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_RESPONSE_END = 'navigation.response_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_TRANSFER_SIZE = 'navigation.transfer_size';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_ENCODED_BODY_SIZE = 'navigation.encoded_body_size';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DECODED_BODY_SIZE = 'navigation.decoded_body_size';\n"],"mappings":";AAWA,MAAa,+BAA+B;;;;AAK5C,MAAa,uBAAuB;;;;AAKpC,MAAa,sBAAsB;;;;AAKnC,MAAa,2BAA2B;;;;AAKxC,MAAa,+BAA+B;;;;AAK5C,MAAa,+CACX;;;;AAKF,MAAa,iDACX;;;;AAKF,MAAa,kCAAkC;;;;AAK/C,MAAa,iCAAiC;;;;AAK9C,MAAa,mCAAmC;;;;AAKhD,MAAa,iCAAiC;;;;AAK9C,MAAa,mCAAmC;;;;AAKhD,MAAa,qCACX;;;;AAKF,MAAa,8BAA8B;;;;AAK3C,MAAa,sCACX;;;;AAKF,MAAa,oCAAoC;;;;AAKjD,MAAa,gCAAgC;;;;AAK7C,MAAa,8BAA8B;;;;AAK3C,MAAa,0CACX;;;;AAKF,MAAa,gCAAgC;;;;AAK7C,MAAa,iCAAiC;;;;AAK9C,MAAa,+BAA+B;;;;AAK5C,MAAa,gCAAgC;;;;AAK7C,MAAa,oCAAoC;;;;AAKjD,MAAa,oCAAoC"}
1
+ {"version":3,"file":"semconv.js","names":[],"sources":["../../src/navigation-timing/semconv.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * This file contains a copy of unstable semantic convention definitions\n * used by this package.\n * @see https://github.com/open-telemetry/opentelemetry-js/tree/main/semantic-conventions#unstable-semconv\n */\n\nexport const NAVIGATION_TIMING_EVENT_NAME = 'browser.navigation_timing';\n\n// Navigation-specific attributes (from PerformanceNavigationTiming).\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_TYPE = 'browser.navigation_timing.type';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_URL = 'url.full';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_COMPLETE =\n 'browser.navigation_timing.dom_complete';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_END =\n 'browser.navigation_timing.dom_content_loaded_event_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_CONTENT_LOADED_EVENT_START =\n 'browser.navigation_timing.dom_content_loaded_event_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOM_INTERACTIVE =\n 'browser.navigation_timing.dom_interactive';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_LOAD_EVENT_END =\n 'browser.navigation_timing.load_event_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_LOAD_EVENT_START =\n 'browser.navigation_timing.load_event_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_REDIRECT_COUNT =\n 'browser.navigation_timing.redirect_count';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_UNLOAD_EVENT_END =\n 'browser.navigation_timing.unload_event_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_UNLOAD_EVENT_START =\n 'browser.navigation_timing.unload_event_start';\n\n// Shared timing attributes inherited from PerformanceResourceTiming.\n// PerformanceNavigationTiming extends PerformanceResourceTiming, so these\n// values mirror resource timing. TODO: extract to a shared module to avoid\n// duplication.\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DURATION = 'browser.resource_timing.duration';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_FETCH_START =\n 'browser.resource_timing.fetch_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOMAIN_LOOKUP_START =\n 'browser.resource_timing.domain_lookup_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DOMAIN_LOOKUP_END =\n 'browser.resource_timing.domain_lookup_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_CONNECT_START =\n 'browser.resource_timing.connect_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_CONNECT_END =\n 'browser.resource_timing.connect_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_SECURE_CONNECTION_START =\n 'browser.resource_timing.secure_connection_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_REQUEST_START =\n 'browser.resource_timing.request_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_RESPONSE_START =\n 'browser.resource_timing.response_start';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_RESPONSE_END =\n 'browser.resource_timing.response_end';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_TRANSFER_SIZE =\n 'browser.resource_timing.transfer_size';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_ENCODED_BODY_SIZE =\n 'browser.resource_timing.encoded_body_size';\n\n/**\n * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`.\n */\nexport const ATTR_NAVIGATION_DECODED_BODY_SIZE =\n 'browser.resource_timing.decoded_body_size';\n"],"mappings":";AAWA,MAAa,+BAA+B;;;;AAO5C,MAAa,uBAAuB;;;;AAKpC,MAAa,sBAAsB;;;;AAKnC,MAAa,+BACX;;;;AAKF,MAAa,+CACX;;;;AAKF,MAAa,iDACX;;;;AAKF,MAAa,kCACX;;;;AAKF,MAAa,iCACX;;;;AAKF,MAAa,mCACX;;;;AAKF,MAAa,iCACX;;;;AAKF,MAAa,mCACX;;;;AAKF,MAAa,qCACX;;;;AAUF,MAAa,2BAA2B;;;;AAKxC,MAAa,8BACX;;;;AAKF,MAAa,sCACX;;;;AAKF,MAAa,oCACX;;;;AAKF,MAAa,gCACX;;;;AAKF,MAAa,8BACX;;;;AAKF,MAAa,0CACX;;;;AAKF,MAAa,gCACX;;;;AAKF,MAAa,iCACX;;;;AAKF,MAAa,+BACX;;;;AAKF,MAAa,gCACX;;;;AAKF,MAAa,oCACX;;;;AAKF,MAAa,oCACX"}
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.2.0";
2
+ var version = "0.4.0";
3
3
  //#endregion
4
4
  export { version };
5
5
 
@@ -0,0 +1,35 @@
1
+ //#region src/resource-timing/idle-callback-shim.ts
2
+ const IDLE_DEADLINE_MS = 50;
3
+ const supportsIdleCallback = typeof window.requestIdleCallback === "function";
4
+ /**
5
+ * Schedules a callback during idle time, using the native requestIdleCallback
6
+ * when available. Falls back to setTimeout with a synthetic IdleDeadline that
7
+ * reports ~50ms of available time (per the W3C spec recommendation).
8
+ */
9
+ function requestIdleCallbackShim(callback, options) {
10
+ if (supportsIdleCallback) return {
11
+ id: window.requestIdleCallback(callback, options),
12
+ native: true
13
+ };
14
+ return {
15
+ id: window.setTimeout(() => {
16
+ const start = performance.now();
17
+ callback({
18
+ didTimeout: false,
19
+ timeRemaining: () => Math.max(0, IDLE_DEADLINE_MS - (performance.now() - start))
20
+ });
21
+ }, 1),
22
+ native: false
23
+ };
24
+ }
25
+ /**
26
+ * Cancels a previously scheduled idle callback.
27
+ */
28
+ function cancelIdleCallbackShim(handle) {
29
+ if (handle.native) window.cancelIdleCallback(handle.id);
30
+ else clearTimeout(handle.id);
31
+ }
32
+ //#endregion
33
+ export { cancelIdleCallbackShim, requestIdleCallbackShim };
34
+
35
+ //# sourceMappingURL=idle-callback-shim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idle-callback-shim.js","names":[],"sources":["../../src/resource-timing/idle-callback-shim.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nconst IDLE_DEADLINE_MS = 50;\n\n// requestIdleCallback is not yet Baseline (not supported in Safari).\n// Feature-detect here so we can shim it below for unsupported browsers.\n// eslint-disable-next-line baseline-js/use-baseline\nconst supportsIdleCallback = typeof window.requestIdleCallback === 'function';\n\nexport interface IdleCallbackHandle {\n id: number;\n native: boolean;\n}\n\n/**\n * Schedules a callback during idle time, using the native requestIdleCallback\n * when available. Falls back to setTimeout with a synthetic IdleDeadline that\n * reports ~50ms of available time (per the W3C spec recommendation).\n */\nexport function requestIdleCallbackShim(\n callback: IdleRequestCallback,\n options?: IdleRequestOptions,\n): IdleCallbackHandle {\n if (supportsIdleCallback) {\n // requestIdleCallback is not yet Baseline (not supported in Safari).\n // eslint-disable-next-line baseline-js/use-baseline\n const id = window.requestIdleCallback(callback, options);\n return { id, native: true };\n }\n\n const id = window.setTimeout(() => {\n const start = performance.now();\n callback({\n didTimeout: false,\n timeRemaining: () =>\n Math.max(0, IDLE_DEADLINE_MS - (performance.now() - start)),\n });\n }, 1);\n return { id, native: false };\n}\n\n/**\n * Cancels a previously scheduled idle callback.\n */\nexport function cancelIdleCallbackShim(handle: IdleCallbackHandle): void {\n if (handle.native) {\n // eslint-disable-next-line baseline-js/use-baseline\n window.cancelIdleCallback(handle.id);\n } else {\n clearTimeout(handle.id);\n }\n}\n"],"mappings":";AAKA,MAAM,mBAAmB;AAKzB,MAAM,uBAAuB,OAAO,OAAO,wBAAwB;;;;;;AAYnE,SAAgB,wBACd,UACA,SACoB;AACpB,KAAI,qBAIF,QAAO;EAAE,IADE,OAAO,oBAAoB,UAAU,QACrC;EAAE,QAAQ;EAAM;AAW7B,QAAO;EAAE,IARE,OAAO,iBAAiB;GACjC,MAAM,QAAQ,YAAY,KAAK;AAC/B,YAAS;IACP,YAAY;IACZ,qBACE,KAAK,IAAI,GAAG,oBAAoB,YAAY,KAAK,GAAG,OAAO;IAC9D,CAAC;KACD,EACQ;EAAE,QAAQ;EAAO;;;;;AAM9B,SAAgB,uBAAuB,QAAkC;AACvE,KAAI,OAAO,OAET,QAAO,mBAAmB,OAAO,GAAG;KAEpC,cAAa,OAAO,GAAG"}
@@ -0,0 +1,3 @@
1
+ import { ResourceTimingInstrumentationConfig } from "./types.js";
2
+ import { ResourceTimingInstrumentation } from "./instrumentation.js";
3
+ export { ResourceTimingInstrumentation, type ResourceTimingInstrumentationConfig };
@@ -0,0 +1,2 @@
1
+ import { ResourceTimingInstrumentation } from "./instrumentation.js";
2
+ export { ResourceTimingInstrumentation };
@@ -0,0 +1,33 @@
1
+ import { ResourceTimingInstrumentationConfig } from "./types.js";
2
+ import { InstrumentationBase } from "@opentelemetry/instrumentation";
3
+
4
+ //#region src/resource-timing/instrumentation.d.ts
5
+ /**
6
+ * OpenTelemetry instrumentation for resource timing for browser applications.
7
+ *
8
+ * This instrumentation captures resource timing data using PerformanceObserver
9
+ * and batches emissions to avoid overwhelming the main thread. It uses
10
+ * requestIdleCallback when available (with fallback for Safari) to ensure
11
+ * processing happens during idle periods.
12
+ */
13
+ declare class ResourceTimingInstrumentation extends InstrumentationBase<ResourceTimingInstrumentationConfig> {
14
+ private _observer?;
15
+ private _pendingEntries;
16
+ private _idleHandle?;
17
+ private _isEnabled;
18
+ private _loadHandler;
19
+ private _visibilityChangeHandler;
20
+ constructor(config?: ResourceTimingInstrumentationConfig);
21
+ protected init(): never[];
22
+ enable(): void;
23
+ disable(): void;
24
+ private _setupObserver;
25
+ private _scheduleProcessing;
26
+ private _processChunk;
27
+ private _emitResource;
28
+ private _flush;
29
+ private _cancelScheduledProcessing;
30
+ }
31
+ //#endregion
32
+ export { ResourceTimingInstrumentation };
33
+ //# sourceMappingURL=instrumentation.d.ts.map
@@ -0,0 +1,160 @@
1
+ import { version } from "../package.js";
2
+ import { cancelIdleCallbackShim, requestIdleCallbackShim } from "./idle-callback-shim.js";
3
+ import { ATTR_RESOURCE_CONNECT_END, ATTR_RESOURCE_CONNECT_START, ATTR_RESOURCE_DECODED_BODY_SIZE, ATTR_RESOURCE_DOMAIN_LOOKUP_END, ATTR_RESOURCE_DOMAIN_LOOKUP_START, ATTR_RESOURCE_DURATION, ATTR_RESOURCE_ENCODED_BODY_SIZE, ATTR_RESOURCE_FETCH_START, ATTR_RESOURCE_INITIATOR_TYPE, ATTR_RESOURCE_NEXT_HOP_PROTOCOL, ATTR_RESOURCE_REDIRECT_END, ATTR_RESOURCE_REDIRECT_START, ATTR_RESOURCE_RENDER_BLOCKING_STATUS, ATTR_RESOURCE_REQUEST_START, ATTR_RESOURCE_RESPONSE_END, ATTR_RESOURCE_RESPONSE_START, ATTR_RESOURCE_SECURE_CONNECTION_START, ATTR_RESOURCE_TRANSFER_SIZE, ATTR_RESOURCE_URL, ATTR_RESOURCE_WORKER_START, RESOURCE_TIMING_EVENT_NAME } from "./semconv.js";
4
+ import { SeverityNumber } from "@opentelemetry/api-logs";
5
+ import { InstrumentationBase } from "@opentelemetry/instrumentation";
6
+ //#region src/resource-timing/instrumentation.ts
7
+ const DEFAULT_BATCH_SIZE = 50;
8
+ const DEFAULT_FORCE_PROCESSING_AFTER = 1e3;
9
+ const DEFAULT_MAX_PROCESSING_TIME = 50;
10
+ const DEFAULT_MAX_QUEUE_SIZE = 1e3;
11
+ const MIN_BATCH_SIZE = 1;
12
+ const MIN_FORCE_PROCESSING_AFTER = 0;
13
+ const MIN_PROCESSING_TIME = 0;
14
+ const MIN_QUEUE_SIZE = 1;
15
+ /**
16
+ * OpenTelemetry instrumentation for resource timing for browser applications.
17
+ *
18
+ * This instrumentation captures resource timing data using PerformanceObserver
19
+ * and batches emissions to avoid overwhelming the main thread. It uses
20
+ * requestIdleCallback when available (with fallback for Safari) to ensure
21
+ * processing happens during idle periods.
22
+ */
23
+ var ResourceTimingInstrumentation = class extends InstrumentationBase {
24
+ _observer;
25
+ _pendingEntries = [];
26
+ _idleHandle;
27
+ constructor(config = {}) {
28
+ super("@opentelemetry/browser-instrumentation/resource-timing", version, config);
29
+ }
30
+ init() {
31
+ return [];
32
+ }
33
+ enable() {
34
+ if (this._isEnabled) return;
35
+ if (!("PerformanceObserver" in window)) {
36
+ this._diag.debug("PerformanceObserver is not supported, resource timings will not be collected");
37
+ return;
38
+ }
39
+ this._isEnabled = true;
40
+ if (document.readyState === "complete") this._setupObserver();
41
+ else {
42
+ this._loadHandler = () => this._setupObserver();
43
+ window.addEventListener("load", this._loadHandler, { once: true });
44
+ }
45
+ this._visibilityChangeHandler = () => {
46
+ if (document.hidden) this._flush();
47
+ };
48
+ document.addEventListener("visibilitychange", this._visibilityChangeHandler);
49
+ }
50
+ disable() {
51
+ this._isEnabled = false;
52
+ this._flush();
53
+ this._observer?.disconnect();
54
+ this._observer = void 0;
55
+ if (this._loadHandler) {
56
+ window.removeEventListener("load", this._loadHandler);
57
+ this._loadHandler = void 0;
58
+ }
59
+ if (this._visibilityChangeHandler) {
60
+ document.removeEventListener("visibilitychange", this._visibilityChangeHandler);
61
+ this._visibilityChangeHandler = void 0;
62
+ }
63
+ }
64
+ _setupObserver() {
65
+ if (!this._isEnabled) return;
66
+ try {
67
+ const observer = new PerformanceObserver((list) => {
68
+ if (!this._isEnabled) return;
69
+ const maxQueueSize = Math.max(this._config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE, MIN_QUEUE_SIZE);
70
+ const entries = list.getEntries();
71
+ const initiatorTypes = this._config.initiatorTypes;
72
+ for (const entry of entries) {
73
+ if (initiatorTypes !== void 0 && !initiatorTypes.includes(entry.initiatorType)) continue;
74
+ if (this._pendingEntries.length >= maxQueueSize) this._flush();
75
+ this._pendingEntries.push(entry);
76
+ }
77
+ if (this._pendingEntries.length > 0) this._scheduleProcessing();
78
+ });
79
+ this._observer = observer;
80
+ observer.observe({
81
+ type: "resource",
82
+ buffered: true
83
+ });
84
+ } catch {
85
+ this._diag.warn("PerformanceObserver not supported, resource timings will not be collected");
86
+ }
87
+ }
88
+ _scheduleProcessing() {
89
+ if (this._idleHandle !== void 0) return;
90
+ const timeout = Math.max(this._config.forceProcessingAfter ?? DEFAULT_FORCE_PROCESSING_AFTER, MIN_FORCE_PROCESSING_AFTER);
91
+ this._idleHandle = requestIdleCallbackShim((deadline) => this._processChunk(deadline), { timeout });
92
+ }
93
+ _processChunk(deadline) {
94
+ this._idleHandle = void 0;
95
+ if (!this._isEnabled || this._pendingEntries.length === 0) return;
96
+ const maxTime = Math.max(this._config.maxProcessingTime ?? DEFAULT_MAX_PROCESSING_TIME, MIN_PROCESSING_TIME);
97
+ const batchSize = Math.max(this._config.batchSize ?? DEFAULT_BATCH_SIZE, MIN_BATCH_SIZE);
98
+ const startTime = performance.now();
99
+ let cursor = 0;
100
+ try {
101
+ for (let i = 0; i < batchSize; i++) {
102
+ const entry = this._pendingEntries[cursor];
103
+ if (entry === void 0) break;
104
+ if (performance.now() - startTime >= maxTime || deadline.timeRemaining() < 1) break;
105
+ cursor++;
106
+ this._emitResource(entry);
107
+ }
108
+ } finally {
109
+ this._pendingEntries.splice(0, cursor);
110
+ if (this._pendingEntries.length > 0) this._scheduleProcessing();
111
+ }
112
+ }
113
+ _emitResource(entry) {
114
+ try {
115
+ this.logger.emit({
116
+ eventName: RESOURCE_TIMING_EVENT_NAME,
117
+ severityNumber: SeverityNumber.INFO,
118
+ attributes: {
119
+ [ATTR_RESOURCE_URL]: entry.name,
120
+ [ATTR_RESOURCE_INITIATOR_TYPE]: entry.initiatorType,
121
+ [ATTR_RESOURCE_DURATION]: entry.duration,
122
+ [ATTR_RESOURCE_FETCH_START]: entry.fetchStart,
123
+ [ATTR_RESOURCE_DOMAIN_LOOKUP_START]: entry.domainLookupStart,
124
+ [ATTR_RESOURCE_DOMAIN_LOOKUP_END]: entry.domainLookupEnd,
125
+ [ATTR_RESOURCE_CONNECT_START]: entry.connectStart,
126
+ [ATTR_RESOURCE_CONNECT_END]: entry.connectEnd,
127
+ [ATTR_RESOURCE_SECURE_CONNECTION_START]: entry.secureConnectionStart,
128
+ [ATTR_RESOURCE_REQUEST_START]: entry.requestStart,
129
+ [ATTR_RESOURCE_RESPONSE_START]: entry.responseStart,
130
+ [ATTR_RESOURCE_RESPONSE_END]: entry.responseEnd,
131
+ [ATTR_RESOURCE_TRANSFER_SIZE]: entry.transferSize,
132
+ [ATTR_RESOURCE_ENCODED_BODY_SIZE]: entry.encodedBodySize,
133
+ [ATTR_RESOURCE_DECODED_BODY_SIZE]: entry.decodedBodySize,
134
+ [ATTR_RESOURCE_REDIRECT_START]: entry.redirectStart,
135
+ [ATTR_RESOURCE_REDIRECT_END]: entry.redirectEnd,
136
+ [ATTR_RESOURCE_WORKER_START]: entry.workerStart,
137
+ [ATTR_RESOURCE_NEXT_HOP_PROTOCOL]: entry.nextHopProtocol,
138
+ [ATTR_RESOURCE_RENDER_BLOCKING_STATUS]: entry.renderBlockingStatus
139
+ }
140
+ });
141
+ } catch (error) {
142
+ this._diag.error(`Failed to emit resource timing entry for "${entry.name}"`, error);
143
+ }
144
+ }
145
+ _flush() {
146
+ this._cancelScheduledProcessing();
147
+ for (const entry of this._pendingEntries) this._emitResource(entry);
148
+ this._pendingEntries = [];
149
+ }
150
+ _cancelScheduledProcessing() {
151
+ if (this._idleHandle !== void 0) {
152
+ cancelIdleCallbackShim(this._idleHandle);
153
+ this._idleHandle = void 0;
154
+ }
155
+ }
156
+ };
157
+ //#endregion
158
+ export { ResourceTimingInstrumentation };
159
+
160
+ //# sourceMappingURL=instrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentation.js","names":[],"sources":["../../src/resource-timing/instrumentation.ts"],"sourcesContent":["/*\n * Copyright The OpenTelemetry Authors\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport { InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { version } from '../../package.json' with { type: 'json' };\nimport type { IdleCallbackHandle } from './idle-callback-shim.ts';\nimport {\n cancelIdleCallbackShim,\n requestIdleCallbackShim,\n} from './idle-callback-shim.ts';\nimport {\n ATTR_RESOURCE_CONNECT_END,\n ATTR_RESOURCE_CONNECT_START,\n ATTR_RESOURCE_DECODED_BODY_SIZE,\n ATTR_RESOURCE_DOMAIN_LOOKUP_END,\n ATTR_RESOURCE_DOMAIN_LOOKUP_START,\n ATTR_RESOURCE_DURATION,\n ATTR_RESOURCE_ENCODED_BODY_SIZE,\n ATTR_RESOURCE_FETCH_START,\n ATTR_RESOURCE_INITIATOR_TYPE,\n ATTR_RESOURCE_NEXT_HOP_PROTOCOL,\n ATTR_RESOURCE_REDIRECT_END,\n ATTR_RESOURCE_REDIRECT_START,\n ATTR_RESOURCE_RENDER_BLOCKING_STATUS,\n ATTR_RESOURCE_REQUEST_START,\n ATTR_RESOURCE_RESPONSE_END,\n ATTR_RESOURCE_RESPONSE_START,\n ATTR_RESOURCE_SECURE_CONNECTION_START,\n ATTR_RESOURCE_TRANSFER_SIZE,\n ATTR_RESOURCE_URL,\n ATTR_RESOURCE_WORKER_START,\n RESOURCE_TIMING_EVENT_NAME,\n} from './semconv.ts';\nimport type { ResourceTimingInstrumentationConfig } from './types.ts';\n\nconst DEFAULT_BATCH_SIZE = 50;\nconst DEFAULT_FORCE_PROCESSING_AFTER = 1000;\nconst DEFAULT_MAX_PROCESSING_TIME = 50;\nconst DEFAULT_MAX_QUEUE_SIZE = 1000;\n\nconst MIN_BATCH_SIZE = 1;\nconst MIN_FORCE_PROCESSING_AFTER = 0;\nconst MIN_PROCESSING_TIME = 0;\nconst MIN_QUEUE_SIZE = 1;\n\n/**\n * OpenTelemetry instrumentation for resource timing for browser applications.\n *\n * This instrumentation captures resource timing data using PerformanceObserver\n * and batches emissions to avoid overwhelming the main thread. It uses\n * requestIdleCallback when available (with fallback for Safari) to ensure\n * processing happens during idle periods.\n */\nexport class ResourceTimingInstrumentation extends InstrumentationBase<ResourceTimingInstrumentationConfig> {\n private _observer?: PerformanceObserver;\n private _pendingEntries: PerformanceResourceTiming[] = [];\n private _idleHandle?: IdleCallbackHandle;\n\n // Use `declare` to prevent JS class field initializers from running after\n // super(), which would reset values set by the enable() call that\n // InstrumentationBase makes during its constructor.\n private declare _isEnabled: boolean;\n private declare _loadHandler: (() => void) | undefined;\n private declare _visibilityChangeHandler: (() => void) | undefined;\n\n constructor(config: ResourceTimingInstrumentationConfig = {}) {\n super(\n '@opentelemetry/browser-instrumentation/resource-timing',\n version,\n config,\n );\n }\n\n protected override init() {\n return [];\n }\n\n override enable(): void {\n if (this._isEnabled) {\n return;\n }\n\n if (!('PerformanceObserver' in window)) {\n this._diag.debug(\n 'PerformanceObserver is not supported, resource timings will not be collected',\n );\n return;\n }\n\n this._isEnabled = true;\n\n if (document.readyState === 'complete') {\n this._setupObserver();\n } else {\n this._loadHandler = () => this._setupObserver();\n window.addEventListener('load', this._loadHandler, { once: true });\n }\n\n this._visibilityChangeHandler = () => {\n if (document.hidden) {\n this._flush();\n }\n };\n document.addEventListener(\n 'visibilitychange',\n this._visibilityChangeHandler,\n );\n }\n\n override disable(): void {\n this._isEnabled = false;\n this._flush();\n this._observer?.disconnect();\n this._observer = undefined;\n if (this._loadHandler) {\n window.removeEventListener('load', this._loadHandler);\n this._loadHandler = undefined;\n }\n if (this._visibilityChangeHandler) {\n document.removeEventListener(\n 'visibilitychange',\n this._visibilityChangeHandler,\n );\n this._visibilityChangeHandler = undefined;\n }\n }\n\n private _setupObserver(): void {\n if (!this._isEnabled) {\n return;\n }\n\n try {\n const observer = new PerformanceObserver((list) => {\n if (!this._isEnabled) {\n return;\n }\n\n const maxQueueSize = Math.max(\n this._config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,\n MIN_QUEUE_SIZE,\n );\n const entries = list.getEntries() as PerformanceResourceTiming[];\n const initiatorTypes = this._config.initiatorTypes;\n\n for (const entry of entries) {\n if (\n initiatorTypes !== undefined &&\n !initiatorTypes.includes(entry.initiatorType)\n ) {\n continue;\n }\n\n if (this._pendingEntries.length >= maxQueueSize) {\n this._flush();\n }\n this._pendingEntries.push(entry);\n }\n\n if (this._pendingEntries.length > 0) {\n this._scheduleProcessing();\n }\n });\n\n this._observer = observer;\n observer.observe({ type: 'resource', buffered: true });\n } catch {\n this._diag.warn(\n 'PerformanceObserver not supported, resource timings will not be collected',\n );\n }\n }\n\n private _scheduleProcessing(): void {\n if (this._idleHandle !== undefined) {\n return;\n }\n\n const timeout = Math.max(\n this._config.forceProcessingAfter ?? DEFAULT_FORCE_PROCESSING_AFTER,\n MIN_FORCE_PROCESSING_AFTER,\n );\n this._idleHandle = requestIdleCallbackShim(\n (deadline) => this._processChunk(deadline),\n { timeout },\n );\n }\n\n private _processChunk(deadline: IdleDeadline): void {\n this._idleHandle = undefined;\n if (!this._isEnabled || this._pendingEntries.length === 0) {\n return;\n }\n\n const maxTime = Math.max(\n this._config.maxProcessingTime ?? DEFAULT_MAX_PROCESSING_TIME,\n MIN_PROCESSING_TIME,\n );\n const batchSize = Math.max(\n this._config.batchSize ?? DEFAULT_BATCH_SIZE,\n MIN_BATCH_SIZE,\n );\n const startTime = performance.now();\n\n let cursor = 0;\n try {\n for (let i = 0; i < batchSize; i++) {\n const entry = this._pendingEntries[cursor];\n if (entry === undefined) {\n break;\n }\n\n const elapsed = performance.now() - startTime;\n // timeRemaining() is not Baseline widely available (no Safari support),\n // compatibility is handled by idle-callback-shim.ts.\n // eslint-disable-next-line baseline-js/use-baseline\n if (elapsed >= maxTime || deadline.timeRemaining() < 1) {\n break;\n }\n\n cursor++;\n this._emitResource(entry);\n }\n } finally {\n this._pendingEntries.splice(0, cursor);\n if (this._pendingEntries.length > 0) {\n this._scheduleProcessing();\n }\n }\n }\n\n private _emitResource(entry: PerformanceResourceTiming): void {\n try {\n this.logger.emit({\n eventName: RESOURCE_TIMING_EVENT_NAME,\n severityNumber: SeverityNumber.INFO,\n attributes: {\n [ATTR_RESOURCE_URL]: entry.name,\n [ATTR_RESOURCE_INITIATOR_TYPE]: entry.initiatorType,\n [ATTR_RESOURCE_DURATION]: entry.duration,\n [ATTR_RESOURCE_FETCH_START]: entry.fetchStart,\n [ATTR_RESOURCE_DOMAIN_LOOKUP_START]: entry.domainLookupStart,\n [ATTR_RESOURCE_DOMAIN_LOOKUP_END]: entry.domainLookupEnd,\n [ATTR_RESOURCE_CONNECT_START]: entry.connectStart,\n [ATTR_RESOURCE_CONNECT_END]: entry.connectEnd,\n [ATTR_RESOURCE_SECURE_CONNECTION_START]: entry.secureConnectionStart,\n [ATTR_RESOURCE_REQUEST_START]: entry.requestStart,\n [ATTR_RESOURCE_RESPONSE_START]: entry.responseStart,\n [ATTR_RESOURCE_RESPONSE_END]: entry.responseEnd,\n [ATTR_RESOURCE_TRANSFER_SIZE]: entry.transferSize,\n [ATTR_RESOURCE_ENCODED_BODY_SIZE]: entry.encodedBodySize,\n [ATTR_RESOURCE_DECODED_BODY_SIZE]: entry.decodedBodySize,\n [ATTR_RESOURCE_REDIRECT_START]: entry.redirectStart,\n [ATTR_RESOURCE_REDIRECT_END]: entry.redirectEnd,\n [ATTR_RESOURCE_WORKER_START]: entry.workerStart,\n [ATTR_RESOURCE_NEXT_HOP_PROTOCOL]: entry.nextHopProtocol,\n // @ts-expect-error renderBlockingStatus is only available in Chromium as of March 2026\n [ATTR_RESOURCE_RENDER_BLOCKING_STATUS]: entry.renderBlockingStatus,\n },\n });\n } catch (error) {\n this._diag.error(\n `Failed to emit resource timing entry for \"${entry.name}\"`,\n error,\n );\n }\n }\n\n private _flush(): void {\n this._cancelScheduledProcessing();\n\n for (const entry of this._pendingEntries) {\n this._emitResource(entry);\n }\n this._pendingEntries = [];\n }\n\n private _cancelScheduledProcessing(): void {\n if (this._idleHandle !== undefined) {\n cancelIdleCallbackShim(this._idleHandle);\n this._idleHandle = undefined;\n }\n }\n}\n"],"mappings":"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"}