1243 lines
37 KiB
JavaScript
1243 lines
37 KiB
JavaScript
import { H as HttpError, j as json, t as text, S as SvelteKitError } from './index-B2LGyy1l.js';
|
||
import { w as with_request_store } from './root-D12ma0No.js';
|
||
import { a as text_decoder, c as base64_decode } from './utils-D_bK1W85.js';
|
||
import { D as DevalueError, i as is_primitive, g as get_type, a as is_plain_object, e as enumerable_symbols, s as stringify_key, b as stringify_string, v as valid_array_indices } from './index3-C4geC3K_.js';
|
||
|
||
/**
|
||
* Base64 Encodes an arraybuffer
|
||
* @param {ArrayBuffer} arraybuffer
|
||
* @returns {string}
|
||
*/
|
||
function encode64(arraybuffer) {
|
||
const dv = new DataView(arraybuffer);
|
||
let binaryString = "";
|
||
|
||
for (let i = 0; i < arraybuffer.byteLength; i++) {
|
||
binaryString += String.fromCharCode(dv.getUint8(i));
|
||
}
|
||
|
||
return binaryToAscii(binaryString);
|
||
}
|
||
|
||
/**
|
||
* Decodes a base64 string into an arraybuffer
|
||
* @param {string} string
|
||
* @returns {ArrayBuffer}
|
||
*/
|
||
function decode64(string) {
|
||
const binaryString = asciiToBinary(string);
|
||
const arraybuffer = new ArrayBuffer(binaryString.length);
|
||
const dv = new DataView(arraybuffer);
|
||
|
||
for (let i = 0; i < arraybuffer.byteLength; i++) {
|
||
dv.setUint8(i, binaryString.charCodeAt(i));
|
||
}
|
||
|
||
return arraybuffer;
|
||
}
|
||
|
||
const KEY_STRING =
|
||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||
|
||
/**
|
||
* Substitute for atob since it's deprecated in node.
|
||
* Does not do any input validation.
|
||
*
|
||
* @see https://github.com/jsdom/abab/blob/master/lib/atob.js
|
||
*
|
||
* @param {string} data
|
||
* @returns {string}
|
||
*/
|
||
function asciiToBinary(data) {
|
||
if (data.length % 4 === 0) {
|
||
data = data.replace(/==?$/, "");
|
||
}
|
||
|
||
let output = "";
|
||
let buffer = 0;
|
||
let accumulatedBits = 0;
|
||
|
||
for (let i = 0; i < data.length; i++) {
|
||
buffer <<= 6;
|
||
buffer |= KEY_STRING.indexOf(data[i]);
|
||
accumulatedBits += 6;
|
||
if (accumulatedBits === 24) {
|
||
output += String.fromCharCode((buffer & 0xff0000) >> 16);
|
||
output += String.fromCharCode((buffer & 0xff00) >> 8);
|
||
output += String.fromCharCode(buffer & 0xff);
|
||
buffer = accumulatedBits = 0;
|
||
}
|
||
}
|
||
if (accumulatedBits === 12) {
|
||
buffer >>= 4;
|
||
output += String.fromCharCode(buffer);
|
||
} else if (accumulatedBits === 18) {
|
||
buffer >>= 2;
|
||
output += String.fromCharCode((buffer & 0xff00) >> 8);
|
||
output += String.fromCharCode(buffer & 0xff);
|
||
}
|
||
return output;
|
||
}
|
||
|
||
/**
|
||
* Substitute for btoa since it's deprecated in node.
|
||
* Does not do any input validation.
|
||
*
|
||
* @see https://github.com/jsdom/abab/blob/master/lib/btoa.js
|
||
*
|
||
* @param {string} str
|
||
* @returns {string}
|
||
*/
|
||
function binaryToAscii(str) {
|
||
let out = "";
|
||
for (let i = 0; i < str.length; i += 3) {
|
||
/** @type {[number, number, number, number]} */
|
||
const groupsOfSix = [undefined, undefined, undefined, undefined];
|
||
groupsOfSix[0] = str.charCodeAt(i) >> 2;
|
||
groupsOfSix[1] = (str.charCodeAt(i) & 0x03) << 4;
|
||
if (str.length > i + 1) {
|
||
groupsOfSix[1] |= str.charCodeAt(i + 1) >> 4;
|
||
groupsOfSix[2] = (str.charCodeAt(i + 1) & 0x0f) << 2;
|
||
}
|
||
if (str.length > i + 2) {
|
||
groupsOfSix[2] |= str.charCodeAt(i + 2) >> 6;
|
||
groupsOfSix[3] = str.charCodeAt(i + 2) & 0x3f;
|
||
}
|
||
for (let j = 0; j < groupsOfSix.length; j++) {
|
||
if (typeof groupsOfSix[j] === "undefined") {
|
||
out += "=";
|
||
} else {
|
||
out += KEY_STRING[groupsOfSix[j]];
|
||
}
|
||
}
|
||
}
|
||
return out;
|
||
}
|
||
|
||
const UNDEFINED = -1;
|
||
const HOLE = -2;
|
||
const NAN = -3;
|
||
const POSITIVE_INFINITY = -4;
|
||
const NEGATIVE_INFINITY = -5;
|
||
const NEGATIVE_ZERO = -6;
|
||
const SPARSE = -7;
|
||
|
||
/**
|
||
* Revive a value serialized with `devalue.stringify`
|
||
* @param {string} serialized
|
||
* @param {Record<string, (value: any) => any>} [revivers]
|
||
*/
|
||
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]
|
||
*/
|
||
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);
|
||
|
||
/**
|
||
* A set of values currently being hydrated with custom revivers,
|
||
* used to detect invalid cyclical dependencies
|
||
* @type {Set<number> | null}
|
||
*/
|
||
let hydrating = null;
|
||
|
||
/**
|
||
* @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 || typeof index !== 'number') {
|
||
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 && Object.hasOwn(revivers, type)
|
||
? revivers[type]
|
||
: undefined;
|
||
|
||
if (reviver) {
|
||
let i = value[1];
|
||
if (typeof i !== 'number') {
|
||
// if it's not a number, it was serialized by a builtin reviver
|
||
// so we need to munge it into the format expected by a custom reviver
|
||
i = values.push(value[1]) - 1;
|
||
}
|
||
|
||
hydrating ??= new Set();
|
||
|
||
if (hydrating.has(i)) {
|
||
throw new Error('Invalid circular reference');
|
||
}
|
||
|
||
hydrating.add(i);
|
||
hydrated[index] = reviver(hydrate(i));
|
||
hydrating.delete(i);
|
||
|
||
return hydrated[index];
|
||
}
|
||
|
||
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;
|
||
|
||
case 'Int8Array':
|
||
case 'Uint8Array':
|
||
case 'Uint8ClampedArray':
|
||
case 'Int16Array':
|
||
case 'Uint16Array':
|
||
case 'Int32Array':
|
||
case 'Uint32Array':
|
||
case 'Float32Array':
|
||
case 'Float64Array':
|
||
case 'BigInt64Array':
|
||
case 'BigUint64Array': {
|
||
if (values[value[1]][0] !== 'ArrayBuffer') {
|
||
// without this, if we receive malformed input we could
|
||
// end up trying to hydrate in a circle or allocate
|
||
// huge amounts of memory when we call `new TypedArrayConstructor(buffer)`
|
||
throw new Error('Invalid data');
|
||
}
|
||
|
||
const TypedArrayConstructor = globalThis[type];
|
||
const buffer = hydrate(value[1]);
|
||
const typedArray = new TypedArrayConstructor(buffer);
|
||
|
||
hydrated[index] =
|
||
value[2] !== undefined
|
||
? typedArray.subarray(value[2], value[3])
|
||
: typedArray;
|
||
|
||
break;
|
||
}
|
||
|
||
case 'ArrayBuffer': {
|
||
const base64 = value[1];
|
||
if (typeof base64 !== 'string') {
|
||
throw new Error('Invalid ArrayBuffer encoding');
|
||
}
|
||
const arraybuffer = decode64(base64);
|
||
hydrated[index] = arraybuffer;
|
||
break;
|
||
}
|
||
|
||
case 'Temporal.Duration':
|
||
case 'Temporal.Instant':
|
||
case 'Temporal.PlainDate':
|
||
case 'Temporal.PlainTime':
|
||
case 'Temporal.PlainDateTime':
|
||
case 'Temporal.PlainMonthDay':
|
||
case 'Temporal.PlainYearMonth':
|
||
case 'Temporal.ZonedDateTime': {
|
||
const temporalName = type.slice(9);
|
||
// @ts-expect-error TS doesn't know about Temporal yet
|
||
hydrated[index] = Temporal[temporalName].from(value[1]);
|
||
break;
|
||
}
|
||
|
||
case 'URL': {
|
||
const url = new URL(value[1]);
|
||
hydrated[index] = url;
|
||
break;
|
||
}
|
||
|
||
case 'URLSearchParams': {
|
||
const url = new URLSearchParams(value[1]);
|
||
hydrated[index] = url;
|
||
break;
|
||
}
|
||
|
||
default:
|
||
throw new Error(`Unknown type ${type}`);
|
||
}
|
||
} else if (value[0] === SPARSE) {
|
||
// Sparse array encoding: [SPARSE, length, idx, val, idx, val, ...]
|
||
const len = value[1];
|
||
|
||
const array = new Array(len);
|
||
hydrated[index] = array;
|
||
|
||
for (let i = 2; i < value.length; i += 2) {
|
||
const idx = value[i];
|
||
array[idx] = hydrate(value[i + 1]);
|
||
}
|
||
} 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 of Object.keys(value)) {
|
||
if (key === '__proto__') {
|
||
throw new Error('Cannot parse an object with a `__proto__` property');
|
||
}
|
||
|
||
const n = value[key];
|
||
object[key] = hydrate(n);
|
||
}
|
||
}
|
||
|
||
return hydrated[index];
|
||
}
|
||
|
||
return hydrate(0);
|
||
}
|
||
|
||
/**
|
||
* Turn a value into a JSON string that can be parsed with `devalue.parse`
|
||
* @param {any} value
|
||
* @param {Record<string, (value: any) => any>} [reducers]
|
||
*/
|
||
function stringify$1(value, reducers) {
|
||
/** @type {any[]} */
|
||
const stringified = [];
|
||
|
||
/** @type {Map<any, number>} */
|
||
const indexes = new Map();
|
||
|
||
/** @type {Array<{ key: string, fn: (value: any) => any }>} */
|
||
const custom = [];
|
||
if (reducers) {
|
||
for (const key of Object.getOwnPropertyNames(reducers)) {
|
||
custom.push({ key, fn: reducers[key] });
|
||
}
|
||
}
|
||
|
||
/** @type {string[]} */
|
||
const keys = [];
|
||
|
||
let p = 0;
|
||
|
||
/** @param {any} thing */
|
||
function flatten(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;
|
||
|
||
if (indexes.has(thing)) return indexes.get(thing);
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
if (typeof thing === 'function') {
|
||
throw new DevalueError(`Cannot stringify a function`, keys, thing, value);
|
||
}
|
||
|
||
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':
|
||
const valid = !isNaN(thing.getDate());
|
||
str = `["Date","${valid ? thing.toISOString() : ''}"]`;
|
||
break;
|
||
|
||
case 'URL':
|
||
str = `["URL",${stringify_string(thing.toString())}]`;
|
||
break;
|
||
|
||
case 'URLSearchParams':
|
||
str = `["URLSearchParams",${stringify_string(thing.toString())}]`;
|
||
break;
|
||
|
||
case 'RegExp':
|
||
const { source, flags } = thing;
|
||
str = flags
|
||
? `["RegExp",${stringify_string(source)},"${flags}"]`
|
||
: `["RegExp",${stringify_string(source)}]`;
|
||
break;
|
||
|
||
case 'Array': {
|
||
// For dense arrays (no holes), we iterate normally.
|
||
// When we encounter the first hole, we call Object.keys
|
||
// to determine the sparseness, then decide between:
|
||
// - HOLE encoding: [-2, val, -2, ...] (default)
|
||
// - Sparse encoding: [-7, length, idx, val, ...] (for very sparse arrays)
|
||
// Only the sparse path avoids iterating every slot, which
|
||
// is what protects against the DoS of e.g. `arr[1000000] = 1`.
|
||
let mostly_dense = false;
|
||
|
||
str = '[';
|
||
|
||
for (let i = 0; i < thing.length; i += 1) {
|
||
if (i > 0) str += ',';
|
||
|
||
if (Object.hasOwn(thing, i)) {
|
||
keys.push(`[${i}]`);
|
||
str += flatten(thing[i]);
|
||
keys.pop();
|
||
} else if (mostly_dense) {
|
||
// Use dense encoding. The heuristic guarantees the
|
||
// array is only mildly sparse, so iterating over every
|
||
// slot is fine.
|
||
str += HOLE;
|
||
} else {
|
||
// Decide between HOLE encoding and sparse encoding.
|
||
//
|
||
// HOLE encoding: each hole is serialized as the HOLE
|
||
// sentinel (-2). For example, [, "a", ,] becomes
|
||
// [-2, 0, -2]. Each hole costs 3 chars ("-2" + comma).
|
||
//
|
||
// Sparse encoding: lists only populated indices.
|
||
// For example, [, "a", ,] becomes [-7, 3, 1, 0] — the
|
||
// -7 sentinel, the array length (3), then index-value
|
||
// pairs. This avoids paying per-hole, but each element
|
||
// costs extra chars to write its index.
|
||
//
|
||
// The values are the same size either way, so the
|
||
// choice comes down to structural overhead:
|
||
//
|
||
// HOLE overhead:
|
||
// 3 chars per hole ("-2" + comma)
|
||
// = (L - P) * 3
|
||
//
|
||
// Sparse overhead:
|
||
// "-7," — 3 chars (sparse sentinel + comma)
|
||
// + length + "," — (d + 1) chars (array length + comma)
|
||
// + per element: index + "," — (d + 1) chars
|
||
// = (4 + d) + P * (d + 1)
|
||
//
|
||
// where L is the array length, P is the number of
|
||
// populated elements, and d is the number of digits
|
||
// in L (an upper bound on the digits in any index).
|
||
//
|
||
// Sparse encoding is cheaper when:
|
||
// (4 + d) + P * (d + 1) < (L - P) * 3
|
||
const populated_keys = valid_array_indices(/** @type {any[]} */ (thing));
|
||
const population = populated_keys.length;
|
||
const d = String(thing.length).length;
|
||
|
||
const hole_cost = (thing.length - population) * 3;
|
||
const sparse_cost = 4 + d + population * (d + 1);
|
||
|
||
if (hole_cost > sparse_cost) {
|
||
str = '[' + SPARSE + ',' + thing.length;
|
||
for (let j = 0; j < populated_keys.length; j++) {
|
||
const key = populated_keys[j];
|
||
keys.push(`[${key}]`);
|
||
str += ',' + key + ',' + flatten(thing[key]);
|
||
keys.pop();
|
||
}
|
||
break;
|
||
} else {
|
||
mostly_dense = true;
|
||
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)}`;
|
||
keys.pop();
|
||
}
|
||
|
||
str += ']';
|
||
break;
|
||
|
||
case 'Int8Array':
|
||
case 'Uint8Array':
|
||
case 'Uint8ClampedArray':
|
||
case 'Int16Array':
|
||
case 'Uint16Array':
|
||
case 'Int32Array':
|
||
case 'Uint32Array':
|
||
case 'Float32Array':
|
||
case 'Float64Array':
|
||
case 'BigInt64Array':
|
||
case 'BigUint64Array': {
|
||
/** @type {import("./types.js").TypedArray} */
|
||
const typedArray = thing;
|
||
str = '["' + type + '",' + flatten(typedArray.buffer);
|
||
|
||
const a = thing.byteOffset;
|
||
const b = a + thing.byteLength;
|
||
|
||
// handle subarrays
|
||
if (a > 0 || b !== typedArray.buffer.byteLength) {
|
||
const m = +/(\d+)/.exec(type)[1] / 8;
|
||
str += `,${a / m},${b / m}`;
|
||
}
|
||
|
||
str += ']';
|
||
break;
|
||
}
|
||
|
||
case 'ArrayBuffer': {
|
||
/** @type {ArrayBuffer} */
|
||
const arraybuffer = thing;
|
||
const base64 = encode64(arraybuffer);
|
||
|
||
str = `["ArrayBuffer","${base64}"]`;
|
||
break;
|
||
}
|
||
|
||
case 'Temporal.Duration':
|
||
case 'Temporal.Instant':
|
||
case 'Temporal.PlainDate':
|
||
case 'Temporal.PlainTime':
|
||
case 'Temporal.PlainDateTime':
|
||
case 'Temporal.PlainMonthDay':
|
||
case 'Temporal.PlainYearMonth':
|
||
case 'Temporal.ZonedDateTime':
|
||
str = `["${type}",${stringify_string(thing.toString())}]`;
|
||
break;
|
||
|
||
default:
|
||
if (!is_plain_object(thing)) {
|
||
throw new DevalueError(
|
||
`Cannot stringify arbitrary non-POJOs`,
|
||
keys,
|
||
thing,
|
||
value
|
||
);
|
||
}
|
||
|
||
if (enumerable_symbols(thing).length > 0) {
|
||
throw new DevalueError(
|
||
`Cannot stringify POJOs with symbolic keys`,
|
||
keys,
|
||
thing,
|
||
value
|
||
);
|
||
}
|
||
|
||
if (Object.getPrototypeOf(thing) === null) {
|
||
str = '["null"';
|
||
for (const key of Object.keys(thing)) {
|
||
if (key === '__proto__') {
|
||
throw new DevalueError(
|
||
`Cannot stringify objects with __proto__ keys`,
|
||
keys,
|
||
thing,
|
||
value
|
||
);
|
||
}
|
||
|
||
keys.push(stringify_key(key));
|
||
str += `,${stringify_string(key)},${flatten(thing[key])}`;
|
||
keys.pop();
|
||
}
|
||
str += ']';
|
||
} else {
|
||
str = '{';
|
||
let started = false;
|
||
for (const key of Object.keys(thing)) {
|
||
if (key === '__proto__') {
|
||
throw new DevalueError(
|
||
`Cannot stringify objects with __proto__ keys`,
|
||
keys,
|
||
thing,
|
||
value
|
||
);
|
||
}
|
||
|
||
if (started) str += ',';
|
||
started = true;
|
||
keys.push(stringify_key(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);
|
||
}
|
||
|
||
const SVELTE_KIT_ASSETS = "/_svelte_kit_assets";
|
||
const ENDPOINT_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"];
|
||
const MUTATIVE_METHODS = ["POST", "PUT", "PATCH", "DELETE"];
|
||
const PAGE_METHODS = ["GET", "POST", "HEAD"];
|
||
function set_nested_value(object, path_string, value) {
|
||
if (path_string.startsWith("n:")) {
|
||
path_string = path_string.slice(2);
|
||
value = value === "" ? void 0 : parseFloat(value);
|
||
} else if (path_string.startsWith("b:")) {
|
||
path_string = path_string.slice(2);
|
||
value = value === "on";
|
||
}
|
||
deep_set(object, split_path(path_string), value);
|
||
}
|
||
function convert_formdata(data) {
|
||
const result = {};
|
||
for (let key of data.keys()) {
|
||
const is_array = key.endsWith("[]");
|
||
let values = data.getAll(key);
|
||
if (is_array) key = key.slice(0, -2);
|
||
if (values.length > 1 && !is_array) {
|
||
throw new Error(`Form cannot contain duplicated keys — "${key}" has ${values.length} values`);
|
||
}
|
||
values = values.filter(
|
||
(entry) => typeof entry === "string" || entry.name !== "" || entry.size > 0
|
||
);
|
||
if (key.startsWith("n:")) {
|
||
key = key.slice(2);
|
||
values = values.map((v) => v === "" ? void 0 : parseFloat(
|
||
/** @type {string} */
|
||
v
|
||
));
|
||
} else if (key.startsWith("b:")) {
|
||
key = key.slice(2);
|
||
values = values.map((v) => v === "on");
|
||
}
|
||
set_nested_value(result, key, is_array ? values : values[0]);
|
||
}
|
||
return result;
|
||
}
|
||
const BINARY_FORM_CONTENT_TYPE = "application/x-sveltekit-formdata";
|
||
const BINARY_FORM_VERSION = 0;
|
||
const HEADER_BYTES = 1 + 4 + 2;
|
||
async function deserialize_binary_form(request) {
|
||
if (request.headers.get("content-type") !== BINARY_FORM_CONTENT_TYPE) {
|
||
const form_data = await request.formData();
|
||
return { data: convert_formdata(form_data), meta: {}, form_data };
|
||
}
|
||
if (!request.body) {
|
||
throw deserialize_error("no body");
|
||
}
|
||
const content_length = parseInt(request.headers.get("content-length") ?? "");
|
||
if (Number.isNaN(content_length)) {
|
||
throw deserialize_error("invalid Content-Length header");
|
||
}
|
||
const reader = request.body.getReader();
|
||
const chunks = [];
|
||
function get_chunk(index) {
|
||
if (index in chunks) return chunks[index];
|
||
let i = chunks.length;
|
||
while (i <= index) {
|
||
chunks[i] = reader.read().then((chunk) => chunk.value);
|
||
i++;
|
||
}
|
||
return chunks[index];
|
||
}
|
||
async function get_buffer(offset, length) {
|
||
let start_chunk;
|
||
let chunk_start = 0;
|
||
let chunk_index;
|
||
for (chunk_index = 0; ; chunk_index++) {
|
||
const chunk = await get_chunk(chunk_index);
|
||
if (!chunk) return null;
|
||
const chunk_end = chunk_start + chunk.byteLength;
|
||
if (offset >= chunk_start && offset < chunk_end) {
|
||
start_chunk = chunk;
|
||
break;
|
||
}
|
||
chunk_start = chunk_end;
|
||
}
|
||
if (offset + length <= chunk_start + start_chunk.byteLength) {
|
||
return start_chunk.subarray(offset - chunk_start, offset + length - chunk_start);
|
||
}
|
||
const chunks2 = [start_chunk.subarray(offset - chunk_start)];
|
||
let cursor = start_chunk.byteLength - offset + chunk_start;
|
||
while (cursor < length) {
|
||
chunk_index++;
|
||
let chunk = await get_chunk(chunk_index);
|
||
if (!chunk) return null;
|
||
if (chunk.byteLength > length - cursor) {
|
||
chunk = chunk.subarray(0, length - cursor);
|
||
}
|
||
chunks2.push(chunk);
|
||
cursor += chunk.byteLength;
|
||
}
|
||
const buffer = new Uint8Array(length);
|
||
cursor = 0;
|
||
for (const chunk of chunks2) {
|
||
buffer.set(chunk, cursor);
|
||
cursor += chunk.byteLength;
|
||
}
|
||
return buffer;
|
||
}
|
||
const header = await get_buffer(0, HEADER_BYTES);
|
||
if (!header) throw deserialize_error("too short");
|
||
if (header[0] !== BINARY_FORM_VERSION) {
|
||
throw deserialize_error(`got version ${header[0]}, expected version ${BINARY_FORM_VERSION}`);
|
||
}
|
||
const header_view = new DataView(header.buffer, header.byteOffset, header.byteLength);
|
||
const data_length = header_view.getUint32(1, true);
|
||
if (HEADER_BYTES + data_length > content_length) {
|
||
throw deserialize_error("data overflow");
|
||
}
|
||
const file_offsets_length = header_view.getUint16(5, true);
|
||
if (HEADER_BYTES + data_length + file_offsets_length > content_length) {
|
||
throw deserialize_error("file offset table overflow");
|
||
}
|
||
const data_buffer = await get_buffer(HEADER_BYTES, data_length);
|
||
if (!data_buffer) throw deserialize_error("data too short");
|
||
let file_offsets;
|
||
let files_start_offset;
|
||
if (file_offsets_length > 0) {
|
||
const file_offsets_buffer = await get_buffer(HEADER_BYTES + data_length, file_offsets_length);
|
||
if (!file_offsets_buffer) throw deserialize_error("file offset table too short");
|
||
const parsed_offsets = JSON.parse(text_decoder.decode(file_offsets_buffer));
|
||
if (!Array.isArray(parsed_offsets) || parsed_offsets.some((n) => typeof n !== "number" || !Number.isInteger(n) || n < 0)) {
|
||
throw deserialize_error("invalid file offset table");
|
||
}
|
||
file_offsets = /** @type {Array<number>} */
|
||
parsed_offsets;
|
||
files_start_offset = HEADER_BYTES + data_length + file_offsets_length;
|
||
}
|
||
const file_spans = [];
|
||
const [data, meta] = parse(text_decoder.decode(data_buffer), {
|
||
File: ([name, type, size, last_modified, index]) => {
|
||
if (typeof name !== "string" || typeof type !== "string" || typeof size !== "number" || typeof last_modified !== "number" || typeof index !== "number") {
|
||
throw deserialize_error("invalid file metadata");
|
||
}
|
||
let offset = file_offsets[index];
|
||
if (offset === void 0) {
|
||
throw deserialize_error("duplicate file offset table index");
|
||
}
|
||
file_offsets[index] = void 0;
|
||
offset += files_start_offset;
|
||
if (offset + size > content_length) {
|
||
throw deserialize_error("file data overflow");
|
||
}
|
||
file_spans.push({ offset, size });
|
||
return new Proxy(new LazyFile(name, type, size, last_modified, get_chunk, offset), {
|
||
getPrototypeOf() {
|
||
return File.prototype;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
file_spans.sort((a, b) => a.offset - b.offset || a.size - b.size);
|
||
for (let i = 1; i < file_spans.length; i++) {
|
||
const previous = file_spans[i - 1];
|
||
const current = file_spans[i];
|
||
const previous_end = previous.offset + previous.size;
|
||
if (previous_end < current.offset) {
|
||
throw deserialize_error("gaps in file data");
|
||
}
|
||
if (previous_end > current.offset) {
|
||
throw deserialize_error("overlapping file data");
|
||
}
|
||
}
|
||
void (async () => {
|
||
let has_more = true;
|
||
while (has_more) {
|
||
const chunk = await get_chunk(chunks.length);
|
||
has_more = !!chunk;
|
||
}
|
||
})();
|
||
return { data, meta, form_data: null };
|
||
}
|
||
function deserialize_error(message) {
|
||
return new SvelteKitError(400, "Bad Request", `Could not deserialize binary form: ${message}`);
|
||
}
|
||
class LazyFile {
|
||
/** @type {(index: number) => Promise<Uint8Array<ArrayBuffer> | undefined>} */
|
||
#get_chunk;
|
||
/** @type {number} */
|
||
#offset;
|
||
/**
|
||
* @param {string} name
|
||
* @param {string} type
|
||
* @param {number} size
|
||
* @param {number} last_modified
|
||
* @param {(index: number) => Promise<Uint8Array<ArrayBuffer> | undefined>} get_chunk
|
||
* @param {number} offset
|
||
*/
|
||
constructor(name, type, size, last_modified, get_chunk, offset) {
|
||
this.name = name;
|
||
this.type = type;
|
||
this.size = size;
|
||
this.lastModified = last_modified;
|
||
this.webkitRelativePath = "";
|
||
this.#get_chunk = get_chunk;
|
||
this.#offset = offset;
|
||
this.arrayBuffer = this.arrayBuffer.bind(this);
|
||
this.bytes = this.bytes.bind(this);
|
||
this.slice = this.slice.bind(this);
|
||
this.stream = this.stream.bind(this);
|
||
this.text = this.text.bind(this);
|
||
}
|
||
/** @type {ArrayBuffer | undefined} */
|
||
#buffer;
|
||
async arrayBuffer() {
|
||
this.#buffer ??= await new Response(this.stream()).arrayBuffer();
|
||
return this.#buffer;
|
||
}
|
||
async bytes() {
|
||
return new Uint8Array(await this.arrayBuffer());
|
||
}
|
||
/**
|
||
* @param {number=} start
|
||
* @param {number=} end
|
||
* @param {string=} contentType
|
||
*/
|
||
slice(start = 0, end = this.size, contentType = this.type) {
|
||
if (start < 0) {
|
||
start = Math.max(this.size + start, 0);
|
||
} else {
|
||
start = Math.min(start, this.size);
|
||
}
|
||
if (end < 0) {
|
||
end = Math.max(this.size + end, 0);
|
||
} else {
|
||
end = Math.min(end, this.size);
|
||
}
|
||
const size = Math.max(end - start, 0);
|
||
const file = new LazyFile(
|
||
this.name,
|
||
contentType,
|
||
size,
|
||
this.lastModified,
|
||
this.#get_chunk,
|
||
this.#offset + start
|
||
);
|
||
return file;
|
||
}
|
||
stream() {
|
||
let cursor = 0;
|
||
let chunk_index = 0;
|
||
return new ReadableStream({
|
||
start: async (controller) => {
|
||
let chunk_start = 0;
|
||
let start_chunk;
|
||
for (chunk_index = 0; ; chunk_index++) {
|
||
const chunk = await this.#get_chunk(chunk_index);
|
||
if (!chunk) return null;
|
||
const chunk_end = chunk_start + chunk.byteLength;
|
||
if (this.#offset >= chunk_start && this.#offset < chunk_end) {
|
||
start_chunk = chunk;
|
||
break;
|
||
}
|
||
chunk_start = chunk_end;
|
||
}
|
||
if (this.#offset + this.size <= chunk_start + start_chunk.byteLength) {
|
||
controller.enqueue(
|
||
start_chunk.subarray(this.#offset - chunk_start, this.#offset + this.size - chunk_start)
|
||
);
|
||
controller.close();
|
||
} else {
|
||
controller.enqueue(start_chunk.subarray(this.#offset - chunk_start));
|
||
cursor = start_chunk.byteLength - this.#offset + chunk_start;
|
||
}
|
||
},
|
||
pull: async (controller) => {
|
||
chunk_index++;
|
||
let chunk = await this.#get_chunk(chunk_index);
|
||
if (!chunk) {
|
||
controller.error("incomplete file data");
|
||
controller.close();
|
||
return;
|
||
}
|
||
if (chunk.byteLength > this.size - cursor) {
|
||
chunk = chunk.subarray(0, this.size - cursor);
|
||
}
|
||
controller.enqueue(chunk);
|
||
cursor += chunk.byteLength;
|
||
if (cursor >= this.size) {
|
||
controller.close();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
async text() {
|
||
return text_decoder.decode(await this.arrayBuffer());
|
||
}
|
||
}
|
||
const path_regex = /^[a-zA-Z_$]\w*(\.[a-zA-Z_$]\w*|\[\d+\])*$/;
|
||
function split_path(path) {
|
||
if (!path_regex.test(path)) {
|
||
throw new Error(`Invalid path ${path}`);
|
||
}
|
||
return path.split(/\.|\[|\]/).filter(Boolean);
|
||
}
|
||
function check_prototype_pollution(key) {
|
||
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
||
throw new Error(
|
||
`Invalid key "${key}"`
|
||
);
|
||
}
|
||
}
|
||
function deep_set(object, keys, value) {
|
||
let current = object;
|
||
for (let i = 0; i < keys.length - 1; i += 1) {
|
||
const key = keys[i];
|
||
check_prototype_pollution(key);
|
||
const is_array = /^\d+$/.test(keys[i + 1]);
|
||
const exists = Object.hasOwn(current, key);
|
||
const inner = current[key];
|
||
if (exists && is_array !== Array.isArray(inner)) {
|
||
throw new Error(`Invalid array key ${keys[i + 1]}`);
|
||
}
|
||
if (!exists) {
|
||
current[key] = is_array ? [] : {};
|
||
}
|
||
current = current[key];
|
||
}
|
||
const final_key = keys[keys.length - 1];
|
||
check_prototype_pollution(final_key);
|
||
current[final_key] = value;
|
||
}
|
||
function negotiate(accept, types) {
|
||
const parts = [];
|
||
accept.split(",").forEach((str, i) => {
|
||
const match = /([^/ \t]+)\/([^; \t]+)[ \t]*(?:;[ \t]*q=([0-9.]+))?/.exec(str);
|
||
if (match) {
|
||
const [, type, subtype, q = "1"] = match;
|
||
parts.push({ type, subtype, q: +q, i });
|
||
}
|
||
});
|
||
parts.sort((a, b) => {
|
||
if (a.q !== b.q) {
|
||
return b.q - a.q;
|
||
}
|
||
if (a.subtype === "*" !== (b.subtype === "*")) {
|
||
return a.subtype === "*" ? 1 : -1;
|
||
}
|
||
if (a.type === "*" !== (b.type === "*")) {
|
||
return a.type === "*" ? 1 : -1;
|
||
}
|
||
return a.i - b.i;
|
||
});
|
||
let accepted;
|
||
let min_priority = Infinity;
|
||
for (const mimetype of types) {
|
||
const [type, subtype] = mimetype.split("/");
|
||
const priority = parts.findIndex(
|
||
(part) => (part.type === type || part.type === "*") && (part.subtype === subtype || part.subtype === "*")
|
||
);
|
||
if (priority !== -1 && priority < min_priority) {
|
||
accepted = mimetype;
|
||
min_priority = priority;
|
||
}
|
||
}
|
||
return accepted;
|
||
}
|
||
function is_content_type(request, ...types) {
|
||
const type = request.headers.get("content-type")?.split(";", 1)[0].trim() ?? "";
|
||
return types.includes(type.toLowerCase());
|
||
}
|
||
function is_form_content_type(request) {
|
||
return is_content_type(
|
||
request,
|
||
"application/x-www-form-urlencoded",
|
||
"multipart/form-data",
|
||
"text/plain",
|
||
BINARY_FORM_CONTENT_TYPE
|
||
);
|
||
}
|
||
function coalesce_to_error(err) {
|
||
return err instanceof Error || err && /** @type {any} */
|
||
err.name && /** @type {any} */
|
||
err.message ? (
|
||
/** @type {Error} */
|
||
err
|
||
) : new Error(JSON.stringify(err));
|
||
}
|
||
function normalize_error(error) {
|
||
return (
|
||
/** @type {import('../exports/internal/index.js').Redirect | HttpError | SvelteKitError | Error} */
|
||
error
|
||
);
|
||
}
|
||
function get_status(error) {
|
||
return error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500;
|
||
}
|
||
function get_message(error) {
|
||
return error instanceof SvelteKitError ? error.text : "Internal Error";
|
||
}
|
||
const escape_html_attr_dict = {
|
||
"&": "&",
|
||
'"': """
|
||
// Svelte also escapes < because the escape function could be called inside a `noscript` there
|
||
// https://github.com/sveltejs/svelte/security/advisories/GHSA-8266-84wp-wv5c
|
||
// However, that doesn't apply in SvelteKit
|
||
};
|
||
const escape_html_dict = {
|
||
"&": "&",
|
||
"<": "<"
|
||
};
|
||
const surrogates = (
|
||
// high surrogate without paired low surrogate
|
||
"[\\ud800-\\udbff](?![\\udc00-\\udfff])|[\\ud800-\\udbff][\\udc00-\\udfff]|[\\udc00-\\udfff]"
|
||
);
|
||
const escape_html_attr_regex = new RegExp(
|
||
`[${Object.keys(escape_html_attr_dict).join("")}]|` + surrogates,
|
||
"g"
|
||
);
|
||
const escape_html_regex = new RegExp(
|
||
`[${Object.keys(escape_html_dict).join("")}]|` + surrogates,
|
||
"g"
|
||
);
|
||
function escape_html(str, is_attr) {
|
||
const dict = is_attr ? escape_html_attr_dict : escape_html_dict;
|
||
const escaped_str = str.replace(is_attr ? escape_html_attr_regex : escape_html_regex, (match) => {
|
||
if (match.length === 2) {
|
||
return match;
|
||
}
|
||
return dict[match] ?? `&#${match.charCodeAt(0)};`;
|
||
});
|
||
return escaped_str;
|
||
}
|
||
function method_not_allowed(mod, method) {
|
||
return text(`${method} method not allowed`, {
|
||
status: 405,
|
||
headers: {
|
||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
|
||
// "The server must generate an Allow header field in a 405 status code response"
|
||
allow: allowed_methods(mod).join(", ")
|
||
}
|
||
});
|
||
}
|
||
function allowed_methods(mod) {
|
||
const allowed = ENDPOINT_METHODS.filter((method) => method in mod);
|
||
if ("GET" in mod && !("HEAD" in mod)) {
|
||
allowed.push("HEAD");
|
||
}
|
||
return allowed;
|
||
}
|
||
function get_global_name(options) {
|
||
return `__sveltekit_${options.version_hash}`;
|
||
}
|
||
function static_error_page(options, status, message) {
|
||
let page = options.templates.error({ status, message: escape_html(message) });
|
||
return text(page, {
|
||
headers: { "content-type": "text/html; charset=utf-8" },
|
||
status
|
||
});
|
||
}
|
||
async function handle_fatal_error(event, state, options, error) {
|
||
error = error instanceof HttpError ? error : coalesce_to_error(error);
|
||
const status = get_status(error);
|
||
const body = await handle_error_and_jsonify(event, state, options, error);
|
||
const type = negotiate(event.request.headers.get("accept") || "text/html", [
|
||
"application/json",
|
||
"text/html"
|
||
]);
|
||
if (event.isDataRequest || type === "application/json") {
|
||
return json(body, {
|
||
status
|
||
});
|
||
}
|
||
return static_error_page(options, status, body.message);
|
||
}
|
||
async function handle_error_and_jsonify(event, state, options, error) {
|
||
if (error instanceof HttpError) {
|
||
return { message: "Unknown Error", ...error.body };
|
||
}
|
||
const status = get_status(error);
|
||
const message = get_message(error);
|
||
return await with_request_store(
|
||
{ event, state },
|
||
() => options.hooks.handleError({ error, event, status, message })
|
||
) ?? { message };
|
||
}
|
||
function redirect_response(status, location) {
|
||
const response = new Response(void 0, {
|
||
status,
|
||
headers: { location }
|
||
});
|
||
return response;
|
||
}
|
||
function clarify_devalue_error(event, error) {
|
||
if (error.path) {
|
||
return `Data returned from \`load\` while rendering ${event.route.id} is not serializable: ${error.message} (${error.path}). If you need to serialize/deserialize custom types, use transport hooks: https://svelte.dev/docs/kit/hooks#Universal-hooks-transport.`;
|
||
}
|
||
if (error.path === "") {
|
||
return `Data returned from \`load\` while rendering ${event.route.id} is not a plain object`;
|
||
}
|
||
return error.message;
|
||
}
|
||
function serialize_uses(node) {
|
||
const uses = {};
|
||
if (node.uses && node.uses.dependencies.size > 0) {
|
||
uses.dependencies = Array.from(node.uses.dependencies);
|
||
}
|
||
if (node.uses && node.uses.search_params.size > 0) {
|
||
uses.search_params = Array.from(node.uses.search_params);
|
||
}
|
||
if (node.uses && node.uses.params.size > 0) {
|
||
uses.params = Array.from(node.uses.params);
|
||
}
|
||
if (node.uses?.parent) uses.parent = 1;
|
||
if (node.uses?.route) uses.route = 1;
|
||
if (node.uses?.url) uses.url = 1;
|
||
return uses;
|
||
}
|
||
function has_prerendered_path(manifest, pathname) {
|
||
return manifest._.prerendered_routes.has(pathname) || pathname.at(-1) === "/" && manifest._.prerendered_routes.has(pathname.slice(0, -1));
|
||
}
|
||
function format_server_error(status, error, event) {
|
||
const formatted_text = `
|
||
\x1B[1;31m[${status}] ${event.request.method} ${event.url.pathname}\x1B[0m`;
|
||
if (status === 404) {
|
||
return formatted_text;
|
||
}
|
||
return `${formatted_text}
|
||
${error.stack}`;
|
||
}
|
||
function get_node_type(node_id) {
|
||
const parts = node_id?.split("/");
|
||
const filename = parts?.at(-1);
|
||
if (!filename) return "unknown";
|
||
const dot_parts = filename.split(".");
|
||
return dot_parts.slice(0, -1).join(".");
|
||
}
|
||
const INVALIDATED_PARAM = "x-sveltekit-invalidated";
|
||
const TRAILING_SLASH_PARAM = "x-sveltekit-trailing-slash";
|
||
function stringify(data, transport) {
|
||
const encoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.encode]));
|
||
return stringify$1(data, encoders);
|
||
}
|
||
function parse_remote_arg(string, transport) {
|
||
if (!string) return void 0;
|
||
const json_string = text_decoder.decode(
|
||
// no need to add back `=` characters, atob can handle it
|
||
base64_decode(string.replaceAll("-", "+").replaceAll("_", "/"))
|
||
);
|
||
const decoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.decode]));
|
||
return parse(json_string, decoders);
|
||
}
|
||
function create_remote_key(id, payload) {
|
||
return id + "/" + payload;
|
||
}
|
||
|
||
export { ENDPOINT_METHODS as E, INVALIDATED_PARAM as I, MUTATIVE_METHODS as M, PAGE_METHODS as P, SVELTE_KIT_ASSETS as S, TRAILING_SLASH_PARAM as T, handle_fatal_error as a, clarify_devalue_error as b, create_remote_key as c, handle_error_and_jsonify as d, get_status as e, format_server_error as f, get_global_name as g, has_prerendered_path as h, is_form_content_type as i, normalize_error as j, static_error_page as k, stringify as l, method_not_allowed as m, negotiate as n, deserialize_binary_form as o, parse_remote_arg as p, get_node_type as q, redirect_response as r, serialize_uses as s, stringify$1 as t, escape_html as u };
|
||
//# sourceMappingURL=shared-DWC9PG0H.js.map
|