Comparing version 1.2.2 to 2.0.0
type Options = { | ||
strict?: boolean; | ||
}; | ||
declare function destr(value: any, options?: Options): any; | ||
declare function destr<T = unknown>(value: any, options?: Options): T; | ||
declare function safeDestr<T = unknown>(value: any, options?: Options): T; | ||
export { Options, destr as default }; | ||
export { Options, destr as default, destr, safeDestr }; |
{ | ||
"name": "destr", | ||
"version": "1.2.2", | ||
"version": "2.0.0", | ||
"description": "A faster, secure and convenient alternative for JSON.parse", | ||
@@ -12,32 +12,35 @@ "repository": "unjs/destr", | ||
"import": "./dist/index.mjs", | ||
"require": "./dist/index.cjs" | ||
"require": "./lib/index.cjs" | ||
} | ||
}, | ||
"main": "./dist/index.cjs", | ||
"main": "./lib/index.cjs", | ||
"module": "./dist/index.mjs", | ||
"types": "./dist/index.d.ts", | ||
"files": [ | ||
"dist" | ||
"dist", | ||
"lib" | ||
], | ||
"scripts": { | ||
"bench": "pnpm build && node ./bench.mjs", | ||
"build": "unbuild", | ||
"dev": "vitest dev", | ||
"lint": "eslint --ext .ts . && prettier -c src test", | ||
"lint:fix": "eslint --ext .ts . --fix && prettier -w src test", | ||
"release": "pnpm test && pnpm build && changelogen --release --push && npm publish", | ||
"test": "pnpm lint && vitest run --coverage" | ||
}, | ||
"devDependencies": { | ||
"@hapi/bourne": "^3.0.0", | ||
"@vitest/coverage-c8": "^0.25.3", | ||
"@vitest/coverage-v8": "^0.32.0", | ||
"benchmark": "^2.1.4", | ||
"eslint": "^8.29.0", | ||
"eslint-config-unjs": "^0.0.2", | ||
"secure-json-parse": "^2.6.0", | ||
"standard-version": "^9.5.0", | ||
"typescript": "^4.9.3", | ||
"unbuild": "^1.0.1", | ||
"vitest": "^0.25.3" | ||
"changelogen": "^0.5.3", | ||
"eslint": "^8.42.0", | ||
"eslint-config-unjs": "^0.2.1", | ||
"prettier": "^2.8.8", | ||
"secure-json-parse": "^2.7.0", | ||
"typescript": "^5.1.3", | ||
"unbuild": "^1.2.1", | ||
"vitest": "^0.32.0" | ||
}, | ||
"packageManager": "pnpm@7.18.0", | ||
"scripts": { | ||
"bench": "pnpm build && node ./bench.cjs", | ||
"build": "unbuild", | ||
"dev": "vitest dev", | ||
"lint": "eslint --ext .ts .", | ||
"release": "pnpm test && pnpm build && standard-version && git push --follow-tags && pnpm publish", | ||
"test": "pnpm lint && vitest run --coverage" | ||
} | ||
"packageManager": "pnpm@8.6.2" | ||
} |
161
README.md
# destr | ||
> A faster, secure and convenient alternative for [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse): | ||
[![npm version][npm-version-src]][npm-version-href] | ||
[![npm downloads][npm-downloads-src]][npm-downloads-href] | ||
[![bundle][bundle-src]][bundle-href] | ||
[![License][license-src]][license-href] | ||
[![npm version][npm-v-src]][npm-v-href] | ||
[![npm downloads][npm-d-src]][npm-d-href] | ||
[![bundle phobia][bundlephobia-src]][bundlephobia-href] | ||
A faster, secure and convenient alternative for [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse). | ||
@@ -13,8 +14,13 @@ ## Usage | ||
Install using npm or yarn: | ||
Install dependency: | ||
```bash | ||
# npm | ||
npm i destr | ||
# or | ||
# yarn | ||
yarn add destr | ||
# pnpm | ||
pnpm i destr | ||
``` | ||
@@ -25,7 +31,7 @@ | ||
```js | ||
// ESM | ||
import { destr, safeDestr } from "destr"; | ||
// CommonJS | ||
const destr = require('destr') | ||
// ESM | ||
import destr from 'destr' | ||
const { destr, safeDestr } = require("destr"); | ||
``` | ||
@@ -36,62 +42,79 @@ | ||
```js | ||
import destr from 'https://deno.land/x/destr/src/index.ts' | ||
import { destr, safeDestr } from "https://deno.land/x/destr/src/index.ts"; | ||
console.log(destr('{ "deno": "yay" }')) | ||
console.log(destr('{ "deno": "yay" }')); | ||
``` | ||
## Why? | ||
**Fast fallback to input if is not string:** | ||
### ✅ Type Safe | ||
```ts | ||
const obj = JSON.parse("{}"); // obj type is any | ||
const obj = destr("{}"); // obj type is unknown by default | ||
const obj = destr<MyInterface>("{}"); // obj is well-typed | ||
``` | ||
### ✅ Fast fallback to input if is not string | ||
> 🚀 Up to 500 faster than `JSON.parse`! | ||
```js | ||
// Uncaught SyntaxError: Unexpected token u in JSON at position 0 | ||
JSON.parse() | ||
JSON.parse(); | ||
// undefined | ||
destr() | ||
destr(); | ||
``` | ||
**Fast lookup for known string values:** | ||
### ✅ Fast lookup for known string values | ||
> 🚀 Up to 900 times faster than `JSON.parse`! | ||
```js | ||
// Uncaught SyntaxError: Unexpected token T in JSON at position 0 | ||
JSON.parse('TRUE') | ||
JSON.parse("TRUE"); | ||
// true | ||
destr('TRUE') | ||
destr("TRUE"); | ||
``` | ||
**Fallback to original value if parse fails (empty or any plain string):** | ||
### ✅ Fallback to original value if parse fails (empty or any plain string) | ||
> 🚀 Up to 900 times faster than `JSON.parse`! | ||
```js | ||
// Uncaught SyntaxError: Unexpected token s in JSON at position 0 | ||
JSON.parse('salam') | ||
JSON.parse("salam"); | ||
// "salam" | ||
destr('salam') | ||
destr("salam"); | ||
``` | ||
**Avoid prototype pollution:** | ||
**Note:** This fails in safe/strict mode with `safeDestr`. | ||
### ✅ Avoid prototype pollution | ||
```js | ||
const input = '{ "user": { "__proto__": { "isAdmin": true } } }' | ||
const input = '{ "user": { "__proto__": { "isAdmin": true } } }'; | ||
// { user: { __proto__: { isAdmin: true } } } | ||
JSON.parse(input) | ||
JSON.parse(input); | ||
// { user: {} } | ||
destr(input) | ||
destr(input); | ||
``` | ||
### Strict Mode | ||
### ✅ Strict Mode | ||
If `{ strict: true }` passed as second argument, `destr` will throw an error if the input is not a valid JSON string or parsing fails. (non string values and built-ins will be still returned as-is) | ||
When using `safeDestr` it will throw an error if the input is not a valid JSON string or parsing fails. (non string values and built-ins will be still returned as-is) | ||
```js | ||
// Returns "[foo" | ||
destr('[foo') | ||
safeDestr("[foo"); | ||
// Throws an error | ||
destr('[foo', { strict: true }) | ||
safeDestr("[foo", { strict: true }); | ||
``` | ||
@@ -101,3 +124,3 @@ | ||
Locally try with `pnpm benchmark`. Below are esults on Node.js 18.11.0 with MBA M2. | ||
Locally try with `pnpm benchmark`. Below are esults on Node.js **v18.16.0** with MBA M2. | ||
@@ -108,39 +131,39 @@ **Note** `destr` is sometimes little bit slower than `JSON.parse` when parsing a valid JSON string mainly because of transform to avoid [prototype pollution](https://learn.snyk.io/lessons/prototype-pollution/javascript/) which can lead to serious security issues if not being sanitized. In the other words, `destr` is better when input is not always a json string or from untrusted source like request body. | ||
=== Non-string fallback == | ||
JSON.parse x 10,323,718 ops/sec ±0.45% (96 runs sampled) | ||
destr x 1,057,268,114 ops/sec ±1.71% (90 runs sampled) | ||
destr (strict) x 977,215,995 ops/sec ±1.43% (97 runs sampled) | ||
JSON.parse x 9,498,532 ops/sec ±0.57% (96 runs sampled) | ||
destr x 153,323,211 ops/sec ±0.13% (99 runs sampled) | ||
safeDestr x 64,237,062 ops/sec ±0.22% (96 runs sampled) | ||
sjson: | ||
@hapi/bourne x 10,151,985 ops/sec ±0.76% (96 runs sampled) | ||
@hapi/bourne x 9,190,459 ops/sec ±0.50% (93 runs sampled) | ||
Fastest is destr | ||
=== Known values == | ||
JSON.parse x 16,359,358 ops/sec ±0.90% (92 runs sampled) | ||
destr x 107,849,085 ops/sec ±0.34% (97 runs sampled) | ||
destr (strict) x 107,891,427 ops/sec ±0.34% (99 runs sampled) | ||
sjson x 14,216,957 ops/sec ±0.98% (89 runs sampled) | ||
@hapi/bourne x 15,209,152 ops/sec ±1.08% (88 runs sampled) | ||
Fastest is destr (strict),destr | ||
JSON.parse x 14,260,909 ops/sec ±0.54% (95 runs sampled) | ||
destr x 72,916,945 ops/sec ±0.15% (98 runs sampled) | ||
safeDestr x 36,544,906 ops/sec ±0.31% (98 runs sampled) | ||
sjson x 11,157,730 ops/sec ±0.53% (96 runs sampled) | ||
@hapi/bourne x 13,241,853 ops/sec ±0.73% (93 runs sampled) | ||
Fastest is destr | ||
=== Plain string == | ||
JSON.parse (try-catch) x 211,560 ops/sec ±0.84% (92 runs sampled) | ||
destr x 60,315,113 ops/sec ±0.46% (98 runs sampled) | ||
destr (strict): | ||
sjson (try-catch) x 186,492 ops/sec ±0.70% (97 runs sampled) | ||
@hapi/bourne: | ||
=== plain string == | ||
JSON.parse (try-catch) x 10,603,912 ops/sec ±0.75% (91 runs sampled) | ||
destr x 82,123,481 ops/sec ±2.37% (99 runs sampled) | ||
safeDestr x 40,737,935 ops/sec ±0.97% (96 runs sampled) | ||
sjson (try-catch) x 9,194,305 ops/sec ±1.96% (94 runs sampled) | ||
@hapi/bourne x 10,816,232 ops/sec ±1.59% (90 runs sampled) | ||
Fastest is destr | ||
=== standard object == | ||
JSON.parse x 492,180 ops/sec ±0.98% (98 runs sampled) | ||
destr x 356,819 ops/sec ±0.40% (98 runs sampled) | ||
destr (strict) x 412,955 ops/sec ±0.88% (94 runs sampled) | ||
sjson x 437,376 ops/sec ±0.42% (102 runs sampled) | ||
@hapi/bourne x 457,020 ops/sec ±0.81% (99 runs sampled) | ||
=== package.json == | ||
JSON.parse x 403,428 ops/sec ±0.31% (101 runs sampled) | ||
destr x 338,668 ops/sec ±0.27% (97 runs sampled) | ||
safeDestr x 335,756 ops/sec ±0.29% (98 runs sampled) | ||
sjson x 355,493 ops/sec ±0.15% (101 runs sampled) | ||
@hapi/bourne x 384,948 ops/sec ±0.24% (98 runs sampled) | ||
Fastest is JSON.parse | ||
=== invalid syntax == | ||
JSON.parse (try-catch) x 493,739 ops/sec ±0.51% (98 runs sampled) | ||
destr x 405,848 ops/sec ±0.56% (100 runs sampled) | ||
destr (strict) x 409,514 ops/sec ±0.57% (101 runs sampled) | ||
sjson (try-catch) x 435,406 ops/sec ±0.41% (100 runs sampled) | ||
@hapi/bourne x 467,163 ops/sec ±0.42% (99 runs sampled) | ||
=== broken object == | ||
JSON.parse (try-catch) x 406,262 ops/sec ±0.18% (100 runs sampled) | ||
destr x 337,602 ops/sec ±0.37% (99 runs sampled) | ||
safeDestr x 320,071 ops/sec ±0.35% (97 runs sampled) | ||
sjson (try-catch) x 326,689 ops/sec ±0.41% (97 runs sampled) | ||
@hapi/bourne x 313,024 ops/sec ±0.91% (94 runs sampled) | ||
Fastest is JSON.parse (try-catch) | ||
@@ -153,13 +176,11 @@ ``` | ||
<!-- Refs --> | ||
[npm-v-src]: https://img.shields.io/npm/v/destr?style=flat-square | ||
[npm-v-href]: https://npmjs.com/package/destr | ||
<!-- Badges --> | ||
[npm-d-src]: https://img.shields.io/npm/dm/destr?style=flat-square | ||
[npm-d-href]: https://npmjs.com/package/destr | ||
[github-actions-src]: https://img.shields.io/github/workflow/status/unjs/destr/ci/master?style=flat-square | ||
[github-actions-href]: https://github.com/unjs/destr/actions?query=workflow%3Aci | ||
[bundlephobia-src]: https://img.shields.io/bundlephobia/min/destr?style=flat-square | ||
[bundlephobia-href]: https://bundlephobia.com/result?p=destr | ||
[npm-version-src]: https://img.shields.io/npm/v/destr?style=flat&colorA=18181B&colorB=F0DB4F | ||
[npm-version-href]: https://npmjs.com/package/destr | ||
[npm-downloads-src]: https://img.shields.io/npm/dm/destr?style=flat&colorA=18181B&colorB=F0DB4F | ||
[npm-downloads-href]: https://npmjs.com/package/destr | ||
[bundle-src]: https://img.shields.io/bundlephobia/minzip/destr?style=flat&colorA=18181B&colorB=F0DB4F | ||
[bundle-href]: https://bundlephobia.com/result?p=destr | ||
[license-src]: https://img.shields.io/github/license/unjs/destr.svg?style=flat&colorA=18181B&colorB=F0DB4F | ||
[license-href]: https://github.com/unjs/destr/blob/main/LICENSE |
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
11925
7
151
181
11