appium-mcp 1.88.5 → 1.89.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 +6 -0
- package/README.md +2 -0
- package/dist/telemetry/wrapOperations.d.ts +8 -0
- package/dist/telemetry/wrapOperations.d.ts.map +1 -1
- package/dist/telemetry/wrapOperations.js +97 -2
- package/dist/telemetry/wrapOperations.js.map +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/telemetry/wrapOperations.ts +109 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [1.89.0](https://github.com/appium/appium-mcp/compare/v1.88.5...v1.89.0) (2026-07-26)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* add payload-free tool result size telemetry ([#476](https://github.com/appium/appium-mcp/issues/476)) ([ca0262d](https://github.com/appium/appium-mcp/commit/ca0262d38a18747f08c1f08c94099c7b82150272))
|
|
6
|
+
|
|
1
7
|
## [1.88.5](https://github.com/appium/appium-mcp/compare/v1.88.4...v1.88.5) (2026-07-26)
|
|
2
8
|
|
|
3
9
|
### Performance Improvements
|
package/README.md
CHANGED
|
@@ -189,6 +189,8 @@ OTEL_TRACES_SAMPLER=parentbased_always_on
|
|
|
189
189
|
|
|
190
190
|
When enabled, appium-mcp creates spans for MCP tool calls, prompt loads, resource reads, and resource template reads. Error status is recorded for thrown operation errors and MCP tool results marked with `isError`. Span attributes intentionally avoid raw screenshots, XML page source, prompts, credentials, and other high-cardinality or sensitive payloads.
|
|
191
191
|
|
|
192
|
+
Tool spans include payload-free result-size attributes: `mcp.tool.result.content_count`, `content_types`, `text_chars`, `resource_count`, `resource_text_chars`, `image_count`, `audio_count`, `base64_chars`, and `base64_bytes_estimate` (all prefixed with `mcp.tool.result.`). `content_types` contains only known MCP types or `other`; payload values, resource URIs, MIME types, and unknown type strings are never recorded. Sizes are counted directly from known result fields without serializing or copying the complete result.
|
|
193
|
+
|
|
192
194
|
For local trace inspection, use the Jaeger setup in `tools/telemetry`:
|
|
193
195
|
|
|
194
196
|
```bash
|
|
@@ -25,6 +25,14 @@ export declare function installTelemetryWrappers(server: FastMCP): void;
|
|
|
25
25
|
* @returns A new tool definition with telemetry spans around the execute function.
|
|
26
26
|
*/
|
|
27
27
|
export declare function wrapToolWithTelemetry(toolDef: ToolDef): ToolDef;
|
|
28
|
+
/**
|
|
29
|
+
* Builds payload-free size attributes for a tool result.
|
|
30
|
+
*
|
|
31
|
+
* This intentionally counts known MCP content fields directly instead of
|
|
32
|
+
* serializing the whole result. Large page sources, UI HTML, and base64 data
|
|
33
|
+
* therefore are not copied or recorded in telemetry.
|
|
34
|
+
*/
|
|
35
|
+
export declare function safeToolResultSizeAttributes(result: unknown): Record<string, number | string[]>;
|
|
28
36
|
export declare function safeInputValueAttributes(args: unknown): Record<string, string | number | boolean | string[]>;
|
|
29
37
|
export {};
|
|
30
38
|
//# sourceMappingURL=wrapOperations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapOperations.d.ts","sourceRoot":"","sources":["../../src/telemetry/wrapOperations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAMrC,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"wrapOperations.d.ts","sourceRoot":"","sources":["../../src/telemetry/wrapOperations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAMrC,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAYjD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAwB9D;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAsB/D;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAwE/F;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAmB5G"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { isSensitiveKey } from '../utils/sensitive.js';
|
|
8
8
|
import { isArgumentValueTelemetryEnabled, safeAttributeValue, safeInputKeys, safeSessionId } from './attributes.js';
|
|
9
9
|
import { getActiveSpan, SpanStatusCode, withSpan } from './tracer.js';
|
|
10
|
+
const SAFE_TOOL_CONTENT_TYPES = new Set(['audio', 'image', 'resource', 'resource_link', 'text']);
|
|
10
11
|
/**
|
|
11
12
|
* Installs telemetry wrappers on the given FastMCP server instance. This should be
|
|
12
13
|
* called early in the server setup process, before registering any tools, prompts,
|
|
@@ -50,14 +51,88 @@ export function wrapToolWithTelemetry(toolDef) {
|
|
|
50
51
|
...toolDef,
|
|
51
52
|
execute: async (args, context) => withSpan(`tools/call ${toolName}`, toolAttributes(toolName, args), async () => {
|
|
52
53
|
const result = await execute(args, context);
|
|
54
|
+
const span = getActiveSpan();
|
|
55
|
+
span?.setAttributes(safeToolResultSizeAttributes(result));
|
|
53
56
|
if (isErrorResult(result)) {
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
span?.setStatus({ code: SpanStatusCode.ERROR });
|
|
58
|
+
span?.setAttribute('mcp.tool.result.is_error', true);
|
|
56
59
|
}
|
|
57
60
|
return result;
|
|
58
61
|
}),
|
|
59
62
|
};
|
|
60
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Builds payload-free size attributes for a tool result.
|
|
66
|
+
*
|
|
67
|
+
* This intentionally counts known MCP content fields directly instead of
|
|
68
|
+
* serializing the whole result. Large page sources, UI HTML, and base64 data
|
|
69
|
+
* therefore are not copied or recorded in telemetry.
|
|
70
|
+
*/
|
|
71
|
+
export function safeToolResultSizeAttributes(result) {
|
|
72
|
+
if (typeof result === 'string') {
|
|
73
|
+
return { 'mcp.tool.result.text_chars': result.length };
|
|
74
|
+
}
|
|
75
|
+
if (!isRecord(result) || !Array.isArray(result.content)) {
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
const contentTypes = new Set();
|
|
79
|
+
let textChars = 0;
|
|
80
|
+
let resourceCount = 0;
|
|
81
|
+
let resourceTextChars = 0;
|
|
82
|
+
let imageCount = 0;
|
|
83
|
+
let audioCount = 0;
|
|
84
|
+
let base64Chars = 0;
|
|
85
|
+
let base64BytesEstimate = 0;
|
|
86
|
+
for (const content of result.content) {
|
|
87
|
+
if (!isRecord(content)) {
|
|
88
|
+
contentTypes.add('other');
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const contentType = typeof content.type === 'string' && SAFE_TOOL_CONTENT_TYPES.has(content.type) ? content.type : 'other';
|
|
92
|
+
contentTypes.add(contentType);
|
|
93
|
+
if (contentType === 'text' && typeof content.text === 'string') {
|
|
94
|
+
textChars += content.text.length;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (contentType === 'image' || contentType === 'audio') {
|
|
98
|
+
if (contentType === 'image') {
|
|
99
|
+
imageCount += 1;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
audioCount += 1;
|
|
103
|
+
}
|
|
104
|
+
if (typeof content.data === 'string') {
|
|
105
|
+
base64Chars += content.data.length;
|
|
106
|
+
base64BytesEstimate += estimateBase64DecodedBytes(content.data);
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (contentType === 'resource') {
|
|
111
|
+
resourceCount += 1;
|
|
112
|
+
if (isRecord(content.resource)) {
|
|
113
|
+
if (typeof content.resource.text === 'string') {
|
|
114
|
+
resourceTextChars += content.resource.text.length;
|
|
115
|
+
}
|
|
116
|
+
if (typeof content.resource.blob === 'string') {
|
|
117
|
+
base64Chars += content.resource.blob.length;
|
|
118
|
+
base64BytesEstimate += estimateBase64DecodedBytes(content.resource.blob);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const attributes = {
|
|
124
|
+
'mcp.tool.result.content_count': result.content.length,
|
|
125
|
+
'mcp.tool.result.content_types': [...contentTypes].sort(),
|
|
126
|
+
'mcp.tool.result.text_chars': textChars,
|
|
127
|
+
'mcp.tool.result.resource_count': resourceCount,
|
|
128
|
+
'mcp.tool.result.resource_text_chars': resourceTextChars,
|
|
129
|
+
'mcp.tool.result.image_count': imageCount,
|
|
130
|
+
'mcp.tool.result.audio_count': audioCount,
|
|
131
|
+
'mcp.tool.result.base64_chars': base64Chars,
|
|
132
|
+
'mcp.tool.result.base64_bytes_estimate': base64BytesEstimate,
|
|
133
|
+
};
|
|
134
|
+
return attributes;
|
|
135
|
+
}
|
|
61
136
|
export function safeInputValueAttributes(args) {
|
|
62
137
|
if (!isArgumentValueTelemetryEnabled()) {
|
|
63
138
|
return {};
|
|
@@ -150,4 +225,24 @@ function inputKeyAttributes(args) {
|
|
|
150
225
|
function isErrorResult(result) {
|
|
151
226
|
return (!!result && typeof result === 'object' && 'isError' in result && result.isError === true);
|
|
152
227
|
}
|
|
228
|
+
function isRecord(value) {
|
|
229
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
230
|
+
}
|
|
231
|
+
function estimateBase64DecodedBytes(value) {
|
|
232
|
+
const commaIndex = value.indexOf(',');
|
|
233
|
+
const hasDataUriPrefix = commaIndex >= 7 && value.slice(commaIndex - 7, commaIndex).toLowerCase() === ';base64';
|
|
234
|
+
const contentStart = hasDataUriPrefix ? commaIndex + 1 : 0;
|
|
235
|
+
const encodedLength = value.length - contentStart;
|
|
236
|
+
if (encodedLength <= 0) {
|
|
237
|
+
return 0;
|
|
238
|
+
}
|
|
239
|
+
let padding = 0;
|
|
240
|
+
if (value.endsWith('==')) {
|
|
241
|
+
padding = 2;
|
|
242
|
+
}
|
|
243
|
+
else if (value.endsWith('=')) {
|
|
244
|
+
padding = 1;
|
|
245
|
+
}
|
|
246
|
+
return Math.max(0, Math.floor((encodedLength * 3) / 4) - padding);
|
|
247
|
+
}
|
|
153
248
|
//# sourceMappingURL=wrapOperations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapOperations.js","sourceRoot":"","sources":["../../src/telemetry/wrapOperations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAC,cAAc,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAC,+BAA+B,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAClH,OAAO,EAAC,aAAa,EAAE,cAAc,EAAE,QAAQ,EAAC,MAAM,aAAa,CAAC;AAYpE;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAe;IACtD,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAuB,CAAC;IAE1E,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,OAAgB,EAAE,EAAE,CAAC,eAAe,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAuB,CAAC;IAE/G,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QAC3C,MAAM,iBAAiB,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAyB,CAAC;QAChF,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC,SAAoB,EAAE,EAAE,CAC3C,iBAAiB,CAAC,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAyB,CAAC;IACnF,CAAC;IAED,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;QAC7C,MAAM,mBAAmB,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAA2B,CAAC;QACtF,MAAM,CAAC,WAAW,GAAG,CAAC,CAAC,WAAwB,EAAE,EAAE,CACjD,mBAAmB,CAAC,yBAAyB,CAAC,WAAW,CAAC,CAAC,CAA2B,CAAC;IAC3F,CAAC;IAED,IAAI,OAAO,MAAM,CAAC,mBAAmB,KAAK,UAAU,EAAE,CAAC;QACrD,MAAM,2BAA2B,GAAG,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAmC,CAAC;QAC9G,MAAM,CAAC,mBAAmB,GAAG,CAAC,CAAC,mBAAwC,EAAE,EAAE,CACzE,2BAA2B,CACzB,iCAAiC,CAAC,mBAAmB,CAAC,CACvD,CAAmC,CAAC;IACzC,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAkC,CAAC;IAC3D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,IAAI,cAAc,CAAC;IAEhD,OAAO;QACL,GAAG,OAAO;QACV,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAC/B,QAAQ,CAAC,cAAc,QAAQ,EAAE,EAAE,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE;YAC5E,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC5C,IAAI,aAAa,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"wrapOperations.js","sourceRoot":"","sources":["../../src/telemetry/wrapOperations.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.89.0",
|
|
4
4
|
"description": "Intelligent MCP server providing AI assistants with powerful tools and resources for Appium mobile automation",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/appium/appium-mcp/issues"
|
package/server.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"name": "io.github.appium/appium-mcp",
|
|
4
4
|
"title": "MCP Appium - Mobile Development and Automation Server",
|
|
5
5
|
"description": "MCP server for Appium mobile automation on iOS and Android devices with test creation tools.",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.89.0",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "appium-mcp",
|
|
11
|
-
"version": "1.
|
|
11
|
+
"version": "1.89.0",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|
|
@@ -21,6 +21,8 @@ type PromptLoad = NonNullable<PromptDef['load']>;
|
|
|
21
21
|
type ResourceLoad = NonNullable<ResourceDef['load']>;
|
|
22
22
|
type ResourceTemplateLoad = NonNullable<ResourceTemplateDef['load']>;
|
|
23
23
|
|
|
24
|
+
const SAFE_TOOL_CONTENT_TYPES = new Set(['audio', 'image', 'resource', 'resource_link', 'text']);
|
|
25
|
+
|
|
24
26
|
/**
|
|
25
27
|
* Installs telemetry wrappers on the given FastMCP server instance. This should be
|
|
26
28
|
* called early in the server setup process, before registering any tools, prompts,
|
|
@@ -77,15 +79,98 @@ export function wrapToolWithTelemetry(toolDef: ToolDef): ToolDef {
|
|
|
77
79
|
execute: async (args, context) =>
|
|
78
80
|
withSpan(`tools/call ${toolName}`, toolAttributes(toolName, args), async () => {
|
|
79
81
|
const result = await execute(args, context);
|
|
82
|
+
const span = getActiveSpan();
|
|
83
|
+
span?.setAttributes(safeToolResultSizeAttributes(result));
|
|
80
84
|
if (isErrorResult(result)) {
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
span?.setStatus({code: SpanStatusCode.ERROR});
|
|
86
|
+
span?.setAttribute('mcp.tool.result.is_error', true);
|
|
83
87
|
}
|
|
84
88
|
return result;
|
|
85
89
|
}),
|
|
86
90
|
};
|
|
87
91
|
}
|
|
88
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Builds payload-free size attributes for a tool result.
|
|
95
|
+
*
|
|
96
|
+
* This intentionally counts known MCP content fields directly instead of
|
|
97
|
+
* serializing the whole result. Large page sources, UI HTML, and base64 data
|
|
98
|
+
* therefore are not copied or recorded in telemetry.
|
|
99
|
+
*/
|
|
100
|
+
export function safeToolResultSizeAttributes(result: unknown): Record<string, number | string[]> {
|
|
101
|
+
if (typeof result === 'string') {
|
|
102
|
+
return {'mcp.tool.result.text_chars': result.length};
|
|
103
|
+
}
|
|
104
|
+
if (!isRecord(result) || !Array.isArray(result.content)) {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const contentTypes = new Set<string>();
|
|
109
|
+
let textChars = 0;
|
|
110
|
+
let resourceCount = 0;
|
|
111
|
+
let resourceTextChars = 0;
|
|
112
|
+
let imageCount = 0;
|
|
113
|
+
let audioCount = 0;
|
|
114
|
+
let base64Chars = 0;
|
|
115
|
+
let base64BytesEstimate = 0;
|
|
116
|
+
|
|
117
|
+
for (const content of result.content) {
|
|
118
|
+
if (!isRecord(content)) {
|
|
119
|
+
contentTypes.add('other');
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const contentType =
|
|
124
|
+
typeof content.type === 'string' && SAFE_TOOL_CONTENT_TYPES.has(content.type) ? content.type : 'other';
|
|
125
|
+
contentTypes.add(contentType);
|
|
126
|
+
|
|
127
|
+
if (contentType === 'text' && typeof content.text === 'string') {
|
|
128
|
+
textChars += content.text.length;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (contentType === 'image' || contentType === 'audio') {
|
|
133
|
+
if (contentType === 'image') {
|
|
134
|
+
imageCount += 1;
|
|
135
|
+
} else {
|
|
136
|
+
audioCount += 1;
|
|
137
|
+
}
|
|
138
|
+
if (typeof content.data === 'string') {
|
|
139
|
+
base64Chars += content.data.length;
|
|
140
|
+
base64BytesEstimate += estimateBase64DecodedBytes(content.data);
|
|
141
|
+
}
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (contentType === 'resource') {
|
|
146
|
+
resourceCount += 1;
|
|
147
|
+
if (isRecord(content.resource)) {
|
|
148
|
+
if (typeof content.resource.text === 'string') {
|
|
149
|
+
resourceTextChars += content.resource.text.length;
|
|
150
|
+
}
|
|
151
|
+
if (typeof content.resource.blob === 'string') {
|
|
152
|
+
base64Chars += content.resource.blob.length;
|
|
153
|
+
base64BytesEstimate += estimateBase64DecodedBytes(content.resource.blob);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const attributes: Record<string, number | string[]> = {
|
|
160
|
+
'mcp.tool.result.content_count': result.content.length,
|
|
161
|
+
'mcp.tool.result.content_types': [...contentTypes].sort(),
|
|
162
|
+
'mcp.tool.result.text_chars': textChars,
|
|
163
|
+
'mcp.tool.result.resource_count': resourceCount,
|
|
164
|
+
'mcp.tool.result.resource_text_chars': resourceTextChars,
|
|
165
|
+
'mcp.tool.result.image_count': imageCount,
|
|
166
|
+
'mcp.tool.result.audio_count': audioCount,
|
|
167
|
+
'mcp.tool.result.base64_chars': base64Chars,
|
|
168
|
+
'mcp.tool.result.base64_bytes_estimate': base64BytesEstimate,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return attributes;
|
|
172
|
+
}
|
|
173
|
+
|
|
89
174
|
export function safeInputValueAttributes(args: unknown): Record<string, string | number | boolean | string[]> {
|
|
90
175
|
if (!isArgumentValueTelemetryEnabled()) {
|
|
91
176
|
return {};
|
|
@@ -213,3 +298,25 @@ function isErrorResult(result: unknown): boolean {
|
|
|
213
298
|
!!result && typeof result === 'object' && 'isError' in result && (result as {isError?: unknown}).isError === true
|
|
214
299
|
);
|
|
215
300
|
}
|
|
301
|
+
|
|
302
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
303
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function estimateBase64DecodedBytes(value: string): number {
|
|
307
|
+
const commaIndex = value.indexOf(',');
|
|
308
|
+
const hasDataUriPrefix = commaIndex >= 7 && value.slice(commaIndex - 7, commaIndex).toLowerCase() === ';base64';
|
|
309
|
+
const contentStart = hasDataUriPrefix ? commaIndex + 1 : 0;
|
|
310
|
+
const encodedLength = value.length - contentStart;
|
|
311
|
+
if (encodedLength <= 0) {
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
let padding = 0;
|
|
316
|
+
if (value.endsWith('==')) {
|
|
317
|
+
padding = 2;
|
|
318
|
+
} else if (value.endsWith('=')) {
|
|
319
|
+
padding = 1;
|
|
320
|
+
}
|
|
321
|
+
return Math.max(0, Math.floor((encodedLength * 3) / 4) - padding);
|
|
322
|
+
}
|