@nextlytics/core 0.3.0 → 0.3.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/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/lib/db.d.ts +29 -29
- package/dist/backends/lib/db.js +17 -17
- package/dist/backends/logging.js +33 -0
- package/dist/backends/neon.js +11 -4
- package/dist/backends/segment.js +1 -0
- 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 +141 -67
- package/dist/config-helpers.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -3
- package/dist/middleware.d.ts +2 -3
- package/dist/middleware.js +39 -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 +40 -21
- package/dist/stable-hash.d.ts +6 -0
- package/dist/stable-hash.js +76 -0
- package/dist/types.d.ts +61 -18
- package/dist/uitils.d.ts +7 -1
- package/dist/uitils.js +39 -5
- package/package.json +1 -1
- package/dist/handlers.d.ts +0 -12
- package/dist/handlers.js +0 -40
package/dist/middleware.js
CHANGED
|
@@ -25,29 +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
|
-
return typeof entry === "object" && entry !== null && "backend" in entry;
|
|
30
|
-
}
|
|
31
|
-
function resolveBackends(config, ctx) {
|
|
32
|
-
const entries = config.backends || [];
|
|
33
|
-
return entries.map((entry) => {
|
|
34
|
-
if (isBackendWithConfig(entry)) {
|
|
35
|
-
const backend2 = typeof entry.backend === "function" ? entry.backend(ctx) : entry.backend;
|
|
36
|
-
return backend2 ? { backend: backend2, ingestPolicy: entry.ingestPolicy ?? "immediate" } : null;
|
|
37
|
-
}
|
|
38
|
-
const backend = typeof entry === "function" ? entry(ctx) : entry;
|
|
39
|
-
return backend ? { backend, ingestPolicy: "immediate" } : null;
|
|
40
|
-
}).filter((b) => b !== null);
|
|
41
|
-
}
|
|
42
|
-
function collectTemplates(backends) {
|
|
43
|
-
const templates = {};
|
|
44
|
-
for (const { backend } of backends) {
|
|
45
|
-
if (backend.getClientSideTemplates) {
|
|
46
|
-
Object.assign(templates, backend.getClientSideTemplates());
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return templates;
|
|
50
|
-
}
|
|
28
|
+
var import_api_handler = require("./api-handler");
|
|
51
29
|
function createRequestContext(request) {
|
|
52
30
|
return {
|
|
53
31
|
headers: request.headers,
|
|
@@ -59,29 +37,57 @@ function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
|
|
|
59
37
|
return async (request) => {
|
|
60
38
|
const pathname = request.nextUrl.pathname;
|
|
61
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
|
+
}
|
|
62
67
|
if (pathname === eventEndpoint) {
|
|
63
68
|
if (request.method === "POST") {
|
|
64
|
-
return handleEventPost(request, config, dispatchEvent, updateEvent);
|
|
69
|
+
return (0, import_api_handler.handleEventPost)(request, config, dispatchEvent, updateEvent);
|
|
65
70
|
}
|
|
66
71
|
return Response.json({ error: "Method not allowed" }, { status: 405 });
|
|
67
72
|
}
|
|
68
73
|
if (reqInfo.isNextjsInternal || reqInfo.isPrefetch || reqInfo.isStaticFile) {
|
|
69
74
|
return import_server.NextResponse.next();
|
|
70
75
|
}
|
|
76
|
+
if (!reqInfo.isPageNavigation && !config.isApiPath(pathname)) {
|
|
77
|
+
return import_server.NextResponse.next();
|
|
78
|
+
}
|
|
71
79
|
const pageRenderId = (0, import_uitils.generateId)();
|
|
72
80
|
const serverContext = (0, import_uitils.createServerContext)(request);
|
|
73
81
|
const response = import_server.NextResponse.next();
|
|
74
82
|
const ctx = createRequestContext(request);
|
|
83
|
+
response.cookies.set(import_server_component_context.LAST_PAGE_RENDER_ID_COOKIE, pageRenderId, { path: "/" });
|
|
75
84
|
const { anonId } = await (0, import_anonymous_user.resolveAnonymousUser)({ ctx, serverContext, config, response });
|
|
76
|
-
const backends = resolveBackends(config, ctx);
|
|
77
|
-
const templates = collectTemplates(backends);
|
|
78
85
|
if (config.excludePaths?.(pathname)) {
|
|
79
86
|
(0, import_server_component_context.serializeServerComponentContext)(response, {
|
|
80
87
|
pageRenderId,
|
|
81
88
|
pathname: request.nextUrl.pathname,
|
|
82
89
|
search: request.nextUrl.search,
|
|
83
|
-
scripts: []
|
|
84
|
-
templates
|
|
90
|
+
scripts: []
|
|
85
91
|
});
|
|
86
92
|
return response;
|
|
87
93
|
}
|
|
@@ -91,12 +97,11 @@ function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
|
|
|
91
97
|
pageRenderId,
|
|
92
98
|
pathname: request.nextUrl.pathname,
|
|
93
99
|
search: request.nextUrl.search,
|
|
94
|
-
scripts: []
|
|
95
|
-
templates
|
|
100
|
+
scripts: []
|
|
96
101
|
});
|
|
97
102
|
return response;
|
|
98
103
|
}
|
|
99
|
-
const userContext = await getUserContext(config, ctx);
|
|
104
|
+
const userContext = await (0, import_api_handler.getUserContext)(config, ctx);
|
|
100
105
|
const pageViewEvent = createPageViewEvent(
|
|
101
106
|
pageRenderId,
|
|
102
107
|
serverContext,
|
|
@@ -104,7 +109,7 @@ function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
|
|
|
104
109
|
userContext,
|
|
105
110
|
anonId
|
|
106
111
|
);
|
|
107
|
-
const { clientActions, completion } = dispatchEvent(pageViewEvent, ctx, "
|
|
112
|
+
const { clientActions, completion } = dispatchEvent(pageViewEvent, ctx, "on-request");
|
|
108
113
|
const actions = await clientActions;
|
|
109
114
|
const scripts = actions.items.filter(
|
|
110
115
|
(i) => i.type === "script-template"
|
|
@@ -114,8 +119,7 @@ function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
|
|
|
114
119
|
pageRenderId,
|
|
115
120
|
pathname: request.nextUrl.pathname,
|
|
116
121
|
search: request.nextUrl.search,
|
|
117
|
-
scripts
|
|
118
|
-
templates
|
|
122
|
+
scripts
|
|
119
123
|
});
|
|
120
124
|
return response;
|
|
121
125
|
};
|
|
@@ -123,6 +127,7 @@ function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
|
|
|
123
127
|
function createPageViewEvent(pageRenderId, serverContext, isApiPath, userContext, anonymousUserId) {
|
|
124
128
|
const eventType = isApiPath ? "apiCall" : "pageView";
|
|
125
129
|
return {
|
|
130
|
+
origin: "server",
|
|
126
131
|
collectedAt: serverContext.collectedAt.toISOString(),
|
|
127
132
|
eventId: pageRenderId,
|
|
128
133
|
type: eventType,
|
|
@@ -132,99 +137,6 @@ function createPageViewEvent(pageRenderId, serverContext, isApiPath, userContext
|
|
|
132
137
|
properties: {}
|
|
133
138
|
};
|
|
134
139
|
}
|
|
135
|
-
async function getUserContext(config, ctx) {
|
|
136
|
-
if (!config.callbacks.getUser) return void 0;
|
|
137
|
-
try {
|
|
138
|
-
return await config.callbacks.getUser(ctx) || void 0;
|
|
139
|
-
} catch {
|
|
140
|
-
return void 0;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
function reconstructServerContext(apiCallContext, clientInit) {
|
|
144
|
-
const searchParams = {};
|
|
145
|
-
if (clientInit.search) {
|
|
146
|
-
const params = new URLSearchParams(clientInit.search);
|
|
147
|
-
params.forEach((value, key) => {
|
|
148
|
-
if (!searchParams[key]) searchParams[key] = [];
|
|
149
|
-
searchParams[key].push(value);
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
...apiCallContext,
|
|
154
|
-
// Override with client-provided values
|
|
155
|
-
host: clientInit.host || apiCallContext.host,
|
|
156
|
-
path: clientInit.path || apiCallContext.path,
|
|
157
|
-
search: Object.keys(searchParams).length > 0 ? searchParams : apiCallContext.search,
|
|
158
|
-
method: "GET"
|
|
159
|
-
// Page loads are always GET
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
async function handleEventPost(request, config, dispatchEvent, updateEvent) {
|
|
163
|
-
const pageRenderId = request.headers.get(import_server_component_context.headers.pageRenderId);
|
|
164
|
-
if (!pageRenderId) {
|
|
165
|
-
return Response.json({ error: "Missing page render ID" }, { status: 400 });
|
|
166
|
-
}
|
|
167
|
-
let body;
|
|
168
|
-
try {
|
|
169
|
-
body = await request.json();
|
|
170
|
-
} catch {
|
|
171
|
-
return Response.json({ error: "Invalid JSON" }, { status: 400 });
|
|
172
|
-
}
|
|
173
|
-
const { type, payload } = body;
|
|
174
|
-
const ctx = createRequestContext(request);
|
|
175
|
-
const apiCallServerContext = (0, import_uitils.createServerContext)(request);
|
|
176
|
-
const userContext = await getUserContext(config, ctx);
|
|
177
|
-
if (type === "client-init") {
|
|
178
|
-
const clientContext = payload;
|
|
179
|
-
const serverContext = reconstructServerContext(apiCallServerContext, clientContext);
|
|
180
|
-
const { anonId: anonymousUserId } = await (0, import_anonymous_user.resolveAnonymousUser)({
|
|
181
|
-
ctx,
|
|
182
|
-
serverContext,
|
|
183
|
-
config
|
|
184
|
-
});
|
|
185
|
-
const event = {
|
|
186
|
-
eventId: pageRenderId,
|
|
187
|
-
type: "pageView",
|
|
188
|
-
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
189
|
-
anonymousUserId,
|
|
190
|
-
serverContext,
|
|
191
|
-
clientContext,
|
|
192
|
-
userContext,
|
|
193
|
-
properties: {}
|
|
194
|
-
};
|
|
195
|
-
const { clientActions, completion } = dispatchEvent(event, ctx, "on-client-event");
|
|
196
|
-
const actions = await clientActions;
|
|
197
|
-
(0, import_server.after)(() => completion);
|
|
198
|
-
(0, import_server.after)(() => updateEvent(pageRenderId, { clientContext, userContext, anonymousUserId }, ctx));
|
|
199
|
-
const scripts = actions.items.filter((i) => i.type === "script-template");
|
|
200
|
-
return Response.json({ ok: true, scripts: scripts.length > 0 ? scripts : void 0 });
|
|
201
|
-
} else if (type === "client-event") {
|
|
202
|
-
const clientContext = payload.clientContext || void 0;
|
|
203
|
-
const serverContext = clientContext ? reconstructServerContext(apiCallServerContext, clientContext) : apiCallServerContext;
|
|
204
|
-
const { anonId: anonymousUserId } = await (0, import_anonymous_user.resolveAnonymousUser)({
|
|
205
|
-
ctx,
|
|
206
|
-
serverContext,
|
|
207
|
-
config
|
|
208
|
-
});
|
|
209
|
-
const event = {
|
|
210
|
-
eventId: (0, import_uitils.generateId)(),
|
|
211
|
-
parentEventId: pageRenderId,
|
|
212
|
-
type: payload.name || type,
|
|
213
|
-
collectedAt: payload.collectedAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
214
|
-
anonymousUserId,
|
|
215
|
-
serverContext,
|
|
216
|
-
clientContext,
|
|
217
|
-
userContext,
|
|
218
|
-
properties: payload.props || {}
|
|
219
|
-
};
|
|
220
|
-
const { clientActions, completion } = dispatchEvent(event, ctx);
|
|
221
|
-
const actions = await clientActions;
|
|
222
|
-
(0, import_server.after)(() => completion);
|
|
223
|
-
const scripts = actions.items.filter((i) => i.type === "script-template");
|
|
224
|
-
return Response.json({ ok: true, scripts: scripts.length > 0 ? scripts : void 0 });
|
|
225
|
-
}
|
|
226
|
-
return Response.json({ ok: true });
|
|
227
|
-
}
|
|
228
140
|
// Annotate the CommonJS export names for ESM import in node:
|
|
229
141
|
0 && (module.exports = {
|
|
230
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,7 +28,6 @@ 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");
|
|
@@ -41,11 +39,17 @@ function resolveBackends(config, ctx, policyFilter) {
|
|
|
41
39
|
return entries.map((entry) => {
|
|
42
40
|
if (isBackendWithConfig(entry)) {
|
|
43
41
|
const backend2 = typeof entry.backend === "function" ? entry.backend(ctx) : entry.backend;
|
|
44
|
-
return backend2 ? { backend: backend2,
|
|
42
|
+
return backend2 ? { backend: backend2, pageViewDelivery: entry.pageViewDelivery ?? "on-request" } : null;
|
|
45
43
|
}
|
|
46
44
|
const backend = typeof entry === "function" ? entry(ctx) : entry;
|
|
47
|
-
return backend ? { backend,
|
|
48
|
-
}).filter((b) => b !== null).filter((b) =>
|
|
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;
|
|
50
|
+
}
|
|
51
|
+
return b.pageViewDelivery === policyFilter;
|
|
52
|
+
});
|
|
49
53
|
}
|
|
50
54
|
function resolvePlugins(config, ctx) {
|
|
51
55
|
const plugins = config.plugins || [];
|
|
@@ -75,16 +79,15 @@ async function createRequestContext() {
|
|
|
75
79
|
headers: _headers
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
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
|
+
}
|
|
86
89
|
}
|
|
87
|
-
return
|
|
90
|
+
return templates;
|
|
88
91
|
}
|
|
89
92
|
function Nextlytics(userConfig) {
|
|
90
93
|
const config = (0, import_config_helpers.withDefaults)(userConfig);
|
|
@@ -136,7 +139,7 @@ function Nextlytics(userConfig) {
|
|
|
136
139
|
return { clientActions, completion };
|
|
137
140
|
};
|
|
138
141
|
const updateEventInternal = async (eventId, patch, ctx) => {
|
|
139
|
-
const resolved = resolveBackends(config, ctx, "
|
|
142
|
+
const resolved = resolveBackends(config, ctx, "on-request").filter(
|
|
140
143
|
({ backend }) => backend.supportsUpdates
|
|
141
144
|
);
|
|
142
145
|
const results = await Promise.all(
|
|
@@ -170,11 +173,21 @@ function Nextlytics(userConfig) {
|
|
|
170
173
|
return updateEventInternal(eventId, patch, ctx);
|
|
171
174
|
};
|
|
172
175
|
const middleware = (0, import_middleware.createNextlyticsMiddleware)(config, dispatchEventInternal, updateEventInternal);
|
|
173
|
-
|
|
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
|
+
}
|
|
174
187
|
const analytics = async () => {
|
|
175
188
|
const headersList = await (0, import_headers.headers)();
|
|
176
189
|
const cookieStore = await (0, import_headers.cookies)();
|
|
177
|
-
const pageRenderId = headersList.get(import_server_component_context.
|
|
190
|
+
const pageRenderId = headersList.get(import_server_component_context.headerNames.pageRenderId);
|
|
178
191
|
const serverContext = createServerContextFromHeaders(headersList);
|
|
179
192
|
const ctx = { headers: headersList, cookies: cookieStore };
|
|
180
193
|
const { anonId: anonymousUserId } = await (0, import_anonymous_user.resolveAnonymousUser)({ ctx, serverContext, config });
|
|
@@ -192,6 +205,7 @@ function Nextlytics(userConfig) {
|
|
|
192
205
|
return { ok: false };
|
|
193
206
|
}
|
|
194
207
|
const event = {
|
|
208
|
+
origin: "server",
|
|
195
209
|
eventId: (0, import_uitils.generateId)(),
|
|
196
210
|
parentEventId: pageRenderId,
|
|
197
211
|
type: eventName,
|
|
@@ -206,7 +220,13 @@ function Nextlytics(userConfig) {
|
|
|
206
220
|
}
|
|
207
221
|
};
|
|
208
222
|
};
|
|
209
|
-
return {
|
|
223
|
+
return {
|
|
224
|
+
middleware,
|
|
225
|
+
analytics,
|
|
226
|
+
dispatchEvent,
|
|
227
|
+
updateEvent,
|
|
228
|
+
NextlyticsServer: Server
|
|
229
|
+
};
|
|
210
230
|
}
|
|
211
231
|
function createServerContextFromHeaders(headersList) {
|
|
212
232
|
const rawHeaders = {};
|
|
@@ -214,8 +234,8 @@ function createServerContextFromHeaders(headersList) {
|
|
|
214
234
|
rawHeaders[key] = value;
|
|
215
235
|
});
|
|
216
236
|
const requestHeaders = (0, import_headers2.removeSensitiveHeaders)(rawHeaders);
|
|
217
|
-
const pathname = headersList.get(import_server_component_context.
|
|
218
|
-
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) || "";
|
|
219
239
|
const searchParams = {};
|
|
220
240
|
if (search) {
|
|
221
241
|
const params = new URLSearchParams(search);
|
|
@@ -240,6 +260,5 @@ function createServerContextFromHeaders(headersList) {
|
|
|
240
260
|
// Annotate the CommonJS export names for ESM import in node:
|
|
241
261
|
0 && (module.exports = {
|
|
242
262
|
Nextlytics,
|
|
243
|
-
NextlyticsServer,
|
|
244
263
|
createRequestContext
|
|
245
264
|
});
|
|
@@ -0,0 +1,76 @@
|
|
|
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 stable_hash_exports = {};
|
|
20
|
+
__export(stable_hash_exports, {
|
|
21
|
+
stableHash: () => stableHash
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(stable_hash_exports);
|
|
24
|
+
const FNV_OFFSET_BASIS = 2166136261;
|
|
25
|
+
const FNV_PRIME = 16777619;
|
|
26
|
+
function stableStringify(value, seen) {
|
|
27
|
+
if (value === null) return "null";
|
|
28
|
+
const valueType = typeof value;
|
|
29
|
+
if (valueType === "string") return JSON.stringify(value);
|
|
30
|
+
if (valueType === "number") return Number.isFinite(value) ? String(value) : "null";
|
|
31
|
+
if (valueType === "boolean") return value ? "true" : "false";
|
|
32
|
+
if (valueType === "bigint") {
|
|
33
|
+
throw new TypeError("Do not know how to serialize a BigInt");
|
|
34
|
+
}
|
|
35
|
+
if (valueType === "undefined" || valueType === "function" || valueType === "symbol") {
|
|
36
|
+
return void 0;
|
|
37
|
+
}
|
|
38
|
+
if (typeof value.toJSON === "function") {
|
|
39
|
+
return stableStringify(value.toJSON(), seen);
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
const items = value.map((item) => stableStringify(item, seen) ?? "null");
|
|
43
|
+
return `[${items.join(",")}]`;
|
|
44
|
+
}
|
|
45
|
+
const obj = value;
|
|
46
|
+
if (seen.has(obj)) {
|
|
47
|
+
throw new TypeError("Converting circular structure to JSON");
|
|
48
|
+
}
|
|
49
|
+
seen.add(obj);
|
|
50
|
+
const keys = Object.keys(obj).sort();
|
|
51
|
+
const parts = [];
|
|
52
|
+
for (const key of keys) {
|
|
53
|
+
const next = stableStringify(obj[key], seen);
|
|
54
|
+
if (next !== void 0) {
|
|
55
|
+
parts.push(`${JSON.stringify(key)}:${next}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
seen.delete(obj);
|
|
59
|
+
return `{${parts.join(",")}}`;
|
|
60
|
+
}
|
|
61
|
+
function fnv1aHash(input) {
|
|
62
|
+
let hash = FNV_OFFSET_BASIS;
|
|
63
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
64
|
+
hash ^= input.charCodeAt(i);
|
|
65
|
+
hash = Math.imul(hash, FNV_PRIME) >>> 0;
|
|
66
|
+
}
|
|
67
|
+
return hash.toString(16).padStart(8, "0");
|
|
68
|
+
}
|
|
69
|
+
function stableHash(value) {
|
|
70
|
+
const stable = stableStringify(value, /* @__PURE__ */ new WeakSet()) ?? "null";
|
|
71
|
+
return fnv1aHash(stable);
|
|
72
|
+
}
|
|
73
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
74
|
+
0 && (module.exports = {
|
|
75
|
+
stableHash
|
|
76
|
+
});
|