@ogcio/o11y-sdk-react 0.4.2 → 0.6.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 (106) hide show
  1. package/dist/sdk-core/index.d.ts +2 -0
  2. package/dist/sdk-core/index.d.ts.map +1 -0
  3. package/dist/sdk-core/index.js +2 -0
  4. package/dist/sdk-core/index.js.map +1 -0
  5. package/dist/sdk-core/lib/index.d.ts +3 -0
  6. package/dist/sdk-core/lib/index.d.ts.map +1 -0
  7. package/dist/sdk-core/lib/index.js +3 -0
  8. package/dist/sdk-core/lib/index.js.map +1 -0
  9. package/dist/sdk-core/lib/redaction/basic-redactor.d.ts +8 -0
  10. package/dist/sdk-core/lib/redaction/basic-redactor.d.ts.map +1 -0
  11. package/dist/sdk-core/lib/redaction/basic-redactor.js +19 -0
  12. package/dist/sdk-core/lib/redaction/basic-redactor.js.map +1 -0
  13. package/dist/sdk-core/lib/redaction/email-redactor.d.ts +9 -0
  14. package/dist/sdk-core/lib/redaction/email-redactor.d.ts.map +1 -0
  15. package/dist/sdk-core/lib/redaction/email-redactor.js +18 -0
  16. package/dist/sdk-core/lib/redaction/email-redactor.js.map +1 -0
  17. package/dist/sdk-core/lib/redaction/index.d.ts +10 -0
  18. package/dist/sdk-core/lib/redaction/index.d.ts.map +1 -0
  19. package/dist/sdk-core/lib/redaction/index.js +5 -0
  20. package/dist/sdk-core/lib/redaction/index.js.map +1 -0
  21. package/dist/sdk-core/lib/redaction/ip-redactor.d.ts +10 -0
  22. package/dist/sdk-core/lib/redaction/ip-redactor.d.ts.map +1 -0
  23. package/dist/sdk-core/lib/redaction/ip-redactor.js +24 -0
  24. package/dist/sdk-core/lib/redaction/ip-redactor.js.map +1 -0
  25. package/dist/sdk-core/lib/redaction/ppsn-redactor.d.ts +9 -0
  26. package/dist/sdk-core/lib/redaction/ppsn-redactor.d.ts.map +1 -0
  27. package/dist/sdk-core/lib/redaction/ppsn-redactor.js +18 -0
  28. package/dist/sdk-core/lib/redaction/ppsn-redactor.js.map +1 -0
  29. package/dist/sdk-core/lib/utils/data-structures.d.ts +16 -0
  30. package/dist/sdk-core/lib/utils/data-structures.d.ts.map +1 -0
  31. package/dist/sdk-core/lib/utils/data-structures.js +58 -0
  32. package/dist/sdk-core/lib/utils/data-structures.js.map +1 -0
  33. package/dist/sdk-core/lib/utils/index.d.ts +3 -0
  34. package/dist/sdk-core/lib/utils/index.d.ts.map +1 -0
  35. package/dist/sdk-core/lib/utils/index.js +3 -0
  36. package/dist/sdk-core/lib/utils/index.js.map +1 -0
  37. package/dist/sdk-core/lib/utils/string-decoding.d.ts +8 -0
  38. package/dist/sdk-core/lib/utils/string-decoding.d.ts.map +1 -0
  39. package/dist/sdk-core/lib/utils/string-decoding.js +23 -0
  40. package/dist/sdk-core/lib/utils/string-decoding.js.map +1 -0
  41. package/dist/sdk-react/index.d.ts +7 -0
  42. package/dist/sdk-react/index.d.ts.map +1 -0
  43. package/dist/{index.js → sdk-react/index.js} +1 -0
  44. package/dist/sdk-react/index.js.map +1 -0
  45. package/dist/{lib → sdk-react/lib}/index.d.ts +12 -0
  46. package/dist/sdk-react/lib/index.d.ts.map +1 -0
  47. package/dist/sdk-react/lib/index.js +2 -0
  48. package/dist/sdk-react/lib/index.js.map +1 -0
  49. package/dist/{lib → sdk-react/lib}/instrumentation.faro.d.ts +1 -0
  50. package/dist/sdk-react/lib/instrumentation.faro.d.ts.map +1 -0
  51. package/dist/{lib → sdk-react/lib}/instrumentation.faro.js +7 -1
  52. package/dist/sdk-react/lib/instrumentation.faro.js.map +1 -0
  53. package/dist/sdk-react/lib/internals/hooks.d.ts +4 -0
  54. package/dist/sdk-react/lib/internals/hooks.d.ts.map +1 -0
  55. package/dist/sdk-react/lib/internals/hooks.js +8 -0
  56. package/dist/sdk-react/lib/internals/hooks.js.map +1 -0
  57. package/dist/sdk-react/lib/internals/redaction/redactors/email.d.ts +6 -0
  58. package/dist/sdk-react/lib/internals/redaction/redactors/email.d.ts.map +1 -0
  59. package/dist/sdk-react/lib/internals/redaction/redactors/email.js +22 -0
  60. package/dist/sdk-react/lib/internals/redaction/redactors/email.js.map +1 -0
  61. package/dist/sdk-react/lib/internals/redaction/redactors/index.d.ts +5 -0
  62. package/dist/sdk-react/lib/internals/redaction/redactors/index.d.ts.map +1 -0
  63. package/dist/sdk-react/lib/internals/redaction/redactors/index.js +9 -0
  64. package/dist/sdk-react/lib/internals/redaction/redactors/index.js.map +1 -0
  65. package/dist/sdk-react/lib/internals/redaction/redactors/ip.d.ts +6 -0
  66. package/dist/sdk-react/lib/internals/redaction/redactors/ip.d.ts.map +1 -0
  67. package/dist/sdk-react/lib/internals/redaction/redactors/ip.js +23 -0
  68. package/dist/sdk-react/lib/internals/redaction/redactors/ip.js.map +1 -0
  69. package/dist/sdk-react/lib/internals/redaction/redactors/ppsn.d.ts +8 -0
  70. package/dist/sdk-react/lib/internals/redaction/redactors/ppsn.d.ts.map +1 -0
  71. package/dist/sdk-react/lib/internals/redaction/redactors/ppsn.js +21 -0
  72. package/dist/sdk-react/lib/internals/redaction/redactors/ppsn.js.map +1 -0
  73. package/dist/{vitest.config.d.ts → sdk-react/vitest.config.d.ts} +1 -0
  74. package/dist/sdk-react/vitest.config.d.ts.map +1 -0
  75. package/dist/{vitest.config.js → sdk-react/vitest.config.js} +3 -2
  76. package/dist/sdk-react/vitest.config.js.map +1 -0
  77. package/package.json +19 -15
  78. package/CHANGELOG.md +0 -83
  79. package/dist/index.d.ts +0 -5
  80. package/dist/lib/index.js +0 -1
  81. package/dist/lib/internals/hooks.d.ts +0 -3
  82. package/dist/lib/internals/hooks.js +0 -34
  83. package/dist/lib/internals/redaction/pii-detection.d.ts +0 -26
  84. package/dist/lib/internals/redaction/pii-detection.js +0 -47
  85. package/dist/lib/internals/redaction/redactors/email.d.ts +0 -8
  86. package/dist/lib/internals/redaction/redactors/email.js +0 -55
  87. package/dist/lib/internals/redaction/redactors/index.d.ts +0 -4
  88. package/dist/lib/internals/redaction/redactors/index.js +0 -6
  89. package/dist/lib/internals/redaction/redactors/ip.d.ts +0 -10
  90. package/dist/lib/internals/redaction/redactors/ip.js +0 -61
  91. package/index.ts +0 -10
  92. package/lib/index.ts +0 -63
  93. package/lib/instrumentation.faro.ts +0 -102
  94. package/lib/internals/hooks.ts +0 -55
  95. package/lib/internals/redaction/pii-detection.ts +0 -64
  96. package/lib/internals/redaction/redactors/email.ts +0 -68
  97. package/lib/internals/redaction/redactors/index.ts +0 -12
  98. package/lib/internals/redaction/redactors/ip.ts +0 -78
  99. package/test/config.test.ts +0 -215
  100. package/test/lib/hooks.test.ts +0 -150
  101. package/test/lib/internals/redactors/email.test.ts +0 -81
  102. package/test/lib/internals/redactors/ip.test.ts +0 -86
  103. package/test/lib/pii-detection.test.ts +0 -179
  104. package/test/validation.test.ts +0 -65
  105. package/tsconfig.json +0 -14
  106. package/vitest.config.ts +0 -26
@@ -1,150 +0,0 @@
1
- import {
2
- LogEvent,
3
- LogLevel,
4
- TransportItem,
5
- TransportItemType,
6
- } from "@grafana/faro-web-sdk";
7
- import { beforeEach, describe, expect, it, vi } from "vitest";
8
- import { _beforeSend } from "../../lib/internals/hooks";
9
-
10
- // Mock faro.api.pushMeasurement
11
- vi.mock("@grafana/faro-web-sdk", async () => {
12
- const actual = await vi.importActual<any>("@grafana/faro-web-sdk");
13
- return {
14
- ...actual,
15
- faro: {
16
- api: {
17
- pushMeasurement: vi.fn(),
18
- },
19
- },
20
- };
21
- });
22
-
23
- import { faro } from "@grafana/faro-web-sdk";
24
- import { redactors } from "../../lib/internals/redaction/redactors";
25
-
26
- describe("_beforeSend", () => {
27
- beforeEach(() => {
28
- vi.clearAllMocks();
29
- });
30
-
31
- it("should deeply redact PII in object", () => {
32
- const item = {
33
- type: TransportItemType.LOG,
34
- payload: {
35
- level: LogLevel.INFO,
36
- context: {},
37
- timestamp: Date.now().toString(),
38
- message: "my email is bob@abc.com",
39
- },
40
- meta: {
41
- user: {
42
- email: "carol@abc.com",
43
- attributes: {
44
- alternative_email: "foo@xyz.org",
45
- },
46
- },
47
- },
48
- } satisfies TransportItem<LogEvent>;
49
-
50
- const redacted: TransportItem<LogEvent> = _beforeSend([redactors.email])(
51
- item,
52
- ) as TransportItem<LogEvent>;
53
-
54
- expect(redacted.payload.message).toBe("my email is [REDACTED EMAIL]");
55
- expect(redacted.meta.user).not.toBeNull();
56
- expect(redacted.meta.user!.email).toBe("[REDACTED EMAIL]");
57
- expect(redacted.meta.user!.attributes).toBeDefined();
58
- expect(redacted.meta.user!.attributes!["alternative_email"]).toBe(
59
- "[REDACTED EMAIL]",
60
- );
61
- expect(faro.api.pushMeasurement).toHaveBeenCalledTimes(3);
62
- });
63
-
64
- it("should redact email inside arrays of primitives", () => {
65
- const item: TransportItem<LogEvent> = {
66
- type: TransportItemType.LOG,
67
- payload: {
68
- message: "start",
69
- context: {
70
- messages: ["hello", "email: test@abc.com", "ok"],
71
- },
72
- timestamp: "123",
73
- level: LogLevel.INFO,
74
- },
75
- };
76
-
77
- const redacted = _beforeSend([redactors.email])(item)!;
78
-
79
- expect(redacted.payload.context.messages[1]).toBe(
80
- "email: [REDACTED EMAIL]",
81
- );
82
- expect(faro.api.pushMeasurement).toHaveBeenCalledOnce();
83
- });
84
-
85
- it("should redact emails in arrays of objects", () => {
86
- const item: TransportItem<LogEvent> = {
87
- type: TransportItemType.LOG,
88
- payload: {
89
- message: "checking",
90
- context: {
91
- events: [
92
- { message: "contact a@b.com" },
93
- { message: "nothing here" },
94
- { note: "try c@d.net" },
95
- ],
96
- },
97
- timestamp: "123",
98
- level: LogLevel.INFO,
99
- },
100
- };
101
-
102
- const redacted = _beforeSend([redactors.email])(item)!;
103
-
104
- expect(redacted.payload.context.events[0].message).toBe(
105
- "contact [REDACTED EMAIL]",
106
- );
107
- expect(redacted.payload.context.events[2].note).toBe(
108
- "try [REDACTED EMAIL]",
109
- );
110
- expect(faro.api.pushMeasurement).toHaveBeenCalledTimes(2);
111
- });
112
-
113
- it("should not break on nulls, undefined, numbers, booleans", () => {
114
- const item: TransportItem<LogEvent> = {
115
- type: TransportItemType.LOG,
116
- payload: {
117
- message: null as any,
118
- count: 123,
119
- active: true,
120
- description: undefined,
121
- } as any,
122
- meta: null,
123
- };
124
-
125
- const redacted = _beforeSend([redactors.email])(item)!;
126
-
127
- expect(redacted.payload.message).toBeNull();
128
- expect(redacted.payload.count).toBe(123);
129
- expect(redacted.payload.active).toBe(true);
130
- expect("description" in redacted.payload).toBe(true); // still defined as undefined
131
- expect(faro.api.pushMeasurement).not.toHaveBeenCalled();
132
- });
133
-
134
- it("should decode and redact encoded emails", () => {
135
- const item: TransportItem<LogEvent> = {
136
- type: TransportItemType.LOG,
137
- payload: {
138
- message: "contact%20user%40domain.com",
139
- level: LogLevel.INFO,
140
- context: {},
141
- timestamp: "now",
142
- },
143
- };
144
-
145
- const redacted = _beforeSend([redactors.email])(item)!;
146
-
147
- expect(redacted.payload.message).toBe("contact [REDACTED EMAIL]");
148
- expect(faro.api.pushMeasurement).toHaveBeenCalledOnce();
149
- });
150
- });
@@ -1,81 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach, afterAll } from "vitest";
2
-
3
- import { emailRedactor } from "../../../../lib/internals/redaction/redactors/email";
4
- import { ipRedactor } from "../../../../lib/internals/redaction/redactors/ip";
5
- import { faro, TransportItemType } from "@grafana/faro-web-sdk";
6
-
7
- // Mock faro.api.pushMeasurement
8
- vi.mock("@grafana/faro-web-sdk", async () => {
9
- const actual = await vi.importActual<any>("@grafana/faro-web-sdk");
10
- return {
11
- ...actual,
12
- faro: {
13
- api: {
14
- pushMeasurement: vi.fn(),
15
- },
16
- },
17
- };
18
- });
19
-
20
- describe("Email Redaction utils", () => {
21
- afterAll(() => {
22
- vi.restoreAllMocks();
23
- });
24
-
25
- describe("tracks metrics", () => {
26
- beforeEach(() => {
27
- vi.clearAllMocks();
28
- });
29
-
30
- it("redacts plain PII and tracks redaction with metric", () => {
31
- const input = "admin@example.com";
32
- const output = emailRedactor(input, "log", "string");
33
-
34
- expect(output).toBe("[REDACTED EMAIL]");
35
- expect(faro.api.pushMeasurement).toHaveBeenCalledWith(
36
- {
37
- type: "faro_o11y_pii_redaction",
38
- values: { redacted: 1 },
39
- },
40
- {
41
- context: {
42
- pii_type: "email",
43
- redaction_source: TransportItemType.LOG,
44
- pii_email_domain: "example.com",
45
- pii_format: "string",
46
- },
47
- },
48
- );
49
- });
50
-
51
- it("handles strings without PII unchanged", () => {
52
- const input = "hello world";
53
- const output = ipRedactor(input, "log", "string");
54
-
55
- expect(output).toBe("hello world");
56
- expect(faro.api.pushMeasurement).not.toHaveBeenCalled();
57
- });
58
- });
59
-
60
- describe("Redacts email addresses", () => {
61
- it.each`
62
- value | expectedRedactedValue
63
- ${"user+tag@example.com"} | ${"[REDACTED EMAIL]"}
64
- ${"user.name+tag+sorting@example.com"} | ${"[REDACTED EMAIL]"}
65
- ${"x@example.museum"} | ${"[REDACTED EMAIL]"}
66
- ${"a.b-c_d@example.co.uk"} | ${"[REDACTED EMAIL]"}
67
- ${"üser@example.de"} | ${"[REDACTED EMAIL]"}
68
- ${"john.doe@xn--exmple-cua.com"} | ${"[REDACTED EMAIL]"}
69
- ${"üser@example.de"} | ${"[REDACTED EMAIL]"}
70
- ${"plainaddress"} | ${"plainaddress"}
71
- ${"@missinglocal.org"} | ${"@missinglocal.org"}
72
- ${"user@invalid_domain.com"} | ${"user@invalid_domain.com"}
73
- `(
74
- "returns $expectedRedactedValue for value '$value'",
75
- async ({ value, expectedRedactedValue }: Record<string, string>) => {
76
- const result = emailRedactor(value, "log", "string");
77
- expect(result).toBe(expectedRedactedValue);
78
- },
79
- );
80
- });
81
- });
@@ -1,86 +0,0 @@
1
- import { describe, expect, it, vi, afterAll } from "vitest";
2
-
3
- import { faro } from "@grafana/faro-web-sdk";
4
- import { ipRedactor } from "../../../../lib/internals/redaction/redactors/ip";
5
-
6
- // Mock faro.api.pushMeasurement
7
- vi.mock("@grafana/faro-web-sdk", async () => {
8
- const actual = await vi.importActual<any>("@grafana/faro-web-sdk");
9
- return {
10
- ...actual,
11
- faro: {
12
- api: {
13
- pushMeasurement: vi.fn(),
14
- },
15
- },
16
- };
17
- });
18
-
19
- describe("IP Redaction utils", () => {
20
- afterAll(() => {
21
- vi.restoreAllMocks();
22
- });
23
-
24
- describe("tracks metrics", () => {
25
- it("redacts plain PII and tracks redaction with metric", () => {
26
- const input = "255.255.255.255";
27
- const output = ipRedactor(input, "log", "string");
28
-
29
- expect(output).toBe("[REDACTED IPV4]");
30
- expect(faro.api.pushMeasurement).toHaveBeenCalledWith(
31
- {
32
- type: "faro_o11y_pii_redaction",
33
- values: {
34
- redacted: 1,
35
- },
36
- },
37
- {
38
- context: {
39
- pii_format: "string",
40
- pii_type: "IPv4",
41
- redaction_source: "log",
42
- },
43
- },
44
- );
45
- });
46
-
47
- it("handles strings without PII unchanged", () => {
48
- const input = "hello world";
49
- const output = ipRedactor(input, "log", "string");
50
-
51
- expect(output).toBe("hello world");
52
- expect(faro.api.pushMeasurement).not.toHaveBeenCalled();
53
- });
54
- });
55
-
56
- describe("Redacts IPv4 and IPv6", () => {
57
- it.each`
58
- value | expectedRedactedValue
59
- ${"hello world"} | ${"hello world"}
60
- ${"hello 127.0.0.1"} | ${"hello [REDACTED IPV4]"}
61
- ${"127.0.0.1, hello!"} | ${"[REDACTED IPV4], hello!"}
62
- ${"127.0.0.1,127.0.0.1"} | ${"[REDACTED IPV4],[REDACTED IPV4]"}
63
- ${"127.0.0.1127.0.0.1"} | ${"127.0.0.1127.0.0.1"}
64
- ${"256.1.1.1"} | ${"256.1.1.1"}
65
- ${"0.0.0.0!"} | ${"[REDACTED IPV4]!"}
66
- ${"text0.0.0.0"} | ${"text[REDACTED IPV4]"}
67
- ${"0.0.text0.0"} | ${"0.0.text0.0"}
68
- ${"2001:0db8::1"} | ${"[REDACTED IPV6]"}
69
- ${"::1"} | ${"[REDACTED IPV6]"}
70
- ${"text::1"} | ${"text[REDACTED IPV6]"}
71
- ${"::1text"} | ${"[REDACTED IPV6]text"}
72
- ${"sentence ending with f::1"} | ${"sentence ending with [REDACTED IPV6]"}
73
- ${"sentence ending with :::1"} | ${"sentence ending with :::1"}
74
- ${"2001:0DB8:85A3:0000:0000:8A2E:0370:7334::1"} | ${"2001:0DB8:85A3:0000:0000:8A2E:0370:7334::1"}
75
- ${"2001:0DB8:85A3:0000:text:8A2E:0370:7334"} | ${"2001:0DB8:85A3:0000:text:8A2E:0370:7334"}
76
- ${"2001:0db8::12001:0db8::1"} | ${"2001:0db8::12001:0db8::1"}
77
- ${"2001:0db8::1,2001:0db8::1"} | ${"[REDACTED IPV6],[REDACTED IPV6]"}
78
- `(
79
- "returns $expectedRedactedValue for value '$value'",
80
- async ({ value, expectedRedactedValue }: Record<string, string>) => {
81
- const result = ipRedactor(value, "log", "string");
82
- expect(result).toBe(expectedRedactedValue);
83
- },
84
- );
85
- });
86
- });
@@ -1,179 +0,0 @@
1
- import { TransportItemType } from "@grafana/faro-web-sdk";
2
- import { beforeEach, describe, expect, it, vi } from "vitest";
3
- import {
4
- _cleanStringPII,
5
- _containsEncodedComponents,
6
- } from "../../lib/internals/redaction/pii-detection";
7
- import { faro } from "@grafana/faro-web-sdk";
8
- import { redactors } from "../../lib/internals/redaction/redactors";
9
-
10
- // Mock faro.api.pushMeasurement
11
- vi.mock("@grafana/faro-web-sdk", async () => {
12
- const actual = await vi.importActual<any>("@grafana/faro-web-sdk");
13
- return {
14
- ...actual,
15
- faro: {
16
- api: {
17
- pushMeasurement: vi.fn(),
18
- },
19
- },
20
- };
21
- });
22
-
23
- describe("_cleanStringPII", () => {
24
- beforeEach(() => {
25
- vi.clearAllMocks();
26
- });
27
-
28
- it("should redact email and call pushMeasurement", () => {
29
- const input = "Contact me at alice@example.com.";
30
- const result = _cleanStringPII(input, TransportItemType.LOG, [
31
- redactors.email,
32
- ]);
33
-
34
- expect(result).toBe("Contact me at [REDACTED EMAIL].");
35
- expect(faro.api.pushMeasurement).toHaveBeenCalledOnce();
36
- expect(faro.api.pushMeasurement).toHaveBeenCalledWith(
37
- {
38
- type: "faro_o11y_pii_redaction",
39
- values: { redacted: 1 },
40
- },
41
- {
42
- context: {
43
- pii_type: "email",
44
- redaction_source: TransportItemType.LOG,
45
- pii_email_domain: "example.com",
46
- pii_format: "string",
47
- },
48
- },
49
- );
50
- });
51
-
52
- it("should decode and redact URI-encoded emails", () => {
53
- const encoded = "mailto%3Ajohn.doe%40gmail.com";
54
- const result = _cleanStringPII(encoded, TransportItemType.EXCEPTION, [
55
- redactors.email,
56
- ]);
57
-
58
- expect(result).toBe("mailto:[REDACTED EMAIL]");
59
- expect(faro.api.pushMeasurement).toHaveBeenCalledOnce();
60
- });
61
-
62
- it("should return same string when no email present", () => {
63
- const input = "Just a normal message.";
64
- const result = _cleanStringPII(input, TransportItemType.LOG, [
65
- redactors.email,
66
- ]);
67
-
68
- expect(result).toBe(input);
69
- expect(faro.api.pushMeasurement).not.toHaveBeenCalled();
70
- });
71
-
72
- it("should return same string when decodeURIComponent fail", () => {
73
- const input = "mailto%john.doe%4gmail.com";
74
- const result = _cleanStringPII(input, TransportItemType.LOG, [
75
- redactors.email,
76
- ]);
77
-
78
- expect(result).toBe(input);
79
- expect(faro.api.pushMeasurement).not.toHaveBeenCalled();
80
- });
81
- });
82
-
83
- describe("_containsEncodedComponents", () => {
84
- describe("should return true for properly URL encoded strings", () => {
85
- it.each([
86
- ["hello%20world", "Space encoded as %20"],
87
- ["test%2Bvalue", "Plus sign encoded as %2B"],
88
- ["path%2Fto%2Ffile", "Forward slashes encoded"],
89
- ["user%40domain.com", "@ symbol encoded"],
90
- ["100%25%20off", "Percent and space encoded"],
91
- ["a%3Db%26c%3Dd", "Query parameters (a=b&c=d)"],
92
- ["caf%C3%A9", "UTF-8 encoded (café)"],
93
- ["price%3A%20%2410", "Colon, space, dollar ($10)"],
94
- ["%22quoted%22", "Double quotes encoded"],
95
- ["https%3A%2F%2Fexample.com", "Full URL encoded"],
96
- ["file%20name.txt", "filename encoded"],
97
- ["search%3Fq%3Dhello%20world", "Query string encoded"],
98
- ["%3C%3E%26%22%27", "HTML special chars encoded (<>&\"')"],
99
- ["%E2%9C%93", "UTF-8 checkmark (✓) encoded"],
100
- ["test%2b", "Lowercase hex"],
101
- ["%E4%B8%AD%E6%96%87", "Chinese characters encoded"],
102
- ])('should detect "%s" as URL encoded (%s)', (input, description) => {
103
- expect(_containsEncodedComponents(input)).toBe(true);
104
- });
105
- });
106
-
107
- describe("should return false for non-URL encoded strings", () => {
108
- it.each([
109
- ["test", "Simple ASCII string"],
110
- ["hello world", "Unencoded space"],
111
- ["user@domain.com", "Unencoded email"],
112
- ["simple123", "Alphanumeric only"],
113
- ["", "Empty string"],
114
- ["25%%", "Literal percent signs"],
115
- ["100% off", "Percent without hex digits"],
116
- ["test%2", "Incomplete percent encoding"],
117
- ["hello%ZZ", "Invalid hex digits"],
118
- ["test%2G", "Invalid hex digit G"],
119
- ["bad%encoding%here", "Percent without hex pairs"],
120
- ["hello%20world and more", "Partially encoded string"],
121
- ["hello%20world%21%20how%20are%20you", "Overly encoded string"],
122
- ["café", "Unicode characters (unencoded)"],
123
- ["hello+world", "Plus sign (form encoding style)"],
124
- ["test%", "Trailing percent"],
125
- ["hello%20world%", "Encoded content with trailing percent"],
126
- ["%", "Single percent"],
127
- ["%%", "Double percent"],
128
- ["normal text with % symbols", "Text with percent but no encoding"],
129
- ["price: $100%", "Currency with percent"],
130
- ["file.txt", "Simple filename"],
131
- ["path/to/file", "Unencoded path"],
132
- ["query?param=value", "Unencoded query string"],
133
- ["hello%world", "Percent without following hex"],
134
- ["test%1", "Single hex digit after percent"],
135
- ["hello%zz", "Non-hex characters after percent"],
136
- ])('should not detect "%s" as URL encoded (%s)', (input, description) => {
137
- expect(_containsEncodedComponents(input)).toBe(false);
138
- });
139
- });
140
-
141
- describe("Error handling", () => {
142
- it.each([
143
- ["%C0%80", "Overlong UTF-8 encoding (security concern)"],
144
- ["%ED%A0%80", "UTF-8 surrogate (invalid)"],
145
- ["%FF%FE", "Invalid UTF-8 sequence"],
146
- ["test%20%ZZ", "Mix of valid and invalid encoding"],
147
- ])('should handle "%s" (%s)', (input, description) => {
148
- // These should not throw errors
149
- expect(() => _containsEncodedComponents(input)).not.toThrow();
150
-
151
- // Most of these should return false due to invalid sequences
152
- // or security-related encoding issues
153
- const result = _containsEncodedComponents(input);
154
- expect(typeof result).toBe("boolean");
155
- });
156
- });
157
-
158
- describe("real-world url examples", () => {
159
- it.each([
160
- [
161
- "https%3A%2F%2Fgoogle.com%2Fsearch%3Fq%3Djavascript",
162
- "Encoded Google search URL",
163
- ],
164
- ["The%20quick%20brown%20fox", "Sentence with spaces encoded"],
165
- [
166
- "/api/user?body=here%20are%20all%20my%20secrets",
167
- "contextually encoded API path",
168
- ],
169
- ["redirect_uri=https%3A%2F%2Fapp.com%2Fcallback", "OAuth redirect URI"],
170
- ["data%3Atext%2Fplain%3Bbase64%2CSGVsbG8%3D", "Data URL encoded"],
171
- ])('real-world case: "%s" (%s)', (input, description) => {
172
- const result = _containsEncodedComponents(input);
173
-
174
- // Verify the function doesn't crash
175
- expect(typeof result).toBe("boolean");
176
- expect(result).toBe(true);
177
- });
178
- });
179
- });
@@ -1,65 +0,0 @@
1
- import { test, describe, assert, vi, expect } from "vitest";
2
- import buildFaroInstrumentation from "../lib/instrumentation.faro.js";
3
- import { Faro } from "@grafana/faro-react";
4
- import { instrumentFaro } from "../index.js";
5
-
6
- describe("validation config: should return without breaking the execution", () => {
7
- test("should call buildFaroInstrumentation without config and skip instrumentation", async () => {
8
- const consoleWarnSpy = vi
9
- .spyOn(console, "warn")
10
- .mockImplementation(vi.fn());
11
-
12
- instrumentFaro();
13
-
14
- expect(consoleWarnSpy).toHaveBeenCalled();
15
- expect(consoleWarnSpy).toHaveBeenCalledWith(
16
- "observability config not set. Skipping Faro OpenTelemetry instrumentation.",
17
- );
18
- });
19
-
20
- test("faro instrumentation: url undefined", () => {
21
- let sdk: Faro | undefined = undefined;
22
-
23
- const consoleWarnSpy = vi
24
- .spyOn(console, "warn")
25
- .mockImplementation(vi.fn());
26
- const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(vi.fn());
27
-
28
- assert.doesNotThrow(() => {
29
- sdk = buildFaroInstrumentation({
30
- collectorUrl: undefined!,
31
- });
32
- });
33
-
34
- assert.equal(sdk, undefined);
35
-
36
- expect(consoleWarnSpy).toHaveBeenCalled();
37
- expect(consoleWarnSpy).toHaveBeenCalledWith(
38
- "collectorUrl not set. Skipping Faro OpenTelemetry instrumentation.",
39
- );
40
- expect(consoleLogSpy).not.toHaveBeenCalled();
41
- });
42
-
43
- test("faro instrumentation: invalid url", () => {
44
- let sdk: Faro | undefined = undefined;
45
-
46
- const consoleErrorSpy = vi
47
- .spyOn(console, "error")
48
- .mockImplementation(vi.fn());
49
- const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(vi.fn());
50
-
51
- assert.doesNotThrow(() => {
52
- sdk = buildFaroInstrumentation({
53
- collectorUrl: "notavalidURL",
54
- });
55
- });
56
-
57
- assert.equal(sdk, undefined);
58
-
59
- expect(consoleErrorSpy).toHaveBeenCalled();
60
- expect(consoleErrorSpy).toHaveBeenCalledWith(
61
- "collectorUrl does not use a valid format. Skipping Faro OpenTelemetry instrumentation.",
62
- );
63
- expect(consoleLogSpy).not.toHaveBeenCalled();
64
- });
65
- });
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
4
- "module": "NodeNext" /* Specify what module code is generated. */,
5
- "outDir": "./dist" /* Specify an output folder for all emitted files. */,
6
- "moduleResolution": "NodeNext",
7
- "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
8
- "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
9
- "strict": true /* Enable all strict type-checking options. */,
10
- "skipLibCheck": true /* Skip type checking all .d.ts files. */,
11
- "declaration": true
12
- },
13
- "exclude": ["**/test/**"]
14
- }
package/vitest.config.ts DELETED
@@ -1,26 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- globals: true,
6
- watch: false,
7
- include: ["**/*.test.ts"],
8
- exclude: ["**/fixtures/**", "**/dist/**"],
9
- poolOptions: {
10
- threads: {
11
- maxThreads: 8,
12
- },
13
- },
14
- clearMocks: true,
15
- testTimeout: 30_000,
16
- coverage: {
17
- enabled: true,
18
- provider: "v8",
19
- reportsDirectory: "coverage",
20
- reporter: ["lcov", "cobertura"],
21
- clean: true,
22
- },
23
- reporters: ["default", ["junit", { outputFile: "test-report.xml" }]],
24
- environment: "node",
25
- },
26
- });