@nextlytics/core 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-handler.d.ts +11 -0
- package/dist/api-handler.js +182 -0
- package/dist/backends/ga.d.ts +5 -0
- package/dist/backends/ga.js +93 -17
- package/dist/backends/gtm.js +25 -8
- package/dist/backends/lib/db.d.ts +29 -29
- package/dist/backends/lib/db.js +17 -17
- package/dist/backends/logging.js +33 -0
- package/dist/backends/neon.js +11 -4
- package/dist/backends/segment.js +1 -0
- package/dist/client-utils.d.ts +35 -0
- package/dist/client-utils.js +121 -0
- package/dist/client.d.ts +1 -4
- package/dist/client.js +141 -67
- package/dist/config-helpers.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -3
- package/dist/middleware.d.ts +2 -3
- package/dist/middleware.js +39 -127
- package/dist/pages-router.d.ts +6 -31
- package/dist/pages-router.js +1 -2
- package/dist/server-component-context.d.ts +10 -11
- package/dist/server-component-context.js +18 -28
- package/dist/server.d.ts +1 -6
- package/dist/server.js +40 -21
- package/dist/stable-hash.d.ts +6 -0
- package/dist/stable-hash.js +76 -0
- package/dist/types.d.ts +61 -18
- package/dist/uitils.d.ts +7 -1
- package/dist/uitils.js +39 -5
- package/package.json +1 -1
- package/dist/handlers.d.ts +0 -12
- package/dist/handlers.js +0 -40
package/dist/backends/logging.js
CHANGED
|
@@ -21,10 +21,24 @@ __export(logging_exports, {
|
|
|
21
21
|
loggingBackend: () => loggingBackend
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(logging_exports);
|
|
24
|
+
const LOG_TEMPLATE_ID = "log-console";
|
|
24
25
|
function loggingBackend() {
|
|
25
26
|
return {
|
|
26
27
|
name: "logging",
|
|
27
28
|
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
|
+
},
|
|
28
42
|
async onEvent(event) {
|
|
29
43
|
const { type, eventId, serverContext, ...rest } = event;
|
|
30
44
|
const method = serverContext?.method || "";
|
|
@@ -32,6 +46,25 @@ function loggingBackend() {
|
|
|
32
46
|
const route = method && path ? `${method} ${path}` : "";
|
|
33
47
|
console.log(`[Nextlytics Log] ${type}${route ? ` ${route}` : ""} (${eventId})`);
|
|
34
48
|
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;
|
|
35
68
|
},
|
|
36
69
|
updateEvent(eventId, patch) {
|
|
37
70
|
console.log(`[Nextlytics Log] Update ${eventId}`);
|
package/dist/backends/neon.js
CHANGED
|
@@ -73,10 +73,17 @@ function neonBackend(config) {
|
|
|
73
73
|
}
|
|
74
74
|
if (sets.length > 0) {
|
|
75
75
|
params.push(eventId);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
try {
|
|
77
|
+
await sql.query(
|
|
78
|
+
`UPDATE ${table} SET ${sets.join(", ")} WHERE event_id = $${paramIndex}`,
|
|
79
|
+
params
|
|
80
|
+
);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
if ((0, import_db.isPgTableNotFoundError)(err)) {
|
|
83
|
+
printCreateTableStatement();
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
80
87
|
}
|
|
81
88
|
}
|
|
82
89
|
};
|
package/dist/backends/segment.js
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
declare const debug: (...args: unknown[]) => void;
|
|
4
|
+
type NavigationEvent = {
|
|
5
|
+
softNavigation: boolean;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
};
|
|
8
|
+
declare function usePathnameSafe(): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* Hook that detects page navigations and calls the callback once per navigation.
|
|
11
|
+
*
|
|
12
|
+
* - App Router: initial load = hard nav, subsequent pathname changes = soft nav
|
|
13
|
+
* - Pages Router: every navigation changes requestId = always hard nav
|
|
14
|
+
*
|
|
15
|
+
* Provides an AbortSignal for soft navigations to cancel in-flight requests.
|
|
16
|
+
*/
|
|
17
|
+
declare function useNavigation(requestId: string, onNavigate: (event: NavigationEvent) => void): void;
|
|
18
|
+
/** Props for the InjectScript utility component */
|
|
19
|
+
type InjectScriptProps = {
|
|
20
|
+
/** Inline script body (mutually exclusive with src) */
|
|
21
|
+
body?: string;
|
|
22
|
+
/** External script URL (mutually exclusive with body) */
|
|
23
|
+
src?: string;
|
|
24
|
+
/** Load external script async */
|
|
25
|
+
async?: boolean;
|
|
26
|
+
/** Dependencies that control re-injection. Empty/undefined = once, changes trigger re-injection */
|
|
27
|
+
deps?: unknown[];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Pure utility component that injects a script into the document head.
|
|
31
|
+
* Agnostic to templates, combining logic - just handles the injection lifecycle.
|
|
32
|
+
*/
|
|
33
|
+
declare const InjectScript: react.NamedExoticComponent<InjectScriptProps>;
|
|
34
|
+
|
|
35
|
+
export { InjectScript, type InjectScriptProps, type NavigationEvent, debug, useNavigation, usePathnameSafe };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var client_utils_exports = {};
|
|
21
|
+
__export(client_utils_exports, {
|
|
22
|
+
InjectScript: () => InjectScript,
|
|
23
|
+
debug: () => debug,
|
|
24
|
+
useNavigation: () => useNavigation,
|
|
25
|
+
usePathnameSafe: () => usePathnameSafe
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(client_utils_exports);
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
const DEBUG_KEY = "nextlytics:debug";
|
|
30
|
+
function isDebugEnabled() {
|
|
31
|
+
if (typeof window === "undefined") return false;
|
|
32
|
+
try {
|
|
33
|
+
return localStorage.getItem(DEBUG_KEY) === "true";
|
|
34
|
+
} catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const debug = (...args) => {
|
|
39
|
+
if (!isDebugEnabled()) return;
|
|
40
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[1].slice(0, -1);
|
|
41
|
+
console.log(`[${timestamp}] [Nextlytics]`, ...args);
|
|
42
|
+
};
|
|
43
|
+
let usePathnameImpl = null;
|
|
44
|
+
try {
|
|
45
|
+
usePathnameImpl = require("next/navigation").usePathname;
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
function usePathnameSafe() {
|
|
49
|
+
if (!usePathnameImpl) return null;
|
|
50
|
+
return usePathnameImpl();
|
|
51
|
+
}
|
|
52
|
+
function useNavigation(requestId, onNavigate) {
|
|
53
|
+
const pathname = usePathnameSafe();
|
|
54
|
+
const stateRef = (0, import_react.useRef)(null);
|
|
55
|
+
const onNavigateRef = (0, import_react.useRef)(onNavigate);
|
|
56
|
+
onNavigateRef.current = onNavigate;
|
|
57
|
+
(0, import_react.useEffect)(() => {
|
|
58
|
+
const prev = stateRef.current;
|
|
59
|
+
const isInitial = prev === null;
|
|
60
|
+
const requestIdChanged = !isInitial && prev.requestId !== requestId;
|
|
61
|
+
const pathnameChanged = !isInitial && pathname !== null && prev.pathname !== pathname;
|
|
62
|
+
stateRef.current = { requestId, pathname };
|
|
63
|
+
if (!isInitial && !requestIdChanged && !pathnameChanged) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const softNavigation = !isInitial && !requestIdChanged && pathnameChanged;
|
|
67
|
+
debug("Navigation", {
|
|
68
|
+
isInitial,
|
|
69
|
+
softNavigation,
|
|
70
|
+
requestId,
|
|
71
|
+
pathname,
|
|
72
|
+
requestIdChanged,
|
|
73
|
+
pathnameChanged
|
|
74
|
+
});
|
|
75
|
+
if (softNavigation) {
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
onNavigateRef.current({ softNavigation: true, signal: controller.signal });
|
|
78
|
+
return () => {
|
|
79
|
+
debug("Aborting previous soft navigation request");
|
|
80
|
+
controller.abort();
|
|
81
|
+
};
|
|
82
|
+
} else {
|
|
83
|
+
onNavigateRef.current({ softNavigation: false });
|
|
84
|
+
}
|
|
85
|
+
}, [requestId, pathname]);
|
|
86
|
+
}
|
|
87
|
+
function arraysEqual(a, b) {
|
|
88
|
+
if (a === b) return true;
|
|
89
|
+
if (!a || !b || a.length !== b.length) return false;
|
|
90
|
+
return a.every((v, i) => v === b[i]);
|
|
91
|
+
}
|
|
92
|
+
const InjectScript = (0, import_react.memo)(
|
|
93
|
+
function InjectScript2({ body, src, async: isAsync, deps = [] }) {
|
|
94
|
+
const depsKey = deps.map(String).join("\0");
|
|
95
|
+
(0, import_react.useEffect)(() => {
|
|
96
|
+
const el = document.createElement("script");
|
|
97
|
+
if (src) {
|
|
98
|
+
el.src = src;
|
|
99
|
+
if (isAsync) el.async = true;
|
|
100
|
+
debug("Inject external", { src, deps });
|
|
101
|
+
} else if (body) {
|
|
102
|
+
el.textContent = body;
|
|
103
|
+
debug("Inject inline", { body: body.slice(0, 100), deps });
|
|
104
|
+
}
|
|
105
|
+
document.head.appendChild(el);
|
|
106
|
+
return () => {
|
|
107
|
+
debug("Remove script", { deps });
|
|
108
|
+
el.remove();
|
|
109
|
+
};
|
|
110
|
+
}, [depsKey]);
|
|
111
|
+
return null;
|
|
112
|
+
},
|
|
113
|
+
(prev, next) => prev.body === next.body && prev.src === next.src && prev.async === next.async && arraysEqual(prev.deps, next.deps)
|
|
114
|
+
);
|
|
115
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
116
|
+
0 && (module.exports = {
|
|
117
|
+
InjectScript,
|
|
118
|
+
debug,
|
|
119
|
+
useNavigation,
|
|
120
|
+
usePathnameSafe
|
|
121
|
+
});
|
package/dist/client.d.ts
CHANGED
|
@@ -11,10 +11,7 @@ type NextlyticsContext = {
|
|
|
11
11
|
templates?: Record<string, JavascriptTemplate>;
|
|
12
12
|
};
|
|
13
13
|
declare function NextlyticsClient(props: {
|
|
14
|
-
ctx
|
|
15
|
-
requestId?: string;
|
|
16
|
-
scripts?: TemplatizedScriptInsertion<unknown>[];
|
|
17
|
-
templates?: Record<string, JavascriptTemplate>;
|
|
14
|
+
ctx: NextlyticsContext;
|
|
18
15
|
children?: ReactNode;
|
|
19
16
|
}): react_jsx_runtime.JSX.Element;
|
|
20
17
|
type NextlyticsClientApi = {
|
package/dist/client.js
CHANGED
|
@@ -25,13 +25,15 @@ __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");
|
|
28
29
|
var import_server_component_context = require("./server-component-context");
|
|
29
30
|
var import_template = require("./template");
|
|
31
|
+
var import_stable_hash = require("./stable-hash");
|
|
30
32
|
const templateFunctions = {
|
|
31
33
|
q: (v) => JSON.stringify(v ?? null),
|
|
32
|
-
json: (v) => JSON.stringify(v ?? null)
|
|
34
|
+
json: (v) => JSON.stringify(v ?? null),
|
|
35
|
+
stableHash: (v) => (0, import_stable_hash.stableHash)(v)
|
|
33
36
|
};
|
|
34
|
-
const compiledCache = {};
|
|
35
37
|
const NextlyticsContext = (0, import_react.createContext)(null);
|
|
36
38
|
function createClientContext() {
|
|
37
39
|
const isBrowser = typeof window !== "undefined";
|
|
@@ -55,94 +57,165 @@ function createClientContext() {
|
|
|
55
57
|
locale: isBrowser ? navigator.language : void 0
|
|
56
58
|
};
|
|
57
59
|
}
|
|
58
|
-
function
|
|
59
|
-
const
|
|
60
|
-
|
|
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;
|
|
60
|
+
function deduplicateScripts(scripts, templates) {
|
|
61
|
+
const result = [];
|
|
62
|
+
const firstSeenByDeps = /* @__PURE__ */ new Set();
|
|
70
63
|
for (const script of scripts) {
|
|
71
|
-
if (script.type !== "script-template")
|
|
72
|
-
|
|
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);
|
|
73
69
|
continue;
|
|
74
70
|
}
|
|
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;
|
|
75
84
|
const template = templates[script.templateId];
|
|
76
85
|
if (!template) {
|
|
77
|
-
console.warn(`[Nextlytics]
|
|
86
|
+
console.warn(`[Nextlytics] Template "${script.templateId}" not found`);
|
|
78
87
|
continue;
|
|
79
88
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
if (item.
|
|
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++;
|
|
86
104
|
continue;
|
|
87
105
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
if (compiled.body) {
|
|
93
|
-
el.textContent = (0, import_template.apply)(compiled.body, params, templateFunctions);
|
|
94
|
-
}
|
|
95
|
-
if (item.async) {
|
|
96
|
-
el.async = true;
|
|
106
|
+
if (!item.body) {
|
|
107
|
+
itemIndex++;
|
|
108
|
+
continue;
|
|
97
109
|
}
|
|
98
|
-
|
|
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++;
|
|
99
115
|
}
|
|
100
116
|
}
|
|
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));
|
|
101
123
|
}
|
|
102
|
-
|
|
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 } = {}) {
|
|
103
157
|
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
|
+
}
|
|
104
165
|
const response = await fetch("/api/event", {
|
|
105
166
|
method: "POST",
|
|
106
|
-
headers
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
},
|
|
110
|
-
body: JSON.stringify({ type, payload })
|
|
167
|
+
headers,
|
|
168
|
+
body: JSON.stringify(request),
|
|
169
|
+
signal
|
|
111
170
|
});
|
|
112
171
|
if (response.status === 404) {
|
|
113
172
|
console.error(
|
|
114
|
-
"[Nextlytics] In order for NextlyticsClient to work, you must
|
|
173
|
+
"[Nextlytics] In order for NextlyticsClient to work, you must install nextlytics middleware"
|
|
115
174
|
);
|
|
116
175
|
return { ok: false };
|
|
117
176
|
}
|
|
118
177
|
const data = await response.json().catch(() => ({ ok: response.ok }));
|
|
119
|
-
return { ok: data.ok ?? response.ok,
|
|
178
|
+
return { ok: data.ok ?? response.ok, items: data.items };
|
|
120
179
|
} catch (error) {
|
|
180
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
181
|
+
return { ok: false };
|
|
182
|
+
}
|
|
121
183
|
console.error("[Nextlytics] Failed to send event:", error);
|
|
122
184
|
return { ok: false };
|
|
123
185
|
}
|
|
124
186
|
}
|
|
125
|
-
const initializedRequestIds = /* @__PURE__ */ new Set();
|
|
126
187
|
function NextlyticsClient(props) {
|
|
127
|
-
const requestId =
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
(0, import_react.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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 });
|
|
136
205
|
const clientContext = createClientContext();
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
+
] });
|
|
146
219
|
}
|
|
147
220
|
function useNextlytics() {
|
|
148
221
|
const context = (0, import_react.useContext)(NextlyticsContext);
|
|
@@ -151,23 +224,24 @@ function useNextlytics() {
|
|
|
151
224
|
"[Nextlytics] useNextlytics() must be used within a component wrapped by <NextlyticsServer>. Add <NextlyticsServer> at the top of your layout.tsx file."
|
|
152
225
|
);
|
|
153
226
|
}
|
|
154
|
-
const { requestId,
|
|
155
|
-
const
|
|
227
|
+
const { requestId, addScripts } = context;
|
|
228
|
+
const sendEvent = (0, import_react.useCallback)(
|
|
156
229
|
async (eventName, opts) => {
|
|
157
|
-
const result = await
|
|
230
|
+
const result = await sendEventToServer(requestId, {
|
|
231
|
+
type: "custom-event",
|
|
158
232
|
name: eventName,
|
|
159
233
|
props: opts?.props,
|
|
160
234
|
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
161
235
|
clientContext: createClientContext()
|
|
162
236
|
});
|
|
163
|
-
if (result.
|
|
164
|
-
|
|
237
|
+
if (result.items && result.items.length > 0) {
|
|
238
|
+
addScripts(result.items);
|
|
165
239
|
}
|
|
166
240
|
return { ok: result.ok };
|
|
167
241
|
},
|
|
168
|
-
[requestId,
|
|
242
|
+
[requestId, addScripts]
|
|
169
243
|
);
|
|
170
|
-
return { sendEvent
|
|
244
|
+
return { sendEvent };
|
|
171
245
|
}
|
|
172
246
|
// Annotate the CommonJS export names for ESM import in node:
|
|
173
247
|
0 && (module.exports = {
|
package/dist/config-helpers.js
CHANGED
|
@@ -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 ?? (() =>
|
|
31
|
+
isApiPath: config.isApiPath ?? ((str) => str.startsWith("/api")),
|
|
32
32
|
backends: config.backends ?? [],
|
|
33
33
|
anonymousUsers: {
|
|
34
34
|
gdprMode: config.anonymousUsers?.gdprMode ?? true,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { Nextlytics
|
|
2
|
-
export { NextlyticsClient, NextlyticsContext, useNextlytics } from './client.js';
|
|
1
|
+
export { Nextlytics } from './server.js';
|
|
3
2
|
export { getNextlyticsProps } from './pages-router.js';
|
|
3
|
+
export { NextlyticsClient, NextlyticsContext, useNextlytics } from './client.js';
|
|
4
4
|
export { loggingBackend } from './backends/logging.js';
|
|
5
|
-
export { AnonymousUserResult, BackendConfigEntry, BackendWithConfig, ClientContext,
|
|
5
|
+
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
6
|
import 'react/jsx-runtime';
|
|
7
7
|
import 'react';
|
|
8
8
|
import 'next/dist/server/web/spec-extension/cookies';
|
package/dist/index.js
CHANGED
|
@@ -20,21 +20,19 @@ var index_exports = {};
|
|
|
20
20
|
__export(index_exports, {
|
|
21
21
|
Nextlytics: () => import_server.Nextlytics,
|
|
22
22
|
NextlyticsClient: () => import_client.NextlyticsClient,
|
|
23
|
-
NextlyticsServer: () => import_server.NextlyticsServer,
|
|
24
23
|
getNextlyticsProps: () => import_pages_router.getNextlyticsProps,
|
|
25
24
|
loggingBackend: () => import_logging.loggingBackend,
|
|
26
25
|
useNextlytics: () => import_client.useNextlytics
|
|
27
26
|
});
|
|
28
27
|
module.exports = __toCommonJS(index_exports);
|
|
29
28
|
var import_server = require("./server");
|
|
30
|
-
var import_client = require("./client");
|
|
31
29
|
var import_pages_router = require("./pages-router");
|
|
30
|
+
var import_client = require("./client");
|
|
32
31
|
var import_logging = require("./backends/logging");
|
|
33
32
|
// Annotate the CommonJS export names for ESM import in node:
|
|
34
33
|
0 && (module.exports = {
|
|
35
34
|
Nextlytics,
|
|
36
35
|
NextlyticsClient,
|
|
37
|
-
NextlyticsServer,
|
|
38
36
|
getNextlyticsProps,
|
|
39
37
|
loggingBackend,
|
|
40
38
|
useNextlytics
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { NextMiddleware } from 'next/server';
|
|
2
|
-
import { NextlyticsEvent, RequestContext, IngestPolicy, DispatchResult } from './types.js';
|
|
3
2
|
import { NextlyticsConfigWithDefaults } from './config-helpers.js';
|
|
3
|
+
import { DispatchEvent, UpdateEvent } from './api-handler.js';
|
|
4
|
+
import './types.js';
|
|
4
5
|
import 'next/dist/server/web/spec-extension/cookies';
|
|
5
6
|
|
|
6
|
-
type DispatchEvent = (event: NextlyticsEvent, ctx: RequestContext, policyFilter?: IngestPolicy) => DispatchResult;
|
|
7
|
-
type UpdateEvent = (eventId: string, patch: Partial<NextlyticsEvent>, ctx: RequestContext) => Promise<void>;
|
|
8
7
|
declare function createNextlyticsMiddleware(config: NextlyticsConfigWithDefaults, dispatchEvent: DispatchEvent, updateEvent: UpdateEvent): NextMiddleware;
|
|
9
8
|
|
|
10
9
|
export { createNextlyticsMiddleware };
|