Skip to content

Commit edfc7fa

Browse files
committed
rename devalue function to uneval, update README
1 parent 15488cc commit edfc7fa

File tree

4 files changed

+51
-19
lines changed

4 files changed

+51
-19
lines changed

README.md

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,32 +25,58 @@ Try it out on [runkit.com](https://npm.runkit.com/devalue).
2525

2626
## Usage
2727

28+
There are two ways to use `devalue`:
29+
30+
### `uneval`
31+
32+
This function takes a JavaScript value and returns the JavaScript code to create an equivalent value — sort of like `eval` in reverse:
33+
34+
```js
35+
import { uneval } from 'devalue';
36+
37+
let obj = { message: 'hello' };
38+
uneval(obj); // '{message:"hello"}'
39+
40+
obj.self = obj;
41+
uneval(obj); // '(function(a){a.message="hello";a.self=a;return a}({}))'
42+
```
43+
44+
Use `uneval` when you want the most compact possible output and don't want to include any code for parsing the serialized value.
45+
46+
### `stringify` and `parse`
47+
48+
These two functions are analogous to `JSON.stringify` and `JSON.parse`:
49+
2850
```js
29-
import { devalue } from 'devalue';
51+
import { stringify, parse } from 'devalue';
3052

31-
let obj = { a: 1, b: 2 };
32-
obj.c = 3;
53+
let obj = { message: 'hello' };
3354

34-
devalue(obj); // '{a:1,b:2,c:3}'
55+
let stringified = stringify(obj); // '[{"message":1},"hello"]'
56+
parse(stringified); // { message: 'hello' }
3557

3658
obj.self = obj;
37-
devalue(obj); // '(function(a){a.a=1;a.b=2;a.c=3;a.self=a;return a}({}))'
59+
60+
stringified = stringify(obj); // '[{"message":1,"self":0},"hello"]'
61+
parse(stringified); // { message: 'hello', self: [Circular] }
3862
```
3963

64+
Use `stringify` and `parse` when evaluating JavaScript isn't an option.
65+
4066
## Error handling
4167

42-
If `devalue` encounters a function or a non-POJO, it will throw an error. You can find where in the input data the offending value lives by inspecting `error.path`:
68+
If `uneval` or `stringify` encounters a function or a non-POJO, it will throw an error. You can find where in the input data the offending value lives by inspecting `error.path`:
4369

4470
```js
4571
try {
4672
const map = new Map();
4773
map.set('key', function invalid() {});
4874

49-
devalue({
75+
uneval({
5076
object: {
5177
array: [map]
5278
}
53-
})
79+
});
5480
} catch (e) {
5581
console.log(e.path); // '.object.array[0].get("key")'
5682
}
@@ -84,12 +110,12 @@ Which would result in this:
84110
</script>
85111
```
86112
87-
Using `devalue`, we're protected against that attack:
113+
Using `uneval` or `stringify`, we're protected against that attack:
88114
89115
```js
90116
const template = `
91117
<script>
92-
var preloaded = ${devalue(state)};
118+
var preloaded = ${uneval(state)};
93119
</script>`;
94120
```
95121
@@ -102,15 +128,15 @@ const template = `
102128
</script>
103129
```
104130
105-
This, along with the fact that `devalue` bails on functions and non-POJOs, stops attackers from executing arbitrary code. Strings generated by `devalue` can be safely deserialized with `eval` or `new Function`:
131+
This, along with the fact that `uneval` and `stringify` bail on functions and non-POJOs, stops attackers from executing arbitrary code. Strings generated by `uneval` can be safely deserialized with `eval` or `new Function`:
106132
107133
```js
108134
const value = (0, eval)('(' + str + ')');
109135
```
110136
111137
## Other security considerations
112138
113-
While `devalue` prevents the XSS vulnerability shown above, meaning you can use it to send data from server to client, **you should not send user data from client to server** using the same method. Since it has to be evaluated, an attacker that successfully submitted data that bypassed `devalue` would have access to your system.
139+
While `uneval` prevents the XSS vulnerability shown above, meaning you can use it to send data from server to client, **you should not send user data from client to server** using the same method. Since it has to be evaluated, an attacker that successfully submitted data that bypassed `uneval` would have access to your system.
114140
115141
When using `eval`, ensure that you call it _indirectly_ so that the evaluated code doesn't have access to the surrounding scope:
116142

index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1-
export { devalue } from './src/devalue.js';
1+
export { uneval } from './src/uneval.js';
22
export { parse } from './src/parse.js';
33
export { stringify } from './src/stringify.js';
4+
5+
export function devalue() {
6+
throw new Error(
7+
'The `devalue` export has been removed. Use `uneval` instead'
8+
);
9+
}

src/devalue.js renamed to src/uneval.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const object_proto_names = Object.getOwnPropertyNames(Object.prototype)
1818
* Turn a value into the JavaScript that creates an equivalent value
1919
* @param {any} value
2020
*/
21-
export function devalue(value) {
21+
export function uneval(value) {
2222
const counts = new Map();
2323

2424
/** @type {string[]} */

test/test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as vm from 'vm';
22
import * as assert from 'uvu/assert';
33
import * as uvu from 'uvu';
4-
import { devalue, parse, stringify } from '../index.js';
4+
import { uneval, parse, stringify } from '../index.js';
55

66
const fixtures = {
77
basics: [
@@ -350,10 +350,10 @@ const fixtures = {
350350
};
351351

352352
for (const [name, tests] of Object.entries(fixtures)) {
353-
const test = uvu.suite(`devalue: ${name}`);
353+
const test = uvu.suite(`uneval: ${name}`);
354354
for (const t of tests) {
355355
test(t.name, () => {
356-
const actual = devalue(t.value);
356+
const actual = uneval(t.value);
357357
const expected = t.js;
358358
assert.equal(actual, expected);
359359
});
@@ -390,7 +390,7 @@ for (const [name, tests] of Object.entries(fixtures)) {
390390
test.run();
391391
}
392392

393-
for (const fn of [devalue, stringify]) {
393+
for (const fn of [uneval, stringify]) {
394394
uvu.test(`${fn.name} throws for non-POJOs`, () => {
395395
class Foo {}
396396
const foo = new Foo();
@@ -432,7 +432,7 @@ for (const fn of [devalue, stringify]) {
432432
uvu.test('does not create duplicate parameter names', () => {
433433
const foo = new Array(20000).fill(0).map((_, i) => i);
434434
const bar = foo.map((_, i) => ({ [i]: foo[i] }));
435-
const serialized = devalue([foo, ...bar]);
435+
const serialized = uneval([foo, ...bar]);
436436

437437
eval(serialized);
438438
});

0 commit comments

Comments
 (0)