Skip to content

chore: keep the call context on the event view instead of cloning the request state - #16968

Open
Nic-Polumeyv wants to merge 11 commits into
prerender-configure-oncefrom
request-context
Open

chore: keep the call context on the event view instead of cloning the request state#16968
Nic-Polumeyv wants to merge 11 commits into
prerender-configure-oncefrom
request-context

Conversation

@Nic-Polumeyv

@Nic-Polumeyv Nic-Polumeyv commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

RequestState carries five booleans that are not about the request. is_in_render, is_in_remote_query, is_in_remote_prerender, is_in_remote_form_or_command and is_in_remote_function say what kind of code is on the stack, and the only way to flip one is to clone the whole state object and re-enter the store with the copy, at 11 sites and twice per query call. It only stays correct because every field mutated inside remote code lives under state.remote, which the spread shares by reference.

Kit already builds a per-call view of the event for remote functions, so the kind of code running is a property of that view. derive_event(event, kind) now builds every view, stamps the kinds on the stack as bit flags under a symbol, and applies what that kind may not do (setHeaders, cookie writes, url in a query); is_in(event, 'query') reads them. The flags accumulate through nested views exactly as the spreads did, so behaviour is unchanged, and RequestState keeps only per-request data.

The url/params/route guards used to be redefined on each query view, which V8 answers by dropping the object into dictionary mode. They now live on a prototype, so every view keeps a fast shape.

@pkg-svelte-dev

pkg-svelte-dev Bot commented Aug 27, 2026

Copy link
Copy Markdown

Install the latest version of @sveltejs/kit from 8ff8bc6:

pnpm add https://pkg.svelte.dev/@sveltejs/kit/c/8ff8bc6518d72dc644a1176deb7302c17b685db0

Open in pkg.svelte.dev: https://pkg.svelte.dev/repos/kit/pr/16968

@changeset-bot

changeset-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 8ff8bc6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@svelte-docs-bot

Copy link
Copy Markdown

@Nic-Polumeyv
Nic-Polumeyv changed the base branch from version-3 to main August 27, 2026 23:54
@Nic-Polumeyv
Nic-Polumeyv changed the base branch from main to version-3 August 27, 2026 23:55
@Nic-Polumeyv Nic-Polumeyv changed the title chore: move call-context flags out of RequestState into the request store chore: keep the call context on the event view instead of cloning the request state Aug 28, 2026
@Nic-Polumeyv
Nic-Polumeyv marked this pull request as ready for review August 28, 2026 03:56
@Nic-Polumeyv
Nic-Polumeyv changed the base branch from version-3 to prerender-configure-once September 2, 2026 21:47
@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: QUIET

Plan: Advanced

Run ID: 3ef2de24-fdbe-4691-a1cf-c19315fa79ea

📥 Commits

Reviewing files that changed from the base of the PR and between 814ef96 and be2f7f2.

📒 Files selected for processing (7)
  • packages/kit/src/runtime/app/server/remote/command.js
  • packages/kit/src/runtime/app/server/remote/query.js
  • packages/kit/src/runtime/app/server/remote/requested.js
  • packages/kit/src/runtime/app/server/remote/shared.js
  • packages/kit/src/runtime/server/context.js
  • packages/kit/src/runtime/server/errors.js
  • packages/kit/src/runtime/server/page/render.js
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • sveltejs/svelte (auto-detected)

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.


Walkthrough

The change replaces transient request-state flags with event context kinds. context.js adds context detection and event derivation with query, cookie, header, and path restrictions. Shared remote-function handlers now receive operation kinds. Remote commands, forms, queries, batches, generators, prerendering, and requested operations use the new API. Rendering and error handling derive render events. The obsolete context flags are removed from request state and internal types.

Merge Risk: ⚪ Minimal · up to be2f7

This change moves transient request-context state onto derived event views while preserving nested context checks and applying context-specific request restrictions. No concrete merge-blocking behavior, security, or runtime risk remains.


Comment @coderabbitai help to get the list of available commands.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks overall good, but I'm slightly concerned about the fact that we're putting the query restrictions on a prototype object, because prototypes aren't preserved through spreads.

The restrictions are applied through this:

const derived = Object.assign(Object.create(QUERY_PROTOTYPE), event, ...);

...but in merge_tracing we spread the event:

export function merge_tracing(event_like, current) {
	return {
		...event_like,
		tracing: {
			...event_like.tracing,
			current
		}
	};
}

Object spread copies the enumerable properties, but does not preserve the prototype. Therefore:

const query_event = derive_event(event, 'query');
const traced_event = merge_tracing(query_event, span);

...produces a normal object, where inside(traced_event, 'query') === true, but the protections for traced_event.url, .params, and .route are gone. I don't think we're actually doing this right now (so it's probably a latent bug). I'm not sure what exactly the best solution for that is... 🤔

@Nic-Polumeyv

Nic-Polumeyv commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Solid catch. v3 has the same hole (not 100% sure if you were alluding to already knowing that). But... the non-enumerable getters this replaces are skipped by that spread too, and either way it gives url === undefined, not the real URL. So this PR doesn't loosen anything.

I'm thinking of just routing merge_tracing through derive_event(event, null, { tracing })... that should fix it?

Edit: Actually, rather than patching merge_tracing, we could make the guard a value instead of an accessor. url, params and route on a query view become three module-level proxies that throw on any use, assigned as ordinary data properties:

const poison = (property) => {
      const fail = () => {
              throw new Error(`Cannot access event.${property} in a query. Pass the value as an argument to the query instead`);
      };
      return new Proxy(Object.freeze({}), { get: fail, has: fail, ownKeys: fail, set: fail });
};

const POISON = { url: poison('url'), params: poison('params'), route: poison('route') };

Every copy mechanism carries data properties, and derive_event collapses to a plain literal:

return {
      ...event,
      ...overrides,
      ...(entering & KINDS.remote ? restrictions(event, flags) : null),
      ...(flags & KINDS.query ? POISON : null),
      [CONTEXT]: flags
};

That also drops Object.create and Object.assign. Amazingly, it's about 3x faster per view on V8. The one behaviour change is that the throw moves from touching event.url to using it, so typeof event.url no longer throws.

@elliott-with-the-longest-name-on-github

Copy link
Copy Markdown
Contributor

Hmm, I'm not sure I love that the error is moving from event.url to "doing something with event.url"... it makes it easier to run into something like this:

const url = getRequestEvent();
if (something) {
  url.pathname // throws
}

which just means there are more opportunities for you to write code that throws sometimes but not all the time. As much as I don't like the additional complexity it brings, I think using derive_event inside of merge_tracing is probably the better call. We could also define the CONTEXT property as non-enumerable so that if we ever spread the event somewhere, it's more immediately obvious that we've screwed up (because it's more likely we run into "CONTEXT no longer exists on this object"-related errors).

@Nic-Polumeyv

Copy link
Copy Markdown
Contributor Author

Ohh ok. I underweighted that. Going with my first option then

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants