working version of appRouter

This commit is contained in:
goenning 2023-09-01 10:52:15 +01:00
parent 90317d71d3
commit f4edab87af
21 changed files with 249 additions and 93 deletions

View file

@ -0,0 +1,48 @@
{
"name": "@aptabase/nextjs",
"version": "0.0.1",
"type": "module",
"description": "Next.js SDK for Aptabase: Open Source, Privacy-First and Simple Analytics for Mobile, Desktop and Web Apps",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./server": {
"types": "./dist/server.d.ts",
"default": "./dist/server.js"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/aptabase/aptabase-js.git",
"directory": "packages/nextjs"
},
"bugs": {
"url": "https://github.com/aptabase/aptabase-js/issues"
},
"homepage": "https://github.com/aptabase/aptabase-js",
"license": "MIT",
"scripts": {
"build": "tsc"
},
"files": [
"README.md",
"LICENSE",
"dist",
"package.json"
],
"devDependencies": {
"@rollup/plugin-replace": "5.0.2",
"@rollup/plugin-typescript": "11.1.3",
"rollup": "3.28.1",
"@rollup/plugin-terser": "0.4.3",
"tslib": "2.6.2",
"typescript": "5.2.2"
},
"peerDependencies": {
"react": "^18.0.0",
"next": "^13.0.0"
}
}

View file

@ -0,0 +1,82 @@
'use client';
import { createContext, useContext } from 'react';
import { AptabaseOptions } from './types';
type TrackEventFn = (eventName: string, props?: Record<string, string | number | boolean>) => Promise<void>;
type ContextProps = {
appKey?: string;
client?: AptabaseClient;
} & AptabaseOptions;
export type AptabaseClient = {
trackEvent: TrackEventFn;
};
const AptabaseContext = createContext<ContextProps>({});
type Props = {
appKey: string;
options?: AptabaseOptions;
children: React.ReactNode;
};
export function AptabaseProvider({ appKey, options, children }: Props) {
const client: AptabaseClient = {
trackEvent: (eventName, props) => sendEvent(appKey, eventName, props),
};
return <AptabaseContext.Provider value={{ appKey, ...options, client }}>{children}</AptabaseContext.Provider>;
}
export function useAptabase(): AptabaseClient {
const ctx = useContext(AptabaseContext);
if (!ctx.client) {
throw new Error(
'useAptabase must be used within AptabaseProvider. Did you forget to wrap your app in <AptabaseProvider>?',
);
}
return ctx.client;
}
async function sendEvent(
appKey: string | undefined,
eventName: string,
props?: Record<string, string | number | boolean>,
): Promise<void> {
if (!appKey) return Promise.resolve();
const body = JSON.stringify({
timestamp: new Date().toISOString(),
sessionId: 'CHANGE-THIS',
eventName: eventName,
systemProps: {
isDebug: true,
locale: 'en',
appVersion: '',
sdkVersion: 'aptabase-nextjs@0.0.1',
},
props: props,
});
try {
const response = await fetch('http://localhost:3000/api/v0/event', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'App-Key': appKey,
},
credentials: 'omit',
body,
});
if (response.status >= 300) {
const responseBody = await response.text();
console.warn(`Failed to send event "${eventName}": ${response.status} ${responseBody}`);
}
} catch (e) {
console.warn(`Failed to send event "${eventName}": ${e}`);
}
}

5
packages/nextjs/src/global.d.ts vendored Normal file
View file

@ -0,0 +1,5 @@
type AptabaseState = {
appKey?: string;
};
declare var __APTABASE__: AptabaseState;

View file

@ -0,0 +1,5 @@
export * from './client';
export function init(appKey: string) {
globalThis.__APTABASE__ = { appKey };
}

View file

@ -0,0 +1,39 @@
import { headers } from 'next/headers';
export async function trackEvent(eventName: string, props?: Record<string, string | number | boolean>): Promise<void> {
const appKey = globalThis.__APTABASE__.appKey;
if (!appKey) return Promise.resolve();
const body = JSON.stringify({
timestamp: new Date().toISOString(),
sessionId: 'CHANGE-THIS',
eventName: eventName,
systemProps: {
isDebug: true,
locale: 'en',
appVersion: '',
sdkVersion: 'aptabase-nextjs@0.0.1',
},
props: props,
});
try {
const response = await fetch('http://localhost:3000/api/v0/event', {
method: 'POST',
headers: {
'User-Agent': headers().get('User-Agent') ?? '',
'Content-Type': 'application/json',
'App-Key': appKey,
},
credentials: 'omit',
body,
});
if (response.status >= 300) {
const responseBody = await response.text();
console.warn(`Failed to send event "${eventName}": ${response.status} ${responseBody}`);
}
} catch (e) {
console.warn(`Failed to send event "${eventName}": ${e}`);
}
}

View file

@ -0,0 +1,4 @@
export type AptabaseOptions = {
host?: string;
appVersion?: string;
};

View file

@ -0,0 +1,22 @@
{
"compilerOptions": {
"declaration": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"module": "esnext",
"jsx": "react-jsx",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"outDir": "dist",
"sourceMap": true,
"strict": true,
"target": "es2020",
"types": ["node"],
},
"include": ["src/*", "src/global.d.ts"],
"exclude": ["node_modules"]
}