B2B-88: add starter kit structure and elements
This commit is contained in:
5
packages/analytics/README.md
Normal file
5
packages/analytics/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Analytics - @kit/analytics
|
||||
|
||||
@kit/analytics Package provides a simple and consistent API for tracking analytics events in web applications.
|
||||
|
||||
Please refer to the [documentation](https://makerkit.dev/docs/next-supabase-turbo/analytics/analytics-and-events).
|
||||
3
packages/analytics/eslint.config.mjs
Normal file
3
packages/analytics/eslint.config.mjs
Normal file
@@ -0,0 +1,3 @@
|
||||
import eslintConfigBase from '@kit/eslint-config/base.js';
|
||||
|
||||
export default eslintConfigBase;
|
||||
1
packages/analytics/node_modules/@kit/eslint-config
generated
vendored
Symbolic link
1
packages/analytics/node_modules/@kit/eslint-config
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../tooling/eslint
|
||||
1
packages/analytics/node_modules/@kit/prettier-config
generated
vendored
Symbolic link
1
packages/analytics/node_modules/@kit/prettier-config
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../tooling/prettier
|
||||
1
packages/analytics/node_modules/@kit/tsconfig
generated
vendored
Symbolic link
1
packages/analytics/node_modules/@kit/tsconfig
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../tooling/typescript
|
||||
1
packages/analytics/node_modules/@types/node
generated
vendored
Symbolic link
1
packages/analytics/node_modules/@types/node
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../node_modules/.pnpm/@types+node@22.15.30/node_modules/@types/node
|
||||
28
packages/analytics/package.json
Normal file
28
packages/analytics/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@kit/analytics",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"clean": "git clean -xdf .turbo node_modules",
|
||||
"format": "prettier --check \"**/*.{ts,tsx}\"",
|
||||
"lint": "eslint .",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"prettier": "@kit/prettier-config",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/eslint-config": "workspace:*",
|
||||
"@kit/prettier-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@types/node": "^22.15.18"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
94
packages/analytics/src/analytics-manager.ts
Normal file
94
packages/analytics/src/analytics-manager.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { NullAnalyticsService } from './null-analytics-service';
|
||||
import type {
|
||||
AnalyticsManager,
|
||||
AnalyticsService,
|
||||
CreateAnalyticsManagerOptions,
|
||||
} from './types';
|
||||
|
||||
export function createAnalyticsManager<T extends string, Config extends object>(
|
||||
options: CreateAnalyticsManagerOptions<T, Config>,
|
||||
): AnalyticsManager {
|
||||
const activeServices = new Map<T, AnalyticsService>();
|
||||
|
||||
const getActiveServices = (): AnalyticsService[] => {
|
||||
if (activeServices.size === 0) {
|
||||
console.debug(
|
||||
'No active analytics services. Using NullAnalyticsService.',
|
||||
);
|
||||
|
||||
return [NullAnalyticsService];
|
||||
}
|
||||
|
||||
return Array.from(activeServices.values());
|
||||
};
|
||||
|
||||
const registerActiveServices = (
|
||||
options: CreateAnalyticsManagerOptions<T, Config>,
|
||||
) => {
|
||||
Object.keys(options.providers).forEach((provider) => {
|
||||
const providerKey = provider as keyof typeof options.providers;
|
||||
const factory = options.providers[providerKey];
|
||||
|
||||
if (!factory) {
|
||||
console.warn(
|
||||
`Analytics provider '${provider}' not registered. Skipping initialization.`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const service = factory();
|
||||
activeServices.set(provider as T, service);
|
||||
|
||||
void service.initialize();
|
||||
});
|
||||
};
|
||||
|
||||
registerActiveServices(options);
|
||||
|
||||
return {
|
||||
addProvider: (provider: T, config: Config) => {
|
||||
const factory = options.providers[provider];
|
||||
|
||||
if (!factory) {
|
||||
console.warn(
|
||||
`Analytics provider '${provider}' not registered. Skipping initialization.`,
|
||||
);
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const service = factory(config);
|
||||
activeServices.set(provider, service);
|
||||
|
||||
return service.initialize();
|
||||
},
|
||||
|
||||
removeProvider: (provider: T) => {
|
||||
activeServices.delete(provider);
|
||||
},
|
||||
|
||||
identify: (userId: string, traits?: Record<string, string>) => {
|
||||
return Promise.all(
|
||||
getActiveServices().map((service) => service.identify(userId, traits)),
|
||||
);
|
||||
},
|
||||
|
||||
trackPageView: (path: string) => {
|
||||
return Promise.all(
|
||||
getActiveServices().map((service) => service.trackPageView(path)),
|
||||
);
|
||||
},
|
||||
|
||||
trackEvent: (
|
||||
eventName: string,
|
||||
eventProperties?: Record<string, string | string[]>,
|
||||
) => {
|
||||
return Promise.all(
|
||||
getActiveServices().map((service) =>
|
||||
service.trackEvent(eventName, eventProperties),
|
||||
),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
9
packages/analytics/src/index.ts
Normal file
9
packages/analytics/src/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createAnalyticsManager } from './analytics-manager';
|
||||
import { NullAnalyticsService } from './null-analytics-service';
|
||||
import type { AnalyticsManager } from './types';
|
||||
|
||||
export const analytics: AnalyticsManager = createAnalyticsManager({
|
||||
providers: {
|
||||
null: () => NullAnalyticsService,
|
||||
},
|
||||
});
|
||||
23
packages/analytics/src/null-analytics-service.ts
Normal file
23
packages/analytics/src/null-analytics-service.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { AnalyticsService } from './types';
|
||||
|
||||
const noop = (event: string) => {
|
||||
// do nothing - this is to prevent errors when the analytics service is not initialized
|
||||
|
||||
return async (...args: unknown[]) => {
|
||||
console.debug(
|
||||
`Noop analytics service called with event: ${event}`,
|
||||
...args.filter(Boolean),
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Null analytics service that does nothing. It is initialized with a noop function. This is useful for testing or when
|
||||
* the user is calling analytics methods before the analytics service is initialized.
|
||||
*/
|
||||
export const NullAnalyticsService: AnalyticsService = {
|
||||
initialize: noop('initialize'),
|
||||
trackPageView: noop('trackPageView'),
|
||||
trackEvent: noop('trackEvent'),
|
||||
identify: noop('identify'),
|
||||
};
|
||||
41
packages/analytics/src/types.ts
Normal file
41
packages/analytics/src/types.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
interface TrackEvent {
|
||||
trackEvent(
|
||||
eventName: string,
|
||||
eventProperties?: Record<string, string | string[]>,
|
||||
): Promise<unknown>;
|
||||
}
|
||||
|
||||
interface TrackPageView {
|
||||
trackPageView(path: string): Promise<unknown>;
|
||||
}
|
||||
|
||||
interface Identify {
|
||||
identify(userId: string, traits?: Record<string, string>): Promise<unknown>;
|
||||
}
|
||||
|
||||
interface ProviderManager {
|
||||
addProvider(provider: string, config: object): Promise<unknown>;
|
||||
|
||||
removeProvider(provider: string): void;
|
||||
}
|
||||
|
||||
export interface AnalyticsService extends TrackPageView, TrackEvent, Identify {
|
||||
initialize(): Promise<unknown>;
|
||||
}
|
||||
|
||||
export type AnalyticsProviderFactory<Config extends object> = (
|
||||
config?: Config,
|
||||
) => AnalyticsService;
|
||||
|
||||
export interface CreateAnalyticsManagerOptions<
|
||||
T extends string,
|
||||
Config extends object,
|
||||
> {
|
||||
providers: Record<T, AnalyticsProviderFactory<Config>>;
|
||||
}
|
||||
|
||||
export interface AnalyticsManager
|
||||
extends TrackPageView,
|
||||
TrackEvent,
|
||||
Identify,
|
||||
ProviderManager {}
|
||||
8
packages/analytics/tsconfig.json
Normal file
8
packages/analytics/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@kit/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||
},
|
||||
"include": ["*.ts", "src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user