@rudderjs/pulse 0.0.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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +107 -0
  3. package/boost/guidelines.md +33 -0
  4. package/dist/aggregators/cache.d.ts +11 -0
  5. package/dist/aggregators/cache.d.ts.map +1 -0
  6. package/dist/aggregators/cache.js +34 -0
  7. package/dist/aggregators/cache.js.map +1 -0
  8. package/dist/aggregators/exception.d.ts +11 -0
  9. package/dist/aggregators/exception.d.ts.map +1 -0
  10. package/dist/aggregators/exception.js +30 -0
  11. package/dist/aggregators/exception.js.map +1 -0
  12. package/dist/aggregators/query.d.ts +12 -0
  13. package/dist/aggregators/query.d.ts.map +1 -0
  14. package/dist/aggregators/query.js +39 -0
  15. package/dist/aggregators/query.js.map +1 -0
  16. package/dist/aggregators/queue.d.ts +12 -0
  17. package/dist/aggregators/queue.d.ts.map +1 -0
  18. package/dist/aggregators/queue.js +43 -0
  19. package/dist/aggregators/queue.js.map +1 -0
  20. package/dist/aggregators/request.d.ts +15 -0
  21. package/dist/aggregators/request.d.ts.map +1 -0
  22. package/dist/aggregators/request.js +46 -0
  23. package/dist/aggregators/request.js.map +1 -0
  24. package/dist/aggregators/server.d.ts +13 -0
  25. package/dist/aggregators/server.d.ts.map +1 -0
  26. package/dist/aggregators/server.js +35 -0
  27. package/dist/aggregators/server.js.map +1 -0
  28. package/dist/aggregators/user.d.ts +17 -0
  29. package/dist/aggregators/user.d.ts.map +1 -0
  30. package/dist/aggregators/user.js +48 -0
  31. package/dist/aggregators/user.js.map +1 -0
  32. package/dist/api/routes.d.ts +6 -0
  33. package/dist/api/routes.d.ts.map +1 -0
  34. package/dist/api/routes.js +167 -0
  35. package/dist/api/routes.js.map +1 -0
  36. package/dist/index.d.ts +37 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +143 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/storage.d.ts +28 -0
  41. package/dist/storage.d.ts.map +1 -0
  42. package/dist/storage.js +219 -0
  43. package/dist/storage.js.map +1 -0
  44. package/dist/types.d.ts +77 -0
  45. package/dist/types.d.ts.map +1 -0
  46. package/dist/types.js +19 -0
  47. package/dist/types.js.map +1 -0
  48. package/dist/ui/dashboard.d.ts +5 -0
  49. package/dist/ui/dashboard.d.ts.map +1 -0
  50. package/dist/ui/dashboard.js +234 -0
  51. package/dist/ui/dashboard.js.map +1 -0
  52. package/dist/ui/layout.d.ts +6 -0
  53. package/dist/ui/layout.d.ts.map +1 -0
  54. package/dist/ui/layout.js +54 -0
  55. package/dist/ui/layout.js.map +1 -0
  56. package/dist/utils.d.ts +6 -0
  57. package/dist/utils.d.ts.map +1 -0
  58. package/dist/utils.js +19 -0
  59. package/dist/utils.js.map +1 -0
  60. package/package.json +66 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Suleiman Shahbari
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # @rudderjs/pulse
2
+
3
+ Application performance monitoring for RudderJS — aggregates request throughput, queue metrics, cache hit rates, exceptions, active users, slow queries, and server resource usage.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @rudderjs/pulse
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ```ts
14
+ // bootstrap/providers.ts
15
+ import { pulse } from '@rudderjs/pulse'
16
+ import configs from '../config/index.js'
17
+ export default [..., pulse(configs.pulse), ...]
18
+ ```
19
+
20
+ ## Pulse Facade
21
+
22
+ ```ts
23
+ import { Pulse } from '@rudderjs/pulse'
24
+
25
+ // Record a metric
26
+ Pulse.record('request_duration', 142)
27
+ Pulse.record('cache_hits', 1, 'user-cache')
28
+
29
+ // Query aggregates
30
+ const since = new Date(Date.now() - 3600_000) // last hour
31
+ const requestMetrics = await Pulse.aggregates('request_count', since)
32
+ const cacheMetrics = await Pulse.aggregates('cache_hits', since, 'user-cache')
33
+
34
+ // Get entries (slow requests, exceptions, etc.)
35
+ const slowRequests = await Pulse.entries('slow_request', { perPage: 25 })
36
+
37
+ // Overview of all metrics
38
+ const overview = await Pulse.overview(since)
39
+ ```
40
+
41
+ ## `Pulse` Methods
42
+
43
+ | Method | Returns | Description |
44
+ |--------|---------|-------------|
45
+ | `record(type, value, key?)` | `void` | Increment an aggregate bucket |
46
+ | `aggregates(type, since, key?)` | `PulseAggregate[]` | Aggregates for a metric type within a period |
47
+ | `entries(type, options?)` | `PulseEntry[]` | Individual entries (slow requests, exceptions) |
48
+ | `overview(since)` | `PulseAggregate[]` | All aggregates since a given date |
49
+
50
+ ## Metric Types
51
+
52
+ | Type | Source |
53
+ |------|--------|
54
+ | `request_count` | Request middleware |
55
+ | `request_duration` | Request middleware |
56
+ | `queue_throughput` | Queue aggregator |
57
+ | `queue_wait_time` | Queue aggregator |
58
+ | `cache_hits` / `cache_misses` | Cache aggregator |
59
+ | `exceptions` | Exception aggregator |
60
+ | `active_users` | User aggregator middleware |
61
+ | `server_cpu` / `server_memory` | Server aggregator (periodic) |
62
+
63
+ ## Storage Drivers
64
+
65
+ - **`memory`** (default) — In-process, capped at `maxEntries`. Good for development.
66
+ - **`sqlite`** — Persistent storage via `better-sqlite3`. Run `pnpm add better-sqlite3` to enable.
67
+
68
+ ## Configuration
69
+
70
+ ```ts
71
+ // config/pulse.ts
72
+ export default {
73
+ enabled: true,
74
+ path: 'pulse',
75
+ storage: 'memory',
76
+ sqlitePath: '.pulse.db',
77
+ pruneAfterHours: 168, // 7 days
78
+ slowRequestThreshold: 1000, // ms
79
+ slowQueryThreshold: 100, // ms
80
+ recordRequests: true,
81
+ recordQueues: true,
82
+ recordCache: true,
83
+ recordExceptions: true,
84
+ recordUsers: true,
85
+ recordServers: true,
86
+ serverStatsIntervalMs: 15_000,
87
+ auth: null,
88
+ } satisfies PulseConfig
89
+ ```
90
+
91
+ ## Aggregators
92
+
93
+ Pulse auto-registers aggregators based on config:
94
+
95
+ - **RequestAggregator** — Tracks request throughput and duration via middleware
96
+ - **QueueAggregator** — Tracks queue throughput and wait times
97
+ - **CacheAggregator** — Tracks cache hit/miss rates
98
+ - **ExceptionAggregator** — Counts unhandled exceptions
99
+ - **QueryAggregator** — Records slow database queries
100
+ - **UserAggregator** — Tracks unique active users via middleware
101
+ - **ServerAggregator** — Periodically records CPU and memory usage
102
+
103
+ ## Notes
104
+
105
+ - Auto-prune runs on a background interval.
106
+ - Optional peers: `@rudderjs/log`, `@rudderjs/orm`, `@rudderjs/cache`, `@rudderjs/queue`.
107
+ - Dashboard served at `/{path}` with auto-refreshing metric cards.
@@ -0,0 +1,33 @@
1
+ # @rudderjs/pulse — AI Coding Guidelines
2
+
3
+ ## What This Package Does
4
+
5
+ Pulse is an application metrics dashboard for RudderJS applications. It aggregates time-series data (request throughput, cache hit rates, queue metrics, server stats) into 1-minute buckets and exposes a JSON API for visualization.
6
+
7
+ ## Architecture
8
+
9
+ Two data models:
10
+ 1. **Aggregates** — Time-bucketed metrics (1-minute resolution). Used for throughput, duration, hit rates.
11
+ 2. **Entries** — Individual notable events (slow requests, slow queries, exceptions, failed jobs).
12
+
13
+ Seven aggregators collect data:
14
+ - `RequestAggregator` — middleware: request count + duration
15
+ - `QueueAggregator` — wraps adapter: throughput + wait time + failures
16
+ - `CacheAggregator` — wraps adapter: hit/miss counting
17
+ - `ExceptionAggregator` — exception reporter hook
18
+ - `UserAggregator` — middleware: unique users per minute
19
+ - `QueryAggregator` — ORM hook: slow query detection
20
+ - `ServerAggregator` — periodic: CPU + memory
21
+
22
+ ## Key Patterns
23
+
24
+ - Aggregators implement the `Aggregator` interface with `register()` method
25
+ - `storage.record(type, value, key?)` increments a 1-minute bucket aggregate
26
+ - `storage.storeEntry(type, content)` records individual notable events
27
+ - API endpoints support `?period=1h|6h|24h|7d` for time range selection
28
+
29
+ ## Do NOT
30
+
31
+ - Import peer dependencies statically — always use dynamic `import()` with try/catch
32
+ - Record Pulse's own API requests (request aggregator skips `/pulse*`)
33
+ - Store high-cardinality keys in aggregates — keep `key` to queue names, route patterns, not full URLs
@@ -0,0 +1,11 @@
1
+ import type { Aggregator, PulseStorage } from '../types.js';
2
+ /**
3
+ * Tracks cache hit/miss ratio by wrapping the CacheRegistry adapter.
4
+ */
5
+ export declare class CacheAggregator implements Aggregator {
6
+ private readonly storage;
7
+ readonly name = "Cache Aggregator";
8
+ constructor(storage: PulseStorage);
9
+ register(): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/aggregators/cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE3D;;GAEG;AACH,qBAAa,eAAgB,YAAW,UAAU;IAGpC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,QAAQ,CAAC,IAAI,sBAAqB;gBAEL,OAAO,EAAE,YAAY;IAE5C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAsBhC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Tracks cache hit/miss ratio by wrapping the CacheRegistry adapter.
3
+ */
4
+ export class CacheAggregator {
5
+ storage;
6
+ name = 'Cache Aggregator';
7
+ constructor(storage) {
8
+ this.storage = storage;
9
+ }
10
+ async register() {
11
+ try {
12
+ const { CacheRegistry } = await import('@rudderjs/cache');
13
+ const adapter = CacheRegistry.get();
14
+ if (!adapter)
15
+ return;
16
+ const storage = this.storage;
17
+ const origGet = adapter.get.bind(adapter);
18
+ adapter['get'] = async (key) => {
19
+ const value = await origGet(key);
20
+ if (value !== null) {
21
+ storage.record('cache_hits', 1);
22
+ }
23
+ else {
24
+ storage.record('cache_misses', 1);
25
+ }
26
+ return value;
27
+ };
28
+ }
29
+ catch {
30
+ // @rudderjs/cache not installed — skip
31
+ }
32
+ }
33
+ }
34
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/aggregators/cache.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,eAAe;IAGG;IAFpB,IAAI,GAAG,kBAAkB,CAAA;IAElC,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC;YACH,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;YACzD,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,EAAE,CAAA;YACnC,IAAI,CAAC,OAAO;gBAAE,OAAM;YAEpB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;YAC5B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAExC;YAAC,OAA8C,CAAC,KAAK,CAAC,GAAG,KAAK,EAAe,GAAW,EAAqB,EAAE;gBAC9G,MAAM,KAAK,GAAG,MAAO,OAA8C,CAAC,GAAG,CAAC,CAAA;gBACxE,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBACnB,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAA;gBACjC,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAA;gBACnC,CAAC;gBACD,OAAO,KAAK,CAAA;YACd,CAAC,CAAA;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uCAAuC;QACzC,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ import type { Aggregator, PulseStorage } from '../types.js';
2
+ /**
3
+ * Tracks exception count over time by hooking into the exception reporter.
4
+ */
5
+ export declare class ExceptionAggregator implements Aggregator {
6
+ private readonly storage;
7
+ readonly name = "Exception Aggregator";
8
+ constructor(storage: PulseStorage);
9
+ register(): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=exception.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exception.d.ts","sourceRoot":"","sources":["../../src/aggregators/exception.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE3D;;GAEG;AACH,qBAAa,mBAAoB,YAAW,UAAU;IAGxC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,QAAQ,CAAC,IAAI,0BAAyB;gBAET,OAAO,EAAE,YAAY;IAE5C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAuBhC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Tracks exception count over time by hooking into the exception reporter.
3
+ */
4
+ export class ExceptionAggregator {
5
+ storage;
6
+ name = 'Exception Aggregator';
7
+ constructor(storage) {
8
+ this.storage = storage;
9
+ }
10
+ async register() {
11
+ try {
12
+ const { setExceptionReporter, report } = await import('@rudderjs/core');
13
+ const storage = this.storage;
14
+ const previousReport = report;
15
+ setExceptionReporter((err) => {
16
+ storage.record('exceptions', 1);
17
+ const isError = err instanceof Error;
18
+ storage.storeEntry('exception', {
19
+ class: isError ? err.constructor.name : 'Unknown',
20
+ message: isError ? err.message : String(err),
21
+ });
22
+ previousReport(err);
23
+ });
24
+ }
25
+ catch {
26
+ // Should not fail
27
+ }
28
+ }
29
+ }
30
+ //# sourceMappingURL=exception.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exception.js","sourceRoot":"","sources":["../../src/aggregators/exception.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,mBAAmB;IAGD;IAFpB,IAAI,GAAG,sBAAsB,CAAA;IAEtC,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC;YACH,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAGrE,CAAA;YAED,MAAM,OAAO,GAAU,IAAI,CAAC,OAAO,CAAA;YACnC,MAAM,cAAc,GAAG,MAAM,CAAA;YAE7B,oBAAoB,CAAC,CAAC,GAAY,EAAE,EAAE;gBACpC,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAA;gBAC/B,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAA;gBACpC,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE;oBAC9B,KAAK,EAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;oBACnD,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBAC7C,CAAC,CAAA;gBACF,cAAc,CAAC,GAAG,CAAC,CAAA;YACrB,CAAC,CAAC,CAAA;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,kBAAkB;QACpB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ import type { Aggregator, PulseStorage, PulseConfig } from '../types.js';
2
+ /**
3
+ * Tracks slow database queries by hooking into the ORM adapter.
4
+ */
5
+ export declare class QueryAggregator implements Aggregator {
6
+ private readonly storage;
7
+ private readonly config;
8
+ readonly name = "Query Aggregator";
9
+ constructor(storage: PulseStorage, config: PulseConfig);
10
+ register(): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/aggregators/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAExE;;GAEG;AACH,qBAAa,eAAgB,YAAW,UAAU;IAI9C,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,QAAQ,CAAC,IAAI,sBAAqB;gBAGf,OAAO,EAAE,YAAY,EACrB,MAAM,EAAG,WAAW;IAGjC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CA4BhC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Tracks slow database queries by hooking into the ORM adapter.
3
+ */
4
+ export class QueryAggregator {
5
+ storage;
6
+ config;
7
+ name = 'Query Aggregator';
8
+ constructor(storage, config) {
9
+ this.storage = storage;
10
+ this.config = config;
11
+ }
12
+ async register() {
13
+ try {
14
+ const orm = await import('@rudderjs/orm');
15
+ const registry = orm.ModelRegistry;
16
+ if (!registry.getAdapter)
17
+ return;
18
+ const adapter = registry.getAdapter();
19
+ if (!adapter?.onQuery)
20
+ return;
21
+ const storage = this.storage;
22
+ const threshold = this.config.slowQueryThreshold ?? 100;
23
+ adapter.onQuery((info) => {
24
+ if (info.duration > threshold) {
25
+ storage.storeEntry('slow_query', {
26
+ sql: info.sql,
27
+ bindings: info.bindings,
28
+ duration: info.duration,
29
+ model: info.model,
30
+ });
31
+ }
32
+ });
33
+ }
34
+ catch {
35
+ // @rudderjs/orm not installed — skip
36
+ }
37
+ }
38
+ }
39
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/aggregators/query.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,eAAe;IAIP;IACA;IAJV,IAAI,GAAG,kBAAkB,CAAA;IAElC,YACmB,OAAqB,EACrB,MAAoB;QADpB,YAAO,GAAP,OAAO,CAAc;QACrB,WAAM,GAAN,MAAM,CAAc;IACpC,CAAC;IAEJ,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAA;YACzC,MAAM,QAAQ,GAAG,GAAG,CAAC,aAEpB,CAAA;YACD,IAAI,CAAC,QAAQ,CAAC,UAAU;gBAAE,OAAM;YAEhC,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,EAAE,CAAA;YACrC,IAAI,CAAC,OAAO,EAAE,OAAO;gBAAE,OAAM;YAE7B,MAAM,OAAO,GAAK,IAAI,CAAC,OAAO,CAAA;YAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,IAAI,GAAG,CAAA;YAEvD,OAAO,CAAC,OAAO,CAAC,CAAC,IAAe,EAAE,EAAE;gBAClC,IAAI,IAAI,CAAC,QAAQ,GAAG,SAAS,EAAE,CAAC;oBAC9B,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE;wBAC/B,GAAG,EAAO,IAAI,CAAC,GAAG;wBAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,KAAK,EAAK,IAAI,CAAC,KAAK;qBACrB,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC,CAAC,CAAA;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,qCAAqC;QACvC,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ import type { Aggregator, PulseStorage } from '../types.js';
2
+ /**
3
+ * Tracks queue throughput, wait time, and failed jobs.
4
+ * Wraps the QueueRegistry adapter's dispatch method.
5
+ */
6
+ export declare class QueueAggregator implements Aggregator {
7
+ private readonly storage;
8
+ readonly name = "Queue Aggregator";
9
+ constructor(storage: PulseStorage);
10
+ register(): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/aggregators/queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE3D;;;GAGG;AACH,qBAAa,eAAgB,YAAW,UAAU;IAGpC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,QAAQ,CAAC,IAAI,sBAAqB;gBAEL,OAAO,EAAE,YAAY;IAE5C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAiChC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Tracks queue throughput, wait time, and failed jobs.
3
+ * Wraps the QueueRegistry adapter's dispatch method.
4
+ */
5
+ export class QueueAggregator {
6
+ storage;
7
+ name = 'Queue Aggregator';
8
+ constructor(storage) {
9
+ this.storage = storage;
10
+ }
11
+ async register() {
12
+ try {
13
+ const { QueueRegistry } = await import('@rudderjs/queue');
14
+ const adapter = QueueRegistry.get();
15
+ if (!adapter)
16
+ return;
17
+ const storage = this.storage;
18
+ const originalDispatch = adapter.dispatch.bind(adapter);
19
+ adapter['dispatch'] = async (job, options) => {
20
+ const dispatchedAt = Date.now();
21
+ try {
22
+ await originalDispatch(job, options);
23
+ const duration = Date.now() - dispatchedAt;
24
+ storage.record('queue_throughput', 1);
25
+ storage.record('queue_wait_time', duration);
26
+ }
27
+ catch (err) {
28
+ storage.record('queue_throughput', 1);
29
+ const j = job;
30
+ storage.storeEntry('failed_job', {
31
+ class: j.constructor.name,
32
+ exception: err instanceof Error ? err.message : String(err),
33
+ });
34
+ throw err;
35
+ }
36
+ };
37
+ }
38
+ catch {
39
+ // @rudderjs/queue not installed — skip
40
+ }
41
+ }
42
+ }
43
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/aggregators/queue.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,OAAO,eAAe;IAGG;IAFpB,IAAI,GAAG,kBAAkB,CAAA;IAElC,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC;YACH,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;YACzD,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,EAAE,CAAA;YACnC,IAAI,CAAC,OAAO;gBAAE,OAAM;YAEpB,MAAM,OAAO,GAAY,IAAI,CAAC,OAAO,CAAA;YACrC,MAAM,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAEtD;YAAC,OAA8C,CAAC,UAAU,CAAC,GAAG,KAAK,EAClE,GAAY,EACZ,OAAiB,EACF,EAAE;gBACjB,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;gBAC/B,IAAI,CAAC;oBACH,MAAO,gBAA0D,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;oBAC/E,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAA;oBAC1C,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAA;oBACrC,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAA;gBAC7C,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAA;oBACrC,MAAM,CAAC,GAAG,GAAwC,CAAA;oBAClD,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE;wBAC/B,KAAK,EAAM,CAAC,CAAC,WAAW,CAAC,IAAI;wBAC7B,SAAS,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;qBAC5D,CAAC,CAAA;oBACF,MAAM,GAAG,CAAA;gBACX,CAAC;YACH,CAAC,CAAA;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uCAAuC;QACzC,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,15 @@
1
+ import type { AppRequest, AppResponse } from '@rudderjs/contracts';
2
+ import type { Aggregator, PulseStorage, PulseConfig } from '../types.js';
3
+ /**
4
+ * Tracks request throughput, duration (p50/p95/p99), and slow requests.
5
+ * Installs as global middleware.
6
+ */
7
+ export declare class RequestAggregator implements Aggregator {
8
+ private readonly storage;
9
+ private readonly config;
10
+ readonly name = "Request Aggregator";
11
+ constructor(storage: PulseStorage, config: PulseConfig);
12
+ register(): void;
13
+ middleware(): (req: AppRequest, res: AppResponse, next: () => Promise<void>) => Promise<void>;
14
+ }
15
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/aggregators/request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAGxE;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,UAAU;IAIhD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJzB,QAAQ,CAAC,IAAI,wBAAuB;gBAGjB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAG,WAAW;IAGvC,QAAQ,IAAI,IAAI;IAIhB,UAAU,KAKM,KAAK,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,MAAM,OAAO,CAAC,IAAI,CAAC;CAyB7E"}
@@ -0,0 +1,46 @@
1
+ import { isAsset } from '../utils.js';
2
+ /**
3
+ * Tracks request throughput, duration (p50/p95/p99), and slow requests.
4
+ * Installs as global middleware.
5
+ */
6
+ export class RequestAggregator {
7
+ storage;
8
+ config;
9
+ name = 'Request Aggregator';
10
+ constructor(storage, config) {
11
+ this.storage = storage;
12
+ this.config = config;
13
+ }
14
+ register() {
15
+ // Middleware is registered by the service provider
16
+ }
17
+ middleware() {
18
+ const storage = this.storage;
19
+ const threshold = this.config.slowRequestThreshold ?? 1000;
20
+ const ignore = this.config.path ?? 'pulse';
21
+ return async (req, res, next) => {
22
+ // Skip pulse's own routes
23
+ if (req.path.startsWith(`/${ignore}`))
24
+ return next();
25
+ // Skip Vite internals, source files, and static assets
26
+ if (isAsset(req.path))
27
+ return next();
28
+ const start = Date.now();
29
+ await next();
30
+ const duration = Date.now() - start;
31
+ // Record aggregates
32
+ storage.record('request_count', 1);
33
+ storage.record('request_duration', duration);
34
+ // Slow request entry
35
+ if (duration > threshold) {
36
+ storage.storeEntry('slow_request', {
37
+ method: req.method,
38
+ url: req.url,
39
+ path: req.path,
40
+ duration,
41
+ });
42
+ }
43
+ };
44
+ }
45
+ }
46
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/aggregators/request.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAErC;;;GAGG;AACH,MAAM,OAAO,iBAAiB;IAIT;IACA;IAJV,IAAI,GAAG,oBAAoB,CAAA;IAEpC,YACmB,OAAqB,EACrB,MAAoB;QADpB,YAAO,GAAP,OAAO,CAAc;QACrB,WAAM,GAAN,MAAM,CAAc;IACpC,CAAC;IAEJ,QAAQ;QACN,mDAAmD;IACrD,CAAC;IAED,UAAU;QACR,MAAM,OAAO,GAAK,IAAI,CAAC,OAAO,CAAA;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,IAAI,IAAI,CAAA;QAC1D,MAAM,MAAM,GAAM,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,CAAA;QAE7C,OAAO,KAAK,EAAE,GAAe,EAAE,GAAgB,EAAE,IAAyB,EAAE,EAAE;YAC5E,0BAA0B;YAC1B,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,MAAM,EAAE,CAAC;gBAAE,OAAO,IAAI,EAAE,CAAA;YACpD,uDAAuD;YACvD,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,EAAE,CAAA;YAEpC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACxB,MAAM,IAAI,EAAE,CAAA;YACZ,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAA;YAEnC,oBAAoB;YACpB,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,CAAA;YAClC,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAA;YAE5C,qBAAqB;YACrB,IAAI,QAAQ,GAAG,SAAS,EAAE,CAAC;gBACzB,OAAO,CAAC,UAAU,CAAC,cAAc,EAAE;oBACjC,MAAM,EAAI,GAAG,CAAC,MAAM;oBACpB,GAAG,EAAO,GAAG,CAAC,GAAG;oBACjB,IAAI,EAAM,GAAG,CAAC,IAAI;oBAClB,QAAQ;iBACT,CAAC,CAAA;YACJ,CAAC;QACH,CAAC,CAAA;IACH,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ import type { Aggregator, PulseStorage } from '../types.js';
2
+ /**
3
+ * Periodically collects server resource metrics — CPU usage and memory.
4
+ */
5
+ export declare class ServerAggregator implements Aggregator {
6
+ private readonly storage;
7
+ private readonly intervalMs;
8
+ readonly name = "Server Aggregator";
9
+ constructor(storage: PulseStorage, intervalMs?: number);
10
+ register(): void;
11
+ private collect;
12
+ }
13
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/aggregators/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE3D;;GAEG;AACH,qBAAa,gBAAiB,YAAW,UAAU;IAI/C,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJ7B,QAAQ,CAAC,IAAI,uBAAsB;gBAGhB,OAAO,EAAE,YAAY,EACrB,UAAU,GAAE,MAAe;IAG9C,QAAQ,IAAI,IAAI;IAOhB,OAAO,CAAC,OAAO;CAiBhB"}
@@ -0,0 +1,35 @@
1
+ import { cpus, totalmem, freemem } from 'node:os';
2
+ /**
3
+ * Periodically collects server resource metrics — CPU usage and memory.
4
+ */
5
+ export class ServerAggregator {
6
+ storage;
7
+ intervalMs;
8
+ name = 'Server Aggregator';
9
+ constructor(storage, intervalMs = 15_000) {
10
+ this.storage = storage;
11
+ this.intervalMs = intervalMs;
12
+ }
13
+ register() {
14
+ const timer = setInterval(() => this.collect(), this.intervalMs);
15
+ timer.unref();
16
+ // Collect once immediately
17
+ this.collect();
18
+ }
19
+ collect() {
20
+ // CPU — average load across cores (0–100)
21
+ const cores = cpus();
22
+ const cpuPercent = cores.reduce((sum, core) => {
23
+ const total = Object.values(core.times).reduce((a, b) => a + b, 0);
24
+ const idle = core.times.idle;
25
+ return sum + ((total - idle) / total) * 100;
26
+ }, 0) / cores.length;
27
+ this.storage.record('server_cpu', Math.round(cpuPercent * 100) / 100);
28
+ // Memory — used percentage
29
+ const total = totalmem();
30
+ const free = freemem();
31
+ const usedPercent = ((total - free) / total) * 100;
32
+ this.storage.record('server_memory', Math.round(usedPercent * 100) / 100);
33
+ }
34
+ }
35
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/aggregators/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAGjD;;GAEG;AACH,MAAM,OAAO,gBAAgB;IAIR;IACA;IAJV,IAAI,GAAG,mBAAmB,CAAA;IAEnC,YACmB,OAAqB,EACrB,aAAqB,MAAM;QAD3B,YAAO,GAAP,OAAO,CAAc;QACrB,eAAU,GAAV,UAAU,CAAiB;IAC3C,CAAC;IAEJ,QAAQ;QACN,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;QAChE,KAAK,CAAC,KAAK,EAAE,CAAA;QACb,2BAA2B;QAC3B,IAAI,CAAC,OAAO,EAAE,CAAA;IAChB,CAAC;IAEO,OAAO;QACb,0CAA0C;QAC1C,MAAM,KAAK,GAAG,IAAI,EAAE,CAAA;QACpB,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;YAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;YAClE,MAAM,IAAI,GAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAA;YAC7B,OAAO,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAA;QAC7C,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAA;QAEpB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAA;QAErE,2BAA2B;QAC3B,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAA;QACxB,MAAM,IAAI,GAAI,OAAO,EAAE,CAAA;QACvB,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAA;QAClD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAA;IAC3E,CAAC;CACF"}
@@ -0,0 +1,17 @@
1
+ import type { AppRequest, AppResponse } from '@rudderjs/contracts';
2
+ import type { Aggregator, PulseStorage } from '../types.js';
3
+ /**
4
+ * Tracks active (unique) users per minute bucket.
5
+ * Identifies users by session cookie, auth header, or IP.
6
+ */
7
+ export declare class UserAggregator implements Aggregator {
8
+ private readonly storage;
9
+ readonly name = "User Aggregator";
10
+ private readonly seen;
11
+ private currentMinute;
12
+ constructor(storage: PulseStorage);
13
+ register(): void;
14
+ middleware(): (req: AppRequest, _res: AppResponse, next: () => Promise<void>) => Promise<void>;
15
+ private identifyUser;
16
+ }
17
+ //# sourceMappingURL=user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/aggregators/user.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG3D;;;GAGG;AACH,qBAAa,cAAe,YAAW,UAAU;IAKnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,QAAQ,CAAC,IAAI,qBAAoB;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,aAAa,CAAI;gBAEI,OAAO,EAAE,YAAY;IAElD,QAAQ,IAAI,IAAI;IAIhB,UAAU,KAGM,KAAK,UAAU,EAAE,MAAM,WAAW,EAAE,MAAM,MAAM,OAAO,CAAC,IAAI,CAAC;IAoB7E,OAAO,CAAC,YAAY;CAWrB"}
@@ -0,0 +1,48 @@
1
+ import { isAsset } from '../utils.js';
2
+ /**
3
+ * Tracks active (unique) users per minute bucket.
4
+ * Identifies users by session cookie, auth header, or IP.
5
+ */
6
+ export class UserAggregator {
7
+ storage;
8
+ name = 'User Aggregator';
9
+ seen = new Set();
10
+ currentMinute = 0;
11
+ constructor(storage) {
12
+ this.storage = storage;
13
+ }
14
+ register() {
15
+ // Middleware is registered by the service provider
16
+ }
17
+ middleware() {
18
+ const storage = this.storage;
19
+ return async (req, _res, next) => {
20
+ // Skip Vite internals, source files, and static assets
21
+ if (isAsset(req.path))
22
+ return next();
23
+ const minute = Math.floor(Date.now() / 60_000);
24
+ if (minute !== this.currentMinute) {
25
+ this.seen.clear();
26
+ this.currentMinute = minute;
27
+ }
28
+ const userId = this.identifyUser(req);
29
+ if (!this.seen.has(userId)) {
30
+ this.seen.add(userId);
31
+ storage.record('active_users', 1);
32
+ }
33
+ return next();
34
+ };
35
+ }
36
+ identifyUser(req) {
37
+ // Prefer auth user, then session, then IP
38
+ const auth = req.headers['authorization'];
39
+ if (auth)
40
+ return `auth:${auth.slice(0, 40)}`;
41
+ const cookie = req.headers['cookie'] ?? '';
42
+ const session = cookie.match(/(?:^|;\s*)session=([^;]+)/)?.[1];
43
+ if (session)
44
+ return `session:${session}`;
45
+ return `ip:${req.headers['x-forwarded-for']?.split(',')[0]?.trim() ?? req.headers['x-real-ip'] ?? 'unknown'}`;
46
+ }
47
+ }
48
+ //# sourceMappingURL=user.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.js","sourceRoot":"","sources":["../../src/aggregators/user.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAErC;;;GAGG;AACH,MAAM,OAAO,cAAc;IAKI;IAJpB,IAAI,GAAG,iBAAiB,CAAA;IAChB,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IACjC,aAAa,GAAG,CAAC,CAAA;IAEzB,YAA6B,OAAqB;QAArB,YAAO,GAAP,OAAO,CAAc;IAAG,CAAC;IAEtD,QAAQ;QACN,mDAAmD;IACrD,CAAC;IAED,UAAU;QACR,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;QAE5B,OAAO,KAAK,EAAE,GAAe,EAAE,IAAiB,EAAE,IAAyB,EAAE,EAAE;YAC7E,uDAAuD;YACvD,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,EAAE,CAAA;YAEpC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,CAAA;YAC9C,IAAI,MAAM,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC;gBAClC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;gBACjB,IAAI,CAAC,aAAa,GAAG,MAAM,CAAA;YAC7B,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;YACrC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;gBACrB,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAA;YACnC,CAAC;YAED,OAAO,IAAI,EAAE,CAAA;QACf,CAAC,CAAA;IACH,CAAC;IAEO,YAAY,CAAC,GAAe;QAClC,0CAA0C;QAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;QACzC,IAAI,IAAI;YAAE,OAAO,QAAQ,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAA;QAE5C,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;QAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;QAC9D,IAAI,OAAO;YAAE,OAAO,WAAW,OAAO,EAAE,CAAA;QAExC,OAAO,MAAM,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,SAAS,EAAE,CAAA;IAC/G,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ import type { PulseStorage, PulseConfig } from '../types.js';
2
+ /**
3
+ * Register all Pulse API routes on the router.
4
+ */
5
+ export declare function registerRoutes(storage: PulseStorage, config: PulseConfig): Promise<void>;
6
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAc,MAAM,aAAa,CAAA;AAexE;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAG,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAkJf"}