svoose 0.1.5 → 0.1.7

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 (38) hide show
  1. package/README.md +87 -13
  2. package/dist/index.d.ts +2 -1
  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 +6 -0
  7. package/dist/metrics/index.d.ts.map +1 -0
  8. package/dist/metrics/index.js +2 -0
  9. package/dist/metrics/index.js.map +7 -0
  10. package/dist/metrics/metric.d.ts +81 -0
  11. package/dist/metrics/metric.d.ts.map +1 -0
  12. package/dist/metrics/metric.js +2 -0
  13. package/dist/metrics/metric.js.map +7 -0
  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 +3 -3
  21. package/dist/observe/sampling.d.ts +2 -20
  22. package/dist/observe/sampling.d.ts.map +1 -1
  23. package/dist/observe/sampling.js +1 -1
  24. package/dist/observe/sampling.js.map +2 -2
  25. package/dist/observe/session.d.ts +2 -7
  26. package/dist/observe/session.d.ts.map +1 -1
  27. package/dist/observe/session.js.map +2 -2
  28. package/dist/svelte/index.svelte.js +2 -0
  29. package/dist/svelte/index.svelte.js.map +7 -0
  30. package/dist/transport/fetch.d.ts +1 -1
  31. package/dist/transport/fetch.d.ts.map +1 -1
  32. package/dist/transport/fetch.js +1 -1
  33. package/dist/transport/fetch.js.map +1 -1
  34. package/dist/transport/transport.d.ts +2 -10
  35. package/dist/transport/transport.d.ts.map +1 -1
  36. package/dist/types/index.d.ts +10 -1
  37. package/dist/types/index.d.ts.map +1 -1
  38. package/package.json +6 -1
package/README.md CHANGED
@@ -2,12 +2,15 @@
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**.
5
+ Lightweight observability + state machines for Svelte 5. Zero dependencies. Tree-shakeable. **< 5KB gzipped** (core ~3.5KB).
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()`, `counter()`, `gauge()`, `histogram()` (v0.1.6+)
12
+ - **Session Tracking** — automatic sessionId with timeout (v0.1.5+)
13
+ - **Sampling** — per-event-type rate limiting (v0.1.3+)
11
14
  - **State Machines** — minimal FSM with TypeScript inference
12
15
  - **Svelte 5 Native** — reactive `useMachine()` hook with $state runes
13
16
  - **Tree-shakeable** — pay only for what you use
@@ -77,9 +80,7 @@ const cleanup = observe({
77
80
  batchSize: 10,
78
81
  flushInterval: 5000,
79
82
 
80
- // Sampling (v0.1.3+)
81
- sampling: 0.1, // 10% of all events
82
- // or per-event-type (recommended)
83
+ // Sampling (v0.1.3+) — number or per-event-type config
83
84
  sampling: {
84
85
  vitals: 0.1, // 10% — sufficient for statistics
85
86
  errors: 1.0, // 100% — all errors matter
@@ -87,6 +88,9 @@ const cleanup = observe({
87
88
  transitions: 0.0, // disabled
88
89
  },
89
90
 
91
+ // Sessions (v0.1.5+)
92
+ session: true, // or { timeout: 30 * 60 * 1000, storage: 'sessionStorage' }
93
+
90
94
  // Debug
91
95
  debug: false,
92
96
  });
@@ -95,7 +99,7 @@ const cleanup = observe({
95
99
  cleanup();
96
100
  ```
97
101
 
98
- > **Note**: If neither `endpoint` nor `transport` is provided, defaults to `endpoint: '/api/observe'`.
102
+ > **Note**: If neither `endpoint` nor `transport` is provided, defaults to `endpoint: '/api/metrics'`.
99
103
 
100
104
  #### Sampling (v0.1.3+)
101
105
 
@@ -116,7 +120,6 @@ observe({
116
120
  errors: 1.0, // 100% — capture all errors
117
121
  custom: 0.5, // 50% of custom metrics
118
122
  transitions: 0.0, // disabled — no state machine events
119
- identify: 1.0, // 100% — always track user identification
120
123
  },
121
124
  });
122
125
  ```
@@ -198,6 +201,73 @@ observe({ vitals: ['CLS', 'LCP', 'INP'] });
198
201
 
199
202
  > **Note (v0.1.5 breaking change)**: CLS, LCP, and INP now report once per page lifecycle instead of on every update. This matches Chrome DevTools and Google Search Console behavior.
200
203
 
204
+ #### Custom Metrics (v0.1.6+)
205
+
206
+ Track custom events for analytics:
207
+
208
+ ```typescript
209
+ import { metric } from 'svoose';
210
+
211
+ // Basic usage
212
+ metric('checkout_started', { step: 1, cartTotal: 99.99 });
213
+ metric('button_clicked', { id: 'submit-btn' });
214
+ metric('feature_used', { name: 'dark_mode', enabled: true });
215
+ ```
216
+
217
+ ##### Metric Helpers (v0.1.7+)
218
+
219
+ Typed helpers for common metric patterns:
220
+
221
+ ```typescript
222
+ import { counter, gauge, histogram } from 'svoose';
223
+
224
+ // Counter — increments (default value: 1)
225
+ counter('page_views');
226
+ counter('items_purchased', 3, { category: 'electronics' });
227
+
228
+ // Gauge — point-in-time values
229
+ gauge('active_users', 42);
230
+ gauge('memory_usage_mb', 256, { heap: 'old' });
231
+
232
+ // Histogram — distribution values
233
+ histogram('response_time_ms', 123);
234
+ histogram('payload_size', 4096, { route: '/api/data' });
235
+ ```
236
+
237
+ All helpers emit events with top-level `metricKind` and `value` fields for easy backend processing.
238
+
239
+ ##### Typed Metrics (v0.1.7+)
240
+
241
+ Full TypeScript autocomplete for metric names and data shapes:
242
+
243
+ ```typescript
244
+ import { createTypedMetric } from 'svoose';
245
+
246
+ const track = createTypedMetric<{
247
+ checkout_started: { step: number; cartTotal: number };
248
+ button_clicked: { id: string };
249
+ }>();
250
+
251
+ track('checkout_started', { step: 1, cartTotal: 99.99 }); // ✅ autocomplete
252
+ track('button_clicked', { id: 'submit' }); // ✅
253
+ track('unknown_event', {}); // ❌ TypeScript error
254
+ ```
255
+
256
+ Events are automatically batched with other metrics. You can control the sampling rate:
257
+
258
+ ```typescript
259
+ observe({
260
+ endpoint: '/api/metrics',
261
+ sampling: {
262
+ custom: 0.5, // 50% of custom metrics
263
+ vitals: 0.1,
264
+ errors: 1.0,
265
+ },
266
+ });
267
+ ```
268
+
269
+ **Buffer behavior**: If `metric()` / `counter()` / `gauge()` / `histogram()` is called before `observe()`, events are buffered (max 100). They're automatically flushed when `observe()` initializes.
270
+
201
271
  ### `createMachine(config)`
202
272
 
203
273
  Create a state machine.
@@ -218,7 +288,7 @@ const machine = createMachine({
218
288
  },
219
289
  });
220
290
 
221
- // State & context (reactive in Svelte 5)
291
+ // State & context (use useMachine() from svoose/svelte for reactivity)
222
292
  machine.state; // 'off'
223
293
  machine.context; // { count: 0 }
224
294
 
@@ -349,12 +419,12 @@ Tree-shakeable — pay only for what you use:
349
419
 
350
420
  | Import | Size (gzip) |
351
421
  |--------|-------------|
352
- | `observe()` core | ~2.5 KB |
422
+ | `observe()` + vitals + errors + metrics | ~3.6 KB |
353
423
  | `createMachine()` only | ~0.8 KB |
354
- | Full bundle (v0.1.x) | ~3.5 KB |
355
- | Full production (v0.2.0+) | ~5.5 KB |
424
+ | Full bundle (v0.1.x) | ~4.7 KB |
425
+ | Full production (v0.2.0+) | ~6 KB |
356
426
 
357
- > Most apps only need `observe()` core (~2.5 KB). Compare: Sentry ~20KB, PostHog ~40KB.
427
+ > Most apps only need `observe()` core (~3.5 KB). Compare: Sentry ~20KB, PostHog ~40KB.
358
428
 
359
429
  ## TypeScript
360
430
 
@@ -501,8 +571,12 @@ const machine = createMachine({
501
571
 
502
572
  - **v0.1.3** ✅ — Sampling (per-event-type rate limiting)
503
573
  - **v0.1.4** ✅ — Hotfix (missing sampling.js)
504
- - **v0.1.5** — Session Tracking + CLS Session Windows fix
505
- - **v0.1.6-v0.1.10** — Custom metrics, retry, beacon transport, privacy
574
+ - **v0.1.5** — Session Tracking + CLS Session Windows fix
575
+ - **v0.1.6** — Custom metrics (`metric()` API)
576
+ - **v0.1.7** ✅ — Extended Metrics (counter/gauge/histogram + typed API)
577
+ - **v0.1.8** — Beacon + Hybrid Transport
578
+ - **v0.1.9** — Retry Logic
579
+ - **v0.1.10** — Privacy Utilities
506
580
  - **v0.2.0** — Production-Ready Observability + Bundle Restructure (modular entry points)
507
581
  - **v0.3.0** — SvelteKit Integration (Vite plugin, hooks, route tracking)
508
582
  - **v1.0.0** — Stable Release (Q1 2027)
package/dist/index.d.ts CHANGED
@@ -15,5 +15,6 @@ export { createFetchTransport, createConsoleTransport } from './transport/index.
15
15
  export type { Transport, TransportOptions } 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 type { VitalEvent, TransitionEvent, ObserveEvent, } from './types/index.js';
18
+ export { metric, counter, gauge, histogram, createTypedMetric } from './metrics/index.js';
19
+ export type { VitalEvent, TransitionEvent, CustomMetricEvent, ObserveEvent, } from './types/index.js';
19
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,YAAY,EACV,UAAU,EACV,eAAe,EACf,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,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,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 m,observeLCP as f,observeFID as b,observeINP as c,observeFCP as E,observeTTFB as T,vitalObservers as y}from"./observe/index.js";import{createFetchTransport as g,createConsoleTransport as l}from"./transport/index.js";import{createMachine as j,createEvent as S}from"./machine/index.js";export{l as createConsoleTransport,S as createEvent,g as createFetchTransport,j as createMachine,n as createSampler,s as eventTypeToSamplingType,t as observe,m as observeCLS,i as observeErrors,E as observeFCP,b as observeFID,c 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 p,registerMachineContext as a,unregisterMachineContext as v}from"./observe/index.js";import{observeCLS as x,observeLCP as c,observeFID as f,observeINP as b,observeFCP as E,observeTTFB as g,vitalObservers as T}from"./observe/index.js";import{createFetchTransport as d,createConsoleTransport as C}from"./transport/index.js";import{createMachine as M,createEvent as h}from"./machine/index.js";import{metric as S,counter as O,gauge as I,histogram as u,createTypedMetric as F}from"./metrics/index.js";export{O as counter,C as createConsoleTransport,h as createEvent,d as createFetchTransport,M as createMachine,n as createSampler,F as createTypedMetric,s as eventTypeToSamplingType,I as gauge,u as histogram,S as metric,t as observe,x as observeCLS,p as observeErrors,E as observeFCP,f as observeFID,b as observeINP,c as observeLCP,g as observeTTFB,a as registerMachineContext,v as unregisterMachineContext,T 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// Shared Types\n// ============================================\nexport type {\n VitalEvent,\n TransitionEvent,\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",
6
- "names": ["observe", "createSampler", "eventTypeToSamplingType", "observeErrors", "registerMachineContext", "unregisterMachineContext", "observeCLS", "observeLCP", "observeFID", "observeINP", "observeFCP", "observeTTFB", "vitalObservers", "createFetchTransport", "createConsoleTransport", "createMachine", "createEvent"]
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, 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,MAA8B,uBAM7D,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", "createMachine", "createEvent", "metric", "counter", "gauge", "histogram", "createTypedMetric"]
7
7
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Metrics module exports
3
+ */
4
+ export { metric, counter, gauge, histogram, setMetricEmitter, getMetricEmitter, _getPendingEventsCount, _clearPendingEvents, } from './metric.js';
5
+ export { createTypedMetric } from './typed.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,2 @@
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
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/metrics/index.ts"],
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
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Custom metrics module
3
+ *
4
+ * Provides metric() function for sending custom events with pending buffer support.
5
+ */
6
+ import type { ObserveEvent } from '../types/index.js';
7
+ /**
8
+ * Set the metric emitter function
9
+ * Called by observe() to wire up the metric system
10
+ *
11
+ * @param emit - The emit function from observe(), or null to disconnect
12
+ */
13
+ export declare function setMetricEmitter(emit: ((event: ObserveEvent) => void) | null): void;
14
+ /**
15
+ * Send a custom metric event
16
+ *
17
+ * Events are automatically batched with other metrics and sent to your backend.
18
+ * If called before observe() is initialized, events are buffered (max 100).
19
+ *
20
+ * @param name - Metric name (e.g., 'checkout_started', 'button_clicked')
21
+ * @param data - Optional data payload
22
+ *
23
+ * @example
24
+ * metric('checkout_started', { step: 1, cartTotal: 99.99 });
25
+ * metric('button_clicked', { id: 'submit-btn' });
26
+ * metric('feature_used', { name: 'dark_mode', enabled: true });
27
+ */
28
+ /**
29
+ * Get the current metric emitter function (for internal use)
30
+ * @internal
31
+ */
32
+ export declare function getMetricEmitter(): ((event: ObserveEvent) => void) | null;
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;
71
+ /**
72
+ * Get number of pending events (for testing)
73
+ * @internal
74
+ */
75
+ export declare function _getPendingEventsCount(): number;
76
+ /**
77
+ * Clear pending events (for testing)
78
+ * @internal
79
+ */
80
+ export declare function _clearPendingEvents(): void;
81
+ //# sourceMappingURL=metric.d.ts.map
@@ -0,0 +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,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"}
@@ -0,0 +1,2 @@
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
+ //# sourceMappingURL=metric.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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 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
+ }
@@ -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;AAWH,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,CAiKhE;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,CA+KhE;AAED,YAAY,EAAE,cAAc,EAAE,CAAC"}
@@ -1,2 +1,2 @@
1
- import{vitalObservers as h}from"./vitals.js";import{observeErrors as y}from"./errors.js";import{createFetchTransport as O}from"../transport/fetch.js";import{createSampler as E,eventTypeToSamplingType as S}from"./sampling.js";import{createSessionManager as T}from"./session.js";const f={endpoint:"/api/observe",vitals:!0,errors:!0,batchSize:10,flushInterval:5e3,sampleRate:1,debug:!1};let d=null;function c(i){d=i}function x(){return d}function R(i={}){if(Math.random()>(i.sampleRate??f.sampleRate))return()=>{};const t={...f,...i},b=t.transport??O(t.endpoint),u=t.sampling!=null?E(t.sampling):null,l=t.session!=null?T(t.session):null,n=[];l&&n.push(()=>l.destroy());const s=[];let p=null;const m=()=>{try{return typeof location<"u"?location.href:""}catch{return""}},v=e=>{if(!(t.filter&&!t.filter(e))){if(u){const r=S(e.type);if(r&&!u.shouldSample(r))return}l&&(e.sessionId=l.getSessionId()),t.debug&&console.log("[svoose]",e),s.push(e),s.length>=t.batchSize&&o()}},o=()=>{if(s.length===0)return;const e=s.splice(0,s.length),r=b.send(e);r&&typeof r.catch=="function"&&r.catch(a=>{t.debug&&console.error("[svoose] transport error:",a)})},g=e=>{const r={type:"vital",name:e.name,value:e.value,rating:e.rating,delta:e.delta,timestamp:e.timestamp,url:m()};v(r)};if(t.vitals){const e=t.vitals===!0?["CLS","LCP","FID","INP","FCP","TTFB"]:t.vitals;for(const r of e){const a=h[r];a&&n.push(a(g))}}if(t.errors&&n.push(y(e=>{v(e)})),c(v),n.push(()=>c(null)),p=setInterval(o,t.flushInterval),n.push(()=>{p&&clearInterval(p)}),typeof document<"u"){const e=()=>{document.visibilityState==="hidden"&&o()};document.addEventListener("visibilitychange",e),n.push(()=>{document.removeEventListener("visibilitychange",e)})}if(typeof window<"u"){const e=()=>{o()};window.addEventListener("beforeunload",e),n.push(()=>{window.removeEventListener("beforeunload",e)})}return()=>{o(),n.forEach(e=>e())}}export{x as getGlobalObserver,R as observe,c 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 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/metrics",vitals:!0,errors:!0,batchSize:10,flushInterval:5e3,sampleRate:1,debug:!1};let m=null;function d(l){m=l}function w(){return m}function N(l={}){if(Math.random()>(l.sampleRate??c.sampleRate))return()=>{};const t={...c,...l},b=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=b.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};
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 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(() => setGlobalObserver(null));\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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",
6
- "names": ["vitalObservers", "observeErrors", "createFetchTransport", "createSampler", "eventTypeToSamplingType", "createSessionManager", "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"]
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 };\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,CAC/B,CACF",
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,26 +1,8 @@
1
1
  /**
2
2
  * Sampling utilities for rate limiting events by type
3
3
  */
4
- /**
5
- * Per-event-type sampling rates
6
- * Each rate is a number between 0 and 1 (0 = disabled, 1 = all)
7
- */
8
- export interface SamplingConfig {
9
- /** Sampling rate for Web Vitals events (default: 1) */
10
- vitals?: number;
11
- /** Sampling rate for error events (default: 1) */
12
- errors?: number;
13
- /** Sampling rate for custom metric events (default: 1) */
14
- custom?: number;
15
- /** Sampling rate for state machine transition events (default: 1) */
16
- transitions?: number;
17
- /** Sampling rate for identify events (default: 1) */
18
- identify?: number;
19
- }
20
- /**
21
- * Sampling option - either a single rate for all events or per-type config
22
- */
23
- export type SamplingOption = number | SamplingConfig;
4
+ import type { SamplingConfig, SamplingOption } from '../types/index.js';
5
+ export type { SamplingConfig, SamplingOption };
24
6
  /**
25
7
  * Event types that can be sampled
26
8
  */
@@ -1 +1 @@
1
- {"version":3,"file":"sampling.d.ts","sourceRoot":"","sources":["../../src/observe/sampling.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,cAAc,CAAC;AAErD;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,cAAc,CAAC;AAMrD;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB;;;;OAIG;IACH,YAAY,CAAC,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAEpD;;;;OAIG;IACH,OAAO,CAAC,SAAS,EAAE,iBAAiB,GAAG,MAAM,CAAC;CAC/C;AAWD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAmC7D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,GAChB,iBAAiB,GAAG,IAAI,CAgB1B"}
1
+ {"version":3,"file":"sampling.d.ts","sourceRoot":"","sources":["../../src/observe/sampling.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,cAAc,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB;;;;OAIG;IACH,YAAY,CAAC,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAEpD;;;;OAIG;IACH,OAAO,CAAC,SAAS,EAAE,iBAAiB,GAAG,MAAM,CAAC;CAC/C;AAWD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAmC7D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,GAChB,iBAAiB,GAAG,IAAI,CAgB1B"}
@@ -1,2 +1,2 @@
1
- function t(e){return e<=0?0:e>=1?1:e}function a(e){const i=typeof e=="number"?{vitals:t(e),errors:t(e),custom:t(e),transitions:t(e),identify:1}:{vitals:t(e.vitals??1),errors:t(e.errors??1),custom:t(e.custom??1),transitions:t(e.transitions??1),identify:t(e.identify??1)};return{shouldSample(n){const r=i[n];return r>=1?!0:r<=0?!1:Math.random()<r},getRate(n){return i[n]}}}function u(e){switch(e){case"vital":return"vitals";case"error":case"unhandled-rejection":return"errors";case"custom":return"custom";case"transition":return"transitions";case"identify":return"identify";default:return null}}export{a as createSampler,u as eventTypeToSamplingType};
1
+ function t(e){return e<=0?0:e>=1?1:e}function a(e){const i=typeof e=="number"?{vitals:t(e),errors:t(e),custom:t(e),transitions:t(e),identify:1}:{vitals:t(e.vitals??1),errors:t(e.errors??1),custom:t(e.custom??1),transitions:t(e.transitions??1),identify:t(e.identify??1)};return{shouldSample(n){const r=i[n];return r>=1?!0:r<=0?!1:Math.random()<r},getRate(n){return i[n]}}}function p(e){switch(e){case"vital":return"vitals";case"error":case"unhandled-rejection":return"errors";case"custom":return"custom";case"transition":return"transitions";case"identify":return"identify";default:return null}}export{a as createSampler,p as eventTypeToSamplingType};
2
2
  //# sourceMappingURL=sampling.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/observe/sampling.ts"],
4
- "sourcesContent": ["/**\n * Sampling utilities for rate limiting events by type\n */\n\n// ============================================\n// Types\n// ============================================\n\n/**\n * Per-event-type sampling rates\n * Each rate is a number between 0 and 1 (0 = disabled, 1 = all)\n */\nexport interface SamplingConfig {\n /** Sampling rate for Web Vitals events (default: 1) */\n vitals?: number;\n /** Sampling rate for error events (default: 1) */\n errors?: number;\n /** Sampling rate for custom metric events (default: 1) */\n custom?: number;\n /** Sampling rate for state machine transition events (default: 1) */\n transitions?: number;\n /** Sampling rate for identify events (default: 1) */\n identify?: number;\n}\n\n/**\n * Sampling option - either a single rate for all events or per-type config\n */\nexport type SamplingOption = number | SamplingConfig;\n\n/**\n * Event types that can be sampled\n */\nexport type SamplingEventType = keyof SamplingConfig;\n\n// ============================================\n// Sampler\n// ============================================\n\n/**\n * Sampler interface returned by createSampler\n */\nexport interface Sampler {\n /**\n * Check if an event should be sampled (included)\n * @param eventType - The type of event\n * @returns true if the event should be included, false if dropped\n */\n shouldSample(eventType: SamplingEventType): boolean;\n\n /**\n * Get the sampling rate for an event type\n * @param eventType - The type of event\n * @returns The rate (0-1)\n */\n getRate(eventType: SamplingEventType): number;\n}\n\n/**\n * Normalize a sampling rate to be between 0 and 1\n */\nfunction normalizeRate(rate: number): number {\n if (rate <= 0) return 0;\n if (rate >= 1) return 1;\n return rate;\n}\n\n/**\n * Create a sampler instance for filtering events by sampling rate\n *\n * @param config - Sampling configuration (number or per-type config)\n * @returns Sampler instance\n *\n * @example\n * // Simple - same rate for all events\n * const sampler = createSampler(0.1); // 10% of all events\n *\n * @example\n * // Per-event-type rates\n * const sampler = createSampler({\n * vitals: 0.1, // 10% of vitals\n * errors: 1.0, // 100% of errors\n * custom: 0.5, // 50% of custom metrics\n * transitions: 0.0, // disabled\n * });\n */\nexport function createSampler(config: SamplingOption): Sampler {\n // Build rates object with defaults\n const rates: Required<SamplingConfig> =\n typeof config === 'number'\n ? {\n vitals: normalizeRate(config),\n errors: normalizeRate(config),\n custom: normalizeRate(config),\n transitions: normalizeRate(config),\n identify: 1, // Always send identify events by default\n }\n : {\n vitals: normalizeRate(config.vitals ?? 1),\n errors: normalizeRate(config.errors ?? 1),\n custom: normalizeRate(config.custom ?? 1),\n transitions: normalizeRate(config.transitions ?? 1),\n identify: normalizeRate(config.identify ?? 1),\n };\n\n return {\n shouldSample(eventType: SamplingEventType): boolean {\n const rate = rates[eventType];\n\n // Fast paths\n if (rate >= 1) return true;\n if (rate <= 0) return false;\n\n // Random sampling\n return Math.random() < rate;\n },\n\n getRate(eventType: SamplingEventType): number {\n return rates[eventType];\n },\n };\n}\n\n/**\n * Map ObserveEvent.type to SamplingEventType\n */\nexport function eventTypeToSamplingType(\n eventType: string\n): SamplingEventType | null {\n switch (eventType) {\n case 'vital':\n return 'vitals';\n case 'error':\n case 'unhandled-rejection':\n return 'errors';\n case 'custom':\n return 'custom';\n case 'transition':\n return 'transitions';\n case 'identify':\n return 'identify';\n default:\n return null;\n }\n}\n"],
5
- "mappings": "AA6DA,SAASA,EAAcC,EAAsB,CAC3C,OAAIA,GAAQ,EAAU,EAClBA,GAAQ,EAAU,EACfA,CACT,CAqBO,SAASC,EAAcC,EAAiC,CAE7D,MAAMC,EACJ,OAAOD,GAAW,SACd,CACE,OAAQH,EAAcG,CAAM,EAC5B,OAAQH,EAAcG,CAAM,EAC5B,OAAQH,EAAcG,CAAM,EAC5B,YAAaH,EAAcG,CAAM,EACjC,SAAU,CACZ,EACA,CACE,OAAQH,EAAcG,EAAO,QAAU,CAAC,EACxC,OAAQH,EAAcG,EAAO,QAAU,CAAC,EACxC,OAAQH,EAAcG,EAAO,QAAU,CAAC,EACxC,YAAaH,EAAcG,EAAO,aAAe,CAAC,EAClD,SAAUH,EAAcG,EAAO,UAAY,CAAC,CAC9C,EAEN,MAAO,CACL,aAAaE,EAAuC,CAClD,MAAMJ,EAAOG,EAAMC,CAAS,EAG5B,OAAIJ,GAAQ,EAAU,GAClBA,GAAQ,EAAU,GAGf,KAAK,OAAO,EAAIA,CACzB,EAEA,QAAQI,EAAsC,CAC5C,OAAOD,EAAMC,CAAS,CACxB,CACF,CACF,CAKO,SAASC,EACdD,EAC0B,CAC1B,OAAQA,EAAW,CACjB,IAAK,QACH,MAAO,SACT,IAAK,QACL,IAAK,sBACH,MAAO,SACT,IAAK,SACH,MAAO,SACT,IAAK,aACH,MAAO,cACT,IAAK,WACH,MAAO,WACT,QACE,OAAO,IACX,CACF",
4
+ "sourcesContent": ["/**\n * Sampling utilities for rate limiting events by type\n */\n\nimport type { SamplingConfig, SamplingOption } from '../types/index.js';\n\n// Re-export types from canonical source\nexport type { SamplingConfig, SamplingOption };\n\n/**\n * Event types that can be sampled\n */\nexport type SamplingEventType = keyof SamplingConfig;\n\n/**\n * Sampler interface returned by createSampler\n */\nexport interface Sampler {\n /**\n * Check if an event should be sampled (included)\n * @param eventType - The type of event\n * @returns true if the event should be included, false if dropped\n */\n shouldSample(eventType: SamplingEventType): boolean;\n\n /**\n * Get the sampling rate for an event type\n * @param eventType - The type of event\n * @returns The rate (0-1)\n */\n getRate(eventType: SamplingEventType): number;\n}\n\n/**\n * Normalize a sampling rate to be between 0 and 1\n */\nfunction normalizeRate(rate: number): number {\n if (rate <= 0) return 0;\n if (rate >= 1) return 1;\n return rate;\n}\n\n/**\n * Create a sampler instance for filtering events by sampling rate\n *\n * @param config - Sampling configuration (number or per-type config)\n * @returns Sampler instance\n *\n * @example\n * // Simple - same rate for all events\n * const sampler = createSampler(0.1); // 10% of all events\n *\n * @example\n * // Per-event-type rates\n * const sampler = createSampler({\n * vitals: 0.1, // 10% of vitals\n * errors: 1.0, // 100% of errors\n * custom: 0.5, // 50% of custom metrics\n * transitions: 0.0, // disabled\n * });\n */\nexport function createSampler(config: SamplingOption): Sampler {\n // Build rates object with defaults\n const rates: Required<SamplingConfig> =\n typeof config === 'number'\n ? {\n vitals: normalizeRate(config),\n errors: normalizeRate(config),\n custom: normalizeRate(config),\n transitions: normalizeRate(config),\n identify: 1, // Always send identify events by default\n }\n : {\n vitals: normalizeRate(config.vitals ?? 1),\n errors: normalizeRate(config.errors ?? 1),\n custom: normalizeRate(config.custom ?? 1),\n transitions: normalizeRate(config.transitions ?? 1),\n identify: normalizeRate(config.identify ?? 1),\n };\n\n return {\n shouldSample(eventType: SamplingEventType): boolean {\n const rate = rates[eventType];\n\n // Fast paths\n if (rate >= 1) return true;\n if (rate <= 0) return false;\n\n // Random sampling\n return Math.random() < rate;\n },\n\n getRate(eventType: SamplingEventType): number {\n return rates[eventType];\n },\n };\n}\n\n/**\n * Map ObserveEvent.type to SamplingEventType\n */\nexport function eventTypeToSamplingType(\n eventType: string\n): SamplingEventType | null {\n switch (eventType) {\n case 'vital':\n return 'vitals';\n case 'error':\n case 'unhandled-rejection':\n return 'errors';\n case 'custom':\n return 'custom';\n case 'transition':\n return 'transitions';\n case 'identify':\n return 'identify';\n default:\n return null;\n }\n}\n"],
5
+ "mappings": "AAoCA,SAASA,EAAcC,EAAsB,CAC3C,OAAIA,GAAQ,EAAU,EAClBA,GAAQ,EAAU,EACfA,CACT,CAqBO,SAASC,EAAcC,EAAiC,CAE7D,MAAMC,EACJ,OAAOD,GAAW,SACd,CACE,OAAQH,EAAcG,CAAM,EAC5B,OAAQH,EAAcG,CAAM,EAC5B,OAAQH,EAAcG,CAAM,EAC5B,YAAaH,EAAcG,CAAM,EACjC,SAAU,CACZ,EACA,CACE,OAAQH,EAAcG,EAAO,QAAU,CAAC,EACxC,OAAQH,EAAcG,EAAO,QAAU,CAAC,EACxC,OAAQH,EAAcG,EAAO,QAAU,CAAC,EACxC,YAAaH,EAAcG,EAAO,aAAe,CAAC,EAClD,SAAUH,EAAcG,EAAO,UAAY,CAAC,CAC9C,EAEN,MAAO,CACL,aAAaE,EAAuC,CAClD,MAAMJ,EAAOG,EAAMC,CAAS,EAG5B,OAAIJ,GAAQ,EAAU,GAClBA,GAAQ,EAAU,GAGf,KAAK,OAAO,EAAIA,CACzB,EAEA,QAAQI,EAAsC,CAC5C,OAAOD,EAAMC,CAAS,CACxB,CACF,CACF,CAKO,SAASC,EACdD,EAC0B,CAC1B,OAAQA,EAAW,CACjB,IAAK,QACH,MAAO,SACT,IAAK,QACL,IAAK,sBACH,MAAO,SACT,IAAK,SACH,MAAO,SACT,IAAK,aACH,MAAO,cACT,IAAK,WACH,MAAO,WACT,QACE,OAAO,IACX,CACF",
6
6
  "names": ["normalizeRate", "rate", "createSampler", "config", "rates", "eventType", "eventTypeToSamplingType"]
7
7
  }
@@ -1,13 +1,8 @@
1
1
  /**
2
2
  * Session Tracking - automatic sessionId generation with timeout
3
3
  */
4
- export interface SessionConfig {
5
- /** Session timeout in milliseconds (default: 30 minutes) */
6
- timeout: number;
7
- /** Storage type for session persistence */
8
- storage: 'sessionStorage' | 'localStorage' | 'memory';
9
- }
10
- export type SessionOption = boolean | Partial<SessionConfig>;
4
+ import type { SessionConfig, SessionOption } from '../types/index.js';
5
+ export type { SessionConfig, SessionOption };
11
6
  export interface SessionManager {
12
7
  /** Get current session ID (creates new session if expired) */
13
8
  getSessionId: () => string;
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/observe/session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,OAAO,EAAE,gBAAgB,GAAG,cAAc,GAAG,QAAQ,CAAC;CACvD;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AAW7D,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B,iCAAiC;IACjC,KAAK,EAAE,MAAM,MAAM,CAAC;IACpB,gDAAgD;IAChD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAYD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,GAAG,IAAI,CAuIjF"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/observe/session.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGtE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AAW7C,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B,iCAAiC;IACjC,KAAK,EAAE,MAAM,MAAM,CAAC;IACpB,gDAAgD;IAChD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAYD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,GAAG,IAAI,CAuIjF"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/observe/session.ts"],
4
- "sourcesContent": ["/**\n * Session Tracking - automatic sessionId generation with timeout\n */\n\nexport interface SessionConfig {\n /** Session timeout in milliseconds (default: 30 minutes) */\n timeout: number;\n /** Storage type for session persistence */\n storage: 'sessionStorage' | 'localStorage' | 'memory';\n}\n\nexport type SessionOption = boolean | Partial<SessionConfig>;\n\ninterface Session {\n id: string;\n startedAt: number;\n lastActivity: number;\n}\n\nconst DEFAULT_TIMEOUT = 30 * 60 * 1000; // 30 minutes\nconst STORAGE_KEY = 'svoose_session';\n\nexport interface SessionManager {\n /** Get current session ID (creates new session if expired) */\n getSessionId: () => string;\n /** Force create a new session */\n reset: () => string;\n /** Destroy session manager and clear storage */\n destroy: () => void;\n}\n\n/**\n * Generate a unique session ID\n * Format: timestamp-randomString (e.g., \"1706123456789-abc123def\")\n */\nfunction generateId(): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).slice(2, 11);\n return `${timestamp}-${random}`;\n}\n\n/**\n * Create a session manager\n *\n * @param config - Session configuration (true = defaults, false = disabled)\n * @returns SessionManager or null if disabled\n *\n * @example\n * // Enable with defaults\n * const session = createSessionManager(true);\n * session?.getSessionId(); // \"1706123456789-abc123def\"\n *\n * @example\n * // Custom config\n * const session = createSessionManager({\n * timeout: 60 * 60 * 1000, // 1 hour\n * storage: 'localStorage',\n * });\n */\nexport function createSessionManager(config: SessionOption): SessionManager | null {\n // Disabled\n if (config === false) return null;\n\n // Normalize config\n const opts: SessionConfig = config === true\n ? { timeout: DEFAULT_TIMEOUT, storage: 'sessionStorage' }\n : {\n timeout: config.timeout ?? DEFAULT_TIMEOUT,\n storage: config.storage ?? 'sessionStorage',\n };\n\n let currentSession: Session | null = null;\n\n /**\n * Get storage instance (with SSR and private mode safety)\n */\n function getStorage(): Storage | null {\n if (opts.storage === 'memory') return null;\n if (typeof window === 'undefined') return null;\n\n try {\n const storage = opts.storage === 'localStorage' ? localStorage : sessionStorage;\n // Test if storage is available (throws in private mode on some browsers)\n const testKey = '__svoose_test__';\n storage.setItem(testKey, '1');\n storage.removeItem(testKey);\n return storage;\n } catch {\n return null;\n }\n }\n\n /**\n * Load session from storage\n */\n function load(): Session | null {\n const storage = getStorage();\n if (!storage) return null;\n\n try {\n const data = storage.getItem(STORAGE_KEY);\n if (!data) return null;\n\n const session = JSON.parse(data) as Session;\n\n // Validate session structure\n if (\n typeof session.id !== 'string' ||\n typeof session.startedAt !== 'number' ||\n typeof session.lastActivity !== 'number'\n ) {\n return null;\n }\n\n return session;\n } catch {\n return null;\n }\n }\n\n /**\n * Save session to storage\n */\n function save(session: Session): void {\n const storage = getStorage();\n if (!storage) return;\n\n try {\n storage.setItem(STORAGE_KEY, JSON.stringify(session));\n } catch {\n // Quota exceeded - fail silently\n }\n }\n\n /**\n * Create a new session\n */\n function createNew(): Session {\n const now = Date.now();\n const session: Session = {\n id: generateId(),\n startedAt: now,\n lastActivity: now,\n };\n save(session);\n return session;\n }\n\n /**\n * Check if session is expired\n */\n function isExpired(session: Session): boolean {\n const now = Date.now();\n return now - session.lastActivity > opts.timeout;\n }\n\n return {\n getSessionId(): string {\n const now = Date.now();\n\n // Try to load from storage if not in memory\n if (!currentSession) {\n currentSession = load();\n }\n\n // Create new session if none exists or expired\n if (!currentSession || isExpired(currentSession)) {\n currentSession = createNew();\n return currentSession.id;\n }\n\n // Update last activity\n currentSession.lastActivity = now;\n save(currentSession);\n return currentSession.id;\n },\n\n reset(): string {\n currentSession = createNew();\n return currentSession.id;\n },\n\n destroy(): void {\n currentSession = null;\n const storage = getStorage();\n if (storage) {\n try {\n storage.removeItem(STORAGE_KEY);\n } catch {\n // Ignore errors\n }\n }\n },\n };\n}\n"],
5
- "mappings": "AAoBA,MAAMA,EAAc,iBAepB,SAASC,GAAqB,CAC5B,MAAMC,EAAY,KAAK,IAAI,EACrBC,EAAS,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAG,EAAE,EACrD,MAAO,GAAGD,CAAS,IAAIC,CAAM,EAC/B,CAoBO,SAASC,EAAqBC,EAA8C,CAEjF,GAAIA,IAAW,GAAO,OAAO,KAG7B,MAAMC,EAAsBD,IAAW,GACnC,CAAE,QAAS,KAAiB,QAAS,gBAAiB,EACtD,CACE,QAASA,EAAO,SAAW,KAC3B,QAASA,EAAO,SAAW,gBAC7B,EAEJ,IAAIE,EAAiC,KAKrC,SAASC,GAA6B,CAEpC,GADIF,EAAK,UAAY,UACjB,OAAO,OAAW,IAAa,OAAO,KAE1C,GAAI,CACF,MAAMG,EAAUH,EAAK,UAAY,eAAiB,aAAe,eAE3DI,EAAU,kBAChB,OAAAD,EAAQ,QAAQC,EAAS,GAAG,EAC5BD,EAAQ,WAAWC,CAAO,EACnBD,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAKA,SAASE,GAAuB,CAC9B,MAAMF,EAAUD,EAAW,EAC3B,GAAI,CAACC,EAAS,OAAO,KAErB,GAAI,CACF,MAAMG,EAAOH,EAAQ,QAAQT,CAAW,EACxC,GAAI,CAACY,EAAM,OAAO,KAElB,MAAMC,EAAU,KAAK,MAAMD,CAAI,EAG/B,OACE,OAAOC,EAAQ,IAAO,UACtB,OAAOA,EAAQ,WAAc,UAC7B,OAAOA,EAAQ,cAAiB,SAEzB,KAGFA,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAKA,SAASC,EAAKD,EAAwB,CACpC,MAAMJ,EAAUD,EAAW,EAC3B,GAAKC,EAEL,GAAI,CACFA,EAAQ,QAAQT,EAAa,KAAK,UAAUa,CAAO,CAAC,CACtD,MAAQ,CAER,CACF,CAKA,SAASE,GAAqB,CAC5B,MAAMC,EAAM,KAAK,IAAI,EACfH,EAAmB,CACvB,GAAIZ,EAAW,EACf,UAAWe,EACX,aAAcA,CAChB,EACA,OAAAF,EAAKD,CAAO,EACLA,CACT,CAKA,SAASI,EAAUJ,EAA2B,CAE5C,OADY,KAAK,IAAI,EACRA,EAAQ,aAAeP,EAAK,OAC3C,CAEA,MAAO,CACL,cAAuB,CACrB,MAAMU,EAAM,KAAK,IAAI,EAQrB,OALKT,IACHA,EAAiBI,EAAK,GAIpB,CAACJ,GAAkBU,EAAUV,CAAc,GAC7CA,EAAiBQ,EAAU,EACpBR,EAAe,KAIxBA,EAAe,aAAeS,EAC9BF,EAAKP,CAAc,EACZA,EAAe,GACxB,EAEA,OAAgB,CACd,OAAAA,EAAiBQ,EAAU,EACpBR,EAAe,EACxB,EAEA,SAAgB,CACdA,EAAiB,KACjB,MAAME,EAAUD,EAAW,EAC3B,GAAIC,EACF,GAAI,CACFA,EAAQ,WAAWT,CAAW,CAChC,MAAQ,CAER,CAEJ,CACF,CACF",
4
+ "sourcesContent": ["/**\n * Session Tracking - automatic sessionId generation with timeout\n */\n\nimport type { SessionConfig, SessionOption } from '../types/index.js';\n\n// Re-export types from canonical source\nexport type { SessionConfig, SessionOption };\n\ninterface Session {\n id: string;\n startedAt: number;\n lastActivity: number;\n}\n\nconst DEFAULT_TIMEOUT = 30 * 60 * 1000; // 30 minutes\nconst STORAGE_KEY = 'svoose_session';\n\nexport interface SessionManager {\n /** Get current session ID (creates new session if expired) */\n getSessionId: () => string;\n /** Force create a new session */\n reset: () => string;\n /** Destroy session manager and clear storage */\n destroy: () => void;\n}\n\n/**\n * Generate a unique session ID\n * Format: timestamp-randomString (e.g., \"1706123456789-abc123def\")\n */\nfunction generateId(): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).slice(2, 11);\n return `${timestamp}-${random}`;\n}\n\n/**\n * Create a session manager\n *\n * @param config - Session configuration (true = defaults, false = disabled)\n * @returns SessionManager or null if disabled\n *\n * @example\n * // Enable with defaults\n * const session = createSessionManager(true);\n * session?.getSessionId(); // \"1706123456789-abc123def\"\n *\n * @example\n * // Custom config\n * const session = createSessionManager({\n * timeout: 60 * 60 * 1000, // 1 hour\n * storage: 'localStorage',\n * });\n */\nexport function createSessionManager(config: SessionOption): SessionManager | null {\n // Disabled\n if (config === false) return null;\n\n // Normalize config\n const opts: SessionConfig = config === true\n ? { timeout: DEFAULT_TIMEOUT, storage: 'sessionStorage' }\n : {\n timeout: config.timeout ?? DEFAULT_TIMEOUT,\n storage: config.storage ?? 'sessionStorage',\n };\n\n let currentSession: Session | null = null;\n\n /**\n * Get storage instance (with SSR and private mode safety)\n */\n function getStorage(): Storage | null {\n if (opts.storage === 'memory') return null;\n if (typeof window === 'undefined') return null;\n\n try {\n const storage = opts.storage === 'localStorage' ? localStorage : sessionStorage;\n // Test if storage is available (throws in private mode on some browsers)\n const testKey = '__svoose_test__';\n storage.setItem(testKey, '1');\n storage.removeItem(testKey);\n return storage;\n } catch {\n return null;\n }\n }\n\n /**\n * Load session from storage\n */\n function load(): Session | null {\n const storage = getStorage();\n if (!storage) return null;\n\n try {\n const data = storage.getItem(STORAGE_KEY);\n if (!data) return null;\n\n const session = JSON.parse(data) as Session;\n\n // Validate session structure\n if (\n typeof session.id !== 'string' ||\n typeof session.startedAt !== 'number' ||\n typeof session.lastActivity !== 'number'\n ) {\n return null;\n }\n\n return session;\n } catch {\n return null;\n }\n }\n\n /**\n * Save session to storage\n */\n function save(session: Session): void {\n const storage = getStorage();\n if (!storage) return;\n\n try {\n storage.setItem(STORAGE_KEY, JSON.stringify(session));\n } catch {\n // Quota exceeded - fail silently\n }\n }\n\n /**\n * Create a new session\n */\n function createNew(): Session {\n const now = Date.now();\n const session: Session = {\n id: generateId(),\n startedAt: now,\n lastActivity: now,\n };\n save(session);\n return session;\n }\n\n /**\n * Check if session is expired\n */\n function isExpired(session: Session): boolean {\n const now = Date.now();\n return now - session.lastActivity > opts.timeout;\n }\n\n return {\n getSessionId(): string {\n const now = Date.now();\n\n // Try to load from storage if not in memory\n if (!currentSession) {\n currentSession = load();\n }\n\n // Create new session if none exists or expired\n if (!currentSession || isExpired(currentSession)) {\n currentSession = createNew();\n return currentSession.id;\n }\n\n // Update last activity\n currentSession.lastActivity = now;\n save(currentSession);\n return currentSession.id;\n },\n\n reset(): string {\n currentSession = createNew();\n return currentSession.id;\n },\n\n destroy(): void {\n currentSession = null;\n const storage = getStorage();\n if (storage) {\n try {\n storage.removeItem(STORAGE_KEY);\n } catch {\n // Ignore errors\n }\n }\n },\n };\n}\n"],
5
+ "mappings": "AAgBA,MAAMA,EAAc,iBAepB,SAASC,GAAqB,CAC5B,MAAMC,EAAY,KAAK,IAAI,EACrBC,EAAS,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAG,EAAE,EACrD,MAAO,GAAGD,CAAS,IAAIC,CAAM,EAC/B,CAoBO,SAASC,EAAqBC,EAA8C,CAEjF,GAAIA,IAAW,GAAO,OAAO,KAG7B,MAAMC,EAAsBD,IAAW,GACnC,CAAE,QAAS,KAAiB,QAAS,gBAAiB,EACtD,CACE,QAASA,EAAO,SAAW,KAC3B,QAASA,EAAO,SAAW,gBAC7B,EAEJ,IAAIE,EAAiC,KAKrC,SAASC,GAA6B,CAEpC,GADIF,EAAK,UAAY,UACjB,OAAO,OAAW,IAAa,OAAO,KAE1C,GAAI,CACF,MAAMG,EAAUH,EAAK,UAAY,eAAiB,aAAe,eAE3DI,EAAU,kBAChB,OAAAD,EAAQ,QAAQC,EAAS,GAAG,EAC5BD,EAAQ,WAAWC,CAAO,EACnBD,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAKA,SAASE,GAAuB,CAC9B,MAAMF,EAAUD,EAAW,EAC3B,GAAI,CAACC,EAAS,OAAO,KAErB,GAAI,CACF,MAAMG,EAAOH,EAAQ,QAAQT,CAAW,EACxC,GAAI,CAACY,EAAM,OAAO,KAElB,MAAMC,EAAU,KAAK,MAAMD,CAAI,EAG/B,OACE,OAAOC,EAAQ,IAAO,UACtB,OAAOA,EAAQ,WAAc,UAC7B,OAAOA,EAAQ,cAAiB,SAEzB,KAGFA,CACT,MAAQ,CACN,OAAO,IACT,CACF,CAKA,SAASC,EAAKD,EAAwB,CACpC,MAAMJ,EAAUD,EAAW,EAC3B,GAAKC,EAEL,GAAI,CACFA,EAAQ,QAAQT,EAAa,KAAK,UAAUa,CAAO,CAAC,CACtD,MAAQ,CAER,CACF,CAKA,SAASE,GAAqB,CAC5B,MAAMC,EAAM,KAAK,IAAI,EACfH,EAAmB,CACvB,GAAIZ,EAAW,EACf,UAAWe,EACX,aAAcA,CAChB,EACA,OAAAF,EAAKD,CAAO,EACLA,CACT,CAKA,SAASI,EAAUJ,EAA2B,CAE5C,OADY,KAAK,IAAI,EACRA,EAAQ,aAAeP,EAAK,OAC3C,CAEA,MAAO,CACL,cAAuB,CACrB,MAAMU,EAAM,KAAK,IAAI,EAQrB,OALKT,IACHA,EAAiBI,EAAK,GAIpB,CAACJ,GAAkBU,EAAUV,CAAc,GAC7CA,EAAiBQ,EAAU,EACpBR,EAAe,KAIxBA,EAAe,aAAeS,EAC9BF,EAAKP,CAAc,EACZA,EAAe,GACxB,EAEA,OAAgB,CACd,OAAAA,EAAiBQ,EAAU,EACpBR,EAAe,EACxB,EAEA,SAAgB,CACdA,EAAiB,KACjB,MAAME,EAAUD,EAAW,EAC3B,GAAIC,EACF,GAAI,CACFA,EAAQ,WAAWT,CAAW,CAChC,MAAQ,CAER,CAEJ,CACF,CACF",
6
6
  "names": ["STORAGE_KEY", "generateId", "timestamp", "random", "createSessionManager", "config", "opts", "currentSession", "getStorage", "storage", "testKey", "load", "data", "session", "save", "createNew", "now", "isExpired"]
7
7
  }
@@ -0,0 +1,2 @@
1
+ import{createMachine as r}from"../machine/index.js";function s(o){const e=r(o);let n=$state(e.state),a=$state(e.context);const c=e.send.bind(e);return{get state(){return n},get context(){return a},matches(t){return n===t},matchesAny(...t){return t.includes(n)},can(t){return e.can(t)},send:t=>{c(t),n=e.state,a=e.context},destroy:e.destroy.bind(e)}}const v=s;export{v as fromMachine,s as useMachine};
2
+ //# sourceMappingURL=index.svelte.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/svelte/index.svelte.ts"],
4
+ "sourcesContent": ["/**\n * Svelte 5 specific utilities for svoose\n *\n * Provides reactive wrappers and hooks for state machines\n */\n\n/// <reference path=\"./runes.d.ts\" />\n\nimport { createMachine, type MachineConfig, type EventObject } from '../machine/index.js';\n\n/**\n * Reactive machine state for Svelte 5 components\n */\nexport interface ReactiveMachine<\n TContext extends object,\n TState extends string,\n TEvent extends EventObject\n> {\n /** Current state (reactive - triggers re-render on change) */\n readonly state: TState;\n /** Current context (reactive - triggers re-render on change) */\n readonly context: TContext;\n /** Check if machine is in given state */\n matches(state: TState): boolean;\n /** Check if machine is in any of given states */\n matchesAny(...states: TState[]): boolean;\n /** Check if event can be sent */\n can(eventType: TEvent['type']): boolean;\n /** Send event to machine */\n send(event: TEvent | TEvent['type']): void;\n /** Cleanup machine */\n destroy(): void;\n}\n\n/**\n * Create a reactive state machine for Svelte 5 components\n *\n * This is the recommended way to use svoose in Svelte 5 components.\n * The returned machine's state and context are automatically reactive\n * and will trigger component re-renders when they change.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { useMachine } from 'svoose/svelte';\n *\n * const toggle = useMachine({\n * id: 'toggle',\n * initial: 'off',\n * states: {\n * off: { on: { TOGGLE: 'on' } },\n * on: { on: { TOGGLE: 'off' } },\n * },\n * });\n * </script>\n *\n * <button onclick={() => toggle.send('TOGGLE')}>\n * {toggle.state}\n * </button>\n * ```\n */\nexport function useMachine<\n TContext extends object,\n TState extends string,\n TEvent extends EventObject\n>(config: MachineConfig<TContext, TState, TEvent>): ReactiveMachine<TContext, TState, TEvent> {\n // Create base machine\n const machine = createMachine(config);\n\n // Reactive state using Svelte 5 $state rune\n let reactiveState = $state<TState>(machine.state);\n let reactiveContext = $state<TContext>(machine.context);\n\n // Wrap send to update reactive state\n const originalSend = machine.send.bind(machine);\n\n const reactiveSend = (event: TEvent | TEvent['type']): void => {\n originalSend(event);\n // Update reactive state after transition\n reactiveState = machine.state;\n reactiveContext = machine.context;\n };\n\n // Return reactive wrapper\n return {\n get state(): TState {\n return reactiveState;\n },\n get context(): TContext {\n return reactiveContext;\n },\n matches(state: TState): boolean {\n return reactiveState === state;\n },\n matchesAny(...states: TState[]): boolean {\n return states.includes(reactiveState);\n },\n can(eventType: TEvent['type']): boolean {\n return machine.can(eventType);\n },\n send: reactiveSend,\n destroy: machine.destroy.bind(machine),\n };\n}\n\n/**\n * Create a machine from an existing config with reactive state\n *\n * Use this when you have a pre-defined machine config that you want\n * to instantiate as a reactive machine in a component.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * import { fromMachine } from 'svoose/svelte';\n * import { toggleConfig } from './machines/toggle';\n *\n * const toggle = fromMachine(toggleConfig);\n * </script>\n * ```\n */\nexport const fromMachine = useMachine;\n\n// Re-export useful types\nexport type { MachineConfig, EventObject } from '../machine/index.js';\n"],
5
+ "mappings": "AAQA,OAAS,iBAAAA,MAA2D,sBAqD7D,SAASC,EAIdC,EAA4F,CAE5F,MAAMC,EAAUH,EAAcE,CAAM,EAGpC,IAAIE,EAAgB,OAAeD,EAAQ,KAAK,EAC5CE,EAAkB,OAAiBF,EAAQ,OAAO,EAGtD,MAAMG,EAAeH,EAAQ,KAAK,KAAKA,CAAO,EAU9C,MAAO,CACL,IAAI,OAAgB,CAClB,OAAOC,CACT,EACA,IAAI,SAAoB,CACtB,OAAOC,CACT,EACA,QAAQE,EAAwB,CAC9B,OAAOH,IAAkBG,CAC3B,EACA,cAAcC,EAA2B,CACvC,OAAOA,EAAO,SAASJ,CAAa,CACtC,EACA,IAAIK,EAAoC,CACtC,OAAON,EAAQ,IAAIM,CAAS,CAC9B,EACA,KAxBoBC,GAAyC,CAC7DJ,EAAaI,CAAK,EAElBN,EAAgBD,EAAQ,MACxBE,EAAkBF,EAAQ,OAC5B,EAoBE,QAASA,EAAQ,QAAQ,KAAKA,CAAO,CACvC,CACF,CAkBO,MAAMQ,EAAcV",
6
+ "names": ["createMachine", "useMachine", "config", "machine", "reactiveState", "reactiveContext", "originalSend", "state", "states", "eventType", "event", "fromMachine"]
7
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Fetch-based transport with sendBeacon fallback
3
3
  */
4
- import type { Transport, TransportOptions } from './transport.js';
4
+ import type { Transport, TransportOptions } from '../types/index.js';
5
5
  /**
6
6
  * Create a fetch-based transport
7
7
  * Uses sendBeacon for page unload, fetch otherwise
@@ -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,gBAAgB,CAAC;AAElE;;;;;;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,CAwCX;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 r=JSON.stringify(e);if(typeof document<"u"&&document.visibilityState==="hidden"&&typeof navigator<"u"&&navigator.sendBeacon){const t=new Blob([r],{type:"application/json"});if(!navigator.sendBeacon(o,t))throw new Error("sendBeacon failed");return}await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",...n.headers},body:r,keepalive:!0})}catch(r){n.onError?.(r)}}}}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 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};
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 './transport.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"],
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
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
6
  "names": ["createFetchTransport", "endpoint", "options", "events", "payload", "blob", "error", "createConsoleTransport", "event"]
7
7
  }
@@ -1,13 +1,5 @@
1
1
  /**
2
- * Transport interface - defines how events are sent to backend
2
+ * Transport interface - re-exported from types for convenience
3
3
  */
4
- import type { ObserveEvent } from '../types/index.js';
5
- export interface Transport {
6
- /** Send events to backend. Can return Promise or void for sync transports. */
7
- send(events: ObserveEvent[]): Promise<void> | void;
8
- }
9
- export interface TransportOptions {
10
- headers?: Record<string, string>;
11
- onError?: (error: Error) => void;
12
- }
4
+ export type { Transport, TransportOptions } from '../types/index.js';
13
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,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,SAAS;IACxB,8EAA8E;IAC9E,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC"}
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"}
@@ -50,7 +50,16 @@ export interface TransitionEvent {
50
50
  sessionId?: string;
51
51
  context?: Record<string, unknown>;
52
52
  }
53
- export type ObserveEvent = VitalEvent | ObserveErrorEvent | TransitionEvent;
53
+ export interface CustomMetricEvent {
54
+ type: 'custom';
55
+ name: string;
56
+ metricKind?: 'counter' | 'gauge' | 'histogram';
57
+ value?: number;
58
+ data: Record<string, unknown>;
59
+ timestamp: number;
60
+ sessionId?: string;
61
+ }
62
+ export type ObserveEvent = VitalEvent | ObserveErrorEvent | TransitionEvent | CustomMetricEvent;
54
63
  export interface Transport {
55
64
  /** Send events to backend. Can return Promise or void for sync transports. */
56
65
  send(events: ObserveEvent[]): Promise<void> | void;
@@ -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.5",
3
+ "version": "0.1.7",
4
4
  "description": "Observability + State Machines for Svelte 5 — the goose that sees everything",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -55,6 +55,11 @@
55
55
  "peerDependencies": {
56
56
  "svelte": "^5.0.0"
57
57
  },
58
+ "peerDependenciesMeta": {
59
+ "svelte": {
60
+ "optional": true
61
+ }
62
+ },
58
63
  "devDependencies": {
59
64
  "@vitest/coverage-v8": "^2.1.9",
60
65
  "esbuild": "^0.27.2",