@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/README.md +151 -11
- package/dist/chaos-config.schema.json +1314 -0
- package/dist/chaos-config.schema.notes.md +32 -0
- package/dist/chaos-maker.cjs +128 -4
- package/dist/chaos-maker.js +3839 -2017
- package/dist/chaos-maker.umd.js +128 -4
- package/dist/sw.js +1 -0
- package/dist/sw.mjs +1074 -0
- package/dist/types/ChaosMaker.d.ts +60 -1
- package/dist/types/builder.d.ts +37 -1
- package/dist/types/config.d.ts +141 -18
- package/dist/types/debug.d.ts +58 -0
- package/dist/types/errors.d.ts +14 -2
- package/dist/types/events.d.ts +65 -1
- package/dist/types/graphql.d.ts +54 -0
- package/dist/types/groups.d.ts +71 -0
- package/dist/types/index.d.ts +34 -5
- package/dist/types/interceptors/domAssailant.d.ts +2 -1
- package/dist/types/interceptors/eventSource.d.ts +42 -0
- package/dist/types/interceptors/networkFetch.d.ts +2 -1
- package/dist/types/interceptors/networkXHR.d.ts +2 -1
- package/dist/types/interceptors/websocket.d.ts +4 -3
- package/dist/types/presets.d.ts +63 -2
- package/dist/types/sw-bridge-source.d.ts +20 -0
- package/dist/types/sw.d.ts +104 -0
- package/dist/types/transport.d.ts +21 -0
- package/dist/types/utils.d.ts +46 -1
- package/dist/types/validation-deprecation.d.ts +7 -0
- package/dist/types/validation-format.d.ts +10 -0
- package/dist/types/validation-strip.d.ts +11 -0
- package/dist/types/validation-types.d.ts +34 -0
- package/dist/types/validation.d.ts +52 -0
- package/package.json +13 -6
package/dist/types/index.d.ts
CHANGED
|
@@ -1,10 +1,39 @@
|
|
|
1
1
|
import { ChaosMaker } from './ChaosMaker';
|
|
2
|
-
import { ChaosConfig, CorruptionStrategy, NetworkFailureConfig, NetworkLatencyConfig, NetworkAbortConfig, NetworkCorruptionConfig, NetworkCorsConfig, NetworkConfig, UiAssaultConfig, UiConfig, WebSocketConfig, WebSocketDropConfig, WebSocketDelayConfig, WebSocketCorruptConfig, WebSocketCloseConfig, WebSocketDirection, WebSocketCorruptionStrategy } from './config';
|
|
2
|
+
import { ChaosConfig, CorruptionStrategy, GraphQLOperationMatcher, NetworkFailureConfig, NetworkLatencyConfig, NetworkAbortConfig, NetworkCorruptionConfig, NetworkCorsConfig, NetworkConfig, NetworkRuleMatchers, RuleGroupAssignment, UiAssaultConfig, UiConfig, WebSocketConfig, WebSocketDropConfig, WebSocketDelayConfig, WebSocketCorruptConfig, WebSocketCloseConfig, WebSocketDirection, WebSocketCorruptionStrategy, SSEConfig, SSEDropConfig, SSEDelayConfig, SSECorruptConfig, SSECloseConfig, SSECorruptionStrategy, SSEEventTypeMatcher } from './config';
|
|
3
3
|
import { ChaosConfigError } from './errors';
|
|
4
|
-
import { validateConfig } from './validation';
|
|
4
|
+
import { validateConfig, prepareChaosConfig, validateChaosConfig, VALIDATOR_BRAND_VERSION, type ValidateChaosConfigOptions, type PrepareChaosConfigOptions } from './validation';
|
|
5
5
|
import { ChaosEvent, ChaosEventType, ChaosEventListener, ChaosEventEmitter } from './events';
|
|
6
6
|
import { ChaosConfigBuilder } from './builder';
|
|
7
|
-
import { presets } from './presets';
|
|
7
|
+
import { presets, PresetRegistry, BUILT_IN_PRESETS, expandPresets } from './presets';
|
|
8
8
|
import { createPrng, generateSeed } from './prng';
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/** `validateChaosConfig` is the canonical structured validation entry. Layers
|
|
10
|
+
* schema-version gating, brand-cache short-circuit, deprecation walk, and
|
|
11
|
+
* custom validators on top of `prepareChaosConfig` (Zod pass 1 + preset
|
|
12
|
+
* expansion + Zod pass 2). The `ChaosMaker` constructor and every adapter
|
|
13
|
+
* call through it.
|
|
14
|
+
*
|
|
15
|
+
* `prepareChaosConfig` is the lower-level primitive without the brand /
|
|
16
|
+
* deprecation / customValidators layers; useful in advanced flows that
|
|
17
|
+
* manage their own re-validation cadence.
|
|
18
|
+
*
|
|
19
|
+
* `validateConfig` is the schema-only primitive — does NOT expand presets.
|
|
20
|
+
* Use only for unit-test structural assertions. */
|
|
21
|
+
export { ChaosMaker, ChaosConfigError, validateConfig, prepareChaosConfig, validateChaosConfig, VALIDATOR_BRAND_VERSION, ChaosEventEmitter, ChaosConfigBuilder, presets, PresetRegistry, BUILT_IN_PRESETS, expandPresets, createPrng, generateSeed };
|
|
22
|
+
/** Internal: prebuilt Zod schema variants. Exported so the JSON-schema build
|
|
23
|
+
* script can serialize the canonical strict variant. Application code should
|
|
24
|
+
* call `validateChaosConfig` instead — the schemas are not the public
|
|
25
|
+
* validation surface and may evolve without a version bump. */
|
|
26
|
+
export { chaosConfigSchemaStrict, chaosConfigSchemaPassthrough } from './validation';
|
|
27
|
+
export type { ValidateChaosConfigOptions, PrepareChaosConfigOptions };
|
|
28
|
+
export type { ValidationIssue, ValidationIssueCode, RuleType, CustomRuleValidator, CustomValidatorMap, DeprecationEntry } from './validation-types';
|
|
29
|
+
export type { Preset, PresetConfigSlice } from './presets';
|
|
30
|
+
export { SW_BRIDGE_SOURCE } from './sw-bridge-source';
|
|
31
|
+
export { extractGraphQLOperation, parseOperationFromQueryString, operationNameMatches } from './graphql';
|
|
32
|
+
export { serializeForTransport, deserializeForTransport } from './transport';
|
|
33
|
+
export { DEFAULT_GROUP_NAME, RuleGroupRegistry } from './groups';
|
|
34
|
+
export { Logger, normalizeDebugOption, formatDebugMessage, buildRuleIdMap } from './debug';
|
|
35
|
+
export type { RuleGroup, RuleGroupConfig } from './groups';
|
|
36
|
+
export type { GraphQLExtractResult, GraphQLRuleOutcome } from './graphql';
|
|
37
|
+
export type { DebugOptions, ChaosDebugStage, RuleIdEntry } from './debug';
|
|
38
|
+
export type { ChaosLifecyclePhase } from './events';
|
|
39
|
+
export type { ChaosConfig, CorruptionStrategy, GraphQLOperationMatcher, NetworkFailureConfig, NetworkLatencyConfig, NetworkAbortConfig, NetworkCorruptionConfig, NetworkCorsConfig, NetworkConfig, NetworkRuleMatchers, RuleGroupAssignment, UiAssaultConfig, UiConfig, WebSocketConfig, WebSocketDropConfig, WebSocketDelayConfig, WebSocketCorruptConfig, WebSocketCloseConfig, WebSocketDirection, WebSocketCorruptionStrategy, SSEConfig, SSEDropConfig, SSEDelayConfig, SSECorruptConfig, SSECloseConfig, SSECorruptionStrategy, SSEEventTypeMatcher, ChaosEvent, ChaosEventType, ChaosEventListener };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { UiConfig } from '../config';
|
|
2
2
|
import { ChaosEventEmitter } from '../events';
|
|
3
|
-
|
|
3
|
+
import type { RuleGroupRegistry } from '../groups';
|
|
4
|
+
export declare function attachDomAssailant(config: UiConfig, random: () => number, emitter?: ChaosEventEmitter, groups?: RuleGroupRegistry): MutationObserver;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EventSource (Server-Sent Events) chaos interceptor.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the WebSocket interceptor's wrapper-constructor strategy: replace
|
|
5
|
+
* `globalThis.EventSource` with a chaos wrapper that owns a hidden real
|
|
6
|
+
* `EventSource` instance, intercepts inbound `MessageEvent`s on the capture
|
|
7
|
+
* phase via `stopImmediatePropagation`, and re-dispatches mutated payloads.
|
|
8
|
+
*
|
|
9
|
+
* Design notes:
|
|
10
|
+
* - SSE is inbound-only (the spec defines no client → server channel beyond
|
|
11
|
+
* the initial GET), so direction/payloadType fields are absent vs. WS.
|
|
12
|
+
* - `event.data` is always a string per the spec — corruption strategies
|
|
13
|
+
* reuse the four text strategies from network/WS chaos.
|
|
14
|
+
* - Counting (onNth/everyNth/afterN) is per-rule, per-event, identical to WS.
|
|
15
|
+
* - Per-rule ordering on a matched event: drop → corrupt → delay. A dropped
|
|
16
|
+
* event short-circuits the rest.
|
|
17
|
+
* - Close chaos dispatches an `error` event then calls `.close()` on the
|
|
18
|
+
* underlying EventSource. Delivery of `error` mirrors what browsers do
|
|
19
|
+
* when the upstream connection drops; the app's reconnection logic (if any)
|
|
20
|
+
* re-runs the original `new EventSource(url)` path, so a fresh wrapper is
|
|
21
|
+
* created and chaos continues.
|
|
22
|
+
* - On `uninstall()`, every pending delay timer is cleared and an
|
|
23
|
+
* `sse:drop` is emitted for it with `reason: 'stop-during-delay'`. Pending
|
|
24
|
+
* close timers cancel silently (they had not fired anything yet).
|
|
25
|
+
*/
|
|
26
|
+
import { SSEConfig } from '../config';
|
|
27
|
+
import { ChaosEventEmitter } from '../events';
|
|
28
|
+
import type { RuleGroupRegistry } from '../groups';
|
|
29
|
+
export interface EventSourceLikeStatic {
|
|
30
|
+
readonly CONNECTING: 0;
|
|
31
|
+
readonly OPEN: 1;
|
|
32
|
+
readonly CLOSED: 2;
|
|
33
|
+
new (url: string | URL, init?: EventSourceInit): EventSource;
|
|
34
|
+
prototype: EventSource;
|
|
35
|
+
}
|
|
36
|
+
export interface EventSourcePatchHandle {
|
|
37
|
+
/** Wrapped EventSource constructor suitable for `globalThis.EventSource = …`. */
|
|
38
|
+
readonly Wrapped: typeof EventSource;
|
|
39
|
+
/** Cancel pending timers and disarm wrapped instances. Call on ChaosMaker.stop(). */
|
|
40
|
+
uninstall(): void;
|
|
41
|
+
}
|
|
42
|
+
export declare function patchEventSource(OriginalEventSource: EventSourceLikeStatic, config: SSEConfig, emitter: ChaosEventEmitter, random: () => number, counters: Map<object, number>, groups?: RuleGroupRegistry): EventSourcePatchHandle;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { NetworkConfig } from '../config';
|
|
2
|
+
import type { RuleGroupRegistry } from '../groups';
|
|
2
3
|
import { ChaosEventEmitter } from '../events';
|
|
3
|
-
export declare function patchFetch(originalFetch: typeof
|
|
4
|
+
export declare function patchFetch(originalFetch: typeof globalThis.fetch, config: NetworkConfig, random: () => number, emitter?: ChaosEventEmitter, counters?: Map<object, number>, groups?: RuleGroupRegistry): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { NetworkConfig } from '../config';
|
|
2
|
+
import type { RuleGroupRegistry } from '../groups';
|
|
2
3
|
import { ChaosEventEmitter } from '../events';
|
|
3
|
-
export declare function patchXHR(originalXhrSend: (body?: Document | XMLHttpRequestBodyInit) => void, config: NetworkConfig, random: () => number, emitter?: ChaosEventEmitter, counters?: Map<object, number
|
|
4
|
+
export declare function patchXHR(originalXhrSend: (body?: Document | XMLHttpRequestBodyInit) => void, config: NetworkConfig, random: () => number, emitter?: ChaosEventEmitter, counters?: Map<object, number>, groups?: RuleGroupRegistry): (this: XMLHttpRequest, body?: Document | XMLHttpRequestBodyInit) => void;
|
|
4
5
|
export declare function patchXHROpen(originalXhrOpen: (method: string, url: string | URL) => void): (this: XMLHttpRequest, method: string, url: string | URL) => void;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* WebSocket chaos interceptor.
|
|
3
3
|
*
|
|
4
4
|
* Design decisions (see V2_PHASE3_WEBSOCKET_PLAN.md §4, §9):
|
|
5
|
-
* - Patch `
|
|
5
|
+
* - Patch `globalThis.WebSocket` with a wrapper constructor. Real socket is returned
|
|
6
6
|
* so `instanceof WebSocket` continues to work. `.send` is overridden on the
|
|
7
7
|
* instance; inbound messages are intercepted via a listener installed *before*
|
|
8
8
|
* user code runs.
|
|
@@ -21,10 +21,11 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { WebSocketConfig } from '../config';
|
|
23
23
|
import { ChaosEventEmitter } from '../events';
|
|
24
|
+
import type { RuleGroupRegistry } from '../groups';
|
|
24
25
|
export interface WebSocketPatchHandle {
|
|
25
|
-
/** Wrapped WebSocket constructor suitable for `
|
|
26
|
+
/** Wrapped WebSocket constructor suitable for `globalThis.WebSocket = …`. */
|
|
26
27
|
readonly Wrapped: typeof WebSocket;
|
|
27
28
|
/** Clear all pending delay timers and emit drop events for them. Call on ChaosMaker.stop(). */
|
|
28
29
|
uninstall(): void;
|
|
29
30
|
}
|
|
30
|
-
export declare function patchWebSocket(OriginalWebSocket: typeof WebSocket, config: WebSocketConfig, emitter: ChaosEventEmitter, random: () => number, counters: Map<object, number
|
|
31
|
+
export declare function patchWebSocket(OriginalWebSocket: typeof WebSocket, config: WebSocketConfig, emitter: ChaosEventEmitter, random: () => number, counters: Map<object, number>, groups?: RuleGroupRegistry): WebSocketPatchHandle;
|
package/dist/types/presets.d.ts
CHANGED
|
@@ -1,2 +1,63 @@
|
|
|
1
|
-
import { ChaosConfig } from './config';
|
|
2
|
-
|
|
1
|
+
import type { ChaosConfig } from './config';
|
|
2
|
+
/** ChaosConfig slice a preset is allowed to carry. Auto-includes any new
|
|
3
|
+
* rule category added to ChaosConfig — the `Omit` is bounded to fields that
|
|
4
|
+
* are explicitly forbidden inside a preset (`presets`, `customPresets`,
|
|
5
|
+
* `seed`, `debug`). */
|
|
6
|
+
export type PresetConfigSlice = Omit<ChaosConfig, 'presets' | 'customPresets' | 'seed' | 'debug' | 'schemaVersion'>;
|
|
7
|
+
/** A named preset packaged for registry registration. */
|
|
8
|
+
export interface Preset {
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly config: PresetConfigSlice;
|
|
11
|
+
}
|
|
12
|
+
/** All built-in presets including kebab aliases.
|
|
13
|
+
* Aliases are EXTRA registry entries pointing at the SAME config object
|
|
14
|
+
* identity as the camelCase entry — so
|
|
15
|
+
* `registry.get('slow-api') === presets.slowNetwork`.
|
|
16
|
+
*
|
|
17
|
+
* Both the array AND each `{ name, config }` descriptor are frozen so that
|
|
18
|
+
* `BUILT_IN_PRESETS[0].name = 'x'` or `BUILT_IN_PRESETS[0].config = {}` cannot
|
|
19
|
+
* poison future `PresetRegistry` constructions. Configs are already deep-
|
|
20
|
+
* frozen above, so the descriptor freeze is the missing layer. */
|
|
21
|
+
export declare const BUILT_IN_PRESETS: ReadonlyArray<Preset>;
|
|
22
|
+
/** Per-instance registry of presets. Constructor seeds the built-ins
|
|
23
|
+
* by default; pass an empty iterable to start from scratch. The slice shape
|
|
24
|
+
* is type-enforced for built-ins and Zod-validated for `customPresets`, so
|
|
25
|
+
* `register` does not re-check structure. */
|
|
26
|
+
export declare class PresetRegistry {
|
|
27
|
+
private map;
|
|
28
|
+
constructor(initial?: Iterable<Preset>);
|
|
29
|
+
register(preset: Preset): void;
|
|
30
|
+
registerAll(entries: Record<string, PresetConfigSlice> | undefined): void;
|
|
31
|
+
has(name: string): boolean;
|
|
32
|
+
get(name: string): PresetConfigSlice;
|
|
33
|
+
list(): string[];
|
|
34
|
+
}
|
|
35
|
+
/** Expand `config.presets` against `registry`. Identity contract:
|
|
36
|
+
*
|
|
37
|
+
* - ALWAYS returns a fresh `ChaosConfig`. Callers own the returned object
|
|
38
|
+
* and may mutate it without affecting the input. Built-in slices stay
|
|
39
|
+
* deep-frozen because each preset is deep-cloned at append time.
|
|
40
|
+
* - The output ALWAYS has `presets` and `customPresets` stripped, even if
|
|
41
|
+
* `presets[]` was empty. Prevents stale `customPresets` from leaking into
|
|
42
|
+
* the post-expansion config.
|
|
43
|
+
* - Append order: preset rules first (in the order they appear in
|
|
44
|
+
* `presets[]`), user rules last. Same rule for `groups`.
|
|
45
|
+
* - Throws when a name in `presets[]` is not registered. Plain `Error` —
|
|
46
|
+
* `prepareChaosConfig` wraps to `ChaosConfigError`.
|
|
47
|
+
*
|
|
48
|
+
* Defensive deduplication on `presets[]` runs here as well as in the Zod
|
|
49
|
+
* transform, because `expandPresets` is exported and a contributor could
|
|
50
|
+
* call it directly on an un-validated config. */
|
|
51
|
+
export declare function expandPresets(config: ChaosConfig, registry: PresetRegistry): ChaosConfig;
|
|
52
|
+
/** Backward-compat: the v0.4.0 frozen-record export. **CamelCase keys ONLY.**
|
|
53
|
+
* kebab aliases (`slow-api`, `flaky-api`, `offline-mode`,
|
|
54
|
+
* `high-latency`) live exclusively on `PresetRegistry` — they are NOT keys
|
|
55
|
+
* on this record. By design:
|
|
56
|
+
*
|
|
57
|
+
* presets['slow-api'] === undefined
|
|
58
|
+
* presets.slowNetwork === new PresetRegistry().get('slow-api') // same identity
|
|
59
|
+
* presets.slowNetwork === new PresetRegistry().get('slowNetwork')
|
|
60
|
+
*
|
|
61
|
+
* Use the camelCase key when reading from this record; use the registry (or
|
|
62
|
+
* the declarative `presets: ['slow-api']` config field) for kebab lookups. */
|
|
63
|
+
export declare const presets: Readonly<Record<string, PresetConfigSlice>>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side bridge installed into the page by each adapter (Playwright /
|
|
3
|
+
* Cypress / WDIO / Puppeteer) to talk to the Service-Worker chaos engine.
|
|
4
|
+
*
|
|
5
|
+
* This is a string because it is injected via `addInitScript`, `eval` or a
|
|
6
|
+
* `<script>` tag — *not* imported as a module. Adapters are Node-side, but
|
|
7
|
+
* this source executes in the AUT window.
|
|
8
|
+
*
|
|
9
|
+
* Exposes `window.__chaosMakerSWBridge`:
|
|
10
|
+
* - `apply(cfg, timeoutMs)` — post config over MessageChannel, wait for ack.
|
|
11
|
+
* - `stop(timeoutMs)` — stop chaos in the SW.
|
|
12
|
+
* - `toggleGroup(name, enabled, timeoutMs)` — flip a rule group inside the SW
|
|
13
|
+
* via `__chaosMakerToggleGroup`; resolves on ack with no engine restart.
|
|
14
|
+
* - `getLocalLog()` / `clearLocalLog()` — page-side buffered event log.
|
|
15
|
+
* - `getRemoteLog(timeoutMs)` — fetch SW's in-memory log.
|
|
16
|
+
*
|
|
17
|
+
* The bridge auto-wires a `controllerchange` listener so SW updates inherit
|
|
18
|
+
* the most recent config. Install is idempotent.
|
|
19
|
+
*/
|
|
20
|
+
export declare const SW_BRIDGE_SOURCE = "\n(function installChaosMakerSWBridge() {\n if (typeof window === 'undefined') return;\n if (window.__chaosMakerSWBridgeInstalled) return;\n window.__chaosMakerSWBridgeInstalled = true;\n\n var log = [];\n var lastConfig = null;\n\n function addSwListeners() {\n if (!('serviceWorker' in navigator)) return;\n navigator.serviceWorker.addEventListener('message', function (evt) {\n var data = evt && evt.data;\n if (data && data.__chaosMakerSWEvent && data.event) {\n log.push(data.event);\n }\n });\n navigator.serviceWorker.addEventListener('controllerchange', function () {\n if (lastConfig) {\n postConfig(lastConfig, 5000).catch(function (err) {\n try { console.warn('[chaos-maker] re-apply after controllerchange failed', err); } catch (_) {}\n });\n }\n });\n }\n\n function waitForController(timeoutMs) {\n return new Promise(function (resolve, reject) {\n if (!('serviceWorker' in navigator)) {\n reject(new Error('[chaos-maker] service worker API not available on this page'));\n return;\n }\n var start = Date.now();\n (function poll() {\n if (navigator.serviceWorker.controller) {\n resolve(navigator.serviceWorker.controller);\n return;\n }\n if (Date.now() - start >= timeoutMs) {\n reject(new Error('[chaos-maker] no SW controller after ' + timeoutMs + 'ms \u2014 did you register the SW?'));\n return;\n }\n setTimeout(poll, 50);\n })();\n });\n }\n\n function postViaPort(controller, message, timeoutMs) {\n return new Promise(function (resolve, reject) {\n var channel = new MessageChannel();\n var settled = false;\n var timer = setTimeout(function () {\n if (settled) return;\n settled = true;\n try { channel.port1.close(); } catch (_) {}\n reject(new Error('[chaos-maker] SW ack timeout after ' + timeoutMs + 'ms'));\n }, timeoutMs);\n channel.port1.onmessage = function (evt) {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(evt && evt.data);\n };\n controller.postMessage(message, [channel.port2]);\n });\n }\n\n function postConfig(cfg, timeoutMs) {\n return waitForController(timeoutMs).then(function (ctrl) {\n return postViaPort(ctrl, { __chaosMakerConfig: cfg }, timeoutMs);\n });\n }\n\n window.__chaosMakerSWBridge = {\n apply: function (cfg, timeoutMs) {\n // Defensive: page-side adapters validate before calling apply. The\n // page-side bundle deliberately excludes Zod, so a null/primitive/\n // array/wildly-wrong payload throws here instead of silently posting\n // garbage to the SW. `typeof [] === 'object'` so reject arrays too.\n if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) {\n return Promise.reject(new Error('[chaos-maker] bridge.apply: config must be an object'));\n }\n // Stash cfg BEFORE awaiting ack \u2014 intentional. If the first ack times\n // out (e.g. SW still installing), the controllerchange listener above\n // will retry with this config once the new SW claims the page. Caller\n // still sees the rejection from this attempt and can re-throw.\n lastConfig = cfg;\n return postConfig(cfg, timeoutMs).then(function (ack) {\n return { seed: ack && typeof ack.seed === 'number' ? ack.seed : null };\n });\n },\n stop: function (timeoutMs) {\n lastConfig = null;\n if (!('serviceWorker' in navigator) || !navigator.serviceWorker.controller) {\n return Promise.resolve({ running: false });\n }\n return postViaPort(navigator.serviceWorker.controller, { __chaosMakerStop: true }, timeoutMs);\n },\n toggleGroup: function (name, enabled, timeoutMs) {\n if (!('serviceWorker' in navigator) || !navigator.serviceWorker.controller) {\n return Promise.reject(new Error('[chaos-maker] no SW controller \u2014 call injectSWChaos before toggleGroup'));\n }\n var t = (typeof timeoutMs === 'number' && timeoutMs > 0) ? timeoutMs : 2000;\n return postViaPort(\n navigator.serviceWorker.controller,\n { __chaosMakerToggleGroup: { name: String(name), enabled: !!enabled } },\n t,\n );\n },\n getLocalLog: function () { return log.slice(); },\n clearLocalLog: function () { log.length = 0; },\n getRemoteLog: function (timeoutMs) {\n if (!('serviceWorker' in navigator) || !navigator.serviceWorker.controller) {\n return Promise.resolve([]);\n }\n return postViaPort(navigator.serviceWorker.controller, { __chaosMakerGetLog: true }, timeoutMs)\n .then(function (reply) {\n return (reply && Array.isArray(reply.log)) ? reply.log : [];\n });\n },\n };\n\n addSwListeners();\n})();\n";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Worker chaos entry point.
|
|
3
|
+
*
|
|
4
|
+
* Loaded into the user's service-worker script (classic `importScripts(…)` or
|
|
5
|
+
* module-worker `import { installChaosSW }`), this module patches
|
|
6
|
+
* `self.fetch` + `self.WebSocket` using the same interceptor engine that
|
|
7
|
+
* powers page-context chaos. Config is delivered from the page via
|
|
8
|
+
* `postMessage` and acked through a `MessageChannel` transferred by the
|
|
9
|
+
* adapter helper. Every chaos event is broadcast to controlled clients so
|
|
10
|
+
* tests can read a unified log on the page side.
|
|
11
|
+
*
|
|
12
|
+
* Deliberately excluded from this bundle: Zod validation, UI DOM assailant,
|
|
13
|
+
* the public `ChaosMaker` class, and the builder — all page-side concerns.
|
|
14
|
+
* The page-side adapter helpers validate config before postMessage, so the
|
|
15
|
+
* SW bundle stays small enough to ship to production service workers.
|
|
16
|
+
*/
|
|
17
|
+
import type { ChaosConfig } from './config';
|
|
18
|
+
import type { ChaosEvent } from './events';
|
|
19
|
+
/** Message sent by page → SW to configure chaos. */
|
|
20
|
+
export interface ChaosSWConfigMessage {
|
|
21
|
+
__chaosMakerConfig: ChaosConfig;
|
|
22
|
+
}
|
|
23
|
+
/** Message sent by page → SW to stop chaos and restore fetch/WebSocket. */
|
|
24
|
+
export interface ChaosSWStopMessage {
|
|
25
|
+
__chaosMakerStop: true;
|
|
26
|
+
}
|
|
27
|
+
/** Message sent by page → SW to read the accumulated event log. */
|
|
28
|
+
export interface ChaosSWGetLogMessage {
|
|
29
|
+
__chaosMakerGetLog: true;
|
|
30
|
+
}
|
|
31
|
+
/** Message sent by page → SW to clear the accumulated event log. */
|
|
32
|
+
export interface ChaosSWClearLogMessage {
|
|
33
|
+
__chaosMakerClearLog: true;
|
|
34
|
+
}
|
|
35
|
+
/** Message sent by page -> SW to toggle a rule group without restarting chaos. */
|
|
36
|
+
export interface ChaosSWToggleGroupMessage {
|
|
37
|
+
__chaosMakerToggleGroup: {
|
|
38
|
+
name: string;
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Ack sent SW → port (or broadcast) after a config / stop message lands. */
|
|
43
|
+
export interface ChaosSWAck {
|
|
44
|
+
__chaosMakerAck: true;
|
|
45
|
+
seed?: number;
|
|
46
|
+
running: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Log payload sent SW → port (or broadcast) in response to getLog. */
|
|
49
|
+
export interface ChaosSWLogReply {
|
|
50
|
+
__chaosMakerLog: true;
|
|
51
|
+
log: ChaosEvent[];
|
|
52
|
+
}
|
|
53
|
+
/** Event broadcast SW → all controlled clients for every chaos decision. */
|
|
54
|
+
export interface ChaosSWEventMessage {
|
|
55
|
+
__chaosMakerSWEvent: true;
|
|
56
|
+
event: ChaosEvent;
|
|
57
|
+
}
|
|
58
|
+
export interface InstallChaosSWOptions {
|
|
59
|
+
/**
|
|
60
|
+
* How the SW receives its chaos config.
|
|
61
|
+
*
|
|
62
|
+
* - `'message'` (default) — wait for a `postMessage({ __chaosMakerConfig: … })`
|
|
63
|
+
* from the page. Typically paired with a `MessageChannel` for ack.
|
|
64
|
+
* - `'self-global'` — read `self.__CHAOS_CONFIG__` synchronously. Useful when
|
|
65
|
+
* the SW script is served with the config baked in (e.g. query-string).
|
|
66
|
+
*/
|
|
67
|
+
source?: 'message' | 'self-global';
|
|
68
|
+
/** Max entries buffered in the SW-side log. Defaults to 2000. */
|
|
69
|
+
maxLogEntries?: number;
|
|
70
|
+
}
|
|
71
|
+
export interface SWChaosHandle {
|
|
72
|
+
/** True while a config is installed and `self.fetch` is patched. */
|
|
73
|
+
isRunning(): boolean;
|
|
74
|
+
/** Seed used by the active PRNG, or null if no config is installed. */
|
|
75
|
+
getSeed(): number | null;
|
|
76
|
+
/** Snapshot of chaos events emitted inside the SW since install. */
|
|
77
|
+
getLog(): ChaosEvent[];
|
|
78
|
+
/** Clear the in-SW log buffer. Does not clear already-delivered page-side events. */
|
|
79
|
+
clearLog(): void;
|
|
80
|
+
/** Stop chaos + remove the message listener. Restores `self.fetch`. */
|
|
81
|
+
uninstall(): void;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Install Service-Worker chaos. Listens for config via `postMessage`,
|
|
85
|
+
* patches `self.fetch` (and `self.WebSocket` when configured), and
|
|
86
|
+
* broadcasts every chaos event to all controlled clients so the test
|
|
87
|
+
* runner's page-side helper can build a unified event log.
|
|
88
|
+
*
|
|
89
|
+
* Idempotent: calling more than once returns the existing handle.
|
|
90
|
+
*
|
|
91
|
+
* @example Classic SW (typical user integration — one line)
|
|
92
|
+
* ```js
|
|
93
|
+
* // user's sw.js
|
|
94
|
+
* importScripts('/vendor/chaos-maker-sw.js');
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* @example Module SW (`type: 'module'`)
|
|
98
|
+
* ```js
|
|
99
|
+
* import { installChaosSW } from '@chaos-maker/core/sw';
|
|
100
|
+
* installChaosSW();
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export declare function installChaosSW(opts?: InstallChaosSWOptions): SWChaosHandle;
|
|
104
|
+
export type { ChaosConfig, ChaosEvent };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport-safe (de)serialization for `ChaosConfig` values that may carry
|
|
3
|
+
* `RegExp` instances.
|
|
4
|
+
*
|
|
5
|
+
* Why: every adapter passes config across the Node ↔ browser boundary via a
|
|
6
|
+
* channel that JSON-encodes its argument (Playwright `addInitScript`,
|
|
7
|
+
* Puppeteer `evaluateOnNewDocument`, WDIO `JSON.stringify(...)` into a script
|
|
8
|
+
* tag). `JSON.stringify` collapses `RegExp` to `{}`, which silently breaks
|
|
9
|
+
* matchers like `graphqlOperation: /^Get/`.
|
|
10
|
+
*
|
|
11
|
+
* `serializeForTransport` walks the config and replaces every `RegExp` with a
|
|
12
|
+
* plain marker object that survives JSON. `deserializeForTransport` reverses
|
|
13
|
+
* the substitution. Both are pure and structurally recursive — they don't
|
|
14
|
+
* touch other values.
|
|
15
|
+
*/
|
|
16
|
+
/** Replace every `RegExp` instance in `value` with a JSON-safe marker object.
|
|
17
|
+
* Pass through everything else (primitives, arrays, plain objects). */
|
|
18
|
+
export declare function serializeForTransport<T>(value: T): T;
|
|
19
|
+
/** Reconstruct any `RegExp` markers in `value` produced by `serializeForTransport`.
|
|
20
|
+
* Idempotent: passing a fully-deserialized value through a second time is a no-op. */
|
|
21
|
+
export declare function deserializeForTransport<T>(value: T): T;
|
package/dist/types/utils.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type { CorruptionStrategy, RequestCountingOptions } from './config';
|
|
1
|
+
import type { ChaosConfig, CorruptionStrategy, RequestCountingOptions } from './config';
|
|
2
|
+
import { RuleGroupRegistry } from './groups';
|
|
3
|
+
import type { ChaosEvent, ChaosEventEmitter } from './events';
|
|
4
|
+
/** Recursive deep clone that preserves `RegExp` instances literally.
|
|
5
|
+
* `JSON.parse(JSON.stringify(...))` would silently drop `RegExp`, breaking
|
|
6
|
+
* matchers like `graphqlOperation: /^Get/`. Shared by builder + presets so a
|
|
7
|
+
* single implementation owns RegExp survival. */
|
|
8
|
+
export declare function cloneValue<T>(value: T): T;
|
|
2
9
|
export declare function shouldApplyChaos(probability: number, random: () => number): boolean;
|
|
3
10
|
/**
|
|
4
11
|
* Increment the per-rule request counter and return the new count.
|
|
@@ -14,3 +21,41 @@ export declare function incrementCounter(rule: object, counters: Map<object, num
|
|
|
14
21
|
export declare function checkCountingCondition(rule: RequestCountingOptions, count: number): boolean;
|
|
15
22
|
export declare function matchUrl(url: string, pattern: string): boolean;
|
|
16
23
|
export declare function corruptText(text: string, strategy: CorruptionStrategy): string;
|
|
24
|
+
/**
|
|
25
|
+
* Group-active gate. Sits between `gateRule` and `shouldApplyChaos`
|
|
26
|
+
* inside every interceptor.
|
|
27
|
+
*
|
|
28
|
+
* - `registry === undefined` (legacy / direct interceptor caller): returns
|
|
29
|
+
* `true` so the interceptor proceeds without group checks.
|
|
30
|
+
* - When the rule's group is enabled: returns `true`.
|
|
31
|
+
* - When disabled: emits at most one `rule-group:gated` event per group per
|
|
32
|
+
* toggle cycle (deduped via `RuleGroupRegistry.shouldEmitGated`) and
|
|
33
|
+
* returns `false`. The emitted detail merges `baseDetail` (e.g. url +
|
|
34
|
+
* method) with `groupName`.
|
|
35
|
+
*
|
|
36
|
+
* Counting (`onNth` / `everyNth` / `afterN`) runs *before* this gate inside
|
|
37
|
+
* `gateRule`, so toggling a group does not desync per-rule counters.
|
|
38
|
+
*/
|
|
39
|
+
export declare function gateGroup(rule: {
|
|
40
|
+
group?: string;
|
|
41
|
+
}, registry: RuleGroupRegistry | undefined, emitter: ChaosEventEmitter | undefined, baseDetail: ChaosEvent['detail']): boolean;
|
|
42
|
+
/** Minimal rule shape walked by `forEachRule`. Carries the optional `group`
|
|
43
|
+
* field every rule type now supports plus an open index for the
|
|
44
|
+
* remaining per-rule fields the walker doesn't care about. */
|
|
45
|
+
export type AnyRule = {
|
|
46
|
+
group?: string;
|
|
47
|
+
[k: string]: unknown;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Iterate every rule across every chaos category in the config exactly once.
|
|
51
|
+
*
|
|
52
|
+
* IMPORTANT: any new rule array added to `ChaosConfig` (e.g. `websocket.timeouts`,
|
|
53
|
+
* `sse.reconnects`) MUST be registered here. The matching test in
|
|
54
|
+
* `forEachRule.test.ts` asserts the visited count against a sample config; that
|
|
55
|
+
* test will fail loudly if a new array is added without updating this walker.
|
|
56
|
+
*
|
|
57
|
+
* Single source of truth for rule iteration. Used by `seedGroupsFromRules`
|
|
58
|
+
* and `collectReferencedGroups` in `ChaosMaker`, plus any future feature
|
|
59
|
+
* that needs to walk all rules.
|
|
60
|
+
*/
|
|
61
|
+
export declare function forEachRule(config: ChaosConfig, fn: (rule: AnyRule) => void): void;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ChaosConfig } from './config';
|
|
2
|
+
import type { DeprecationEntry, ValidationIssue } from './validation-types';
|
|
3
|
+
/** Empty for v0.5.0 — rails only. First real deprecation lands in v0.5.x by
|
|
4
|
+
* adding an entry here keyed by a dot-notation path the walker can detect.
|
|
5
|
+
* Top-level paths only for now (e.g. `'someTopLevelField'`). */
|
|
6
|
+
export declare const DEPRECATED_FIELDS: Map<string, DeprecationEntry>;
|
|
7
|
+
export declare function checkDeprecations(config: ChaosConfig, onDeprecation?: (issue: ValidationIssue) => void): void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ZodIssue } from 'zod';
|
|
2
|
+
import type { ValidationIssue } from './validation-types';
|
|
3
|
+
export declare function formatZodIssue(issue: ZodIssue): ValidationIssue;
|
|
4
|
+
export interface RenderOptions {
|
|
5
|
+
maxIssues?: number;
|
|
6
|
+
}
|
|
7
|
+
/** Deterministic sort: lex on path, then lex on code. Pure (returns a new
|
|
8
|
+
* array). Modern `Array#sort` is stable so equal pairs preserve input order. */
|
|
9
|
+
export declare function sortIssues(issues: ValidationIssue[]): ValidationIssue[];
|
|
10
|
+
export declare function renderValidationIssues(issues: ValidationIssue[], opts?: RenderOptions): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Known-key projection for `unknownFields: 'warn' | 'ignore'`. The walker is
|
|
2
|
+
* a small recursive helper, NOT a third Zod schema. Returns a fresh object
|
|
3
|
+
* detached from the passthrough parse result so subsequent stages cannot leak
|
|
4
|
+
* unknown fields back through aliasing. */
|
|
5
|
+
import type { ChaosConfig } from './config';
|
|
6
|
+
/** Project `input` to a fresh object containing only the keys recognized by
|
|
7
|
+
* the strict schema. Never mutates the input. */
|
|
8
|
+
export declare function stripUnknownKeys(input: unknown): ChaosConfig;
|
|
9
|
+
/** Collect dot-notation paths of unknown keys. Deterministic sorted output.
|
|
10
|
+
* Does not mutate the input. */
|
|
11
|
+
export declare function collectUnknownPaths(input: unknown): string[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Public types for the structured validation surface.
|
|
2
|
+
* Pure types — no runtime. Imported by `errors.ts`, `validation.ts`, and the
|
|
3
|
+
* format / strip / deprecation helpers. */
|
|
4
|
+
export type RuleType = 'network.failure' | 'network.latency' | 'network.abort' | 'network.corruption' | 'network.cors' | 'ui.assault' | 'websocket.drop' | 'websocket.delay' | 'websocket.corrupt' | 'websocket.close' | 'sse.drop' | 'sse.delay' | 'sse.corrupt' | 'sse.close' | 'group' | 'preset' | 'top-level';
|
|
5
|
+
export type ValidationIssueCode = 'unknown_field' | 'missing_field' | 'invalid_type' | 'value_too_small' | 'value_too_large' | 'invalid_enum' | 'invalid_string' | 'invalid_regex' | 'mutually_exclusive' | 'duplicate' | 'unknown_preset' | 'preset_chain' | 'preset_collision' | 'unknown_schema_version' | 'deprecated' | 'custom' | 'legacy';
|
|
6
|
+
export interface ValidationIssue {
|
|
7
|
+
/** Dot-notation path: 'network.failures[0].statusCode'. Empty string for top-level. */
|
|
8
|
+
path: string;
|
|
9
|
+
code: ValidationIssueCode;
|
|
10
|
+
ruleType: RuleType;
|
|
11
|
+
message: string;
|
|
12
|
+
/** JSON-stringifiable. e.g. 'number 0..1', "'truncate'|'malformed-json'|...". */
|
|
13
|
+
expected?: string;
|
|
14
|
+
/** Received value via JSON.stringify (clipped to 80 chars). */
|
|
15
|
+
received?: string;
|
|
16
|
+
}
|
|
17
|
+
/** Custom validator hook. Rule passed by reference as `unknown`; callers
|
|
18
|
+
* narrow via type guards. Concrete `Readonly<NetworkFailureConfig>`-style
|
|
19
|
+
* typings per rule type are deferred post-v0.5.0.
|
|
20
|
+
*
|
|
21
|
+
* Mutation of the rule arg is undefined behavior. The engine deep-clones
|
|
22
|
+
* the canonical config at expansion time, so mutations made here may be
|
|
23
|
+
* observable on the validator's input but not by the running engine. */
|
|
24
|
+
export type CustomRuleValidator = (rule: unknown, ctx: Readonly<{
|
|
25
|
+
ruleType: RuleType;
|
|
26
|
+
path: string;
|
|
27
|
+
}>) => ValidationIssue[] | void;
|
|
28
|
+
export type CustomValidatorMap = Readonly<Partial<Record<RuleType, CustomRuleValidator>>>;
|
|
29
|
+
export interface DeprecationEntry {
|
|
30
|
+
since: string;
|
|
31
|
+
replacement?: string;
|
|
32
|
+
removeIn?: string;
|
|
33
|
+
message: string;
|
|
34
|
+
}
|
|
@@ -1,2 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import type { ChaosConfig } from './config';
|
|
3
|
+
import type { CustomValidatorMap, ValidationIssue } from './validation-types';
|
|
4
|
+
/** Prebuilt strict variant. Default for `unknownFields: 'reject'` and for the
|
|
5
|
+
* post-preset-expansion second pass. Built once at module load. Typed as
|
|
6
|
+
* `z.ZodTypeAny` to keep DTS output tractable; runtime keeps the full
|
|
7
|
+
* schema graph. */
|
|
8
|
+
export declare const chaosConfigSchemaStrict: z.ZodTypeAny;
|
|
9
|
+
/** Prebuilt passthrough variant. Used for `unknownFields: 'warn' | 'ignore'`
|
|
10
|
+
* before `stripUnknownKeys` projects the result to known keys only. */
|
|
11
|
+
export declare const chaosConfigSchemaPassthrough: z.ZodTypeAny;
|
|
12
|
+
/** Bumped whenever the validator's invariants change semantically. Stale
|
|
13
|
+
* brands fail the strict-equality check inside the short-circuit and re-
|
|
14
|
+
* validate. Do NOT bump for cosmetic / refactor-only changes. */
|
|
15
|
+
export declare const VALIDATOR_BRAND_VERSION = 1;
|
|
16
|
+
/** Schema-only validation. Does NOT expand presets and does NOT run the
|
|
17
|
+
* post-merge re-validation pass. Calling this in a runtime path silently
|
|
18
|
+
* bypasses preset expansion. For runtime preparation, call
|
|
19
|
+
* `prepareChaosConfig` (or `validateChaosConfig` for the full structured
|
|
20
|
+
* pipeline). */
|
|
2
21
|
export declare function validateConfig(config: unknown): ChaosConfig;
|
|
22
|
+
export interface PrepareChaosConfigOptions {
|
|
23
|
+
unknownFields?: 'reject' | 'warn' | 'ignore';
|
|
24
|
+
}
|
|
25
|
+
/** Canonical runtime preparation entry point for a `ChaosConfig`.
|
|
26
|
+
*
|
|
27
|
+
* Composes:
|
|
28
|
+
* 1. Zod pass 1 (strict OR passthrough+strip per `opts.unknownFields`).
|
|
29
|
+
* 2. Build per-instance `PresetRegistry`, register customs.
|
|
30
|
+
* 3. `expandPresets` — append rule arrays + groups, strip preset fields.
|
|
31
|
+
* 4. Zod pass 2 (strict, on the merged config).
|
|
32
|
+
*
|
|
33
|
+
* v0.4.x callers pass no opts and get strict-by-default behavior identical
|
|
34
|
+
* to before. */
|
|
35
|
+
export declare function prepareChaosConfig(input: unknown, opts?: PrepareChaosConfigOptions): ChaosConfig;
|
|
36
|
+
export interface ValidateChaosConfigOptions {
|
|
37
|
+
unknownFields?: 'reject' | 'warn' | 'ignore';
|
|
38
|
+
onDeprecation?: (issue: ValidationIssue) => void;
|
|
39
|
+
customValidators?: CustomValidatorMap;
|
|
40
|
+
}
|
|
41
|
+
/** Canonical validation entry point for adapters and the engine.
|
|
42
|
+
*
|
|
43
|
+
* Pipeline:
|
|
44
|
+
* 1. Schema-version gate (BEFORE Zod, unambiguous message).
|
|
45
|
+
* 2. Brand short-circuit (only when brand-version matches AND opts empty).
|
|
46
|
+
* 3-5. `prepareChaosConfig` — Zod pass 1 + preset expansion + Zod pass 2.
|
|
47
|
+
* 6. Deprecation walk.
|
|
48
|
+
* 7. Custom validators.
|
|
49
|
+
* 8. Issue sort (inside ChaosConfigError construction).
|
|
50
|
+
* 9. Brand stamp — final step only.
|
|
51
|
+
*
|
|
52
|
+
* Throws `ChaosConfigError` aggregating all issues from the first failing
|
|
53
|
+
* layer. Subsequent layers are skipped on failure. */
|
|
54
|
+
export declare function validateChaosConfig(input: unknown, opts?: ValidateChaosConfigOptions): ChaosConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaos-maker/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A lightweight, framework-agnostic toolkit for injecting chaos into web applications to test frontend resilience",
|
|
6
6
|
"keywords": [
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "https://github.com/
|
|
20
|
+
"url": "https://github.com/chaos-maker-dev/chaos-maker.git",
|
|
21
21
|
"directory": "packages/core"
|
|
22
22
|
},
|
|
23
|
-
"homepage": "https://github.com/
|
|
23
|
+
"homepage": "https://github.com/chaos-maker-dev/chaos-maker",
|
|
24
24
|
"bugs": {
|
|
25
|
-
"url": "https://github.com/
|
|
25
|
+
"url": "https://github.com/chaos-maker-dev/chaos-maker/issues"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
28
|
"node": ">=18"
|
|
@@ -40,6 +40,11 @@
|
|
|
40
40
|
"types": "./dist/types/index.d.ts",
|
|
41
41
|
"default": "./dist/chaos-maker.cjs"
|
|
42
42
|
}
|
|
43
|
+
},
|
|
44
|
+
"./sw": {
|
|
45
|
+
"types": "./dist/types/sw.d.ts",
|
|
46
|
+
"import": "./dist/sw.mjs",
|
|
47
|
+
"default": "./dist/sw.js"
|
|
43
48
|
}
|
|
44
49
|
},
|
|
45
50
|
"files": [
|
|
@@ -53,11 +58,13 @@
|
|
|
53
58
|
"jsdom": "^27.0.1",
|
|
54
59
|
"typescript": "^5.4.5",
|
|
55
60
|
"vite": "^6.4.2",
|
|
56
|
-
"vitest": "^3.2.4"
|
|
61
|
+
"vitest": "^3.2.4",
|
|
62
|
+
"zod-to-json-schema": "3.24.5"
|
|
57
63
|
},
|
|
58
64
|
"scripts": {
|
|
59
65
|
"dev": "vite build --watch",
|
|
60
|
-
"build": "vite build && tsc -p tsconfig.build.json",
|
|
66
|
+
"build": "vite build && vite build -c vite.config.sw.ts && tsc -p tsconfig.build.json && pnpm run build:schema",
|
|
67
|
+
"build:schema": "node scripts/build-schema.mjs",
|
|
61
68
|
"test": "vitest run",
|
|
62
69
|
"test:watch": "vitest"
|
|
63
70
|
}
|