@nextlytics/core 0.1.0-canary-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/dist/anonymous-user.cjs +118 -0
- package/dist/anonymous-user.d.mts +22 -0
- package/dist/anonymous-user.d.ts +22 -0
- package/dist/anonymous-user.js +94 -0
- package/dist/backends/clickhouse.cjs +110 -0
- package/dist/backends/clickhouse.d.mts +58 -0
- package/dist/backends/clickhouse.d.ts +58 -0
- package/dist/backends/clickhouse.js +92 -0
- package/dist/backends/ga.cjs +207 -0
- package/dist/backends/ga.d.mts +21 -0
- package/dist/backends/ga.d.ts +21 -0
- package/dist/backends/ga.js +183 -0
- package/dist/backends/gtm.cjs +155 -0
- package/dist/backends/gtm.d.mts +11 -0
- package/dist/backends/gtm.d.ts +11 -0
- package/dist/backends/gtm.js +131 -0
- package/dist/backends/lib/db.cjs +150 -0
- package/dist/backends/lib/db.d.mts +121 -0
- package/dist/backends/lib/db.d.ts +121 -0
- package/dist/backends/lib/db.js +119 -0
- package/dist/backends/logging.cjs +45 -0
- package/dist/backends/logging.d.mts +7 -0
- package/dist/backends/logging.d.ts +7 -0
- package/dist/backends/logging.js +21 -0
- package/dist/backends/neon.cjs +84 -0
- package/dist/backends/neon.d.mts +11 -0
- package/dist/backends/neon.d.ts +11 -0
- package/dist/backends/neon.js +66 -0
- package/dist/backends/postgrest.cjs +98 -0
- package/dist/backends/postgrest.d.mts +46 -0
- package/dist/backends/postgrest.d.ts +46 -0
- package/dist/backends/postgrest.js +73 -0
- package/dist/backends/posthog.cjs +120 -0
- package/dist/backends/posthog.d.mts +13 -0
- package/dist/backends/posthog.d.ts +13 -0
- package/dist/backends/posthog.js +96 -0
- package/dist/backends/segment.cjs +112 -0
- package/dist/backends/segment.d.mts +43 -0
- package/dist/backends/segment.d.ts +43 -0
- package/dist/backends/segment.js +88 -0
- package/dist/client.cjs +171 -0
- package/dist/client.d.mts +29 -0
- package/dist/client.d.ts +29 -0
- package/dist/client.js +146 -0
- package/dist/config-helpers.cjs +71 -0
- package/dist/config-helpers.d.mts +16 -0
- package/dist/config-helpers.d.ts +16 -0
- package/dist/config-helpers.js +45 -0
- package/dist/handlers.cjs +123 -0
- package/dist/handlers.d.mts +9 -0
- package/dist/handlers.d.ts +9 -0
- package/dist/handlers.js +99 -0
- package/dist/headers.cjs +41 -0
- package/dist/headers.d.mts +3 -0
- package/dist/headers.d.ts +3 -0
- package/dist/headers.js +17 -0
- package/dist/index.cjs +41 -0
- package/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +12 -0
- package/dist/middleware.cjs +204 -0
- package/dist/middleware.d.mts +10 -0
- package/dist/middleware.d.ts +10 -0
- package/dist/middleware.js +183 -0
- package/dist/pages-router.cjs +45 -0
- package/dist/pages-router.d.mts +45 -0
- package/dist/pages-router.d.ts +45 -0
- package/dist/pages-router.js +21 -0
- package/dist/plugins/vercel-geo.cjs +60 -0
- package/dist/plugins/vercel-geo.d.mts +25 -0
- package/dist/plugins/vercel-geo.d.ts +25 -0
- package/dist/plugins/vercel-geo.js +36 -0
- package/dist/server-component-context.cjs +95 -0
- package/dist/server-component-context.d.mts +30 -0
- package/dist/server-component-context.d.ts +30 -0
- package/dist/server-component-context.js +69 -0
- package/dist/server.cjs +236 -0
- package/dist/server.d.mts +13 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +213 -0
- package/dist/template.cjs +108 -0
- package/dist/template.d.mts +27 -0
- package/dist/template.d.ts +27 -0
- package/dist/template.js +83 -0
- package/dist/types.cjs +16 -0
- package/dist/types.d.mts +216 -0
- package/dist/types.d.ts +216 -0
- package/dist/types.js +0 -0
- package/dist/uitils.cjs +94 -0
- package/dist/uitils.d.mts +22 -0
- package/dist/uitils.d.ts +22 -0
- package/dist/uitils.js +68 -0
- package/package.json +162 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { NextlyticsPlugin } from '../types.js';
|
|
2
|
+
import 'next/dist/server/web/spec-extension/cookies';
|
|
3
|
+
import 'next/server';
|
|
4
|
+
|
|
5
|
+
type VercelGeoPluginOptions = {
|
|
6
|
+
/** Property name to store geo data under. Default: "geo" */
|
|
7
|
+
geoPropertyName?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Plugin that extracts Vercel geo headers and adds them to event properties.
|
|
11
|
+
* Only works when deployed to Vercel.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { vercelGeoPlugin } from "@nextlytics/core/plugins/vercel-geo";
|
|
16
|
+
*
|
|
17
|
+
* export const { middleware, handlers } = Nextlytics({
|
|
18
|
+
* plugins: [vercelGeoPlugin()],
|
|
19
|
+
* // ...
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
declare function vercelGeoPlugin(options?: VercelGeoPluginOptions): NextlyticsPlugin;
|
|
24
|
+
|
|
25
|
+
export { type VercelGeoPluginOptions, vercelGeoPlugin };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const VERCEL_GEO_HEADERS = {
|
|
2
|
+
"x-vercel-ip-country": "country",
|
|
3
|
+
"x-vercel-ip-country-region": "region",
|
|
4
|
+
"x-vercel-ip-city": "city",
|
|
5
|
+
"x-vercel-ip-latitude": "latitude",
|
|
6
|
+
"x-vercel-ip-longitude": "longitude",
|
|
7
|
+
"x-vercel-ip-timezone": "timezone"
|
|
8
|
+
};
|
|
9
|
+
function vercelGeoPlugin(options) {
|
|
10
|
+
const geoPropertyName = options?.geoPropertyName ?? "geo";
|
|
11
|
+
return {
|
|
12
|
+
async onDispatch(event) {
|
|
13
|
+
const headers = event.serverContext.requestHeaders;
|
|
14
|
+
const geo = {};
|
|
15
|
+
for (const [header, prop] of Object.entries(VERCEL_GEO_HEADERS)) {
|
|
16
|
+
const value = headers[header];
|
|
17
|
+
if (value) {
|
|
18
|
+
if (prop === "latitude" || prop === "longitude") {
|
|
19
|
+
const num = parseFloat(value);
|
|
20
|
+
if (!isNaN(num)) {
|
|
21
|
+
geo[prop] = num;
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
geo[prop] = value;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (Object.keys(geo).length > 0) {
|
|
29
|
+
event.properties[geoPropertyName] = geo;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
vercelGeoPlugin
|
|
36
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var server_component_context_exports = {};
|
|
20
|
+
__export(server_component_context_exports, {
|
|
21
|
+
headers: () => headerKeys,
|
|
22
|
+
restoreServerComponentContext: () => restoreServerComponentContext,
|
|
23
|
+
serializeServerComponentContext: () => serializeServerComponentContext
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(server_component_context_exports);
|
|
26
|
+
const HEADER_PREFIX = "x-nc-";
|
|
27
|
+
const headerKeys = {
|
|
28
|
+
pathname: `${HEADER_PREFIX}pathname`,
|
|
29
|
+
search: `${HEADER_PREFIX}search`,
|
|
30
|
+
pageRenderId: `${HEADER_PREFIX}page-render-id`,
|
|
31
|
+
scripts: `${HEADER_PREFIX}scripts`,
|
|
32
|
+
templates: `${HEADER_PREFIX}templates`
|
|
33
|
+
};
|
|
34
|
+
function serializeServerComponentContext(response, ctx) {
|
|
35
|
+
response.headers.set(headerKeys.pageRenderId, ctx.pageRenderId);
|
|
36
|
+
response.headers.set(headerKeys.pathname, ctx.pathname);
|
|
37
|
+
response.headers.set(headerKeys.search, ctx.search);
|
|
38
|
+
if (ctx.scripts.length > 0) {
|
|
39
|
+
const scriptParts = ctx.scripts.filter((item) => item.type === "script-template").map((s) => `${s.templateId}=${JSON.stringify(s.params)}`);
|
|
40
|
+
if (scriptParts.length > 0) {
|
|
41
|
+
response.headers.set(headerKeys.scripts, scriptParts.join(";"));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (Object.keys(ctx.templates).length > 0) {
|
|
45
|
+
response.headers.set(headerKeys.templates, JSON.stringify(ctx.templates));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function parseScriptsHeader(header) {
|
|
49
|
+
const scripts = [];
|
|
50
|
+
for (const part of header.split(";")) {
|
|
51
|
+
const eqIdx = part.indexOf("=");
|
|
52
|
+
if (eqIdx === -1) continue;
|
|
53
|
+
const templateId = part.slice(0, eqIdx);
|
|
54
|
+
const paramsJson = part.slice(eqIdx + 1);
|
|
55
|
+
try {
|
|
56
|
+
const params = JSON.parse(paramsJson);
|
|
57
|
+
scripts.push({ type: "script-template", templateId, params });
|
|
58
|
+
} catch {
|
|
59
|
+
console.warn(`[Nextlytics] Failed to parse script params for ${templateId}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return scripts;
|
|
63
|
+
}
|
|
64
|
+
function restoreServerComponentContext(headersList) {
|
|
65
|
+
const pageRenderId = headersList.get(headerKeys.pageRenderId);
|
|
66
|
+
if (!pageRenderId) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const pathname = headersList.get(headerKeys.pathname) || "";
|
|
70
|
+
const search = headersList.get(headerKeys.search) || "";
|
|
71
|
+
const scriptsHeader = headersList.get(headerKeys.scripts);
|
|
72
|
+
const scripts = scriptsHeader ? parseScriptsHeader(scriptsHeader) : [];
|
|
73
|
+
let templates = {};
|
|
74
|
+
const templatesHeader = headersList.get(headerKeys.templates);
|
|
75
|
+
if (templatesHeader) {
|
|
76
|
+
try {
|
|
77
|
+
templates = JSON.parse(templatesHeader);
|
|
78
|
+
} catch {
|
|
79
|
+
console.warn("[Nextlytics] Failed to parse templates header");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
pageRenderId,
|
|
84
|
+
pathname,
|
|
85
|
+
search,
|
|
86
|
+
scripts,
|
|
87
|
+
templates
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
91
|
+
0 && (module.exports = {
|
|
92
|
+
headers,
|
|
93
|
+
restoreServerComponentContext,
|
|
94
|
+
serializeServerComponentContext
|
|
95
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { TemplatizedScriptInsertion, JavascriptTemplate } from './types.mjs';
|
|
3
|
+
import 'next/dist/server/web/spec-extension/cookies';
|
|
4
|
+
|
|
5
|
+
declare const headerKeys: {
|
|
6
|
+
readonly pathname: "x-nc-pathname";
|
|
7
|
+
readonly search: "x-nc-search";
|
|
8
|
+
readonly pageRenderId: "x-nc-page-render-id";
|
|
9
|
+
readonly scripts: "x-nc-scripts";
|
|
10
|
+
readonly templates: "x-nc-templates";
|
|
11
|
+
};
|
|
12
|
+
/** Context passed from middleware to server components via headers */
|
|
13
|
+
type ServerComponentContext = {
|
|
14
|
+
/** Unique page render ID (event ID) */
|
|
15
|
+
pageRenderId: string;
|
|
16
|
+
/** Request pathname */
|
|
17
|
+
pathname: string;
|
|
18
|
+
/** Query string */
|
|
19
|
+
search: string;
|
|
20
|
+
/** Script actions to execute on client */
|
|
21
|
+
scripts: TemplatizedScriptInsertion<unknown>[];
|
|
22
|
+
/** Template definitions for scripts */
|
|
23
|
+
templates: Record<string, JavascriptTemplate>;
|
|
24
|
+
};
|
|
25
|
+
/** Serialize context to response headers (called in middleware) */
|
|
26
|
+
declare function serializeServerComponentContext(response: NextResponse, ctx: ServerComponentContext): void;
|
|
27
|
+
/** Restore context from request headers (called in server components) */
|
|
28
|
+
declare function restoreServerComponentContext(headersList: Headers): ServerComponentContext | null;
|
|
29
|
+
|
|
30
|
+
export { type ServerComponentContext, headerKeys as headers, restoreServerComponentContext, serializeServerComponentContext };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { TemplatizedScriptInsertion, JavascriptTemplate } from './types.js';
|
|
3
|
+
import 'next/dist/server/web/spec-extension/cookies';
|
|
4
|
+
|
|
5
|
+
declare const headerKeys: {
|
|
6
|
+
readonly pathname: "x-nc-pathname";
|
|
7
|
+
readonly search: "x-nc-search";
|
|
8
|
+
readonly pageRenderId: "x-nc-page-render-id";
|
|
9
|
+
readonly scripts: "x-nc-scripts";
|
|
10
|
+
readonly templates: "x-nc-templates";
|
|
11
|
+
};
|
|
12
|
+
/** Context passed from middleware to server components via headers */
|
|
13
|
+
type ServerComponentContext = {
|
|
14
|
+
/** Unique page render ID (event ID) */
|
|
15
|
+
pageRenderId: string;
|
|
16
|
+
/** Request pathname */
|
|
17
|
+
pathname: string;
|
|
18
|
+
/** Query string */
|
|
19
|
+
search: string;
|
|
20
|
+
/** Script actions to execute on client */
|
|
21
|
+
scripts: TemplatizedScriptInsertion<unknown>[];
|
|
22
|
+
/** Template definitions for scripts */
|
|
23
|
+
templates: Record<string, JavascriptTemplate>;
|
|
24
|
+
};
|
|
25
|
+
/** Serialize context to response headers (called in middleware) */
|
|
26
|
+
declare function serializeServerComponentContext(response: NextResponse, ctx: ServerComponentContext): void;
|
|
27
|
+
/** Restore context from request headers (called in server components) */
|
|
28
|
+
declare function restoreServerComponentContext(headersList: Headers): ServerComponentContext | null;
|
|
29
|
+
|
|
30
|
+
export { type ServerComponentContext, headerKeys as headers, restoreServerComponentContext, serializeServerComponentContext };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const HEADER_PREFIX = "x-nc-";
|
|
2
|
+
const headerKeys = {
|
|
3
|
+
pathname: `${HEADER_PREFIX}pathname`,
|
|
4
|
+
search: `${HEADER_PREFIX}search`,
|
|
5
|
+
pageRenderId: `${HEADER_PREFIX}page-render-id`,
|
|
6
|
+
scripts: `${HEADER_PREFIX}scripts`,
|
|
7
|
+
templates: `${HEADER_PREFIX}templates`
|
|
8
|
+
};
|
|
9
|
+
function serializeServerComponentContext(response, ctx) {
|
|
10
|
+
response.headers.set(headerKeys.pageRenderId, ctx.pageRenderId);
|
|
11
|
+
response.headers.set(headerKeys.pathname, ctx.pathname);
|
|
12
|
+
response.headers.set(headerKeys.search, ctx.search);
|
|
13
|
+
if (ctx.scripts.length > 0) {
|
|
14
|
+
const scriptParts = ctx.scripts.filter((item) => item.type === "script-template").map((s) => `${s.templateId}=${JSON.stringify(s.params)}`);
|
|
15
|
+
if (scriptParts.length > 0) {
|
|
16
|
+
response.headers.set(headerKeys.scripts, scriptParts.join(";"));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (Object.keys(ctx.templates).length > 0) {
|
|
20
|
+
response.headers.set(headerKeys.templates, JSON.stringify(ctx.templates));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function parseScriptsHeader(header) {
|
|
24
|
+
const scripts = [];
|
|
25
|
+
for (const part of header.split(";")) {
|
|
26
|
+
const eqIdx = part.indexOf("=");
|
|
27
|
+
if (eqIdx === -1) continue;
|
|
28
|
+
const templateId = part.slice(0, eqIdx);
|
|
29
|
+
const paramsJson = part.slice(eqIdx + 1);
|
|
30
|
+
try {
|
|
31
|
+
const params = JSON.parse(paramsJson);
|
|
32
|
+
scripts.push({ type: "script-template", templateId, params });
|
|
33
|
+
} catch {
|
|
34
|
+
console.warn(`[Nextlytics] Failed to parse script params for ${templateId}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return scripts;
|
|
38
|
+
}
|
|
39
|
+
function restoreServerComponentContext(headersList) {
|
|
40
|
+
const pageRenderId = headersList.get(headerKeys.pageRenderId);
|
|
41
|
+
if (!pageRenderId) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const pathname = headersList.get(headerKeys.pathname) || "";
|
|
45
|
+
const search = headersList.get(headerKeys.search) || "";
|
|
46
|
+
const scriptsHeader = headersList.get(headerKeys.scripts);
|
|
47
|
+
const scripts = scriptsHeader ? parseScriptsHeader(scriptsHeader) : [];
|
|
48
|
+
let templates = {};
|
|
49
|
+
const templatesHeader = headersList.get(headerKeys.templates);
|
|
50
|
+
if (templatesHeader) {
|
|
51
|
+
try {
|
|
52
|
+
templates = JSON.parse(templatesHeader);
|
|
53
|
+
} catch {
|
|
54
|
+
console.warn("[Nextlytics] Failed to parse templates header");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
pageRenderId,
|
|
59
|
+
pathname,
|
|
60
|
+
search,
|
|
61
|
+
scripts,
|
|
62
|
+
templates
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
headerKeys as headers,
|
|
67
|
+
restoreServerComponentContext,
|
|
68
|
+
serializeServerComponentContext
|
|
69
|
+
};
|
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var server_exports = {};
|
|
20
|
+
__export(server_exports, {
|
|
21
|
+
Nextlytics: () => Nextlytics,
|
|
22
|
+
NextlyticsServer: () => NextlyticsServer,
|
|
23
|
+
createRequestContext: () => createRequestContext
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(server_exports);
|
|
26
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
27
|
+
var import_headers = require("next/headers");
|
|
28
|
+
var import_headers2 = require("./headers");
|
|
29
|
+
var import_server_component_context = require("./server-component-context");
|
|
30
|
+
var import_anonymous_user = require("./anonymous-user");
|
|
31
|
+
var import_client = require("./client");
|
|
32
|
+
var import_handlers = require("./handlers");
|
|
33
|
+
var import_config_helpers = require("./config-helpers");
|
|
34
|
+
var import_middleware = require("./middleware");
|
|
35
|
+
var import_uitils = require("./uitils");
|
|
36
|
+
function resolveBackends(config, ctx) {
|
|
37
|
+
const backends = config.backends || [];
|
|
38
|
+
return backends.map((backend) => {
|
|
39
|
+
if (typeof backend === "function") {
|
|
40
|
+
return backend(ctx);
|
|
41
|
+
}
|
|
42
|
+
return backend;
|
|
43
|
+
}).filter((b) => b !== null);
|
|
44
|
+
}
|
|
45
|
+
function resolvePlugins(config, ctx) {
|
|
46
|
+
const plugins = config.plugins || [];
|
|
47
|
+
return plugins.map((plugin) => {
|
|
48
|
+
if (typeof plugin === "function") {
|
|
49
|
+
if (!ctx) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return plugin(ctx);
|
|
53
|
+
}
|
|
54
|
+
return plugin;
|
|
55
|
+
}).filter((p) => p !== null);
|
|
56
|
+
}
|
|
57
|
+
function mergeClientActions(actions) {
|
|
58
|
+
const items = [];
|
|
59
|
+
for (const action of actions) {
|
|
60
|
+
if (action?.items) {
|
|
61
|
+
items.push(...action.items);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { items };
|
|
65
|
+
}
|
|
66
|
+
async function createRequestContext() {
|
|
67
|
+
const [_cookies, _headers] = await Promise.all([(0, import_headers.cookies)(), (0, import_headers.headers)()]);
|
|
68
|
+
return {
|
|
69
|
+
cookies: _cookies,
|
|
70
|
+
headers: _headers
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async function NextlyticsServer({ children }) {
|
|
74
|
+
const headersList = await (0, import_headers.headers)();
|
|
75
|
+
const ctx = (0, import_server_component_context.restoreServerComponentContext)(headersList);
|
|
76
|
+
if (!ctx) {
|
|
77
|
+
console.warn(
|
|
78
|
+
"[Nextlytics] nextlyticsMiddleware should be added in order for NextlyticsServer to work"
|
|
79
|
+
);
|
|
80
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
|
|
81
|
+
}
|
|
82
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_client.NextlyticsClient, { requestId: ctx.pageRenderId, scripts: ctx.scripts, templates: ctx.templates, children });
|
|
83
|
+
}
|
|
84
|
+
function Nextlytics(userConfig) {
|
|
85
|
+
const config = (0, import_config_helpers.withDefaults)(userConfig);
|
|
86
|
+
const validationResult = (0, import_config_helpers.validateConfig)(config);
|
|
87
|
+
(0, import_config_helpers.logConfigWarnings)(validationResult);
|
|
88
|
+
const dispatchEventInternal = (event, ctx) => {
|
|
89
|
+
const plugins = resolvePlugins(config, ctx);
|
|
90
|
+
const backends = resolveBackends(config, ctx);
|
|
91
|
+
const pluginsDone = (async () => {
|
|
92
|
+
for (const plugin of plugins) {
|
|
93
|
+
try {
|
|
94
|
+
await plugin.onDispatch(event);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.warn("[Nextlytics] Plugin failed on onDispatch:", err);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
})();
|
|
100
|
+
const backendResults = pluginsDone.then(() => {
|
|
101
|
+
return backends.map((backend) => {
|
|
102
|
+
const start = Date.now();
|
|
103
|
+
const promise = backend.onEvent(event).then((result) => ({ ok: true, ms: Date.now() - start, result })).catch((err) => {
|
|
104
|
+
console.warn(`[Nextlytics] Backend "${backend.name}" failed on onEvent:`, err);
|
|
105
|
+
return { ok: false, ms: Date.now() - start, result: void 0 };
|
|
106
|
+
});
|
|
107
|
+
return { backend, promise };
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
const clientActions = backendResults.then(async (results) => {
|
|
111
|
+
const actionResults = results.filter((r) => r.backend.returnsClientActions);
|
|
112
|
+
const actions = await Promise.all(actionResults.map((r) => r.promise));
|
|
113
|
+
return mergeClientActions(actions.map((a) => a.result));
|
|
114
|
+
});
|
|
115
|
+
const completion = backendResults.then(async (results) => {
|
|
116
|
+
const settled = await Promise.all(results.map((r) => r.promise));
|
|
117
|
+
if (config.debug) {
|
|
118
|
+
const nameWidth = Math.max(...results.map((r) => r.backend.name.length));
|
|
119
|
+
console.log(
|
|
120
|
+
`[Nextlytics] dispatchEvent ${event.type} ${event.eventId} (${results.length} backends)`
|
|
121
|
+
);
|
|
122
|
+
results.forEach((r, i) => {
|
|
123
|
+
const s = settled[i];
|
|
124
|
+
const status = s.ok ? "ok" : "fail";
|
|
125
|
+
console.log(` ${r.backend.name.padEnd(nameWidth)} ${status.padEnd(4)} ${s.ms}ms`);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}).then(() => {
|
|
129
|
+
});
|
|
130
|
+
return { clientActions, completion };
|
|
131
|
+
};
|
|
132
|
+
const updateEventInternal = async (eventId, patch, ctx) => {
|
|
133
|
+
const backends = resolveBackends(config, ctx).filter((backend) => backend.supportsUpdates);
|
|
134
|
+
const results = await Promise.all(
|
|
135
|
+
backends.map(async (backend) => {
|
|
136
|
+
const start = Date.now();
|
|
137
|
+
try {
|
|
138
|
+
await backend.updateEvent(eventId, patch);
|
|
139
|
+
return { backend, ok: true, ms: Date.now() - start };
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.warn(`[Nextlytics] Backend "${backend.name}" failed on updateEvent:`, err);
|
|
142
|
+
return { backend, ok: false, ms: Date.now() - start };
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
);
|
|
146
|
+
if (config.debug && backends.length > 0) {
|
|
147
|
+
const nameWidth = Math.max(...backends.map((b) => b.name.length));
|
|
148
|
+
console.log(`[Nextlytics] updateEvent ${eventId} (${backends.length} backends)`);
|
|
149
|
+
results.forEach((r) => {
|
|
150
|
+
const status = r.ok ? "ok" : "fail";
|
|
151
|
+
console.log(` ${r.backend.name.padEnd(nameWidth)} ${status.padEnd(4)} ${r.ms}ms`);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const dispatchEvent = async (event) => {
|
|
156
|
+
const ctx = await createRequestContext();
|
|
157
|
+
return dispatchEventInternal(event, ctx);
|
|
158
|
+
};
|
|
159
|
+
const updateEvent = async (eventId, patch) => {
|
|
160
|
+
const ctx = await createRequestContext();
|
|
161
|
+
return updateEventInternal(eventId, patch, ctx);
|
|
162
|
+
};
|
|
163
|
+
const middleware = (0, import_middleware.createNextlyticsMiddleware)(config, dispatchEventInternal, updateEventInternal);
|
|
164
|
+
const handlers = (0, import_handlers.createHandlers)(config, dispatchEventInternal, updateEventInternal);
|
|
165
|
+
const analytics = async () => {
|
|
166
|
+
const headersList = await (0, import_headers.headers)();
|
|
167
|
+
const cookieStore = await (0, import_headers.cookies)();
|
|
168
|
+
const pageRenderId = headersList.get(import_server_component_context.headers.pageRenderId);
|
|
169
|
+
const serverContext = createServerContextFromHeaders(headersList);
|
|
170
|
+
const ctx = { headers: headersList, cookies: cookieStore };
|
|
171
|
+
const { anonId: anonymousUserId } = await (0, import_anonymous_user.resolveAnonymousUser)({ ctx, serverContext, config });
|
|
172
|
+
let userContext;
|
|
173
|
+
if (config.callbacks.getUser) {
|
|
174
|
+
try {
|
|
175
|
+
userContext = await config.callbacks.getUser(ctx) || void 0;
|
|
176
|
+
} catch {
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
sendEvent: async (eventName, opts) => {
|
|
181
|
+
if (!pageRenderId) {
|
|
182
|
+
console.error("[Nextlytics] analytics() requires nextlyticsMiddleware");
|
|
183
|
+
return { ok: false };
|
|
184
|
+
}
|
|
185
|
+
const event = {
|
|
186
|
+
eventId: (0, import_uitils.generateId)(),
|
|
187
|
+
parentEventId: pageRenderId,
|
|
188
|
+
type: eventName,
|
|
189
|
+
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
190
|
+
anonymousUserId,
|
|
191
|
+
serverContext,
|
|
192
|
+
userContext,
|
|
193
|
+
properties: opts?.props || {}
|
|
194
|
+
};
|
|
195
|
+
await dispatchEventInternal(event, ctx);
|
|
196
|
+
return { ok: true };
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
};
|
|
200
|
+
return { middleware, handlers, analytics, dispatchEvent, updateEvent };
|
|
201
|
+
}
|
|
202
|
+
function createServerContextFromHeaders(headersList) {
|
|
203
|
+
const rawHeaders = {};
|
|
204
|
+
headersList.forEach((value, key) => {
|
|
205
|
+
rawHeaders[key] = value;
|
|
206
|
+
});
|
|
207
|
+
const requestHeaders = (0, import_headers2.removeSensitiveHeaders)(rawHeaders);
|
|
208
|
+
const pathname = headersList.get(import_server_component_context.headers.pathname) || "";
|
|
209
|
+
const search = headersList.get(import_server_component_context.headers.search) || "";
|
|
210
|
+
const searchParams = {};
|
|
211
|
+
if (search) {
|
|
212
|
+
const params = new URLSearchParams(search);
|
|
213
|
+
params.forEach((value, key) => {
|
|
214
|
+
if (!searchParams[key]) {
|
|
215
|
+
searchParams[key] = [];
|
|
216
|
+
}
|
|
217
|
+
searchParams[key].push(value);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
collectedAt: /* @__PURE__ */ new Date(),
|
|
222
|
+
host: headersList.get("host") || "",
|
|
223
|
+
method: "GET",
|
|
224
|
+
path: pathname,
|
|
225
|
+
search: searchParams,
|
|
226
|
+
ip: headersList.get("x-forwarded-for")?.split(",")[0]?.trim() || "",
|
|
227
|
+
requestHeaders,
|
|
228
|
+
responseHeaders: {}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
232
|
+
0 && (module.exports = {
|
|
233
|
+
Nextlytics,
|
|
234
|
+
NextlyticsServer,
|
|
235
|
+
createRequestContext
|
|
236
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { NextlyticsConfig, NextlyticsResult, RequestContext } from './types.mjs';
|
|
4
|
+
import 'next/dist/server/web/spec-extension/cookies';
|
|
5
|
+
import 'next/server';
|
|
6
|
+
|
|
7
|
+
declare function createRequestContext(): Promise<RequestContext>;
|
|
8
|
+
declare function NextlyticsServer({ children }: {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}): Promise<react_jsx_runtime.JSX.Element>;
|
|
11
|
+
declare function Nextlytics(userConfig: NextlyticsConfig): NextlyticsResult;
|
|
12
|
+
|
|
13
|
+
export { Nextlytics, NextlyticsServer, createRequestContext };
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { NextlyticsConfig, NextlyticsResult, RequestContext } from './types.js';
|
|
4
|
+
import 'next/dist/server/web/spec-extension/cookies';
|
|
5
|
+
import 'next/server';
|
|
6
|
+
|
|
7
|
+
declare function createRequestContext(): Promise<RequestContext>;
|
|
8
|
+
declare function NextlyticsServer({ children }: {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}): Promise<react_jsx_runtime.JSX.Element>;
|
|
11
|
+
declare function Nextlytics(userConfig: NextlyticsConfig): NextlyticsResult;
|
|
12
|
+
|
|
13
|
+
export { Nextlytics, NextlyticsServer, createRequestContext };
|