@mechris3/glassbox 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/README.md +213 -0
- package/dist/bin/glassbox.d.ts +14 -0
- package/dist/bin/glassbox.d.ts.map +1 -0
- package/dist/bin/glassbox.js +140 -0
- package/dist/bin/glassbox.js.map +1 -0
- package/dist/src/adapters/base-adapter.d.ts +70 -0
- package/dist/src/adapters/base-adapter.d.ts.map +1 -0
- package/dist/src/adapters/base-adapter.js +155 -0
- package/dist/src/adapters/base-adapter.js.map +1 -0
- package/dist/src/adapters/browser-adapter.interface.d.ts +41 -0
- package/dist/src/adapters/browser-adapter.interface.d.ts.map +1 -0
- package/dist/src/adapters/browser-adapter.interface.js +7 -0
- package/dist/src/adapters/browser-adapter.interface.js.map +1 -0
- package/dist/src/adapters/cdp-adapter.d.ts +63 -0
- package/dist/src/adapters/cdp-adapter.d.ts.map +1 -0
- package/dist/src/adapters/cdp-adapter.js +231 -0
- package/dist/src/adapters/cdp-adapter.js.map +1 -0
- package/dist/src/ai/collectors.d.ts +35 -0
- package/dist/src/ai/collectors.d.ts.map +1 -0
- package/dist/src/ai/collectors.js +170 -0
- package/dist/src/ai/collectors.js.map +1 -0
- package/dist/src/ai/queries.d.ts +21 -0
- package/dist/src/ai/queries.d.ts.map +1 -0
- package/dist/src/ai/queries.js +59 -0
- package/dist/src/ai/queries.js.map +1 -0
- package/dist/src/ai/types.d.ts +55 -0
- package/dist/src/ai/types.d.ts.map +1 -0
- package/dist/src/ai/types.js +6 -0
- package/dist/src/ai/types.js.map +1 -0
- package/dist/src/cdp/connection.d.ts +37 -0
- package/dist/src/cdp/connection.d.ts.map +1 -0
- package/dist/src/cdp/connection.js +86 -0
- package/dist/src/cdp/connection.js.map +1 -0
- package/dist/src/cdp/connection.test.d.ts +6 -0
- package/dist/src/cdp/connection.test.d.ts.map +1 -0
- package/dist/src/cdp/connection.test.js +93 -0
- package/dist/src/cdp/connection.test.js.map +1 -0
- package/dist/src/cdp/domains.d.ts +116 -0
- package/dist/src/cdp/domains.d.ts.map +1 -0
- package/dist/src/cdp/domains.js +153 -0
- package/dist/src/cdp/domains.js.map +1 -0
- package/dist/src/cdp/launcher.d.ts +26 -0
- package/dist/src/cdp/launcher.d.ts.map +1 -0
- package/dist/src/cdp/launcher.js +175 -0
- package/dist/src/cdp/launcher.js.map +1 -0
- package/dist/src/config.d.ts +28 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +72 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/config.test.d.ts +5 -0
- package/dist/src/config.test.d.ts.map +1 -0
- package/dist/src/config.test.js +46 -0
- package/dist/src/config.test.js.map +1 -0
- package/dist/src/errors.d.ts +26 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +27 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +28 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/journey.d.ts +35 -0
- package/dist/src/journey.d.ts.map +1 -0
- package/dist/src/journey.js +39 -0
- package/dist/src/journey.js.map +1 -0
- package/dist/src/mcp/server.d.ts +22 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +229 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/page-objects/base.page.d.ts +28 -0
- package/dist/src/page-objects/base.page.d.ts.map +1 -0
- package/dist/src/page-objects/base.page.js +51 -0
- package/dist/src/page-objects/base.page.js.map +1 -0
- package/dist/src/page-objects/browser-context.page.d.ts +14 -0
- package/dist/src/page-objects/browser-context.page.d.ts.map +1 -0
- package/dist/src/page-objects/browser-context.page.js +28 -0
- package/dist/src/page-objects/browser-context.page.js.map +1 -0
- package/dist/src/runner/lifecycle-hooks.d.ts +43 -0
- package/dist/src/runner/lifecycle-hooks.d.ts.map +1 -0
- package/dist/src/runner/lifecycle-hooks.js +70 -0
- package/dist/src/runner/lifecycle-hooks.js.map +1 -0
- package/dist/src/runner/run-all-journeys.d.ts +35 -0
- package/dist/src/runner/run-all-journeys.d.ts.map +1 -0
- package/dist/src/runner/run-all-journeys.js +167 -0
- package/dist/src/runner/run-all-journeys.js.map +1 -0
- package/dist/src/runner/run-journey.d.ts +17 -0
- package/dist/src/runner/run-journey.d.ts.map +1 -0
- package/dist/src/runner/run-journey.js +179 -0
- package/dist/src/runner/run-journey.js.map +1 -0
- package/dist/src/runner/spawn-journey.d.ts +36 -0
- package/dist/src/runner/spawn-journey.d.ts.map +1 -0
- package/dist/src/runner/spawn-journey.js +40 -0
- package/dist/src/runner/spawn-journey.js.map +1 -0
- package/dist/src/server/diagnostic-service.d.ts +69 -0
- package/dist/src/server/diagnostic-service.d.ts.map +1 -0
- package/dist/src/server/diagnostic-service.js +165 -0
- package/dist/src/server/diagnostic-service.js.map +1 -0
- package/dist/src/server/diagnostic-service.test.d.ts +5 -0
- package/dist/src/server/diagnostic-service.test.d.ts.map +1 -0
- package/dist/src/server/diagnostic-service.test.js +82 -0
- package/dist/src/server/diagnostic-service.test.js.map +1 -0
- package/dist/src/server/index.d.ts +11 -0
- package/dist/src/server/index.d.ts.map +1 -0
- package/dist/src/server/index.js +167 -0
- package/dist/src/server/index.js.map +1 -0
- package/dist/src/server/journey-discovery.d.ts +16 -0
- package/dist/src/server/journey-discovery.d.ts.map +1 -0
- package/dist/src/server/journey-discovery.js +38 -0
- package/dist/src/server/journey-discovery.js.map +1 -0
- package/dist/src/server/settings-storage.d.ts +27 -0
- package/dist/src/server/settings-storage.d.ts.map +1 -0
- package/dist/src/server/settings-storage.js +27 -0
- package/dist/src/server/settings-storage.js.map +1 -0
- package/dist/src/server/test-executor.d.ts +64 -0
- package/dist/src/server/test-executor.d.ts.map +1 -0
- package/dist/src/server/test-executor.js +279 -0
- package/dist/src/server/test-executor.js.map +1 -0
- package/dist/src/server/websocket.d.ts +51 -0
- package/dist/src/server/websocket.d.ts.map +1 -0
- package/dist/src/server/websocket.js +28 -0
- package/dist/src/server/websocket.js.map +1 -0
- package/dist/src/utils/browser-discovery.d.ts +11 -0
- package/dist/src/utils/browser-discovery.d.ts.map +1 -0
- package/dist/src/utils/browser-discovery.js +102 -0
- package/dist/src/utils/browser-discovery.js.map +1 -0
- package/dist/src/utils/error-formatter.d.ts +13 -0
- package/dist/src/utils/error-formatter.d.ts.map +1 -0
- package/dist/src/utils/error-formatter.js +29 -0
- package/dist/src/utils/error-formatter.js.map +1 -0
- package/dist/src/utils/escape.d.ts +10 -0
- package/dist/src/utils/escape.d.ts.map +1 -0
- package/dist/src/utils/escape.js +22 -0
- package/dist/src/utils/escape.js.map +1 -0
- package/dist/src/utils/escape.test.d.ts +5 -0
- package/dist/src/utils/escape.test.d.ts.map +1 -0
- package/dist/src/utils/escape.test.js +49 -0
- package/dist/src/utils/escape.test.js.map +1 -0
- package/dist/src/utils/page-object-error.d.ts +5 -0
- package/dist/src/utils/page-object-error.d.ts.map +1 -0
- package/dist/src/utils/page-object-error.js +22 -0
- package/dist/src/utils/page-object-error.js.map +1 -0
- package/dist/src/utils/profile-discovery.d.ts +20 -0
- package/dist/src/utils/profile-discovery.d.ts.map +1 -0
- package/dist/src/utils/profile-discovery.js +68 -0
- package/dist/src/utils/profile-discovery.js.map +1 -0
- package/dist/src/utils/wait-utils.d.ts +10 -0
- package/dist/src/utils/wait-utils.d.ts.map +1 -0
- package/dist/src/utils/wait-utils.js +20 -0
- package/dist/src/utils/wait-utils.js.map +1 -0
- package/dist/src/utils/wait-utils.test.d.ts +5 -0
- package/dist/src/utils/wait-utils.test.d.ts.map +1 -0
- package/dist/src/utils/wait-utils.test.js +30 -0
- package/dist/src/utils/wait-utils.test.js.map +1 -0
- package/package.json +60 -0
- package/src/server/static/index.html +541 -0
- package/ui/dist/3rdpartylicenses.txt +419 -0
- package/ui/dist/browser/assets/logo.svg +29 -0
- package/ui/dist/browser/index.html +13 -0
- package/ui/dist/browser/main-7OJWN7GZ.js +16 -0
- package/ui/dist/browser/polyfills-5CFQRCPP.js +2 -0
- package/ui/dist/browser/styles-HMZYQRBV.css +1 -0
- package/ui/dist/prerendered-routes.json +3 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Escape utilities for safely interpolating values into JavaScript strings
|
|
4
|
+
* evaluated via CDP's Runtime.evaluate.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.escapeJS = escapeJS;
|
|
8
|
+
/**
|
|
9
|
+
* Escape a string for safe interpolation into a single-quoted JS string.
|
|
10
|
+
* Handles quotes, backslashes, newlines, null bytes, and Unicode line/paragraph separators.
|
|
11
|
+
*/
|
|
12
|
+
function escapeJS(str) {
|
|
13
|
+
return str
|
|
14
|
+
.replace(/\\/g, '\\\\')
|
|
15
|
+
.replace(/'/g, "\\'")
|
|
16
|
+
.replace(/\n/g, '\\n')
|
|
17
|
+
.replace(/\r/g, '\\r')
|
|
18
|
+
.replace(/\0/g, '\\0')
|
|
19
|
+
.replace(/\u2028/g, '\\u2028')
|
|
20
|
+
.replace(/\u2029/g, '\\u2029');
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=escape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape.js","sourceRoot":"","sources":["../../../src/utils/escape.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAMH,4BASC;AAbD;;;GAGG;AACH,SAAgB,QAAQ,CAAC,GAAW;IAClC,OAAO,GAAG;SACP,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC;SAC7B,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape.test.d.ts","sourceRoot":"","sources":["../../../src/utils/escape.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for escapeJS utility.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const escape_js_1 = require("./escape.js");
|
|
8
|
+
(0, vitest_1.describe)('escapeJS', () => {
|
|
9
|
+
(0, vitest_1.it)('passes through plain strings unchanged', () => {
|
|
10
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)('hello')).toBe('hello');
|
|
11
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)('[data-testid="submit"]')).toBe('[data-testid="submit"]');
|
|
12
|
+
});
|
|
13
|
+
(0, vitest_1.it)('escapes single quotes', () => {
|
|
14
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)("it's")).toBe("it\\'s");
|
|
15
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)("a'b'c")).toBe("a\\'b\\'c");
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.it)('escapes backslashes', () => {
|
|
18
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)('path\\to\\file')).toBe('path\\\\to\\\\file');
|
|
19
|
+
});
|
|
20
|
+
(0, vitest_1.it)('escapes newlines and carriage returns', () => {
|
|
21
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)('line1\nline2')).toBe('line1\\nline2');
|
|
22
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)('line1\rline2')).toBe('line1\\rline2');
|
|
23
|
+
});
|
|
24
|
+
(0, vitest_1.it)('escapes null bytes', () => {
|
|
25
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)('before\0after')).toBe('before\\0after');
|
|
26
|
+
});
|
|
27
|
+
(0, vitest_1.it)('escapes Unicode line/paragraph separators', () => {
|
|
28
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)('a\u2028b')).toBe('a\\u2028b');
|
|
29
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)('a\u2029b')).toBe('a\\u2029b');
|
|
30
|
+
});
|
|
31
|
+
(0, vitest_1.it)('handles combined special characters', () => {
|
|
32
|
+
const input = "it's a\nnew\\line\0end";
|
|
33
|
+
const expected = "it\\'s a\\nnew\\\\line\\0end";
|
|
34
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)(input)).toBe(expected);
|
|
35
|
+
});
|
|
36
|
+
(0, vitest_1.it)('handles empty string', () => {
|
|
37
|
+
(0, vitest_1.expect)((0, escape_js_1.escapeJS)('')).toBe('');
|
|
38
|
+
});
|
|
39
|
+
(0, vitest_1.it)('prevents selector injection', () => {
|
|
40
|
+
// A malicious selector that tries to break out of the string
|
|
41
|
+
const malicious = "'); document.location='http://evil.com';//";
|
|
42
|
+
const escaped = (0, escape_js_1.escapeJS)(malicious);
|
|
43
|
+
// When placed inside a JS string like '${escaped}', it should not break out
|
|
44
|
+
// All single quotes should be preceded by a backslash
|
|
45
|
+
(0, vitest_1.expect)(escaped).not.toMatch(/(?<!\\)'/);
|
|
46
|
+
(0, vitest_1.expect)(escaped).toContain("\\'");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=escape.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape.test.js","sourceRoot":"","sources":["../../../src/utils/escape.test.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAEH,mCAA8C;AAC9C,2CAAuC;AAEvC,IAAA,iBAAQ,EAAC,UAAU,EAAE,GAAG,EAAE;IACxB,IAAA,WAAE,EAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACvD,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC/C,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,KAAK,GAAG,wBAAwB,CAAC;QACvC,MAAM,QAAQ,GAAG,8BAA8B,CAAC;QAChD,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,IAAA,eAAM,EAAC,IAAA,oBAAQ,EAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,6DAA6D;QAC7D,MAAM,SAAS,GAAG,4CAA4C,CAAC;QAC/D,MAAM,OAAO,GAAG,IAAA,oBAAQ,EAAC,SAAS,CAAC,CAAC;QACpC,4EAA4E;QAC5E,sDAAsD;QACtD,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACxC,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @withErrorContext decorator — enhances page object errors with class/method context.
|
|
3
|
+
*/
|
|
4
|
+
export declare function withErrorContext(_target: unknown, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor;
|
|
5
|
+
//# sourceMappingURL=page-object-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-object-error.d.ts","sourceRoot":"","sources":["../../../src/utils/page-object-error.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,kBAAkB,GAC7B,kBAAkB,CAepB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @withErrorContext decorator — enhances page object errors with class/method context.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.withErrorContext = withErrorContext;
|
|
7
|
+
function withErrorContext(_target, propertyKey, descriptor) {
|
|
8
|
+
const originalMethod = descriptor.value;
|
|
9
|
+
descriptor.value = async function (...args) {
|
|
10
|
+
try {
|
|
11
|
+
return await originalMethod.apply(this, args);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
const className = this.constructor.name;
|
|
15
|
+
const enhanced = error instanceof Error ? error : new Error(String(error));
|
|
16
|
+
enhanced.message = `\n[${className}.${propertyKey}]\n${enhanced.message}`;
|
|
17
|
+
throw enhanced;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
return descriptor;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=page-object-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-object-error.js","sourceRoot":"","sources":["../../../src/utils/page-object-error.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAEH,4CAmBC;AAnBD,SAAgB,gBAAgB,CAC9B,OAAgB,EAChB,WAAmB,EACnB,UAA8B;IAE9B,MAAM,cAAc,GAAG,UAAU,CAAC,KAAiD,CAAC;IAEpF,UAAU,CAAC,KAAK,GAAG,KAAK,WAAoD,GAAG,IAAe;QAC5F,IAAI,CAAC;YACH,OAAO,MAAM,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YACxC,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC3E,QAAQ,CAAC,OAAO,GAAG,MAAM,SAAS,IAAI,WAAW,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC;YAC1E,MAAM,QAAQ,CAAC;QACjB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile discovery — scans a browser's user data directory for available profiles.
|
|
3
|
+
*
|
|
4
|
+
* Chrome/Brave/Edge store profiles as subdirectories (Default, Profile 1, Profile 2, etc.)
|
|
5
|
+
* each containing a Preferences JSON file with the profile name.
|
|
6
|
+
*/
|
|
7
|
+
export interface DiscoveredProfile {
|
|
8
|
+
/** Display name from Chrome Preferences (e.g. "Personal", "Work"). */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Directory name (e.g. "Default", "Profile 1"). */
|
|
11
|
+
directory: string;
|
|
12
|
+
/** Full path to the profile directory. */
|
|
13
|
+
path: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Discover all profiles within a browser's user data directory.
|
|
17
|
+
* Returns an empty array if the directory doesn't exist or has no valid profiles.
|
|
18
|
+
*/
|
|
19
|
+
export declare function discoverProfiles(userDataDir: string): DiscoveredProfile[];
|
|
20
|
+
//# sourceMappingURL=profile-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-discovery.d.ts","sourceRoot":"","sources":["../../../src/utils/profile-discovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAqBzE"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Profile discovery — scans a browser's user data directory for available profiles.
|
|
4
|
+
*
|
|
5
|
+
* Chrome/Brave/Edge store profiles as subdirectories (Default, Profile 1, Profile 2, etc.)
|
|
6
|
+
* each containing a Preferences JSON file with the profile name.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.discoverProfiles = discoverProfiles;
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
/**
|
|
13
|
+
* Discover all profiles within a browser's user data directory.
|
|
14
|
+
* Returns an empty array if the directory doesn't exist or has no valid profiles.
|
|
15
|
+
*/
|
|
16
|
+
function discoverProfiles(userDataDir) {
|
|
17
|
+
if (!userDataDir || !(0, fs_1.existsSync)(userDataDir))
|
|
18
|
+
return [];
|
|
19
|
+
try {
|
|
20
|
+
const entries = (0, fs_1.readdirSync)(userDataDir);
|
|
21
|
+
return entries
|
|
22
|
+
.filter((entry) => isProfileDir(userDataDir, entry))
|
|
23
|
+
.map((entry) => ({
|
|
24
|
+
name: getProfileName(userDataDir, entry),
|
|
25
|
+
directory: entry,
|
|
26
|
+
path: (0, path_1.join)(userDataDir, entry),
|
|
27
|
+
}))
|
|
28
|
+
.sort((a, b) => {
|
|
29
|
+
// Default always first, then alphabetical
|
|
30
|
+
if (a.directory === 'Default')
|
|
31
|
+
return -1;
|
|
32
|
+
if (b.directory === 'Default')
|
|
33
|
+
return 1;
|
|
34
|
+
return a.name.localeCompare(b.name);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Check if a subdirectory looks like a Chrome profile (has a Preferences file). */
|
|
42
|
+
function isProfileDir(userDataDir, entry) {
|
|
43
|
+
// Chrome profiles are named "Default" or "Profile N"
|
|
44
|
+
if (entry !== 'Default' && !entry.startsWith('Profile '))
|
|
45
|
+
return false;
|
|
46
|
+
try {
|
|
47
|
+
const dirPath = (0, path_1.join)(userDataDir, entry);
|
|
48
|
+
if (!(0, fs_1.statSync)(dirPath).isDirectory())
|
|
49
|
+
return false;
|
|
50
|
+
return (0, fs_1.existsSync)((0, path_1.join)(dirPath, 'Preferences'));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Read the display name from a profile's Preferences file. */
|
|
57
|
+
function getProfileName(userDataDir, entry) {
|
|
58
|
+
try {
|
|
59
|
+
const prefsPath = (0, path_1.join)(userDataDir, entry, 'Preferences');
|
|
60
|
+
const content = (0, fs_1.readFileSync)(prefsPath, 'utf-8');
|
|
61
|
+
const prefs = JSON.parse(content);
|
|
62
|
+
return prefs?.profile?.name ?? entry;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return entry;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=profile-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-discovery.js","sourceRoot":"","sources":["../../../src/utils/profile-discovery.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAkBH,4CAqBC;AArCD,2BAAqE;AACrE,+BAA4B;AAW5B;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,WAAmB;IAClD,IAAI,CAAC,WAAW,IAAI,CAAC,IAAA,eAAU,EAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IAExD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,gBAAW,EAAC,WAAW,CAAC,CAAC;QACzC,OAAO,OAAO;aACX,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;aACnD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACf,IAAI,EAAE,cAAc,CAAC,WAAW,EAAE,KAAK,CAAC;YACxC,SAAS,EAAE,KAAK;YAChB,IAAI,EAAE,IAAA,WAAI,EAAC,WAAW,EAAE,KAAK,CAAC;SAC/B,CAAC,CAAC;aACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACb,0CAA0C;YAC1C,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS;gBAAE,OAAO,CAAC,CAAC,CAAC;YACzC,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS;gBAAE,OAAO,CAAC,CAAC;YACxC,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC,CAAC,CAAC;IACP,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,SAAS,YAAY,CAAC,WAAmB,EAAE,KAAa;IACtD,qDAAqD;IACrD,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAEvE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,IAAA,aAAQ,EAAC,OAAO,CAAC,CAAC,WAAW,EAAE;YAAE,OAAO,KAAK,CAAC;QACnD,OAAO,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,+DAA+D;AAC/D,SAAS,cAAc,CAAC,WAAmB,EAAE,KAAa;IACxD,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAA,WAAI,EAAC,WAAW,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;QAC1D,MAAM,OAAO,GAAG,IAAA,iBAAY,EAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAClC,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,IAAI,KAAK,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* waitForCondition — polls a condition until met or timeout.
|
|
3
|
+
*/
|
|
4
|
+
export interface WaitOptions {
|
|
5
|
+
timeout?: number;
|
|
6
|
+
interval?: number;
|
|
7
|
+
errorMessage?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function waitForCondition<T>(getValue: () => Promise<T>, condition: (value: T) => boolean, options?: WaitOptions): Promise<T>;
|
|
10
|
+
//# sourceMappingURL=wait-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-utils.d.ts","sourceRoot":"","sources":["../../../src/utils/wait-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC1B,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,EAChC,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,CAAC,CAAC,CAaZ"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* waitForCondition — polls a condition until met or timeout.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.waitForCondition = waitForCondition;
|
|
7
|
+
async function waitForCondition(getValue, condition, options) {
|
|
8
|
+
const timeout = options?.timeout ?? 5000;
|
|
9
|
+
const interval = options?.interval ?? 250;
|
|
10
|
+
const errorMessage = options?.errorMessage ?? 'Condition not met within timeout';
|
|
11
|
+
const start = Date.now();
|
|
12
|
+
while (Date.now() - start < timeout) {
|
|
13
|
+
const value = await getValue();
|
|
14
|
+
if (condition(value))
|
|
15
|
+
return value;
|
|
16
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
17
|
+
}
|
|
18
|
+
throw new Error(`${errorMessage} (${timeout}ms)`);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=wait-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-utils.js","sourceRoot":"","sources":["../../../src/utils/wait-utils.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAQH,4CAiBC;AAjBM,KAAK,UAAU,gBAAgB,CACpC,QAA0B,EAC1B,SAAgC,EAChC,OAAqB;IAErB,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC;IACzC,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,GAAG,CAAC;IAC1C,MAAM,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,kCAAkC,CAAC;IACjF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEzB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,OAAO,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,MAAM,QAAQ,EAAE,CAAC;QAC/B,IAAI,SAAS,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACnC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,GAAG,YAAY,KAAK,OAAO,KAAK,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-utils.test.d.ts","sourceRoot":"","sources":["../../../src/utils/wait-utils.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for waitForCondition utility.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const wait_utils_js_1 = require("./wait-utils.js");
|
|
8
|
+
(0, vitest_1.describe)('waitForCondition', () => {
|
|
9
|
+
(0, vitest_1.it)('resolves immediately when condition is already met', async () => {
|
|
10
|
+
const result = await (0, wait_utils_js_1.waitForCondition)(async () => 42, (v) => v === 42);
|
|
11
|
+
(0, vitest_1.expect)(result).toBe(42);
|
|
12
|
+
});
|
|
13
|
+
(0, vitest_1.it)('polls until condition is met', async () => {
|
|
14
|
+
let counter = 0;
|
|
15
|
+
const result = await (0, wait_utils_js_1.waitForCondition)(async () => ++counter, (v) => v >= 3, { interval: 10 });
|
|
16
|
+
(0, vitest_1.expect)(result).toBe(3);
|
|
17
|
+
});
|
|
18
|
+
(0, vitest_1.it)('throws on timeout', async () => {
|
|
19
|
+
await (0, vitest_1.expect)((0, wait_utils_js_1.waitForCondition)(async () => false, (v) => v === true, { timeout: 50, interval: 10 })).rejects.toThrow('(50ms)');
|
|
20
|
+
});
|
|
21
|
+
(0, vitest_1.it)('uses custom error message', async () => {
|
|
22
|
+
await (0, vitest_1.expect)((0, wait_utils_js_1.waitForCondition)(async () => null, (v) => v !== null, { timeout: 50, interval: 10, errorMessage: 'Element not found' })).rejects.toThrow('Element not found (50ms)');
|
|
23
|
+
});
|
|
24
|
+
(0, vitest_1.it)('returns the value that satisfied the condition', async () => {
|
|
25
|
+
let count = 0;
|
|
26
|
+
const result = await (0, wait_utils_js_1.waitForCondition)(async () => ({ ready: ++count > 2 }), (v) => v.ready, { interval: 10 });
|
|
27
|
+
(0, vitest_1.expect)(result.ready).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=wait-utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-utils.test.js","sourceRoot":"","sources":["../../../src/utils/wait-utils.test.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAEH,mCAA8C;AAC9C,mDAAmD;AAEnD,IAAA,iBAAQ,EAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,IAAA,WAAE,EAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,MAAM,GAAG,MAAM,IAAA,gCAAgB,EACnC,KAAK,IAAI,EAAE,CAAC,EAAE,EACd,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAChB,CAAC;QACF,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,MAAM,MAAM,GAAG,MAAM,IAAA,gCAAgB,EACnC,KAAK,IAAI,EAAE,CAAC,EAAE,OAAO,EACrB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EACb,EAAE,QAAQ,EAAE,EAAE,EAAE,CACjB,CAAC;QACF,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,mBAAmB,EAAE,KAAK,IAAI,EAAE;QACjC,MAAM,IAAA,eAAM,EACV,IAAA,gCAAgB,EACd,KAAK,IAAI,EAAE,CAAC,KAAK,EACjB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EACjB,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAC9B,CACF,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,IAAA,eAAM,EACV,IAAA,gCAAgB,EACd,KAAK,IAAI,EAAE,CAAC,IAAI,EAChB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EACjB,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,mBAAmB,EAAE,CACjE,CACF,CAAC,OAAO,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,MAAM,MAAM,GAAG,MAAM,IAAA,gCAAgB,EACnC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,EACpC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EACd,EAAE,QAAQ,EAAE,EAAE,EAAE,CACjB,CAAC;QACF,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mechris3/glassbox",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Debugger-style browser automation test runner with live execution control via CDP",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/src/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"glassbox": "dist/bin/glassbox.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"ui/dist/",
|
|
13
|
+
"src/server/static/",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"build:watch": "tsc --watch",
|
|
19
|
+
"dev": "tsx watch bin/glassbox.ts serve",
|
|
20
|
+
"start": "node dist/bin/glassbox.js serve",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"test:e2e": "tsx scripts/test-e2e.ts",
|
|
24
|
+
"sample-app": "node examples/sample-app/server.js",
|
|
25
|
+
"lint": "eslint src/ bin/",
|
|
26
|
+
"prepublishOnly": "npm run build"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"browser-automation",
|
|
30
|
+
"cdp",
|
|
31
|
+
"chrome-devtools-protocol",
|
|
32
|
+
"test-runner",
|
|
33
|
+
"debugger",
|
|
34
|
+
"e2e"
|
|
35
|
+
],
|
|
36
|
+
"author": "",
|
|
37
|
+
"license": "UNLICENSED",
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
40
|
+
"commander": "^12.1.0",
|
|
41
|
+
"cors": "^2.8.5",
|
|
42
|
+
"express": "^4.21.0",
|
|
43
|
+
"glob": "^11.0.0",
|
|
44
|
+
"jiti": "^2.6.1",
|
|
45
|
+
"tsx": "^4.21.0",
|
|
46
|
+
"ws": "^8.18.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/cors": "^2.8.17",
|
|
50
|
+
"@types/express": "^5.0.0",
|
|
51
|
+
"@types/node": "^22.0.0",
|
|
52
|
+
"@types/ws": "^8.5.0",
|
|
53
|
+
"devtools-protocol": "^0.0.1648812",
|
|
54
|
+
"typescript": "^5.6.0",
|
|
55
|
+
"vitest": "^2.1.0"
|
|
56
|
+
},
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=20.0.0"
|
|
59
|
+
}
|
|
60
|
+
}
|