@databehave/admin 0.0.0-stage → 0.1.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/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +39 -2
- package/assets/ui.css +1 -0
- package/assets/ui.html +13 -0
- package/assets/ui.js +233 -0
- package/dist/admin-plugin.d.ts +23 -0
- package/dist/admin-plugin.d.ts.map +1 -0
- package/dist/admin-plugin.js +71 -0
- package/dist/admin-plugin.js.map +1 -0
- package/dist/admin-routes.d.ts +22 -0
- package/dist/admin-routes.d.ts.map +1 -0
- package/dist/admin-routes.js +316 -0
- package/dist/admin-routes.js.map +1 -0
- package/dist/admin-types.d.ts +98 -0
- package/dist/admin-types.d.ts.map +1 -0
- package/dist/admin-types.js +2 -0
- package/dist/admin-types.js.map +1 -0
- package/dist/banner.d.ts +10 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/banner.js +5 -0
- package/dist/banner.js.map +1 -0
- package/dist/fault-plugin.d.ts +29 -0
- package/dist/fault-plugin.d.ts.map +1 -0
- package/dist/fault-plugin.js +32 -0
- package/dist/fault-plugin.js.map +1 -0
- package/dist/header-parser.d.ts +14 -0
- package/dist/header-parser.d.ts.map +1 -0
- package/dist/header-parser.js +171 -0
- package/dist/header-parser.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/inject.d.ts +35 -0
- package/dist/inject.d.ts.map +1 -0
- package/dist/inject.js +155 -0
- package/dist/inject.js.map +1 -0
- package/dist/open-browser-plugin.d.ts +6 -0
- package/dist/open-browser-plugin.d.ts.map +1 -0
- package/dist/open-browser-plugin.js +32 -0
- package/dist/open-browser-plugin.js.map +1 -0
- package/dist/overrides-store.d.ts +21 -0
- package/dist/overrides-store.d.ts.map +1 -0
- package/dist/overrides-store.js +114 -0
- package/dist/overrides-store.js.map +1 -0
- package/dist/resolve-cors.d.ts +6 -0
- package/dist/resolve-cors.d.ts.map +1 -0
- package/dist/resolve-cors.js +39 -0
- package/dist/resolve-cors.js.map +1 -0
- package/dist/scenarios-store.d.ts +8 -0
- package/dist/scenarios-store.d.ts.map +1 -0
- package/dist/scenarios-store.js +136 -0
- package/dist/scenarios-store.js.map +1 -0
- package/dist/scenarios.d.ts +9 -0
- package/dist/scenarios.d.ts.map +1 -0
- package/dist/scenarios.js +2 -0
- package/dist/scenarios.js.map +1 -0
- package/dist/validation.d.ts +17 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +169 -0
- package/dist/validation.js.map +1 -0
- package/package.json +52 -3
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse `x-mock-*` request headers into an `ErrorMode` (or surface a
|
|
3
|
+
* conflict error). One header set ↔ one ErrorMode. Multiple terminal
|
|
4
|
+
* modes (e.g. both `x-mock-status` and `x-mock-business-failure`) are
|
|
5
|
+
* rejected with `kind: 'conflict'` so the caller can return 400.
|
|
6
|
+
*
|
|
7
|
+
* Base64 inputs (`x-mock-body`, `x-mock-business-failure-extra`) use
|
|
8
|
+
* standard base64 (NOT url-safe), per the README contract.
|
|
9
|
+
*/
|
|
10
|
+
import { Buffer } from 'node:buffer';
|
|
11
|
+
// Note: `x-mock-business-failure-b64` is an alternative to
|
|
12
|
+
// `x-mock-business-failure` that accepts a base64-UTF8 message so
|
|
13
|
+
// non-ASCII strings can travel through an HTTP header — Node's fetch
|
|
14
|
+
// rejects non-ASCII byte values in header values.
|
|
15
|
+
const TERMINAL_HEADERS = [
|
|
16
|
+
'x-mock-status',
|
|
17
|
+
'x-mock-business-failure',
|
|
18
|
+
'x-mock-business-failure-b64',
|
|
19
|
+
'x-mock-body',
|
|
20
|
+
'x-mock-empty',
|
|
21
|
+
'x-mock-malformed',
|
|
22
|
+
'x-mock-hang',
|
|
23
|
+
'x-mock-destroy',
|
|
24
|
+
];
|
|
25
|
+
const get = (h, name) => h[name];
|
|
26
|
+
const decodeBase64Json = (raw, headerName) => {
|
|
27
|
+
let decoded;
|
|
28
|
+
try {
|
|
29
|
+
decoded = Buffer.from(raw, 'base64').toString('utf8');
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
throw new Error(`invalid ${headerName} header: base64 decode failed (${err instanceof Error ? err.message : String(err)})`);
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(decoded);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
throw new Error(`invalid ${headerName} header: JSON parse failed (${err instanceof Error ? err.message : String(err)})`);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const parseIntStrict = (raw, headerName) => {
|
|
42
|
+
const n = Number.parseInt(raw, 10);
|
|
43
|
+
if (!Number.isFinite(n)) {
|
|
44
|
+
throw new Error(`invalid ${headerName} header: not an integer (${JSON.stringify(raw)})`);
|
|
45
|
+
}
|
|
46
|
+
return n;
|
|
47
|
+
};
|
|
48
|
+
const parseTerminal = (h) => {
|
|
49
|
+
const present = TERMINAL_HEADERS.filter((name) => get(h, name) !== undefined);
|
|
50
|
+
if (present.length === 0)
|
|
51
|
+
return null;
|
|
52
|
+
if (present.length > 1) {
|
|
53
|
+
throw new Error(`conflicting x-mock-* headers: ${present.join(', ')} — pick exactly one terminal mode`);
|
|
54
|
+
}
|
|
55
|
+
const [only] = present;
|
|
56
|
+
switch (only) {
|
|
57
|
+
case 'x-mock-status': {
|
|
58
|
+
const status = parseIntStrict(get(h, 'x-mock-status'), 'x-mock-status');
|
|
59
|
+
return { kind: 'http-status', status };
|
|
60
|
+
}
|
|
61
|
+
case 'x-mock-business-failure':
|
|
62
|
+
case 'x-mock-business-failure-b64': {
|
|
63
|
+
let message;
|
|
64
|
+
if (only === 'x-mock-business-failure-b64') {
|
|
65
|
+
const raw = get(h, 'x-mock-business-failure-b64');
|
|
66
|
+
try {
|
|
67
|
+
message = Buffer.from(raw, 'base64').toString('utf8');
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
throw new Error(`invalid x-mock-business-failure-b64 header: base64 decode failed (${err instanceof Error ? err.message : String(err)})`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
message = get(h, 'x-mock-business-failure');
|
|
75
|
+
}
|
|
76
|
+
const extraRaw = get(h, 'x-mock-business-failure-extra');
|
|
77
|
+
if (extraRaw === undefined)
|
|
78
|
+
return { kind: 'business-failure', message };
|
|
79
|
+
const extra = decodeBase64Json(extraRaw, 'x-mock-business-failure-extra');
|
|
80
|
+
if (extra === null || typeof extra !== 'object' || Array.isArray(extra)) {
|
|
81
|
+
throw new Error('invalid x-mock-business-failure-extra header: decoded value must be a JSON object');
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
kind: 'business-failure',
|
|
85
|
+
message,
|
|
86
|
+
extra: extra,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
case 'x-mock-body': {
|
|
90
|
+
const body = decodeBase64Json(get(h, 'x-mock-body'), 'x-mock-body');
|
|
91
|
+
const statusRaw = get(h, 'x-mock-body-status');
|
|
92
|
+
const contentType = get(h, 'x-mock-body-content-type');
|
|
93
|
+
const mode = { kind: 'custom-body', body };
|
|
94
|
+
if (statusRaw !== undefined) {
|
|
95
|
+
;
|
|
96
|
+
mode.status = parseIntStrict(statusRaw, 'x-mock-body-status');
|
|
97
|
+
}
|
|
98
|
+
if (contentType !== undefined) {
|
|
99
|
+
;
|
|
100
|
+
mode.contentType = contentType;
|
|
101
|
+
}
|
|
102
|
+
return mode;
|
|
103
|
+
}
|
|
104
|
+
case 'x-mock-empty': {
|
|
105
|
+
const statusRaw = get(h, 'x-mock-empty-status');
|
|
106
|
+
if (statusRaw === undefined)
|
|
107
|
+
return { kind: 'empty-body' };
|
|
108
|
+
return {
|
|
109
|
+
kind: 'empty-body',
|
|
110
|
+
status: parseIntStrict(statusRaw, 'x-mock-empty-status'),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
case 'x-mock-malformed': {
|
|
114
|
+
const statusRaw = get(h, 'x-mock-malformed-status');
|
|
115
|
+
if (statusRaw === undefined)
|
|
116
|
+
return { kind: 'malformed-json' };
|
|
117
|
+
return {
|
|
118
|
+
kind: 'malformed-json',
|
|
119
|
+
status: parseIntStrict(statusRaw, 'x-mock-malformed-status'),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
case 'x-mock-hang':
|
|
123
|
+
return { kind: 'hang' };
|
|
124
|
+
case 'x-mock-destroy':
|
|
125
|
+
return { kind: 'destroy' };
|
|
126
|
+
/* v8 ignore next 3 -- exhaustiveness: unreachable */
|
|
127
|
+
default:
|
|
128
|
+
// exhaustiveness — unreachable.
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
export const parseHeaderMode = (headers) => {
|
|
133
|
+
try {
|
|
134
|
+
const terminal = parseTerminal(headers);
|
|
135
|
+
const delayRaw = get(headers, 'x-mock-delay');
|
|
136
|
+
if (terminal === null && delayRaw === undefined) {
|
|
137
|
+
return { kind: 'none' };
|
|
138
|
+
}
|
|
139
|
+
if (delayRaw === undefined) {
|
|
140
|
+
return { kind: 'mode', mode: terminal };
|
|
141
|
+
}
|
|
142
|
+
const ms = parseIntStrict(delayRaw, 'x-mock-delay');
|
|
143
|
+
if (ms < 0) {
|
|
144
|
+
return {
|
|
145
|
+
kind: 'error',
|
|
146
|
+
message: 'invalid x-mock-delay header: must be >= 0',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (terminal === null) {
|
|
150
|
+
return { kind: 'mode', mode: { kind: 'delay', ms } };
|
|
151
|
+
}
|
|
152
|
+
if (terminal.kind === 'delay' ||
|
|
153
|
+
terminal.kind === 'hang' ||
|
|
154
|
+
terminal.kind === 'destroy') {
|
|
155
|
+
return {
|
|
156
|
+
kind: 'error',
|
|
157
|
+
message: `x-mock-delay cannot wrap kind=${terminal.kind}`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return { kind: 'mode', mode: { kind: 'delay', ms, then: terminal } };
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
return {
|
|
164
|
+
kind: 'error',
|
|
165
|
+
message: err instanceof Error ? err.message : String(err),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
/** Public alias of {@link parseHeaderMode}. */
|
|
170
|
+
export const parseMockHeaders = parseHeaderMode;
|
|
171
|
+
//# sourceMappingURL=header-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header-parser.js","sourceRoot":"","sources":["../src/header-parser.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { adminPlugin, adminCapability } from './admin-plugin.js';
|
|
2
|
+
export type { AdminOptions, AdminInfo } from './admin-plugin.js';
|
|
3
|
+
export { faultInjectionPlugin, faultControlCapability } from './fault-plugin.js';
|
|
4
|
+
export type { FaultInjectionOptions, FaultControl, FaultOverrideInput } from './fault-plugin.js';
|
|
5
|
+
export type { ErrorMode, OverrideMatcher, StickyOverride, AdminModeCors, Scenario, ScenarioSummary } from './admin-types.js';
|
|
6
|
+
export { openAdminBrowserPlugin } from './open-browser-plugin.js';
|
|
7
|
+
export type { ScenariosStore } from './scenarios.js';
|
|
8
|
+
export { fileScenariosStore } from './scenarios-store.js';
|
|
9
|
+
export type { FileScenariosStoreOptions } from './scenarios-store.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAChE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAChF,YAAY,EAAE,qBAAqB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAChG,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC5H,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AACjE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,YAAY,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { adminPlugin, adminCapability } from './admin-plugin.js';
|
|
2
|
+
export { faultInjectionPlugin, faultControlCapability } from './fault-plugin.js';
|
|
3
|
+
export { openAdminBrowserPlugin } from './open-browser-plugin.js';
|
|
4
|
+
export { fileScenariosStore } from './scenarios-store.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEhE,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAGhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAA;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/inject.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Fault interception; HTTP owns cancellation and terminal transport effects. */
|
|
2
|
+
import type { MockRequest, MockResponse } from '@databehave/server';
|
|
3
|
+
import type { OverridesStore } from './overrides-store.js';
|
|
4
|
+
import type { ErrorMode } from './admin-types.js';
|
|
5
|
+
/** Header precedence, malformed input, or no applicable override. */
|
|
6
|
+
export type ResolveResult = {
|
|
7
|
+
kind: 'mode';
|
|
8
|
+
mode: ErrorMode;
|
|
9
|
+
source: 'header' | 'sticky';
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'error';
|
|
12
|
+
message: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'none';
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Pure resolver: header overrides beat sticky overrides. Returns
|
|
18
|
+
* `'none'` when nothing applies and `'error'` for malformed headers.
|
|
19
|
+
*/
|
|
20
|
+
export declare const resolveMode: (store: Pick<OverridesStore, "resolve">, method: string, path: string, headers: Readonly<Record<string, string>>) => ResolveResult;
|
|
21
|
+
/** Interception policy and fault state supplied by the fault plugin. */
|
|
22
|
+
export interface InjectHookOptions {
|
|
23
|
+
readonly store: Pick<OverridesStore, 'resolve'>;
|
|
24
|
+
readonly headers?: boolean;
|
|
25
|
+
/** Paths matching any of these prefixes are passed through (no injection). */
|
|
26
|
+
readonly bypassPathPrefixes?: readonly string[];
|
|
27
|
+
/** When false, destroy mode resolves to a 503 instead of dropping the socket. */
|
|
28
|
+
readonly allowDestroy?: boolean;
|
|
29
|
+
readonly logger?: {
|
|
30
|
+
warn(message: string): void;
|
|
31
|
+
info?(message: string): void;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export declare const createInjectHook: (opts: InjectHookOptions) => (req: MockRequest) => Promise<MockResponse | void>;
|
|
35
|
+
//# sourceMappingURL=inject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inject.d.ts","sourceRoot":"","sources":["../src/inject.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACb,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAKjD,qEAAqE;AACrE,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpB;;;GAGG;AACH,eAAO,MAAM,WAAW,GACtB,OAAO,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,EACtC,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KACxC,aAUF,CAAA;AAkFD,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,CAAA;IAC/C,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B,8EAA8E;IAC9E,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/C,iFAAiF;IACjF,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAC7B,CAAA;CACF;AAED,eAAO,MAAM,gBAAgB,GAAI,MAAM,iBAAiB,MAUpD,KAAK,WAAW,KACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAuC/B,CAAA"}
|
package/dist/inject.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { parseHeaderMode } from './header-parser.js';
|
|
2
|
+
import { terminateTransport } from '@databehave/server';
|
|
3
|
+
const TAG_HEADER = 'x-mock-injected';
|
|
4
|
+
/**
|
|
5
|
+
* Pure resolver: header overrides beat sticky overrides. Returns
|
|
6
|
+
* `'none'` when nothing applies and `'error'` for malformed headers.
|
|
7
|
+
*/
|
|
8
|
+
export const resolveMode = (store, method, path, headers) => {
|
|
9
|
+
const fromHeader = parseHeaderMode(headers);
|
|
10
|
+
if (fromHeader.kind === 'error')
|
|
11
|
+
return { kind: 'error', message: fromHeader.message };
|
|
12
|
+
if (fromHeader.kind === 'mode') {
|
|
13
|
+
return { kind: 'mode', mode: fromHeader.mode, source: 'header' };
|
|
14
|
+
}
|
|
15
|
+
const sticky = store.resolve(method, path);
|
|
16
|
+
if (sticky)
|
|
17
|
+
return { kind: 'mode', mode: sticky.mode, source: 'sticky' };
|
|
18
|
+
return { kind: 'none' };
|
|
19
|
+
};
|
|
20
|
+
const buildResponse = (mode, source) => {
|
|
21
|
+
const tagHeaders = {
|
|
22
|
+
[TAG_HEADER]: `${mode.kind}:${source}`,
|
|
23
|
+
};
|
|
24
|
+
switch (mode.kind) {
|
|
25
|
+
case 'http-status':
|
|
26
|
+
return {
|
|
27
|
+
status: mode.status,
|
|
28
|
+
json: { error: true, status: mode.status, injected: mode.kind },
|
|
29
|
+
headers: tagHeaders,
|
|
30
|
+
};
|
|
31
|
+
case 'business-failure':
|
|
32
|
+
return {
|
|
33
|
+
status: 200,
|
|
34
|
+
json: {
|
|
35
|
+
success: false,
|
|
36
|
+
message: mode.message,
|
|
37
|
+
...(mode.extra ?? {}),
|
|
38
|
+
},
|
|
39
|
+
headers: { ...tagHeaders, 'content-type': 'application/json' },
|
|
40
|
+
};
|
|
41
|
+
case 'custom-body': {
|
|
42
|
+
const status = mode.status ?? 200;
|
|
43
|
+
const contentType = mode.contentType ?? 'application/json';
|
|
44
|
+
if (typeof mode.body === 'string' && !contentType.includes('json')) {
|
|
45
|
+
return {
|
|
46
|
+
status,
|
|
47
|
+
text: mode.body,
|
|
48
|
+
headers: { ...tagHeaders, 'content-type': contentType },
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const payload = typeof mode.body === 'string' ? mode.body : JSON.stringify(mode.body);
|
|
52
|
+
return {
|
|
53
|
+
status,
|
|
54
|
+
raw: payload,
|
|
55
|
+
headers: { ...tagHeaders, 'content-type': contentType },
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
case 'empty-body':
|
|
59
|
+
return {
|
|
60
|
+
status: mode.status ?? 204,
|
|
61
|
+
empty: true,
|
|
62
|
+
headers: tagHeaders,
|
|
63
|
+
};
|
|
64
|
+
case 'malformed-json':
|
|
65
|
+
return {
|
|
66
|
+
status: mode.status ?? 200,
|
|
67
|
+
raw: '{',
|
|
68
|
+
headers: { ...tagHeaders, 'content-type': 'application/json' },
|
|
69
|
+
};
|
|
70
|
+
/* v8 ignore start -- defensive: delay/hang/destroy never reach here */
|
|
71
|
+
case 'delay':
|
|
72
|
+
case 'hang':
|
|
73
|
+
case 'destroy':
|
|
74
|
+
// delay/hang are handled before this function; destroy is dispatched
|
|
75
|
+
// through HTTP transport termination. Reaching here is a bug.
|
|
76
|
+
return {
|
|
77
|
+
status: 500,
|
|
78
|
+
json: {
|
|
79
|
+
error: true,
|
|
80
|
+
message: `inject: ${mode.kind} should be handled before buildResponse`,
|
|
81
|
+
},
|
|
82
|
+
headers: tagHeaders,
|
|
83
|
+
};
|
|
84
|
+
/* v8 ignore stop */
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const buildDestroyDisabledResponse = () => ({
|
|
88
|
+
status: 503,
|
|
89
|
+
json: {
|
|
90
|
+
error: 'x-mock-destroy disabled by faultInjectionPlugin allowDestroy:false',
|
|
91
|
+
},
|
|
92
|
+
headers: { [TAG_HEADER]: 'destroy-disabled' },
|
|
93
|
+
});
|
|
94
|
+
export const createInjectHook = (opts) => {
|
|
95
|
+
const { store } = opts;
|
|
96
|
+
const bypass = opts.bypassPathPrefixes ?? ['/databehave'];
|
|
97
|
+
const allowDestroy = opts.allowDestroy !== false;
|
|
98
|
+
const logger = opts.logger ?? console;
|
|
99
|
+
const shouldBypass = (path) => bypass.some((p) => path === p || path.startsWith(p.endsWith('/') ? p : `${p}/`));
|
|
100
|
+
return async (req) => {
|
|
101
|
+
if (shouldBypass(req.path))
|
|
102
|
+
return undefined;
|
|
103
|
+
const resolved = resolveMode(store, req.effectiveMethod, req.path, opts.headers === false ? {} : req.headers);
|
|
104
|
+
if (resolved.kind === 'none')
|
|
105
|
+
return undefined;
|
|
106
|
+
if (resolved.kind === 'error') {
|
|
107
|
+
return {
|
|
108
|
+
status: 400,
|
|
109
|
+
json: { error: 'invalid x-mock-* header', detail: resolved.message },
|
|
110
|
+
headers: { [TAG_HEADER]: 'header-error' },
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const { mode, source } = resolved;
|
|
114
|
+
if (mode.kind === 'hang') {
|
|
115
|
+
await waitForFault(req.signal);
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
if (mode.kind === 'destroy') {
|
|
119
|
+
if (!allowDestroy) {
|
|
120
|
+
logger.warn(`[@databehave/admin] destroy requested on ${req.method} ${req.path} ` +
|
|
121
|
+
`(source=${source}) but faultInjectionPlugin allowDestroy:false — responding 503.`);
|
|
122
|
+
return buildDestroyDisabledResponse();
|
|
123
|
+
}
|
|
124
|
+
return terminateTransport('Fault injection requested connection destruction');
|
|
125
|
+
}
|
|
126
|
+
if (mode.kind === 'delay') {
|
|
127
|
+
await waitForFault(req.signal, mode.ms);
|
|
128
|
+
if (mode.then === undefined)
|
|
129
|
+
return undefined;
|
|
130
|
+
return buildResponse(mode.then, source);
|
|
131
|
+
}
|
|
132
|
+
return buildResponse(mode, source);
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
/** Wait until the delay elapses or cancel immediately when HTTP aborts the request. */
|
|
136
|
+
const waitForFault = (signal, ms) => new Promise((resolve, reject) => {
|
|
137
|
+
if (signal.aborted) {
|
|
138
|
+
reject(signal.reason);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
let timer;
|
|
142
|
+
const abort = () => {
|
|
143
|
+
if (timer !== undefined)
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
signal.removeEventListener('abort', abort);
|
|
146
|
+
reject(signal.reason);
|
|
147
|
+
};
|
|
148
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
149
|
+
if (ms !== undefined)
|
|
150
|
+
timer = setTimeout(() => {
|
|
151
|
+
signal.removeEventListener('abort', abort);
|
|
152
|
+
resolve();
|
|
153
|
+
}, ms);
|
|
154
|
+
});
|
|
155
|
+
//# sourceMappingURL=inject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inject.js","sourceRoot":"","sources":["../src/inject.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Plugin } from '@databehave/core';
|
|
2
|
+
/** Select an argument-based opener without a shell. */
|
|
3
|
+
export declare const resolveBrowserCommand: (platform: NodeJS.Platform) => string;
|
|
4
|
+
/** Explicit opt-in browser opener; application composition authorizes this local effect. */
|
|
5
|
+
export declare const openAdminBrowserPlugin: () => Plugin;
|
|
6
|
+
//# sourceMappingURL=open-browser-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-browser-plugin.d.ts","sourceRoot":"","sources":["../src/open-browser-plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAK9C,uDAAuD;AACvD,eAAO,MAAM,qBAAqB,GAAI,UAAU,MAAM,CAAC,QAAQ,KAAG,MACmB,CAAA;AAErF,4FAA4F;AAC5F,eAAO,MAAM,sBAAsB,QAAO,MAqBxC,CAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { listenerCapability } from '@databehave/server';
|
|
3
|
+
import { adminCapability } from './admin-plugin.js';
|
|
4
|
+
import { emitAdminBanner } from './banner.js';
|
|
5
|
+
/** Select an argument-based opener without a shell. */
|
|
6
|
+
export const resolveBrowserCommand = (platform) => platform === 'darwin' ? 'open' : platform === 'win32' ? 'explorer.exe' : 'xdg-open';
|
|
7
|
+
/** Explicit opt-in browser opener; application composition authorizes this local effect. */
|
|
8
|
+
export const openAdminBrowserPlugin = () => ({
|
|
9
|
+
id: 'databehave.admin.open-browser', apiVersion: 1,
|
|
10
|
+
requires: [listenerCapability, adminCapability],
|
|
11
|
+
setup() { },
|
|
12
|
+
open(ctx) {
|
|
13
|
+
const admin = ctx.use(adminCapability);
|
|
14
|
+
const address = ctx.use(listenerCapability).address;
|
|
15
|
+
if (!admin.ui || !address)
|
|
16
|
+
return;
|
|
17
|
+
const host = address.host === '0.0.0.0' || address.host === '::' ? '127.0.0.1' : address.host;
|
|
18
|
+
const urlHost = host.includes(':') ? `[${host}]` : host;
|
|
19
|
+
const url = `http://${urlHost}:${address.port}${admin.path}`;
|
|
20
|
+
emitAdminBanner(urlHost, address.port, admin.path);
|
|
21
|
+
const command = resolveBrowserCommand(process.platform);
|
|
22
|
+
const child = spawn(command, [url], { stdio: 'ignore', shell: false });
|
|
23
|
+
ctx.defer(() => { if (child.exitCode === null)
|
|
24
|
+
child.kill(); });
|
|
25
|
+
child.on('error', (error) => {
|
|
26
|
+
// eslint-disable-next-line no-console
|
|
27
|
+
console.warn(`Unable to open admin browser: ${error.message}`);
|
|
28
|
+
});
|
|
29
|
+
child.unref();
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=open-browser-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-browser-plugin.js","sourceRoot":"","sources":["../src/open-browser-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C,uDAAuD;AACvD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,QAAyB,EAAU,EAAE,CACzE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,CAAA;AAErF,4FAA4F;AAC5F,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAW,EAAE,CAAC,CAAC;IACnD,EAAE,EAAE,+BAA+B,EAAE,UAAU,EAAE,CAAC;IAClD,QAAQ,EAAE,CAAC,kBAAkB,EAAE,eAAe,CAAC;IAC/C,KAAK,KAAI,CAAC;IACV,IAAI,CAAC,GAAG;QACN,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;QACtC,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,OAAO,CAAA;QACnD,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO;YAAE,OAAM;QACjC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAA;QAC7F,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAA;QACvD,MAAM,GAAG,GAAG,UAAU,OAAO,IAAI,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;QAC5D,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QAClD,MAAM,OAAO,GAAG,qBAAqB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QACvD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;QACtE,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,EAAE,CAAA,CAAC,CAAC,CAAC,CAAA;QAC9D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC1B,sCAAsC;YACtC,OAAO,CAAC,IAAI,CAAC,iCAAiC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;QAChE,CAAC,CAAC,CAAA;QACF,KAAK,CAAC,KAAK,EAAE,CAAA;IACf,CAAC;CACF,CAAC,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ErrorMode, StickyMatcher, StickyOverride } from './admin-types.js';
|
|
2
|
+
/** Optional duplicate-rule diagnostics. */
|
|
3
|
+
export interface OverridesStoreLogger {
|
|
4
|
+
warn(message: string): void;
|
|
5
|
+
info?(message: string): void;
|
|
6
|
+
}
|
|
7
|
+
/** Instance-local state owned exclusively by the fault plugin. */
|
|
8
|
+
export interface OverridesStore {
|
|
9
|
+
replace(input: unknown): readonly StickyOverride[];
|
|
10
|
+
add(input: {
|
|
11
|
+
matcher: StickyMatcher;
|
|
12
|
+
mode: ErrorMode;
|
|
13
|
+
description?: string;
|
|
14
|
+
}): StickyOverride;
|
|
15
|
+
remove(id: string): boolean;
|
|
16
|
+
list(): readonly StickyOverride[];
|
|
17
|
+
clear(): number;
|
|
18
|
+
resolve(method: string, path: string): StickyOverride | undefined;
|
|
19
|
+
}
|
|
20
|
+
export declare const createOverridesStore: (logger?: OverridesStoreLogger) => OverridesStore;
|
|
21
|
+
//# sourceMappingURL=overrides-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overrides-store.d.ts","sourceRoot":"","sources":["../src/overrides-store.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEhF,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,cAAc,EAAE,CAAA;IAClD,GAAG,CAAC,KAAK,EAAE;QACT,OAAO,EAAE,aAAa,CAAA;QACtB,IAAI,EAAE,SAAS,CAAA;QACf,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,GAAG,cAAc,CAAA;IAClB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,IAAI,IAAI,SAAS,cAAc,EAAE,CAAA;IACjC,KAAK,IAAI,MAAM,CAAA;IACf,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAA;CAClE;AAED,eAAO,MAAM,oBAAoB,GAC/B,SAAQ,oBAA8B,KACrC,cAgHF,CAAA"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory sticky overrides registry. Lost on process restart by
|
|
3
|
+
* design — this tool is for ad-hoc dev sessions, not persistence.
|
|
4
|
+
*
|
|
5
|
+
* Resolution priority (first hit wins): `exact` > `path` > `global`.
|
|
6
|
+
* When two stickies live at the same priority level, the **last** one
|
|
7
|
+
* added wins (and a warning is logged) so the dev's most-recent click
|
|
8
|
+
* in the UI is always the active rule.
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
import { validateOverrides } from './validation.js';
|
|
12
|
+
export const createOverridesStore = (logger = console) => {
|
|
13
|
+
// Insertion-ordered. `Map.values()` yields oldest → newest, so
|
|
14
|
+
// when iterating we keep track of the *last* hit per priority bucket.
|
|
15
|
+
let byId = new Map();
|
|
16
|
+
const sameMatcher = (a, b) => {
|
|
17
|
+
if (a.kind !== b.kind)
|
|
18
|
+
return false;
|
|
19
|
+
if (a.kind === 'exact' && b.kind === 'exact') {
|
|
20
|
+
return (a.method.toUpperCase() === b.method.toUpperCase() && a.path === b.path);
|
|
21
|
+
}
|
|
22
|
+
if (a.kind === 'path' && b.kind === 'path')
|
|
23
|
+
return a.path === b.path;
|
|
24
|
+
return a.kind === 'global' && b.kind === 'global';
|
|
25
|
+
};
|
|
26
|
+
const describeMatcher = (m) => {
|
|
27
|
+
if (m.kind === 'exact')
|
|
28
|
+
return `${m.method.toUpperCase()} ${m.path}`;
|
|
29
|
+
if (m.kind === 'path')
|
|
30
|
+
return `* ${m.path}`;
|
|
31
|
+
return '* (global)';
|
|
32
|
+
};
|
|
33
|
+
const add = ({ matcher, mode, description }) => {
|
|
34
|
+
// Detect same-matcher collision so the warning is informative.
|
|
35
|
+
for (const existing of byId.values()) {
|
|
36
|
+
if (sameMatcher(existing.matcher, matcher)) {
|
|
37
|
+
logger.warn(`[mock-server/overrides] duplicate matcher ${describeMatcher(matcher)} ` +
|
|
38
|
+
`— new rule shadows ${existing.id} (last-wins). Remove the old one if unintended.`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const override = {
|
|
42
|
+
id: randomUUID(),
|
|
43
|
+
matcher: matcher.kind === 'exact'
|
|
44
|
+
? {
|
|
45
|
+
kind: 'exact',
|
|
46
|
+
method: matcher.method.toUpperCase(),
|
|
47
|
+
path: matcher.path,
|
|
48
|
+
}
|
|
49
|
+
: matcher.kind === 'global' && matcher.methods && matcher.methods.length > 0
|
|
50
|
+
? {
|
|
51
|
+
kind: 'global',
|
|
52
|
+
methods: matcher.methods.map((m) => m.toUpperCase()),
|
|
53
|
+
}
|
|
54
|
+
: matcher.kind === 'global'
|
|
55
|
+
? { kind: 'global' }
|
|
56
|
+
: matcher,
|
|
57
|
+
mode,
|
|
58
|
+
createdAt: new Date().toISOString(),
|
|
59
|
+
...(description !== undefined ? { description } : {}),
|
|
60
|
+
};
|
|
61
|
+
byId.set(override.id, override);
|
|
62
|
+
return override;
|
|
63
|
+
};
|
|
64
|
+
const remove = (id) => byId.delete(id);
|
|
65
|
+
const list = () => [...byId.values()];
|
|
66
|
+
const clear = () => {
|
|
67
|
+
const n = byId.size;
|
|
68
|
+
byId.clear();
|
|
69
|
+
return n;
|
|
70
|
+
};
|
|
71
|
+
const resolve = (method, path) => {
|
|
72
|
+
const m = method.toUpperCase();
|
|
73
|
+
// Walk newest → oldest so the last-added rule wins per bucket.
|
|
74
|
+
const all = [...byId.values()].reverse();
|
|
75
|
+
let exactHit;
|
|
76
|
+
let pathHit;
|
|
77
|
+
let globalHit;
|
|
78
|
+
for (const o of all) {
|
|
79
|
+
if (o.matcher.kind === 'exact') {
|
|
80
|
+
if (!exactHit && o.matcher.method === m && o.matcher.path === path) {
|
|
81
|
+
exactHit = o;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else if (o.matcher.kind === 'path') {
|
|
85
|
+
if (!pathHit && o.matcher.path === path)
|
|
86
|
+
pathHit = o;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
if (!globalHit
|
|
90
|
+
&& (!o.matcher.methods
|
|
91
|
+
|| o.matcher.methods.length === 0
|
|
92
|
+
|| o.matcher.methods.includes(m))) {
|
|
93
|
+
globalHit = o;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return exactHit ?? pathHit ?? globalHit;
|
|
98
|
+
};
|
|
99
|
+
const replace = (input) => {
|
|
100
|
+
const validated = validateOverrides(input);
|
|
101
|
+
const replacement = new Map();
|
|
102
|
+
for (const override of validated) {
|
|
103
|
+
const id = randomUUID();
|
|
104
|
+
replacement.set(id, { ...override, id,
|
|
105
|
+
matcher: override.matcher.kind === 'exact'
|
|
106
|
+
? { ...override.matcher, method: override.matcher.method.toUpperCase() }
|
|
107
|
+
: override.matcher });
|
|
108
|
+
}
|
|
109
|
+
byId = replacement;
|
|
110
|
+
return list();
|
|
111
|
+
};
|
|
112
|
+
return { add, remove, list, clear, resolve, replace };
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=overrides-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overrides-store.js","sourceRoot":"","sources":["../src/overrides-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AdminModeCors } from './admin-types.js';
|
|
2
|
+
/** Origin-less local clients are allowed; a present Origin must be a valid HTTP origin. */
|
|
3
|
+
export declare const isAdminOriginAllowed: (bind: "loopback-only" | "any", cors: AdminModeCors, origin: string | undefined, requestUrl: string) => boolean;
|
|
4
|
+
/** Admin owns response sharing; allowlists reflect exactly one permitted request origin. */
|
|
5
|
+
export declare const resolveAdminCors: (bind: "loopback-only" | "any", cors: AdminModeCors, requestOrigin?: string) => Record<string, string>;
|
|
6
|
+
//# sourceMappingURL=resolve-cors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-cors.d.ts","sourceRoot":"","sources":["../src/resolve-cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAKrD,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,GAC/B,MAAM,eAAe,GAAG,KAAK,EAAE,MAAM,aAAa,EAAE,QAAQ,MAAM,GAAG,SAAS,EAAE,YAAY,MAAM,KACjG,OASF,CAAA;AAED,4FAA4F;AAC5F,eAAO,MAAM,gBAAgB,GAC3B,MAAM,eAAe,GAAG,KAAK,EAAE,MAAM,aAAa,EAAE,sBAAkB,KACrE,MAAM,CAAC,MAAM,EAAE,MAAM,CAcvB,CAAA"}
|