@debugbundle/shared-types 1.1.0 → 1.2.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.
@@ -0,0 +1,156 @@
1
+ import { z } from "zod";
2
+ import { type CaptureRule, type CaptureRuleAction, type CaptureRuleClientKind, type CaptureRuleEventType, type CaptureRuleFingerprint, type CaptureRuleSampleEventClass } from "./capture-rule-schemas.js";
3
+ declare const CaptureRuleEvaluationUrlSchema: z.ZodObject<{
4
+ host: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
5
+ path: z.ZodEffects<z.ZodString, string, string>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ path: string;
8
+ host?: string | undefined;
9
+ }, {
10
+ path: string;
11
+ host?: string | undefined;
12
+ }>;
13
+ export type CaptureRuleEvaluationUrl = z.infer<typeof CaptureRuleEvaluationUrlSchema>;
14
+ export declare const CaptureRuleEvaluationContextSchema: z.ZodObject<{
15
+ project_id: z.ZodString;
16
+ event_id: z.ZodString;
17
+ event_type: z.ZodEnum<["backend_exception", "request_event", "log_event", "frontend_breadcrumb", "frontend_exception", "deploy_metadata", "error_suppressed", "probe_event"]>;
18
+ service: z.ZodOptional<z.ZodString>;
19
+ environment: z.ZodOptional<z.ZodString>;
20
+ runtime: z.ZodEnum<["browser", "node", "python", "php", "java", "go", "ruby", "unknown"]>;
21
+ first_party: z.ZodOptional<z.ZodBoolean>;
22
+ error_name: z.ZodOptional<z.ZodString>;
23
+ message: z.ZodOptional<z.ZodString>;
24
+ browser_event_kind: z.ZodOptional<z.ZodEnum<["window_error", "resource_error"]>>;
25
+ browser_event_opaque: z.ZodOptional<z.ZodBoolean>;
26
+ client_kind: z.ZodOptional<z.ZodEnum<["human", "bot", "unknown"]>>;
27
+ bot_family: z.ZodOptional<z.ZodString>;
28
+ resource_url: z.ZodOptional<z.ZodObject<{
29
+ host: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
30
+ path: z.ZodEffects<z.ZodString, string, string>;
31
+ }, "strip", z.ZodTypeAny, {
32
+ path: string;
33
+ host?: string | undefined;
34
+ }, {
35
+ path: string;
36
+ host?: string | undefined;
37
+ }>>;
38
+ request_url: z.ZodOptional<z.ZodObject<{
39
+ host: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
40
+ path: z.ZodEffects<z.ZodString, string, string>;
41
+ }, "strip", z.ZodTypeAny, {
42
+ path: string;
43
+ host?: string | undefined;
44
+ }, {
45
+ path: string;
46
+ host?: string | undefined;
47
+ }>>;
48
+ status_code: z.ZodOptional<z.ZodNumber>;
49
+ fingerprint: z.ZodOptional<z.ZodObject<{
50
+ version: z.ZodString;
51
+ value: z.ZodString;
52
+ }, "strip", z.ZodTypeAny, {
53
+ value: string;
54
+ version: string;
55
+ }, {
56
+ value: string;
57
+ version: string;
58
+ }>>;
59
+ }, "strip", z.ZodTypeAny, {
60
+ project_id: string;
61
+ runtime: "unknown" | "browser" | "node" | "python" | "php" | "java" | "go" | "ruby";
62
+ event_id: string;
63
+ event_type: "backend_exception" | "frontend_exception" | "deploy_metadata" | "error_suppressed" | "log_event" | "request_event" | "frontend_breadcrumb" | "probe_event";
64
+ message?: string | undefined;
65
+ status_code?: number | undefined;
66
+ first_party?: boolean | undefined;
67
+ error_name?: string | undefined;
68
+ browser_event_kind?: "window_error" | "resource_error" | undefined;
69
+ browser_event_opaque?: boolean | undefined;
70
+ client_kind?: "unknown" | "human" | "bot" | undefined;
71
+ bot_family?: string | undefined;
72
+ resource_url?: {
73
+ path: string;
74
+ host?: string | undefined;
75
+ } | undefined;
76
+ request_url?: {
77
+ path: string;
78
+ host?: string | undefined;
79
+ } | undefined;
80
+ fingerprint?: {
81
+ value: string;
82
+ version: string;
83
+ } | undefined;
84
+ service?: string | undefined;
85
+ environment?: string | undefined;
86
+ }, {
87
+ project_id: string;
88
+ runtime: "unknown" | "browser" | "node" | "python" | "php" | "java" | "go" | "ruby";
89
+ event_id: string;
90
+ event_type: "backend_exception" | "frontend_exception" | "deploy_metadata" | "error_suppressed" | "log_event" | "request_event" | "frontend_breadcrumb" | "probe_event";
91
+ message?: string | undefined;
92
+ status_code?: number | undefined;
93
+ first_party?: boolean | undefined;
94
+ error_name?: string | undefined;
95
+ browser_event_kind?: "window_error" | "resource_error" | undefined;
96
+ browser_event_opaque?: boolean | undefined;
97
+ client_kind?: "unknown" | "human" | "bot" | undefined;
98
+ bot_family?: string | undefined;
99
+ resource_url?: {
100
+ path: string;
101
+ host?: string | undefined;
102
+ } | undefined;
103
+ request_url?: {
104
+ path: string;
105
+ host?: string | undefined;
106
+ } | undefined;
107
+ fingerprint?: {
108
+ value: string;
109
+ version: string;
110
+ } | undefined;
111
+ service?: string | undefined;
112
+ environment?: string | undefined;
113
+ }>;
114
+ export type CaptureRuleEvaluationContext = z.infer<typeof CaptureRuleEvaluationContextSchema>;
115
+ export interface CaptureRuleEvaluationResult {
116
+ rule_id: string;
117
+ action: CaptureRuleAction;
118
+ outcome: "demote" | "drop" | "sampled_in" | "sampled_out";
119
+ sample_rate: number | null;
120
+ sample_event_class: CaptureRuleSampleEventClass | null;
121
+ }
122
+ export type CaptureRuleEvaluableEvent = {
123
+ event_id: string;
124
+ event_type: CaptureRuleEventType;
125
+ service: {
126
+ name: string;
127
+ environment: string;
128
+ runtime?: string | null;
129
+ };
130
+ payload: Record<string, unknown>;
131
+ };
132
+ export declare function classifyCaptureRuleClientFromUserAgent(userAgent: string | null | undefined): {
133
+ client_kind: CaptureRuleClientKind;
134
+ bot_family?: string;
135
+ };
136
+ export declare function buildCaptureRuleEvaluationContext(input: {
137
+ project_id: string;
138
+ event: CaptureRuleEvaluableEvent;
139
+ fingerprint?: CaptureRuleFingerprint;
140
+ }): CaptureRuleEvaluationContext;
141
+ export declare function applyCaptureRuleEventClass(input: {
142
+ event_class: "incident_signal" | "context_signal" | "operational_signal";
143
+ capture_rule: CaptureRuleEvaluationResult | null;
144
+ }): "incident_signal" | "context_signal" | "operational_signal";
145
+ export declare function matchesCaptureRule(rule: CaptureRule, contextInput: CaptureRuleEvaluationContext): boolean;
146
+ export declare function isCaptureRuleActive(rule: CaptureRule, now: string): boolean;
147
+ export declare function getCaptureRuleSpecificityScore(rule: CaptureRule): number;
148
+ export declare function shouldSampleCaptureRuleEvent(input: {
149
+ project_id: string;
150
+ rule_id: string;
151
+ event_id: string;
152
+ sample_rate: number;
153
+ }): boolean;
154
+ export declare function evaluateCaptureRules(rules: readonly CaptureRule[], contextInput: CaptureRuleEvaluationContext, now: string): CaptureRuleEvaluationResult | null;
155
+ export {};
156
+ //# sourceMappingURL=capture-rule-evaluation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-rule-evaluation.d.ts","sourceRoot":"","sources":["../src/capture-rule-evaluation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAML,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAE3B,KAAK,2BAA2B,EAEjC,MAAM,2BAA2B,CAAC;AAEnC,QAAA,MAAM,8BAA8B;;;;;;;;;EAGlC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,CAAC;AAEtF,eAAO,MAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkB7C,CAAC;AAEH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,YAAY,GAAG,aAAa,CAAC;IAC1D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kBAAkB,EAAE,2BAA2B,GAAG,IAAI,CAAC;CACxD;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,oBAAoB,CAAC;IACjC,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACzB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC;AAiHF,wBAAgB,sCAAsC,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG;IAC5F,WAAW,EAAE,qBAAqB,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CA6BA;AAoBD,wBAAgB,iCAAiC,CAAC,KAAK,EAAE;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,yBAAyB,CAAC;IACjC,WAAW,CAAC,EAAE,sBAAsB,CAAC;CACtC,GAAG,4BAA4B,CA2F/B;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE;IAChD,WAAW,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;IACzE,YAAY,EAAE,2BAA2B,GAAG,IAAI,CAAC;CAClD,GAAG,iBAAiB,GAAG,gBAAgB,GAAG,oBAAoB,CAc9D;AA4DD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,EAAE,YAAY,EAAE,4BAA4B,GAAG,OAAO,CAuFzG;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAU3E;AAED,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CA+DxE;AA0BD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAWV;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,YAAY,EAAE,4BAA4B,EAC1C,GAAG,EAAE,MAAM,GACV,2BAA2B,GAAG,IAAI,CA8CpC"}
@@ -0,0 +1,495 @@
1
+ import { z } from "zod";
2
+ import { BrowserEventKindSchema, CaptureRuleClientKindSchema, CaptureRuleEventTypeSchema, CaptureRuleFingerprintSchema, CaptureRuleRuntimeSchema, } from "./capture-rule-schemas.js";
3
+ const CaptureRuleEvaluationUrlSchema = z.object({
4
+ host: z.string().min(1).transform((value) => value.toLowerCase()).optional(),
5
+ path: z.string().min(1).transform((value) => (value.startsWith("/") ? value : `/${value}`)),
6
+ });
7
+ export const CaptureRuleEvaluationContextSchema = z.object({
8
+ project_id: z.string().min(1).max(120),
9
+ event_id: z.string().uuid(),
10
+ event_type: CaptureRuleEventTypeSchema,
11
+ service: z.string().min(1).optional(),
12
+ environment: z.string().min(1).optional(),
13
+ runtime: CaptureRuleRuntimeSchema,
14
+ first_party: z.boolean().optional(),
15
+ error_name: z.string().min(1).optional(),
16
+ message: z.string().min(1).optional(),
17
+ browser_event_kind: BrowserEventKindSchema.optional(),
18
+ browser_event_opaque: z.boolean().optional(),
19
+ client_kind: CaptureRuleClientKindSchema.optional(),
20
+ bot_family: z.string().min(1).max(120).optional(),
21
+ resource_url: CaptureRuleEvaluationUrlSchema.optional(),
22
+ request_url: CaptureRuleEvaluationUrlSchema.optional(),
23
+ status_code: z.number().int().min(0).max(599).optional(),
24
+ fingerprint: CaptureRuleFingerprintSchema.optional(),
25
+ });
26
+ function normalizeRuntime(value) {
27
+ switch (value?.trim().toLowerCase()) {
28
+ case "browser":
29
+ return "browser";
30
+ case "node":
31
+ case "nodejs":
32
+ return "node";
33
+ case "python":
34
+ return "python";
35
+ case "php":
36
+ return "php";
37
+ case "java":
38
+ return "java";
39
+ case "go":
40
+ case "golang":
41
+ return "go";
42
+ case "ruby":
43
+ return "ruby";
44
+ default:
45
+ return "unknown";
46
+ }
47
+ }
48
+ function normalizeRoutePath(value) {
49
+ const trimmed = value?.trim();
50
+ if (trimmed === undefined || trimmed.length === 0) {
51
+ return undefined;
52
+ }
53
+ const pathWithoutQueryOrFragment = trimmed.split(/[?#]/, 1)[0] ?? "";
54
+ if (pathWithoutQueryOrFragment.length === 0) {
55
+ return "/";
56
+ }
57
+ return pathWithoutQueryOrFragment.startsWith("/") ? pathWithoutQueryOrFragment : `/${pathWithoutQueryOrFragment}`;
58
+ }
59
+ function normalizeEvaluationUrl(value) {
60
+ const trimmed = value?.trim();
61
+ if (trimmed === undefined || trimmed.length === 0) {
62
+ return {};
63
+ }
64
+ const relativePath = normalizeRoutePath(trimmed);
65
+ if (relativePath !== undefined && trimmed.startsWith("/")) {
66
+ return {
67
+ url: { path: relativePath },
68
+ first_party: true,
69
+ };
70
+ }
71
+ try {
72
+ const parsed = new URL(trimmed);
73
+ if (parsed.protocol === "http:" || parsed.protocol === "https:") {
74
+ return {
75
+ url: {
76
+ host: parsed.hostname.length > 0 ? parsed.hostname.toLowerCase() : undefined,
77
+ path: normalizeRoutePath(parsed.pathname) ?? "/",
78
+ },
79
+ first_party: false,
80
+ };
81
+ }
82
+ }
83
+ catch {
84
+ if (relativePath !== undefined) {
85
+ return {
86
+ url: { path: relativePath },
87
+ first_party: true,
88
+ };
89
+ }
90
+ }
91
+ return {};
92
+ }
93
+ function readString(value) {
94
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
95
+ }
96
+ function readNumber(value) {
97
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
98
+ }
99
+ function readRecord(value) {
100
+ return typeof value === "object" && value !== null && !Array.isArray(value)
101
+ ? value
102
+ : undefined;
103
+ }
104
+ function readHeaderValue(headers, name) {
105
+ if (headers === undefined) {
106
+ return undefined;
107
+ }
108
+ const lowerName = name.toLowerCase();
109
+ for (const [key, value] of Object.entries(headers)) {
110
+ if (key.toLowerCase() === lowerName) {
111
+ return readString(value);
112
+ }
113
+ }
114
+ return undefined;
115
+ }
116
+ function readDeviceUserAgent(payload) {
117
+ const device = readRecord(payload["device"]);
118
+ return readString(device?.["user_agent"]);
119
+ }
120
+ export function classifyCaptureRuleClientFromUserAgent(userAgent) {
121
+ if (userAgent === null || userAgent === undefined) {
122
+ return { client_kind: "unknown" };
123
+ }
124
+ const lower = userAgent.toLowerCase();
125
+ const knownBots = [
126
+ { family: "Googlebot", markers: ["googlebot", "adsbot-google", "google-inspectiontool"] },
127
+ { family: "Bingbot", markers: ["bingbot", "msnbot"] },
128
+ { family: "DuckDuckBot", markers: ["duckduckbot"] },
129
+ { family: "Applebot", markers: ["applebot"] },
130
+ { family: "YandexBot", markers: ["yandexbot"] },
131
+ { family: "Baiduspider", markers: ["baiduspider"] },
132
+ { family: "FacebookBot", markers: ["facebookexternalhit", "facebot"] },
133
+ { family: "LinkedInBot", markers: ["linkedinbot"] },
134
+ { family: "TwitterBot", markers: ["twitterbot"] },
135
+ { family: "Slackbot", markers: ["slackbot"] },
136
+ ];
137
+ const knownBot = knownBots.find((entry) => entry.markers.some((marker) => lower.includes(marker)));
138
+ if (knownBot !== undefined) {
139
+ return { client_kind: "bot", bot_family: knownBot.family };
140
+ }
141
+ if (["bot", "crawler", "spider", "slurp"].some((marker) => lower.includes(marker))) {
142
+ return { client_kind: "bot", bot_family: "OtherBot" };
143
+ }
144
+ return { client_kind: "human" };
145
+ }
146
+ function readEventUserAgent(event) {
147
+ const deviceUserAgent = readDeviceUserAgent(event.payload);
148
+ if (deviceUserAgent !== undefined) {
149
+ return deviceUserAgent;
150
+ }
151
+ if (event.event_type === "backend_exception") {
152
+ const request = readRecord(event.payload["request"]);
153
+ return readHeaderValue(readRecord(request?.["headers"]), "user-agent");
154
+ }
155
+ if (event.event_type === "request_event") {
156
+ return readHeaderValue(readRecord(event.payload["headers"]), "user-agent");
157
+ }
158
+ return undefined;
159
+ }
160
+ export function buildCaptureRuleEvaluationContext(input) {
161
+ const context = {
162
+ project_id: input.project_id,
163
+ event_id: input.event.event_id,
164
+ event_type: input.event.event_type,
165
+ service: input.event.service.name,
166
+ environment: input.event.service.environment,
167
+ runtime: normalizeRuntime(input.event.service.runtime),
168
+ ...classifyCaptureRuleClientFromUserAgent(readEventUserAgent(input.event)),
169
+ ...(input.fingerprint === undefined ? {} : { fingerprint: input.fingerprint }),
170
+ };
171
+ switch (input.event.event_type) {
172
+ case "backend_exception": {
173
+ const request = readRecord(input.event.payload["request"]);
174
+ const response = readRecord(input.event.payload["response"]);
175
+ const requestUrl = normalizeEvaluationUrl(readString(request?.["path"]));
176
+ return CaptureRuleEvaluationContextSchema.parse({
177
+ ...context,
178
+ first_party: true,
179
+ error_name: readString(input.event.payload["name"]),
180
+ message: readString(input.event.payload["message"]),
181
+ request_url: requestUrl.url,
182
+ status_code: readNumber(response?.["status_code"]),
183
+ });
184
+ }
185
+ case "request_event": {
186
+ const requestUrl = normalizeEvaluationUrl(readString(input.event.payload["path"]));
187
+ return CaptureRuleEvaluationContextSchema.parse({
188
+ ...context,
189
+ first_party: requestUrl.first_party ?? true,
190
+ request_url: requestUrl.url,
191
+ status_code: readNumber(input.event.payload["response_status"]),
192
+ });
193
+ }
194
+ case "log_event":
195
+ return CaptureRuleEvaluationContextSchema.parse({
196
+ ...context,
197
+ message: readString(input.event.payload["message"]),
198
+ });
199
+ case "frontend_exception": {
200
+ const browserEvent = readRecord(input.event.payload["browser_event"]);
201
+ const target = readRecord(browserEvent?.["target"]);
202
+ const browserEventKind = browserEvent?.["kind"];
203
+ const resourceSource = typeof target?.["source_url"] === "string"
204
+ ? target["source_url"]
205
+ : typeof browserEvent?.["file_name"] === "string"
206
+ ? browserEvent["file_name"]
207
+ : undefined;
208
+ const resourceUrl = normalizeEvaluationUrl(resourceSource);
209
+ return CaptureRuleEvaluationContextSchema.parse({
210
+ ...context,
211
+ ...(resourceUrl.first_party === undefined ? {} : { first_party: resourceUrl.first_party }),
212
+ error_name: readString(input.event.payload["name"]),
213
+ message: readString(input.event.payload["message"]),
214
+ browser_event_kind: browserEventKind === "window_error" || browserEventKind === "resource_error"
215
+ ? browserEventKind
216
+ : undefined,
217
+ browser_event_opaque: typeof browserEvent?.["opaque"] === "boolean" ? browserEvent["opaque"] : undefined,
218
+ resource_url: resourceUrl.url,
219
+ });
220
+ }
221
+ case "frontend_breadcrumb": {
222
+ if (input.event.payload["breadcrumb_type"] !== "network_request") {
223
+ return CaptureRuleEvaluationContextSchema.parse(context);
224
+ }
225
+ const data = readRecord(input.event.payload["data"]);
226
+ const requestUrl = normalizeEvaluationUrl(readString(data?.["url"]));
227
+ return CaptureRuleEvaluationContextSchema.parse({
228
+ ...context,
229
+ ...(requestUrl.first_party === undefined ? {} : { first_party: requestUrl.first_party }),
230
+ request_url: requestUrl.url,
231
+ status_code: readNumber(data?.["status_code"]),
232
+ });
233
+ }
234
+ default:
235
+ return CaptureRuleEvaluationContextSchema.parse(context);
236
+ }
237
+ }
238
+ export function applyCaptureRuleEventClass(input) {
239
+ if (input.capture_rule === null) {
240
+ return input.event_class;
241
+ }
242
+ if (input.capture_rule.outcome === "demote") {
243
+ return "context_signal";
244
+ }
245
+ if (input.capture_rule.action === "sample" && input.capture_rule.sample_event_class === "context") {
246
+ return "context_signal";
247
+ }
248
+ return input.event_class;
249
+ }
250
+ function matchesUrlMatcher(matcher, value) {
251
+ if (matcher === undefined) {
252
+ return true;
253
+ }
254
+ if (value === undefined) {
255
+ return false;
256
+ }
257
+ if (matcher.host !== undefined && value.host !== matcher.host) {
258
+ return false;
259
+ }
260
+ if (matcher.host_suffix !== undefined && (value.host === undefined || !value.host.endsWith(matcher.host_suffix))) {
261
+ return false;
262
+ }
263
+ if (matcher.path_equals !== undefined && value.path !== matcher.path_equals) {
264
+ return false;
265
+ }
266
+ if (matcher.path_prefix !== undefined && !value.path.startsWith(matcher.path_prefix)) {
267
+ return false;
268
+ }
269
+ return true;
270
+ }
271
+ function matchesStatusRange(statusCode, ranges) {
272
+ if (ranges === undefined) {
273
+ return true;
274
+ }
275
+ if (statusCode === undefined) {
276
+ return false;
277
+ }
278
+ return ranges.some((range) => statusCode >= range.start && statusCode <= range.end);
279
+ }
280
+ function matchesStatusCodes(statusCode, statusCodes) {
281
+ if (statusCodes === undefined) {
282
+ return true;
283
+ }
284
+ if (statusCode === undefined) {
285
+ return false;
286
+ }
287
+ return statusCodes.includes(statusCode);
288
+ }
289
+ export function matchesCaptureRule(rule, contextInput) {
290
+ const context = CaptureRuleEvaluationContextSchema.parse(contextInput);
291
+ const matcher = rule.matcher;
292
+ if (matcher.event_types !== undefined && !matcher.event_types.includes(context.event_type)) {
293
+ return false;
294
+ }
295
+ if (matcher.services !== undefined && (context.service === undefined || !matcher.services.includes(context.service))) {
296
+ return false;
297
+ }
298
+ if (matcher.environments !== undefined &&
299
+ (context.environment === undefined || !matcher.environments.includes(context.environment))) {
300
+ return false;
301
+ }
302
+ if (matcher.runtime !== undefined && !matcher.runtime.includes(context.runtime)) {
303
+ return false;
304
+ }
305
+ if (matcher.first_party !== undefined && context.first_party !== matcher.first_party) {
306
+ return false;
307
+ }
308
+ if (matcher.error_name !== undefined && context.error_name !== matcher.error_name) {
309
+ return false;
310
+ }
311
+ if (matcher.message_equals !== undefined && context.message !== matcher.message_equals) {
312
+ return false;
313
+ }
314
+ if (matcher.message_contains !== undefined) {
315
+ if (context.message === undefined || !context.message.includes(matcher.message_contains)) {
316
+ return false;
317
+ }
318
+ }
319
+ if (matcher.browser_event_kind !== undefined && context.browser_event_kind !== matcher.browser_event_kind) {
320
+ return false;
321
+ }
322
+ if (matcher.browser_event_opaque !== undefined && context.browser_event_opaque !== matcher.browser_event_opaque) {
323
+ return false;
324
+ }
325
+ if (matcher.client_kind !== undefined && context.client_kind !== matcher.client_kind) {
326
+ return false;
327
+ }
328
+ if (matcher.bot_family !== undefined && context.bot_family !== matcher.bot_family) {
329
+ return false;
330
+ }
331
+ if (!matchesUrlMatcher(matcher.resource_url, context.resource_url)) {
332
+ return false;
333
+ }
334
+ if (!matchesUrlMatcher(matcher.request_url, context.request_url)) {
335
+ return false;
336
+ }
337
+ if (!matchesStatusCodes(context.status_code, matcher.status_codes)) {
338
+ return false;
339
+ }
340
+ if (!matchesStatusRange(context.status_code, matcher.status_ranges)) {
341
+ return false;
342
+ }
343
+ if (matcher.fingerprint !== undefined) {
344
+ if (context.fingerprint === undefined) {
345
+ return false;
346
+ }
347
+ if (context.fingerprint.version !== matcher.fingerprint.version ||
348
+ context.fingerprint.value !== matcher.fingerprint.value) {
349
+ return false;
350
+ }
351
+ }
352
+ return true;
353
+ }
354
+ export function isCaptureRuleActive(rule, now) {
355
+ if (!rule.enabled) {
356
+ return false;
357
+ }
358
+ if (rule.expires_at === null) {
359
+ return true;
360
+ }
361
+ return Date.parse(rule.expires_at) > Date.parse(now);
362
+ }
363
+ export function getCaptureRuleSpecificityScore(rule) {
364
+ const matcher = rule.matcher;
365
+ let score = 0;
366
+ if (matcher.fingerprint !== undefined) {
367
+ score += 1000;
368
+ }
369
+ if (matcher.resource_url?.host !== undefined) {
370
+ score += 250;
371
+ }
372
+ if (matcher.request_url?.host !== undefined) {
373
+ score += 250;
374
+ }
375
+ if (matcher.resource_url?.path_equals !== undefined || matcher.request_url?.path_equals !== undefined) {
376
+ score += 200;
377
+ }
378
+ if (matcher.status_codes !== undefined) {
379
+ score += 150;
380
+ }
381
+ if (matcher.browser_event_kind !== undefined) {
382
+ score += 100;
383
+ }
384
+ if (matcher.browser_event_opaque !== undefined) {
385
+ score += 100;
386
+ }
387
+ if (matcher.resource_url?.host_suffix !== undefined || matcher.request_url?.host_suffix !== undefined) {
388
+ score += 90;
389
+ }
390
+ if (matcher.resource_url?.path_prefix !== undefined || matcher.request_url?.path_prefix !== undefined) {
391
+ score += 80;
392
+ }
393
+ if (matcher.error_name !== undefined) {
394
+ score += 70;
395
+ }
396
+ if (matcher.message_equals !== undefined) {
397
+ score += 60;
398
+ }
399
+ if (matcher.message_contains !== undefined) {
400
+ score += 50;
401
+ }
402
+ if (matcher.bot_family !== undefined) {
403
+ score += 45;
404
+ }
405
+ if (matcher.first_party !== undefined) {
406
+ score += 40;
407
+ }
408
+ if (matcher.client_kind !== undefined) {
409
+ score += 35;
410
+ }
411
+ if (matcher.services !== undefined) {
412
+ score += 30;
413
+ }
414
+ if (matcher.environments !== undefined) {
415
+ score += 20;
416
+ }
417
+ if (matcher.runtime !== undefined) {
418
+ score += 10;
419
+ }
420
+ if (matcher.event_types !== undefined) {
421
+ score += 5;
422
+ }
423
+ return score;
424
+ }
425
+ function compareCaptureRules(left, right) {
426
+ const specificityDifference = getCaptureRuleSpecificityScore(right) - getCaptureRuleSpecificityScore(left);
427
+ if (specificityDifference !== 0) {
428
+ return specificityDifference;
429
+ }
430
+ const updatedDifference = Date.parse(right.updated_at) - Date.parse(left.updated_at);
431
+ if (updatedDifference !== 0) {
432
+ return updatedDifference;
433
+ }
434
+ return left.id.localeCompare(right.id);
435
+ }
436
+ function stableUnitFloat(seed) {
437
+ let hash = 2166136261;
438
+ for (let index = 0; index < seed.length; index += 1) {
439
+ hash ^= seed.charCodeAt(index);
440
+ hash = Math.imul(hash, 16777619);
441
+ }
442
+ return (hash >>> 0) / 0x100000000;
443
+ }
444
+ export function shouldSampleCaptureRuleEvent(input) {
445
+ if (input.sample_rate <= 0) {
446
+ return false;
447
+ }
448
+ if (input.sample_rate >= 1) {
449
+ return true;
450
+ }
451
+ const seed = `${input.project_id}:${input.rule_id}:${input.event_id}`;
452
+ return stableUnitFloat(seed) < input.sample_rate;
453
+ }
454
+ export function evaluateCaptureRules(rules, contextInput, now) {
455
+ const context = CaptureRuleEvaluationContextSchema.parse(contextInput);
456
+ const activeRules = rules.filter((rule) => isCaptureRuleActive(rule, now)).sort(compareCaptureRules);
457
+ for (const rule of activeRules) {
458
+ if (!matchesCaptureRule(rule, context)) {
459
+ continue;
460
+ }
461
+ if (rule.action === "demote") {
462
+ return {
463
+ rule_id: rule.id,
464
+ action: "demote",
465
+ outcome: "demote",
466
+ sample_rate: null,
467
+ sample_event_class: null,
468
+ };
469
+ }
470
+ if (rule.action === "drop") {
471
+ return {
472
+ rule_id: rule.id,
473
+ action: "drop",
474
+ outcome: "drop",
475
+ sample_rate: null,
476
+ sample_event_class: null,
477
+ };
478
+ }
479
+ const sampledIn = shouldSampleCaptureRuleEvent({
480
+ project_id: context.project_id,
481
+ rule_id: rule.id,
482
+ event_id: context.event_id,
483
+ sample_rate: rule.sample_rate ?? 0,
484
+ });
485
+ return {
486
+ rule_id: rule.id,
487
+ action: "sample",
488
+ outcome: sampledIn ? "sampled_in" : "sampled_out",
489
+ sample_rate: rule.sample_rate,
490
+ sample_event_class: rule.sample_event_class,
491
+ };
492
+ }
493
+ return null;
494
+ }
495
+ //# sourceMappingURL=capture-rule-evaluation.js.map