@sentry/junior-plugin-api 0.181.1 → 0.182.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/dist/index.js +85 -0
- package/dist/resource-events.d.ts +46 -1
- package/package.json +1 -1
- package/src/resource-events.ts +112 -1
package/dist/index.js
CHANGED
|
@@ -357,10 +357,90 @@ var resourceEventTypeSchema = z5.string().regex(/^[a-z][a-z0-9_-]*(?:\.[a-z][a-z
|
|
|
357
357
|
var resourceTypeSchema = z5.string().regex(/^[a-z][a-z0-9_]*$/).describe(
|
|
358
358
|
"Canonical plugin-defined resource type, such as issue or pull_request."
|
|
359
359
|
);
|
|
360
|
+
var RESOURCE_EVENT_MATCH_FIELD_NAME = /^[a-z][a-zA-Z0-9]*$/;
|
|
361
|
+
var resourceEventMatchFieldSchema = z5.object({
|
|
362
|
+
kind: z5.enum(["boolean", "string", "number"]),
|
|
363
|
+
description: z5.string().trim().min(1).max(200),
|
|
364
|
+
enum: z5.array(z5.string().min(1)).min(1).optional()
|
|
365
|
+
}).strict().superRefine((field, context) => {
|
|
366
|
+
if (field.enum && field.kind !== "string") {
|
|
367
|
+
context.addIssue({
|
|
368
|
+
code: "custom",
|
|
369
|
+
message: 'Resource event match field enum requires kind "string".',
|
|
370
|
+
path: ["enum"]
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
var resourceEventMatchFieldsSchema = z5.record(z5.string().regex(RESOURCE_EVENT_MATCH_FIELD_NAME), resourceEventMatchFieldSchema).superRefine((fields, context) => {
|
|
375
|
+
if (Object.keys(fields).length > RESOURCE_EVENT_DATA_MAX_KEYS) {
|
|
376
|
+
context.addIssue({
|
|
377
|
+
code: "custom",
|
|
378
|
+
message: `Resource event match keys may include at most ${RESOURCE_EVENT_DATA_MAX_KEYS} keys.`
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
var resourceEventMatchSchema = z5.record(
|
|
383
|
+
z5.string().regex(RESOURCE_EVENT_MATCH_FIELD_NAME),
|
|
384
|
+
z5.union([
|
|
385
|
+
z5.boolean(),
|
|
386
|
+
z5.number().finite(),
|
|
387
|
+
z5.string().min(1),
|
|
388
|
+
z5.array(z5.union([z5.number().finite(), z5.string().min(1)])).min(1)
|
|
389
|
+
])
|
|
390
|
+
).superRefine((match, context) => {
|
|
391
|
+
if (Object.keys(match).length > RESOURCE_EVENT_DATA_MAX_KEYS) {
|
|
392
|
+
context.addIssue({
|
|
393
|
+
code: "custom",
|
|
394
|
+
message: `Resource event match may include at most ${RESOURCE_EVENT_DATA_MAX_KEYS} keys.`
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
for (const [key, value] of Object.entries(match)) {
|
|
398
|
+
if (!Array.isArray(value)) continue;
|
|
399
|
+
if (value.some((entry) => typeof entry === "boolean")) {
|
|
400
|
+
context.addIssue({
|
|
401
|
+
code: "custom",
|
|
402
|
+
message: `Resource event match field "${key}" cannot use a boolean list.`,
|
|
403
|
+
path: [key]
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
function stableMatchValue(value) {
|
|
409
|
+
if (!Array.isArray(value)) return value;
|
|
410
|
+
return [...value].sort((left, right) => {
|
|
411
|
+
if (typeof left === "number" && typeof right === "number") {
|
|
412
|
+
return left - right;
|
|
413
|
+
}
|
|
414
|
+
return String(left).localeCompare(String(right));
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
function stableResourceEventMatchKey(match) {
|
|
418
|
+
if (!match || Object.keys(match).length === 0) return "";
|
|
419
|
+
return JSON.stringify(
|
|
420
|
+
Object.fromEntries(
|
|
421
|
+
Object.entries(match).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, stableMatchValue(value)])
|
|
422
|
+
)
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
function resourceEventMatches(match, data) {
|
|
426
|
+
if (!match || Object.keys(match).length === 0) return true;
|
|
427
|
+
if (!data) return false;
|
|
428
|
+
for (const [key, expected] of Object.entries(match)) {
|
|
429
|
+
const actual = data[key];
|
|
430
|
+
if (actual === void 0) return false;
|
|
431
|
+
if (Array.isArray(expected)) {
|
|
432
|
+
if (!expected.some((value) => Object.is(value, actual))) return false;
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
if (!Object.is(expected, actual)) return false;
|
|
436
|
+
}
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
360
439
|
var pluginResourceEventTypeSchema = z5.object({
|
|
361
440
|
type: resourceTypeSchema,
|
|
362
441
|
supportedEvents: z5.array(resourceEventTypeSchema).min(1),
|
|
363
442
|
suggestedEvents: z5.array(resourceEventTypeSchema).optional(),
|
|
443
|
+
matchFields: resourceEventMatchFieldsSchema.optional(),
|
|
364
444
|
guidance: z5.record(
|
|
365
445
|
resourceEventTypeSchema,
|
|
366
446
|
z5.string().trim().min(1).max(RESOURCE_EVENT_GUIDANCE_MAX_LENGTH)
|
|
@@ -945,6 +1025,10 @@ export {
|
|
|
945
1025
|
replyAttributionSchema,
|
|
946
1026
|
resourceEventDataSchema,
|
|
947
1027
|
resourceEventInputSchema,
|
|
1028
|
+
resourceEventMatchFieldSchema,
|
|
1029
|
+
resourceEventMatchFieldsSchema,
|
|
1030
|
+
resourceEventMatchSchema,
|
|
1031
|
+
resourceEventMatches,
|
|
948
1032
|
resourceEventSchema,
|
|
949
1033
|
resourceEventSubscriptionResultSchema,
|
|
950
1034
|
resourceEventTypeSchema,
|
|
@@ -955,6 +1039,7 @@ export {
|
|
|
955
1039
|
slackSourceSchema,
|
|
956
1040
|
sourceSchema,
|
|
957
1041
|
sourceVisibilitySchema,
|
|
1042
|
+
stableResourceEventMatchKey,
|
|
958
1043
|
subscribableResourceSchema,
|
|
959
1044
|
systemActorSchema,
|
|
960
1045
|
toolApprovalModeSchema,
|
|
@@ -4,15 +4,51 @@ export declare const RESOURCE_EVENT_TEXT_MAX_LENGTH = 8000;
|
|
|
4
4
|
export declare const RESOURCE_EVENT_DATA_MAX_KEYS = 32;
|
|
5
5
|
export declare const RESOURCE_EVENT_DATA_MAX_JSON_BYTES = 4000;
|
|
6
6
|
export declare const RESOURCE_EVENT_GUIDANCE_MAX_LENGTH = 1000;
|
|
7
|
-
/** Small trusted
|
|
7
|
+
/** Small trusted data from the plugin. The agent should not look these up again. */
|
|
8
8
|
export declare const resourceEventDataSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
9
9
|
/** Canonical dotted event type published and selected across plugins. */
|
|
10
10
|
export declare const resourceEventTypeSchema: z.ZodString;
|
|
11
11
|
export declare const resourceTypeSchema: z.ZodString;
|
|
12
|
+
/** One exact value a watch or event task may require on trusted event data. */
|
|
13
|
+
export declare const resourceEventMatchFieldSchema: z.ZodObject<{
|
|
14
|
+
kind: z.ZodEnum<{
|
|
15
|
+
string: "string";
|
|
16
|
+
number: "number";
|
|
17
|
+
boolean: "boolean";
|
|
18
|
+
}>;
|
|
19
|
+
description: z.ZodString;
|
|
20
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
21
|
+
}, z.core.$strict>;
|
|
22
|
+
export declare const resourceEventMatchFieldsSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
23
|
+
kind: z.ZodEnum<{
|
|
24
|
+
string: "string";
|
|
25
|
+
number: "number";
|
|
26
|
+
boolean: "boolean";
|
|
27
|
+
}>;
|
|
28
|
+
description: z.ZodString;
|
|
29
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
30
|
+
}, z.core.$strict>>;
|
|
31
|
+
/** Exact trusted values required before a watch or event task runs. */
|
|
32
|
+
export declare const resourceEventMatchSchema: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodNumber, z.ZodString, z.ZodArray<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>]>>;
|
|
33
|
+
export type ResourceEventMatch = z.output<typeof resourceEventMatchSchema>;
|
|
34
|
+
export type ResourceEventMatchFields = z.output<typeof resourceEventMatchFieldsSchema>;
|
|
35
|
+
/** Stable JSON for one match object. List order does not matter. */
|
|
36
|
+
export declare function stableResourceEventMatchKey(match: ResourceEventMatch | undefined): string;
|
|
37
|
+
/** Return whether trusted event data matches one exact match object. */
|
|
38
|
+
export declare function resourceEventMatches(match: ResourceEventMatch | undefined, data: ResourceEventData | undefined): boolean;
|
|
12
39
|
export declare const pluginResourceEventTypeSchema: z.ZodObject<{
|
|
13
40
|
type: z.ZodString;
|
|
14
41
|
supportedEvents: z.ZodArray<z.ZodString>;
|
|
15
42
|
suggestedEvents: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
43
|
+
matchFields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
44
|
+
kind: z.ZodEnum<{
|
|
45
|
+
string: "string";
|
|
46
|
+
number: "number";
|
|
47
|
+
boolean: "boolean";
|
|
48
|
+
}>;
|
|
49
|
+
description: z.ZodString;
|
|
50
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
51
|
+
}, z.core.$strict>>>;
|
|
16
52
|
guidance: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
17
53
|
}, z.core.$strict>;
|
|
18
54
|
export declare const pluginResourceEventsSchema: z.ZodObject<{
|
|
@@ -20,6 +56,15 @@ export declare const pluginResourceEventsSchema: z.ZodObject<{
|
|
|
20
56
|
type: z.ZodString;
|
|
21
57
|
supportedEvents: z.ZodArray<z.ZodString>;
|
|
22
58
|
suggestedEvents: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
59
|
+
matchFields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
60
|
+
kind: z.ZodEnum<{
|
|
61
|
+
string: "string";
|
|
62
|
+
number: "number";
|
|
63
|
+
boolean: "boolean";
|
|
64
|
+
}>;
|
|
65
|
+
description: z.ZodString;
|
|
66
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
67
|
+
}, z.core.$strict>>>;
|
|
23
68
|
guidance: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
24
69
|
}, z.core.$strict>>;
|
|
25
70
|
isEnabled: z.ZodOptional<z.ZodFunction<z.ZodTuple<readonly [], null>, z.ZodBoolean>>;
|
package/package.json
CHANGED
package/src/resource-events.ts
CHANGED
|
@@ -6,7 +6,7 @@ export const RESOURCE_EVENT_DATA_MAX_KEYS = 32;
|
|
|
6
6
|
export const RESOURCE_EVENT_DATA_MAX_JSON_BYTES = 4_000;
|
|
7
7
|
export const RESOURCE_EVENT_GUIDANCE_MAX_LENGTH = 1_000;
|
|
8
8
|
|
|
9
|
-
/** Small trusted
|
|
9
|
+
/** Small trusted data from the plugin. The agent should not look these up again. */
|
|
10
10
|
export const resourceEventDataSchema = z
|
|
11
11
|
.record(z.string(), z.unknown())
|
|
12
12
|
.superRefine((value, context) => {
|
|
@@ -43,11 +43,122 @@ export const resourceTypeSchema = z
|
|
|
43
43
|
"Canonical plugin-defined resource type, such as issue or pull_request.",
|
|
44
44
|
);
|
|
45
45
|
|
|
46
|
+
const RESOURCE_EVENT_MATCH_FIELD_NAME =
|
|
47
|
+
/^[a-z][a-zA-Z0-9]*$/;
|
|
48
|
+
|
|
49
|
+
/** One exact value a watch or event task may require on trusted event data. */
|
|
50
|
+
export const resourceEventMatchFieldSchema = z
|
|
51
|
+
.object({
|
|
52
|
+
kind: z.enum(["boolean", "string", "number"]),
|
|
53
|
+
description: z.string().trim().min(1).max(200),
|
|
54
|
+
enum: z.array(z.string().min(1)).min(1).optional(),
|
|
55
|
+
})
|
|
56
|
+
.strict()
|
|
57
|
+
.superRefine((field, context) => {
|
|
58
|
+
if (field.enum && field.kind !== "string") {
|
|
59
|
+
context.addIssue({
|
|
60
|
+
code: "custom",
|
|
61
|
+
message: 'Resource event match field enum requires kind "string".',
|
|
62
|
+
path: ["enum"],
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
export const resourceEventMatchFieldsSchema = z
|
|
68
|
+
.record(z.string().regex(RESOURCE_EVENT_MATCH_FIELD_NAME), resourceEventMatchFieldSchema)
|
|
69
|
+
.superRefine((fields, context) => {
|
|
70
|
+
if (Object.keys(fields).length > RESOURCE_EVENT_DATA_MAX_KEYS) {
|
|
71
|
+
context.addIssue({
|
|
72
|
+
code: "custom",
|
|
73
|
+
message: `Resource event match keys may include at most ${RESOURCE_EVENT_DATA_MAX_KEYS} keys.`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/** Exact trusted values required before a watch or event task runs. */
|
|
79
|
+
export const resourceEventMatchSchema = z
|
|
80
|
+
.record(
|
|
81
|
+
z.string().regex(RESOURCE_EVENT_MATCH_FIELD_NAME),
|
|
82
|
+
z.union([
|
|
83
|
+
z.boolean(),
|
|
84
|
+
z.number().finite(),
|
|
85
|
+
z.string().min(1),
|
|
86
|
+
z.array(z.union([z.number().finite(), z.string().min(1)])).min(1),
|
|
87
|
+
]),
|
|
88
|
+
)
|
|
89
|
+
.superRefine((match, context) => {
|
|
90
|
+
if (Object.keys(match).length > RESOURCE_EVENT_DATA_MAX_KEYS) {
|
|
91
|
+
context.addIssue({
|
|
92
|
+
code: "custom",
|
|
93
|
+
message: `Resource event match may include at most ${RESOURCE_EVENT_DATA_MAX_KEYS} keys.`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
for (const [key, value] of Object.entries(match)) {
|
|
97
|
+
if (!Array.isArray(value)) continue;
|
|
98
|
+
if (value.some((entry) => typeof entry === "boolean")) {
|
|
99
|
+
context.addIssue({
|
|
100
|
+
code: "custom",
|
|
101
|
+
message: `Resource event match field "${key}" cannot use a boolean list.`,
|
|
102
|
+
path: [key],
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
export type ResourceEventMatch = z.output<typeof resourceEventMatchSchema>;
|
|
109
|
+
export type ResourceEventMatchFields = z.output<
|
|
110
|
+
typeof resourceEventMatchFieldsSchema
|
|
111
|
+
>;
|
|
112
|
+
|
|
113
|
+
function stableMatchValue(value: ResourceEventMatch[string]): unknown {
|
|
114
|
+
if (!Array.isArray(value)) return value;
|
|
115
|
+
return [...value].sort((left, right) => {
|
|
116
|
+
if (typeof left === "number" && typeof right === "number") {
|
|
117
|
+
return left - right;
|
|
118
|
+
}
|
|
119
|
+
return String(left).localeCompare(String(right));
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Stable JSON for one match object. List order does not matter. */
|
|
124
|
+
export function stableResourceEventMatchKey(
|
|
125
|
+
match: ResourceEventMatch | undefined,
|
|
126
|
+
): string {
|
|
127
|
+
if (!match || Object.keys(match).length === 0) return "";
|
|
128
|
+
return JSON.stringify(
|
|
129
|
+
Object.fromEntries(
|
|
130
|
+
Object.entries(match)
|
|
131
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
132
|
+
.map(([key, value]) => [key, stableMatchValue(value)]),
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Return whether trusted event data matches one exact match object. */
|
|
138
|
+
export function resourceEventMatches(
|
|
139
|
+
match: ResourceEventMatch | undefined,
|
|
140
|
+
data: ResourceEventData | undefined,
|
|
141
|
+
): boolean {
|
|
142
|
+
if (!match || Object.keys(match).length === 0) return true;
|
|
143
|
+
if (!data) return false;
|
|
144
|
+
for (const [key, expected] of Object.entries(match)) {
|
|
145
|
+
const actual = data[key];
|
|
146
|
+
if (actual === undefined) return false;
|
|
147
|
+
if (Array.isArray(expected)) {
|
|
148
|
+
if (!expected.some((value) => Object.is(value, actual))) return false;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (!Object.is(expected, actual)) return false;
|
|
152
|
+
}
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
46
156
|
export const pluginResourceEventTypeSchema = z
|
|
47
157
|
.object({
|
|
48
158
|
type: resourceTypeSchema,
|
|
49
159
|
supportedEvents: z.array(resourceEventTypeSchema).min(1),
|
|
50
160
|
suggestedEvents: z.array(resourceEventTypeSchema).optional(),
|
|
161
|
+
matchFields: resourceEventMatchFieldsSchema.optional(),
|
|
51
162
|
guidance: z
|
|
52
163
|
.record(
|
|
53
164
|
resourceEventTypeSchema,
|