feat: docker compose maybe
This commit is contained in:
7
node_modules/devalue/LICENSE
generated
vendored
Normal file
7
node_modules/devalue/LICENSE
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
Copyright (c) 2018-19 [these people](https://github.com/rich-harris/devalue/graphs/contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
225
node_modules/devalue/README.md
generated
vendored
Normal file
225
node_modules/devalue/README.md
generated
vendored
Normal file
@ -0,0 +1,225 @@
|
||||
# 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`
|
||||
- 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 serialize 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)
|
3
node_modules/devalue/index.js
generated
vendored
Normal file
3
node_modules/devalue/index.js
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
export { uneval } from './src/uneval.js';
|
||||
export { parse, unflatten } from './src/parse.js';
|
||||
export { stringify } from './src/stringify.js';
|
33
node_modules/devalue/package.json
generated
vendored
Normal file
33
node_modules/devalue/package.json
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "devalue",
|
||||
"description": "Gets the job done when JSON.stringify can't",
|
||||
"version": "4.3.2",
|
||||
"repository": "Rich-Harris/devalue",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/index.d.ts",
|
||||
"import": "./index.js",
|
||||
"default": "./index.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"src",
|
||||
"types"
|
||||
],
|
||||
"types": "./types/index.d.ts",
|
||||
"devDependencies": {
|
||||
"dts-buddy": "^0.0.4",
|
||||
"publint": "^0.1.7",
|
||||
"typescript": "^3.1.3",
|
||||
"uvu": "^0.5.6"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "dts-buddy",
|
||||
"test": "uvu test",
|
||||
"prepublishOnly": "npm test && publint && npm run build"
|
||||
},
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@8.5.1"
|
||||
}
|
6
node_modules/devalue/src/constants.js
generated
vendored
Normal file
6
node_modules/devalue/src/constants.js
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
export const UNDEFINED = -1;
|
||||
export const HOLE = -2;
|
||||
export const NAN = -3;
|
||||
export const POSITIVE_INFINITY = -4;
|
||||
export const NEGATIVE_INFINITY = -5;
|
||||
export const NEGATIVE_ZERO = -6;
|
133
node_modules/devalue/src/parse.js
generated
vendored
Normal file
133
node_modules/devalue/src/parse.js
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
import {
|
||||
HOLE,
|
||||
NAN,
|
||||
NEGATIVE_INFINITY,
|
||||
NEGATIVE_ZERO,
|
||||
POSITIVE_INFINITY,
|
||||
UNDEFINED
|
||||
} from './constants.js';
|
||||
|
||||
/**
|
||||
* Revive a value serialized with `devalue.stringify`
|
||||
* @param {string} serialized
|
||||
* @param {Record<string, (value: any) => any>} [revivers]
|
||||
*/
|
||||
export function parse(serialized, revivers) {
|
||||
return unflatten(JSON.parse(serialized), revivers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revive a value flattened with `devalue.stringify`
|
||||
* @param {number | any[]} parsed
|
||||
* @param {Record<string, (value: any) => any>} [revivers]
|
||||
*/
|
||||
export function unflatten(parsed, revivers) {
|
||||
if (typeof parsed === 'number') return hydrate(parsed, true);
|
||||
|
||||
if (!Array.isArray(parsed) || parsed.length === 0) {
|
||||
throw new Error('Invalid input');
|
||||
}
|
||||
|
||||
const values = /** @type {any[]} */ (parsed);
|
||||
|
||||
const hydrated = Array(values.length);
|
||||
|
||||
/**
|
||||
* @param {number} index
|
||||
* @returns {any}
|
||||
*/
|
||||
function hydrate(index, standalone = false) {
|
||||
if (index === UNDEFINED) return undefined;
|
||||
if (index === NAN) return NaN;
|
||||
if (index === POSITIVE_INFINITY) return Infinity;
|
||||
if (index === NEGATIVE_INFINITY) return -Infinity;
|
||||
if (index === NEGATIVE_ZERO) return -0;
|
||||
|
||||
if (standalone) throw new Error(`Invalid input`);
|
||||
|
||||
if (index in hydrated) return hydrated[index];
|
||||
|
||||
const value = values[index];
|
||||
|
||||
if (!value || typeof value !== 'object') {
|
||||
hydrated[index] = value;
|
||||
} else if (Array.isArray(value)) {
|
||||
if (typeof value[0] === 'string') {
|
||||
const type = value[0];
|
||||
|
||||
const reviver = revivers?.[type];
|
||||
if (reviver) {
|
||||
return (hydrated[index] = reviver(hydrate(value[1])));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'Date':
|
||||
hydrated[index] = new Date(value[1]);
|
||||
break;
|
||||
|
||||
case 'Set':
|
||||
const set = new Set();
|
||||
hydrated[index] = set;
|
||||
for (let i = 1; i < value.length; i += 1) {
|
||||
set.add(hydrate(value[i]));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Map':
|
||||
const map = new Map();
|
||||
hydrated[index] = map;
|
||||
for (let i = 1; i < value.length; i += 2) {
|
||||
map.set(hydrate(value[i]), hydrate(value[i + 1]));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'RegExp':
|
||||
hydrated[index] = new RegExp(value[1], value[2]);
|
||||
break;
|
||||
|
||||
case 'Object':
|
||||
hydrated[index] = Object(value[1]);
|
||||
break;
|
||||
|
||||
case 'BigInt':
|
||||
hydrated[index] = BigInt(value[1]);
|
||||
break;
|
||||
|
||||
case 'null':
|
||||
const obj = Object.create(null);
|
||||
hydrated[index] = obj;
|
||||
for (let i = 1; i < value.length; i += 2) {
|
||||
obj[value[i]] = hydrate(value[i + 1]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown type ${type}`);
|
||||
}
|
||||
} else {
|
||||
const array = new Array(value.length);
|
||||
hydrated[index] = array;
|
||||
|
||||
for (let i = 0; i < value.length; i += 1) {
|
||||
const n = value[i];
|
||||
if (n === HOLE) continue;
|
||||
|
||||
array[i] = hydrate(n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/** @type {Record<string, any>} */
|
||||
const object = {};
|
||||
hydrated[index] = object;
|
||||
|
||||
for (const key in value) {
|
||||
const n = value[key];
|
||||
object[key] = hydrate(n);
|
||||
}
|
||||
}
|
||||
|
||||
return hydrated[index];
|
||||
}
|
||||
|
||||
return hydrate(0);
|
||||
}
|
198
node_modules/devalue/src/stringify.js
generated
vendored
Normal file
198
node_modules/devalue/src/stringify.js
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
import {
|
||||
DevalueError,
|
||||
get_type,
|
||||
is_plain_object,
|
||||
is_primitive,
|
||||
stringify_string
|
||||
} from './utils.js';
|
||||
import {
|
||||
HOLE,
|
||||
NAN,
|
||||
NEGATIVE_INFINITY,
|
||||
NEGATIVE_ZERO,
|
||||
POSITIVE_INFINITY,
|
||||
UNDEFINED
|
||||
} from './constants.js';
|
||||
|
||||
/**
|
||||
* Turn a value into a JSON string that can be parsed with `devalue.parse`
|
||||
* @param {any} value
|
||||
* @param {Record<string, (value: any) => any>} [reducers]
|
||||
*/
|
||||
export function stringify(value, reducers) {
|
||||
/** @type {any[]} */
|
||||
const stringified = [];
|
||||
|
||||
/** @type {Map<any, number>} */
|
||||
const indexes = new Map();
|
||||
|
||||
/** @type {Array<{ key: string, fn: (value: any) => any }>} */
|
||||
const custom = [];
|
||||
for (const key in reducers) {
|
||||
custom.push({ key, fn: reducers[key] });
|
||||
}
|
||||
|
||||
/** @type {string[]} */
|
||||
const keys = [];
|
||||
|
||||
let p = 0;
|
||||
|
||||
/** @param {any} thing */
|
||||
function flatten(thing) {
|
||||
if (typeof thing === 'function') {
|
||||
throw new DevalueError(`Cannot stringify a function`, keys);
|
||||
}
|
||||
|
||||
if (indexes.has(thing)) return indexes.get(thing);
|
||||
|
||||
if (thing === undefined) return UNDEFINED;
|
||||
if (Number.isNaN(thing)) return NAN;
|
||||
if (thing === Infinity) return POSITIVE_INFINITY;
|
||||
if (thing === -Infinity) return NEGATIVE_INFINITY;
|
||||
if (thing === 0 && 1 / thing < 0) return NEGATIVE_ZERO;
|
||||
|
||||
const index = p++;
|
||||
indexes.set(thing, index);
|
||||
|
||||
for (const { key, fn } of custom) {
|
||||
const value = fn(thing);
|
||||
if (value) {
|
||||
stringified[index] = `["${key}",${flatten(value)}]`;
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
let str = '';
|
||||
|
||||
if (is_primitive(thing)) {
|
||||
str = stringify_primitive(thing);
|
||||
} else {
|
||||
const type = get_type(thing);
|
||||
|
||||
switch (type) {
|
||||
case 'Number':
|
||||
case 'String':
|
||||
case 'Boolean':
|
||||
str = `["Object",${stringify_primitive(thing)}]`;
|
||||
break;
|
||||
|
||||
case 'BigInt':
|
||||
str = `["BigInt",${thing}]`;
|
||||
break;
|
||||
|
||||
case 'Date':
|
||||
str = `["Date","${thing.toISOString()}"]`;
|
||||
break;
|
||||
|
||||
case 'RegExp':
|
||||
const { source, flags } = thing;
|
||||
str = flags
|
||||
? `["RegExp",${stringify_string(source)},"${flags}"]`
|
||||
: `["RegExp",${stringify_string(source)}]`;
|
||||
break;
|
||||
|
||||
case 'Array':
|
||||
str = '[';
|
||||
|
||||
for (let i = 0; i < thing.length; i += 1) {
|
||||
if (i > 0) str += ',';
|
||||
|
||||
if (i in thing) {
|
||||
keys.push(`[${i}]`);
|
||||
str += flatten(thing[i]);
|
||||
keys.pop();
|
||||
} else {
|
||||
str += HOLE;
|
||||
}
|
||||
}
|
||||
|
||||
str += ']';
|
||||
|
||||
break;
|
||||
|
||||
case 'Set':
|
||||
str = '["Set"';
|
||||
|
||||
for (const value of thing) {
|
||||
str += `,${flatten(value)}`;
|
||||
}
|
||||
|
||||
str += ']';
|
||||
break;
|
||||
|
||||
case 'Map':
|
||||
str = '["Map"';
|
||||
|
||||
for (const [key, value] of thing) {
|
||||
keys.push(
|
||||
`.get(${is_primitive(key) ? stringify_primitive(key) : '...'})`
|
||||
);
|
||||
str += `,${flatten(key)},${flatten(value)}`;
|
||||
}
|
||||
|
||||
str += ']';
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!is_plain_object(thing)) {
|
||||
throw new DevalueError(
|
||||
`Cannot stringify arbitrary non-POJOs`,
|
||||
keys
|
||||
);
|
||||
}
|
||||
|
||||
if (Object.getOwnPropertySymbols(thing).length > 0) {
|
||||
throw new DevalueError(
|
||||
`Cannot stringify POJOs with symbolic keys`,
|
||||
keys
|
||||
);
|
||||
}
|
||||
|
||||
if (Object.getPrototypeOf(thing) === null) {
|
||||
str = '["null"';
|
||||
for (const key in thing) {
|
||||
keys.push(`.${key}`);
|
||||
str += `,${stringify_string(key)},${flatten(thing[key])}`;
|
||||
keys.pop();
|
||||
}
|
||||
str += ']';
|
||||
} else {
|
||||
str = '{';
|
||||
let started = false;
|
||||
for (const key in thing) {
|
||||
if (started) str += ',';
|
||||
started = true;
|
||||
keys.push(`.${key}`);
|
||||
str += `${stringify_string(key)}:${flatten(thing[key])}`;
|
||||
keys.pop();
|
||||
}
|
||||
str += '}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stringified[index] = str;
|
||||
return index;
|
||||
}
|
||||
|
||||
const index = flatten(value);
|
||||
|
||||
// special case — value is represented as a negative index
|
||||
if (index < 0) return `${index}`;
|
||||
|
||||
return `[${stringified.join(',')}]`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} thing
|
||||
* @returns {string}
|
||||
*/
|
||||
function stringify_primitive(thing) {
|
||||
const type = typeof thing;
|
||||
if (type === 'string') return stringify_string(thing);
|
||||
if (thing instanceof String) return stringify_string(thing.toString());
|
||||
if (thing === void 0) return UNDEFINED.toString();
|
||||
if (thing === 0 && 1 / thing < 0) return NEGATIVE_ZERO.toString();
|
||||
if (type === 'bigint') return `["BigInt","${thing}"]`;
|
||||
return String(thing);
|
||||
}
|
312
node_modules/devalue/src/uneval.js
generated
vendored
Normal file
312
node_modules/devalue/src/uneval.js
generated
vendored
Normal file
@ -0,0 +1,312 @@
|
||||
import {
|
||||
DevalueError,
|
||||
escaped,
|
||||
get_type,
|
||||
is_plain_object,
|
||||
is_primitive,
|
||||
stringify_string
|
||||
} from './utils.js';
|
||||
|
||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';
|
||||
const unsafe_chars = /[<\b\f\n\r\t\0\u2028\u2029]/g;
|
||||
const reserved =
|
||||
/^(?:do|if|in|for|int|let|new|try|var|byte|case|char|else|enum|goto|long|this|void|with|await|break|catch|class|const|final|float|short|super|throw|while|yield|delete|double|export|import|native|return|switch|throws|typeof|boolean|default|extends|finally|package|private|abstract|continue|debugger|function|volatile|interface|protected|transient|implements|instanceof|synchronized)$/;
|
||||
|
||||
/**
|
||||
* Turn a value into the JavaScript that creates an equivalent value
|
||||
* @param {any} value
|
||||
* @param {(value: any) => string | void} [replacer]
|
||||
*/
|
||||
export function uneval(value, replacer) {
|
||||
const counts = new Map();
|
||||
|
||||
/** @type {string[]} */
|
||||
const keys = [];
|
||||
|
||||
const custom = new Map();
|
||||
|
||||
/** @param {any} thing */
|
||||
function walk(thing) {
|
||||
if (typeof thing === 'function') {
|
||||
throw new DevalueError(`Cannot stringify a function`, keys);
|
||||
}
|
||||
|
||||
if (!is_primitive(thing)) {
|
||||
if (counts.has(thing)) {
|
||||
counts.set(thing, counts.get(thing) + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
counts.set(thing, 1);
|
||||
|
||||
if (replacer) {
|
||||
const str = replacer(thing);
|
||||
|
||||
if (typeof str === 'string') {
|
||||
custom.set(thing, str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const type = get_type(thing);
|
||||
|
||||
switch (type) {
|
||||
case 'Number':
|
||||
case 'BigInt':
|
||||
case 'String':
|
||||
case 'Boolean':
|
||||
case 'Date':
|
||||
case 'RegExp':
|
||||
return;
|
||||
|
||||
case 'Array':
|
||||
/** @type {any[]} */ (thing).forEach((value, i) => {
|
||||
keys.push(`[${i}]`);
|
||||
walk(value);
|
||||
keys.pop();
|
||||
});
|
||||
break;
|
||||
|
||||
case 'Set':
|
||||
Array.from(thing).forEach(walk);
|
||||
break;
|
||||
|
||||
case 'Map':
|
||||
for (const [key, value] of thing) {
|
||||
keys.push(
|
||||
`.get(${is_primitive(key) ? stringify_primitive(key) : '...'})`
|
||||
);
|
||||
walk(value);
|
||||
keys.pop();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!is_plain_object(thing)) {
|
||||
throw new DevalueError(
|
||||
`Cannot stringify arbitrary non-POJOs`,
|
||||
keys
|
||||
);
|
||||
}
|
||||
|
||||
if (Object.getOwnPropertySymbols(thing).length > 0) {
|
||||
throw new DevalueError(
|
||||
`Cannot stringify POJOs with symbolic keys`,
|
||||
keys
|
||||
);
|
||||
}
|
||||
|
||||
for (const key in thing) {
|
||||
keys.push(`.${key}`);
|
||||
walk(thing[key]);
|
||||
keys.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk(value);
|
||||
|
||||
const names = new Map();
|
||||
|
||||
Array.from(counts)
|
||||
.filter((entry) => entry[1] > 1)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.forEach((entry, i) => {
|
||||
names.set(entry[0], get_name(i));
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {any} thing
|
||||
* @returns {string}
|
||||
*/
|
||||
function stringify(thing) {
|
||||
if (names.has(thing)) {
|
||||
return names.get(thing);
|
||||
}
|
||||
|
||||
if (is_primitive(thing)) {
|
||||
return stringify_primitive(thing);
|
||||
}
|
||||
|
||||
if (custom.has(thing)) {
|
||||
return custom.get(thing);
|
||||
}
|
||||
|
||||
const type = get_type(thing);
|
||||
|
||||
switch (type) {
|
||||
case 'Number':
|
||||
case 'String':
|
||||
case 'Boolean':
|
||||
return `Object(${stringify(thing.valueOf())})`;
|
||||
|
||||
case 'RegExp':
|
||||
return `new RegExp(${stringify_string(thing.source)}, "${
|
||||
thing.flags
|
||||
}")`;
|
||||
|
||||
case 'Date':
|
||||
return `new Date(${thing.getTime()})`;
|
||||
|
||||
case 'Array':
|
||||
const members = /** @type {any[]} */ (thing).map((v, i) =>
|
||||
i in thing ? stringify(v) : ''
|
||||
);
|
||||
const tail = thing.length === 0 || thing.length - 1 in thing ? '' : ',';
|
||||
return `[${members.join(',')}${tail}]`;
|
||||
|
||||
case 'Set':
|
||||
case 'Map':
|
||||
return `new ${type}([${Array.from(thing).map(stringify).join(',')}])`;
|
||||
|
||||
default:
|
||||
const obj = `{${Object.keys(thing)
|
||||
.map((key) => `${safe_key(key)}:${stringify(thing[key])}`)
|
||||
.join(',')}}`;
|
||||
const proto = Object.getPrototypeOf(thing);
|
||||
if (proto === null) {
|
||||
return Object.keys(thing).length > 0
|
||||
? `Object.assign(Object.create(null),${obj})`
|
||||
: `Object.create(null)`;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
const str = stringify(value);
|
||||
|
||||
if (names.size) {
|
||||
/** @type {string[]} */
|
||||
const params = [];
|
||||
|
||||
/** @type {string[]} */
|
||||
const statements = [];
|
||||
|
||||
/** @type {string[]} */
|
||||
const values = [];
|
||||
|
||||
names.forEach((name, thing) => {
|
||||
params.push(name);
|
||||
|
||||
if (custom.has(thing)) {
|
||||
values.push(/** @type {string} */ (custom.get(thing)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_primitive(thing)) {
|
||||
values.push(stringify_primitive(thing));
|
||||
return;
|
||||
}
|
||||
|
||||
const type = get_type(thing);
|
||||
|
||||
switch (type) {
|
||||
case 'Number':
|
||||
case 'String':
|
||||
case 'Boolean':
|
||||
values.push(`Object(${stringify(thing.valueOf())})`);
|
||||
break;
|
||||
|
||||
case 'RegExp':
|
||||
values.push(thing.toString());
|
||||
break;
|
||||
|
||||
case 'Date':
|
||||
values.push(`new Date(${thing.getTime()})`);
|
||||
break;
|
||||
|
||||
case 'Array':
|
||||
values.push(`Array(${thing.length})`);
|
||||
/** @type {any[]} */ (thing).forEach((v, i) => {
|
||||
statements.push(`${name}[${i}]=${stringify(v)}`);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'Set':
|
||||
values.push(`new Set`);
|
||||
statements.push(
|
||||
`${name}.${Array.from(thing)
|
||||
.map((v) => `add(${stringify(v)})`)
|
||||
.join('.')}`
|
||||
);
|
||||
break;
|
||||
|
||||
case 'Map':
|
||||
values.push(`new Map`);
|
||||
statements.push(
|
||||
`${name}.${Array.from(thing)
|
||||
.map(([k, v]) => `set(${stringify(k)}, ${stringify(v)})`)
|
||||
.join('.')}`
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
values.push(
|
||||
Object.getPrototypeOf(thing) === null ? 'Object.create(null)' : '{}'
|
||||
);
|
||||
Object.keys(thing).forEach((key) => {
|
||||
statements.push(
|
||||
`${name}${safe_prop(key)}=${stringify(thing[key])}`
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
statements.push(`return ${str}`);
|
||||
|
||||
return `(function(${params.join(',')}){${statements.join(
|
||||
';'
|
||||
)}}(${values.join(',')}))`;
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {number} num */
|
||||
function get_name(num) {
|
||||
let name = '';
|
||||
|
||||
do {
|
||||
name = chars[num % chars.length] + name;
|
||||
num = ~~(num / chars.length) - 1;
|
||||
} while (num >= 0);
|
||||
|
||||
return reserved.test(name) ? `${name}0` : name;
|
||||
}
|
||||
|
||||
/** @param {string} c */
|
||||
function escape_unsafe_char(c) {
|
||||
return escaped[c] || c;
|
||||
}
|
||||
|
||||
/** @param {string} str */
|
||||
function escape_unsafe_chars(str) {
|
||||
return str.replace(unsafe_chars, escape_unsafe_char);
|
||||
}
|
||||
|
||||
/** @param {string} key */
|
||||
function safe_key(key) {
|
||||
return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key)
|
||||
? key
|
||||
: escape_unsafe_chars(JSON.stringify(key));
|
||||
}
|
||||
|
||||
/** @param {string} key */
|
||||
function safe_prop(key) {
|
||||
return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key)
|
||||
? `.${key}`
|
||||
: `[${escape_unsafe_chars(JSON.stringify(key))}]`;
|
||||
}
|
||||
|
||||
/** @param {any} thing */
|
||||
function stringify_primitive(thing) {
|
||||
if (typeof thing === 'string') return stringify_string(thing);
|
||||
if (thing === void 0) return 'void 0';
|
||||
if (thing === 0 && 1 / thing < 0) return '-0';
|
||||
const str = String(thing);
|
||||
if (typeof thing === 'number') return str.replace(/^(-)?0\./, '$1.');
|
||||
if (typeof thing === 'bigint') return thing + 'n';
|
||||
return str;
|
||||
}
|
99
node_modules/devalue/src/utils.js
generated
vendored
Normal file
99
node_modules/devalue/src/utils.js
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
/** @type {Record<string, string>} */
|
||||
export const escaped = {
|
||||
'<': '\\u003C',
|
||||
'\\': '\\\\',
|
||||
'\b': '\\b',
|
||||
'\f': '\\f',
|
||||
'\n': '\\n',
|
||||
'\r': '\\r',
|
||||
'\t': '\\t',
|
||||
'\u2028': '\\u2028',
|
||||
'\u2029': '\\u2029'
|
||||
};
|
||||
|
||||
export class DevalueError extends Error {
|
||||
/**
|
||||
* @param {string} message
|
||||
* @param {string[]} keys
|
||||
*/
|
||||
constructor(message, keys) {
|
||||
super(message);
|
||||
this.name = 'DevalueError';
|
||||
this.path = keys.join('');
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {any} thing */
|
||||
export function is_primitive(thing) {
|
||||
return Object(thing) !== thing;
|
||||
}
|
||||
|
||||
const object_proto_names = /* @__PURE__ */ Object.getOwnPropertyNames(
|
||||
Object.prototype
|
||||
)
|
||||
.sort()
|
||||
.join('\0');
|
||||
|
||||
/** @param {any} thing */
|
||||
export function is_plain_object(thing) {
|
||||
const proto = Object.getPrototypeOf(thing);
|
||||
|
||||
return (
|
||||
proto === Object.prototype ||
|
||||
proto === null ||
|
||||
Object.getOwnPropertyNames(proto).sort().join('\0') === object_proto_names
|
||||
);
|
||||
}
|
||||
|
||||
/** @param {any} thing */
|
||||
export function get_type(thing) {
|
||||
return Object.prototype.toString.call(thing).slice(8, -1);
|
||||
}
|
||||
|
||||
/** @param {string} char */
|
||||
function get_escaped_char(char) {
|
||||
switch (char) {
|
||||
case '"':
|
||||
return '\\"';
|
||||
case '<':
|
||||
return '\\u003C';
|
||||
case '\\':
|
||||
return '\\\\';
|
||||
case '\n':
|
||||
return '\\n';
|
||||
case '\r':
|
||||
return '\\r';
|
||||
case '\t':
|
||||
return '\\t';
|
||||
case '\b':
|
||||
return '\\b';
|
||||
case '\f':
|
||||
return '\\f';
|
||||
case '\u2028':
|
||||
return '\\u2028';
|
||||
case '\u2029':
|
||||
return '\\u2029';
|
||||
default:
|
||||
return char < ' '
|
||||
? `\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`
|
||||
: '';
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {string} str */
|
||||
export function stringify_string(str) {
|
||||
let result = '';
|
||||
let last_pos = 0;
|
||||
const len = str.length;
|
||||
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
const char = str[i];
|
||||
const replacement = get_escaped_char(char);
|
||||
if (replacement) {
|
||||
result += str.slice(last_pos, i) + replacement;
|
||||
last_pos = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return `"${last_pos === 0 ? str : result + str.slice(last_pos)}"`;
|
||||
}
|
26
node_modules/devalue/types/index.d.ts
generated
vendored
Normal file
26
node_modules/devalue/types/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
declare module 'devalue' {
|
||||
|
||||
/**
|
||||
* Turn a value into the JavaScript that creates an equivalent value
|
||||
*
|
||||
*/
|
||||
export function uneval(value: any, replacer?: (value: any) => string | void): string;
|
||||
/**
|
||||
* Turn a value into a JSON string that can be parsed with `devalue.parse`
|
||||
*
|
||||
*/
|
||||
export function stringify(value: any, reducers?: Record<string, (value: any) => any>): string;
|
||||
/**
|
||||
* Revive a value serialized with `devalue.stringify`
|
||||
*
|
||||
*/
|
||||
export function parse(serialized: string, revivers?: Record<string, (value: any) => any>): any;
|
||||
/**
|
||||
* Revive a value flattened with `devalue.stringify`
|
||||
*
|
||||
*/
|
||||
export function unflatten(parsed: number | any[], revivers?: Record<string, (value: any) => any>): any;
|
||||
}
|
||||
|
||||
|
||||
//# sourceMappingURL=index.d.ts.map
|
21
node_modules/devalue/types/index.d.ts.map
generated
vendored
Normal file
21
node_modules/devalue/types/index.d.ts.map
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"version": 3,
|
||||
"file": "index.d.ts",
|
||||
"names": [
|
||||
"uneval",
|
||||
"stringify",
|
||||
"parse",
|
||||
"unflatten"
|
||||
],
|
||||
"sources": [
|
||||
"../src/uneval.js",
|
||||
"../src/stringify.js",
|
||||
"../src/parse.js"
|
||||
],
|
||||
"sourcesContent": [
|
||||
null,
|
||||
null,
|
||||
null
|
||||
],
|
||||
"mappings": ";;;;;;iBAmBgBA,MAAMA;;;;;iBCENC,SAASA;;;;;iBCPTC,KAAKA;;;;;iBASLC,SAASA"
|
||||
}
|
Reference in New Issue
Block a user