@sveltejs/kit 1.0.0-next.462 → 1.0.0-next.463

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/kit",
3
- "version": "1.0.0-next.462",
3
+ "version": "1.0.0-next.463",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/sveltejs/kit",
@@ -10,7 +10,7 @@
10
10
  "homepage": "https://kit.svelte.dev",
11
11
  "type": "module",
12
12
  "dependencies": {
13
- "@sveltejs/vite-plugin-svelte": "^1.0.3",
13
+ "@sveltejs/vite-plugin-svelte": "^1.0.4",
14
14
  "cookie": "^0.5.0",
15
15
  "devalue": "^3.1.2",
16
16
  "kleur": "^4.1.4",
@@ -2,7 +2,7 @@ import { onMount, tick } from 'svelte';
2
2
  import { normalize_error } from '../../utils/error.js';
3
3
  import { make_trackable, decode_params, normalize_path } from '../../utils/url.js';
4
4
  import { find_anchor, get_base_uri, scroll_state } from './utils.js';
5
- import { lock_fetch, unlock_fetch, initial_fetch, native_fetch } from './fetcher.js';
5
+ import { lock_fetch, unlock_fetch, initial_fetch, subsequent_fetch } from './fetcher.js';
6
6
  import { parse } from './parse.js';
7
7
  import { error } from '../../exports/index.js';
8
8
 
@@ -584,11 +584,13 @@ export function create_client({ target, base, trailing_slash }) {
584
584
  }
585
585
 
586
586
  // we must fixup relative urls so they are resolved from the target page
587
- const normalized = new URL(requested, url).href;
588
- depends(normalized);
587
+ const resolved = new URL(requested, url).href;
588
+ depends(resolved);
589
589
 
590
- // prerendered pages may be served from any origin, so `initial_fetch` urls shouldn't be normalized
591
- return started ? native_fetch(normalized, init) : initial_fetch(requested, init);
590
+ // prerendered pages may be served from any origin, so `initial_fetch` urls shouldn't be resolved
591
+ return started
592
+ ? subsequent_fetch(resolved, init)
593
+ : initial_fetch(requested, resolved, init);
592
594
  },
593
595
  setHeaders: () => {}, // noop
594
596
  depends,
@@ -2,7 +2,7 @@ import { hash } from '../hash.js';
2
2
 
3
3
  let loading = 0;
4
4
 
5
- export const native_fetch = window.fetch;
5
+ const native_fetch = window.fetch;
6
6
 
7
7
  export function lock_fetch() {
8
8
  loading += 1;
@@ -33,15 +33,40 @@ if (import.meta.env.DEV) {
33
33
  );
34
34
  }
35
35
 
36
+ const method = input instanceof Request ? input.method : init?.method || 'GET';
37
+
38
+ if (method !== 'GET') {
39
+ const url = new URL(input instanceof Request ? input.url : input.toString(), document.baseURI)
40
+ .href;
41
+ cache.delete(url);
42
+ }
43
+
44
+ return native_fetch(input, init);
45
+ };
46
+ } else {
47
+ window.fetch = (input, init) => {
48
+ const method = input instanceof Request ? input.method : init?.method || 'GET';
49
+
50
+ if (method !== 'GET') {
51
+ const url = new URL(input instanceof Request ? input.url : input.toString(), document.baseURI)
52
+ .href;
53
+ cache.delete(url);
54
+ }
55
+
36
56
  return native_fetch(input, init);
37
57
  };
38
58
  }
39
59
 
60
+ const cache = new Map();
61
+
40
62
  /**
63
+ * Should be called on the initial run of load functions that hydrate the page.
64
+ * Saves any requests with cache-control max-age to the cache.
41
65
  * @param {RequestInfo} resource
66
+ * @param {string} resolved
42
67
  * @param {RequestInit} [opts]
43
68
  */
44
- export function initial_fetch(resource, opts) {
69
+ export function initial_fetch(resource, resolved, opts) {
45
70
  const url = JSON.stringify(typeof resource === 'string' ? resource : resource.url);
46
71
 
47
72
  let selector = `script[data-sveltekit-fetched][data-url=${url}]`;
@@ -51,10 +76,32 @@ export function initial_fetch(resource, opts) {
51
76
  }
52
77
 
53
78
  const script = document.querySelector(selector);
54
- if (script && script.textContent) {
79
+ if (script?.textContent) {
55
80
  const { body, ...init } = JSON.parse(script.textContent);
81
+
82
+ const ttl = script.getAttribute('data-ttl');
83
+ if (ttl) cache.set(resolved, { body, init, ttl: 1000 * Number(ttl) });
84
+
56
85
  return Promise.resolve(new Response(body, init));
57
86
  }
58
87
 
59
88
  return native_fetch(resource, opts);
60
89
  }
90
+
91
+ /**
92
+ * Tries to get the response from the cache, if max-age allows it, else does a fetch.
93
+ * @param {string} resolved
94
+ * @param {RequestInit} [opts]
95
+ */
96
+ export function subsequent_fetch(resolved, opts) {
97
+ const cached = cache.get(resolved);
98
+ if (cached) {
99
+ if (performance.now() < cached.ttl) {
100
+ return new Response(cached.body, cached.init);
101
+ }
102
+
103
+ cache.delete(resolved);
104
+ }
105
+
106
+ return native_fetch(resolved, opts);
107
+ }
@@ -214,6 +214,7 @@ export function create_fetch({ event, options, state, route, prerender_default }
214
214
 
215
215
  fetched.push({
216
216
  url: requested,
217
+ method: opts.method || 'GET',
217
218
  body: opts.body,
218
219
  response: {
219
220
  status: status_number,
@@ -284,7 +284,7 @@ export async function render_response({
284
284
  }
285
285
 
286
286
  if (page_config.ssr && page_config.csr) {
287
- body += `\n\t${fetched.map(serialize_data).join('\n\t')}`;
287
+ body += `\n\t${fetched.map((item) => serialize_data(item, !!state.prerendering)).join('\n\t')}`;
288
288
  }
289
289
 
290
290
  if (options.service_worker) {
@@ -35,10 +35,11 @@ const pattern = new RegExp(`[${Object.keys(replacements).join('')}]`, 'g');
35
35
  * and that the resulting string isn't further modified.
36
36
  *
37
37
  * @param {import('./types.js').Fetched} fetched
38
+ * @param {boolean} [prerendering]
38
39
  * @returns {string} The raw HTML of a script element carrying the JSON payload.
39
40
  * @example const html = serialize_data('/data.json', null, { foo: 'bar' });
40
41
  */
41
- export function serialize_data(fetched) {
42
+ export function serialize_data(fetched, prerendering = false) {
42
43
  const safe_payload = JSON.stringify(fetched.response).replace(
43
44
  pattern,
44
45
  (match) => replacements[match]
@@ -54,5 +55,18 @@ export function serialize_data(fetched) {
54
55
  attrs.push(`data-hash=${escape_html_attr(hash(fetched.body))}`);
55
56
  }
56
57
 
58
+ if (!prerendering && fetched.method === 'GET') {
59
+ const cache_control = /** @type {string} */ (fetched.response.headers['cache-control']);
60
+ if (cache_control) {
61
+ const match = /s-maxage=(\d+)/g.exec(cache_control) ?? /max-age=(\d+)/g.exec(cache_control);
62
+ if (match) {
63
+ const age = /** @type {string} */ (fetched.response.headers['age']) ?? '0';
64
+
65
+ const ttl = +match[1] - +age;
66
+ attrs.push(`data-ttl="${ttl}"`);
67
+ }
68
+ }
69
+ }
70
+
57
71
  return `<script ${attrs.join(' ')}>${safe_payload}</script>`;
58
72
  }
@@ -3,6 +3,7 @@ import { HttpError } from '../../control.js';
3
3
 
4
4
  export interface Fetched {
5
5
  url: string;
6
+ method: string;
6
7
  body?: string | null;
7
8
  response: {
8
9
  status: number;
@@ -88,6 +88,7 @@ declare module '$app/environment' {
88
88
  * disableScrollHandling,
89
89
  * goto,
90
90
  * invalidate,
91
+ * invalidateAll,
91
92
  * prefetch,
92
93
  * prefetchRoutes
93
94
  * } from '$app/navigation';