@adonis-agora/telescope 0.6.0 → 0.7.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.
Files changed (120) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/configure.d.ts.map +1 -1
  3. package/dist/configure.js +6 -0
  4. package/dist/configure.js.map +1 -1
  5. package/dist/providers/telescope_cpu_profiling_provider.d.ts +28 -0
  6. package/dist/providers/telescope_cpu_profiling_provider.d.ts.map +1 -0
  7. package/dist/providers/telescope_cpu_profiling_provider.js +45 -0
  8. package/dist/providers/telescope_cpu_profiling_provider.js.map +1 -0
  9. package/dist/providers/telescope_ui_provider.d.ts.map +1 -1
  10. package/dist/providers/telescope_ui_provider.js +163 -12
  11. package/dist/providers/telescope_ui_provider.js.map +1 -1
  12. package/dist/providers/telescope_watchers_provider.d.ts +10 -0
  13. package/dist/providers/telescope_watchers_provider.d.ts.map +1 -1
  14. package/dist/providers/telescope_watchers_provider.js +32 -0
  15. package/dist/providers/telescope_watchers_provider.js.map +1 -1
  16. package/dist/src/ai/index.d.ts +1 -1
  17. package/dist/src/ai/index.js +1 -1
  18. package/dist/src/alerts/index.d.ts +1 -1
  19. package/dist/src/alerts/index.js +1 -1
  20. package/dist/src/entry.d.ts +8 -0
  21. package/dist/src/entry.d.ts.map +1 -1
  22. package/dist/src/entry.js +8 -0
  23. package/dist/src/entry.js.map +1 -1
  24. package/dist/src/index.d.ts +1 -1
  25. package/dist/src/index.js +1 -1
  26. package/dist/src/mcp/index.d.ts +1 -1
  27. package/dist/src/mcp/index.js +1 -1
  28. package/dist/src/profiling/aggregate-profile.d.ts +21 -0
  29. package/dist/src/profiling/aggregate-profile.d.ts.map +1 -0
  30. package/dist/src/profiling/aggregate-profile.js +124 -0
  31. package/dist/src/profiling/aggregate-profile.js.map +1 -0
  32. package/dist/src/profiling/cpu-profiler.d.ts +59 -0
  33. package/dist/src/profiling/cpu-profiler.d.ts.map +1 -0
  34. package/dist/src/profiling/cpu-profiler.js +94 -0
  35. package/dist/src/profiling/cpu-profiler.js.map +1 -0
  36. package/dist/src/profiling/define_config.d.ts +38 -0
  37. package/dist/src/profiling/define_config.d.ts.map +1 -0
  38. package/dist/src/profiling/define_config.js +23 -0
  39. package/dist/src/profiling/define_config.js.map +1 -0
  40. package/dist/src/profiling/index.d.ts +11 -0
  41. package/dist/src/profiling/index.d.ts.map +1 -0
  42. package/dist/src/profiling/index.js +7 -0
  43. package/dist/src/profiling/index.js.map +1 -0
  44. package/dist/src/profiling/profiler_service.d.ts +100 -0
  45. package/dist/src/profiling/profiler_service.d.ts.map +1 -0
  46. package/dist/src/profiling/profiler_service.js +162 -0
  47. package/dist/src/profiling/profiler_service.js.map +1 -0
  48. package/dist/src/profiling/types.d.ts +86 -0
  49. package/dist/src/profiling/types.d.ts.map +1 -0
  50. package/dist/src/profiling/types.js +5 -0
  51. package/dist/src/profiling/types.js.map +1 -0
  52. package/dist/src/registry.d.ts +28 -0
  53. package/dist/src/registry.d.ts.map +1 -1
  54. package/dist/src/registry.js +20 -0
  55. package/dist/src/registry.js.map +1 -1
  56. package/dist/src/telescope_middleware.d.ts.map +1 -1
  57. package/dist/src/telescope_middleware.js +25 -0
  58. package/dist/src/telescope_middleware.js.map +1 -1
  59. package/dist/src/ui/api.d.ts +41 -1
  60. package/dist/src/ui/api.d.ts.map +1 -1
  61. package/dist/src/ui/api.js +27 -1
  62. package/dist/src/ui/api.js.map +1 -1
  63. package/dist/src/ui/define_config.d.ts +26 -0
  64. package/dist/src/ui/define_config.d.ts.map +1 -1
  65. package/dist/src/ui/define_config.js +8 -0
  66. package/dist/src/ui/define_config.js.map +1 -1
  67. package/dist/src/ui/diagnosis_api.d.ts +33 -0
  68. package/dist/src/ui/diagnosis_api.d.ts.map +1 -0
  69. package/dist/src/ui/diagnosis_api.js +60 -0
  70. package/dist/src/ui/diagnosis_api.js.map +1 -0
  71. package/dist/src/ui/extension_api.d.ts +6 -2
  72. package/dist/src/ui/extension_api.d.ts.map +1 -1
  73. package/dist/src/ui/extension_api.js +7 -2
  74. package/dist/src/ui/extension_api.js.map +1 -1
  75. package/dist/src/ui/guard.d.ts +7 -0
  76. package/dist/src/ui/guard.d.ts.map +1 -1
  77. package/dist/src/ui/guard.js +9 -0
  78. package/dist/src/ui/guard.js.map +1 -1
  79. package/dist/src/ui/http.d.ts +7 -0
  80. package/dist/src/ui/http.d.ts.map +1 -1
  81. package/dist/src/ui/http.js +3 -0
  82. package/dist/src/ui/http.js.map +1 -1
  83. package/dist/src/ui/index.d.ts +8 -2
  84. package/dist/src/ui/index.d.ts.map +1 -1
  85. package/dist/src/ui/index.js +7 -1
  86. package/dist/src/ui/index.js.map +1 -1
  87. package/dist/src/ui/profiles_api.d.ts +48 -0
  88. package/dist/src/ui/profiles_api.d.ts.map +1 -0
  89. package/dist/src/ui/profiles_api.js +94 -0
  90. package/dist/src/ui/profiles_api.js.map +1 -0
  91. package/dist/src/ui/queue_manager_api.d.ts +46 -0
  92. package/dist/src/ui/queue_manager_api.d.ts.map +1 -0
  93. package/dist/src/ui/queue_manager_api.js +118 -0
  94. package/dist/src/ui/queue_manager_api.js.map +1 -0
  95. package/dist/src/ui/schedules_api.d.ts +43 -0
  96. package/dist/src/ui/schedules_api.d.ts.map +1 -0
  97. package/dist/src/ui/schedules_api.js +46 -0
  98. package/dist/src/ui/schedules_api.js.map +1 -0
  99. package/dist/src/watchers/cron_next_run.d.ts +8 -0
  100. package/dist/src/watchers/cron_next_run.d.ts.map +1 -0
  101. package/dist/src/watchers/cron_next_run.js +67 -0
  102. package/dist/src/watchers/cron_next_run.js.map +1 -0
  103. package/dist/src/watchers/define_config.d.ts +28 -1
  104. package/dist/src/watchers/define_config.d.ts.map +1 -1
  105. package/dist/src/watchers/define_config.js +5 -0
  106. package/dist/src/watchers/define_config.js.map +1 -1
  107. package/dist/src/watchers/index.d.ts +7 -4
  108. package/dist/src/watchers/index.d.ts.map +1 -1
  109. package/dist/src/watchers/index.js +5 -2
  110. package/dist/src/watchers/index.js.map +1 -1
  111. package/dist/src/watchers/queue_manager.d.ts +229 -0
  112. package/dist/src/watchers/queue_manager.d.ts.map +1 -0
  113. package/dist/src/watchers/queue_manager.js +223 -0
  114. package/dist/src/watchers/queue_manager.js.map +1 -0
  115. package/dist/src/watchers/schedule_watcher.d.ts +94 -1
  116. package/dist/src/watchers/schedule_watcher.d.ts.map +1 -1
  117. package/dist/src/watchers/schedule_watcher.js +83 -1
  118. package/dist/src/watchers/schedule_watcher.js.map +1 -1
  119. package/dist/stubs/config/telescope_cpu_profiling.stub +0 -0
  120. package/package.json +31 -16
@@ -1 +1 @@
1
- {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/entry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,SAAS;IACpB,wEAAwE;;IAExE,0DAA0D;;;;;;;;;;IAW1D,kEAAkE;;IAElE;;;;OAIG;;IAEH;;;;;OAKG;;IAEH;;;;;OAKG;;CAEK,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAE1E;;;;GAIG;AACH,QAAA,MAAM,aAAa,yDAA0D,CAAC;AAC9E,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAED;;;;GAIG;AACH,MAAM,WAAW,KAAK,CAAC,QAAQ,GAAG,OAAO;IACvC,+BAA+B;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iCAAiC;IACjC,OAAO,EAAE,QAAQ,CAAC;IAClB,iFAAiF;IACjF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oCAAoC;IACpC,MAAM,EAAE,WAAW,CAAC;IACpB,qEAAqE;IACrE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,mCAAmC;IACnC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW,CAAC,QAAQ,GAAG,OAAO;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,QAAQ,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB"}
1
+ {"version":3,"file":"entry.d.ts","sourceRoot":"","sources":["../../src/entry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,SAAS;IACpB,wEAAwE;;IAExE,0DAA0D;;;;;;;;;;IAW1D,kEAAkE;;IAElE;;;;OAIG;;IAEH;;;;;OAKG;;IAEH;;;;;OAKG;;IAEH;;;;;;OAMG;;CAEK,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAE1E;;;;GAIG;AACH,QAAA,MAAM,aAAa,yDAA0D,CAAC;AAC9E,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAED;;;;GAIG;AACH,MAAM,WAAW,KAAK,CAAC,QAAQ,GAAG,OAAO;IACvC,+BAA+B;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iCAAiC;IACjC,OAAO,EAAE,QAAQ,CAAC;IAClB,iFAAiF;IACjF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oCAAoC;IACpC,MAAM,EAAE,WAAW,CAAC;IACpB,qEAAqE;IACrE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,mCAAmC;IACnC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW,CAAC,QAAQ,GAAG,OAAO;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,QAAQ,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uFAAuF;IACvF,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB"}
package/dist/src/entry.js CHANGED
@@ -41,6 +41,14 @@ export const EntryType = {
41
41
  * tags, so it composes with dedup, prune, sampling, and the dashboard.
42
42
  */
43
43
  ClientException: 'client_exception',
44
+ /**
45
+ * An aggregated V8 CPU profile (flamegraph tree + precomputed hot frames)
46
+ * captured via `node:inspector`'s `Profiler.start`/`Profiler.stop`, recorded
47
+ * by the OPTIONAL `cpu_profiling` feature (`@adonis-agora/telescope/cpu_profiling`).
48
+ * Distinct from {@link EntryType.Profile} (user-instrumented timing spans) —
49
+ * this is a real V8 sampling-profiler capture.
50
+ */
51
+ CpuProfile: 'cpu_profile',
44
52
  };
45
53
  /**
46
54
  * Where a batch of entries originated. An entry recorded inside an HTTP request
@@ -1 +1 @@
1
- {"version":3,"file":"entry.js","sourceRoot":"","sources":["../../src/entry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,wEAAwE;IACxE,OAAO,EAAE,SAAS;IAClB,0DAA0D;IAC1D,UAAU,EAAE,YAAY;IACxB,8DAA8D;IAC9D,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,KAAK;IACV,SAAS,EAAE,WAAW;IACtB,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,KAAK;IACV,kEAAkE;IAClE,UAAU,EAAE,aAAa;IACzB;;;;OAIG;IACH,OAAO,EAAE,SAAS;IAClB;;;;;OAKG;IACH,aAAa,EAAE,gBAAgB;IAC/B;;;;;OAKG;IACH,eAAe,EAAE,kBAAkB;CAC3B,CAAC;AAIX;;;;GAIG;AACH,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAU,CAAC;AAG9E,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC3F,CAAC"}
1
+ {"version":3,"file":"entry.js","sourceRoot":"","sources":["../../src/entry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,wEAAwE;IACxE,OAAO,EAAE,SAAS;IAClB,0DAA0D;IAC1D,UAAU,EAAE,YAAY;IACxB,8DAA8D;IAC9D,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,KAAK;IACV,SAAS,EAAE,WAAW;IACtB,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,KAAK;IACV,kEAAkE;IAClE,UAAU,EAAE,aAAa;IACzB;;;;OAIG;IACH,OAAO,EAAE,SAAS;IAClB;;;;;OAKG;IACH,aAAa,EAAE,gBAAgB;IAC/B;;;;;OAKG;IACH,eAAe,EAAE,kBAAkB;IACnC;;;;;;OAMG;IACH,UAAU,EAAE,aAAa;CACjB,CAAC;AAIX;;;;GAIG;AACH,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAU,CAAC;AAG9E,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,aAAmC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC3F,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /** Keep in sync with this package's `version` in package.json. */
2
- export declare const VERSION = "0.6.0";
2
+ export declare const VERSION = "0.7.1";
3
3
  export { EntryType, isBatchOrigin } from './entry.js';
4
4
  export type { BatchOrigin, BuiltinEntryType, Entry, RecordInput } from './entry.js';
5
5
  export type { EntryQuery, TelescopeStore } from './store.js';
package/dist/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Keep in sync with this package's `version` in package.json. */
2
- export const VERSION = '0.6.0';
2
+ export const VERSION = '0.7.1';
3
3
  // — entry model —
4
4
  export { EntryType, isBatchOrigin } from './entry.js';
5
5
  export { InMemoryTelescopeStore } from './stores/memory.js';
@@ -1,5 +1,5 @@
1
1
  /** Keep in sync with this package's `version` in package.json. */
2
- export declare const VERSION = "0.6.0";
2
+ export declare const VERSION = "0.7.1";
3
3
  export { defineConfig, resolveConfig, MCP_TOOL_NAMES } from './define_config.js';
4
4
  export type { McpToolName, ResolvedTelescopeMcpConfig, TelescopeMcpConfig, } from './define_config.js';
5
5
  export { MCP_INTERNAL_ERROR, MCP_METHOD_NOT_FOUND, MCP_TOOLS, MCP_UNAUTHORIZED, TelescopeMcpServer, } from './server.js';
@@ -1,5 +1,5 @@
1
1
  /** Keep in sync with this package's `version` in package.json. */
2
- export const VERSION = '0.6.0';
2
+ export const VERSION = '0.7.1';
3
3
  // — config —
4
4
  export { defineConfig, resolveConfig, MCP_TOOL_NAMES } from './define_config.js';
5
5
  // — server (plain, testable JSON-RPC surface) —
@@ -0,0 +1,21 @@
1
+ import type { FlameNode, HotFrame, V8CpuProfile } from './types.js';
2
+ interface Aggregated {
3
+ tree: FlameNode;
4
+ durationMs: number;
5
+ sampleCount: number;
6
+ hot: HotFrame[];
7
+ }
8
+ /**
9
+ * Aggregates a raw V8 CPU profile into a flamegraph frame tree with self/total
10
+ * time in milliseconds. Pure and deterministic — no inspector dependency, so it
11
+ * is trivially unit-testable and runs OUTSIDE any hot path (only when a capture
12
+ * actually completes).
13
+ *
14
+ * Method: each sample's `timeDelta` (microseconds) is charged to the SELF time
15
+ * of the node it landed on. Total time is then the sum of a node's self time and
16
+ * its descendants', computed by a single post-order walk. The flame tree mirrors
17
+ * V8's `children` call-graph so callers nest exactly as they did at runtime.
18
+ */
19
+ export declare function aggregateCpuProfile(profile: V8CpuProfile): Aggregated;
20
+ export {};
21
+ //# sourceMappingURL=aggregate-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate-profile.d.ts","sourceRoot":"","sources":["../../../src/profiling/aggregate-profile.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAiB,MAAM,YAAY,CAAC;AAQnF,UAAU,UAAU;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,QAAQ,EAAE,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU,CA2FrE"}
@@ -0,0 +1,124 @@
1
+ /** How many hottest-by-self frames to precompute for the dashboard. */
2
+ const HOT_FRAME_LIMIT = 12;
3
+ /** The synthetic root name V8 uses; we keep/normalize to it. */
4
+ const ROOT_NAME = '(root)';
5
+ /**
6
+ * Aggregates a raw V8 CPU profile into a flamegraph frame tree with self/total
7
+ * time in milliseconds. Pure and deterministic — no inspector dependency, so it
8
+ * is trivially unit-testable and runs OUTSIDE any hot path (only when a capture
9
+ * actually completes).
10
+ *
11
+ * Method: each sample's `timeDelta` (microseconds) is charged to the SELF time
12
+ * of the node it landed on. Total time is then the sum of a node's self time and
13
+ * its descendants', computed by a single post-order walk. The flame tree mirrors
14
+ * V8's `children` call-graph so callers nest exactly as they did at runtime.
15
+ */
16
+ export function aggregateCpuProfile(profile) {
17
+ const byId = new Map();
18
+ for (const node of profile.nodes)
19
+ byId.set(node.id, node);
20
+ // 1. Charge each sample's time delta to its node's self time (microseconds).
21
+ const selfMicros = new Map();
22
+ const selfSamples = new Map();
23
+ const samples = profile.samples ?? [];
24
+ const deltas = profile.timeDeltas ?? [];
25
+ let totalMicros = 0;
26
+ for (let i = 0; i < samples.length; i++) {
27
+ const id = samples[i];
28
+ if (id === undefined)
29
+ continue;
30
+ // V8 emits one delta per sample (the time elapsed before that sample). Fall
31
+ // back to 0 if the arrays are mismatched rather than guessing.
32
+ const delta = deltas[i] ?? 0;
33
+ totalMicros += delta;
34
+ selfMicros.set(id, (selfMicros.get(id) ?? 0) + delta);
35
+ selfSamples.set(id, (selfSamples.get(id) ?? 0) + 1);
36
+ }
37
+ // 2. Find (or synthesize) the root. V8 puts the (root) node first; tolerate
38
+ // its absence by stitching all top-level nodes under a synthetic root.
39
+ const rootNode = profile.nodes.find((n) => n.callFrame.functionName === ROOT_NAME);
40
+ const build = (node) => {
41
+ const childIds = node.children ?? [];
42
+ const children = [];
43
+ for (const childId of childIds) {
44
+ const child = byId.get(childId);
45
+ if (child)
46
+ children.push(build(child));
47
+ }
48
+ const self = (selfMicros.get(node.id) ?? 0) / 1000;
49
+ const selfCount = selfSamples.get(node.id) ?? 0;
50
+ const childTotal = children.reduce((sum, c) => sum + c.totalMs, 0);
51
+ const childSamples = children.reduce((sum, c) => sum + c.totalSamples, 0);
52
+ return {
53
+ name: normalizeName(node.callFrame.functionName),
54
+ file: frameFile(node),
55
+ selfMs: self,
56
+ totalMs: self + childTotal,
57
+ totalSamples: selfCount + childSamples,
58
+ children,
59
+ };
60
+ };
61
+ let tree;
62
+ if (rootNode) {
63
+ tree = build(rootNode);
64
+ }
65
+ else {
66
+ // No explicit root: every node referenced as a child belongs to a parent;
67
+ // top-level nodes are those never referenced. Stitch them under a synthetic root.
68
+ const referenced = new Set();
69
+ for (const node of profile.nodes) {
70
+ for (const childId of node.children ?? [])
71
+ referenced.add(childId);
72
+ }
73
+ const tops = profile.nodes.filter((n) => !referenced.has(n.id)).map(build);
74
+ const childTotal = tops.reduce((sum, c) => sum + c.totalMs, 0);
75
+ tree = {
76
+ name: ROOT_NAME,
77
+ file: '',
78
+ selfMs: 0,
79
+ totalMs: childTotal,
80
+ totalSamples: tops.reduce((sum, c) => sum + c.totalSamples, 0),
81
+ children: tops,
82
+ };
83
+ }
84
+ // 3. Hot frames by self time (flatten, sort desc, take N).
85
+ const totalMs = totalMicros / 1000;
86
+ const flat = [];
87
+ const visit = (node) => {
88
+ if (node.selfMs > 0) {
89
+ flat.push({
90
+ name: node.name,
91
+ file: node.file,
92
+ selfMs: node.selfMs,
93
+ selfPct: totalMs > 0 ? (node.selfMs / totalMs) * 100 : 0,
94
+ });
95
+ }
96
+ for (const child of node.children)
97
+ visit(child);
98
+ };
99
+ visit(tree);
100
+ flat.sort((a, b) => b.selfMs - a.selfMs);
101
+ return {
102
+ tree,
103
+ durationMs: durationFor(profile, totalMicros),
104
+ sampleCount: samples.length,
105
+ hot: flat.slice(0, HOT_FRAME_LIMIT),
106
+ };
107
+ }
108
+ /** Prefer the summed sample deltas; fall back to start/end when samples absent. */
109
+ function durationFor(profile, totalMicros) {
110
+ if (totalMicros > 0)
111
+ return totalMicros / 1000;
112
+ const wall = profile.endTime - profile.startTime;
113
+ return wall > 0 ? wall / 1000 : 0;
114
+ }
115
+ function normalizeName(name) {
116
+ return name === '' ? '(anonymous)' : name;
117
+ }
118
+ function frameFile(node) {
119
+ const { url, lineNumber } = node.callFrame;
120
+ if (url === '')
121
+ return '';
122
+ return lineNumber >= 0 ? `${url}:${lineNumber + 1}` : url;
123
+ }
124
+ //# sourceMappingURL=aggregate-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate-profile.js","sourceRoot":"","sources":["../../../src/profiling/aggregate-profile.ts"],"names":[],"mappings":"AAKA,uEAAuE;AACvE,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,gEAAgE;AAChE,MAAM,SAAS,GAAG,QAAQ,CAAC;AAS3B;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAqB;IACvD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK;QAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAE1D,6EAA6E;IAC7E,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;IACxC,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,EAAE,KAAK,SAAS;YAAE,SAAS;QAC/B,4EAA4E;QAC5E,+DAA+D;QAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7B,WAAW,IAAI,KAAK,CAAC;QACrB,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;QACtD,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC;IAEnF,MAAM,KAAK,GAAG,CAAC,IAAmB,EAAa,EAAE;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAgB,EAAE,CAAC;QACjC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAChC,IAAI,KAAK;gBAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;QACnD,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAChD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACnE,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC1E,OAAO;YACL,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC;YAChD,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC;YACrB,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,IAAI,GAAG,UAAU;YAC1B,YAAY,EAAE,SAAS,GAAG,YAAY;YACtC,QAAQ;SACT,CAAC;IACJ,CAAC,CAAC;IAEF,IAAI,IAAe,CAAC;IACpB,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,0EAA0E;QAC1E,kFAAkF;QAClF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE;gBAAE,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrE,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3E,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC/D,IAAI,GAAG;YACL,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,EAAE;YACR,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,UAAU;YACnB,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC;YAC9D,QAAQ,EAAE,IAAI;SACf,CAAC;IACJ,CAAC;IAED,2DAA2D;IAC3D,MAAM,OAAO,GAAG,WAAW,GAAG,IAAI,CAAC;IACnC,MAAM,IAAI,GAAe,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,CAAC,IAAe,EAAQ,EAAE;QACtC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC;gBACR,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;aACzD,CAAC,CAAC;QACL,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ;YAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,CAAC;IACZ,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAEzC,OAAO;QACL,IAAI;QACJ,UAAU,EAAE,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC;QAC7C,WAAW,EAAE,OAAO,CAAC,MAAM;QAC3B,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,SAAS,WAAW,CAAC,OAAqB,EAAE,WAAmB;IAC7D,IAAI,WAAW,GAAG,CAAC;QAAE,OAAO,WAAW,GAAG,IAAI,CAAC;IAC/C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC;IACjD,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED,SAAS,SAAS,CAAC,IAAmB;IACpC,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC;IAC3C,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,EAAE,CAAC;IAC1B,OAAO,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { FlameNode, HotFrame } from './types.js';
2
+ /**
3
+ * The slice of `inspector.Session` we depend on. Declared structurally so the
4
+ * profiler can be unit-tested with a fake and — crucially — so the real
5
+ * `node:inspector` module is NEVER imported at module load. It is required
6
+ * lazily by {@link defaultSessionFactory} ONLY when a capture actually starts.
7
+ */
8
+ export interface InspectorSessionLike {
9
+ connect(): void;
10
+ disconnect(): void;
11
+ post(method: string, paramsOrCallback?: unknown | ((err: Error | null, result?: unknown) => void), callback?: (err: Error | null, result?: unknown) => void): void;
12
+ }
13
+ export type SessionFactory = () => InspectorSessionLike;
14
+ export interface CpuProfilerOptions {
15
+ /**
16
+ * V8 sampling interval in microseconds. Lower = finer-grained but more
17
+ * overhead. V8's default is 1000µs (1ms); we keep that default.
18
+ */
19
+ samplingIntervalMicros?: number;
20
+ }
21
+ export interface CpuProfilerResult {
22
+ tree: FlameNode;
23
+ durationMs: number;
24
+ sampleCount: number;
25
+ hot: HotFrame[];
26
+ }
27
+ /**
28
+ * Wraps a V8 CPU profiling session over the inspector protocol. A single
29
+ * profiler instance captures ONE profile at a time (`start` then `stop`).
30
+ *
31
+ * OVERHEAD GUARANTEE: constructing a profiler does no work and creates no
32
+ * session. The inspector session is created (and `node:inspector` is required)
33
+ * only inside `start()`. When profiling is disabled the controller never
34
+ * constructs one, so there is provably zero inspector activity.
35
+ */
36
+ export declare class CpuProfiler {
37
+ private readonly sessionFactory;
38
+ private session;
39
+ private running;
40
+ private readonly intervalMicros;
41
+ constructor(sessionFactory?: SessionFactory, options?: CpuProfilerOptions);
42
+ get isRunning(): boolean;
43
+ /** Begin a CPU profile. No-op if one is already running. */
44
+ start(): Promise<void>;
45
+ /**
46
+ * Stop the profile and aggregate it into a flame tree. Returns `null` when no
47
+ * profile is running. Always disconnects the session, even on error.
48
+ */
49
+ stop(): Promise<CpuProfilerResult | null>;
50
+ private postAsync;
51
+ }
52
+ /**
53
+ * Lazily require `node:inspector` and return a fresh Session. Kept out of the
54
+ * module's import graph so that importing this file (or the whole package) does
55
+ * NOT load the inspector binding — that only happens the first time a capture
56
+ * starts, i.e. only when profiling is enabled AND a request is sampled.
57
+ */
58
+ export declare function defaultSessionFactory(): InspectorSessionLike;
59
+ //# sourceMappingURL=cpu-profiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpu-profiler.d.ts","sourceRoot":"","sources":["../../../src/profiling/cpu-profiler.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAgB,MAAM,YAAY,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,IAAI,IAAI,CAAC;IAChB,UAAU,IAAI,IAAI,CAAC;IACnB,IAAI,CACF,MAAM,EAAE,MAAM,EACd,gBAAgB,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC,EAC5E,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,GACvD,IAAI,CAAC;CACT;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,oBAAoB,CAAC;AAExD,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,QAAQ,EAAE,CAAC;CACjB;AAID;;;;;;;;GAQG;AACH,qBAAa,WAAW;IAMpB,OAAO,CAAC,QAAQ,CAAC,cAAc;IALjC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAGrB,cAAc,GAAE,cAAsC,EACvE,OAAO,GAAE,kBAAuB;IAKlC,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,4DAA4D;IACtD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAmB/C,OAAO,CAAC,SAAS;CAclB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,oBAAoB,CAS5D"}
@@ -0,0 +1,94 @@
1
+ // Ported near-verbatim (only the import path changed) from `nestjs-telescope`'s
2
+ // `packages/core/src/profiling/cpu-profiler.ts`. `node:inspector`'s Session API is a Node.js
3
+ // built-in, identical under NestJS or AdonisJS — this file has NO framework glue at all.
4
+ import { createRequire } from 'node:module';
5
+ import { aggregateCpuProfile } from './aggregate-profile.js';
6
+ const DEFAULT_SAMPLING_INTERVAL_MICROS = 1000;
7
+ /**
8
+ * Wraps a V8 CPU profiling session over the inspector protocol. A single
9
+ * profiler instance captures ONE profile at a time (`start` then `stop`).
10
+ *
11
+ * OVERHEAD GUARANTEE: constructing a profiler does no work and creates no
12
+ * session. The inspector session is created (and `node:inspector` is required)
13
+ * only inside `start()`. When profiling is disabled the controller never
14
+ * constructs one, so there is provably zero inspector activity.
15
+ */
16
+ export class CpuProfiler {
17
+ sessionFactory;
18
+ session = null;
19
+ running = false;
20
+ intervalMicros;
21
+ constructor(sessionFactory = defaultSessionFactory, options = {}) {
22
+ this.sessionFactory = sessionFactory;
23
+ this.intervalMicros = options.samplingIntervalMicros ?? DEFAULT_SAMPLING_INTERVAL_MICROS;
24
+ }
25
+ get isRunning() {
26
+ return this.running;
27
+ }
28
+ /** Begin a CPU profile. No-op if one is already running. */
29
+ async start() {
30
+ if (this.running)
31
+ return;
32
+ const session = this.sessionFactory();
33
+ session.connect();
34
+ this.session = session;
35
+ await this.postAsync(session, 'Profiler.enable');
36
+ await this.postAsync(session, 'Profiler.setSamplingInterval', {
37
+ interval: this.intervalMicros,
38
+ });
39
+ await this.postAsync(session, 'Profiler.start');
40
+ this.running = true;
41
+ }
42
+ /**
43
+ * Stop the profile and aggregate it into a flame tree. Returns `null` when no
44
+ * profile is running. Always disconnects the session, even on error.
45
+ */
46
+ async stop() {
47
+ const session = this.session;
48
+ if (!this.running || session === null)
49
+ return null;
50
+ try {
51
+ const result = (await this.postAsync(session, 'Profiler.stop'));
52
+ return aggregateCpuProfile(result.profile);
53
+ }
54
+ finally {
55
+ this.running = false;
56
+ this.session = null;
57
+ try {
58
+ session.disconnect();
59
+ }
60
+ catch {
61
+ // A disconnect failure must never mask the captured profile or throw.
62
+ }
63
+ }
64
+ }
65
+ postAsync(session, method, params) {
66
+ return new Promise((resolve, reject) => {
67
+ const cb = (err, result) => {
68
+ if (err)
69
+ reject(err);
70
+ else
71
+ resolve(result);
72
+ };
73
+ if (params === undefined)
74
+ session.post(method, cb);
75
+ else
76
+ session.post(method, params, cb);
77
+ });
78
+ }
79
+ }
80
+ /**
81
+ * Lazily require `node:inspector` and return a fresh Session. Kept out of the
82
+ * module's import graph so that importing this file (or the whole package) does
83
+ * NOT load the inspector binding — that only happens the first time a capture
84
+ * starts, i.e. only when profiling is enabled AND a request is sampled.
85
+ */
86
+ export function defaultSessionFactory() {
87
+ // `createRequire` performs a SYNCHRONOUS require of the built-in without it
88
+ // appearing in this module's static import graph, so loading the package never
89
+ // pulls in the inspector binding. Built only on first capture.
90
+ const requireFn = createRequire(import.meta.url);
91
+ const inspector = requireFn('node:inspector');
92
+ return new inspector.Session();
93
+ }
94
+ //# sourceMappingURL=cpu-profiler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpu-profiler.js","sourceRoot":"","sources":["../../../src/profiling/cpu-profiler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The shape of `config/telescope_cpu_profiling.ts`. Mirrors `nestjs-telescope`'s
3
+ * `profiling-config.ts` (`ResolvedProfilingConfig`) — the knobs are framework-agnostic (they gate a
4
+ * `node:inspector` capture, not anything NestJS/AdonisJS-specific).
5
+ */
6
+ export interface TelescopeCpuProfilingConfig {
7
+ /**
8
+ * Master switch. CPU profiling carries real overhead (a running V8 sampling
9
+ * profiler slows the process it profiles), so it is OFF by default — opt in
10
+ * explicitly. When `false` the middleware hook is a single boolean check and
11
+ * `node:inspector` is never required. Default `false`.
12
+ */
13
+ enabled?: boolean;
14
+ /**
15
+ * Fraction (0–1) of requests to automatically capture. `0` (the default) means
16
+ * captures only happen when armed via `POST <path>/api/profiles/arm`.
17
+ */
18
+ sampleRate?: number;
19
+ /** Maximum concurrent captures. Keeps overhead bounded under load. Default 2. */
20
+ maxConcurrent?: number;
21
+ /** Captures shorter than this are discarded (not worth an entry). Default 5. */
22
+ minDurationMs?: number;
23
+ /** V8 sampling interval in microseconds (lower = finer-grained, more overhead). Default 1000. */
24
+ samplingIntervalMicros?: number;
25
+ }
26
+ /** The fully-resolved config the provider + `ProfilerService` act on (no optionals). */
27
+ export interface ResolvedTelescopeCpuProfilingConfig {
28
+ enabled: boolean;
29
+ sampleRate: number;
30
+ maxConcurrent: number;
31
+ minDurationMs: number;
32
+ samplingIntervalMicros: number;
33
+ }
34
+ /** Identity helper giving `config/telescope_cpu_profiling.ts` full type-checking. */
35
+ export declare function defineConfig(config: TelescopeCpuProfilingConfig): TelescopeCpuProfilingConfig;
36
+ /** Apply defaults to a (possibly partial) config. */
37
+ export declare function resolveConfig(config?: TelescopeCpuProfilingConfig): ResolvedTelescopeCpuProfilingConfig;
38
+ //# sourceMappingURL=define_config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../../src/profiling/define_config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iGAAiG;IACjG,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,wFAAwF;AACxF,MAAM,WAAW,mCAAmC;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAMD,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,MAAM,EAAE,2BAA2B,GAAG,2BAA2B,CAE7F;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAC3B,MAAM,GAAE,2BAAgC,GACvC,mCAAmC,CAWrC"}
@@ -0,0 +1,23 @@
1
+ const DEFAULT_MAX_CONCURRENT = 2;
2
+ const DEFAULT_MIN_DURATION_MS = 5;
3
+ const DEFAULT_SAMPLING_INTERVAL_MICROS = 1000;
4
+ /** Identity helper giving `config/telescope_cpu_profiling.ts` full type-checking. */
5
+ export function defineConfig(config) {
6
+ return config;
7
+ }
8
+ /** Apply defaults to a (possibly partial) config. */
9
+ export function resolveConfig(config = {}) {
10
+ return {
11
+ enabled: config.enabled ?? false,
12
+ sampleRate: clamp01(config.sampleRate ?? 0),
13
+ maxConcurrent: Math.max(1, Math.floor(config.maxConcurrent ?? DEFAULT_MAX_CONCURRENT)),
14
+ minDurationMs: Math.max(0, config.minDurationMs ?? DEFAULT_MIN_DURATION_MS),
15
+ samplingIntervalMicros: Math.max(100, Math.floor(config.samplingIntervalMicros ?? DEFAULT_SAMPLING_INTERVAL_MICROS)),
16
+ };
17
+ }
18
+ function clamp01(value) {
19
+ if (!Number.isFinite(value))
20
+ return 0;
21
+ return Math.min(1, Math.max(0, value));
22
+ }
23
+ //# sourceMappingURL=define_config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../../src/profiling/define_config.ts"],"names":[],"mappings":"AAmCA,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACjC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AAClC,MAAM,gCAAgC,GAAG,IAAI,CAAC;AAE9C,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,MAAmC;IAC9D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,aAAa,CAC3B,SAAsC,EAAE;IAExC,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,KAAK;QAChC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC;QAC3C,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,IAAI,sBAAsB,CAAC,CAAC;QACtF,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,aAAa,IAAI,uBAAuB,CAAC;QAC3E,sBAAsB,EAAE,IAAI,CAAC,GAAG,CAC9B,GAAG,EACH,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,IAAI,gCAAgC,CAAC,CAC9E;KACF,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CAAC,KAAa;IAC5B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACtC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,11 @@
1
+ /** Keep in sync with this package's `version` in package.json. */
2
+ export declare const VERSION = "0.7.1";
3
+ export { defineConfig, resolveConfig } from './define_config.js';
4
+ export type { ResolvedTelescopeCpuProfilingConfig, TelescopeCpuProfilingConfig, } from './define_config.js';
5
+ export { CPU_PROFILE_ENTRY_TYPE, ProfilerService, } from './profiler_service.js';
6
+ export type { ProfileHandle, ProfilerLike, ProfilerServiceDeps, ProfilerStatus, } from './profiler_service.js';
7
+ export { CpuProfiler, defaultSessionFactory } from './cpu-profiler.js';
8
+ export type { CpuProfilerOptions, CpuProfilerResult, InspectorSessionLike, SessionFactory, } from './cpu-profiler.js';
9
+ export { aggregateCpuProfile } from './aggregate-profile.js';
10
+ export type { CpuProfileContent, FlameNode, HotFrame, V8CpuProfile, V8ProfileNode, } from './types.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/profiling/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACjE,YAAY,EACV,mCAAmC,EACnC,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,sBAAsB,EACtB,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AACvE,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,GACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,YAAY,EACV,iBAAiB,EACjB,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,aAAa,GACd,MAAM,YAAY,CAAC"}
@@ -0,0 +1,7 @@
1
+ /** Keep in sync with this package's `version` in package.json. */
2
+ export const VERSION = '0.7.1';
3
+ export { defineConfig, resolveConfig } from './define_config.js';
4
+ export { CPU_PROFILE_ENTRY_TYPE, ProfilerService, } from './profiler_service.js';
5
+ export { CpuProfiler, defaultSessionFactory } from './cpu-profiler.js';
6
+ export { aggregateCpuProfile } from './aggregate-profile.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/profiling/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAMjE,OAAO,EACL,sBAAsB,EACtB,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAQvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,100 @@
1
+ import { type RecordInput } from '../entry.js';
2
+ import { type CpuProfilerResult } from './cpu-profiler.js';
3
+ import type { ResolvedTelescopeCpuProfilingConfig } from './define_config.js';
4
+ import type { CpuProfileContent } from './types.js';
5
+ /** The `type` of the entries this service records. */
6
+ export declare const CPU_PROFILE_ENTRY_TYPE: "cpu_profile";
7
+ /** Opaque handle for an in-flight capture, returned by {@link ProfilerService.begin}. */
8
+ export interface ProfileHandle {
9
+ readonly profiler: ProfilerLike;
10
+ readonly startedAt: number;
11
+ readonly reason: 'manual' | 'sampled';
12
+ /**
13
+ * The (fire-and-forget) start promise. {@link ProfilerService.end} awaits it
14
+ * before calling `stop()` so a capture that ends before `start()` settled (a
15
+ * very fast request, or a synchronous test) is still stopped correctly rather
16
+ * than seeing `isRunning === false` and silently dropping the profile.
17
+ */
18
+ readonly started: Promise<void>;
19
+ }
20
+ /** Minimal profiler contract so the service can be tested with a fake. */
21
+ export interface ProfilerLike {
22
+ readonly isRunning: boolean;
23
+ start(): Promise<void>;
24
+ stop(): Promise<CpuProfilerResult | null>;
25
+ }
26
+ export interface ProfilerServiceDeps {
27
+ /** RNG for sampling — injectable for deterministic tests. */
28
+ random?: () => number;
29
+ /** Builds a profiler; injectable so tests avoid the real inspector. */
30
+ profilerFactory?: () => ProfilerLike;
31
+ /** Records the finished profile; injectable for tests. Defaults to `safeRecord`
32
+ * through the runtime store slot (mirrors every other watcher). */
33
+ record?: (input: RecordInput<CpuProfileContent>) => void;
34
+ }
35
+ export interface ProfilerStatus {
36
+ enabled: boolean;
37
+ sampleRate: number;
38
+ /** Currently in-flight captures. */
39
+ active: number;
40
+ maxConcurrent: number;
41
+ /** Remaining manual budget across all arms. */
42
+ pendingManual: number;
43
+ }
44
+ /**
45
+ * Decides whether to profile a request and drives the capture lifecycle around
46
+ * it. {@link TelescopeMiddleware} calls {@link shouldProfile} (cheap gate), then
47
+ * {@link begin}/{@link end} around the request body — ported near-verbatim from
48
+ * `nestjs-telescope`'s `ProfilerService`; only the recording seam changed (fire-and-forget
49
+ * `safeRecord` through the runtime store slot, matching every other Adonis watcher, instead of a
50
+ * NestJS-injected recorder).
51
+ *
52
+ * OVERHEAD: while disabled, {@link shouldProfile} returns false and {@link begin}
53
+ * returns null after a single boolean check — no profiler is ever constructed
54
+ * and `node:inspector` is never required. While enabled but a request isn't
55
+ * selected, the only added cost is the gate and (if sampling) one RNG call.
56
+ */
57
+ export declare class ProfilerService {
58
+ private readonly config;
59
+ private active;
60
+ private readonly manual;
61
+ private readonly random;
62
+ private readonly profilerFactory;
63
+ private readonly recordFn;
64
+ constructor(config: ResolvedTelescopeCpuProfilingConfig, deps?: ProfilerServiceDeps);
65
+ /**
66
+ * Arm a manual capture for the next `count` requests (optionally only those
67
+ * matching `label`). Returns the pending budget. No-op when disabled.
68
+ */
69
+ arm(arm: {
70
+ count: number;
71
+ label?: string;
72
+ }): {
73
+ pendingManual: number;
74
+ };
75
+ /**
76
+ * Whether this request should be profiled. Consults the manual arm budget
77
+ * first (targeted captures win), then sampling. Cheap and side-effect-free
78
+ * EXCEPT it does not yet consume the manual budget — that happens in
79
+ * {@link begin} so a request rejected by the concurrency cap doesn't waste it.
80
+ */
81
+ shouldProfile(label: string | null): boolean;
82
+ /**
83
+ * Begin a capture for a request that {@link shouldProfile} accepted. Returns a
84
+ * handle to pass to {@link end}, or `null` if the request should not be
85
+ * profiled (disabled, concurrency cap reached, or not selected). The caller
86
+ * MUST treat a null handle as "no profiling" and proceed untouched.
87
+ */
88
+ begin(label: string | null): ProfileHandle | null;
89
+ /**
90
+ * Stop a capture and record it as a `cpu_profile` entry. Safe to call with a
91
+ * null handle (no-op). Never throws into the host request path. Captures below
92
+ * the `minDurationMs` floor are discarded.
93
+ */
94
+ end(handle: ProfileHandle | null, label: string | null): Promise<void>;
95
+ status(): ProfilerStatus;
96
+ private pendingManual;
97
+ /** First arm matching this label (labelled arms require an exact match). */
98
+ private matchingArm;
99
+ }
100
+ //# sourceMappingURL=profiler_service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profiler_service.d.ts","sourceRoot":"","sources":["../../../src/profiling/profiler_service.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAAe,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,sDAAsD;AACtD,eAAO,MAAM,sBAAsB,eAAuB,CAAC;AAE3D,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;CAC3C;AASD,MAAM,WAAW,mBAAmB;IAClC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,YAAY,CAAC;IACrC;wEACoE;IACpE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,eAAe;IAQxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkD;gBAGxD,MAAM,EAAE,mCAAmC,EAC5D,IAAI,GAAE,mBAAwB;IAShC;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE;IAOtE;;;;;OAKG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO;IAO5C;;;;;OAKG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,aAAa,GAAG,IAAI;IAuBjD;;;;OAIG;IACG,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAgC5E,MAAM,IAAI,cAAc;IAUxB,OAAO,CAAC,aAAa;IAIrB,4EAA4E;IAC5E,OAAO,CAAC,WAAW;CAQpB"}