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.
Files changed (79) hide show
  1. package/README.md +36 -53
  2. package/dist/adapters/custom.d.ts +3 -0
  3. package/dist/adapters/custom.d.ts.map +1 -0
  4. package/dist/adapters/sqlite.d.ts +3 -0
  5. package/dist/adapters/sqlite.d.ts.map +1 -0
  6. package/dist/create-locallytics.d.ts +3 -0
  7. package/dist/create-locallytics.d.ts.map +1 -0
  8. package/dist/index.d.ts +3 -5
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +254 -7
  11. package/dist/index.js.map +15 -1
  12. package/dist/normalize.d.ts +5 -0
  13. package/dist/normalize.d.ts.map +1 -0
  14. package/dist/session.d.ts +4 -0
  15. package/dist/session.d.ts.map +1 -0
  16. package/dist/sql.d.ts +3 -0
  17. package/dist/sql.d.ts.map +1 -0
  18. package/dist/types.d.ts +42 -0
  19. package/dist/types.d.ts.map +1 -0
  20. package/package.json +27 -46
  21. package/dist/client/LocallyticsGrabber.d.ts +0 -30
  22. package/dist/client/LocallyticsGrabber.d.ts.map +0 -1
  23. package/dist/client/LocallyticsGrabber.js +0 -71
  24. package/dist/client/LocallyticsGrabber.js.map +0 -1
  25. package/dist/client/batcher.d.ts +0 -48
  26. package/dist/client/batcher.d.ts.map +0 -1
  27. package/dist/client/batcher.js +0 -134
  28. package/dist/client/batcher.js.map +0 -1
  29. package/dist/client/tracker.d.ts +0 -18
  30. package/dist/client/tracker.d.ts.map +0 -1
  31. package/dist/client/tracker.js +0 -101
  32. package/dist/client/tracker.js.map +0 -1
  33. package/dist/db/factory.d.ts +0 -11
  34. package/dist/db/factory.d.ts.map +0 -1
  35. package/dist/db/factory.js +0 -46
  36. package/dist/db/factory.js.map +0 -1
  37. package/dist/db/mysql.d.ts +0 -17
  38. package/dist/db/mysql.d.ts.map +0 -1
  39. package/dist/db/mysql.js +0 -144
  40. package/dist/db/mysql.js.map +0 -1
  41. package/dist/db/postgres.d.ts +0 -16
  42. package/dist/db/postgres.d.ts.map +0 -1
  43. package/dist/db/postgres.js +0 -142
  44. package/dist/db/postgres.js.map +0 -1
  45. package/dist/db/sqlite.d.ts +0 -17
  46. package/dist/db/sqlite.d.ts.map +0 -1
  47. package/dist/db/sqlite.js +0 -130
  48. package/dist/db/sqlite.js.map +0 -1
  49. package/dist/server/handlers.d.ts +0 -10
  50. package/dist/server/handlers.d.ts.map +0 -1
  51. package/dist/server/handlers.js +0 -96
  52. package/dist/server/handlers.js.map +0 -1
  53. package/dist/server/index.d.ts +0 -42
  54. package/dist/server/index.d.ts.map +0 -1
  55. package/dist/server/index.js +0 -96
  56. package/dist/server/index.js.map +0 -1
  57. package/dist/server/queries.d.ts +0 -10
  58. package/dist/server/queries.d.ts.map +0 -1
  59. package/dist/server/queries.js +0 -24
  60. package/dist/server/queries.js.map +0 -1
  61. package/dist/server/validator.d.ts +0 -32
  62. package/dist/server/validator.d.ts.map +0 -1
  63. package/dist/server/validator.js +0 -129
  64. package/dist/server/validator.js.map +0 -1
  65. package/dist/types/index.d.ts +0 -135
  66. package/dist/types/index.d.ts.map +0 -1
  67. package/dist/types/index.js +0 -24
  68. package/dist/types/index.js.map +0 -1
  69. package/dist/utils/hash.d.ts +0 -16
  70. package/dist/utils/hash.d.ts.map +0 -1
  71. package/dist/utils/hash.js +0 -36
  72. package/dist/utils/hash.js.map +0 -1
  73. package/dist/utils/rate-limit.d.ts +0 -32
  74. package/dist/utils/rate-limit.d.ts.map +0 -1
  75. package/dist/utils/rate-limit.js +0 -73
  76. package/dist/utils/rate-limit.js.map +0 -1
  77. package/src/db/schema-mysql.sql +0 -17
  78. package/src/db/schema-sqlite.sql +0 -29
  79. package/src/db/schema.sql +0 -35
package/README.md CHANGED
@@ -1,73 +1,56 @@
1
- # Locallytics
1
+ # locallytics
2
2
 
3
- Self-hosted, privacy-first analytics SDK for Next.js.
3
+ `locallytics` is a lightweight, framework-agnostic analytics runtime.
4
+ This release currently supports SQLite.
4
5
 
5
- ## Installation
6
+ ## Install
6
7
 
7
- ```bash
8
- npm install locallytics
8
+ ```npm
9
+ npm install locallytics better-sqlite3
9
10
  ```
10
11
 
11
- ## Quick Start
12
+ ## Quickstart
12
13
 
13
- ### 1. Setup Database
14
+ Create one shared Locallytics instance.
14
15
 
15
- Use our CLI to set up your PostgreSQL database automatically:
16
+ ```ts
17
+ import { createLocallytics } from "locallytics";
18
+ import Database from "better-sqlite3";
16
19
 
17
- ```bash
18
- npx locallytics-cli migrate
20
+ export const locallytics = createLocallytics({
21
+ database: new Database("database.sqlite"),
22
+ sessionTracking: true,
23
+ });
19
24
  ```
20
25
 
21
- ### 2. Add API Route
26
+ Track custom events.
22
27
 
23
- ```typescript
24
- // app/api/analytics/route.ts
25
- import { locallytics } from "locallytics";
28
+ ```ts
29
+ await locallytics.track("button_click", { button: "cta" });
30
+ ```
26
31
 
27
- const analytics = await locallytics({
28
- database: process.env.DATABASE_URL!,
29
- });
32
+ Read stored events.
30
33
 
31
- export const { GET, POST } = analytics;
34
+ ```ts
35
+ const events = await locallytics.listEvents({ limit: 25 });
32
36
  ```
33
37
 
34
- ### 3. Add Tracker
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
- ### 4. Fetch Data
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
- ```tsx
55
- // app/page.tsx
56
- import { LocallyticsData } from "locallytics";
48
+ ## CLI
57
49
 
58
- export default async function Home() {
59
- const data = await LocallyticsData(); // Simple! No arguments.
50
+ Use `@locallytics/cli` to generate and run migrations:
60
51
 
61
- return <pre>{JSON.stringify(data, null, 2)}</pre>;
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,3 @@
1
+ import type { DatabaseAdapter } from "../types";
2
+ export declare function createCustomAdapter(adapter: DatabaseAdapter): DatabaseAdapter;
3
+ //# sourceMappingURL=custom.d.ts.map
@@ -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,3 @@
1
+ import type { DatabaseAdapter, SqliteDatabase } from "../types";
2
+ export declare function createSqliteAdapter(database: SqliteDatabase): DatabaseAdapter;
3
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -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,3 @@
1
+ import type { Locallytics, LocallyticsConfig } from "./types";
2
+ export declare function createLocallytics(config: LocallyticsConfig): Locallytics;
3
+ //# sourceMappingURL=create-locallytics.d.ts.map
@@ -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 { LocallyticsGrabber } from "./client/LocallyticsGrabber.js";
2
- export { createDatabase } from "./db/factory.js";
3
- export { LocallyticsData, locallytics } from "./server/index.js";
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEjE,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,eAAe,EACf,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,SAAS,EACT,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
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
- // Server exports
2
- // Client exports
3
- export { LocallyticsGrabber } from "./client/LocallyticsGrabber.js";
4
- export { createDatabase } from "./db/factory.js";
5
- export { LocallyticsData, locallytics } from "./server/index.js";
6
- export { LocallyticsError } from "./types/index.js";
7
- //# sourceMappingURL=index.js.map
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
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iBAAiB;AAEjB,iBAAiB;AACjB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAajE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
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,4 @@
1
+ export declare function createSessionManager(sessionTracking: boolean): {
2
+ getSessionId: () => string | undefined;
3
+ };
4
+ //# sourceMappingURL=session.d.ts.map
@@ -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"}
@@ -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.9",
4
- "description": "Self-hosted, privacy-first analytics SDK for Next.js",
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
- "src/db/schema.sql",
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
- "next": ">=13.0.0",
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
- "optionalDependencies": {
58
- "better-sqlite3": "^11.0.0",
59
- "mysql2": "^3.9.0"
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
- "@types/better-sqlite3": "^7.6.8",
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