@sveltejs/kit 1.0.0-next.314 → 1.0.0-next.317

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -398,6 +398,8 @@ const routes = parse(components, dictionary, matchers);
398
398
  const default_layout = components[0]();
399
399
  const default_error = components[1]();
400
400
 
401
+ const root_stuff = {};
402
+
401
403
  // We track the scroll position associated with each history entry in sessionStorage,
402
404
  // rather than on history.state itself, because when navigation is driven by
403
405
  // popstate it's too late to update the scroll position associated with the
@@ -430,8 +432,8 @@ function create_client({ target, session, base, trailing_slash }) {
430
432
  /** @type {Map<string, import('./types').NavigationResult>} */
431
433
  const cache = new Map();
432
434
 
433
- /** @type {Set<string>} */
434
- const invalidated = new Set();
435
+ /** @type {Array<((href: string) => boolean)>} */
436
+ const invalidated = [];
435
437
 
436
438
  const stores = {
437
439
  url: notifiable_store({}),
@@ -457,10 +459,12 @@ function create_client({ target, session, base, trailing_slash }) {
457
459
 
458
460
  /** @type {import('./types').NavigationState} */
459
461
  let current = {
460
- // @ts-ignore - we need the initial value to be null
461
- url: null,
462
+ branch: [],
463
+ error: null,
462
464
  session_id: 0,
463
- branch: []
465
+ stuff: root_stuff,
466
+ // @ts-ignore - we need the initial value to be null
467
+ url: null
464
468
  };
465
469
 
466
470
  let started = false;
@@ -488,23 +492,31 @@ function create_client({ target, session, base, trailing_slash }) {
488
492
  });
489
493
  ready = true;
490
494
 
491
- /** Keeps tracks of multiple navigations caused by redirects during rendering */
492
- let navigating = 0;
493
-
494
495
  let router_enabled = true;
495
496
 
496
497
  // keeping track of the history index in order to prevent popstate navigation events if needed
497
- let current_history_index = history.state?.[INDEX_KEY] ?? 0;
498
+ let current_history_index = history.state?.[INDEX_KEY];
499
+
500
+ if (!current_history_index) {
501
+ // we use Date.now() as an offset so that cross-document navigations
502
+ // within the app don't result in data loss
503
+ current_history_index = Date.now();
498
504
 
499
- if (current_history_index === 0) {
500
505
  // create initial history entry, so we can return here
501
- history.replaceState({ ...history.state, [INDEX_KEY]: 0 }, '', location.href);
506
+ history.replaceState(
507
+ { ...history.state, [INDEX_KEY]: current_history_index },
508
+ '',
509
+ location.href
510
+ );
502
511
  }
503
512
 
504
513
  // if we reload the page, or Cmd-Shift-T back to it,
505
514
  // recover scroll position
506
515
  const scroll = scroll_positions[current_history_index];
507
- if (scroll) scrollTo(scroll.x, scroll.y);
516
+ if (scroll) {
517
+ history.scrollRestoration = 'manual';
518
+ scrollTo(scroll.x, scroll.y);
519
+ }
508
520
 
509
521
  let hash_navigating = false;
510
522
 
@@ -514,9 +526,6 @@ function create_client({ target, session, base, trailing_slash }) {
514
526
  /** @type {{}} */
515
527
  let token;
516
528
 
517
- /** @type {{}} */
518
- let navigating_token;
519
-
520
529
  /**
521
530
  * @param {string} href
522
531
  * @param {{ noscroll?: boolean; replaceState?: boolean; keepfocus?: boolean; state?: any }} opts
@@ -562,6 +571,7 @@ function create_client({ target, session, base, trailing_slash }) {
562
571
  }
563
572
 
564
573
  /**
574
+ * Returns `true` if update completes, `false` if it is aborted
565
575
  * @param {URL} url
566
576
  * @param {string[]} redirect_chain
567
577
  * @param {boolean} no_cache
@@ -593,13 +603,13 @@ function create_client({ target, session, base, trailing_slash }) {
593
603
 
594
604
  if (!navigation_result) {
595
605
  await native_navigation(url);
596
- return; // unnecessary, but TypeScript prefers it this way
606
+ return false; // unnecessary, but TypeScript prefers it this way
597
607
  }
598
608
 
599
609
  // abort if user navigated during update
600
- if (token !== current_token) return;
610
+ if (token !== current_token) return false;
601
611
 
602
- invalidated.clear();
612
+ invalidated.length = 0;
603
613
 
604
614
  if (navigation_result.redirect) {
605
615
  if (redirect_chain.length > 10 || redirect_chain.includes(url.pathname)) {
@@ -619,7 +629,7 @@ function create_client({ target, session, base, trailing_slash }) {
619
629
  await native_navigation(new URL(navigation_result.redirect, location.href));
620
630
  }
621
631
 
622
- return;
632
+ return false;
623
633
  }
624
634
  } else if (navigation_result.props?.page?.status >= 400) {
625
635
  const updated = await stores.updated.check();
@@ -702,13 +712,15 @@ function create_client({ target, session, base, trailing_slash }) {
702
712
 
703
713
  const leaf_node = navigation_result.state.branch[navigation_result.state.branch.length - 1];
704
714
  router_enabled = leaf_node?.module.router !== false;
715
+
716
+ return true;
705
717
  }
706
718
 
707
719
  /** @param {import('./types').NavigationResult} result */
708
720
  function initialize(result) {
709
721
  current = result.state;
710
722
 
711
- const style = document.querySelector('style[data-svelte]');
723
+ const style = document.querySelector('style[data-sveltekit]');
712
724
  if (style) style.remove();
713
725
 
714
726
  page = result.props.page;
@@ -735,7 +747,7 @@ function create_client({ target, session, base, trailing_slash }) {
735
747
  * stuff: Record<string, any>;
736
748
  * branch: Array<import('./types').BranchNode | undefined>;
737
749
  * status: number;
738
- * error?: Error;
750
+ * error: Error | null;
739
751
  * routeId: string | null;
740
752
  * }} opts
741
753
  */
@@ -758,6 +770,8 @@ function create_client({ target, session, base, trailing_slash }) {
758
770
  url,
759
771
  params,
760
772
  branch,
773
+ error,
774
+ stuff,
761
775
  session_id
762
776
  },
763
777
  props: {
@@ -770,7 +784,13 @@ function create_client({ target, session, base, trailing_slash }) {
770
784
  result.props[`props_${i}`] = loaded ? await loaded.props : null;
771
785
  }
772
786
 
773
- if (!current.url || url.href !== current.url.href) {
787
+ const page_changed =
788
+ !current.url ||
789
+ url.href !== current.url.href ||
790
+ current.error !== error ||
791
+ current.stuff !== stuff;
792
+
793
+ if (page_changed) {
774
794
  result.props.page = { error, params, routeId, status, stuff, url };
775
795
 
776
796
  // TODO remove this for 1.0
@@ -954,14 +974,14 @@ function create_client({ target, session, base, trailing_slash }) {
954
974
  let branch = [];
955
975
 
956
976
  /** @type {Record<string, any>} */
957
- let stuff = {};
977
+ let stuff = root_stuff;
958
978
  let stuff_changed = false;
959
979
 
960
980
  /** @type {number | undefined} */
961
981
  let status = 200;
962
982
 
963
- /** @type {Error | undefined} */
964
- let error;
983
+ /** @type {Error | null} */
984
+ let error = null;
965
985
 
966
986
  // preload modules
967
987
  a.forEach((loader) => loader());
@@ -982,7 +1002,7 @@ function create_client({ target, session, base, trailing_slash }) {
982
1002
  (changed.url && previous.uses.url) ||
983
1003
  changed.params.some((param) => previous.uses.params.has(param)) ||
984
1004
  (changed.session && previous.uses.session) ||
985
- Array.from(previous.uses.dependencies).some((dep) => invalidated.has(dep)) ||
1005
+ Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))) ||
986
1006
  (stuff_changed && previous.uses.stuff);
987
1007
 
988
1008
  if (changed_since_last_render) {
@@ -1246,10 +1266,6 @@ function create_client({ target, session, base, trailing_slash }) {
1246
1266
 
1247
1267
  accepted();
1248
1268
 
1249
- navigating++;
1250
-
1251
- const current_navigating_token = (navigating_token = {});
1252
-
1253
1269
  if (started) {
1254
1270
  stores.navigating.set({
1255
1271
  from: current.url,
@@ -1257,18 +1273,13 @@ function create_client({ target, session, base, trailing_slash }) {
1257
1273
  });
1258
1274
  }
1259
1275
 
1260
- await update(normalized, redirect_chain, false, {
1276
+ const completed = await update(normalized, redirect_chain, false, {
1261
1277
  scroll,
1262
1278
  keepfocus,
1263
1279
  details
1264
1280
  });
1265
1281
 
1266
- navigating--;
1267
-
1268
- // navigation was aborted
1269
- if (navigating_token !== current_navigating_token) return;
1270
-
1271
- if (!navigating) {
1282
+ if (completed) {
1272
1283
  const navigation = { from, to: normalized };
1273
1284
  callbacks.after_navigate.forEach((fn) => fn(navigation));
1274
1285
 
@@ -1323,9 +1334,12 @@ function create_client({ target, session, base, trailing_slash }) {
1323
1334
  goto: (href, opts = {}) => goto(href, opts, []),
1324
1335
 
1325
1336
  invalidate: (resource) => {
1326
- const { href } = new URL(resource, location.href);
1327
-
1328
- invalidated.add(href);
1337
+ if (typeof resource === 'function') {
1338
+ invalidated.push(resource);
1339
+ } else {
1340
+ const { href } = new URL(resource, location.href);
1341
+ invalidated.push((dep) => dep === href);
1342
+ }
1329
1343
 
1330
1344
  if (!invalidating) {
1331
1345
  invalidating = Promise.resolve().then(async () => {
@@ -1458,11 +1472,6 @@ function create_client({ target, session, base, trailing_slash }) {
1458
1472
  // Ignore if <a> has a target
1459
1473
  if (is_svg_a_element ? a.target.baseVal : a.target) return;
1460
1474
 
1461
- if (url.href === location.href) {
1462
- if (!location.hash) event.preventDefault();
1463
- return;
1464
- }
1465
-
1466
1475
  // Check if new url only differs by hash and use the browser default behavior in that case
1467
1476
  // This will ensure the `hashchange` event is fired
1468
1477
  // Removing the hash does a full page navigation in the browser, so make sure a hash is present
@@ -1487,7 +1496,7 @@ function create_client({ target, session, base, trailing_slash }) {
1487
1496
  redirect_chain: [],
1488
1497
  details: {
1489
1498
  state: {},
1490
- replaceState: false
1499
+ replaceState: url.href === location.href
1491
1500
  },
1492
1501
  accepted: () => event.preventDefault(),
1493
1502
  blocked: () => event.preventDefault()
@@ -1,5 +1,5 @@
1
1
  <script context="module">
2
- /** @type {import('@sveltejs/kit').ErrorLoad} */
2
+ /** @type {import('@sveltejs/kit').Load} */
3
3
  export function load({ error, status }) {
4
4
  return {
5
5
  props: { error, status }
@@ -143,14 +143,28 @@ async function render_endpoint(event, mod) {
143
143
  }
144
144
 
145
145
  if (!handler) {
146
+ const allowed = [];
147
+
148
+ for (const method in ['get', 'post', 'put', 'patch']) {
149
+ if (mod[method]) allowed.push(method.toUpperCase());
150
+ }
151
+
152
+ if (mod.del) allowed.push('DELETE');
153
+ if (mod.get || mod.head) allowed.push('HEAD');
154
+
146
155
  return event.request.headers.get('x-sveltekit-load')
147
156
  ? // TODO would be nice to avoid these requests altogether,
148
157
  // by noting whether or not page endpoints export `get`
149
158
  new Response(undefined, {
150
159
  status: 204
151
160
  })
152
- : new Response('Method not allowed', {
153
- status: 405
161
+ : new Response(`${event.request.method} method not allowed`, {
162
+ status: 405,
163
+ headers: {
164
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
165
+ // "The server must generate an Allow header field in a 405 status code response"
166
+ allow: allowed.join(', ')
167
+ }
154
168
  });
155
169
  }
156
170
 
@@ -639,7 +653,7 @@ function sha256(data) {
639
653
  if (!key[0]) precompute();
640
654
 
641
655
  const out = init.slice(0);
642
- const array = encode(data);
656
+ const array = encode$1(data);
643
657
 
644
658
  for (let i = 0; i < array.length; i += 16) {
645
659
  const w = array.subarray(i, i + 16);
@@ -789,7 +803,7 @@ function reverse_endianness(bytes) {
789
803
  }
790
804
 
791
805
  /** @param {string} str */
792
- function encode(str) {
806
+ function encode$1(str) {
793
807
  const encoded = encoder.encode(str);
794
808
  const length = encoded.length * 8;
795
809
 
@@ -1276,7 +1290,7 @@ async function render_response({
1276
1290
  } else {
1277
1291
  if (inlined_style) {
1278
1292
  const attributes = [];
1279
- if (options.dev) attributes.push(' data-svelte');
1293
+ if (options.dev) attributes.push(' data-sveltekit');
1280
1294
  if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`);
1281
1295
 
1282
1296
  csp.add_style(inlined_style);
@@ -1424,6 +1438,478 @@ function serialize_error(error) {
1424
1438
  return serialized;
1425
1439
  }
1426
1440
 
1441
+ /*!
1442
+ * cookie
1443
+ * Copyright(c) 2012-2014 Roman Shtylman
1444
+ * Copyright(c) 2015 Douglas Christopher Wilson
1445
+ * MIT Licensed
1446
+ */
1447
+
1448
+ /**
1449
+ * Module exports.
1450
+ * @public
1451
+ */
1452
+
1453
+ var parse_1 = parse$1;
1454
+ var serialize_1 = serialize;
1455
+
1456
+ /**
1457
+ * Module variables.
1458
+ * @private
1459
+ */
1460
+
1461
+ var __toString = Object.prototype.toString;
1462
+
1463
+ /**
1464
+ * RegExp to match field-content in RFC 7230 sec 3.2
1465
+ *
1466
+ * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
1467
+ * field-vchar = VCHAR / obs-text
1468
+ * obs-text = %x80-FF
1469
+ */
1470
+
1471
+ var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
1472
+
1473
+ /**
1474
+ * Parse a cookie header.
1475
+ *
1476
+ * Parse the given cookie header string into an object
1477
+ * The object has the various cookies as keys(names) => values
1478
+ *
1479
+ * @param {string} str
1480
+ * @param {object} [options]
1481
+ * @return {object}
1482
+ * @public
1483
+ */
1484
+
1485
+ function parse$1(str, options) {
1486
+ if (typeof str !== 'string') {
1487
+ throw new TypeError('argument str must be a string');
1488
+ }
1489
+
1490
+ var obj = {};
1491
+ var opt = options || {};
1492
+ var dec = opt.decode || decode;
1493
+
1494
+ var index = 0;
1495
+ while (index < str.length) {
1496
+ var eqIdx = str.indexOf('=', index);
1497
+
1498
+ // no more cookie pairs
1499
+ if (eqIdx === -1) {
1500
+ break
1501
+ }
1502
+
1503
+ var endIdx = str.indexOf(';', index);
1504
+
1505
+ if (endIdx === -1) {
1506
+ endIdx = str.length;
1507
+ } else if (endIdx < eqIdx) {
1508
+ // backtrack on prior semicolon
1509
+ index = str.lastIndexOf(';', eqIdx - 1) + 1;
1510
+ continue
1511
+ }
1512
+
1513
+ var key = str.slice(index, eqIdx).trim();
1514
+
1515
+ // only assign once
1516
+ if (undefined === obj[key]) {
1517
+ var val = str.slice(eqIdx + 1, endIdx).trim();
1518
+
1519
+ // quoted values
1520
+ if (val.charCodeAt(0) === 0x22) {
1521
+ val = val.slice(1, -1);
1522
+ }
1523
+
1524
+ obj[key] = tryDecode(val, dec);
1525
+ }
1526
+
1527
+ index = endIdx + 1;
1528
+ }
1529
+
1530
+ return obj;
1531
+ }
1532
+
1533
+ /**
1534
+ * Serialize data into a cookie header.
1535
+ *
1536
+ * Serialize the a name value pair into a cookie string suitable for
1537
+ * http headers. An optional options object specified cookie parameters.
1538
+ *
1539
+ * serialize('foo', 'bar', { httpOnly: true })
1540
+ * => "foo=bar; httpOnly"
1541
+ *
1542
+ * @param {string} name
1543
+ * @param {string} val
1544
+ * @param {object} [options]
1545
+ * @return {string}
1546
+ * @public
1547
+ */
1548
+
1549
+ function serialize(name, val, options) {
1550
+ var opt = options || {};
1551
+ var enc = opt.encode || encode;
1552
+
1553
+ if (typeof enc !== 'function') {
1554
+ throw new TypeError('option encode is invalid');
1555
+ }
1556
+
1557
+ if (!fieldContentRegExp.test(name)) {
1558
+ throw new TypeError('argument name is invalid');
1559
+ }
1560
+
1561
+ var value = enc(val);
1562
+
1563
+ if (value && !fieldContentRegExp.test(value)) {
1564
+ throw new TypeError('argument val is invalid');
1565
+ }
1566
+
1567
+ var str = name + '=' + value;
1568
+
1569
+ if (null != opt.maxAge) {
1570
+ var maxAge = opt.maxAge - 0;
1571
+
1572
+ if (isNaN(maxAge) || !isFinite(maxAge)) {
1573
+ throw new TypeError('option maxAge is invalid')
1574
+ }
1575
+
1576
+ str += '; Max-Age=' + Math.floor(maxAge);
1577
+ }
1578
+
1579
+ if (opt.domain) {
1580
+ if (!fieldContentRegExp.test(opt.domain)) {
1581
+ throw new TypeError('option domain is invalid');
1582
+ }
1583
+
1584
+ str += '; Domain=' + opt.domain;
1585
+ }
1586
+
1587
+ if (opt.path) {
1588
+ if (!fieldContentRegExp.test(opt.path)) {
1589
+ throw new TypeError('option path is invalid');
1590
+ }
1591
+
1592
+ str += '; Path=' + opt.path;
1593
+ }
1594
+
1595
+ if (opt.expires) {
1596
+ var expires = opt.expires;
1597
+
1598
+ if (!isDate(expires) || isNaN(expires.valueOf())) {
1599
+ throw new TypeError('option expires is invalid');
1600
+ }
1601
+
1602
+ str += '; Expires=' + expires.toUTCString();
1603
+ }
1604
+
1605
+ if (opt.httpOnly) {
1606
+ str += '; HttpOnly';
1607
+ }
1608
+
1609
+ if (opt.secure) {
1610
+ str += '; Secure';
1611
+ }
1612
+
1613
+ if (opt.priority) {
1614
+ var priority = typeof opt.priority === 'string'
1615
+ ? opt.priority.toLowerCase()
1616
+ : opt.priority;
1617
+
1618
+ switch (priority) {
1619
+ case 'low':
1620
+ str += '; Priority=Low';
1621
+ break
1622
+ case 'medium':
1623
+ str += '; Priority=Medium';
1624
+ break
1625
+ case 'high':
1626
+ str += '; Priority=High';
1627
+ break
1628
+ default:
1629
+ throw new TypeError('option priority is invalid')
1630
+ }
1631
+ }
1632
+
1633
+ if (opt.sameSite) {
1634
+ var sameSite = typeof opt.sameSite === 'string'
1635
+ ? opt.sameSite.toLowerCase() : opt.sameSite;
1636
+
1637
+ switch (sameSite) {
1638
+ case true:
1639
+ str += '; SameSite=Strict';
1640
+ break;
1641
+ case 'lax':
1642
+ str += '; SameSite=Lax';
1643
+ break;
1644
+ case 'strict':
1645
+ str += '; SameSite=Strict';
1646
+ break;
1647
+ case 'none':
1648
+ str += '; SameSite=None';
1649
+ break;
1650
+ default:
1651
+ throw new TypeError('option sameSite is invalid');
1652
+ }
1653
+ }
1654
+
1655
+ return str;
1656
+ }
1657
+
1658
+ /**
1659
+ * URL-decode string value. Optimized to skip native call when no %.
1660
+ *
1661
+ * @param {string} str
1662
+ * @returns {string}
1663
+ */
1664
+
1665
+ function decode (str) {
1666
+ return str.indexOf('%') !== -1
1667
+ ? decodeURIComponent(str)
1668
+ : str
1669
+ }
1670
+
1671
+ /**
1672
+ * URL-encode value.
1673
+ *
1674
+ * @param {string} str
1675
+ * @returns {string}
1676
+ */
1677
+
1678
+ function encode (val) {
1679
+ return encodeURIComponent(val)
1680
+ }
1681
+
1682
+ /**
1683
+ * Determine if value is a Date.
1684
+ *
1685
+ * @param {*} val
1686
+ * @private
1687
+ */
1688
+
1689
+ function isDate (val) {
1690
+ return __toString.call(val) === '[object Date]' ||
1691
+ val instanceof Date
1692
+ }
1693
+
1694
+ /**
1695
+ * Try decoding a string using a decoding function.
1696
+ *
1697
+ * @param {string} str
1698
+ * @param {function} decode
1699
+ * @private
1700
+ */
1701
+
1702
+ function tryDecode(str, decode) {
1703
+ try {
1704
+ return decode(str);
1705
+ } catch (e) {
1706
+ return str;
1707
+ }
1708
+ }
1709
+
1710
+ var setCookie = {exports: {}};
1711
+
1712
+ var defaultParseOptions = {
1713
+ decodeValues: true,
1714
+ map: false,
1715
+ silent: false,
1716
+ };
1717
+
1718
+ function isNonEmptyString(str) {
1719
+ return typeof str === "string" && !!str.trim();
1720
+ }
1721
+
1722
+ function parseString(setCookieValue, options) {
1723
+ var parts = setCookieValue.split(";").filter(isNonEmptyString);
1724
+ var nameValue = parts.shift().split("=");
1725
+ var name = nameValue.shift();
1726
+ var value = nameValue.join("="); // everything after the first =, joined by a "=" if there was more than one part
1727
+
1728
+ options = options
1729
+ ? Object.assign({}, defaultParseOptions, options)
1730
+ : defaultParseOptions;
1731
+
1732
+ try {
1733
+ value = options.decodeValues ? decodeURIComponent(value) : value; // decode cookie value
1734
+ } catch (e) {
1735
+ console.error(
1736
+ "set-cookie-parser encountered an error while decoding a cookie with value '" +
1737
+ value +
1738
+ "'. Set options.decodeValues to false to disable this feature.",
1739
+ e
1740
+ );
1741
+ }
1742
+
1743
+ var cookie = {
1744
+ name: name, // grab everything before the first =
1745
+ value: value,
1746
+ };
1747
+
1748
+ parts.forEach(function (part) {
1749
+ var sides = part.split("=");
1750
+ var key = sides.shift().trimLeft().toLowerCase();
1751
+ var value = sides.join("=");
1752
+ if (key === "expires") {
1753
+ cookie.expires = new Date(value);
1754
+ } else if (key === "max-age") {
1755
+ cookie.maxAge = parseInt(value, 10);
1756
+ } else if (key === "secure") {
1757
+ cookie.secure = true;
1758
+ } else if (key === "httponly") {
1759
+ cookie.httpOnly = true;
1760
+ } else if (key === "samesite") {
1761
+ cookie.sameSite = value;
1762
+ } else {
1763
+ cookie[key] = value;
1764
+ }
1765
+ });
1766
+
1767
+ return cookie;
1768
+ }
1769
+
1770
+ function parse(input, options) {
1771
+ options = options
1772
+ ? Object.assign({}, defaultParseOptions, options)
1773
+ : defaultParseOptions;
1774
+
1775
+ if (!input) {
1776
+ if (!options.map) {
1777
+ return [];
1778
+ } else {
1779
+ return {};
1780
+ }
1781
+ }
1782
+
1783
+ if (input.headers && input.headers["set-cookie"]) {
1784
+ // fast-path for node.js (which automatically normalizes header names to lower-case
1785
+ input = input.headers["set-cookie"];
1786
+ } else if (input.headers) {
1787
+ // slow-path for other environments - see #25
1788
+ var sch =
1789
+ input.headers[
1790
+ Object.keys(input.headers).find(function (key) {
1791
+ return key.toLowerCase() === "set-cookie";
1792
+ })
1793
+ ];
1794
+ // warn if called on a request-like object with a cookie header rather than a set-cookie header - see #34, 36
1795
+ if (!sch && input.headers.cookie && !options.silent) {
1796
+ console.warn(
1797
+ "Warning: set-cookie-parser appears to have been called on a request object. It is designed to parse Set-Cookie headers from responses, not Cookie headers from requests. Set the option {silent: true} to suppress this warning."
1798
+ );
1799
+ }
1800
+ input = sch;
1801
+ }
1802
+ if (!Array.isArray(input)) {
1803
+ input = [input];
1804
+ }
1805
+
1806
+ options = options
1807
+ ? Object.assign({}, defaultParseOptions, options)
1808
+ : defaultParseOptions;
1809
+
1810
+ if (!options.map) {
1811
+ return input.filter(isNonEmptyString).map(function (str) {
1812
+ return parseString(str, options);
1813
+ });
1814
+ } else {
1815
+ var cookies = {};
1816
+ return input.filter(isNonEmptyString).reduce(function (cookies, str) {
1817
+ var cookie = parseString(str, options);
1818
+ cookies[cookie.name] = cookie;
1819
+ return cookies;
1820
+ }, cookies);
1821
+ }
1822
+ }
1823
+
1824
+ /*
1825
+ Set-Cookie header field-values are sometimes comma joined in one string. This splits them without choking on commas
1826
+ that are within a single set-cookie field-value, such as in the Expires portion.
1827
+
1828
+ This is uncommon, but explicitly allowed - see https://tools.ietf.org/html/rfc2616#section-4.2
1829
+ Node.js does this for every header *except* set-cookie - see https://github.com/nodejs/node/blob/d5e363b77ebaf1caf67cd7528224b651c86815c1/lib/_http_incoming.js#L128
1830
+ React Native's fetch does this for *every* header, including set-cookie.
1831
+
1832
+ Based on: https://github.com/google/j2objc/commit/16820fdbc8f76ca0c33472810ce0cb03d20efe25
1833
+ Credits to: https://github.com/tomball for original and https://github.com/chrusart for JavaScript implementation
1834
+ */
1835
+ function splitCookiesString(cookiesString) {
1836
+ if (Array.isArray(cookiesString)) {
1837
+ return cookiesString;
1838
+ }
1839
+ if (typeof cookiesString !== "string") {
1840
+ return [];
1841
+ }
1842
+
1843
+ var cookiesStrings = [];
1844
+ var pos = 0;
1845
+ var start;
1846
+ var ch;
1847
+ var lastComma;
1848
+ var nextStart;
1849
+ var cookiesSeparatorFound;
1850
+
1851
+ function skipWhitespace() {
1852
+ while (pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))) {
1853
+ pos += 1;
1854
+ }
1855
+ return pos < cookiesString.length;
1856
+ }
1857
+
1858
+ function notSpecialChar() {
1859
+ ch = cookiesString.charAt(pos);
1860
+
1861
+ return ch !== "=" && ch !== ";" && ch !== ",";
1862
+ }
1863
+
1864
+ while (pos < cookiesString.length) {
1865
+ start = pos;
1866
+ cookiesSeparatorFound = false;
1867
+
1868
+ while (skipWhitespace()) {
1869
+ ch = cookiesString.charAt(pos);
1870
+ if (ch === ",") {
1871
+ // ',' is a cookie separator if we have later first '=', not ';' or ','
1872
+ lastComma = pos;
1873
+ pos += 1;
1874
+
1875
+ skipWhitespace();
1876
+ nextStart = pos;
1877
+
1878
+ while (pos < cookiesString.length && notSpecialChar()) {
1879
+ pos += 1;
1880
+ }
1881
+
1882
+ // currently special character
1883
+ if (pos < cookiesString.length && cookiesString.charAt(pos) === "=") {
1884
+ // we found cookies separator
1885
+ cookiesSeparatorFound = true;
1886
+ // pos is inside the next cookie, so back up and return it.
1887
+ pos = nextStart;
1888
+ cookiesStrings.push(cookiesString.substring(start, lastComma));
1889
+ start = pos;
1890
+ } else {
1891
+ // in param ',' or param separator ';',
1892
+ // we continue from that comma
1893
+ pos = lastComma + 1;
1894
+ }
1895
+ } else {
1896
+ pos += 1;
1897
+ }
1898
+ }
1899
+
1900
+ if (!cookiesSeparatorFound || pos >= cookiesString.length) {
1901
+ cookiesStrings.push(cookiesString.substring(start, cookiesString.length));
1902
+ }
1903
+ }
1904
+
1905
+ return cookiesStrings;
1906
+ }
1907
+
1908
+ setCookie.exports = parse;
1909
+ setCookie.exports.parse = parse;
1910
+ var parseString_1 = setCookie.exports.parseString = parseString;
1911
+ var splitCookiesString_1 = setCookie.exports.splitCookiesString = splitCookiesString;
1912
+
1427
1913
  /**
1428
1914
  * @param {import('types').LoadOutput} loaded
1429
1915
  * @returns {import('types').NormalizedLoadOutput}
@@ -1553,6 +2039,32 @@ function normalize_path(path, trailing_slash) {
1553
2039
  return path;
1554
2040
  }
1555
2041
 
2042
+ /**
2043
+ * @param {string} hostname
2044
+ * @param {string} [constraint]
2045
+ */
2046
+ function domain_matches(hostname, constraint) {
2047
+ if (!constraint) return true;
2048
+
2049
+ const normalized = constraint[0] === '.' ? constraint.slice(1) : constraint;
2050
+
2051
+ if (hostname === normalized) return true;
2052
+ return hostname.endsWith('.' + normalized);
2053
+ }
2054
+
2055
+ /**
2056
+ * @param {string} path
2057
+ * @param {string} [constraint]
2058
+ */
2059
+ function path_matches(path, constraint) {
2060
+ if (!constraint) return true;
2061
+
2062
+ const normalized = constraint.endsWith('/') ? constraint.slice(0, -1) : constraint;
2063
+
2064
+ if (path === normalized) return true;
2065
+ return path.startsWith(normalized + '/');
2066
+ }
2067
+
1556
2068
  /**
1557
2069
  * @param {{
1558
2070
  * event: import('types').RequestEvent;
@@ -1589,10 +2101,10 @@ async function load_node({
1589
2101
  /** @type {Array<import('./types').Fetched>} */
1590
2102
  const fetched = [];
1591
2103
 
1592
- /**
1593
- * @type {string[]}
1594
- */
1595
- let set_cookie_headers = [];
2104
+ const cookies = parse_1(event.request.headers.get('cookie') || '');
2105
+
2106
+ /** @type {import('set-cookie-parser').Cookie[]} */
2107
+ const new_cookies = [];
1596
2108
 
1597
2109
  /** @type {import('types').LoadOutput} */
1598
2110
  let loaded;
@@ -1608,7 +2120,9 @@ async function load_node({
1608
2120
  : {};
1609
2121
 
1610
2122
  if (shadow.cookies) {
1611
- set_cookie_headers.push(...shadow.cookies);
2123
+ shadow.cookies.forEach((header) => {
2124
+ new_cookies.push(parseString_1(header));
2125
+ });
1612
2126
  }
1613
2127
 
1614
2128
  if (shadow.error) {
@@ -1714,9 +2228,23 @@ async function load_node({
1714
2228
  if (opts.credentials !== 'omit') {
1715
2229
  uses_credentials = true;
1716
2230
 
1717
- const cookie = event.request.headers.get('cookie');
1718
2231
  const authorization = event.request.headers.get('authorization');
1719
2232
 
2233
+ // combine cookies from the initiating request with any that were
2234
+ // added via set-cookie
2235
+ const combined_cookies = { ...cookies };
2236
+
2237
+ for (const cookie of new_cookies) {
2238
+ if (!domain_matches(event.url.hostname, cookie.domain)) continue;
2239
+ if (!path_matches(resolved, cookie.path)) continue;
2240
+
2241
+ combined_cookies[cookie.name] = cookie.value;
2242
+ }
2243
+
2244
+ const cookie = Object.entries(combined_cookies)
2245
+ .map(([name, value]) => `${name}=${value}`)
2246
+ .join('; ');
2247
+
1720
2248
  if (cookie) {
1721
2249
  opts.headers.set('cookie', cookie);
1722
2250
  }
@@ -1779,6 +2307,14 @@ async function load_node({
1779
2307
  response = await options.hooks.externalFetch.call(null, external_request);
1780
2308
  }
1781
2309
 
2310
+ const set_cookie = response.headers.get('set-cookie');
2311
+ if (set_cookie) {
2312
+ new_cookies.push(
2313
+ ...splitCookiesString_1(set_cookie)
2314
+ .map((str) => parseString_1(str))
2315
+ );
2316
+ }
2317
+
1782
2318
  const proxy = new Proxy(response, {
1783
2319
  get(response, key, _receiver) {
1784
2320
  async function text() {
@@ -1787,9 +2323,8 @@ async function load_node({
1787
2323
  /** @type {import('types').ResponseHeaders} */
1788
2324
  const headers = {};
1789
2325
  for (const [key, value] of response.headers) {
1790
- if (key === 'set-cookie') {
1791
- set_cookie_headers = set_cookie_headers.concat(value);
1792
- } else if (key !== 'etag') {
2326
+ // TODO skip others besides set-cookie and etag?
2327
+ if (key !== 'set-cookie' && key !== 'etag') {
1793
2328
  headers[key] = value;
1794
2329
  }
1795
2330
  }
@@ -1910,7 +2445,11 @@ async function load_node({
1910
2445
  loaded: normalize(loaded),
1911
2446
  stuff: loaded.stuff || stuff,
1912
2447
  fetched,
1913
- set_cookie_headers,
2448
+ set_cookie_headers: new_cookies.map((new_cookie) => {
2449
+ const { name, value, ...options } = new_cookie;
2450
+ // @ts-expect-error
2451
+ return serialize_1(name, value, options);
2452
+ }),
1914
2453
  uses_credentials
1915
2454
  };
1916
2455
  }
@@ -9,7 +9,7 @@ import { s } from './misc.js';
9
9
  import { d as deep_merge } from './object.js';
10
10
  import { n as normalize_path, r as resolve, i as is_root_relative } from './url.js';
11
11
  import { svelte } from '@sveltejs/vite-plugin-svelte';
12
- import { pathToFileURL, URL } from 'url';
12
+ import { pathToFileURL, URL as URL$1 } from 'url';
13
13
  import { installFetch } from '../install-fetch.js';
14
14
  import 'sade';
15
15
  import 'child_process';
@@ -293,7 +293,6 @@ import root from '__GENERATED__/root.svelte';
293
293
  import { respond } from '${runtime}/server/index.js';
294
294
  import { set_paths, assets, base } from '${runtime}/paths.js';
295
295
  import { set_prerendering } from '${runtime}/env.js';
296
- import * as user_hooks from ${s(hooks)};
297
296
 
298
297
  const template = ({ head, body, assets, nonce }) => ${s(template)
299
298
  .replace('%svelte.head%', '" + head + "')
@@ -463,6 +462,13 @@ async function build_server(
463
462
  const default_config = {
464
463
  build: {
465
464
  target: 'es2020'
465
+ },
466
+ ssr: {
467
+ // when developing against the Kit src code, we want to ensure that
468
+ // our dependencies are bundled so that apps don't need to install
469
+ // them as peerDependencies
470
+ noExternal: []
471
+
466
472
  }
467
473
  };
468
474
 
@@ -1096,7 +1102,7 @@ async function prerender({ config, entries, files, log }) {
1096
1102
  for (const [dependency_path, result] of dependencies) {
1097
1103
  // this seems circuitous, but using new URL allows us to not care
1098
1104
  // whether dependency_path is encoded or not
1099
- const encoded_dependency_path = new URL(dependency_path, 'http://localhost').pathname;
1105
+ const encoded_dependency_path = new URL$1(dependency_path, 'http://localhost').pathname;
1100
1106
  const decoded_dependency_path = decodeURI(encoded_dependency_path);
1101
1107
 
1102
1108
  const body = result.body ?? new Uint8Array(await result.response.arrayBuffer());
@@ -1118,7 +1124,7 @@ async function prerender({ config, entries, files, log }) {
1118
1124
  const resolved = resolve(encoded, href);
1119
1125
  if (!is_root_relative(resolved)) continue;
1120
1126
 
1121
- const parsed = new URL(resolved, 'http://localhost');
1127
+ const parsed = new URL$1(resolved, 'http://localhost');
1122
1128
 
1123
1129
  if (parsed.search) ;
1124
1130
 
@@ -42,11 +42,12 @@ function create_builder({ config, build_data, prerendered, log }) {
42
42
  config,
43
43
  prerendered,
44
44
 
45
- createEntries(fn) {
45
+ async createEntries(fn) {
46
46
  const { routes } = build_data.manifest_data;
47
47
 
48
48
  /** @type {import('types').RouteDefinition[]} */
49
49
  const facades = routes.map((route) => ({
50
+ id: route.id,
50
51
  type: route.type,
51
52
  segments: route.id.split('/').map((segment) => ({
52
53
  dynamic: segment.includes('['),
@@ -90,7 +91,7 @@ function create_builder({ config, build_data, prerendered, log }) {
90
91
  });
91
92
 
92
93
  if (filtered.size > 0) {
93
- complete({
94
+ await complete({
94
95
  generateManifest: ({ relativePath, format }) =>
95
96
  generate_manifest({
96
97
  build_data,
@@ -789,32 +789,23 @@ function write_tsconfig(config) {
789
789
  JSON.stringify(
790
790
  {
791
791
  compilerOptions: {
792
- moduleResolution: 'node',
793
- module: 'es2020',
794
- lib: ['es2020', 'DOM'],
795
- target: 'es2020',
796
- // svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
797
- // to enforce using \`import type\` instead of \`import\` for Types.
798
- importsNotUsedAsValues: 'error',
799
- // TypeScript doesn't know about import usages in the template because it only sees the
800
- // script of a Svelte file. Therefore preserve all value imports. Requires TS 4.5 or higher.
801
- preserveValueImports: true,
802
- isolatedModules: true,
803
- resolveJsonModule: true,
804
- // To have warnings/errors of the Svelte compiler at the correct position,
805
- // enable source maps by default.
806
- sourceMap: true,
807
- esModuleInterop: true,
808
- skipLibCheck: true,
809
- forceConsistentCasingInFileNames: true,
792
+ // generated options
810
793
  baseUrl: config_relative('.'),
811
- allowJs: true,
812
- checkJs: true,
813
794
  paths: {
814
795
  $lib: [project_relative(config.kit.files.lib)],
815
796
  '$lib/*': [project_relative(config.kit.files.lib + '/*')]
816
797
  },
817
- rootDirs: [config_relative('.'), './types']
798
+ rootDirs: [config_relative('.'), './types'],
799
+
800
+ // essential options
801
+ // svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
802
+ // to enforce using \`import type\` instead of \`import\` for Types.
803
+ importsNotUsedAsValues: 'error',
804
+ // Vite compiles modules one at a time
805
+ isolatedModules: true,
806
+ // TypeScript doesn't know about import usages in the template because it only sees the
807
+ // script of a Svelte file. Therefore preserve all value imports. Requires TS 4.5 or higher.
808
+ preserveValueImports: true
818
809
  },
819
810
  include,
820
811
  exclude: [config_relative('node_modules/**'), './**']
package/dist/cli.js CHANGED
@@ -870,7 +870,7 @@ async function launch(port, https, base) {
870
870
  exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}${base}`);
871
871
  }
872
872
 
873
- const prog = sade('svelte-kit').version('1.0.0-next.314');
873
+ const prog = sade('svelte-kit').version('1.0.0-next.317');
874
874
 
875
875
  prog
876
876
  .command('dev')
@@ -1049,7 +1049,7 @@ async function check_port(port) {
1049
1049
  function welcome({ port, host, https, open, base, loose, allow, cwd }) {
1050
1050
  if (open) launch(port, https, base);
1051
1051
 
1052
- console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.314'}\n`));
1052
+ console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.317'}\n`));
1053
1053
 
1054
1054
  const protocol = https ? 'https:' : 'http:';
1055
1055
  const exposed = typeof host !== 'undefined' && host !== 'localhost' && host !== '127.0.0.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.314",
3
+ "version": "1.0.0-next.317",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -23,6 +23,7 @@
23
23
  "@types/mime": "^2.0.3",
24
24
  "@types/node": "^16.11.11",
25
25
  "@types/sade": "^1.7.3",
26
+ "@types/set-cookie-parser": "^2.4.2",
26
27
  "amphtml-validator": "^1.0.35",
27
28
  "cookie": "^0.5.0",
28
29
  "cross-env": "^7.0.3",
@@ -36,6 +37,7 @@
36
37
  "port-authority": "^1.1.2",
37
38
  "rollup": "^2.60.2",
38
39
  "selfsigned": "^2.0.0",
40
+ "set-cookie-parser": "^2.4.8",
39
41
  "sirv": "^2.0.0",
40
42
  "svelte": "^3.44.2",
41
43
  "svelte-check": "^2.5.0",
@@ -103,9 +103,9 @@ declare module '$app/navigation' {
103
103
  ): Promise<void>;
104
104
  /**
105
105
  * Causes any `load` functions belonging to the currently active page to re-run if they `fetch` the resource in question. Returns a `Promise` that resolves when the page is subsequently updated.
106
- * @param href The invalidated resource
106
+ * @param dependency The invalidated resource
107
107
  */
108
- export function invalidate(href: string): Promise<void>;
108
+ export function invalidate(dependency: string | ((href: string) => boolean)): Promise<void>;
109
109
  /**
110
110
  * Programmatically prefetches the given page, which means
111
111
  * 1. ensuring that the code for the page is loaded, and
package/types/index.d.ts CHANGED
@@ -38,7 +38,7 @@ export interface Builder {
38
38
  * Create entry points that map to individual functions
39
39
  * @param fn A function that groups a set of routes into an entry point
40
40
  */
41
- createEntries(fn: (route: RouteDefinition) => AdapterEntry): void;
41
+ createEntries(fn: (route: RouteDefinition) => AdapterEntry): Promise<void>;
42
42
 
43
43
  generateManifest: (opts: { relativePath: string; format?: 'esm' | 'cjs' }) => string;
44
44
 
@@ -23,7 +23,7 @@ export interface AdapterEntry {
23
23
  */
24
24
  complete: (entry: {
25
25
  generateManifest: (opts: { relativePath: string; format?: 'esm' | 'cjs' }) => string;
26
- }) => void;
26
+ }) => Promise<void>;
27
27
  }
28
28
 
29
29
  // Based on https://github.com/josh-hemphill/csp-typed-directives/blob/latest/src/csp.types.ts
@@ -222,6 +222,7 @@ export interface ResolveOptions {
222
222
  export type ResponseHeaders = Record<string, string | number | string[]>;
223
223
 
224
224
  export interface RouteDefinition {
225
+ id: string;
225
226
  type: 'page' | 'endpoint';
226
227
  pattern: RegExp;
227
228
  segments: RouteSegment[];