mirror of
https://github.com/withastro/astro.git
synced 2024-12-30 22:03:56 -05:00
Remove support for simple objects in endpoints (#9181)
* Deprecate simple object from endpoints * Update changeset * Add missing Response return Co-authored-by: Happydev <81974850+MoustaphaDev@users.noreply.github.com> * Update .changeset/clever-beds-notice.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> --------- Co-authored-by: Happydev <81974850+MoustaphaDev@users.noreply.github.com> Co-authored-by: Matthew Phillips <matthew@skypack.dev> Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
parent
37697a2c55
commit
cdabf6ef02
56 changed files with 206 additions and 529 deletions
9
.changeset/clever-beds-notice.md
Normal file
9
.changeset/clever-beds-notice.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
'astro': major
|
||||
---
|
||||
|
||||
Removes support for returning simple objects from endpoints (deprecated since Astro 3.0). You should return a `Response` instead.
|
||||
|
||||
`ResponseWithEncoding` is also removed. You can refactor the code to return a response with an array buffer instead, which is encoding agnostic.
|
||||
|
||||
The types for middlewares have also been revised. To type a middleware function, you should now use `MiddlewareHandler` instead of `MiddlewareResponseHandler`. If you used `defineMiddleware()` to type the function, no changes are needed.
|
|
@ -19,7 +19,6 @@ import type { AstroConfigType } from '../core/config/index.js';
|
|||
import type { AstroTimer } from '../core/config/timer.js';
|
||||
import type { TSConfig } from '../core/config/tsconfig.js';
|
||||
import type { AstroCookies } from '../core/cookies/index.js';
|
||||
import type { ResponseWithEncoding } from '../core/endpoint/index.js';
|
||||
import type { AstroIntegrationLogger, Logger, LoggerLevel } from '../core/logger/core.js';
|
||||
import type { AstroDevOverlay, DevOverlayCanvas } from '../runtime/client/dev-overlay/overlay.js';
|
||||
import type { DevOverlayHighlight } from '../runtime/client/dev-overlay/ui-library/highlight.js';
|
||||
|
@ -2005,8 +2004,6 @@ export interface AstroAdapter {
|
|||
supportedAstroFeatures: AstroFeatureMap;
|
||||
}
|
||||
|
||||
type Body = string;
|
||||
|
||||
export type ValidRedirectStatus = 300 | 301 | 302 | 303 | 304 | 307 | 308;
|
||||
|
||||
// Shared types between `Astro` global and API context object
|
||||
|
@ -2163,7 +2160,6 @@ export interface APIContext<
|
|||
* ```
|
||||
*/
|
||||
locals: App.Locals;
|
||||
ResponseWithEncoding: typeof ResponseWithEncoding;
|
||||
|
||||
/**
|
||||
* Available only when `experimental.i18n` enabled and in SSR.
|
||||
|
@ -2199,22 +2195,12 @@ export interface APIContext<
|
|||
currentLocale: string | undefined;
|
||||
}
|
||||
|
||||
export type EndpointOutput =
|
||||
| {
|
||||
body: Body;
|
||||
encoding?: BufferEncoding;
|
||||
}
|
||||
| {
|
||||
body: Uint8Array;
|
||||
encoding: 'binary';
|
||||
};
|
||||
|
||||
export type APIRoute<Props extends Record<string, any> = Record<string, any>> = (
|
||||
context: APIContext<Props>
|
||||
) => EndpointOutput | Response | Promise<EndpointOutput | Response>;
|
||||
) => Response | Promise<Response>;
|
||||
|
||||
export interface EndpointHandler {
|
||||
[method: string]: APIRoute | ((params: Params, request: Request) => EndpointOutput | Response);
|
||||
[method: string]: APIRoute | ((params: Params, request: Request) => Response);
|
||||
}
|
||||
|
||||
export type Props = Record<string, unknown>;
|
||||
|
@ -2319,20 +2305,16 @@ export interface AstroIntegration {
|
|||
};
|
||||
}
|
||||
|
||||
export type MiddlewareNext<R> = () => Promise<R>;
|
||||
export type MiddlewareHandler<R> = (
|
||||
export type MiddlewareNext = () => Promise<Response>;
|
||||
export type MiddlewareHandler = (
|
||||
context: APIContext,
|
||||
next: MiddlewareNext<R>
|
||||
) => Promise<R> | R | Promise<void> | void;
|
||||
|
||||
export type MiddlewareResponseHandler = MiddlewareHandler<Response>;
|
||||
export type MiddlewareEndpointHandler = MiddlewareHandler<Response | EndpointOutput>;
|
||||
export type MiddlewareNextResponse = MiddlewareNext<Response>;
|
||||
next: MiddlewareNext
|
||||
) => Promise<Response> | Response | Promise<void> | void;
|
||||
|
||||
// NOTE: when updating this file with other functions,
|
||||
// remember to update `plugin-page.ts` too, to add that function as a no-op function.
|
||||
export type AstroMiddlewareInstance<R> = {
|
||||
onRequest?: MiddlewareHandler<R>;
|
||||
export type AstroMiddlewareInstance = {
|
||||
onRequest?: MiddlewareHandler;
|
||||
};
|
||||
|
||||
export type AstroIntegrationMiddleware = {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import type {
|
||||
EndpointHandler,
|
||||
ManifestData,
|
||||
MiddlewareEndpointHandler,
|
||||
RouteData,
|
||||
SSRElement,
|
||||
SSRManifest,
|
||||
|
@ -181,16 +180,14 @@ export class App {
|
|||
);
|
||||
if (i18nMiddleware) {
|
||||
if (mod.onRequest) {
|
||||
this.#pipeline.setMiddlewareFunction(
|
||||
sequence(i18nMiddleware, mod.onRequest as MiddlewareEndpointHandler)
|
||||
);
|
||||
this.#pipeline.setMiddlewareFunction(sequence(i18nMiddleware, mod.onRequest));
|
||||
} else {
|
||||
this.#pipeline.setMiddlewareFunction(i18nMiddleware);
|
||||
}
|
||||
this.#pipeline.onBeforeRenderRoute(i18nPipelineHook);
|
||||
} else {
|
||||
if (mod.onRequest) {
|
||||
this.#pipeline.setMiddlewareFunction(mod.onRequest as MiddlewareEndpointHandler);
|
||||
this.#pipeline.setMiddlewareFunction(mod.onRequest);
|
||||
}
|
||||
}
|
||||
response = await this.#pipeline.renderRoute(renderContext, pageModule);
|
||||
|
@ -322,7 +319,7 @@ export class App {
|
|||
);
|
||||
const page = (await mod.page()) as any;
|
||||
if (skipMiddleware === false && mod.onRequest) {
|
||||
this.#pipeline.setMiddlewareFunction(mod.onRequest as MiddlewareEndpointHandler);
|
||||
this.#pipeline.setMiddlewareFunction(mod.onRequest);
|
||||
}
|
||||
if (skipMiddleware) {
|
||||
// make sure middleware set by other requests is cleared out
|
||||
|
@ -367,8 +364,8 @@ export class App {
|
|||
const status = override?.status
|
||||
? override.status
|
||||
: oldResponse.status === 200
|
||||
? newResponse.status
|
||||
: oldResponse.status;
|
||||
? newResponse.status
|
||||
: oldResponse.status;
|
||||
|
||||
return new Response(newResponse.body, {
|
||||
status,
|
||||
|
|
|
@ -8,7 +8,6 @@ import type {
|
|||
AstroSettings,
|
||||
ComponentInstance,
|
||||
GetStaticPathsItem,
|
||||
MiddlewareEndpointHandler,
|
||||
RouteData,
|
||||
RouteType,
|
||||
SSRError,
|
||||
|
@ -269,15 +268,13 @@ async function generatePage(
|
|||
);
|
||||
if (config.experimental.i18n && i18nMiddleware) {
|
||||
if (onRequest) {
|
||||
pipeline.setMiddlewareFunction(
|
||||
sequence(i18nMiddleware, onRequest as MiddlewareEndpointHandler)
|
||||
);
|
||||
pipeline.setMiddlewareFunction(sequence(i18nMiddleware, onRequest));
|
||||
} else {
|
||||
pipeline.setMiddlewareFunction(i18nMiddleware);
|
||||
}
|
||||
pipeline.onBeforeRenderRoute(i18nPipelineHook);
|
||||
} else if (onRequest) {
|
||||
pipeline.setMiddlewareFunction(onRequest as MiddlewareEndpointHandler);
|
||||
pipeline.setMiddlewareFunction(onRequest);
|
||||
}
|
||||
if (!pageModulePromise) {
|
||||
throw new Error(
|
||||
|
@ -560,7 +557,6 @@ async function generatePath(pathname: string, gopts: GeneratePathOptions, pipeli
|
|||
});
|
||||
|
||||
let body: string | Uint8Array;
|
||||
let encoding: BufferEncoding | undefined;
|
||||
|
||||
let response: Response;
|
||||
try {
|
||||
|
@ -603,7 +599,6 @@ async function generatePath(pathname: string, gopts: GeneratePathOptions, pipeli
|
|||
// If there's no body, do nothing
|
||||
if (!response.body) return;
|
||||
body = Buffer.from(await response.arrayBuffer());
|
||||
encoding = (response.headers.get('X-Astro-Encoding') as BufferEncoding | null) ?? 'utf-8';
|
||||
}
|
||||
|
||||
const outFolder = getOutFolder(pipeline.getConfig(), pathname, route.type);
|
||||
|
@ -611,7 +606,7 @@ async function generatePath(pathname: string, gopts: GeneratePathOptions, pipeli
|
|||
route.distURL = outFile;
|
||||
|
||||
await fs.promises.mkdir(outFolder, { recursive: true });
|
||||
await fs.promises.writeFile(outFile, body, encoding);
|
||||
await fs.promises.writeFile(outFile, body);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ export interface SinglePageBuiltModule {
|
|||
/**
|
||||
* The `onRequest` hook exported by the middleware
|
||||
*/
|
||||
onRequest?: MiddlewareHandler<unknown>;
|
||||
onRequest?: MiddlewareHandler;
|
||||
renderers: SSRLoadedRenderer[];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,4 @@
|
|||
import mime from 'mime';
|
||||
import type {
|
||||
APIContext,
|
||||
EndpointHandler,
|
||||
EndpointOutput,
|
||||
MiddlewareEndpointHandler,
|
||||
MiddlewareHandler,
|
||||
Params,
|
||||
} from '../../@types/astro.js';
|
||||
import type { APIContext, EndpointHandler, MiddlewareHandler, Params } from '../../@types/astro.js';
|
||||
import { renderEndpoint } from '../../runtime/server/index.js';
|
||||
import { ASTRO_VERSION } from '../constants.js';
|
||||
import { AstroCookies, attachCookiesToResponse } from '../cookies/index.js';
|
||||
|
@ -19,8 +11,6 @@ import {
|
|||
} from '../render/context.js';
|
||||
import { type Environment, type RenderContext } from '../render/index.js';
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
const clientAddressSymbol = Symbol.for('astro.clientAddress');
|
||||
const clientLocalsSymbol = Symbol.for('astro.locals');
|
||||
|
||||
|
@ -69,7 +59,6 @@ export function createAPIContext({
|
|||
},
|
||||
});
|
||||
},
|
||||
ResponseWithEncoding,
|
||||
get preferredLocale(): string | undefined {
|
||||
if (preferredLocale) {
|
||||
return preferredLocale;
|
||||
|
@ -143,36 +132,11 @@ export function createAPIContext({
|
|||
return context;
|
||||
}
|
||||
|
||||
type ResponseParameters = ConstructorParameters<typeof Response>;
|
||||
|
||||
export class ResponseWithEncoding extends Response {
|
||||
constructor(body: ResponseParameters[0], init: ResponseParameters[1], encoding?: BufferEncoding) {
|
||||
// If a body string is given, try to encode it to preserve the behaviour as simple objects.
|
||||
// We don't do the full handling as simple objects so users can control how headers are set instead.
|
||||
if (typeof body === 'string') {
|
||||
// In NodeJS, we can use Buffer.from which supports all BufferEncoding
|
||||
if (typeof Buffer !== 'undefined' && Buffer.from) {
|
||||
body = Buffer.from(body, encoding);
|
||||
}
|
||||
// In non-NodeJS, use the web-standard TextEncoder for utf-8 strings
|
||||
else if (encoding == null || encoding === 'utf8' || encoding === 'utf-8') {
|
||||
body = encoder.encode(body);
|
||||
}
|
||||
}
|
||||
|
||||
super(body, init);
|
||||
|
||||
if (encoding) {
|
||||
this.headers.set('X-Astro-Encoding', encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>(
|
||||
export async function callEndpoint(
|
||||
mod: EndpointHandler,
|
||||
env: Environment,
|
||||
ctx: RenderContext,
|
||||
onRequest: MiddlewareHandler<MiddlewareResult> | undefined
|
||||
onRequest: MiddlewareHandler | undefined
|
||||
): Promise<Response> {
|
||||
const context = createAPIContext({
|
||||
request: ctx.request,
|
||||
|
@ -187,107 +151,13 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>
|
|||
|
||||
let response;
|
||||
if (onRequest) {
|
||||
response = await callMiddleware<Response | EndpointOutput>(
|
||||
env.logger,
|
||||
onRequest as MiddlewareEndpointHandler,
|
||||
context,
|
||||
async () => {
|
||||
return await renderEndpoint(mod, context, env.ssr, env.logger);
|
||||
}
|
||||
);
|
||||
response = await callMiddleware(onRequest, context, async () => {
|
||||
return await renderEndpoint(mod, context, env.ssr, env.logger);
|
||||
});
|
||||
} else {
|
||||
response = await renderEndpoint(mod, context, env.ssr, env.logger);
|
||||
}
|
||||
|
||||
const isEndpointSSR = env.ssr && !ctx.route?.prerender;
|
||||
|
||||
if (response instanceof Response) {
|
||||
if (isEndpointSSR && response.headers.get('X-Astro-Encoding')) {
|
||||
env.logger.warn(
|
||||
null,
|
||||
'`ResponseWithEncoding` is ignored in SSR. Please return an instance of Response. See https://docs.astro.build/en/core-concepts/endpoints/#server-endpoints-api-routes for more information.'
|
||||
);
|
||||
}
|
||||
attachCookiesToResponse(response, context.cookies);
|
||||
return response;
|
||||
}
|
||||
|
||||
// The endpoint returned a simple object, convert it to a Response
|
||||
|
||||
// TODO: Remove in Astro 4.0
|
||||
env.logger.warn(
|
||||
null,
|
||||
`${ctx.route.component} returns a simple object which is deprecated. Please return an instance of Response. See https://docs.astro.build/en/core-concepts/endpoints/#server-endpoints-api-routes for more information.`
|
||||
);
|
||||
|
||||
if (isEndpointSSR) {
|
||||
if (response.hasOwnProperty('headers')) {
|
||||
env.logger.warn(
|
||||
null,
|
||||
'Setting headers is not supported when returning an object. Please return an instance of Response. See https://docs.astro.build/en/core-concepts/endpoints/#server-endpoints-api-routes for more information.'
|
||||
);
|
||||
}
|
||||
|
||||
if (response.encoding) {
|
||||
env.logger.warn(
|
||||
null,
|
||||
'`encoding` is ignored in SSR. To return a charset other than UTF-8, please return an instance of Response. See https://docs.astro.build/en/core-concepts/endpoints/#server-endpoints-api-routes for more information.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let body: BodyInit;
|
||||
const headers = new Headers();
|
||||
|
||||
// Try to get the MIME type for this route
|
||||
const pathname = ctx.route
|
||||
? // Try the static route `pathname`
|
||||
ctx.route.pathname ??
|
||||
// Dynamic routes don't include `pathname`, so synthesize a path for these (e.g. 'src/pages/[slug].svg')
|
||||
ctx.route.segments.map((s) => s.map((p) => p.content).join('')).join('/')
|
||||
: // Fallback to pathname of the request
|
||||
ctx.pathname;
|
||||
const mimeType = mime.getType(pathname) || 'text/plain';
|
||||
headers.set('Content-Type', `${mimeType};charset=utf-8`);
|
||||
|
||||
// Save encoding to X-Astro-Encoding to be used later during SSG with `fs.writeFile`.
|
||||
// It won't work in SSR and is already warned above.
|
||||
if (response.encoding) {
|
||||
headers.set('X-Astro-Encoding', response.encoding);
|
||||
}
|
||||
|
||||
// For Uint8Array (binary), it can passed to Response directly
|
||||
if (response.body instanceof Uint8Array) {
|
||||
body = response.body;
|
||||
headers.set('Content-Length', body.byteLength.toString());
|
||||
}
|
||||
// In NodeJS, we can use Buffer.from which supports all BufferEncoding
|
||||
else if (typeof Buffer !== 'undefined' && Buffer.from) {
|
||||
body = Buffer.from(response.body, response.encoding);
|
||||
headers.set('Content-Length', body.byteLength.toString());
|
||||
}
|
||||
// In non-NodeJS, use the web-standard TextEncoder for utf-8 strings only
|
||||
// to calculate the content length
|
||||
else if (
|
||||
response.encoding == null ||
|
||||
response.encoding === 'utf8' ||
|
||||
response.encoding === 'utf-8'
|
||||
) {
|
||||
body = encoder.encode(response.body);
|
||||
headers.set('Content-Length', body.byteLength.toString());
|
||||
}
|
||||
// Fallback pass it to Response directly. It will mainly rely on X-Astro-Encoding
|
||||
// to be further processed in SSG.
|
||||
else {
|
||||
body = response.body;
|
||||
// NOTE: Can't calculate the content length as we can't encode to figure out the real length.
|
||||
// But also because we don't need the length for SSG as it's only being written to disk.
|
||||
}
|
||||
|
||||
response = new Response(body, {
|
||||
status: 200,
|
||||
headers,
|
||||
});
|
||||
attachCookiesToResponse(response, context.cookies);
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import { bold } from 'kleur/colors';
|
||||
import type {
|
||||
APIContext,
|
||||
EndpointOutput,
|
||||
MiddlewareHandler,
|
||||
MiddlewareNext,
|
||||
} from '../../@types/astro.js';
|
||||
import type { APIContext, MiddlewareHandler, MiddlewareNext } from '../../@types/astro.js';
|
||||
import { attachCookiesToResponse, responseHasCookies } from '../cookies/index.js';
|
||||
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||
import type { Environment } from '../render/index.js';
|
||||
|
||||
/**
|
||||
* Utility function that is in charge of calling the middleware.
|
||||
|
@ -43,15 +36,14 @@ import type { Environment } from '../render/index.js';
|
|||
* @param apiContext The API context
|
||||
* @param responseFunction A callback function that should return a promise with the response
|
||||
*/
|
||||
export async function callMiddleware<R>(
|
||||
logger: Environment['logger'],
|
||||
onRequest: MiddlewareHandler<R>,
|
||||
export async function callMiddleware(
|
||||
onRequest: MiddlewareHandler,
|
||||
apiContext: APIContext,
|
||||
responseFunction: () => Promise<R>
|
||||
): Promise<Response | R> {
|
||||
responseFunction: () => Promise<Response>
|
||||
): Promise<Response> {
|
||||
let nextCalled = false;
|
||||
let responseFunctionPromise: Promise<R> | undefined = undefined;
|
||||
const next: MiddlewareNext<R> = async () => {
|
||||
let responseFunctionPromise: Promise<Response> | undefined = undefined;
|
||||
const next: MiddlewareNext = async () => {
|
||||
nextCalled = true;
|
||||
responseFunctionPromise = responseFunction();
|
||||
return responseFunctionPromise;
|
||||
|
@ -60,15 +52,6 @@ export async function callMiddleware<R>(
|
|||
let middlewarePromise = onRequest(apiContext, next);
|
||||
|
||||
return await Promise.resolve(middlewarePromise).then(async (value) => {
|
||||
if (isEndpointOutput(value)) {
|
||||
logger.warn(
|
||||
null,
|
||||
apiContext.url.pathname +
|
||||
' Using simple endpoints can cause unexpected issues in the chain of middleware functions.' +
|
||||
`\nIt's strongly suggested to use full ${bold('Response')} objects.`
|
||||
);
|
||||
}
|
||||
|
||||
// first we check if `next` was called
|
||||
if (nextCalled) {
|
||||
/**
|
||||
|
@ -84,7 +67,7 @@ export async function callMiddleware<R>(
|
|||
if (value instanceof Response === false) {
|
||||
throw new AstroError(AstroErrorData.MiddlewareNotAResponse);
|
||||
}
|
||||
return ensureCookiesAttached(apiContext, value as Response);
|
||||
return ensureCookiesAttached(apiContext, value);
|
||||
} else {
|
||||
/**
|
||||
* Here we handle the case where `next` was called and returned nothing.
|
||||
|
@ -107,7 +90,7 @@ export async function callMiddleware<R>(
|
|||
throw new AstroError(AstroErrorData.MiddlewareNotAResponse);
|
||||
} else {
|
||||
// Middleware did not call resolve and returned a value
|
||||
return ensureCookiesAttached(apiContext, value as Response);
|
||||
return ensureCookiesAttached(apiContext, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -118,11 +101,3 @@ function ensureCookiesAttached(apiContext: APIContext, response: Response): Resp
|
|||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
function isEndpointOutput(endpointResult: any): endpointResult is EndpointOutput {
|
||||
return (
|
||||
!(endpointResult instanceof Response) &&
|
||||
typeof endpointResult === 'object' &&
|
||||
typeof endpointResult.body === 'string'
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import type { MiddlewareEndpointHandler, Params } from '../../@types/astro.js';
|
||||
import type { MiddlewareHandler, Params } from '../../@types/astro.js';
|
||||
import { createAPIContext } from '../endpoint/index.js';
|
||||
import { sequence } from './sequence.js';
|
||||
|
||||
function defineMiddleware(fn: MiddlewareEndpointHandler) {
|
||||
function defineMiddleware(fn: MiddlewareHandler) {
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { APIContext, MiddlewareEndpointHandler } from '../../@types/astro.js';
|
||||
import type { APIContext, MiddlewareHandler } from '../../@types/astro.js';
|
||||
import { defineMiddleware } from './index.js';
|
||||
|
||||
// From SvelteKit: https://github.com/sveltejs/kit/blob/master/packages/kit/src/exports/hooks/sequence.js
|
||||
|
@ -6,11 +6,11 @@ import { defineMiddleware } from './index.js';
|
|||
*
|
||||
* It accepts one or more middleware handlers and makes sure that they are run in sequence.
|
||||
*/
|
||||
export function sequence(...handlers: MiddlewareEndpointHandler[]): MiddlewareEndpointHandler {
|
||||
export function sequence(...handlers: MiddlewareHandler[]): MiddlewareHandler {
|
||||
const filtered = handlers.filter((h) => !!h);
|
||||
const length = filtered.length;
|
||||
if (!length) {
|
||||
const handler: MiddlewareEndpointHandler = defineMiddleware((context, next) => {
|
||||
const handler: MiddlewareHandler = defineMiddleware((context, next) => {
|
||||
return next();
|
||||
});
|
||||
return handler;
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
import type {
|
||||
ComponentInstance,
|
||||
EndpointHandler,
|
||||
MiddlewareEndpointHandler,
|
||||
MiddlewareHandler,
|
||||
MiddlewareResponseHandler,
|
||||
} from '../@types/astro.js';
|
||||
import type { ComponentInstance, EndpointHandler, MiddlewareHandler } from '../@types/astro.js';
|
||||
import { callEndpoint, createAPIContext } from './endpoint/index.js';
|
||||
import { callMiddleware } from './middleware/callMiddleware.js';
|
||||
import { renderPage } from './render/core.js';
|
||||
|
@ -28,7 +22,7 @@ export type PipelineHookFunction = (ctx: RenderContext, mod: ComponentInstance |
|
|||
*/
|
||||
export class Pipeline {
|
||||
env: Environment;
|
||||
#onRequest?: MiddlewareEndpointHandler;
|
||||
#onRequest?: MiddlewareHandler;
|
||||
#hooks: PipelineHooks = {
|
||||
before: [],
|
||||
};
|
||||
|
@ -60,7 +54,7 @@ export class Pipeline {
|
|||
/**
|
||||
* A middleware function that will be called before each request.
|
||||
*/
|
||||
setMiddlewareFunction(onRequest: MiddlewareEndpointHandler) {
|
||||
setMiddlewareFunction(onRequest: MiddlewareHandler) {
|
||||
this.#onRequest = onRequest;
|
||||
}
|
||||
|
||||
|
@ -115,11 +109,11 @@ export class Pipeline {
|
|||
*
|
||||
* It throws an error if the page can't be rendered.
|
||||
*/
|
||||
async #tryRenderRoute<MiddlewareReturnType = Response>(
|
||||
async #tryRenderRoute(
|
||||
renderContext: Readonly<RenderContext>,
|
||||
env: Readonly<Environment>,
|
||||
mod: Readonly<ComponentInstance> | undefined,
|
||||
onRequest?: MiddlewareHandler<MiddlewareReturnType>
|
||||
onRequest?: MiddlewareHandler
|
||||
): Promise<Response> {
|
||||
const apiContext = createAPIContext({
|
||||
request: renderContext.request,
|
||||
|
@ -137,19 +131,14 @@ export class Pipeline {
|
|||
case 'fallback':
|
||||
case 'redirect': {
|
||||
if (onRequest) {
|
||||
return await callMiddleware<Response>(
|
||||
env.logger,
|
||||
onRequest as MiddlewareResponseHandler,
|
||||
apiContext,
|
||||
() => {
|
||||
return renderPage({
|
||||
mod,
|
||||
renderContext,
|
||||
env,
|
||||
cookies: apiContext.cookies,
|
||||
});
|
||||
}
|
||||
);
|
||||
return await callMiddleware(onRequest, apiContext, () => {
|
||||
return renderPage({
|
||||
mod,
|
||||
renderContext,
|
||||
env,
|
||||
cookies: apiContext.cookies,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
return await renderPage({
|
||||
mod,
|
||||
|
|
|
@ -24,5 +24,5 @@ export interface SSROptions {
|
|||
/**
|
||||
* Optional middlewares
|
||||
*/
|
||||
middleware?: AstroMiddlewareInstance<unknown>;
|
||||
middleware?: AstroMiddlewareInstance;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { appendForwardSlash, joinPaths } from '@astrojs/internal-helpers/path';
|
||||
import type { MiddlewareEndpointHandler, RouteData, SSRManifest } from '../@types/astro.js';
|
||||
import type { MiddlewareHandler, RouteData, SSRManifest } from '../@types/astro.js';
|
||||
import type { PipelineHookFunction } from '../core/pipeline.js';
|
||||
|
||||
const routeDataSymbol = Symbol.for('astro.routeData');
|
||||
|
@ -19,7 +19,7 @@ export function createI18nMiddleware(
|
|||
i18n: SSRManifest['i18n'],
|
||||
base: SSRManifest['base'],
|
||||
trailingSlash: SSRManifest['trailingSlash']
|
||||
): MiddlewareEndpointHandler | undefined {
|
||||
): MiddlewareHandler | undefined {
|
||||
if (!i18n) {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url';
|
|||
import type {
|
||||
ComponentInstance,
|
||||
ManifestData,
|
||||
MiddlewareEndpointHandler,
|
||||
MiddlewareHandler,
|
||||
RouteData,
|
||||
SSRElement,
|
||||
SSRManifest,
|
||||
|
@ -288,7 +288,7 @@ export async function handleRoute({
|
|||
});
|
||||
}
|
||||
|
||||
const onRequest = middleware?.onRequest as MiddlewareEndpointHandler | undefined;
|
||||
const onRequest = middleware?.onRequest as MiddlewareHandler | undefined;
|
||||
if (config.experimental.i18n) {
|
||||
const i18Middleware = createI18nMiddleware(
|
||||
config.experimental.i18n,
|
||||
|
|
|
@ -15,10 +15,10 @@ export function getStaticPaths() {
|
|||
}
|
||||
|
||||
export function GET({ params, request }) {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
param: params.param,
|
||||
pathname: new URL(request.url).pathname
|
||||
})
|
||||
};
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
export async function getStaticPaths() {
|
||||
return [
|
||||
return [
|
||||
{ params: { slug: 'thing1' } },
|
||||
{ params: { slug: 'thing2' } }
|
||||
];
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
title: '[slug]'
|
||||
}, null, 4)
|
||||
};
|
||||
return Response.json({
|
||||
title: '[slug]',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
export async function GET() {
|
||||
const docs = await import.meta.glob('./*.md', { eager: true });
|
||||
return {
|
||||
body: JSON.stringify(Object.values(docs).map(doc => doc.frontmatter)),
|
||||
}
|
||||
return Response.json(Object.values(docs).map((doc) => doc.frontmatter));
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { getHeadings } from './with-layout.md';
|
||||
|
||||
export async function GET() {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
headings: getHeadings(),
|
||||
}),
|
||||
}
|
||||
return Response.json({
|
||||
headings: getHeadings(),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { rawContent, compiledContent } from './basic.md';
|
||||
|
||||
export async function GET() {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
raw: rawContent(),
|
||||
compiled: await compiledContent(),
|
||||
}),
|
||||
}
|
||||
return Response.json({
|
||||
raw: rawContent(),
|
||||
compiled: await compiledContent(),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { frontmatter } from './vite-env-vars.md';
|
||||
|
||||
export async function GET() {
|
||||
return {
|
||||
body: JSON.stringify(frontmatter),
|
||||
}
|
||||
return Response.json(frontmatter);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ export async function GET() {
|
|||
const withSlugConfig = stripAllRenderFn(await getCollection('with-custom-slugs'));
|
||||
const withUnionSchema = stripAllRenderFn(await getCollection('with-union-schema'));
|
||||
|
||||
return {
|
||||
body: devalue.stringify({withoutConfig, withSchemaConfig, withSlugConfig, withUnionSchema}),
|
||||
}
|
||||
return new Response(
|
||||
devalue.stringify({ withoutConfig, withSchemaConfig, withSlugConfig, withUnionSchema })
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,10 +5,17 @@ import { stripRenderFn } from '../utils.js';
|
|||
export async function GET() {
|
||||
const columbiaWithoutConfig = stripRenderFn(await getEntryBySlug('without-config', 'columbia'));
|
||||
const oneWithSchemaConfig = stripRenderFn(await getEntryBySlug('with-schema-config', 'one'));
|
||||
const twoWithSlugConfig = stripRenderFn(await getEntryBySlug('with-custom-slugs', 'interesting-two'));
|
||||
const twoWithSlugConfig = stripRenderFn(
|
||||
await getEntryBySlug('with-custom-slugs', 'interesting-two')
|
||||
);
|
||||
const postWithUnionSchema = stripRenderFn(await getEntryBySlug('with-union-schema', 'post'));
|
||||
|
||||
return {
|
||||
body: devalue.stringify({columbiaWithoutConfig, oneWithSchemaConfig, twoWithSlugConfig, postWithUnionSchema}),
|
||||
}
|
||||
return new Response(
|
||||
devalue.stringify({
|
||||
columbiaWithoutConfig,
|
||||
oneWithSchemaConfig,
|
||||
twoWithSlugConfig,
|
||||
postWithUnionSchema,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,5 @@ import type { APIRoute } from "../../../../../src/@types/astro";
|
|||
export const GET = (async ({ params, request }) => {
|
||||
const url = new URL(request.url);
|
||||
const src = url.searchParams.get("src");
|
||||
|
||||
return {
|
||||
body: "An image: " + JSON.stringify(src),
|
||||
};
|
||||
return new Response("An image: " + JSON.stringify(src));
|
||||
}) satisfies APIRoute;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { getEntry } from 'astro:content';
|
||||
|
||||
const ids = ['Ben Holmes', 'Fred K Schott', 'Nate Moore']
|
||||
const ids = ['Ben Holmes', 'Fred K Schott', 'Nate Moore'];
|
||||
|
||||
export function getStaticPaths() {
|
||||
return ids.map(id => ({ params: { id } }))
|
||||
return ids.map((id) => ({ params: { id } }));
|
||||
}
|
||||
|
||||
/** @param {import('astro').APIContext} params */
|
||||
|
@ -11,12 +11,8 @@ export async function GET({ params }) {
|
|||
const { id } = params;
|
||||
const author = await getEntry('authors-without-config', id);
|
||||
if (!author) {
|
||||
return {
|
||||
body: JSON.stringify({ error: `Author ${id} Not found` }),
|
||||
}
|
||||
return Response.json({ error: `Author ${id} Not found` });
|
||||
} else {
|
||||
return {
|
||||
body: JSON.stringify(author),
|
||||
}
|
||||
return Response.json(author);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,5 @@ import { getCollection } from 'astro:content';
|
|||
|
||||
export async function GET() {
|
||||
const authors = await getCollection('authors-without-config');
|
||||
|
||||
return {
|
||||
body: JSON.stringify(authors),
|
||||
}
|
||||
return Response.json(authors);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { getEntry } from 'astro:content';
|
||||
|
||||
const langs = ['en', 'es', 'fr']
|
||||
const langs = ['en', 'es', 'fr'];
|
||||
|
||||
export function getStaticPaths() {
|
||||
return langs.map(lang => ({ params: { lang } }))
|
||||
return langs.map((lang) => ({ params: { lang } }));
|
||||
}
|
||||
|
||||
/** @param {import('astro').APIContext} params */
|
||||
|
@ -11,12 +11,8 @@ export async function GET({ params }) {
|
|||
const { lang } = params;
|
||||
const translations = await getEntry('i18n', lang);
|
||||
if (!translations) {
|
||||
return {
|
||||
body: JSON.stringify({ error: `Translation ${lang} Not found` }),
|
||||
}
|
||||
return Response.json({ error: `Translation ${lang} Not found` });
|
||||
} else {
|
||||
return {
|
||||
body: JSON.stringify(translations),
|
||||
}
|
||||
return Response.json(translations);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,5 @@ import { getCollection } from 'astro:content';
|
|||
|
||||
export async function GET() {
|
||||
const translations = await getCollection('i18n');
|
||||
|
||||
return {
|
||||
body: JSON.stringify(translations),
|
||||
}
|
||||
return Response.json(translations);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,5 @@ import { getDataEntryById } from 'astro:content';
|
|||
|
||||
export async function GET() {
|
||||
const item = await getDataEntryById('i18n', 'en');
|
||||
|
||||
return {
|
||||
body: JSON.stringify(item),
|
||||
}
|
||||
return Response.json(item);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import type { APIRoute } from 'astro';
|
||||
|
||||
const slugs = ["one", undefined];
|
||||
const slugs = ['one', undefined];
|
||||
|
||||
export const GET: APIRoute = ({ params }) => {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
slug: params.slug || "index",
|
||||
}),
|
||||
};
|
||||
return Response.json({
|
||||
slug: params.slug || 'index',
|
||||
});
|
||||
};
|
||||
|
||||
export function getStaticPaths() {
|
||||
return slugs.map((u) => ({ params: { slug: u } }));
|
||||
return slugs.map((u) => ({ params: { slug: u } }));
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
// NOTE: test deprecated object form
|
||||
// Returns the file body for this non-HTML file.
|
||||
// The content type is based off of the extension in the filename,
|
||||
// in this case: about.json.
|
||||
export async function GET() {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
name: 'Astro',
|
||||
url: 'https://astro.build/',
|
||||
}),
|
||||
};
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import { promises as fs } from 'node:fs';
|
||||
|
||||
import type { APIRoute } from 'astro';
|
||||
|
||||
// NOTE: test deprecated object form
|
||||
export const GET: APIRoute = async function get() {
|
||||
try {
|
||||
// Image is in the public domain. Sourced from
|
||||
// https://en.wikipedia.org/wiki/File:Portrait_placeholder.png
|
||||
const buffer = await fs.readFile('./test/fixtures/non-html-pages/src/images/placeholder.png');
|
||||
return {
|
||||
body: buffer.toString('binary'),
|
||||
encoding: 'binary',
|
||||
} as const;
|
||||
} catch (error: unknown) {
|
||||
throw new Error(`Something went wrong in placeholder.png route!: ${error as string}`);
|
||||
}
|
||||
};
|
|
@ -2,12 +2,13 @@ import { promises as fs } from 'node:fs';
|
|||
|
||||
import type { APIRoute } from 'astro';
|
||||
|
||||
export const GET: APIRoute = async function get({ ResponseWithEncoding }) {
|
||||
export const GET: APIRoute = async function get() {
|
||||
try {
|
||||
// Image is in the public domain. Sourced from
|
||||
// https://en.wikipedia.org/wiki/File:Portrait_placeholder.png
|
||||
const buffer = await fs.readFile('./test/fixtures/non-html-pages/src/images/placeholder.png');
|
||||
return new ResponseWithEncoding(buffer.toString('binary'), undefined, 'binary')
|
||||
// NOTE: SSG only so not Content-Type needed
|
||||
return new Response(buffer.buffer)
|
||||
} catch (error: unknown) {
|
||||
throw new Error(`Something went wrong in placeholder.png route!: ${error as string}`);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import type { APIRoute } from 'astro';
|
||||
|
||||
export const GET: APIRoute = async ({ params }) => {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
path: params.slug,
|
||||
}),
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export function getStaticPaths() {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import type { APIRoute } from 'astro';
|
||||
|
||||
export const GET: APIRoute = async ({ params }) => {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
foo: params.foo,
|
||||
bar: params.bar,
|
||||
}),
|
||||
};
|
||||
bar: params.bar,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export function getStaticPaths() {
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
/**
|
||||
* @param {import('astro').APIContext} api
|
||||
* @param {import('astro').APIContext} api
|
||||
*/
|
||||
export function GET(ctx) {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
cookiesExist: !!ctx.cookies,
|
||||
requestExist: !!ctx.request,
|
||||
redirectExist: !!ctx.redirect,
|
||||
propsExist: !!ctx.props,
|
||||
params: ctx.params,
|
||||
site: ctx.site?.toString(),
|
||||
generator: ctx.generator,
|
||||
url: ctx.url.toString(),
|
||||
clientAddress: ctx.clientAddress,
|
||||
})
|
||||
};
|
||||
return Response.json({
|
||||
cookiesExist: !!ctx.cookies,
|
||||
requestExist: !!ctx.request,
|
||||
redirectExist: !!ctx.redirect,
|
||||
propsExist: !!ctx.props,
|
||||
params: ctx.params,
|
||||
site: ctx.site?.toString(),
|
||||
generator: ctx.generator,
|
||||
url: ctx.url.toString(),
|
||||
clientAddress: ctx.clientAddress,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
// NOTE: test deprecated object form
|
||||
export function GET() {
|
||||
return {
|
||||
body: JSON.stringify([
|
||||
{ name: 'lettuce' },
|
||||
{ name: 'broccoli' },
|
||||
{ name: 'pizza' }
|
||||
])
|
||||
};
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
|
||||
export function GET({ params }) {
|
||||
return {
|
||||
body: JSON.stringify(params)
|
||||
};
|
||||
return Response.json(params);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
export const prerender = true;
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return [
|
||||
return [
|
||||
{ params: { slug: 'thing1' } },
|
||||
{ params: { slug: 'thing2' } }
|
||||
];
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
title: '[slug]'
|
||||
}, null, 4)
|
||||
};
|
||||
return Response.json({
|
||||
title: '[slug]',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
export async function GET() {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
name: 'Astro Technology Company',
|
||||
url: 'https://astro.build/'
|
||||
})
|
||||
}
|
||||
export function GET() {
|
||||
return Response.json({
|
||||
name: 'Astro Technology Company',
|
||||
url: 'https://astro.build/',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,11 +6,9 @@ export async function getStaticPaths() {
|
|||
}
|
||||
|
||||
export async function GET({ params }) {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
slug: params.slug,
|
||||
name: 'Astro Technology Company',
|
||||
url: 'https://astro.build/'
|
||||
})
|
||||
}
|
||||
return Response.json({
|
||||
slug: params.slug,
|
||||
name: 'Astro Technology Company',
|
||||
url: 'https://astro.build/'
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,8 +15,5 @@ async function fetchPosts() {
|
|||
|
||||
export async function GET() {
|
||||
const posts = await fetchPosts();
|
||||
|
||||
return {
|
||||
body: JSON.stringify(posts, null, 4),
|
||||
};
|
||||
return Response.json(posts);
|
||||
}
|
||||
|
|
|
@ -6,10 +6,8 @@ export async function getStaticPaths() {
|
|||
}
|
||||
|
||||
export async function GET({ params }) {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
slug: params.slug,
|
||||
title: '[slug]'
|
||||
})
|
||||
};
|
||||
return Response.json({
|
||||
slug: params.slug,
|
||||
title: '[slug]'
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,10 +6,8 @@ export async function getStaticPaths() {
|
|||
}
|
||||
|
||||
export async function GET({ params }) {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
slug: params.slug,
|
||||
title: 'data [slug]'
|
||||
})
|
||||
};
|
||||
return Response.json({
|
||||
slug: params.slug,
|
||||
title: 'data [slug]'
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
export async function GET() {
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
title: 'home'
|
||||
})
|
||||
};
|
||||
return Response.json({ title: 'home' });
|
||||
}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
export async function getStaticPaths() {
|
||||
return [
|
||||
{ params: { image: 1 } },
|
||||
{ params: { image: 2 } },
|
||||
];
|
||||
return [{ params: { image: 1 } }, { params: { image: 2 } }];
|
||||
}
|
||||
|
||||
export async function GET({ params }) {
|
||||
return {
|
||||
body: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200">
|
||||
return new Response(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200">
|
||||
<title>${params.image}</title>
|
||||
</svg>`
|
||||
};
|
||||
</svg>`,
|
||||
{
|
||||
headers: {
|
||||
'content-type': 'image/svg+xml',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { readFileSync } from "node:fs";
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
export async function GET({ params, request }) {
|
||||
const buffer = readFileSync(new URL('../../astro.png', import.meta.url));
|
||||
return {
|
||||
body: buffer.toString('hex'),
|
||||
encoding: 'hex',
|
||||
};
|
||||
export async function GET() {
|
||||
const buffer = await readFile(new URL('../../astro.png', import.meta.url));
|
||||
return new Response(buffer.buffer);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
export async function GET() {
|
||||
return {
|
||||
body: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200">
|
||||
export function GET() {
|
||||
return new Response(
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 200">
|
||||
<title>Static SVG</title>
|
||||
</svg>`
|
||||
};
|
||||
</svg>`,
|
||||
{
|
||||
headers: {
|
||||
'content-type': 'image/svg+xml',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,12 +15,6 @@ describe('Non-HTML Pages', () => {
|
|||
expect(json).to.have.property('name', 'Astro');
|
||||
expect(json).to.have.property('url', 'https://astro.build/');
|
||||
});
|
||||
|
||||
it('should match contents (deprecated object form)', async () => {
|
||||
const json = JSON.parse(await fixture.readFile('/about-object.json'));
|
||||
expect(json).to.have.property('name', 'Astro');
|
||||
expect(json).to.have.property('url', 'https://astro.build/');
|
||||
});
|
||||
});
|
||||
|
||||
describe('png', () => {
|
||||
|
@ -40,22 +34,5 @@ describe('Non-HTML Pages', () => {
|
|||
'iVBORw0KGgoAAAANSUhEUgAAAGQAAACWCAMAAAAfZt10AAAABlBMVEXd3d3+/v7B/CFgAAAA3UlEQVR42u3ZMQ7DIBQFQeb+l06bNgUbG/5eYApLFjzWNE3TNE3TNE035av9AhAQEBBQGAQEFAaFQWFQGBQGhUGCKAwKgwQpDJ6JECgCRYIEikH8YAyCRyEGyRCDvBWRIPNNBpm/8G6kUM45EhXKlQfuFSHFpbFH+jt2j/S7xwqUYvBaCRIozZy6X2km7v1K8uwQIIWBwkBAQEBg3Tyj3z4LnzRBKgwKg8KgMEgQhaEwSBCFQWBEiMIgQQqDBCkMEqQw+APixYgcsa0TERs7D/F6xGmIAxCD/Iw4AvEB92Ec3ZAPdlMAAAAASUVORK5CYII='
|
||||
);
|
||||
});
|
||||
|
||||
it('should not have had its encoding mangled (deprecated object form)', async () => {
|
||||
const buffer = await fixture.readFile('/placeholder-object.png', 'base64');
|
||||
|
||||
// Sanity check the first byte
|
||||
const hex = Buffer.from(buffer, 'base64').toString('hex');
|
||||
const firstHexByte = hex.slice(0, 2);
|
||||
// If we accidentally utf8 encode the png, the first byte (in hex) will be 'c2'
|
||||
expect(firstHexByte).to.not.equal('c2');
|
||||
// and if correctly encoded in binary, it should be '89'
|
||||
expect(firstHexByte).to.equal('89');
|
||||
|
||||
// Make sure the whole buffer (in base64) matches this snapshot
|
||||
expect(buffer).to.equal(
|
||||
'iVBORw0KGgoAAAANSUhEUgAAAGQAAACWCAMAAAAfZt10AAAABlBMVEXd3d3+/v7B/CFgAAAA3UlEQVR42u3ZMQ7DIBQFQeb+l06bNgUbG/5eYApLFjzWNE3TNE3TNE035av9AhAQEBBQGAQEFAaFQWFQGBQGhUGCKAwKgwQpDJ6JECgCRYIEikH8YAyCRyEGyRCDvBWRIPNNBpm/8G6kUM45EhXKlQfuFSHFpbFH+jt2j/S7xwqUYvBaCRIozZy6X2km7v1K8uwQIIWBwkBAQEBg3Tyj3z4LnzRBKgwKg8KgMEgQhaEwSBCFQWBEiMIgQQqDBCkMEqQw+APixYgcsa0TERs7D/F6xGmIAxCD/Iw4AvEB92Ec3ZAPdlMAAAAASUVORK5CYII='
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -33,17 +33,6 @@ describe('API routes in SSR', () => {
|
|||
expect(body.length).to.equal(3);
|
||||
});
|
||||
|
||||
it('Can load the API route too (deprecated object form)', async () => {
|
||||
const app = await fixture.loadTestAdapterApp();
|
||||
const request = new Request('http://example.com/food-object.json');
|
||||
const response = await app.render(request);
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.headers.get('Content-Type')).to.equal('application/json;charset=utf-8');
|
||||
expect(response.headers.get('Content-Length')).to.not.be.empty;
|
||||
const body = await response.json();
|
||||
expect(body.length).to.equal(3);
|
||||
});
|
||||
|
||||
it('Has valid api context', async () => {
|
||||
const app = await fixture.loadTestAdapterApp();
|
||||
const request = new Request('http://example.com/context/any');
|
||||
|
@ -96,13 +85,6 @@ describe('API routes in SSR', () => {
|
|||
expect(res.status).to.equal(200);
|
||||
});
|
||||
|
||||
it('Infer content type with charset for { body } shorthand (deprecated object form)', async () => {
|
||||
const response = await fixture.fetch('/food-object.json', {
|
||||
method: 'GET',
|
||||
});
|
||||
expect(response.headers.get('Content-Type')).to.equal('application/json;charset=utf-8');
|
||||
});
|
||||
|
||||
it('Can set multiple headers of the same type', async () => {
|
||||
const response = await new Promise((resolve) => {
|
||||
let { port } = devServer.address;
|
||||
|
|
|
@ -4,7 +4,5 @@ import { stripAllRenderFn } from '../../utils.js';
|
|||
|
||||
export async function GET() {
|
||||
const posts = await getCollection('blog');
|
||||
return {
|
||||
body: stringify(stripAllRenderFn(posts))
|
||||
};
|
||||
return new Response(stringify(stripAllRenderFn(posts)));
|
||||
}
|
||||
|
|
|
@ -4,7 +4,5 @@ import { stripRenderFn } from '../../utils.js';
|
|||
|
||||
export async function GET() {
|
||||
const post = await getEntryBySlug('blog', 'post-1');
|
||||
return {
|
||||
body: stringify(stripRenderFn(post)),
|
||||
};
|
||||
return new Response(stringify(stripRenderFn(post)));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export async function GET() {
|
||||
const docs = await import.meta.glob('./*.mdx', { eager: true });
|
||||
return {
|
||||
body: JSON.stringify(Object.values(docs).map(doc => doc.frontmatter)),
|
||||
}
|
||||
return new Response(
|
||||
JSON.stringify(Object.values(docs).map(doc => doc.frontmatter))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
export async function GET() {
|
||||
const mdxPages = await import.meta.glob('./*.mdx', { eager: true });
|
||||
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
titles: Object.values(mdxPages ?? {}).map(v => v?.frontmatter?.title),
|
||||
})
|
||||
}
|
||||
return Response.json({
|
||||
titles: Object.values(mdxPages ?? {}).map((v) => v?.frontmatter?.title),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
export async function GET() {
|
||||
const mdxPages = await import.meta.glob('./*.mdx', { eager: true });
|
||||
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
headingsByPage: Object.fromEntries(
|
||||
Object.entries(mdxPages ?? {}).map(([k, v]) => [k, v?.getHeadings()])
|
||||
),
|
||||
}),
|
||||
}
|
||||
return Response.json({
|
||||
headingsByPage: Object.fromEntries(
|
||||
Object.entries(mdxPages ?? {}).map(([k, v]) => [k, v?.getHeadings()])
|
||||
),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
export async function GET() {
|
||||
const mdxPages = await import.meta.glob('./*.mdx', { eager: true });
|
||||
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
urls: Object.values(mdxPages ?? {}).map(v => v?.url),
|
||||
})
|
||||
}
|
||||
return Response.json({
|
||||
urls: Object.values(mdxPages ?? {}).map((v) => v?.url),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { frontmatter } from './vite-env-vars.mdx';
|
||||
|
||||
export function GET() {
|
||||
return {
|
||||
body: JSON.stringify(frontmatter),
|
||||
}
|
||||
return Response.json(frontmatter);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
export async function GET() {
|
||||
let number = Math.random();
|
||||
return {
|
||||
body: JSON.stringify({
|
||||
number,
|
||||
message: `Here's a random number: ${number}`,
|
||||
}),
|
||||
};
|
||||
return Response.json({
|
||||
number,
|
||||
message: `Here's a random number: ${number}`,
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue