@databehave/server 0.1.4 → 0.2.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 +17 -0
- package/README.md +85 -83
- package/dist/cors.d.ts +1 -1
- package/dist/cors.js +1 -1
- package/dist/cors.js.map +1 -1
- package/dist/http.d.ts +94 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +23 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +10 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -18
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +9 -49
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +16 -92
- package/dist/log.js.map +1 -1
- package/dist/node-authority.d.ts +12 -0
- package/dist/node-authority.d.ts.map +1 -0
- package/dist/node-authority.js +72 -0
- package/dist/node-authority.js.map +1 -0
- package/dist/node-body.d.ts +11 -0
- package/dist/node-body.d.ts.map +1 -0
- package/dist/node-body.js +61 -0
- package/dist/node-body.js.map +1 -0
- package/dist/node-http.d.ts +19 -0
- package/dist/node-http.d.ts.map +1 -0
- package/dist/node-http.js +210 -0
- package/dist/node-http.js.map +1 -0
- package/dist/plugins/access-log.d.ts +11 -0
- package/dist/plugins/access-log.d.ts.map +1 -0
- package/dist/plugins/access-log.js +27 -0
- package/dist/plugins/access-log.js.map +1 -0
- package/dist/plugins/cors.d.ts +5 -0
- package/dist/plugins/cors.d.ts.map +1 -0
- package/dist/plugins/cors.js +35 -0
- package/dist/plugins/cors.js.map +1 -0
- package/dist/plugins/routes.d.ts +5 -0
- package/dist/plugins/routes.d.ts.map +1 -0
- package/dist/plugins/routes.js +10 -0
- package/dist/plugins/routes.js.map +1 -0
- package/dist/request.d.ts +2 -2
- package/dist/request.d.ts.map +1 -1
- package/dist/request.js +6 -4
- package/dist/request.js.map +1 -1
- package/dist/route-key.d.ts +2 -0
- package/dist/route-key.d.ts.map +1 -1
- package/dist/route-key.js +5 -1
- package/dist/route-key.js.map +1 -1
- package/dist/server.d.ts +7 -59
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +289 -466
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +10 -156
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/package.json +9 -22
- package/dist/admin/admin-routes.d.ts +0 -42
- package/dist/admin/admin-routes.d.ts.map +0 -1
- package/dist/admin/admin-routes.js +0 -472
- package/dist/admin/admin-routes.js.map +0 -1
- package/dist/admin/admin-types.d.ts +0 -170
- package/dist/admin/admin-types.d.ts.map +0 -1
- package/dist/admin/admin-types.js +0 -30
- package/dist/admin/admin-types.js.map +0 -1
- package/dist/admin/banner.d.ts +0 -11
- package/dist/admin/banner.d.ts.map +0 -1
- package/dist/admin/banner.js +0 -5
- package/dist/admin/banner.js.map +0 -1
- package/dist/admin/header-parser.d.ts +0 -14
- package/dist/admin/header-parser.d.ts.map +0 -1
- package/dist/admin/header-parser.js +0 -171
- package/dist/admin/header-parser.js.map +0 -1
- package/dist/admin/inject.d.ts +0 -48
- package/dist/admin/inject.d.ts.map +0 -1
- package/dist/admin/inject.js +0 -167
- package/dist/admin/inject.js.map +0 -1
- package/dist/admin/overrides-store.d.ts +0 -18
- package/dist/admin/overrides-store.d.ts.map +0 -1
- package/dist/admin/overrides-store.js +0 -100
- package/dist/admin/overrides-store.js.map +0 -1
- package/dist/admin/resolve-cors.d.ts +0 -22
- package/dist/admin/resolve-cors.d.ts.map +0 -1
- package/dist/admin/resolve-cors.js +0 -28
- package/dist/admin/resolve-cors.js.map +0 -1
- package/dist/admin/scenarios-store.d.ts +0 -15
- package/dist/admin/scenarios-store.d.ts.map +0 -1
- package/dist/admin/scenarios-store.js +0 -109
- package/dist/admin/scenarios-store.js.map +0 -1
- package/dist/admin/ui.css +0 -1
- package/dist/admin/ui.html +0 -13
- package/dist/admin/ui.js +0 -240
- package/dist/bin-helpers.d.ts +0 -46
- package/dist/bin-helpers.d.ts.map +0 -1
- package/dist/bin-helpers.js +0 -61
- package/dist/bin-helpers.js.map +0 -1
- package/dist/bin.d.ts +0 -3
- package/dist/bin.d.ts.map +0 -1
- package/dist/bin.js +0 -86
- package/dist/bin.js.map +0 -1
- package/dist/define.d.ts +0 -26
- package/dist/define.d.ts.map +0 -1
- package/dist/define.js +0 -55
- package/dist/define.js.map +0 -1
- package/dist/json-config.d.ts +0 -170
- package/dist/json-config.d.ts.map +0 -1
- package/dist/json-config.js +0 -684
- package/dist/json-config.js.map +0 -1
- package/dist/jsonc.d.ts +0 -24
- package/dist/jsonc.d.ts.map +0 -1
- package/dist/jsonc.js +0 -125
- package/dist/jsonc.js.map +0 -1
- package/dist/middleware/request-validation.d.ts +0 -58
- package/dist/middleware/request-validation.d.ts.map +0 -1
- package/dist/middleware/request-validation.js +0 -328
- package/dist/middleware/request-validation.js.map +0 -1
- package/dist/mock-mode.d.ts +0 -95
- package/dist/mock-mode.d.ts.map +0 -1
- package/dist/mock-mode.js +0 -95
- package/dist/mock-mode.js.map +0 -1
- package/dist/openapi/auto-schema.d.ts +0 -83
- package/dist/openapi/auto-schema.d.ts.map +0 -1
- package/dist/openapi/auto-schema.js +0 -232
- package/dist/openapi/auto-schema.js.map +0 -1
- package/dist/openapi/generate.d.ts +0 -20
- package/dist/openapi/generate.d.ts.map +0 -1
- package/dist/openapi/generate.js +0 -121
- package/dist/openapi/generate.js.map +0 -1
- package/dist/openapi/loader.d.ts +0 -18
- package/dist/openapi/loader.d.ts.map +0 -1
- package/dist/openapi/loader.js +0 -25
- package/dist/openapi/loader.js.map +0 -1
- package/dist/openapi/register.d.ts +0 -58
- package/dist/openapi/register.d.ts.map +0 -1
- package/dist/openapi/register.js +0 -166
- package/dist/openapi/register.js.map +0 -1
- package/dist/openapi/seed.d.ts +0 -16
- package/dist/openapi/seed.d.ts.map +0 -1
- package/dist/openapi/seed.js +0 -58
- package/dist/openapi/seed.js.map +0 -1
- package/dist/openapi/translate.d.ts +0 -55
- package/dist/openapi/translate.d.ts.map +0 -1
- package/dist/openapi/translate.js +0 -319
- package/dist/openapi/translate.js.map +0 -1
- package/dist/openapi/walker.d.ts +0 -44
- package/dist/openapi/walker.d.ts.map +0 -1
- package/dist/openapi/walker.js +0 -15
- package/dist/openapi/walker.js.map +0 -1
- package/dist/run.d.ts +0 -27
- package/dist/run.d.ts.map +0 -1
- package/dist/run.js +0 -87
- package/dist/run.js.map +0 -1
- package/dist/validation/validate.d.ts +0 -38
- package/dist/validation/validate.d.ts.map +0 -1
- package/dist/validation/validate.js +0 -372
- package/dist/validation/validate.js.map +0 -1
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolve the static CORS header bag added to admin-route responses.
|
|
3
|
-
*
|
|
4
|
-
* The policy is intentionally tiny: admin endpoints are dev-only and
|
|
5
|
-
* never see browser credentials, so a full `Access-Control-*`
|
|
6
|
-
* negotiation would be overkill. We just need to decide whether the
|
|
7
|
-
* UI / a bookmarklet on a different origin can XHR into them.
|
|
8
|
-
*
|
|
9
|
-
* Defaults:
|
|
10
|
-
* - `'auto'` + loopback-only bind → `'*'` (UI on any localhost port works)
|
|
11
|
-
* - `'auto'` + bind: 'any' → `'same-origin'` (LAN-exposed: lock down)
|
|
12
|
-
* - `'any'` → `'*'`
|
|
13
|
-
* - `'same-origin'` → no CORS headers added
|
|
14
|
-
* - `{ origin: 'x' | ['x', ...] }` → first allowed origin (string join with ', ')
|
|
15
|
-
*
|
|
16
|
-
* Returned object is a `Record<string, string>` ready to splat into
|
|
17
|
-
* `MockResponse.headers`. Empty object when no CORS headers
|
|
18
|
-
* should be added (same-origin).
|
|
19
|
-
*/
|
|
20
|
-
import type { AdminModeCors, AdminModeConfig } from './admin-types.js';
|
|
21
|
-
export declare const resolveAdminCors: (bind: NonNullable<AdminModeConfig["bind"]>, cors: AdminModeCors) => Record<string, string>;
|
|
22
|
-
//# sourceMappingURL=resolve-cors.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-cors.d.ts","sourceRoot":"","sources":["../../src/admin/resolve-cors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAuBtE,eAAO,MAAM,gBAAgB,GAC3B,MAAM,WAAW,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAC1C,MAAM,aAAa,KAClB,MAAM,CAAC,MAAM,EAAE,MAAM,CAQvB,CAAA"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
const PERMISSIVE_METHODS = 'GET,POST,DELETE,OPTIONS';
|
|
2
|
-
const PERMISSIVE_HEADERS = 'content-type';
|
|
3
|
-
const wildcardHeaders = () => ({
|
|
4
|
-
'access-control-allow-origin': '*',
|
|
5
|
-
'access-control-allow-methods': PERMISSIVE_METHODS,
|
|
6
|
-
'access-control-allow-headers': PERMISSIVE_HEADERS,
|
|
7
|
-
});
|
|
8
|
-
const explicitOriginHeaders = (origin) => {
|
|
9
|
-
const value = Array.isArray(origin) ? origin.join(', ') : origin;
|
|
10
|
-
return {
|
|
11
|
-
'access-control-allow-origin': value,
|
|
12
|
-
'access-control-allow-methods': PERMISSIVE_METHODS,
|
|
13
|
-
'access-control-allow-headers': PERMISSIVE_HEADERS,
|
|
14
|
-
vary: 'Origin',
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
export const resolveAdminCors = (bind, cors) => {
|
|
18
|
-
if (cors === 'same-origin')
|
|
19
|
-
return {};
|
|
20
|
-
if (cors === 'any')
|
|
21
|
-
return wildcardHeaders();
|
|
22
|
-
if (cors === 'auto') {
|
|
23
|
-
return bind === 'loopback-only' ? wildcardHeaders() : {};
|
|
24
|
-
}
|
|
25
|
-
// explicit { origin } form
|
|
26
|
-
return explicitOriginHeaders(cors.origin);
|
|
27
|
-
};
|
|
28
|
-
//# sourceMappingURL=resolve-cors.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-cors.js","sourceRoot":"","sources":["../../src/admin/resolve-cors.ts"],"names":[],"mappings":"AAqBA,MAAM,kBAAkB,GAAG,yBAAyB,CAAA;AACpD,MAAM,kBAAkB,GAAG,cAAc,CAAA;AAEzC,MAAM,eAAe,GAAG,GAA2B,EAAE,CAAC,CAAC;IACrD,6BAA6B,EAAE,GAAG;IAClC,8BAA8B,EAAE,kBAAkB;IAClD,8BAA8B,EAAE,kBAAkB;CACnD,CAAC,CAAA;AAEF,MAAM,qBAAqB,GAAG,CAC5B,MAAkC,EACV,EAAE;IAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,MAAiB,CAAA;IAC5E,OAAO;QACL,6BAA6B,EAAE,KAAK;QACpC,8BAA8B,EAAE,kBAAkB;QAClD,8BAA8B,EAAE,kBAAkB;QAClD,IAAI,EAAE,QAAQ;KACf,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,IAA0C,EAC1C,IAAmB,EACK,EAAE;IAC1B,IAAI,IAAI,KAAK,aAAa;QAAE,OAAO,EAAE,CAAA;IACrC,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,eAAe,EAAE,CAAA;IAC5C,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IAC1D,CAAC;IACD,2BAA2B;IAC3B,OAAO,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AAC3C,CAAC,CAAA"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { Scenario, ScenarioSummary, StickyOverride } from './admin-types.js';
|
|
2
|
-
export declare const isValidScenarioName: (name: string) => boolean;
|
|
3
|
-
export interface ScenariosStore {
|
|
4
|
-
readonly dir: string;
|
|
5
|
-
list(): Promise<ScenarioSummary[]>;
|
|
6
|
-
get(name: string): Promise<Scenario | null>;
|
|
7
|
-
save(name: string, overrides: readonly StickyOverride[]): Promise<Scenario>;
|
|
8
|
-
remove(name: string): Promise<boolean>;
|
|
9
|
-
}
|
|
10
|
-
interface CreateScenariosStoreOptions {
|
|
11
|
-
readonly dir: string;
|
|
12
|
-
}
|
|
13
|
-
export declare const createScenariosStore: (opts: CreateScenariosStoreOptions) => ScenariosStore;
|
|
14
|
-
export {};
|
|
15
|
-
//# sourceMappingURL=scenarios-store.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scenarios-store.d.ts","sourceRoot":"","sources":["../../src/admin/scenarios-store.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAIjF,eAAO,MAAM,mBAAmB,GAAI,MAAM,MAAM,KAAG,OAA6B,CAAA;AAEhF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,IAAI,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;IAClC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IAC3C,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,cAAc,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC3E,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACvC;AAED,UAAU,2BAA2B;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAKD,eAAO,MAAM,oBAAoB,GAC/B,MAAM,2BAA2B,KAChC,cAgFF,CAAA"}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File-backed scenarios registry.
|
|
3
|
-
*
|
|
4
|
-
* A *scenario* is a named JSON snapshot of one or more sticky
|
|
5
|
-
* overrides. Scenarios live as `<name>.json` files inside the
|
|
6
|
-
* configured directory (default `<cwd>/mock-scenarios`). Names are
|
|
7
|
-
* sanitised — only `[A-Za-z0-9_-]`, max 64 chars — so they map
|
|
8
|
-
* directly to safe filenames and round-trip through HTTP path
|
|
9
|
-
* segments without escaping.
|
|
10
|
-
*
|
|
11
|
-
* The directory is opt-in: a missing dir yields an empty list rather
|
|
12
|
-
* than throwing. The first save creates the dir.
|
|
13
|
-
*
|
|
14
|
-
* Writes are atomic: write to `<name>.json.<tmp>` then rename, so a
|
|
15
|
-
* partial write never produces a half-parsed file.
|
|
16
|
-
*/
|
|
17
|
-
import { randomBytes } from 'node:crypto';
|
|
18
|
-
import { mkdir, readFile, readdir, rename, stat, unlink, writeFile } from 'node:fs/promises';
|
|
19
|
-
import { join } from 'node:path';
|
|
20
|
-
const NAME_RE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
21
|
-
export const isValidScenarioName = (name) => NAME_RE.test(name);
|
|
22
|
-
const isENOENT = (err) => typeof err === 'object' && err !== null && err.code === 'ENOENT';
|
|
23
|
-
export const createScenariosStore = (opts) => {
|
|
24
|
-
const { dir } = opts;
|
|
25
|
-
const filePath = (name) => join(dir, `${name}.json`);
|
|
26
|
-
const list = async () => {
|
|
27
|
-
let entries;
|
|
28
|
-
try {
|
|
29
|
-
entries = await readdir(dir);
|
|
30
|
-
}
|
|
31
|
-
catch (err) {
|
|
32
|
-
if (isENOENT(err))
|
|
33
|
-
return [];
|
|
34
|
-
throw err;
|
|
35
|
-
}
|
|
36
|
-
const out = [];
|
|
37
|
-
for (const entry of entries) {
|
|
38
|
-
if (!entry.endsWith('.json'))
|
|
39
|
-
continue;
|
|
40
|
-
const name = entry.slice(0, -5);
|
|
41
|
-
if (!isValidScenarioName(name))
|
|
42
|
-
continue;
|
|
43
|
-
try {
|
|
44
|
-
const full = join(dir, entry);
|
|
45
|
-
const [text, st] = await Promise.all([readFile(full, 'utf8'), stat(full)]);
|
|
46
|
-
const parsed = JSON.parse(text);
|
|
47
|
-
out.push({
|
|
48
|
-
name,
|
|
49
|
-
count: Array.isArray(parsed.overrides) ? parsed.overrides.length : 0,
|
|
50
|
-
created: parsed.created ?? st.mtime.toISOString(),
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// skip unreadable / malformed files; the UI still works.
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
out.sort((a, b) => a.name.localeCompare(b.name));
|
|
58
|
-
return out;
|
|
59
|
-
};
|
|
60
|
-
const get = async (name) => {
|
|
61
|
-
if (!isValidScenarioName(name))
|
|
62
|
-
return null;
|
|
63
|
-
try {
|
|
64
|
-
const text = await readFile(filePath(name), 'utf8');
|
|
65
|
-
const parsed = JSON.parse(text);
|
|
66
|
-
return {
|
|
67
|
-
name,
|
|
68
|
-
overrides: Array.isArray(parsed.overrides) ? parsed.overrides : [],
|
|
69
|
-
...(parsed.created !== undefined ? { created: parsed.created } : {}),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
catch (err) {
|
|
73
|
-
if (isENOENT(err))
|
|
74
|
-
return null;
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
const save = async (name, overrides) => {
|
|
79
|
-
if (!isValidScenarioName(name)) {
|
|
80
|
-
throw new Error(`@databehave/server: invalid scenario name: ${JSON.stringify(name)}`);
|
|
81
|
-
}
|
|
82
|
-
await mkdir(dir, { recursive: true });
|
|
83
|
-
const scenario = {
|
|
84
|
-
name,
|
|
85
|
-
overrides: [...overrides],
|
|
86
|
-
created: new Date().toISOString(),
|
|
87
|
-
};
|
|
88
|
-
const final = filePath(name);
|
|
89
|
-
const tmp = `${final}.${randomBytes(6).toString('hex')}.tmp`;
|
|
90
|
-
await writeFile(tmp, JSON.stringify(scenario, null, 2), 'utf8');
|
|
91
|
-
await rename(tmp, final);
|
|
92
|
-
return scenario;
|
|
93
|
-
};
|
|
94
|
-
const remove = async (name) => {
|
|
95
|
-
if (!isValidScenarioName(name))
|
|
96
|
-
return false;
|
|
97
|
-
try {
|
|
98
|
-
await unlink(filePath(name));
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
if (isENOENT(err))
|
|
103
|
-
return false;
|
|
104
|
-
throw err;
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
return { dir, list, get, save, remove };
|
|
108
|
-
};
|
|
109
|
-
//# sourceMappingURL=scenarios-store.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scenarios-store.js","sourceRoot":"","sources":["../../src/admin/scenarios-store.ts"],"names":[],"mappings":"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"}
|
package/dist/admin/ui.css
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
*{box-sizing:border-box}html,body{margin:0;padding:0;font:13px/1.45 -apple-system,BlinkMacSystemFont,Segoe UI,system-ui,sans-serif;color:#1f2328;background:#f6f8fa}button{font:inherit;cursor:pointer}input,select,textarea{font:inherit;color:inherit}code,pre,.mono{font-family:ui-monospace,SF Mono,Menlo,monospace}a{color:#0969da}.app{min-height:100vh;padding-bottom:60px}.top-bar{position:sticky;top:0;z-index:30;display:flex;align-items:center;gap:12px;padding:10px 16px;background:#24292f;color:#fff}.top-bar h1{margin:0;font-size:15px;font-weight:600;flex:1}.top-bar .url{font-size:11px;color:#adbac7;font-family:ui-monospace,Menlo,monospace}.top-bar .badge{background:#1f883d;color:#fff;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600}.top-bar button,.top-bar select{background:#fff;color:#24292f;border:1px solid #57606a;border-radius:6px;padding:4px 10px;font-size:12px}.top-bar button.ghost{background:transparent;color:#fff;border-color:#57606a}.filter-bar{position:sticky;top:48px;z-index:25;background:#fff;border-bottom:1px solid #d0d7de;padding:10px 16px;display:flex;gap:16px;align-items:center;flex-wrap:wrap}.filter-bar input[type=text]{padding:6px 10px;border:1px solid #d0d7de;border-radius:6px;min-width:260px;flex:1}.filter-bar label{display:inline-flex;align-items:center;gap:4px;font-size:12px}.main{padding:12px 16px;max-width:1300px;margin:0 auto}.right-panel{position:fixed;top:0;right:0;width:360px;max-width:90vw;height:100vh;background:#fff;border-left:1px solid #d0d7de;box-shadow:-4px 0 12px #0000000f;transform:translate(100%);transition:transform .18s ease;z-index:40;display:flex;flex-direction:column}.right-panel.open{transform:translate(0)}.right-panel header{padding:12px 16px;border-bottom:1px solid #d0d7de;display:flex;align-items:center;gap:8px}.right-panel header h2{margin:0;font-size:14px;flex:1}.right-panel .list{flex:1;overflow-y:auto;padding:8px}.pill-float{position:fixed;right:16px;bottom:16px;background:#cf222e;color:#fff;padding:8px 14px;border-radius:999px;font-size:12px;font-weight:600;border:none;box-shadow:0 2px 8px #00000026;z-index:35}.toast-stack{position:fixed;right:16px;bottom:60px;display:flex;flex-direction:column;gap:6px;z-index:50}.toast{padding:8px 12px;border-radius:6px;font-size:12px;color:#fff;box-shadow:0 2px 8px #00000026;cursor:pointer;min-width:180px}.toast.success{background:#1f883d}.toast.error{background:#cf222e}.toast.info{background:#0969da}.modal-backdrop{position:fixed;inset:0;background:#0006;z-index:60;display:flex;align-items:center;justify-content:center}.modal{background:#fff;border-radius:8px;padding:20px;max-width:520px;width:92%}.modal h2{margin-top:0}.modal table{width:100%;font-size:12px}.modal kbd{background:#f6f8fa;border:1px solid #d0d7de;border-radius:3px;padding:1px 6px;font-family:ui-monospace,Menlo,monospace;font-size:11px}.group{background:#fff;border:1px solid #d0d7de;border-radius:8px;margin-bottom:10px;overflow:hidden}.group-header{display:flex;align-items:center;gap:10px;padding:10px 14px;cursor:pointer;background:#f6f8fa;border-bottom:1px solid #eaeef2}.group-header h3{margin:0;font-size:13px;flex:1;text-transform:capitalize}.group-header .count{font-size:11px;color:#57606a}.group-body{display:none}.group.open .group-body{display:block}.op-row{display:flex;align-items:center;gap:10px;padding:8px 14px;border-bottom:1px solid #eaeef2;cursor:pointer}.op-row:last-child{border-bottom:none}.op-row:hover{background:#f6f8fa}.op-row.focused{background:#ddf4ff}.op-row .path{flex:1;font-family:ui-monospace,Menlo,monospace;font-size:12px}.op-row .path .no-oas-pill{margin-left:6px;color:#6e7781;font-size:10px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-weight:500;letter-spacing:.02em}.op-row .summary{color:#57606a;font-size:11px;max-width:380px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.op-row .dot{width:10px;height:10px;border-radius:50%;background:transparent;border:1px solid #d0d7de}.op-row .dot.active{border-color:transparent}.method-badge{display:inline-block;min-width:56px;text-align:center;padding:3px 8px;border-radius:4px;color:#fff;font-weight:700;font-size:11px;font-family:ui-monospace,Menlo,monospace}.op-card{padding:0;border-top:1px solid #eaeef2}.spec-zone{background:#fff;padding:14px 18px;border-bottom:1px solid #eaeef2}.spec-zone h4{margin:0 0 6px;font-size:12px;color:#57606a;text-transform:uppercase;letter-spacing:.5px}.spec-zone .desc{font-size:12px;color:#1f2328}.spec-zone table{width:100%;border-collapse:collapse;font-size:12px;margin-top:4px}.spec-zone th,.spec-zone td{text-align:left;padding:4px 6px;border-bottom:1px solid #eaeef2}.spec-zone th{color:#57606a;font-weight:600}.mock-zone{background:#fff8c5;border-left:4px solid #d4a72c;padding:14px 18px}.mock-zone h4{margin:0 0 8px;font-size:12px;color:#7d4e00;text-transform:uppercase;letter-spacing:.5px}.mock-zone .row{display:grid;grid-template-columns:160px 1fr;gap:10px;align-items:center;margin-bottom:8px}.mock-zone label{font-size:12px;color:#4a3500;font-weight:500}.mock-zone input[type=text],.mock-zone input[type=number],.mock-zone select,.mock-zone textarea{width:100%;padding:4px 8px;border:1px solid #d4a72c;border-radius:4px;background:#fff}.mock-zone textarea{font-family:ui-monospace,Menlo,monospace;min-height:70px}.mock-zone .radio-group{display:flex;gap:12px;flex-wrap:wrap}.mock-zone .radio-group label{display:inline-flex;align-items:center;gap:4px;font-weight:400}.mock-zone .actions{display:flex;gap:8px;margin-top:10px}.btn{padding:6px 12px;border-radius:6px;border:1px solid #d0d7de;background:#fff;color:#1f2328}.btn.primary{background:#1f883d;color:#fff;border-color:transparent;font-weight:600}.btn.danger{background:#cf222e;color:#fff;border-color:transparent}.btn.ghost{background:transparent}.try-it{padding:14px 18px;border-top:1px solid #eaeef2;background:#f6f8fa}.try-it summary{cursor:pointer;font-weight:600;font-size:13px}.try-it .field{margin:6px 0}.try-it .response{margin-top:8px;background:#fff;border:1px solid #d0d7de;border-radius:6px;padding:10px;font-family:ui-monospace,Menlo,monospace;font-size:12px;max-height:320px;overflow:auto}.schema-tree{font-family:ui-monospace,Menlo,monospace;font-size:12px}.schema-tree details{margin-left:12px}.schema-tree summary{cursor:pointer}.schema-tree .key{color:#6f42c1}.schema-tree .type{color:#1f883d;font-style:italic;margin-left:6px}.schema-tree .req{color:#cf222e}.override-item{display:flex;align-items:center;gap:8px;padding:8px;border-bottom:1px solid #eaeef2;cursor:pointer}.override-item:hover{background:#f6f8fa}.override-item .info{flex:1;font-size:12px}.override-item .info .route{font-family:ui-monospace,Menlo,monospace}.override-item .dot{width:10px;height:10px;border-radius:50%}.global-active-hint{display:flex;align-items:center;gap:8px;padding:6px 10px;margin-bottom:8px;background:#f3e8ff;border-left:3px solid #6f42c1;font-size:12px;color:#4a2370;border-radius:4px}.global-active-hint .btn{padding:2px 8px;font-size:11px}.scope-badge{display:inline-block;padding:2px 6px;border-radius:4px;font-size:10px;font-weight:700;letter-spacing:.5px;text-transform:uppercase;color:#fff}.scope-badge.scope-global{background:#6f42c1}.override-item.is-global{background:#faf5ff}.override-item.is-global:hover{background:#f3e8ff}.top-bar .global-override-btn{display:inline-flex;align-items:center;gap:6px}.top-bar .global-badge{background:#6f42c1}.global-override-modal{max-width:560px}.global-override-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}.global-override-header h2{margin:0;flex:1;font-size:16px}.global-override-header .close-x{font-size:18px;line-height:1;padding:2px 8px;color:#57606a;border:1px solid #d0d7de;background:#fff}.global-override-desc{font-size:12px;color:#57606a;margin:0 0 12px}.global-override-modal .row{display:grid;grid-template-columns:140px 1fr;gap:10px;align-items:center;margin-bottom:8px}.global-override-modal label{font-size:12px;color:#1f2328;font-weight:500}.global-override-modal input[type=text],.global-override-modal input[type=number],.global-override-modal select,.global-override-modal textarea{width:100%;padding:4px 8px;border:1px solid #d0d7de;border-radius:4px;background:#fff}.global-override-modal textarea{font-family:ui-monospace,Menlo,monospace;min-height:70px}.global-override-modal .actions{display:flex;gap:8px;margin-top:12px}.global-override-modal .method-checkboxes{display:flex;flex-wrap:wrap;gap:6px 12px;align-items:center}.global-override-modal .method-checkbox{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:500;color:#1f2328;cursor:pointer}.global-override-modal .method-checkbox input[type=checkbox]{width:auto;margin:0}.global-override-list{margin-top:16px;padding-top:12px;border-top:1px solid #eaeef2}.global-override-list h3{margin:0 0 8px;font-size:12px;color:#57606a;text-transform:uppercase;letter-spacing:.5px}
|
package/dist/admin/ui.html
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<title>Mock Injection</title>
|
|
7
|
-
<script type="module" crossorigin src="./ui.js"></script>
|
|
8
|
-
<link rel="stylesheet" crossorigin href="./ui.css">
|
|
9
|
-
</head>
|
|
10
|
-
<body>
|
|
11
|
-
<div id="root"></div>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
package/dist/admin/ui.js
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
var R=Object.defineProperty;var F=(a,i,e)=>i in a?R(a,i,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[i]=e;var l=(a,i,e)=>F(a,typeof i!="symbol"?i+"":i,e);(function(){const i=document.createElement("link").relList;if(i&&i.supports&&i.supports("modulepreload"))return;for(const s of document.querySelectorAll('link[rel="modulepreload"]'))t(s);new MutationObserver(s=>{for(const n of s)if(n.type==="childList")for(const o of n.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&t(o)}).observe(document,{childList:!0,subtree:!0});function e(s){const n={};return s.integrity&&(n.integrity=s.integrity),s.referrerPolicy&&(n.referrerPolicy=s.referrerPolicy),s.crossOrigin==="use-credentials"?n.credentials="include":s.crossOrigin==="anonymous"?n.credentials="omit":n.credentials="same-origin",n}function t(s){if(s.ep)return;s.ep=!0;const n=e(s);fetch(s.href,n)}})();const U=()=>({search:"",methods:new Set(["GET","POST","PUT","DELETE","PATCH"]),activeOnly:!1,rightPanelOpen:!1,helpOpen:!1,globalOpen:!1,expandedKey:null,focusedKey:null,routes:[],doc:null,overrides:[],scenarios:[],loading:!0}),L=(a,i)=>`${a.toUpperCase()} ${i}`,B=(...a)=>{let i=U();const e=new Set;return{get:()=>i,set:o=>{i={...i,...o};for(const r of e)r(i)},subscribe:o=>(e.add(o),()=>{e.delete(o)})}},G=(a,i)=>{const e=[];for(const t of a){const s=i?.paths?.[t.path]?.[t.method.toLowerCase()]??{},n=s.tags?.[0],o=t.path.split("/").filter(Boolean)[0]??"other",r=n??o;e.push({method:t.method.toUpperCase(),path:t.path,op:{...s,...t.summary&&!s.summary?{summary:t.summary}:{}},groupKey:r.toLowerCase(),groupLabel:r,source:t.source??"openapi"})}return e},Q=(a,i)=>{const e=new Map;for(const t of a){const s=e.get(t.groupKey)??{key:t.groupKey,label:t.groupLabel,ops:[]};s.ops.push(t),e.set(t.groupKey,s)}for(const t of e.values())t.ops.sort((s,n)=>s.path===n.path?s.method.localeCompare(n.method):s.path.localeCompare(n.path));return[...e.values()].sort((t,s)=>t.label.localeCompare(s.label))},z=a=>{const i=new Map;for(const e of a){if(e.matcher.kind!=="exact")continue;const t=L(e.matcher.method,e.matcher.path);i.set(t,(i.get(t)??0)+1)}return i},m=typeof window<"u"&&window.__DATABEHAVE_BASE__||"";let k;const v=async a=>{const i=await a.text(),e=i?JSON.parse(i):void 0;if(!a.ok){const t=typeof e=="object"&&e!==null?e.error:void 0;throw new Error(t??`HTTP ${a.status}`)}return e},f={async listOverrides(){const a=await fetch(m+"/overrides");return(await v(a)).overrides??[]},async addOverride(a){const i=await fetch(m+"/overrides",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(a)});return(await v(i)).override},async removeOverride(a){await v(await fetch(m+"/overrides/"+encodeURIComponent(a),{method:"DELETE"}))},async clearOverrides(){await v(await fetch(m+"/overrides",{method:"DELETE"}))},async listRoutes(){const a=await fetch(m+"/openapi-routes");return(await v(a)).routes??[]},async fetchOpenApi(){if(k!==void 0)return k;try{const a=await fetch(m+"/openapi.json");return a.ok?(k=await a.json(),k):(k=null,null)}catch{return k=null,null}},async listScenarios(){const a=await fetch(m+"/scenarios");return a.status===404?[]:(await v(a)).scenarios??[]},async saveScenario(a){await v(await fetch(m+"/scenarios",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:a})}))},async loadScenario(a){await v(await fetch(m+"/scenarios/"+encodeURIComponent(a)+"/load",{method:"POST"}))},async deleteScenario(a){await v(await fetch(m+"/scenarios/"+encodeURIComponent(a),{method:"DELETE"}))}},c=B();let J=0;const T=new Set;let g=[];const V=a=>(T.add(a),()=>{T.delete(a)}),O=()=>{for(const a of T)a(g)},b=(a,i)=>{const e=++J;g=[...g,{id:e,kind:a,message:i}],O(),setTimeout(()=>{g=g.filter(t=>t.id!==e),O()},3e3)},X=a=>{g=g.filter(i=>i.id!==a),O()},Z=()=>g,w=async()=>{try{c.set({overrides:await f.listOverrides()})}catch{}},q=async()=>{try{c.set({scenarios:await f.listScenarios()})}catch{}},D=async(a,i,e)=>{const t=await f.addOverride({matcher:a,mode:i,description:e});return b("success","Override applied"),await w(),t},E=async a=>{await f.removeOverride(a),b("success","Override removed"),await w()},W=async()=>{if(window.confirm("Clear ALL sticky overrides?"))try{await f.clearOverrides(),b("success","Cleared"),await w()}catch(a){b("error",a instanceof Error?a.message:String(a))}},Y=async a=>{try{await f.saveScenario(a),b("success","Saved: "+a),await q()}catch(i){b("error",i instanceof Error?i.message:String(i))}},ee=async a=>{try{await f.loadScenario(a),b("success","Loaded: "+a),await w()}catch(i){b("error",i instanceof Error?i.message:String(i))}},te=async a=>{try{await f.deleteScenario(a),b("success","Deleted: "+a),await q()}catch(i){b("error",i instanceof Error?i.message:String(i))}},C=()=>c.set({globalOpen:!0,rightPanelOpen:!1}),se={"&":"&","<":"<",">":">",'"':""","'":"'"},h=a=>a==null?"":String(a).replace(/[&<>"']/g,i=>se[i]??i);class ae extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub");l(this,"_open",!1)}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){const e=c.get().scenarios;this.innerHTML=`
|
|
2
|
-
<div style="position:relative">
|
|
3
|
-
<button class="ghost" type="button" data-toggle>Scenarios (${e.length})</button>
|
|
4
|
-
${this._open?`
|
|
5
|
-
<div style="position:absolute;top:110%;right:0;background:#fff;color:#1f2328;border:1px solid #d0d7de;border-radius:6px;padding:8px;min-width:240px;z-index:50;box-shadow:0 2px 8px rgba(0,0,0,0.1)">
|
|
6
|
-
<button class="btn" style="width:100%;margin-bottom:8px" type="button" data-save>Save current as…</button>
|
|
7
|
-
${e.map(t=>`
|
|
8
|
-
<div style="display:flex;gap:4px;align-items:center;padding:4px 0;border-top:1px solid #eaeef2">
|
|
9
|
-
<span style="flex:1;font-size:12px">${h(t.name)} <small>(${t.count})</small></span>
|
|
10
|
-
<button class="btn" type="button" data-load="${h(t.name)}">Load</button>
|
|
11
|
-
<button class="btn danger" type="button" data-delete="${h(t.name)}">Delete</button>
|
|
12
|
-
</div>`).join("")}
|
|
13
|
-
</div>`:""}
|
|
14
|
-
</div>`,this.querySelector("[data-toggle]")?.addEventListener("click",()=>{this._open=!this._open,this._render()}),this.querySelector("[data-save]")?.addEventListener("click",()=>{const t=window.prompt("Scenario name (a-z, 0-9, _ -):");t&&Y(t),this._open=!1,this._render()});for(const t of this.querySelectorAll("[data-load]"))t.addEventListener("click",()=>{ee(t.dataset.load??""),this._open=!1,this._render()});for(const t of this.querySelectorAll("[data-delete]"))t.addEventListener("click",()=>{te(t.dataset.delete??"")})}}customElements.define("dbh-scenarios-menu",ae);class ne extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){const e=c.get(),t=e.overrides.length,s=e.overrides.filter(o=>o.matcher.kind==="global").length,n=typeof window<"u"?window.location.origin:"";this.innerHTML=`
|
|
15
|
-
<div class="top-bar">
|
|
16
|
-
<h1>Mock Injection</h1>
|
|
17
|
-
<span class="url">${n}</span>
|
|
18
|
-
<span class="badge">${t}</span>
|
|
19
|
-
<button class="ghost" type="button" data-clear>Clear all</button>
|
|
20
|
-
<button class="ghost global-override-btn" type="button" data-global title="Global override">
|
|
21
|
-
Global override${s>0?`<span class="badge global-badge">${s}</span>`:""}
|
|
22
|
-
</button>
|
|
23
|
-
<dbh-scenarios-menu></dbh-scenarios-menu>
|
|
24
|
-
<button class="ghost" type="button" data-help title="?">?</button>
|
|
25
|
-
<button class="ghost" type="button" data-panel>Active overrides</button>
|
|
26
|
-
</div>`,this.querySelector("[data-clear]")?.addEventListener("click",()=>{W()}),this.querySelector("[data-global]")?.addEventListener("click",()=>C()),this.querySelector("[data-help]")?.addEventListener("click",()=>c.set({helpOpen:!0})),this.querySelector("[data-panel]")?.addEventListener("click",()=>c.set({rightPanelOpen:!c.get().rightPanelOpen}))}}customElements.define("dbh-top-bar",ne);const ie=["GET","POST","PUT","DELETE","PATCH"];class oe extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub");l(this,"_rendered",!1);l(this,"searchInput")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._sync())}disconnectedCallback(){this._unsub?.()}_render(){const e=c.get();this.innerHTML=`<div class="filter-bar"><input type="text" placeholder="Search path or summary…" value="${re(e.search)}" data-search />`+ie.map(t=>`<label><input type="checkbox" data-method="${t}"${e.methods.has(t)?" checked":""}/> ${t}</label>`).join("")+`<label><input type="checkbox" data-active${e.activeOnly?" checked":""}/> Active overrides only</label></div>`,this.searchInput=this.querySelector("[data-search]")??void 0,this.searchInput?.addEventListener("input",t=>{const s=t.target.value;c.set({search:s})});for(const t of this.querySelectorAll("[data-method]"))t.addEventListener("change",()=>{const s=new Set(c.get().methods),n=t.dataset.method??"";t.checked?s.add(n):s.delete(n),c.set({methods:s})});this.querySelector("[data-active]")?.addEventListener("change",t=>{c.set({activeOnly:t.target.checked})}),this._rendered=!0}_sync(){if(!this._rendered)return;const e=c.get();this.searchInput&&this.searchInput.value!==e.search&&(this.searchInput.value=e.search);for(const s of this.querySelectorAll("[data-method]"))s.checked=e.methods.has(s.dataset.method??"");const t=this.querySelector("[data-active]");t&&(t.checked=e.activeOnly)}}const re=a=>a.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<");customElements.define("dbh-filter-bar",oe);const de={GET:"#1f883d",POST:"#0969da",PUT:"#bf8700",DELETE:"#cf222e",PATCH:"#1f6feb"},M=a=>a==="http-status"||a==="business-failure"||a==="destroy"?"#cf222e":a==="delay"||a==="hang"?"#bf8700":"#0969da";class le extends HTMLElement{static get observedAttributes(){return["method"]}connectedCallback(){this._render()}attributeChangedCallback(){this.isConnected&&this._render()}_render(){const i=(this.getAttribute("method")??"").toUpperCase(),e=de[i]??"#57606a";this.innerHTML=`<span class="method-badge" style="background:${e}">${i}</span>`}}customElements.define("dbh-method-badge",le);class ce extends HTMLElement{setRow(i){const{op:e,override:t,expanded:s,focused:n}=i,o=t?M(t.mode.kind):"transparent",r=e.source==="handler";this.innerHTML=`
|
|
27
|
-
<div class="op-row${n?" focused":""}${s?" expanded":""}" data-key="${h(e.method+" "+e.path)}">
|
|
28
|
-
<dbh-method-badge method="${h(e.method)}"></dbh-method-badge>
|
|
29
|
-
<span class="path">${h(e.path)}${r?' <span class="no-oas-pill" title="No OpenAPI spec for this route">(no OAS)</span>':""}</span>
|
|
30
|
-
<span class="summary">${h(e.op.summary??"")}</span>
|
|
31
|
-
<span class="dot${t?" active":""}" style="${t?`background:${o}`:""}" title="${h(t?.mode.kind??"")}"></span>
|
|
32
|
-
</div>`,this.querySelector(".op-row")?.addEventListener("click",()=>{const p=e.method+" "+e.path,u=c.get().expandedKey;c.set({expandedKey:u===p?null:p})})}}customElements.define("dbh-operation-row",ce);const he=a=>a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""),ue=a=>a?a.split(/\n{2,}/).map(e=>`<p>${he(e.trim()).replace(/`([^`]+)`/g,(o,r)=>`<code>${r}</code>`).replace(/\n/g,"<br/>")}</p>`).join(""):"",A={string:"string",integer:0,number:0,boolean:!0,null:null},_=(a,i,e=0,t=new Set)=>{if(!a||e>8)return null;if(a.example!==void 0)return a.example;if(a.default!==void 0)return a.default;if(a.enum&&a.enum.length>0)return a.enum[0];if(a.$ref){if(t.has(a.$ref))return null;const s=new Set(t);s.add(a.$ref);const n=a.$ref.replace(/^#\/components\/schemas\//,""),o=i?.components?.schemas?.[n];return _(o,i,e+1,s)}if(a.oneOf?.[0])return _(a.oneOf[0],i,e+1,t);if(a.anyOf?.[0])return _(a.anyOf[0],i,e+1,t);if(a.allOf&&a.allOf.length>0){const s={};for(const n of a.allOf){const o=_(n,i,e+1,t);o&&typeof o=="object"&&!Array.isArray(o)&&Object.assign(s,o)}return s}if(a.type==="object"||a.properties){const s={},n=a.properties??{};for(const[o,r]of Object.entries(n))s[o]=_(r,i,e+1,t);return s}return a.type==="array"?[_(a.items,i,e+1,t)]:typeof a.type=="string"&&a.type in A?A[a.type]:null},pe=(a,i)=>{const e=a.replace(/^#\/components\/schemas\//,"");return i?.components?.schemas?.[e]},P=a=>a?a.$ref?a.$ref.split("/").pop()??"ref":a.type==="array"?`${P(a.items)}[]`:a.type??(a.properties?"object":"any"):"any",x=(a,i,e=0,t,s)=>{if(!a)return'<span class="type">any</span>';if(e>6)return'<span class="type">…</span>';const n=a.$ref?pe(a.$ref,i)??a:a,o=t?`<span class="key">${h(t)}${s?'<span class="req">*</span>':""}: </span>`:"",r=`<span class="type">${h(P(n))}</span>`,d=e<2?" open":"";return n.type==="object"||n.properties?`<details${d}><summary>${o}${r}</summary>`+Object.entries(n.properties??{}).map(([p,u])=>x(u,i,e+1,p,n.required?.includes(p))).join("")+"</details>":n.type==="array"?`<details${d}><summary>${o}${r}</summary>`+x(n.items,i,e+1)+"</details>":`<div style="margin-left:${e?14:0}px">${o}${r}</div>`};class be extends HTMLElement{constructor(){super(...arguments);l(this,"_op",null);l(this,"_doc",null)}setOp(e,t){this._op=e,this._doc=t,this._render()}_render(){const e=this._op;if(!e){this.innerHTML="";return}if(e.source==="handler"){this.innerHTML=`
|
|
33
|
-
<div class="spec-zone">
|
|
34
|
-
<h4>Spec</h4>
|
|
35
|
-
<div class="desc" style="color:#57606a;font-style:italic">
|
|
36
|
-
No OpenAPI spec for this route. Inject controls and Try-it-out still
|
|
37
|
-
work; request/response schemas are unavailable.
|
|
38
|
-
</div>
|
|
39
|
-
</div>`;return}const t=this._doc,s=e.op.requestBody?.content?.["application/json"],n=s?.schema,o=s?s.example??_(n,t):null,r=o!=null?JSON.stringify(o,null,2):"",d=e.op.parameters??[],p=e.op.responses??{};this.innerHTML=`
|
|
40
|
-
<div class="spec-zone">
|
|
41
|
-
<h4>Spec</h4>
|
|
42
|
-
${e.op.description?`<div class="desc">${ue(e.op.description)}</div>`:""}
|
|
43
|
-
${d.length>0?`
|
|
44
|
-
<div style="margin-top:8px">
|
|
45
|
-
<h4>Parameters</h4>
|
|
46
|
-
<table>
|
|
47
|
-
<thead><tr><th>name</th><th>in</th><th>type</th><th>required</th><th>description</th></tr></thead>
|
|
48
|
-
<tbody>
|
|
49
|
-
${d.map(u=>`
|
|
50
|
-
<tr>
|
|
51
|
-
<td><code>${h(u.name)}</code></td>
|
|
52
|
-
<td>${h(u.in)}</td>
|
|
53
|
-
<td>${h(u.schema?.type??"any")}</td>
|
|
54
|
-
<td>${u.required?"yes":""}</td>
|
|
55
|
-
<td>${h(u.description??"")}</td>
|
|
56
|
-
</tr>`).join("")}
|
|
57
|
-
</tbody>
|
|
58
|
-
</table>
|
|
59
|
-
</div>`:""}
|
|
60
|
-
${n?`
|
|
61
|
-
<div style="margin-top:8px">
|
|
62
|
-
<h4>Request body</h4>
|
|
63
|
-
<div class="schema-tree">${x(n,t)}</div>
|
|
64
|
-
${r?'<button class="btn ghost" style="margin-top:6px" type="button" data-copy>Copy as JSON example</button>':""}
|
|
65
|
-
</div>`:""}
|
|
66
|
-
${Object.keys(p).length>0?`
|
|
67
|
-
<div style="margin-top:8px">
|
|
68
|
-
<h4>Responses</h4>
|
|
69
|
-
${Object.entries(p).map(([u,y])=>{const S=y.content?.["application/json"]?.schema;return`
|
|
70
|
-
<details style="margin-top:4px">
|
|
71
|
-
<summary><strong>${h(u)}</strong> — ${h(y.description??"")}</summary>
|
|
72
|
-
${S?`<div class="schema-tree">${x(S,t)}</div>`:""}
|
|
73
|
-
</details>`}).join("")}
|
|
74
|
-
</div>`:""}
|
|
75
|
-
</div>`,this.querySelector("[data-copy]")?.addEventListener("click",()=>{navigator.clipboard?.writeText(r),b("info","Copied")})}}customElements.define("dbh-spec-zone",be);const K=a=>{const i=new TextEncoder().encode(a);let e="";for(const t of i)e+=String.fromCharCode(t);return btoa(e)},N=a=>{for(let i=0;i<a.length;i++)if(a.charCodeAt(i)>127)return!1;return!0},me=["http-status","business-failure","custom-body","empty-body","malformed-json","delay","hang","destroy"];class fe extends HTMLElement{constructor(){super(...arguments);l(this,"_op",null);l(this,"_existing");l(this,"_globalActive",!1);l(this,"headerOverride",null);l(this,"_scope","exact");l(this,"_kind","http-status");l(this,"_status",500);l(this,"_message","");l(this,"_body","{}");l(this,"_contentType","application/json");l(this,"_ms",1e3);l(this,"_description","")}setOp(e){const t=this._existing?.id===e.existing?.id&&this._op?.path===e.op.path&&this._op?.method===e.op.method;this._op=e.op,this._existing=e.existing,this._globalActive=e.globalActive,t||this._syncFromExisting(),this._render()}_syncFromExisting(){const e=this._existing;if(!e){this._scope="exact",this._kind="http-status",this._status=500,this._message="",this._body="{}",this._contentType="application/json",this._ms=1e3,this._description="";return}(e.matcher.kind==="exact"||e.matcher.kind==="path")&&(this._scope=e.matcher.kind),this._kind=e.mode.kind,this._description=e.description??"";const t=e.mode;t.kind==="http-status"?this._status=t.status:t.kind==="business-failure"?this._message=t.message:t.kind==="custom-body"?(this._status=t.status??200,this._body=JSON.stringify(t.body,null,2),this._contentType=t.contentType??"application/json"):t.kind==="empty-body"?this._status=t.status??204:t.kind==="delay"&&(this._ms=t.ms)}_renderFieldsHtml(){const e=this._kind;return e==="http-status"||e==="empty-body"||e==="malformed-json"?`<div class="row"><label>Status</label><input type="number" data-field="status" value="${this._status}" /></div>`:e==="business-failure"?`<div class="row"><label>Message</label><input type="text" data-field="message" value="${h(this._message)}" placeholder="Optional message returned with the failure" /></div>`:e==="custom-body"?`
|
|
76
|
-
<div class="row"><label>Status</label><input type="number" data-field="status" value="${this._status}" /></div>
|
|
77
|
-
<div class="row"><label>Content-Type</label><input type="text" data-field="contentType" value="${h(this._contentType)}" /></div>
|
|
78
|
-
<div class="row"><label>Body</label><textarea data-field="body">${h(this._body)}</textarea></div>`:e==="delay"?`<div class="row"><label>Delay (ms)</label><input type="number" data-field="ms" value="${this._ms}" /></div>`:""}_render(){if(!this._op){this.innerHTML="";return}const e=this._existing;this.innerHTML=`
|
|
79
|
-
<div class="mock-zone">
|
|
80
|
-
<h4>Inject error</h4>
|
|
81
|
-
${this._globalActive?`
|
|
82
|
-
<div class="global-active-hint" role="note">
|
|
83
|
-
<span>A global override is active. Manage it from the top bar.</span>
|
|
84
|
-
<button type="button" class="btn ghost" data-open-global>Open</button>
|
|
85
|
-
</div>`:""}
|
|
86
|
-
<div class="row">
|
|
87
|
-
<label>Scope</label>
|
|
88
|
-
<div class="radio-group">
|
|
89
|
-
<label><input type="radio" name="scope" data-scope="exact"${this._scope==="exact"?" checked":""}/> Exact (METHOD + this path)</label>
|
|
90
|
-
<label><input type="radio" name="scope" data-scope="path"${this._scope==="path"?" checked":""}/> Path (any method on this path)</label>
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
<div class="row">
|
|
94
|
-
<label>Mode</label>
|
|
95
|
-
<select data-mode>${me.map(t=>`<option value="${t}"${t===this._kind?" selected":""}>${t}</option>`).join("")}</select>
|
|
96
|
-
</div>
|
|
97
|
-
<div data-fields>${this._renderFieldsHtml()}</div>
|
|
98
|
-
<div class="row">
|
|
99
|
-
<label>Description</label>
|
|
100
|
-
<input type="text" data-field="description" value="${h(this._description)}" />
|
|
101
|
-
</div>
|
|
102
|
-
<div class="actions">
|
|
103
|
-
<button class="btn primary" type="button" data-submit>${e?"Update":"Apply"}</button>
|
|
104
|
-
${e?`<button class="btn danger" type="button" data-remove="${h(e.id)}">Remove</button>`:""}
|
|
105
|
-
</div>
|
|
106
|
-
</div>`,this._wire()}_wire(){this.querySelector("[data-open-global]")?.addEventListener("click",()=>C());for(const s of this.querySelectorAll("[data-scope]"))s.addEventListener("change",()=>{s.checked&&(this._scope=s.dataset.scope)});const e=this.querySelector("[data-mode]");e?.addEventListener("change",()=>{this._kind=e.value;const s=this.querySelector("[data-fields]");s&&(s.innerHTML=this._renderFieldsHtml(),this._wireFields())}),this._wireFields(),this.querySelector("[data-submit]")?.addEventListener("click",()=>this._submit());const t=this.querySelector("[data-remove]");t?.addEventListener("click",()=>{E(t.dataset.remove??"")})}_wireFields(){const e=this.querySelector('[data-field="description"]');e?.addEventListener("input",()=>{this._description=e.value});for(const t of this.querySelectorAll("[data-fields] [data-field]"))t.addEventListener("input",()=>{const s=t.dataset.field;s==="status"?this._status=Number(t.value):s==="ms"?this._ms=Number(t.value):s==="message"?this._message=t.value:s==="body"?this._body=t.value:s==="contentType"&&(this._contentType=t.value)})}_submit(){const e=this._op;if(!e)return;const t=this._scope==="exact"?{kind:"exact",method:e.method,path:e.path}:{kind:"path",path:e.path};let s,n=null;const o=this._kind;switch(o){case"http-status":s={kind:o,status:this._status};break;case"business-failure":s={kind:o,message:this._message},N(this._message)||(n={name:"x-mock-business-failure-b64",value:K(this._message)});break;case"custom-body":{let r;try{r=JSON.parse(this._body)}catch{r=this._body}s={kind:o,status:this._status,body:r,contentType:this._contentType};break}case"empty-body":s={kind:o,status:this._status};break;case"malformed-json":s={kind:o,status:this._status};break;case"delay":s={kind:o,ms:this._ms};break;case"hang":s={kind:o};break;case"destroy":s={kind:o};break}this.headerOverride=n,this.dispatchEvent(new CustomEvent("dbh-header-override",{detail:n,bubbles:!0})),(async()=>{const r=this._existing;if(r&&t.kind===r.matcher.kind)try{await E(r.id)}catch{}try{await D(t,s,this._description||void 0)}catch(d){b("error",d instanceof Error?d.message:String(d))}})()}}customElements.define("dbh-inject-error-panel",fe);const ye=(a,i)=>{const e=a.op.requestBody?.content?.["application/json"]?.schema;return e?JSON.stringify(_(e,i),null,2):""};class ve extends HTMLElement{constructor(){super(...arguments);l(this,"_op",null);l(this,"_doc",null);l(this,"extraHeader",null);l(this,"_pvals",{});l(this,"_freeQuery",[]);l(this,"_body","");l(this,"_busy",!1)}setOp(e,t){this._op=e,this._doc=t,this._pvals={};for(const s of e.op.parameters??[])this._pvals[s.name]=String(s.schema?.default??"");this._body=ye(e,t),this._freeQuery=e.source==="handler"?[{key:"",value:""}]:[],this._render()}_render(){const e=this._op;if(!e){this.innerHTML="";return}const t=e.op.parameters??[],s=e.method!=="GET"&&e.method!=="HEAD",n=e.source==="handler";this.innerHTML=`
|
|
107
|
-
<details class="try-it">
|
|
108
|
-
<summary>Try it out</summary>
|
|
109
|
-
${t.map(r=>`
|
|
110
|
-
<div class="field">
|
|
111
|
-
<label>${h(r.name)} <small>(${h(r.in)}${r.required?", required":""})</small></label>
|
|
112
|
-
<input type="text" data-pname="${h(r.name)}" value="${h(this._pvals[r.name]??"")}" />
|
|
113
|
-
</div>`).join("")}
|
|
114
|
-
${n?`
|
|
115
|
-
<div class="field">
|
|
116
|
-
<label>query params <small>(free-form)</small></label>
|
|
117
|
-
<div data-free-query>
|
|
118
|
-
${this._freeQuery.map((r,d)=>`
|
|
119
|
-
<div class="free-q-row" style="display:flex;gap:6px;margin-bottom:4px">
|
|
120
|
-
<input type="text" data-fq-key="${d}" placeholder="name" value="${h(r.key)}" style="flex:1" />
|
|
121
|
-
<input type="text" data-fq-val="${d}" placeholder="value" value="${h(r.value)}" style="flex:2" />
|
|
122
|
-
<button class="btn ghost" type="button" data-fq-rm="${d}" title="Remove">−</button>
|
|
123
|
-
</div>`).join("")}
|
|
124
|
-
<button class="btn ghost" type="button" data-fq-add>+ Add query param</button>
|
|
125
|
-
</div>
|
|
126
|
-
</div>`:""}
|
|
127
|
-
${s?`
|
|
128
|
-
<div class="field">
|
|
129
|
-
<label>body${n?" <small>(free-form)</small>":""}</label>
|
|
130
|
-
<textarea data-body style="width:100%;min-height:100px;font-family:ui-monospace,Menlo,monospace">${h(this._body)}</textarea>
|
|
131
|
-
</div>`:""}
|
|
132
|
-
<button class="btn primary" type="button" data-send${this._busy?" disabled":""}>${this._busy?"…":"Send"}</button>
|
|
133
|
-
<dbh-response-viewer></dbh-response-viewer>
|
|
134
|
-
</details>`;for(const r of this.querySelectorAll("[data-pname]"))r.addEventListener("input",()=>{this._pvals[r.dataset.pname??""]=r.value});for(const r of this.querySelectorAll("[data-fq-key]"))r.addEventListener("input",()=>{const d=Number(r.dataset.fqKey);this._freeQuery[d]&&(this._freeQuery[d].key=r.value)});for(const r of this.querySelectorAll("[data-fq-val]"))r.addEventListener("input",()=>{const d=Number(r.dataset.fqVal);this._freeQuery[d]&&(this._freeQuery[d].value=r.value)});for(const r of this.querySelectorAll("[data-fq-rm]"))r.addEventListener("click",()=>{const d=Number(r.dataset.fqRm);this._freeQuery.splice(d,1),this._freeQuery.length===0&&this._freeQuery.push({key:"",value:""}),this._render()});this.querySelector("[data-fq-add]")?.addEventListener("click",()=>{this._freeQuery.push({key:"",value:""}),this._render()});const o=this.querySelector("[data-body]");o?.addEventListener("input",()=>{this._body=o.value}),this.querySelector("[data-send]")?.addEventListener("click",()=>{this._send()})}_buildUrl(){const e=this._op;if(!e)return"";const t=e.op.parameters??[];let s=e.path;const n=new URLSearchParams;for(const r of t){const d=this._pvals[r.name];d&&(r.in==="path"?s=s.replace(`{${r.name}}`,encodeURIComponent(d)):r.in==="query"&&n.set(r.name,d))}for(const r of this._freeQuery)r.key&&n.append(r.key,r.value);const o=n.toString();return o?`${s}?${o}`:s}async _send(){const e=this._op;if(!e)return;this._busy=!0;const t=this.querySelector("dbh-response-viewer");t?.setResult(null);const s=this.querySelector("[data-send]");s&&(s.disabled=!0,s.textContent="…");const n=performance.now();try{const o={};for(const y of e.op.parameters??[])y.in==="header"&&this._pvals[y.name]&&(o[y.name]=this._pvals[y.name]);this.extraHeader&&(o[this.extraHeader.name]=this.extraHeader.value);const r={method:e.method,headers:o};e.method!=="GET"&&e.method!=="HEAD"&&this._body&&(o["content-type"]=o["content-type"]??"application/json",r.body=this._body);const d=await fetch(this._buildUrl(),r),p=await d.text(),u={status:d.status,headers:{},body:p,elapsedMs:Math.round(performance.now()-n)};d.headers.forEach((y,S)=>{u.headers[S]=y}),t?.setResult(u)}catch(o){t?.setResult({error:o instanceof Error?o.message:String(o)})}finally{this._busy=!1,s&&(s.disabled=!1,s.textContent="Send")}}}customElements.define("dbh-try-it-out",ve);const I=(a,i)=>{if(!a.methods||a.methods.length===0)return!0;const e=i.toUpperCase();return a.methods.some(t=>t.toUpperCase()===e)};class _e extends HTMLElement{constructor(){super(...arguments);l(this,"_spec");l(this,"_inject");l(this,"_try")}connectedCallback(){this.childElementCount===0&&(this.innerHTML=`
|
|
135
|
-
<div class="op-card">
|
|
136
|
-
<dbh-spec-zone></dbh-spec-zone>
|
|
137
|
-
<dbh-inject-error-panel></dbh-inject-error-panel>
|
|
138
|
-
<dbh-try-it-out></dbh-try-it-out>
|
|
139
|
-
</div>`,this._spec=this.querySelector("dbh-spec-zone"),this._inject=this.querySelector("dbh-inject-error-panel"),this._try=this.querySelector("dbh-try-it-out"),this._inject.addEventListener("dbh-header-override",e=>{this._try.extraHeader=e.detail}))}setData(e){this._spec||this.connectedCallback();const{op:t,doc:s,overrides:n}=e,o=n.find(d=>d.matcher.kind==="exact"&&d.matcher.method===t.method&&d.matcher.path===t.path),r=n.some(d=>d.matcher.kind==="global"&&I(d.matcher,t.method));this._spec.setOp(t,s),this._inject.setOp({op:t,existing:o,globalActive:r}),this._try.setOp(t,s)}}customElements.define("dbh-operation-card",_e);class ge extends HTMLElement{constructor(){super(...arguments);l(this,"_label","");l(this,"_ops",[]);l(this,"_doc",null);l(this,"_overrides",[]);l(this,"_expandedKey",null);l(this,"_focusedKey",null);l(this,"_open",!0)}setData(e){this._label=e.label,this._ops=e.ops,this._doc=e.doc,this._overrides=e.overrides,this._expandedKey=e.expandedKey,this._focusedKey=e.focusedKey,this._render()}_render(){const e=this._ops.reduce((s,n)=>s+(this._overrides.some(o=>o.matcher.kind==="exact"&&o.matcher.method===n.method&&o.matcher.path===n.path)?1:0),0);this.innerHTML=`
|
|
140
|
-
<div class="group${this._open?" open":""}">
|
|
141
|
-
<div class="group-header" data-header>
|
|
142
|
-
<h3>${h(this._label)}</h3>
|
|
143
|
-
<span class="count">${this._ops.length} ops${e?` · ${e} active`:""}</span>
|
|
144
|
-
<span>${this._open?"−":"+"}</span>
|
|
145
|
-
</div>
|
|
146
|
-
<div class="group-body" data-body></div>
|
|
147
|
-
</div>`,this.querySelector("[data-header]")?.addEventListener("click",()=>{this._open=!this._open,this._render()});const t=this.querySelector("[data-body]");if(t)for(const s of this._ops){const n=`${s.method} ${s.path}`,o=this._overrides.find(d=>d.matcher.kind==="exact"&&d.matcher.method===s.method&&d.matcher.path===s.path),r=document.createElement("dbh-operation-row");if(r.setRow({op:s,override:o,expanded:this._expandedKey===n,focused:this._focusedKey===n}),t.appendChild(r),this._expandedKey===n){const d=document.createElement("dbh-operation-card");t.appendChild(d),d.setData({op:s,doc:this._doc,overrides:this._overrides})}}}}customElements.define("dbh-tag-group",ge);const ke=a=>{if(a.matcher.kind==="exact")return`${a.matcher.method} ${a.matcher.path}`;if(a.matcher.kind==="path")return`* ${a.matcher.path}`;const i=a.matcher.methods;return i&&i.length>0?`${i.join("|")} (global)`:"* (global)"},H=a=>a.matcher.kind==="global"?0:a.matcher.kind==="path"?1:2;class $e extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){const e=c.get(),t=[...e.overrides].sort((s,n)=>H(s)-H(n));this.innerHTML=`
|
|
148
|
-
<aside class="right-panel${e.rightPanelOpen?" open":""}">
|
|
149
|
-
<header>
|
|
150
|
-
<h2>Active overrides (${e.overrides.length})</h2>
|
|
151
|
-
<button class="btn ghost" type="button" data-close>×</button>
|
|
152
|
-
</header>
|
|
153
|
-
<div class="list">
|
|
154
|
-
${e.overrides.length===0?'<p style="padding:12px;color:#57606a">No active overrides</p>':t.map(s=>`
|
|
155
|
-
<div class="override-item${s.matcher.kind==="global"?" is-global":""}" data-jump="${h(s.id)}">
|
|
156
|
-
<span class="dot" style="background:${M(s.mode.kind)}"></span>
|
|
157
|
-
${s.matcher.kind==="global"?'<span class="scope-badge scope-global">GLOBAL</span>':""}
|
|
158
|
-
<div class="info">
|
|
159
|
-
<div class="route">${h(ke(s))}</div>
|
|
160
|
-
<small>${h(s.mode.kind)}${s.description?` — ${h(s.description)}`:""}</small>
|
|
161
|
-
</div>
|
|
162
|
-
<button class="btn danger" type="button" data-remove="${h(s.id)}">Remove</button>
|
|
163
|
-
</div>`).join("")}
|
|
164
|
-
</div>
|
|
165
|
-
</aside>`,this.querySelector("[data-close]")?.addEventListener("click",()=>c.set({rightPanelOpen:!1}));for(const s of this.querySelectorAll("[data-jump]"))s.addEventListener("click",()=>{const n=s.dataset.jump??"",o=c.get().overrides.find(d=>d.id===n);if(!o)return;if(o.matcher.kind==="global"){C();return}if(o.matcher.kind!=="exact"){c.set({rightPanelOpen:!1});return}const r=L(o.matcher.method,o.matcher.path);c.set({expandedKey:r,focusedKey:r,rightPanelOpen:!1}),setTimeout(()=>{const d=Array.from(document.querySelectorAll("[data-key]")).find(p=>p.getAttribute("data-key")===r);d&&"scrollIntoView"in d&&d.scrollIntoView({behavior:"smooth",block:"center"})},50)});for(const s of this.querySelectorAll("[data-remove]"))s.addEventListener("click",n=>{n.stopPropagation(),E(s.dataset.remove??"")})}}customElements.define("dbh-active-overrides-panel",$e);class Ee extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){const t=c.get().overrides.length;if(t===0){this.innerHTML="";return}this.innerHTML=`<button class="pill-float" type="button">${t} Active overrides</button>`,this.querySelector("button")?.addEventListener("click",()=>c.set({rightPanelOpen:!0}))}}customElements.define("dbh-floating-pill",Ee);class we extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub")}connectedCallback(){this._render(Z()),this._unsub=V(e=>this._render(e))}disconnectedCallback(){this._unsub?.()}_render(e){this.innerHTML='<div class="toast-stack">'+e.map(t=>`<div class="toast ${h(t.kind)}" data-id="${t.id}">${h(t.message)}</div>`).join("")+"</div>";for(const t of this.querySelectorAll(".toast"))t.addEventListener("click",()=>{const s=Number(t.dataset.id);Number.isFinite(s)&&X(s)})}}customElements.define("dbh-toasts",we);class Se extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_render(){if(!c.get().helpOpen){this.innerHTML="";return}this.innerHTML=`
|
|
166
|
-
<div class="modal-backdrop">
|
|
167
|
-
<div class="modal">
|
|
168
|
-
<h2>Keyboard shortcuts</h2>
|
|
169
|
-
<table>
|
|
170
|
-
<tbody>
|
|
171
|
-
<tr><td><kbd>/</kbd> or <kbd>Ctrl/⌘ + K</kbd></td><td>Focus search</td></tr>
|
|
172
|
-
<tr><td><kbd>Esc</kbd></td><td>Close panel / blur search</td></tr>
|
|
173
|
-
<tr><td><kbd>?</kbd></td><td>Open this help</td></tr>
|
|
174
|
-
<tr><td><kbd>↑</kbd> / <kbd>↓</kbd></td><td>Navigate operation list</td></tr>
|
|
175
|
-
<tr><td><kbd>Enter</kbd></td><td>Expand / collapse selected</td></tr>
|
|
176
|
-
</tbody>
|
|
177
|
-
</table>
|
|
178
|
-
<div style="margin-top:12px;text-align:right">
|
|
179
|
-
<button class="btn" type="button" data-ok>OK</button>
|
|
180
|
-
</div>
|
|
181
|
-
</div>
|
|
182
|
-
</div>`;const t=()=>c.set({helpOpen:!1});this.querySelector(".modal-backdrop")?.addEventListener("click",t),this.querySelector(".modal")?.addEventListener("click",s=>s.stopPropagation()),this.querySelector("[data-ok]")?.addEventListener("click",t)}}customElements.define("dbh-help-modal",Se);const xe=["http-status","business-failure","custom-body","empty-body","malformed-json","delay","hang","destroy"],$=["GET","POST","PUT","PATCH","DELETE","HEAD","OPTIONS"];class Te extends HTMLElement{constructor(){super(...arguments);l(this,"_unsub");l(this,"_kind","http-status");l(this,"_status",503);l(this,"_message","");l(this,"_body","{}");l(this,"_contentType","application/json");l(this,"_ms",1e3);l(this,"_description","");l(this,"_methods",new Set($));l(this,"_lastExistingId")}connectedCallback(){this._render(),this._unsub=c.subscribe(()=>this._render())}disconnectedCallback(){this._unsub?.()}_globals(){return c.get().overrides.filter(e=>e.matcher.kind==="global")}_syncFromExisting(e,t){if(t&&!(e&&e.id===this._lastExistingId))if(this._lastExistingId=e?.id,e){this._kind=e.mode.kind,this._description=e.description??"";const s=e.matcher.kind==="global"?e.matcher.methods:void 0;this._methods=new Set(s&&s.length>0?s.map(o=>o.toUpperCase()):$);const n=e.mode;n.kind==="http-status"?this._status=n.status:n.kind==="business-failure"?this._message=n.message:n.kind==="custom-body"?(this._status=n.status??200,this._body=JSON.stringify(n.body,null,2),this._contentType=n.contentType??"application/json"):n.kind==="empty-body"?this._status=n.status??204:n.kind==="delay"&&(this._ms=n.ms)}else this._kind="http-status",this._status=503,this._message="",this._body="{}",this._contentType="application/json",this._ms=1e3,this._description="",this._methods=new Set($)}_renderFieldsHtml(){const e=this._kind;return e==="http-status"||e==="empty-body"||e==="malformed-json"?`<div class="row"><label>Status</label><input type="number" data-field="status" value="${this._status}" /></div>`:e==="business-failure"?`<div class="row"><label>Message</label><input type="text" data-field="message" value="${h(this._message)}" /></div>`:e==="custom-body"?`
|
|
183
|
-
<div class="row"><label>Status</label><input type="number" data-field="status" value="${this._status}" /></div>
|
|
184
|
-
<div class="row"><label>Content-Type</label><input type="text" data-field="contentType" value="${h(this._contentType)}" /></div>
|
|
185
|
-
<div class="row"><label>Body</label><textarea data-field="body">${h(this._body)}</textarea></div>`:e==="delay"?`<div class="row"><label>Delay (ms)</label><input type="number" data-field="ms" value="${this._ms}" /></div>`:""}_render(){const e=c.get().globalOpen;if(!e){this.innerHTML="",this._lastExistingId=void 0;return}const t=this._globals(),s=t[0];this._syncFromExisting(s,e),this.innerHTML=`
|
|
186
|
-
<div class="modal-backdrop">
|
|
187
|
-
<div class="modal global-override-modal">
|
|
188
|
-
<header class="global-override-header">
|
|
189
|
-
<h2>Global override</h2>
|
|
190
|
-
<button type="button" class="btn ghost close-x" data-close aria-label="Close">×</button>
|
|
191
|
-
</header>
|
|
192
|
-
<p class="global-override-desc">Applies to every route on this server. Use to simulate the backend being down, slow, etc.</p>
|
|
193
|
-
<div class="row">
|
|
194
|
-
<label>Mode</label>
|
|
195
|
-
<select data-mode>${xe.map(n=>`<option value="${n}"${n===this._kind?" selected":""}>${n}</option>`).join("")}</select>
|
|
196
|
-
</div>
|
|
197
|
-
<div class="row global-override-methods">
|
|
198
|
-
<label>Methods</label>
|
|
199
|
-
<div class="method-checkboxes">
|
|
200
|
-
${$.map(n=>`
|
|
201
|
-
<label class="method-checkbox">
|
|
202
|
-
<input type="checkbox" data-method="${n}"${this._methods.has(n)?" checked":""} />
|
|
203
|
-
<span>${n}</span>
|
|
204
|
-
</label>`).join("")}
|
|
205
|
-
</div>
|
|
206
|
-
</div>
|
|
207
|
-
<div data-fields>${this._renderFieldsHtml()}</div>
|
|
208
|
-
<div class="row">
|
|
209
|
-
<label>Description</label>
|
|
210
|
-
<input type="text" data-field="description" value="${h(this._description)}" />
|
|
211
|
-
</div>
|
|
212
|
-
<div class="actions">
|
|
213
|
-
<button type="button" class="btn primary" data-submit>${s?"Update":"Apply"}</button>
|
|
214
|
-
${s?`<button type="button" class="btn danger" data-remove="${h(s.id)}">Remove</button>`:""}
|
|
215
|
-
</div>
|
|
216
|
-
${t.length>0?`
|
|
217
|
-
<div class="global-override-list">
|
|
218
|
-
<h3>Active global overrides (${t.length})</h3>
|
|
219
|
-
${t.map(n=>{const o=n.matcher.kind==="global"?n.matcher.methods:void 0,r=o&&o.length>0?`${o.join("|")} (global)`:"* (global)";return`
|
|
220
|
-
<div class="override-item">
|
|
221
|
-
<span class="dot" style="background:${M(n.mode.kind)}"></span>
|
|
222
|
-
<div class="info">
|
|
223
|
-
<div class="route">${h(r)}</div>
|
|
224
|
-
<small>${h(n.mode.kind)}${n.description?` — ${h(n.description)}`:""}</small>
|
|
225
|
-
</div>
|
|
226
|
-
<button type="button" class="btn danger" data-remove="${h(n.id)}">Remove</button>
|
|
227
|
-
</div>`}).join("")}
|
|
228
|
-
</div>`:""}
|
|
229
|
-
</div>
|
|
230
|
-
</div>`,this._wire()}_wire(){const e=()=>c.set({globalOpen:!1});this.querySelector(".modal-backdrop")?.addEventListener("click",e),this.querySelector(".modal")?.addEventListener("click",s=>s.stopPropagation()),this.querySelector("[data-close]")?.addEventListener("click",e);const t=this.querySelector("[data-mode]");t?.addEventListener("change",()=>{this._kind=t.value;const s=this.querySelector("[data-fields]");s&&(s.innerHTML=this._renderFieldsHtml(),this._wireFields())}),this._wireFields();for(const s of this.querySelectorAll("[data-method]"))s.addEventListener("change",()=>{const n=s.dataset.method??"";s.checked?this._methods.add(n):this._methods.delete(n)});this.querySelector("[data-submit]")?.addEventListener("click",()=>this._submit());for(const s of this.querySelectorAll("[data-remove]"))s.addEventListener("click",()=>{E(s.dataset.remove??"")})}_wireFields(){const e=this.querySelector('[data-field="description"]');e?.addEventListener("input",()=>{this._description=e.value});for(const t of this.querySelectorAll("[data-fields] [data-field]"))t.addEventListener("input",()=>{const s=t.dataset.field;s==="status"?this._status=Number(t.value):s==="ms"?this._ms=Number(t.value):s==="message"?this._message=t.value:s==="body"?this._body=t.value:s==="contentType"&&(this._contentType=t.value)})}_submit(){let e;const t=this._kind;switch(t){case"http-status":e={kind:t,status:this._status};break;case"business-failure":e={kind:t,message:this._message},N(this._message)||K(this._message);break;case"custom-body":{let r;try{r=JSON.parse(this._body)}catch{r=this._body}e={kind:t,status:this._status,body:r,contentType:this._contentType};break}case"empty-body":e={kind:t,status:this._status};break;case"malformed-json":e={kind:t,status:this._status};break;case"delay":e={kind:t,ms:this._ms};break;case"hang":e={kind:t};break;case"destroy":e={kind:t};break}const s=$.length,n=$.filter(r=>this._methods.has(r));if(n.length===0){b("error","Select at least one HTTP method.");return}const o=n.length===s?{kind:"global"}:{kind:"global",methods:n};(async()=>{const r=this._globals()[0];if(r)try{await E(r.id)}catch{}try{await D(o,e,this._description||void 0)}catch(d){b("error",d instanceof Error?d.message:String(d))}})()}}customElements.define("dbh-global-override-panel",Te);const Oe=["GET","POST","PUT","DELETE","PATCH"];class Le extends HTMLElement{constructor(){super(...arguments);l(this,"_filter");l(this,"_main");l(this,"_unsub");l(this,"_debounce");l(this,"_debouncedSearch","");l(this,"_onKeyDown",e=>this._handleKey(e))}connectedCallback(){this.innerHTML=`
|
|
231
|
-
<div class="app">
|
|
232
|
-
<dbh-top-bar></dbh-top-bar>
|
|
233
|
-
<dbh-filter-bar></dbh-filter-bar>
|
|
234
|
-
<main class="main" data-main></main>
|
|
235
|
-
<dbh-active-overrides-panel></dbh-active-overrides-panel>
|
|
236
|
-
<dbh-floating-pill></dbh-floating-pill>
|
|
237
|
-
<dbh-toasts></dbh-toasts>
|
|
238
|
-
<dbh-help-modal></dbh-help-modal>
|
|
239
|
-
<dbh-global-override-panel></dbh-global-override-panel>
|
|
240
|
-
</div>`,this._filter=this.querySelector("dbh-filter-bar"),this._main=this.querySelector("[data-main]"),this._unsub=c.subscribe(()=>this._onStateChange()),window.addEventListener("keydown",this._onKeyDown),(async()=>{const[e,t]=await Promise.all([f.listRoutes(),f.fetchOpenApi()]);c.set({routes:e,doc:t??null,loading:!1}),await w(),await q(),this._renderList()})()}disconnectedCallback(){this._unsub?.(),window.removeEventListener("keydown",this._onKeyDown),this._debounce&&clearTimeout(this._debounce)}_onStateChange(){c.get().search!==this._debouncedSearch?(this._debounce&&clearTimeout(this._debounce),this._debounce=setTimeout(()=>{this._debouncedSearch=c.get().search,this._renderList()},150)):this._renderList()}_filtered(){const e=c.get(),t=G(e.routes,e.doc),s=this._debouncedSearch.trim().toLowerCase(),n=e.methods.size>0?e.methods:new Set(Oe);return t.filter(o=>!(!n.has(o.method)||s&&!o.path.toLowerCase().includes(s)&&!o.op.summary?.toLowerCase().includes(s)||e.activeOnly&&!e.overrides.some(d=>d.matcher.kind==="exact"&&d.matcher.method===o.method&&d.matcher.path===o.path||d.matcher.kind==="path"&&d.matcher.path===o.path||d.matcher.kind==="global"&&I(d.matcher,o.method))))}_renderList(){const e=c.get(),t=this._filtered(),s=Q(t,z(e.overrides));if(this._main.innerHTML="",s.length===0){const n=document.createElement("p");n.style.color="#57606a",n.textContent="No operations match these filters.",this._main.appendChild(n);return}for(const n of s){const o=document.createElement("dbh-tag-group");this._main.appendChild(o),o.setData({label:n.label,ops:n.ops,doc:e.doc,overrides:e.overrides,expandedKey:e.expandedKey,focusedKey:e.focusedKey})}}_handleKey(e){const t=e.target,s=!!t&&(t.tagName==="INPUT"||t.tagName==="TEXTAREA"||t.tagName==="SELECT"),n=this._filter?.searchInput,o=c.get();if(e.key==="/"&&!s)e.preventDefault(),n?.focus();else if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==="k")e.preventDefault(),n?.focus();else if(e.key==="?"&&!s)e.preventDefault(),c.set({helpOpen:!0});else if(e.key==="Escape")o.helpOpen?c.set({helpOpen:!1}):o.globalOpen?c.set({globalOpen:!1}):o.rightPanelOpen?c.set({rightPanelOpen:!1}):o.expandedKey?c.set({expandedKey:null}):n?.blur();else if((e.key==="ArrowDown"||e.key==="ArrowUp")&&document.activeElement===n){e.preventDefault();const r=this._filtered().map(u=>L(u.method,u.path));if(r.length===0)return;const d=o.focusedKey?r.indexOf(o.focusedKey):-1,p=e.key==="ArrowDown"?Math.min(r.length-1,d+1):Math.max(0,d-1);c.set({focusedKey:r[p]??null})}else e.key==="Enter"&&document.activeElement===n&&o.focusedKey&&(e.preventDefault(),c.set({expandedKey:o.expandedKey===o.focusedKey?null:o.focusedKey}))}}customElements.define("dbh-app",Le);const j=document.getElementById("root");if(j){const a=document.createElement("dbh-app");j.appendChild(a)}
|
package/dist/bin-helpers.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure helpers for the `@databehave/server` CLI.
|
|
3
|
-
*
|
|
4
|
-
* Everything in this module is side-effect-free (or accepts injected
|
|
5
|
-
* side effects) so it can be unit-tested without spawning child
|
|
6
|
-
* processes or opening sockets. The thin executable entry point
|
|
7
|
-
* (`bin.ts`) wires these together.
|
|
8
|
-
*/
|
|
9
|
-
/** Parsed CLI invocation. `configPath` is the first positional arg. */
|
|
10
|
-
export interface CliOptions {
|
|
11
|
-
open: boolean;
|
|
12
|
-
help: boolean;
|
|
13
|
-
configPath?: string;
|
|
14
|
-
unknown: string[];
|
|
15
|
-
}
|
|
16
|
-
export declare const HELP_TEXT = "Usage: databehave-server [options] <config>\n\nOptions:\n --open After the server is listening, open the admin UI in\n the default browser. No-op (with a log line) when\n admin is absent or admin.enabled !== true.\n -h, --help Show this help and exit.\n";
|
|
17
|
-
/**
|
|
18
|
-
* Parse argv (without `node` / script name). Unknown flags are
|
|
19
|
-
* collected into `unknown` for the entry point to reject — we never
|
|
20
|
-
* silently ignore an unrecognized option.
|
|
21
|
-
*/
|
|
22
|
-
export declare const parseCliArgs: (argv: readonly string[]) => CliOptions;
|
|
23
|
-
/** Subset of the listen handle we need to build the admin URL. */
|
|
24
|
-
export interface ListenInfo {
|
|
25
|
-
readonly host: string;
|
|
26
|
-
readonly port: number;
|
|
27
|
-
}
|
|
28
|
-
/** Subset of the resolved admin config we care about for --open. */
|
|
29
|
-
export interface AdminUrlInfo {
|
|
30
|
-
readonly enabled?: boolean;
|
|
31
|
-
readonly path?: string;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Build the URL `--open` should hit. Returns `null` when the admin
|
|
35
|
-
* panel is not active (no `admin`, or `enabled !== true`) — the
|
|
36
|
-
* caller surfaces that as a skip-with-log message. Hosts bound to
|
|
37
|
-
* `0.0.0.0` / `::` are rewritten to the matching loopback so the
|
|
38
|
-
* browser doesn't try to dial the wildcard.
|
|
39
|
-
*/
|
|
40
|
-
export declare const composeAdminUrl: (handle: ListenInfo, admin: AdminUrlInfo | undefined) => string | null;
|
|
41
|
-
/** Platform-specific browser opener. Pure — returns the spawn args. */
|
|
42
|
-
export declare const resolveOpener: (platform: NodeJS.Platform, url: string) => {
|
|
43
|
-
cmd: string;
|
|
44
|
-
args: string[];
|
|
45
|
-
};
|
|
46
|
-
//# sourceMappingURL=bin-helpers.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bin-helpers.d.ts","sourceRoot":"","sources":["../src/bin-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,uEAAuE;AACvE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,IAAI,EAAE,OAAO,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,eAAO,MAAM,SAAS,wTAOrB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,UActD,CAAA;AAED,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,oEAAoE;AACpE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,QAAQ,UAAU,EAClB,OAAO,YAAY,GAAG,SAAS,KAC9B,MAAM,GAAG,IAQX,CAAA;AAED,uEAAuE;AACvE,eAAO,MAAM,aAAa,GACxB,UAAU,MAAM,CAAC,QAAQ,EACzB,KAAK,MAAM,KACV;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAO/B,CAAA"}
|