coonlink-openpanel-dev 26.1.1

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.
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # `coonlink-openpanel`
2
+
3
+ Reusable OpenPanel proxy setup for Next.js projects.
4
+
5
+ ## What it gives you
6
+
7
+ - Loads the SDK through your own domain
8
+ - Sends browser requests to `/api/op`
9
+ - Proxies tracking requests from your app to your configured upstream API
10
+ - Keeps the env shape consistent across projects
11
+
12
+ ## Expected environment variables
13
+
14
+ ```env
15
+ OPENPANEL_CLIENT_ID=your-client-id
16
+ OPENPANEL_EVENTS_ENDPOINT=https://analysisapi.example.com
17
+ NEXT_PUBLIC_URL_CANONICAL=https://your-app.example.com
18
+ ```
19
+
20
+ `OPENPANEL_EVENTS_ENDPOINT` must be the upstream API base URL, not `/track`.
21
+
22
+ ## Usage in a Next.js app
23
+
24
+ ### `app/layout.tsx`
25
+
26
+ ```tsx
27
+ import { OpenPanelAnalytics, getOpenPanelConfigFromEnv } from 'coonlink-openpanel'
28
+
29
+ const openPanelConfig = getOpenPanelConfigFromEnv()
30
+
31
+ export default function RootLayout({
32
+ children,
33
+ }: Readonly<{ children: React.ReactNode }>) {
34
+ return (
35
+ <html lang="en">
36
+ <body>
37
+ {openPanelConfig ? <OpenPanelAnalytics config={openPanelConfig} /> : null}
38
+ {children}
39
+ </body>
40
+ </html>
41
+ )
42
+ }
43
+ ```
44
+
45
+ ### `app/api/[...op]/route.ts`
46
+
47
+ ```ts
48
+ import { createOpenPanelProxyRouteHandlersFromEnv } from 'coonlink-openpanel/server'
49
+
50
+ export const { GET, POST } = createOpenPanelProxyRouteHandlersFromEnv()
51
+ ```
52
+
53
+ ### Client components
54
+
55
+ ```tsx
56
+ 'use client'
57
+
58
+ import { useOpenPanel } from 'coonlink-openpanel/client'
59
+
60
+ export function ExampleButton() {
61
+ const op = useOpenPanel()
62
+
63
+ return (
64
+ <button type="button" onClick={() => op.track('example_click')}>
65
+ Track event
66
+ </button>
67
+ )
68
+ }
69
+ ```
@@ -0,0 +1,6 @@
1
+ import type { CoonlinkOpenPanelConfig } from './config';
2
+ type OpenPanelAnalyticsProps = {
3
+ config: CoonlinkOpenPanelConfig;
4
+ };
5
+ export declare function OpenPanelAnalytics({ config }: OpenPanelAnalyticsProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { OpenPanelComponent } from '@openpanel/nextjs';
3
+ export function OpenPanelAnalytics({ config }) {
4
+ return (_jsx(OpenPanelComponent, { apiUrl: config.proxyPath, clientId: config.clientId, scriptUrl: config.scriptUrl, trackAttributes: true, trackOutgoingLinks: true, trackScreenViews: true }));
5
+ }
@@ -0,0 +1 @@
1
+ export { useOpenPanel } from '@openpanel/nextjs';
package/dist/client.js ADDED
@@ -0,0 +1,2 @@
1
+ 'use client';
2
+ export { useOpenPanel } from '@openpanel/nextjs';
@@ -0,0 +1,23 @@
1
+ export type OpenPanelEnvSource = {
2
+ [key: string]: string | undefined;
3
+ NEXT_PUBLIC_URL_CANONICAL?: string;
4
+ OPENPANEL_CLIENT_ID?: string;
5
+ OPENPANEL_EVENTS_ENDPOINT?: string;
6
+ };
7
+ export type CoonlinkOpenPanelConfig = {
8
+ clientId: string;
9
+ proxyPath: string;
10
+ scriptUrl: string;
11
+ upstreamApiUrl: string;
12
+ };
13
+ export declare const defaultOpenPanelProxyPath = "/api/op";
14
+ export declare function createOpenPanelConfig(input: {
15
+ canonicalUrl?: string;
16
+ clientId?: string;
17
+ eventsEndpoint?: string;
18
+ proxyPath?: string;
19
+ }): CoonlinkOpenPanelConfig | null;
20
+ export declare function getOpenPanelConfigFromEnv(env?: OpenPanelEnvSource, options?: {
21
+ proxyPath?: string;
22
+ }): CoonlinkOpenPanelConfig | null;
23
+ export declare function isOpenPanelEnabled(config: CoonlinkOpenPanelConfig | null): config is CoonlinkOpenPanelConfig;
package/dist/config.js ADDED
@@ -0,0 +1,53 @@
1
+ export const defaultOpenPanelProxyPath = '/api/op';
2
+ function normalize(value) {
3
+ return (value ?? '').trim();
4
+ }
5
+ function stripTrailingSlash(value) {
6
+ return value.replace(/\/+$/, '');
7
+ }
8
+ function withLeadingSlash(value) {
9
+ return value.startsWith('/') ? value : `/${value}`;
10
+ }
11
+ function joinPath(basePath, suffix) {
12
+ return `${stripTrailingSlash(basePath)}${suffix}`;
13
+ }
14
+ export function createOpenPanelConfig(input) {
15
+ const clientId = normalize(input.clientId);
16
+ const eventsEndpoint = normalize(input.eventsEndpoint);
17
+ const canonicalUrl = normalize(input.canonicalUrl);
18
+ const proxyPath = withLeadingSlash(stripTrailingSlash(normalize(input.proxyPath) || defaultOpenPanelProxyPath));
19
+ if (!clientId || !eventsEndpoint)
20
+ return null;
21
+ let upstreamApiUrl;
22
+ if (eventsEndpoint.startsWith('http://') ||
23
+ eventsEndpoint.startsWith('https://')) {
24
+ upstreamApiUrl = stripTrailingSlash(eventsEndpoint);
25
+ }
26
+ else {
27
+ if (!canonicalUrl)
28
+ return null;
29
+ try {
30
+ upstreamApiUrl = stripTrailingSlash(new URL(eventsEndpoint, canonicalUrl).toString());
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ return {
37
+ clientId,
38
+ proxyPath,
39
+ scriptUrl: joinPath(proxyPath, '/op1.js'),
40
+ upstreamApiUrl,
41
+ };
42
+ }
43
+ export function getOpenPanelConfigFromEnv(env = process.env, options) {
44
+ return createOpenPanelConfig({
45
+ canonicalUrl: env.NEXT_PUBLIC_URL_CANONICAL,
46
+ clientId: env.OPENPANEL_CLIENT_ID,
47
+ eventsEndpoint: env.OPENPANEL_EVENTS_ENDPOINT,
48
+ proxyPath: options?.proxyPath,
49
+ });
50
+ }
51
+ export function isOpenPanelEnabled(config) {
52
+ return config !== null;
53
+ }
@@ -0,0 +1,4 @@
1
+ import 'server-only';
2
+ export { OpenPanelAnalytics } from './OpenPanelAnalytics';
3
+ export { createOpenPanelConfig, defaultOpenPanelProxyPath, getOpenPanelConfigFromEnv, isOpenPanelEnabled, } from './config';
4
+ export type { CoonlinkOpenPanelConfig, OpenPanelEnvSource } from './config';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import 'server-only';
2
+ export { OpenPanelAnalytics } from './OpenPanelAnalytics';
3
+ export { createOpenPanelConfig, defaultOpenPanelProxyPath, getOpenPanelConfigFromEnv, isOpenPanelEnabled, } from './config';
@@ -0,0 +1,15 @@
1
+ import 'server-only';
2
+ import { createOpenPanelConfig, getOpenPanelConfigFromEnv } from './config';
3
+ import type { CoonlinkOpenPanelConfig, OpenPanelEnvSource } from './config';
4
+ export declare function createOpenPanelProxyRouteHandlers(config: CoonlinkOpenPanelConfig | null): {
5
+ GET: any;
6
+ POST: any;
7
+ };
8
+ export declare function createOpenPanelProxyRouteHandlersFromEnv(env?: OpenPanelEnvSource, options?: {
9
+ proxyPath?: string;
10
+ }): {
11
+ GET: any;
12
+ POST: any;
13
+ };
14
+ export { createOpenPanelConfig, getOpenPanelConfigFromEnv };
15
+ export type { CoonlinkOpenPanelConfig, OpenPanelEnvSource };
package/dist/server.js ADDED
@@ -0,0 +1,30 @@
1
+ import 'server-only';
2
+ import { createRouteHandler } from '@openpanel/nextjs/server';
3
+ import { NextResponse } from 'next/server';
4
+ import { createOpenPanelConfig, getOpenPanelConfigFromEnv, } from './config';
5
+ function notConfigured() {
6
+ return NextResponse.json({
7
+ error: 'OpenPanel is not configured',
8
+ }, {
9
+ status: 503,
10
+ });
11
+ }
12
+ export function createOpenPanelProxyRouteHandlers(config) {
13
+ if (!config) {
14
+ return {
15
+ GET: notConfigured,
16
+ POST: notConfigured,
17
+ };
18
+ }
19
+ const routeHandler = createRouteHandler({
20
+ apiUrl: config.upstreamApiUrl,
21
+ });
22
+ return {
23
+ GET: routeHandler.GET,
24
+ POST: routeHandler.POST,
25
+ };
26
+ }
27
+ export function createOpenPanelProxyRouteHandlersFromEnv(env = process.env, options) {
28
+ return createOpenPanelProxyRouteHandlers(getOpenPanelConfigFromEnv(env, options));
29
+ }
30
+ export { createOpenPanelConfig, getOpenPanelConfigFromEnv };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "coonlink-openpanel-dev",
3
+ "version": "26.1.1",
4
+ "description": "Reusable OpenPanel proxy integration for Next.js projects",
5
+ "type": "module",
6
+ "homepage": "https://dev.coonlink.com",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.build.json",
15
+ "typecheck": "tsc -p tsconfig.build.json --noEmit"
16
+ },
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./client": {
23
+ "types": "./dist/client.d.ts",
24
+ "default": "./dist/client.js"
25
+ },
26
+ "./server": {
27
+ "types": "./dist/server.d.ts",
28
+ "default": "./dist/server.js"
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
32
+ "peerDependencies": {
33
+ "@openpanel/nextjs": "^1.4.0",
34
+ "next": ">=16.0.0",
35
+ "react": ">=19.0.0",
36
+ "react-dom": ">=19.0.0"
37
+ }
38
+ }