Repository: sveltejs/kit (js)

Issue:
+server.js - fetch call: Error: Not found (but works on +page.server.js)
### Describe the bug

I am trying to implement a RSS feed similarly to what the docs explain for doing a sitemap (https://kit.svelte.dev/docs/seo#manual-setup-sitemaps). 

In the +server.js, When trying to do a fetch call to the endpoint "/api/posts", I get a 404 response when using "event.fetch" and then a crash:

```js
function getFeedItems(fetch) {  
     return fetchPosts(fetch).then(data => {
     console.log(data); // JSON parse error, because i received a 404 html response
     }
  );
}

export async function GET(event) {  
    getFeedItems(event.fetch);  
    return new Response( `...` 
         .trim(),    
           {      
               headers: {        
                    'Content-Type': 'application/xml'      
                }    
           }  
     );
}
```

I've tested calling this endpoint from a +page.server.js, and the data is retrieved as expected. But not using `event.fetch` in a +server.js (also tested using fetch in place of event in the exported GET)


### Reproduction

1. `git clone https://github.com/rsousacode/AspNetSPA_SvelteKit && cd AspNetSPA_SvelteKit && git checkout maybe-bug `
2. Run `dotnet run` (it'll run the whole thing) 
3. In the menu, click on "Not working" and check routes/not-working to see the code

Reproduction github repo: https://github.com/rsousacode/AspNetSPA_SvelteKit/tree/maybe-bug

### Logs

```Shell
Error: Not found: /api/posts
    at resolve (/node_modules/@sveltejs/kit/src/runtime/server/respond.js:391:13)
    at resolve (/node_modules/@sveltejs/kit/src/runtime/server/respond.js:232:5)
    at #options.hooks.handle (/node_modules/@sveltejs/kit/src/runtime/server/index.js:42:55)
    at Module.respond (/node_modules/@sveltejs/kit/src/runtime/server/respond.js:229:40)
    at async fetch (/node_modules/@sveltejs/kit/src/runtime/server/fetch.js:134:16)
    at async eval (/node_modules/@sveltejs/kit/src/runtime/server/fetch.js:27:10)
undefined:1
<!DOCTYPE html>
^

SyntaxError: Unexpected token < in JSON at position 0
```


### System Info

```Shell
MacOs Arm64
Node v18.13.0
Kit: 1.1.0 (main project)
```


### Severity

blocking an upgrade

### Additional Information

_No response_

Pull request description:
fix: prerendered endpoint callable from non-prerendered endpoint
fixes #8851

adds `state.initiator` in `endpoint.js#render_endpoint` similar to https://github.com/sveltejs/kit/pull/8453 as suggested by Simon (dummdidumm)

### Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [x] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
- [x] This message body should clearly illustrate what problems it solves.
- [x] Ideally, include a test that fails without this PR but passes with it.

### Tests
- [x] Run the tests with `pnpm test` and lint the project with `pnpm lint` and `pnpm check`

### Changesets
- [x] If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpm changeset` and following the prompts. Changesets that add features should be `minor` and those that fix bugs should be `patch`. Please prefix changeset messages with `feat:`, `fix:`, or `chore:`.


Patch:
diff --git a/.changeset/loud-pans-fly.md b/.changeset/loud-pans-fly.md
new file mode 100644
index 000000000..1e595deb3
--- /dev/null
+++ b/.changeset/loud-pans-fly.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+docs: discourage use of `goto` with external URLs
diff --git a/.changeset/slimy-donkeys-itch.md b/.changeset/slimy-donkeys-itch.md
new file mode 100644
index 000000000..01aa7b70c
--- /dev/null
+++ b/.changeset/slimy-donkeys-itch.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+fix: ensure endpoints can fetch endpoints on the same host but not part of the application
diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js
index fcce53a98..571080368 100644
--- a/packages/kit/src/runtime/server/endpoint.js
+++ b/packages/kit/src/runtime/server/endpoint.js
@@ -4,11 +4,12 @@ import { method_not_allowed } from './utils.js';
 
 /**
  * @param {import('types').RequestEvent} event
+ * @param {import('types').SSRRoute} route
  * @param {import('types').SSREndpoint} mod
  * @param {import('types').SSRState} state
  * @returns {Promise<Response>}
  */
-export async function render_endpoint(event, mod, state) {
+export async function render_endpoint(event, route, mod, state) {
 	const method = /** @type {import('types').HttpMethod} */ (event.request.method);
 
 	let handler = mod[method];
@@ -38,6 +39,8 @@ export async function render_endpoint(event, mod, state) {
 		}
 	}
 
+	state.initiator = route;
+
 	try {
 		const response = await handler(
 			/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js
index 7b2283630..658b9b25d 100644
--- a/packages/kit/src/runtime/server/respond.js
+++ b/packages/kit/src/runtime/server/respond.js
@@ -358,7 +358,7 @@ export async function respond(request, options, manifest, state) {
 						trailing_slash ?? 'never'
 					);
 				} else if (route.endpoint && (!route.page || is_endpoint_request(event))) {
-					response = await render_endpoint(event, await route.endpoint(), state);
+					response = await render_endpoint(event, route, await route.endpoint(), state);
 				} else if (route.page) {
 					response = await render_page(
 						event,
diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts
index 79801c8a3..606475b4c 100644
--- a/packages/kit/types/ambient.d.ts
+++ b/packages/kit/types/ambient.d.ts
@@ -170,6 +170,7 @@ declare module '$app/navigation' {
 	export function disableScrollHandling(): void;
 	/**
 	 * Returns a Promise that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) to the specified `url`.
+	 * For external URLs, use `window.location = url` instead of calling `goto(url)`.
 	 *
 	 * @param url Where to navigate to. Note that if you've set [`config.kit.paths.base`](https://kit.svelte.dev/docs/configuration#paths) and the URL is root-relative, you need to prepend the base path if you want to navigate within the app.
 	 * @param opts Options related to the navigation
diff --git a/sites/kit.svelte.dev/src/lib/search/SearchResultList.svelte b/sites/kit.svelte.dev/src/lib/search/SearchResultList.svelte
index 41fd1900a..2fd143429 100644
--- a/sites/kit.svelte.dev/src/lib/search/SearchResultList.svelte
+++ b/sites/kit.svelte.dev/src/lib/search/SearchResultList.svelte
@@ -9,14 +9,19 @@
 
 	const dispatch = createEventDispatcher();
 
+	/** @param {string} text */
 	function escape(text) {
 		return text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
 	}
 
+	/**
+	 * @param {string} content
+	 * @param {string} query
+	 */
 	function excerpt(content, query) {
 		const index = content.toLowerCase().indexOf(query.toLowerCase());
 		if (index === -1) {
-			return content.slice(0, 100);
+			return escape(content.slice(0, 100));
 		}
 
 		const prefix = index > 20 ? `…${content.slice(index - 15, index)}` : content.slice(0, index);
@@ -34,7 +39,7 @@
 </script>
 
 <ul>
-	{#each results as result, i}
+	{#each results as result (result.href)}
 		<li>
 			<a
 				data-sveltekit-preload-data


Test patch:
diff --git a/packages/kit/test/apps/basics/src/routes/prerendering/prerendered-endpoint/proxy/+server.js b/packages/kit/test/apps/basics/src/routes/prerendering/prerendered-endpoint/proxy/+server.js
new file mode 100644
index 000000000..59396b64d
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/prerendering/prerendered-endpoint/proxy/+server.js
@@ -0,0 +1,3 @@
+export async function GET({ fetch }) {
+	return await fetch('/prerendering/prerendered-endpoint/api');
+}
diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js
index a375d7621..cad3f9f88 100644
--- a/packages/kit/test/apps/basics/test/server.test.js
+++ b/packages/kit/test/apps/basics/test/server.test.js
@@ -97,6 +97,17 @@ test.describe('Endpoints', () => {
 		expect(headers.head).toEqual(headers.get);
 	});
 
+	test('Prerendered +server.js called from a non-prerendered +server.js works', async ({
+		baseURL
+	}) => {
+		const res = await fetch(`${baseURL}/prerendering/prerendered-endpoint/proxy`);
+
+		expect(res.status).toBe(200);
+		expect(await res.json()).toStrictEqual({
+			message: 'Im prerendered and called from a non-prerendered +page.server.js'
+		});
+	});
+
 	// TODO all the remaining tests in this section are really only testing
 	// setResponse, since we're not otherwise changing anything on the response.
 	// might be worth making these unit tests instead