chrome-devtools-mcp 0.26.0 → 1.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 +59 -10
- package/build/src/DevtoolsUtils.js +14 -1
- package/build/src/HeapSnapshotManager.js +42 -18
- package/build/src/McpContext.js +61 -23
- package/build/src/McpResponse.js +51 -21
- package/build/src/ToolHandler.js +30 -1
- package/build/src/WaitForHelper.js +18 -4
- package/build/src/bin/check-latest-version.js +25 -1
- package/build/src/bin/chrome-devtools-cli-options.js +81 -39
- package/build/src/bin/chrome-devtools-mcp-cli-options.js +2 -8
- package/build/src/bin/chrome-devtools-mcp-main.js +38 -0
- package/build/src/browser.js +36 -2
- package/build/src/daemon/client.js +12 -6
- package/build/src/daemon/daemon.js +62 -5
- package/build/src/formatters/HeapSnapshotFormatter.js +30 -9
- package/build/src/index.js +3 -1
- package/build/src/telemetry/ClearcutLogger.js +8 -119
- package/build/src/telemetry/errors.js +4 -0
- package/build/src/telemetry/flagUtils.js +4 -3
- package/build/src/telemetry/{toolMetricsUtils.js → metricsRegistry.js} +3 -3
- package/build/src/telemetry/persistence.js +20 -2
- package/build/src/telemetry/transformation.js +134 -0
- package/build/src/third_party/THIRD_PARTY_NOTICES +4 -719
- package/build/src/third_party/bundled-packages.json +2 -2
- package/build/src/third_party/devtools-formatter-worker.js +447 -114
- package/build/src/third_party/devtools-heap-snapshot-worker.js +2 -3
- package/build/src/third_party/index.js +3443 -30153
- package/build/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
- package/build/src/tools/ToolDefinition.js +2 -2
- package/build/src/tools/emulation.js +28 -2
- package/build/src/tools/extensions.js +2 -0
- package/build/src/tools/input.js +19 -10
- package/build/src/tools/lighthouse.js +1 -1
- package/build/src/tools/memory.js +39 -17
- package/build/src/tools/network.js +2 -2
- package/build/src/tools/performance.js +9 -6
- package/build/src/tools/screencast.js +1 -1
- package/build/src/tools/screenshot.js +1 -1
- package/build/src/tools/script.js +32 -10
- package/build/src/tools/snapshot.js +1 -1
- package/build/src/trace-processing/parse.js +2 -2
- package/build/src/utils/files.js +43 -0
- package/build/src/version.js +1 -1
- package/package.json +7 -4
- package/build/src/telemetry/metricUtils.js +0 -15
package/build/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { logger } from './logger.js';
|
|
|
9
9
|
import { McpContext } from './McpContext.js';
|
|
10
10
|
import { Mutex } from './Mutex.js';
|
|
11
11
|
import { ClearcutLogger } from './telemetry/ClearcutLogger.js';
|
|
12
|
+
import { FilePersistence } from './telemetry/persistence.js';
|
|
12
13
|
import { McpServer, SetLevelRequestSchema, ListRootsResultSchema, RootsListChangedNotificationSchema, } from './third_party/index.js';
|
|
13
14
|
import { ToolHandler } from './ToolHandler.js';
|
|
14
15
|
import { createTools } from './tools/tools.js';
|
|
@@ -17,6 +18,7 @@ export { buildFlag } from './ToolHandler.js';
|
|
|
17
18
|
export async function createMcpServer(serverArgs, options) {
|
|
18
19
|
if (serverArgs.usageStatistics) {
|
|
19
20
|
ClearcutLogger.initialize({
|
|
21
|
+
persistence: new FilePersistence(),
|
|
20
22
|
logFile: serverArgs.logFile,
|
|
21
23
|
appVersion: VERSION,
|
|
22
24
|
clearcutEndpoint: serverArgs.clearcutEndpoint,
|
|
@@ -109,7 +111,7 @@ export async function createMcpServer(serverArgs, options) {
|
|
|
109
111
|
}
|
|
110
112
|
server.registerTool(tool.name, {
|
|
111
113
|
description: tool.description,
|
|
112
|
-
inputSchema: toolHandler.
|
|
114
|
+
inputSchema: toolHandler.registeredInputSchema,
|
|
113
115
|
annotations: tool.annotations,
|
|
114
116
|
}, async (params) => {
|
|
115
117
|
return await toolHandler.handle(params);
|
|
@@ -6,124 +6,10 @@
|
|
|
6
6
|
import process from 'node:process';
|
|
7
7
|
import { DAEMON_CLIENT_NAME } from '../daemon/utils.js';
|
|
8
8
|
import { logger } from '../logger.js';
|
|
9
|
-
import {
|
|
9
|
+
import { sanitizeParams, stripUnderscoreBeforeNumber } from './transformation.js';
|
|
10
10
|
import { McpClient, WatchdogMessageType, OsType, } from './types.js';
|
|
11
11
|
import { WatchdogClient } from './WatchdogClient.js';
|
|
12
12
|
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
13
|
-
export const PARAM_BLOCKLIST = new Set(['uid', 'reqid', 'msgid']);
|
|
14
|
-
const SUPPORTED_ZOD_TYPES = [
|
|
15
|
-
'ZodString',
|
|
16
|
-
'ZodNumber',
|
|
17
|
-
'ZodBoolean',
|
|
18
|
-
'ZodArray',
|
|
19
|
-
'ZodEnum',
|
|
20
|
-
];
|
|
21
|
-
function isZodType(type) {
|
|
22
|
-
return SUPPORTED_ZOD_TYPES.includes(type);
|
|
23
|
-
}
|
|
24
|
-
export function getZodType(zodType) {
|
|
25
|
-
const def = zodType._def;
|
|
26
|
-
const typeName = def.typeName;
|
|
27
|
-
if (typeName === 'ZodOptional' ||
|
|
28
|
-
typeName === 'ZodDefault' ||
|
|
29
|
-
typeName === 'ZodNullable') {
|
|
30
|
-
return getZodType(def.innerType);
|
|
31
|
-
}
|
|
32
|
-
if (typeName === 'ZodEffects') {
|
|
33
|
-
return getZodType(def.schema);
|
|
34
|
-
}
|
|
35
|
-
if (isZodType(typeName)) {
|
|
36
|
-
return typeName;
|
|
37
|
-
}
|
|
38
|
-
throw new Error(`Unsupported zod type for tool parameter: ${typeName}`);
|
|
39
|
-
}
|
|
40
|
-
export function transformArgName(zodType, name) {
|
|
41
|
-
const snakeCaseName = name.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
|
|
42
|
-
if (zodType === 'ZodString') {
|
|
43
|
-
return `${snakeCaseName}_length`;
|
|
44
|
-
}
|
|
45
|
-
else if (zodType === 'ZodArray') {
|
|
46
|
-
return `${snakeCaseName}_count`;
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
return snakeCaseName;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
export function transformArgType(zodType) {
|
|
53
|
-
if (zodType === 'ZodString' || zodType === 'ZodArray') {
|
|
54
|
-
return 'number';
|
|
55
|
-
}
|
|
56
|
-
switch (zodType) {
|
|
57
|
-
case 'ZodNumber':
|
|
58
|
-
return 'number';
|
|
59
|
-
case 'ZodBoolean':
|
|
60
|
-
return 'boolean';
|
|
61
|
-
case 'ZodEnum':
|
|
62
|
-
return 'enum';
|
|
63
|
-
default:
|
|
64
|
-
throw new Error(`Unsupported zod type for tool parameter: ${zodType}`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
const BUCKETS = [
|
|
68
|
-
0, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000,
|
|
69
|
-
];
|
|
70
|
-
function bucketize(value) {
|
|
71
|
-
for (const bucket of BUCKETS) {
|
|
72
|
-
if (bucket >= value) {
|
|
73
|
-
return bucket;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return BUCKETS[BUCKETS.length - 1];
|
|
77
|
-
}
|
|
78
|
-
function transformValue(zodType, value) {
|
|
79
|
-
if (zodType === 'ZodString') {
|
|
80
|
-
return bucketize(value.length);
|
|
81
|
-
}
|
|
82
|
-
else if (zodType === 'ZodArray') {
|
|
83
|
-
return value.length;
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
return value;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function hasEquivalentType(zodType, value) {
|
|
90
|
-
if (zodType === 'ZodString') {
|
|
91
|
-
return typeof value === 'string';
|
|
92
|
-
}
|
|
93
|
-
else if (zodType === 'ZodArray') {
|
|
94
|
-
return Array.isArray(value);
|
|
95
|
-
}
|
|
96
|
-
else if (zodType === 'ZodNumber') {
|
|
97
|
-
return typeof value === 'number';
|
|
98
|
-
}
|
|
99
|
-
else if (zodType === 'ZodBoolean') {
|
|
100
|
-
return typeof value === 'boolean';
|
|
101
|
-
}
|
|
102
|
-
else if (zodType === 'ZodEnum') {
|
|
103
|
-
return (typeof value === 'string' ||
|
|
104
|
-
typeof value === 'number' ||
|
|
105
|
-
typeof value === 'boolean');
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
export function sanitizeParams(params, schema) {
|
|
112
|
-
const transformed = {};
|
|
113
|
-
for (const [name, value] of Object.entries(params)) {
|
|
114
|
-
if (PARAM_BLOCKLIST.has(name)) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
const zodType = getZodType(schema[name]);
|
|
118
|
-
if (!hasEquivalentType(zodType, value)) {
|
|
119
|
-
throw new Error(`parameter ${name} has type ${zodType} but value ${value} is not of equivalent type`);
|
|
120
|
-
}
|
|
121
|
-
const transformedName = transformArgName(zodType, name);
|
|
122
|
-
const transformedValue = transformValue(zodType, value);
|
|
123
|
-
transformed[transformedName] = transformedValue;
|
|
124
|
-
}
|
|
125
|
-
return transformed;
|
|
126
|
-
}
|
|
127
13
|
function detectOsType() {
|
|
128
14
|
switch (process.platform) {
|
|
129
15
|
case 'win32':
|
|
@@ -156,7 +42,7 @@ export class ClearcutLogger {
|
|
|
156
42
|
_clearcut_logger_instance = undefined;
|
|
157
43
|
}
|
|
158
44
|
constructor(options) {
|
|
159
|
-
this.#persistence = options.persistence
|
|
45
|
+
this.#persistence = options.persistence;
|
|
160
46
|
this.#watchdog =
|
|
161
47
|
options.watchdogClient ??
|
|
162
48
|
new WatchdogClient({
|
|
@@ -195,14 +81,15 @@ export class ClearcutLogger {
|
|
|
195
81
|
}
|
|
196
82
|
}
|
|
197
83
|
async logToolInvocation(args) {
|
|
84
|
+
const sanitizedToolName = stripUnderscoreBeforeNumber(args.toolName);
|
|
198
85
|
const tool_invocation = {
|
|
199
|
-
tool_name:
|
|
86
|
+
tool_name: sanitizedToolName,
|
|
200
87
|
success: args.success,
|
|
201
88
|
latency_ms: args.latencyMs,
|
|
202
89
|
};
|
|
203
90
|
if (Object.keys(args.params).length > 0) {
|
|
204
91
|
tool_invocation.tool_params = {
|
|
205
|
-
[`${
|
|
92
|
+
[`${sanitizedToolName}_params`]: sanitizeParams(args.params, args.schema),
|
|
206
93
|
};
|
|
207
94
|
}
|
|
208
95
|
this.#watchdog.send({
|
|
@@ -258,7 +145,9 @@ export class ClearcutLogger {
|
|
|
258
145
|
payload: {
|
|
259
146
|
mcp_client: this.#mcpClient,
|
|
260
147
|
server_error: {
|
|
261
|
-
tool_name: args.toolName
|
|
148
|
+
tool_name: args.toolName
|
|
149
|
+
? stripUnderscoreBeforeNumber(args.toolName)
|
|
150
|
+
: '',
|
|
262
151
|
error_code: args.errorCode,
|
|
263
152
|
},
|
|
264
153
|
},
|
|
@@ -6,9 +6,13 @@
|
|
|
6
6
|
* IMPORTANT:
|
|
7
7
|
* 1. this module must only contain ErrorCode.
|
|
8
8
|
* 2. do not refactor ErrorCode to elsewhere.
|
|
9
|
+
* 3. prefix new enum values with "ERROR_CODE_". This makes it easier to
|
|
10
|
+
* programmtically parse this file.
|
|
9
11
|
*/
|
|
10
12
|
export var ErrorCode;
|
|
11
13
|
(function (ErrorCode) {
|
|
12
14
|
ErrorCode[ErrorCode["ERROR_CODE_UNSPECIFIED"] = 0] = "ERROR_CODE_UNSPECIFIED";
|
|
15
|
+
ErrorCode[ErrorCode["ERROR_CODE_PERSISTENCE_FILE_READ_FAILED"] = 1] = "ERROR_CODE_PERSISTENCE_FILE_READ_FAILED";
|
|
16
|
+
ErrorCode[ErrorCode["ERROR_CODE_PERSISTENCE_FILE_SAVE_FAILED"] = 2] = "ERROR_CODE_PERSISTENCE_FILE_SAVE_FAILED";
|
|
13
17
|
})(ErrorCode || (ErrorCode = {}));
|
|
14
18
|
//# sourceMappingURL=errors.js.map
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import { toSnakeCase } from '../utils/string.js';
|
|
7
|
+
import { stripUnderscoreBeforeNumber } from './transformation.js';
|
|
7
8
|
/**
|
|
8
9
|
* For enums, log the value as uppercase.
|
|
9
10
|
* We're going to have an enum for such flags with choices represented
|
|
10
11
|
* as an `enum` where the keys of the enum will map to the uppercase `choice`.
|
|
11
12
|
*/
|
|
12
13
|
function formatEnumChoice(snakeCaseName, choice) {
|
|
13
|
-
return `${snakeCaseName}_${choice}
|
|
14
|
+
return stripUnderscoreBeforeNumber(`${snakeCaseName}_${choice}`).toUpperCase();
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
17
|
* Computes telemetry flag usage from parsed arguments and CLI options.
|
|
@@ -29,7 +30,7 @@ export function computeFlagUsage(args, options) {
|
|
|
29
30
|
const usage = {};
|
|
30
31
|
for (const [flagName, config] of Object.entries(options)) {
|
|
31
32
|
const value = args[flagName];
|
|
32
|
-
const snakeCaseName = toSnakeCase(flagName);
|
|
33
|
+
const snakeCaseName = stripUnderscoreBeforeNumber(toSnakeCase(flagName));
|
|
33
34
|
// If there isn't a default value provided for the flag,
|
|
34
35
|
// we're going to log whether it's present on the args user
|
|
35
36
|
// provided or not. If there is a default value, we only log presence
|
|
@@ -58,7 +59,7 @@ export function computeFlagUsage(args, options) {
|
|
|
58
59
|
export function getPossibleFlagMetrics(options) {
|
|
59
60
|
const metrics = [];
|
|
60
61
|
for (const [flagName, config] of Object.entries(options)) {
|
|
61
|
-
const snakeCaseName = toSnakeCase(flagName);
|
|
62
|
+
const snakeCaseName = stripUnderscoreBeforeNumber(toSnakeCase(flagName));
|
|
62
63
|
// _present is always a possible metric
|
|
63
64
|
metrics.push({
|
|
64
65
|
name: `${snakeCaseName}_present`,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright 2026 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { transformArgName, transformArgType, getZodType, PARAM_BLOCKLIST, } from './
|
|
6
|
+
import { transformArgName, transformArgType, getZodType, PARAM_BLOCKLIST, stripUnderscoreBeforeNumber, } from './transformation.js';
|
|
7
7
|
/**
|
|
8
8
|
* Validates that all values in an enum are of the homogeneous primitive type.
|
|
9
9
|
* Returns the primitive type string. Throws an error if heterogeneous.
|
|
@@ -81,9 +81,9 @@ export function generateToolMetrics(tools) {
|
|
|
81
81
|
});
|
|
82
82
|
}
|
|
83
83
|
return {
|
|
84
|
-
name: tool.name,
|
|
84
|
+
name: stripUnderscoreBeforeNumber(tool.name),
|
|
85
85
|
args,
|
|
86
86
|
};
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
|
-
//# sourceMappingURL=
|
|
89
|
+
//# sourceMappingURL=metricsRegistry.js.map
|
|
@@ -8,6 +8,8 @@ import os from 'node:os';
|
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import process from 'node:process';
|
|
10
10
|
import { logger } from '../logger.js';
|
|
11
|
+
import { ClearcutLogger } from './ClearcutLogger.js';
|
|
12
|
+
import { ErrorCode } from './errors.js';
|
|
11
13
|
const STATE_FILE_NAME = 'telemetry_state.json';
|
|
12
14
|
function getDataFolder() {
|
|
13
15
|
const homedir = os.homedir();
|
|
@@ -28,12 +30,25 @@ export class FilePersistence {
|
|
|
28
30
|
this.#dataFolder = dataFolderOverride ?? getDataFolder();
|
|
29
31
|
}
|
|
30
32
|
async loadState() {
|
|
33
|
+
const filePath = path.join(this.#dataFolder, STATE_FILE_NAME);
|
|
34
|
+
try {
|
|
35
|
+
await fs.access(filePath);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// File doesn't exist. Not an error because new users do not have the state file.
|
|
39
|
+
return {
|
|
40
|
+
lastActive: '',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
31
43
|
try {
|
|
32
|
-
const filePath = path.join(this.#dataFolder, STATE_FILE_NAME);
|
|
33
44
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
34
45
|
return JSON.parse(content);
|
|
35
46
|
}
|
|
36
|
-
catch {
|
|
47
|
+
catch (error) {
|
|
48
|
+
logger(`Failed to read telemetry state from ${filePath}:`, error);
|
|
49
|
+
void ClearcutLogger.get()?.logServerError({
|
|
50
|
+
errorCode: ErrorCode.ERROR_CODE_PERSISTENCE_FILE_READ_FAILED,
|
|
51
|
+
});
|
|
37
52
|
return {
|
|
38
53
|
lastActive: '',
|
|
39
54
|
};
|
|
@@ -48,6 +63,9 @@ export class FilePersistence {
|
|
|
48
63
|
catch (error) {
|
|
49
64
|
// Ignore errors during state saving to avoid crashing the server
|
|
50
65
|
logger(`Failed to save telemetry state to ${filePath}:`, error);
|
|
66
|
+
void ClearcutLogger.get()?.logServerError({
|
|
67
|
+
errorCode: ErrorCode.ERROR_CODE_PERSISTENCE_FILE_SAVE_FAILED,
|
|
68
|
+
});
|
|
51
69
|
}
|
|
52
70
|
}
|
|
53
71
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
const LATENCY_BUCKETS = [50, 100, 250, 500, 1000, 2500, 5000, 10000];
|
|
7
|
+
export function bucketizeLatency(latencyMs) {
|
|
8
|
+
for (const bucket of LATENCY_BUCKETS) {
|
|
9
|
+
if (latencyMs <= bucket) {
|
|
10
|
+
return bucket;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return LATENCY_BUCKETS[LATENCY_BUCKETS.length - 1];
|
|
14
|
+
}
|
|
15
|
+
export const PARAM_BLOCKLIST = new Set(['uid', 'reqid', 'msgid']);
|
|
16
|
+
const SUPPORTED_ZOD_TYPES = [
|
|
17
|
+
'ZodString',
|
|
18
|
+
'ZodNumber',
|
|
19
|
+
'ZodBoolean',
|
|
20
|
+
'ZodArray',
|
|
21
|
+
'ZodEnum',
|
|
22
|
+
];
|
|
23
|
+
function isZodType(type) {
|
|
24
|
+
return SUPPORTED_ZOD_TYPES.includes(type);
|
|
25
|
+
}
|
|
26
|
+
export function getZodType(zodType) {
|
|
27
|
+
const def = zodType._def;
|
|
28
|
+
const typeName = def.typeName;
|
|
29
|
+
if (typeName === 'ZodOptional' ||
|
|
30
|
+
typeName === 'ZodDefault' ||
|
|
31
|
+
typeName === 'ZodNullable') {
|
|
32
|
+
return getZodType(def.innerType);
|
|
33
|
+
}
|
|
34
|
+
if (typeName === 'ZodEffects') {
|
|
35
|
+
return getZodType(def.schema);
|
|
36
|
+
}
|
|
37
|
+
if (isZodType(typeName)) {
|
|
38
|
+
return typeName;
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Unsupported zod type for tool parameter: ${typeName}`);
|
|
41
|
+
}
|
|
42
|
+
export function stripUnderscoreBeforeNumber(name) {
|
|
43
|
+
return name.replace(/_([0-9])/g, '$1');
|
|
44
|
+
}
|
|
45
|
+
export function transformArgName(zodType, name) {
|
|
46
|
+
const snakeCaseName = name.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
|
|
47
|
+
let transformed;
|
|
48
|
+
if (zodType === 'ZodString') {
|
|
49
|
+
transformed = `${snakeCaseName}_length`;
|
|
50
|
+
}
|
|
51
|
+
else if (zodType === 'ZodArray') {
|
|
52
|
+
transformed = `${snakeCaseName}_count`;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
transformed = snakeCaseName;
|
|
56
|
+
}
|
|
57
|
+
return stripUnderscoreBeforeNumber(transformed);
|
|
58
|
+
}
|
|
59
|
+
export function transformArgType(zodType) {
|
|
60
|
+
if (zodType === 'ZodString' || zodType === 'ZodArray') {
|
|
61
|
+
return 'number';
|
|
62
|
+
}
|
|
63
|
+
switch (zodType) {
|
|
64
|
+
case 'ZodNumber':
|
|
65
|
+
return 'number';
|
|
66
|
+
case 'ZodBoolean':
|
|
67
|
+
return 'boolean';
|
|
68
|
+
case 'ZodEnum':
|
|
69
|
+
return 'enum';
|
|
70
|
+
default:
|
|
71
|
+
throw new Error(`Unsupported zod type for tool parameter: ${zodType}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const BUCKETS = [
|
|
75
|
+
0, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000,
|
|
76
|
+
];
|
|
77
|
+
function bucketize(value) {
|
|
78
|
+
for (const bucket of BUCKETS) {
|
|
79
|
+
if (bucket >= value) {
|
|
80
|
+
return bucket;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return BUCKETS[BUCKETS.length - 1];
|
|
84
|
+
}
|
|
85
|
+
function transformValue(zodType, value) {
|
|
86
|
+
if (zodType === 'ZodString') {
|
|
87
|
+
return bucketize(value.length);
|
|
88
|
+
}
|
|
89
|
+
else if (zodType === 'ZodArray') {
|
|
90
|
+
return value.length;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function hasEquivalentType(zodType, value) {
|
|
97
|
+
if (zodType === 'ZodString') {
|
|
98
|
+
return typeof value === 'string';
|
|
99
|
+
}
|
|
100
|
+
else if (zodType === 'ZodArray') {
|
|
101
|
+
return Array.isArray(value);
|
|
102
|
+
}
|
|
103
|
+
else if (zodType === 'ZodNumber') {
|
|
104
|
+
return typeof value === 'number';
|
|
105
|
+
}
|
|
106
|
+
else if (zodType === 'ZodBoolean') {
|
|
107
|
+
return typeof value === 'boolean';
|
|
108
|
+
}
|
|
109
|
+
else if (zodType === 'ZodEnum') {
|
|
110
|
+
return (typeof value === 'string' ||
|
|
111
|
+
typeof value === 'number' ||
|
|
112
|
+
typeof value === 'boolean');
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export function sanitizeParams(params, schema) {
|
|
119
|
+
const transformed = {};
|
|
120
|
+
for (const [name, value] of Object.entries(params)) {
|
|
121
|
+
if (PARAM_BLOCKLIST.has(name)) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const zodType = getZodType(schema[name]);
|
|
125
|
+
if (!hasEquivalentType(zodType, value)) {
|
|
126
|
+
throw new Error(`parameter ${name} has type ${zodType} but value ${value} is not of equivalent type`);
|
|
127
|
+
}
|
|
128
|
+
const transformedName = transformArgName(zodType, name);
|
|
129
|
+
const transformedValue = transformValue(zodType, value);
|
|
130
|
+
transformed[transformedName] = transformedValue;
|
|
131
|
+
}
|
|
132
|
+
return transformed;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=transformation.js.map
|