Refactor routing in App component to enhance navigation and improve error handling by integrating dynamic routes and updating the NotFound route.
This commit is contained in:
226
node_modules/devalue/README.md
generated
vendored
Normal file
226
node_modules/devalue/README.md
generated
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
# devalue
|
||||
|
||||
Like `JSON.stringify`, but handles
|
||||
|
||||
- cyclical references (`obj.self = obj`)
|
||||
- repeated references (`[value, value]`)
|
||||
- `undefined`, `Infinity`, `NaN`, `-0`
|
||||
- regular expressions
|
||||
- dates
|
||||
- `Map` and `Set`
|
||||
- `BigInt`
|
||||
- `ArrayBuffer` and Typed Arrays
|
||||
- custom types via replacers, reducers and revivers
|
||||
|
||||
Try it out [here](https://svelte.dev/repl/138d70def7a748ce9eda736ef1c71239?version=3.49.0).
|
||||
|
||||
## Goals:
|
||||
|
||||
- Performance
|
||||
- Security (see [XSS mitigation](#xss-mitigation))
|
||||
- Compact output
|
||||
|
||||
## Non-goals:
|
||||
|
||||
- Human-readable output
|
||||
- Stringifying functions
|
||||
|
||||
## Usage
|
||||
|
||||
There are two ways to use `devalue`:
|
||||
|
||||
### `uneval`
|
||||
|
||||
This function takes a JavaScript value and returns the JavaScript code to create an equivalent value — sort of like `eval` in reverse:
|
||||
|
||||
```js
|
||||
import * as devalue from 'devalue';
|
||||
|
||||
let obj = { message: 'hello' };
|
||||
devalue.uneval(obj); // '{message:"hello"}'
|
||||
|
||||
obj.self = obj;
|
||||
devalue.uneval(obj); // '(function(a){a.message="hello";a.self=a;return a}({}))'
|
||||
```
|
||||
|
||||
Use `uneval` when you want the most compact possible output and don't want to include any code for parsing the serialized value.
|
||||
|
||||
### `stringify` and `parse`
|
||||
|
||||
These two functions are analogous to `JSON.stringify` and `JSON.parse`:
|
||||
|
||||
```js
|
||||
import * as devalue from 'devalue';
|
||||
|
||||
let obj = { message: 'hello' };
|
||||
|
||||
let stringified = devalue.stringify(obj); // '[{"message":1},"hello"]'
|
||||
devalue.parse(stringified); // { message: 'hello' }
|
||||
|
||||
obj.self = obj;
|
||||
|
||||
stringified = devalue.stringify(obj); // '[{"message":1,"self":0},"hello"]'
|
||||
devalue.parse(stringified); // { message: 'hello', self: [Circular] }
|
||||
```
|
||||
|
||||
Use `stringify` and `parse` when evaluating JavaScript isn't an option.
|
||||
|
||||
### `unflatten`
|
||||
|
||||
In the case where devalued data is one part of a larger JSON string, `unflatten` allows you to revive just the bit you need:
|
||||
|
||||
```js
|
||||
import * as devalue from 'devalue';
|
||||
|
||||
const json = `{
|
||||
"type": "data",
|
||||
"data": ${devalue.stringify(data)}
|
||||
}`;
|
||||
|
||||
const data = devalue.unflatten(JSON.parse(json).data);
|
||||
```
|
||||
|
||||
## Custom types
|
||||
|
||||
You can serialize and deserialize custom types by passing a second argument to `stringify` containing an object of types and their _reducers_, and a second argument to `parse` or `unflatten` containing an object of types and their _revivers_:
|
||||
|
||||
```js
|
||||
class Vector {
|
||||
constructor(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
magnitude() {
|
||||
return Math.sqrt(this.x * this.x + this.y * this.y);
|
||||
}
|
||||
}
|
||||
|
||||
const stringified = devalue.stringify(new Vector(30, 40), {
|
||||
Vector: (value) => value instanceof Vector && [value.x, value.y]
|
||||
});
|
||||
|
||||
console.log(stringified); // [["Vector",1],[2,3],30,40]
|
||||
|
||||
const vector = devalue.parse(stringified, {
|
||||
Vector: ([x, y]) => new Vector(x, y)
|
||||
});
|
||||
|
||||
console.log(vector.magnitude()); // 50
|
||||
```
|
||||
|
||||
If a function passed to `stringify` returns a truthy value, it's treated as a match.
|
||||
|
||||
You can also use custom types with `uneval` by specifying a custom replacer:
|
||||
|
||||
```js
|
||||
devalue.uneval(vector, (value, uneval) => {
|
||||
if (value instanceof Vector) {
|
||||
return `new Vector(${value.x},${value.y})`;
|
||||
}
|
||||
}); // `new Vector(30,40)`
|
||||
```
|
||||
|
||||
Note that any variables referenced in the resulting JavaScript (like `Vector` in the example above) must be in scope when it runs.
|
||||
|
||||
## Error handling
|
||||
|
||||
If `uneval` or `stringify` encounters a function or a non-POJO that isn't handled by a custom replacer/reducer, it will throw an error. You can find where in the input data the offending value lives by inspecting `error.path`:
|
||||
|
||||
```js
|
||||
try {
|
||||
const map = new Map();
|
||||
map.set('key', function invalid() {});
|
||||
|
||||
uneval({
|
||||
object: {
|
||||
array: [map]
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e.path); // '.object.array[0].get("key")'
|
||||
}
|
||||
```
|
||||
|
||||
## XSS mitigation
|
||||
|
||||
Say you're server-rendering a page and want to serialize some state, which could include user input. `JSON.stringify` doesn't protect against XSS attacks:
|
||||
|
||||
```js
|
||||
const state = {
|
||||
userinput: `</script><script src='https://evil.com/mwahaha.js'>`
|
||||
};
|
||||
|
||||
const template = `
|
||||
<script>
|
||||
// NEVER DO THIS
|
||||
var preloaded = ${JSON.stringify(state)};
|
||||
</script>`;
|
||||
```
|
||||
|
||||
Which would result in this:
|
||||
|
||||
```html
|
||||
<script>
|
||||
// NEVER DO THIS
|
||||
var preloaded = {"userinput":"
|
||||
</script>
|
||||
<script src="https://evil.com/mwahaha.js">
|
||||
"};
|
||||
</script>
|
||||
```
|
||||
|
||||
Using `uneval` or `stringify`, we're protected against that attack:
|
||||
|
||||
```js
|
||||
const template = `
|
||||
<script>
|
||||
var preloaded = ${uneval(state)};
|
||||
</script>`;
|
||||
```
|
||||
|
||||
```html
|
||||
<script>
|
||||
var preloaded = {
|
||||
userinput:
|
||||
"\\u003C\\u002Fscript\\u003E\\u003Cscript src='https:\\u002F\\u002Fevil.com\\u002Fmwahaha.js'\\u003E"
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
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`:
|
||||
|
||||
```js
|
||||
const value = (0, eval)('(' + str + ')');
|
||||
```
|
||||
|
||||
## Other security considerations
|
||||
|
||||
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.
|
||||
|
||||
When using `eval`, ensure that you call it _indirectly_ so that the evaluated code doesn't have access to the surrounding scope:
|
||||
|
||||
```js
|
||||
{
|
||||
const sensitiveData = 'Setec Astronomy';
|
||||
eval('sendToEvilServer(sensitiveData)'); // pwned :(
|
||||
(0, eval)('sendToEvilServer(sensitiveData)'); // nice try, evildoer!
|
||||
}
|
||||
```
|
||||
|
||||
Using `new Function(code)` is akin to using indirect eval.
|
||||
|
||||
## See also
|
||||
|
||||
- [lave](https://github.com/jed/lave) by Jed Schmidt
|
||||
- [arson](https://github.com/benjamn/arson) by Ben Newman. The `stringify`/`parse` approach in `devalue` was inspired by `arson`
|
||||
- [oson](https://github.com/KnorpelSenf/oson) by Steffen Trog
|
||||
- [tosource](https://github.com/marcello3d/node-tosource) by Marcello Bastéa-Forte
|
||||
- [serialize-javascript](https://github.com/yahoo/serialize-javascript) by Eric Ferraiuolo
|
||||
- [jsesc](https://github.com/mathiasbynens/jsesc) by Mathias Bynens
|
||||
- [superjson](https://github.com/blitz-js/superjson) by Blitz
|
||||
- [next-json](https://github.com/iccicci/next-json) by Daniele Ricci
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
Reference in New Issue
Block a user