@flakiness/sdk 2.1.0 → 2.2.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.
package/README.md CHANGED
@@ -79,6 +79,7 @@ Use this entry point when you need to process or manipulate reports in browser-b
79
79
 
80
80
  ### Building Reports
81
81
  - **`CIUtils`** - Utilities to extract CI/CD information (run URLs, environment detection)
82
+ - **`GithubOIDC`** - GitHub Actions OIDC integration for passwordless Flakiness.io authentication
82
83
  - **`GitWorktree`** - Git repository utilities for path conversion and commit information
83
84
  - **`ReportUtils`** - Namespace with utilities for report creation and manipulation:
84
85
  - `createEnvironment()` - Create environment objects with system information
package/lib/browser.js CHANGED
@@ -13,6 +13,154 @@ __export(reportUtilsBrowser_exports, {
13
13
  visitTests: () => visitTests
14
14
  });
15
15
 
16
+ // node_modules/.pnpm/@flakiness+flakiness-report@0.27.0_zod@4.3.5/node_modules/@flakiness/flakiness-report/lib/flakinessReport.js
17
+ var FlakinessReport;
18
+ ((FlakinessReport22) => {
19
+ FlakinessReport22.CATEGORY_PLAYWRIGHT = "playwright";
20
+ FlakinessReport22.CATEGORY_PYTEST = "pytest";
21
+ FlakinessReport22.CATEGORY_JUNIT = "junit";
22
+ FlakinessReport22.STREAM_STDOUT = 1;
23
+ FlakinessReport22.STREAM_STDERR = 2;
24
+ })(FlakinessReport || (FlakinessReport = {}));
25
+
26
+ // node_modules/.pnpm/@flakiness+flakiness-report@0.27.0_zod@4.3.5/node_modules/@flakiness/flakiness-report/lib/schema.js
27
+ import z from "zod/v4";
28
+ var Schema;
29
+ ((Schema2) => {
30
+ Schema2.CommitId = z.string().min(40).max(40);
31
+ Schema2.AttachmentId = z.string().min(1).max(1024);
32
+ Schema2.UnixTimestampMS = z.number().min(0);
33
+ Schema2.DurationMS = z.number().min(0);
34
+ Schema2.Number1Based = z.number();
35
+ Schema2.GitFilePath = z.string().min(0);
36
+ Schema2.Location = z.object({
37
+ file: Schema2.GitFilePath,
38
+ line: Schema2.Number1Based,
39
+ // Note: Locations for file suites are (0, 0).
40
+ column: Schema2.Number1Based
41
+ });
42
+ Schema2.TestStatus = z.enum(["passed", "failed", "timedOut", "skipped", "interrupted"]);
43
+ Schema2.Environment = z.object({
44
+ name: z.string().min(1).max(512),
45
+ systemData: z.object({
46
+ osName: z.string().optional(),
47
+ osVersion: z.string().optional(),
48
+ osArch: z.string().optional()
49
+ }).optional(),
50
+ metadata: z.any().optional(),
51
+ userSuppliedData: z.any().optional()
52
+ });
53
+ Schema2.STDIOEntry = z.union([
54
+ z.object({ text: z.string() }),
55
+ z.object({ buffer: z.string() })
56
+ ]);
57
+ Schema2.STREAM_STDOUT = z.literal(FlakinessReport.STREAM_STDOUT);
58
+ Schema2.STREAM_STDERR = z.literal(FlakinessReport.STREAM_STDERR);
59
+ Schema2.TimedSTDIOEntry = z.object({
60
+ stream: z.union([Schema2.STREAM_STDOUT, Schema2.STREAM_STDERR]).optional(),
61
+ dts: Schema2.DurationMS
62
+ }).and(z.union([
63
+ z.object({ text: z.string() }),
64
+ z.object({ buffer: z.string() })
65
+ ]));
66
+ Schema2.ReportError = z.object({
67
+ location: Schema2.Location.optional(),
68
+ message: z.string().optional(),
69
+ stack: z.string().optional(),
70
+ snippet: z.string().optional(),
71
+ value: z.string().optional()
72
+ });
73
+ Schema2.SuiteType = z.enum(["file", "anonymous suite", "suite"]);
74
+ Schema2.TestStep = z.object({
75
+ title: z.string(),
76
+ duration: Schema2.DurationMS.optional(),
77
+ location: Schema2.Location.optional(),
78
+ snippet: z.string().optional(),
79
+ error: Schema2.ReportError.optional(),
80
+ get steps() {
81
+ return z.array(Schema2.TestStep).optional();
82
+ }
83
+ });
84
+ Schema2.Attachment = z.object({
85
+ name: z.string(),
86
+ contentType: z.string(),
87
+ id: Schema2.AttachmentId
88
+ });
89
+ Schema2.Annotation = z.object({
90
+ type: z.string(),
91
+ description: z.string().optional(),
92
+ location: Schema2.Location.optional()
93
+ });
94
+ Schema2.RunAttempt = z.object({
95
+ // Index of the environment in the environments array (must be >= 0).
96
+ environmentIdx: z.number().min(0).optional(),
97
+ expectedStatus: Schema2.TestStatus.optional(),
98
+ status: Schema2.TestStatus.optional(),
99
+ startTimestamp: Schema2.UnixTimestampMS,
100
+ duration: Schema2.DurationMS.optional(),
101
+ timeout: Schema2.DurationMS.optional(),
102
+ annotations: z.array(Schema2.Annotation).optional(),
103
+ errors: z.array(Schema2.ReportError).optional(),
104
+ parallelIndex: z.number().optional(),
105
+ steps: z.array(Schema2.TestStep).optional(),
106
+ stdio: z.array(Schema2.TimedSTDIOEntry).optional(),
107
+ stdout: z.array(Schema2.STDIOEntry).optional(),
108
+ stderr: z.array(Schema2.STDIOEntry).optional(),
109
+ attachments: z.array(Schema2.Attachment).optional()
110
+ });
111
+ Schema2.Suite = z.object({
112
+ type: Schema2.SuiteType,
113
+ title: z.string(),
114
+ location: Schema2.Location.optional(),
115
+ get suites() {
116
+ return z.array(Schema2.Suite).optional();
117
+ },
118
+ get tests() {
119
+ return z.array(Schema2.Test).optional();
120
+ }
121
+ });
122
+ Schema2.Test = z.object({
123
+ title: z.string(),
124
+ location: Schema2.Location.optional(),
125
+ tags: z.array(z.string()).optional(),
126
+ attempts: z.array(Schema2.RunAttempt)
127
+ });
128
+ Schema2.SystemUtilizationSample = z.object({
129
+ dts: Schema2.DurationMS,
130
+ // Must be between 0 and 100 (inclusive). Can be a rational number.
131
+ cpuUtilization: z.number().min(0).max(100),
132
+ // Must be between 0 and 100 (inclusive). Can be a rational number.
133
+ memoryUtilization: z.number().min(0).max(100)
134
+ });
135
+ Schema2.SystemUtilization = z.object({
136
+ totalMemoryBytes: z.number().min(0),
137
+ startTimestamp: Schema2.UnixTimestampMS,
138
+ samples: z.array(Schema2.SystemUtilizationSample)
139
+ });
140
+ Schema2.UtilizationTelemetry = z.tuple([Schema2.DurationMS, z.number().min(0).max(100)]);
141
+ Schema2.FlakinessProject = z.string();
142
+ Schema2.Report = z.object({
143
+ flakinessProject: Schema2.FlakinessProject.optional(),
144
+ category: z.string().min(1).max(100),
145
+ commitId: Schema2.CommitId,
146
+ relatedCommitIds: z.array(Schema2.CommitId).optional(),
147
+ configPath: Schema2.GitFilePath.optional(),
148
+ url: z.string().optional(),
149
+ environments: z.array(Schema2.Environment).min(1),
150
+ suites: z.array(Schema2.Suite).optional(),
151
+ tests: z.array(Schema2.Test).optional(),
152
+ unattributedErrors: z.array(Schema2.ReportError).optional(),
153
+ startTimestamp: Schema2.UnixTimestampMS,
154
+ duration: Schema2.DurationMS,
155
+ systemUtilization: z.optional(Schema2.SystemUtilization),
156
+ cpuCount: z.number().min(0).optional(),
157
+ cpuAvg: z.array(Schema2.UtilizationTelemetry).optional(),
158
+ cpuMax: z.array(Schema2.UtilizationTelemetry).optional(),
159
+ ram: z.array(Schema2.UtilizationTelemetry).optional(),
160
+ ramBytes: z.number().min(0).optional()
161
+ });
162
+ })(Schema || (Schema = {}));
163
+
16
164
  // src/normalizeReport.ts
17
165
  import stableObjectHash from "stable-hash";
18
166
  var Multimap = class {
@@ -44,6 +192,10 @@ function normalizeReport(report) {
44
192
  duration: attempt.duration === 0 ? void 0 : attempt.duration,
45
193
  stdout: attempt.stdout && attempt.stdout.length ? attempt.stdout : void 0,
46
194
  stderr: attempt.stderr && attempt.stderr.length ? attempt.stderr : void 0,
195
+ stdio: attempt.stdio && attempt.stdio.length ? attempt.stdio.map((entry) => ({
196
+ ...entry,
197
+ stream: entry.stream === FlakinessReport.STREAM_STDOUT ? void 0 : entry.stream
198
+ })) : void 0,
47
199
  attachments: attempt.attachments && attempt.attachments.length ? attempt.attachments : void 0,
48
200
  steps: attempt.steps && attempt.steps.length ? attempt.steps.map(cleanupTestStep) : void 0
49
201
  };
@@ -81,7 +233,6 @@ function deduplicateTestsSuitesEnvironments(report) {
81
233
  gEnvs.set(envId, env);
82
234
  gEnvIds.set(env, envId);
83
235
  }
84
- const usedEnvIds = /* @__PURE__ */ new Set();
85
236
  function visitTests2(tests, suiteId) {
86
237
  for (const test of tests ?? []) {
87
238
  const testId = computeTestId(test, suiteId);
@@ -89,9 +240,6 @@ function deduplicateTestsSuitesEnvironments(report) {
89
240
  gTestIds.set(test, testId);
90
241
  gSuiteTests.set(suiteId, test);
91
242
  for (const attempt of test.attempts) {
92
- const env = report.environments[attempt.environmentIdx ?? 0];
93
- const envId = gEnvIds.get(env);
94
- usedEnvIds.add(envId);
95
243
  if (attempt.annotations && !attempt.annotations.length)
96
244
  delete attempt.annotations;
97
245
  if (attempt.stdout && !attempt.stdout.length)
@@ -143,11 +291,11 @@ function deduplicateTestsSuitesEnvironments(report) {
143
291
  visitTests2(report.tests ?? [], "suiteless");
144
292
  for (const suite of report.suites ?? [])
145
293
  visitSuite(suite);
146
- const newEnvironments = [...usedEnvIds];
147
- const envIdToIndex = new Map(newEnvironments.map((envId, index) => [envId, index]));
294
+ const uniqueEnvIds = [...gEnvs.keys()];
295
+ const envIdToIndex = new Map(uniqueEnvIds.map((envId, index) => [envId, index]));
148
296
  return {
149
297
  ...report,
150
- environments: newEnvironments.map((envId) => gEnvs.get(envId)),
298
+ environments: uniqueEnvIds.map((envId) => gEnvs.get(envId)),
151
299
  suites: transformSuites(report.suites ?? []),
152
300
  tests: transformTests(report.tests ?? [])
153
301
  };
@@ -171,142 +319,6 @@ function computeTestId(test, suiteId) {
171
319
  });
172
320
  }
173
321
 
174
- // node_modules/.pnpm/@flakiness+flakiness-report@0.25.0_zod@4.3.5/node_modules/@flakiness/flakiness-report/lib/flakinessReport.js
175
- var FlakinessReport;
176
- ((FlakinessReport22) => {
177
- FlakinessReport22.CATEGORY_PLAYWRIGHT = "playwright";
178
- FlakinessReport22.CATEGORY_PYTEST = "pytest";
179
- FlakinessReport22.CATEGORY_JUNIT = "junit";
180
- })(FlakinessReport || (FlakinessReport = {}));
181
-
182
- // node_modules/.pnpm/@flakiness+flakiness-report@0.25.0_zod@4.3.5/node_modules/@flakiness/flakiness-report/lib/schema.js
183
- import z from "zod/v4";
184
- var Schema;
185
- ((Schema2) => {
186
- Schema2.CommitId = z.string().min(40).max(40);
187
- Schema2.AttachmentId = z.string().min(1).max(1024);
188
- Schema2.UnixTimestampMS = z.number().min(0);
189
- Schema2.DurationMS = z.number().min(0);
190
- Schema2.Number1Based = z.number();
191
- Schema2.GitFilePath = z.string().min(0);
192
- Schema2.Location = z.object({
193
- file: Schema2.GitFilePath,
194
- line: Schema2.Number1Based,
195
- // Note: Locations for file suites are (0, 0).
196
- column: Schema2.Number1Based
197
- });
198
- Schema2.TestStatus = z.enum(["passed", "failed", "timedOut", "skipped", "interrupted"]);
199
- Schema2.Environment = z.object({
200
- name: z.string().min(1).max(512),
201
- systemData: z.object({
202
- osName: z.string().optional(),
203
- osVersion: z.string().optional(),
204
- osArch: z.string().optional()
205
- }).optional(),
206
- metadata: z.any().optional(),
207
- userSuppliedData: z.any().optional()
208
- });
209
- Schema2.STDIOEntry = z.union([
210
- z.object({ text: z.string() }),
211
- z.object({ buffer: z.string() })
212
- ]);
213
- Schema2.ReportError = z.object({
214
- location: Schema2.Location.optional(),
215
- message: z.string().optional(),
216
- stack: z.string().optional(),
217
- snippet: z.string().optional(),
218
- value: z.string().optional()
219
- });
220
- Schema2.SuiteType = z.enum(["file", "anonymous suite", "suite"]);
221
- Schema2.TestStep = z.object({
222
- title: z.string(),
223
- duration: Schema2.DurationMS.optional(),
224
- location: Schema2.Location.optional(),
225
- snippet: z.string().optional(),
226
- error: Schema2.ReportError.optional(),
227
- get steps() {
228
- return z.array(Schema2.TestStep).optional();
229
- }
230
- });
231
- Schema2.Attachment = z.object({
232
- name: z.string(),
233
- contentType: z.string(),
234
- id: Schema2.AttachmentId
235
- });
236
- Schema2.Annotation = z.object({
237
- type: z.string(),
238
- description: z.string().optional(),
239
- location: Schema2.Location.optional()
240
- });
241
- Schema2.RunAttempt = z.object({
242
- // Index of the environment in the environments array (must be >= 0).
243
- environmentIdx: z.number().min(0).optional(),
244
- expectedStatus: Schema2.TestStatus.optional(),
245
- status: Schema2.TestStatus.optional(),
246
- startTimestamp: Schema2.UnixTimestampMS,
247
- duration: Schema2.DurationMS.optional(),
248
- timeout: Schema2.DurationMS.optional(),
249
- annotations: z.array(Schema2.Annotation).optional(),
250
- errors: z.array(Schema2.ReportError).optional(),
251
- parallelIndex: z.number().optional(),
252
- steps: z.array(Schema2.TestStep).optional(),
253
- stdout: z.array(Schema2.STDIOEntry).optional(),
254
- stderr: z.array(Schema2.STDIOEntry).optional(),
255
- attachments: z.array(Schema2.Attachment).optional()
256
- });
257
- Schema2.Suite = z.object({
258
- type: Schema2.SuiteType,
259
- title: z.string(),
260
- location: Schema2.Location.optional(),
261
- get suites() {
262
- return z.array(Schema2.Suite).optional();
263
- },
264
- get tests() {
265
- return z.array(Schema2.Test).optional();
266
- }
267
- });
268
- Schema2.Test = z.object({
269
- title: z.string(),
270
- location: Schema2.Location.optional(),
271
- tags: z.array(z.string()).optional(),
272
- attempts: z.array(Schema2.RunAttempt)
273
- });
274
- Schema2.SystemUtilizationSample = z.object({
275
- dts: Schema2.DurationMS,
276
- // Must be between 0 and 100 (inclusive). Can be a rational number.
277
- cpuUtilization: z.number().min(0).max(100),
278
- // Must be between 0 and 100 (inclusive). Can be a rational number.
279
- memoryUtilization: z.number().min(0).max(100)
280
- });
281
- Schema2.SystemUtilization = z.object({
282
- totalMemoryBytes: z.number().min(0),
283
- startTimestamp: Schema2.UnixTimestampMS,
284
- samples: z.array(Schema2.SystemUtilizationSample)
285
- });
286
- Schema2.UtilizationTelemetry = z.tuple([Schema2.DurationMS, z.number().min(0).max(100)]);
287
- Schema2.FlakinessProject = z.string();
288
- Schema2.Report = z.object({
289
- flakinessProject: Schema2.FlakinessProject.optional(),
290
- category: z.string().min(1).max(100),
291
- commitId: Schema2.CommitId,
292
- relatedCommitIds: z.array(Schema2.CommitId).optional(),
293
- configPath: Schema2.GitFilePath.optional(),
294
- url: z.string().optional(),
295
- environments: z.array(Schema2.Environment).min(1),
296
- suites: z.array(Schema2.Suite).optional(),
297
- tests: z.array(Schema2.Test).optional(),
298
- unattributedErrors: z.array(Schema2.ReportError).optional(),
299
- startTimestamp: Schema2.UnixTimestampMS,
300
- duration: Schema2.DurationMS,
301
- systemUtilization: z.optional(Schema2.SystemUtilization),
302
- cpuCount: z.number().min(0).optional(),
303
- cpuAvg: z.array(Schema2.UtilizationTelemetry).optional(),
304
- cpuMax: z.array(Schema2.UtilizationTelemetry).optional(),
305
- ram: z.array(Schema2.UtilizationTelemetry).optional(),
306
- ramBytes: z.number().min(0).optional()
307
- });
308
- })(Schema || (Schema = {}));
309
-
310
322
  // src/validateReport.ts
311
323
  import z2 from "zod/v4";
312
324
  function validateReport(report) {
package/lib/index.js CHANGED
@@ -461,6 +461,60 @@ var RAMUtilization = class {
461
461
  }
462
462
  };
463
463
 
464
+ // src/githubOIDC.ts
465
+ var GithubOIDC = class _GithubOIDC {
466
+ constructor(_requestUrl, _requestToken) {
467
+ this._requestUrl = _requestUrl;
468
+ this._requestToken = _requestToken;
469
+ }
470
+ /**
471
+ * Creates a GithubOIDC instance from GitHub Actions environment variables.
472
+ *
473
+ * Reads the `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN`
474
+ * environment variables that GitHub Actions sets for jobs with `id-token: write` permission.
475
+ *
476
+ * @returns {GithubOIDC | undefined} A GithubOIDC instance if both environment variables
477
+ * are present, or `undefined` if not running in GitHub Actions with OIDC enabled.
478
+ */
479
+ static initializeFromEnv() {
480
+ const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
481
+ const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
482
+ return requestUrl && requestToken ? new _GithubOIDC(requestUrl, requestToken) : void 0;
483
+ }
484
+ /**
485
+ * Mints a Flakiness access token for the specified project via GitHub OIDC.
486
+ *
487
+ * This method always succeeds as long as the GitHub Actions environment is properly
488
+ * configured. However, the returned token can only be used to upload reports if the
489
+ * Flakiness.io project is bound to the GitHub repository running the workflow.
490
+ * If the project is not bound, Flakiness.io will reject the token on upload.
491
+ *
492
+ * @param {string} flakinessProject - The flakiness project identifier in `"org/project"` format.
493
+ *
494
+ * @returns {Promise<string>} A Flakiness access token.
495
+ *
496
+ * @throws {Error} If the token request fails or the response does not contain a token value.
497
+ */
498
+ async createFlakinessAccessToken(flakinessProject) {
499
+ const url = new URL(this._requestUrl);
500
+ url.searchParams.set("audience", flakinessProject);
501
+ const response = await fetch(url, {
502
+ headers: {
503
+ "Authorization": `bearer ${this._requestToken}`,
504
+ "Accept": "application/json; api-version=2.0"
505
+ }
506
+ });
507
+ if (!response.ok) {
508
+ const body = await response.text().catch(() => "");
509
+ throw new Error(`Failed to request GitHub OIDC token: ${response.status} ${body}`);
510
+ }
511
+ const json = await response.json();
512
+ if (!json.value)
513
+ throw new Error("GitHub OIDC token response did not contain a token value.");
514
+ return json.value;
515
+ }
516
+ };
517
+
464
518
  // src/reportUtils.ts
465
519
  var reportUtils_exports = {};
466
520
  __export(reportUtils_exports, {
@@ -617,6 +671,154 @@ function createEnvironment(options) {
617
671
  };
618
672
  }
619
673
 
674
+ // node_modules/.pnpm/@flakiness+flakiness-report@0.27.0_zod@4.3.5/node_modules/@flakiness/flakiness-report/lib/flakinessReport.js
675
+ var FlakinessReport;
676
+ ((FlakinessReport22) => {
677
+ FlakinessReport22.CATEGORY_PLAYWRIGHT = "playwright";
678
+ FlakinessReport22.CATEGORY_PYTEST = "pytest";
679
+ FlakinessReport22.CATEGORY_JUNIT = "junit";
680
+ FlakinessReport22.STREAM_STDOUT = 1;
681
+ FlakinessReport22.STREAM_STDERR = 2;
682
+ })(FlakinessReport || (FlakinessReport = {}));
683
+
684
+ // node_modules/.pnpm/@flakiness+flakiness-report@0.27.0_zod@4.3.5/node_modules/@flakiness/flakiness-report/lib/schema.js
685
+ import z from "zod/v4";
686
+ var Schema;
687
+ ((Schema2) => {
688
+ Schema2.CommitId = z.string().min(40).max(40);
689
+ Schema2.AttachmentId = z.string().min(1).max(1024);
690
+ Schema2.UnixTimestampMS = z.number().min(0);
691
+ Schema2.DurationMS = z.number().min(0);
692
+ Schema2.Number1Based = z.number();
693
+ Schema2.GitFilePath = z.string().min(0);
694
+ Schema2.Location = z.object({
695
+ file: Schema2.GitFilePath,
696
+ line: Schema2.Number1Based,
697
+ // Note: Locations for file suites are (0, 0).
698
+ column: Schema2.Number1Based
699
+ });
700
+ Schema2.TestStatus = z.enum(["passed", "failed", "timedOut", "skipped", "interrupted"]);
701
+ Schema2.Environment = z.object({
702
+ name: z.string().min(1).max(512),
703
+ systemData: z.object({
704
+ osName: z.string().optional(),
705
+ osVersion: z.string().optional(),
706
+ osArch: z.string().optional()
707
+ }).optional(),
708
+ metadata: z.any().optional(),
709
+ userSuppliedData: z.any().optional()
710
+ });
711
+ Schema2.STDIOEntry = z.union([
712
+ z.object({ text: z.string() }),
713
+ z.object({ buffer: z.string() })
714
+ ]);
715
+ Schema2.STREAM_STDOUT = z.literal(FlakinessReport.STREAM_STDOUT);
716
+ Schema2.STREAM_STDERR = z.literal(FlakinessReport.STREAM_STDERR);
717
+ Schema2.TimedSTDIOEntry = z.object({
718
+ stream: z.union([Schema2.STREAM_STDOUT, Schema2.STREAM_STDERR]).optional(),
719
+ dts: Schema2.DurationMS
720
+ }).and(z.union([
721
+ z.object({ text: z.string() }),
722
+ z.object({ buffer: z.string() })
723
+ ]));
724
+ Schema2.ReportError = z.object({
725
+ location: Schema2.Location.optional(),
726
+ message: z.string().optional(),
727
+ stack: z.string().optional(),
728
+ snippet: z.string().optional(),
729
+ value: z.string().optional()
730
+ });
731
+ Schema2.SuiteType = z.enum(["file", "anonymous suite", "suite"]);
732
+ Schema2.TestStep = z.object({
733
+ title: z.string(),
734
+ duration: Schema2.DurationMS.optional(),
735
+ location: Schema2.Location.optional(),
736
+ snippet: z.string().optional(),
737
+ error: Schema2.ReportError.optional(),
738
+ get steps() {
739
+ return z.array(Schema2.TestStep).optional();
740
+ }
741
+ });
742
+ Schema2.Attachment = z.object({
743
+ name: z.string(),
744
+ contentType: z.string(),
745
+ id: Schema2.AttachmentId
746
+ });
747
+ Schema2.Annotation = z.object({
748
+ type: z.string(),
749
+ description: z.string().optional(),
750
+ location: Schema2.Location.optional()
751
+ });
752
+ Schema2.RunAttempt = z.object({
753
+ // Index of the environment in the environments array (must be >= 0).
754
+ environmentIdx: z.number().min(0).optional(),
755
+ expectedStatus: Schema2.TestStatus.optional(),
756
+ status: Schema2.TestStatus.optional(),
757
+ startTimestamp: Schema2.UnixTimestampMS,
758
+ duration: Schema2.DurationMS.optional(),
759
+ timeout: Schema2.DurationMS.optional(),
760
+ annotations: z.array(Schema2.Annotation).optional(),
761
+ errors: z.array(Schema2.ReportError).optional(),
762
+ parallelIndex: z.number().optional(),
763
+ steps: z.array(Schema2.TestStep).optional(),
764
+ stdio: z.array(Schema2.TimedSTDIOEntry).optional(),
765
+ stdout: z.array(Schema2.STDIOEntry).optional(),
766
+ stderr: z.array(Schema2.STDIOEntry).optional(),
767
+ attachments: z.array(Schema2.Attachment).optional()
768
+ });
769
+ Schema2.Suite = z.object({
770
+ type: Schema2.SuiteType,
771
+ title: z.string(),
772
+ location: Schema2.Location.optional(),
773
+ get suites() {
774
+ return z.array(Schema2.Suite).optional();
775
+ },
776
+ get tests() {
777
+ return z.array(Schema2.Test).optional();
778
+ }
779
+ });
780
+ Schema2.Test = z.object({
781
+ title: z.string(),
782
+ location: Schema2.Location.optional(),
783
+ tags: z.array(z.string()).optional(),
784
+ attempts: z.array(Schema2.RunAttempt)
785
+ });
786
+ Schema2.SystemUtilizationSample = z.object({
787
+ dts: Schema2.DurationMS,
788
+ // Must be between 0 and 100 (inclusive). Can be a rational number.
789
+ cpuUtilization: z.number().min(0).max(100),
790
+ // Must be between 0 and 100 (inclusive). Can be a rational number.
791
+ memoryUtilization: z.number().min(0).max(100)
792
+ });
793
+ Schema2.SystemUtilization = z.object({
794
+ totalMemoryBytes: z.number().min(0),
795
+ startTimestamp: Schema2.UnixTimestampMS,
796
+ samples: z.array(Schema2.SystemUtilizationSample)
797
+ });
798
+ Schema2.UtilizationTelemetry = z.tuple([Schema2.DurationMS, z.number().min(0).max(100)]);
799
+ Schema2.FlakinessProject = z.string();
800
+ Schema2.Report = z.object({
801
+ flakinessProject: Schema2.FlakinessProject.optional(),
802
+ category: z.string().min(1).max(100),
803
+ commitId: Schema2.CommitId,
804
+ relatedCommitIds: z.array(Schema2.CommitId).optional(),
805
+ configPath: Schema2.GitFilePath.optional(),
806
+ url: z.string().optional(),
807
+ environments: z.array(Schema2.Environment).min(1),
808
+ suites: z.array(Schema2.Suite).optional(),
809
+ tests: z.array(Schema2.Test).optional(),
810
+ unattributedErrors: z.array(Schema2.ReportError).optional(),
811
+ startTimestamp: Schema2.UnixTimestampMS,
812
+ duration: Schema2.DurationMS,
813
+ systemUtilization: z.optional(Schema2.SystemUtilization),
814
+ cpuCount: z.number().min(0).optional(),
815
+ cpuAvg: z.array(Schema2.UtilizationTelemetry).optional(),
816
+ cpuMax: z.array(Schema2.UtilizationTelemetry).optional(),
817
+ ram: z.array(Schema2.UtilizationTelemetry).optional(),
818
+ ramBytes: z.number().min(0).optional()
819
+ });
820
+ })(Schema || (Schema = {}));
821
+
620
822
  // src/normalizeReport.ts
621
823
  import stableObjectHash from "stable-hash";
622
824
  var Multimap = class {
@@ -648,6 +850,10 @@ function normalizeReport(report) {
648
850
  duration: attempt.duration === 0 ? void 0 : attempt.duration,
649
851
  stdout: attempt.stdout && attempt.stdout.length ? attempt.stdout : void 0,
650
852
  stderr: attempt.stderr && attempt.stderr.length ? attempt.stderr : void 0,
853
+ stdio: attempt.stdio && attempt.stdio.length ? attempt.stdio.map((entry) => ({
854
+ ...entry,
855
+ stream: entry.stream === FlakinessReport.STREAM_STDOUT ? void 0 : entry.stream
856
+ })) : void 0,
651
857
  attachments: attempt.attachments && attempt.attachments.length ? attempt.attachments : void 0,
652
858
  steps: attempt.steps && attempt.steps.length ? attempt.steps.map(cleanupTestStep) : void 0
653
859
  };
@@ -685,7 +891,6 @@ function deduplicateTestsSuitesEnvironments(report) {
685
891
  gEnvs.set(envId, env);
686
892
  gEnvIds.set(env, envId);
687
893
  }
688
- const usedEnvIds = /* @__PURE__ */ new Set();
689
894
  function visitTests2(tests, suiteId) {
690
895
  for (const test of tests ?? []) {
691
896
  const testId = computeTestId(test, suiteId);
@@ -693,9 +898,6 @@ function deduplicateTestsSuitesEnvironments(report) {
693
898
  gTestIds.set(test, testId);
694
899
  gSuiteTests.set(suiteId, test);
695
900
  for (const attempt of test.attempts) {
696
- const env = report.environments[attempt.environmentIdx ?? 0];
697
- const envId = gEnvIds.get(env);
698
- usedEnvIds.add(envId);
699
901
  if (attempt.annotations && !attempt.annotations.length)
700
902
  delete attempt.annotations;
701
903
  if (attempt.stdout && !attempt.stdout.length)
@@ -747,11 +949,11 @@ function deduplicateTestsSuitesEnvironments(report) {
747
949
  visitTests2(report.tests ?? [], "suiteless");
748
950
  for (const suite of report.suites ?? [])
749
951
  visitSuite(suite);
750
- const newEnvironments = [...usedEnvIds];
751
- const envIdToIndex = new Map(newEnvironments.map((envId, index) => [envId, index]));
952
+ const uniqueEnvIds = [...gEnvs.keys()];
953
+ const envIdToIndex = new Map(uniqueEnvIds.map((envId, index) => [envId, index]));
752
954
  return {
753
955
  ...report,
754
- environments: newEnvironments.map((envId) => gEnvs.get(envId)),
956
+ environments: uniqueEnvIds.map((envId) => gEnvs.get(envId)),
755
957
  suites: transformSuites(report.suites ?? []),
756
958
  tests: transformTests(report.tests ?? [])
757
959
  };
@@ -775,142 +977,6 @@ function computeTestId(test, suiteId) {
775
977
  });
776
978
  }
777
979
 
778
- // node_modules/.pnpm/@flakiness+flakiness-report@0.25.0_zod@4.3.5/node_modules/@flakiness/flakiness-report/lib/flakinessReport.js
779
- var FlakinessReport;
780
- ((FlakinessReport22) => {
781
- FlakinessReport22.CATEGORY_PLAYWRIGHT = "playwright";
782
- FlakinessReport22.CATEGORY_PYTEST = "pytest";
783
- FlakinessReport22.CATEGORY_JUNIT = "junit";
784
- })(FlakinessReport || (FlakinessReport = {}));
785
-
786
- // node_modules/.pnpm/@flakiness+flakiness-report@0.25.0_zod@4.3.5/node_modules/@flakiness/flakiness-report/lib/schema.js
787
- import z from "zod/v4";
788
- var Schema;
789
- ((Schema2) => {
790
- Schema2.CommitId = z.string().min(40).max(40);
791
- Schema2.AttachmentId = z.string().min(1).max(1024);
792
- Schema2.UnixTimestampMS = z.number().min(0);
793
- Schema2.DurationMS = z.number().min(0);
794
- Schema2.Number1Based = z.number();
795
- Schema2.GitFilePath = z.string().min(0);
796
- Schema2.Location = z.object({
797
- file: Schema2.GitFilePath,
798
- line: Schema2.Number1Based,
799
- // Note: Locations for file suites are (0, 0).
800
- column: Schema2.Number1Based
801
- });
802
- Schema2.TestStatus = z.enum(["passed", "failed", "timedOut", "skipped", "interrupted"]);
803
- Schema2.Environment = z.object({
804
- name: z.string().min(1).max(512),
805
- systemData: z.object({
806
- osName: z.string().optional(),
807
- osVersion: z.string().optional(),
808
- osArch: z.string().optional()
809
- }).optional(),
810
- metadata: z.any().optional(),
811
- userSuppliedData: z.any().optional()
812
- });
813
- Schema2.STDIOEntry = z.union([
814
- z.object({ text: z.string() }),
815
- z.object({ buffer: z.string() })
816
- ]);
817
- Schema2.ReportError = z.object({
818
- location: Schema2.Location.optional(),
819
- message: z.string().optional(),
820
- stack: z.string().optional(),
821
- snippet: z.string().optional(),
822
- value: z.string().optional()
823
- });
824
- Schema2.SuiteType = z.enum(["file", "anonymous suite", "suite"]);
825
- Schema2.TestStep = z.object({
826
- title: z.string(),
827
- duration: Schema2.DurationMS.optional(),
828
- location: Schema2.Location.optional(),
829
- snippet: z.string().optional(),
830
- error: Schema2.ReportError.optional(),
831
- get steps() {
832
- return z.array(Schema2.TestStep).optional();
833
- }
834
- });
835
- Schema2.Attachment = z.object({
836
- name: z.string(),
837
- contentType: z.string(),
838
- id: Schema2.AttachmentId
839
- });
840
- Schema2.Annotation = z.object({
841
- type: z.string(),
842
- description: z.string().optional(),
843
- location: Schema2.Location.optional()
844
- });
845
- Schema2.RunAttempt = z.object({
846
- // Index of the environment in the environments array (must be >= 0).
847
- environmentIdx: z.number().min(0).optional(),
848
- expectedStatus: Schema2.TestStatus.optional(),
849
- status: Schema2.TestStatus.optional(),
850
- startTimestamp: Schema2.UnixTimestampMS,
851
- duration: Schema2.DurationMS.optional(),
852
- timeout: Schema2.DurationMS.optional(),
853
- annotations: z.array(Schema2.Annotation).optional(),
854
- errors: z.array(Schema2.ReportError).optional(),
855
- parallelIndex: z.number().optional(),
856
- steps: z.array(Schema2.TestStep).optional(),
857
- stdout: z.array(Schema2.STDIOEntry).optional(),
858
- stderr: z.array(Schema2.STDIOEntry).optional(),
859
- attachments: z.array(Schema2.Attachment).optional()
860
- });
861
- Schema2.Suite = z.object({
862
- type: Schema2.SuiteType,
863
- title: z.string(),
864
- location: Schema2.Location.optional(),
865
- get suites() {
866
- return z.array(Schema2.Suite).optional();
867
- },
868
- get tests() {
869
- return z.array(Schema2.Test).optional();
870
- }
871
- });
872
- Schema2.Test = z.object({
873
- title: z.string(),
874
- location: Schema2.Location.optional(),
875
- tags: z.array(z.string()).optional(),
876
- attempts: z.array(Schema2.RunAttempt)
877
- });
878
- Schema2.SystemUtilizationSample = z.object({
879
- dts: Schema2.DurationMS,
880
- // Must be between 0 and 100 (inclusive). Can be a rational number.
881
- cpuUtilization: z.number().min(0).max(100),
882
- // Must be between 0 and 100 (inclusive). Can be a rational number.
883
- memoryUtilization: z.number().min(0).max(100)
884
- });
885
- Schema2.SystemUtilization = z.object({
886
- totalMemoryBytes: z.number().min(0),
887
- startTimestamp: Schema2.UnixTimestampMS,
888
- samples: z.array(Schema2.SystemUtilizationSample)
889
- });
890
- Schema2.UtilizationTelemetry = z.tuple([Schema2.DurationMS, z.number().min(0).max(100)]);
891
- Schema2.FlakinessProject = z.string();
892
- Schema2.Report = z.object({
893
- flakinessProject: Schema2.FlakinessProject.optional(),
894
- category: z.string().min(1).max(100),
895
- commitId: Schema2.CommitId,
896
- relatedCommitIds: z.array(Schema2.CommitId).optional(),
897
- configPath: Schema2.GitFilePath.optional(),
898
- url: z.string().optional(),
899
- environments: z.array(Schema2.Environment).min(1),
900
- suites: z.array(Schema2.Suite).optional(),
901
- tests: z.array(Schema2.Test).optional(),
902
- unattributedErrors: z.array(Schema2.ReportError).optional(),
903
- startTimestamp: Schema2.UnixTimestampMS,
904
- duration: Schema2.DurationMS,
905
- systemUtilization: z.optional(Schema2.SystemUtilization),
906
- cpuCount: z.number().min(0).optional(),
907
- cpuAvg: z.array(Schema2.UtilizationTelemetry).optional(),
908
- cpuMax: z.array(Schema2.UtilizationTelemetry).optional(),
909
- ram: z.array(Schema2.UtilizationTelemetry).optional(),
910
- ramBytes: z.number().min(0).optional()
911
- });
912
- })(Schema || (Schema = {}));
913
-
914
980
  // src/validateReport.ts
915
981
  import z2 from "zod/v4";
916
982
  function validateReport(report) {
@@ -938,39 +1004,6 @@ function stripAnsi(str) {
938
1004
  import assert2 from "assert";
939
1005
  import fs4 from "fs";
940
1006
  import { URL as URL2 } from "url";
941
-
942
- // src/_githubOIDC.ts
943
- var GithubOIDC = class _GithubOIDC {
944
- constructor(_requestUrl, _requestToken) {
945
- this._requestUrl = _requestUrl;
946
- this._requestToken = _requestToken;
947
- }
948
- static initializeFromEnv() {
949
- const requestUrl = process.env.ACTIONS_ID_TOKEN_REQUEST_URL;
950
- const requestToken = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
951
- return requestUrl && requestToken ? new _GithubOIDC(requestUrl, requestToken) : void 0;
952
- }
953
- async fetchToken(audience) {
954
- const url = new URL(this._requestUrl);
955
- url.searchParams.set("audience", audience);
956
- const response = await fetch(url, {
957
- headers: {
958
- "Authorization": `bearer ${this._requestToken}`,
959
- "Accept": "application/json; api-version=2.0"
960
- }
961
- });
962
- if (!response.ok) {
963
- const body = await response.text().catch(() => "");
964
- throw new Error(`Failed to request GitHub OIDC token: ${response.status} ${body}`);
965
- }
966
- const json = await response.json();
967
- if (!json.value)
968
- throw new Error("GitHub OIDC token response did not contain a token value.");
969
- return json.value;
970
- }
971
- };
972
-
973
- // src/uploadReport.ts
974
1007
  async function createFileAttachment(contentType, filePath) {
975
1008
  return {
976
1009
  type: "file",
@@ -999,7 +1032,7 @@ async function uploadReport(report, attachments, options) {
999
1032
  return { status: "skipped", reason };
1000
1033
  }
1001
1034
  try {
1002
- flakinessAccessToken = await githubOIDC.fetchToken(report.flakinessProject);
1035
+ flakinessAccessToken = await githubOIDC.createFlakinessAccessToken(report.flakinessProject);
1003
1036
  if (!flakinessAccessToken)
1004
1037
  throw new Error("token is empty");
1005
1038
  } catch (e) {
@@ -1440,6 +1473,7 @@ export {
1440
1473
  CIUtils,
1441
1474
  CPUUtilization,
1442
1475
  GitWorktree,
1476
+ GithubOIDC,
1443
1477
  RAMUtilization,
1444
1478
  reportUtils_exports as ReportUtils,
1445
1479
  readReport,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flakiness/sdk",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,7 @@
25
25
  "author": "Degu Labs, Inc",
26
26
  "license": "MIT",
27
27
  "devDependencies": {
28
- "@flakiness/flakiness-report": "^0.25.0",
28
+ "@flakiness/flakiness-report": "^0.27.0",
29
29
  "@types/debug": "^4.1.12",
30
30
  "@types/node": "^25.0.3",
31
31
  "esbuild": "^0.27.0",
@@ -34,7 +34,7 @@
34
34
  "typescript": "^5.6.2"
35
35
  },
36
36
  "peerDependencies": {
37
- "@flakiness/flakiness-report": "^0.25.0"
37
+ "@flakiness/flakiness-report": "^0.27.0"
38
38
  },
39
39
  "dependencies": {
40
40
  "chalk": "^5.6.2",
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Provides GitHub Actions OIDC (OpenID Connect) token exchange.
3
+ *
4
+ * Enables passwordless authentication with Flakiness.io from GitHub Actions workflows
5
+ * by exchanging GitHub's OIDC tokens for Flakiness access tokens. Used internally by
6
+ * {@link uploadReport} for automatic authentication, but can also be used directly.
7
+ *
8
+ * Requires the workflow to have `id-token: write` permission.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const oidc = GithubOIDC.initializeFromEnv();
13
+ * if (oidc) {
14
+ * const token = await oidc.createFlakinessAccessToken('my-org/my-project');
15
+ * }
16
+ * ```
17
+ */
18
+ export declare class GithubOIDC {
19
+ private _requestUrl;
20
+ private _requestToken;
21
+ /**
22
+ * Creates a GithubOIDC instance from GitHub Actions environment variables.
23
+ *
24
+ * Reads the `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN`
25
+ * environment variables that GitHub Actions sets for jobs with `id-token: write` permission.
26
+ *
27
+ * @returns {GithubOIDC | undefined} A GithubOIDC instance if both environment variables
28
+ * are present, or `undefined` if not running in GitHub Actions with OIDC enabled.
29
+ */
30
+ static initializeFromEnv(): GithubOIDC | undefined;
31
+ constructor(_requestUrl: string, _requestToken: string);
32
+ /**
33
+ * Mints a Flakiness access token for the specified project via GitHub OIDC.
34
+ *
35
+ * This method always succeeds as long as the GitHub Actions environment is properly
36
+ * configured. However, the returned token can only be used to upload reports if the
37
+ * Flakiness.io project is bound to the GitHub repository running the workflow.
38
+ * If the project is not bound, Flakiness.io will reject the token on upload.
39
+ *
40
+ * @param {string} flakinessProject - The flakiness project identifier in `"org/project"` format.
41
+ *
42
+ * @returns {Promise<string>} A Flakiness access token.
43
+ *
44
+ * @throws {Error} If the token request fails or the response does not contain a token value.
45
+ */
46
+ createFlakinessAccessToken(flakinessProject: string): Promise<string>;
47
+ }
48
+ //# sourceMappingURL=githubOIDC.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"githubOIDC.d.ts","sourceRoot":"","sources":["../../src/githubOIDC.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,UAAU;IAiBnB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,aAAa;IAjBvB;;;;;;;;OAQG;IACH,MAAM,CAAC,iBAAiB,IAAI,UAAU,GAAC,SAAS;gBAOtC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM;IAK/B;;;;;;;;;;;;;OAaG;IACG,0BAA0B,CAAC,gBAAgB,EAAE,MAAM;CAsB1D"}
@@ -2,6 +2,7 @@ export { CIUtils } from './ciUtils.js';
2
2
  export { CPUUtilization } from './cpuUtilization.js';
3
3
  export { GitWorktree } from './gitWorktree.js';
4
4
  export { RAMUtilization } from './ramUtilization.js';
5
+ export { GithubOIDC } from './githubOIDC.js';
5
6
  export * as ReportUtils from './reportUtils.js';
6
7
  export { readReport } from './readReport.js';
7
8
  export { showReport } from './showReport.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"normalizeReport.d.ts","sourceRoot":"","sources":["../../src/normalizeReport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAyB9D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,GAAG,eAAe,CAAC,MAAM,CA+CtF"}
1
+ {"version":3,"file":"normalizeReport.d.ts","sourceRoot":"","sources":["../../src/normalizeReport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAyB9D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,GAAG,eAAe,CAAC,MAAM,CAmDtF"}
@@ -1,8 +0,0 @@
1
- export declare class GithubOIDC {
2
- private _requestUrl;
3
- private _requestToken;
4
- static initializeFromEnv(): GithubOIDC | undefined;
5
- constructor(_requestUrl: string, _requestToken: string);
6
- fetchToken(audience: string): Promise<string>;
7
- }
8
- //# sourceMappingURL=_githubOIDC.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_githubOIDC.d.ts","sourceRoot":"","sources":["../../src/_githubOIDC.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAU;IAQnB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,aAAa;IARvB,MAAM,CAAC,iBAAiB,IAAI,UAAU,GAAC,SAAS;gBAOtC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM;IAKzB,UAAU,CAAC,QAAQ,EAAE,MAAM;CAsBlC"}