@nextlytics/core 0.2.2 → 0.3.0-canary.80
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/api-handler.d.ts +11 -0
- package/dist/api-handler.js +182 -0
- package/dist/backends/ga.d.ts +5 -0
- package/dist/backends/ga.js +93 -17
- package/dist/backends/gtm.js +25 -8
- package/dist/backends/logging.js +33 -0
- package/dist/backends/segment.js +50 -17
- package/dist/client-utils.d.ts +35 -0
- package/dist/client-utils.js +121 -0
- package/dist/client.d.ts +1 -4
- package/dist/client.js +146 -67
- package/dist/config-helpers.d.ts +2 -2
- package/dist/config-helpers.js +3 -18
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -3
- package/dist/middleware.d.ts +2 -3
- package/dist/middleware.js +66 -127
- package/dist/pages-router.d.ts +6 -31
- package/dist/pages-router.js +1 -2
- package/dist/server-component-context.d.ts +10 -11
- package/dist/server-component-context.js +18 -28
- package/dist/server.d.ts +1 -6
- package/dist/server.js +59 -33
- package/dist/stable-hash.d.ts +6 -0
- package/dist/stable-hash.js +76 -0
- package/dist/types.d.ts +78 -17
- package/dist/uitils.d.ts +7 -1
- package/dist/uitils.js +39 -5
- package/package.json +1 -1
- package/dist/handlers.d.ts +0 -9
- package/dist/handlers.js +0 -123
package/dist/middleware.js
CHANGED
|
@@ -25,19 +25,7 @@ var import_server = require("next/server");
|
|
|
25
25
|
var import_server_component_context = require("./server-component-context");
|
|
26
26
|
var import_uitils = require("./uitils");
|
|
27
27
|
var import_anonymous_user = require("./anonymous-user");
|
|
28
|
-
|
|
29
|
-
const backends = config.backends || [];
|
|
30
|
-
return backends.map((backend) => typeof backend === "function" ? backend(ctx) : backend).filter((b) => b !== null);
|
|
31
|
-
}
|
|
32
|
-
function collectTemplates(backends) {
|
|
33
|
-
const templates = {};
|
|
34
|
-
for (const backend of backends) {
|
|
35
|
-
if (backend.getClientSideTemplates) {
|
|
36
|
-
Object.assign(templates, backend.getClientSideTemplates());
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return templates;
|
|
40
|
-
}
|
|
28
|
+
var import_api_handler = require("./api-handler");
|
|
41
29
|
function createRequestContext(request) {
|
|
42
30
|
return {
|
|
43
31
|
headers: request.headers,
|
|
@@ -49,66 +37,89 @@ function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
|
|
|
49
37
|
return async (request) => {
|
|
50
38
|
const pathname = request.nextUrl.pathname;
|
|
51
39
|
const reqInfo = (0, import_uitils.getRequestInfo)(request);
|
|
40
|
+
const middlewareDebug = config.debug || process.env.NEXTLYTICS_MIDDLEWARE_DEBUG === "true";
|
|
41
|
+
if (middlewareDebug) {
|
|
42
|
+
const headers = request.headers;
|
|
43
|
+
const debugHeaders = {};
|
|
44
|
+
headers.forEach((value, key) => {
|
|
45
|
+
debugHeaders[key] = value;
|
|
46
|
+
});
|
|
47
|
+
console.log("[Nextlytics][middleware]", {
|
|
48
|
+
url: request.url,
|
|
49
|
+
pathname,
|
|
50
|
+
search: request.nextUrl.search,
|
|
51
|
+
method: request.method,
|
|
52
|
+
nextVersion: (0, import_uitils.getNextVersion)(),
|
|
53
|
+
destination: request.destination,
|
|
54
|
+
referrer: request.referrer,
|
|
55
|
+
mode: request.mode,
|
|
56
|
+
cache: request.cache,
|
|
57
|
+
redirect: request.redirect,
|
|
58
|
+
integrity: request.integrity,
|
|
59
|
+
isPrefetch: reqInfo.isPrefetch,
|
|
60
|
+
isRsc: reqInfo.isRsc,
|
|
61
|
+
isPageNavigation: reqInfo.isPageNavigation,
|
|
62
|
+
isStaticFile: reqInfo.isStaticFile,
|
|
63
|
+
isNextjsInternal: reqInfo.isNextjsInternal,
|
|
64
|
+
headers: debugHeaders
|
|
65
|
+
});
|
|
66
|
+
}
|
|
52
67
|
if (pathname === eventEndpoint) {
|
|
53
68
|
if (request.method === "POST") {
|
|
54
|
-
return handleEventPost(request, config, dispatchEvent, updateEvent);
|
|
69
|
+
return (0, import_api_handler.handleEventPost)(request, config, dispatchEvent, updateEvent);
|
|
55
70
|
}
|
|
56
71
|
return Response.json({ error: "Method not allowed" }, { status: 405 });
|
|
57
72
|
}
|
|
58
73
|
if (reqInfo.isNextjsInternal || reqInfo.isPrefetch || reqInfo.isStaticFile) {
|
|
59
74
|
return import_server.NextResponse.next();
|
|
60
75
|
}
|
|
76
|
+
if (!reqInfo.isPageNavigation && !config.isApiPath(pathname)) {
|
|
77
|
+
return import_server.NextResponse.next();
|
|
78
|
+
}
|
|
61
79
|
const pageRenderId = (0, import_uitils.generateId)();
|
|
62
80
|
const serverContext = (0, import_uitils.createServerContext)(request);
|
|
63
81
|
const response = import_server.NextResponse.next();
|
|
64
82
|
const ctx = createRequestContext(request);
|
|
83
|
+
response.cookies.set(import_server_component_context.LAST_PAGE_RENDER_ID_COOKIE, pageRenderId, { path: "/" });
|
|
65
84
|
const { anonId } = await (0, import_anonymous_user.resolveAnonymousUser)({ ctx, serverContext, config, response });
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return response;
|
|
79
|
-
}
|
|
80
|
-
const isApiPath = config.isApiPath(pathname);
|
|
81
|
-
if (isApiPath && config.excludeApiCalls) {
|
|
82
|
-
(0, import_server_component_context.serializeServerComponentContext)(response, {
|
|
83
|
-
pageRenderId,
|
|
84
|
-
pathname: request.nextUrl.pathname,
|
|
85
|
-
search: request.nextUrl.search,
|
|
86
|
-
scripts,
|
|
87
|
-
templates
|
|
88
|
-
});
|
|
89
|
-
return response;
|
|
90
|
-
}
|
|
91
|
-
const userContext = await getUserContext(config, ctx);
|
|
92
|
-
const pageViewEvent = createPageViewEvent(
|
|
85
|
+
if (config.excludePaths?.(pathname)) {
|
|
86
|
+
(0, import_server_component_context.serializeServerComponentContext)(response, {
|
|
87
|
+
pageRenderId,
|
|
88
|
+
pathname: request.nextUrl.pathname,
|
|
89
|
+
search: request.nextUrl.search,
|
|
90
|
+
scripts: []
|
|
91
|
+
});
|
|
92
|
+
return response;
|
|
93
|
+
}
|
|
94
|
+
const isApiPath = config.isApiPath(pathname);
|
|
95
|
+
if (isApiPath && config.excludeApiCalls) {
|
|
96
|
+
(0, import_server_component_context.serializeServerComponentContext)(response, {
|
|
93
97
|
pageRenderId,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const { clientActions, completion } = dispatchEvent(pageViewEvent, ctx);
|
|
100
|
-
const actions = await clientActions;
|
|
101
|
-
scripts = actions.items.filter(
|
|
102
|
-
(i) => i.type === "script-template"
|
|
103
|
-
);
|
|
104
|
-
(0, import_server.after)(() => completion);
|
|
98
|
+
pathname: request.nextUrl.pathname,
|
|
99
|
+
search: request.nextUrl.search,
|
|
100
|
+
scripts: []
|
|
101
|
+
});
|
|
102
|
+
return response;
|
|
105
103
|
}
|
|
104
|
+
const userContext = await (0, import_api_handler.getUserContext)(config, ctx);
|
|
105
|
+
const pageViewEvent = createPageViewEvent(
|
|
106
|
+
pageRenderId,
|
|
107
|
+
serverContext,
|
|
108
|
+
isApiPath,
|
|
109
|
+
userContext,
|
|
110
|
+
anonId
|
|
111
|
+
);
|
|
112
|
+
const { clientActions, completion } = dispatchEvent(pageViewEvent, ctx, "on-request");
|
|
113
|
+
const actions = await clientActions;
|
|
114
|
+
const scripts = actions.items.filter(
|
|
115
|
+
(i) => i.type === "script-template"
|
|
116
|
+
);
|
|
117
|
+
(0, import_server.after)(() => completion);
|
|
106
118
|
(0, import_server_component_context.serializeServerComponentContext)(response, {
|
|
107
119
|
pageRenderId,
|
|
108
120
|
pathname: request.nextUrl.pathname,
|
|
109
121
|
search: request.nextUrl.search,
|
|
110
|
-
scripts
|
|
111
|
-
templates
|
|
122
|
+
scripts
|
|
112
123
|
});
|
|
113
124
|
return response;
|
|
114
125
|
};
|
|
@@ -116,6 +127,7 @@ function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
|
|
|
116
127
|
function createPageViewEvent(pageRenderId, serverContext, isApiPath, userContext, anonymousUserId) {
|
|
117
128
|
const eventType = isApiPath ? "apiCall" : "pageView";
|
|
118
129
|
return {
|
|
130
|
+
origin: "server",
|
|
119
131
|
collectedAt: serverContext.collectedAt.toISOString(),
|
|
120
132
|
eventId: pageRenderId,
|
|
121
133
|
type: eventType,
|
|
@@ -125,79 +137,6 @@ function createPageViewEvent(pageRenderId, serverContext, isApiPath, userContext
|
|
|
125
137
|
properties: {}
|
|
126
138
|
};
|
|
127
139
|
}
|
|
128
|
-
async function getUserContext(config, ctx) {
|
|
129
|
-
if (!config.callbacks.getUser) return void 0;
|
|
130
|
-
try {
|
|
131
|
-
return await config.callbacks.getUser(ctx) || void 0;
|
|
132
|
-
} catch {
|
|
133
|
-
return void 0;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
async function handleEventPost(request, config, dispatchEvent, updateEvent) {
|
|
137
|
-
const pageRenderId = request.headers.get(import_server_component_context.headers.pageRenderId);
|
|
138
|
-
if (!pageRenderId) {
|
|
139
|
-
return Response.json({ error: "Missing page render ID" }, { status: 400 });
|
|
140
|
-
}
|
|
141
|
-
let body;
|
|
142
|
-
try {
|
|
143
|
-
body = await request.json();
|
|
144
|
-
} catch {
|
|
145
|
-
return Response.json({ error: "Invalid JSON" }, { status: 400 });
|
|
146
|
-
}
|
|
147
|
-
const { type, payload } = body;
|
|
148
|
-
const ctx = createRequestContext(request);
|
|
149
|
-
const serverContext = (0, import_uitils.createServerContext)(request);
|
|
150
|
-
const userContext = await getUserContext(config, ctx);
|
|
151
|
-
const { anonId: anonymousUserId } = await (0, import_anonymous_user.resolveAnonymousUser)({ ctx, serverContext, config });
|
|
152
|
-
if (type === "client-init") {
|
|
153
|
-
const clientContext = payload;
|
|
154
|
-
if (clientContext?.path) {
|
|
155
|
-
serverContext.path = clientContext.path;
|
|
156
|
-
}
|
|
157
|
-
if (config.pageViewMode === "client-init") {
|
|
158
|
-
const event = {
|
|
159
|
-
eventId: pageRenderId,
|
|
160
|
-
type: "pageView",
|
|
161
|
-
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
162
|
-
anonymousUserId,
|
|
163
|
-
serverContext,
|
|
164
|
-
clientContext,
|
|
165
|
-
userContext,
|
|
166
|
-
properties: {}
|
|
167
|
-
};
|
|
168
|
-
const { clientActions, completion } = dispatchEvent(event, ctx);
|
|
169
|
-
const actions = await clientActions;
|
|
170
|
-
(0, import_server.after)(() => completion);
|
|
171
|
-
const scripts = actions.items.filter((i) => i.type === "script-template");
|
|
172
|
-
return Response.json({ ok: true, scripts: scripts.length > 0 ? scripts : void 0 });
|
|
173
|
-
} else {
|
|
174
|
-
(0, import_server.after)(() => updateEvent(pageRenderId, { clientContext, userContext, anonymousUserId }, ctx));
|
|
175
|
-
return Response.json({ ok: true });
|
|
176
|
-
}
|
|
177
|
-
} else if (type === "client-event") {
|
|
178
|
-
const clientContext = payload.clientContext || void 0;
|
|
179
|
-
if (clientContext?.path) {
|
|
180
|
-
serverContext.path = clientContext.path;
|
|
181
|
-
}
|
|
182
|
-
const event = {
|
|
183
|
-
eventId: (0, import_uitils.generateId)(),
|
|
184
|
-
parentEventId: pageRenderId,
|
|
185
|
-
type: payload.name || type,
|
|
186
|
-
collectedAt: payload.collectedAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
187
|
-
anonymousUserId,
|
|
188
|
-
serverContext,
|
|
189
|
-
clientContext,
|
|
190
|
-
userContext,
|
|
191
|
-
properties: payload.props || {}
|
|
192
|
-
};
|
|
193
|
-
const { clientActions, completion } = dispatchEvent(event, ctx);
|
|
194
|
-
const actions = await clientActions;
|
|
195
|
-
(0, import_server.after)(() => completion);
|
|
196
|
-
const scripts = actions.items.filter((i) => i.type === "script-template");
|
|
197
|
-
return Response.json({ ok: true, scripts: scripts.length > 0 ? scripts : void 0 });
|
|
198
|
-
}
|
|
199
|
-
return Response.json({ ok: true });
|
|
200
|
-
}
|
|
201
140
|
// Annotate the CommonJS export names for ESM import in node:
|
|
202
141
|
0 && (module.exports = {
|
|
203
142
|
createNextlyticsMiddleware
|
package/dist/pages-router.d.ts
CHANGED
|
@@ -5,41 +5,16 @@ import './types.js';
|
|
|
5
5
|
import 'next/dist/server/web/spec-extension/cookies';
|
|
6
6
|
import 'next/server';
|
|
7
7
|
|
|
8
|
-
type
|
|
8
|
+
type PagesRouterContext = {
|
|
9
9
|
req: {
|
|
10
10
|
headers: Record<string, string | string[] | undefined>;
|
|
11
|
+
cookies?: Record<string, string>;
|
|
11
12
|
};
|
|
12
13
|
};
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```tsx
|
|
19
|
-
* // pages/_app.tsx
|
|
20
|
-
* import type { AppContext, AppProps } from 'next/app'
|
|
21
|
-
* import { NextlyticsClient, getNextlyticsProps, type NextlyticsContext } from '@nextlytics/core'
|
|
22
|
-
*
|
|
23
|
-
* type MyAppProps = AppProps & { nextlyticsCtx: NextlyticsContext }
|
|
24
|
-
*
|
|
25
|
-
* function MyApp({ Component, pageProps, nextlyticsCtx }: MyAppProps) {
|
|
26
|
-
* return (
|
|
27
|
-
* <NextlyticsClient ctx={nextlyticsCtx}>
|
|
28
|
-
* <Component {...pageProps} />
|
|
29
|
-
* </NextlyticsClient>
|
|
30
|
-
* )
|
|
31
|
-
* }
|
|
32
|
-
*
|
|
33
|
-
* MyApp.getInitialProps = async (appContext: AppContext) => {
|
|
34
|
-
* return {
|
|
35
|
-
* pageProps: appContext.Component.getInitialProps
|
|
36
|
-
* ? await appContext.Component.getInitialProps(appContext.ctx)
|
|
37
|
-
* : {},
|
|
38
|
-
* nextlyticsCtx: getNextlyticsProps(appContext.ctx),
|
|
39
|
-
* }
|
|
40
|
-
* }
|
|
41
|
-
* ```
|
|
15
|
+
* Get Nextlytics props for Pages Router _app.tsx.
|
|
16
|
+
* Reads context from headers set by middleware.
|
|
42
17
|
*/
|
|
43
|
-
declare function getNextlyticsProps(ctx:
|
|
18
|
+
declare function getNextlyticsProps(ctx: PagesRouterContext): NextlyticsContext;
|
|
44
19
|
|
|
45
|
-
export { getNextlyticsProps };
|
|
20
|
+
export { type PagesRouterContext, getNextlyticsProps };
|
package/dist/pages-router.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
|
-
import { TemplatizedScriptInsertion
|
|
2
|
+
import { TemplatizedScriptInsertion } from './types.js';
|
|
3
3
|
import 'next/dist/server/web/spec-extension/cookies';
|
|
4
4
|
|
|
5
|
-
declare const
|
|
6
|
-
readonly pathname: "x-
|
|
7
|
-
readonly search: "x-
|
|
8
|
-
readonly pageRenderId: "x-
|
|
9
|
-
readonly
|
|
10
|
-
readonly
|
|
5
|
+
declare const headerNames: {
|
|
6
|
+
readonly pathname: "x-nl-pathname";
|
|
7
|
+
readonly search: "x-nl-search";
|
|
8
|
+
readonly pageRenderId: "x-nl-page-render-id";
|
|
9
|
+
readonly isSoftNavigation: "x-nl-is-soft-nav";
|
|
10
|
+
readonly scripts: "x-nl-scripts";
|
|
11
11
|
};
|
|
12
|
+
declare const LAST_PAGE_RENDER_ID_COOKIE = "last-page-render-id";
|
|
12
13
|
/** Context passed from middleware to server components via headers */
|
|
13
14
|
type ServerComponentContext = {
|
|
14
15
|
/** Unique page render ID (event ID) */
|
|
@@ -17,14 +18,12 @@ type ServerComponentContext = {
|
|
|
17
18
|
pathname: string;
|
|
18
19
|
/** Query string */
|
|
19
20
|
search: string;
|
|
20
|
-
/** Script actions to execute on client */
|
|
21
|
+
/** Script actions to execute on client (params only, templates come from config) */
|
|
21
22
|
scripts: TemplatizedScriptInsertion<unknown>[];
|
|
22
|
-
/** Template definitions for scripts */
|
|
23
|
-
templates: Record<string, JavascriptTemplate>;
|
|
24
23
|
};
|
|
25
24
|
/** Serialize context to response headers (called in middleware) */
|
|
26
25
|
declare function serializeServerComponentContext(response: NextResponse, ctx: ServerComponentContext): void;
|
|
27
26
|
/** Restore context from request headers (called in server components) */
|
|
28
27
|
declare function restoreServerComponentContext(headersList: Headers): ServerComponentContext | null;
|
|
29
28
|
|
|
30
|
-
export { type ServerComponentContext,
|
|
29
|
+
export { LAST_PAGE_RENDER_ID_COOKIE, type ServerComponentContext, headerNames, restoreServerComponentContext, serializeServerComponentContext };
|
|
@@ -18,32 +18,31 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var server_component_context_exports = {};
|
|
20
20
|
__export(server_component_context_exports, {
|
|
21
|
-
|
|
21
|
+
LAST_PAGE_RENDER_ID_COOKIE: () => LAST_PAGE_RENDER_ID_COOKIE,
|
|
22
|
+
headerNames: () => headerNames,
|
|
22
23
|
restoreServerComponentContext: () => restoreServerComponentContext,
|
|
23
24
|
serializeServerComponentContext: () => serializeServerComponentContext
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(server_component_context_exports);
|
|
26
|
-
const HEADER_PREFIX = "x-
|
|
27
|
-
const
|
|
27
|
+
const HEADER_PREFIX = "x-nl-";
|
|
28
|
+
const headerNames = {
|
|
28
29
|
pathname: `${HEADER_PREFIX}pathname`,
|
|
29
30
|
search: `${HEADER_PREFIX}search`,
|
|
30
31
|
pageRenderId: `${HEADER_PREFIX}page-render-id`,
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
isSoftNavigation: `${HEADER_PREFIX}is-soft-nav`,
|
|
33
|
+
scripts: `${HEADER_PREFIX}scripts`
|
|
33
34
|
};
|
|
35
|
+
const LAST_PAGE_RENDER_ID_COOKIE = "last-page-render-id";
|
|
34
36
|
function serializeServerComponentContext(response, ctx) {
|
|
35
|
-
response.headers.set(
|
|
36
|
-
response.headers.set(
|
|
37
|
-
response.headers.set(
|
|
37
|
+
response.headers.set(headerNames.pageRenderId, ctx.pageRenderId);
|
|
38
|
+
response.headers.set(headerNames.pathname, ctx.pathname);
|
|
39
|
+
response.headers.set(headerNames.search, ctx.search);
|
|
38
40
|
if (ctx.scripts.length > 0) {
|
|
39
41
|
const scriptParts = ctx.scripts.filter((item) => item.type === "script-template").map((s) => `${s.templateId}=${JSON.stringify(s.params)}`);
|
|
40
42
|
if (scriptParts.length > 0) {
|
|
41
|
-
response.headers.set(
|
|
43
|
+
response.headers.set(headerNames.scripts, scriptParts.join(";"));
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
|
-
if (Object.keys(ctx.templates).length > 0) {
|
|
45
|
-
response.headers.set(headerKeys.templates, JSON.stringify(ctx.templates));
|
|
46
|
-
}
|
|
47
46
|
}
|
|
48
47
|
function parseScriptsHeader(header) {
|
|
49
48
|
const scripts = [];
|
|
@@ -62,34 +61,25 @@ function parseScriptsHeader(header) {
|
|
|
62
61
|
return scripts;
|
|
63
62
|
}
|
|
64
63
|
function restoreServerComponentContext(headersList) {
|
|
65
|
-
const pageRenderId = headersList.get(
|
|
64
|
+
const pageRenderId = headersList.get(headerNames.pageRenderId);
|
|
66
65
|
if (!pageRenderId) {
|
|
67
66
|
return null;
|
|
68
67
|
}
|
|
69
|
-
const pathname = headersList.get(
|
|
70
|
-
const search = headersList.get(
|
|
71
|
-
const scriptsHeader = headersList.get(
|
|
68
|
+
const pathname = headersList.get(headerNames.pathname) || "";
|
|
69
|
+
const search = headersList.get(headerNames.search) || "";
|
|
70
|
+
const scriptsHeader = headersList.get(headerNames.scripts);
|
|
72
71
|
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
72
|
return {
|
|
83
73
|
pageRenderId,
|
|
84
74
|
pathname,
|
|
85
75
|
search,
|
|
86
|
-
scripts
|
|
87
|
-
templates
|
|
76
|
+
scripts
|
|
88
77
|
};
|
|
89
78
|
}
|
|
90
79
|
// Annotate the CommonJS export names for ESM import in node:
|
|
91
80
|
0 && (module.exports = {
|
|
92
|
-
|
|
81
|
+
LAST_PAGE_RENDER_ID_COOKIE,
|
|
82
|
+
headerNames,
|
|
93
83
|
restoreServerComponentContext,
|
|
94
84
|
serializeServerComponentContext
|
|
95
85
|
});
|
package/dist/server.d.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ReactNode } from 'react';
|
|
3
1
|
import { NextlyticsConfig, NextlyticsResult, RequestContext } from './types.js';
|
|
4
2
|
import 'next/dist/server/web/spec-extension/cookies';
|
|
5
3
|
import 'next/server';
|
|
6
4
|
|
|
7
5
|
declare function createRequestContext(): Promise<RequestContext>;
|
|
8
|
-
declare function NextlyticsServer({ children }: {
|
|
9
|
-
children: ReactNode;
|
|
10
|
-
}): Promise<react_jsx_runtime.JSX.Element>;
|
|
11
6
|
declare function Nextlytics(userConfig: NextlyticsConfig): NextlyticsResult;
|
|
12
7
|
|
|
13
|
-
export { Nextlytics,
|
|
8
|
+
export { Nextlytics, createRequestContext };
|
package/dist/server.js
CHANGED
|
@@ -19,7 +19,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var server_exports = {};
|
|
20
20
|
__export(server_exports, {
|
|
21
21
|
Nextlytics: () => Nextlytics,
|
|
22
|
-
NextlyticsServer: () => NextlyticsServer,
|
|
23
22
|
createRequestContext: () => createRequestContext
|
|
24
23
|
});
|
|
25
24
|
module.exports = __toCommonJS(server_exports);
|
|
@@ -29,18 +28,28 @@ var import_headers2 = require("./headers");
|
|
|
29
28
|
var import_server_component_context = require("./server-component-context");
|
|
30
29
|
var import_anonymous_user = require("./anonymous-user");
|
|
31
30
|
var import_client = require("./client");
|
|
32
|
-
var import_handlers = require("./handlers");
|
|
33
31
|
var import_config_helpers = require("./config-helpers");
|
|
34
32
|
var import_middleware = require("./middleware");
|
|
35
33
|
var import_uitils = require("./uitils");
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
function isBackendWithConfig(entry) {
|
|
35
|
+
return typeof entry === "object" && entry !== null && "backend" in entry;
|
|
36
|
+
}
|
|
37
|
+
function resolveBackends(config, ctx, policyFilter) {
|
|
38
|
+
const entries = config.backends || [];
|
|
39
|
+
return entries.map((entry) => {
|
|
40
|
+
if (isBackendWithConfig(entry)) {
|
|
41
|
+
const backend2 = typeof entry.backend === "function" ? entry.backend(ctx) : entry.backend;
|
|
42
|
+
return backend2 ? { backend: backend2, pageViewDelivery: entry.pageViewDelivery ?? "on-request" } : null;
|
|
43
|
+
}
|
|
44
|
+
const backend = typeof entry === "function" ? entry(ctx) : entry;
|
|
45
|
+
return backend ? { backend, pageViewDelivery: "on-request" } : null;
|
|
46
|
+
}).filter((b) => b !== null).filter((b) => {
|
|
47
|
+
if (!policyFilter) return true;
|
|
48
|
+
if (policyFilter === "client-actions") {
|
|
49
|
+
return b.pageViewDelivery === "on-page-load" || b.backend.returnsClientActions;
|
|
41
50
|
}
|
|
42
|
-
return
|
|
43
|
-
})
|
|
51
|
+
return b.pageViewDelivery === policyFilter;
|
|
52
|
+
});
|
|
44
53
|
}
|
|
45
54
|
function resolvePlugins(config, ctx) {
|
|
46
55
|
const plugins = config.plugins || [];
|
|
@@ -70,24 +79,23 @@ async function createRequestContext() {
|
|
|
70
79
|
headers: _headers
|
|
71
80
|
};
|
|
72
81
|
}
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
|
|
82
|
+
function collectTemplates(config, ctx) {
|
|
83
|
+
const templates = {};
|
|
84
|
+
const backends = resolveBackends(config, ctx);
|
|
85
|
+
for (const { backend } of backends) {
|
|
86
|
+
if (backend.getClientSideTemplates) {
|
|
87
|
+
Object.assign(templates, backend.getClientSideTemplates());
|
|
88
|
+
}
|
|
81
89
|
}
|
|
82
|
-
return
|
|
90
|
+
return templates;
|
|
83
91
|
}
|
|
84
92
|
function Nextlytics(userConfig) {
|
|
85
93
|
const config = (0, import_config_helpers.withDefaults)(userConfig);
|
|
86
94
|
const validationResult = (0, import_config_helpers.validateConfig)(config);
|
|
87
95
|
(0, import_config_helpers.logConfigWarnings)(validationResult);
|
|
88
|
-
const dispatchEventInternal = (event, ctx) => {
|
|
96
|
+
const dispatchEventInternal = (event, ctx, policyFilter) => {
|
|
89
97
|
const plugins = resolvePlugins(config, ctx);
|
|
90
|
-
const
|
|
98
|
+
const resolved = resolveBackends(config, ctx, policyFilter);
|
|
91
99
|
const pluginsDone = (async () => {
|
|
92
100
|
for (const plugin of plugins) {
|
|
93
101
|
try {
|
|
@@ -98,7 +106,7 @@ function Nextlytics(userConfig) {
|
|
|
98
106
|
}
|
|
99
107
|
})();
|
|
100
108
|
const backendResults = pluginsDone.then(() => {
|
|
101
|
-
return
|
|
109
|
+
return resolved.map(({ backend }) => {
|
|
102
110
|
const start = Date.now();
|
|
103
111
|
const promise = backend.onEvent(event).then((result) => ({ ok: true, ms: Date.now() - start, result })).catch((err) => {
|
|
104
112
|
console.error(`[Nextlytics] Backend "${backend.name}" failed on onEvent:`, err);
|
|
@@ -116,7 +124,7 @@ function Nextlytics(userConfig) {
|
|
|
116
124
|
const completion = backendResults.then(async (results) => {
|
|
117
125
|
const settled = await Promise.all(results.map((r) => r.promise));
|
|
118
126
|
if (config.debug) {
|
|
119
|
-
const nameWidth = Math.max(...results.map((r) => r.backend.name.length));
|
|
127
|
+
const nameWidth = Math.max(...results.map((r) => r.backend.name.length), 1);
|
|
120
128
|
console.log(
|
|
121
129
|
`[Nextlytics] dispatchEvent ${event.type} ${event.eventId} (${results.length} backends)`
|
|
122
130
|
);
|
|
@@ -131,9 +139,11 @@ function Nextlytics(userConfig) {
|
|
|
131
139
|
return { clientActions, completion };
|
|
132
140
|
};
|
|
133
141
|
const updateEventInternal = async (eventId, patch, ctx) => {
|
|
134
|
-
const
|
|
142
|
+
const resolved = resolveBackends(config, ctx, "on-request").filter(
|
|
143
|
+
({ backend }) => backend.supportsUpdates
|
|
144
|
+
);
|
|
135
145
|
const results = await Promise.all(
|
|
136
|
-
|
|
146
|
+
resolved.map(async ({ backend }) => {
|
|
137
147
|
const start = Date.now();
|
|
138
148
|
try {
|
|
139
149
|
await backend.updateEvent(eventId, patch);
|
|
@@ -145,9 +155,9 @@ function Nextlytics(userConfig) {
|
|
|
145
155
|
}
|
|
146
156
|
})
|
|
147
157
|
);
|
|
148
|
-
if (config.debug &&
|
|
149
|
-
const nameWidth = Math.max(...
|
|
150
|
-
console.log(`[Nextlytics] updateEvent ${eventId} (${
|
|
158
|
+
if (config.debug && resolved.length > 0) {
|
|
159
|
+
const nameWidth = Math.max(...resolved.map(({ backend }) => backend.name.length));
|
|
160
|
+
console.log(`[Nextlytics] updateEvent ${eventId} (${resolved.length} backends)`);
|
|
151
161
|
results.forEach((r) => {
|
|
152
162
|
const status = r.ok ? "ok" : "fail";
|
|
153
163
|
console.log(` ${r.backend.name.padEnd(nameWidth)} ${status.padEnd(4)} ${r.ms}ms`);
|
|
@@ -163,11 +173,21 @@ function Nextlytics(userConfig) {
|
|
|
163
173
|
return updateEventInternal(eventId, patch, ctx);
|
|
164
174
|
};
|
|
165
175
|
const middleware = (0, import_middleware.createNextlyticsMiddleware)(config, dispatchEventInternal, updateEventInternal);
|
|
166
|
-
|
|
176
|
+
async function Server({ children }) {
|
|
177
|
+
const headersList = await (0, import_headers.headers)();
|
|
178
|
+
const ctx = (0, import_server_component_context.restoreServerComponentContext)(headersList);
|
|
179
|
+
if (!ctx) {
|
|
180
|
+
console.warn("[Nextlytics] nextlyticsMiddleware should be added in order for Server to work");
|
|
181
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
|
|
182
|
+
}
|
|
183
|
+
const requestCtx = await createRequestContext();
|
|
184
|
+
const templates = collectTemplates(config, requestCtx);
|
|
185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_client.NextlyticsClient, { ctx: { requestId: ctx.pageRenderId, scripts: ctx.scripts, templates }, children });
|
|
186
|
+
}
|
|
167
187
|
const analytics = async () => {
|
|
168
188
|
const headersList = await (0, import_headers.headers)();
|
|
169
189
|
const cookieStore = await (0, import_headers.cookies)();
|
|
170
|
-
const pageRenderId = headersList.get(import_server_component_context.
|
|
190
|
+
const pageRenderId = headersList.get(import_server_component_context.headerNames.pageRenderId);
|
|
171
191
|
const serverContext = createServerContextFromHeaders(headersList);
|
|
172
192
|
const ctx = { headers: headersList, cookies: cookieStore };
|
|
173
193
|
const { anonId: anonymousUserId } = await (0, import_anonymous_user.resolveAnonymousUser)({ ctx, serverContext, config });
|
|
@@ -185,6 +205,7 @@ function Nextlytics(userConfig) {
|
|
|
185
205
|
return { ok: false };
|
|
186
206
|
}
|
|
187
207
|
const event = {
|
|
208
|
+
origin: "server",
|
|
188
209
|
eventId: (0, import_uitils.generateId)(),
|
|
189
210
|
parentEventId: pageRenderId,
|
|
190
211
|
type: eventName,
|
|
@@ -199,7 +220,13 @@ function Nextlytics(userConfig) {
|
|
|
199
220
|
}
|
|
200
221
|
};
|
|
201
222
|
};
|
|
202
|
-
return {
|
|
223
|
+
return {
|
|
224
|
+
middleware,
|
|
225
|
+
analytics,
|
|
226
|
+
dispatchEvent,
|
|
227
|
+
updateEvent,
|
|
228
|
+
NextlyticsServer: Server
|
|
229
|
+
};
|
|
203
230
|
}
|
|
204
231
|
function createServerContextFromHeaders(headersList) {
|
|
205
232
|
const rawHeaders = {};
|
|
@@ -207,8 +234,8 @@ function createServerContextFromHeaders(headersList) {
|
|
|
207
234
|
rawHeaders[key] = value;
|
|
208
235
|
});
|
|
209
236
|
const requestHeaders = (0, import_headers2.removeSensitiveHeaders)(rawHeaders);
|
|
210
|
-
const pathname = headersList.get(import_server_component_context.
|
|
211
|
-
const search = headersList.get(import_server_component_context.
|
|
237
|
+
const pathname = headersList.get(import_server_component_context.headerNames.pathname) || "";
|
|
238
|
+
const search = headersList.get(import_server_component_context.headerNames.search) || "";
|
|
212
239
|
const searchParams = {};
|
|
213
240
|
if (search) {
|
|
214
241
|
const params = new URLSearchParams(search);
|
|
@@ -233,6 +260,5 @@ function createServerContextFromHeaders(headersList) {
|
|
|
233
260
|
// Annotate the CommonJS export names for ESM import in node:
|
|
234
261
|
0 && (module.exports = {
|
|
235
262
|
Nextlytics,
|
|
236
|
-
NextlyticsServer,
|
|
237
263
|
createRequestContext
|
|
238
264
|
});
|