locallytics 0.1.9 → 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/README.md +36 -53
- package/dist/adapters/custom.d.ts +3 -0
- package/dist/adapters/custom.d.ts.map +1 -0
- package/dist/adapters/sqlite.d.ts +3 -0
- package/dist/adapters/sqlite.d.ts.map +1 -0
- package/dist/create-locallytics.d.ts +3 -0
- package/dist/create-locallytics.d.ts.map +1 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +254 -7
- package/dist/index.js.map +15 -1
- package/dist/normalize.d.ts +5 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/session.d.ts +4 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/sql.d.ts +3 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +27 -46
- package/dist/client/LocallyticsGrabber.d.ts +0 -30
- package/dist/client/LocallyticsGrabber.d.ts.map +0 -1
- package/dist/client/LocallyticsGrabber.js +0 -71
- package/dist/client/LocallyticsGrabber.js.map +0 -1
- package/dist/client/batcher.d.ts +0 -48
- package/dist/client/batcher.d.ts.map +0 -1
- package/dist/client/batcher.js +0 -134
- package/dist/client/batcher.js.map +0 -1
- package/dist/client/tracker.d.ts +0 -18
- package/dist/client/tracker.d.ts.map +0 -1
- package/dist/client/tracker.js +0 -101
- package/dist/client/tracker.js.map +0 -1
- package/dist/db/factory.d.ts +0 -11
- package/dist/db/factory.d.ts.map +0 -1
- package/dist/db/factory.js +0 -46
- package/dist/db/factory.js.map +0 -1
- package/dist/db/mysql.d.ts +0 -17
- package/dist/db/mysql.d.ts.map +0 -1
- package/dist/db/mysql.js +0 -144
- package/dist/db/mysql.js.map +0 -1
- package/dist/db/postgres.d.ts +0 -16
- package/dist/db/postgres.d.ts.map +0 -1
- package/dist/db/postgres.js +0 -142
- package/dist/db/postgres.js.map +0 -1
- package/dist/db/sqlite.d.ts +0 -17
- package/dist/db/sqlite.d.ts.map +0 -1
- package/dist/db/sqlite.js +0 -130
- package/dist/db/sqlite.js.map +0 -1
- package/dist/server/handlers.d.ts +0 -10
- package/dist/server/handlers.d.ts.map +0 -1
- package/dist/server/handlers.js +0 -96
- package/dist/server/handlers.js.map +0 -1
- package/dist/server/index.d.ts +0 -42
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/index.js +0 -96
- package/dist/server/index.js.map +0 -1
- package/dist/server/queries.d.ts +0 -10
- package/dist/server/queries.d.ts.map +0 -1
- package/dist/server/queries.js +0 -24
- package/dist/server/queries.js.map +0 -1
- package/dist/server/validator.d.ts +0 -32
- package/dist/server/validator.d.ts.map +0 -1
- package/dist/server/validator.js +0 -129
- package/dist/server/validator.js.map +0 -1
- package/dist/types/index.d.ts +0 -135
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -24
- package/dist/types/index.js.map +0 -1
- package/dist/utils/hash.d.ts +0 -16
- package/dist/utils/hash.d.ts.map +0 -1
- package/dist/utils/hash.js +0 -36
- package/dist/utils/hash.js.map +0 -1
- package/dist/utils/rate-limit.d.ts +0 -32
- package/dist/utils/rate-limit.d.ts.map +0 -1
- package/dist/utils/rate-limit.js +0 -73
- package/dist/utils/rate-limit.js.map +0 -1
- package/src/db/schema-mysql.sql +0 -17
- package/src/db/schema-sqlite.sql +0 -29
- package/src/db/schema.sql +0 -35
package/README.md
CHANGED
|
@@ -1,73 +1,56 @@
|
|
|
1
|
-
#
|
|
1
|
+
# locallytics
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`locallytics` is a lightweight, framework-agnostic analytics runtime.
|
|
4
|
+
This release currently supports SQLite.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
+
## Install
|
|
6
7
|
|
|
7
|
-
```
|
|
8
|
-
npm install locallytics
|
|
8
|
+
```npm
|
|
9
|
+
npm install locallytics better-sqlite3
|
|
9
10
|
```
|
|
10
11
|
|
|
11
|
-
##
|
|
12
|
+
## Quickstart
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
Create one shared Locallytics instance.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
```ts
|
|
17
|
+
import { createLocallytics } from "locallytics";
|
|
18
|
+
import Database from "better-sqlite3";
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
export const locallytics = createLocallytics({
|
|
21
|
+
database: new Database("database.sqlite"),
|
|
22
|
+
sessionTracking: true,
|
|
23
|
+
});
|
|
19
24
|
```
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
Track custom events.
|
|
22
27
|
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
```ts
|
|
29
|
+
await locallytics.track("button_click", { button: "cta" });
|
|
30
|
+
```
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
database: process.env.DATABASE_URL!,
|
|
29
|
-
});
|
|
32
|
+
Read stored events.
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
```ts
|
|
35
|
+
const events = await locallytics.listEvents({ limit: 25 });
|
|
32
36
|
```
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```tsx
|
|
37
|
-
// app/layout.tsx
|
|
38
|
-
import { LocallyticsGrabber } from "locallytics";
|
|
39
|
-
|
|
40
|
-
export default function RootLayout({ children }) {
|
|
41
|
-
return (
|
|
42
|
-
<html>
|
|
43
|
-
<body>
|
|
44
|
-
{children}
|
|
45
|
-
<LocallyticsGrabber />
|
|
46
|
-
</body>
|
|
47
|
-
</html>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
```
|
|
38
|
+
## API
|
|
51
39
|
|
|
52
|
-
|
|
40
|
+
- `createLocallytics(config)`
|
|
41
|
+
- `locallytics.track(type, metadata?)`
|
|
42
|
+
- `locallytics.trackPageView(route?)`
|
|
43
|
+
- `locallytics.listEvents(options?)`
|
|
44
|
+
- `locallytics.setUserId(userId)`
|
|
45
|
+
- `locallytics.getUserId()`
|
|
46
|
+
- `locallytics.getSessionId()`
|
|
53
47
|
|
|
54
|
-
|
|
55
|
-
// app/page.tsx
|
|
56
|
-
import { LocallyticsData } from "locallytics";
|
|
48
|
+
## CLI
|
|
57
49
|
|
|
58
|
-
|
|
59
|
-
const data = await LocallyticsData(); // Simple! No arguments.
|
|
50
|
+
Use `@locallytics/cli` to generate and run migrations:
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
```npm
|
|
53
|
+
npm install -D @locallytics/cli
|
|
54
|
+
npx locallytics generate
|
|
55
|
+
npx locallytics migrate
|
|
63
56
|
```
|
|
64
|
-
|
|
65
|
-
## Features
|
|
66
|
-
|
|
67
|
-
- **Privacy**: No cookies, hashed IPs, supports Do Not Track.
|
|
68
|
-
- **Simplicity**: Zero-config data fetching.
|
|
69
|
-
- **Speed**: Lightweight (< 5KB) and fast.
|
|
70
|
-
|
|
71
|
-
## License
|
|
72
|
-
|
|
73
|
-
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/adapters/custom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,CAE7E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EAIf,cAAc,EACf,MAAM,UAAU,CAAC;AAmDlB,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,GAAG,eAAe,CAkF7E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-locallytics.d.ts","sourceRoot":"","sources":["../src/create-locallytics.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,WAAW,EACX,iBAAiB,EAKlB,MAAM,SAAS,CAAC;AA8DjB,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CA4DxE"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export type { AnalyticsDataOptions, AnalyticsDB, AnalyticsResult, DailyStats, DateRange, LocallyticsConfig, PageStats, PageviewEvent, } from "./types/index.js";
|
|
5
|
-
export { LocallyticsError } from "./types/index.js";
|
|
1
|
+
export { createLocallytics } from "./create-locallytics";
|
|
2
|
+
export { EVENTS_TABLE_NAME, SQLITE_CREATE_EVENTS_TABLE_SQL } from "./sql";
|
|
3
|
+
export type { DatabaseAdapter, EventMetadata, ListEventsOptions, Locallytics, LocallyticsConfig, LocallyticsEvent, LocallyticsEventInput, SqliteDatabase, SqliteStatement, SupportedDatabase, } from "./types";
|
|
6
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,8BAA8B,EAAE,MAAM,OAAO,CAAC;AAC1E,YAAY,EACV,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,254 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
// src/adapters/custom.ts
|
|
2
|
+
function createCustomAdapter(adapter) {
|
|
3
|
+
return adapter;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/normalize.ts
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === "object" && value !== null;
|
|
9
|
+
}
|
|
10
|
+
function parseMetadata(value) {
|
|
11
|
+
if (value == null) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (isRecord(value)) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
if (typeof value !== "string") {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(value);
|
|
22
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
23
|
+
} catch {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function stringifyMetadata(metadata) {
|
|
28
|
+
if (!metadata) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return JSON.stringify(metadata);
|
|
32
|
+
}
|
|
33
|
+
function normalizeEventRow(row) {
|
|
34
|
+
return {
|
|
35
|
+
id: row.id,
|
|
36
|
+
type: String(row.type ?? ""),
|
|
37
|
+
timestamp: Number(row.timestamp ?? Date.now()),
|
|
38
|
+
sessionId: row.session_id ?? row.sessionId,
|
|
39
|
+
userId: row.user_id ?? row.userId,
|
|
40
|
+
metadata: parseMetadata(row.metadata)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/sql.ts
|
|
45
|
+
var EVENTS_TABLE_NAME = "locallytics_events";
|
|
46
|
+
var SQLITE_CREATE_EVENTS_TABLE_SQL = `
|
|
47
|
+
CREATE TABLE IF NOT EXISTS locallytics_events (
|
|
48
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
+
type TEXT NOT NULL,
|
|
50
|
+
timestamp INTEGER NOT NULL,
|
|
51
|
+
session_id TEXT,
|
|
52
|
+
user_id TEXT,
|
|
53
|
+
metadata TEXT
|
|
54
|
+
);
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
// src/adapters/sqlite.ts
|
|
58
|
+
function toRow(value) {
|
|
59
|
+
if (typeof value === "object" && value !== null) {
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
function toInsertId(value) {
|
|
65
|
+
if (!value || typeof value !== "object") {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const row = value;
|
|
69
|
+
const id = row.lastInsertRowid;
|
|
70
|
+
if (typeof id === "number") {
|
|
71
|
+
return id;
|
|
72
|
+
}
|
|
73
|
+
if (typeof id === "bigint") {
|
|
74
|
+
return Number(id);
|
|
75
|
+
}
|
|
76
|
+
if (typeof id === "string" && id.length > 0) {
|
|
77
|
+
return Number(id);
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
function toSafeLimit(limit) {
|
|
82
|
+
if (limit == null) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const normalized = Math.floor(limit);
|
|
86
|
+
return normalized > 0 ? normalized : 1;
|
|
87
|
+
}
|
|
88
|
+
function toSqlResultRows(database, sql, params) {
|
|
89
|
+
const statement = database.prepare(sql);
|
|
90
|
+
return statement.all(...params).map((row) => normalizeEventRow(toRow(row)));
|
|
91
|
+
}
|
|
92
|
+
function createSqliteAdapter(database) {
|
|
93
|
+
let initialized = false;
|
|
94
|
+
function ensureInitialized() {
|
|
95
|
+
if (initialized) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
database.prepare(SQLITE_CREATE_EVENTS_TABLE_SQL).run();
|
|
99
|
+
initialized = true;
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
initialize: ensureInitialized,
|
|
103
|
+
async insertEvent(event) {
|
|
104
|
+
ensureInitialized();
|
|
105
|
+
const result = database.prepare(`
|
|
106
|
+
INSERT INTO locallytics_events (type, timestamp, session_id, user_id, metadata)
|
|
107
|
+
VALUES (?, ?, ?, ?, ?)
|
|
108
|
+
`).run(event.type, event.timestamp, event.sessionId ?? null, event.userId ?? null, stringifyMetadata(event.metadata));
|
|
109
|
+
const id = toInsertId(result);
|
|
110
|
+
if (id == null) {
|
|
111
|
+
return { ...event };
|
|
112
|
+
}
|
|
113
|
+
const select = database.prepare(`
|
|
114
|
+
SELECT id, type, timestamp, session_id, user_id, metadata
|
|
115
|
+
FROM locallytics_events
|
|
116
|
+
WHERE id = ?
|
|
117
|
+
LIMIT 1
|
|
118
|
+
`);
|
|
119
|
+
const row = select.get ? select.get(id) : select.all(id)[0];
|
|
120
|
+
return row ? normalizeEventRow(toRow(row)) : { ...event, id };
|
|
121
|
+
},
|
|
122
|
+
async listEvents(options) {
|
|
123
|
+
ensureInitialized();
|
|
124
|
+
const params = [];
|
|
125
|
+
const where = [];
|
|
126
|
+
if (options?.type) {
|
|
127
|
+
where.push("type = ?");
|
|
128
|
+
params.push(options.type);
|
|
129
|
+
}
|
|
130
|
+
let sql = `
|
|
131
|
+
SELECT id, type, timestamp, session_id, user_id, metadata
|
|
132
|
+
FROM locallytics_events
|
|
133
|
+
`;
|
|
134
|
+
if (where.length > 0) {
|
|
135
|
+
sql += ` WHERE ${where.join(" AND ")}`;
|
|
136
|
+
}
|
|
137
|
+
sql += " ORDER BY timestamp DESC, id DESC";
|
|
138
|
+
const limit = toSafeLimit(options?.limit);
|
|
139
|
+
if (limit) {
|
|
140
|
+
sql += " LIMIT ?";
|
|
141
|
+
params.push(limit);
|
|
142
|
+
}
|
|
143
|
+
return toSqlResultRows(database, sql, params);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/session.ts
|
|
149
|
+
function randomSuffix() {
|
|
150
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
151
|
+
return crypto.randomUUID();
|
|
152
|
+
}
|
|
153
|
+
return `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
154
|
+
}
|
|
155
|
+
function createSessionManager(sessionTracking) {
|
|
156
|
+
let sessionId;
|
|
157
|
+
return {
|
|
158
|
+
getSessionId() {
|
|
159
|
+
if (!sessionTracking) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (!sessionId) {
|
|
163
|
+
sessionId = `ltx_${randomSuffix()}`;
|
|
164
|
+
}
|
|
165
|
+
return sessionId;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/create-locallytics.ts
|
|
171
|
+
function isDatabaseAdapter(database) {
|
|
172
|
+
return typeof database === "object" && database !== null && "insertEvent" in database && typeof database.insertEvent === "function" && "listEvents" in database && typeof database.listEvents === "function";
|
|
173
|
+
}
|
|
174
|
+
function isSqliteDatabase(database) {
|
|
175
|
+
return typeof database === "object" && database !== null && "prepare" in database && typeof database.prepare === "function";
|
|
176
|
+
}
|
|
177
|
+
function toDatabaseAdapter(database) {
|
|
178
|
+
if (isDatabaseAdapter(database)) {
|
|
179
|
+
return createCustomAdapter(database);
|
|
180
|
+
}
|
|
181
|
+
if (isSqliteDatabase(database)) {
|
|
182
|
+
return createSqliteAdapter(database);
|
|
183
|
+
}
|
|
184
|
+
throw new Error("Unsupported database input. Pass a better-sqlite3-style database or a custom adapter.");
|
|
185
|
+
}
|
|
186
|
+
function normalizeEventType(type) {
|
|
187
|
+
const normalized = type.trim();
|
|
188
|
+
if (!normalized) {
|
|
189
|
+
throw new Error("Event type is required.");
|
|
190
|
+
}
|
|
191
|
+
return normalized;
|
|
192
|
+
}
|
|
193
|
+
function resolvePageViewRoute(route) {
|
|
194
|
+
if (route) {
|
|
195
|
+
return route;
|
|
196
|
+
}
|
|
197
|
+
if (typeof location === "undefined") {
|
|
198
|
+
return "/";
|
|
199
|
+
}
|
|
200
|
+
return `${location.pathname}${location.search}`;
|
|
201
|
+
}
|
|
202
|
+
function createLocallytics(config) {
|
|
203
|
+
const adapter = toDatabaseAdapter(config.database);
|
|
204
|
+
const sessionManager = createSessionManager(Boolean(config.sessionTracking));
|
|
205
|
+
let initialized = false;
|
|
206
|
+
let userId = config.userId;
|
|
207
|
+
async function ensureInitialized() {
|
|
208
|
+
if (initialized) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (adapter.initialize) {
|
|
212
|
+
await adapter.initialize();
|
|
213
|
+
}
|
|
214
|
+
initialized = true;
|
|
215
|
+
}
|
|
216
|
+
async function track(type, metadata) {
|
|
217
|
+
await ensureInitialized();
|
|
218
|
+
return adapter.insertEvent({
|
|
219
|
+
type: normalizeEventType(type),
|
|
220
|
+
timestamp: Date.now(),
|
|
221
|
+
sessionId: sessionManager.getSessionId(),
|
|
222
|
+
userId,
|
|
223
|
+
metadata
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
async function listEvents(options) {
|
|
227
|
+
await ensureInitialized();
|
|
228
|
+
return adapter.listEvents(options);
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
track,
|
|
232
|
+
async trackPageView(route) {
|
|
233
|
+
return track("page_view", { route: resolvePageViewRoute(route) });
|
|
234
|
+
},
|
|
235
|
+
listEvents,
|
|
236
|
+
setUserId(nextUserId) {
|
|
237
|
+
userId = nextUserId;
|
|
238
|
+
},
|
|
239
|
+
getUserId() {
|
|
240
|
+
return userId;
|
|
241
|
+
},
|
|
242
|
+
getSessionId() {
|
|
243
|
+
return sessionManager.getSessionId();
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
export {
|
|
248
|
+
createLocallytics,
|
|
249
|
+
SQLITE_CREATE_EVENTS_TABLE_SQL,
|
|
250
|
+
EVENTS_TABLE_NAME
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
//# debugId=8CAF93D1E8D8A8E664756E2164756E21
|
|
254
|
+
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1,15 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/adapters/custom.ts", "../src/normalize.ts", "../src/sql.ts", "../src/adapters/sqlite.ts", "../src/session.ts", "../src/create-locallytics.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { DatabaseAdapter } from \"../types\";\n\nexport function createCustomAdapter(adapter: DatabaseAdapter): DatabaseAdapter {\n return adapter;\n}\n",
|
|
6
|
+
"import type { EventMetadata, LocallyticsEvent } from \"./types\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nexport function parseMetadata(value: unknown): EventMetadata | undefined {\n if (value == null) {\n return undefined;\n }\n\n if (isRecord(value)) {\n return value;\n }\n\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(value);\n return isRecord(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function stringifyMetadata(metadata: EventMetadata | undefined): string | null {\n if (!metadata) {\n return null;\n }\n\n return JSON.stringify(metadata);\n}\n\nexport function normalizeEventRow(row: Record<string, unknown>): LocallyticsEvent {\n return {\n id: row.id as string | number | undefined,\n type: String(row.type ?? \"\"),\n timestamp: Number(row.timestamp ?? Date.now()),\n sessionId:\n (row.session_id as string | undefined) ??\n (row.sessionId as string | undefined),\n userId: (row.user_id as string | undefined) ?? (row.userId as string | undefined),\n metadata: parseMetadata(row.metadata),\n };\n}\n",
|
|
7
|
+
"export const EVENTS_TABLE_NAME = \"locallytics_events\";\n\nexport const SQLITE_CREATE_EVENTS_TABLE_SQL = `\nCREATE TABLE IF NOT EXISTS locallytics_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n type TEXT NOT NULL,\n timestamp INTEGER NOT NULL,\n session_id TEXT,\n user_id TEXT,\n metadata TEXT\n);\n`;\n",
|
|
8
|
+
"import { normalizeEventRow, stringifyMetadata } from \"../normalize\";\nimport { SQLITE_CREATE_EVENTS_TABLE_SQL } from \"../sql\";\nimport type {\n DatabaseAdapter,\n ListEventsOptions,\n LocallyticsEvent,\n LocallyticsEventInput,\n SqliteDatabase,\n} from \"../types\";\n\nfunction toRow(value: unknown): Record<string, unknown> {\n if (typeof value === \"object\" && value !== null) {\n return value as Record<string, unknown>;\n }\n\n return {};\n}\n\nfunction toInsertId(value: unknown): number | undefined {\n if (!value || typeof value !== \"object\") {\n return undefined;\n }\n\n const row = value as { lastInsertRowid?: string | number | bigint };\n const id = row.lastInsertRowid;\n\n if (typeof id === \"number\") {\n return id;\n }\n\n if (typeof id === \"bigint\") {\n return Number(id);\n }\n\n if (typeof id === \"string\" && id.length > 0) {\n return Number(id);\n }\n\n return undefined;\n}\n\nfunction toSafeLimit(limit: number | undefined): number | undefined {\n if (limit == null) {\n return undefined;\n }\n\n const normalized = Math.floor(limit);\n return normalized > 0 ? normalized : 1;\n}\n\nfunction toSqlResultRows(\n database: SqliteDatabase,\n sql: string,\n params: unknown[]\n): LocallyticsEvent[] {\n const statement = database.prepare(sql);\n return statement.all(...params).map((row) => normalizeEventRow(toRow(row)));\n}\n\nexport function createSqliteAdapter(database: SqliteDatabase): DatabaseAdapter {\n let initialized = false;\n\n function ensureInitialized(): void {\n if (initialized) {\n return;\n }\n\n database.prepare(SQLITE_CREATE_EVENTS_TABLE_SQL).run();\n initialized = true;\n }\n\n return {\n initialize: ensureInitialized,\n\n async insertEvent(event: LocallyticsEventInput) {\n ensureInitialized();\n\n const result = database\n .prepare(\n `\n INSERT INTO locallytics_events (type, timestamp, session_id, user_id, metadata)\n VALUES (?, ?, ?, ?, ?)\n `\n )\n .run(\n event.type,\n event.timestamp,\n event.sessionId ?? null,\n event.userId ?? null,\n stringifyMetadata(event.metadata)\n );\n\n const id = toInsertId(result);\n if (id == null) {\n return { ...event };\n }\n\n const select = database.prepare(\n `\n SELECT id, type, timestamp, session_id, user_id, metadata\n FROM locallytics_events\n WHERE id = ?\n LIMIT 1\n `\n );\n\n const row = select.get ? select.get(id) : select.all(id)[0];\n return row ? normalizeEventRow(toRow(row)) : { ...event, id };\n },\n\n async listEvents(options?: ListEventsOptions) {\n ensureInitialized();\n\n const params: unknown[] = [];\n const where: string[] = [];\n\n if (options?.type) {\n where.push(\"type = ?\");\n params.push(options.type);\n }\n\n let sql = `\n SELECT id, type, timestamp, session_id, user_id, metadata\n FROM locallytics_events\n `;\n\n if (where.length > 0) {\n sql += ` WHERE ${where.join(\" AND \")}`;\n }\n\n sql += \" ORDER BY timestamp DESC, id DESC\";\n\n const limit = toSafeLimit(options?.limit);\n if (limit) {\n sql += \" LIMIT ?\";\n params.push(limit);\n }\n\n return toSqlResultRows(database, sql, params);\n },\n };\n}\n",
|
|
9
|
+
"function randomSuffix(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n return `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport function createSessionManager(sessionTracking: boolean): {\n getSessionId: () => string | undefined;\n} {\n let sessionId: string | undefined;\n\n return {\n getSessionId() {\n if (!sessionTracking) {\n return undefined;\n }\n\n if (!sessionId) {\n sessionId = `ltx_${randomSuffix()}`;\n }\n\n return sessionId;\n },\n };\n}\n",
|
|
10
|
+
"import { createCustomAdapter } from \"./adapters/custom\";\nimport { createSqliteAdapter } from \"./adapters/sqlite\";\nimport { createSessionManager } from \"./session\";\nimport type {\n DatabaseAdapter,\n EventMetadata,\n Locallytics,\n LocallyticsConfig,\n LocallyticsEvent,\n ListEventsOptions,\n SqliteDatabase,\n SupportedDatabase,\n} from \"./types\";\n\nfunction isDatabaseAdapter(\n database: SupportedDatabase,\n): database is DatabaseAdapter {\n return (\n typeof database === \"object\" &&\n database !== null &&\n \"insertEvent\" in database &&\n typeof database.insertEvent === \"function\" &&\n \"listEvents\" in database &&\n typeof database.listEvents === \"function\"\n );\n}\n\nfunction isSqliteDatabase(\n database: SupportedDatabase,\n): database is SqliteDatabase {\n return (\n typeof database === \"object\" &&\n database !== null &&\n \"prepare\" in database &&\n typeof database.prepare === \"function\"\n );\n}\n\nfunction toDatabaseAdapter(database: SupportedDatabase): DatabaseAdapter {\n if (isDatabaseAdapter(database)) {\n return createCustomAdapter(database);\n }\n\n if (isSqliteDatabase(database)) {\n return createSqliteAdapter(database);\n }\n\n throw new Error(\n \"Unsupported database input. Pass a better-sqlite3-style database or a custom adapter.\",\n );\n}\n\nfunction normalizeEventType(type: string): string {\n const normalized = type.trim();\n\n if (!normalized) {\n throw new Error(\"Event type is required.\");\n }\n\n return normalized;\n}\n\nfunction resolvePageViewRoute(route: string | undefined): string {\n if (route) {\n return route;\n }\n\n if (typeof location === \"undefined\") {\n return \"/\";\n }\n\n return `${location.pathname}${location.search}`;\n}\n\nexport function createLocallytics(config: LocallyticsConfig): Locallytics {\n const adapter = toDatabaseAdapter(config.database);\n const sessionManager = createSessionManager(Boolean(config.sessionTracking));\n\n let initialized = false;\n let userId = config.userId;\n\n async function ensureInitialized(): Promise<void> {\n if (initialized) {\n return;\n }\n\n if (adapter.initialize) {\n await adapter.initialize();\n }\n\n initialized = true;\n }\n\n async function track(\n type: string,\n metadata?: EventMetadata,\n ): Promise<LocallyticsEvent> {\n await ensureInitialized();\n\n return adapter.insertEvent({\n type: normalizeEventType(type),\n timestamp: Date.now(),\n sessionId: sessionManager.getSessionId(),\n userId,\n metadata,\n });\n }\n\n async function listEvents(options?: ListEventsOptions) {\n await ensureInitialized();\n return adapter.listEvents(options);\n }\n\n return {\n track,\n\n async trackPageView(route?: string) {\n return track(\"page_view\", { route: resolvePageViewRoute(route) });\n },\n\n listEvents,\n\n setUserId(nextUserId) {\n userId = nextUserId;\n },\n\n getUserId() {\n return userId;\n },\n\n getSessionId() {\n return sessionManager.getSessionId();\n },\n };\n}\n"
|
|
11
|
+
],
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "8CAF93D1E8D8A8E664756E2164756E21",
|
|
14
|
+
"names": []
|
|
15
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { EventMetadata, LocallyticsEvent } from "./types";
|
|
2
|
+
export declare function parseMetadata(value: unknown): EventMetadata | undefined;
|
|
3
|
+
export declare function stringifyMetadata(metadata: EventMetadata | undefined): string | null;
|
|
4
|
+
export declare function normalizeEventRow(row: Record<string, unknown>): LocallyticsEvent;
|
|
5
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../src/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAM/D,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAmBvE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAMpF;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,CAWhF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAQA,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,OAAO,GAAG;IAC9D,YAAY,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;CACxC,CAgBA"}
|
package/dist/sql.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const EVENTS_TABLE_NAME = "locallytics_events";
|
|
2
|
+
export declare const SQLITE_CREATE_EVENTS_TABLE_SQL = "\nCREATE TABLE IF NOT EXISTS locallytics_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n type TEXT NOT NULL,\n timestamp INTEGER NOT NULL,\n session_id TEXT,\n user_id TEXT,\n metadata TEXT\n);\n";
|
|
3
|
+
//# sourceMappingURL=sql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,uBAAuB,CAAC;AAEtD,eAAO,MAAM,8BAA8B,iNAS1C,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type EventMetadata = Record<string, unknown>;
|
|
2
|
+
export type LocallyticsEvent = {
|
|
3
|
+
id?: string | number;
|
|
4
|
+
type: string;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
sessionId?: string;
|
|
7
|
+
userId?: string;
|
|
8
|
+
metadata?: EventMetadata;
|
|
9
|
+
};
|
|
10
|
+
export type LocallyticsEventInput = Omit<LocallyticsEvent, "id">;
|
|
11
|
+
export type ListEventsOptions = {
|
|
12
|
+
limit?: number;
|
|
13
|
+
type?: string;
|
|
14
|
+
};
|
|
15
|
+
export interface DatabaseAdapter {
|
|
16
|
+
initialize?(): Promise<void> | void;
|
|
17
|
+
insertEvent(event: LocallyticsEventInput): Promise<LocallyticsEvent>;
|
|
18
|
+
listEvents(options?: ListEventsOptions): Promise<LocallyticsEvent[]>;
|
|
19
|
+
}
|
|
20
|
+
export type SqliteStatement = {
|
|
21
|
+
run: (...params: any[]) => unknown;
|
|
22
|
+
all: (...params: any[]) => unknown[];
|
|
23
|
+
get?: (...params: any[]) => unknown;
|
|
24
|
+
};
|
|
25
|
+
export type SqliteDatabase = {
|
|
26
|
+
prepare: (sql: string) => SqliteStatement;
|
|
27
|
+
};
|
|
28
|
+
export type SupportedDatabase = SqliteDatabase | DatabaseAdapter;
|
|
29
|
+
export type LocallyticsConfig = {
|
|
30
|
+
database: SupportedDatabase;
|
|
31
|
+
sessionTracking?: boolean;
|
|
32
|
+
userId?: string;
|
|
33
|
+
};
|
|
34
|
+
export interface Locallytics {
|
|
35
|
+
track(type: string, metadata?: EventMetadata): Promise<LocallyticsEvent>;
|
|
36
|
+
trackPageView(route?: string): Promise<LocallyticsEvent>;
|
|
37
|
+
listEvents(options?: ListEventsOptions): Promise<LocallyticsEvent[]>;
|
|
38
|
+
setUserId(userId: string | undefined): void;
|
|
39
|
+
getUserId(): string | undefined;
|
|
40
|
+
getSessionId(): string | undefined;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpC,WAAW,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACrE,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;CACtE;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,CAAC,GAAG,MAAM,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;IACnC,GAAG,EAAE,CAAC,GAAG,MAAM,EAAE,GAAG,EAAE,KAAK,OAAO,EAAE,CAAC;IACrC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,eAAe,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,eAAe,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzE,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzD,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACrE,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAC5C,SAAS,IAAI,MAAM,GAAG,SAAS,CAAC;IAChC,YAAY,IAAI,MAAM,GAAG,SAAS,CAAC;CACpC"}
|
package/package.json
CHANGED
|
@@ -1,70 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "locallytics",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Simple self-hosted analytics for custom events.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"analytics",
|
|
8
|
+
"sqlite",
|
|
9
|
+
"self-hosted",
|
|
10
|
+
"events"
|
|
11
|
+
],
|
|
12
|
+
"author": "Aidan McAlister",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/aidankmcalister/locallytics.git"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/aidankmcalister/locallytics#readme",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/aidankmcalister/locallytics/issues"
|
|
21
|
+
},
|
|
6
22
|
"main": "./dist/index.js",
|
|
7
23
|
"types": "./dist/index.d.ts",
|
|
8
24
|
"exports": {
|
|
9
25
|
".": {
|
|
10
26
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js",
|
|
12
27
|
"default": "./dist/index.js"
|
|
13
|
-
}
|
|
14
|
-
"./schema.sql": "./src/db/schema.sql",
|
|
15
|
-
"./schema-sqlite.sql": "./src/db/schema-sqlite.sql",
|
|
16
|
-
"./schema-mysql.sql": "./src/db/schema-mysql.sql"
|
|
28
|
+
}
|
|
17
29
|
},
|
|
18
30
|
"files": [
|
|
19
31
|
"dist",
|
|
20
|
-
"
|
|
21
|
-
"src/db/schema-sqlite.sql",
|
|
22
|
-
"src/db/schema-mysql.sql"
|
|
23
|
-
],
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build": "tsc",
|
|
26
|
-
"dev": "tsc --watch",
|
|
27
|
-
"typecheck": "tsc --noEmit",
|
|
28
|
-
"test": "vitest",
|
|
29
|
-
"prepublishOnly": "npm run build"
|
|
30
|
-
},
|
|
31
|
-
"keywords": [
|
|
32
|
-
"analytics",
|
|
33
|
-
"privacy",
|
|
34
|
-
"nextjs",
|
|
35
|
-
"postgresql",
|
|
36
|
-
"mysql",
|
|
37
|
-
"sqlite",
|
|
38
|
-
"self-hosted"
|
|
32
|
+
"README.md"
|
|
39
33
|
],
|
|
40
|
-
"author": "",
|
|
41
|
-
"license": "MIT",
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"pg": "^8.11.3"
|
|
44
|
-
},
|
|
45
34
|
"peerDependencies": {
|
|
46
|
-
"
|
|
47
|
-
"react": ">=18.0.0"
|
|
35
|
+
"better-sqlite3": ">=9.0.0"
|
|
48
36
|
},
|
|
49
37
|
"peerDependenciesMeta": {
|
|
50
38
|
"better-sqlite3": {
|
|
51
39
|
"optional": true
|
|
52
|
-
},
|
|
53
|
-
"mysql2": {
|
|
54
|
-
"optional": true
|
|
55
40
|
}
|
|
56
41
|
},
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target node --format esm --sourcemap && tsc -p tsconfig.build.json",
|
|
44
|
+
"prepack": "bun run build",
|
|
45
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
46
|
+
"test": "bun test"
|
|
60
47
|
},
|
|
61
48
|
"devDependencies": {
|
|
62
|
-
"
|
|
63
|
-
"@types/node": "^20.10.0",
|
|
64
|
-
"@types/pg": "^8.10.9",
|
|
65
|
-
"@types/react": "^18.2.0",
|
|
66
|
-
"mysql2": "^3.16.1",
|
|
67
|
-
"typescript": "^5.3.0",
|
|
68
|
-
"vitest": "^1.1.0"
|
|
49
|
+
"typescript": "^5.9.3"
|
|
69
50
|
}
|
|
70
51
|
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
interface LocallyticsGrabberProps {
|
|
2
|
-
/** API endpoint URL (defaults to /api/analytics) */
|
|
3
|
-
endpoint?: string;
|
|
4
|
-
/** Interval in ms to batch events before sending (defaults to 30000) */
|
|
5
|
-
trackInterval?: number;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* React component for tracking pageviews
|
|
9
|
-
* Drop this into your layout to automatically track pageviews
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```tsx
|
|
13
|
-
* // app/layout.tsx
|
|
14
|
-
* import { LocallyticsGrabber } from 'locallytics';
|
|
15
|
-
*
|
|
16
|
-
* export default function RootLayout({ children }) {
|
|
17
|
-
* return (
|
|
18
|
-
* <html>
|
|
19
|
-
* <body>
|
|
20
|
-
* {children}
|
|
21
|
-
* <LocallyticsGrabber maxLength={5} trackInterval={5000} />
|
|
22
|
-
* </body>
|
|
23
|
-
* </html>
|
|
24
|
-
* );
|
|
25
|
-
* }
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
export declare function LocallyticsGrabber({ endpoint, trackInterval, }: LocallyticsGrabberProps): null;
|
|
29
|
-
export {};
|
|
30
|
-
//# sourceMappingURL=LocallyticsGrabber.d.ts.map
|