@nextlytics/core 0.2.0 → 0.2.1-canary.55

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.
Files changed (70) hide show
  1. package/dist/anonymous-user.js +26 -2
  2. package/dist/backends/clickhouse.js +32 -14
  3. package/dist/backends/ga.js +26 -2
  4. package/dist/backends/gtm.js +26 -2
  5. package/dist/backends/lib/db.js +33 -2
  6. package/dist/backends/logging.js +26 -2
  7. package/dist/backends/neon.js +41 -20
  8. package/dist/backends/postgrest.js +33 -8
  9. package/dist/backends/posthog.js +26 -2
  10. package/dist/backends/segment.js +26 -2
  11. package/dist/client.js +41 -16
  12. package/dist/config-helpers.js +28 -2
  13. package/dist/handlers.js +35 -11
  14. package/dist/headers.js +26 -2
  15. package/dist/index.js +35 -6
  16. package/dist/middleware.js +47 -26
  17. package/dist/pages-router.js +28 -4
  18. package/dist/plugins/vercel-geo.js +26 -2
  19. package/dist/server-component-context.js +29 -3
  20. package/dist/server.js +60 -35
  21. package/dist/template.js +27 -2
  22. package/dist/types.js +16 -0
  23. package/dist/uitils.js +30 -4
  24. package/package.json +28 -101
  25. package/dist/anonymous-user.cjs +0 -118
  26. package/dist/anonymous-user.d.mts +0 -22
  27. package/dist/backends/clickhouse.cjs +0 -110
  28. package/dist/backends/clickhouse.d.mts +0 -58
  29. package/dist/backends/ga.cjs +0 -207
  30. package/dist/backends/ga.d.mts +0 -21
  31. package/dist/backends/gtm.cjs +0 -155
  32. package/dist/backends/gtm.d.mts +0 -11
  33. package/dist/backends/lib/db.cjs +0 -150
  34. package/dist/backends/lib/db.d.mts +0 -121
  35. package/dist/backends/logging.cjs +0 -45
  36. package/dist/backends/logging.d.mts +0 -7
  37. package/dist/backends/neon.cjs +0 -84
  38. package/dist/backends/neon.d.mts +0 -11
  39. package/dist/backends/postgrest.cjs +0 -98
  40. package/dist/backends/postgrest.d.mts +0 -46
  41. package/dist/backends/posthog.cjs +0 -120
  42. package/dist/backends/posthog.d.mts +0 -13
  43. package/dist/backends/segment.cjs +0 -112
  44. package/dist/backends/segment.d.mts +0 -43
  45. package/dist/client.cjs +0 -171
  46. package/dist/client.d.mts +0 -29
  47. package/dist/config-helpers.cjs +0 -71
  48. package/dist/config-helpers.d.mts +0 -16
  49. package/dist/handlers.cjs +0 -123
  50. package/dist/handlers.d.mts +0 -9
  51. package/dist/headers.cjs +0 -41
  52. package/dist/headers.d.mts +0 -3
  53. package/dist/index.cjs +0 -41
  54. package/dist/index.d.mts +0 -9
  55. package/dist/middleware.cjs +0 -204
  56. package/dist/middleware.d.mts +0 -10
  57. package/dist/pages-router.cjs +0 -45
  58. package/dist/pages-router.d.mts +0 -45
  59. package/dist/plugins/vercel-geo.cjs +0 -60
  60. package/dist/plugins/vercel-geo.d.mts +0 -25
  61. package/dist/server-component-context.cjs +0 -95
  62. package/dist/server-component-context.d.mts +0 -30
  63. package/dist/server.cjs +0 -236
  64. package/dist/server.d.mts +0 -13
  65. package/dist/template.cjs +0 -108
  66. package/dist/template.d.mts +0 -27
  67. package/dist/types.cjs +0 -16
  68. package/dist/types.d.mts +0 -216
  69. package/dist/uitils.cjs +0 -94
  70. package/dist/uitils.d.mts +0 -22
package/dist/template.js CHANGED
@@ -1,3 +1,27 @@
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 template_exports = {};
20
+ __export(template_exports, {
21
+ apply: () => apply,
22
+ compile: () => compile
23
+ });
24
+ module.exports = __toCommonJS(template_exports);
1
25
  function compile(template) {
2
26
  const result = [];
3
27
  let i = 0, text = "";
@@ -77,7 +101,8 @@ function evalExpr(e, params, fns) {
77
101
  )
78
102
  );
79
103
  }
80
- export {
104
+ // Annotate the CommonJS export names for ESM import in node:
105
+ 0 && (module.exports = {
81
106
  apply,
82
107
  compile
83
- };
108
+ });
package/dist/types.js CHANGED
@@ -0,0 +1,16 @@
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 __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var types_exports = {};
16
+ module.exports = __toCommonJS(types_exports);
package/dist/uitils.js CHANGED
@@ -1,4 +1,29 @@
1
- import { removeSensitiveHeaders } from "./headers";
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 uitils_exports = {};
20
+ __export(uitils_exports, {
21
+ createServerContext: () => createServerContext,
22
+ generateId: () => generateId,
23
+ getRequestInfo: () => getRequestInfo
24
+ });
25
+ module.exports = __toCommonJS(uitils_exports);
26
+ var import_headers = require("./headers");
2
27
  const BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3
28
  function generateId() {
4
29
  const length = 16;
@@ -42,7 +67,7 @@ function createServerContext(request) {
42
67
  request.headers.forEach((value, key) => {
43
68
  rawHeaders[key] = value;
44
69
  });
45
- const requestHeaders = removeSensitiveHeaders(rawHeaders);
70
+ const requestHeaders = (0, import_headers.removeSensitiveHeaders)(rawHeaders);
46
71
  const searchParams = {};
47
72
  request.nextUrl.searchParams.forEach((value, key) => {
48
73
  if (!searchParams[key]) {
@@ -61,8 +86,9 @@ function createServerContext(request) {
61
86
  responseHeaders: {}
62
87
  };
63
88
  }
64
- export {
89
+ // Annotate the CommonJS export names for ESM import in node:
90
+ 0 && (module.exports = {
65
91
  createServerContext,
66
92
  generateId,
67
93
  getRequestInfo
68
- };
94
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextlytics/core",
3
- "version": "0.2.0",
3
+ "version": "0.2.1-canary.55",
4
4
  "description": "Analytics library for Next.js",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -8,129 +8,56 @@
8
8
  "url": "https://github.com/jitsucom/nextlytics",
9
9
  "directory": "packages/core"
10
10
  },
11
- "main": "./dist/index.cjs",
12
- "module": "./dist/index.js",
11
+ "main": "./dist/index.js",
13
12
  "types": "./dist/index.d.ts",
14
13
  "exports": {
15
14
  ".": {
16
- "import": {
17
- "types": "./dist/index.d.ts",
18
- "default": "./dist/index.js"
19
- },
20
- "require": {
21
- "types": "./dist/index.d.ts",
22
- "default": "./dist/index.cjs"
23
- }
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
24
17
  },
25
18
  "./client": {
26
- "import": {
27
- "types": "./dist/client.d.ts",
28
- "default": "./dist/client.js"
29
- },
30
- "require": {
31
- "types": "./dist/client.d.ts",
32
- "default": "./dist/client.cjs"
33
- }
19
+ "types": "./dist/client.d.ts",
20
+ "default": "./dist/client.js"
34
21
  },
35
22
  "./server": {
36
- "import": {
37
- "types": "./dist/server.d.ts",
38
- "default": "./dist/server.js"
39
- },
40
- "require": {
41
- "types": "./dist/server.d.ts",
42
- "default": "./dist/server.cjs"
43
- }
23
+ "types": "./dist/server.d.ts",
24
+ "default": "./dist/server.js"
44
25
  },
45
26
  "./backends/logging": {
46
- "import": {
47
- "types": "./dist/backends/logging.d.ts",
48
- "default": "./dist/backends/logging.js"
49
- },
50
- "require": {
51
- "types": "./dist/backends/logging.d.ts",
52
- "default": "./dist/backends/logging.cjs"
53
- }
27
+ "types": "./dist/backends/logging.d.ts",
28
+ "default": "./dist/backends/logging.js"
54
29
  },
55
30
  "./backends/posthog": {
56
- "import": {
57
- "types": "./dist/backends/posthog.d.ts",
58
- "default": "./dist/backends/posthog.js"
59
- },
60
- "require": {
61
- "types": "./dist/backends/posthog.d.ts",
62
- "default": "./dist/backends/posthog.cjs"
63
- }
31
+ "types": "./dist/backends/posthog.d.ts",
32
+ "default": "./dist/backends/posthog.js"
64
33
  },
65
34
  "./backends/neon": {
66
- "import": {
67
- "types": "./dist/backends/neon.d.ts",
68
- "default": "./dist/backends/neon.js"
69
- },
70
- "require": {
71
- "types": "./dist/backends/neon.d.ts",
72
- "default": "./dist/backends/neon.cjs"
73
- }
35
+ "types": "./dist/backends/neon.d.ts",
36
+ "default": "./dist/backends/neon.js"
74
37
  },
75
38
  "./backends/postgrest": {
76
- "import": {
77
- "types": "./dist/backends/postgrest.d.ts",
78
- "default": "./dist/backends/postgrest.js"
79
- },
80
- "require": {
81
- "types": "./dist/backends/postgrest.d.ts",
82
- "default": "./dist/backends/postgrest.cjs"
83
- }
39
+ "types": "./dist/backends/postgrest.d.ts",
40
+ "default": "./dist/backends/postgrest.js"
84
41
  },
85
42
  "./backends/segment": {
86
- "import": {
87
- "types": "./dist/backends/segment.d.ts",
88
- "default": "./dist/backends/segment.js"
89
- },
90
- "require": {
91
- "types": "./dist/backends/segment.d.ts",
92
- "default": "./dist/backends/segment.cjs"
93
- }
43
+ "types": "./dist/backends/segment.d.ts",
44
+ "default": "./dist/backends/segment.js"
94
45
  },
95
46
  "./backends/clickhouse": {
96
- "import": {
97
- "types": "./dist/backends/clickhouse.d.ts",
98
- "default": "./dist/backends/clickhouse.js"
99
- },
100
- "require": {
101
- "types": "./dist/backends/clickhouse.d.ts",
102
- "default": "./dist/backends/clickhouse.cjs"
103
- }
47
+ "types": "./dist/backends/clickhouse.d.ts",
48
+ "default": "./dist/backends/clickhouse.js"
104
49
  },
105
50
  "./backends/gtm": {
106
- "import": {
107
- "types": "./dist/backends/gtm.d.ts",
108
- "default": "./dist/backends/gtm.js"
109
- },
110
- "require": {
111
- "types": "./dist/backends/gtm.d.ts",
112
- "default": "./dist/backends/gtm.cjs"
113
- }
51
+ "types": "./dist/backends/gtm.d.ts",
52
+ "default": "./dist/backends/gtm.js"
114
53
  },
115
54
  "./backends/ga": {
116
- "import": {
117
- "types": "./dist/backends/ga.d.ts",
118
- "default": "./dist/backends/ga.js"
119
- },
120
- "require": {
121
- "types": "./dist/backends/ga.d.ts",
122
- "default": "./dist/backends/ga.cjs"
123
- }
55
+ "types": "./dist/backends/ga.d.ts",
56
+ "default": "./dist/backends/ga.js"
124
57
  },
125
58
  "./plugins/vercel-geo": {
126
- "import": {
127
- "types": "./dist/plugins/vercel-geo.d.ts",
128
- "default": "./dist/plugins/vercel-geo.js"
129
- },
130
- "require": {
131
- "types": "./dist/plugins/vercel-geo.d.ts",
132
- "default": "./dist/plugins/vercel-geo.cjs"
133
- }
59
+ "types": "./dist/plugins/vercel-geo.d.ts",
60
+ "default": "./dist/plugins/vercel-geo.js"
134
61
  }
135
62
  },
136
63
  "files": [
@@ -147,7 +74,7 @@
147
74
  },
148
75
  "peerDependencies": {
149
76
  "next": ">=15.0.0",
150
- "@neondatabase/serverless": ">=0.10.0"
77
+ "@neondatabase/serverless": "^1.0.0"
151
78
  },
152
79
  "peerDependenciesMeta": {
153
80
  "@neondatabase/serverless": {
@@ -155,7 +82,7 @@
155
82
  }
156
83
  },
157
84
  "devDependencies": {
158
- "@neondatabase/serverless": "^0.10.0",
85
+ "@neondatabase/serverless": "^1.0.0",
159
86
  "@types/react": "^19.0.0",
160
87
  "eslint": "^9.0.0",
161
88
  "next": "^15.0.0",
@@ -1,118 +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 anonymous_user_exports = {};
20
- __export(anonymous_user_exports, {
21
- resolveAnonymousUser: () => resolveAnonymousUser
22
- });
23
- module.exports = __toCommonJS(anonymous_user_exports);
24
- const BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
25
- const ANON_ID_LENGTH = 10;
26
- const DEFAULTS = {
27
- gdprMode: true,
28
- useCookies: false,
29
- dailySalt: true,
30
- cookieName: "__nextlytics_anon",
31
- cookieMaxAge: 60 * 60 * 24 * 365 * 2
32
- // 2 years
33
- };
34
- function isSecureRequest(headers) {
35
- const proto = headers.get("x-forwarded-proto");
36
- if (proto) return proto === "https";
37
- return false;
38
- }
39
- function getDailySalt() {
40
- const now = /* @__PURE__ */ new Date();
41
- return `${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDate()}`;
42
- }
43
- function bytesToBase62(bytes, length) {
44
- let result = "";
45
- for (let i = 0; i < length; i++) {
46
- const idx = (bytes[i * 2] * 256 + bytes[i * 2 + 1]) % 62;
47
- result += BASE62_CHARS[idx];
48
- }
49
- return result;
50
- }
51
- async function hashToShortId(input) {
52
- const encoder = new TextEncoder();
53
- const data = encoder.encode(input);
54
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
55
- return bytesToBase62(new Uint8Array(hashBuffer), ANON_ID_LENGTH);
56
- }
57
- async function generateGdprAnonId(serverContext, useDailySalt) {
58
- const ip = serverContext.ip || "unknown";
59
- const userAgent = serverContext.requestHeaders["user-agent"] || "unknown";
60
- const host = serverContext.host || "unknown";
61
- const parts = useDailySalt ? [getDailySalt(), ip, userAgent, host] : [ip, userAgent, host];
62
- return hashToShortId(parts.join("|"));
63
- }
64
- function generateRandomAnonId() {
65
- const bytes = new Uint8Array(ANON_ID_LENGTH * 2);
66
- crypto.getRandomValues(bytes);
67
- return bytesToBase62(bytes, ANON_ID_LENGTH);
68
- }
69
- async function resolveAnonymousUser(params) {
70
- const { ctx, serverContext, config, response } = params;
71
- const { headers, cookies } = ctx;
72
- const gdprMode = config.anonymousUsers?.gdprMode ?? DEFAULTS.gdprMode;
73
- const useCookies = config.anonymousUsers?.useCookies ?? DEFAULTS.useCookies;
74
- const dailySalt = config.anonymousUsers?.dailySalt ?? DEFAULTS.dailySalt;
75
- const cookieName = config.anonymousUsers?.cookieName ?? DEFAULTS.cookieName;
76
- const cookieMaxAge = config.anonymousUsers?.cookieMaxAge ?? DEFAULTS.cookieMaxAge;
77
- let anonId;
78
- let shouldSetCookie = false;
79
- if (useCookies) {
80
- const existingCookie = cookies.get(cookieName);
81
- if (existingCookie?.value) {
82
- anonId = existingCookie.value;
83
- } else {
84
- anonId = gdprMode ? await generateGdprAnonId(serverContext, dailySalt) : generateRandomAnonId();
85
- shouldSetCookie = true;
86
- }
87
- } else {
88
- if (gdprMode) {
89
- anonId = await generateGdprAnonId(serverContext, dailySalt);
90
- } else {
91
- anonId = generateRandomAnonId();
92
- }
93
- }
94
- if (config.callbacks?.getAnonymousUserId) {
95
- try {
96
- const overrideResult = await config.callbacks.getAnonymousUserId({
97
- ctx,
98
- originalAnonymousUserId: anonId
99
- });
100
- anonId = overrideResult.anonId;
101
- } catch {
102
- }
103
- }
104
- if (shouldSetCookie && response) {
105
- response.cookies.set(cookieName, anonId, {
106
- maxAge: cookieMaxAge,
107
- httpOnly: true,
108
- secure: isSecureRequest(headers),
109
- sameSite: "lax",
110
- path: "/"
111
- });
112
- }
113
- return { anonId };
114
- }
115
- // Annotate the CommonJS export names for ESM import in node:
116
- 0 && (module.exports = {
117
- resolveAnonymousUser
118
- });
@@ -1,22 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { RequestContext, ServerEventContext, NextlyticsConfig, AnonymousUserResult } from './types.mjs';
3
- import 'next/dist/server/web/spec-extension/cookies';
4
-
5
- type ResolveAnonymousUserParams = {
6
- ctx: RequestContext;
7
- serverContext: ServerEventContext;
8
- config: NextlyticsConfig;
9
- /** Optional response for setting cookies. If not provided, cookies won't be set. */
10
- response?: NextResponse | null;
11
- };
12
- /**
13
- * Resolve anonymous user ID based on config.
14
- *
15
- * Modes:
16
- * - gdprMode=true (default): Hash-based ID (Fathom-like)
17
- * - gdprMode=false + useCookies=true: Persistent cookie-based ID
18
- * - gdprMode=false + useCookies=false: Random ID per request
19
- */
20
- declare function resolveAnonymousUser(params: ResolveAnonymousUserParams): Promise<AnonymousUserResult>;
21
-
22
- export { type ResolveAnonymousUserParams, resolveAnonymousUser };
@@ -1,110 +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 clickhouse_exports = {};
20
- __export(clickhouse_exports, {
21
- clickhouseBackend: () => clickhouseBackend
22
- });
23
- module.exports = __toCommonJS(clickhouse_exports);
24
- var import_db = require("./lib/db");
25
- function clickhouseBackend(config) {
26
- const baseUrl = config.url.replace(/\/$/, "");
27
- const database = config.database ?? "default";
28
- const table = config.tableName ?? "analytics";
29
- const asyncInsert = config.asyncInsert ?? true;
30
- const acceptUpdates = config.acceptUpdates ?? false;
31
- const updateLookbackMinutes = config.updateLookbackMinutes ?? 60;
32
- const authHeader = "Basic " + btoa((config.username ?? "default") + ":" + (config.password ?? ""));
33
- function printCreateTableStatement() {
34
- console.error(`[Nextlytics ClickHouse] Table "${database}.${table}" does not exist. Run:
35
- `);
36
- console.error((0, import_db.generateChCreateTableSQL)(database, table));
37
- }
38
- async function query(sql) {
39
- const params = new URLSearchParams({
40
- database,
41
- query: sql
42
- });
43
- const res = await fetch(`${baseUrl}/?${params}`, {
44
- method: "GET",
45
- headers: { Authorization: authHeader }
46
- });
47
- if (!res.ok) {
48
- throw new Error(`ClickHouse query error ${res.status}: ${await res.text()}`);
49
- }
50
- const text = await res.text();
51
- if (!text.trim()) return [];
52
- return text.trim().split("\n").map((line) => JSON.parse(line));
53
- }
54
- async function insert(row) {
55
- const params = new URLSearchParams({
56
- database,
57
- query: `INSERT INTO ${table} FORMAT JSONEachRow`,
58
- date_time_input_format: "best_effort"
59
- });
60
- if (asyncInsert) {
61
- params.set("async_insert", "1");
62
- params.set("wait_for_async_insert", "0");
63
- }
64
- const res = await fetch(`${baseUrl}/?${params}`, {
65
- method: "POST",
66
- headers: {
67
- "Content-Type": "application/json",
68
- Authorization: authHeader
69
- },
70
- body: JSON.stringify(row)
71
- });
72
- if (!res.ok) {
73
- const text = await res.text();
74
- if ((0, import_db.isChTableNotFoundError)(text)) {
75
- printCreateTableStatement();
76
- }
77
- throw new Error(`ClickHouse error ${res.status}: ${text}`);
78
- }
79
- }
80
- return {
81
- name: "clickhouse",
82
- supportsUpdates: acceptUpdates,
83
- async onEvent(event) {
84
- const row = (0, import_db.eventToJsonRow)(event);
85
- await insert(row);
86
- },
87
- async updateEvent(eventId, patch) {
88
- if (!acceptUpdates || !patch.clientContext) return;
89
- const cols = import_db.tableColumns.map((c) => c.name).join(", ");
90
- const rows = await query(
91
- `SELECT ${cols} FROM ${table} WHERE event_id = '${eventId}' AND timestamp > now() - INTERVAL ${updateLookbackMinutes} MINUTE FORMAT JSONEachRow`
92
- );
93
- if (rows.length === 0) return;
94
- const existing = rows[0];
95
- const clientCtx = (0, import_db.extractClientContext)(patch.clientContext);
96
- const updated = {
97
- ...existing,
98
- referer: clientCtx.referer ?? existing.referer,
99
- user_agent: clientCtx.user_agent ?? existing.user_agent,
100
- locale: clientCtx.locale ?? existing.locale,
101
- client_context: clientCtx.rest ?? existing.client_context
102
- };
103
- await insert(updated);
104
- }
105
- };
106
- }
107
- // Annotate the CommonJS export names for ESM import in node:
108
- 0 && (module.exports = {
109
- clickhouseBackend
110
- });
@@ -1,58 +0,0 @@
1
- import { NextlyticsBackend } from '../types.mjs';
2
- import 'next/dist/server/web/spec-extension/cookies';
3
- import 'next/server';
4
-
5
- /**
6
- * ClickHouse backend for Nextlytics
7
- *
8
- * Sends events to ClickHouse via HTTP API with async inserts.
9
- *
10
- * ## ClickHouse Cloud Usage
11
- *
12
- * ```typescript
13
- * import { clickhouseBackend } from "@nextlytics/core/backends/clickhouse"
14
- *
15
- * const analytics = Nextlytics({
16
- * backends: [
17
- * clickhouseBackend({
18
- * url: "https://xxx.clickhouse.cloud:8443",
19
- * username: "default",
20
- * password: process.env.CLICKHOUSE_PASSWORD!,
21
- * })
22
- * ]
23
- * })
24
- * ```
25
- *
26
- * ## Self-hosted ClickHouse
27
- *
28
- * ```typescript
29
- * clickhouseBackend({
30
- * url: "http://localhost:8123",
31
- * username: "default",
32
- * password: "",
33
- * database: "analytics",
34
- * })
35
- * ```
36
- */
37
-
38
- type ClickHouseBackendConfig = {
39
- /** ClickHouse HTTP API URL */
40
- url: string;
41
- /** Username (default: "default") */
42
- username?: string;
43
- /** Password */
44
- password?: string;
45
- /** Database name (default: "default") */
46
- database?: string;
47
- /** Table name (default: "analytics") */
48
- tableName?: string;
49
- /** Enable async inserts (default: true) */
50
- asyncInsert?: boolean;
51
- /** Enable updates via select+insert (uses ReplacingMergeTree deduplication) */
52
- acceptUpdates?: boolean;
53
- /** Lookback window in minutes for update SELECT queries (default: 60) */
54
- updateLookbackMinutes?: number;
55
- };
56
- declare function clickhouseBackend(config: ClickHouseBackendConfig): NextlyticsBackend;
57
-
58
- export { type ClickHouseBackendConfig, clickhouseBackend };