@pl4yzonellc/empire-analytics 0.0.2

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.
@@ -0,0 +1,507 @@
1
+ /**
2
+ * Error model.
3
+ *
4
+ * Every error the library surfaces is an {@link AnalyticsError} carrying a stable
5
+ * machine-readable `code`. Provider implementation details are never leaked through
6
+ * the `message`; the originating error (if any) is attached as `cause` for opt-in
7
+ * inspection by `config.onError`.
8
+ */
9
+ type AnalyticsErrorCode = 'config_invalid' | 'initialization_failed' | 'provider_unavailable' | 'script_load_failed' | 'track_failed' | 'pageview_failed' | 'destroy_failed' | 'privacy_violation' | 'unknown';
10
+ interface AnalyticsErrorOptions {
11
+ code?: AnalyticsErrorCode;
12
+ cause?: unknown;
13
+ }
14
+ declare class AnalyticsError extends Error {
15
+ readonly code: AnalyticsErrorCode;
16
+ readonly cause?: unknown;
17
+ constructor(message: string, options?: AnalyticsErrorOptions);
18
+ }
19
+ /** Thrown synchronously from `initialize()` when the supplied config is invalid. */
20
+ declare class AnalyticsConfigError extends AnalyticsError {
21
+ constructor(message: string, options?: Omit<AnalyticsErrorOptions, 'code'>);
22
+ }
23
+ /**
24
+ * Normalize an unknown thrown value into an {@link AnalyticsError} with a generic,
25
+ * provider-neutral message. Existing `AnalyticsError`s pass through unchanged.
26
+ */
27
+ declare function toAnalyticsError(value: unknown, code: AnalyticsErrorCode): AnalyticsError;
28
+
29
+ /** How the sanitizer reacts to a prohibited key. */
30
+ type PrivacyViolationMode = 'strip' | 'warn' | 'throw';
31
+ /** How the sanitizer reacts to a personally-identifiable key. */
32
+ type PiiMode = 'allow' | 'strip' | 'warn' | 'throw';
33
+ /**
34
+ * Application-facing privacy configuration. Everything is optional; sensible,
35
+ * environment-aware defaults are applied by `resolveConfig`.
36
+ */
37
+ interface PrivacyConfig {
38
+ /**
39
+ * Handling for always-blocked sensitive keys (passwords, tokens, card data…).
40
+ * Default: `throw` when `debug` is on, `warn` on staging/test, `strip` in production.
41
+ * `throw` is always downgraded to `strip` in the `production` environment so
42
+ * analytics can never crash a production app.
43
+ */
44
+ onSensitive?: PrivacyViolationMode;
45
+ /**
46
+ * Handling for direct personal identifiers (email, phone, name, address…).
47
+ * Default: `warn`. `throw` is downgraded to `warn` in `production`.
48
+ */
49
+ onPii?: PiiMode;
50
+ /** Extra keys to always block (normalized substring match). */
51
+ blockKeys?: readonly string[];
52
+ /** Keys to force-allow even when a built-in rule matches (normalized exact match). */
53
+ allowKeys?: readonly string[];
54
+ /** Maximum object nesting depth to traverse. Default `4`. */
55
+ maxDepth?: number;
56
+ /** Maximum number of properties kept (across the whole tree). Default `64`. */
57
+ maxProperties?: number;
58
+ /** Strings longer than this are truncated. Default `1024`. */
59
+ maxStringLength?: number;
60
+ /** Arrays longer than this are truncated. Default `64`. */
61
+ maxArrayLength?: number;
62
+ }
63
+ /** Fully-resolved policy consumed by the sanitizer. */
64
+ interface ResolvedPrivacyPolicy {
65
+ onSensitive: PrivacyViolationMode;
66
+ onPii: PiiMode;
67
+ blockKeys: readonly string[];
68
+ allowKeys: readonly string[];
69
+ maxDepth: number;
70
+ maxProperties: number;
71
+ maxStringLength: number;
72
+ maxArrayLength: number;
73
+ }
74
+ interface SanitizeResult {
75
+ /** Safe, adapter-ready property bag. */
76
+ value: Record<string, unknown>;
77
+ /** Key paths that were removed because they matched a privacy rule. */
78
+ blocked: string[];
79
+ }
80
+
81
+ interface Logger {
82
+ debug(message: string, data?: unknown): void;
83
+ info(message: string, data?: unknown): void;
84
+ warn(message: string, data?: unknown): void;
85
+ error(message: string, data?: unknown): void;
86
+ /** Returns a logger with an extra scope segment appended to the prefix. */
87
+ child(scope: string): Logger;
88
+ }
89
+
90
+ /**
91
+ * Provider contract.
92
+ *
93
+ * The core client depends on *this interface only*. Everything Plausible-specific
94
+ * lives behind `PlausibleAdapter`.
95
+ *
96
+ * Refinements over the naive `initialize(config: unknown)` shape:
97
+ * - provider config is injected through the constructor / factory and is
98
+ * strongly typed per provider, so `unknown` never travels through the core
99
+ * - `initialize()` may be sync or async; the core always awaits it
100
+ * - adapters expose a stable `name` for diagnostics
101
+ * - `track()` receives an already privacy-sanitized property bag
102
+ */
103
+ interface AnalyticsAdapter {
104
+ /** Stable identifier, e.g. `'plausible'`. */
105
+ readonly name: string;
106
+ /** Prepare the provider (load scripts, wire globals). Awaited by the core. */
107
+ initialize(): Promise<void> | void;
108
+ /** Send a business event. `properties` is already sanitized. */
109
+ track(eventName: string, properties?: Record<string, unknown>): void;
110
+ /** Send an explicit page view. `path` is already sanitized when provided. */
111
+ pageView(path?: string): void;
112
+ /** Release resources: remove injected scripts, drop listeners, null out globals. */
113
+ destroy(): void;
114
+ }
115
+ /** Everything an adapter is handed at construction time. */
116
+ interface AnalyticsAdapterContext {
117
+ logger: Logger;
118
+ }
119
+ /**
120
+ * Factory for a custom adapter, supplied via `{ provider: 'custom', adapter }`.
121
+ * This is the extension point for future first-class providers
122
+ * (`GoogleAnalyticsAdapter`, `PostHogAdapter`, …) without touching app code.
123
+ */
124
+ type AnalyticsAdapterFactory = (context: AnalyticsAdapterContext) => AnalyticsAdapter;
125
+
126
+ /**
127
+ * Optional Plausible script variants. The library composes the CDN filename
128
+ * (`script.manual.revenue.tagged-events.js`) from these flags.
129
+ * @see https://plausible.io/docs/script-extensions
130
+ */
131
+ type PlausibleScriptExtension = 'manual' | 'hash' | 'outbound-links' | 'file-downloads' | 'tagged-events' | 'revenue' | 'pageview-props' | 'compat' | 'local';
132
+ /**
133
+ * How the Plausible tracker runs in the browser:
134
+ * - `'cdn'` — inject the hosted `<script data-domain>` from plausible.io (default)
135
+ * - `'package'` — load the official `@plausible-analytics/tracker` npm module via a
136
+ * lazy dynamic import (no remote `<script>`, pinned version, friendlier to CSP and
137
+ * ad-blockers). The consuming app must install it: `pnpm add @plausible-analytics/tracker`.
138
+ */
139
+ type PlausibleRuntimeMode = 'cdn' | 'package';
140
+ /**
141
+ * Provider-specific configuration block. This is the *only* place Plausible
142
+ * terminology is allowed to surface in the public API.
143
+ */
144
+ interface PlausibleProviderConfig {
145
+ /** The site domain registered in Plausible, e.g. `"example.com"`. Required. */
146
+ domain: string;
147
+ /**
148
+ * Runtime for the tracker. Default `'cdn'`.
149
+ * `'package'` uses `@plausible-analytics/tracker` (an **optional peer dependency**,
150
+ * loaded via dynamic `import()`); `scriptUrl`, `injectScript` and `taggedEvents`
151
+ * are ignored in that mode.
152
+ */
153
+ mode?: PlausibleRuntimeMode;
154
+ /**
155
+ * Full URL of the tracking script (`mode: 'cdn'` only). Overrides the composed
156
+ * CDN URL entirely. Use this for a self-hosted Plausible instance or a first-party proxy.
157
+ */
158
+ scriptUrl?: string;
159
+ /** Custom events API endpoint (`data-api`). Use with a first-party proxy. */
160
+ endpoint?: string;
161
+ /**
162
+ * Inject the browser script automatically. Default `true`.
163
+ * Set to `false` if the host page already includes the Plausible snippet.
164
+ */
165
+ injectScript?: boolean;
166
+ /**
167
+ * Let Plausible auto-track SPA navigations. Default `true`.
168
+ * When `false`, the `manual` script variant is used and page views only happen
169
+ * via explicit `analytics.pageView()` calls.
170
+ */
171
+ autoPageViews?: boolean;
172
+ /** Use the `hash` script variant for hash-based routing. Default `false`. */
173
+ hashRouting?: boolean;
174
+ /** Load the `outbound-links` script variant. Default `false`. */
175
+ outboundLinks?: boolean;
176
+ /** Load the `file-downloads` script variant. Default `false`. */
177
+ fileDownloads?: boolean;
178
+ /** Load the `tagged-events` script variant. Default `false`. */
179
+ taggedEvents?: boolean;
180
+ /**
181
+ * Enable revenue tracking (`revenue` script variant). Default `true` — the SDK's
182
+ * revenue abstraction depends on it. Individual goals must still be marked as
183
+ * revenue goals in the Plausible dashboard.
184
+ */
185
+ revenue?: boolean;
186
+ /** Load the `local` script variant so events fire on `localhost`. Default `false`. */
187
+ trackLocalhost?: boolean;
188
+ }
189
+ interface ResolvedPlausibleConfig {
190
+ domain: string;
191
+ mode: PlausibleRuntimeMode;
192
+ scriptUrl?: string;
193
+ endpoint?: string;
194
+ injectScript: boolean;
195
+ autoPageViews: boolean;
196
+ hashRouting: boolean;
197
+ outboundLinks: boolean;
198
+ fileDownloads: boolean;
199
+ taggedEvents: boolean;
200
+ revenue: boolean;
201
+ trackLocalhost: boolean;
202
+ }
203
+
204
+ interface ConsoleProviderConfig {
205
+ /** Where to write. Defaults to the global `console`. */
206
+ sink?: Pick<Console, 'log' | 'info' | 'group' | 'groupEnd'>;
207
+ /** Prefix for every line. Default `"[Analytics:console]"`. */
208
+ prefix?: string;
209
+ }
210
+ interface ResolvedConsoleConfig {
211
+ sink: Pick<Console, 'log' | 'info' | 'group' | 'groupEnd'>;
212
+ prefix: string;
213
+ }
214
+
215
+ /**
216
+ * Typed event system
217
+ * ==================
218
+ *
219
+ * The library deliberately does *not* recommend `track(event: string)`. Instead it
220
+ * ships a shared catalog (`BaseAnalyticsEventMap`) and lets each application extend
221
+ * an open registry interface (`AnalyticsEventMap`) via TypeScript module augmentation:
222
+ *
223
+ * ```ts
224
+ * // app/src/analytics-events.ts
225
+ * declare module '@pl4yzonellc/empire-analytics' {
226
+ * interface AnalyticsEventMap {
227
+ * quote_requested: { service: string; budgetRange?: string };
228
+ * }
229
+ * }
230
+ * ```
231
+ *
232
+ * After that, `analytics.track('quote_requested', { service: 'roofing' })` is fully
233
+ * typed everywhere (hooks, `<Track>`, the global client) with zero generic plumbing,
234
+ * while unknown strings are rejected by the compiler.
235
+ */
236
+ /** Common UI interactions shared by most applications. */
237
+ interface CommonEventMap {
238
+ cta_clicked: {
239
+ id?: string;
240
+ label?: string;
241
+ location?: string;
242
+ };
243
+ outbound_link_clicked: {
244
+ url: string;
245
+ location?: string;
246
+ };
247
+ download_clicked: {
248
+ file: string;
249
+ location?: string;
250
+ };
251
+ /** `query` is free text — never put PII in it. */
252
+ search_performed: {
253
+ query?: string;
254
+ resultsCount?: number;
255
+ };
256
+ }
257
+ /** Lead-generation / contact funnels. */
258
+ interface LeadGenerationEventMap {
259
+ contact_form_started: {
260
+ form: string;
261
+ };
262
+ contact_form_submitted: {
263
+ form: string;
264
+ };
265
+ phone_clicked: {
266
+ location?: string;
267
+ };
268
+ email_clicked: {
269
+ location?: string;
270
+ };
271
+ appointment_started: {
272
+ service?: string;
273
+ };
274
+ appointment_completed: {
275
+ service?: string;
276
+ durationMinutes?: number;
277
+ };
278
+ }
279
+ /** Authentication lifecycle. */
280
+ interface AuthenticationEventMap {
281
+ login_started: {
282
+ method?: string;
283
+ };
284
+ login_completed: {
285
+ method?: string;
286
+ };
287
+ login_failed: {
288
+ method?: string;
289
+ reason?: string;
290
+ };
291
+ signup_started: {
292
+ method?: string;
293
+ };
294
+ signup_completed: {
295
+ method?: string;
296
+ plan?: string;
297
+ };
298
+ }
299
+ /** Ecommerce funnel. `purchase_completed` carries first-class revenue fields. */
300
+ interface EcommerceEventMap {
301
+ product_viewed: {
302
+ productId: string;
303
+ name?: string;
304
+ price?: number;
305
+ currency?: string;
306
+ category?: string;
307
+ };
308
+ add_to_cart: {
309
+ productId: string;
310
+ quantity?: number;
311
+ price?: number;
312
+ currency?: string;
313
+ };
314
+ remove_from_cart: {
315
+ productId: string;
316
+ quantity?: number;
317
+ };
318
+ checkout_started: {
319
+ cartValue?: number;
320
+ itemCount?: number;
321
+ currency?: string;
322
+ };
323
+ checkout_completed: {
324
+ cartValue?: number;
325
+ itemCount?: number;
326
+ currency?: string;
327
+ };
328
+ purchase_completed: {
329
+ revenue: number;
330
+ currency: string;
331
+ orderId?: string;
332
+ itemCount?: number;
333
+ };
334
+ }
335
+ /** Content engagement. */
336
+ interface EngagementEventMap {
337
+ video_started: {
338
+ id?: string;
339
+ title?: string;
340
+ };
341
+ video_completed: {
342
+ id?: string;
343
+ title?: string;
344
+ durationSeconds?: number;
345
+ };
346
+ document_downloaded: {
347
+ file: string;
348
+ category?: string;
349
+ };
350
+ social_link_clicked: {
351
+ network: string;
352
+ location?: string;
353
+ };
354
+ }
355
+ /**
356
+ * The standardized, framework-agnostic event catalog. Applications should reuse
357
+ * these names rather than inventing near-duplicates.
358
+ */
359
+ interface BaseAnalyticsEventMap extends CommonEventMap, LeadGenerationEventMap, AuthenticationEventMap, EcommerceEventMap, EngagementEventMap {
360
+ }
361
+ /**
362
+ * Open registry. Augment this interface from application code to add strongly
363
+ * typed application-specific events. It extends {@link BaseAnalyticsEventMap} so
364
+ * the standard catalog is always available.
365
+ */
366
+ interface AnalyticsEventMap extends BaseAnalyticsEventMap {
367
+ }
368
+ /** Every known event name (standard catalog + application augmentations). */
369
+ type AnalyticsEventName = Extract<keyof AnalyticsEventMap, string>;
370
+ /** The typed property model for a given event. */
371
+ type AnalyticsEventProperties<K extends AnalyticsEventName> = AnalyticsEventMap[K];
372
+ type HasRequiredKeys<T> = {
373
+ [K in keyof T]-?: undefined extends T[K] ? never : K;
374
+ }[keyof T] extends never ? false : true;
375
+ /**
376
+ * The `track()` argument tuple for an event. Events with at least one required
377
+ * property force a `properties` argument; events with only optional properties
378
+ * (or none) make it optional.
379
+ */
380
+ type TrackArgs<K extends AnalyticsEventName> = HasRequiredKeys<AnalyticsEventProperties<K>> extends true ? [properties: AnalyticsEventProperties<K>] : [properties?: AnalyticsEventProperties<K>];
381
+
382
+ type AnalyticsEnvironment = 'development' | 'test' | 'staging' | 'production';
383
+ type AnalyticsProviderName = 'plausible' | 'console' | 'custom';
384
+ type AnalyticsErrorHandler = (error: AnalyticsError) => void;
385
+ /** Observability payload passed to `config.onEvent` for every attempted send. */
386
+ interface AnalyticsEventInfo {
387
+ type: 'event' | 'pageview';
388
+ name: string;
389
+ provider: AnalyticsProviderName;
390
+ environment: AnalyticsEnvironment;
391
+ enabled: boolean;
392
+ /** `false` when analytics is disabled or the provider was unavailable. */
393
+ delivered: boolean;
394
+ /** Human-readable explanation when `delivered` is `false`. */
395
+ reason?: string;
396
+ /** Sanitized properties actually forwarded (never contains blocked keys). */
397
+ properties?: Record<string, unknown>;
398
+ /** Key paths removed by the privacy layer. */
399
+ blockedKeys?: string[];
400
+ }
401
+ interface AnalyticsConfigBase {
402
+ /**
403
+ * Current environment. Drives the enabled/disabled default:
404
+ * `development` → off, `test` → off, `staging` → off (opt-in), `production` → on.
405
+ */
406
+ environment: AnalyticsEnvironment;
407
+ /**
408
+ * Explicit master switch. When set it always wins over the environment default —
409
+ * e.g. `enabled: true` in `development`, or `enabled: false` in `production`.
410
+ */
411
+ enabled?: boolean;
412
+ /** Verbose logging + stricter privacy handling. Default: `true` in `development`. */
413
+ debug?: boolean;
414
+ /**
415
+ * Multi-tenant metadata. Treated as configuration only — it is **not** sent as a
416
+ * property unless `includeTenantKey` is `true`.
417
+ */
418
+ tenantKey?: string;
419
+ /** Send `tenantKey` as a property on every event. Default `false`. */
420
+ includeTenantKey?: boolean;
421
+ /** Property name used when `includeTenantKey` is `true`. Default `"tenant"`. */
422
+ tenantPropertyName?: string;
423
+ /** Properties merged into every event (lowest precedence). */
424
+ defaultProperties?: Record<string, unknown>;
425
+ /** Privacy / sanitization overrides. */
426
+ privacy?: PrivacyConfig;
427
+ /** Observability hook for internal errors. Never receives provider internals. */
428
+ onError?: AnalyticsErrorHandler;
429
+ /** Observability hook fired for every `track`/`pageView` attempt. */
430
+ onEvent?: (info: AnalyticsEventInfo) => void;
431
+ }
432
+ interface PlausibleAnalyticsConfig extends AnalyticsConfigBase {
433
+ provider: 'plausible';
434
+ plausible: PlausibleProviderConfig;
435
+ }
436
+ interface ConsoleAnalyticsConfig extends AnalyticsConfigBase {
437
+ provider: 'console';
438
+ console?: ConsoleProviderConfig;
439
+ }
440
+ interface CustomAnalyticsConfig extends AnalyticsConfigBase {
441
+ provider: 'custom';
442
+ /** Factory for a bespoke adapter — the forward-compatibility hook for new providers. */
443
+ adapter: AnalyticsAdapterFactory;
444
+ }
445
+ /**
446
+ * Strongly typed, provider-agnostic configuration. A discriminated union on
447
+ * `provider` so the compiler requires the matching provider block.
448
+ */
449
+ type AnalyticsConfig = PlausibleAnalyticsConfig | ConsoleAnalyticsConfig | CustomAnalyticsConfig;
450
+ /** Config after defaults, validation and environment resolution have been applied. */
451
+ interface ResolvedAnalyticsConfig {
452
+ provider: AnalyticsProviderName;
453
+ environment: AnalyticsEnvironment;
454
+ enabled: boolean;
455
+ disabledReason?: string;
456
+ debug: boolean;
457
+ tenantKey?: string;
458
+ includeTenantKey: boolean;
459
+ tenantPropertyName: string;
460
+ defaultProperties: Record<string, unknown>;
461
+ privacy: ResolvedPrivacyPolicy;
462
+ onError?: AnalyticsErrorHandler;
463
+ onEvent?: (info: AnalyticsEventInfo) => void;
464
+ plausible?: ResolvedPlausibleConfig;
465
+ console?: ResolvedConsoleConfig;
466
+ adapter?: AnalyticsAdapterFactory;
467
+ }
468
+ /** Small, safe snapshot of client state (no internal config leaks). */
469
+ interface AnalyticsClientState {
470
+ initialized: boolean;
471
+ enabled: boolean;
472
+ environment: AnalyticsEnvironment | null;
473
+ provider: AnalyticsProviderName | null;
474
+ debug: boolean;
475
+ }
476
+ /**
477
+ * The framework-independent analytics client. The core never imports React.
478
+ */
479
+ interface AnalyticsClient {
480
+ /**
481
+ * Configure and start the client. Idempotent: calling again with an equivalent
482
+ * config is a no-op; calling with a different config reconfigures in place.
483
+ */
484
+ initialize(config: AnalyticsConfig): Promise<void>;
485
+ /** Send a strongly typed business event (revenue fields included where relevant). */
486
+ track<K extends AnalyticsEventName>(event: K, ...args: TrackArgs<K>): void;
487
+ /**
488
+ * Send an explicit page view. Normal SPA navigation is handled by the provider
489
+ * itself — use this only for special cases (virtual pages, wizards, modals).
490
+ */
491
+ pageView(path?: string): void;
492
+ /** `true` when analytics is currently sending events. */
493
+ isEnabled(): boolean;
494
+ /** `true` once `initialize()` has been called (and not since `destroy()`d). */
495
+ isInitialized(): boolean;
496
+ /**
497
+ * Toggle sending at runtime (e.g. after a consent decision). Enabling a client
498
+ * that was initialized while disabled will lazily create + start the provider.
499
+ */
500
+ setEnabled(enabled: boolean): void;
501
+ /** Safe state snapshot for debugging / dev tooling. */
502
+ getState(): AnalyticsClientState;
503
+ /** Tear down the provider and reset to the pre-`initialize()` state. */
504
+ destroy(): void;
505
+ }
506
+
507
+ export { type AnalyticsClient as A, type BaseAnalyticsEventMap as B, type CommonEventMap as C, type PrivacyConfig as D, type EcommerceEventMap as E, type PrivacyViolationMode as F, type ResolvedAnalyticsConfig as G, toAnalyticsError as H, type Logger as L, type PiiMode as P, type ResolvedPrivacyPolicy as R, type SanitizeResult as S, type TrackArgs as T, AnalyticsError as a, type AnalyticsEnvironment as b, type AnalyticsConfig as c, type AnalyticsEventName as d, type AnalyticsAdapter as e, type AnalyticsAdapterContext as f, type AnalyticsAdapterFactory as g, type AnalyticsClientState as h, type AnalyticsConfigBase as i, AnalyticsConfigError as j, type AnalyticsErrorCode as k, type AnalyticsErrorHandler as l, type AnalyticsEventInfo as m, type AnalyticsEventMap as n, type AnalyticsEventProperties as o, type AnalyticsProviderName as p, type AuthenticationEventMap as q, type ConsoleAnalyticsConfig as r, type ConsoleProviderConfig as s, type CustomAnalyticsConfig as t, type EngagementEventMap as u, type LeadGenerationEventMap as v, type PlausibleAnalyticsConfig as w, type PlausibleProviderConfig as x, type PlausibleRuntimeMode as y, type PlausibleScriptExtension as z };
package/package.json ADDED
@@ -0,0 +1,97 @@
1
+ {
2
+ "name": "@pl4yzonellc/empire-analytics",
3
+ "version": "0.0.2",
4
+ "description": "Provider-agnostic analytics SDK for React 19 applications. First provider: Plausible.",
5
+ "license": "MIT",
6
+ "author": "pl4yzonellc",
7
+ "type": "module",
8
+ "sideEffects": false,
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE",
19
+ "CHANGELOG.md"
20
+ ],
21
+ "main": "./dist/index.js",
22
+ "module": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js"
28
+ },
29
+ "./testing": {
30
+ "types": "./dist/testing/index.d.ts",
31
+ "import": "./dist/testing/index.js"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "peerDependencies": {
36
+ "@plausible-analytics/tracker": "^0.4.0",
37
+ "react": "^19.0.0",
38
+ "react-dom": "^19.0.0"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "@plausible-analytics/tracker": {
42
+ "optional": true
43
+ },
44
+ "react": {
45
+ "optional": true
46
+ },
47
+ "react-dom": {
48
+ "optional": true
49
+ }
50
+ },
51
+ "devDependencies": {
52
+ "@eslint/js": "^9.18.0",
53
+ "@plausible-analytics/tracker": "^0.4.6",
54
+ "@testing-library/jest-dom": "^6.6.3",
55
+ "@testing-library/react": "^16.1.0",
56
+ "@testing-library/user-event": "^14.5.2",
57
+ "@types/node": "^22.10.5",
58
+ "@types/react": "^19.0.7",
59
+ "@types/react-dom": "^19.0.3",
60
+ "@vitest/coverage-v8": "^3.0.5",
61
+ "eslint": "^9.18.0",
62
+ "eslint-config-prettier": "^9.1.0",
63
+ "eslint-plugin-react": "^7.37.4",
64
+ "eslint-plugin-react-hooks": "^5.1.0",
65
+ "jsdom": "^25.0.1",
66
+ "prettier": "^3.4.2",
67
+ "react": "^19.0.0",
68
+ "react-dom": "^19.0.0",
69
+ "rimraf": "^6.0.1",
70
+ "tsup": "^8.3.5",
71
+ "typescript": "^5.7.3",
72
+ "typescript-eslint": "^8.20.0",
73
+ "vitest": "^3.0.5"
74
+ },
75
+ "keywords": [
76
+ "analytics",
77
+ "plausible",
78
+ "react",
79
+ "sdk",
80
+ "provider-agnostic",
81
+ "typescript",
82
+ "privacy"
83
+ ],
84
+ "scripts": {
85
+ "build": "tsup",
86
+ "dev": "tsup --watch",
87
+ "clean": "rimraf dist coverage",
88
+ "test": "vitest run",
89
+ "test:watch": "vitest",
90
+ "test:coverage": "vitest run --coverage",
91
+ "typecheck": "tsc --noEmit",
92
+ "lint": "eslint .",
93
+ "lint:fix": "eslint . --fix",
94
+ "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
95
+ "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\""
96
+ }
97
+ }