@nextlytics/core 0.3.1-canary.94 → 0.3.1-canary.98

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.
@@ -6,6 +6,7 @@ import 'next/dist/server/web/spec-extension/cookies';
6
6
  type DispatchEvent = (event: NextlyticsEvent, ctx: RequestContext, policyFilter?: PageViewDelivery | "client-actions") => DispatchResult;
7
7
  type UpdateEvent = (eventId: string, patch: Partial<NextlyticsEvent>, ctx: RequestContext) => Promise<void>;
8
8
  declare function getUserContext(config: NextlyticsConfigWithDefaults, ctx: RequestContext): Promise<UserContext | undefined>;
9
+ declare function getEventProps(config: NextlyticsConfigWithDefaults, ctx: RequestContext, userContext?: UserContext): Promise<Record<string, unknown> | undefined>;
9
10
  declare function handleEventPost(request: NextRequest, config: NextlyticsConfigWithDefaults, dispatchEvent: DispatchEvent, updateEvent: UpdateEvent): Promise<Response>;
10
11
 
11
- export { type DispatchEvent, type UpdateEvent, getUserContext, handleEventPost };
12
+ export { type DispatchEvent, type UpdateEvent, getEventProps, getUserContext, handleEventPost };
@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var api_handler_exports = {};
20
20
  __export(api_handler_exports, {
21
+ getEventProps: () => getEventProps,
21
22
  getUserContext: () => getUserContext,
22
23
  handleEventPost: () => handleEventPost
23
24
  });
@@ -29,7 +30,8 @@ var import_anonymous_user = require("./anonymous-user");
29
30
  function createRequestContext(request) {
30
31
  return {
31
32
  headers: request.headers,
32
- cookies: request.cookies
33
+ cookies: request.cookies,
34
+ path: request.nextUrl.pathname
33
35
  };
34
36
  }
35
37
  async function getUserContext(config, ctx) {
@@ -40,6 +42,14 @@ async function getUserContext(config, ctx) {
40
42
  return void 0;
41
43
  }
42
44
  }
45
+ async function getEventProps(config, ctx, userContext) {
46
+ if (!config.callbacks.getProps) return void 0;
47
+ try {
48
+ return await config.callbacks.getProps({ ...ctx, user: userContext }) || void 0;
49
+ } catch {
50
+ return void 0;
51
+ }
52
+ }
43
53
  function reconstructServerContext(apiCallContext, clientContext) {
44
54
  const searchParams = {};
45
55
  if (clientContext.search) {
@@ -78,6 +88,8 @@ async function handleClientInit(request, hctx) {
78
88
  serverContext,
79
89
  config
80
90
  });
91
+ const pageCtx = { ...ctx, path: serverContext.path };
92
+ const propsFromCallback = await getEventProps(config, pageCtx, userContext);
81
93
  const isSoftNavigation = hctx.isSoftNavigation;
82
94
  const eventId = isSoftNavigation ? (0, import_uitils.generateId)() : pageRenderId;
83
95
  const event = {
@@ -90,7 +102,7 @@ async function handleClientInit(request, hctx) {
90
102
  serverContext,
91
103
  clientContext,
92
104
  userContext,
93
- properties: {}
105
+ properties: { ...propsFromCallback }
94
106
  };
95
107
  if (isSoftNavigation) {
96
108
  const { clientActions, completion: completion2 } = dispatchEvent(event, ctx);
@@ -115,6 +127,8 @@ async function handleClientEvent(request, hctx) {
115
127
  serverContext,
116
128
  config
117
129
  });
130
+ const pageCtx = { ...ctx, path: serverContext.path };
131
+ const propsFromCallback = await getEventProps(config, pageCtx, userContext);
118
132
  const event = {
119
133
  origin: "client",
120
134
  eventId: (0, import_uitils.generateId)(),
@@ -125,7 +139,7 @@ async function handleClientEvent(request, hctx) {
125
139
  serverContext,
126
140
  clientContext,
127
141
  userContext,
128
- properties: props || {}
142
+ properties: { ...propsFromCallback, ...props }
129
143
  };
130
144
  const { clientActions, completion } = dispatchEvent(event, ctx);
131
145
  const actions = await clientActions;
@@ -177,6 +191,7 @@ async function handleEventPost(request, config, dispatchEvent, updateEvent) {
177
191
  }
178
192
  // Annotate the CommonJS export names for ESM import in node:
179
193
  0 && (module.exports = {
194
+ getEventProps,
180
195
  getUserContext,
181
196
  handleEventPost
182
197
  });
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { Nextlytics } from './server.js';
2
2
  export { getNextlyticsProps } from './pages-router.js';
3
3
  export { NextlyticsClient, NextlyticsContext, useNextlytics } from './client.js';
4
4
  export { loggingBackend } from './backends/logging.js';
5
+ export { PathMatcherOptions, pathMatcher } from './path-matcher.js';
5
6
  export { AnonymousUserResult, BackendConfigEntry, BackendWithConfig, ClientAction, ClientContext, ClientRequest, JavascriptTemplate, NextlyticsBackend, NextlyticsBackendFactory, NextlyticsClientContext, NextlyticsConfig, NextlyticsEvent, NextlyticsPlugin, NextlyticsPluginFactory, NextlyticsResult, NextlyticsServerSide, PageViewDelivery, PagesRouterContext, RequestContext, ServerEventContext, UserContext } from './types.js';
6
7
  import 'react/jsx-runtime';
7
8
  import 'react';
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ __export(index_exports, {
22
22
  NextlyticsClient: () => import_client.NextlyticsClient,
23
23
  getNextlyticsProps: () => import_pages_router.getNextlyticsProps,
24
24
  loggingBackend: () => import_logging.loggingBackend,
25
+ pathMatcher: () => import_path_matcher.pathMatcher,
25
26
  useNextlytics: () => import_client.useNextlytics
26
27
  });
27
28
  module.exports = __toCommonJS(index_exports);
@@ -29,11 +30,13 @@ var import_server = require("./server");
29
30
  var import_pages_router = require("./pages-router");
30
31
  var import_client = require("./client");
31
32
  var import_logging = require("./backends/logging");
33
+ var import_path_matcher = require("./path-matcher");
32
34
  // Annotate the CommonJS export names for ESM import in node:
33
35
  0 && (module.exports = {
34
36
  Nextlytics,
35
37
  NextlyticsClient,
36
38
  getNextlyticsProps,
37
39
  loggingBackend,
40
+ pathMatcher,
38
41
  useNextlytics
39
42
  });
@@ -29,7 +29,8 @@ var import_api_handler = require("./api-handler");
29
29
  function createRequestContext(request) {
30
30
  return {
31
31
  headers: request.headers,
32
- cookies: request.cookies
32
+ cookies: request.cookies,
33
+ path: request.nextUrl.pathname
33
34
  };
34
35
  }
35
36
  function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
@@ -71,10 +72,10 @@ function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
71
72
  return Response.json({ error: "Method not allowed" }, { status: 405 });
72
73
  }
73
74
  if (reqInfo.isNextjsInternal || reqInfo.isPrefetch || reqInfo.isStaticFile) {
74
- return import_server.NextResponse.next();
75
+ return void 0;
75
76
  }
76
77
  if (!reqInfo.isPageNavigation && !config.isApiPath(pathname)) {
77
- return import_server.NextResponse.next();
78
+ return void 0;
78
79
  }
79
80
  const pageRenderId = (0, import_uitils.generateId)();
80
81
  const serverContext = (0, import_uitils.createServerContext)(request);
@@ -102,12 +103,14 @@ function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
102
103
  return response;
103
104
  }
104
105
  const userContext = await (0, import_api_handler.getUserContext)(config, ctx);
106
+ const extraProps = await (0, import_api_handler.getEventProps)(config, ctx, userContext);
105
107
  const pageViewEvent = createPageViewEvent(
106
108
  pageRenderId,
107
109
  serverContext,
108
110
  isApiPath,
109
111
  userContext,
110
- anonId
112
+ anonId,
113
+ extraProps
111
114
  );
112
115
  const { clientActions, completion } = dispatchEvent(pageViewEvent, ctx, "on-request");
113
116
  const actions = await clientActions;
@@ -124,7 +127,7 @@ function createNextlyticsMiddleware(config, dispatchEvent, updateEvent) {
124
127
  return response;
125
128
  };
126
129
  }
127
- function createPageViewEvent(pageRenderId, serverContext, isApiPath, userContext, anonymousUserId) {
130
+ function createPageViewEvent(pageRenderId, serverContext, isApiPath, userContext, anonymousUserId, extraProps) {
128
131
  const eventType = isApiPath ? "apiCall" : "pageView";
129
132
  return {
130
133
  origin: "server",
@@ -134,7 +137,7 @@ function createPageViewEvent(pageRenderId, serverContext, isApiPath, userContext
134
137
  anonymousUserId,
135
138
  serverContext,
136
139
  userContext,
137
- properties: {}
140
+ properties: { ...extraProps }
138
141
  };
139
142
  }
140
143
  // Annotate the CommonJS export names for ESM import in node:
@@ -0,0 +1,20 @@
1
+ type PathMatcherOptions = {
2
+ /** Paths to exclude. Matches exact path or path prefix (with `/` boundary). */
3
+ not?: string | string[];
4
+ /** Allow partial matches — path can have fewer segments than pattern. */
5
+ prefix?: boolean;
6
+ };
7
+ /**
8
+ * Match a URL path against a Next.js-style `[param]` pattern.
9
+ *
10
+ * Returns extracted params on match, or `null` on mismatch.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * pathMatcher("/[workspace]/[project]", "/acme/myproject")
15
+ * // => { workspace: "acme", project: "myproject" }
16
+ * ```
17
+ */
18
+ declare function pathMatcher(pattern: string, path: string, opts?: PathMatcherOptions): Record<string, string> | null;
19
+
20
+ export { type PathMatcherOptions, pathMatcher };
@@ -0,0 +1,58 @@
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 path_matcher_exports = {};
20
+ __export(path_matcher_exports, {
21
+ pathMatcher: () => pathMatcher
22
+ });
23
+ module.exports = __toCommonJS(path_matcher_exports);
24
+ function pathMatcher(pattern, path, opts) {
25
+ if (opts?.not) {
26
+ const exclusions = Array.isArray(opts.not) ? opts.not : [opts.not];
27
+ for (const excl of exclusions) {
28
+ if (path === excl || path.startsWith(excl + "/")) {
29
+ return null;
30
+ }
31
+ }
32
+ }
33
+ const patternSegments = pattern.split("/").filter(Boolean);
34
+ const pathSegments = path.split("/").filter(Boolean);
35
+ if (opts?.prefix) {
36
+ if (pathSegments.length === 0) return null;
37
+ if (pathSegments.length > patternSegments.length) return null;
38
+ } else {
39
+ if (pathSegments.length !== patternSegments.length) return null;
40
+ }
41
+ const params = {};
42
+ const segmentsToMatch = Math.min(patternSegments.length, pathSegments.length);
43
+ for (let i = 0; i < segmentsToMatch; i++) {
44
+ const pat = patternSegments[i];
45
+ const seg = pathSegments[i];
46
+ const paramMatch = pat.match(/^\[(\w+)]$/);
47
+ if (paramMatch) {
48
+ params[paramMatch[1]] = decodeURIComponent(seg);
49
+ } else if (pat !== seg) {
50
+ return null;
51
+ }
52
+ }
53
+ return params;
54
+ }
55
+ // Annotate the CommonJS export names for ESM import in node:
56
+ 0 && (module.exports = {
57
+ pathMatcher
58
+ });
package/dist/server.js CHANGED
@@ -31,6 +31,7 @@ var import_client = require("./client");
31
31
  var import_config_helpers = require("./config-helpers");
32
32
  var import_middleware = require("./middleware");
33
33
  var import_uitils = require("./uitils");
34
+ var import_api_handler = require("./api-handler");
34
35
  function isBackendWithConfig(entry) {
35
36
  return typeof entry === "object" && entry !== null && "backend" in entry;
36
37
  }
@@ -76,7 +77,8 @@ async function createRequestContext() {
76
77
  const [_cookies, _headers] = await Promise.all([(0, import_headers.cookies)(), (0, import_headers.headers)()]);
77
78
  return {
78
79
  cookies: _cookies,
79
- headers: _headers
80
+ headers: _headers,
81
+ path: _headers.get("x-nl-pathname") || ""
80
82
  };
81
83
  }
82
84
  function collectTemplates(config, ctx) {
@@ -189,7 +191,11 @@ function Nextlytics(userConfig) {
189
191
  const cookieStore = await (0, import_headers.cookies)();
190
192
  const pageRenderId = headersList.get(import_server_component_context.headerNames.pageRenderId);
191
193
  const serverContext = createServerContextFromHeaders(headersList);
192
- const ctx = { headers: headersList, cookies: cookieStore };
194
+ const ctx = {
195
+ headers: headersList,
196
+ cookies: cookieStore,
197
+ path: headersList.get(import_server_component_context.headerNames.pathname) || ""
198
+ };
193
199
  const { anonId: anonymousUserId } = await (0, import_anonymous_user.resolveAnonymousUser)({ ctx, serverContext, config });
194
200
  let userContext;
195
201
  if (config.callbacks.getUser) {
@@ -198,6 +204,7 @@ function Nextlytics(userConfig) {
198
204
  } catch {
199
205
  }
200
206
  }
207
+ const propsFromCallback = await (0, import_api_handler.getEventProps)(config, ctx, userContext);
201
208
  return {
202
209
  sendEvent: async (eventName, opts) => {
203
210
  if (!pageRenderId) {
@@ -213,7 +220,7 @@ function Nextlytics(userConfig) {
213
220
  anonymousUserId,
214
221
  serverContext,
215
222
  userContext,
216
- properties: opts?.props || {}
223
+ properties: { ...propsFromCallback, ...opts?.props }
217
224
  };
218
225
  await dispatchEventInternal(event, ctx);
219
226
  return { ok: true };
package/dist/types.d.ts CHANGED
@@ -104,6 +104,7 @@ type AnonymousUserResult = {
104
104
  type RequestContext = {
105
105
  headers: Headers;
106
106
  cookies: Pick<RequestCookies, "get" | "getAll" | "has">;
107
+ path: string;
107
108
  };
108
109
  type NextlyticsPlugin = {
109
110
  /**
@@ -160,6 +161,14 @@ type NextlyticsConfig = {
160
161
  ctx: RequestContext;
161
162
  originalAnonymousUserId?: string;
162
163
  }) => Promise<AnonymousUserResult>;
164
+ /**
165
+ * Derive extra event properties from the request context.
166
+ * Called on every request; the returned object is merged into `event.properties`.
167
+ * User-provided properties (e.g. from `sendEvent` or client custom events) take priority.
168
+ */
169
+ getProps?: (ctx: RequestContext & {
170
+ user?: UserContext;
171
+ }) => Record<string, unknown> | undefined | Promise<Record<string, unknown> | undefined>;
163
172
  };
164
173
  /** Analytics backends to send events to */
165
174
  backends?: BackendConfigEntry[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextlytics/core",
3
- "version": "0.3.1-canary.94",
3
+ "version": "0.3.1-canary.98",
4
4
  "description": "Analytics library for Next.js",
5
5
  "license": "MIT",
6
6
  "repository": {