Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clever-boxes-feel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

fix: serialize server `load` data before passing to universal `load`, to handle mutations
40 changes: 34 additions & 6 deletions packages/kit/src/runtime/server/page/load_data.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { DEV } from 'esm-env';
import * as devalue from 'devalue';
import { disable_search, make_trackable } from '../../../utils/url.js';
import { validate_depends, validate_load_response } from '../../shared.js';
import { with_request_store, merge_tracing } from '@sveltejs/kit/internal/server';
import { record_span } from '../../telemetry/record_span.js';
import { get_node_type } from '../utils.js';
import { clarify_devalue_error, get_node_type } from '../utils.js';
import { base64_encode, text_decoder } from '../../utils.js';

/**
Expand Down Expand Up @@ -232,20 +233,47 @@ export async function load_data({
},
fn: async (current) => {
const traced_event = merge_tracing(event, current);
return await with_request_store({ event: traced_event, state: event_state }, () =>
load.call(null, {
return await with_request_store({ event: traced_event, state: event_state }, () => {
/** @type {Record<string, any> | null} */
let data = null;

return load.call(null, {
url: event.url,
params: event.params,
data: server_data_node?.data ?? null,
get data() {
if (data === null && server_data_node?.data != null) {
/** @type {Record<string, (value: any) => any>} */
const reducers = {};

/** @type {Record<string, (value: any) => any>} */
const revivers = {};

for (const key in event_state.transport) {
reducers[key] = event_state.transport[key].encode;
revivers[key] = event_state.transport[key].decode;
}

// run it through devalue so that the developer can't accidentally mutate it
try {
data = devalue.parse(devalue.stringify(server_data_node.data, reducers), revivers);
} catch (e) {
// @ts-expect-error
e.path = e.path.slice(1);
throw new Error(clarify_devalue_error(event, /** @type {any} */ (e)));
}
}

return data;
},
route: event.route,
fetch: create_universal_fetch(event, state, fetched, csr, resolve_opts),
setHeaders: event.setHeaders,
depends: () => {},
parent,
untrack: (fn) => fn(),
tracing: traced_event.tracing
})
);
});
});
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,11 @@ export async function load({ fetch, data, url }) {
const res = await fetch(new URL('/load/serialization/fetched-from-shared.json', url.origin));
const { b } = await res.json();

return { a, b, c: a + b };
// check that this doesn't mutate the original object
// and make the server data unserializable
// @ts-expect-error
data.sum = () => a + b;

// @ts-expect-error
return { a, b, c: data.sum() };
}
Loading