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