locallytics 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +56 -0
  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 +4 -59
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +172 -285
  11. package/dist/index.js.map +15 -0
  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 +13 -44
  21. package/dist/adapters/drizzle.d.mts +0 -25
  22. package/dist/adapters/drizzle.d.ts +0 -25
  23. package/dist/adapters/drizzle.js +0 -38
  24. package/dist/adapters/drizzle.mjs +0 -7
  25. package/dist/adapters/prisma.d.mts +0 -25
  26. package/dist/adapters/prisma.d.ts +0 -25
  27. package/dist/adapters/prisma.js +0 -38
  28. package/dist/adapters/prisma.mjs +0 -7
  29. package/dist/adapters.d.mts +0 -7
  30. package/dist/adapters.d.ts +0 -7
  31. package/dist/adapters.js +0 -39
  32. package/dist/adapters.mjs +0 -8
  33. package/dist/browser.d.mts +0 -41
  34. package/dist/browser.d.ts +0 -41
  35. package/dist/browser.js +0 -79
  36. package/dist/browser.mjs +0 -10
  37. package/dist/index.d.mts +0 -59
  38. package/dist/index.mjs +0 -336
  39. package/dist/react.d.mts +0 -48
  40. package/dist/react.d.ts +0 -48
  41. package/dist/react.js +0 -132
  42. package/dist/react.mjs +0 -61
  43. package/dist/shared/chunk-8tbv1rg5.js +0 -45
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # locallytics
2
+
3
+ `locallytics` is a lightweight, framework-agnostic analytics runtime.
4
+ This release currently supports SQLite.
5
+
6
+ ## Install
7
+
8
+ ```npm
9
+ npm install locallytics better-sqlite3
10
+ ```
11
+
12
+ ## Quickstart
13
+
14
+ Create one shared Locallytics instance.
15
+
16
+ ```ts
17
+ import { createLocallytics } from "locallytics";
18
+ import Database from "better-sqlite3";
19
+
20
+ export const locallytics = createLocallytics({
21
+ database: new Database("database.sqlite"),
22
+ sessionTracking: true,
23
+ });
24
+ ```
25
+
26
+ Track custom events.
27
+
28
+ ```ts
29
+ await locallytics.track("button_click", { button: "cta" });
30
+ ```
31
+
32
+ Read stored events.
33
+
34
+ ```ts
35
+ const events = await locallytics.listEvents({ limit: 25 });
36
+ ```
37
+
38
+ ## API
39
+
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()`
47
+
48
+ ## CLI
49
+
50
+ Use `@locallytics/cli` to generate and run migrations:
51
+
52
+ ```npm
53
+ npm install -D @locallytics/cli
54
+ npx locallytics generate
55
+ npx locallytics migrate
56
+ ```
@@ -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,59 +1,4 @@
1
- /**
2
- * Locallytics - Analytics built around a database-first API.
3
- * @packageDocumentation
4
- */
5
- type LocallyticsEvent = {
6
- id?: string | number;
7
- type: string;
8
- timestamp: number;
9
- sessionId?: string;
10
- userId?: string;
11
- metadata?: Record<string, unknown>;
12
- };
13
- interface DatabaseAdapter {
14
- initialize?(): Promise<void> | void;
15
- insertEvent(event: Omit<LocallyticsEvent, "id">): Promise<LocallyticsEvent>;
16
- findEvents(): Promise<LocallyticsEvent[]>;
17
- }
18
- type PostgresLikeDatabase = {
19
- query: (sql: string, params?: unknown[]) => Promise<{
20
- rows: Record<string, unknown>[];
21
- }>;
22
- };
23
- type MysqlLikeDatabase = {
24
- execute: (sql: string, params?: unknown[]) => Promise<[unknown, unknown]>;
25
- };
26
- type SqliteLikeStatement = {
27
- run: (...params: unknown[]) => {
28
- lastInsertRowid?: number | bigint;
29
- };
30
- all: (...params: unknown[]) => Record<string, unknown>[];
31
- get?: (...params: unknown[]) => Record<string, unknown> | undefined;
32
- };
33
- type SqliteLikeDatabase = {
34
- prepare: (sql: string) => SqliteLikeStatement;
35
- };
36
- type SupportedDatabase = DatabaseAdapter | PostgresLikeDatabase | MysqlLikeDatabase | SqliteLikeDatabase;
37
- type LocallyticsConfig = {
38
- database: SupportedDatabase;
39
- sessionTracking?: boolean;
40
- userId?: string;
41
- };
42
- type ClientQueryResult = {
43
- events: LocallyticsEvent[];
44
- loading: boolean;
45
- error: Error | null;
46
- refresh: () => Promise<LocallyticsEvent[]>;
47
- };
48
- type QueryResult = ClientQueryResult & PromiseLike<LocallyticsEvent[]>;
49
- interface Locallytics {
50
- track(type: string, metadata?: Record<string, unknown>): Promise<LocallyticsEvent>;
51
- trackPageView(route?: string): Promise<LocallyticsEvent>;
52
- query(): QueryResult;
53
- setUserId(userId: string | undefined): void;
54
- getUserId(): string | undefined;
55
- getSessionId(): string | undefined;
56
- subscribe(listener: () => void): () => void;
57
- }
58
- declare function createLocallytics(config: LocallyticsConfig): Locallytics;
59
- export { createLocallytics, SupportedDatabase, SqliteLikeStatement, SqliteLikeDatabase, QueryResult, PostgresLikeDatabase, MysqlLikeDatabase, LocallyticsEvent, LocallyticsConfig, Locallytics, DatabaseAdapter, ClientQueryResult };
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";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
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,41 +1,9 @@
1
- var import_node_module = require("node:module");
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __moduleCache = /* @__PURE__ */ new WeakMap;
7
- var __toCommonJS = (from) => {
8
- var entry = __moduleCache.get(from), desc;
9
- if (entry)
10
- return entry;
11
- entry = __defProp({}, "__esModule", { value: true });
12
- if (from && typeof from === "object" || typeof from === "function")
13
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
14
- get: () => from[key],
15
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
- }));
17
- __moduleCache.set(from, entry);
18
- return entry;
19
- };
20
- var __export = (target, all) => {
21
- for (var name in all)
22
- __defProp(target, name, {
23
- get: all[name],
24
- enumerable: true,
25
- configurable: true,
26
- set: (newValue) => all[name] = () => newValue
27
- });
28
- };
29
-
30
- // src/index.ts
31
- var exports_src = {};
32
- __export(exports_src, {
33
- createLocallytics: () => createLocallytics
34
- });
35
- module.exports = __toCommonJS(exports_src);
36
- function toError(error) {
37
- return error instanceof Error ? error : new Error(String(error));
1
+ // src/adapters/custom.ts
2
+ function createCustomAdapter(adapter) {
3
+ return adapter;
38
4
  }
5
+
6
+ // src/normalize.ts
39
7
  function isRecord(value) {
40
8
  return typeof value === "object" && value !== null;
41
9
  }
@@ -46,15 +14,21 @@ function parseMetadata(value) {
46
14
  if (isRecord(value)) {
47
15
  return value;
48
16
  }
49
- if (typeof value === "string") {
50
- try {
51
- const parsed = JSON.parse(value);
52
- return isRecord(parsed) ? parsed : undefined;
53
- } catch {
54
- return;
55
- }
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;
56
25
  }
57
- return;
26
+ }
27
+ function stringifyMetadata(metadata) {
28
+ if (!metadata) {
29
+ return null;
30
+ }
31
+ return JSON.stringify(metadata);
58
32
  }
59
33
  function normalizeEventRow(row) {
60
34
  return {
@@ -66,123 +40,54 @@ function normalizeEventRow(row) {
66
40
  metadata: parseMetadata(row.metadata)
67
41
  };
68
42
  }
69
- function stringifyMetadata(metadata) {
70
- return metadata ? JSON.stringify(metadata) : null;
71
- }
72
- function isDatabaseAdapter(database) {
73
- return "insertEvent" in database && typeof database.insertEvent === "function" && "findEvents" in database && typeof database.findEvents === "function";
74
- }
75
- function isPostgresLikeDatabase(database) {
76
- return "query" in database && typeof database.query === "function";
77
- }
78
- function isMysqlLikeDatabase(database) {
79
- return "execute" in database && typeof database.execute === "function";
80
- }
81
- function isSqliteLikeDatabase(database) {
82
- return "prepare" in database && typeof database.prepare === "function";
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 {};
83
63
  }
84
- function createPostgresAdapter(database) {
85
- let initialized = false;
86
- async function ensureInitialized() {
87
- if (initialized) {
88
- return;
89
- }
90
- await database.query(`
91
- CREATE TABLE IF NOT EXISTS locallytics_events (
92
- id BIGSERIAL PRIMARY KEY,
93
- type TEXT NOT NULL,
94
- timestamp BIGINT NOT NULL,
95
- session_id TEXT,
96
- user_id TEXT,
97
- metadata JSONB
98
- )
99
- `);
100
- initialized = true;
64
+ function toInsertId(value) {
65
+ if (!value || typeof value !== "object") {
66
+ return;
101
67
  }
102
- return {
103
- initialize: ensureInitialized,
104
- async insertEvent(event) {
105
- await ensureInitialized();
106
- const result = await database.query(`
107
- INSERT INTO locallytics_events (type, timestamp, session_id, user_id, metadata)
108
- VALUES ($1, $2, $3, $4, $5::jsonb)
109
- RETURNING id, type, timestamp, session_id, user_id, metadata
110
- `, [
111
- event.type,
112
- event.timestamp,
113
- event.sessionId ?? null,
114
- event.userId ?? null,
115
- stringifyMetadata(event.metadata)
116
- ]);
117
- const first = result.rows[0];
118
- return first ? normalizeEventRow(first) : { ...event };
119
- },
120
- async findEvents() {
121
- await ensureInitialized();
122
- const result = await database.query(`
123
- SELECT id, type, timestamp, session_id, user_id, metadata
124
- FROM locallytics_events
125
- ORDER BY timestamp DESC
126
- `);
127
- return result.rows.map(normalizeEventRow);
128
- }
129
- };
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;
130
80
  }
131
- function createMysqlAdapter(database) {
132
- let initialized = false;
133
- async function ensureInitialized() {
134
- if (initialized) {
135
- return;
136
- }
137
- await database.execute(`
138
- CREATE TABLE IF NOT EXISTS locallytics_events (
139
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
140
- type VARCHAR(255) NOT NULL,
141
- timestamp BIGINT NOT NULL,
142
- session_id VARCHAR(255),
143
- user_id VARCHAR(255),
144
- metadata JSON
145
- )
146
- `);
147
- initialized = true;
81
+ function toSafeLimit(limit) {
82
+ if (limit == null) {
83
+ return;
148
84
  }
149
- return {
150
- initialize: ensureInitialized,
151
- async insertEvent(event) {
152
- await ensureInitialized();
153
- const [insertResult] = await database.execute(`
154
- INSERT INTO locallytics_events (type, timestamp, session_id, user_id, metadata)
155
- VALUES (?, ?, ?, ?, ?)
156
- `, [
157
- event.type,
158
- event.timestamp,
159
- event.sessionId ?? null,
160
- event.userId ?? null,
161
- stringifyMetadata(event.metadata)
162
- ]);
163
- const insertId = Number(insertResult.insertId ?? 0);
164
- if (!insertId) {
165
- return { ...event };
166
- }
167
- const [rows] = await database.execute(`
168
- SELECT id, type, timestamp, session_id, user_id, metadata
169
- FROM locallytics_events
170
- WHERE id = ?
171
- LIMIT 1
172
- `, [insertId]);
173
- const first = rows[0];
174
- return first ? normalizeEventRow(first) : { ...event, id: insertId };
175
- },
176
- async findEvents() {
177
- await ensureInitialized();
178
- const [rows] = await database.execute(`
179
- SELECT id, type, timestamp, session_id, user_id, metadata
180
- FROM locallytics_events
181
- ORDER BY timestamp DESC
182
- `);
183
- return rows.map(normalizeEventRow);
184
- }
185
- };
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)));
186
91
  }
187
92
  function createSqliteAdapter(database) {
188
93
  let initialized = false;
@@ -190,178 +95,160 @@ function createSqliteAdapter(database) {
190
95
  if (initialized) {
191
96
  return;
192
97
  }
193
- database.prepare(`
194
- CREATE TABLE IF NOT EXISTS locallytics_events (
195
- id INTEGER PRIMARY KEY AUTOINCREMENT,
196
- type TEXT NOT NULL,
197
- timestamp INTEGER NOT NULL,
198
- session_id TEXT,
199
- user_id TEXT,
200
- metadata TEXT
201
- )
202
- `).run();
98
+ database.prepare(SQLITE_CREATE_EVENTS_TABLE_SQL).run();
203
99
  initialized = true;
204
100
  }
205
101
  return {
206
102
  initialize: ensureInitialized,
207
103
  async insertEvent(event) {
208
104
  ensureInitialized();
209
- const insert = database.prepare(`
210
- INSERT INTO locallytics_events (type, timestamp, session_id, user_id, metadata)
211
- VALUES (?, ?, ?, ?, ?)
212
- `);
213
- const result = insert.run(event.type, event.timestamp, event.sessionId ?? null, event.userId ?? null, stringifyMetadata(event.metadata));
214
- const insertId = Number(result.lastInsertRowid ?? 0);
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
+ }
215
113
  const select = database.prepare(`
216
114
  SELECT id, type, timestamp, session_id, user_id, metadata
217
115
  FROM locallytics_events
218
116
  WHERE id = ?
219
117
  LIMIT 1
220
118
  `);
221
- const row = select.get ? select.get(insertId) : select.all(insertId)[0];
222
- return row ? normalizeEventRow(row) : { ...event, id: insertId };
119
+ const row = select.get ? select.get(id) : select.all(id)[0];
120
+ return row ? normalizeEventRow(toRow(row)) : { ...event, id };
223
121
  },
224
- async findEvents() {
122
+ async listEvents(options) {
225
123
  ensureInitialized();
226
- const rows = database.prepare(`
227
- SELECT id, type, timestamp, session_id, user_id, metadata
228
- FROM locallytics_events
229
- ORDER BY timestamp DESC
230
- `).all();
231
- return rows.map(normalizeEventRow);
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);
232
144
  }
233
145
  };
234
146
  }
235
- function createDatabaseAdapter(database) {
236
- if (isDatabaseAdapter(database)) {
237
- return database;
238
- }
239
- if (isPostgresLikeDatabase(database)) {
240
- return createPostgresAdapter(database);
147
+
148
+ // src/session.ts
149
+ function randomSuffix() {
150
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
151
+ return crypto.randomUUID();
241
152
  }
242
- if (isMysqlLikeDatabase(database)) {
243
- return createMysqlAdapter(database);
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);
244
180
  }
245
- if (isSqliteLikeDatabase(database)) {
181
+ if (isSqliteDatabase(database)) {
246
182
  return createSqliteAdapter(database);
247
183
  }
248
- throw new Error("Unsupported database input. Use a pg Pool, mysql2 pool, better-sqlite3 Database, or a DatabaseAdapter.");
184
+ throw new Error("Unsupported database input. Pass a better-sqlite3-style database or a custom adapter.");
249
185
  }
250
- function createSessionId() {
251
- return `session_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
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}`;
252
201
  }
253
202
  function createLocallytics(config) {
254
- const adapter = createDatabaseAdapter(config.database);
255
- const listeners = new Set;
256
- const isBrowser = typeof window !== "undefined";
203
+ const adapter = toDatabaseAdapter(config.database);
204
+ const sessionManager = createSessionManager(Boolean(config.sessionTracking));
205
+ let initialized = false;
257
206
  let userId = config.userId;
258
- let currentSessionId;
259
- const queryState = {
260
- events: [],
261
- loading: false,
262
- error: null,
263
- loaded: false,
264
- pending: undefined
265
- };
266
- function notify() {
267
- for (const listener of listeners) {
268
- listener();
269
- }
270
- }
271
- function getSessionId() {
272
- if (!config.sessionTracking) {
207
+ async function ensureInitialized() {
208
+ if (initialized) {
273
209
  return;
274
210
  }
275
- if (!currentSessionId) {
276
- currentSessionId = createSessionId();
277
- }
278
- return currentSessionId;
279
- }
280
- async function runQuery(force = false) {
281
- if (!force && queryState.pending) {
282
- return queryState.pending;
283
- }
284
- if (!force && isBrowser && queryState.loaded) {
285
- return queryState.events;
286
- }
287
- queryState.loading = true;
288
- queryState.error = null;
289
- notify();
290
- const pending = (async () => {
291
- if (adapter.initialize) {
292
- await adapter.initialize();
293
- }
294
- const events = await adapter.findEvents();
295
- queryState.events = events;
296
- queryState.loaded = true;
297
- return events;
298
- })().catch((error) => {
299
- const normalized = toError(error);
300
- queryState.error = normalized;
301
- throw normalized;
302
- }).finally(() => {
303
- queryState.loading = false;
304
- queryState.pending = undefined;
305
- notify();
306
- });
307
- queryState.pending = pending;
308
- return pending;
309
- }
310
- async function track(type, metadata) {
311
211
  if (adapter.initialize) {
312
212
  await adapter.initialize();
313
213
  }
314
- const event = {
315
- type,
214
+ initialized = true;
215
+ }
216
+ async function track(type, metadata) {
217
+ await ensureInitialized();
218
+ return adapter.insertEvent({
219
+ type: normalizeEventType(type),
316
220
  timestamp: Date.now(),
317
- sessionId: getSessionId(),
221
+ sessionId: sessionManager.getSessionId(),
318
222
  userId,
319
223
  metadata
320
- };
321
- const saved = await adapter.insertEvent(event);
322
- if (isBrowser && queryState.loaded) {
323
- queryState.events = [saved, ...queryState.events];
324
- notify();
325
- }
326
- return saved;
224
+ });
327
225
  }
328
- function createQueryResult(promise) {
329
- return {
330
- get events() {
331
- return queryState.events;
332
- },
333
- get loading() {
334
- return queryState.loading;
335
- },
336
- get error() {
337
- return queryState.error;
338
- },
339
- refresh: () => runQuery(true),
340
- then: promise.then.bind(promise)
341
- };
226
+ async function listEvents(options) {
227
+ await ensureInitialized();
228
+ return adapter.listEvents(options);
342
229
  }
343
230
  return {
344
231
  track,
345
232
  async trackPageView(route) {
346
- const resolvedRoute = route ?? (typeof location !== "undefined" ? `${location.pathname}${location.search}` : "/");
347
- return track("page_view", { route: resolvedRoute });
348
- },
349
- query() {
350
- const promise = runQuery(!isBrowser);
351
- return createQueryResult(promise);
233
+ return track("page_view", { route: resolvePageViewRoute(route) });
352
234
  },
235
+ listEvents,
353
236
  setUserId(nextUserId) {
354
237
  userId = nextUserId;
355
238
  },
356
239
  getUserId() {
357
240
  return userId;
358
241
  },
359
- getSessionId,
360
- subscribe(listener) {
361
- listeners.add(listener);
362
- return () => {
363
- listeners.delete(listener);
364
- };
242
+ getSessionId() {
243
+ return sessionManager.getSessionId();
365
244
  }
366
245
  };
367
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