@nextlytics/core 0.3.0-canary.70 → 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.
@@ -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
+ });
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RequestCookies } from 'next/dist/server/web/spec-extension/cookies';
2
- import { NextRequest, NextMiddleware } from 'next/server';
2
+ import { NextMiddleware } from 'next/server';
3
3
 
4
4
  /** Server-side request context collected in middleware */
5
5
  interface ServerEventContext {
@@ -69,6 +69,12 @@ interface UserContext {
69
69
  }
70
70
  /** Analytics event sent to backends */
71
71
  interface NextlyticsEvent {
72
+ /**
73
+ * Where the event was triggered?
74
+ * - server — on server, e.g in route or server side component
75
+ * - client — on client
76
+ */
77
+ origin: "server" | "client";
72
78
  /** ISO timestamp when event was collected */
73
79
  collectedAt: string;
74
80
  /** Unique event ID */
@@ -109,17 +115,17 @@ type NextlyticsPlugin = {
109
115
  };
110
116
  /** Factory to create plugin per-request (for request-scoped plugins) */
111
117
  type NextlyticsPluginFactory = (ctx: RequestContext) => NextlyticsPlugin;
112
- /** When to ingest events for a backend */
113
- type IngestPolicy =
114
- /** Dispatch immediately in middleware (default) - faster but no client context */
115
- "immediate"
116
- /** Dispatch when client-init is received - has full client context (title, screen, etc) */
117
- | "on-client-event";
118
+ /** When to deliver page view events for a backend */
119
+ type PageViewDelivery =
120
+ /** Dispatch on server request in middleware (default) - faster but no client context */
121
+ "on-request"
122
+ /** Dispatch on page load (client-side) - has full client context (title, screen, etc) */
123
+ | "on-page-load";
118
124
  /** Backend with configuration options */
119
125
  type BackendWithConfig = {
120
126
  backend: NextlyticsBackend | NextlyticsBackendFactory;
121
- /** When to send events. Default: "immediate" */
122
- ingestPolicy?: IngestPolicy;
127
+ /** When to send events. Default: "on-request" */
128
+ pageViewDelivery?: PageViewDelivery;
123
129
  };
124
130
  /** Backend config entry - either a backend directly or with config */
125
131
  type BackendConfigEntry = NextlyticsBackend | NextlyticsBackendFactory | BackendWithConfig;
@@ -181,13 +187,16 @@ type DispatchResult = {
181
187
  };
182
188
  type JavascriptTemplate = {
183
189
  items: ScriptElement[];
190
+ /**
191
+ * Optional dependency key template. When this value changes, the script re-injects.
192
+ * If omitted, the template is treated as "once".
193
+ */
194
+ deps?: string | string[];
184
195
  };
185
196
  type ScriptElement = {
186
- async?: string;
187
- body?: string;
197
+ async?: boolean;
198
+ body?: string | string[];
188
199
  src?: string;
189
- /** If true, skip insertion if script with same src already exists */
190
- singleton?: boolean;
191
200
  };
192
201
  /** Backend that receives analytics events */
193
202
  type NextlyticsBackend = {
@@ -201,7 +210,7 @@ type NextlyticsBackend = {
201
210
  returnsClientActions?: boolean;
202
211
  getClientSideTemplates?: () => Record<string, JavascriptTemplate>;
203
212
  /** Handle new event */
204
- onEvent(event: NextlyticsEvent): Promise<void | ClientAction>;
213
+ onEvent(event: NextlyticsEvent): Promise<ClientAction | void | undefined>;
205
214
  /** Update existing event (e.g. add client context to server pageView) */
206
215
  updateEvent(eventId: string, patch: Partial<NextlyticsEvent>): Promise<void> | void;
207
216
  };
@@ -216,11 +225,41 @@ type NextlyticsServerSide = {
216
225
  ok: boolean;
217
226
  }>;
218
227
  };
219
- type AppRouteHandlers = Record<"GET" | "POST", (req: NextRequest) => Promise<Response>>;
228
+ /** Context for Pages Router _app.tsx */
229
+ type PagesRouterContext = {
230
+ req: {
231
+ headers: Record<string, string | string[] | undefined>;
232
+ cookies?: Record<string, string>;
233
+ };
234
+ };
235
+ /** Context passed to NextlyticsClient */
236
+ type NextlyticsClientContext = {
237
+ requestId: string;
238
+ scripts?: TemplatizedScriptInsertion<unknown>[];
239
+ templates?: Record<string, JavascriptTemplate>;
240
+ };
241
+ /** Client-to-server request types (discriminated union) */
242
+ type ClientRequest = {
243
+ type: "page-view";
244
+ clientContext: ClientContext;
245
+ /** If true, only update existing event (soft navigation - no dispatch, no scripts) */
246
+ softNavigation?: boolean;
247
+ } | {
248
+ type: "custom-event";
249
+ name: string;
250
+ props?: Record<string, unknown>;
251
+ collectedAt: string;
252
+ clientContext: ClientContext;
253
+ };
254
+ /**
255
+ * Result of any /api/event call
256
+ */
257
+ type ClientRequestResult = {
258
+ ok: boolean;
259
+ items?: ClientActionItem[];
260
+ };
220
261
  /** Return value from Nextlytics() */
221
262
  type NextlyticsResult = {
222
- /** Route handlers for /api/event */
223
- handlers: AppRouteHandlers;
224
263
  /** Get server-side analytics API */
225
264
  analytics: () => Promise<NextlyticsServerSide>;
226
265
  /** Middleware to intercept requests */
@@ -229,6 +268,10 @@ type NextlyticsResult = {
229
268
  dispatchEvent: (event: NextlyticsEvent) => Promise<DispatchResult>;
230
269
  /** Manually update existing event */
231
270
  updateEvent: (eventId: string, patch: Partial<NextlyticsEvent>) => Promise<void>;
271
+ /** Server component that wraps your app to provide analytics context (App Router) */
272
+ NextlyticsServer: (props: {
273
+ children: React.ReactNode;
274
+ }) => Promise<React.ReactElement>;
232
275
  };
233
276
 
234
- export type { AnonymousUserResult, BackendConfigEntry, BackendWithConfig, ClientAction, ClientActionItem, ClientContext, DispatchResult, IngestPolicy, JavascriptTemplate, NextlyticsBackend, NextlyticsBackendFactory, NextlyticsConfig, NextlyticsEvent, NextlyticsPlugin, NextlyticsPluginFactory, NextlyticsResult, NextlyticsServerSide, RequestContext, ScriptElement, ServerEventContext, TemplatizedScriptInsertion, UserContext };
277
+ export type { AnonymousUserResult, BackendConfigEntry, BackendWithConfig, ClientAction, ClientActionItem, ClientContext, ClientRequest, ClientRequestResult, DispatchResult, JavascriptTemplate, NextlyticsBackend, NextlyticsBackendFactory, NextlyticsClientContext, NextlyticsConfig, NextlyticsEvent, NextlyticsPlugin, NextlyticsPluginFactory, NextlyticsResult, NextlyticsServerSide, PageViewDelivery, PagesRouterContext, RequestContext, ScriptElement, ServerEventContext, TemplatizedScriptInsertion, UserContext };
package/dist/uitils.d.ts CHANGED
@@ -2,6 +2,12 @@ import { NextRequest } from 'next/server';
2
2
  import { ServerEventContext } from './types.js';
3
3
  import 'next/dist/server/web/spec-extension/cookies';
4
4
 
5
+ /** Returns the full installed Next.js version string (e.g. "16.1.6"), or undefined. */
6
+ declare function getNextVersion(): string | undefined;
7
+ /** True if the installed Next.js major version is 15. */
8
+ declare function isNext15(): boolean;
9
+ /** True if the installed Next.js major version is 16. */
10
+ declare function isNext16(): boolean;
5
11
  /** Generate a random base62 ID (16 chars = 62^16 ≈ 4.7 × 10^28 combinations) */
6
12
  declare function generateId(): string;
7
13
  type RequestInfo = {
@@ -19,4 +25,4 @@ type RequestInfo = {
19
25
  declare function getRequestInfo(request: NextRequest): RequestInfo;
20
26
  declare function createServerContext(request: NextRequest): ServerEventContext;
21
27
 
22
- export { type RequestInfo, createServerContext, generateId, getRequestInfo };
28
+ export { type RequestInfo, createServerContext, generateId, getNextVersion, getRequestInfo, isNext15, isNext16 };
package/dist/uitils.js CHANGED
@@ -20,10 +20,41 @@ var uitils_exports = {};
20
20
  __export(uitils_exports, {
21
21
  createServerContext: () => createServerContext,
22
22
  generateId: () => generateId,
23
- getRequestInfo: () => getRequestInfo
23
+ getNextVersion: () => getNextVersion,
24
+ getRequestInfo: () => getRequestInfo,
25
+ isNext15: () => isNext15,
26
+ isNext16: () => isNext16
24
27
  });
25
28
  module.exports = __toCommonJS(uitils_exports);
26
29
  var import_headers = require("./headers");
30
+ let _nextVersion;
31
+ function detectNextVersion() {
32
+ if (_nextVersion !== void 0) return _nextVersion;
33
+ try {
34
+ const pkg = require("next/package.json");
35
+ _nextVersion = pkg.version;
36
+ } catch {
37
+ console.warn(
38
+ "[Nextlytics] Could not read Next.js version from next/package.json.\nThis can happen if your bundler does not support JSON imports from\nnode_modules. Ensure `resolveJsonModule: true` is set in tsconfig\nand your bundler can resolve peer-dependency subpath imports.\nTurbopack and webpack (default Next.js bundlers) both support this."
39
+ );
40
+ _nextVersion = "";
41
+ }
42
+ return _nextVersion || void 0;
43
+ }
44
+ function parseMajor(version) {
45
+ if (!version) return void 0;
46
+ const major = parseInt(version.split(".")[0], 10);
47
+ return Number.isFinite(major) ? major : void 0;
48
+ }
49
+ function getNextVersion() {
50
+ return detectNextVersion();
51
+ }
52
+ function isNext15() {
53
+ return parseMajor(detectNextVersion()) === 15;
54
+ }
55
+ function isNext16() {
56
+ return parseMajor(detectNextVersion()) === 16;
57
+ }
27
58
  const BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
28
59
  function generateId() {
29
60
  const length = 16;
@@ -45,15 +76,15 @@ function getRequestInfo(request) {
45
76
  );
46
77
  const hasStandardPrefetchHeader = headers.get("next-router-prefetch") === "1" || headers.get("purpose") === "prefetch" || headers.get("sec-purpose") === "prefetch";
47
78
  const nextUrl = headers.get("next-url");
48
- const isRscPrefetch = nextUrl !== null && nextUrl !== pathname;
49
- const isPrefetch = hasStandardPrefetchHeader || isRscPrefetch;
50
79
  const isRsc = !!(nextUrl || headers.get("rsc"));
51
80
  const secFetchDest = headers.get("sec-fetch-dest");
52
81
  const secFetchMode = headers.get("sec-fetch-mode");
53
82
  const accept = headers.get("accept") || "";
54
83
  const isDocumentRequest = secFetchDest === "document" || secFetchMode === "navigate";
55
84
  const acceptsHtml = accept.includes("text/html");
56
- const isPageNavigation = isRsc || isDocumentRequest || acceptsHtml;
85
+ const isPageNavigation = isDocumentRequest || acceptsHtml;
86
+ const isRscPrefetch = nextUrl !== null && nextUrl !== pathname;
87
+ const isPrefetch = hasStandardPrefetchHeader || isRscPrefetch && !isPageNavigation;
57
88
  return {
58
89
  isPrefetch,
59
90
  isRsc,
@@ -90,5 +121,8 @@ function createServerContext(request) {
90
121
  0 && (module.exports = {
91
122
  createServerContext,
92
123
  generateId,
93
- getRequestInfo
124
+ getNextVersion,
125
+ getRequestInfo,
126
+ isNext15,
127
+ isNext16
94
128
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextlytics/core",
3
- "version": "0.3.0-canary.70",
3
+ "version": "0.3.0-canary.80",
4
4
  "description": "Analytics library for Next.js",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,12 +0,0 @@
1
- import { NextRequest } from 'next/server';
2
-
3
- type AppRouteHandlers = Record<"GET" | "POST", (req: NextRequest) => Promise<Response>>;
4
- /**
5
- * Route handlers for /api/event (deprecated - middleware handles this now)
6
- *
7
- * Kept for backward compatibility. If you have mounted these handlers at /api/event,
8
- * the middleware will intercept the request first, so these won't be called.
9
- */
10
- declare function createHandlers(): AppRouteHandlers;
11
-
12
- export { createHandlers };
package/dist/handlers.js DELETED
@@ -1,40 +0,0 @@
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 handlers_exports = {};
20
- __export(handlers_exports, {
21
- createHandlers: () => createHandlers
22
- });
23
- module.exports = __toCommonJS(handlers_exports);
24
- function createHandlers() {
25
- return {
26
- GET: async () => {
27
- return Response.json({ status: "ok" });
28
- },
29
- POST: async () => {
30
- return Response.json(
31
- { error: "Middleware not configured. Events are handled by nextlyticsMiddleware." },
32
- { status: 500 }
33
- );
34
- }
35
- };
36
- }
37
- // Annotate the CommonJS export names for ESM import in node:
38
- 0 && (module.exports = {
39
- createHandlers
40
- });