Add Caching
This commit is contained in:
parent
35930857fd
commit
32e914fdc1
32
README.md
32
README.md
@ -26,7 +26,7 @@ A Next.js-style full-stack meta-framework built on [Bun](https://bun.sh) and Rea
|
|||||||
- [Error Pages](#error-pages)
|
- [Error Pages](#error-pages)
|
||||||
- [Static Files](#static-files)
|
- [Static Files](#static-files)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
- [Middleware](#middleware)
|
- [Middleware](#middleware)
|
||||||
- [Environment Variables](#environment-variables)
|
- [Environment Variables](#environment-variables)
|
||||||
- [How It Works](#how-it-works)
|
- [How It Works](#how-it-works)
|
||||||
- [Development Server](#development-server)
|
- [Development Server](#development-server)
|
||||||
@ -402,7 +402,10 @@ The `ctx` parameter has the same shape as the page `server` function context —
|
|||||||
Export a `config` object to override the per-route request body limit (default: 10 MB):
|
Export a `config` object to override the per-route request body limit (default: 10 MB):
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import type { BunextServerRouteConfig, BunxRouteParams } from "bunext/src/types";
|
import type {
|
||||||
|
BunextServerRouteConfig,
|
||||||
|
BunxRouteParams,
|
||||||
|
} from "bunext/src/types";
|
||||||
|
|
||||||
export const config: BunextServerRouteConfig = {
|
export const config: BunextServerRouteConfig = {
|
||||||
maxRequestBodyMB: 50, // allow up to 50 MB
|
maxRequestBodyMB: 50, // allow up to 50 MB
|
||||||
@ -482,15 +485,15 @@ const config: BunextConfig = {
|
|||||||
export default config;
|
export default config;
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
| Option | Type | Default | Description |
|
||||||
| -------------- | -------------------------------------------------------------------- | ---------------- | -------------------------------------------------- |
|
| -------------- | --------------------------------------------------------------------------------- | ---------------- | -------------------------------------------------- |
|
||||||
| `port` | `number` | `7000` | HTTP server port |
|
| `port` | `number` | `7000` | HTTP server port |
|
||||||
| `origin` | `string` | — | Canonical origin URL |
|
| `origin` | `string` | — | Canonical origin URL |
|
||||||
| `distDir` | `string` | `.bunext` | Internal artifact directory |
|
| `distDir` | `string` | `.bunext` | Internal artifact directory |
|
||||||
| `assetsPrefix` | `string` | `_bunext/static` | URL prefix for static assets |
|
| `assetsPrefix` | `string` | `_bunext/static` | URL prefix for static assets |
|
||||||
| `globalVars` | `{ [k: string]: any }` | — | Variables injected globally at build time |
|
| `globalVars` | `{ [k: string]: any }` | — | Variables injected globally at build time |
|
||||||
| `development` | `boolean` | — | Overridden to `true` by `bunext dev` automatically |
|
| `development` | `boolean` | — | Overridden to `true` by `bunext dev` automatically |
|
||||||
| `middleware` | `(params: BunextConfigMiddlewareParams) => Response \| undefined \| Promise<...>` | — | Global middleware — see [Middleware](#middleware) |
|
| `middleware` | `(params: BunextConfigMiddlewareParams) => Response \| undefined \| Promise<...>` | — | Global middleware — see [Middleware](#middleware) |
|
||||||
|
|
||||||
### Middleware
|
### Middleware
|
||||||
|
|
||||||
@ -501,7 +504,10 @@ Middleware runs on every request before any routing. Define it in `bunext.config
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
// bunext.config.ts
|
// bunext.config.ts
|
||||||
import type { BunextConfig, BunextConfigMiddlewareParams } from "bunext/src/types";
|
import type {
|
||||||
|
BunextConfig,
|
||||||
|
BunextConfigMiddlewareParams,
|
||||||
|
} from "bunext/src/types";
|
||||||
|
|
||||||
const config: BunextConfig = {
|
const config: BunextConfig = {
|
||||||
middleware: async ({ req, url, server }) => {
|
middleware: async ({ req, url, server }) => {
|
||||||
@ -631,6 +637,6 @@ Request
|
|||||||
Server-rendered HTML includes:
|
Server-rendered HTML includes:
|
||||||
|
|
||||||
- `window.__PAGE_PROPS__` — the serialized server function return value, read by `hydrateRoot` on the client.
|
- `window.__PAGE_PROPS__` — the serialized server function return value, read by `hydrateRoot` on the client.
|
||||||
- A `<script type="module" defer>` tag pointing to the page's bundled client script.
|
- A `<script type="module" async>` tag pointing to the page's bundled client script.
|
||||||
- A `<link rel="stylesheet">` tag if the bundler emitted a CSS file for the page.
|
- A `<link rel="stylesheet">` tag if the bundler emitted a CSS file for the page.
|
||||||
- In development: the HMR client script.
|
- In development: the HMR client script.
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
"name": "bunext",
|
"name": "bunext",
|
||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"version": "1.0.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"bunext": "dist/index.js"
|
"bunext": "dist/index.js"
|
||||||
},
|
},
|
||||||
|
|||||||
5
src/data/app-data.ts
Normal file
5
src/data/app-data.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const AppData = {
|
||||||
|
DefaultCacheExpiryTimeSeconds: 60 * 60,
|
||||||
|
DefaultCronInterval: 30000,
|
||||||
|
BunextStaticFilesCacheExpiry: 60 * 60 * 24 * 7,
|
||||||
|
} as const;
|
||||||
@ -165,7 +165,7 @@ export default async function allPagesBundler(params?: Params) {
|
|||||||
entryPoints: Object.keys(virtualEntries).map((k) => `virtual:${k}`),
|
entryPoints: Object.keys(virtualEntries).map((k) => `virtual:${k}`),
|
||||||
outdir: HYDRATION_DST_DIR,
|
outdir: HYDRATION_DST_DIR,
|
||||||
bundle: true,
|
bundle: true,
|
||||||
minify: !dev,
|
minify: true,
|
||||||
format: "esm",
|
format: "esm",
|
||||||
target: "es2020",
|
target: "es2020",
|
||||||
platform: "browser",
|
platform: "browser",
|
||||||
@ -178,6 +178,7 @@ export default async function allPagesBundler(params?: Params) {
|
|||||||
metafile: true,
|
metafile: true,
|
||||||
plugins: [tailwindPlugin, virtualPlugin, artifactTracker],
|
plugins: [tailwindPlugin, virtualPlugin, artifactTracker],
|
||||||
jsx: "automatic",
|
jsx: "automatic",
|
||||||
|
splitting: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
await ctx.rebuild();
|
await ctx.rebuild();
|
||||||
|
|||||||
26
src/functions/cache/get-cache.ts
vendored
Normal file
26
src/functions/cache/get-cache.ts
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { readFileSync } from "fs";
|
||||||
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
|
import grabCacheNames from "./grab-cache-names";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
key: string;
|
||||||
|
paradigm?: "html" | "json";
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function getCache({ key, paradigm }: Params) {
|
||||||
|
try {
|
||||||
|
const { BUNEXT_CACHE_DIR } = grabDirNames();
|
||||||
|
|
||||||
|
const { cache_name } = grabCacheNames({ key, paradigm });
|
||||||
|
|
||||||
|
const content = readFileSync(
|
||||||
|
path.join(BUNEXT_CACHE_DIR, cache_name),
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
} catch (error) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/functions/cache/grab-cache-names.ts
vendored
Normal file
12
src/functions/cache/grab-cache-names.ts
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
type Params = {
|
||||||
|
key: string;
|
||||||
|
paradigm?: "html" | "json";
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function grabCacheNames({ key, paradigm = "html" }: Params) {
|
||||||
|
const parsed_key = key.replace(/\//g, "-");
|
||||||
|
const cache_name = `${parsed_key}.res.${paradigm}`;
|
||||||
|
const cache_meta_name = `${parsed_key}.meta.json`;
|
||||||
|
|
||||||
|
return { cache_name, cache_meta_name };
|
||||||
|
}
|
||||||
24
src/functions/cache/trim-all-cache.ts
vendored
Normal file
24
src/functions/cache/trim-all-cache.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { readdirSync } from "fs";
|
||||||
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
|
import trimCacheKey from "./trim-cache-key";
|
||||||
|
|
||||||
|
export default async function trimAllCache() {
|
||||||
|
try {
|
||||||
|
const { BUNEXT_CACHE_DIR } = grabDirNames();
|
||||||
|
|
||||||
|
const cached_items = readdirSync(BUNEXT_CACHE_DIR);
|
||||||
|
|
||||||
|
for (let i = 0; i < cached_items.length; i++) {
|
||||||
|
const cached_item = cached_items[i];
|
||||||
|
if (!cached_item.endsWith(`.meta.json`)) continue;
|
||||||
|
|
||||||
|
const cache_key = cached_item.replace(/\.meta\.json/, "");
|
||||||
|
|
||||||
|
const trim_key = await trimCacheKey({
|
||||||
|
key: cache_key,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/functions/cache/trim-cache-key.ts
vendored
Normal file
60
src/functions/cache/trim-cache-key.ts
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { readFileSync, unlinkSync } from "fs";
|
||||||
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
|
import grabCacheNames from "./grab-cache-names";
|
||||||
|
import path from "path";
|
||||||
|
import type { APIResponseObject, BunextCacheFileMeta } from "../../types";
|
||||||
|
import { AppData } from "../../data/app-data";
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
key: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function trimCacheKey({
|
||||||
|
key,
|
||||||
|
}: Params): Promise<APIResponseObject> {
|
||||||
|
try {
|
||||||
|
const { BUNEXT_CACHE_DIR } = grabDirNames();
|
||||||
|
const { cache_name, cache_meta_name } = grabCacheNames({
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
|
||||||
|
const config = global.CONFIG;
|
||||||
|
|
||||||
|
const default_expiry_time_seconds =
|
||||||
|
config.defaultCacheExpiry ||
|
||||||
|
AppData["DefaultCacheExpiryTimeSeconds"];
|
||||||
|
|
||||||
|
const default_expiry_time_milliseconds =
|
||||||
|
default_expiry_time_seconds * 1000;
|
||||||
|
|
||||||
|
const cache_content_path = path.join(BUNEXT_CACHE_DIR, cache_name);
|
||||||
|
const cache_meta_path = path.join(BUNEXT_CACHE_DIR, cache_meta_name);
|
||||||
|
|
||||||
|
const cache_meta: BunextCacheFileMeta = JSON.parse(
|
||||||
|
readFileSync(cache_meta_path, "utf-8"),
|
||||||
|
);
|
||||||
|
|
||||||
|
const expiry_milliseconds = cache_meta.expiry_seconds
|
||||||
|
? cache_meta.expiry_seconds * 1000
|
||||||
|
: default_expiry_time_milliseconds;
|
||||||
|
|
||||||
|
if (Date.now() - cache_meta.date_created < expiry_milliseconds) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
msg: `Cache has not expired yet`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unlinkSync(cache_content_path);
|
||||||
|
unlinkSync(cache_meta_path);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
msg: `Trim cache key ERROR: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
62
src/functions/cache/write-cache.ts
vendored
Normal file
62
src/functions/cache/write-cache.ts
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { existsSync, writeFileSync } from "fs";
|
||||||
|
import grabDirNames from "../../utils/grab-dir-names";
|
||||||
|
import grabCacheNames from "./grab-cache-names";
|
||||||
|
import type { APIResponseObject, BunextCacheFileMeta } from "../../types";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
type Params = {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
paradigm?: "html" | "json";
|
||||||
|
expiry_seconds?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function writeCache({
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
paradigm = "html",
|
||||||
|
expiry_seconds,
|
||||||
|
}: Params): Promise<APIResponseObject> {
|
||||||
|
try {
|
||||||
|
const { BUNEXT_CACHE_DIR } = grabDirNames();
|
||||||
|
|
||||||
|
const { cache_meta_name, cache_name } = grabCacheNames({
|
||||||
|
key,
|
||||||
|
paradigm,
|
||||||
|
});
|
||||||
|
|
||||||
|
const target_path = path.join(BUNEXT_CACHE_DIR, cache_name);
|
||||||
|
|
||||||
|
if (existsSync(target_path)) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
msg: `Cache entry already exists`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(path.join(target_path), value);
|
||||||
|
|
||||||
|
const cache_file_meta: BunextCacheFileMeta = {
|
||||||
|
date_created: Date.now(),
|
||||||
|
paradigm,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (expiry_seconds) {
|
||||||
|
cache_file_meta.expiry_seconds = expiry_seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFileSync(
|
||||||
|
path.join(BUNEXT_CACHE_DIR, cache_meta_name),
|
||||||
|
JSON.stringify(cache_file_meta),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
msg: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,12 @@
|
|||||||
import { existsSync, mkdirSync, statSync, writeFileSync } from "fs";
|
import { existsSync, mkdirSync, statSync, writeFileSync } from "fs";
|
||||||
import grabDirNames from "../utils/grab-dir-names";
|
import grabDirNames from "../utils/grab-dir-names";
|
||||||
|
import { execSync } from "child_process";
|
||||||
|
|
||||||
export default async function () {
|
export default async function () {
|
||||||
const dirNames = grabDirNames();
|
const dirNames = grabDirNames();
|
||||||
|
|
||||||
|
execSync(`rm -rf ${dirNames.BUNEXT_CACHE_DIR}`);
|
||||||
|
|
||||||
const keys = Object.keys(dirNames) as (keyof ReturnType<
|
const keys = Object.keys(dirNames) as (keyof ReturnType<
|
||||||
typeof grabDirNames
|
typeof grabDirNames
|
||||||
>)[];
|
>)[];
|
||||||
|
|||||||
9
src/functions/server/cron.tsx
Normal file
9
src/functions/server/cron.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { AppData } from "../../data/app-data";
|
||||||
|
import trimAllCache from "../cache/trim-all-cache";
|
||||||
|
|
||||||
|
export default async function cron() {
|
||||||
|
while (true) {
|
||||||
|
await trimAllCache();
|
||||||
|
await Bun.sleep(AppData["DefaultCronInterval"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import handleWebPages from "./web-pages/handle-web-pages";
|
|||||||
import handleRoutes from "./handle-routes";
|
import handleRoutes from "./handle-routes";
|
||||||
import isDevelopment from "../../utils/is-development";
|
import isDevelopment from "../../utils/is-development";
|
||||||
import grabConstants from "../../utils/grab-constants";
|
import grabConstants from "../../utils/grab-constants";
|
||||||
|
import { AppData } from "../../data/app-data";
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
dev?: boolean;
|
dev?: boolean;
|
||||||
@ -15,6 +16,8 @@ export default async function (params?: Params): Promise<ServeOptions> {
|
|||||||
const port = grabAppPort();
|
const port = grabAppPort();
|
||||||
const { PUBLIC_DIR } = grabDirNames();
|
const { PUBLIC_DIR } = grabDirNames();
|
||||||
|
|
||||||
|
const is_dev = isDevelopment();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async fetch(req, server) {
|
async fetch(req, server) {
|
||||||
try {
|
try {
|
||||||
@ -34,7 +37,7 @@ export default async function (params?: Params): Promise<ServeOptions> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url.pathname === "/__hmr" && isDevelopment()) {
|
if (url.pathname === "/__hmr" && is_dev) {
|
||||||
const referer_url = new URL(
|
const referer_url = new URL(
|
||||||
req.headers.get("referer") || "",
|
req.headers.get("referer") || "",
|
||||||
);
|
);
|
||||||
@ -95,7 +98,15 @@ export default async function (params?: Params): Promise<ServeOptions> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Response(file);
|
let res_opts: ResponseInit = {};
|
||||||
|
|
||||||
|
if (!is_dev && url.pathname.match(/__bunext/)) {
|
||||||
|
res_opts.headers = {
|
||||||
|
"Cache-Control": `public, max-age=${AppData["BunextStaticFilesCacheExpiry"]}, must-revalidate`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(file, res_opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url.pathname.startsWith("/favicon.")) {
|
if (url.pathname.startsWith("/favicon.")) {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import grabDirNames from "../../utils/grab-dir-names";
|
|||||||
import EJSON from "../../utils/ejson";
|
import EJSON from "../../utils/ejson";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import type { BundlerCTXMap } from "../../types";
|
import type { BundlerCTXMap } from "../../types";
|
||||||
|
import cron from "./cron";
|
||||||
|
|
||||||
const { HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames();
|
const { HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames();
|
||||||
|
|
||||||
@ -36,6 +37,7 @@ export default async function startServer(params?: Params) {
|
|||||||
}
|
}
|
||||||
global.BUNDLER_CTX_MAP = artifacts;
|
global.BUNDLER_CTX_MAP = artifacts;
|
||||||
global.IS_FIRST_BUNDLE_READY = true;
|
global.IS_FIRST_BUNDLE_READY = true;
|
||||||
|
cron();
|
||||||
}
|
}
|
||||||
|
|
||||||
let bundle_ready_retries = 0;
|
let bundle_ready_retries = 0;
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export default async function genWebHTML({
|
|||||||
}</script>\n`;
|
}</script>\n`;
|
||||||
|
|
||||||
if (bundledMap?.path) {
|
if (bundledMap?.path) {
|
||||||
html += ` <script src="/${bundledMap.path}" type="module" defer></script>\n`;
|
html += ` <script src="/${bundledMap.path}" type="module" async></script>\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDevelopment()) {
|
if (isDevelopment()) {
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import type { GrabPageComponentRes } from "../../../types";
|
import type { GrabPageComponentRes } from "../../../types";
|
||||||
import isDevelopment from "../../../utils/is-development";
|
import isDevelopment from "../../../utils/is-development";
|
||||||
|
import getCache from "../../cache/get-cache";
|
||||||
|
import writeCache from "../../cache/write-cache";
|
||||||
import genWebHTML from "./generate-web-html";
|
import genWebHTML from "./generate-web-html";
|
||||||
import grabPageComponent from "./grab-page-component";
|
import grabPageComponent from "./grab-page-component";
|
||||||
import grabPageErrorComponent from "./grab-page-error-component";
|
import grabPageErrorComponent from "./grab-page-error-component";
|
||||||
@ -12,6 +14,24 @@ export default async function handleWebPages({
|
|||||||
req,
|
req,
|
||||||
}: Params): Promise<Response> {
|
}: Params): Promise<Response> {
|
||||||
try {
|
try {
|
||||||
|
if (!isDevelopment()) {
|
||||||
|
const url = new URL(req.url);
|
||||||
|
const key = url.pathname + (url.search || "");
|
||||||
|
|
||||||
|
const existing_cache = getCache({ key, paradigm: "html" });
|
||||||
|
|
||||||
|
if (existing_cache) {
|
||||||
|
const res_opts: ResponseInit = {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/html",
|
||||||
|
"X-Bunext-Cache": "HIT",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Response(existing_cache, res_opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const componentRes = await grabPageComponent({ req });
|
const componentRes = await grabPageComponent({ req });
|
||||||
return await generateRes(componentRes);
|
return await generateRes(componentRes);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@ -65,6 +85,20 @@ async function generateRes({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cache_page =
|
||||||
|
module.config?.cachePage || serverRes?.cachePage || false;
|
||||||
|
const expiry_seconds = module.config?.cacheExpiry || serverRes?.cacheExpiry;
|
||||||
|
|
||||||
|
if (cache_page && routeParams?.url) {
|
||||||
|
const key = routeParams.url.pathname + (routeParams.url.search || "");
|
||||||
|
writeCache({
|
||||||
|
key,
|
||||||
|
value: html,
|
||||||
|
paradigm: "html",
|
||||||
|
expiry_seconds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const res = new Response(html, res_opts);
|
const res = new Response(html, res_opts);
|
||||||
|
|
||||||
if (routeParams?.resTransform) {
|
if (routeParams?.resTransform) {
|
||||||
|
|||||||
@ -51,6 +51,7 @@ export type BunextConfig = {
|
|||||||
middleware?: (
|
middleware?: (
|
||||||
params: BunextConfigMiddlewareParams,
|
params: BunextConfigMiddlewareParams,
|
||||||
) => Promise<Response | undefined> | Response | undefined;
|
) => Promise<Response | undefined> | Response | undefined;
|
||||||
|
defaultCacheExpiry?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BunextConfigMiddlewareParams = {
|
export type BunextConfigMiddlewareParams = {
|
||||||
@ -157,6 +158,7 @@ export type BunextPageModule = {
|
|||||||
server?: BunextPageServerFn;
|
server?: BunextPageServerFn;
|
||||||
meta?: BunextPageModuleMeta | BunextPageModuleMetaFn;
|
meta?: BunextPageModuleMeta | BunextPageModuleMetaFn;
|
||||||
Head?: FC<BunextPageHeadFCProps>;
|
Head?: FC<BunextPageHeadFCProps>;
|
||||||
|
config?: BunextRouteConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BunextPageModuleMetaFn = (params: {
|
export type BunextPageModuleMetaFn = (params: {
|
||||||
@ -197,6 +199,14 @@ export type BunextPageServerFn<
|
|||||||
ctx: Omit<BunxRouteParams, "body">,
|
ctx: Omit<BunxRouteParams, "body">,
|
||||||
) => Promise<BunextPageModuleServerReturn<T>>;
|
) => Promise<BunextPageModuleServerReturn<T>>;
|
||||||
|
|
||||||
|
export type BunextRouteConfig = {
|
||||||
|
cachePage?: boolean;
|
||||||
|
/**
|
||||||
|
* Expiry time of the cache in seconds
|
||||||
|
*/
|
||||||
|
cacheExpiry?: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type BunextPageModuleServerReturn<
|
export type BunextPageModuleServerReturn<
|
||||||
T extends { [k: string]: any } = { [k: string]: any },
|
T extends { [k: string]: any } = { [k: string]: any },
|
||||||
Q extends { [k: string]: any } = { [k: string]: any },
|
Q extends { [k: string]: any } = { [k: string]: any },
|
||||||
@ -205,6 +215,11 @@ export type BunextPageModuleServerReturn<
|
|||||||
query?: Q;
|
query?: Q;
|
||||||
redirect?: BunextPageModuleServerRedirect;
|
redirect?: BunextPageModuleServerRedirect;
|
||||||
responseOptions?: ResponseInit;
|
responseOptions?: ResponseInit;
|
||||||
|
cachePage?: boolean;
|
||||||
|
/**
|
||||||
|
* Expiry time of the cache in seconds
|
||||||
|
*/
|
||||||
|
cacheExpiry?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BunextPageModuleServerRedirect = {
|
export type BunextPageModuleServerRedirect = {
|
||||||
@ -250,3 +265,9 @@ export type GlobalHMRControllerObject = {
|
|||||||
page_url: string;
|
page_url: string;
|
||||||
target_map?: BundlerCTXMap;
|
target_map?: BundlerCTXMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BunextCacheFileMeta = {
|
||||||
|
date_created: number;
|
||||||
|
paradigm: "html" | "json";
|
||||||
|
expiry_seconds?: number;
|
||||||
|
};
|
||||||
|
|||||||
@ -6,7 +6,9 @@ export default function grabDirNames() {
|
|||||||
const PAGES_DIR = path.join(SRC_DIR, "pages");
|
const PAGES_DIR = path.join(SRC_DIR, "pages");
|
||||||
const API_DIR = path.join(PAGES_DIR, "api");
|
const API_DIR = path.join(PAGES_DIR, "api");
|
||||||
const PUBLIC_DIR = path.join(ROOT_DIR, "public");
|
const PUBLIC_DIR = path.join(ROOT_DIR, "public");
|
||||||
const HYDRATION_DST_DIR = path.join(PUBLIC_DIR, "pages");
|
const BUNEXT_PUBLIC_DIR = path.join(PUBLIC_DIR, "__bunext");
|
||||||
|
const HYDRATION_DST_DIR = path.join(BUNEXT_PUBLIC_DIR, "pages");
|
||||||
|
const BUNEXT_CACHE_DIR = path.join(BUNEXT_PUBLIC_DIR, "cache");
|
||||||
const HYDRATION_DST_DIR_MAP_JSON_FILE = path.join(
|
const HYDRATION_DST_DIR_MAP_JSON_FILE = path.join(
|
||||||
HYDRATION_DST_DIR,
|
HYDRATION_DST_DIR,
|
||||||
"map.json",
|
"map.json",
|
||||||
@ -54,5 +56,6 @@ export default function grabDirNames() {
|
|||||||
BUNX_ROOT_404_PRESET_COMPONENT,
|
BUNX_ROOT_404_PRESET_COMPONENT,
|
||||||
BUNX_ROOT_404_FILE_NAME,
|
BUNX_ROOT_404_FILE_NAME,
|
||||||
HYDRATION_DST_DIR_MAP_JSON_FILE,
|
HYDRATION_DST_DIR_MAP_JSON_FILE,
|
||||||
|
BUNEXT_CACHE_DIR,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user