@metamask/utils
Advanced tools
Comparing version 3.6.0 to 4.0.0
@@ -7,4 +7,6 @@ export * from './assert'; | ||
export * from './collections'; | ||
export * from './encryption-types'; | ||
export * from './hex'; | ||
export * from './json'; | ||
export * from './keyring'; | ||
export * from './logging'; | ||
@@ -15,2 +17,3 @@ export * from './misc'; | ||
export * from './time'; | ||
export * from './transaction-types'; | ||
export * from './versions'; |
@@ -23,4 +23,6 @@ "use strict"; | ||
__exportStar(require("./collections"), exports); | ||
__exportStar(require("./encryption-types"), exports); | ||
__exportStar(require("./hex"), exports); | ||
__exportStar(require("./json"), exports); | ||
__exportStar(require("./keyring"), exports); | ||
__exportStar(require("./logging"), exports); | ||
@@ -31,3 +33,4 @@ __exportStar(require("./misc"), exports); | ||
__exportStar(require("./time"), exports); | ||
__exportStar(require("./transaction-types"), exports); | ||
__exportStar(require("./versions"), exports); | ||
//# sourceMappingURL=index.js.map |
import { Infer, Struct } from 'superstruct'; | ||
import { AssertionErrorConstructor } from './assert'; | ||
export declare const JsonStruct: Struct<Json, null>; | ||
/** | ||
@@ -11,2 +10,15 @@ * Any JSON-compatible value. | ||
/** | ||
* A struct to check if the given value is a valid JSON-serializable value. | ||
* | ||
* Note that this struct is unsafe. For safe validation, use {@link JsonStruct}. | ||
*/ | ||
export declare const UnsafeJsonStruct: Struct<Json>; | ||
/** | ||
* A struct to check if the given value is a valid JSON-serializable value. | ||
* | ||
* This struct sanitizes the value before validating it, so that it is safe to | ||
* use with untrusted input. | ||
*/ | ||
export declare const JsonStruct: Struct<Json, null>; | ||
/** | ||
* Check if the given value is a valid {@link Json} value, i.e., a value that is | ||
@@ -20,2 +32,9 @@ * serializable to JSON. | ||
/** | ||
* Get the size of a JSON value in bytes. This also validates the value. | ||
* | ||
* @param value - The JSON value to get the size of. | ||
* @returns The size of the JSON value in bytes. | ||
*/ | ||
export declare function getJsonSize(value: unknown): number; | ||
/** | ||
* The string '2.0'. | ||
@@ -61,3 +80,3 @@ */ | ||
export declare type JsonRpcError = OptionalField<Infer<typeof JsonRpcErrorStruct>, 'data'>; | ||
export declare const JsonRpcParamsStruct: Struct<Record<string, Json> | Json[] | undefined, null>; | ||
export declare const JsonRpcParamsStruct: Struct<Json[] | Record<string, Json> | undefined, null>; | ||
export declare type JsonRpcParams = Infer<typeof JsonRpcParamsStruct>; | ||
@@ -68,3 +87,3 @@ export declare const JsonRpcRequestStruct: Struct<{ | ||
jsonrpc: "2.0"; | ||
params?: Record<string, Json> | Json[] | undefined; | ||
params?: Json[] | Record<string, Json> | undefined; | ||
}, { | ||
@@ -74,3 +93,3 @@ id: Struct<string | number | null, null>; | ||
method: Struct<string, null>; | ||
params: Struct<Record<string, Json> | Json[] | undefined, null>; | ||
params: Struct<Json[] | Record<string, Json> | undefined, null>; | ||
}>; | ||
@@ -89,3 +108,3 @@ export declare type InferWithParams<Type extends Struct<any>, Params extends JsonRpcParams> = Omit<Infer<Type>, 'params'> & (keyof Params extends undefined ? { | ||
jsonrpc: "2.0"; | ||
params?: Record<string, Json> | Json[] | undefined; | ||
params?: Json[] | Record<string, Json> | undefined; | ||
}, Omit<{ | ||
@@ -95,3 +114,3 @@ id: Struct<string | number | null, null>; | ||
method: Struct<string, null>; | ||
params: Struct<Record<string, Json> | Json[] | undefined, null>; | ||
params: Struct<Json[] | Record<string, Json> | undefined, null>; | ||
}, "id">>; | ||
@@ -322,12 +341,2 @@ /** | ||
export declare function getJsonRpcIdValidator(options?: JsonRpcValidatorOptions): (id: unknown) => id is string | number | null; | ||
/** | ||
* Checks whether a value is JSON serializable and counts the total number | ||
* of bytes needed to store the serialized version of the value. | ||
* | ||
* @param jsObject - Potential JSON serializable object. | ||
* @param skipSizingProcess - Skip JSON size calculation (default: false). | ||
* @returns Tuple [isValid, plainTextSizeInBytes] containing a boolean that signals whether | ||
* the value was serializable and a number of bytes that it will use when serialized to JSON. | ||
*/ | ||
export declare function validateJsonAndGetSize(jsObject: unknown, skipSizingProcess?: boolean): [isValid: boolean, plainTextSizeInBytes: number]; | ||
export {}; |
214
dist/json.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.validateJsonAndGetSize = exports.getJsonRpcIdValidator = exports.assertIsJsonRpcError = exports.isJsonRpcError = exports.assertIsJsonRpcFailure = exports.isJsonRpcFailure = exports.assertIsJsonRpcSuccess = exports.isJsonRpcSuccess = exports.assertIsJsonRpcResponse = exports.isJsonRpcResponse = exports.assertIsPendingJsonRpcResponse = exports.isPendingJsonRpcResponse = exports.JsonRpcResponseStruct = exports.JsonRpcFailureStruct = exports.JsonRpcSuccessStruct = exports.PendingJsonRpcResponseStruct = exports.assertIsJsonRpcRequest = exports.isJsonRpcRequest = exports.assertIsJsonRpcNotification = exports.isJsonRpcNotification = exports.JsonRpcNotificationStruct = exports.JsonRpcRequestStruct = exports.JsonRpcParamsStruct = exports.JsonRpcErrorStruct = exports.JsonRpcIdStruct = exports.JsonRpcVersionStruct = exports.jsonrpc2 = exports.isValidJson = exports.JsonStruct = void 0; | ||
exports.getJsonRpcIdValidator = exports.assertIsJsonRpcError = exports.isJsonRpcError = exports.assertIsJsonRpcFailure = exports.isJsonRpcFailure = exports.assertIsJsonRpcSuccess = exports.isJsonRpcSuccess = exports.assertIsJsonRpcResponse = exports.isJsonRpcResponse = exports.assertIsPendingJsonRpcResponse = exports.isPendingJsonRpcResponse = exports.JsonRpcResponseStruct = exports.JsonRpcFailureStruct = exports.JsonRpcSuccessStruct = exports.PendingJsonRpcResponseStruct = exports.assertIsJsonRpcRequest = exports.isJsonRpcRequest = exports.assertIsJsonRpcNotification = exports.isJsonRpcNotification = exports.JsonRpcNotificationStruct = exports.JsonRpcRequestStruct = exports.JsonRpcParamsStruct = exports.JsonRpcErrorStruct = exports.JsonRpcIdStruct = exports.JsonRpcVersionStruct = exports.jsonrpc2 = exports.getJsonSize = exports.isValidJson = exports.JsonStruct = exports.UnsafeJsonStruct = void 0; | ||
const superstruct_1 = require("superstruct"); | ||
const assert_1 = require("./assert"); | ||
const misc_1 = require("./misc"); | ||
exports.JsonStruct = (0, superstruct_1.define)('Json', (value) => { | ||
const [isValid] = validateJsonAndGetSize(value, true); | ||
if (!isValid) { | ||
return 'Expected a valid JSON-serializable value'; | ||
/** | ||
* A struct to check if the given value is finite number. Superstruct's | ||
* `number()` struct does not check if the value is finite. | ||
* | ||
* @returns A struct to check if the given value is finite number. | ||
*/ | ||
const finiteNumber = () => (0, superstruct_1.define)('finite number', (value) => { | ||
return (0, superstruct_1.is)(value, (0, superstruct_1.number)()) && Number.isFinite(value); | ||
}); | ||
/** | ||
* A struct to check if the given value is a valid JSON-serializable value. | ||
* | ||
* Note that this struct is unsafe. For safe validation, use {@link JsonStruct}. | ||
*/ | ||
// We cannot infer the type of the struct, because it is recursive. | ||
exports.UnsafeJsonStruct = (0, superstruct_1.union)([ | ||
(0, superstruct_1.literal)(null), | ||
(0, superstruct_1.boolean)(), | ||
finiteNumber(), | ||
(0, superstruct_1.string)(), | ||
(0, superstruct_1.array)((0, superstruct_1.lazy)(() => exports.UnsafeJsonStruct)), | ||
(0, superstruct_1.record)((0, superstruct_1.string)(), (0, superstruct_1.lazy)(() => exports.UnsafeJsonStruct)), | ||
]); | ||
/** | ||
* A struct to check if the given value is a valid JSON-serializable value. | ||
* | ||
* This struct sanitizes the value before validating it, so that it is safe to | ||
* use with untrusted input. | ||
*/ | ||
exports.JsonStruct = (0, superstruct_1.define)('Json', (value, context) => { | ||
/** | ||
* Helper function that runs the given struct validator and returns the | ||
* validation errors, if any. If the value is valid, it returns `true`. | ||
* | ||
* @param innerValue - The value to validate. | ||
* @param struct - The struct to use for validation. | ||
* @returns The validation errors, or `true` if the value is valid. | ||
*/ | ||
function checkStruct(innerValue, struct) { | ||
const iterator = struct.validator(innerValue, context); | ||
const errors = [...iterator]; | ||
if (errors.length > 0) { | ||
return errors; | ||
} | ||
return true; | ||
} | ||
return true; | ||
try { | ||
// The plain value must be a valid JSON value, but it may be altered in the | ||
// process of JSON serialization, so we need to validate it again after | ||
// serialization. This has the added benefit that the returned error messages | ||
// will be more helpful, as they will point to the exact location of the | ||
// invalid value. | ||
// | ||
// This seems overcomplicated, but without checking the plain value first, | ||
// there are some cases where the validation passes, even though the value is | ||
// not valid JSON. For example, `undefined` is not valid JSON, but serializing | ||
// it will remove it from the object, so the validation will pass. | ||
const unsafeResult = checkStruct(value, exports.UnsafeJsonStruct); | ||
if (unsafeResult !== true) { | ||
return unsafeResult; | ||
} | ||
// JavaScript engines are highly optimized for this specific use case of | ||
// JSON parsing and stringifying, so there should be no performance impact. | ||
return checkStruct(JSON.parse(JSON.stringify(value)), exports.UnsafeJsonStruct); | ||
} | ||
catch (error) { | ||
if (error instanceof RangeError) { | ||
return 'Circular reference detected'; | ||
} | ||
return false; | ||
} | ||
}); | ||
@@ -26,2 +90,14 @@ /** | ||
/** | ||
* Get the size of a JSON value in bytes. This also validates the value. | ||
* | ||
* @param value - The JSON value to get the size of. | ||
* @returns The size of the JSON value in bytes. | ||
*/ | ||
function getJsonSize(value) { | ||
(0, assert_1.assertStruct)(value, exports.JsonStruct, 'Invalid JSON value'); | ||
const json = JSON.stringify(value); | ||
return new TextEncoder().encode(json).byteLength; | ||
} | ||
exports.getJsonSize = getJsonSize; | ||
/** | ||
* The string '2.0'. | ||
@@ -277,126 +353,2 @@ */ | ||
exports.getJsonRpcIdValidator = getJsonRpcIdValidator; | ||
/** | ||
* Checks whether a value is JSON serializable and counts the total number | ||
* of bytes needed to store the serialized version of the value. | ||
* | ||
* @param jsObject - Potential JSON serializable object. | ||
* @param skipSizingProcess - Skip JSON size calculation (default: false). | ||
* @returns Tuple [isValid, plainTextSizeInBytes] containing a boolean that signals whether | ||
* the value was serializable and a number of bytes that it will use when serialized to JSON. | ||
*/ | ||
function validateJsonAndGetSize(jsObject, skipSizingProcess = false) { | ||
const seenObjects = new Set(); | ||
/** | ||
* Checks whether a value is JSON serializable and counts the total number | ||
* of bytes needed to store the serialized version of the value. | ||
* | ||
* This function assumes the encoding of the JSON is done in UTF-8. | ||
* | ||
* @param value - Potential JSON serializable value. | ||
* @param skipSizing - Skip JSON size calculation (default: false). | ||
* @returns Tuple [isValid, plainTextSizeInBytes] containing a boolean that signals whether | ||
* the value was serializable and a number of bytes that it will use when serialized to JSON. | ||
*/ | ||
function getJsonSerializableInfo(value, skipSizing) { | ||
if (value === undefined) { | ||
return [false, 0]; | ||
} | ||
else if (value === null) { | ||
// Return already specified constant size for null (special object) | ||
return [true, skipSizing ? 0 : misc_1.JsonSize.Null]; | ||
} | ||
// Check and calculate sizes for basic (and some special) types | ||
const typeOfValue = typeof value; | ||
try { | ||
if (typeOfValue === 'function') { | ||
return [false, 0]; | ||
} | ||
else if (typeOfValue === 'string' || value instanceof String) { | ||
return [ | ||
true, | ||
skipSizing | ||
? 0 | ||
: (0, misc_1.calculateStringSize)(value) + misc_1.JsonSize.Quote * 2, | ||
]; | ||
} | ||
else if (typeOfValue === 'boolean' || value instanceof Boolean) { | ||
if (skipSizing) { | ||
return [true, 0]; | ||
} | ||
// eslint-disable-next-line eqeqeq | ||
return [true, value == true ? misc_1.JsonSize.True : misc_1.JsonSize.False]; | ||
} | ||
else if (typeOfValue === 'number' || value instanceof Number) { | ||
if (skipSizing) { | ||
return [true, 0]; | ||
} | ||
return [true, (0, misc_1.calculateNumberSize)(value)]; | ||
} | ||
else if (value instanceof Date) { | ||
if (skipSizing) { | ||
return [true, 0]; | ||
} | ||
return [ | ||
true, | ||
// Note: Invalid dates will serialize to null | ||
isNaN(value.getDate()) | ||
? misc_1.JsonSize.Null | ||
: misc_1.JsonSize.Date + misc_1.JsonSize.Quote * 2, | ||
]; | ||
} | ||
} | ||
catch (_) { | ||
return [false, 0]; | ||
} | ||
// If object is not plain and cannot be serialized properly, | ||
// stop here and return false for serialization | ||
if (!(0, misc_1.isPlainObject)(value) && !Array.isArray(value)) { | ||
return [false, 0]; | ||
} | ||
// Circular object detection (handling) | ||
// Check if the same object already exists | ||
if (seenObjects.has(value)) { | ||
return [false, 0]; | ||
} | ||
// Add new object to the seen objects set | ||
// Only the plain objects should be added (Primitive types are skipped) | ||
seenObjects.add(value); | ||
// Continue object decomposition | ||
try { | ||
return [ | ||
true, | ||
Object.entries(value).reduce((sum, [key, nestedValue], idx, arr) => { | ||
// Recursively process next nested object or primitive type | ||
// eslint-disable-next-line prefer-const | ||
let [valid, size] = getJsonSerializableInfo(nestedValue, skipSizing); | ||
if (!valid) { | ||
throw new Error('JSON validation did not pass. Validation process stopped.'); | ||
} | ||
// Circular object detection | ||
// Once a child node is visited and processed remove it from the set. | ||
// This will prevent false positives with the same adjacent objects. | ||
seenObjects.delete(value); | ||
if (skipSizing) { | ||
return 0; | ||
} | ||
// Objects will have be serialized with "key": value, | ||
// therefore we include the key in the calculation here | ||
const keySize = Array.isArray(value) | ||
? 0 | ||
: key.length + misc_1.JsonSize.Comma + misc_1.JsonSize.Colon * 2; | ||
const separator = idx < arr.length - 1 ? misc_1.JsonSize.Comma : 0; | ||
return sum + keySize + size + separator; | ||
}, | ||
// Starts at 2 because the serialized JSON string data (plain text) | ||
// will minimally contain {}/[] | ||
skipSizing ? 0 : misc_1.JsonSize.Wrapper * 2), | ||
]; | ||
} | ||
catch (_) { | ||
return [false, 0]; | ||
} | ||
} | ||
return getJsonSerializableInfo(jsObject, skipSizingProcess); | ||
} | ||
exports.validateJsonAndGetSize = validateJsonAndGetSize; | ||
//# sourceMappingURL=json.js.map |
{ | ||
"name": "@metamask/utils", | ||
"version": "3.6.0", | ||
"version": "4.0.0", | ||
"description": "Various JavaScript/TypeScript utilities of wide relevance to the MetaMask codebase.", | ||
@@ -5,0 +5,0 @@ "repository": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
240827
3217