@stacksee/analytics 0.2.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,2 @@
1
+ export * from './src/server'
2
+ export {}
package/dist/server.js ADDED
@@ -0,0 +1,78 @@
1
+ var o = Object.defineProperty;
2
+ var f = (r, e, t) => e in r ? o(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t;
3
+ var a = (r, e, t) => f(r, typeof e != "symbol" ? e + "" : e, t);
4
+ class u {
5
+ constructor(e) {
6
+ a(this, "providers", []);
7
+ a(this, "config");
8
+ a(this, "initialized", !1);
9
+ this.config = e, this.providers = e.providers;
10
+ }
11
+ initialize() {
12
+ if (!this.initialized) {
13
+ for (const e of this.providers)
14
+ e.initialize();
15
+ this.initialized = !0;
16
+ }
17
+ }
18
+ identify(e, t) {
19
+ for (const i of this.providers)
20
+ i.identify(e, t);
21
+ }
22
+ async track(e, t, i) {
23
+ if (!this.initialized) {
24
+ console.warn("[Analytics] Not initialized. Call initialize() first.");
25
+ return;
26
+ }
27
+ const n = {
28
+ action: e,
29
+ category: this.getCategoryFromEventName(e),
30
+ properties: t,
31
+ timestamp: Date.now(),
32
+ userId: i == null ? void 0 : i.userId,
33
+ sessionId: i == null ? void 0 : i.sessionId
34
+ }, c = {
35
+ ...this.config.defaultContext,
36
+ ...i == null ? void 0 : i.context
37
+ }, d = this.providers.map(async (s) => {
38
+ try {
39
+ await s.track(n, c);
40
+ } catch (l) {
41
+ console.error(
42
+ `[Analytics] Provider ${s.name} failed to track event:`,
43
+ l
44
+ );
45
+ }
46
+ });
47
+ await Promise.all(d);
48
+ }
49
+ page(e, t) {
50
+ if (!this.initialized) return;
51
+ const i = {
52
+ ...this.config.defaultContext,
53
+ ...t == null ? void 0 : t.context
54
+ };
55
+ for (const n of this.providers)
56
+ n.page(e, i);
57
+ }
58
+ async shutdown() {
59
+ const e = this.providers.map((t) => "shutdown" in t && typeof t.shutdown == "function" ? t.shutdown() : Promise.resolve());
60
+ await Promise.all(e);
61
+ }
62
+ getCategoryFromEventName(e) {
63
+ const t = e.split("_");
64
+ return t.length > 1 && t[0] ? t[0] : "engagement";
65
+ }
66
+ }
67
+ function v(r) {
68
+ const e = {
69
+ providers: r.providers || [],
70
+ debug: r.debug,
71
+ enabled: r.enabled
72
+ }, t = new u(e);
73
+ return t.initialize(), t;
74
+ }
75
+ export {
76
+ u as ServerAnalytics,
77
+ v as createServerAnalytics
78
+ };
@@ -0,0 +1,24 @@
1
+ import { AnyEventName, AnyEventProperties } from '../../core/events/index.js';
2
+ import { AnalyticsConfig, EventContext } from '../../core/events/types.js';
3
+ export declare class BrowserAnalytics<TEventName extends string = AnyEventName, TEventProperties extends Record<string, unknown> = AnyEventProperties> {
4
+ private providers;
5
+ private context;
6
+ private userId?;
7
+ private sessionId?;
8
+ private initialized;
9
+ private initializePromise?;
10
+ constructor(config: AnalyticsConfig);
11
+ initialize(): Promise<void>;
12
+ private _doInitialize;
13
+ private ensureInitialized;
14
+ identify(userId: string, traits?: Record<string, unknown>): void;
15
+ track(eventName: TEventName, properties: TEventProperties): Promise<void>;
16
+ page(properties?: Record<string, unknown>): void;
17
+ reset(): void;
18
+ updateContext(context: Partial<EventContext>): void;
19
+ private getCategoryFromEventName;
20
+ private generateSessionId;
21
+ private getDeviceType;
22
+ private getOS;
23
+ private getBrowser;
24
+ }
@@ -0,0 +1,20 @@
1
+ import { AnyEventName, AnyEventProperties } from '../../core/events/index.js';
2
+ import { AnalyticsConfig, EventContext } from '../../core/events/types.js';
3
+ export declare class ServerAnalytics<TEventName extends string = AnyEventName, TEventProperties extends Record<string, unknown> = AnyEventProperties> {
4
+ private providers;
5
+ private config;
6
+ private initialized;
7
+ constructor(config: AnalyticsConfig);
8
+ initialize(): void;
9
+ identify(userId: string, traits?: Record<string, unknown>): void;
10
+ track(eventName: TEventName, properties: TEventProperties, options?: {
11
+ userId?: string;
12
+ sessionId?: string;
13
+ context?: EventContext;
14
+ }): Promise<void>;
15
+ page(properties?: Record<string, unknown>, options?: {
16
+ context?: EventContext;
17
+ }): void;
18
+ shutdown(): Promise<void>;
19
+ private getCategoryFromEventName;
20
+ }
@@ -0,0 +1,7 @@
1
+ export { createClientAnalytics, createAnalytics, getAnalytics, track, identify, page, reset, type ClientAnalyticsConfig, } from '../client.js';
2
+ export { BrowserAnalytics } from '../adapters/client/browser-analytics.js';
3
+ export { PostHogClientProvider } from '../providers/posthog/client.js';
4
+ export type { PostHogConfig } from '../providers/posthog/types.js';
5
+ export { BaseAnalyticsProvider } from '../providers/base.provider.js';
6
+ export type { EventCategory, BaseEvent, EventContext, AnalyticsProvider, AnalyticsConfig, } from '../core/events/types.js';
7
+ export type { CreateEventDefinition, ExtractEventNames, ExtractEventPropertiesFromCollection, EventCollection, AnyEventName, AnyEventProperties, } from '../core/events/index.js';
@@ -0,0 +1,57 @@
1
+ import { BrowserAnalytics } from './adapters/client/browser-analytics.js';
2
+ import { AnalyticsProvider } from './core/events/types.js';
3
+ export interface ClientAnalyticsConfig {
4
+ providers?: AnalyticsProvider[];
5
+ debug?: boolean;
6
+ enabled?: boolean;
7
+ }
8
+ /**
9
+ * Initialize analytics for the browser
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { createClientAnalytics } from '@stacksee/analytics/client';
14
+ * import { PostHogClientProvider } from '@stacksee/analytics/providers/posthog';
15
+ *
16
+ * const analytics = createClientAnalytics({
17
+ * providers: [
18
+ * new PostHogClientProvider({
19
+ * apiKey: 'your-api-key',
20
+ * host: 'https://app.posthog.com'
21
+ * })
22
+ * ],
23
+ * debug: true,
24
+ * enabled: true
25
+ * });
26
+ *
27
+ * // Optional: explicitly initialize (otherwise happens on first track/identify/page call)
28
+ * await analytics.initialize();
29
+ * ```
30
+ */
31
+ export declare function createClientAnalytics(config: ClientAnalyticsConfig): BrowserAnalytics;
32
+ export { createClientAnalytics as createAnalytics };
33
+ /**
34
+ * Get the current analytics instance
35
+ */
36
+ export declare function getAnalytics(): BrowserAnalytics;
37
+ /**
38
+ * Convenience function to track events
39
+ */
40
+ export declare function track(eventName: string, properties: Record<string, unknown>): Promise<void>;
41
+ /**
42
+ * Convenience function to identify users
43
+ */
44
+ export declare function identify(userId: string, traits?: Record<string, unknown>): void;
45
+ /**
46
+ * Convenience function to track page views
47
+ */
48
+ export declare function page(properties?: Record<string, unknown>): void;
49
+ /**
50
+ * Convenience function to reset user session
51
+ */
52
+ export declare function reset(): void;
53
+ /**
54
+ * Reset the analytics instance (for testing purposes)
55
+ * @internal
56
+ */
57
+ export declare function resetAnalyticsInstance(): void;
@@ -0,0 +1,18 @@
1
+ export * from './types.js';
2
+ export type CreateEventDefinition<TName extends string, TProperties extends Record<string, unknown> = Record<string, unknown>> = {
3
+ name: TName;
4
+ category: import('./types.js').EventCategory;
5
+ properties: TProperties;
6
+ };
7
+ export type ExtractEventNames<T extends Record<string, {
8
+ name: string;
9
+ }>> = T[keyof T]["name"];
10
+ export type ExtractEventPropertiesFromCollection<T extends Record<string, {
11
+ name: string;
12
+ properties: Record<string, unknown>;
13
+ }>, TEventName extends ExtractEventNames<T>> = Extract<T[keyof T], {
14
+ name: TEventName;
15
+ }>["properties"];
16
+ export type EventCollection<T extends Record<string, CreateEventDefinition<string>>> = T;
17
+ export type AnyEventName = string;
18
+ export type AnyEventProperties = Record<string, unknown>;
@@ -0,0 +1,48 @@
1
+ export type PredefinedEventCategory = "engagement" | "user" | "navigation" | "error" | "performance" | "conversion";
2
+ export type EventCategory = PredefinedEventCategory | (string & Record<never, never>);
3
+ export interface BaseEvent {
4
+ category: EventCategory;
5
+ action: string;
6
+ timestamp?: number;
7
+ userId?: string;
8
+ sessionId?: string;
9
+ properties?: Record<string, unknown>;
10
+ }
11
+ export interface EventContext {
12
+ page?: {
13
+ path: string;
14
+ title?: string;
15
+ referrer?: string;
16
+ };
17
+ device?: {
18
+ type?: string;
19
+ os?: string;
20
+ browser?: string;
21
+ };
22
+ campaign?: {
23
+ source?: string;
24
+ medium?: string;
25
+ name?: string;
26
+ };
27
+ }
28
+ export interface AnalyticsProvider {
29
+ name: string;
30
+ initialize(): Promise<void> | void;
31
+ identify(userId: string, traits?: Record<string, unknown>): Promise<void> | void;
32
+ track(event: BaseEvent, context?: EventContext): Promise<void> | void;
33
+ page(properties?: Record<string, unknown>, context?: EventContext): Promise<void> | void;
34
+ reset(): Promise<void> | void;
35
+ }
36
+ export interface AnalyticsConfig {
37
+ providers: AnalyticsProvider[];
38
+ debug?: boolean;
39
+ enabled?: boolean;
40
+ defaultContext?: Partial<EventContext>;
41
+ }
42
+ export type EventDefinition<T extends string, P = Record<string, unknown>> = {
43
+ name: T;
44
+ category: EventCategory;
45
+ properties?: P;
46
+ };
47
+ export type ExtractEventName<T> = T extends EventDefinition<infer N, unknown> ? N : never;
48
+ export type ExtractEventProperties<T> = T extends EventDefinition<string, infer P> ? P : never;
@@ -0,0 +1,6 @@
1
+ export type { EventCategory, BaseEvent, EventContext, AnalyticsProvider, AnalyticsConfig, } from './core/events/types.js';
2
+ export type { CreateEventDefinition, ExtractEventNames, ExtractEventPropertiesFromCollection, EventCollection, AnyEventName, AnyEventProperties, } from './core/events/index.js';
3
+ export { createAnalytics as createClientAnalytics, getAnalytics, track as trackClient, identify as identifyClient, page as pageClient, reset as resetClient, type ClientAnalyticsConfig, } from './client.js';
4
+ export { createServerAnalytics, ServerAnalytics, type ServerAnalyticsConfig, } from './server.js';
5
+ export { BaseAnalyticsProvider, PostHogClientProvider, PostHogServerProvider, type PostHogConfig, } from './providers/index.js';
6
+ export { BrowserAnalytics } from './adapters/client/browser-analytics.js';
@@ -0,0 +1,17 @@
1
+ import { AnalyticsProvider, BaseEvent, EventContext } from '../core/events/types.js';
2
+ export declare abstract class BaseAnalyticsProvider implements AnalyticsProvider {
3
+ abstract name: string;
4
+ protected debug: boolean;
5
+ protected enabled: boolean;
6
+ constructor(config?: {
7
+ debug?: boolean;
8
+ enabled?: boolean;
9
+ });
10
+ abstract initialize(): Promise<void> | void;
11
+ abstract identify(userId: string, traits?: Record<string, unknown>): Promise<void> | void;
12
+ abstract track(event: BaseEvent, context?: EventContext): Promise<void> | void;
13
+ abstract page(properties?: Record<string, unknown>, context?: EventContext): Promise<void> | void;
14
+ abstract reset(): Promise<void> | void;
15
+ protected log(message: string, data?: unknown): void;
16
+ protected isEnabled(): boolean;
17
+ }
@@ -0,0 +1,4 @@
1
+ export { BaseAnalyticsProvider } from './base.provider.js';
2
+ export { PostHogClientProvider } from './posthog/client.js';
3
+ export { PostHogServerProvider } from './posthog/server.js';
4
+ export type { PostHogConfig } from './posthog/types.js';
@@ -0,0 +1,24 @@
1
+ import { BaseEvent, EventContext } from '../../core/events/types.js';
2
+ import { BaseAnalyticsProvider } from '../base.provider.js';
3
+ import { PostHogConfig } from './types.js';
4
+ import { PostHog } from 'posthog-js';
5
+ declare global {
6
+ interface Window {
7
+ posthog?: PostHog;
8
+ }
9
+ }
10
+ export declare class PostHogClientProvider extends BaseAnalyticsProvider {
11
+ name: string;
12
+ private posthog?;
13
+ private initialized;
14
+ private config;
15
+ constructor(config: PostHogConfig & {
16
+ debug?: boolean;
17
+ enabled?: boolean;
18
+ });
19
+ initialize(): Promise<void>;
20
+ identify(userId: string, traits?: Record<string, unknown>): void;
21
+ track(event: BaseEvent, context?: EventContext): void;
22
+ page(properties?: Record<string, unknown>, context?: EventContext): void;
23
+ reset(): void;
24
+ }
@@ -0,0 +1,19 @@
1
+ import { BaseEvent, EventContext } from '../../core/events/types.js';
2
+ import { BaseAnalyticsProvider } from '../base.provider.js';
3
+ import { PostHogConfig } from './types.js';
4
+ export declare class PostHogServerProvider extends BaseAnalyticsProvider {
5
+ name: string;
6
+ private client?;
7
+ private initialized;
8
+ private config;
9
+ constructor(config: PostHogConfig & {
10
+ debug?: boolean;
11
+ enabled?: boolean;
12
+ });
13
+ initialize(): void;
14
+ identify(userId: string, traits?: Record<string, unknown>): void;
15
+ track(event: BaseEvent, context?: EventContext): void;
16
+ page(properties?: Record<string, unknown>, context?: EventContext): void;
17
+ reset(): Promise<void>;
18
+ shutdown(): Promise<void>;
19
+ }
@@ -0,0 +1,27 @@
1
+ export interface PostHogConfig {
2
+ apiKey: string;
3
+ host?: string;
4
+ autocapture?: boolean;
5
+ capturePageview?: boolean;
6
+ capturePageleave?: boolean;
7
+ debug?: boolean;
8
+ disableCookie?: boolean;
9
+ persistenceType?: "cookie" | "localStorage" | "memory";
10
+ personProfiles?: "always" | "never" | "identified_only";
11
+ }
12
+ export interface PostHogInstance {
13
+ init(apiKey: string, config?: Partial<PostHogConfig>): void;
14
+ identify(distinctId: string, properties?: Record<string, unknown>): void;
15
+ capture(event: string, properties?: Record<string, unknown>): void;
16
+ reset(): void;
17
+ debug(enabled?: boolean): void;
18
+ opt_out_capturing(): void;
19
+ opt_in_capturing(): void;
20
+ has_opted_out_capturing(): boolean;
21
+ get_distinct_id(): string;
22
+ alias(alias: string): void;
23
+ set_config(config: Partial<PostHogConfig>): void;
24
+ get_config<K extends keyof PostHogConfig>(key: K): PostHogConfig[K];
25
+ capture_pageview(): void;
26
+ capture_pageleave(): void;
27
+ }
@@ -0,0 +1,6 @@
1
+ export { createServerAnalytics, ServerAnalytics, type ServerAnalyticsConfig, } from '../server.js';
2
+ export { PostHogServerProvider } from '../providers/posthog/server.js';
3
+ export type { PostHogConfig } from '../providers/posthog/types.js';
4
+ export { BaseAnalyticsProvider } from '../providers/base.provider.js';
5
+ export type { EventCategory, BaseEvent, EventContext, AnalyticsProvider, AnalyticsConfig, } from '../core/events/types.js';
6
+ export type { CreateEventDefinition, ExtractEventNames, ExtractEventPropertiesFromCollection, EventCollection, AnyEventName, AnyEventProperties, } from '../core/events/index.js';
@@ -0,0 +1,29 @@
1
+ import { ServerAnalytics } from './adapters/server/server-analytics.js';
2
+ import { AnalyticsProvider } from './core/events/types.js';
3
+ export interface ServerAnalyticsConfig {
4
+ providers?: AnalyticsProvider[];
5
+ debug?: boolean;
6
+ enabled?: boolean;
7
+ }
8
+ /**
9
+ * Create a server analytics instance
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { createServerAnalytics } from '@stacksee/analytics/server';
14
+ * import { PostHogServerProvider } from '@stacksee/analytics/providers/posthog';
15
+ *
16
+ * const analytics = createServerAnalytics({
17
+ * providers: [
18
+ * new PostHogServerProvider({
19
+ * apiKey: process.env.POSTHOG_API_KEY,
20
+ * host: process.env.POSTHOG_HOST
21
+ * })
22
+ * ],
23
+ * debug: true,
24
+ * enabled: true
25
+ * });
26
+ * ```
27
+ */
28
+ export declare function createServerAnalytics(config: ServerAnalyticsConfig): ServerAnalytics;
29
+ export { ServerAnalytics };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { BaseAnalyticsProvider } from '../src/providers/base.provider';
2
+ import { BaseEvent, EventContext } from '../src/core/events/types';
3
+ export declare class MockAnalyticsProvider extends BaseAnalyticsProvider {
4
+ name: string;
5
+ private initialized;
6
+ calls: {
7
+ initialize: number;
8
+ identify: Array<{
9
+ userId: string;
10
+ traits?: Record<string, unknown>;
11
+ }>;
12
+ track: Array<{
13
+ event: BaseEvent;
14
+ context?: EventContext;
15
+ }>;
16
+ page: Array<{
17
+ properties?: Record<string, unknown>;
18
+ context?: EventContext;
19
+ }>;
20
+ reset: number;
21
+ };
22
+ initialize(): void;
23
+ identify(userId: string, traits?: Record<string, unknown>): void;
24
+ track(event: BaseEvent, context?: EventContext): void;
25
+ page(properties?: Record<string, unknown>, context?: EventContext): void;
26
+ reset(): void;
27
+ clearCalls(): void;
28
+ isInitialized(): boolean;
29
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@stacksee/analytics",
3
+ "version": "0.2.2",
4
+ "description": "A highly typed, provider-agnostic analytics library for TypeScript applications",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ },
11
+ "./client": {
12
+ "types": "./dist/client.d.ts",
13
+ "import": "./dist/client.js"
14
+ },
15
+ "./server": {
16
+ "types": "./dist/server.d.ts",
17
+ "import": "./dist/server.js"
18
+ }
19
+ },
20
+ "main": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "keywords": [
28
+ "analytics",
29
+ "typescript",
30
+ "posthog",
31
+ "tracking",
32
+ "events",
33
+ "type-safe"
34
+ ],
35
+ "author": "Chris Jayden <https://github.com/multiplehats>",
36
+ "license": "ISC",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/stackseehq/analytics.git"
40
+ },
41
+ "devDependencies": {
42
+ "@biomejs/biome": "1.9.4",
43
+ "@changesets/cli": "^2.29.4",
44
+ "@svitejs/changesets-changelog-github-compact": "^1.2.0",
45
+ "@types/node": "^20.14.11",
46
+ "@vitest/coverage-v8": "2.1.9",
47
+ "globals": "^15.8.0",
48
+ "jsdom": "^26.1.0",
49
+ "prettier": "^3.3.3",
50
+ "typescript": "~5.5.3",
51
+ "vite": "^6.3.5",
52
+ "vite-plugin-dts": "^4.5.4",
53
+ "vitest": "^2.0.3"
54
+ },
55
+ "optionalDependencies": {
56
+ "posthog-js": "^1.96.0",
57
+ "posthog-node": "^3.2.0"
58
+ },
59
+ "engines": {
60
+ "pnpm": ">=9.0.0",
61
+ "node": ">=20"
62
+ },
63
+ "scripts": {
64
+ "test": "vitest run",
65
+ "test:watch": "vitest",
66
+ "build": "vite build",
67
+ "deploy": "npm publish",
68
+ "typecheck": "tsc --noEmit",
69
+ "ci:publish": "pnpm build && changeset publish",
70
+ "lint": "biome lint .",
71
+ "format": "biome format --write .",
72
+ "biome": "biome"
73
+ }
74
+ }