@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.
- package/dist/backends/ga.d.ts +0 -5
- package/dist/backends/ga.js +17 -93
- package/dist/backends/gtm.js +8 -25
- package/dist/backends/logging.js +0 -33
- package/dist/backends/segment.js +0 -1
- package/dist/client.d.ts +4 -1
- package/dist/client.js +67 -141
- package/dist/config-helpers.js +1 -1
- package/dist/handlers.d.ts +12 -0
- package/dist/handlers.js +40 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -1
- package/dist/middleware.d.ts +3 -2
- package/dist/middleware.js +127 -39
- package/dist/pages-router.d.ts +31 -6
- package/dist/pages-router.js +2 -1
- package/dist/server-component-context.d.ts +11 -10
- package/dist/server-component-context.js +28 -18
- package/dist/server.d.ts +6 -1
- package/dist/server.js +21 -40
- package/dist/types.d.ts +18 -61
- package/dist/uitils.d.ts +1 -7
- package/dist/uitils.js +5 -39
- package/package.json +1 -1
- package/dist/api-handler.d.ts +0 -11
- package/dist/api-handler.js +0 -182
- package/dist/client-utils.d.ts +0 -35
- package/dist/client-utils.js +0 -121
- package/dist/stable-hash.d.ts +0 -6
- package/dist/stable-hash.js +0 -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 { NextMiddleware } from 'next/server';
|
|
2
|
+
import { NextRequest, NextMiddleware } from 'next/server';
|
|
3
3
|
|
|
4
4
|
/** Server-side request context collected in middleware */
|
|
5
5
|
interface ServerEventContext {
|
|
@@ -69,12 +69,6 @@ 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";
|
|
78
72
|
/** ISO timestamp when event was collected */
|
|
79
73
|
collectedAt: string;
|
|
80
74
|
/** Unique event ID */
|
|
@@ -115,17 +109,17 @@ type NextlyticsPlugin = {
|
|
|
115
109
|
};
|
|
116
110
|
/** Factory to create plugin per-request (for request-scoped plugins) */
|
|
117
111
|
type NextlyticsPluginFactory = (ctx: RequestContext) => NextlyticsPlugin;
|
|
118
|
-
/** When to
|
|
119
|
-
type
|
|
120
|
-
/** Dispatch
|
|
121
|
-
"
|
|
122
|
-
/** Dispatch
|
|
123
|
-
| "on-
|
|
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";
|
|
124
118
|
/** Backend with configuration options */
|
|
125
119
|
type BackendWithConfig = {
|
|
126
120
|
backend: NextlyticsBackend | NextlyticsBackendFactory;
|
|
127
|
-
/** When to send events. Default: "
|
|
128
|
-
|
|
121
|
+
/** When to send events. Default: "immediate" */
|
|
122
|
+
ingestPolicy?: IngestPolicy;
|
|
129
123
|
};
|
|
130
124
|
/** Backend config entry - either a backend directly or with config */
|
|
131
125
|
type BackendConfigEntry = NextlyticsBackend | NextlyticsBackendFactory | BackendWithConfig;
|
|
@@ -187,16 +181,13 @@ type DispatchResult = {
|
|
|
187
181
|
};
|
|
188
182
|
type JavascriptTemplate = {
|
|
189
183
|
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[];
|
|
195
184
|
};
|
|
196
185
|
type ScriptElement = {
|
|
197
|
-
async?:
|
|
198
|
-
body?: string
|
|
186
|
+
async?: string;
|
|
187
|
+
body?: string;
|
|
199
188
|
src?: string;
|
|
189
|
+
/** If true, skip insertion if script with same src already exists */
|
|
190
|
+
singleton?: boolean;
|
|
200
191
|
};
|
|
201
192
|
/** Backend that receives analytics events */
|
|
202
193
|
type NextlyticsBackend = {
|
|
@@ -210,7 +201,7 @@ type NextlyticsBackend = {
|
|
|
210
201
|
returnsClientActions?: boolean;
|
|
211
202
|
getClientSideTemplates?: () => Record<string, JavascriptTemplate>;
|
|
212
203
|
/** Handle new event */
|
|
213
|
-
onEvent(event: NextlyticsEvent): Promise<
|
|
204
|
+
onEvent(event: NextlyticsEvent): Promise<void | ClientAction>;
|
|
214
205
|
/** Update existing event (e.g. add client context to server pageView) */
|
|
215
206
|
updateEvent(eventId: string, patch: Partial<NextlyticsEvent>): Promise<void> | void;
|
|
216
207
|
};
|
|
@@ -225,41 +216,11 @@ type NextlyticsServerSide = {
|
|
|
225
216
|
ok: boolean;
|
|
226
217
|
}>;
|
|
227
218
|
};
|
|
228
|
-
|
|
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
|
-
};
|
|
219
|
+
type AppRouteHandlers = Record<"GET" | "POST", (req: NextRequest) => Promise<Response>>;
|
|
261
220
|
/** Return value from Nextlytics() */
|
|
262
221
|
type NextlyticsResult = {
|
|
222
|
+
/** Route handlers for /api/event */
|
|
223
|
+
handlers: AppRouteHandlers;
|
|
263
224
|
/** Get server-side analytics API */
|
|
264
225
|
analytics: () => Promise<NextlyticsServerSide>;
|
|
265
226
|
/** Middleware to intercept requests */
|
|
@@ -268,10 +229,6 @@ type NextlyticsResult = {
|
|
|
268
229
|
dispatchEvent: (event: NextlyticsEvent) => Promise<DispatchResult>;
|
|
269
230
|
/** Manually update existing event */
|
|
270
231
|
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>;
|
|
275
232
|
};
|
|
276
233
|
|
|
277
|
-
export type { AnonymousUserResult, BackendConfigEntry, BackendWithConfig, ClientAction, ClientActionItem, ClientContext,
|
|
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 };
|
package/dist/uitils.d.ts
CHANGED
|
@@ -2,12 +2,6 @@ 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;
|
|
11
5
|
/** Generate a random base62 ID (16 chars = 62^16 ≈ 4.7 × 10^28 combinations) */
|
|
12
6
|
declare function generateId(): string;
|
|
13
7
|
type RequestInfo = {
|
|
@@ -25,4 +19,4 @@ type RequestInfo = {
|
|
|
25
19
|
declare function getRequestInfo(request: NextRequest): RequestInfo;
|
|
26
20
|
declare function createServerContext(request: NextRequest): ServerEventContext;
|
|
27
21
|
|
|
28
|
-
export { type RequestInfo, createServerContext, generateId,
|
|
22
|
+
export { type RequestInfo, createServerContext, generateId, getRequestInfo };
|
package/dist/uitils.js
CHANGED
|
@@ -20,41 +20,10 @@ var uitils_exports = {};
|
|
|
20
20
|
__export(uitils_exports, {
|
|
21
21
|
createServerContext: () => createServerContext,
|
|
22
22
|
generateId: () => generateId,
|
|
23
|
-
|
|
24
|
-
getRequestInfo: () => getRequestInfo,
|
|
25
|
-
isNext15: () => isNext15,
|
|
26
|
-
isNext16: () => isNext16
|
|
23
|
+
getRequestInfo: () => getRequestInfo
|
|
27
24
|
});
|
|
28
25
|
module.exports = __toCommonJS(uitils_exports);
|
|
29
26
|
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
|
-
}
|
|
58
27
|
const BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
59
28
|
function generateId() {
|
|
60
29
|
const length = 16;
|
|
@@ -76,15 +45,15 @@ function getRequestInfo(request) {
|
|
|
76
45
|
);
|
|
77
46
|
const hasStandardPrefetchHeader = headers.get("next-router-prefetch") === "1" || headers.get("purpose") === "prefetch" || headers.get("sec-purpose") === "prefetch";
|
|
78
47
|
const nextUrl = headers.get("next-url");
|
|
48
|
+
const isRscPrefetch = nextUrl !== null && nextUrl !== pathname;
|
|
49
|
+
const isPrefetch = hasStandardPrefetchHeader || isRscPrefetch;
|
|
79
50
|
const isRsc = !!(nextUrl || headers.get("rsc"));
|
|
80
51
|
const secFetchDest = headers.get("sec-fetch-dest");
|
|
81
52
|
const secFetchMode = headers.get("sec-fetch-mode");
|
|
82
53
|
const accept = headers.get("accept") || "";
|
|
83
54
|
const isDocumentRequest = secFetchDest === "document" || secFetchMode === "navigate";
|
|
84
55
|
const acceptsHtml = accept.includes("text/html");
|
|
85
|
-
const isPageNavigation = isDocumentRequest || acceptsHtml;
|
|
86
|
-
const isRscPrefetch = nextUrl !== null && nextUrl !== pathname;
|
|
87
|
-
const isPrefetch = hasStandardPrefetchHeader || isRscPrefetch && !isPageNavigation;
|
|
56
|
+
const isPageNavigation = isRsc || isDocumentRequest || acceptsHtml;
|
|
88
57
|
return {
|
|
89
58
|
isPrefetch,
|
|
90
59
|
isRsc,
|
|
@@ -121,8 +90,5 @@ function createServerContext(request) {
|
|
|
121
90
|
0 && (module.exports = {
|
|
122
91
|
createServerContext,
|
|
123
92
|
generateId,
|
|
124
|
-
|
|
125
|
-
getRequestInfo,
|
|
126
|
-
isNext15,
|
|
127
|
-
isNext16
|
|
93
|
+
getRequestInfo
|
|
128
94
|
});
|
package/package.json
CHANGED
package/dist/api-handler.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { NextRequest } from 'next/server';
|
|
2
|
-
import { NextlyticsEvent, RequestContext, PageViewDelivery, DispatchResult, UserContext } from './types.js';
|
|
3
|
-
import { NextlyticsConfigWithDefaults } from './config-helpers.js';
|
|
4
|
-
import 'next/dist/server/web/spec-extension/cookies';
|
|
5
|
-
|
|
6
|
-
type DispatchEvent = (event: NextlyticsEvent, ctx: RequestContext, policyFilter?: PageViewDelivery | "client-actions") => DispatchResult;
|
|
7
|
-
type UpdateEvent = (eventId: string, patch: Partial<NextlyticsEvent>, ctx: RequestContext) => Promise<void>;
|
|
8
|
-
declare function getUserContext(config: NextlyticsConfigWithDefaults, ctx: RequestContext): Promise<UserContext | undefined>;
|
|
9
|
-
declare function handleEventPost(request: NextRequest, config: NextlyticsConfigWithDefaults, dispatchEvent: DispatchEvent, updateEvent: UpdateEvent): Promise<Response>;
|
|
10
|
-
|
|
11
|
-
export { type DispatchEvent, type UpdateEvent, getUserContext, handleEventPost };
|
package/dist/api-handler.js
DELETED
|
@@ -1,182 +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 api_handler_exports = {};
|
|
20
|
-
__export(api_handler_exports, {
|
|
21
|
-
getUserContext: () => getUserContext,
|
|
22
|
-
handleEventPost: () => handleEventPost
|
|
23
|
-
});
|
|
24
|
-
module.exports = __toCommonJS(api_handler_exports);
|
|
25
|
-
var import_server = require("next/server");
|
|
26
|
-
var import_server_component_context = require("./server-component-context");
|
|
27
|
-
var import_uitils = require("./uitils");
|
|
28
|
-
var import_anonymous_user = require("./anonymous-user");
|
|
29
|
-
function createRequestContext(request) {
|
|
30
|
-
return {
|
|
31
|
-
headers: request.headers,
|
|
32
|
-
cookies: request.cookies
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
async function getUserContext(config, ctx) {
|
|
36
|
-
if (!config.callbacks.getUser) return void 0;
|
|
37
|
-
try {
|
|
38
|
-
return await config.callbacks.getUser(ctx) || void 0;
|
|
39
|
-
} catch {
|
|
40
|
-
return void 0;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function reconstructServerContext(apiCallContext, clientContext) {
|
|
44
|
-
const searchParams = {};
|
|
45
|
-
if (clientContext.search) {
|
|
46
|
-
const params = new URLSearchParams(clientContext.search);
|
|
47
|
-
params.forEach((value, key) => {
|
|
48
|
-
if (!searchParams[key]) searchParams[key] = [];
|
|
49
|
-
searchParams[key].push(value);
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
...apiCallContext,
|
|
54
|
-
host: clientContext.host || apiCallContext.host,
|
|
55
|
-
path: clientContext.path || apiCallContext.path,
|
|
56
|
-
search: Object.keys(searchParams).length > 0 ? searchParams : apiCallContext.search,
|
|
57
|
-
method: "GET"
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
function filterScripts(actions) {
|
|
61
|
-
const scripts = actions.items.filter((i) => i.type === "script-template");
|
|
62
|
-
return scripts.length > 0 ? scripts : void 0;
|
|
63
|
-
}
|
|
64
|
-
async function handleClientInit(request, hctx) {
|
|
65
|
-
const {
|
|
66
|
-
pageRenderId,
|
|
67
|
-
ctx,
|
|
68
|
-
apiCallServerContext,
|
|
69
|
-
userContext,
|
|
70
|
-
config,
|
|
71
|
-
dispatchEvent,
|
|
72
|
-
updateEvent
|
|
73
|
-
} = hctx;
|
|
74
|
-
const { clientContext } = request;
|
|
75
|
-
const serverContext = reconstructServerContext(apiCallServerContext, clientContext);
|
|
76
|
-
const { anonId: anonymousUserId } = await (0, import_anonymous_user.resolveAnonymousUser)({
|
|
77
|
-
ctx,
|
|
78
|
-
serverContext,
|
|
79
|
-
config
|
|
80
|
-
});
|
|
81
|
-
const isSoftNavigation = hctx.isSoftNavigation;
|
|
82
|
-
const eventId = isSoftNavigation ? (0, import_uitils.generateId)() : pageRenderId;
|
|
83
|
-
const event = {
|
|
84
|
-
origin: "client",
|
|
85
|
-
eventId,
|
|
86
|
-
parentEventId: isSoftNavigation ? pageRenderId : void 0,
|
|
87
|
-
type: "pageView",
|
|
88
|
-
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
89
|
-
anonymousUserId,
|
|
90
|
-
serverContext,
|
|
91
|
-
clientContext,
|
|
92
|
-
userContext,
|
|
93
|
-
properties: {}
|
|
94
|
-
};
|
|
95
|
-
if (isSoftNavigation) {
|
|
96
|
-
const { clientActions, completion: completion2 } = dispatchEvent(event, ctx);
|
|
97
|
-
const actions = await clientActions;
|
|
98
|
-
(0, import_server.after)(() => completion2);
|
|
99
|
-
return Response.json({
|
|
100
|
-
ok: true,
|
|
101
|
-
items: filterScripts(actions)
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
const { completion } = dispatchEvent(event, ctx, "client-actions");
|
|
105
|
-
(0, import_server.after)(() => completion);
|
|
106
|
-
(0, import_server.after)(() => updateEvent(pageRenderId, { clientContext, userContext, anonymousUserId }, ctx));
|
|
107
|
-
return Response.json({ ok: true });
|
|
108
|
-
}
|
|
109
|
-
async function handleClientEvent(request, hctx) {
|
|
110
|
-
const { pageRenderId, ctx, apiCallServerContext, userContext, config, dispatchEvent } = hctx;
|
|
111
|
-
const { clientContext, name, props, collectedAt } = request;
|
|
112
|
-
const serverContext = clientContext ? reconstructServerContext(apiCallServerContext, clientContext) : apiCallServerContext;
|
|
113
|
-
const { anonId: anonymousUserId } = await (0, import_anonymous_user.resolveAnonymousUser)({
|
|
114
|
-
ctx,
|
|
115
|
-
serverContext,
|
|
116
|
-
config
|
|
117
|
-
});
|
|
118
|
-
const event = {
|
|
119
|
-
origin: "client",
|
|
120
|
-
eventId: (0, import_uitils.generateId)(),
|
|
121
|
-
parentEventId: pageRenderId,
|
|
122
|
-
type: name,
|
|
123
|
-
collectedAt: collectedAt || (/* @__PURE__ */ new Date()).toISOString(),
|
|
124
|
-
anonymousUserId,
|
|
125
|
-
serverContext,
|
|
126
|
-
clientContext,
|
|
127
|
-
userContext,
|
|
128
|
-
properties: props || {}
|
|
129
|
-
};
|
|
130
|
-
const { clientActions, completion } = dispatchEvent(event, ctx);
|
|
131
|
-
const actions = await clientActions;
|
|
132
|
-
(0, import_server.after)(() => completion);
|
|
133
|
-
return Response.json({ ok: true, items: filterScripts(actions) });
|
|
134
|
-
}
|
|
135
|
-
async function handleEventPost(request, config, dispatchEvent, updateEvent) {
|
|
136
|
-
const softNavHeader = request.headers.get(import_server_component_context.headerNames.isSoftNavigation);
|
|
137
|
-
const isSoftNavigation = softNavHeader === "1";
|
|
138
|
-
const pageRenderIdHeader = request.headers.get(import_server_component_context.headerNames.pageRenderId);
|
|
139
|
-
if (!pageRenderIdHeader) {
|
|
140
|
-
return Response.json({ error: "Missing page render ID" }, { status: 400 });
|
|
141
|
-
}
|
|
142
|
-
let body;
|
|
143
|
-
try {
|
|
144
|
-
body = await request.json();
|
|
145
|
-
} catch {
|
|
146
|
-
return Response.json({ error: "Invalid JSON" }, { status: 400 });
|
|
147
|
-
}
|
|
148
|
-
const ctx = createRequestContext(request);
|
|
149
|
-
const apiCallServerContext = (0, import_uitils.createServerContext)(request);
|
|
150
|
-
const userContext = await getUserContext(config, ctx);
|
|
151
|
-
const cookiePageRenderId = request.cookies.get(import_server_component_context.LAST_PAGE_RENDER_ID_COOKIE)?.value;
|
|
152
|
-
const pageRenderId = isSoftNavigation ? cookiePageRenderId ?? (0, import_uitils.generateId)() : pageRenderIdHeader;
|
|
153
|
-
if (isSoftNavigation && !cookiePageRenderId && config.debug) {
|
|
154
|
-
console.warn(
|
|
155
|
-
"[Nextlytics] Missing last-page-render-id cookie on soft navigation; using a new id."
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
const hctx = {
|
|
159
|
-
pageRenderId,
|
|
160
|
-
isSoftNavigation,
|
|
161
|
-
ctx,
|
|
162
|
-
apiCallServerContext,
|
|
163
|
-
userContext,
|
|
164
|
-
config,
|
|
165
|
-
dispatchEvent,
|
|
166
|
-
updateEvent
|
|
167
|
-
};
|
|
168
|
-
const bodyType = body.type;
|
|
169
|
-
switch (bodyType) {
|
|
170
|
-
case "page-view":
|
|
171
|
-
return handleClientInit(body, hctx);
|
|
172
|
-
case "custom-event":
|
|
173
|
-
return handleClientEvent(body, hctx);
|
|
174
|
-
default:
|
|
175
|
-
return Response.json({ ok: false, error: `Unknown body type ${bodyType}` }, { status: 400 });
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
179
|
-
0 && (module.exports = {
|
|
180
|
-
getUserContext,
|
|
181
|
-
handleEventPost
|
|
182
|
-
});
|
package/dist/client-utils.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
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 };
|
package/dist/client-utils.js
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
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/stable-hash.d.ts
DELETED
package/dist/stable-hash.js
DELETED
|
@@ -1,76 +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 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
|
-
});
|