@nextlytics/core 0.1.0-canary-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/anonymous-user.cjs +118 -0
- package/dist/anonymous-user.d.mts +22 -0
- package/dist/anonymous-user.d.ts +22 -0
- package/dist/anonymous-user.js +94 -0
- package/dist/backends/clickhouse.cjs +110 -0
- package/dist/backends/clickhouse.d.mts +58 -0
- package/dist/backends/clickhouse.d.ts +58 -0
- package/dist/backends/clickhouse.js +92 -0
- package/dist/backends/ga.cjs +207 -0
- package/dist/backends/ga.d.mts +21 -0
- package/dist/backends/ga.d.ts +21 -0
- package/dist/backends/ga.js +183 -0
- package/dist/backends/gtm.cjs +155 -0
- package/dist/backends/gtm.d.mts +11 -0
- package/dist/backends/gtm.d.ts +11 -0
- package/dist/backends/gtm.js +131 -0
- package/dist/backends/lib/db.cjs +150 -0
- package/dist/backends/lib/db.d.mts +121 -0
- package/dist/backends/lib/db.d.ts +121 -0
- package/dist/backends/lib/db.js +119 -0
- package/dist/backends/logging.cjs +45 -0
- package/dist/backends/logging.d.mts +7 -0
- package/dist/backends/logging.d.ts +7 -0
- package/dist/backends/logging.js +21 -0
- package/dist/backends/neon.cjs +84 -0
- package/dist/backends/neon.d.mts +11 -0
- package/dist/backends/neon.d.ts +11 -0
- package/dist/backends/neon.js +66 -0
- package/dist/backends/postgrest.cjs +98 -0
- package/dist/backends/postgrest.d.mts +46 -0
- package/dist/backends/postgrest.d.ts +46 -0
- package/dist/backends/postgrest.js +73 -0
- package/dist/backends/posthog.cjs +120 -0
- package/dist/backends/posthog.d.mts +13 -0
- package/dist/backends/posthog.d.ts +13 -0
- package/dist/backends/posthog.js +96 -0
- package/dist/backends/segment.cjs +112 -0
- package/dist/backends/segment.d.mts +43 -0
- package/dist/backends/segment.d.ts +43 -0
- package/dist/backends/segment.js +88 -0
- package/dist/client.cjs +171 -0
- package/dist/client.d.mts +29 -0
- package/dist/client.d.ts +29 -0
- package/dist/client.js +146 -0
- package/dist/config-helpers.cjs +71 -0
- package/dist/config-helpers.d.mts +16 -0
- package/dist/config-helpers.d.ts +16 -0
- package/dist/config-helpers.js +45 -0
- package/dist/handlers.cjs +123 -0
- package/dist/handlers.d.mts +9 -0
- package/dist/handlers.d.ts +9 -0
- package/dist/handlers.js +99 -0
- package/dist/headers.cjs +41 -0
- package/dist/headers.d.mts +3 -0
- package/dist/headers.d.ts +3 -0
- package/dist/headers.js +17 -0
- package/dist/index.cjs +41 -0
- package/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +12 -0
- package/dist/middleware.cjs +204 -0
- package/dist/middleware.d.mts +10 -0
- package/dist/middleware.d.ts +10 -0
- package/dist/middleware.js +183 -0
- package/dist/pages-router.cjs +45 -0
- package/dist/pages-router.d.mts +45 -0
- package/dist/pages-router.d.ts +45 -0
- package/dist/pages-router.js +21 -0
- package/dist/plugins/vercel-geo.cjs +60 -0
- package/dist/plugins/vercel-geo.d.mts +25 -0
- package/dist/plugins/vercel-geo.d.ts +25 -0
- package/dist/plugins/vercel-geo.js +36 -0
- package/dist/server-component-context.cjs +95 -0
- package/dist/server-component-context.d.mts +30 -0
- package/dist/server-component-context.d.ts +30 -0
- package/dist/server-component-context.js +69 -0
- package/dist/server.cjs +236 -0
- package/dist/server.d.mts +13 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +213 -0
- package/dist/template.cjs +108 -0
- package/dist/template.d.mts +27 -0
- package/dist/template.d.ts +27 -0
- package/dist/template.js +83 -0
- package/dist/types.cjs +16 -0
- package/dist/types.d.mts +216 -0
- package/dist/types.d.ts +216 -0
- package/dist/types.js +0 -0
- package/dist/uitils.cjs +94 -0
- package/dist/uitils.d.mts +22 -0
- package/dist/uitils.d.ts +22 -0
- package/dist/uitils.js +68 -0
- package/package.json +162 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
const GTM_INIT_TEMPLATE_ID = "gtm-init";
|
|
2
|
+
const GTM_PAGEVIEW_TEMPLATE_ID = "gtm-pageview";
|
|
3
|
+
const GTM_EVENT_TEMPLATE_ID = "gtm-event";
|
|
4
|
+
function toSnakeCase(str) {
|
|
5
|
+
return str.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
|
|
6
|
+
}
|
|
7
|
+
function googleTagManagerBackend(opts) {
|
|
8
|
+
const { containerId } = opts;
|
|
9
|
+
return {
|
|
10
|
+
name: "google-tag-manager",
|
|
11
|
+
returnsClientActions: true,
|
|
12
|
+
supportsUpdates: false,
|
|
13
|
+
getClientSideTemplates() {
|
|
14
|
+
return {
|
|
15
|
+
[GTM_INIT_TEMPLATE_ID]: {
|
|
16
|
+
items: [
|
|
17
|
+
{
|
|
18
|
+
body: [
|
|
19
|
+
"window.dataLayer = window.dataLayer || [];",
|
|
20
|
+
"dataLayer.push({{json(initialData)}});",
|
|
21
|
+
"if (!window.google_tag_manager || !window.google_tag_manager['{{containerId}}']) {",
|
|
22
|
+
" (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':",
|
|
23
|
+
" new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],",
|
|
24
|
+
" j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=",
|
|
25
|
+
" 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);",
|
|
26
|
+
" })(window,document,'script','dataLayer','{{containerId}}');",
|
|
27
|
+
"}"
|
|
28
|
+
].join("\n")
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
[GTM_PAGEVIEW_TEMPLATE_ID]: {
|
|
33
|
+
items: [
|
|
34
|
+
{
|
|
35
|
+
body: [
|
|
36
|
+
"window.dataLayer = window.dataLayer || [];",
|
|
37
|
+
"dataLayer.push({{json(pageData)}});"
|
|
38
|
+
].join("\n")
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
[GTM_EVENT_TEMPLATE_ID]: {
|
|
43
|
+
items: [
|
|
44
|
+
{
|
|
45
|
+
body: [
|
|
46
|
+
"window.dataLayer = window.dataLayer || [];",
|
|
47
|
+
"dataLayer.push({{json(eventData)}});"
|
|
48
|
+
].join("\n")
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
async onEvent(event) {
|
|
55
|
+
if (event.type !== "pageView") {
|
|
56
|
+
const eventData = {
|
|
57
|
+
event: toSnakeCase(event.type),
|
|
58
|
+
eventId: event.eventId,
|
|
59
|
+
...event.properties
|
|
60
|
+
};
|
|
61
|
+
if (event.userContext?.userId) {
|
|
62
|
+
eventData.userId = event.userContext.userId;
|
|
63
|
+
}
|
|
64
|
+
if (event.anonymousUserId) {
|
|
65
|
+
eventData.anonymousUserId = event.anonymousUserId;
|
|
66
|
+
}
|
|
67
|
+
const traits2 = event.userContext?.traits;
|
|
68
|
+
if (traits2 && Object.keys(traits2).length > 0) {
|
|
69
|
+
eventData.userTraits = traits2;
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
items: [
|
|
73
|
+
{
|
|
74
|
+
type: "script-template",
|
|
75
|
+
templateId: GTM_EVENT_TEMPLATE_ID,
|
|
76
|
+
params: { eventData }
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const initialData = {};
|
|
82
|
+
if (event.userContext?.userId) {
|
|
83
|
+
initialData.userId = event.userContext.userId;
|
|
84
|
+
}
|
|
85
|
+
if (event.anonymousUserId) {
|
|
86
|
+
initialData.anonymousUserId = event.anonymousUserId;
|
|
87
|
+
}
|
|
88
|
+
const traits = event.userContext?.traits;
|
|
89
|
+
if (traits && Object.keys(traits).length > 0) {
|
|
90
|
+
initialData.userTraits = traits;
|
|
91
|
+
}
|
|
92
|
+
const pageData = {
|
|
93
|
+
event: "page_view",
|
|
94
|
+
eventId: event.eventId,
|
|
95
|
+
page_path: event.serverContext.path,
|
|
96
|
+
page_title: event.properties.title ?? void 0,
|
|
97
|
+
page_location: `${event.serverContext.host}${event.serverContext.path}`
|
|
98
|
+
};
|
|
99
|
+
if (event.clientContext) {
|
|
100
|
+
return {
|
|
101
|
+
items: [
|
|
102
|
+
{
|
|
103
|
+
type: "script-template",
|
|
104
|
+
templateId: GTM_PAGEVIEW_TEMPLATE_ID,
|
|
105
|
+
params: { pageData }
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
items: [
|
|
112
|
+
{
|
|
113
|
+
type: "script-template",
|
|
114
|
+
templateId: GTM_INIT_TEMPLATE_ID,
|
|
115
|
+
params: { containerId, initialData }
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
type: "script-template",
|
|
119
|
+
templateId: GTM_PAGEVIEW_TEMPLATE_ID,
|
|
120
|
+
params: { pageData }
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
updateEvent() {
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
googleTagManagerBackend
|
|
131
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
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 db_exports = {};
|
|
20
|
+
__export(db_exports, {
|
|
21
|
+
eventToJsonRow: () => eventToJsonRow,
|
|
22
|
+
eventToRow: () => eventToRow,
|
|
23
|
+
extractClientContext: () => extractClientContext,
|
|
24
|
+
generateChCreateTableSQL: () => generateChCreateTableSQL,
|
|
25
|
+
generatePgCreateTableSQL: () => generatePgCreateTableSQL,
|
|
26
|
+
isChTableNotFoundError: () => isChTableNotFoundError,
|
|
27
|
+
isPgTableNotFoundError: () => isPgTableNotFoundError,
|
|
28
|
+
tableColumns: () => tableColumns
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(db_exports);
|
|
31
|
+
const tableColumns = [
|
|
32
|
+
{ name: "event_id", pgType: "TEXT PRIMARY KEY", chType: "String" },
|
|
33
|
+
{ name: "parent_event_id", pgType: "TEXT", chType: "Nullable(String)" },
|
|
34
|
+
{ name: "timestamp", pgType: "TIMESTAMPTZ", chType: "DateTime64(3)" },
|
|
35
|
+
{ name: "type", pgType: "TEXT", chType: "LowCardinality(String)" },
|
|
36
|
+
{ name: "anonymous_user_id", pgType: "TEXT", chType: "Nullable(String)" },
|
|
37
|
+
{ name: "user_id", pgType: "TEXT", chType: "Nullable(String)" },
|
|
38
|
+
{ name: "user_email", pgType: "TEXT", chType: "Nullable(String)" },
|
|
39
|
+
{ name: "user_name", pgType: "TEXT", chType: "Nullable(String)" },
|
|
40
|
+
{ name: "host", pgType: "TEXT", chType: "LowCardinality(String)" },
|
|
41
|
+
{ name: "method", pgType: "TEXT", chType: "LowCardinality(String)" },
|
|
42
|
+
{ name: "path", pgType: "TEXT", chType: "String" },
|
|
43
|
+
{ name: "ip", pgType: "INET", chType: "Nullable(IPv6)" },
|
|
44
|
+
{ name: "referer", pgType: "TEXT", chType: "Nullable(String)" },
|
|
45
|
+
{ name: "user_agent", pgType: "TEXT", chType: "Nullable(String)" },
|
|
46
|
+
{ name: "locale", pgType: "TEXT", chType: "LowCardinality(Nullable(String))" },
|
|
47
|
+
{ name: "server_context", pgType: "JSONB", chType: "JSON" },
|
|
48
|
+
{ name: "client_context", pgType: "JSONB", chType: "JSON" },
|
|
49
|
+
{ name: "user_traits", pgType: "JSONB", chType: "JSON" },
|
|
50
|
+
{ name: "properties", pgType: "JSONB", chType: "JSON" }
|
|
51
|
+
];
|
|
52
|
+
function extractClientContext(ctx, serverCtx) {
|
|
53
|
+
const serverReferer = serverCtx?.requestHeaders?.["referer"];
|
|
54
|
+
const serverUserAgent = serverCtx?.requestHeaders?.["user-agent"];
|
|
55
|
+
const serverLocale = serverCtx?.requestHeaders?.["accept-language"]?.split(",")[0];
|
|
56
|
+
if (!ctx) {
|
|
57
|
+
return {
|
|
58
|
+
referer: serverReferer,
|
|
59
|
+
user_agent: serverUserAgent,
|
|
60
|
+
locale: serverLocale,
|
|
61
|
+
rest: null
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const { referer, userAgent, locale, ...rest } = ctx;
|
|
65
|
+
return {
|
|
66
|
+
referer: referer ?? serverReferer,
|
|
67
|
+
user_agent: userAgent ?? serverUserAgent,
|
|
68
|
+
locale: locale ?? serverLocale,
|
|
69
|
+
rest: Object.keys(rest).length > 0 ? rest : null
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function extractCommonFields(event) {
|
|
73
|
+
const { host, method, path, ip, ...serverContextRest } = event.serverContext;
|
|
74
|
+
const clientCtx = extractClientContext(event.clientContext, event.serverContext);
|
|
75
|
+
const userTraitsRest = event.userContext?.traits ? (() => {
|
|
76
|
+
const { email: _email, name: _name, ...rest } = event.userContext.traits;
|
|
77
|
+
return Object.keys(rest).length > 0 ? rest : null;
|
|
78
|
+
})() : null;
|
|
79
|
+
return {
|
|
80
|
+
event_id: event.eventId,
|
|
81
|
+
parent_event_id: event.parentEventId ?? null,
|
|
82
|
+
timestamp: event.collectedAt,
|
|
83
|
+
type: event.type,
|
|
84
|
+
anonymous_user_id: event.anonymousUserId ?? null,
|
|
85
|
+
user_id: event.userContext?.userId ?? null,
|
|
86
|
+
user_email: event.userContext?.traits?.email ?? null,
|
|
87
|
+
user_name: event.userContext?.traits?.name ?? null,
|
|
88
|
+
host,
|
|
89
|
+
method,
|
|
90
|
+
path,
|
|
91
|
+
ip: ip || null,
|
|
92
|
+
referer: clientCtx.referer ?? null,
|
|
93
|
+
user_agent: clientCtx.user_agent ?? null,
|
|
94
|
+
locale: clientCtx.locale ?? null,
|
|
95
|
+
serverContextRest,
|
|
96
|
+
clientContextRest: clientCtx.rest,
|
|
97
|
+
userTraitsRest,
|
|
98
|
+
properties: event.properties
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function eventToRow(event) {
|
|
102
|
+
const { serverContextRest, clientContextRest, userTraitsRest, properties, ...common } = extractCommonFields(event);
|
|
103
|
+
return {
|
|
104
|
+
...common,
|
|
105
|
+
server_context: JSON.stringify(serverContextRest),
|
|
106
|
+
client_context: clientContextRest ? JSON.stringify(clientContextRest) : null,
|
|
107
|
+
user_traits: userTraitsRest ? JSON.stringify(userTraitsRest) : null,
|
|
108
|
+
properties: JSON.stringify(properties)
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function eventToJsonRow(event) {
|
|
112
|
+
const { serverContextRest, clientContextRest, userTraitsRest, ...common } = extractCommonFields(event);
|
|
113
|
+
return {
|
|
114
|
+
...common,
|
|
115
|
+
server_context: serverContextRest,
|
|
116
|
+
client_context: clientContextRest ?? {},
|
|
117
|
+
user_traits: userTraitsRest ?? {}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function generatePgCreateTableSQL(tableName) {
|
|
121
|
+
const pk = tableColumns[0];
|
|
122
|
+
const alters = tableColumns.slice(1).map((col) => `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS ${col.name} ${col.pgType};`).join("\n");
|
|
123
|
+
return `CREATE TABLE IF NOT EXISTS ${tableName} (${pk.name} ${pk.pgType});
|
|
124
|
+
${alters}`;
|
|
125
|
+
}
|
|
126
|
+
function isPgTableNotFoundError(err) {
|
|
127
|
+
return err instanceof Error && err.message.includes("does not exist");
|
|
128
|
+
}
|
|
129
|
+
function generateChCreateTableSQL(database, tableName) {
|
|
130
|
+
const fullTable = `${database}.${tableName}`;
|
|
131
|
+
const createCols = tableColumns.filter((c) => c.name === "event_id" || c.name === "timestamp").map((c) => `${c.name} ${c.chType}`).join(", ");
|
|
132
|
+
const create = `CREATE TABLE IF NOT EXISTS ${fullTable} (${createCols}) ENGINE = ReplacingMergeTree() PARTITION BY toYYYYMM(timestamp) ORDER BY event_id;`;
|
|
133
|
+
const alters = tableColumns.filter((c) => c.name !== "event_id" && c.name !== "timestamp").map((c) => `ALTER TABLE ${fullTable} ADD COLUMN IF NOT EXISTS ${c.name} ${c.chType};`).join("\n");
|
|
134
|
+
return `${create}
|
|
135
|
+
${alters}`;
|
|
136
|
+
}
|
|
137
|
+
function isChTableNotFoundError(text) {
|
|
138
|
+
return text.includes("UNKNOWN_TABLE") || text.includes("doesn't exist");
|
|
139
|
+
}
|
|
140
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
141
|
+
0 && (module.exports = {
|
|
142
|
+
eventToJsonRow,
|
|
143
|
+
eventToRow,
|
|
144
|
+
extractClientContext,
|
|
145
|
+
generateChCreateTableSQL,
|
|
146
|
+
generatePgCreateTableSQL,
|
|
147
|
+
isChTableNotFoundError,
|
|
148
|
+
isPgTableNotFoundError,
|
|
149
|
+
tableColumns
|
|
150
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { NextlyticsEvent, ClientContext, ServerEventContext } from '../../types.mjs';
|
|
2
|
+
import 'next/dist/server/web/spec-extension/cookies';
|
|
3
|
+
import 'next/server';
|
|
4
|
+
|
|
5
|
+
declare const tableColumns: readonly [{
|
|
6
|
+
readonly name: "event_id";
|
|
7
|
+
readonly pgType: "TEXT PRIMARY KEY";
|
|
8
|
+
readonly chType: "String";
|
|
9
|
+
}, {
|
|
10
|
+
readonly name: "parent_event_id";
|
|
11
|
+
readonly pgType: "TEXT";
|
|
12
|
+
readonly chType: "Nullable(String)";
|
|
13
|
+
}, {
|
|
14
|
+
readonly name: "timestamp";
|
|
15
|
+
readonly pgType: "TIMESTAMPTZ";
|
|
16
|
+
readonly chType: "DateTime64(3)";
|
|
17
|
+
}, {
|
|
18
|
+
readonly name: "type";
|
|
19
|
+
readonly pgType: "TEXT";
|
|
20
|
+
readonly chType: "LowCardinality(String)";
|
|
21
|
+
}, {
|
|
22
|
+
readonly name: "anonymous_user_id";
|
|
23
|
+
readonly pgType: "TEXT";
|
|
24
|
+
readonly chType: "Nullable(String)";
|
|
25
|
+
}, {
|
|
26
|
+
readonly name: "user_id";
|
|
27
|
+
readonly pgType: "TEXT";
|
|
28
|
+
readonly chType: "Nullable(String)";
|
|
29
|
+
}, {
|
|
30
|
+
readonly name: "user_email";
|
|
31
|
+
readonly pgType: "TEXT";
|
|
32
|
+
readonly chType: "Nullable(String)";
|
|
33
|
+
}, {
|
|
34
|
+
readonly name: "user_name";
|
|
35
|
+
readonly pgType: "TEXT";
|
|
36
|
+
readonly chType: "Nullable(String)";
|
|
37
|
+
}, {
|
|
38
|
+
readonly name: "host";
|
|
39
|
+
readonly pgType: "TEXT";
|
|
40
|
+
readonly chType: "LowCardinality(String)";
|
|
41
|
+
}, {
|
|
42
|
+
readonly name: "method";
|
|
43
|
+
readonly pgType: "TEXT";
|
|
44
|
+
readonly chType: "LowCardinality(String)";
|
|
45
|
+
}, {
|
|
46
|
+
readonly name: "path";
|
|
47
|
+
readonly pgType: "TEXT";
|
|
48
|
+
readonly chType: "String";
|
|
49
|
+
}, {
|
|
50
|
+
readonly name: "ip";
|
|
51
|
+
readonly pgType: "INET";
|
|
52
|
+
readonly chType: "Nullable(IPv6)";
|
|
53
|
+
}, {
|
|
54
|
+
readonly name: "referer";
|
|
55
|
+
readonly pgType: "TEXT";
|
|
56
|
+
readonly chType: "Nullable(String)";
|
|
57
|
+
}, {
|
|
58
|
+
readonly name: "user_agent";
|
|
59
|
+
readonly pgType: "TEXT";
|
|
60
|
+
readonly chType: "Nullable(String)";
|
|
61
|
+
}, {
|
|
62
|
+
readonly name: "locale";
|
|
63
|
+
readonly pgType: "TEXT";
|
|
64
|
+
readonly chType: "LowCardinality(Nullable(String))";
|
|
65
|
+
}, {
|
|
66
|
+
readonly name: "server_context";
|
|
67
|
+
readonly pgType: "JSONB";
|
|
68
|
+
readonly chType: "JSON";
|
|
69
|
+
}, {
|
|
70
|
+
readonly name: "client_context";
|
|
71
|
+
readonly pgType: "JSONB";
|
|
72
|
+
readonly chType: "JSON";
|
|
73
|
+
}, {
|
|
74
|
+
readonly name: "user_traits";
|
|
75
|
+
readonly pgType: "JSONB";
|
|
76
|
+
readonly chType: "JSON";
|
|
77
|
+
}, {
|
|
78
|
+
readonly name: "properties";
|
|
79
|
+
readonly pgType: "JSONB";
|
|
80
|
+
readonly chType: "JSON";
|
|
81
|
+
}];
|
|
82
|
+
type ColumnName = (typeof tableColumns)[number]["name"];
|
|
83
|
+
type ClientContextColumns = {
|
|
84
|
+
referer: string | undefined;
|
|
85
|
+
user_agent: string | undefined;
|
|
86
|
+
locale: string | undefined;
|
|
87
|
+
rest: Record<string, unknown> | null;
|
|
88
|
+
};
|
|
89
|
+
declare function extractClientContext(ctx: ClientContext | undefined, serverCtx?: ServerEventContext): ClientContextColumns;
|
|
90
|
+
/** For PostgreSQL - JSON fields as strings */
|
|
91
|
+
declare function eventToRow(event: NextlyticsEvent): Record<ColumnName, unknown>;
|
|
92
|
+
/** For ClickHouse/PostgREST - JSON fields as objects */
|
|
93
|
+
declare function eventToJsonRow(event: NextlyticsEvent): Record<ColumnName, unknown>;
|
|
94
|
+
declare function generatePgCreateTableSQL(tableName: string): string;
|
|
95
|
+
declare function isPgTableNotFoundError(err: unknown): boolean;
|
|
96
|
+
declare function generateChCreateTableSQL(database: string, tableName: string): string;
|
|
97
|
+
declare function isChTableNotFoundError(text: string): boolean;
|
|
98
|
+
/** Row type returned from analytics table queries */
|
|
99
|
+
interface AnalyticsEventRow {
|
|
100
|
+
event_id: string;
|
|
101
|
+
parent_event_id: string | null;
|
|
102
|
+
timestamp: string;
|
|
103
|
+
type: string;
|
|
104
|
+
anonymous_user_id: string | null;
|
|
105
|
+
user_id: string | null;
|
|
106
|
+
user_email: string | null;
|
|
107
|
+
user_name: string | null;
|
|
108
|
+
host: string;
|
|
109
|
+
method: string;
|
|
110
|
+
path: string;
|
|
111
|
+
ip: string | null;
|
|
112
|
+
referer: string | null;
|
|
113
|
+
user_agent: string | null;
|
|
114
|
+
locale: string | null;
|
|
115
|
+
server_context: Record<string, unknown>;
|
|
116
|
+
client_context: Record<string, unknown>;
|
|
117
|
+
user_traits: Record<string, unknown>;
|
|
118
|
+
properties: Record<string, unknown>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export { type AnalyticsEventRow, type ClientContextColumns, type ColumnName, eventToJsonRow, eventToRow, extractClientContext, generateChCreateTableSQL, generatePgCreateTableSQL, isChTableNotFoundError, isPgTableNotFoundError, tableColumns };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { NextlyticsEvent, ClientContext, ServerEventContext } from '../../types.js';
|
|
2
|
+
import 'next/dist/server/web/spec-extension/cookies';
|
|
3
|
+
import 'next/server';
|
|
4
|
+
|
|
5
|
+
declare const tableColumns: readonly [{
|
|
6
|
+
readonly name: "event_id";
|
|
7
|
+
readonly pgType: "TEXT PRIMARY KEY";
|
|
8
|
+
readonly chType: "String";
|
|
9
|
+
}, {
|
|
10
|
+
readonly name: "parent_event_id";
|
|
11
|
+
readonly pgType: "TEXT";
|
|
12
|
+
readonly chType: "Nullable(String)";
|
|
13
|
+
}, {
|
|
14
|
+
readonly name: "timestamp";
|
|
15
|
+
readonly pgType: "TIMESTAMPTZ";
|
|
16
|
+
readonly chType: "DateTime64(3)";
|
|
17
|
+
}, {
|
|
18
|
+
readonly name: "type";
|
|
19
|
+
readonly pgType: "TEXT";
|
|
20
|
+
readonly chType: "LowCardinality(String)";
|
|
21
|
+
}, {
|
|
22
|
+
readonly name: "anonymous_user_id";
|
|
23
|
+
readonly pgType: "TEXT";
|
|
24
|
+
readonly chType: "Nullable(String)";
|
|
25
|
+
}, {
|
|
26
|
+
readonly name: "user_id";
|
|
27
|
+
readonly pgType: "TEXT";
|
|
28
|
+
readonly chType: "Nullable(String)";
|
|
29
|
+
}, {
|
|
30
|
+
readonly name: "user_email";
|
|
31
|
+
readonly pgType: "TEXT";
|
|
32
|
+
readonly chType: "Nullable(String)";
|
|
33
|
+
}, {
|
|
34
|
+
readonly name: "user_name";
|
|
35
|
+
readonly pgType: "TEXT";
|
|
36
|
+
readonly chType: "Nullable(String)";
|
|
37
|
+
}, {
|
|
38
|
+
readonly name: "host";
|
|
39
|
+
readonly pgType: "TEXT";
|
|
40
|
+
readonly chType: "LowCardinality(String)";
|
|
41
|
+
}, {
|
|
42
|
+
readonly name: "method";
|
|
43
|
+
readonly pgType: "TEXT";
|
|
44
|
+
readonly chType: "LowCardinality(String)";
|
|
45
|
+
}, {
|
|
46
|
+
readonly name: "path";
|
|
47
|
+
readonly pgType: "TEXT";
|
|
48
|
+
readonly chType: "String";
|
|
49
|
+
}, {
|
|
50
|
+
readonly name: "ip";
|
|
51
|
+
readonly pgType: "INET";
|
|
52
|
+
readonly chType: "Nullable(IPv6)";
|
|
53
|
+
}, {
|
|
54
|
+
readonly name: "referer";
|
|
55
|
+
readonly pgType: "TEXT";
|
|
56
|
+
readonly chType: "Nullable(String)";
|
|
57
|
+
}, {
|
|
58
|
+
readonly name: "user_agent";
|
|
59
|
+
readonly pgType: "TEXT";
|
|
60
|
+
readonly chType: "Nullable(String)";
|
|
61
|
+
}, {
|
|
62
|
+
readonly name: "locale";
|
|
63
|
+
readonly pgType: "TEXT";
|
|
64
|
+
readonly chType: "LowCardinality(Nullable(String))";
|
|
65
|
+
}, {
|
|
66
|
+
readonly name: "server_context";
|
|
67
|
+
readonly pgType: "JSONB";
|
|
68
|
+
readonly chType: "JSON";
|
|
69
|
+
}, {
|
|
70
|
+
readonly name: "client_context";
|
|
71
|
+
readonly pgType: "JSONB";
|
|
72
|
+
readonly chType: "JSON";
|
|
73
|
+
}, {
|
|
74
|
+
readonly name: "user_traits";
|
|
75
|
+
readonly pgType: "JSONB";
|
|
76
|
+
readonly chType: "JSON";
|
|
77
|
+
}, {
|
|
78
|
+
readonly name: "properties";
|
|
79
|
+
readonly pgType: "JSONB";
|
|
80
|
+
readonly chType: "JSON";
|
|
81
|
+
}];
|
|
82
|
+
type ColumnName = (typeof tableColumns)[number]["name"];
|
|
83
|
+
type ClientContextColumns = {
|
|
84
|
+
referer: string | undefined;
|
|
85
|
+
user_agent: string | undefined;
|
|
86
|
+
locale: string | undefined;
|
|
87
|
+
rest: Record<string, unknown> | null;
|
|
88
|
+
};
|
|
89
|
+
declare function extractClientContext(ctx: ClientContext | undefined, serverCtx?: ServerEventContext): ClientContextColumns;
|
|
90
|
+
/** For PostgreSQL - JSON fields as strings */
|
|
91
|
+
declare function eventToRow(event: NextlyticsEvent): Record<ColumnName, unknown>;
|
|
92
|
+
/** For ClickHouse/PostgREST - JSON fields as objects */
|
|
93
|
+
declare function eventToJsonRow(event: NextlyticsEvent): Record<ColumnName, unknown>;
|
|
94
|
+
declare function generatePgCreateTableSQL(tableName: string): string;
|
|
95
|
+
declare function isPgTableNotFoundError(err: unknown): boolean;
|
|
96
|
+
declare function generateChCreateTableSQL(database: string, tableName: string): string;
|
|
97
|
+
declare function isChTableNotFoundError(text: string): boolean;
|
|
98
|
+
/** Row type returned from analytics table queries */
|
|
99
|
+
interface AnalyticsEventRow {
|
|
100
|
+
event_id: string;
|
|
101
|
+
parent_event_id: string | null;
|
|
102
|
+
timestamp: string;
|
|
103
|
+
type: string;
|
|
104
|
+
anonymous_user_id: string | null;
|
|
105
|
+
user_id: string | null;
|
|
106
|
+
user_email: string | null;
|
|
107
|
+
user_name: string | null;
|
|
108
|
+
host: string;
|
|
109
|
+
method: string;
|
|
110
|
+
path: string;
|
|
111
|
+
ip: string | null;
|
|
112
|
+
referer: string | null;
|
|
113
|
+
user_agent: string | null;
|
|
114
|
+
locale: string | null;
|
|
115
|
+
server_context: Record<string, unknown>;
|
|
116
|
+
client_context: Record<string, unknown>;
|
|
117
|
+
user_traits: Record<string, unknown>;
|
|
118
|
+
properties: Record<string, unknown>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export { type AnalyticsEventRow, type ClientContextColumns, type ColumnName, eventToJsonRow, eventToRow, extractClientContext, generateChCreateTableSQL, generatePgCreateTableSQL, isChTableNotFoundError, isPgTableNotFoundError, tableColumns };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const tableColumns = [
|
|
2
|
+
{ name: "event_id", pgType: "TEXT PRIMARY KEY", chType: "String" },
|
|
3
|
+
{ name: "parent_event_id", pgType: "TEXT", chType: "Nullable(String)" },
|
|
4
|
+
{ name: "timestamp", pgType: "TIMESTAMPTZ", chType: "DateTime64(3)" },
|
|
5
|
+
{ name: "type", pgType: "TEXT", chType: "LowCardinality(String)" },
|
|
6
|
+
{ name: "anonymous_user_id", pgType: "TEXT", chType: "Nullable(String)" },
|
|
7
|
+
{ name: "user_id", pgType: "TEXT", chType: "Nullable(String)" },
|
|
8
|
+
{ name: "user_email", pgType: "TEXT", chType: "Nullable(String)" },
|
|
9
|
+
{ name: "user_name", pgType: "TEXT", chType: "Nullable(String)" },
|
|
10
|
+
{ name: "host", pgType: "TEXT", chType: "LowCardinality(String)" },
|
|
11
|
+
{ name: "method", pgType: "TEXT", chType: "LowCardinality(String)" },
|
|
12
|
+
{ name: "path", pgType: "TEXT", chType: "String" },
|
|
13
|
+
{ name: "ip", pgType: "INET", chType: "Nullable(IPv6)" },
|
|
14
|
+
{ name: "referer", pgType: "TEXT", chType: "Nullable(String)" },
|
|
15
|
+
{ name: "user_agent", pgType: "TEXT", chType: "Nullable(String)" },
|
|
16
|
+
{ name: "locale", pgType: "TEXT", chType: "LowCardinality(Nullable(String))" },
|
|
17
|
+
{ name: "server_context", pgType: "JSONB", chType: "JSON" },
|
|
18
|
+
{ name: "client_context", pgType: "JSONB", chType: "JSON" },
|
|
19
|
+
{ name: "user_traits", pgType: "JSONB", chType: "JSON" },
|
|
20
|
+
{ name: "properties", pgType: "JSONB", chType: "JSON" }
|
|
21
|
+
];
|
|
22
|
+
function extractClientContext(ctx, serverCtx) {
|
|
23
|
+
const serverReferer = serverCtx?.requestHeaders?.["referer"];
|
|
24
|
+
const serverUserAgent = serverCtx?.requestHeaders?.["user-agent"];
|
|
25
|
+
const serverLocale = serverCtx?.requestHeaders?.["accept-language"]?.split(",")[0];
|
|
26
|
+
if (!ctx) {
|
|
27
|
+
return {
|
|
28
|
+
referer: serverReferer,
|
|
29
|
+
user_agent: serverUserAgent,
|
|
30
|
+
locale: serverLocale,
|
|
31
|
+
rest: null
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const { referer, userAgent, locale, ...rest } = ctx;
|
|
35
|
+
return {
|
|
36
|
+
referer: referer ?? serverReferer,
|
|
37
|
+
user_agent: userAgent ?? serverUserAgent,
|
|
38
|
+
locale: locale ?? serverLocale,
|
|
39
|
+
rest: Object.keys(rest).length > 0 ? rest : null
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function extractCommonFields(event) {
|
|
43
|
+
const { host, method, path, ip, ...serverContextRest } = event.serverContext;
|
|
44
|
+
const clientCtx = extractClientContext(event.clientContext, event.serverContext);
|
|
45
|
+
const userTraitsRest = event.userContext?.traits ? (() => {
|
|
46
|
+
const { email: _email, name: _name, ...rest } = event.userContext.traits;
|
|
47
|
+
return Object.keys(rest).length > 0 ? rest : null;
|
|
48
|
+
})() : null;
|
|
49
|
+
return {
|
|
50
|
+
event_id: event.eventId,
|
|
51
|
+
parent_event_id: event.parentEventId ?? null,
|
|
52
|
+
timestamp: event.collectedAt,
|
|
53
|
+
type: event.type,
|
|
54
|
+
anonymous_user_id: event.anonymousUserId ?? null,
|
|
55
|
+
user_id: event.userContext?.userId ?? null,
|
|
56
|
+
user_email: event.userContext?.traits?.email ?? null,
|
|
57
|
+
user_name: event.userContext?.traits?.name ?? null,
|
|
58
|
+
host,
|
|
59
|
+
method,
|
|
60
|
+
path,
|
|
61
|
+
ip: ip || null,
|
|
62
|
+
referer: clientCtx.referer ?? null,
|
|
63
|
+
user_agent: clientCtx.user_agent ?? null,
|
|
64
|
+
locale: clientCtx.locale ?? null,
|
|
65
|
+
serverContextRest,
|
|
66
|
+
clientContextRest: clientCtx.rest,
|
|
67
|
+
userTraitsRest,
|
|
68
|
+
properties: event.properties
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function eventToRow(event) {
|
|
72
|
+
const { serverContextRest, clientContextRest, userTraitsRest, properties, ...common } = extractCommonFields(event);
|
|
73
|
+
return {
|
|
74
|
+
...common,
|
|
75
|
+
server_context: JSON.stringify(serverContextRest),
|
|
76
|
+
client_context: clientContextRest ? JSON.stringify(clientContextRest) : null,
|
|
77
|
+
user_traits: userTraitsRest ? JSON.stringify(userTraitsRest) : null,
|
|
78
|
+
properties: JSON.stringify(properties)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function eventToJsonRow(event) {
|
|
82
|
+
const { serverContextRest, clientContextRest, userTraitsRest, ...common } = extractCommonFields(event);
|
|
83
|
+
return {
|
|
84
|
+
...common,
|
|
85
|
+
server_context: serverContextRest,
|
|
86
|
+
client_context: clientContextRest ?? {},
|
|
87
|
+
user_traits: userTraitsRest ?? {}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function generatePgCreateTableSQL(tableName) {
|
|
91
|
+
const pk = tableColumns[0];
|
|
92
|
+
const alters = tableColumns.slice(1).map((col) => `ALTER TABLE ${tableName} ADD COLUMN IF NOT EXISTS ${col.name} ${col.pgType};`).join("\n");
|
|
93
|
+
return `CREATE TABLE IF NOT EXISTS ${tableName} (${pk.name} ${pk.pgType});
|
|
94
|
+
${alters}`;
|
|
95
|
+
}
|
|
96
|
+
function isPgTableNotFoundError(err) {
|
|
97
|
+
return err instanceof Error && err.message.includes("does not exist");
|
|
98
|
+
}
|
|
99
|
+
function generateChCreateTableSQL(database, tableName) {
|
|
100
|
+
const fullTable = `${database}.${tableName}`;
|
|
101
|
+
const createCols = tableColumns.filter((c) => c.name === "event_id" || c.name === "timestamp").map((c) => `${c.name} ${c.chType}`).join(", ");
|
|
102
|
+
const create = `CREATE TABLE IF NOT EXISTS ${fullTable} (${createCols}) ENGINE = ReplacingMergeTree() PARTITION BY toYYYYMM(timestamp) ORDER BY event_id;`;
|
|
103
|
+
const alters = tableColumns.filter((c) => c.name !== "event_id" && c.name !== "timestamp").map((c) => `ALTER TABLE ${fullTable} ADD COLUMN IF NOT EXISTS ${c.name} ${c.chType};`).join("\n");
|
|
104
|
+
return `${create}
|
|
105
|
+
${alters}`;
|
|
106
|
+
}
|
|
107
|
+
function isChTableNotFoundError(text) {
|
|
108
|
+
return text.includes("UNKNOWN_TABLE") || text.includes("doesn't exist");
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
eventToJsonRow,
|
|
112
|
+
eventToRow,
|
|
113
|
+
extractClientContext,
|
|
114
|
+
generateChCreateTableSQL,
|
|
115
|
+
generatePgCreateTableSQL,
|
|
116
|
+
isChTableNotFoundError,
|
|
117
|
+
isPgTableNotFoundError,
|
|
118
|
+
tableColumns
|
|
119
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
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 logging_exports = {};
|
|
20
|
+
__export(logging_exports, {
|
|
21
|
+
loggingBackend: () => loggingBackend
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(logging_exports);
|
|
24
|
+
function loggingBackend() {
|
|
25
|
+
return {
|
|
26
|
+
name: "logging",
|
|
27
|
+
supportsUpdates: true,
|
|
28
|
+
async onEvent(event) {
|
|
29
|
+
const { type, eventId, serverContext, ...rest } = event;
|
|
30
|
+
const method = serverContext?.method || "";
|
|
31
|
+
const path = serverContext?.path || "";
|
|
32
|
+
const route = method && path ? `${method} ${path}` : "";
|
|
33
|
+
console.log(`[Nextlytics Log] ${type}${route ? ` ${route}` : ""} (${eventId})`);
|
|
34
|
+
console.log(JSON.stringify({ serverContext, ...rest }, null, 2));
|
|
35
|
+
},
|
|
36
|
+
updateEvent(eventId, patch) {
|
|
37
|
+
console.log(`[Nextlytics Log] Update ${eventId}`);
|
|
38
|
+
console.log(JSON.stringify(patch, null, 2));
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
43
|
+
0 && (module.exports = {
|
|
44
|
+
loggingBackend
|
|
45
|
+
});
|