shared.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. import { json, text } from "@sveltejs/kit";
  2. import { SvelteKitError, HttpError } from "@sveltejs/kit/internal";
  3. import { with_request_store } from "@sveltejs/kit/internal/server";
  4. import * as devalue from "devalue";
  5. import { t as text_decoder, b as base64_encode, c as base64_decode } from "./utils.js";
  6. const SVELTE_KIT_ASSETS = "/_svelte_kit_assets";
  7. const ENDPOINT_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"];
  8. const MUTATIVE_METHODS = ["POST", "PUT", "PATCH", "DELETE"];
  9. const PAGE_METHODS = ["GET", "POST", "HEAD"];
  10. function set_nested_value(object, path_string, value) {
  11. if (path_string.startsWith("n:")) {
  12. path_string = path_string.slice(2);
  13. value = value === "" ? void 0 : parseFloat(value);
  14. } else if (path_string.startsWith("b:")) {
  15. path_string = path_string.slice(2);
  16. value = value === "on";
  17. }
  18. deep_set(object, split_path(path_string), value);
  19. }
  20. function convert_formdata(data) {
  21. const result = {};
  22. for (let key of data.keys()) {
  23. const is_array = key.endsWith("[]");
  24. let values = data.getAll(key);
  25. if (is_array) key = key.slice(0, -2);
  26. if (values.length > 1 && !is_array) {
  27. throw new Error(`Form cannot contain duplicated keys — "${key}" has ${values.length} values`);
  28. }
  29. values = values.filter(
  30. (entry) => typeof entry === "string" || entry.name !== "" || entry.size > 0
  31. );
  32. if (key.startsWith("n:")) {
  33. key = key.slice(2);
  34. values = values.map((v) => v === "" ? void 0 : parseFloat(
  35. /** @type {string} */
  36. v
  37. ));
  38. } else if (key.startsWith("b:")) {
  39. key = key.slice(2);
  40. values = values.map((v) => v === "on");
  41. }
  42. set_nested_value(result, key, is_array ? values : values[0]);
  43. }
  44. return result;
  45. }
  46. const BINARY_FORM_CONTENT_TYPE = "application/x-sveltekit-formdata";
  47. const BINARY_FORM_VERSION = 0;
  48. const HEADER_BYTES = 1 + 4 + 2;
  49. async function deserialize_binary_form(request) {
  50. if (request.headers.get("content-type") !== BINARY_FORM_CONTENT_TYPE) {
  51. const form_data = await request.formData();
  52. return { data: convert_formdata(form_data), meta: {}, form_data };
  53. }
  54. if (!request.body) {
  55. throw deserialize_error("no body");
  56. }
  57. const content_length = parseInt(request.headers.get("content-length") ?? "");
  58. if (Number.isNaN(content_length)) {
  59. throw deserialize_error("invalid Content-Length header");
  60. }
  61. const reader = request.body.getReader();
  62. const chunks = [];
  63. function get_chunk(index) {
  64. if (index in chunks) return chunks[index];
  65. let i = chunks.length;
  66. while (i <= index) {
  67. chunks[i] = reader.read().then((chunk) => chunk.value);
  68. i++;
  69. }
  70. return chunks[index];
  71. }
  72. async function get_buffer(offset, length) {
  73. let start_chunk;
  74. let chunk_start = 0;
  75. let chunk_index;
  76. for (chunk_index = 0; ; chunk_index++) {
  77. const chunk = await get_chunk(chunk_index);
  78. if (!chunk) return null;
  79. const chunk_end = chunk_start + chunk.byteLength;
  80. if (offset >= chunk_start && offset < chunk_end) {
  81. start_chunk = chunk;
  82. break;
  83. }
  84. chunk_start = chunk_end;
  85. }
  86. if (offset + length <= chunk_start + start_chunk.byteLength) {
  87. return start_chunk.subarray(offset - chunk_start, offset + length - chunk_start);
  88. }
  89. const chunks2 = [start_chunk.subarray(offset - chunk_start)];
  90. let cursor = start_chunk.byteLength - offset + chunk_start;
  91. while (cursor < length) {
  92. chunk_index++;
  93. let chunk = await get_chunk(chunk_index);
  94. if (!chunk) return null;
  95. if (chunk.byteLength > length - cursor) {
  96. chunk = chunk.subarray(0, length - cursor);
  97. }
  98. chunks2.push(chunk);
  99. cursor += chunk.byteLength;
  100. }
  101. const buffer = new Uint8Array(length);
  102. cursor = 0;
  103. for (const chunk of chunks2) {
  104. buffer.set(chunk, cursor);
  105. cursor += chunk.byteLength;
  106. }
  107. return buffer;
  108. }
  109. const header = await get_buffer(0, HEADER_BYTES);
  110. if (!header) throw deserialize_error("too short");
  111. if (header[0] !== BINARY_FORM_VERSION) {
  112. throw deserialize_error(`got version ${header[0]}, expected version ${BINARY_FORM_VERSION}`);
  113. }
  114. const header_view = new DataView(header.buffer, header.byteOffset, header.byteLength);
  115. const data_length = header_view.getUint32(1, true);
  116. if (HEADER_BYTES + data_length > content_length) {
  117. throw deserialize_error("data overflow");
  118. }
  119. const file_offsets_length = header_view.getUint16(5, true);
  120. if (HEADER_BYTES + data_length + file_offsets_length > content_length) {
  121. throw deserialize_error("file offset table overflow");
  122. }
  123. const data_buffer = await get_buffer(HEADER_BYTES, data_length);
  124. if (!data_buffer) throw deserialize_error("data too short");
  125. let file_offsets;
  126. let files_start_offset;
  127. if (file_offsets_length > 0) {
  128. const file_offsets_buffer = await get_buffer(HEADER_BYTES + data_length, file_offsets_length);
  129. if (!file_offsets_buffer) throw deserialize_error("file offset table too short");
  130. const parsed_offsets = JSON.parse(text_decoder.decode(file_offsets_buffer));
  131. if (!Array.isArray(parsed_offsets) || parsed_offsets.some((n) => typeof n !== "number" || !Number.isInteger(n) || n < 0)) {
  132. throw deserialize_error("invalid file offset table");
  133. }
  134. file_offsets = /** @type {Array<number>} */
  135. parsed_offsets;
  136. files_start_offset = HEADER_BYTES + data_length + file_offsets_length;
  137. }
  138. const file_spans = [];
  139. const [data, meta] = devalue.parse(text_decoder.decode(data_buffer), {
  140. File: ([name, type, size, last_modified, index]) => {
  141. if (typeof name !== "string" || typeof type !== "string" || typeof size !== "number" || typeof last_modified !== "number" || typeof index !== "number") {
  142. throw deserialize_error("invalid file metadata");
  143. }
  144. let offset = file_offsets[index];
  145. if (offset === void 0) {
  146. throw deserialize_error("duplicate file offset table index");
  147. }
  148. file_offsets[index] = void 0;
  149. offset += files_start_offset;
  150. if (offset + size > content_length) {
  151. throw deserialize_error("file data overflow");
  152. }
  153. file_spans.push({ offset, size });
  154. return new Proxy(new LazyFile(name, type, size, last_modified, get_chunk, offset), {
  155. getPrototypeOf() {
  156. return File.prototype;
  157. }
  158. });
  159. }
  160. });
  161. file_spans.sort((a, b) => a.offset - b.offset || a.size - b.size);
  162. for (let i = 1; i < file_spans.length; i++) {
  163. const previous = file_spans[i - 1];
  164. const current = file_spans[i];
  165. const previous_end = previous.offset + previous.size;
  166. if (previous_end < current.offset) {
  167. throw deserialize_error("gaps in file data");
  168. }
  169. if (previous_end > current.offset) {
  170. throw deserialize_error("overlapping file data");
  171. }
  172. }
  173. void (async () => {
  174. let has_more = true;
  175. while (has_more) {
  176. const chunk = await get_chunk(chunks.length);
  177. has_more = !!chunk;
  178. }
  179. })();
  180. return { data, meta, form_data: null };
  181. }
  182. function deserialize_error(message) {
  183. return new SvelteKitError(400, "Bad Request", `Could not deserialize binary form: ${message}`);
  184. }
  185. class LazyFile {
  186. /** @type {(index: number) => Promise<Uint8Array<ArrayBuffer> | undefined>} */
  187. #get_chunk;
  188. /** @type {number} */
  189. #offset;
  190. /**
  191. * @param {string} name
  192. * @param {string} type
  193. * @param {number} size
  194. * @param {number} last_modified
  195. * @param {(index: number) => Promise<Uint8Array<ArrayBuffer> | undefined>} get_chunk
  196. * @param {number} offset
  197. */
  198. constructor(name, type, size, last_modified, get_chunk, offset) {
  199. this.name = name;
  200. this.type = type;
  201. this.size = size;
  202. this.lastModified = last_modified;
  203. this.webkitRelativePath = "";
  204. this.#get_chunk = get_chunk;
  205. this.#offset = offset;
  206. this.arrayBuffer = this.arrayBuffer.bind(this);
  207. this.bytes = this.bytes.bind(this);
  208. this.slice = this.slice.bind(this);
  209. this.stream = this.stream.bind(this);
  210. this.text = this.text.bind(this);
  211. }
  212. /** @type {ArrayBuffer | undefined} */
  213. #buffer;
  214. async arrayBuffer() {
  215. this.#buffer ??= await new Response(this.stream()).arrayBuffer();
  216. return this.#buffer;
  217. }
  218. async bytes() {
  219. return new Uint8Array(await this.arrayBuffer());
  220. }
  221. /**
  222. * @param {number=} start
  223. * @param {number=} end
  224. * @param {string=} contentType
  225. */
  226. slice(start = 0, end = this.size, contentType = this.type) {
  227. if (start < 0) {
  228. start = Math.max(this.size + start, 0);
  229. } else {
  230. start = Math.min(start, this.size);
  231. }
  232. if (end < 0) {
  233. end = Math.max(this.size + end, 0);
  234. } else {
  235. end = Math.min(end, this.size);
  236. }
  237. const size = Math.max(end - start, 0);
  238. const file = new LazyFile(
  239. this.name,
  240. contentType,
  241. size,
  242. this.lastModified,
  243. this.#get_chunk,
  244. this.#offset + start
  245. );
  246. return file;
  247. }
  248. stream() {
  249. let cursor = 0;
  250. let chunk_index = 0;
  251. return new ReadableStream({
  252. start: async (controller) => {
  253. let chunk_start = 0;
  254. let start_chunk;
  255. for (chunk_index = 0; ; chunk_index++) {
  256. const chunk = await this.#get_chunk(chunk_index);
  257. if (!chunk) return null;
  258. const chunk_end = chunk_start + chunk.byteLength;
  259. if (this.#offset >= chunk_start && this.#offset < chunk_end) {
  260. start_chunk = chunk;
  261. break;
  262. }
  263. chunk_start = chunk_end;
  264. }
  265. if (this.#offset + this.size <= chunk_start + start_chunk.byteLength) {
  266. controller.enqueue(
  267. start_chunk.subarray(this.#offset - chunk_start, this.#offset + this.size - chunk_start)
  268. );
  269. controller.close();
  270. } else {
  271. controller.enqueue(start_chunk.subarray(this.#offset - chunk_start));
  272. cursor = start_chunk.byteLength - this.#offset + chunk_start;
  273. }
  274. },
  275. pull: async (controller) => {
  276. chunk_index++;
  277. let chunk = await this.#get_chunk(chunk_index);
  278. if (!chunk) {
  279. controller.error("incomplete file data");
  280. controller.close();
  281. return;
  282. }
  283. if (chunk.byteLength > this.size - cursor) {
  284. chunk = chunk.subarray(0, this.size - cursor);
  285. }
  286. controller.enqueue(chunk);
  287. cursor += chunk.byteLength;
  288. if (cursor >= this.size) {
  289. controller.close();
  290. }
  291. }
  292. });
  293. }
  294. async text() {
  295. return text_decoder.decode(await this.arrayBuffer());
  296. }
  297. }
  298. const path_regex = /^[a-zA-Z_$]\w*(\.[a-zA-Z_$]\w*|\[\d+\])*$/;
  299. function split_path(path) {
  300. if (!path_regex.test(path)) {
  301. throw new Error(`Invalid path ${path}`);
  302. }
  303. return path.split(/\.|\[|\]/).filter(Boolean);
  304. }
  305. function check_prototype_pollution(key) {
  306. if (key === "__proto__" || key === "constructor" || key === "prototype") {
  307. throw new Error(
  308. `Invalid key "${key}"`
  309. );
  310. }
  311. }
  312. function deep_set(object, keys, value) {
  313. let current = object;
  314. for (let i = 0; i < keys.length - 1; i += 1) {
  315. const key = keys[i];
  316. check_prototype_pollution(key);
  317. const is_array = /^\d+$/.test(keys[i + 1]);
  318. const exists = Object.hasOwn(current, key);
  319. const inner = current[key];
  320. if (exists && is_array !== Array.isArray(inner)) {
  321. throw new Error(`Invalid array key ${keys[i + 1]}`);
  322. }
  323. if (!exists) {
  324. current[key] = is_array ? [] : {};
  325. }
  326. current = current[key];
  327. }
  328. const final_key = keys[keys.length - 1];
  329. check_prototype_pollution(final_key);
  330. current[final_key] = value;
  331. }
  332. function normalize_issue(issue, server = false) {
  333. const normalized = { name: "", path: [], message: issue.message, server };
  334. if (issue.path !== void 0) {
  335. let name = "";
  336. for (const segment of issue.path) {
  337. const key = (
  338. /** @type {string | number} */
  339. typeof segment === "object" ? segment.key : segment
  340. );
  341. normalized.path.push(key);
  342. if (typeof key === "number") {
  343. name += `[${key}]`;
  344. } else if (typeof key === "string") {
  345. name += name === "" ? key : "." + key;
  346. }
  347. }
  348. normalized.name = name;
  349. }
  350. return normalized;
  351. }
  352. function flatten_issues(issues) {
  353. const result = {};
  354. for (const issue of issues) {
  355. (result.$ ??= []).push(issue);
  356. let name = "";
  357. if (issue.path !== void 0) {
  358. for (const key of issue.path) {
  359. if (typeof key === "number") {
  360. name += `[${key}]`;
  361. } else if (typeof key === "string") {
  362. name += name === "" ? key : "." + key;
  363. }
  364. (result[name] ??= []).push(issue);
  365. }
  366. }
  367. }
  368. return result;
  369. }
  370. function deep_get(object, path) {
  371. let current = object;
  372. for (const key of path) {
  373. if (current == null || typeof current !== "object") {
  374. return current;
  375. }
  376. current = current[key];
  377. }
  378. return current;
  379. }
  380. function create_field_proxy(target, get_input, set_input, get_issues, path = []) {
  381. const get_value = () => {
  382. return deep_get(get_input(), path);
  383. };
  384. return new Proxy(target, {
  385. get(target2, prop) {
  386. if (typeof prop === "symbol") return target2[prop];
  387. if (/^\d+$/.test(prop)) {
  388. return create_field_proxy({}, get_input, set_input, get_issues, [
  389. ...path,
  390. parseInt(prop, 10)
  391. ]);
  392. }
  393. const key = build_path_string(path);
  394. if (prop === "set") {
  395. const set_func = function(newValue) {
  396. set_input(path, newValue);
  397. return newValue;
  398. };
  399. return create_field_proxy(set_func, get_input, set_input, get_issues, [...path, prop]);
  400. }
  401. if (prop === "value") {
  402. return create_field_proxy(get_value, get_input, set_input, get_issues, [...path, prop]);
  403. }
  404. if (prop === "issues" || prop === "allIssues") {
  405. const issues_func = () => {
  406. const all_issues = get_issues()[key === "" ? "$" : key];
  407. if (prop === "allIssues") {
  408. return all_issues?.map((issue) => ({
  409. path: issue.path,
  410. message: issue.message
  411. }));
  412. }
  413. return all_issues?.filter((issue) => issue.name === key)?.map((issue) => ({
  414. path: issue.path,
  415. message: issue.message
  416. }));
  417. };
  418. return create_field_proxy(issues_func, get_input, set_input, get_issues, [...path, prop]);
  419. }
  420. if (prop === "as") {
  421. const as_func = (type, input_value) => {
  422. const is_array = type === "file multiple" || type === "select multiple" || type === "checkbox" && typeof input_value === "string";
  423. const prefix = type === "number" || type === "range" ? "n:" : type === "checkbox" && !is_array ? "b:" : "";
  424. const base_props = {
  425. name: prefix + key + (is_array ? "[]" : ""),
  426. get "aria-invalid"() {
  427. const issues = get_issues();
  428. return key in issues ? "true" : void 0;
  429. }
  430. };
  431. if (type !== "text" && type !== "select" && type !== "select multiple") {
  432. base_props.type = type === "file multiple" ? "file" : type;
  433. }
  434. if (type === "submit" || type === "hidden") {
  435. return Object.defineProperties(base_props, {
  436. value: { value: input_value, enumerable: true }
  437. });
  438. }
  439. if (type === "select" || type === "select multiple") {
  440. return Object.defineProperties(base_props, {
  441. multiple: { value: is_array, enumerable: true },
  442. value: {
  443. enumerable: true,
  444. get() {
  445. return get_value();
  446. }
  447. }
  448. });
  449. }
  450. if (type === "checkbox" || type === "radio") {
  451. return Object.defineProperties(base_props, {
  452. value: { value: input_value ?? "on", enumerable: true },
  453. checked: {
  454. enumerable: true,
  455. get() {
  456. const value = get_value();
  457. if (type === "radio") {
  458. return value === input_value;
  459. }
  460. if (is_array) {
  461. return (value ?? []).includes(input_value);
  462. }
  463. return value;
  464. }
  465. }
  466. });
  467. }
  468. if (type === "file" || type === "file multiple") {
  469. return Object.defineProperties(base_props, {
  470. multiple: { value: is_array, enumerable: true },
  471. files: {
  472. enumerable: true,
  473. get() {
  474. const value = get_value();
  475. if (value instanceof File) {
  476. if (typeof DataTransfer !== "undefined") {
  477. const fileList = new DataTransfer();
  478. fileList.items.add(value);
  479. return fileList.files;
  480. }
  481. return { 0: value, length: 1 };
  482. }
  483. if (Array.isArray(value) && value.every((f) => f instanceof File)) {
  484. if (typeof DataTransfer !== "undefined") {
  485. const fileList = new DataTransfer();
  486. value.forEach((file) => fileList.items.add(file));
  487. return fileList.files;
  488. }
  489. const fileListLike = { length: value.length };
  490. value.forEach((file, index) => {
  491. fileListLike[index] = file;
  492. });
  493. return fileListLike;
  494. }
  495. return null;
  496. }
  497. }
  498. });
  499. }
  500. return Object.defineProperties(base_props, {
  501. value: {
  502. enumerable: true,
  503. get() {
  504. const value = get_value();
  505. return value != null ? String(value) : "";
  506. }
  507. }
  508. });
  509. };
  510. return create_field_proxy(as_func, get_input, set_input, get_issues, [...path, "as"]);
  511. }
  512. return create_field_proxy({}, get_input, set_input, get_issues, [...path, prop]);
  513. }
  514. });
  515. }
  516. function build_path_string(path) {
  517. let result = "";
  518. for (const segment of path) {
  519. if (typeof segment === "number") {
  520. result += `[${segment}]`;
  521. } else {
  522. result += result === "" ? segment : "." + segment;
  523. }
  524. }
  525. return result;
  526. }
  527. function negotiate(accept, types) {
  528. const parts = [];
  529. accept.split(",").forEach((str, i) => {
  530. const match = /([^/ \t]+)\/([^; \t]+)[ \t]*(?:;[ \t]*q=([0-9.]+))?/.exec(str);
  531. if (match) {
  532. const [, type, subtype, q = "1"] = match;
  533. parts.push({ type, subtype, q: +q, i });
  534. }
  535. });
  536. parts.sort((a, b) => {
  537. if (a.q !== b.q) {
  538. return b.q - a.q;
  539. }
  540. if (a.subtype === "*" !== (b.subtype === "*")) {
  541. return a.subtype === "*" ? 1 : -1;
  542. }
  543. if (a.type === "*" !== (b.type === "*")) {
  544. return a.type === "*" ? 1 : -1;
  545. }
  546. return a.i - b.i;
  547. });
  548. let accepted;
  549. let min_priority = Infinity;
  550. for (const mimetype of types) {
  551. const [type, subtype] = mimetype.split("/");
  552. const priority = parts.findIndex(
  553. (part) => (part.type === type || part.type === "*") && (part.subtype === subtype || part.subtype === "*")
  554. );
  555. if (priority !== -1 && priority < min_priority) {
  556. accepted = mimetype;
  557. min_priority = priority;
  558. }
  559. }
  560. return accepted;
  561. }
  562. function is_content_type(request, ...types) {
  563. const type = request.headers.get("content-type")?.split(";", 1)[0].trim() ?? "";
  564. return types.includes(type.toLowerCase());
  565. }
  566. function is_form_content_type(request) {
  567. return is_content_type(
  568. request,
  569. "application/x-www-form-urlencoded",
  570. "multipart/form-data",
  571. "text/plain",
  572. BINARY_FORM_CONTENT_TYPE
  573. );
  574. }
  575. function coalesce_to_error(err) {
  576. return err instanceof Error || err && /** @type {any} */
  577. err.name && /** @type {any} */
  578. err.message ? (
  579. /** @type {Error} */
  580. err
  581. ) : new Error(JSON.stringify(err));
  582. }
  583. function normalize_error(error) {
  584. return (
  585. /** @type {import('../exports/internal/index.js').Redirect | HttpError | SvelteKitError | Error} */
  586. error
  587. );
  588. }
  589. function get_status(error) {
  590. return error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500;
  591. }
  592. function get_message(error) {
  593. return error instanceof SvelteKitError ? error.text : "Internal Error";
  594. }
  595. const escape_html_attr_dict = {
  596. "&": "&amp;",
  597. '"': "&quot;"
  598. // Svelte also escapes < because the escape function could be called inside a `noscript` there
  599. // https://github.com/sveltejs/svelte/security/advisories/GHSA-8266-84wp-wv5c
  600. // However, that doesn't apply in SvelteKit
  601. };
  602. const escape_html_dict = {
  603. "&": "&amp;",
  604. "<": "&lt;"
  605. };
  606. const surrogates = (
  607. // high surrogate without paired low surrogate
  608. "[\\ud800-\\udbff](?![\\udc00-\\udfff])|[\\ud800-\\udbff][\\udc00-\\udfff]|[\\udc00-\\udfff]"
  609. );
  610. const escape_html_attr_regex = new RegExp(
  611. `[${Object.keys(escape_html_attr_dict).join("")}]|` + surrogates,
  612. "g"
  613. );
  614. const escape_html_regex = new RegExp(
  615. `[${Object.keys(escape_html_dict).join("")}]|` + surrogates,
  616. "g"
  617. );
  618. function escape_html(str, is_attr) {
  619. const dict = is_attr ? escape_html_attr_dict : escape_html_dict;
  620. const escaped_str = str.replace(is_attr ? escape_html_attr_regex : escape_html_regex, (match) => {
  621. if (match.length === 2) {
  622. return match;
  623. }
  624. return dict[match] ?? `&#${match.charCodeAt(0)};`;
  625. });
  626. return escaped_str;
  627. }
  628. function method_not_allowed(mod, method) {
  629. return text(`${method} method not allowed`, {
  630. status: 405,
  631. headers: {
  632. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
  633. // "The server must generate an Allow header field in a 405 status code response"
  634. allow: allowed_methods(mod).join(", ")
  635. }
  636. });
  637. }
  638. function allowed_methods(mod) {
  639. const allowed = ENDPOINT_METHODS.filter((method) => method in mod);
  640. if ("GET" in mod && !("HEAD" in mod)) {
  641. allowed.push("HEAD");
  642. }
  643. return allowed;
  644. }
  645. function get_global_name(options) {
  646. return `__sveltekit_${options.version_hash}`;
  647. }
  648. function static_error_page(options, status, message) {
  649. let page = options.templates.error({ status, message: escape_html(message) });
  650. return text(page, {
  651. headers: { "content-type": "text/html; charset=utf-8" },
  652. status
  653. });
  654. }
  655. async function handle_fatal_error(event, state, options, error) {
  656. error = error instanceof HttpError ? error : coalesce_to_error(error);
  657. const status = get_status(error);
  658. const body = await handle_error_and_jsonify(event, state, options, error);
  659. const type = negotiate(event.request.headers.get("accept") || "text/html", [
  660. "application/json",
  661. "text/html"
  662. ]);
  663. if (event.isDataRequest || type === "application/json") {
  664. return json(body, {
  665. status
  666. });
  667. }
  668. return static_error_page(options, status, body.message);
  669. }
  670. async function handle_error_and_jsonify(event, state, options, error) {
  671. if (error instanceof HttpError) {
  672. return { message: "Unknown Error", ...error.body };
  673. }
  674. const status = get_status(error);
  675. const message = get_message(error);
  676. return await with_request_store(
  677. { event, state },
  678. () => options.hooks.handleError({ error, event, status, message })
  679. ) ?? { message };
  680. }
  681. function redirect_response(status, location) {
  682. const response = new Response(void 0, {
  683. status,
  684. headers: { location }
  685. });
  686. return response;
  687. }
  688. function clarify_devalue_error(event, error) {
  689. if (error.path) {
  690. 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.`;
  691. }
  692. if (error.path === "") {
  693. return `Data returned from \`load\` while rendering ${event.route.id} is not a plain object`;
  694. }
  695. return error.message;
  696. }
  697. function serialize_uses(node) {
  698. const uses = {};
  699. if (node.uses && node.uses.dependencies.size > 0) {
  700. uses.dependencies = Array.from(node.uses.dependencies);
  701. }
  702. if (node.uses && node.uses.search_params.size > 0) {
  703. uses.search_params = Array.from(node.uses.search_params);
  704. }
  705. if (node.uses && node.uses.params.size > 0) {
  706. uses.params = Array.from(node.uses.params);
  707. }
  708. if (node.uses?.parent) uses.parent = 1;
  709. if (node.uses?.route) uses.route = 1;
  710. if (node.uses?.url) uses.url = 1;
  711. return uses;
  712. }
  713. function has_prerendered_path(manifest, pathname) {
  714. return manifest._.prerendered_routes.has(pathname) || pathname.at(-1) === "/" && manifest._.prerendered_routes.has(pathname.slice(0, -1));
  715. }
  716. function format_server_error(status, error, event) {
  717. const formatted_text = `
  718. \x1B[1;31m[${status}] ${event.request.method} ${event.url.pathname}\x1B[0m`;
  719. if (status === 404) {
  720. return formatted_text;
  721. }
  722. return `${formatted_text}
  723. ${error.stack}`;
  724. }
  725. function get_node_type(node_id) {
  726. const parts = node_id?.split("/");
  727. const filename = parts?.at(-1);
  728. if (!filename) return "unknown";
  729. const dot_parts = filename.split(".");
  730. return dot_parts.slice(0, -1).join(".");
  731. }
  732. const INVALIDATED_PARAM = "x-sveltekit-invalidated";
  733. const TRAILING_SLASH_PARAM = "x-sveltekit-trailing-slash";
  734. function stringify(data, transport) {
  735. const encoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.encode]));
  736. return devalue.stringify(data, encoders);
  737. }
  738. function stringify_remote_arg(value, transport) {
  739. if (value === void 0) return "";
  740. const json_string = stringify(value, transport);
  741. const bytes = new TextEncoder().encode(json_string);
  742. return base64_encode(bytes).replaceAll("=", "").replaceAll("+", "-").replaceAll("/", "_");
  743. }
  744. function parse_remote_arg(string, transport) {
  745. if (!string) return void 0;
  746. const json_string = text_decoder.decode(
  747. // no need to add back `=` characters, atob can handle it
  748. base64_decode(string.replaceAll("-", "+").replaceAll("_", "/"))
  749. );
  750. const decoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.decode]));
  751. return devalue.parse(json_string, decoders);
  752. }
  753. function create_remote_key(id, payload) {
  754. return id + "/" + payload;
  755. }
  756. export {
  757. ENDPOINT_METHODS as E,
  758. INVALIDATED_PARAM as I,
  759. MUTATIVE_METHODS as M,
  760. PAGE_METHODS as P,
  761. SVELTE_KIT_ASSETS as S,
  762. TRAILING_SLASH_PARAM as T,
  763. normalize_error as a,
  764. get_global_name as b,
  765. clarify_devalue_error as c,
  766. get_node_type as d,
  767. escape_html as e,
  768. create_remote_key as f,
  769. get_status as g,
  770. handle_error_and_jsonify as h,
  771. is_form_content_type as i,
  772. static_error_page as j,
  773. stringify as k,
  774. deserialize_binary_form as l,
  775. method_not_allowed as m,
  776. negotiate as n,
  777. has_prerendered_path as o,
  778. parse_remote_arg as p,
  779. handle_fatal_error as q,
  780. redirect_response as r,
  781. serialize_uses as s,
  782. format_server_error as t,
  783. stringify_remote_arg as u,
  784. create_field_proxy as v,
  785. normalize_issue as w,
  786. set_nested_value as x,
  787. flatten_issues as y,
  788. deep_set as z
  789. };