node-avo-inspector 1.2.0-alpha.1 → 2.0.0-beta.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 (105) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +268 -4
  3. package/package.json +17 -1
  4. package/.claude/scheduled_tasks.lock +0 -1
  5. package/.claude/settings.local.json +0 -11
  6. package/.claude/worktrees/release-1.1.1/LICENSE +0 -21
  7. package/.claude/worktrees/release-1.1.1/README.md +0 -69
  8. package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/index.js +0 -30
  9. package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/package.json +0 -15
  10. package/.claude/worktrees/release-1.1.1/examples/node-inspector-js/yarn.lock +0 -6
  11. package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/dist/index.js +0 -34
  12. package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/index.ts +0 -30
  13. package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/package.json +0 -18
  14. package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/tsconfig.json +0 -15
  15. package/.claude/worktrees/release-1.1.1/examples/node-inspector-ts/yarn.lock +0 -11
  16. package/.claude/worktrees/release-1.1.1/package-lock.json +0 -4039
  17. package/.claude/worktrees/release-1.1.1/package.json +0 -48
  18. package/.claude/worktrees/release-1.1.1/reason/AvoInspector.rei +0 -11
  19. package/.claude/worktrees/release-1.1.1/reason/NodeAvoInspector.re +0 -27
  20. package/.claude/worktrees/release-1.1.1/src/AvoDeduplicator.ts +0 -146
  21. package/.claude/worktrees/release-1.1.1/src/AvoEncryption.ts +0 -90
  22. package/.claude/worktrees/release-1.1.1/src/AvoGuid.ts +0 -9
  23. package/.claude/worktrees/release-1.1.1/src/AvoInspector.ts +0 -383
  24. package/.claude/worktrees/release-1.1.1/src/AvoInspectorEnv.ts +0 -8
  25. package/.claude/worktrees/release-1.1.1/src/AvoNetworkCallsHandler.ts +0 -267
  26. package/.claude/worktrees/release-1.1.1/src/AvoSchemaParser.ts +0 -115
  27. package/.claude/worktrees/release-1.1.1/src/AvoStreamId.ts +0 -16
  28. package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEncryption.ts +0 -515
  29. package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEventSpecCache.ts +0 -242
  30. package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoEventSpecFetcher.ts +0 -241
  31. package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoInspector.ts +0 -360
  32. package/.claude/worktrees/release-1.1.1/src/__tests__/TestAvoStreamId.ts +0 -72
  33. package/.claude/worktrees/release-1.1.1/src/__tests__/TestDeduplicator.ts +0 -191
  34. package/.claude/worktrees/release-1.1.1/src/__tests__/TestEventValidator.ts +0 -231
  35. package/.claude/worktrees/release-1.1.1/src/__tests__/TestNetworkCallsHandler.ts +0 -179
  36. package/.claude/worktrees/release-1.1.1/src/__tests__/TestNetworkCallsHandlerPublic.ts +0 -208
  37. package/.claude/worktrees/release-1.1.1/src/__tests__/TestParsing.ts +0 -153
  38. package/.claude/worktrees/release-1.1.1/src/__tests__/constants.ts +0 -63
  39. package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecCache.ts +0 -109
  40. package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecFetchTypes.ts +0 -47
  41. package/.claude/worktrees/release-1.1.1/src/eventSpec/AvoEventSpecFetcher.ts +0 -106
  42. package/.claude/worktrees/release-1.1.1/src/eventSpec/EventValidator.ts +0 -74
  43. package/.claude/worktrees/release-1.1.1/src/index.ts +0 -2
  44. package/.claude/worktrees/release-1.1.1/src/utils.ts +0 -49
  45. package/.claude/worktrees/release-1.1.1/tsconfig.json +0 -13
  46. package/.claude/worktrees/release-1.1.1/yarn.lock +0 -2310
  47. package/.github/workflows/claude.yml +0 -55
  48. package/dist/__tests__/TestAvoEncryption.d.ts +0 -1
  49. package/dist/__tests__/TestAvoEncryption.js +0 -288
  50. package/dist/__tests__/TestAvoEventSpecCache.d.ts +0 -1
  51. package/dist/__tests__/TestAvoEventSpecCache.js +0 -194
  52. package/dist/__tests__/TestAvoEventSpecFetcher.d.ts +0 -1
  53. package/dist/__tests__/TestAvoEventSpecFetcher.js +0 -219
  54. package/dist/__tests__/TestAvoInspector.d.ts +0 -1
  55. package/dist/__tests__/TestAvoInspector.js +0 -291
  56. package/dist/__tests__/TestAvoStreamId.d.ts +0 -1
  57. package/dist/__tests__/TestAvoStreamId.js +0 -55
  58. package/dist/__tests__/TestDeduplicator.d.ts +0 -1
  59. package/dist/__tests__/TestDeduplicator.js +0 -102
  60. package/dist/__tests__/TestEventValidator.d.ts +0 -1
  61. package/dist/__tests__/TestEventValidator.js +0 -196
  62. package/dist/__tests__/TestNetworkCallsHandler.d.ts +0 -1
  63. package/dist/__tests__/TestNetworkCallsHandler.js +0 -86
  64. package/dist/__tests__/TestNetworkCallsHandlerPublic.d.ts +0 -1
  65. package/dist/__tests__/TestNetworkCallsHandlerPublic.js +0 -160
  66. package/dist/__tests__/TestParsing.d.ts +0 -1
  67. package/dist/__tests__/TestParsing.js +0 -128
  68. package/dist/__tests__/constants.d.ts +0 -42
  69. package/dist/__tests__/constants.js +0 -53
  70. package/examples/node-inspector-js/index.js +0 -30
  71. package/examples/node-inspector-js/package.json +0 -15
  72. package/examples/node-inspector-js/yarn.lock +0 -6
  73. package/examples/node-inspector-ts/dist/index.js +0 -34
  74. package/examples/node-inspector-ts/index.ts +0 -30
  75. package/examples/node-inspector-ts/package.json +0 -18
  76. package/examples/node-inspector-ts/tsconfig.json +0 -15
  77. package/examples/node-inspector-ts/yarn.lock +0 -11
  78. package/reason/AvoInspector.rei +0 -11
  79. package/reason/NodeAvoInspector.re +0 -27
  80. package/src/AvoDeduplicator.ts +0 -150
  81. package/src/AvoEncryption.ts +0 -90
  82. package/src/AvoGuid.ts +0 -9
  83. package/src/AvoInspector.ts +0 -498
  84. package/src/AvoInspectorEnv.ts +0 -8
  85. package/src/AvoNetworkCallsHandler.ts +0 -340
  86. package/src/AvoSchemaParser.ts +0 -115
  87. package/src/AvoStreamId.ts +0 -16
  88. package/src/__tests__/TestAvoEncryption.ts +0 -515
  89. package/src/__tests__/TestAvoEventSpecCache.ts +0 -242
  90. package/src/__tests__/TestAvoEventSpecFetcher.ts +0 -279
  91. package/src/__tests__/TestAvoInspector.ts +0 -376
  92. package/src/__tests__/TestAvoStreamId.ts +0 -72
  93. package/src/__tests__/TestDeduplicator.ts +0 -191
  94. package/src/__tests__/TestEventValidator.ts +0 -231
  95. package/src/__tests__/TestNetworkCallsHandler.ts +0 -181
  96. package/src/__tests__/TestNetworkCallsHandlerPublic.ts +0 -208
  97. package/src/__tests__/TestParsing.ts +0 -153
  98. package/src/__tests__/constants.ts +0 -63
  99. package/src/eventSpec/AvoEventSpecCache.ts +0 -119
  100. package/src/eventSpec/AvoEventSpecFetchTypes.ts +0 -47
  101. package/src/eventSpec/AvoEventSpecFetcher.ts +0 -186
  102. package/src/eventSpec/EventValidator.ts +0 -74
  103. package/src/index.ts +0 -2
  104. package/src/utils.ts +0 -49
  105. package/tsconfig.json +0 -13
@@ -1,231 +0,0 @@
1
- import { EventValidator } from "../eventSpec/EventValidator";
2
- import { EventSpec, PropertyConstraint, PropertyValidationResult } from "../eventSpec/AvoEventSpecFetchTypes";
3
-
4
- // We need to mock safe-regex2 for some tests
5
- jest.mock("safe-regex2", () => {
6
- const actual = jest.requireActual("safe-regex2");
7
- return {
8
- __esModule: true,
9
- default: actual.default || actual,
10
- _mockable: true,
11
- };
12
- });
13
-
14
- describe("EventValidator", () => {
15
- let validator: EventValidator;
16
-
17
- beforeEach(() => {
18
- validator = new EventValidator();
19
- jest.spyOn(console, "warn").mockImplementation(() => {});
20
- });
21
-
22
- afterEach(() => {
23
- jest.restoreAllMocks();
24
- });
25
-
26
- describe("type validation", () => {
27
- test("passes when property types match spec", () => {
28
- const spec: EventSpec = {
29
- eventName: "click",
30
- properties: [
31
- { propertyName: "target", propertyType: "string" },
32
- { propertyName: "count", propertyType: "int" },
33
- ],
34
- };
35
- const eventProperties = [
36
- { propertyName: "target", propertyType: "string" },
37
- { propertyName: "count", propertyType: "int" },
38
- ];
39
-
40
- const results = validator.validate(spec, eventProperties, "evt-1");
41
- // All properties passed type check, so failedEventIds should be empty
42
- expect(results.every((r: PropertyValidationResult) => r.failedEventIds.length === 0)).toBe(true);
43
- // Bandwidth optimization: passedEventIds cleared because passed >= failed for each property
44
- expect(results.every((r: PropertyValidationResult) => r.passedEventIds.length === 0)).toBe(true);
45
- });
46
-
47
- test("fails when property type does not match spec", () => {
48
- const spec: EventSpec = {
49
- eventName: "click",
50
- properties: [{ propertyName: "count", propertyType: "int" }],
51
- };
52
- const eventProperties = [
53
- { propertyName: "count", propertyType: "string" },
54
- ];
55
-
56
- const results = validator.validate(spec, eventProperties, "evt-2");
57
- const countResult = results.find((r: PropertyValidationResult) => r.propertyName === "count");
58
- expect(countResult?.failedEventIds).toContain("evt-2");
59
- expect(countResult?.passedEventIds).not.toContain("evt-2");
60
- });
61
-
62
- test("fails for missing required properties", () => {
63
- const spec: EventSpec = {
64
- eventName: "click",
65
- properties: [
66
- { propertyName: "target", propertyType: "string" },
67
- { propertyName: "count", propertyType: "int" },
68
- ],
69
- };
70
- const eventProperties = [
71
- { propertyName: "target", propertyType: "string" },
72
- // count is missing
73
- ];
74
-
75
- const results = validator.validate(spec, eventProperties, "evt-3");
76
- const countResult = results.find((r: PropertyValidationResult) => r.propertyName === "count");
77
- expect(countResult?.failedEventIds).toContain("evt-3");
78
- });
79
- });
80
-
81
- describe("regex validation", () => {
82
- test("passes when value matches safe regex pattern (type match)", () => {
83
- const spec: EventSpec = {
84
- eventName: "navigate",
85
- properties: [
86
- {
87
- propertyName: "url",
88
- propertyType: "string",
89
- regex: "^https://",
90
- },
91
- ],
92
- };
93
- const eventProperties = [
94
- { propertyName: "url", propertyType: "string" },
95
- ];
96
-
97
- const results = validator.validate(spec, eventProperties, "evt-4");
98
- const urlResult = results.find((r: PropertyValidationResult) => r.propertyName === "url");
99
- // Type matches, property passed. Bandwidth optimization: passedEventIds cleared
100
- // because passed count (1) >= failed count (0)
101
- expect(urlResult?.failedEventIds).toEqual([]);
102
- });
103
-
104
- test("fails when value does not match safe regex pattern", () => {
105
- const spec: EventSpec = {
106
- eventName: "navigate",
107
- properties: [
108
- {
109
- propertyName: "url",
110
- propertyType: "string",
111
- regex: "^https://",
112
- },
113
- ],
114
- };
115
- const eventProperties = [
116
- { propertyName: "url", propertyType: "string", propertyValue: "http://example.com" },
117
- ];
118
-
119
- const results = validator.validate(spec, eventProperties, "evt-regex-fail");
120
- const urlResult = results.find((r: PropertyValidationResult) => r.propertyName === "url");
121
- expect(urlResult?.failedEventIds).toContain("evt-regex-fail");
122
- expect(urlResult?.passedEventIds).not.toContain("evt-regex-fail");
123
- });
124
-
125
- test("logs warning and passes type check when regex syntax is invalid", () => {
126
- const spec: EventSpec = {
127
- eventName: "filter",
128
- properties: [
129
- {
130
- propertyName: "pattern",
131
- propertyType: "string",
132
- regex: "[invalid",
133
- },
134
- ],
135
- };
136
- const eventProperties = [
137
- { propertyName: "pattern", propertyType: "string", propertyValue: "anything" },
138
- ];
139
-
140
- const results = validator.validate(spec, eventProperties, "evt-bad-regex");
141
- const patternResult = results.find((r: PropertyValidationResult) => r.propertyName === "pattern");
142
- // Invalid regex is caught, property still passes type check
143
- expect(patternResult?.failedEventIds).toEqual([]);
144
- expect(console.warn).toHaveBeenCalledWith(
145
- expect.stringContaining("[Avo Inspector] Warning")
146
- );
147
- });
148
-
149
- test("skips unsafe regex patterns with warning", () => {
150
- const spec: EventSpec = {
151
- eventName: "search",
152
- properties: [
153
- {
154
- propertyName: "query",
155
- propertyType: "string",
156
- // Catastrophic backtracking pattern
157
- regex: "(a+)+$",
158
- },
159
- ],
160
- };
161
- const eventProperties = [
162
- { propertyName: "query", propertyType: "string", propertyValue: "aaa" },
163
- ];
164
-
165
- const results = validator.validate(spec, eventProperties, "evt-5");
166
- // Unsafe regex should be skipped, property still passes type check
167
- const queryResult = results.find((r: PropertyValidationResult) => r.propertyName === "query");
168
- // Bandwidth optimization: passedEventIds cleared because passed count >= failed count
169
- expect(queryResult?.failedEventIds).toEqual([]);
170
- expect(console.warn).toHaveBeenCalledWith(
171
- expect.stringContaining("[Avo Inspector] Warning")
172
- );
173
- });
174
- });
175
-
176
- describe("extra/unexpected properties", () => {
177
- test("ignores properties present in event but not in the spec", () => {
178
- const spec: EventSpec = {
179
- eventName: "click",
180
- properties: [
181
- { propertyName: "target", propertyType: "string" },
182
- ],
183
- };
184
- const eventProperties = [
185
- { propertyName: "target", propertyType: "string" },
186
- { propertyName: "extraProp", propertyType: "int" },
187
- { propertyName: "anotherExtra", propertyType: "boolean" },
188
- ];
189
-
190
- const results = validator.validate(spec, eventProperties, "evt-extra");
191
- // Only spec properties get results — extra properties are not reported
192
- expect(results).toHaveLength(1);
193
- expect(results[0].propertyName).toBe("target");
194
- expect(results.find((r: PropertyValidationResult) => r.propertyName === "extraProp")).toBeUndefined();
195
- expect(results.find((r: PropertyValidationResult) => r.propertyName === "anotherExtra")).toBeUndefined();
196
- });
197
- });
198
-
199
- describe("bandwidth optimization", () => {
200
- test("returns passedEventIds only when strictly smaller than failedEventIds", () => {
201
- const spec: EventSpec = {
202
- eventName: "click",
203
- properties: [{ propertyName: "target", propertyType: "string" }],
204
- };
205
-
206
- // Single pass
207
- const passResults = validator.validate(
208
- spec,
209
- [{ propertyName: "target", propertyType: "string" }],
210
- "evt-pass"
211
- );
212
- const passResult = passResults.find((r: PropertyValidationResult) => r.propertyName === "target");
213
-
214
- // When there's only passed and no failed, passedEventIds count (1) > failedEventIds count (0)
215
- // So passedEventIds is NOT strictly smaller - it should be returned
216
- // But the optimization says: return passedEventIds ONLY when strictly smaller than failedEventIds
217
- // So when passedEventIds.length >= failedEventIds.length, we clear passedEventIds
218
- expect(passResult?.passedEventIds).toEqual([]);
219
-
220
- // Single fail
221
- const failResults = validator.validate(
222
- spec,
223
- [{ propertyName: "target", propertyType: "int" }],
224
- "evt-fail"
225
- );
226
- const failResult = failResults.find((r: PropertyValidationResult) => r.propertyName === "target");
227
- // failedEventIds has 1, passedEventIds has 0 -> passedEventIds is strictly smaller, return it
228
- expect(failResult?.failedEventIds).toContain("evt-fail");
229
- });
230
- });
231
- });
@@ -1,179 +0,0 @@
1
- import { AvoGuid } from "../AvoGuid";
2
- import { AvoNetworkCallsHandler, BaseBody } from "../AvoNetworkCallsHandler";
3
-
4
- import {
5
- defaultOptions,
6
- mockedReturns,
7
- } from "./constants";
8
-
9
- const inspectorVersion = process.env.npm_package_version || "";
10
-
11
- describe("NetworkCallsHandler", () => {
12
- const { apiKey, env, version } = defaultOptions;
13
- const appName = "";
14
-
15
- let networkHandler: AvoNetworkCallsHandler;
16
- let baseBody: BaseBody;
17
-
18
- const now = new Date();
19
-
20
- beforeAll(() => {
21
- // @ts-ignore
22
- jest.spyOn(global, "Date").mockImplementation(() => now);
23
-
24
- jest
25
- .spyOn(AvoGuid as any, "newGuid")
26
- .mockImplementation(() => mockedReturns.GUID);
27
-
28
- networkHandler = new AvoNetworkCallsHandler(
29
- apiKey,
30
- env,
31
- "",
32
- version,
33
- inspectorVersion
34
- );
35
-
36
- baseBody = {
37
- apiKey,
38
- appName,
39
- appVersion: version,
40
- libVersion: inspectorVersion,
41
- env,
42
- libPlatform: "node",
43
- messageId: mockedReturns.GUID,
44
- anonymousId: "",
45
- createdAt: new Date().toISOString(),
46
- samplingRate: 1.0,
47
- };
48
- });
49
-
50
- afterEach(() => {
51
- jest.clearAllMocks();
52
- });
53
-
54
- test("bodyForEventSchemaCall returns base body + event schema used for event sending from non Codegen", () => {
55
- const eventName = "event name";
56
- const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
57
-
58
- const body = networkHandler.bodyForEventSchemaCall(
59
- "",
60
- eventName,
61
- eventProperties,
62
- null,
63
- null
64
- );
65
-
66
- expect(body).toEqual({
67
- ...baseBody,
68
- anonymousId: "",
69
- type: "event",
70
- eventName,
71
- eventProperties,
72
- avoFunction: false,
73
- eventId: null,
74
- eventHash: null,
75
- });
76
- });
77
-
78
- test("bodyForEventSchemaCall returns base body + event schema used for event sending from Codegen", () => {
79
- const eventName = "event name";
80
- const eventId = "event id";
81
- const eventHash = "event hash";
82
- const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
83
-
84
- const body = networkHandler.bodyForEventSchemaCall(
85
- "",
86
- eventName,
87
- eventProperties,
88
- eventId,
89
- eventHash
90
- );
91
-
92
- expect(body).toEqual({
93
- ...baseBody,
94
- anonymousId: "",
95
- type: "event",
96
- eventName,
97
- eventProperties,
98
- avoFunction: true,
99
- eventId,
100
- eventHash,
101
- });
102
- });
103
-
104
- test("bodyForEventSchemaCall uses streamId as anonymousId", () => {
105
- const eventName = "event name";
106
- const streamId = "user-123";
107
- const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
108
-
109
- const body = networkHandler.bodyForEventSchemaCall(
110
- streamId,
111
- eventName,
112
- eventProperties,
113
- null,
114
- null
115
- );
116
-
117
- expect(body.anonymousId).toBe("user-123");
118
- });
119
-
120
- test("bodyForEventSchemaCall with rawEventProperties passes them through (no encryption in dev without key)", () => {
121
- const eventName = "event name";
122
- const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
123
- const rawEventProperties = { prop0: "hello" };
124
-
125
- const body = networkHandler.bodyForEventSchemaCall(
126
- "",
127
- eventName,
128
- eventProperties,
129
- null,
130
- null,
131
- rawEventProperties
132
- );
133
-
134
- // Without a publicEncryptionKey, encryption is not triggered,
135
- // so eventProperties should be the plain schema (not encrypted)
136
- expect(body.eventProperties).toEqual(eventProperties);
137
- expect(body.type).toBe("event");
138
- expect(body.eventName).toBe(eventName);
139
- });
140
-
141
- test("base body includes publicEncryptionKey when handler is constructed with one", () => {
142
- const handlerWithKey = new AvoNetworkCallsHandler(
143
- apiKey,
144
- env,
145
- "",
146
- version,
147
- inspectorVersion,
148
- "test-public-key-abc"
149
- );
150
-
151
- const eventName = "event name";
152
- const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
153
-
154
- const body = handlerWithKey.bodyForEventSchemaCall(
155
- "",
156
- eventName,
157
- eventProperties,
158
- null,
159
- null
160
- );
161
-
162
- expect(body.publicEncryptionKey).toBe("test-public-key-abc");
163
- });
164
-
165
- test("base body does not include publicEncryptionKey when handler is constructed without one", () => {
166
- const eventName = "event name";
167
- const eventProperties = [{ propertyName: "prop0", propertyType: "string" }];
168
-
169
- const body = networkHandler.bodyForEventSchemaCall(
170
- "",
171
- eventName,
172
- eventProperties,
173
- null,
174
- null
175
- );
176
-
177
- expect(body.publicEncryptionKey).toBeUndefined();
178
- });
179
- });
@@ -1,208 +0,0 @@
1
- import { AvoInspector } from "../AvoInspector";
2
- import { AvoNetworkCallsHandler } from "../AvoNetworkCallsHandler";
3
-
4
- import { defaultOptions } from "./constants";
5
-
6
- describe("AvoNetworkCallsHandler", () => {
7
- let inspectorCallSpy: jest.SpyInstance<any, unknown[]>;
8
- let consoleLogSpy: jest.SpyInstance<any, unknown[]>;
9
-
10
- let inspector: AvoInspector;
11
-
12
- const { apiKey, env, version, appName } = defaultOptions;
13
-
14
- const eventName = "event name";
15
- const properties = {
16
- prop0: "",
17
- prop2: false,
18
- prop3: 0,
19
- prop4: 0.0,
20
- };
21
-
22
- const happyPathInspectorCall = () => {
23
- return Promise.resolve();
24
- };
25
-
26
- beforeAll(() => {
27
- inspector = new AvoInspector(defaultOptions);
28
- inspector.enableLogging(true);
29
-
30
- inspectorCallSpy = jest
31
- .spyOn(AvoNetworkCallsHandler.prototype as any, "callInspectorWithBatchBody");
32
-
33
- consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => {});
34
- });
35
-
36
- beforeEach(() => {
37
- inspectorCallSpy.mockImplementation(happyPathInspectorCall);
38
- })
39
-
40
- afterEach(() => {
41
- jest.clearAllMocks();
42
- // @ts-ignore
43
- inspector.avoDeduplicator._clearEvents();
44
- });
45
-
46
- test("trackSchemaFromEvent sends only event call (no sessionStarted)", async () => {
47
- await inspector.trackSchemaFromEvent(eventName, properties);
48
-
49
- // Should be called once (event only, no sessionStarted)
50
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledTimes(1);
51
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledWith([
52
- expect.objectContaining({
53
- type: "event",
54
- apiKey: apiKey,
55
- appName: appName,
56
- appVersion: version,
57
- libVersion: expect.anything(),
58
- env: env,
59
- libPlatform: "node",
60
- messageId: expect.anything(),
61
- anonymousId: "",
62
- createdAt: expect.any(String),
63
- samplingRate: 1,
64
- }),
65
- ]);
66
- });
67
-
68
- test("trackSchemaFromEvent with streamId sets anonymousId to streamId", async () => {
69
- await inspector.trackSchemaFromEvent(eventName, properties, "user-stream-123");
70
-
71
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledTimes(1);
72
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledWith([
73
- expect.objectContaining({
74
- type: "event",
75
- anonymousId: "user-stream-123",
76
- }),
77
- ]);
78
- });
79
-
80
- test("trackSchemaFromEvent without streamId sets anonymousId to empty string", async () => {
81
- await inspector.trackSchemaFromEvent(eventName, properties);
82
-
83
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledTimes(1);
84
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledWith([
85
- expect.objectContaining({
86
- anonymousId: "",
87
- }),
88
- ]);
89
- });
90
-
91
- test("trackSchemaFromEvent with streamId containing ':' logs warning", async () => {
92
- const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
93
-
94
- await inspector.trackSchemaFromEvent(eventName, properties, "invalid:stream");
95
-
96
- expect(consoleWarnSpy).toHaveBeenCalledWith(
97
- "[Avo Inspector] Warning: streamId contains ':' which is not supported"
98
- );
99
-
100
- consoleWarnSpy.mockRestore();
101
- });
102
-
103
- test("_avoFunctionTrackSchemaFromEvent sends only event call (no sessionStarted)", async () => {
104
- const eventId = "testId";
105
- const eventHash = "testHash";
106
-
107
- // @ts-ignore
108
- await inspector._avoFunctionTrackSchemaFromEvent(
109
- eventName,
110
- properties,
111
- eventId,
112
- eventHash
113
- );
114
-
115
- // Should be called once (event only, no sessionStarted)
116
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledTimes(1);
117
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledWith([
118
- expect.objectContaining({
119
- type: "event",
120
- apiKey: apiKey,
121
- appName: appName,
122
- appVersion: version,
123
- libVersion: expect.anything(),
124
- env: env,
125
- libPlatform: "node",
126
- messageId: expect.anything(),
127
- anonymousId: "",
128
- createdAt: expect.any(String),
129
- }),
130
- ]);
131
- });
132
-
133
- test("an error is logged if event schema call fails and AvoInspector.shouldLog is true", async () => {
134
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
135
- inspectorCallSpy.mockRejectedValueOnce('Network error');
136
-
137
- await inspector.trackSchemaFromEvent(eventName, properties);
138
-
139
- expect(consoleErrorSpy).toHaveBeenCalledWith('Avo Inspector: schema sending failed: Network error.');
140
- consoleErrorSpy.mockRestore();
141
- });
142
-
143
- test("_avoFunctionTrackSchemaFromEvent passes empty string as anonymousId", async () => {
144
- const eventId = "testId";
145
- const eventHash = "testHash";
146
-
147
- // @ts-ignore
148
- await inspector._avoFunctionTrackSchemaFromEvent(
149
- eventName,
150
- properties,
151
- eventId,
152
- eventHash
153
- );
154
-
155
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledWith([
156
- expect.objectContaining({
157
- anonymousId: "",
158
- }),
159
- ]);
160
- });
161
-
162
- test("trackSchemaFromEvent with streamId containing ':' still sends the event", async () => {
163
- const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
164
-
165
- await inspector.trackSchemaFromEvent(eventName, properties, "bad:stream");
166
-
167
- // Warning is logged
168
- expect(consoleWarnSpy).toHaveBeenCalledWith(
169
- "[Avo Inspector] Warning: streamId contains ':' which is not supported"
170
- );
171
-
172
- // Event is still sent despite the warning
173
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledTimes(1);
174
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledWith([
175
- expect.objectContaining({
176
- type: "event",
177
- anonymousId: "bad:stream",
178
- }),
179
- ]);
180
-
181
- consoleWarnSpy.mockRestore();
182
- });
183
-
184
- test("same event with different streamIds is not deduplicated", async () => {
185
- await inspector.trackSchemaFromEvent(eventName, properties, "stream-a");
186
- await inspector.trackSchemaFromEvent(eventName, properties, "stream-b");
187
-
188
- expect(inspector.avoNetworkCallsHandler.callInspectorWithBatchBody).toHaveBeenCalledTimes(2);
189
- });
190
-
191
- test("trackSchemaFromEvent rejection contains the expected error string", async () => {
192
- const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation(() => {});
193
-
194
- // Force the catch block in trackSchemaFromEvent by making extractSchema throw
195
- jest.spyOn(inspector, "extractSchema").mockImplementation(() => {
196
- throw new Error("test explosion");
197
- });
198
-
199
- await expect(
200
- inspector.trackSchemaFromEvent(eventName, properties)
201
- ).rejects.toBe(
202
- "Avo Inspector: something went wrong. Please report to support@avo.app."
203
- );
204
-
205
- consoleErrorSpy.mockRestore();
206
- (inspector.extractSchema as jest.Mock).mockRestore();
207
- });
208
- });