svoose 0.1.6 → 0.1.8

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 (44) hide show
  1. package/README.md +78 -10
  2. package/dist/index.d.ts +3 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +3 -3
  6. package/dist/metrics/index.d.ts +2 -1
  7. package/dist/metrics/index.d.ts.map +1 -1
  8. package/dist/metrics/index.js +1 -1
  9. package/dist/metrics/index.js.map +3 -3
  10. package/dist/metrics/metric.d.ts +37 -0
  11. package/dist/metrics/metric.d.ts.map +1 -1
  12. package/dist/metrics/metric.js +1 -1
  13. package/dist/metrics/metric.js.map +3 -3
  14. package/dist/metrics/typed.d.ts +18 -0
  15. package/dist/metrics/typed.d.ts.map +1 -0
  16. package/dist/metrics/typed.js +2 -0
  17. package/dist/metrics/typed.js.map +7 -0
  18. package/dist/observe/observe.svelte.d.ts.map +1 -1
  19. package/dist/observe/observe.svelte.js +1 -1
  20. package/dist/observe/observe.svelte.js.map +2 -2
  21. package/dist/observe/vitals.d.ts.map +1 -1
  22. package/dist/observe/vitals.js +1 -1
  23. package/dist/observe/vitals.js.map +2 -2
  24. package/dist/transport/beacon.d.ts +14 -0
  25. package/dist/transport/beacon.d.ts.map +1 -0
  26. package/dist/transport/beacon.js +2 -0
  27. package/dist/transport/beacon.js.map +7 -0
  28. package/dist/transport/fetch.d.ts +2 -2
  29. package/dist/transport/fetch.d.ts.map +1 -1
  30. package/dist/transport/fetch.js +1 -1
  31. package/dist/transport/fetch.js.map +3 -3
  32. package/dist/transport/hybrid.d.ts +13 -0
  33. package/dist/transport/hybrid.d.ts.map +1 -0
  34. package/dist/transport/hybrid.js +2 -0
  35. package/dist/transport/hybrid.js.map +7 -0
  36. package/dist/transport/index.d.ts +3 -1
  37. package/dist/transport/index.d.ts.map +1 -1
  38. package/dist/transport/index.js +1 -1
  39. package/dist/transport/index.js.map +3 -3
  40. package/dist/transport/transport.d.ts +1 -1
  41. package/dist/transport/transport.d.ts.map +1 -1
  42. package/dist/types/index.d.ts +17 -0
  43. package/dist/types/index.d.ts.map +1 -1
  44. package/package.json +1 -1
package/README.md CHANGED
@@ -2,13 +2,14 @@
2
2
 
3
3
  > Svelte + Goose = **svoose** — the goose that sees everything
4
4
 
5
- Lightweight observability + state machines for Svelte 5. Zero dependencies. Tree-shakeable. **< 5KB gzipped** (core ~3.5KB).
5
+ Lightweight observability + state machines for Svelte 5. Zero dependencies. Tree-shakeable. **~5.3KB gzipped** (core ~3.6KB).
6
6
 
7
7
  ## Features
8
8
 
9
9
  - **Web Vitals** — CLS, LCP, FID, INP, FCP, TTFB (no external deps)
10
10
  - **Error Tracking** — global errors + unhandled rejections
11
- - **Custom Metrics** — `metric()` for custom analytics events (v0.1.6+)
11
+ - **Custom Metrics** — `metric()`, `counter()`, `gauge()`, `histogram()` (v0.1.6+)
12
+ - **Beacon Transport** — reliable delivery on page close with auto-chunking (v0.1.8+)
12
13
  - **Session Tracking** — automatic sessionId with timeout (v0.1.5+)
13
14
  - **Sampling** — per-event-type rate limiting (v0.1.3+)
14
15
  - **State Machines** — minimal FSM with TypeScript inference
@@ -99,7 +100,7 @@ const cleanup = observe({
99
100
  cleanup();
100
101
  ```
101
102
 
102
- > **Note**: If neither `endpoint` nor `transport` is provided, defaults to `endpoint: '/api/observe'`.
103
+ > **Note**: If neither `endpoint` nor `transport` is provided, defaults to `endpoint: '/api/metrics'`.
103
104
 
104
105
  #### Sampling (v0.1.3+)
105
106
 
@@ -214,6 +215,45 @@ metric('button_clicked', { id: 'submit-btn' });
214
215
  metric('feature_used', { name: 'dark_mode', enabled: true });
215
216
  ```
216
217
 
218
+ ##### Metric Helpers (v0.1.7+)
219
+
220
+ Typed helpers for common metric patterns:
221
+
222
+ ```typescript
223
+ import { counter, gauge, histogram } from 'svoose';
224
+
225
+ // Counter — increments (default value: 1)
226
+ counter('page_views');
227
+ counter('items_purchased', 3, { category: 'electronics' });
228
+
229
+ // Gauge — point-in-time values
230
+ gauge('active_users', 42);
231
+ gauge('memory_usage_mb', 256, { heap: 'old' });
232
+
233
+ // Histogram — distribution values
234
+ histogram('response_time_ms', 123);
235
+ histogram('payload_size', 4096, { route: '/api/data' });
236
+ ```
237
+
238
+ All helpers emit events with top-level `metricKind` and `value` fields for easy backend processing.
239
+
240
+ ##### Typed Metrics (v0.1.7+)
241
+
242
+ Full TypeScript autocomplete for metric names and data shapes:
243
+
244
+ ```typescript
245
+ import { createTypedMetric } from 'svoose';
246
+
247
+ const track = createTypedMetric<{
248
+ checkout_started: { step: number; cartTotal: number };
249
+ button_clicked: { id: string };
250
+ }>();
251
+
252
+ track('checkout_started', { step: 1, cartTotal: 99.99 }); // ✅ autocomplete
253
+ track('button_clicked', { id: 'submit' }); // ✅
254
+ track('unknown_event', {}); // ❌ TypeScript error
255
+ ```
256
+
217
257
  Events are automatically batched with other metrics. You can control the sampling rate:
218
258
 
219
259
  ```typescript
@@ -227,7 +267,7 @@ observe({
227
267
  });
228
268
  ```
229
269
 
230
- **Buffer behavior**: If `metric()` is called before `observe()`, events are buffered (max 100). They're automatically flushed when `observe()` initializes.
270
+ **Buffer behavior**: If `metric()` / `counter()` / `gauge()` / `histogram()` is called before `observe()`, events are buffered (max 100). They're automatically flushed when `observe()` initializes.
231
271
 
232
272
  ### `createMachine(config)`
233
273
 
@@ -249,7 +289,7 @@ const machine = createMachine({
249
289
  },
250
290
  });
251
291
 
252
- // State & context (reactive in Svelte 5)
292
+ // State & context (use useMachine() from svoose/svelte for reactivity)
253
293
  machine.state; // 'off'
254
294
  machine.context; // { count: 0 }
255
295
 
@@ -374,18 +414,46 @@ observe({
374
414
  });
375
415
  ```
376
416
 
417
+ #### Beacon & Hybrid Transport (v0.1.8+)
418
+
419
+ Prevent data loss on page close with `sendBeacon`:
420
+
421
+ ```typescript
422
+ import { createBeaconTransport, createHybridTransport } from 'svoose';
423
+
424
+ // Beacon only — guaranteed delivery on page close
425
+ observe({
426
+ transport: createBeaconTransport('/api/metrics', {
427
+ maxPayloadSize: 60000, // auto-chunks if exceeded (default: 60KB)
428
+ }),
429
+ });
430
+
431
+ // Hybrid (recommended for production)
432
+ // Uses fetch normally, switches to beacon on page close
433
+ const transport = createHybridTransport('/api/metrics', {
434
+ default: 'fetch', // normal operation
435
+ onUnload: 'beacon', // page close / tab switch
436
+ headers: { 'Authorization': 'Bearer xxx' },
437
+ });
438
+
439
+ observe({ transport });
440
+
441
+ // Cleanup when done (removes lifecycle listeners)
442
+ transport.destroy();
443
+ ```
444
+
377
445
  ## Bundle Size
378
446
 
379
447
  Tree-shakeable — pay only for what you use:
380
448
 
381
449
  | Import | Size (gzip) |
382
450
  |--------|-------------|
383
- | `observe()` + vitals + errors + metrics | ~3.5 KB |
451
+ | `observe()` + vitals + errors + metrics | ~3.6 KB |
384
452
  | `createMachine()` only | ~0.8 KB |
385
- | Full bundle (v0.1.x) | ~4.5 KB |
453
+ | Full bundle (v0.1.x) | ~5.3 KB |
386
454
  | Full production (v0.2.0+) | ~6 KB |
387
455
 
388
- > Most apps only need `observe()` core (~3.5 KB). Compare: Sentry ~20KB, PostHog ~40KB.
456
+ > Most apps only need `observe()` core (~3.6 KB). Compare: Sentry ~20KB, PostHog ~40KB.
389
457
 
390
458
  ## TypeScript
391
459
 
@@ -534,8 +602,8 @@ const machine = createMachine({
534
602
  - **v0.1.4** ✅ — Hotfix (missing sampling.js)
535
603
  - **v0.1.5** ✅ — Session Tracking + CLS Session Windows fix
536
604
  - **v0.1.6** ✅ — Custom metrics (`metric()` API)
537
- - **v0.1.7** — Extended Metrics (counter/gauge/histogram + typed API)
538
- - **v0.1.8** — Beacon + Hybrid Transport
605
+ - **v0.1.7** — Extended Metrics (counter/gauge/histogram + typed API)
606
+ - **v0.1.8** — Beacon + Hybrid Transport
539
607
  - **v0.1.9** — Retry Logic
540
608
  - **v0.1.10** — Privacy Utilities
541
609
  - **v0.2.0** — Production-Ready Observability + Bundle Restructure (modular entry points)
package/dist/index.d.ts CHANGED
@@ -11,10 +11,10 @@ export { observeErrors, registerMachineContext, unregisterMachineContext, } from
11
11
  export type { ObserveErrorEvent, ErrorEvent, UnhandledRejectionEvent, } from './observe/index.js';
12
12
  export { observeCLS, observeLCP, observeFID, observeINP, observeFCP, observeTTFB, vitalObservers, } from './observe/index.js';
13
13
  export type { Metric, MetricName, MetricRating } from './observe/index.js';
14
- export { createFetchTransport, createConsoleTransport } from './transport/index.js';
15
- export type { Transport, TransportOptions } from './transport/index.js';
14
+ export { createFetchTransport, createConsoleTransport, createBeaconTransport, createHybridTransport } from './transport/index.js';
15
+ export type { Transport, TransportOptions, BeaconTransportOptions, HybridTransportOptions, HybridTransport } from './transport/index.js';
16
16
  export { createMachine, createEvent } from './machine/index.js';
17
17
  export type { MachineConfig, Machine, EventObject, StateNode, TransitionConfig, InferStates, InferEvents, InferContext, } from './machine/index.js';
18
- export { metric } from './metrics/index.js';
18
+ export { metric, counter, gauge, histogram, createTypedMetric } from './metrics/index.js';
19
19
  export type { VitalEvent, TransitionEvent, CustomMetricEvent, ObserveEvent, } from './types/index.js';
20
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKzD,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAKrG,OAAO,EACL,aAAa,EACb,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,iBAAiB,EACjB,UAAU,EACV,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EACL,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAK3E,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACpF,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAKxE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAChE,YAAY,EACV,aAAa,EACb,OAAO,EACP,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,YAAY,GACb,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAK5C,YAAY,EACV,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,YAAY,GACb,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKzD,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAKrG,OAAO,EACL,aAAa,EACb,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,iBAAiB,EACjB,UAAU,EACV,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EACL,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAK3E,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClI,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAKzI,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAChE,YAAY,EACV,aAAa,EACb,OAAO,EACP,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,WAAW,EACX,YAAY,GACb,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAK1F,YAAY,EACV,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,YAAY,GACb,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{observe as t}from"./observe/index.js";import{createSampler as n,eventTypeToSamplingType as s}from"./observe/index.js";import{observeErrors as i,registerMachineContext as a,unregisterMachineContext as v}from"./observe/index.js";import{observeCLS as x,observeLCP as f,observeFID as c,observeINP as b,observeFCP as E,observeTTFB as T,vitalObservers as y}from"./observe/index.js";import{createFetchTransport as d,createConsoleTransport as g}from"./transport/index.js";import{createMachine as j,createEvent as M}from"./machine/index.js";import{metric as h}from"./metrics/index.js";export{g as createConsoleTransport,M as createEvent,d as createFetchTransport,j as createMachine,n as createSampler,s as eventTypeToSamplingType,h as metric,t as observe,x as observeCLS,i as observeErrors,E as observeFCP,c as observeFID,b as observeINP,f as observeLCP,T as observeTTFB,a as registerMachineContext,v as unregisterMachineContext,y as vitalObservers};
1
+ import{observe as t}from"./observe/index.js";import{createSampler as n,eventTypeToSamplingType as s}from"./observe/index.js";import{observeErrors as i,registerMachineContext as a,unregisterMachineContext as v}from"./observe/index.js";import{observeCLS as m,observeLCP as x,observeFID as b,observeINP as f,observeFCP as T,observeTTFB as y,vitalObservers as d}from"./observe/index.js";import{createFetchTransport as g,createConsoleTransport as C,createBeaconTransport as l,createHybridTransport as M}from"./transport/index.js";import{createMachine as h,createEvent as j}from"./machine/index.js";import{metric as I,counter as u,gauge as F,histogram as B,createTypedMetric as H}from"./metrics/index.js";export{u as counter,l as createBeaconTransport,C as createConsoleTransport,j as createEvent,g as createFetchTransport,M as createHybridTransport,h as createMachine,n as createSampler,H as createTypedMetric,s as eventTypeToSamplingType,F as gauge,B as histogram,I as metric,t as observe,m as observeCLS,i as observeErrors,T as observeFCP,b as observeFID,f as observeINP,x as observeLCP,y as observeTTFB,a as registerMachineContext,v as unregisterMachineContext,d as vitalObservers};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["/**\n * \uD83E\uDEBF svoose - Observability + State Machines for Svelte 5\n *\n * @packageDocumentation\n */\n\n// ============================================\n// Core Observability\n// ============================================\nexport { observe } from './observe/index.js';\nexport type { ObserveOptions } from './observe/index.js';\n\n// ============================================\n// Sampling\n// ============================================\nexport { createSampler, eventTypeToSamplingType } from './observe/index.js';\nexport type { SamplingConfig, SamplingOption, SamplingEventType, Sampler } from './observe/index.js';\n\n// ============================================\n// Error Tracking\n// ============================================\nexport {\n observeErrors,\n registerMachineContext,\n unregisterMachineContext,\n} from './observe/index.js';\nexport type {\n ObserveErrorEvent,\n ErrorEvent,\n UnhandledRejectionEvent,\n} from './observe/index.js';\n\n// ============================================\n// Web Vitals\n// ============================================\nexport {\n observeCLS,\n observeLCP,\n observeFID,\n observeINP,\n observeFCP,\n observeTTFB,\n vitalObservers,\n} from './observe/index.js';\nexport type { Metric, MetricName, MetricRating } from './observe/index.js';\n\n// ============================================\n// Transport\n// ============================================\nexport { createFetchTransport, createConsoleTransport } from './transport/index.js';\nexport type { Transport, TransportOptions } from './transport/index.js';\n\n// ============================================\n// State Machines\n// ============================================\nexport { createMachine, createEvent } from './machine/index.js';\nexport type {\n MachineConfig,\n Machine,\n EventObject,\n StateNode,\n TransitionConfig,\n InferStates,\n InferEvents,\n InferContext,\n} from './machine/index.js';\n\n// ============================================\n// Custom Metrics\n// ============================================\nexport { metric } from './metrics/index.js';\n\n// ============================================\n// Shared Types\n// ============================================\nexport type {\n VitalEvent,\n TransitionEvent,\n CustomMetricEvent,\n ObserveEvent,\n} from './types/index.js';\n"],
5
- "mappings": "AASA,OAAS,WAAAA,MAAe,qBAMxB,OAAS,iBAAAC,EAAe,2BAAAC,MAA+B,qBAMvD,OACE,iBAAAC,EACA,0BAAAC,EACA,4BAAAC,MACK,qBAUP,OACE,cAAAC,EACA,cAAAC,EACA,cAAAC,EACA,cAAAC,EACA,cAAAC,EACA,eAAAC,EACA,kBAAAC,MACK,qBAMP,OAAS,wBAAAC,EAAsB,0BAAAC,MAA8B,uBAM7D,OAAS,iBAAAC,EAAe,eAAAC,MAAmB,qBAe3C,OAAS,UAAAC,MAAc",
6
- "names": ["observe", "createSampler", "eventTypeToSamplingType", "observeErrors", "registerMachineContext", "unregisterMachineContext", "observeCLS", "observeLCP", "observeFID", "observeINP", "observeFCP", "observeTTFB", "vitalObservers", "createFetchTransport", "createConsoleTransport", "createMachine", "createEvent", "metric"]
4
+ "sourcesContent": ["/**\n * \uD83E\uDEBF svoose - Observability + State Machines for Svelte 5\n *\n * @packageDocumentation\n */\n\n// ============================================\n// Core Observability\n// ============================================\nexport { observe } from './observe/index.js';\nexport type { ObserveOptions } from './observe/index.js';\n\n// ============================================\n// Sampling\n// ============================================\nexport { createSampler, eventTypeToSamplingType } from './observe/index.js';\nexport type { SamplingConfig, SamplingOption, SamplingEventType, Sampler } from './observe/index.js';\n\n// ============================================\n// Error Tracking\n// ============================================\nexport {\n observeErrors,\n registerMachineContext,\n unregisterMachineContext,\n} from './observe/index.js';\nexport type {\n ObserveErrorEvent,\n ErrorEvent,\n UnhandledRejectionEvent,\n} from './observe/index.js';\n\n// ============================================\n// Web Vitals\n// ============================================\nexport {\n observeCLS,\n observeLCP,\n observeFID,\n observeINP,\n observeFCP,\n observeTTFB,\n vitalObservers,\n} from './observe/index.js';\nexport type { Metric, MetricName, MetricRating } from './observe/index.js';\n\n// ============================================\n// Transport\n// ============================================\nexport { createFetchTransport, createConsoleTransport, createBeaconTransport, createHybridTransport } from './transport/index.js';\nexport type { Transport, TransportOptions, BeaconTransportOptions, HybridTransportOptions, HybridTransport } from './transport/index.js';\n\n// ============================================\n// State Machines\n// ============================================\nexport { createMachine, createEvent } from './machine/index.js';\nexport type {\n MachineConfig,\n Machine,\n EventObject,\n StateNode,\n TransitionConfig,\n InferStates,\n InferEvents,\n InferContext,\n} from './machine/index.js';\n\n// ============================================\n// Custom Metrics\n// ============================================\nexport { metric, counter, gauge, histogram, createTypedMetric } from './metrics/index.js';\n\n// ============================================\n// Shared Types\n// ============================================\nexport type {\n VitalEvent,\n TransitionEvent,\n CustomMetricEvent,\n ObserveEvent,\n} from './types/index.js';\n"],
5
+ "mappings": "AASA,OAAS,WAAAA,MAAe,qBAMxB,OAAS,iBAAAC,EAAe,2BAAAC,MAA+B,qBAMvD,OACE,iBAAAC,EACA,0BAAAC,EACA,4BAAAC,MACK,qBAUP,OACE,cAAAC,EACA,cAAAC,EACA,cAAAC,EACA,cAAAC,EACA,cAAAC,EACA,eAAAC,EACA,kBAAAC,MACK,qBAMP,OAAS,wBAAAC,EAAsB,0BAAAC,EAAwB,yBAAAC,EAAuB,yBAAAC,MAA6B,uBAM3G,OAAS,iBAAAC,EAAe,eAAAC,MAAmB,qBAe3C,OAAS,UAAAC,EAAQ,WAAAC,EAAS,SAAAC,EAAO,aAAAC,EAAW,qBAAAC,MAAyB",
6
+ "names": ["observe", "createSampler", "eventTypeToSamplingType", "observeErrors", "registerMachineContext", "unregisterMachineContext", "observeCLS", "observeLCP", "observeFID", "observeINP", "observeFCP", "observeTTFB", "vitalObservers", "createFetchTransport", "createConsoleTransport", "createBeaconTransport", "createHybridTransport", "createMachine", "createEvent", "metric", "counter", "gauge", "histogram", "createTypedMetric"]
7
7
  }
@@ -1,5 +1,6 @@
1
1
  /**
2
2
  * Metrics module exports
3
3
  */
4
- export { metric, setMetricEmitter, getMetricEmitter, _getPendingEventsCount, _clearPendingEvents, } from './metric.js';
4
+ export { metric, counter, gauge, histogram, setMetricEmitter, getMetricEmitter, _getPendingEventsCount, _clearPendingEvents, } from './metric.js';
5
+ export { createTypedMetric } from './typed.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/metrics/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,MAAM,EACN,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/metrics/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,MAAM,EACN,OAAO,EACP,KAAK,EACL,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
@@ -1,2 +1,2 @@
1
- import{metric as r,setMetricEmitter as i,getMetricEmitter as n,_getPendingEventsCount as c,_clearPendingEvents as g}from"./metric.js";export{g as _clearPendingEvents,c as _getPendingEventsCount,n as getMetricEmitter,r as metric,i as setMetricEmitter};
1
+ import{metric as r,counter as i,gauge as n,histogram as c,setMetricEmitter as g,getMetricEmitter as o,_getPendingEventsCount as m,_clearPendingEvents as a}from"./metric.js";import{createTypedMetric as E}from"./typed.js";export{a as _clearPendingEvents,m as _getPendingEventsCount,i as counter,E as createTypedMetric,n as gauge,o as getMetricEmitter,c as histogram,r as metric,g as setMetricEmitter};
2
2
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/metrics/index.ts"],
4
- "sourcesContent": ["/**\n * Metrics module exports\n */\n\nexport {\n metric,\n setMetricEmitter,\n getMetricEmitter,\n _getPendingEventsCount,\n _clearPendingEvents,\n} from './metric.js';\n"],
5
- "mappings": "AAIA,OACE,UAAAA,EACA,oBAAAC,EACA,oBAAAC,EACA,0BAAAC,EACA,uBAAAC,MACK",
6
- "names": ["metric", "setMetricEmitter", "getMetricEmitter", "_getPendingEventsCount", "_clearPendingEvents"]
4
+ "sourcesContent": ["/**\n * Metrics module exports\n */\n\nexport {\n metric,\n counter,\n gauge,\n histogram,\n setMetricEmitter,\n getMetricEmitter,\n _getPendingEventsCount,\n _clearPendingEvents,\n} from './metric.js';\n\nexport { createTypedMetric } from './typed.js';\n"],
5
+ "mappings": "AAIA,OACE,UAAAA,EACA,WAAAC,EACA,SAAAC,EACA,aAAAC,EACA,oBAAAC,EACA,oBAAAC,EACA,0BAAAC,EACA,uBAAAC,MACK,cAEP,OAAS,qBAAAC,MAAyB",
6
+ "names": ["metric", "counter", "gauge", "histogram", "setMetricEmitter", "getMetricEmitter", "_getPendingEventsCount", "_clearPendingEvents", "createTypedMetric"]
7
7
  }
@@ -31,6 +31,43 @@ export declare function setMetricEmitter(emit: ((event: ObserveEvent) => void) |
31
31
  */
32
32
  export declare function getMetricEmitter(): ((event: ObserveEvent) => void) | null;
33
33
  export declare function metric(name: string, data?: Record<string, unknown>): void;
34
+ /**
35
+ * Increment a counter metric
36
+ *
37
+ * @param name - Counter name
38
+ * @param value - Increment amount (default: 1)
39
+ * @param metadata - Optional metadata
40
+ *
41
+ * @example
42
+ * counter('page_views');
43
+ * counter('items_purchased', 3);
44
+ * counter('api_calls', 1, { endpoint: '/users' });
45
+ */
46
+ export declare function counter(name: string, value?: number, metadata?: Record<string, unknown>): void;
47
+ /**
48
+ * Set a gauge metric (point-in-time value)
49
+ *
50
+ * @param name - Gauge name
51
+ * @param value - Current value
52
+ * @param metadata - Optional metadata
53
+ *
54
+ * @example
55
+ * gauge('active_users', 42);
56
+ * gauge('memory_usage_mb', 256, { heap: 'old' });
57
+ */
58
+ export declare function gauge(name: string, value: number, metadata?: Record<string, unknown>): void;
59
+ /**
60
+ * Record a histogram metric (distribution value)
61
+ *
62
+ * @param name - Histogram name
63
+ * @param value - Observed value
64
+ * @param metadata - Optional metadata
65
+ *
66
+ * @example
67
+ * histogram('response_time_ms', 123);
68
+ * histogram('payload_size', 4096, { route: '/api/data' });
69
+ */
70
+ export declare function histogram(name: string, value: number, metadata?: Record<string, unknown>): void;
34
71
  /**
35
72
  * Get number of pending events (for testing)
36
73
  * @internal
@@ -1 +1 @@
1
- {"version":3,"file":"metric.d.ts","sourceRoot":"","sources":["../../src/metrics/metric.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAqB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAWzE;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAUnF;AAED;;;;;;;;;;;;;GAaG;AACH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,IAAI,CAEzE;AAkBD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI,CAuB7E;AAMD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
1
+ {"version":3,"file":"metric.d.ts","sourceRoot":"","sources":["../../src/metrics/metric.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAqB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAWzE;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAUnF;AAED;;;;;;;;;;;;;GAaG;AACH;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,IAAI,CAEzE;AAkBD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI,CAE7E;AAkCD;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAEjG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAE3F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAE/F;AAMD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C"}
@@ -1,2 +1,2 @@
1
- let t=null;const e=[];function i(r){if(t=r,t&&e.length>0){const o=e.splice(0,e.length);for(const n of o)t(n)}}function v(){return t}function s(){try{return typeof process<"u",import.meta.env?.DEV===!0}catch{return!1}}function u(r,o={}){const n={type:"custom",name:r,data:o,timestamp:Date.now()};if(t)t(n);else{if(e.length>=100){s()&&console.warn("[svoose] metric() buffer full (100 events). Call observe() to start sending events. New events are being dropped.");return}e.push(n)}}function c(){return e.length}function l(){e.length=0}export{l as _clearPendingEvents,c as _getPendingEventsCount,v as getMetricEmitter,u as metric,i as setMetricEmitter};
1
+ let o=null;const r=[];function v(e){if(o=e,o&&r.length>0){const n=r.splice(0,r.length);for(const t of n)o(t)}}function d(){return o}function c(){try{return typeof process<"u",import.meta.env?.DEV===!0}catch{return!1}}function g(e,n={}){s({type:"custom",name:e,data:n,timestamp:Date.now()})}function s(e){if(o)o(e);else{if(r.length>=100){c()&&console.warn("[svoose] metric() buffer full (100 events). Call observe() to start sending events. New events are being dropped.");return}r.push(e)}}function i(e,n,t,u={}){s({type:"custom",name:e,metricKind:n,value:t,data:u,timestamp:Date.now()})}function f(e,n=1,t){i(e,"counter",n,t)}function m(e,n,t){i(e,"gauge",n,t)}function l(e,n,t){i(e,"histogram",n,t)}function p(){return r.length}function a(){r.length=0}export{a as _clearPendingEvents,p as _getPendingEventsCount,f as counter,m as gauge,d as getMetricEmitter,l as histogram,g as metric,v as setMetricEmitter};
2
2
  //# sourceMappingURL=metric.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/metrics/metric.ts"],
4
- "sourcesContent": ["/**\n * Custom metrics module\n *\n * Provides metric() function for sending custom events with pending buffer support.\n */\n\nimport type { CustomMetricEvent, ObserveEvent } from '../types/index.js';\n\n// Maximum pending events before dropping (with warning in dev)\nconst MAX_PENDING_EVENTS = 100;\n\n// Emitter function set by observe()\nlet emitter: ((event: ObserveEvent) => void) | null = null;\n\n// Pending events buffer for events sent before observe() is initialized\nconst pendingEvents: CustomMetricEvent[] = [];\n\n/**\n * Set the metric emitter function\n * Called by observe() to wire up the metric system\n *\n * @param emit - The emit function from observe(), or null to disconnect\n */\nexport function setMetricEmitter(emit: ((event: ObserveEvent) => void) | null): void {\n emitter = emit;\n\n // Flush pending events when emitter is set\n if (emitter && pendingEvents.length > 0) {\n const events = pendingEvents.splice(0, pendingEvents.length);\n for (const event of events) {\n emitter(event);\n }\n }\n}\n\n/**\n * Send a custom metric event\n *\n * Events are automatically batched with other metrics and sent to your backend.\n * If called before observe() is initialized, events are buffered (max 100).\n *\n * @param name - Metric name (e.g., 'checkout_started', 'button_clicked')\n * @param data - Optional data payload\n *\n * @example\n * metric('checkout_started', { step: 1, cartTotal: 99.99 });\n * metric('button_clicked', { id: 'submit-btn' });\n * metric('feature_used', { name: 'dark_mode', enabled: true });\n */\n/**\n * Get the current metric emitter function (for internal use)\n * @internal\n */\nexport function getMetricEmitter(): ((event: ObserveEvent) => void) | null {\n return emitter;\n}\n\ndeclare const process: { env: Record<string, string | undefined> } | undefined;\n\nfunction isDev(): boolean {\n try {\n if (typeof process !== 'undefined' && process?.env?.NODE_ENV === 'development') {\n return true;\n }\n if ((import.meta as any).env?.DEV === true) {\n return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\nexport function metric(name: string, data: Record<string, unknown> = {}): void {\n const event: CustomMetricEvent = {\n type: 'custom',\n name,\n data,\n timestamp: Date.now(),\n };\n\n if (emitter) {\n emitter(event);\n } else {\n // Buffer event until observe() is initialized\n if (pendingEvents.length >= MAX_PENDING_EVENTS) {\n if (isDev()) {\n console.warn(\n `[svoose] metric() buffer full (${MAX_PENDING_EVENTS} events). ` +\n `Call observe() to start sending events. New events are being dropped.`\n );\n }\n return;\n }\n pendingEvents.push(event);\n }\n}\n\n// ============================================\n// Test helpers (not exported in production builds)\n// ============================================\n\n/**\n * Get number of pending events (for testing)\n * @internal\n */\nexport function _getPendingEventsCount(): number {\n return pendingEvents.length;\n}\n\n/**\n * Clear pending events (for testing)\n * @internal\n */\nexport function _clearPendingEvents(): void {\n pendingEvents.length = 0;\n}\n"],
5
- "mappings": "AAYA,IAAIA,EAAkD,KAGtD,MAAMC,EAAqC,CAAC,EAQrC,SAASC,EAAiBC,EAAoD,CAInF,GAHAH,EAAUG,EAGNH,GAAWC,EAAc,OAAS,EAAG,CACvC,MAAMG,EAASH,EAAc,OAAO,EAAGA,EAAc,MAAM,EAC3D,UAAWI,KAASD,EAClBJ,EAAQK,CAAK,CAEjB,CACF,CAoBO,SAASC,GAA2D,CACzE,OAAON,CACT,CAIA,SAASO,GAAiB,CACxB,GAAI,CAIF,OAHI,OAAO,QAAY,IAGlB,YAAoB,KAAK,MAAQ,EAIxC,MAAQ,CACN,MAAO,EACT,CACF,CAEO,SAASC,EAAOC,EAAcC,EAAgC,CAAC,EAAS,CAC7E,MAAML,EAA2B,CAC/B,KAAM,SACN,KAAAI,EACA,KAAAC,EACA,UAAW,KAAK,IAAI,CACtB,EAEA,GAAIV,EACFA,EAAQK,CAAK,MACR,CAEL,GAAIJ,EAAc,QAAU,IAAoB,CAC1CM,EAAM,GACR,QAAQ,KACN,mHAEF,EAEF,MACF,CACAN,EAAc,KAAKI,CAAK,CAC1B,CACF,CAUO,SAASM,GAAiC,CAC/C,OAAOV,EAAc,MACvB,CAMO,SAASW,GAA4B,CAC1CX,EAAc,OAAS,CACzB",
6
- "names": ["emitter", "pendingEvents", "setMetricEmitter", "emit", "events", "event", "getMetricEmitter", "isDev", "metric", "name", "data", "_getPendingEventsCount", "_clearPendingEvents"]
4
+ "sourcesContent": ["/**\n * Custom metrics module\n *\n * Provides metric() function for sending custom events with pending buffer support.\n */\n\nimport type { CustomMetricEvent, ObserveEvent } from '../types/index.js';\n\n// Maximum pending events before dropping (with warning in dev)\nconst MAX_PENDING_EVENTS = 100;\n\n// Emitter function set by observe()\nlet emitter: ((event: ObserveEvent) => void) | null = null;\n\n// Pending events buffer for events sent before observe() is initialized\nconst pendingEvents: CustomMetricEvent[] = [];\n\n/**\n * Set the metric emitter function\n * Called by observe() to wire up the metric system\n *\n * @param emit - The emit function from observe(), or null to disconnect\n */\nexport function setMetricEmitter(emit: ((event: ObserveEvent) => void) | null): void {\n emitter = emit;\n\n // Flush pending events when emitter is set\n if (emitter && pendingEvents.length > 0) {\n const events = pendingEvents.splice(0, pendingEvents.length);\n for (const event of events) {\n emitter(event);\n }\n }\n}\n\n/**\n * Send a custom metric event\n *\n * Events are automatically batched with other metrics and sent to your backend.\n * If called before observe() is initialized, events are buffered (max 100).\n *\n * @param name - Metric name (e.g., 'checkout_started', 'button_clicked')\n * @param data - Optional data payload\n *\n * @example\n * metric('checkout_started', { step: 1, cartTotal: 99.99 });\n * metric('button_clicked', { id: 'submit-btn' });\n * metric('feature_used', { name: 'dark_mode', enabled: true });\n */\n/**\n * Get the current metric emitter function (for internal use)\n * @internal\n */\nexport function getMetricEmitter(): ((event: ObserveEvent) => void) | null {\n return emitter;\n}\n\ndeclare const process: { env: Record<string, string | undefined> } | undefined;\n\nfunction isDev(): boolean {\n try {\n if (typeof process !== 'undefined' && process?.env?.NODE_ENV === 'development') {\n return true;\n }\n if ((import.meta as any).env?.DEV === true) {\n return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\nexport function metric(name: string, data: Record<string, unknown> = {}): void {\n emitEvent({ type: 'custom', name, data, timestamp: Date.now() });\n}\n\n/**\n * Internal helper to emit or buffer a CustomMetricEvent\n */\nfunction emitEvent(event: CustomMetricEvent): void {\n if (emitter) {\n emitter(event);\n } else {\n if (pendingEvents.length >= MAX_PENDING_EVENTS) {\n if (isDev()) {\n console.warn(\n `[svoose] metric() buffer full (${MAX_PENDING_EVENTS} events). ` +\n `Call observe() to start sending events. New events are being dropped.`\n );\n }\n return;\n }\n pendingEvents.push(event);\n }\n}\n\n/**\n * Internal helper for typed metric helpers (counter, gauge, histogram)\n */\nfunction emitMetric(\n name: string,\n metricKind: 'counter' | 'gauge' | 'histogram',\n value: number,\n metadata: Record<string, unknown> = {},\n): void {\n emitEvent({ type: 'custom', name, metricKind, value, data: metadata, timestamp: Date.now() });\n}\n\n/**\n * Increment a counter metric\n *\n * @param name - Counter name\n * @param value - Increment amount (default: 1)\n * @param metadata - Optional metadata\n *\n * @example\n * counter('page_views');\n * counter('items_purchased', 3);\n * counter('api_calls', 1, { endpoint: '/users' });\n */\nexport function counter(name: string, value: number = 1, metadata?: Record<string, unknown>): void {\n emitMetric(name, 'counter', value, metadata);\n}\n\n/**\n * Set a gauge metric (point-in-time value)\n *\n * @param name - Gauge name\n * @param value - Current value\n * @param metadata - Optional metadata\n *\n * @example\n * gauge('active_users', 42);\n * gauge('memory_usage_mb', 256, { heap: 'old' });\n */\nexport function gauge(name: string, value: number, metadata?: Record<string, unknown>): void {\n emitMetric(name, 'gauge', value, metadata);\n}\n\n/**\n * Record a histogram metric (distribution value)\n *\n * @param name - Histogram name\n * @param value - Observed value\n * @param metadata - Optional metadata\n *\n * @example\n * histogram('response_time_ms', 123);\n * histogram('payload_size', 4096, { route: '/api/data' });\n */\nexport function histogram(name: string, value: number, metadata?: Record<string, unknown>): void {\n emitMetric(name, 'histogram', value, metadata);\n}\n\n// ============================================\n// Test helpers (not exported in production builds)\n// ============================================\n\n/**\n * Get number of pending events (for testing)\n * @internal\n */\nexport function _getPendingEventsCount(): number {\n return pendingEvents.length;\n}\n\n/**\n * Clear pending events (for testing)\n * @internal\n */\nexport function _clearPendingEvents(): void {\n pendingEvents.length = 0;\n}\n"],
5
+ "mappings": "AAYA,IAAIA,EAAkD,KAGtD,MAAMC,EAAqC,CAAC,EAQrC,SAASC,EAAiBC,EAAoD,CAInF,GAHAH,EAAUG,EAGNH,GAAWC,EAAc,OAAS,EAAG,CACvC,MAAMG,EAASH,EAAc,OAAO,EAAGA,EAAc,MAAM,EAC3D,UAAWI,KAASD,EAClBJ,EAAQK,CAAK,CAEjB,CACF,CAoBO,SAASC,GAA2D,CACzE,OAAON,CACT,CAIA,SAASO,GAAiB,CACxB,GAAI,CAIF,OAHI,OAAO,QAAY,IAGlB,YAAoB,KAAK,MAAQ,EAIxC,MAAQ,CACN,MAAO,EACT,CACF,CAEO,SAASC,EAAOC,EAAcC,EAAgC,CAAC,EAAS,CAC7EC,EAAU,CAAE,KAAM,SAAU,KAAAF,EAAM,KAAAC,EAAM,UAAW,KAAK,IAAI,CAAE,CAAC,CACjE,CAKA,SAASC,EAAUN,EAAgC,CACjD,GAAIL,EACFA,EAAQK,CAAK,MACR,CACL,GAAIJ,EAAc,QAAU,IAAoB,CAC1CM,EAAM,GACR,QAAQ,KACN,mHAEF,EAEF,MACF,CACAN,EAAc,KAAKI,CAAK,CAC1B,CACF,CAKA,SAASO,EACPH,EACAI,EACAC,EACAC,EAAoC,CAAC,EAC/B,CACNJ,EAAU,CAAE,KAAM,SAAU,KAAAF,EAAM,WAAAI,EAAY,MAAAC,EAAO,KAAMC,EAAU,UAAW,KAAK,IAAI,CAAE,CAAC,CAC9F,CAcO,SAASC,EAAQP,EAAcK,EAAgB,EAAGC,EAA0C,CACjGH,EAAWH,EAAM,UAAWK,EAAOC,CAAQ,CAC7C,CAaO,SAASE,EAAMR,EAAcK,EAAeC,EAA0C,CAC3FH,EAAWH,EAAM,QAASK,EAAOC,CAAQ,CAC3C,CAaO,SAASG,EAAUT,EAAcK,EAAeC,EAA0C,CAC/FH,EAAWH,EAAM,YAAaK,EAAOC,CAAQ,CAC/C,CAUO,SAASI,GAAiC,CAC/C,OAAOlB,EAAc,MACvB,CAMO,SAASmB,GAA4B,CAC1CnB,EAAc,OAAS,CACzB",
6
+ "names": ["emitter", "pendingEvents", "setMetricEmitter", "emit", "events", "event", "getMetricEmitter", "isDev", "metric", "name", "data", "emitEvent", "emitMetric", "metricKind", "value", "metadata", "counter", "gauge", "histogram", "_getPendingEventsCount", "_clearPendingEvents"]
7
7
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Typed metric factory for TypeScript autocomplete
3
+ */
4
+ /**
5
+ * Create a typed metric function with autocomplete for metric names and data shapes
6
+ *
7
+ * @example
8
+ * const m = createTypedMetric<{
9
+ * checkout_started: { step: number; cartTotal: number };
10
+ * button_clicked: { id: string };
11
+ * }>();
12
+ *
13
+ * m('checkout_started', { step: 1, cartTotal: 99.99 }); // ✅ autocomplete works
14
+ * m('button_clicked', { id: 'submit' }); // ✅
15
+ * m('unknown_event', {}); // ❌ TypeScript error
16
+ */
17
+ export declare function createTypedMetric<T extends Record<string, Record<string, unknown>>>(): <K extends keyof T & string>(name: K, data: T[K]) => void;
18
+ //# sourceMappingURL=typed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed.d.ts","sourceRoot":"","sources":["../../src/metrics/typed.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,MAChE,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,KAAG,IAAI,CAGxE"}
@@ -0,0 +1,2 @@
1
+ import{metric as r}from"./metric.js";function o(){return function(e,n){r(e,n)}}export{o as createTypedMetric};
2
+ //# sourceMappingURL=typed.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/metrics/typed.ts"],
4
+ "sourcesContent": ["/**\n * Typed metric factory for TypeScript autocomplete\n */\n\nimport { metric } from './metric.js';\n\n/**\n * Create a typed metric function with autocomplete for metric names and data shapes\n *\n * @example\n * const m = createTypedMetric<{\n * checkout_started: { step: number; cartTotal: number };\n * button_clicked: { id: string };\n * }>();\n *\n * m('checkout_started', { step: 1, cartTotal: 99.99 }); // \u2705 autocomplete works\n * m('button_clicked', { id: 'submit' }); // \u2705\n * m('unknown_event', {}); // \u274C TypeScript error\n */\nexport function createTypedMetric<T extends Record<string, Record<string, unknown>>>() {\n return function <K extends keyof T & string>(name: K, data: T[K]): void {\n metric(name, data);\n };\n}\n"],
5
+ "mappings": "AAIA,OAAS,UAAAA,MAAc,cAehB,SAASC,GAAuE,CACrF,OAAO,SAAsCC,EAASC,EAAkB,CACtEH,EAAOE,EAAMC,CAAI,CACnB,CACF",
6
+ "names": ["metric", "createTypedMetric", "name", "data"]
7
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"observe.svelte.d.ts","sourceRoot":"","sources":["../../src/observe/observe.svelte.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,OAAO,KAAK,EAAE,cAAc,EAAc,YAAY,EAAa,MAAM,mBAAmB,CAAC;AAc7F,QAAA,IAAI,sBAAsB,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,IAAW,CAAC;AAE1E;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,sBAAsB,GAAG,IAAI,CAE/E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,sBAAsB,CAEjE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,MAAM,IAAI,CA+KhE;AAED,YAAY,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"observe.svelte.d.ts","sourceRoot":"","sources":["../../src/observe/observe.svelte.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,OAAO,KAAK,EAAE,cAAc,EAAc,YAAY,EAAa,MAAM,mBAAmB,CAAC;AAc7F,QAAA,IAAI,sBAAsB,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,IAAW,CAAC;AAE1E;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,sBAAsB,GAAG,IAAI,CAE/E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,sBAAsB,CAEjE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,MAAM,IAAI,CAiLhE;AAED,YAAY,EAAE,cAAc,EAAE,CAAC"}
@@ -1,2 +1,2 @@
1
- import{vitalObservers as y}from"./vitals.js";import{observeErrors as O}from"./errors.js";import{createFetchTransport as E}from"../transport/fetch.js";import{createSampler as S,eventTypeToSamplingType as M}from"./sampling.js";import{createSessionManager as T}from"./session.js";import{setMetricEmitter as f,getMetricEmitter as I}from"../metrics/index.js";const c={endpoint:"/api/observe",vitals:!0,errors:!0,batchSize:10,flushInterval:5e3,sampleRate:1,debug:!1};let b=null;function d(l){b=l}function w(){return b}function N(l={}){if(Math.random()>(l.sampleRate??c.sampleRate))return()=>{};const t={...c,...l},m=t.transport??E(t.endpoint),u=t.sampling!=null?S(t.sampling):null,a=t.session!=null?T(t.session):null,n=[];a&&n.push(()=>a.destroy());const o=[];let v=null;const g=()=>{try{return typeof location<"u"?location.href:""}catch{return""}},s=e=>{if(!(t.filter&&!t.filter(e))){if(u){const r=M(e.type);if(r&&!u.shouldSample(r))return}a&&(e.sessionId=a.getSessionId()),t.debug&&console.log("[svoose]",e),o.push(e),o.length>=t.batchSize&&i()}},i=()=>{if(o.length===0)return;const e=o.splice(0,o.length),r=m.send(e);r&&typeof r.catch=="function"&&r.catch(p=>{t.debug&&console.error("[svoose] transport error:",p)})},h=e=>{const r={type:"vital",name:e.name,value:e.value,rating:e.rating,delta:e.delta,timestamp:e.timestamp,url:g()};s(r)};if(t.vitals){const e=t.vitals===!0?["CLS","LCP","FID","INP","FCP","TTFB"]:t.vitals;for(const r of e){const p=y[r];p&&n.push(p(h))}}if(t.errors&&n.push(O(e=>{s(e)})),d(s),n.push(()=>{w()===s&&d(null)}),f(s),n.push(()=>{I()===s&&f(null)}),v=setInterval(i,t.flushInterval),n.push(()=>{v&&clearInterval(v)}),typeof document<"u"){const e=()=>{document.visibilityState==="hidden"&&i()};document.addEventListener("visibilitychange",e),n.push(()=>{document.removeEventListener("visibilitychange",e)})}if(typeof window<"u"){const e=()=>{i()};window.addEventListener("beforeunload",e),n.push(()=>{window.removeEventListener("beforeunload",e)})}return()=>{i(),n.forEach(e=>e())}}export{w as getGlobalObserver,N as observe,d as setGlobalObserver};
1
+ import{vitalObservers as y}from"./vitals.js";import{observeErrors as O}from"./errors.js";import{createFetchTransport as E}from"../transport/fetch.js";import{createSampler as S,eventTypeToSamplingType as T}from"./sampling.js";import{createSessionManager as M}from"./session.js";import{setMetricEmitter as c,getMetricEmitter as I}from"../metrics/index.js";const d={endpoint:"/api/metrics",vitals:!0,errors:!0,batchSize:10,flushInterval:5e3,sampleRate:1,debug:!1};let b=null;function m(l){b=l}function w(){return b}function N(l={}){if(Math.random()>(l.sampleRate??d.sampleRate))return()=>{};const t={...d,...l},u=t.transport??E(t.endpoint),f=t.sampling!=null?S(t.sampling):null,a=t.session!=null?M(t.session):null,s=[];a&&s.push(()=>a.destroy());const o=[];let v=null;const g=()=>{try{return typeof location<"u"?location.href:""}catch{return""}},n=e=>{if(!(t.filter&&!t.filter(e))){if(f){const r=T(e.type);if(r&&!f.shouldSample(r))return}a&&(e.sessionId=a.getSessionId()),t.debug&&console.log("[svoose]",e),o.push(e),o.length>=t.batchSize&&i()}},i=()=>{if(o.length===0)return;const e=o.splice(0,o.length),r=u.send(e);r&&typeof r.catch=="function"&&r.catch(p=>{t.debug&&console.error("[svoose] transport error:",p)})},h=e=>{const r={type:"vital",name:e.name,value:e.value,rating:e.rating,delta:e.delta,timestamp:e.timestamp,url:g()};n(r)};if(t.vitals){const e=t.vitals===!0?["CLS","LCP","FID","INP","FCP","TTFB"]:t.vitals;for(const r of e){const p=y[r];p&&s.push(p(h))}}if(t.errors&&s.push(O(e=>{n(e)})),m(n),s.push(()=>{w()===n&&m(null)}),c(n),s.push(()=>{I()===n&&c(null)}),v=setInterval(i,t.flushInterval),s.push(()=>{v&&clearInterval(v)}),typeof document<"u"){const e=()=>{document.visibilityState==="hidden"&&i()};document.addEventListener("visibilitychange",e),s.push(()=>{document.removeEventListener("visibilitychange",e)})}if(typeof window<"u"){const e=()=>{i()};window.addEventListener("beforeunload",e),s.push(()=>{window.removeEventListener("beforeunload",e)})}return()=>{i(),s.forEach(e=>e()),u.destroy?.()}}export{w as getGlobalObserver,N as observe,m as setGlobalObserver};
2
2
  //# sourceMappingURL=observe.svelte.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/observe/observe.svelte.ts"],
4
- "sourcesContent": ["/**\n * Main observe() function - combines vitals, errors, and transport\n */\n\nimport { vitalObservers, type Metric, type MetricName } from './vitals.js';\nimport { observeErrors, type ObserveErrorEvent } from './errors.js';\nimport { createFetchTransport } from '../transport/fetch.js';\nimport {\n createSampler,\n eventTypeToSamplingType,\n type Sampler,\n} from './sampling.js';\nimport { createSessionManager, type SessionManager } from './session.js';\nimport { setMetricEmitter, getMetricEmitter } from '../metrics/index.js';\nimport type { ObserveOptions, VitalEvent, ObserveEvent, Transport } from '../types/index.js';\n\n// Default configuration\nconst defaults = {\n endpoint: '/api/observe',\n vitals: true as const,\n errors: true,\n batchSize: 10,\n flushInterval: 5000,\n sampleRate: 1,\n debug: false,\n} satisfies Required<Omit<ObserveOptions, 'transport' | 'filter' | 'sampling' | 'session'>>;\n\n// Global observer callback for state machines\nlet globalObserverCallback: ((event: ObserveEvent) => void) | null = null;\n\n/**\n * Set global observer callback for state machines\n * Called internally to connect machines to observe()\n */\nexport function setGlobalObserver(callback: typeof globalObserverCallback): void {\n globalObserverCallback = callback;\n}\n\n/**\n * Get global observer callback\n * Used by createMachine to send transition events\n */\nexport function getGlobalObserver(): typeof globalObserverCallback {\n return globalObserverCallback;\n}\n\n/**\n * Main observe function - starts collecting metrics and errors\n *\n * @param options - Configuration options\n * @returns Cleanup function to stop observing\n *\n * @example\n * // Basic usage\n * observe();\n *\n * @example\n * // With options\n * observe({\n * endpoint: '/api/metrics',\n * vitals: ['CLS', 'LCP', 'INP'],\n * errors: true,\n * debug: true,\n * });\n */\nexport function observe(options: ObserveOptions = {}): () => void {\n // Legacy sampleRate support (deprecated) - skip entire observer\n if (Math.random() > (options.sampleRate ?? defaults.sampleRate)) {\n return () => {};\n }\n\n const config = { ...defaults, ...options };\n const transport: Transport = config.transport ?? createFetchTransport(config.endpoint);\n\n // Create sampler if sampling option is provided\n const sampler: Sampler | null = config.sampling != null\n ? createSampler(config.sampling)\n : null;\n\n // Create session manager if session option is provided\n const sessionManager: SessionManager | null = config.session != null\n ? createSessionManager(config.session)\n : null;\n\n const cleanups: (() => void)[] = [];\n\n // Cleanup session manager on destroy\n if (sessionManager) {\n cleanups.push(() => sessionManager.destroy());\n }\n const buffer: ObserveEvent[] = [];\n let flushTimer: ReturnType<typeof setInterval> | null = null;\n\n // Get current URL\n const getUrl = (): string => {\n try {\n return typeof location !== 'undefined' ? location.href : '';\n } catch {\n return '';\n }\n };\n\n // Buffer an event and potentially flush\n const bufferEvent = (event: ObserveEvent): void => {\n // Apply filter if provided\n if (config.filter && !config.filter(event)) {\n return;\n }\n\n // Apply per-event-type sampling\n if (sampler) {\n const samplingType = eventTypeToSamplingType(event.type);\n if (samplingType && !sampler.shouldSample(samplingType)) {\n return;\n }\n }\n\n // Add sessionId if session manager is enabled\n if (sessionManager) {\n (event as ObserveEvent & { sessionId?: string }).sessionId = sessionManager.getSessionId();\n }\n\n if (config.debug) {\n console.log('[svoose]', event);\n }\n\n buffer.push(event);\n\n if (buffer.length >= config.batchSize) {\n flush();\n }\n };\n\n // Send buffered events to transport\n const flush = (): void => {\n if (buffer.length === 0) return;\n\n const events = buffer.splice(0, buffer.length);\n // Handle both Promise and non-Promise returns from transport.send()\n const result = transport.send(events);\n if (result && typeof result.catch === 'function') {\n result.catch((err) => {\n if (config.debug) {\n console.error('[svoose] transport error:', err);\n }\n });\n }\n };\n\n // Convert metric to vital event\n const handleMetric = (metric: Metric): void => {\n const vitalEvent: VitalEvent = {\n type: 'vital',\n name: metric.name,\n value: metric.value,\n rating: metric.rating,\n delta: metric.delta,\n timestamp: metric.timestamp,\n url: getUrl(),\n };\n bufferEvent(vitalEvent);\n };\n\n // Setup vitals observers\n if (config.vitals) {\n const vitalsToObserve: MetricName[] =\n config.vitals === true\n ? ['CLS', 'LCP', 'FID', 'INP', 'FCP', 'TTFB']\n : config.vitals;\n\n for (const name of vitalsToObserve) {\n const observer = vitalObservers[name];\n if (observer) {\n cleanups.push(observer(handleMetric));\n }\n }\n }\n\n // Setup error observer\n if (config.errors) {\n cleanups.push(\n observeErrors((event: ObserveErrorEvent) => {\n bufferEvent(event);\n })\n );\n }\n\n // Setup global observer for state machines\n setGlobalObserver(bufferEvent);\n cleanups.push(() => {\n // Only clear if we're still the active observer\n if (getGlobalObserver() === bufferEvent) {\n setGlobalObserver(null);\n }\n });\n\n // Setup metric emitter for custom metrics\n setMetricEmitter(bufferEvent);\n cleanups.push(() => {\n // Only clear if we're still the active emitter\n if (getMetricEmitter() === bufferEvent) {\n setMetricEmitter(null);\n }\n });\n\n // Setup flush interval\n flushTimer = setInterval(flush, config.flushInterval);\n cleanups.push(() => {\n if (flushTimer) clearInterval(flushTimer);\n });\n\n // Flush on page visibility change (user navigating away)\n if (typeof document !== 'undefined') {\n const visibilityHandler = (): void => {\n if (document.visibilityState === 'hidden') {\n flush();\n }\n };\n document.addEventListener('visibilitychange', visibilityHandler);\n cleanups.push(() => {\n document.removeEventListener('visibilitychange', visibilityHandler);\n });\n }\n\n // Flush on beforeunload\n if (typeof window !== 'undefined') {\n const unloadHandler = (): void => {\n flush();\n };\n window.addEventListener('beforeunload', unloadHandler);\n cleanups.push(() => {\n window.removeEventListener('beforeunload', unloadHandler);\n });\n }\n\n // Return cleanup function\n return () => {\n flush();\n cleanups.forEach((fn) => fn());\n };\n}\n\nexport type { ObserveOptions };\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Main observe() function - combines vitals, errors, and transport\n */\n\nimport { vitalObservers, type Metric, type MetricName } from './vitals.js';\nimport { observeErrors, type ObserveErrorEvent } from './errors.js';\nimport { createFetchTransport } from '../transport/fetch.js';\nimport {\n createSampler,\n eventTypeToSamplingType,\n type Sampler,\n} from './sampling.js';\nimport { createSessionManager, type SessionManager } from './session.js';\nimport { setMetricEmitter, getMetricEmitter } from '../metrics/index.js';\nimport type { ObserveOptions, VitalEvent, ObserveEvent, Transport } from '../types/index.js';\n\n// Default configuration\nconst defaults = {\n endpoint: '/api/metrics',\n vitals: true as const,\n errors: true,\n batchSize: 10,\n flushInterval: 5000,\n sampleRate: 1,\n debug: false,\n} satisfies Required<Omit<ObserveOptions, 'transport' | 'filter' | 'sampling' | 'session'>>;\n\n// Global observer callback for state machines\nlet globalObserverCallback: ((event: ObserveEvent) => void) | null = null;\n\n/**\n * Set global observer callback for state machines\n * Called internally to connect machines to observe()\n */\nexport function setGlobalObserver(callback: typeof globalObserverCallback): void {\n globalObserverCallback = callback;\n}\n\n/**\n * Get global observer callback\n * Used by createMachine to send transition events\n */\nexport function getGlobalObserver(): typeof globalObserverCallback {\n return globalObserverCallback;\n}\n\n/**\n * Main observe function - starts collecting metrics and errors\n *\n * @param options - Configuration options\n * @returns Cleanup function to stop observing\n *\n * @example\n * // Basic usage\n * observe();\n *\n * @example\n * // With options\n * observe({\n * endpoint: '/api/metrics',\n * vitals: ['CLS', 'LCP', 'INP'],\n * errors: true,\n * debug: true,\n * });\n */\nexport function observe(options: ObserveOptions = {}): () => void {\n // Legacy sampleRate support (deprecated) - skip entire observer\n if (Math.random() > (options.sampleRate ?? defaults.sampleRate)) {\n return () => {};\n }\n\n const config = { ...defaults, ...options };\n const transport: Transport = config.transport ?? createFetchTransport(config.endpoint);\n\n // Create sampler if sampling option is provided\n const sampler: Sampler | null = config.sampling != null\n ? createSampler(config.sampling)\n : null;\n\n // Create session manager if session option is provided\n const sessionManager: SessionManager | null = config.session != null\n ? createSessionManager(config.session)\n : null;\n\n const cleanups: (() => void)[] = [];\n\n // Cleanup session manager on destroy\n if (sessionManager) {\n cleanups.push(() => sessionManager.destroy());\n }\n const buffer: ObserveEvent[] = [];\n let flushTimer: ReturnType<typeof setInterval> | null = null;\n\n // Get current URL\n const getUrl = (): string => {\n try {\n return typeof location !== 'undefined' ? location.href : '';\n } catch {\n return '';\n }\n };\n\n // Buffer an event and potentially flush\n const bufferEvent = (event: ObserveEvent): void => {\n // Apply filter if provided\n if (config.filter && !config.filter(event)) {\n return;\n }\n\n // Apply per-event-type sampling\n if (sampler) {\n const samplingType = eventTypeToSamplingType(event.type);\n if (samplingType && !sampler.shouldSample(samplingType)) {\n return;\n }\n }\n\n // Add sessionId if session manager is enabled\n if (sessionManager) {\n (event as ObserveEvent & { sessionId?: string }).sessionId = sessionManager.getSessionId();\n }\n\n if (config.debug) {\n console.log('[svoose]', event);\n }\n\n buffer.push(event);\n\n if (buffer.length >= config.batchSize) {\n flush();\n }\n };\n\n // Send buffered events to transport\n const flush = (): void => {\n if (buffer.length === 0) return;\n\n const events = buffer.splice(0, buffer.length);\n // Handle both Promise and non-Promise returns from transport.send()\n const result = transport.send(events);\n if (result && typeof result.catch === 'function') {\n result.catch((err) => {\n if (config.debug) {\n console.error('[svoose] transport error:', err);\n }\n });\n }\n };\n\n // Convert metric to vital event\n const handleMetric = (metric: Metric): void => {\n const vitalEvent: VitalEvent = {\n type: 'vital',\n name: metric.name,\n value: metric.value,\n rating: metric.rating,\n delta: metric.delta,\n timestamp: metric.timestamp,\n url: getUrl(),\n };\n bufferEvent(vitalEvent);\n };\n\n // Setup vitals observers\n if (config.vitals) {\n const vitalsToObserve: MetricName[] =\n config.vitals === true\n ? ['CLS', 'LCP', 'FID', 'INP', 'FCP', 'TTFB']\n : config.vitals;\n\n for (const name of vitalsToObserve) {\n const observer = vitalObservers[name];\n if (observer) {\n cleanups.push(observer(handleMetric));\n }\n }\n }\n\n // Setup error observer\n if (config.errors) {\n cleanups.push(\n observeErrors((event: ObserveErrorEvent) => {\n bufferEvent(event);\n })\n );\n }\n\n // Setup global observer for state machines\n setGlobalObserver(bufferEvent);\n cleanups.push(() => {\n // Only clear if we're still the active observer\n if (getGlobalObserver() === bufferEvent) {\n setGlobalObserver(null);\n }\n });\n\n // Setup metric emitter for custom metrics\n setMetricEmitter(bufferEvent);\n cleanups.push(() => {\n // Only clear if we're still the active emitter\n if (getMetricEmitter() === bufferEvent) {\n setMetricEmitter(null);\n }\n });\n\n // Setup flush interval\n flushTimer = setInterval(flush, config.flushInterval);\n cleanups.push(() => {\n if (flushTimer) clearInterval(flushTimer);\n });\n\n // Flush on page visibility change (user navigating away)\n if (typeof document !== 'undefined') {\n const visibilityHandler = (): void => {\n if (document.visibilityState === 'hidden') {\n flush();\n }\n };\n document.addEventListener('visibilitychange', visibilityHandler);\n cleanups.push(() => {\n document.removeEventListener('visibilitychange', visibilityHandler);\n });\n }\n\n // Flush on beforeunload\n if (typeof window !== 'undefined') {\n const unloadHandler = (): void => {\n flush();\n };\n window.addEventListener('beforeunload', unloadHandler);\n cleanups.push(() => {\n window.removeEventListener('beforeunload', unloadHandler);\n });\n }\n\n // Return cleanup function\n return () => {\n flush();\n cleanups.forEach((fn) => fn());\n // Destroy transport if it has a destroy method (e.g. HybridTransport)\n (transport as Transport & { destroy?: () => void }).destroy?.();\n };\n}\n\nexport type { ObserveOptions };\n"],
5
+ "mappings": "AAIA,OAAS,kBAAAA,MAAoD,cAC7D,OAAS,iBAAAC,MAA6C,cACtD,OAAS,wBAAAC,MAA4B,wBACrC,OACE,iBAAAC,EACA,2BAAAC,MAEK,gBACP,OAAS,wBAAAC,MAAiD,eAC1D,OAAS,oBAAAC,EAAkB,oBAAAC,MAAwB,sBAInD,MAAMC,EAAW,CACf,SAAU,eACV,OAAQ,GACR,OAAQ,GACR,UAAW,GACX,cAAe,IACf,WAAY,EACZ,MAAO,EACT,EAGA,IAAIC,EAAiE,KAM9D,SAASC,EAAkBC,EAA+C,CAC/EF,EAAyBE,CAC3B,CAMO,SAASC,GAAmD,CACjE,OAAOH,CACT,CAqBO,SAASI,EAAQC,EAA0B,CAAC,EAAe,CAEhE,GAAI,KAAK,OAAO,GAAKA,EAAQ,YAAcN,EAAS,YAClD,MAAO,IAAM,CAAC,EAGhB,MAAMO,EAAS,CAAE,GAAGP,EAAU,GAAGM,CAAQ,EACnCE,EAAuBD,EAAO,WAAab,EAAqBa,EAAO,QAAQ,EAG/EE,EAA0BF,EAAO,UAAY,KAC/CZ,EAAcY,EAAO,QAAQ,EAC7B,KAGEG,EAAwCH,EAAO,SAAW,KAC5DV,EAAqBU,EAAO,OAAO,EACnC,KAEEI,EAA2B,CAAC,EAG9BD,GACFC,EAAS,KAAK,IAAMD,EAAe,QAAQ,CAAC,EAE9C,MAAME,EAAyB,CAAC,EAChC,IAAIC,EAAoD,KAGxD,MAAMC,EAAS,IAAc,CAC3B,GAAI,CACF,OAAO,OAAO,SAAa,IAAc,SAAS,KAAO,EAC3D,MAAQ,CACN,MAAO,EACT,CACF,EAGMC,EAAeC,GAA8B,CAEjD,GAAI,EAAAT,EAAO,QAAU,CAACA,EAAO,OAAOS,CAAK,GAKzC,IAAIP,EAAS,CACX,MAAMQ,EAAerB,EAAwBoB,EAAM,IAAI,EACvD,GAAIC,GAAgB,CAACR,EAAQ,aAAaQ,CAAY,EACpD,MAEJ,CAGIP,IACDM,EAAgD,UAAYN,EAAe,aAAa,GAGvFH,EAAO,OACT,QAAQ,IAAI,WAAYS,CAAK,EAG/BJ,EAAO,KAAKI,CAAK,EAEbJ,EAAO,QAAUL,EAAO,WAC1BW,EAAM,EAEV,EAGMA,EAAQ,IAAY,CACxB,GAAIN,EAAO,SAAW,EAAG,OAEzB,MAAMO,EAASP,EAAO,OAAO,EAAGA,EAAO,MAAM,EAEvCQ,EAASZ,EAAU,KAAKW,CAAM,EAChCC,GAAU,OAAOA,EAAO,OAAU,YACpCA,EAAO,MAAOC,GAAQ,CAChBd,EAAO,OACT,QAAQ,MAAM,4BAA6Bc,CAAG,CAElD,CAAC,CAEL,EAGMC,EAAgBC,GAAyB,CAC7C,MAAMC,EAAyB,CAC7B,KAAM,QACN,KAAMD,EAAO,KACb,MAAOA,EAAO,MACd,OAAQA,EAAO,OACf,MAAOA,EAAO,MACd,UAAWA,EAAO,UAClB,IAAKT,EAAO,CACd,EACAC,EAAYS,CAAU,CACxB,EAGA,GAAIjB,EAAO,OAAQ,CACjB,MAAMkB,EACJlB,EAAO,SAAW,GACd,CAAC,MAAO,MAAO,MAAO,MAAO,MAAO,MAAM,EAC1CA,EAAO,OAEb,UAAWmB,KAAQD,EAAiB,CAClC,MAAME,EAAWnC,EAAekC,CAAI,EAChCC,GACFhB,EAAS,KAAKgB,EAASL,CAAY,CAAC,CAExC,CACF,CAoCA,GAjCIf,EAAO,QACTI,EAAS,KACPlB,EAAeuB,GAA6B,CAC1CD,EAAYC,CAAK,CACnB,CAAC,CACH,EAIFd,EAAkBa,CAAW,EAC7BJ,EAAS,KAAK,IAAM,CAEdP,EAAkB,IAAMW,GAC1Bb,EAAkB,IAAI,CAE1B,CAAC,EAGDJ,EAAiBiB,CAAW,EAC5BJ,EAAS,KAAK,IAAM,CAEdZ,EAAiB,IAAMgB,GACzBjB,EAAiB,IAAI,CAEzB,CAAC,EAGDe,EAAa,YAAYK,EAAOX,EAAO,aAAa,EACpDI,EAAS,KAAK,IAAM,CACdE,GAAY,cAAcA,CAAU,CAC1C,CAAC,EAGG,OAAO,SAAa,IAAa,CACnC,MAAMe,EAAoB,IAAY,CAChC,SAAS,kBAAoB,UAC/BV,EAAM,CAEV,EACA,SAAS,iBAAiB,mBAAoBU,CAAiB,EAC/DjB,EAAS,KAAK,IAAM,CAClB,SAAS,oBAAoB,mBAAoBiB,CAAiB,CACpE,CAAC,CACH,CAGA,GAAI,OAAO,OAAW,IAAa,CACjC,MAAMC,EAAgB,IAAY,CAChCX,EAAM,CACR,EACA,OAAO,iBAAiB,eAAgBW,CAAa,EACrDlB,EAAS,KAAK,IAAM,CAClB,OAAO,oBAAoB,eAAgBkB,CAAa,CAC1D,CAAC,CACH,CAGA,MAAO,IAAM,CACXX,EAAM,EACNP,EAAS,QAASmB,GAAOA,EAAG,CAAC,EAE5BtB,EAAmD,UAAU,CAChE,CACF",
6
6
  "names": ["vitalObservers", "observeErrors", "createFetchTransport", "createSampler", "eventTypeToSamplingType", "createSessionManager", "setMetricEmitter", "getMetricEmitter", "defaults", "globalObserverCallback", "setGlobalObserver", "callback", "getGlobalObserver", "observe", "options", "config", "transport", "sampler", "sessionManager", "cleanups", "buffer", "flushTimer", "getUrl", "bufferEvent", "event", "samplingType", "flush", "events", "result", "err", "handleMetric", "metric", "vitalEvent", "vitalsToObserve", "name", "observer", "visibilityHandler", "unloadHandler", "fn"]
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vitals.d.ts","sourceRoot":"","sources":["../../src/observe/vitals.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA2C1E;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CA4FzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CA8DzE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAczE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CA8DzE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAczE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAgB1E;AAcD,eAAO,MAAM,cAAc;;;;;;;CAOjB,CAAC;AAEX,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"vitals.d.ts","sourceRoot":"","sources":["../../src/observe/vitals.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA2C1E;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CA4FzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CA8DzE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAczE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAmEzE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAczE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAgB1E;AAcD,eAAO,MAAM,cAAc;;;;;;;CAOjB,CAAC;AAEX,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC"}
@@ -1,2 +1,2 @@
1
- const P={CLS:[.1,.25],LCP:[2500,4e3],FID:[100,300],INP:[200,500],FCP:[1800,3e3],TTFB:[800,1800]};function h(r,e){const[n,t]=P[r];return e<=n?"good":e<=t?"needs-improvement":"poor"}function v(r,e,n=0){return{name:r,value:e,rating:h(r,e),delta:e-n,timestamp:Date.now()}}function m(r){if(typeof PerformanceObserver>"u")return!1;try{return PerformanceObserver.supportedEntryTypes?.includes(r)??!1}catch{return!1}}function L(r){if(!m("layout-shift"))return()=>{};let e=0,n=-1,t=0,i=0,o=0,a=!1;const d=1e3,u=5e3,f=l=>{for(const E of l){const g=E;if(g.hadRecentInput)continue;const b=g.startTime;(n===-1||b-t>d||b-n>u)&&(n=b,e=0),e+=g.value,t=b,e>i&&(i=e)}},s=()=>{i>0&&i!==o&&(r(v("CLS",i,o)),o=i,a=!0)},c=()=>{document.visibilityState==="hidden"&&s()},p=()=>{s()},y=new PerformanceObserver(l=>{f(l.getEntries())});return y.observe({type:"layout-shift",buffered:!0}),document.addEventListener("visibilitychange",c),window.addEventListener("pagehide",p),()=>{y.disconnect(),document.removeEventListener("visibilitychange",c),window.removeEventListener("pagehide",p),!a&&i>0&&s()}}function S(r){if(!m("largest-contentful-paint"))return()=>{};let e=0,n=0,t=!1;const i=()=>{e>0&&!t&&(r(v("LCP",e,n)),n=e,t=!0)},o=new PerformanceObserver(s=>{const c=s.getEntries(),p=c[c.length-1];p&&(e=p.startTime)}),a=()=>{i(),o.disconnect(),f()},d=()=>a(),u=()=>{document.visibilityState==="hidden"&&a()},f=()=>{document.removeEventListener("visibilitychange",u),["keydown","click","pointerdown"].forEach(s=>{document.removeEventListener(s,d,{capture:!0})})};return o.observe({type:"largest-contentful-paint",buffered:!0}),document.addEventListener("visibilitychange",u),["keydown","click","pointerdown"].forEach(s=>{document.addEventListener(s,d,{capture:!0,once:!0})}),()=>{!t&&e>0&&i(),o.disconnect(),f()}}function T(r){if(!m("first-input"))return()=>{};const e=new PerformanceObserver(n=>{const t=n.getEntries()[0];if(t){const i=t.processingStart-t.startTime;r(v("FID",i)),e.disconnect()}});return e.observe({type:"first-input",buffered:!0}),()=>e.disconnect()}function I(r){if(!m("event"))return()=>{};let e=0,n=0,t=!1;const i=new Set,o=()=>{e>0&&!t&&(r(v("INP",e,n)),n=e,t=!0)},a=new PerformanceObserver(f=>{for(const s of f.getEntries()){const c=s;c.interactionId&&(i.has(c.interactionId)||(i.add(c.interactionId),c.duration>e&&(e=c.duration)))}}),d=()=>{document.visibilityState==="hidden"&&o()},u=()=>{o()};return a.observe({type:"event",buffered:!0,durationThreshold:16}),document.addEventListener("visibilitychange",d),window.addEventListener("pagehide",u),()=>{a.disconnect(),document.removeEventListener("visibilitychange",d),window.removeEventListener("pagehide",u),!t&&e>0&&o()}}function w(r){if(!m("paint"))return()=>{};const e=new PerformanceObserver(n=>{for(const t of n.getEntries())t.name==="first-contentful-paint"&&(r(v("FCP",t.startTime)),e.disconnect())});return e.observe({type:"paint",buffered:!0}),()=>e.disconnect()}function M(r){if(!m("navigation"))return()=>{};const e=new PerformanceObserver(n=>{const t=n.getEntries()[0];if(t){const i=t.activationStart??0,o=Math.max(t.responseStart-i,0);r(v("TTFB",o)),e.disconnect()}});return e.observe({type:"navigation",buffered:!0}),()=>e.disconnect()}const C={CLS:L,LCP:S,FID:T,INP:I,FCP:w,TTFB:M};export{L as observeCLS,w as observeFCP,T as observeFID,I as observeINP,S as observeLCP,M as observeTTFB,C as vitalObservers};
1
+ const P={CLS:[.1,.25],LCP:[2500,4e3],FID:[100,300],INP:[200,500],FCP:[1800,3e3],TTFB:[800,1800]};function h(i,e){const[n,t]=P[i];return e<=n?"good":e<=t?"needs-improvement":"poor"}function v(i,e,n=0){return{name:i,value:e,rating:h(i,e),delta:e-n,timestamp:Date.now()}}function m(i){if(typeof PerformanceObserver>"u")return!1;try{return PerformanceObserver.supportedEntryTypes?.includes(i)??!1}catch{return!1}}function L(i){if(!m("layout-shift"))return()=>{};let e=0,n=-1,t=0,r=0,o=0,a=!1;const d=1e3,f=5e3,u=l=>{for(const E of l){const g=E;if(g.hadRecentInput)continue;const b=g.startTime;(n===-1||b-t>d||b-n>f)&&(n=b,e=0),e+=g.value,t=b,e>r&&(r=e)}},s=()=>{r>0&&r!==o&&(i(v("CLS",r,o)),o=r,a=!0)},c=()=>{document.visibilityState==="hidden"&&s()},p=()=>{s()},y=new PerformanceObserver(l=>{u(l.getEntries())});return y.observe({type:"layout-shift",buffered:!0}),document.addEventListener("visibilitychange",c),window.addEventListener("pagehide",p),()=>{y.disconnect(),document.removeEventListener("visibilitychange",c),window.removeEventListener("pagehide",p),!a&&r>0&&s()}}function S(i){if(!m("largest-contentful-paint"))return()=>{};let e=0,n=0,t=!1;const r=()=>{e>0&&!t&&(i(v("LCP",e,n)),n=e,t=!0)},o=new PerformanceObserver(s=>{const c=s.getEntries(),p=c[c.length-1];p&&(e=p.startTime)}),a=()=>{r(),o.disconnect(),u()},d=()=>a(),f=()=>{document.visibilityState==="hidden"&&a()},u=()=>{document.removeEventListener("visibilitychange",f),["keydown","click","pointerdown"].forEach(s=>{document.removeEventListener(s,d,{capture:!0})})};return o.observe({type:"largest-contentful-paint",buffered:!0}),document.addEventListener("visibilitychange",f),["keydown","click","pointerdown"].forEach(s=>{document.addEventListener(s,d,{capture:!0,once:!0})}),()=>{!t&&e>0&&r(),o.disconnect(),u()}}function T(i){if(!m("first-input"))return()=>{};const e=new PerformanceObserver(n=>{const t=n.getEntries()[0];if(t){const r=t.processingStart-t.startTime;i(v("FID",r)),e.disconnect()}});return e.observe({type:"first-input",buffered:!0}),()=>e.disconnect()}function I(i){if(!m("event"))return()=>{};let e=0,n=0,t=!1;const r=new Set,o=()=>{e>0&&!t&&(i(v("INP",e,n)),n=e,t=!0)},a=new PerformanceObserver(u=>{for(const s of u.getEntries()){const c=s;c.interactionId&&(r.has(c.interactionId)||(r.size>=1e3&&r.clear(),r.add(c.interactionId),c.duration>e&&(e=c.duration)))}}),d=()=>{document.visibilityState==="hidden"&&o()},f=()=>{o()};return a.observe({type:"event",buffered:!0,durationThreshold:16}),document.addEventListener("visibilitychange",d),window.addEventListener("pagehide",f),()=>{a.disconnect(),document.removeEventListener("visibilitychange",d),window.removeEventListener("pagehide",f),!t&&e>0&&o()}}function w(i){if(!m("paint"))return()=>{};const e=new PerformanceObserver(n=>{for(const t of n.getEntries())t.name==="first-contentful-paint"&&(i(v("FCP",t.startTime)),e.disconnect())});return e.observe({type:"paint",buffered:!0}),()=>e.disconnect()}function M(i){if(!m("navigation"))return()=>{};const e=new PerformanceObserver(n=>{const t=n.getEntries()[0];if(t){const r=t.activationStart??0,o=Math.max(t.responseStart-r,0);i(v("TTFB",o)),e.disconnect()}});return e.observe({type:"navigation",buffered:!0}),()=>e.disconnect()}const C={CLS:L,LCP:S,FID:T,INP:I,FCP:w,TTFB:M};export{L as observeCLS,w as observeFCP,T as observeFID,I as observeINP,S as observeLCP,M as observeTTFB,C as vitalObservers};
2
2
  //# sourceMappingURL=vitals.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/observe/vitals.ts"],
4
- "sourcesContent": ["/**\n * Web Vitals collection - NO external dependencies\n * Uses PerformanceObserver API directly\n */\n\nimport type { MetricName, MetricRating, Metric } from '../types/index.js';\n\n// Google's official thresholds\nconst THRESHOLDS: Record<MetricName, [number, number]> = {\n CLS: [0.1, 0.25],\n LCP: [2500, 4000],\n FID: [100, 300],\n INP: [200, 500],\n FCP: [1800, 3000],\n TTFB: [800, 1800],\n};\n\nfunction getRating(name: MetricName, value: number): MetricRating {\n const [good, poor] = THRESHOLDS[name];\n if (value <= good) return 'good';\n if (value <= poor) return 'needs-improvement';\n return 'poor';\n}\n\nfunction createMetric(\n name: MetricName,\n value: number,\n prevValue: number = 0\n): Metric {\n return {\n name,\n value,\n rating: getRating(name, value),\n delta: value - prevValue,\n timestamp: Date.now(),\n };\n}\n\n// Check if PerformanceObserver supports given entry type\nfunction isSupported(type: string): boolean {\n if (typeof PerformanceObserver === 'undefined') return false;\n try {\n return PerformanceObserver.supportedEntryTypes?.includes(type) ?? false;\n } catch {\n return false;\n }\n}\n\n/**\n * CLS - Cumulative Layout Shift\n * Measures visual stability using session windows algorithm (web-vitals standard)\n *\n * Session window rules:\n * - Max duration: 5 seconds\n * - Max gap between shifts: 1 second\n * - Reports final value on visibility change (hidden) or pagehide\n *\n * @see https://web.dev/cls/#what-is-a-good-cls-score\n * @see https://github.com/GoogleChrome/web-vitals/blob/main/src/onCLS.ts\n */\nexport function observeCLS(callback: (metric: Metric) => void): () => void {\n if (!isSupported('layout-shift')) return () => {};\n\n // Session window state\n let sessionValue = 0;\n let sessionStart = -1;\n let lastEntryTime = 0;\n\n // Track max session for final CLS value\n let maxSessionValue = 0;\n let prevReportedValue = 0;\n let hasReported = false;\n\n const SESSION_GAP = 1000; // 1 second max gap\n const SESSION_MAX = 5000; // 5 seconds max duration\n\n const processEntries = (entries: PerformanceEntryList) => {\n for (const entry of entries) {\n const shift = entry as LayoutShiftEntry;\n\n // Ignore shifts with recent user input (clicks, taps, key presses)\n if (shift.hadRecentInput) continue;\n\n const entryTime = shift.startTime;\n\n // Start new session if:\n // 1. First entry ever\n // 2. Gap from last entry > 1 second\n // 3. Session duration would exceed 5 seconds\n if (\n sessionStart === -1 ||\n entryTime - lastEntryTime > SESSION_GAP ||\n entryTime - sessionStart > SESSION_MAX\n ) {\n // Start new session\n sessionStart = entryTime;\n sessionValue = 0;\n }\n\n // Accumulate shift in current session\n sessionValue += shift.value;\n lastEntryTime = entryTime;\n\n // Track maximum session value (this is the CLS score)\n if (sessionValue > maxSessionValue) {\n maxSessionValue = sessionValue;\n }\n }\n };\n\n const reportCLS = () => {\n // Only report if we have a value and it changed\n if (maxSessionValue > 0 && maxSessionValue !== prevReportedValue) {\n callback(createMetric('CLS', maxSessionValue, prevReportedValue));\n prevReportedValue = maxSessionValue;\n hasReported = true;\n }\n };\n\n // Report on visibility change (user switches tab or minimizes)\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n reportCLS();\n }\n };\n\n // Report on page unload (closing tab, navigation away)\n const onPageHide = () => {\n reportCLS();\n };\n\n const observer = new PerformanceObserver((list) => {\n processEntries(list.getEntries());\n });\n\n observer.observe({ type: 'layout-shift', buffered: true });\n\n // Listen for lifecycle events to report final CLS\n document.addEventListener('visibilitychange', onVisibilityChange);\n window.addEventListener('pagehide', onPageHide);\n\n // Cleanup function\n return () => {\n observer.disconnect();\n document.removeEventListener('visibilitychange', onVisibilityChange);\n window.removeEventListener('pagehide', onPageHide);\n\n // Report final value on disconnect if not already reported\n if (!hasReported && maxSessionValue > 0) {\n reportCLS();\n }\n };\n}\n\n/**\n * LCP - Largest Contentful Paint\n * Measures loading performance\n *\n * LCP is finalized when:\n * - User interacts with the page (click, keydown, scroll, etc.)\n * - Page becomes hidden (visibility change)\n *\n * @see https://web.dev/lcp/\n * @see https://github.com/GoogleChrome/web-vitals/blob/main/src/onLCP.ts\n */\nexport function observeLCP(callback: (metric: Metric) => void): () => void {\n if (!isSupported('largest-contentful-paint')) return () => {};\n\n let lcpValue = 0;\n let prevReportedValue = 0;\n let hasReported = false;\n\n const reportLCP = () => {\n if (lcpValue > 0 && !hasReported) {\n callback(createMetric('LCP', lcpValue, prevReportedValue));\n prevReportedValue = lcpValue;\n hasReported = true;\n }\n };\n\n const observer = new PerformanceObserver((list) => {\n const entries = list.getEntries();\n // LCP can have multiple entries, track the last one\n const lastEntry = entries[entries.length - 1];\n if (lastEntry) {\n lcpValue = lastEntry.startTime;\n }\n });\n\n // LCP is finalized on first user input\n const stopListening = () => {\n reportLCP();\n observer.disconnect();\n removeEventListeners();\n };\n\n // User input events that finalize LCP\n const onInput = () => stopListening();\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n stopListening();\n }\n };\n\n const removeEventListeners = () => {\n document.removeEventListener('visibilitychange', onVisibilityChange);\n // Use capture to catch events before they're handled\n ['keydown', 'click', 'pointerdown'].forEach((type) => {\n document.removeEventListener(type, onInput, { capture: true } as EventListenerOptions);\n });\n };\n\n observer.observe({ type: 'largest-contentful-paint', buffered: true });\n\n // Listen for events that finalize LCP\n document.addEventListener('visibilitychange', onVisibilityChange);\n ['keydown', 'click', 'pointerdown'].forEach((type) => {\n document.addEventListener(type, onInput, { capture: true, once: true });\n });\n\n return () => {\n if (!hasReported && lcpValue > 0) {\n reportLCP();\n }\n observer.disconnect();\n removeEventListeners();\n };\n}\n\n/**\n * FID - First Input Delay\n * Measures interactivity (deprecated in favor of INP)\n */\nexport function observeFID(callback: (metric: Metric) => void): () => void {\n if (!isSupported('first-input')) return () => {};\n\n const observer = new PerformanceObserver((list) => {\n const entry = list.getEntries()[0] as PerformanceEventTiming | undefined;\n if (entry) {\n const value = entry.processingStart - entry.startTime;\n callback(createMetric('FID', value));\n observer.disconnect();\n }\n });\n\n observer.observe({ type: 'first-input', buffered: true });\n return () => observer.disconnect();\n}\n\n/**\n * INP - Interaction to Next Paint\n * Measures responsiveness (replaced FID as Core Web Vital)\n *\n * INP tracks the worst interaction latency during the page lifecycle.\n * Reports on visibility change (hidden) or pagehide.\n *\n * Simplified algorithm (vs web-vitals p98):\n * - Tracks max interaction duration\n * - Only counts discrete events (click, keydown, pointerdown)\n * - Reports once on page hide\n *\n * @see https://web.dev/inp/\n * @see https://github.com/GoogleChrome/web-vitals/blob/main/src/onINP.ts\n */\nexport function observeINP(callback: (metric: Metric) => void): () => void {\n if (!isSupported('event')) return () => {};\n\n let maxINP = 0;\n let prevReportedValue = 0;\n let hasReported = false;\n\n // Track interactions by interactionId to avoid counting same interaction twice\n const processedInteractions = new Set<number>();\n\n const reportINP = () => {\n if (maxINP > 0 && !hasReported) {\n callback(createMetric('INP', maxINP, prevReportedValue));\n prevReportedValue = maxINP;\n hasReported = true;\n }\n };\n\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n const eventEntry = entry as PerformanceEventTimingExtended;\n\n // Only count discrete events (not scroll, mousemove, etc.)\n // interactionId is 0 or undefined for non-interaction events\n if (!eventEntry.interactionId) continue;\n\n // Avoid counting the same interaction multiple times\n // (e.g., pointerdown + pointerup for same click)\n if (processedInteractions.has(eventEntry.interactionId)) continue;\n processedInteractions.add(eventEntry.interactionId);\n\n // Track maximum interaction duration\n if (eventEntry.duration > maxINP) {\n maxINP = eventEntry.duration;\n }\n }\n });\n\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n reportINP();\n }\n };\n\n const onPageHide = () => {\n reportINP();\n };\n\n observer.observe({ type: 'event', buffered: true, durationThreshold: 16 } as PerformanceObserverInit);\n\n document.addEventListener('visibilitychange', onVisibilityChange);\n window.addEventListener('pagehide', onPageHide);\n\n return () => {\n observer.disconnect();\n document.removeEventListener('visibilitychange', onVisibilityChange);\n window.removeEventListener('pagehide', onPageHide);\n\n if (!hasReported && maxINP > 0) {\n reportINP();\n }\n };\n}\n\n/**\n * FCP - First Contentful Paint\n * Measures when first content is painted\n */\nexport function observeFCP(callback: (metric: Metric) => void): () => void {\n if (!isSupported('paint')) return () => {};\n\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n if (entry.name === 'first-contentful-paint') {\n callback(createMetric('FCP', entry.startTime));\n observer.disconnect();\n }\n }\n });\n\n observer.observe({ type: 'paint', buffered: true });\n return () => observer.disconnect();\n}\n\n/**\n * TTFB - Time to First Byte\n * Measures server response time from navigation start\n *\n * TTFB = responseStart - activationStart (or 0 if no bfcache)\n *\n * activationStart is non-zero for bfcache restores, ensuring we measure\n * the actual server response time, not time in cache.\n *\n * @see https://web.dev/ttfb/\n * @see https://github.com/GoogleChrome/web-vitals/blob/main/src/onTTFB.ts\n */\nexport function observeTTFB(callback: (metric: Metric) => void): () => void {\n if (!isSupported('navigation')) return () => {};\n\n const observer = new PerformanceObserver((list) => {\n const entry = list.getEntries()[0] as PerformanceNavigationTiming | undefined;\n if (entry) {\n // activationStart is non-zero for bfcache restores\n const activationStart = (entry as PerformanceNavigationTiming & { activationStart?: number }).activationStart ?? 0;\n const value = Math.max(entry.responseStart - activationStart, 0);\n callback(createMetric('TTFB', value));\n observer.disconnect();\n }\n });\n\n observer.observe({ type: 'navigation', buffered: true });\n return () => observer.disconnect();\n}\n\n// Type for layout-shift entries (not in standard lib)\ninterface LayoutShiftEntry extends PerformanceEntry {\n value: number;\n hadRecentInput: boolean;\n}\n\n// Extended PerformanceEventTiming with interactionId (not in all TS versions)\ninterface PerformanceEventTimingExtended extends PerformanceEventTiming {\n interactionId?: number;\n}\n\n// Export observer map for easy access\nexport const vitalObservers = {\n CLS: observeCLS,\n LCP: observeLCP,\n FID: observeFID,\n INP: observeINP,\n FCP: observeFCP,\n TTFB: observeTTFB,\n} as const;\n\nexport type { Metric, MetricName, MetricRating };\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Web Vitals collection - NO external dependencies\n * Uses PerformanceObserver API directly\n */\n\nimport type { MetricName, MetricRating, Metric } from '../types/index.js';\n\n// Google's official thresholds\nconst THRESHOLDS: Record<MetricName, [number, number]> = {\n CLS: [0.1, 0.25],\n LCP: [2500, 4000],\n FID: [100, 300],\n INP: [200, 500],\n FCP: [1800, 3000],\n TTFB: [800, 1800],\n};\n\nfunction getRating(name: MetricName, value: number): MetricRating {\n const [good, poor] = THRESHOLDS[name];\n if (value <= good) return 'good';\n if (value <= poor) return 'needs-improvement';\n return 'poor';\n}\n\nfunction createMetric(\n name: MetricName,\n value: number,\n prevValue: number = 0\n): Metric {\n return {\n name,\n value,\n rating: getRating(name, value),\n delta: value - prevValue,\n timestamp: Date.now(),\n };\n}\n\n// Check if PerformanceObserver supports given entry type\nfunction isSupported(type: string): boolean {\n if (typeof PerformanceObserver === 'undefined') return false;\n try {\n return PerformanceObserver.supportedEntryTypes?.includes(type) ?? false;\n } catch {\n return false;\n }\n}\n\n/**\n * CLS - Cumulative Layout Shift\n * Measures visual stability using session windows algorithm (web-vitals standard)\n *\n * Session window rules:\n * - Max duration: 5 seconds\n * - Max gap between shifts: 1 second\n * - Reports final value on visibility change (hidden) or pagehide\n *\n * @see https://web.dev/cls/#what-is-a-good-cls-score\n * @see https://github.com/GoogleChrome/web-vitals/blob/main/src/onCLS.ts\n */\nexport function observeCLS(callback: (metric: Metric) => void): () => void {\n if (!isSupported('layout-shift')) return () => {};\n\n // Session window state\n let sessionValue = 0;\n let sessionStart = -1;\n let lastEntryTime = 0;\n\n // Track max session for final CLS value\n let maxSessionValue = 0;\n let prevReportedValue = 0;\n let hasReported = false;\n\n const SESSION_GAP = 1000; // 1 second max gap\n const SESSION_MAX = 5000; // 5 seconds max duration\n\n const processEntries = (entries: PerformanceEntryList) => {\n for (const entry of entries) {\n const shift = entry as LayoutShiftEntry;\n\n // Ignore shifts with recent user input (clicks, taps, key presses)\n if (shift.hadRecentInput) continue;\n\n const entryTime = shift.startTime;\n\n // Start new session if:\n // 1. First entry ever\n // 2. Gap from last entry > 1 second\n // 3. Session duration would exceed 5 seconds\n if (\n sessionStart === -1 ||\n entryTime - lastEntryTime > SESSION_GAP ||\n entryTime - sessionStart > SESSION_MAX\n ) {\n // Start new session\n sessionStart = entryTime;\n sessionValue = 0;\n }\n\n // Accumulate shift in current session\n sessionValue += shift.value;\n lastEntryTime = entryTime;\n\n // Track maximum session value (this is the CLS score)\n if (sessionValue > maxSessionValue) {\n maxSessionValue = sessionValue;\n }\n }\n };\n\n const reportCLS = () => {\n // Only report if we have a value and it changed\n if (maxSessionValue > 0 && maxSessionValue !== prevReportedValue) {\n callback(createMetric('CLS', maxSessionValue, prevReportedValue));\n prevReportedValue = maxSessionValue;\n hasReported = true;\n }\n };\n\n // Report on visibility change (user switches tab or minimizes)\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n reportCLS();\n }\n };\n\n // Report on page unload (closing tab, navigation away)\n const onPageHide = () => {\n reportCLS();\n };\n\n const observer = new PerformanceObserver((list) => {\n processEntries(list.getEntries());\n });\n\n observer.observe({ type: 'layout-shift', buffered: true });\n\n // Listen for lifecycle events to report final CLS\n document.addEventListener('visibilitychange', onVisibilityChange);\n window.addEventListener('pagehide', onPageHide);\n\n // Cleanup function\n return () => {\n observer.disconnect();\n document.removeEventListener('visibilitychange', onVisibilityChange);\n window.removeEventListener('pagehide', onPageHide);\n\n // Report final value on disconnect if not already reported\n if (!hasReported && maxSessionValue > 0) {\n reportCLS();\n }\n };\n}\n\n/**\n * LCP - Largest Contentful Paint\n * Measures loading performance\n *\n * LCP is finalized when:\n * - User interacts with the page (click, keydown, scroll, etc.)\n * - Page becomes hidden (visibility change)\n *\n * @see https://web.dev/lcp/\n * @see https://github.com/GoogleChrome/web-vitals/blob/main/src/onLCP.ts\n */\nexport function observeLCP(callback: (metric: Metric) => void): () => void {\n if (!isSupported('largest-contentful-paint')) return () => {};\n\n let lcpValue = 0;\n let prevReportedValue = 0;\n let hasReported = false;\n\n const reportLCP = () => {\n if (lcpValue > 0 && !hasReported) {\n callback(createMetric('LCP', lcpValue, prevReportedValue));\n prevReportedValue = lcpValue;\n hasReported = true;\n }\n };\n\n const observer = new PerformanceObserver((list) => {\n const entries = list.getEntries();\n // LCP can have multiple entries, track the last one\n const lastEntry = entries[entries.length - 1];\n if (lastEntry) {\n lcpValue = lastEntry.startTime;\n }\n });\n\n // LCP is finalized on first user input\n const stopListening = () => {\n reportLCP();\n observer.disconnect();\n removeEventListeners();\n };\n\n // User input events that finalize LCP\n const onInput = () => stopListening();\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n stopListening();\n }\n };\n\n const removeEventListeners = () => {\n document.removeEventListener('visibilitychange', onVisibilityChange);\n // Use capture to catch events before they're handled\n ['keydown', 'click', 'pointerdown'].forEach((type) => {\n document.removeEventListener(type, onInput, { capture: true } as EventListenerOptions);\n });\n };\n\n observer.observe({ type: 'largest-contentful-paint', buffered: true });\n\n // Listen for events that finalize LCP\n document.addEventListener('visibilitychange', onVisibilityChange);\n ['keydown', 'click', 'pointerdown'].forEach((type) => {\n document.addEventListener(type, onInput, { capture: true, once: true });\n });\n\n return () => {\n if (!hasReported && lcpValue > 0) {\n reportLCP();\n }\n observer.disconnect();\n removeEventListeners();\n };\n}\n\n/**\n * FID - First Input Delay\n * Measures interactivity (deprecated in favor of INP)\n */\nexport function observeFID(callback: (metric: Metric) => void): () => void {\n if (!isSupported('first-input')) return () => {};\n\n const observer = new PerformanceObserver((list) => {\n const entry = list.getEntries()[0] as PerformanceEventTiming | undefined;\n if (entry) {\n const value = entry.processingStart - entry.startTime;\n callback(createMetric('FID', value));\n observer.disconnect();\n }\n });\n\n observer.observe({ type: 'first-input', buffered: true });\n return () => observer.disconnect();\n}\n\n/**\n * INP - Interaction to Next Paint\n * Measures responsiveness (replaced FID as Core Web Vital)\n *\n * INP tracks the worst interaction latency during the page lifecycle.\n * Reports on visibility change (hidden) or pagehide.\n *\n * Simplified algorithm (vs web-vitals p98):\n * - Tracks max interaction duration\n * - Only counts discrete events (click, keydown, pointerdown)\n * - Reports once on page hide\n *\n * @see https://web.dev/inp/\n * @see https://github.com/GoogleChrome/web-vitals/blob/main/src/onINP.ts\n */\nexport function observeINP(callback: (metric: Metric) => void): () => void {\n if (!isSupported('event')) return () => {};\n\n let maxINP = 0;\n let prevReportedValue = 0;\n let hasReported = false;\n\n // Track interactions by interactionId to avoid counting same interaction twice\n const processedInteractions = new Set<number>();\n\n const reportINP = () => {\n if (maxINP > 0 && !hasReported) {\n callback(createMetric('INP', maxINP, prevReportedValue));\n prevReportedValue = maxINP;\n hasReported = true;\n }\n };\n\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n const eventEntry = entry as PerformanceEventTimingExtended;\n\n // Only count discrete events (not scroll, mousemove, etc.)\n // interactionId is 0 or undefined for non-interaction events\n if (!eventEntry.interactionId) continue;\n\n // Avoid counting the same interaction multiple times\n // (e.g., pointerdown + pointerup for same click)\n if (processedInteractions.has(eventEntry.interactionId)) continue;\n\n // Prevent unbounded growth in long-lived SPAs\n if (processedInteractions.size >= 1000) {\n processedInteractions.clear();\n }\n processedInteractions.add(eventEntry.interactionId);\n\n // Track maximum interaction duration\n if (eventEntry.duration > maxINP) {\n maxINP = eventEntry.duration;\n }\n }\n });\n\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n reportINP();\n }\n };\n\n const onPageHide = () => {\n reportINP();\n };\n\n observer.observe({ type: 'event', buffered: true, durationThreshold: 16 } as PerformanceObserverInit);\n\n document.addEventListener('visibilitychange', onVisibilityChange);\n window.addEventListener('pagehide', onPageHide);\n\n return () => {\n observer.disconnect();\n document.removeEventListener('visibilitychange', onVisibilityChange);\n window.removeEventListener('pagehide', onPageHide);\n\n if (!hasReported && maxINP > 0) {\n reportINP();\n }\n };\n}\n\n/**\n * FCP - First Contentful Paint\n * Measures when first content is painted\n */\nexport function observeFCP(callback: (metric: Metric) => void): () => void {\n if (!isSupported('paint')) return () => {};\n\n const observer = new PerformanceObserver((list) => {\n for (const entry of list.getEntries()) {\n if (entry.name === 'first-contentful-paint') {\n callback(createMetric('FCP', entry.startTime));\n observer.disconnect();\n }\n }\n });\n\n observer.observe({ type: 'paint', buffered: true });\n return () => observer.disconnect();\n}\n\n/**\n * TTFB - Time to First Byte\n * Measures server response time from navigation start\n *\n * TTFB = responseStart - activationStart (or 0 if no bfcache)\n *\n * activationStart is non-zero for bfcache restores, ensuring we measure\n * the actual server response time, not time in cache.\n *\n * @see https://web.dev/ttfb/\n * @see https://github.com/GoogleChrome/web-vitals/blob/main/src/onTTFB.ts\n */\nexport function observeTTFB(callback: (metric: Metric) => void): () => void {\n if (!isSupported('navigation')) return () => {};\n\n const observer = new PerformanceObserver((list) => {\n const entry = list.getEntries()[0] as PerformanceNavigationTiming | undefined;\n if (entry) {\n // activationStart is non-zero for bfcache restores\n const activationStart = (entry as PerformanceNavigationTiming & { activationStart?: number }).activationStart ?? 0;\n const value = Math.max(entry.responseStart - activationStart, 0);\n callback(createMetric('TTFB', value));\n observer.disconnect();\n }\n });\n\n observer.observe({ type: 'navigation', buffered: true });\n return () => observer.disconnect();\n}\n\n// Type for layout-shift entries (not in standard lib)\ninterface LayoutShiftEntry extends PerformanceEntry {\n value: number;\n hadRecentInput: boolean;\n}\n\n// Extended PerformanceEventTiming with interactionId (not in all TS versions)\ninterface PerformanceEventTimingExtended extends PerformanceEventTiming {\n interactionId?: number;\n}\n\n// Export observer map for easy access\nexport const vitalObservers = {\n CLS: observeCLS,\n LCP: observeLCP,\n FID: observeFID,\n INP: observeINP,\n FCP: observeFCP,\n TTFB: observeTTFB,\n} as const;\n\nexport type { Metric, MetricName, MetricRating };\n"],
5
+ "mappings": "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",
6
6
  "names": ["THRESHOLDS", "getRating", "name", "value", "good", "poor", "createMetric", "prevValue", "isSupported", "type", "observeCLS", "callback", "sessionValue", "sessionStart", "lastEntryTime", "maxSessionValue", "prevReportedValue", "hasReported", "SESSION_GAP", "SESSION_MAX", "processEntries", "entries", "entry", "shift", "entryTime", "reportCLS", "onVisibilityChange", "onPageHide", "observer", "list", "observeLCP", "lcpValue", "reportLCP", "lastEntry", "stopListening", "removeEventListeners", "onInput", "observeFID", "observeINP", "maxINP", "processedInteractions", "reportINP", "eventEntry", "observeFCP", "observeTTFB", "activationStart", "vitalObservers"]
7
7
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Beacon-based transport using navigator.sendBeacon
3
+ * Guarantees delivery even during page unload
4
+ */
5
+ import type { Transport, BeaconTransportOptions } from '../types/index.js';
6
+ /**
7
+ * Create a beacon-based transport
8
+ * Uses navigator.sendBeacon for reliable delivery during page unload
9
+ *
10
+ * @param endpoint - URL to send events to
11
+ * @param options - Beacon transport options
12
+ */
13
+ export declare function createBeaconTransport(endpoint: string, options?: BeaconTransportOptions): Transport;
14
+ //# sourceMappingURL=beacon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beacon.d.ts","sourceRoot":"","sources":["../../src/transport/beacon.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAgB,MAAM,mBAAmB,CAAC;AAKzF;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,sBAA2B,GACnC,SAAS,CA8CX"}
@@ -0,0 +1,2 @@
1
+ function l(i,r={}){const s=r.maxPayloadSize??61440;function e(n,o){if(n.length===0)return;if(o>=20){console.error("[svoose] Beacon chunk depth limit reached, dropping events");return}const t=JSON.stringify(n);if(t.length>s){if(n.length===1){console.error("[svoose] Single event exceeds maxPayloadSize, dropping");return}const a=Math.floor(n.length/2);e(n.slice(0,a),o+1),e(n.slice(a),o+1);return}if(typeof navigator>"u"||!navigator.sendBeacon)return;const c=new Blob([t],{type:"application/json"});navigator.sendBeacon(i,c)||r.onError?.(new Error("sendBeacon failed"))}return{send(n){n.length!==0&&(typeof navigator>"u"||e(n,0))}}}export{l as createBeaconTransport};
2
+ //# sourceMappingURL=beacon.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/transport/beacon.ts"],
4
+ "sourcesContent": ["/**\n * Beacon-based transport using navigator.sendBeacon\n * Guarantees delivery even during page unload\n */\n\nimport type { Transport, BeaconTransportOptions, ObserveEvent } from '../types/index.js';\n\nconst DEFAULT_MAX_PAYLOAD_SIZE = 60 * 1024; // 60KB\nconst MAX_CHUNK_DEPTH = 20;\n\n/**\n * Create a beacon-based transport\n * Uses navigator.sendBeacon for reliable delivery during page unload\n *\n * @param endpoint - URL to send events to\n * @param options - Beacon transport options\n */\nexport function createBeaconTransport(\n endpoint: string,\n options: BeaconTransportOptions = {}\n): Transport {\n const maxPayloadSize = options.maxPayloadSize ?? DEFAULT_MAX_PAYLOAD_SIZE;\n\n function sendChunk(events: ObserveEvent[], depth: number): void {\n if (events.length === 0) return;\n\n if (depth >= MAX_CHUNK_DEPTH) {\n console.error('[svoose] Beacon chunk depth limit reached, dropping events');\n return;\n }\n\n const payload = JSON.stringify(events);\n\n if (payload.length > maxPayloadSize) {\n if (events.length === 1) {\n console.error('[svoose] Single event exceeds maxPayloadSize, dropping');\n return;\n }\n\n // Split in half and recurse\n const mid = Math.floor(events.length / 2);\n sendChunk(events.slice(0, mid), depth + 1);\n sendChunk(events.slice(mid), depth + 1);\n return;\n }\n\n if (typeof navigator === 'undefined' || !navigator.sendBeacon) return;\n\n const blob = new Blob([payload], { type: 'application/json' });\n const success = navigator.sendBeacon(endpoint, blob);\n\n if (!success) {\n options.onError?.(new Error('sendBeacon failed'));\n }\n }\n\n return {\n send(events) {\n if (events.length === 0) return;\n\n // SSR guard\n if (typeof navigator === 'undefined') return;\n\n sendChunk(events, 0);\n },\n };\n}\n"],
5
+ "mappings": "AAiBO,SAASA,EACdC,EACAC,EAAkC,CAAC,EACxB,CACX,MAAMC,EAAiBD,EAAQ,gBAAkB,MAEjD,SAASE,EAAUC,EAAwBC,EAAqB,CAC9D,GAAID,EAAO,SAAW,EAAG,OAEzB,GAAIC,GAAS,GAAiB,CAC5B,QAAQ,MAAM,4DAA4D,EAC1E,MACF,CAEA,MAAMC,EAAU,KAAK,UAAUF,CAAM,EAErC,GAAIE,EAAQ,OAASJ,EAAgB,CACnC,GAAIE,EAAO,SAAW,EAAG,CACvB,QAAQ,MAAM,wDAAwD,EACtE,MACF,CAGA,MAAMG,EAAM,KAAK,MAAMH,EAAO,OAAS,CAAC,EACxCD,EAAUC,EAAO,MAAM,EAAGG,CAAG,EAAGF,EAAQ,CAAC,EACzCF,EAAUC,EAAO,MAAMG,CAAG,EAAGF,EAAQ,CAAC,EACtC,MACF,CAEA,GAAI,OAAO,UAAc,KAAe,CAAC,UAAU,WAAY,OAE/D,MAAMG,EAAO,IAAI,KAAK,CAACF,CAAO,EAAG,CAAE,KAAM,kBAAmB,CAAC,EAC7C,UAAU,WAAWN,EAAUQ,CAAI,GAGjDP,EAAQ,UAAU,IAAI,MAAM,mBAAmB,CAAC,CAEpD,CAEA,MAAO,CACL,KAAKG,EAAQ,CACPA,EAAO,SAAW,IAGlB,OAAO,UAAc,KAEzBD,EAAUC,EAAQ,CAAC,EACrB,CACF,CACF",
6
+ "names": ["createBeaconTransport", "endpoint", "options", "maxPayloadSize", "sendChunk", "events", "depth", "payload", "mid", "blob"]
7
+ }
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Fetch-based transport with sendBeacon fallback
2
+ * Fetch-based transport
3
3
  */
4
4
  import type { Transport, TransportOptions } from '../types/index.js';
5
5
  /**
6
6
  * Create a fetch-based transport
7
- * Uses sendBeacon for page unload, fetch otherwise
7
+ * Always uses fetch with keepalive: true
8
8
  *
9
9
  * @param endpoint - URL to send events to
10
10
  * @param options - Transport options (headers, error callback)
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/transport/fetch.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,SAAS,CAwCX;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,SAAS,CAYpF"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/transport/fetch.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,SAAS,CAoBX;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,SAAS,CAYpF"}
@@ -1,2 +1,2 @@
1
- function a(o,n={}){return{async send(e){if(e.length!==0)try{const t=JSON.stringify(e);if(typeof document<"u"&&document.visibilityState==="hidden"&&typeof navigator<"u"&&navigator.sendBeacon){const r=new Blob([t],{type:"application/json"});if(!navigator.sendBeacon(o,r))throw new Error("sendBeacon failed");return}await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",...n.headers},body:t,keepalive:!0})}catch(t){n.onError?.(t)}}}}function i(o={}){return{async send(n){for(const e of n)o.pretty?console.log("[svoose]",JSON.stringify(e,null,2)):console.log("[svoose]",e)}}}export{i as createConsoleTransport,a as createFetchTransport};
1
+ function n(o,e={}){return{async send(r){if(r.length!==0)try{await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(r),keepalive:!0})}catch(t){e.onError?.(t)}}}}function s(o={}){return{async send(e){for(const r of e)o.pretty?console.log("[svoose]",JSON.stringify(r,null,2)):console.log("[svoose]",r)}}}export{s as createConsoleTransport,n as createFetchTransport};
2
2
  //# sourceMappingURL=fetch.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/transport/fetch.ts"],
4
- "sourcesContent": ["/**\n * Fetch-based transport with sendBeacon fallback\n */\n\nimport type { Transport, TransportOptions } from '../types/index.js';\n\n/**\n * Create a fetch-based transport\n * Uses sendBeacon for page unload, fetch otherwise\n *\n * @param endpoint - URL to send events to\n * @param options - Transport options (headers, error callback)\n */\nexport function createFetchTransport(\n endpoint: string,\n options: TransportOptions = {}\n): Transport {\n return {\n async send(events) {\n if (events.length === 0) return;\n\n try {\n const payload = JSON.stringify(events);\n\n // Use sendBeacon when page is hidden (e.g., user navigating away)\n // sendBeacon is more reliable for unload scenarios\n if (\n typeof document !== 'undefined' &&\n document.visibilityState === 'hidden' &&\n typeof navigator !== 'undefined' &&\n navigator.sendBeacon\n ) {\n const blob = new Blob([payload], { type: 'application/json' });\n const success = navigator.sendBeacon(endpoint, blob);\n if (!success) {\n throw new Error('sendBeacon failed');\n }\n return;\n }\n\n // Use fetch for normal operation\n await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: payload,\n // keepalive ensures request completes even if page is closed\n keepalive: true,\n });\n } catch (error) {\n options.onError?.(error as Error);\n }\n },\n };\n}\n\n/**\n * Create a console transport for development/debugging\n */\nexport function createConsoleTransport(options: { pretty?: boolean } = {}): Transport {\n return {\n async send(events) {\n for (const event of events) {\n if (options.pretty) {\n console.log('[svoose]', JSON.stringify(event, null, 2));\n } else {\n console.log('[svoose]', event);\n }\n }\n },\n };\n}\n"],
5
- "mappings": "AAaO,SAASA,EACdC,EACAC,EAA4B,CAAC,EAClB,CACX,MAAO,CACL,MAAM,KAAKC,EAAQ,CACjB,GAAIA,EAAO,SAAW,EAEtB,GAAI,CACF,MAAMC,EAAU,KAAK,UAAUD,CAAM,EAIrC,GACE,OAAO,SAAa,KACpB,SAAS,kBAAoB,UAC7B,OAAO,UAAc,KACrB,UAAU,WACV,CACA,MAAME,EAAO,IAAI,KAAK,CAACD,CAAO,EAAG,CAAE,KAAM,kBAAmB,CAAC,EAE7D,GAAI,CADY,UAAU,WAAWH,EAAUI,CAAI,EAEjD,MAAM,IAAI,MAAM,mBAAmB,EAErC,MACF,CAGA,MAAM,MAAMJ,EAAU,CACpB,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,GAAGC,EAAQ,OACb,EACA,KAAME,EAEN,UAAW,EACb,CAAC,CACH,OAASE,EAAO,CACdJ,EAAQ,UAAUI,CAAc,CAClC,CACF,CACF,CACF,CAKO,SAASC,EAAuBL,EAAgC,CAAC,EAAc,CACpF,MAAO,CACL,MAAM,KAAKC,EAAQ,CACjB,UAAWK,KAASL,EACdD,EAAQ,OACV,QAAQ,IAAI,WAAY,KAAK,UAAUM,EAAO,KAAM,CAAC,CAAC,EAEtD,QAAQ,IAAI,WAAYA,CAAK,CAGnC,CACF,CACF",
6
- "names": ["createFetchTransport", "endpoint", "options", "events", "payload", "blob", "error", "createConsoleTransport", "event"]
4
+ "sourcesContent": ["/**\n * Fetch-based transport\n */\n\nimport type { Transport, TransportOptions } from '../types/index.js';\n\n/**\n * Create a fetch-based transport\n * Always uses fetch with keepalive: true\n *\n * @param endpoint - URL to send events to\n * @param options - Transport options (headers, error callback)\n */\nexport function createFetchTransport(\n endpoint: string,\n options: TransportOptions = {}\n): Transport {\n return {\n async send(events) {\n if (events.length === 0) return;\n\n try {\n await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...options.headers,\n },\n body: JSON.stringify(events),\n keepalive: true,\n });\n } catch (error) {\n options.onError?.(error as Error);\n }\n },\n };\n}\n\n/**\n * Create a console transport for development/debugging\n */\nexport function createConsoleTransport(options: { pretty?: boolean } = {}): Transport {\n return {\n async send(events) {\n for (const event of events) {\n if (options.pretty) {\n console.log('[svoose]', JSON.stringify(event, null, 2));\n } else {\n console.log('[svoose]', event);\n }\n }\n },\n };\n}\n"],
5
+ "mappings": "AAaO,SAASA,EACdC,EACAC,EAA4B,CAAC,EAClB,CACX,MAAO,CACL,MAAM,KAAKC,EAAQ,CACjB,GAAIA,EAAO,SAAW,EAEtB,GAAI,CACF,MAAM,MAAMF,EAAU,CACpB,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,GAAGC,EAAQ,OACb,EACA,KAAM,KAAK,UAAUC,CAAM,EAC3B,UAAW,EACb,CAAC,CACH,OAASC,EAAO,CACdF,EAAQ,UAAUE,CAAc,CAClC,CACF,CACF,CACF,CAKO,SAASC,EAAuBH,EAAgC,CAAC,EAAc,CACpF,MAAO,CACL,MAAM,KAAKC,EAAQ,CACjB,UAAWG,KAASH,EACdD,EAAQ,OACV,QAAQ,IAAI,WAAY,KAAK,UAAUI,EAAO,KAAM,CAAC,CAAC,EAEtD,QAAQ,IAAI,WAAYA,CAAK,CAGnC,CACF,CACF",
6
+ "names": ["createFetchTransport", "endpoint", "options", "events", "error", "createConsoleTransport", "event"]
7
7
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Hybrid transport — fetch by default, beacon on page unload
3
+ */
4
+ import type { HybridTransport, HybridTransportOptions } from '../types/index.js';
5
+ /**
6
+ * Create a hybrid transport that switches between fetch and beacon
7
+ * based on page lifecycle events
8
+ *
9
+ * @param endpoint - URL to send events to
10
+ * @param options - Hybrid transport options
11
+ */
12
+ export declare function createHybridTransport(endpoint: string, options?: HybridTransportOptions): HybridTransport;
13
+ //# sourceMappingURL=hybrid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/transport/hybrid.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAgB,MAAM,mBAAmB,CAAC;AAI/F;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,sBAA2B,GACnC,eAAe,CA2CjB"}
@@ -0,0 +1,2 @@
1
+ import{createFetchTransport as b}from"./fetch.js";import{createBeaconTransport as l}from"./beacon.js";function v(n,e={}){const d=e.default??"fetch",s=e.onUnload??"beacon",a=b(n,e),c=l(n,e);let t=!1;const r=()=>{t=!0},o=()=>{document.visibilityState==="hidden"?t=!0:document.visibilityState==="visible"&&(t=!1)};typeof window<"u"&&(window.addEventListener("beforeunload",r),document.addEventListener("visibilitychange",o));function f(){return(t?s:d)==="beacon"?c:a}return{send(i){return f().send(i)},destroy(){typeof window<"u"&&(window.removeEventListener("beforeunload",r),document.removeEventListener("visibilitychange",o))}}}export{v as createHybridTransport};
2
+ //# sourceMappingURL=hybrid.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/transport/hybrid.ts"],
4
+ "sourcesContent": ["/**\n * Hybrid transport \u2014 fetch by default, beacon on page unload\n */\n\nimport type { HybridTransport, HybridTransportOptions, ObserveEvent } from '../types/index.js';\nimport { createFetchTransport } from './fetch.js';\nimport { createBeaconTransport } from './beacon.js';\n\n/**\n * Create a hybrid transport that switches between fetch and beacon\n * based on page lifecycle events\n *\n * @param endpoint - URL to send events to\n * @param options - Hybrid transport options\n */\nexport function createHybridTransport(\n endpoint: string,\n options: HybridTransportOptions = {}\n): HybridTransport {\n const defaultMode = options.default ?? 'fetch';\n const unloadMode = options.onUnload ?? 'beacon';\n\n const fetchTransport = createFetchTransport(endpoint, options);\n const beaconTransport = createBeaconTransport(endpoint, options);\n\n let isUnloading = false;\n\n const onBeforeUnload = () => {\n isUnloading = true;\n };\n\n const onVisibilityChange = () => {\n if (document.visibilityState === 'hidden') {\n isUnloading = true;\n } else if (document.visibilityState === 'visible') {\n isUnloading = false;\n }\n };\n\n // SSR guard\n if (typeof window !== 'undefined') {\n window.addEventListener('beforeunload', onBeforeUnload);\n document.addEventListener('visibilitychange', onVisibilityChange);\n }\n\n function getActiveTransport() {\n const mode = isUnloading ? unloadMode : defaultMode;\n return mode === 'beacon' ? beaconTransport : fetchTransport;\n }\n\n return {\n send(events: ObserveEvent[]) {\n return getActiveTransport().send(events);\n },\n destroy() {\n if (typeof window !== 'undefined') {\n window.removeEventListener('beforeunload', onBeforeUnload);\n document.removeEventListener('visibilitychange', onVisibilityChange);\n }\n },\n };\n}\n"],
5
+ "mappings": "AAKA,OAAS,wBAAAA,MAA4B,aACrC,OAAS,yBAAAC,MAA6B,cAS/B,SAASC,EACdC,EACAC,EAAkC,CAAC,EAClB,CACjB,MAAMC,EAAcD,EAAQ,SAAW,QACjCE,EAAaF,EAAQ,UAAY,SAEjCG,EAAiBP,EAAqBG,EAAUC,CAAO,EACvDI,EAAkBP,EAAsBE,EAAUC,CAAO,EAE/D,IAAIK,EAAc,GAElB,MAAMC,EAAiB,IAAM,CAC3BD,EAAc,EAChB,EAEME,EAAqB,IAAM,CAC3B,SAAS,kBAAoB,SAC/BF,EAAc,GACL,SAAS,kBAAoB,YACtCA,EAAc,GAElB,EAGI,OAAO,OAAW,MACpB,OAAO,iBAAiB,eAAgBC,CAAc,EACtD,SAAS,iBAAiB,mBAAoBC,CAAkB,GAGlE,SAASC,GAAqB,CAE5B,OADaH,EAAcH,EAAaD,KACxB,SAAWG,EAAkBD,CAC/C,CAEA,MAAO,CACL,KAAKM,EAAwB,CAC3B,OAAOD,EAAmB,EAAE,KAAKC,CAAM,CACzC,EACA,SAAU,CACJ,OAAO,OAAW,MACpB,OAAO,oBAAoB,eAAgBH,CAAc,EACzD,SAAS,oBAAoB,mBAAoBC,CAAkB,EAEvE,CACF,CACF",
6
+ "names": ["createFetchTransport", "createBeaconTransport", "createHybridTransport", "endpoint", "options", "defaultMode", "unloadMode", "fetchTransport", "beaconTransport", "isUnloading", "onBeforeUnload", "onVisibilityChange", "getActiveTransport", "events"]
7
+ }
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Transport exports
3
3
  */
4
- export type { Transport, TransportOptions } from './transport.js';
4
+ export type { Transport, TransportOptions, BeaconTransportOptions, HybridTransportOptions, HybridTransport } from './transport.js';
5
5
  export { createFetchTransport, createConsoleTransport } from './fetch.js';
6
+ export { createBeaconTransport } from './beacon.js';
7
+ export { createHybridTransport } from './hybrid.js';
6
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transport/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transport/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACnI,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC"}
@@ -1,2 +1,2 @@
1
- import{createFetchTransport as o,createConsoleTransport as e}from"./fetch.js";export{e as createConsoleTransport,o as createFetchTransport};
1
+ import{createFetchTransport as t,createConsoleTransport as p}from"./fetch.js";import{createBeaconTransport as a}from"./beacon.js";import{createHybridTransport as s}from"./hybrid.js";export{a as createBeaconTransport,p as createConsoleTransport,t as createFetchTransport,s as createHybridTransport};
2
2
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/transport/index.ts"],
4
- "sourcesContent": ["/**\n * Transport exports\n */\n\nexport type { Transport, TransportOptions } from './transport.js';\nexport { createFetchTransport, createConsoleTransport } from './fetch.js';\n"],
5
- "mappings": "AAKA,OAAS,wBAAAA,EAAsB,0BAAAC,MAA8B",
6
- "names": ["createFetchTransport", "createConsoleTransport"]
4
+ "sourcesContent": ["/**\n * Transport exports\n */\n\nexport type { Transport, TransportOptions, BeaconTransportOptions, HybridTransportOptions, HybridTransport } from './transport.js';\nexport { createFetchTransport, createConsoleTransport } from './fetch.js';\nexport { createBeaconTransport } from './beacon.js';\nexport { createHybridTransport } from './hybrid.js';\n"],
5
+ "mappings": "AAKA,OAAS,wBAAAA,EAAsB,0BAAAC,MAA8B,aAC7D,OAAS,yBAAAC,MAA6B,cACtC,OAAS,yBAAAC,MAA6B",
6
+ "names": ["createFetchTransport", "createConsoleTransport", "createBeaconTransport", "createHybridTransport"]
7
7
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Transport interface - re-exported from types for convenience
3
3
  */
4
- export type { Transport, TransportOptions } from '../types/index.js';
4
+ export type { Transport, TransportOptions, BeaconTransportOptions, HybridTransportOptions, HybridTransport } from '../types/index.js';
5
5
  //# sourceMappingURL=transport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/transport/transport.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/transport/transport.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
@@ -53,6 +53,8 @@ export interface TransitionEvent {
53
53
  export interface CustomMetricEvent {
54
54
  type: 'custom';
55
55
  name: string;
56
+ metricKind?: 'counter' | 'gauge' | 'histogram';
57
+ value?: number;
56
58
  data: Record<string, unknown>;
57
59
  timestamp: number;
58
60
  sessionId?: string;
@@ -66,6 +68,21 @@ export interface TransportOptions {
66
68
  headers?: Record<string, string>;
67
69
  onError?: (error: Error) => void;
68
70
  }
71
+ export interface BeaconTransportOptions {
72
+ /** Maximum payload size in bytes before chunking (default: 60KB) */
73
+ maxPayloadSize?: number;
74
+ onError?: (error: Error) => void;
75
+ }
76
+ export interface HybridTransportOptions extends TransportOptions {
77
+ /** Default transport mode (default: 'fetch') */
78
+ default?: 'fetch' | 'beacon';
79
+ /** Transport mode during page unload (default: 'beacon') */
80
+ onUnload?: 'fetch' | 'beacon';
81
+ }
82
+ export interface HybridTransport extends Transport {
83
+ /** Remove event listeners and stop the transport */
84
+ destroy(): void;
85
+ }
69
86
  /**
70
87
  * Session configuration
71
88
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svoose",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Observability + State Machines for Svelte 5 — the goose that sees everything",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",