@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.
@@ -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
- function isBackendWithConfig(entry) {
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, "immediate");
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
@@ -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 ContextWithHeaders = {
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
- * Extract Nextlytics context from Pages Router context (getServerSideProps or getInitialProps).
15
- * Use this in _app.tsx with getInitialProps to pass context to NextlyticsClient.
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: ContextWithHeaders): NextlyticsContext;
18
+ declare function getNextlyticsProps(ctx: PagesRouterContext): NextlyticsContext;
44
19
 
45
- export { getNextlyticsProps };
20
+ export { type PagesRouterContext, getNextlyticsProps };
@@ -35,8 +35,7 @@ function getNextlyticsProps(ctx) {
35
35
  }
36
36
  return {
37
37
  requestId: context.pageRenderId,
38
- scripts: context.scripts,
39
- templates: context.templates
38
+ scripts: context.scripts
40
39
  };
41
40
  }
42
41
  // Annotate the CommonJS export names for ESM import in node:
@@ -1,14 +1,15 @@
1
1
  import { NextResponse } from 'next/server';
2
- import { TemplatizedScriptInsertion, JavascriptTemplate } from './types.js';
2
+ import { TemplatizedScriptInsertion } from './types.js';
3
3
  import 'next/dist/server/web/spec-extension/cookies';
4
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";
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, headerKeys as headers, restoreServerComponentContext, serializeServerComponentContext };
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
- headers: () => headerKeys,
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-nc-";
27
- const headerKeys = {
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
- scripts: `${HEADER_PREFIX}scripts`,
32
- templates: `${HEADER_PREFIX}templates`
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(headerKeys.pageRenderId, ctx.pageRenderId);
36
- response.headers.set(headerKeys.pathname, ctx.pathname);
37
- response.headers.set(headerKeys.search, ctx.search);
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(headerKeys.scripts, scriptParts.join(";"));
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(headerKeys.pageRenderId);
64
+ const pageRenderId = headersList.get(headerNames.pageRenderId);
66
65
  if (!pageRenderId) {
67
66
  return null;
68
67
  }
69
- const pathname = headersList.get(headerKeys.pathname) || "";
70
- const search = headersList.get(headerKeys.search) || "";
71
- const scriptsHeader = headersList.get(headerKeys.scripts);
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
- headers,
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, NextlyticsServer, createRequestContext };
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, ingestPolicy: entry.ingestPolicy ?? "immediate" } : null;
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, ingestPolicy: "immediate" } : null;
48
- }).filter((b) => b !== null).filter((b) => !policyFilter || b.ingestPolicy === policyFilter);
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
- async function NextlyticsServer({ children }) {
79
- const headersList = await (0, import_headers.headers)();
80
- const ctx = (0, import_server_component_context.restoreServerComponentContext)(headersList);
81
- if (!ctx) {
82
- console.warn(
83
- "[Nextlytics] nextlyticsMiddleware should be added in order for NextlyticsServer to work"
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 /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_client.NextlyticsClient, { requestId: ctx.pageRenderId, scripts: ctx.scripts, templates: ctx.templates, children });
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, "immediate").filter(
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
- const handlers = (0, import_handlers.createHandlers)();
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.headers.pageRenderId);
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 { middleware, handlers, analytics, dispatchEvent, updateEvent };
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.headers.pathname) || "";
218
- const search = headersList.get(import_server_component_context.headers.search) || "";
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,6 @@
1
+ type JsonValue = string | number | boolean | null | JsonValue[] | {
2
+ [key: string]: JsonValue;
3
+ };
4
+ declare function stableHash(value: JsonValue): string;
5
+
6
+ export { type JsonValue, stableHash };
@@ -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
+ });