@interfere/next 0.0.7-32ca917f3c6af1d90d7b0a54eb0b5dfa3b9d2305 → 0.0.8

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 (57) hide show
  1. package/dist/core/client.d.ts +22 -0
  2. package/dist/core/client.d.ts.map +1 -0
  3. package/dist/core/client.js +115 -0
  4. package/dist/core/client.js.map +1 -0
  5. package/dist/core/client.test.d.ts +2 -0
  6. package/dist/core/client.test.d.ts.map +1 -0
  7. package/dist/core/client.test.js +227 -0
  8. package/dist/core/client.test.js.map +1 -0
  9. package/dist/core/encoders.d.ts +3 -0
  10. package/dist/core/encoders.d.ts.map +1 -0
  11. package/dist/core/encoders.js +5 -0
  12. package/dist/core/encoders.js.map +1 -0
  13. package/dist/core/encoders.test.d.ts +2 -0
  14. package/dist/core/encoders.test.d.ts.map +1 -0
  15. package/dist/core/encoders.test.js +55 -0
  16. package/dist/core/encoders.test.js.map +1 -0
  17. package/dist/core/index.d.ts +3 -0
  18. package/dist/core/index.d.ts.map +1 -0
  19. package/dist/core/index.js +3 -0
  20. package/dist/core/index.js.map +1 -0
  21. package/dist/edge/edge.d.ts +11 -0
  22. package/dist/edge/edge.d.ts.map +1 -0
  23. package/dist/edge/edge.js +41 -0
  24. package/dist/edge/edge.js.map +1 -0
  25. package/dist/edge/edge.test.d.ts +2 -0
  26. package/dist/edge/edge.test.d.ts.map +1 -0
  27. package/dist/edge/edge.test.js +109 -0
  28. package/dist/edge/edge.test.js.map +1 -0
  29. package/dist/edge/index.d.ts +2 -0
  30. package/dist/edge/index.d.ts.map +1 -0
  31. package/dist/edge/index.js +2 -0
  32. package/dist/edge/index.js.map +1 -0
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +2 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/react/index.d.ts +2 -0
  38. package/dist/react/index.d.ts.map +1 -0
  39. package/dist/react/index.js +2 -0
  40. package/dist/react/index.js.map +1 -0
  41. package/dist/react/provider.d.ts +15 -0
  42. package/dist/react/provider.d.ts.map +1 -0
  43. package/dist/react/provider.jsx +21 -0
  44. package/dist/react/provider.jsx.map +1 -0
  45. package/dist/server/index.d.ts +2 -0
  46. package/dist/server/index.d.ts.map +1 -0
  47. package/dist/server/index.js +2 -0
  48. package/dist/server/index.js.map +1 -0
  49. package/dist/server/server.d.ts +6 -0
  50. package/dist/server/server.d.ts.map +1 -0
  51. package/dist/server/server.js +35 -0
  52. package/dist/server/server.js.map +1 -0
  53. package/dist/server/server.test.d.ts +2 -0
  54. package/dist/server/server.test.d.ts.map +1 -0
  55. package/dist/server/server.test.js +88 -0
  56. package/dist/server/server.test.js.map +1 -0
  57. package/package.json +1 -1
@@ -0,0 +1,22 @@
1
+ import type { Config, EventType } from '@interfere/types';
2
+ declare class InterfereClient {
3
+ private config;
4
+ private queue;
5
+ private seq;
6
+ private sessionId;
7
+ private runtime;
8
+ private flushTimer?;
9
+ constructor(config: Partial<Config>);
10
+ private detectRuntime;
11
+ private generateSessionId;
12
+ private startFlushTimer;
13
+ capture(type: EventType, payload: unknown): void;
14
+ flush(): void;
15
+ getSessionId(): string;
16
+ }
17
+ export declare const init: (config: Partial<Config>) => InterfereClient;
18
+ export declare const capture: (type: EventType, payload: unknown) => void;
19
+ export declare const flush: () => void;
20
+ export declare const getSessionId: () => string;
21
+ export {};
22
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAY,SAAS,EAAW,MAAM,kBAAkB,CAAC;AAG7E,cAAM,eAAe;IACnB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,UAAU,CAAC,CAAiB;gBAExB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC;IAyBnC,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAoBhD,KAAK,IAAI,IAAI;IA8Bb,YAAY,IAAI,MAAM;CAGvB;AAID,eAAO,MAAM,IAAI,GAAI,QAAQ,OAAO,CAAC,MAAM,CAAC,KAAG,eAM9C,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,MAAM,SAAS,EAAE,SAAS,OAAO,KAAG,IAK3D,CAAC;AAEF,eAAO,MAAM,KAAK,QAAO,IAExB,CAAC;AAEF,eAAO,MAAM,YAAY,QAAO,MAK/B,CAAC"}
@@ -0,0 +1,115 @@
1
+ import { encode } from './encoders.js';
2
+ class InterfereClient {
3
+ config;
4
+ queue = [];
5
+ seq = 0;
6
+ sessionId;
7
+ runtime;
8
+ flushTimer;
9
+ constructor(config) {
10
+ if (!config.projectId) {
11
+ throw new Error('[interfere] projectId required');
12
+ }
13
+ this.runtime = this.detectRuntime();
14
+ this.sessionId = config.sessionId || this.generateSessionId();
15
+ this.config = {
16
+ projectId: config.projectId,
17
+ endpoint: config.endpoint || 'https://ingest.interfere.com/v0',
18
+ env: config.env || 'development',
19
+ flushInterval: config.flushInterval || 5000,
20
+ debug: config.debug,
21
+ runtime: this.runtime,
22
+ sessionId: this.sessionId,
23
+ };
24
+ if (this.runtime === 'client') {
25
+ this.startFlushTimer();
26
+ if (typeof window !== 'undefined') {
27
+ window.addEventListener('beforeunload', () => this.flush());
28
+ }
29
+ }
30
+ }
31
+ detectRuntime() {
32
+ if (typeof window !== 'undefined') {
33
+ return 'client';
34
+ }
35
+ if (typeof globalThis !== 'undefined' && 'EdgeRuntime' in globalThis) {
36
+ return 'edge';
37
+ }
38
+ return 'server';
39
+ }
40
+ generateSessionId() {
41
+ return crypto.randomUUID();
42
+ }
43
+ startFlushTimer() {
44
+ this.flushTimer = setInterval(() => this.flush(), this.config.flushInterval);
45
+ }
46
+ capture(type, payload) {
47
+ const envelope = {
48
+ v: '0',
49
+ runtime: this.runtime,
50
+ project: this.config.projectId,
51
+ env: this.config.env,
52
+ client_ts: Date.now(),
53
+ session_id: this.sessionId,
54
+ seq: ++this.seq,
55
+ type,
56
+ payload: encode(payload),
57
+ };
58
+ this.queue.push(envelope);
59
+ if (this.runtime !== 'client' || this.queue.length >= 10) {
60
+ this.flush();
61
+ }
62
+ }
63
+ flush() {
64
+ if (this.queue.length === 0) {
65
+ return;
66
+ }
67
+ const batch = [...this.queue];
68
+ this.queue = [];
69
+ const body = JSON.stringify(batch);
70
+ if (this.runtime === 'client' &&
71
+ typeof window !== 'undefined' &&
72
+ typeof window.navigator?.sendBeacon === 'function') {
73
+ window.navigator.sendBeacon(this.config.endpoint, body);
74
+ }
75
+ else {
76
+ fetch(this.config.endpoint, {
77
+ method: 'POST',
78
+ headers: { 'Content-Type': 'application/json' },
79
+ body,
80
+ }).catch((_err) => {
81
+ if (this.config.debug) {
82
+ // In production, this would be sent to a logging service
83
+ // For now, we'll silently fail in non-debug mode
84
+ }
85
+ });
86
+ }
87
+ }
88
+ getSessionId() {
89
+ return this.sessionId;
90
+ }
91
+ }
92
+ let instance = null;
93
+ export const init = (config) => {
94
+ if (!config.projectId) {
95
+ throw new Error('[interfere] projectId required');
96
+ }
97
+ instance = new InterfereClient(config);
98
+ return instance;
99
+ };
100
+ export const capture = (type, payload) => {
101
+ if (!instance) {
102
+ throw new Error('[interfere] not initialized');
103
+ }
104
+ instance.capture(type, payload);
105
+ };
106
+ export const flush = () => {
107
+ instance?.flush();
108
+ };
109
+ export const getSessionId = () => {
110
+ if (!instance) {
111
+ throw new Error('[interfere] not initialized');
112
+ }
113
+ return instance.getSessionId();
114
+ };
115
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../src/core/client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,227 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { capture, flush, getSessionId, init } from './client.js';
3
+ // Top level regex for UUID validation
4
+ const UUID_REGEX = /^[0-9a-f-]{36}$/;
5
+ describe('client', () => {
6
+ beforeEach(() => {
7
+ vi.resetModules();
8
+ vi.clearAllMocks();
9
+ vi.clearAllTimers();
10
+ vi.useFakeTimers();
11
+ });
12
+ afterEach(() => {
13
+ vi.useRealTimers();
14
+ });
15
+ describe('init', () => {
16
+ it('throws without projectId', () => {
17
+ expect(() => init({})).toThrow('[interfere] projectId required');
18
+ });
19
+ it('initializes with required config', () => {
20
+ const client = init({ projectId: 'if_p_test' });
21
+ expect(client).toBeDefined();
22
+ });
23
+ it('uses default endpoint', () => {
24
+ const fetchSpy = vi
25
+ .spyOn(global, 'fetch')
26
+ .mockResolvedValue(new Response());
27
+ init({ projectId: 'if_p_test' });
28
+ capture('error', { test: true });
29
+ flush();
30
+ expect(fetchSpy).toHaveBeenCalledWith('https://ingest.interfere.com/v0', expect.any(Object));
31
+ });
32
+ it('uses custom endpoint', () => {
33
+ const fetchSpy = vi
34
+ .spyOn(global, 'fetch')
35
+ .mockResolvedValue(new Response());
36
+ init({ projectId: 'if_p_test', endpoint: 'https://custom.example.com' });
37
+ capture('error', { test: true });
38
+ flush();
39
+ expect(fetchSpy).toHaveBeenCalledWith('https://custom.example.com', expect.any(Object));
40
+ });
41
+ });
42
+ describe('capture', () => {
43
+ it('throws if not initialized', async () => {
44
+ vi.resetModules();
45
+ const { capture: freshCapture } = await import('./client.js');
46
+ expect(() => freshCapture('error', {})).toThrow('[interfere] not initialized');
47
+ });
48
+ it('queues events', () => {
49
+ const fetchSpy = vi
50
+ .spyOn(global, 'fetch')
51
+ .mockResolvedValue(new Response());
52
+ init({ projectId: 'if_p_test' });
53
+ capture('ui_event', { action: 'click' });
54
+ capture('error', { message: 'test error' });
55
+ // In server mode, each capture auto-flushes
56
+ // So we expect 2 fetch calls
57
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
58
+ const call1 = fetchSpy.mock.calls[0];
59
+ const call2 = fetchSpy.mock.calls[1];
60
+ if (!(call1?.[1] && call2?.[1])) {
61
+ throw new Error('Expected 2 fetch calls');
62
+ }
63
+ const body1 = JSON.parse(call1[1].body);
64
+ const body2 = JSON.parse(call2[1].body);
65
+ expect(body1).toHaveLength(1);
66
+ expect(body1[0].type).toBe('ui_event');
67
+ expect(body2).toHaveLength(1);
68
+ expect(body2[0].type).toBe('error');
69
+ });
70
+ it('auto-flushes after 10 events', () => {
71
+ const fetchSpy = vi
72
+ .spyOn(global, 'fetch')
73
+ .mockResolvedValue(new Response());
74
+ init({ projectId: 'if_p_test' });
75
+ // In server mode, each capture triggers a flush
76
+ for (let i = 0; i < 10; i++) {
77
+ capture('ui_event', { index: i });
78
+ }
79
+ // Each capture causes a flush in server mode
80
+ expect(fetchSpy).toHaveBeenCalledTimes(10);
81
+ });
82
+ it('includes correct envelope structure', () => {
83
+ const fetchSpy = vi
84
+ .spyOn(global, 'fetch')
85
+ .mockResolvedValue(new Response());
86
+ init({ projectId: 'if_p_test', env: 'production' });
87
+ capture('network', { url: '/api/test' });
88
+ flush();
89
+ const call = fetchSpy.mock.calls[0];
90
+ if (!call?.[1]) {
91
+ throw new Error('Expected fetch call');
92
+ }
93
+ const body = JSON.parse(call[1].body);
94
+ const envelope = body[0];
95
+ expect(envelope.v).toBe('0');
96
+ expect(envelope.runtime).toBe('server'); // Default in test environment
97
+ expect(envelope.project).toBe('if_p_test');
98
+ expect(envelope.env).toBe('production');
99
+ expect(envelope.client_ts).toBeGreaterThan(0);
100
+ expect(envelope.session_id).toMatch(UUID_REGEX);
101
+ expect(envelope.seq).toBe(1);
102
+ expect(envelope.type).toBe('network');
103
+ expect(envelope.payload).toBeDefined();
104
+ });
105
+ });
106
+ describe('flush', () => {
107
+ it('does nothing with empty queue', () => {
108
+ const fetchSpy = vi
109
+ .spyOn(global, 'fetch')
110
+ .mockResolvedValue(new Response());
111
+ init({ projectId: 'if_p_test' });
112
+ flush();
113
+ expect(fetchSpy).not.toHaveBeenCalled();
114
+ });
115
+ it('clears queue after flush', () => {
116
+ const fetchSpy = vi
117
+ .spyOn(global, 'fetch')
118
+ .mockResolvedValue(new Response());
119
+ init({ projectId: 'if_p_test' });
120
+ capture('error', { test: true });
121
+ flush();
122
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
123
+ flush(); // Second flush should do nothing
124
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
125
+ });
126
+ it('handles fetch errors in debug mode', async () => {
127
+ const fetchSpy = vi
128
+ .spyOn(global, 'fetch')
129
+ .mockRejectedValue(new Error('Network error'));
130
+ init({ projectId: 'if_p_test', debug: true });
131
+ capture('error', { test: true });
132
+ // Should not throw
133
+ expect(() => flush()).not.toThrow();
134
+ await vi.waitFor(() => {
135
+ expect(fetchSpy).toHaveBeenCalled();
136
+ });
137
+ });
138
+ });
139
+ describe('getSessionId', () => {
140
+ it('throws if not initialized', async () => {
141
+ vi.resetModules();
142
+ const { getSessionId: freshGetSessionId } = await import('./client.js');
143
+ expect(() => freshGetSessionId()).toThrow('[interfere] not initialized');
144
+ });
145
+ it('returns consistent session ID', () => {
146
+ init({ projectId: 'if_p_test' });
147
+ const id1 = getSessionId();
148
+ const id2 = getSessionId();
149
+ expect(id1).toBe(id2);
150
+ expect(id1).toMatch(UUID_REGEX);
151
+ });
152
+ it('uses provided session ID', () => {
153
+ const customId = 'custom-session-id';
154
+ init({ projectId: 'if_p_test', sessionId: customId });
155
+ expect(getSessionId()).toBe(customId);
156
+ });
157
+ });
158
+ describe('runtime detection', () => {
159
+ it('detects server runtime by default', () => {
160
+ const fetchSpy = vi
161
+ .spyOn(global, 'fetch')
162
+ .mockResolvedValue(new Response());
163
+ init({ projectId: 'if_p_test' });
164
+ capture('server_req', {});
165
+ flush();
166
+ const call = fetchSpy.mock.calls[0];
167
+ if (!call?.[1]) {
168
+ throw new Error('Expected fetch call');
169
+ }
170
+ const body = JSON.parse(call[1].body);
171
+ expect(body[0].runtime).toBe('server');
172
+ });
173
+ it('detects client runtime with window', async () => {
174
+ const originalWindow = global.window;
175
+ global.window = {
176
+ addEventListener: vi.fn(),
177
+ };
178
+ vi.resetModules();
179
+ const { init: freshInit, capture: freshCapture, flush: freshFlush, } = await import('./client.js');
180
+ const fetchSpy = vi
181
+ .spyOn(global, 'fetch')
182
+ .mockResolvedValue(new Response());
183
+ freshInit({ projectId: 'if_p_test' });
184
+ freshCapture('ui_event', {});
185
+ // Client mode doesn't auto-flush
186
+ expect(fetchSpy).not.toHaveBeenCalled();
187
+ freshFlush();
188
+ const call = fetchSpy.mock.calls[0];
189
+ if (!call?.[1]) {
190
+ throw new Error('Expected fetch call');
191
+ }
192
+ const body = JSON.parse(call[1].body);
193
+ expect(body[0].runtime).toBe('client');
194
+ global.window = originalWindow;
195
+ });
196
+ });
197
+ describe('client mode features', () => {
198
+ it('uses sendBeacon when available', async () => {
199
+ const originalWindow = global.window;
200
+ const sendBeaconSpy = vi.fn().mockReturnValue(true);
201
+ // Create a proper mock window with sendBeacon on it
202
+ global.window = {
203
+ addEventListener: vi.fn(),
204
+ navigator: {
205
+ sendBeacon: sendBeaconSpy,
206
+ },
207
+ };
208
+ vi.resetModules();
209
+ const { init: freshInit, capture: freshCapture, flush: freshFlush, } = await import('./client.js');
210
+ freshInit({ projectId: 'if_p_test' });
211
+ freshCapture('ui_event', {});
212
+ freshFlush();
213
+ expect(sendBeaconSpy).toHaveBeenCalledWith('https://ingest.interfere.com/v0', expect.any(String));
214
+ global.window = originalWindow;
215
+ });
216
+ it('sets up auto-flush timer in client mode', async () => {
217
+ const originalWindow = global.window;
218
+ global.window = { addEventListener: vi.fn() };
219
+ vi.resetModules();
220
+ const { init: freshInit } = await import('./client.js');
221
+ freshInit({ projectId: 'if_p_test', flushInterval: 1000 });
222
+ expect(global.window.addEventListener).toHaveBeenCalledWith('beforeunload', expect.any(Function));
223
+ global.window = originalWindow;
224
+ });
225
+ });
226
+ });
227
+ //# sourceMappingURL=client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.test.js","sourceRoot":"","sources":["../../src/core/client.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare const encode: (data: unknown) => Uint8Array;
2
+ export declare const decode: (data: Uint8Array) => unknown;
3
+ //# sourceMappingURL=encoders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoders.d.ts","sourceRoot":"","sources":["../../src/core/encoders.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,MAAM,GAAI,MAAM,OAAO,KAAG,UACD,CAAC;AACvC,eAAO,MAAM,MAAM,GAAI,MAAM,UAAU,KAAG,OACR,CAAC"}
@@ -0,0 +1,5 @@
1
+ const encoder = new TextEncoder();
2
+ const decoder = new TextDecoder();
3
+ export const encode = (data) => encoder.encode(JSON.stringify(data));
4
+ export const decode = (data) => JSON.parse(decoder.decode(data));
5
+ //# sourceMappingURL=encoders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoders.js","sourceRoot":"","sources":["../../src/core/encoders.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAClC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAElC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAa,EAAc,EAAE,CAClD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AACvC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAgB,EAAW,EAAE,CAClD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=encoders.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoders.test.d.ts","sourceRoot":"","sources":["../../src/core/encoders.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,55 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { decode, encode } from './encoders.js';
3
+ describe('encoders', () => {
4
+ describe('encode', () => {
5
+ it('encodes strings to Uint8Array', () => {
6
+ const result = encode('hello');
7
+ expect(result).toBeInstanceOf(Uint8Array);
8
+ expect(new TextDecoder().decode(result)).toBe('"hello"');
9
+ });
10
+ it('encodes objects to Uint8Array', () => {
11
+ const obj = { foo: 'bar', num: 123 };
12
+ const result = encode(obj);
13
+ expect(result).toBeInstanceOf(Uint8Array);
14
+ expect(new TextDecoder().decode(result)).toBe('{"foo":"bar","num":123}');
15
+ });
16
+ it('encodes arrays to Uint8Array', () => {
17
+ const arr = [1, 2, 3];
18
+ const result = encode(arr);
19
+ expect(result).toBeInstanceOf(Uint8Array);
20
+ expect(new TextDecoder().decode(result)).toBe('[1,2,3]');
21
+ });
22
+ it('encodes null', () => {
23
+ expect(new TextDecoder().decode(encode(null))).toBe('null');
24
+ });
25
+ it('handles undefined by encoding as null', () => {
26
+ // JSON.stringify(undefined) returns undefined, which becomes empty string
27
+ // This is expected behavior for JSON
28
+ const result = encode(undefined);
29
+ expect(result).toBeInstanceOf(Uint8Array);
30
+ });
31
+ });
32
+ describe('decode', () => {
33
+ it('decodes Uint8Array back to original values', () => {
34
+ const testCases = [
35
+ 'hello',
36
+ { foo: 'bar', num: 123 },
37
+ [1, 2, 3],
38
+ null,
39
+ true,
40
+ false,
41
+ 42,
42
+ ];
43
+ for (const original of testCases) {
44
+ const encoded = encode(original);
45
+ const decoded = decode(encoded);
46
+ expect(decoded).toEqual(original);
47
+ }
48
+ });
49
+ it('throws on invalid JSON', () => {
50
+ const invalidJson = new TextEncoder().encode('not valid json');
51
+ expect(() => decode(invalidJson)).toThrow();
52
+ });
53
+ });
54
+ });
55
+ //# sourceMappingURL=encoders.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoders.test.js","sourceRoot":"","sources":["../../src/core/encoders.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { capture, flush, getSessionId, init } from './client.js';
2
+ export { decode, encode } from './encoders.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { capture, flush, getSessionId, init } from './client.js';
2
+ export { decode, encode } from './encoders.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Config } from '@interfere/types';
2
+ import type { NextRequest } from 'next/server';
3
+ import { NextResponse } from 'next/server';
4
+ type EdgeHandler = (req: NextRequest) => Promise<NextResponse | Response>;
5
+ export declare function withInterfereEdge(handler: EdgeHandler, edgeConfig?: Partial<Config>): EdgeHandler;
6
+ export declare function interfereMiddleware(req: NextRequest): Promise<NextResponse>;
7
+ export declare const config: {
8
+ matcher: string[];
9
+ };
10
+ export {};
11
+ //# sourceMappingURL=edge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge.d.ts","sourceRoot":"","sources":["../../src/edge/edge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,CAAC;AAE1E,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,UAAU,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAC3B,WAAW,CAmCb;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,YAAY,CAAC,CAGvB;AAED,eAAO,MAAM,MAAM;;CAElB,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { capture, init } from '../core/index.js';
3
+ export function withInterfereEdge(handler, edgeConfig) {
4
+ return async (req) => {
5
+ const start = Date.now();
6
+ const sessionId = req.headers.get('x-interfere-session') || undefined;
7
+ if (edgeConfig || sessionId) {
8
+ init({ ...edgeConfig, sessionId });
9
+ }
10
+ try {
11
+ const response = await handler(req);
12
+ const latency = Date.now() - start;
13
+ capture('edge_req', {
14
+ method: req.method,
15
+ url: req.url,
16
+ latency,
17
+ status: response.status,
18
+ });
19
+ return response;
20
+ }
21
+ catch (error) {
22
+ const latency = Date.now() - start;
23
+ capture('edge_error', {
24
+ method: req.method,
25
+ url: req.url,
26
+ latency,
27
+ error: error instanceof Error ? error.message : String(error),
28
+ stack: error instanceof Error ? error.stack : undefined,
29
+ });
30
+ throw error;
31
+ }
32
+ };
33
+ }
34
+ export async function interfereMiddleware(req) {
35
+ const wrapped = withInterfereEdge(async () => NextResponse.next());
36
+ return (await wrapped(req));
37
+ }
38
+ export const config = {
39
+ matcher: ['/(?!_next/static|_next/image|favicon.ico).*'],
40
+ };
41
+ //# sourceMappingURL=edge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge.js","sourceRoot":"","sources":["../../src/edge/edge.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAIjD,MAAM,UAAU,iBAAiB,CAC/B,OAAoB,EACpB,UAA4B;IAE5B,OAAO,KAAK,EAAE,GAAgB,EAAE,EAAE;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,SAAS,CAAC;QAEtE,IAAI,UAAU,IAAI,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,EAAE,GAAG,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YAEnC,OAAO,CAAC,UAAU,EAAE;gBAClB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,OAAO;gBACP,MAAM,EAAE,QAAQ,CAAC,MAAM;aACxB,CAAC,CAAC;YAEH,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YAEnC,OAAO,CAAC,YAAY,EAAE;gBACpB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,OAAO;gBACP,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;gBAC7D,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;aACxD,CAAC,CAAC;YAEH,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAAgB;IAEhB,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC;IACnE,OAAO,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,CAAiB,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,OAAO,EAAE,CAAC,6CAA6C,CAAC;CACzD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=edge.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge.test.d.ts","sourceRoot":"","sources":["../../src/edge/edge.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,109 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { capture, init } from '../core/index.js';
4
+ import { config, interfereMiddleware, withInterfereEdge } from './edge.js';
5
+ vi.mock('../core/index.js', () => ({
6
+ init: vi.fn(),
7
+ capture: vi.fn(),
8
+ }));
9
+ // Top level regex for static asset matching
10
+ const STATIC_ASSET_REGEX = /^\/(?!_next\/static|_next\/image|favicon\.ico).*/;
11
+ describe('withInterfereEdge', () => {
12
+ beforeEach(() => {
13
+ vi.clearAllMocks();
14
+ });
15
+ it('wraps handler and captures successful requests', async () => {
16
+ const mockResponse = NextResponse.json({ success: true });
17
+ const handler = vi.fn().mockResolvedValue(mockResponse);
18
+ const wrapped = withInterfereEdge(handler);
19
+ const req = new NextRequest('http://localhost/api/edge', {
20
+ method: 'POST',
21
+ });
22
+ const response = await wrapped(req);
23
+ expect(handler).toHaveBeenCalledWith(req);
24
+ expect(response).toBe(mockResponse);
25
+ expect(capture).toHaveBeenCalledWith('edge_req', {
26
+ method: 'POST',
27
+ url: 'http://localhost/api/edge',
28
+ latency: expect.any(Number),
29
+ status: 200,
30
+ });
31
+ });
32
+ it('captures errors and rethrows them', async () => {
33
+ const testError = new Error('Edge error');
34
+ const handler = vi.fn().mockRejectedValue(testError);
35
+ const wrapped = withInterfereEdge(handler);
36
+ const req = new NextRequest('http://localhost/api/edge');
37
+ await expect(wrapped(req)).rejects.toThrow('Edge error');
38
+ expect(capture).toHaveBeenCalledWith('edge_error', {
39
+ method: 'GET',
40
+ url: 'http://localhost/api/edge',
41
+ latency: expect.any(Number),
42
+ error: 'Edge error',
43
+ stack: expect.stringContaining('Error: Edge error'),
44
+ });
45
+ });
46
+ it('uses session ID from headers', async () => {
47
+ const handler = vi.fn().mockResolvedValue(NextResponse.json({}));
48
+ const wrapped = withInterfereEdge(handler);
49
+ const req = new NextRequest('http://localhost/api/edge', {
50
+ headers: {
51
+ 'x-interfere-session': 'edge-session-456',
52
+ },
53
+ });
54
+ await wrapped(req);
55
+ expect(init).toHaveBeenCalledWith({
56
+ sessionId: 'edge-session-456',
57
+ });
58
+ });
59
+ it('initializes with edge config if provided', async () => {
60
+ const edgeConfig = { projectId: 'if_p_edge', env: 'production' };
61
+ const handler = vi.fn().mockResolvedValue(NextResponse.json({}));
62
+ const wrapped = withInterfereEdge(handler, edgeConfig);
63
+ await wrapped(new NextRequest('http://localhost/api/edge'));
64
+ expect(init).toHaveBeenCalledWith(edgeConfig);
65
+ });
66
+ });
67
+ describe('interfereMiddleware', () => {
68
+ beforeEach(() => {
69
+ vi.clearAllMocks();
70
+ });
71
+ it('returns NextResponse.next() for requests', async () => {
72
+ const req = new NextRequest('http://localhost/some-page');
73
+ const response = await interfereMiddleware(req);
74
+ expect(response).toBeInstanceOf(NextResponse);
75
+ expect(response.headers.get('x-middleware-next')).toBe('1');
76
+ });
77
+ it('captures edge requests', async () => {
78
+ const req = new NextRequest('http://localhost/some-page');
79
+ await interfereMiddleware(req);
80
+ expect(capture).toHaveBeenCalledWith('edge_req', {
81
+ method: 'GET',
82
+ url: 'http://localhost/some-page',
83
+ latency: expect.any(Number),
84
+ status: 200,
85
+ });
86
+ });
87
+ });
88
+ describe('config export', () => {
89
+ it('has correct matcher pattern', () => {
90
+ expect(config).toEqual({
91
+ matcher: ['/(?!_next/static|_next/image|favicon.ico).*'],
92
+ });
93
+ });
94
+ it('excludes static assets', () => {
95
+ const matcher = config.matcher?.[0];
96
+ if (!matcher) {
97
+ throw new Error('Matcher not found');
98
+ }
99
+ // Should match regular routes
100
+ expect('/api/users'.match(STATIC_ASSET_REGEX)).toBeTruthy();
101
+ expect('/dashboard'.match(STATIC_ASSET_REGEX)).toBeTruthy();
102
+ expect('/'.match(STATIC_ASSET_REGEX)).toBeTruthy();
103
+ // Should not match static assets
104
+ expect('/_next/static/chunk.js'.match(STATIC_ASSET_REGEX)).toBeFalsy();
105
+ expect('/_next/image'.match(STATIC_ASSET_REGEX)).toBeFalsy();
106
+ expect('/favicon.ico'.match(STATIC_ASSET_REGEX)).toBeFalsy();
107
+ });
108
+ });
109
+ //# sourceMappingURL=edge.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge.test.js","sourceRoot":"","sources":["../../src/edge/edge.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { config, interfereMiddleware, withInterfereEdge } from './edge.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/edge/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { config, interfereMiddleware, withInterfereEdge } from './edge.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/edge/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { capture, flush, getSessionId, init } from './core/index.js';
2
+ //# 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,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { capture, flush, getSessionId, init } from './core/index.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { InterfereProvider, useInterfere } from './provider.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { InterfereProvider, useInterfere } from './provider.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Config } from '@interfere/types';
2
+ import { type ReactNode } from 'react';
3
+ import { capture, flush, getSessionId } from '../core/index.js';
4
+ interface InterfereProviderProps {
5
+ cfg: Partial<Config>;
6
+ children: ReactNode;
7
+ }
8
+ export declare function InterfereProvider({ cfg, children }: InterfereProviderProps): import("react").JSX.Element;
9
+ export declare function useInterfere(): {
10
+ capture: typeof capture;
11
+ flush: typeof flush;
12
+ getSessionId: typeof getSessionId;
13
+ };
14
+ export {};
15
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/react/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAiB,KAAK,SAAS,EAAyB,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAQ,MAAM,kBAAkB,CAAC;AAQtE,UAAU,sBAAsB;IAC9B,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,sBAAsB,+BAW1E;AAED,wBAAgB,YAAY;aAvBjB,OAAO,OAAO;WAChB,OAAO,KAAK;kBACL,OAAO,YAAY;EA2BlC"}
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { createContext, useContext, useEffect } from 'react';
3
+ import { capture, flush, getSessionId, init } from '../core/index.js';
4
+ const InterfereContext = createContext(null);
5
+ export function InterfereProvider({ cfg, children }) {
6
+ useEffect(() => {
7
+ init(cfg);
8
+ return () => flush();
9
+ }, [cfg]);
10
+ return (<InterfereContext.Provider value={{ capture, flush, getSessionId }}>
11
+ {children}
12
+ </InterfereContext.Provider>);
13
+ }
14
+ export function useInterfere() {
15
+ const context = useContext(InterfereContext);
16
+ if (!context) {
17
+ throw new Error('useInterfere() must be used within InterfereProvider');
18
+ }
19
+ return context;
20
+ }
21
+ //# sourceMappingURL=provider.jsx.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.jsx","sourceRoot":"","sources":["../../src/react/provider.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAGb,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEtE,MAAM,gBAAgB,GAAG,aAAa,CAI5B,IAAI,CAAC,CAAC;AAOhB,MAAM,UAAU,iBAAiB,CAAC,EAAE,GAAG,EAAE,QAAQ,EAA0B;IACzE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,OAAO,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAEV,OAAO,CACL,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CACjE;MAAA,CAAC,QAAQ,CACX;IAAA,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAC7B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,OAAO,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { withInterfereApi } from './server.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { withInterfereApi } from './server.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Config } from '@interfere/types';
2
+ import type { NextRequest, NextResponse } from 'next/server';
3
+ type Handler = (req: NextRequest) => Promise<NextResponse | Response>;
4
+ export declare function withInterfereApi(handler: Handler, config?: Partial<Config>): Handler;
5
+ export {};
6
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG7D,KAAK,OAAO,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC,CAAC;AAEtE,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GACvB,OAAO,CAoCT"}
@@ -0,0 +1,35 @@
1
+ import { headers } from 'next/headers';
2
+ import { capture, init } from '../core/index.js';
3
+ export function withInterfereApi(handler, config) {
4
+ return async (req) => {
5
+ const start = Date.now();
6
+ const headersList = await headers();
7
+ const sessionId = headersList.get('x-interfere-session') || undefined;
8
+ if (config || sessionId) {
9
+ init({ ...config, sessionId });
10
+ }
11
+ try {
12
+ const response = await handler(req);
13
+ const latency = Date.now() - start;
14
+ capture('server_req', {
15
+ method: req.method,
16
+ url: req.url,
17
+ latency,
18
+ status: response.status,
19
+ });
20
+ return response;
21
+ }
22
+ catch (error) {
23
+ const latency = Date.now() - start;
24
+ capture('server_error', {
25
+ method: req.method,
26
+ url: req.url,
27
+ latency,
28
+ error: error instanceof Error ? error.message : String(error),
29
+ stack: error instanceof Error ? error.stack : undefined,
30
+ });
31
+ throw error;
32
+ }
33
+ };
34
+ }
35
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAIjD,MAAM,UAAU,gBAAgB,CAC9B,OAAgB,EAChB,MAAwB;IAExB,OAAO,KAAK,EAAE,GAAgB,EAAE,EAAE;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,SAAS,CAAC;QAEtE,IAAI,MAAM,IAAI,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YAEnC,OAAO,CAAC,YAAY,EAAE;gBACpB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,OAAO;gBACP,MAAM,EAAE,QAAQ,CAAC,MAAM;aACxB,CAAC,CAAC;YAEH,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YAEnC,OAAO,CAAC,cAAc,EAAE;gBACtB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,OAAO;gBACP,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;gBAC7D,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;aACxD,CAAC,CAAC;YAEH,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.test.d.ts","sourceRoot":"","sources":["../../src/server/server.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,88 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { capture, init } from '../core/index.js';
4
+ import { withInterfereApi } from './server.js';
5
+ vi.mock('../core/index.js', () => ({
6
+ init: vi.fn(),
7
+ capture: vi.fn(),
8
+ }));
9
+ vi.mock('next/headers', () => ({
10
+ headers: vi.fn(() => ({
11
+ get: vi.fn(() => null),
12
+ })),
13
+ }));
14
+ describe('withInterfereApi', () => {
15
+ beforeEach(() => {
16
+ vi.clearAllMocks();
17
+ });
18
+ it('wraps handler and captures successful requests', async () => {
19
+ const mockResponse = new NextResponse('Success', { status: 200 });
20
+ const handler = vi.fn().mockResolvedValue(mockResponse);
21
+ const wrapped = withInterfereApi(handler);
22
+ const req = new NextRequest('http://localhost/api/test', {
23
+ method: 'POST',
24
+ });
25
+ const response = await wrapped(req);
26
+ expect(handler).toHaveBeenCalledWith(req);
27
+ expect(response).toBe(mockResponse);
28
+ expect(capture).toHaveBeenCalledWith('server_req', {
29
+ method: 'POST',
30
+ url: 'http://localhost/api/test',
31
+ latency: expect.any(Number),
32
+ status: 200,
33
+ });
34
+ });
35
+ it('captures errors and rethrows them', async () => {
36
+ const testError = new Error('Test error');
37
+ const handler = vi.fn().mockRejectedValue(testError);
38
+ const wrapped = withInterfereApi(handler);
39
+ const req = new NextRequest('http://localhost/api/test');
40
+ await expect(wrapped(req)).rejects.toThrow('Test error');
41
+ expect(capture).toHaveBeenCalledWith('server_error', {
42
+ method: 'GET',
43
+ url: 'http://localhost/api/test',
44
+ latency: expect.any(Number),
45
+ error: 'Test error',
46
+ stack: expect.stringContaining('Error: Test error'),
47
+ });
48
+ });
49
+ it('uses session ID from headers', async () => {
50
+ const { headers } = await import('next/headers');
51
+ vi.mocked(headers).mockResolvedValue({
52
+ get: vi.fn((key) => key === 'x-interfere-session' ? 'session-123' : null),
53
+ });
54
+ const handler = vi.fn().mockResolvedValue(new NextResponse());
55
+ const wrapped = withInterfereApi(handler);
56
+ await wrapped(new NextRequest('http://localhost/api/test'));
57
+ expect(init).toHaveBeenCalledWith({
58
+ sessionId: 'session-123',
59
+ });
60
+ });
61
+ it('initializes with config if provided', async () => {
62
+ // Reset headers mock to return null for session ID
63
+ const { headers } = await import('next/headers');
64
+ vi.mocked(headers).mockResolvedValue({
65
+ get: vi.fn(() => null),
66
+ });
67
+ const config = { projectId: 'if_p_test', env: 'production' };
68
+ const handler = vi.fn().mockResolvedValue(new NextResponse());
69
+ const wrapped = withInterfereApi(handler, config);
70
+ await wrapped(new NextRequest('http://localhost/api/test'));
71
+ expect(init).toHaveBeenCalledWith(config);
72
+ });
73
+ it('measures latency accurately', async () => {
74
+ let capturedLatency = 0;
75
+ vi.mocked(capture).mockImplementation((_type, payload) => {
76
+ capturedLatency = payload.latency;
77
+ });
78
+ const handler = vi.fn().mockImplementation(async () => {
79
+ await new Promise((resolve) => setTimeout(resolve, 50));
80
+ return new NextResponse();
81
+ });
82
+ const wrapped = withInterfereApi(handler);
83
+ await wrapped(new NextRequest('http://localhost/api/test'));
84
+ expect(capturedLatency).toBeGreaterThanOrEqual(50);
85
+ expect(capturedLatency).toBeLessThan(100);
86
+ });
87
+ });
88
+ //# sourceMappingURL=server.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.test.js","sourceRoot":"","sources":["../../src/server/server.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interfere/next",
3
- "version": "0.0.7-32ca917f3c6af1d90d7b0a54eb0b5dfa3b9d2305",
3
+ "version": "0.0.8",
4
4
  "license": "MIT",
5
5
  "description": "Build apps that never break.",
6
6
  "keywords": [