(value: unknown, label: string)
| 924 | } |
| 925 | |
| 926 | function decodeJsonValue(value: unknown, label: string): unknown { |
| 927 | let nodes = 0; |
| 928 | const ancestors = new WeakSet<object>(); |
| 929 | const visit = (entry: unknown, depth: number): unknown => { |
| 930 | nodes += 1; |
| 931 | if (nodes > CLIENT_CAPABILITY_JSON_MAX_NODES || depth > CLIENT_CAPABILITY_JSON_MAX_DEPTH) { |
| 932 | throw invalidProtocolFrame(`Invalid ${label}`); |
| 933 | } |
| 934 | if ( |
| 935 | entry === null || |
| 936 | typeof entry === 'string' || |
| 937 | typeof entry === 'boolean' || |
| 938 | (typeof entry === 'number' && Number.isFinite(entry)) |
| 939 | ) { |
| 940 | return entry; |
| 941 | } |
| 942 | if (Array.isArray(entry)) { |
| 943 | if (ancestors.has(entry)) throw invalidProtocolFrame(`Invalid ${label}`); |
| 944 | ancestors.add(entry); |
| 945 | try { |
| 946 | return entry.map((item) => visit(item, depth + 1)); |
| 947 | } finally { |
| 948 | ancestors.delete(entry); |
| 949 | } |
| 950 | } |
| 951 | if (entry && typeof entry === 'object') { |
| 952 | if (ancestors.has(entry)) throw invalidProtocolFrame(`Invalid ${label}`); |
| 953 | ancestors.add(entry); |
| 954 | const output: Record<string, unknown> = {}; |
| 955 | try { |
| 956 | for (const [key, item] of Object.entries(entry)) { |
| 957 | if (key.length === 0 || key.length > 256) throw invalidProtocolFrame(`Invalid ${label}`); |
| 958 | Object.defineProperty(output, key, { |
| 959 | value: visit(item, depth + 1), |
| 960 | enumerable: true, |
| 961 | configurable: true, |
| 962 | writable: true, |
| 963 | }); |
| 964 | } |
| 965 | } finally { |
| 966 | ancestors.delete(entry); |
| 967 | } |
| 968 | return output; |
| 969 | } |
| 970 | throw invalidProtocolFrame(`Invalid ${label}`); |
| 971 | }; |
| 972 | return visit(value, 0); |
| 973 | } |
| 974 | |
| 975 | function requireBoundedString(value: unknown, label: string, maxLength: number): string { |
| 976 | if (typeof value !== 'string' || value.length > maxLength) { |
no test coverage detected