@chaos-maker/core 0.3.0 → 0.5.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/sw.mjs ADDED
@@ -0,0 +1,1074 @@
1
+ var ce = Object.defineProperty;
2
+ var ie = (n, e, t) => e in n ? ce(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t;
3
+ var O = (n, e, t) => ie(n, typeof e != "symbol" ? e + "" : e, t);
4
+ class de {
5
+ constructor(e = 2e3) {
6
+ O(this, "listeners", /* @__PURE__ */ new Map());
7
+ O(this, "log", []);
8
+ O(this, "logger");
9
+ O(this, "ruleIds");
10
+ this.maxLogEntries = e;
11
+ }
12
+ on(e, t) {
13
+ this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(t);
14
+ }
15
+ off(e, t) {
16
+ var o;
17
+ (o = this.listeners.get(e)) == null || o.delete(t);
18
+ }
19
+ emit(e) {
20
+ this.log.push(e), this.log.length > this.maxLogEntries && this.log.shift(), this.notify(this.listeners.get(e.type), e), this.notify(this.listeners.get("*"), e);
21
+ }
22
+ /** Attach a Debug Mode logger. When unset, `debug()` is a fast-path no-op. */
23
+ setLogger(e) {
24
+ this.logger = e;
25
+ }
26
+ /** Attach the rule-id map so debug events auto-resolve `ruleType` /
27
+ * `ruleId` from a rule object reference. */
28
+ setRuleIds(e) {
29
+ this.ruleIds = e;
30
+ }
31
+ /**
32
+ * Emit a Debug Mode event. Fast-path no-op when no logger is attached —
33
+ * single undefined-check before any allocation. When `rule` is supplied
34
+ * and present in the rule-id map, `detail.ruleType` and `detail.ruleId`
35
+ * are filled in automatically.
36
+ */
37
+ debug(e, t, o) {
38
+ var f;
39
+ if (!this.logger) return;
40
+ const s = o ? (f = this.ruleIds) == null ? void 0 : f.get(o) : void 0, p = s ? { ...t, ruleType: s.ruleType, ruleId: s.ruleId } : t, l = this.logger.log(e, p);
41
+ l && this.emit(l);
42
+ }
43
+ getLog() {
44
+ return [...this.log];
45
+ }
46
+ clearLog() {
47
+ this.log = [];
48
+ }
49
+ notify(e, t) {
50
+ if (e)
51
+ for (const o of e)
52
+ try {
53
+ o(t);
54
+ } catch {
55
+ }
56
+ }
57
+ }
58
+ function pe(n) {
59
+ let e = n | 0;
60
+ return () => {
61
+ e = e + 1831565813 | 0;
62
+ let t = Math.imul(e ^ e >>> 15, 1 | e);
63
+ return t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t, ((t ^ t >>> 14) >>> 0) / 4294967296;
64
+ };
65
+ }
66
+ function fe() {
67
+ return Math.random() * 4294967296 >>> 0;
68
+ }
69
+ function re(n) {
70
+ const e = n ?? fe();
71
+ return {
72
+ random: pe(e),
73
+ seed: e
74
+ };
75
+ }
76
+ const z = "default";
77
+ class se {
78
+ constructor() {
79
+ O(this, "groups", /* @__PURE__ */ new Map());
80
+ /**
81
+ * Tracks groups that have already emitted a `rule-group:gated` event since
82
+ * the last toggle. Cleared on every `setEnabled()` so the next toggle cycle
83
+ * gets a fresh diagnostic event without flooding.
84
+ */
85
+ O(this, "gatedEmitted", /* @__PURE__ */ new Set());
86
+ }
87
+ /**
88
+ * Look up an existing group or create a new one.
89
+ *
90
+ * - Implicit (auto-create from `isActive`): `explicit: false`, defaults
91
+ * `enabled: true`.
92
+ * - Explicit (`ChaosConfig.groups` / `createGroup()`): `explicit: true`.
93
+ * When called with both `explicit: true` and `enabled` set, the existing
94
+ * group's `enabled` is overwritten; the explicit form is the source of truth.
95
+ */
96
+ ensure(e, t) {
97
+ const o = this.groups.get(e);
98
+ if (o)
99
+ return t != null && t.explicit && (o.explicit = !0), (t == null ? void 0 : t.enabled) !== void 0 && (o.enabled = t.enabled), o;
100
+ const s = {
101
+ name: e,
102
+ enabled: (t == null ? void 0 : t.enabled) ?? !0,
103
+ explicit: (t == null ? void 0 : t.explicit) ?? !1
104
+ };
105
+ return this.groups.set(e, s), s;
106
+ }
107
+ setEnabled(e, t) {
108
+ this.ensure(e).enabled = t, this.gatedEmitted.clear();
109
+ }
110
+ /**
111
+ * Auto-creates unknown groups on first check (implicit). Rationale:
112
+ * silently returning `true` for unknown names lets typos like
113
+ * `group: 'paymets'` mask chaos as if no group existed; auto-registering
114
+ * surfaces the typo via `list()` / `getSnapshot()` and keeps default-on
115
+ * backward compat.
116
+ */
117
+ isActive(e) {
118
+ return this.ensure(e ?? z).enabled;
119
+ }
120
+ /** True when this group should emit a `rule-group:gated` event right now (first block since last toggle). */
121
+ shouldEmitGated(e) {
122
+ return this.gatedEmitted.has(e) ? !1 : (this.gatedEmitted.add(e), !0);
123
+ }
124
+ has(e) {
125
+ return this.groups.has(e);
126
+ }
127
+ /**
128
+ * Remove a group from the registry.
129
+ * - `'default'` cannot be removed (returns `false`).
130
+ * - By default, throws when any rule in `referencedBy` still uses the group.
131
+ * - Pass `{ force: true }` to remove anyway. Subsequent `isActive(name)`
132
+ * calls auto-recreate the group (default-on).
133
+ */
134
+ remove(e, t, o) {
135
+ if (e === z) return !1;
136
+ if (!(o != null && o.force) && t.has(e))
137
+ throw new Error(
138
+ `[chaos-maker] Cannot remove group '${e}': still referenced by one or more rules. Pass { force: true } to override.`
139
+ );
140
+ const s = this.groups.delete(e);
141
+ return s && this.gatedEmitted.delete(e), s;
142
+ }
143
+ list() {
144
+ return [...this.groups.values()].map(
145
+ (e) => ({ ...e })
146
+ );
147
+ }
148
+ getSnapshot() {
149
+ const e = {};
150
+ for (const t of this.groups.values()) e[t.name] = t.enabled;
151
+ return e;
152
+ }
153
+ }
154
+ function $(n, e) {
155
+ return e() < n;
156
+ }
157
+ function q(n, e) {
158
+ const t = (e.get(n) ?? 0) + 1;
159
+ return e.set(n, t), t;
160
+ }
161
+ function P(n, e) {
162
+ return n.onNth !== void 0 ? e === n.onNth : n.everyNth !== void 0 ? e % n.everyNth === 0 : n.afterN !== void 0 ? e > n.afterN : !0;
163
+ }
164
+ function Z(n, e) {
165
+ return e === "*" ? !0 : n.includes(e);
166
+ }
167
+ function ge(n, e) {
168
+ switch (e) {
169
+ case "truncate":
170
+ return n.slice(0, Math.max(0, Math.floor(n.length / 2)));
171
+ case "malformed-json":
172
+ return `${n}"}`;
173
+ case "empty":
174
+ return "";
175
+ case "wrong-type":
176
+ return "<html><body>Unexpected HTML</body></html>";
177
+ }
178
+ }
179
+ function G(n, e, t, o) {
180
+ if (!e || e.isActive(n.group)) return !0;
181
+ const s = n.group ?? z;
182
+ return e.shouldEmitGated(s) && t && t.emit({
183
+ type: "rule-group:gated",
184
+ timestamp: Date.now(),
185
+ applied: !1,
186
+ detail: { ...o, groupName: s }
187
+ }), t == null || t.debug("rule-skip-group", { ...o, groupName: s }, n), !1;
188
+ }
189
+ function he(n, e) {
190
+ var o, s, p, l, f, u, a, E, x, M, L, A, S, d;
191
+ const t = (c) => {
192
+ if (c)
193
+ for (const i of c) e(i);
194
+ };
195
+ t((o = n.network) == null ? void 0 : o.failures), t((s = n.network) == null ? void 0 : s.latencies), t((p = n.network) == null ? void 0 : p.aborts), t((l = n.network) == null ? void 0 : l.corruptions), t((f = n.network) == null ? void 0 : f.cors), t((u = n.ui) == null ? void 0 : u.assaults), t((a = n.websocket) == null ? void 0 : a.drops), t((E = n.websocket) == null ? void 0 : E.delays), t((x = n.websocket) == null ? void 0 : x.corruptions), t((M = n.websocket) == null ? void 0 : M.closes), t((L = n.sse) == null ? void 0 : L.drops), t((A = n.sse) == null ? void 0 : A.delays), t((S = n.sse) == null ? void 0 : S.corruptions), t((d = n.sse) == null ? void 0 : d.closes);
196
+ }
197
+ function ae(n) {
198
+ const e = n.replace(/#[^\r\n]*/g, " "), t = /\b(?:query|mutation|subscription)\s+([A-Za-z_][A-Za-z0-9_]*)/.exec(e);
199
+ return (t == null ? void 0 : t[1]) ?? null;
200
+ }
201
+ function be(n) {
202
+ try {
203
+ return JSON.parse(n);
204
+ } catch {
205
+ return;
206
+ }
207
+ }
208
+ function ue(n) {
209
+ if (!n || typeof n != "object") return { isGraphQL: !1, operationName: null };
210
+ if (Array.isArray(n))
211
+ return n.length === 0 ? { isGraphQL: !1, operationName: null } : ue(n[0]);
212
+ const e = n, t = typeof e.query == "string", o = typeof e.operationName == "string";
213
+ return !t && !o ? { isGraphQL: !1, operationName: null } : o && e.operationName.length > 0 ? { isGraphQL: !0, operationName: e.operationName } : t ? { isGraphQL: !0, operationName: ae(e.query) } : { isGraphQL: !0, operationName: null };
214
+ }
215
+ function ye(n) {
216
+ let e;
217
+ try {
218
+ e = new URL(n, "http://_chaos-maker.invalid");
219
+ } catch {
220
+ return { kind: "not-graphql" };
221
+ }
222
+ const t = e.searchParams.get("operationName"), o = e.searchParams.get("query");
223
+ return t && t.length > 0 ? { kind: "extracted", operationName: t } : o && o.length > 0 ? { kind: "extracted", operationName: ae(o) } : { kind: "not-graphql" };
224
+ }
225
+ function ke(n, e, t, o) {
226
+ const s = n.toUpperCase();
227
+ if (s === "POST") {
228
+ if (t !== null) {
229
+ const p = be(t);
230
+ if (p === void 0) return { kind: "not-graphql" };
231
+ const { isGraphQL: l, operationName: f } = ue(p);
232
+ return l ? { kind: "extracted", operationName: f } : { kind: "not-graphql" };
233
+ }
234
+ return o ? { kind: "unparseable" } : { kind: "not-graphql" };
235
+ }
236
+ return s === "GET" ? ye(e) : { kind: "not-graphql" };
237
+ }
238
+ function we(n, e) {
239
+ return e === null ? !1 : typeof n == "string" ? n === e : ((n.global || n.sticky) && (n.lastIndex = 0), n.test(e));
240
+ }
241
+ function Me(n, e) {
242
+ return n ? e.kind === "not-graphql" ? { kind: "no-match" } : e.kind === "unparseable" ? { kind: "unparseable" } : we(n, e.operationName) ? { kind: "match", operationName: e.operationName } : { kind: "no-match" } : e.kind === "extracted" ? { kind: "skip-no-constraint", operationName: e.operationName } : { kind: "skip-no-constraint", operationName: null };
243
+ }
244
+ function J(n) {
245
+ return typeof Request < "u" && n instanceof Request;
246
+ }
247
+ function ve(n) {
248
+ return J(n) ? n.url : n.toString();
249
+ }
250
+ function Ce(n, e) {
251
+ return e != null && e.method ? e.method.toUpperCase() : J(n) ? n.method.toUpperCase() : "GET";
252
+ }
253
+ function Ee(n, e) {
254
+ if (e != null && e.signal)
255
+ return e.signal;
256
+ if (J(n))
257
+ return n.signal;
258
+ }
259
+ function Se() {
260
+ if (typeof DOMException < "u")
261
+ return new DOMException("The user aborted a request.", "AbortError");
262
+ const n = new Error("The user aborted a request.");
263
+ return n.name = "AbortError", n;
264
+ }
265
+ function Te(n, e) {
266
+ if (!e)
267
+ return n;
268
+ const t = new AbortController(), o = (f) => {
269
+ l(), t.signal.aborted || t.abort(f.reason);
270
+ }, s = () => o(n), p = () => o(e), l = () => {
271
+ n.removeEventListener("abort", s), e.removeEventListener("abort", p);
272
+ };
273
+ return n.aborted ? (o(n), t.signal) : e.aborted ? (o(e), t.signal) : (n.addEventListener("abort", s, { once: !0 }), e.addEventListener("abort", p, { once: !0 }), t.signal);
274
+ }
275
+ function Le(n, e) {
276
+ return e ? {
277
+ ...n ?? {},
278
+ signal: e
279
+ } : n;
280
+ }
281
+ async function xe(n, e) {
282
+ const t = e == null ? void 0 : e.body;
283
+ if (t != null) {
284
+ if (typeof t == "string") return { text: t, unparseable: !1 };
285
+ if (typeof URLSearchParams < "u" && t instanceof URLSearchParams)
286
+ return { text: t.toString(), unparseable: !1 };
287
+ if (typeof Blob < "u" && t instanceof Blob) {
288
+ if (!(t.type === "" || /json|text|graphql/i.test(t.type))) return { text: null, unparseable: !0 };
289
+ try {
290
+ return { text: await t.text(), unparseable: !1 };
291
+ } catch {
292
+ return { text: null, unparseable: !0 };
293
+ }
294
+ }
295
+ return { text: null, unparseable: !0 };
296
+ }
297
+ if (J(n)) {
298
+ if (n.body === null) return { text: null, unparseable: !1 };
299
+ try {
300
+ return { text: await n.clone().text(), unparseable: !1 };
301
+ } catch {
302
+ return { text: null, unparseable: !0 };
303
+ }
304
+ }
305
+ return { text: null, unparseable: !1 };
306
+ }
307
+ function U(n, e, t, o, s) {
308
+ n == null || n.emit({
309
+ type: e,
310
+ timestamp: Date.now(),
311
+ applied: !1,
312
+ detail: { url: t, method: o, ...s, reason: "graphql-body-unparseable" }
313
+ });
314
+ }
315
+ function B(n, e, t, o) {
316
+ if (!Z(e, n.urlPattern)) return { proceed: !1, outcome: null };
317
+ if (n.methods && !n.methods.includes(t)) return { proceed: !1, outcome: null };
318
+ const s = Me(n.graphqlOperation, o);
319
+ return s.kind === "no-match" ? { proceed: !1, outcome: s } : { proceed: !0, outcome: s };
320
+ }
321
+ function W(n) {
322
+ return n ? n.kind === "match" || n.kind === "skip-no-constraint" ? n.operationName ? { operationName: n.operationName } : {} : {} : {};
323
+ }
324
+ function V(n, e, t, o, s, p) {
325
+ n == null || n.emit({
326
+ type: "network:abort",
327
+ timestamp: Date.now(),
328
+ applied: s,
329
+ detail: { url: t, method: o, timeoutMs: e.timeout, ...W(p) }
330
+ });
331
+ }
332
+ function Q(n, e, t, o, s, p) {
333
+ n == null || n.emit({
334
+ type: "network:corruption",
335
+ timestamp: Date.now(),
336
+ applied: s,
337
+ detail: { url: t, method: o, strategy: e.strategy, ...W(p) }
338
+ });
339
+ }
340
+ function Ae(n) {
341
+ return n.graphqlOperation !== void 0;
342
+ }
343
+ function Ne(n, e, t, o, s = /* @__PURE__ */ new Map(), p) {
344
+ return async (l, f) => {
345
+ var k, w, C, T, R;
346
+ const u = ve(l), a = Ce(l, f), E = Ee(l, f), x = (() => {
347
+ const r = [e.failures, e.latencies, e.aborts, e.corruptions, e.cors];
348
+ for (const b of r)
349
+ if (b != null && b.some(Ae)) return !0;
350
+ return !1;
351
+ })();
352
+ let M = { kind: "not-graphql" };
353
+ if (x) {
354
+ const r = await xe(l, f);
355
+ M = ke(a, u, r.text, r.unparseable);
356
+ }
357
+ if (e.cors)
358
+ for (const r of e.cors) {
359
+ o == null || o.debug("rule-evaluating", { url: u, method: a }, r);
360
+ const b = B(r, u, a, M);
361
+ if (!b.proceed) {
362
+ o == null || o.debug("rule-skip-match", { url: u, method: a }, r);
363
+ continue;
364
+ }
365
+ o == null || o.debug("rule-matched", { url: u, method: a }, r);
366
+ const N = q(r, s);
367
+ if (!P(r, N)) {
368
+ o == null || o.debug("rule-skip-counting", { url: u, method: a }, r);
369
+ continue;
370
+ }
371
+ if (!G(r, p, o, { url: u, method: a })) continue;
372
+ const v = $(r.probability, t);
373
+ if (((k = b.outcome) == null ? void 0 : k.kind) === "unparseable") {
374
+ v && U(o, "network:cors", u, a, {});
375
+ continue;
376
+ }
377
+ if (o == null || o.emit({
378
+ type: "network:cors",
379
+ timestamp: Date.now(),
380
+ applied: v,
381
+ detail: { url: u, method: a, ...W(b.outcome) }
382
+ }), !v) {
383
+ o == null || o.debug("rule-skip-probability", { url: u, method: a }, r);
384
+ continue;
385
+ }
386
+ o == null || o.debug("rule-applied", { url: u, method: a }, r);
387
+ const _ = new TypeError("Failed to fetch");
388
+ throw _.stack = "", _;
389
+ }
390
+ let L = null, A = null, S, d, c = !1;
391
+ const i = (r) => {
392
+ !L || c || (c = !0, d && (clearTimeout(d), d = void 0), V(o, L, u, a, r, A));
393
+ };
394
+ if (e.aborts)
395
+ for (const r of e.aborts) {
396
+ o == null || o.debug("rule-evaluating", { url: u, method: a, timeoutMs: r.timeout }, r);
397
+ const b = B(r, u, a, M);
398
+ if (!b.proceed) {
399
+ o == null || o.debug("rule-skip-match", { url: u, method: a, timeoutMs: r.timeout }, r);
400
+ continue;
401
+ }
402
+ o == null || o.debug("rule-matched", { url: u, method: a, timeoutMs: r.timeout }, r);
403
+ const N = q(r, s);
404
+ if (!P(r, N)) {
405
+ o == null || o.debug("rule-skip-counting", { url: u, method: a, timeoutMs: r.timeout }, r);
406
+ continue;
407
+ }
408
+ if (!G(r, p, o, { url: u, method: a, timeoutMs: r.timeout })) continue;
409
+ const v = $(r.probability, t);
410
+ if (((w = b.outcome) == null ? void 0 : w.kind) === "unparseable") {
411
+ v && U(o, "network:abort", u, a, { timeoutMs: r.timeout });
412
+ continue;
413
+ }
414
+ if (!v) {
415
+ o == null || o.debug("rule-skip-probability", { url: u, method: a, timeoutMs: r.timeout }, r), V(o, r, u, a, !1, b.outcome);
416
+ continue;
417
+ }
418
+ o == null || o.debug("rule-applied", { url: u, method: a, timeoutMs: r.timeout }, r), console.warn(`CHAOS: Aborting ${a} ${u} after ${r.timeout || 0}ms`), L = r, A = b.outcome;
419
+ const _ = new AbortController();
420
+ S = Te(_.signal, E);
421
+ const H = () => {
422
+ c || (i(!0), _.abort(Se()));
423
+ };
424
+ r.timeout ? d = setTimeout(H, r.timeout) : H();
425
+ break;
426
+ }
427
+ if (L)
428
+ try {
429
+ const r = await n(l, Le(f, S));
430
+ return i(!1), r;
431
+ } catch (r) {
432
+ throw i(!1), r;
433
+ }
434
+ if (e.failures)
435
+ for (const r of e.failures) {
436
+ o == null || o.debug("rule-evaluating", { url: u, method: a, statusCode: r.statusCode }, r);
437
+ const b = B(r, u, a, M);
438
+ if (!b.proceed) {
439
+ o == null || o.debug("rule-skip-match", { url: u, method: a, statusCode: r.statusCode }, r);
440
+ continue;
441
+ }
442
+ o == null || o.debug("rule-matched", { url: u, method: a, statusCode: r.statusCode }, r);
443
+ const N = q(r, s);
444
+ if (!P(r, N)) {
445
+ o == null || o.debug("rule-skip-counting", { url: u, method: a, statusCode: r.statusCode }, r);
446
+ continue;
447
+ }
448
+ if (!G(r, p, o, { url: u, method: a, statusCode: r.statusCode })) continue;
449
+ const v = $(r.probability, t);
450
+ if (((C = b.outcome) == null ? void 0 : C.kind) === "unparseable") {
451
+ v && U(o, "network:failure", u, a, { statusCode: r.statusCode });
452
+ continue;
453
+ }
454
+ if (o == null || o.emit({
455
+ type: "network:failure",
456
+ timestamp: Date.now(),
457
+ applied: v,
458
+ detail: { url: u, method: a, statusCode: r.statusCode, ...W(b.outcome) }
459
+ }), !v) {
460
+ o == null || o.debug("rule-skip-probability", { url: u, method: a, statusCode: r.statusCode }, r);
461
+ continue;
462
+ }
463
+ o == null || o.debug("rule-applied", { url: u, method: a, statusCode: r.statusCode }, r), console.warn(`CHAOS: Forcing ${r.statusCode} for ${a} ${u}`);
464
+ const _ = r.body ?? JSON.stringify({ error: "Chaos Maker Attack!" }), H = r.headers ?? {};
465
+ return new Response(_, {
466
+ status: r.statusCode,
467
+ statusText: r.statusText ?? "Service Unavailable (Chaos)",
468
+ headers: H
469
+ });
470
+ }
471
+ if (e.latencies)
472
+ for (const r of e.latencies) {
473
+ o == null || o.debug("rule-evaluating", { url: u, method: a, delayMs: r.delayMs }, r);
474
+ const b = B(r, u, a, M);
475
+ if (!b.proceed) {
476
+ o == null || o.debug("rule-skip-match", { url: u, method: a, delayMs: r.delayMs }, r);
477
+ continue;
478
+ }
479
+ o == null || o.debug("rule-matched", { url: u, method: a, delayMs: r.delayMs }, r);
480
+ const N = q(r, s);
481
+ if (!P(r, N)) {
482
+ o == null || o.debug("rule-skip-counting", { url: u, method: a, delayMs: r.delayMs }, r);
483
+ continue;
484
+ }
485
+ if (!G(r, p, o, { url: u, method: a, delayMs: r.delayMs })) continue;
486
+ const v = $(r.probability, t);
487
+ if (((T = b.outcome) == null ? void 0 : T.kind) === "unparseable") {
488
+ v && U(o, "network:latency", u, a, { delayMs: r.delayMs });
489
+ continue;
490
+ }
491
+ if (o == null || o.emit({
492
+ type: "network:latency",
493
+ timestamp: Date.now(),
494
+ applied: v,
495
+ detail: { url: u, method: a, delayMs: r.delayMs, ...W(b.outcome) }
496
+ }), !v) {
497
+ o == null || o.debug("rule-skip-probability", { url: u, method: a, delayMs: r.delayMs }, r);
498
+ continue;
499
+ }
500
+ o == null || o.debug("rule-applied", { url: u, method: a, delayMs: r.delayMs }, r), console.warn(`CHAOS: Adding ${r.delayMs}ms latency to ${a} ${u}`), await new Promise((_) => setTimeout(_, r.delayMs));
501
+ }
502
+ let g = null, h = null;
503
+ if (e.corruptions)
504
+ for (const r of e.corruptions) {
505
+ o == null || o.debug("rule-evaluating", { url: u, method: a, strategy: r.strategy }, r);
506
+ const b = B(r, u, a, M);
507
+ if (!b.proceed) {
508
+ o == null || o.debug("rule-skip-match", { url: u, method: a, strategy: r.strategy }, r);
509
+ continue;
510
+ }
511
+ o == null || o.debug("rule-matched", { url: u, method: a, strategy: r.strategy }, r);
512
+ const N = q(r, s);
513
+ if (!P(r, N)) {
514
+ o == null || o.debug("rule-skip-counting", { url: u, method: a, strategy: r.strategy }, r);
515
+ continue;
516
+ }
517
+ if (!G(r, p, o, { url: u, method: a, strategy: r.strategy })) continue;
518
+ const v = $(r.probability, t);
519
+ if (((R = b.outcome) == null ? void 0 : R.kind) === "unparseable") {
520
+ v && U(o, "network:corruption", u, a, { strategy: r.strategy });
521
+ continue;
522
+ }
523
+ if (!v) {
524
+ o == null || o.debug("rule-skip-probability", { url: u, method: a, strategy: r.strategy }, r), Q(o, r, u, a, !1, b.outcome);
525
+ continue;
526
+ }
527
+ o == null || o.debug("rule-applied", { url: u, method: a, strategy: r.strategy }, r), g = r, h = b.outcome;
528
+ break;
529
+ }
530
+ let y;
531
+ try {
532
+ y = await n(l, f);
533
+ } catch (r) {
534
+ throw g && Q(o, g, u, a, !1, h), r;
535
+ }
536
+ if (!g)
537
+ return y;
538
+ try {
539
+ console.warn(`CHAOS: Corrupting response for ${a} ${u} with strategy: ${g.strategy}`);
540
+ const r = await y.text(), b = ge(r, g.strategy);
541
+ return Q(o, g, u, a, !0, h), new Response(b, {
542
+ status: y.status,
543
+ statusText: y.statusText,
544
+ headers: y.headers
545
+ });
546
+ } catch (r) {
547
+ throw Q(o, g, u, a, !1, h), r;
548
+ }
549
+ };
550
+ }
551
+ const X = /* @__PURE__ */ Symbol.for("chaos-maker.websocket.intercepted");
552
+ function _e(n, e) {
553
+ return n === "both" ? !0 : n === e;
554
+ }
555
+ function m(n) {
556
+ return typeof n == "string" ? "text" : "binary";
557
+ }
558
+ function ee(n, e) {
559
+ switch (e) {
560
+ case "truncate":
561
+ return n.slice(0, Math.max(0, Math.floor(n.length / 2)));
562
+ case "malformed-json":
563
+ return `${n}"}`;
564
+ case "empty":
565
+ return "";
566
+ case "wrong-type":
567
+ return "<html><body>Unexpected HTML</body></html>";
568
+ }
569
+ }
570
+ function ne(n, e) {
571
+ if (e === "malformed-json" || e === "wrong-type") return null;
572
+ if (e === "empty")
573
+ return typeof Blob < "u" && n instanceof Blob ? new Blob([]) : n instanceof ArrayBuffer ? new ArrayBuffer(0) : new Uint8Array(0);
574
+ if (typeof Blob < "u" && n instanceof Blob)
575
+ return n.slice(0, Math.max(0, Math.floor(n.size / 2)));
576
+ if (n instanceof ArrayBuffer)
577
+ return n.slice(0, Math.max(0, Math.floor(n.byteLength / 2)));
578
+ const t = n, o = Math.max(0, Math.floor(t.byteLength / 2));
579
+ return new Uint8Array(t.buffer.slice(t.byteOffset, t.byteOffset + o));
580
+ }
581
+ function I(n, e, t, o, s, p, l) {
582
+ if (!n) return null;
583
+ for (const f of n) {
584
+ if (l == null || l.debug("rule-evaluating", { url: e, direction: t }, f), !Z(e, f.urlPattern)) {
585
+ l == null || l.debug("rule-skip-match", { url: e, direction: t }, f);
586
+ continue;
587
+ }
588
+ if (!_e(f.direction, t)) {
589
+ l == null || l.debug("rule-skip-match", { url: e, direction: t }, f);
590
+ continue;
591
+ }
592
+ l == null || l.debug("rule-matched", { url: e, direction: t }, f);
593
+ const u = q(f, s);
594
+ if (!P(f, u)) {
595
+ l == null || l.debug("rule-skip-counting", { url: e, direction: t }, f);
596
+ continue;
597
+ }
598
+ if (G(f, p, l, { url: e, direction: t })) {
599
+ if (!$(f.probability, o)) {
600
+ l == null || l.debug("rule-skip-probability", { url: e, direction: t }, f);
601
+ continue;
602
+ }
603
+ return l == null || l.debug("rule-applied", { url: e, direction: t }, f), f;
604
+ }
605
+ }
606
+ return null;
607
+ }
608
+ function j(n, e, t, o, s) {
609
+ n.emit({
610
+ type: "websocket:drop",
611
+ timestamp: Date.now(),
612
+ applied: !0,
613
+ detail: { url: e, direction: t, payloadType: o, ...s ? { reason: s } : {} }
614
+ });
615
+ }
616
+ function oe(n, e, t, o, s) {
617
+ n.emit({
618
+ type: "websocket:delay",
619
+ timestamp: Date.now(),
620
+ applied: !0,
621
+ detail: { url: e, direction: t, payloadType: o, delayMs: s }
622
+ });
623
+ }
624
+ function D(n, e, t, o, s, p, l) {
625
+ n.emit({
626
+ type: "websocket:corrupt",
627
+ timestamp: Date.now(),
628
+ applied: p,
629
+ detail: { url: e, direction: t, payloadType: o, strategy: s, ...l ? { reason: l } : {} }
630
+ });
631
+ }
632
+ function Re(n, e, t, o) {
633
+ n.emit({
634
+ type: "websocket:close",
635
+ timestamp: Date.now(),
636
+ applied: !0,
637
+ detail: { url: e, closeCode: t, closeReason: o }
638
+ });
639
+ }
640
+ function Oe(n, e, t, o, s, p) {
641
+ const l = /* @__PURE__ */ new Map();
642
+ let f = !0;
643
+ const u = (d, c) => {
644
+ let i = l.get(d);
645
+ i || (i = /* @__PURE__ */ new Set(), l.set(d, i)), i.add(c);
646
+ }, a = (d, c) => {
647
+ var i;
648
+ (i = l.get(d)) == null || i.delete(c);
649
+ }, E = (d, c) => {
650
+ const i = l.get(d);
651
+ if (i) {
652
+ for (const g of i)
653
+ clearTimeout(g.handle), g.kind === "delay" && j(t, g.url, g.direction, g.payloadType, c);
654
+ l.delete(d);
655
+ }
656
+ }, x = (d, c, i) => {
657
+ const g = new MessageEvent("message", {
658
+ data: i,
659
+ origin: c.origin,
660
+ lastEventId: c.lastEventId,
661
+ source: c.source,
662
+ ports: Array.from(c.ports ?? [])
663
+ });
664
+ g[X] = !0, d.dispatchEvent(g);
665
+ }, M = (d, c, i, g) => {
666
+ if (!f) return { handled: !1, data: i };
667
+ const h = "outbound", y = m(i);
668
+ if (I(e.drops, c, h, o, s, p, t))
669
+ return j(t, c, h, y), { handled: !0, data: i };
670
+ let k = i;
671
+ const w = I(e.corruptions, c, h, o, s, p, t);
672
+ if (w)
673
+ if (y === "text")
674
+ k = ee(k, w.strategy), D(t, c, h, y, w.strategy, !0);
675
+ else {
676
+ const T = ne(k, w.strategy);
677
+ T === null ? D(t, c, h, y, w.strategy, !1, "incompatible-payload-type") : (k = T, D(t, c, h, y, w.strategy, !0));
678
+ }
679
+ const C = I(e.delays, c, h, o, s, p, t);
680
+ if (C) {
681
+ oe(t, c, h, y, C.delayMs);
682
+ const T = {
683
+ kind: "delay",
684
+ handle: setTimeout(() => {
685
+ a(d, T);
686
+ try {
687
+ g(k);
688
+ } catch {
689
+ }
690
+ }, C.delayMs),
691
+ url: c,
692
+ direction: h,
693
+ payloadType: y
694
+ };
695
+ return u(d, T), { handled: !0, data: k };
696
+ }
697
+ return { handled: !1, data: k };
698
+ }, L = (d, c) => {
699
+ d.addEventListener("message", (i) => {
700
+ const g = i;
701
+ if (g[X] || !f) return;
702
+ const h = "inbound", y = m(g.data);
703
+ if (I(e.drops, c, h, o, s, p, t)) {
704
+ g.stopImmediatePropagation(), j(t, c, h, y);
705
+ return;
706
+ }
707
+ let k = g.data, w = !1;
708
+ const C = I(e.corruptions, c, h, o, s, p, t);
709
+ if (C)
710
+ if (y === "text")
711
+ k = ee(k, C.strategy), w = !0, D(t, c, h, y, C.strategy, !0);
712
+ else {
713
+ const R = ne(k, C.strategy);
714
+ R === null ? D(t, c, h, y, C.strategy, !1, "incompatible-payload-type") : (k = R, w = !0, D(t, c, h, y, C.strategy, !0));
715
+ }
716
+ const T = I(e.delays, c, h, o, s, p, t);
717
+ if (T) {
718
+ g.stopImmediatePropagation(), oe(t, c, h, y, T.delayMs);
719
+ const R = {
720
+ kind: "delay",
721
+ handle: setTimeout(() => {
722
+ a(d, R), x(d, g, k);
723
+ }, T.delayMs),
724
+ url: c,
725
+ direction: h,
726
+ payloadType: y
727
+ };
728
+ u(d, R);
729
+ return;
730
+ }
731
+ w && (g.stopImmediatePropagation(), x(d, g, k));
732
+ });
733
+ }, A = (d, c) => {
734
+ if (e.closes)
735
+ for (const i of e.closes) {
736
+ if (t.debug("rule-evaluating", { url: c }, i), !Z(c, i.urlPattern)) {
737
+ t.debug("rule-skip-match", { url: c }, i);
738
+ continue;
739
+ }
740
+ t.debug("rule-matched", { url: c }, i);
741
+ const g = q(i, s);
742
+ if (!P(i, g)) {
743
+ t.debug("rule-skip-counting", { url: c }, i);
744
+ continue;
745
+ }
746
+ if (!G(i, p, t, { url: c })) continue;
747
+ if (!$(i.probability, o)) {
748
+ t.debug("rule-skip-probability", { url: c }, i);
749
+ continue;
750
+ }
751
+ t.debug("rule-applied", { url: c }, i);
752
+ const h = i.code ?? 1e3, y = i.reason ?? "Chaos Maker close", k = i.afterMs ?? 0, w = () => {
753
+ if (f) {
754
+ E(d, "close-interrupt"), Re(t, c, h, y);
755
+ try {
756
+ d.close(h, y);
757
+ } catch {
758
+ try {
759
+ d.close();
760
+ } catch {
761
+ }
762
+ }
763
+ }
764
+ };
765
+ if (k <= 0)
766
+ d.readyState === d.OPEN ? w() : d.addEventListener("open", w, { once: !0 });
767
+ else {
768
+ const C = () => {
769
+ const T = {
770
+ kind: "close",
771
+ handle: setTimeout(w, k)
772
+ };
773
+ u(d, T);
774
+ };
775
+ d.readyState === d.OPEN ? C() : d.addEventListener("open", C, { once: !0 });
776
+ }
777
+ return;
778
+ }
779
+ };
780
+ function S(d, c) {
781
+ const i = new n(d, c), g = typeof d == "string" ? d : d.toString(), h = i.send.bind(i);
782
+ return i.send = function(k) {
783
+ const w = M(i, g, k, h);
784
+ w.handled || h(w.data);
785
+ }, L(i, g), A(i, g), i;
786
+ }
787
+ Object.defineProperty(S, "prototype", {
788
+ value: n.prototype,
789
+ writable: !1
790
+ });
791
+ for (const d of ["CONNECTING", "OPEN", "CLOSING", "CLOSED"])
792
+ S[d] = n[d];
793
+ return {
794
+ Wrapped: S,
795
+ uninstall() {
796
+ f = !1;
797
+ for (const [, d] of l)
798
+ for (const c of d)
799
+ clearTimeout(c.handle), c.kind === "delay" && j(t, c.url, c.direction, c.payloadType, "stop-during-delay");
800
+ l.clear();
801
+ }
802
+ };
803
+ }
804
+ function $e(n) {
805
+ return n === void 0 ? { enabled: !1 } : typeof n == "boolean" ? { enabled: n } : { enabled: n.enabled };
806
+ }
807
+ const qe = [
808
+ { pick: (n) => {
809
+ var e;
810
+ return (e = n.network) == null ? void 0 : e.failures;
811
+ }, ruleType: "failure" },
812
+ { pick: (n) => {
813
+ var e;
814
+ return (e = n.network) == null ? void 0 : e.latencies;
815
+ }, ruleType: "latency" },
816
+ { pick: (n) => {
817
+ var e;
818
+ return (e = n.network) == null ? void 0 : e.aborts;
819
+ }, ruleType: "abort" },
820
+ { pick: (n) => {
821
+ var e;
822
+ return (e = n.network) == null ? void 0 : e.corruptions;
823
+ }, ruleType: "corruption" },
824
+ { pick: (n) => {
825
+ var e;
826
+ return (e = n.network) == null ? void 0 : e.cors;
827
+ }, ruleType: "cors" },
828
+ { pick: (n) => {
829
+ var e;
830
+ return (e = n.ui) == null ? void 0 : e.assaults;
831
+ }, ruleType: "ui-assault" },
832
+ { pick: (n) => {
833
+ var e;
834
+ return (e = n.websocket) == null ? void 0 : e.drops;
835
+ }, ruleType: "ws-drop" },
836
+ { pick: (n) => {
837
+ var e;
838
+ return (e = n.websocket) == null ? void 0 : e.delays;
839
+ }, ruleType: "ws-delay" },
840
+ { pick: (n) => {
841
+ var e;
842
+ return (e = n.websocket) == null ? void 0 : e.corruptions;
843
+ }, ruleType: "ws-corrupt" },
844
+ { pick: (n) => {
845
+ var e;
846
+ return (e = n.websocket) == null ? void 0 : e.closes;
847
+ }, ruleType: "ws-close" },
848
+ { pick: (n) => {
849
+ var e;
850
+ return (e = n.sse) == null ? void 0 : e.drops;
851
+ }, ruleType: "sse-drop" },
852
+ { pick: (n) => {
853
+ var e;
854
+ return (e = n.sse) == null ? void 0 : e.delays;
855
+ }, ruleType: "sse-delay" },
856
+ { pick: (n) => {
857
+ var e;
858
+ return (e = n.sse) == null ? void 0 : e.corruptions;
859
+ }, ruleType: "sse-corrupt" },
860
+ { pick: (n) => {
861
+ var e;
862
+ return (e = n.sse) == null ? void 0 : e.closes;
863
+ }, ruleType: "sse-close" }
864
+ ];
865
+ function Pe(n) {
866
+ const e = /* @__PURE__ */ new WeakMap();
867
+ for (const { pick: t, ruleType: o } of qe) {
868
+ const s = t(n);
869
+ s && s.forEach((p, l) => {
870
+ e.set(p, { ruleType: o, ruleId: `${o}#${l}` });
871
+ });
872
+ }
873
+ return e;
874
+ }
875
+ function Ge(n, e) {
876
+ const t = [];
877
+ return e.ruleId && t.push(`rule=${e.ruleId}`), e.phase && t.push(e.phase), e.method && t.push(e.method), e.url && t.push(e.url), e.statusCode !== void 0 && t.push(`-> ${e.statusCode}`), e.delayMs !== void 0 && t.push(`+${e.delayMs}ms`), e.direction && t.push(e.direction), e.eventType && t.push(`event=${e.eventType}`), e.selector && t.push(`selector=${e.selector}`), e.action && t.push(`action=${e.action}`), e.strategy && t.push(`strategy=${e.strategy}`), e.groupName && t.push(`group=${e.groupName}`), e.reason && t.push(`reason=${e.reason}`), t.length === 0 ? n : `${n}: ${t.join(" ")}`;
878
+ }
879
+ class Ie {
880
+ constructor(e, t = "page") {
881
+ this.opts = e, this.target = t;
882
+ }
883
+ isEnabled() {
884
+ return this.opts.enabled;
885
+ }
886
+ /**
887
+ * Build a `type: 'debug'` event with `detail.stage = stage`, mirror a
888
+ * `[Chaos] ...` (page) or `[Chaos SW] ...` (Service Worker) line to
889
+ * `console.debug`, and return the event for the emitter to fan out. The
890
+ * formatted string is never stored on the event payload.
891
+ *
892
+ * Returns `null` when the logger was constructed with `enabled: false`.
893
+ * Internal callers (the emitter fast-path) never reach this branch because
894
+ * `ChaosMaker` does not attach a logger when debug is off, but the guard
895
+ * keeps the public `Logger` API consistent with the `DebugOptions.enabled`
896
+ * contract for external consumers who instantiate it directly.
897
+ */
898
+ log(e, t) {
899
+ if (!this.opts.enabled) return null;
900
+ const o = { ...t, stage: e }, s = {
901
+ type: "debug",
902
+ timestamp: Date.now(),
903
+ applied: !1,
904
+ detail: o
905
+ };
906
+ if (typeof console < "u" && typeof console.debug == "function") {
907
+ const p = this.target === "sw" ? "[Chaos SW]" : "[Chaos]";
908
+ try {
909
+ console.debug(`${p} ${Ge(e, o)}`);
910
+ } catch {
911
+ }
912
+ }
913
+ return s;
914
+ }
915
+ }
916
+ const Y = /* @__PURE__ */ Symbol.for("chaos-maker.sw.installed");
917
+ function De() {
918
+ return typeof self < "u" ? self : typeof globalThis < "u" ? globalThis : null;
919
+ }
920
+ function le(n, e) {
921
+ const t = n.clients;
922
+ !t || typeof t.matchAll != "function" || t.matchAll({ includeUncontrolled: !0 }).then((o) => {
923
+ for (const s of o)
924
+ try {
925
+ s.postMessage(e);
926
+ } catch {
927
+ }
928
+ }).catch(() => {
929
+ });
930
+ }
931
+ function te(n, e) {
932
+ n.running && K(n);
933
+ const t = re(e.seed);
934
+ n.seed = t.seed, n.random = t.random, n.requestCounters = /* @__PURE__ */ new Map(), n.groups = new se();
935
+ for (const s of e.groups ?? [])
936
+ n.groups.ensure(s.name, { enabled: s.enabled ?? !0, explicit: !0 });
937
+ he(e, (s) => {
938
+ s.group && n.groups.ensure(s.group);
939
+ }), n.groups.ensure(z, { enabled: !0 });
940
+ const o = $e(e.debug);
941
+ if (o.enabled ? (n.emitter.setRuleIds(Pe(e)), n.emitter.setLogger(new Ie(o, "sw"))) : (n.emitter.setRuleIds(void 0), n.emitter.setLogger(void 0)), n.emitter.debug("lifecycle", { phase: "sw:config-applied" }), e.network) {
942
+ const s = n.target;
943
+ typeof s.fetch == "function" && (n.originalFetch = s.fetch, s.fetch = Ne(
944
+ n.originalFetch.bind(s),
945
+ e.network,
946
+ n.random,
947
+ n.emitter,
948
+ n.requestCounters,
949
+ n.groups
950
+ ));
951
+ }
952
+ return e.websocket && typeof n.target.WebSocket < "u" && (n.originalWebSocket = n.target.WebSocket, n.webSocketHandle = Oe(
953
+ n.originalWebSocket,
954
+ e.websocket,
955
+ n.emitter,
956
+ n.random,
957
+ n.requestCounters,
958
+ n.groups
959
+ ), n.target.WebSocket = n.webSocketHandle.Wrapped), n.running = !0, n.seed;
960
+ }
961
+ function K(n) {
962
+ !n.running && !n.originalFetch && !n.originalWebSocket || (n.originalFetch && (n.target.fetch = n.originalFetch, n.originalFetch = void 0), n.originalWebSocket && (n.target.WebSocket = n.originalWebSocket, n.originalWebSocket = void 0), n.webSocketHandle && (n.webSocketHandle.uninstall(), n.webSocketHandle = void 0), n.emitter.debug("lifecycle", { phase: "sw:config-stopped" }), n.emitter.setLogger(void 0), n.emitter.setRuleIds(void 0), n.running = !1);
963
+ }
964
+ function F(n, e, t) {
965
+ var s;
966
+ const o = (s = e.ports) == null ? void 0 : s[0];
967
+ if (o && typeof o.postMessage == "function")
968
+ try {
969
+ o.postMessage(t);
970
+ return;
971
+ } catch {
972
+ }
973
+ le(n, t);
974
+ }
975
+ function Fe(n = {}) {
976
+ const e = De(), t = {
977
+ isRunning: () => !1,
978
+ getSeed: () => null,
979
+ getLog: () => [],
980
+ clearLog: () => {
981
+ },
982
+ uninstall: () => {
983
+ }
984
+ };
985
+ if (!e || typeof e.fetch != "function" || typeof e.addEventListener != "function")
986
+ return t;
987
+ const o = e[Y];
988
+ if (o) return o;
989
+ const s = new de(n.maxLogEntries ?? 2e3), p = re(0), l = {
990
+ target: e,
991
+ emitter: s,
992
+ running: !1,
993
+ seed: null,
994
+ random: p.random,
995
+ requestCounters: /* @__PURE__ */ new Map(),
996
+ groups: new se()
997
+ };
998
+ s.on("*", (a) => {
999
+ le(e, { __chaosMakerSWEvent: !0, event: a });
1000
+ });
1001
+ const f = (a) => {
1002
+ const E = a, x = E.data;
1003
+ if (!x || typeof x != "object") return;
1004
+ const M = x;
1005
+ if (M.__chaosMakerConfig) {
1006
+ const L = te(l, M.__chaosMakerConfig);
1007
+ F(e, E, {
1008
+ __chaosMakerAck: !0,
1009
+ seed: L,
1010
+ running: l.running
1011
+ });
1012
+ return;
1013
+ }
1014
+ if (M.__chaosMakerStop) {
1015
+ K(l), F(e, E, {
1016
+ __chaosMakerAck: !0,
1017
+ running: !1
1018
+ });
1019
+ return;
1020
+ }
1021
+ if (M.__chaosMakerToggleGroup) {
1022
+ const { name: L, enabled: A } = M.__chaosMakerToggleGroup, S = L.trim();
1023
+ if (!S) {
1024
+ F(e, E, {
1025
+ __chaosMakerAck: !0,
1026
+ running: l.running
1027
+ });
1028
+ return;
1029
+ }
1030
+ l.groups.setEnabled(S, A), l.emitter.emit({
1031
+ type: A ? "rule-group:enabled" : "rule-group:disabled",
1032
+ timestamp: Date.now(),
1033
+ applied: !0,
1034
+ detail: { groupName: S }
1035
+ }), l.emitter.debug("lifecycle", { phase: "sw:group-toggled", groupName: S, enabled: A }), F(e, E, {
1036
+ __chaosMakerAck: !0,
1037
+ running: l.running
1038
+ });
1039
+ return;
1040
+ }
1041
+ if (M.__chaosMakerGetLog) {
1042
+ F(e, E, {
1043
+ __chaosMakerLog: !0,
1044
+ log: s.getLog()
1045
+ });
1046
+ return;
1047
+ }
1048
+ if (M.__chaosMakerClearLog) {
1049
+ s.clearLog(), F(e, E, {
1050
+ __chaosMakerAck: !0,
1051
+ running: l.running
1052
+ });
1053
+ return;
1054
+ }
1055
+ };
1056
+ if (e.addEventListener("message", f), (n.source ?? "message") === "self-global") {
1057
+ const a = e.__CHAOS_CONFIG__;
1058
+ a && typeof a == "object" && te(l, a);
1059
+ }
1060
+ const u = {
1061
+ isRunning: () => l.running,
1062
+ getSeed: () => l.seed,
1063
+ getLog: () => s.getLog(),
1064
+ clearLog: () => s.clearLog(),
1065
+ uninstall: () => {
1066
+ e.removeEventListener("message", f), K(l), delete e[Y];
1067
+ }
1068
+ };
1069
+ return e[Y] = u, u;
1070
+ }
1071
+ typeof self < "u" && typeof importScripts == "function" && Fe({ source: "message" });
1072
+ export {
1073
+ Fe as installChaosSW
1074
+ };