@nextlytics/core 0.3.0-canary.80 → 0.3.0

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.
@@ -15,11 +15,6 @@ type GoogleAnalyticsBackendOptions = {
15
15
  * - "anonymousUserId": Always use Nextlytics anonymousUserId
16
16
  */
17
17
  clientIdSource?: "gaCookie" | "anonymousUserId";
18
- /**
19
- * Prefer sending client-origin events from the browser (gtag) instead of Measurement Protocol.
20
- * Default: true. Set to false to force Measurement Protocol when apiSecret is provided.
21
- */
22
- preferClientSideForClientEvents?: boolean;
23
18
  };
24
19
  declare function googleAnalyticsBackend(opts: GoogleAnalyticsBackendOptions): NextlyticsBackendFactory;
25
20
 
@@ -21,9 +21,7 @@ __export(ga_exports, {
21
21
  googleAnalyticsBackend: () => googleAnalyticsBackend
22
22
  });
23
23
  module.exports = __toCommonJS(ga_exports);
24
- const GA_INIT_TEMPLATE = "ga-gtag";
25
- const GA_PROPERTIES_TEMPLATE = "ga-properties";
26
- const GA_EVENT_TEMPLATE = "ga-event";
24
+ const GA_TEMPLATE_ID = "ga-gtag";
27
25
  function parseGaCookie(cookieValue) {
28
26
  const match = cookieValue.match(/^GA\d+\.\d+\.(.+)$/);
29
27
  return match ? match[1] : null;
@@ -120,51 +118,27 @@ function googleAnalyticsBackend(opts) {
120
118
  return (ctx) => {
121
119
  const gaCookie = ctx.cookies.get("_ga");
122
120
  const gaCookieClientId = gaCookie ? parseGaCookie(gaCookie.value) : null;
123
- const preferClientSideForClientEvents = opts.preferClientSideForClientEvents ?? true;
124
121
  return {
125
122
  name: "google-analytics",
126
123
  returnsClientActions: true,
127
124
  supportsUpdates: false,
128
125
  getClientSideTemplates() {
129
126
  return {
130
- [GA_EVENT_TEMPLATE]: {
131
- deps: "{{eventId}}",
127
+ [GA_TEMPLATE_ID]: {
132
128
  items: [
133
- // Update user properties for this event (if provided)
134
- {
135
- body: [
136
- "gtag('set', {{json(properties)}});",
137
- "gtag('event', '{{eventName}}', {{json(eventParams)}});"
138
- ]
139
- }
140
- ]
141
- },
142
- [GA_INIT_TEMPLATE]: {
143
- deps: "{{measurementId}}{{json(initial_config)}}",
144
- items: [
145
- // External gtag.js - load once
146
129
  {
130
+ async: "true",
147
131
  src: "https://www.googletagmanager.com/gtag/js?id={{measurementId}}",
148
- async: true
132
+ singleton: true
149
133
  },
150
- // gtag definition and initialization - run once
151
134
  {
152
135
  body: [
153
136
  "window.dataLayer = window.dataLayer || [];",
154
- opts.debugMode ? "function gtag(){ console.log('[gtag() call]', arguments); dataLayer.push(arguments); }" : "function gtag(){dataLayer.push(arguments);}",
155
- "window.gtag = gtag;",
137
+ "function gtag(){dataLayer.push(arguments);}",
156
138
  "gtag('js', new Date());",
157
- "gtag('config', '{{measurementId}}', {{json(initial_config)}});"
158
- ]
159
- }
160
- ]
161
- },
162
- [GA_PROPERTIES_TEMPLATE]: {
163
- deps: "{{json(properties)}}",
164
- items: [
165
- // Updates that should NOT trigger page_view (e.g., user_id, user_properties)
166
- {
167
- body: "gtag('set', {{json(properties)}});"
139
+ "gtag('config', '{{measurementId}}', {{json(config)}});",
140
+ "gtag('event', 'page_view');"
141
+ ].join("\n")
168
142
  }
169
143
  ]
170
144
  }
@@ -181,66 +155,33 @@ function googleAnalyticsBackend(opts) {
181
155
  } = event.userContext?.traits ?? {};
182
156
  const userProperties = Object.keys(customTraits).length > 0 ? customTraits : void 0;
183
157
  if (event.type === "pageView") {
184
- const initial_config = {
185
- // Rely on GA auto page_view (including SPA history changes).
186
- send_page_view: true,
158
+ const config = {
159
+ send_page_view: false,
187
160
  client_id: clientId
188
161
  };
189
162
  if (debugMode) {
190
- initial_config.debug_mode = true;
163
+ config.debug_mode = true;
191
164
  }
192
- const properties2 = {};
193
165
  if (userId) {
194
- properties2.user_id = userId;
166
+ config.user_id = userId;
195
167
  }
196
168
  if (userProperties) {
197
- properties2.user_properties = userProperties;
169
+ config.user_properties = userProperties;
198
170
  }
199
171
  return {
200
172
  items: [
201
173
  {
202
174
  type: "script-template",
203
- templateId: GA_INIT_TEMPLATE,
175
+ templateId: GA_TEMPLATE_ID,
204
176
  params: {
205
177
  measurementId,
206
- initial_config
207
- }
208
- },
209
- {
210
- type: "script-template",
211
- templateId: GA_PROPERTIES_TEMPLATE,
212
- params: {
213
- properties: properties2
178
+ config
214
179
  }
215
180
  }
216
181
  ]
217
182
  };
218
183
  }
219
- const eventParams = buildEventParams(event);
220
- const properties = {};
221
- if (userId) {
222
- properties.user_id = userId;
223
- }
224
- if (userProperties) {
225
- properties.user_properties = userProperties;
226
- }
227
- if (event.origin === "client") {
228
- if (preferClientSideForClientEvents || !apiSecret) {
229
- return {
230
- items: [
231
- {
232
- type: "script-template",
233
- templateId: GA_EVENT_TEMPLATE,
234
- params: {
235
- eventId: event.eventId,
236
- eventName: toGA4EventName(event.type),
237
- eventParams,
238
- properties
239
- }
240
- }
241
- ]
242
- };
243
- }
184
+ if (apiSecret) {
244
185
  await sendToMeasurementProtocol({
245
186
  measurementId,
246
187
  apiSecret,
@@ -248,29 +189,12 @@ function googleAnalyticsBackend(opts) {
248
189
  userId,
249
190
  userProperties,
250
191
  eventName: toGA4EventName(event.type),
251
- eventParams,
192
+ eventParams: buildEventParams(event),
252
193
  userAgent: getUserAgent(event),
253
194
  clientIp: getClientIp(event),
254
195
  debugMode
255
196
  });
256
- return void 0;
257
- }
258
- if (!apiSecret) {
259
- return void 0;
260
197
  }
261
- await sendToMeasurementProtocol({
262
- measurementId,
263
- apiSecret,
264
- clientId,
265
- userId,
266
- userProperties,
267
- eventName: toGA4EventName(event.type),
268
- eventParams,
269
- userAgent: getUserAgent(event),
270
- clientIp: getClientIp(event),
271
- debugMode
272
- });
273
- return void 0;
274
198
  },
275
199
  updateEvent() {
276
200
  }
@@ -22,7 +22,6 @@ __export(gtm_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(gtm_exports);
24
24
  const GTM_INIT_TEMPLATE_ID = "gtm-init";
25
- const GTM_INIT_DATA_TEMPLATE_ID = "gtm-init-data";
26
25
  const GTM_PAGEVIEW_TEMPLATE_ID = "gtm-pageview";
27
26
  const GTM_EVENT_TEMPLATE_ID = "gtm-event";
28
27
  function toSnakeCase(str) {
@@ -38,27 +37,21 @@ function googleTagManagerBackend(opts) {
38
37
  return {
39
38
  [GTM_INIT_TEMPLATE_ID]: {
40
39
  items: [
41
- // GTM script loader - run once
42
40
  {
43
41
  body: [
44
42
  "window.dataLayer = window.dataLayer || [];",
45
- "(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':",
46
- "new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],",
47
- "j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=",
48
- "'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);",
49
- "})(window,document,'script','dataLayer','{{containerId}}');"
43
+ "dataLayer.push({{json(initialData)}});",
44
+ "if (!window.google_tag_manager || !window.google_tag_manager['{{containerId}}']) {",
45
+ " (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':",
46
+ " new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],",
47
+ " j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=",
48
+ " 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);",
49
+ " })(window,document,'script','dataLayer','{{containerId}}');",
50
+ "}"
50
51
  ].join("\n")
51
52
  }
52
53
  ]
53
54
  },
54
- [GTM_INIT_DATA_TEMPLATE_ID]: {
55
- items: [
56
- // Initial data push - run when params change (e.g., user logs in)
57
- {
58
- body: "dataLayer.push({{json(initialData)}});"
59
- }
60
- ]
61
- },
62
55
  [GTM_PAGEVIEW_TEMPLATE_ID]: {
63
56
  items: [
64
57
  {
@@ -129,11 +122,6 @@ function googleTagManagerBackend(opts) {
129
122
  if (event.clientContext) {
130
123
  return {
131
124
  items: [
132
- {
133
- type: "script-template",
134
- templateId: GTM_INIT_DATA_TEMPLATE_ID,
135
- params: { initialData }
136
- },
137
125
  {
138
126
  type: "script-template",
139
127
  templateId: GTM_PAGEVIEW_TEMPLATE_ID,
@@ -149,11 +137,6 @@ function googleTagManagerBackend(opts) {
149
137
  templateId: GTM_INIT_TEMPLATE_ID,
150
138
  params: { containerId, initialData }
151
139
  },
152
- {
153
- type: "script-template",
154
- templateId: GTM_INIT_DATA_TEMPLATE_ID,
155
- params: { initialData }
156
- },
157
140
  {
158
141
  type: "script-template",
159
142
  templateId: GTM_PAGEVIEW_TEMPLATE_ID,
@@ -21,24 +21,10 @@ __export(logging_exports, {
21
21
  loggingBackend: () => loggingBackend
22
22
  });
23
23
  module.exports = __toCommonJS(logging_exports);
24
- const LOG_TEMPLATE_ID = "log-console";
25
24
  function loggingBackend() {
26
25
  return {
27
26
  name: "logging",
28
27
  supportsUpdates: true,
29
- returnsClientActions: true,
30
- getClientSideTemplates() {
31
- return {
32
- [LOG_TEMPLATE_ID]: {
33
- deps: "{{eventId}}",
34
- items: [
35
- {
36
- body: "console.log('[Nextlytics Log][client]', {{json(event)}});"
37
- }
38
- ]
39
- }
40
- };
41
- },
42
28
  async onEvent(event) {
43
29
  const { type, eventId, serverContext, ...rest } = event;
44
30
  const method = serverContext?.method || "";
@@ -46,25 +32,6 @@ function loggingBackend() {
46
32
  const route = method && path ? `${method} ${path}` : "";
47
33
  console.log(`[Nextlytics Log] ${type}${route ? ` ${route}` : ""} (${eventId})`);
48
34
  console.log(JSON.stringify({ serverContext, ...rest }, null, 2));
49
- if (event.origin === "client") {
50
- return {
51
- items: [
52
- {
53
- type: "script-template",
54
- templateId: LOG_TEMPLATE_ID,
55
- params: {
56
- eventId: event.eventId,
57
- event: {
58
- type: event.type,
59
- eventProps: event.properties,
60
- userProps: event.userContext
61
- }
62
- }
63
- }
64
- ]
65
- };
66
- }
67
- return void 0;
68
35
  },
69
36
  updateEvent(eventId, patch) {
70
37
  console.log(`[Nextlytics Log] Update ${eventId}`);
@@ -124,7 +124,6 @@ function segmentBackend(config) {
124
124
  } else {
125
125
  await send([{ type: "track", event: event.type, ...basePayload }]);
126
126
  }
127
- return void 0;
128
127
  },
129
128
  updateEvent() {
130
129
  }
package/dist/client.d.ts CHANGED
@@ -11,7 +11,10 @@ type NextlyticsContext = {
11
11
  templates?: Record<string, JavascriptTemplate>;
12
12
  };
13
13
  declare function NextlyticsClient(props: {
14
- ctx: NextlyticsContext;
14
+ ctx?: NextlyticsContext;
15
+ requestId?: string;
16
+ scripts?: TemplatizedScriptInsertion<unknown>[];
17
+ templates?: Record<string, JavascriptTemplate>;
15
18
  children?: ReactNode;
16
19
  }): react_jsx_runtime.JSX.Element;
17
20
  type NextlyticsClientApi = {
package/dist/client.js CHANGED
@@ -25,15 +25,13 @@ __export(client_exports, {
25
25
  module.exports = __toCommonJS(client_exports);
26
26
  var import_jsx_runtime = require("react/jsx-runtime");
27
27
  var import_react = require("react");
28
- var import_client_utils = require("./client-utils");
29
28
  var import_server_component_context = require("./server-component-context");
30
29
  var import_template = require("./template");
31
- var import_stable_hash = require("./stable-hash");
32
30
  const templateFunctions = {
33
31
  q: (v) => JSON.stringify(v ?? null),
34
- json: (v) => JSON.stringify(v ?? null),
35
- stableHash: (v) => (0, import_stable_hash.stableHash)(v)
32
+ json: (v) => JSON.stringify(v ?? null)
36
33
  };
34
+ const compiledCache = {};
37
35
  const NextlyticsContext = (0, import_react.createContext)(null);
38
36
  function createClientContext() {
39
37
  const isBrowser = typeof window !== "undefined";
@@ -57,165 +55,94 @@ function createClientContext() {
57
55
  locale: isBrowser ? navigator.language : void 0
58
56
  };
59
57
  }
60
- function deduplicateScripts(scripts, templates) {
61
- const result = [];
62
- const firstSeenByDeps = /* @__PURE__ */ new Set();
58
+ function getCompiledTemplate(templateId, itemIndex, item) {
59
+ const cacheKey = `${templateId}:${itemIndex}`;
60
+ if (!compiledCache[cacheKey]) {
61
+ compiledCache[cacheKey] = {
62
+ src: item.src ? (0, import_template.compile)(item.src) : void 0,
63
+ body: item.body ? (0, import_template.compile)(item.body) : void 0
64
+ };
65
+ }
66
+ return compiledCache[cacheKey];
67
+ }
68
+ function executeTemplatedScripts(scripts, templates) {
69
+ if (!scripts || typeof window === "undefined") return;
63
70
  for (const script of scripts) {
64
- if (script.type !== "script-template") continue;
65
- const template = templates[script.templateId];
66
- if (!template) continue;
67
- if (!template.deps) {
68
- result.push(script);
71
+ if (script.type !== "script-template") {
72
+ console.warn(`[Nextlytics] unsupported script type ${script.type} `);
69
73
  continue;
70
74
  }
71
- const paramsRecord = script.params || {};
72
- const deps = compileTemplateDeps(template, paramsRecord);
73
- const depsKey = `${script.templateId}\0${deps.join("\0")}`;
74
- if (firstSeenByDeps.has(depsKey)) continue;
75
- firstSeenByDeps.add(depsKey);
76
- result.push(script);
77
- }
78
- return result;
79
- }
80
- function compileScripts(scripts, templates) {
81
- const result = [];
82
- for (const [scriptIndex, script] of scripts.entries()) {
83
- if (script.type !== "script-template") continue;
84
75
  const template = templates[script.templateId];
85
76
  if (!template) {
86
- console.warn(`[Nextlytics] Template "${script.templateId}" not found`);
77
+ console.warn(`[Nextlytics] Missing template: ${script.templateId}`);
87
78
  continue;
88
79
  }
89
- const paramsRecord = script.params || {};
90
- const deps = compileTemplateDeps(template, paramsRecord);
91
- let itemIndex = 0;
92
- for (const item of template.items) {
93
- const keyPrefix = `${script.templateId}:${scriptIndex}:${item.src ? "ext" : "body"}:${itemIndex}`;
94
- if (item.src) {
95
- const compiledSrc = (0, import_template.compile)(item.src);
96
- const src = (0, import_template.apply)(compiledSrc, paramsRecord, templateFunctions);
97
- result.push({
98
- key: keyPrefix,
99
- src,
100
- async: item.async,
101
- deps
102
- });
103
- itemIndex++;
80
+ const params = script.params;
81
+ for (let i = 0; i < template.items.length; i++) {
82
+ const item = template.items[i];
83
+ const compiled = getCompiledTemplate(script.templateId, i, item);
84
+ const src = compiled.src ? (0, import_template.apply)(compiled.src, params, templateFunctions) : void 0;
85
+ if (item.singleton && src && document.querySelector(`script[src="${src}"]`)) {
104
86
  continue;
105
87
  }
106
- if (!item.body) {
107
- itemIndex++;
108
- continue;
88
+ const el = document.createElement("script");
89
+ if (src) {
90
+ el.src = src;
91
+ }
92
+ if (compiled.body) {
93
+ el.textContent = (0, import_template.apply)(compiled.body, params, templateFunctions);
109
94
  }
110
- const bodyText = Array.isArray(item.body) ? item.body.join("\n") : item.body;
111
- const compiled = (0, import_template.compile)(bodyText);
112
- const body = (0, import_template.apply)(compiled, paramsRecord, templateFunctions);
113
- result.push({ key: keyPrefix, body, deps });
114
- itemIndex++;
95
+ if (item.async) {
96
+ el.async = true;
97
+ }
98
+ document.head.appendChild(el);
115
99
  }
116
100
  }
117
- return result;
118
- }
119
- function compileTemplateDeps(template, paramsRecord) {
120
- if (!template.deps) return [];
121
- const rawDeps = Array.isArray(template.deps) ? template.deps : [template.deps];
122
- return rawDeps.map((dep) => (0, import_template.apply)((0, import_template.compile)(dep), paramsRecord, templateFunctions));
123
101
  }
124
- function NextlyticsScripts({
125
- initialScripts
126
- }) {
127
- const context = (0, import_react.useContext)(NextlyticsContext);
128
- if (!context) {
129
- throw new Error("NextlyticsScripts should be called within NextlyticsContext");
130
- }
131
- const { scriptsRef, subscribersRef, templates } = context;
132
- const [, forceUpdate] = (0, import_react.useReducer)((x) => x + 1, 0);
133
- (0, import_react.useEffect)(() => {
134
- subscribersRef.current.add(forceUpdate);
135
- return () => {
136
- subscribersRef.current.delete(forceUpdate);
137
- };
138
- }, [subscribersRef]);
139
- const allScripts = [...initialScripts, ...scriptsRef.current];
140
- const dedupedScripts = (0, import_react.useMemo)(
141
- () => deduplicateScripts(allScripts, templates),
142
- [allScripts, templates]
143
- );
144
- const compiled = (0, import_react.useMemo)(
145
- () => compileScripts(dedupedScripts, templates),
146
- [dedupedScripts, templates]
147
- );
148
- (0, import_client_utils.debug)("Rendering scripts", {
149
- initialCount: initialScripts.length,
150
- dynamicCount: scriptsRef.current.length,
151
- totalCount: allScripts.length,
152
- compiledCount: compiled.length
153
- });
154
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: compiled.map(({ key, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_client_utils.InjectScript, { ...props }, key)) });
155
- }
156
- async function sendEventToServer(requestId, request, { signal, isSoftNavigation } = {}) {
102
+ async function sendEvent(requestId, type, payload) {
157
103
  try {
158
- const headers = {
159
- "Content-Type": "application/json",
160
- [import_server_component_context.headerNames.pageRenderId]: requestId
161
- };
162
- if (isSoftNavigation) {
163
- headers[import_server_component_context.headerNames.isSoftNavigation] = "1";
164
- }
165
104
  const response = await fetch("/api/event", {
166
105
  method: "POST",
167
- headers,
168
- body: JSON.stringify(request),
169
- signal
106
+ headers: {
107
+ "Content-Type": "application/json",
108
+ [import_server_component_context.headers.pageRenderId]: requestId
109
+ },
110
+ body: JSON.stringify({ type, payload })
170
111
  });
171
112
  if (response.status === 404) {
172
113
  console.error(
173
- "[Nextlytics] In order for NextlyticsClient to work, you must install nextlytics middleware"
114
+ "[Nextlytics] In order for NextlyticsClient to work, you must mount nextlyticsRouteHandler to /api/event"
174
115
  );
175
116
  return { ok: false };
176
117
  }
177
118
  const data = await response.json().catch(() => ({ ok: response.ok }));
178
- return { ok: data.ok ?? response.ok, items: data.items };
119
+ return { ok: data.ok ?? response.ok, scripts: data.scripts };
179
120
  } catch (error) {
180
- if (error instanceof Error && error.name === "AbortError") {
181
- return { ok: false };
182
- }
183
121
  console.error("[Nextlytics] Failed to send event:", error);
184
122
  return { ok: false };
185
123
  }
186
124
  }
125
+ const initializedRequestIds = /* @__PURE__ */ new Set();
187
126
  function NextlyticsClient(props) {
188
- const { requestId, scripts: initialScripts = [], templates = {} } = props.ctx;
189
- const scriptsRef = (0, import_react.useRef)([]);
190
- const subscribersRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
191
- const addScripts = (0, import_react.useCallback)((newScripts) => {
192
- (0, import_client_utils.debug)("Adding scripts", {
193
- newCount: newScripts.length,
194
- templateIds: newScripts.map((s) => s.templateId)
195
- });
196
- scriptsRef.current = [...scriptsRef.current, ...newScripts];
197
- subscribersRef.current.forEach((cb) => cb());
198
- }, []);
199
- const contextValue = (0, import_react.useMemo)(
200
- () => ({ requestId, templates, addScripts, scriptsRef, subscribersRef }),
201
- [requestId, templates, addScripts]
202
- );
203
- (0, import_client_utils.useNavigation)(requestId, ({ softNavigation, signal }) => {
204
- (0, import_client_utils.debug)("Sending page-view", { requestId, softNavigation });
127
+ const requestId = props.ctx?.requestId ?? props.requestId ?? "";
128
+ const scripts = props.ctx?.scripts ?? props.scripts;
129
+ const templates = props.ctx?.templates ?? props.templates ?? {};
130
+ (0, import_react.useEffect)(() => {
131
+ if (initializedRequestIds.has(requestId)) return;
132
+ initializedRequestIds.add(requestId);
133
+ if (scripts && Object.keys(templates).length > 0) {
134
+ executeTemplatedScripts(scripts, templates);
135
+ }
205
136
  const clientContext = createClientContext();
206
- sendEventToServer(
207
- requestId,
208
- { type: "page-view", clientContext, softNavigation: softNavigation || void 0 },
209
- { signal, isSoftNavigation: softNavigation }
210
- ).then(({ items }) => {
211
- (0, import_client_utils.debug)("page-view response", { scriptsCount: items?.length ?? 0 });
212
- if (items?.length) addScripts(items);
213
- });
214
- });
215
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(NextlyticsContext.Provider, { value: contextValue, children: [
216
- props.children,
217
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NextlyticsScripts, { initialScripts })
218
- ] });
137
+ sendEvent(requestId, "client-init", clientContext).then(
138
+ ({ scripts: responseScripts }) => {
139
+ if (responseScripts) {
140
+ executeTemplatedScripts(responseScripts, templates);
141
+ }
142
+ }
143
+ );
144
+ }, [requestId, scripts, templates]);
145
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NextlyticsContext.Provider, { value: { requestId, templates }, children: props.children });
219
146
  }
220
147
  function useNextlytics() {
221
148
  const context = (0, import_react.useContext)(NextlyticsContext);
@@ -224,24 +151,23 @@ function useNextlytics() {
224
151
  "[Nextlytics] useNextlytics() must be used within a component wrapped by <NextlyticsServer>. Add <NextlyticsServer> at the top of your layout.tsx file."
225
152
  );
226
153
  }
227
- const { requestId, addScripts } = context;
228
- const sendEvent = (0, import_react.useCallback)(
154
+ const { requestId, templates } = context;
155
+ const send = (0, import_react.useCallback)(
229
156
  async (eventName, opts) => {
230
- const result = await sendEventToServer(requestId, {
231
- type: "custom-event",
157
+ const result = await sendEvent(requestId, "client-event", {
232
158
  name: eventName,
233
159
  props: opts?.props,
234
160
  collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
235
161
  clientContext: createClientContext()
236
162
  });
237
- if (result.items && result.items.length > 0) {
238
- addScripts(result.items);
163
+ if (result.scripts) {
164
+ executeTemplatedScripts(result.scripts, templates);
239
165
  }
240
166
  return { ok: result.ok };
241
167
  },
242
- [requestId, addScripts]
168
+ [requestId, templates]
243
169
  );
244
- return { sendEvent };
170
+ return { sendEvent: send };
245
171
  }
246
172
  // Annotate the CommonJS export names for ESM import in node:
247
173
  0 && (module.exports = {
@@ -28,7 +28,7 @@ function withDefaults(config) {
28
28
  ...config,
29
29
  excludeApiCalls: config.excludeApiCalls ?? false,
30
30
  eventEndpoint: config.eventEndpoint ?? "/api/event",
31
- isApiPath: config.isApiPath ?? ((str) => str.startsWith("/api")),
31
+ isApiPath: config.isApiPath ?? (() => false),
32
32
  backends: config.backends ?? [],
33
33
  anonymousUsers: {
34
34
  gdprMode: config.anonymousUsers?.gdprMode ?? true,
@@ -0,0 +1,12 @@
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 };
@@ -0,0 +1,40 @@
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
+ });