@faststats/web 0.2.13 → 0.3.0

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 (51) hide show
  1. package/dist/chunks/api-urls-nKmJnyjD.js +1 -0
  2. package/dist/chunks/identifiers-wylBcW5K.js +1 -0
  3. package/dist/chunks/{replay-BuX3_0xs.d.ts → replay-Dpl1P6cw.d.ts} +17 -12
  4. package/dist/chunks/send-data-D6WdryL7.js +1 -0
  5. package/dist/error.d.ts +4 -12
  6. package/dist/error.js +2 -1
  7. package/dist/feature-flags.d.ts +19 -2
  8. package/dist/feature-flags.js +1 -1
  9. package/dist/index.d.ts +15 -37
  10. package/dist/index.js +1 -1
  11. package/dist/replay.d.ts +2 -2
  12. package/dist/replay.js +1 -1
  13. package/dist/web-vitals.d.ts +4 -7
  14. package/dist/web-vitals.js +1 -1
  15. package/package.json +9 -11
  16. package/CHANGELOG.md +0 -177
  17. package/dist/chunks/api-urls-DaeYkG0_.js +0 -1
  18. package/dist/chunks/error-CDVOMiI9.js +0 -2
  19. package/dist/chunks/feature-flags-BClx56v5.d.ts +0 -19
  20. package/dist/chunks/feature-flags-DSOCIZHK.js +0 -1
  21. package/dist/chunks/identifiers-CQeWm7wi.js +0 -1
  22. package/dist/chunks/replay-CHFW2q4E.js +0 -1
  23. package/dist/chunks/rolldown-runtime-MP-BAFHD.js +0 -1
  24. package/dist/chunks/send-data-DL_GlsQw.js +0 -1
  25. package/dist/chunks/types-CYzR5xtT.js +0 -1
  26. package/dist/chunks/web-vitals-GwPlL7Zy.js +0 -1
  27. package/scripts/check-bundle-size.mjs +0 -96
  28. package/src/analytics.ts +0 -776
  29. package/src/entries/error.ts +0 -6
  30. package/src/entries/feature-flags.ts +0 -5
  31. package/src/entries/main.ts +0 -21
  32. package/src/entries/replay.ts +0 -1
  33. package/src/entries/web-vitals.ts +0 -1
  34. package/src/env.d.ts +0 -2
  35. package/src/error.ts +0 -252
  36. package/src/feature-flags.ts +0 -84
  37. package/src/replay.ts +0 -469
  38. package/src/sdk.ts +0 -8
  39. package/src/utils/api-urls.ts +0 -24
  40. package/src/utils/identifiers.ts +0 -89
  41. package/src/utils/send-data.ts +0 -52
  42. package/src/utils/types.ts +0 -15
  43. package/src/web-vitals.ts +0 -159
  44. package/tests/analytics.test.ts +0 -450
  45. package/tests/identifiers.test.ts +0 -95
  46. package/tests/replay.test.ts +0 -412
  47. package/tests/web-vitals.test.ts +0 -208
  48. package/tsconfig.json +0 -30
  49. package/tsdown.config.ts +0 -27
  50. package/worker/index.ts +0 -22
  51. package/wrangler.toml +0 -8
@@ -1,412 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import { gunzipSync } from "node:zlib";
3
- import ReplayTracker from "../src/replay";
4
- import { getOrCreateSessionId } from "../src/utils/identifiers";
5
-
6
- function decodeBody(data: string | Uint8Array): string {
7
- if (typeof data === "string") return data;
8
- return gunzipSync(data).toString("utf8");
9
- }
10
-
11
- class MockStorage {
12
- private readonly data = new Map<string, string>();
13
-
14
- getItem(key: string): string | null {
15
- return this.data.get(key) ?? null;
16
- }
17
-
18
- setItem(key: string, value: string): void {
19
- this.data.set(key, value);
20
- }
21
-
22
- removeItem(key: string): void {
23
- this.data.delete(key);
24
- }
25
- }
26
-
27
- type ReplayEvent = {
28
- type: number;
29
- timestamp: number;
30
- data?: Record<string, unknown>;
31
- };
32
-
33
- type ReplayBatch = {
34
- token: string;
35
- sessionId?: string;
36
- identifier?: string;
37
- batchId: string;
38
- sequence: number;
39
- timestamp: number;
40
- url: string;
41
- events: ReplayEvent[];
42
- };
43
-
44
- type ReplayTrackerInternals = {
45
- events: ReplayEvent[];
46
- pending: ReplayBatch[];
47
- pendingSizeBytes: number;
48
- minLengthFlushTask: ReturnType<typeof setTimeout> | null;
49
- retryTask: ReturnType<typeof setTimeout> | null;
50
- startTime: number;
51
- onEvent: (event: ReplayEvent, isCheckout?: boolean) => void;
52
- flush: (lowLatency: boolean) => Promise<void>;
53
- send: (
54
- data: string | Uint8Array,
55
- isCompressed: boolean,
56
- lowLatency: boolean,
57
- ) => Promise<boolean>;
58
- };
59
-
60
- function setGlobal(name: keyof typeof globalThis, value: unknown): void {
61
- Object.defineProperty(globalThis, name, {
62
- configurable: true,
63
- writable: true,
64
- value,
65
- });
66
- }
67
-
68
- const original = {
69
- window: globalThis.window,
70
- document: globalThis.document,
71
- location: globalThis.location,
72
- navigator: globalThis.navigator,
73
- fetch: globalThis.fetch,
74
- localStorage: globalThis.localStorage,
75
- sessionStorage: globalThis.sessionStorage,
76
- };
77
-
78
- beforeEach(() => {
79
- setGlobal("window", {
80
- location: { href: "https://example.com/" },
81
- addEventListener: () => {},
82
- removeEventListener: () => {},
83
- } as unknown as Window);
84
- setGlobal("document", {
85
- visibilityState: "visible",
86
- addEventListener: () => {},
87
- removeEventListener: () => {},
88
- } as unknown as Document);
89
- setGlobal("location", { href: "https://example.com/" } as Location);
90
- setGlobal("localStorage", new MockStorage());
91
- setGlobal("sessionStorage", new MockStorage());
92
- setGlobal("navigator", { sendBeacon: () => false });
93
- setGlobal(
94
- "fetch",
95
- (async () => new Response("", { status: 204 })) as unknown as typeof fetch,
96
- );
97
- });
98
-
99
- afterEach(() => {
100
- setGlobal("window", original.window);
101
- setGlobal("document", original.document);
102
- setGlobal("location", original.location);
103
- setGlobal("navigator", original.navigator);
104
- setGlobal("fetch", original.fetch);
105
- setGlobal("localStorage", original.localStorage);
106
- setGlobal("sessionStorage", original.sessionStorage);
107
- });
108
-
109
- function createTracker(
110
- options: Partial<ConstructorParameters<typeof ReplayTracker>[0]> = {},
111
- ): ReplayTrackerInternals {
112
- return new ReplayTracker({
113
- siteKey: "site_test",
114
- ...options,
115
- }) as unknown as ReplayTrackerInternals;
116
- }
117
-
118
- describe("ReplayTracker", () => {
119
- test("buffers replay events", () => {
120
- const tracker = createTracker({
121
- maxEvents: 1000,
122
- minReplayLengthMs: Number.MAX_SAFE_INTEGER,
123
- });
124
-
125
- tracker.onEvent({ type: 2, timestamp: 1, data: {} }, false);
126
- tracker.onEvent({ type: 3, timestamp: 2, data: {} }, false);
127
-
128
- expect(tracker.events.length).toBe(2);
129
- });
130
-
131
- test("does not flush before minimum replay length", async () => {
132
- const tracker = createTracker({
133
- minReplayLengthMs: Number.MAX_SAFE_INTEGER,
134
- });
135
-
136
- let sent = 0;
137
- tracker.send = async () => {
138
- sent++;
139
- return true;
140
- };
141
- tracker.startTime = Date.now();
142
- tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
143
-
144
- await tracker.flush(false);
145
-
146
- expect(sent).toBe(0);
147
- expect(tracker.events.length).toBe(1);
148
- expect(tracker.pending.length).toBe(0);
149
-
150
- if (tracker.minLengthFlushTask) clearTimeout(tracker.minLengthFlushTask);
151
- });
152
-
153
- test("flushes buffered events once minimum replay length is reached", async () => {
154
- const tracker = createTracker({
155
- minReplayLengthMs: 20,
156
- });
157
-
158
- let sent = 0;
159
- tracker.send = async () => {
160
- sent++;
161
- return true;
162
- };
163
- tracker.startTime = Date.now();
164
- tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
165
-
166
- await new Promise((resolve) => setTimeout(resolve, 40));
167
-
168
- expect(sent).toBe(1);
169
- expect(tracker.events.length).toBe(0);
170
- expect(tracker.pending.length).toBe(0);
171
- });
172
-
173
- test("emits replay payload when flushed", async () => {
174
- const tracker = createTracker({
175
- minReplayLengthMs: 0,
176
- });
177
-
178
- const captured: { payload: ReplayBatch | null; lowLatency: boolean } = {
179
- payload: null,
180
- lowLatency: false,
181
- };
182
- tracker.send = async (data, _isCompressed, nextLowLatency) => {
183
- captured.payload = JSON.parse(decodeBody(data)) as ReplayBatch;
184
- captured.lowLatency = nextLowLatency;
185
- return true;
186
- };
187
- tracker.startTime = Date.now() - 1000;
188
- tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
189
-
190
- await tracker.flush(true);
191
-
192
- expect(captured.payload).not.toBeNull();
193
- expect(captured.payload?.token).toBe("site_test");
194
- expect(captured.payload?.sessionId).toBeTruthy();
195
- expect(captured.payload?.batchId).toBeTruthy();
196
- expect(captured.payload?.events).toHaveLength(1);
197
- expect(captured.lowLatency).toBe(true);
198
- expect(tracker.pending.length).toBe(0);
199
- });
200
-
201
- test("emits stable batch ids while queued for retry", async () => {
202
- const tracker = createTracker({
203
- minReplayLengthMs: 0,
204
- });
205
-
206
- const payloads: ReplayBatch[] = [];
207
- tracker.send = async (data) => {
208
- payloads.push(JSON.parse(decodeBody(data)) as ReplayBatch);
209
- return payloads.length > 1;
210
- };
211
- tracker.startTime = Date.now() - 1000;
212
- tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
213
-
214
- await tracker.flush(false);
215
- await tracker.flush(false);
216
-
217
- expect(payloads).toHaveLength(2);
218
- expect(payloads[1]?.batchId).toBe(payloads[0]?.batchId);
219
- expect(payloads[1]?.sequence).toBe(payloads[0]?.sequence);
220
-
221
- if (tracker.retryTask) clearTimeout(tracker.retryTask);
222
- });
223
-
224
- test("keeps the same replay session id across later batches", async () => {
225
- const tracker = createTracker({
226
- minReplayLengthMs: 0,
227
- });
228
-
229
- const payloads: ReplayBatch[] = [];
230
- tracker.send = async (data) => {
231
- payloads.push(JSON.parse(decodeBody(data)) as ReplayBatch);
232
- return true;
233
- };
234
-
235
- (tracker as unknown as { sessionId?: string }).sessionId =
236
- getOrCreateSessionId();
237
- tracker.startTime = Date.now() - 1000;
238
- tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
239
- await tracker.flush(false);
240
-
241
- const storage = globalThis.sessionStorage as unknown as MockStorage;
242
- storage.setItem("session_timestamp", "0");
243
-
244
- tracker.onEvent({ type: 3, timestamp: Date.now(), data: {} }, false);
245
- await tracker.flush(false);
246
-
247
- expect(payloads).toHaveLength(2);
248
- expect(payloads[0]?.sessionId).toBeTruthy();
249
- expect(payloads[1]?.sessionId).toBe(payloads[0]?.sessionId);
250
- });
251
-
252
- test("sends replay as plain JSON when CompressionStream is unavailable", async () => {
253
- const tracker = createTracker({
254
- minReplayLengthMs: 0,
255
- });
256
-
257
- const captured: { bodyType: "string" | "blob" | null } = {
258
- bodyType: null,
259
- };
260
- tracker.send = async (data) => {
261
- captured.bodyType = typeof data === "string" ? "string" : "blob";
262
- return true;
263
- };
264
- tracker.startTime = Date.now() - 1000;
265
- tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
266
-
267
- await tracker.flush(false);
268
-
269
- expect(captured.bodyType).toBe("string");
270
- });
271
-
272
- test("sends replay as gzip Uint8Array when CompressionStream is available", async () => {
273
- setGlobal("window", {
274
- ...(globalThis.window as unknown as Record<string, unknown>),
275
- CompressionStream: globalThis.CompressionStream,
276
- });
277
-
278
- const tracker = createTracker({
279
- minReplayLengthMs: 0,
280
- });
281
-
282
- const captured: {
283
- bodyType: "string" | "binary" | null;
284
- compressed: boolean;
285
- size: number;
286
- } = { bodyType: null, compressed: false, size: 0 };
287
- tracker.send = async (data, isCompressed) => {
288
- captured.bodyType = typeof data === "string" ? "string" : "binary";
289
- captured.compressed = isCompressed;
290
- if (data instanceof Uint8Array) {
291
- captured.size = data.byteLength;
292
- }
293
- return true;
294
- };
295
- tracker.startTime = Date.now() - 1000;
296
- for (let i = 0; i < 10; i++) {
297
- tracker.onEvent(
298
- {
299
- type: 2,
300
- timestamp: Date.now(),
301
- data: { i, payload: "x".repeat(500) },
302
- },
303
- false,
304
- );
305
- }
306
-
307
- await tracker.flush(false);
308
-
309
- expect(captured.bodyType).toBe("binary");
310
- expect(captured.compressed).toBe(true);
311
- expect(captured.size).toBeGreaterThan(0);
312
- expect(captured.size).toBeLessThan(5000);
313
- });
314
-
315
- test("compresses on unload (low latency) flush", async () => {
316
- setGlobal("window", {
317
- ...(globalThis.window as unknown as Record<string, unknown>),
318
- CompressionStream: globalThis.CompressionStream,
319
- });
320
-
321
- const tracker = createTracker({
322
- minReplayLengthMs: 0,
323
- });
324
-
325
- let compressedFlag = false;
326
- let lowLatencyFlag = false;
327
- tracker.send = async (_data, isCompressed, lowLatency) => {
328
- compressedFlag = isCompressed;
329
- lowLatencyFlag = lowLatency;
330
- return true;
331
- };
332
- tracker.startTime = Date.now() - 1000;
333
- tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
334
-
335
- await tracker.flush(true);
336
-
337
- expect(compressedFlag).toBe(true);
338
- expect(lowLatencyFlag).toBe(true);
339
- });
340
-
341
- test("keeps failed batches queued for retry", async () => {
342
- const tracker = createTracker({
343
- minReplayLengthMs: 0,
344
- });
345
-
346
- tracker.send = async () => false;
347
- tracker.startTime = Date.now() - 1000;
348
- tracker.onEvent({ type: 2, timestamp: Date.now(), data: {} }, false);
349
-
350
- await tracker.flush(false);
351
-
352
- expect(tracker.pending.length).toBe(1);
353
- expect(tracker.retryTask).not.toBeNull();
354
-
355
- if (tracker.retryTask) clearTimeout(tracker.retryTask);
356
- });
357
-
358
- test("drops replay batches that exceed the queue byte limit", async () => {
359
- const tracker = createTracker({
360
- minReplayLengthMs: 0,
361
- maxQueueSizeBytes: 600,
362
- });
363
-
364
- let sent = 0;
365
- tracker.send = async () => {
366
- sent++;
367
- return true;
368
- };
369
- tracker.startTime = Date.now() - 1000;
370
- tracker.onEvent(
371
- {
372
- type: 2,
373
- timestamp: Date.now(),
374
- data: { payload: "x".repeat(1000) },
375
- },
376
- false,
377
- );
378
-
379
- await tracker.flush(false);
380
-
381
- expect(sent).toBe(0);
382
- expect(tracker.pending.length).toBe(0);
383
- expect(tracker.pendingSizeBytes).toBe(0);
384
- });
385
-
386
- test("keeps pending replay queue under the byte limit", async () => {
387
- const tracker = createTracker({
388
- minReplayLengthMs: 0,
389
- maxQueueSizeBytes: 900,
390
- });
391
-
392
- tracker.send = async () => false;
393
- tracker.startTime = Date.now() - 1000;
394
-
395
- for (let i = 0; i < 4; i++) {
396
- tracker.onEvent(
397
- {
398
- type: 2,
399
- timestamp: Date.now(),
400
- data: { i, payload: "x".repeat(300) },
401
- },
402
- false,
403
- );
404
- await tracker.flush(false);
405
- }
406
-
407
- expect(tracker.pending.length).toBeGreaterThan(0);
408
- expect(tracker.pendingSizeBytes).toBeLessThanOrEqual(900);
409
-
410
- if (tracker.retryTask) clearTimeout(tracker.retryTask);
411
- });
412
- });
@@ -1,208 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import WebVitalsTracker from "../src/web-vitals";
3
-
4
- type FetchCall = {
5
- url: string;
6
- body?: string;
7
- };
8
-
9
- type Harness = {
10
- fetchCalls: FetchCall[];
11
- fetchStatuses: number[];
12
- restore(): void;
13
- };
14
-
15
- function setGlobal(name: keyof typeof globalThis, value: unknown): void {
16
- Object.defineProperty(globalThis, name, {
17
- configurable: true,
18
- writable: true,
19
- value,
20
- });
21
- }
22
-
23
- function setupBrowser(): Harness {
24
- const originalValues = {
25
- window: globalThis.window,
26
- document: globalThis.document,
27
- location: globalThis.location,
28
- fetch: globalThis.fetch,
29
- sessionStorage: globalThis.sessionStorage,
30
- };
31
- const fetchCalls: FetchCall[] = [];
32
- const fetchStatuses: number[] = [];
33
- const listeners = new Map<string, Set<EventListenerOrEventListenerObject>>();
34
- const addEventListener = (
35
- type: string,
36
- listener: EventListenerOrEventListenerObject,
37
- ): void => {
38
- const existing = listeners.get(type) ?? new Set();
39
- existing.add(listener);
40
- listeners.set(type, existing);
41
- };
42
- const removeEventListener = (
43
- type: string,
44
- listener: EventListenerOrEventListenerObject,
45
- ): void => {
46
- listeners.get(type)?.delete(listener);
47
- };
48
- const location = {
49
- href: "https://example.com/",
50
- };
51
- const storage = new Map<string, string>();
52
- const sessionStorage = {
53
- getItem: (key: string) => storage.get(key) ?? null,
54
- setItem: (key: string, value: string) => storage.set(key, value),
55
- removeItem: (key: string) => storage.delete(key),
56
- };
57
-
58
- setGlobal("location", location as Location);
59
- setGlobal("document", {
60
- addEventListener,
61
- removeEventListener,
62
- visibilityState: "visible",
63
- } as unknown as Document);
64
- setGlobal("window", {
65
- addEventListener,
66
- removeEventListener,
67
- location,
68
- setTimeout,
69
- clearTimeout,
70
- } as unknown as Window & typeof globalThis);
71
- setGlobal("sessionStorage", sessionStorage);
72
- setGlobal("fetch", (async (
73
- url: string | URL | Request,
74
- init?: RequestInit,
75
- ): Promise<Response> => {
76
- fetchCalls.push({
77
- url: String(url),
78
- body: typeof init?.body === "string" ? init.body : undefined,
79
- });
80
- return new Response("", { status: fetchStatuses.shift() ?? 204 });
81
- }) as typeof fetch);
82
-
83
- return {
84
- fetchCalls,
85
- fetchStatuses,
86
- restore() {
87
- setGlobal("window", originalValues.window);
88
- setGlobal("document", originalValues.document);
89
- setGlobal("location", originalValues.location);
90
- setGlobal("fetch", originalValues.fetch);
91
- setGlobal("sessionStorage", originalValues.sessionStorage);
92
- },
93
- };
94
- }
95
-
96
- function captureMetric(
97
- tracker: WebVitalsTracker,
98
- metric: Parameters<(metric: unknown) => void>[0],
99
- ): void {
100
- (
101
- tracker as unknown as {
102
- captureMetric(metric: unknown): void;
103
- }
104
- ).captureMetric(metric);
105
- }
106
-
107
- let harness: Harness;
108
-
109
- beforeEach(() => {
110
- harness = setupBrowser();
111
- });
112
-
113
- afterEach(() => {
114
- harness.restore();
115
- });
116
-
117
- describe("WebVitalsTracker", () => {
118
- test("flushes the current vitals bucket when the SPA route changes", async () => {
119
- const tracker = new WebVitalsTracker({
120
- siteKey: "site_test",
121
- baseUrl: "https://analytics.example.com",
122
- samplingPercentage: 100,
123
- });
124
-
125
- tracker.start();
126
- captureMetric(tracker, {
127
- name: "CLS",
128
- value: 0.12,
129
- id: "v1",
130
- rating: "needs-improvement",
131
- delta: 0.12,
132
- navigationType: "navigate",
133
- });
134
-
135
- tracker.trackPageChange("https://example.com/about");
136
- await new Promise((resolve) => setTimeout(resolve, 0));
137
-
138
- expect(harness.fetchCalls.length).toBe(1);
139
- const firstPayload = JSON.parse(harness.fetchCalls[0]?.body ?? "{}") as {
140
- metadata?: { url?: string };
141
- vitals?: Array<{ metric?: string }>;
142
- };
143
- expect(firstPayload.metadata?.url).toBe("https://example.com/");
144
- expect(firstPayload.vitals?.[0]?.metric).toBe("CLS");
145
-
146
- captureMetric(tracker, {
147
- name: "INP",
148
- value: 32,
149
- id: "v2",
150
- rating: "good",
151
- delta: 32,
152
- navigationType: "navigate",
153
- });
154
-
155
- tracker.stop();
156
- await new Promise((resolve) => setTimeout(resolve, 0));
157
-
158
- expect(harness.fetchCalls.length).toBe(2);
159
- const secondPayload = JSON.parse(harness.fetchCalls[1]?.body ?? "{}") as {
160
- metadata?: { url?: string };
161
- vitals?: Array<{ metric?: string }>;
162
- };
163
- expect(secondPayload.metadata?.url).toBe("https://example.com/about");
164
- expect(secondPayload.vitals?.[0]?.metric).toBe("INP");
165
- });
166
-
167
- test("keeps metrics buffered when a route flush fails", async () => {
168
- harness.fetchStatuses.push(500, 204, 204);
169
- const tracker = new WebVitalsTracker({
170
- siteKey: "site_test",
171
- baseUrl: "https://analytics.example.com",
172
- samplingPercentage: 100,
173
- });
174
-
175
- tracker.start();
176
- captureMetric(tracker, {
177
- name: "CLS",
178
- value: 0.12,
179
- id: "v1",
180
- rating: "needs-improvement",
181
- delta: 0.12,
182
- navigationType: "navigate",
183
- });
184
- tracker.trackPageChange("https://example.com/about");
185
- await new Promise((resolve) => setTimeout(resolve, 0));
186
-
187
- expect(harness.fetchCalls.length).toBe(1);
188
-
189
- captureMetric(tracker, {
190
- name: "INP",
191
- value: 32,
192
- id: "v2",
193
- rating: "good",
194
- delta: 32,
195
- navigationType: "navigate",
196
- });
197
- tracker.trackPageChange("https://example.com/contact");
198
- await new Promise((resolve) => setTimeout(resolve, 0));
199
-
200
- expect(harness.fetchCalls.length).toBe(3);
201
- const retriedPayload = JSON.parse(harness.fetchCalls[1]?.body ?? "{}") as {
202
- metadata?: { url?: string };
203
- vitals?: Array<{ metric?: string }>;
204
- };
205
- expect(retriedPayload.metadata?.url).toBe("https://example.com/");
206
- expect(retriedPayload.vitals?.[0]?.metric).toBe("CLS");
207
- });
208
- });
package/tsconfig.json DELETED
@@ -1,30 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- // Environment setup & latest features
4
- "lib": ["ESNext", "DOM"],
5
- "target": "ESNext",
6
- "module": "Preserve",
7
- "moduleDetection": "force",
8
- "jsx": "react-jsx",
9
- "allowJs": true,
10
-
11
- // Bundler mode
12
- "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
- "verbatimModuleSyntax": true,
15
- "noEmit": true,
16
-
17
- // Best practices
18
- "strict": true,
19
- "skipLibCheck": true,
20
- "noFallthroughCasesInSwitch": true,
21
- "noUncheckedIndexedAccess": true,
22
- "noImplicitOverride": true,
23
-
24
- // Some stricter flags (disabled by default)
25
- "noUnusedLocals": false,
26
- "noUnusedParameters": false,
27
- "noPropertyAccessFromIndexSignature": false
28
- },
29
- "include": ["src"]
30
- }
package/tsdown.config.ts DELETED
@@ -1,27 +0,0 @@
1
- import { defineConfig } from "tsdown";
2
- import pkg from "./package.json" with { type: "json" };
3
-
4
- export default defineConfig({
5
- entry: {
6
- index: "src/entries/main.ts",
7
- "feature-flags": "src/entries/feature-flags.ts",
8
- replay: "src/entries/replay.ts",
9
- error: "src/entries/error.ts",
10
- "web-vitals": "src/entries/web-vitals.ts",
11
- },
12
- format: ["esm"],
13
- platform: "browser",
14
- outDir: "dist",
15
- dts: true,
16
- minify: true,
17
- deps: { onlyBundle: false },
18
- checks: { pluginTimings: false },
19
- define: {
20
- __FASTSTATS_SDK_NAME__: JSON.stringify(pkg.name),
21
- __FASTSTATS_SDK_VERSION__: JSON.stringify(pkg.version),
22
- },
23
- outputOptions: {
24
- chunkFileNames: "chunks/[name]-[hash].js",
25
- entryFileNames: "[name].js",
26
- },
27
- });
package/worker/index.ts DELETED
@@ -1,22 +0,0 @@
1
- export default {
2
- async fetch(
3
- request: Request,
4
- env: { ASSETS: { fetch: typeof fetch } },
5
- ): Promise<Response> {
6
- const response = await env.ASSETS.fetch(request);
7
-
8
- if (response.ok) {
9
- const newHeaders = new Headers(response.headers);
10
- newHeaders.set("Cache-Control", "public, max-age=31536000, immutable");
11
- newHeaders.set("Access-Control-Allow-Origin", "*");
12
-
13
- return new Response(response.body, {
14
- status: response.status,
15
- statusText: response.statusText,
16
- headers: newHeaders,
17
- });
18
- }
19
-
20
- return response;
21
- },
22
- };
package/wrangler.toml DELETED
@@ -1,8 +0,0 @@
1
- name = "web-analytics"
2
- main = "worker/index.ts"
3
- compatibility_date = "2024-01-01"
4
-
5
- [assets]
6
- directory = "./dist"
7
- binding = "ASSETS"
8
- run_worker_first = true