@eventvisor/sdk 0.23.0 → 0.25.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventvisor/sdk",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "Eventvisor SDK for Node.js and the browser",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.mjs",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "license": "MIT",
41
41
  "dependencies": {
42
- "@eventvisor/types": "0.23.0"
42
+ "@eventvisor/types": "0.25.0"
43
43
  },
44
- "gitHead": "8e89c74cde2a35ab0cf76b00f964cf8e606d9b22"
44
+ "gitHead": "a111ef75495811e9992463bae964c43f5b3482b9"
45
45
  }
@@ -208,7 +208,7 @@ describe("sdk: instance", function () {
208
208
  };
209
209
 
210
210
  async function createEventvisorWithDatafile(datafile: DatafileContent) {
211
- const captured: Record<string, any>[] = [];
211
+ const captured: any[] = [];
212
212
  const eventvisor = createInstance({
213
213
  datafile,
214
214
  modules: [
@@ -470,4 +470,90 @@ describe("sdk: instance", function () {
470
470
  expect(captured.length).toBe(1);
471
471
  });
472
472
  });
473
+
474
+ describe("strict object schemas", function () {
475
+ it("rejects tracked event payloads with unknown object properties", async function () {
476
+ const captured: any[] = [];
477
+ const eventvisor = createInstance({
478
+ datafile: {
479
+ ...emptyDatafile,
480
+ events: {
481
+ strictEvent: {
482
+ type: "object",
483
+ properties: {
484
+ screen: {
485
+ type: "object",
486
+ properties: {
487
+ width: { type: "number" },
488
+ },
489
+ required: ["width"],
490
+ },
491
+ },
492
+ required: ["screen"],
493
+ },
494
+ },
495
+ destinations: {
496
+ test: {
497
+ transport: "test",
498
+ transforms: [
499
+ { type: "set", value: {} },
500
+ { type: "set", source: "payload", target: "payload" },
501
+ ],
502
+ },
503
+ },
504
+ },
505
+ modules: [
506
+ {
507
+ name: "test",
508
+ transport: async ({ payload }) => {
509
+ captured.push(payload);
510
+ },
511
+ },
512
+ ],
513
+ logLevel: "warn",
514
+ });
515
+
516
+ await eventvisor.onReady();
517
+
518
+ const result = await eventvisor.trackAsync("strictEvent", {
519
+ screen: {
520
+ width: 100,
521
+ height: 200,
522
+ },
523
+ } as any);
524
+
525
+ expect(result).toBeNull();
526
+ expect(captured).toEqual([]);
527
+ });
528
+
529
+ it("rejects attribute values with unknown object properties", async function () {
530
+ const eventvisor = createInstance({
531
+ datafile: {
532
+ ...emptyDatafile,
533
+ attributes: {
534
+ browser: {
535
+ type: "object",
536
+ properties: {
537
+ name: { type: "string" },
538
+ version: { type: "string" },
539
+ },
540
+ required: ["name", "version"],
541
+ },
542
+ },
543
+ },
544
+ logLevel: "warn",
545
+ });
546
+
547
+ await eventvisor.onReady();
548
+
549
+ const result = await eventvisor.setAttributeAsync("browser", {
550
+ name: "Chrome",
551
+ version: "123",
552
+ major: 123,
553
+ } as any);
554
+
555
+ expect(result).toBeNull();
556
+ expect(eventvisor.getAttributeValue("browser")).toBeNull();
557
+ });
558
+ });
473
559
  });
@@ -34,7 +34,7 @@ export interface TransportOptions {
34
34
  destinationName: DestinationName;
35
35
  eventName: EventName;
36
36
  eventLevel?: EventLevel;
37
- payload: Value; // @TODO: rename to body?
37
+ payload: Value;
38
38
  error?: Error;
39
39
  }
40
40
 
@@ -60,11 +60,11 @@ export interface ModuleDependencies {
60
60
  export interface Module {
61
61
  name: ModuleName;
62
62
 
63
- // initialize?
63
+ // @TODO: initialize?
64
64
 
65
65
  lookup?: (options: LookupOptions, deps: ModuleDependencies) => Promise<Value>;
66
66
 
67
- // transform?: (options: TransformOptions, deps: ModuleDependencies) => Promise<Value>;
67
+ // @TODO: transform?: (options: TransformOptions, deps: ModuleDependencies) => Promise<Value>;
68
68
 
69
69
  handle?: (options: HandleOptions, deps: ModuleDependencies) => Promise<void>;
70
70
 
@@ -39,6 +39,9 @@ describe("SourceResolver", () => {
39
39
  destinations: {},
40
40
  effects: {
41
41
  effect1: {
42
+ on: {
43
+ event_tracked: ["event1"],
44
+ },
42
45
  state: {
43
46
  nested: {
44
47
  value: "effect1 value",
@@ -46,6 +49,9 @@ describe("SourceResolver", () => {
46
49
  },
47
50
  },
48
51
  effect2: {
52
+ on: {
53
+ event_tracked: ["event2"],
54
+ },
49
55
  state: 123,
50
56
  },
51
57
  },
@@ -165,6 +171,12 @@ describe("SourceResolver", () => {
165
171
  },
166
172
  ),
167
173
  ).toEqual(25);
174
+
175
+ expect(
176
+ await sourceResolver.resolve("payload.screen.width", {
177
+ payload: {},
178
+ }),
179
+ ).toEqual(undefined);
168
180
  });
169
181
 
170
182
  it("should resolve attributes", async () => {
@@ -27,7 +27,13 @@ export type SourceOrigin = SourcePath & {
27
27
  };
28
28
 
29
29
  function findValueAtPath(obj: any, path: string[]): any {
30
- return path.reduce((acc, part) => acc[part], obj);
30
+ return path.reduce((acc, part) => {
31
+ if (acc === null || acc === undefined) {
32
+ return undefined;
33
+ }
34
+
35
+ return acc[part];
36
+ }, obj);
31
37
  }
32
38
 
33
39
  // @TODO: redo it with a better approach
@@ -93,7 +93,7 @@ export class Transformer {
93
93
  if (transform.type === "set") {
94
94
  if ("value" in transform) {
95
95
  result = Transformer.setValueAtPath(result, target, transform.value);
96
- } else {
96
+ } else if (sourceValue !== null && sourceValue !== undefined) {
97
97
  result = Transformer.setValueAtPath(result, target, sourceValue);
98
98
  }
99
99
  }
@@ -406,6 +406,25 @@ describe("Transformer types", () => {
406
406
  lastName: "Baggins",
407
407
  });
408
408
  });
409
+
410
+ it("does not set target when resolved source is missing", async () => {
411
+ expect(
412
+ await transformer.applyAll(
413
+ {
414
+ firstName: "Bilbo",
415
+ },
416
+ [
417
+ {
418
+ type: "set",
419
+ target: "profile.lastName",
420
+ lookup: "browser.user.lastName",
421
+ },
422
+ ],
423
+ ),
424
+ ).toEqual({
425
+ firstName: "Bilbo",
426
+ });
427
+ });
409
428
  });
410
429
 
411
430
  /**
@@ -201,7 +201,7 @@ describe("Validator", () => {
201
201
  expect(result.errors![0].message).toContain("Expected type number");
202
202
  });
203
203
 
204
- it("should allow additional properties by default", async () => {
204
+ it("should reject additional properties", async () => {
205
205
  const schema: JSONSchema = {
206
206
  type: "object",
207
207
  properties: {
@@ -210,8 +210,9 @@ describe("Validator", () => {
210
210
  };
211
211
  const result = await validate(schema, { name: "John", extra: "value" }, {});
212
212
 
213
- expect(result.valid).toBe(true);
214
- expect(result.value).toEqual({ name: "John", extra: "value" });
213
+ expect(result.valid).toBe(false);
214
+ expect(result.errors![0].path).toBe("extra");
215
+ expect(result.errors![0].message).toContain("not allowed by schema");
215
216
  });
216
217
  });
217
218
 
@@ -342,6 +343,44 @@ describe("Validator", () => {
342
343
  expect(result.errors![0].path).toBe("user.profile.age");
343
344
  });
344
345
 
346
+ it("should reject additional properties in nested objects", async () => {
347
+ const schema: JSONSchema = {
348
+ type: "object",
349
+ properties: {
350
+ user: {
351
+ type: "object",
352
+ properties: {
353
+ profile: {
354
+ type: "object",
355
+ properties: {
356
+ name: { type: "string" },
357
+ },
358
+ required: ["name"],
359
+ },
360
+ },
361
+ required: ["profile"],
362
+ },
363
+ },
364
+ };
365
+
366
+ const result = await validate(
367
+ schema,
368
+ {
369
+ user: {
370
+ profile: {
371
+ name: "John",
372
+ age: 30,
373
+ },
374
+ },
375
+ },
376
+ {},
377
+ );
378
+
379
+ expect(result.valid).toBe(false);
380
+ expect(result.errors![0].path).toBe("user.profile.age");
381
+ expect(result.errors![0].message).toContain("not allowed by schema");
382
+ });
383
+
345
384
  it("should validate arrays of objects", async () => {
346
385
  const schema: JSONSchema = {
347
386
  type: "array",
package/src/validator.ts CHANGED
@@ -187,8 +187,12 @@ function validateValue(
187
187
  validatedObj[prop] = validatedProp;
188
188
  }
189
189
  } else {
190
- // Allow additional properties by default (JSON Schema behavior)
191
- validatedObj[prop] = propValue;
190
+ errors.push({
191
+ path: path ? `${path}.${prop}` : prop,
192
+ message: `Property '${prop}' is not allowed by schema`,
193
+ schema,
194
+ value: propValue,
195
+ });
192
196
  }
193
197
  }
194
198