veryfront 0.1.1057 → 0.1.1059
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/deno.js +1 -1
- package/esm/src/server/context/cache-invalidation.d.ts.map +1 -1
- package/esm/src/server/context/cache-invalidation.js +7 -0
- package/esm/src/server/dev-server/middleware.d.ts +7 -5
- package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
- package/esm/src/server/dev-server/middleware.js +22 -14
- package/esm/src/server/production-server.d.ts.map +1 -1
- package/esm/src/server/production-server.js +7 -5
- package/esm/src/server/project-env/index.d.ts +1 -1
- package/esm/src/server/project-env/index.d.ts.map +1 -1
- package/esm/src/server/project-env/index.js +1 -1
- package/esm/src/server/runtime-handler/index.d.ts.map +1 -1
- package/esm/src/server/runtime-handler/index.js +7 -1
- package/esm/src/server/runtime-handler/project-middleware.d.ts +28 -0
- package/esm/src/server/runtime-handler/project-middleware.d.ts.map +1 -0
- package/esm/src/server/runtime-handler/project-middleware.js +136 -0
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +5 -5
package/esm/deno.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-invalidation.d.ts","sourceRoot":"","sources":["../../../../src/src/server/context/cache-invalidation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cache-invalidation.d.ts","sourceRoot":"","sources":["../../../../src/src/server/context/cache-invalidation.ts"],"names":[],"mappings":"AAuBA,UAAU,mBAAmB;IAC3B,qEAAqE;IACrE,WAAW,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACvC,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,MAAM,EAAE,EACvB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA0Hf"}
|
|
@@ -8,6 +8,7 @@ import { clearRouterDetectionCache, clearRouterDetectionCacheForProject, } from
|
|
|
8
8
|
import { clearSnippetCacheForProject } from "../../rendering/snippet-renderer.js";
|
|
9
9
|
import { resetApiHandlerForProject } from "../handlers/request/api/pages-api-handler.js";
|
|
10
10
|
import { clearSourceMissCache } from "../../modules/server/module-source-resolution-cache.js";
|
|
11
|
+
import { invalidateProjectMiddlewareCache } from "../runtime-handler/project-middleware.js";
|
|
11
12
|
const logger = serverLogger.component("cache-invalidation");
|
|
12
13
|
/**
|
|
13
14
|
* Invalidate project caches with optional environment scoping.
|
|
@@ -36,6 +37,12 @@ export async function invalidateProjectCaches(projectSlug, changedPaths, options
|
|
|
36
37
|
clearModulePathCache();
|
|
37
38
|
}
|
|
38
39
|
clearSourceMissCache();
|
|
40
|
+
const middlewareEntries = invalidateProjectMiddlewareCache(projectSlug, projectId);
|
|
41
|
+
logger.debug("Clearing project middleware cache", {
|
|
42
|
+
projectSlug,
|
|
43
|
+
projectId,
|
|
44
|
+
entriesDeleted: middlewareEntries,
|
|
45
|
+
});
|
|
39
46
|
logger.debug("Clearing SSR module cache", {
|
|
40
47
|
projectSlug,
|
|
41
48
|
projectId,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { MiddlewarePipeline } from "../../middleware/core/pipeline/index.js";
|
|
2
|
+
import type { MiddlewareHandler } from "../../middleware/core/types.js";
|
|
2
3
|
import type { RuntimeAdapter } from "../../platform/adapters/base.js";
|
|
3
4
|
import type { VeryfrontConfig } from "../../config/index.js";
|
|
4
|
-
export type MiddlewareFunction =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
export declare function loadMiddlewareFile(projectDir: string, adapter: RuntimeAdapter): Promise<MiddlewareFunction[]>;
|
|
5
|
+
export type MiddlewareFunction = MiddlewareHandler;
|
|
6
|
+
interface MiddlewareLoadOptions {
|
|
7
|
+
throwOnError?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function loadMiddlewareFile(projectDir: string, adapter: RuntimeAdapter, options?: MiddlewareLoadOptions): Promise<MiddlewareFunction[]>;
|
|
9
10
|
export declare function setupMiddleware(pipeline: MiddlewarePipeline, config: VeryfrontConfig, requestHandler: (req: Request) => Promise<Response>, projectDir?: string, adapter?: RuntimeAdapter): Promise<void>;
|
|
11
|
+
export {};
|
|
10
12
|
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../../src/src/server/dev-server/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../../src/src/server/dev-server/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAU7D,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEnD,UAAU,qBAAqB;IAC7B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAkFD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CA6B/B;AAkFD,wBAAsB,eAAe,CACnC,QAAQ,EAAE,kBAAkB,EAC5B,MAAM,EAAE,eAAe,EACvB,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,EACnD,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
|
|
@@ -78,7 +78,7 @@ function createRequestLoggerMiddleware() {
|
|
|
78
78
|
});
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
-
export async function loadMiddlewareFile(projectDir, adapter) {
|
|
81
|
+
export async function loadMiddlewareFile(projectDir, adapter, options = {}) {
|
|
82
82
|
const middlewareFiles = ["middleware.ts", "middleware.js", "middleware.mjs"];
|
|
83
83
|
for (const middlewareFile of middlewareFiles) {
|
|
84
84
|
const middlewarePath = join(projectDir, middlewareFile);
|
|
@@ -87,20 +87,22 @@ export async function loadMiddlewareFile(projectDir, adapter) {
|
|
|
87
87
|
try {
|
|
88
88
|
logger.debug(`Loading ${middlewareFile}`);
|
|
89
89
|
if (isVirtualFilesystem(adapter.fs)) {
|
|
90
|
-
return await loadMiddlewareFromVirtualFS(middlewarePath, adapter);
|
|
90
|
+
return await loadMiddlewareFromVirtualFS(middlewarePath, adapter, options.throwOnError === true);
|
|
91
91
|
}
|
|
92
92
|
const middlewareUrl = `file://${middlewarePath}?t=${Date.now()}-${dntShim.crypto.randomUUID()}`;
|
|
93
93
|
const middlewareModule = await import(middlewareUrl);
|
|
94
|
-
return normalizeMiddlewareExport(middlewareModule);
|
|
94
|
+
return normalizeMiddlewareExport(middlewareModule, options.throwOnError === true);
|
|
95
95
|
}
|
|
96
96
|
catch (error) {
|
|
97
97
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
98
98
|
logger.warn(`Failed to load ${middlewareFile}: ${errorMessage}`);
|
|
99
|
+
if (options.throwOnError)
|
|
100
|
+
throw error;
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
return [];
|
|
102
104
|
}
|
|
103
|
-
async function loadMiddlewareFromVirtualFS(middlewarePath, adapter) {
|
|
105
|
+
async function loadMiddlewareFromVirtualFS(middlewarePath, adapter, strictExport) {
|
|
104
106
|
const fs = createFileSystem();
|
|
105
107
|
const content = await adapter.fs.readFile(middlewarePath);
|
|
106
108
|
const source = typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
@@ -129,24 +131,30 @@ async function loadMiddlewareFromVirtualFS(middlewarePath, adapter) {
|
|
|
129
131
|
try {
|
|
130
132
|
await fs.writeTextFile(tempFile, js);
|
|
131
133
|
const middlewareModule = await import(`file://${tempFile}?v=${Date.now()}`);
|
|
132
|
-
return normalizeMiddlewareExport(middlewareModule);
|
|
134
|
+
return normalizeMiddlewareExport(middlewareModule, strictExport);
|
|
133
135
|
}
|
|
134
136
|
finally {
|
|
135
137
|
await fs.remove(tempDir, { recursive: true });
|
|
136
138
|
}
|
|
137
139
|
}
|
|
138
|
-
function normalizeMiddlewareExport(middlewareModule) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
function normalizeMiddlewareExport(middlewareModule, strict = false) {
|
|
141
|
+
const exported = middlewareModule && typeof middlewareModule === "object" &&
|
|
142
|
+
"default" in middlewareModule
|
|
143
|
+
? middlewareModule.default
|
|
144
|
+
: middlewareModule;
|
|
145
|
+
if (Array.isArray(exported)) {
|
|
146
|
+
if (strict && (exported.length === 0 || exported.some((value) => typeof value !== "function"))) {
|
|
147
|
+
throw new TypeError("Invalid middleware export: expected a function or non-empty array of functions");
|
|
143
148
|
}
|
|
144
|
-
return typeof
|
|
149
|
+
return exported.filter((middleware) => typeof middleware === "function");
|
|
150
|
+
}
|
|
151
|
+
if (typeof exported === "function") {
|
|
152
|
+
return [exported];
|
|
145
153
|
}
|
|
146
|
-
if (
|
|
147
|
-
|
|
154
|
+
if (strict) {
|
|
155
|
+
throw new TypeError("Invalid middleware export: expected a function or non-empty array of functions");
|
|
148
156
|
}
|
|
149
|
-
return
|
|
157
|
+
return [];
|
|
150
158
|
}
|
|
151
159
|
export async function setupMiddleware(pipeline, config, requestHandler, projectDir, adapter) {
|
|
152
160
|
pipeline.use(createRequestLoggerMiddleware());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"production-server.d.ts","sourceRoot":"","sources":["../../../src/src/server/production-server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAKnE,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA4BrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AA4EtE,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,aAAa;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uGAAuG;IACvG,kBAAkB,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IAC9C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,oFAAoF;IACpF,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,sFAAsF;IACtF,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,6CAA6C;AAC7C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,mDAAmD;AACnD,MAAM,WAAW,4BAA6B,SAAQ,aAAa;IACjE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,8FAA8F;IAC9F,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,gCAAgC;AAChC,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"production-server.d.ts","sourceRoot":"","sources":["../../../src/src/server/production-server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAKnE,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA4BrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AA4EtE,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,aAAa;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uGAAuG;IACvG,kBAAkB,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IAC9C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClE,oFAAoF;IACpF,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,sFAAsF;IACtF,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,6CAA6C;AAC7C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,mDAAmD;AACnD,MAAM,WAAW,4BAA6B,SAAQ,aAAa;IACjE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,8FAA8F;IAC9F,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,gCAAgC;AAChC,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,YAAY,CAAC,CAmLvB"}
|
|
@@ -144,11 +144,13 @@ export function startProductionServer(options) {
|
|
|
144
144
|
defaultEnvironment,
|
|
145
145
|
localProjects,
|
|
146
146
|
});
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
const projectMiddleware =
|
|
147
|
+
// Shared proxy filesystems need tenant context from the request, so
|
|
148
|
+
// startup cannot discover a root middleware file. Standalone runtimes
|
|
149
|
+
// load project middleware with the same semantics as the dev server.
|
|
150
|
+
const isProxyMode = bootstrap.config.fs?.veryfront?.proxyMode === true;
|
|
151
|
+
const projectMiddleware = isProxyMode
|
|
152
|
+
? []
|
|
153
|
+
: await loadMiddlewareFile(projectDir, adapter, { throwOnError: true });
|
|
152
154
|
let coreHandler = baseHandler;
|
|
153
155
|
if (projectMiddleware.length > 0) {
|
|
154
156
|
const pipeline = new MiddlewarePipeline();
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module server/project-env
|
|
5
5
|
*/
|
|
6
|
-
export { getProjectEnv, isProjectEnvActive, runWithProjectEnv } from "./storage.js";
|
|
6
|
+
export { getProjectEnv, getProjectEnvSnapshot, isProjectEnvActive, runWithProjectEnv, } from "./storage.js";
|
|
7
7
|
export { EnvironmentVariableCache } from "./cache.js";
|
|
8
8
|
export { filterRuntimeProjectEnv, filterSharedRuntimeProjectEnv } from "./reserved-env.js";
|
|
9
9
|
export { fetchProjectEnvVars } from "./fetcher.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/server/project-env/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/server/project-env/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module server/project-env
|
|
5
5
|
*/
|
|
6
|
-
export { getProjectEnv, isProjectEnvActive, runWithProjectEnv } from "./storage.js";
|
|
6
|
+
export { getProjectEnv, getProjectEnvSnapshot, isProjectEnvActive, runWithProjectEnv, } from "./storage.js";
|
|
7
7
|
export { EnvironmentVariableCache } from "./cache.js";
|
|
8
8
|
export { filterRuntimeProjectEnv, filterSharedRuntimeProjectEnv } from "./reserved-env.js";
|
|
9
9
|
export { fetchProjectEnvVars } from "./fetcher.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAK7D,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAmCpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAK7D,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAmCpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAsErE,OAAO,EAAE,qBAAqB,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAMtF,2CAA2C;AAC3C,eAAO,MAAM,aAAa,4vBAqChB,CAAC;AAEX,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IAClD,mDAAmD;IACnD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA6CD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,IAAI,GAAE,mBAAwB,GAC7B;IAAE,QAAQ,EAAE,aAAa,CAAC;IAAC,UAAU,EAAE,iBAAiB,CAAA;CAAE,CAuB5D;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,oFAAoF;IACpF,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uGAAuG;IACvG,kBAAkB,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;CAC/C;AAED,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,EACvB,IAAI,GAAE,qBAAsC,GAC3C,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAodnE;AAGD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -69,6 +69,7 @@ import { withRequestTimeout } from "./timeout-manager.js";
|
|
|
69
69
|
import { EnvironmentVariableCache, fetchProjectEnvVars, filterRuntimeProjectEnv, runWithProjectEnv, } from "../project-env/index.js";
|
|
70
70
|
import { SCANNER_PATH_PATTERN } from "../../utils/constants/security.js";
|
|
71
71
|
import { isProxyTrusted } from "../utils/proxy-trust.js";
|
|
72
|
+
import { projectMiddlewareRuntime } from "./project-middleware.js";
|
|
72
73
|
// Re-export from dedicated module for lightweight imports
|
|
73
74
|
export { parseProxyEnvironment } from "./proxy-environment.js";
|
|
74
75
|
const baseLogger = getBaseLogger("SERVER");
|
|
@@ -463,7 +464,12 @@ export function createVeryfrontHandler(projectDir, adapter, opts = { projectDir
|
|
|
463
464
|
});
|
|
464
465
|
}
|
|
465
466
|
await incrementRequestMetrics();
|
|
466
|
-
const executeRoute = () =>
|
|
467
|
+
const executeRoute = () => projectMiddlewareRuntime.execute({
|
|
468
|
+
request: req,
|
|
469
|
+
handlerContext: ctx,
|
|
470
|
+
isSharedProxy: isProxyMode,
|
|
471
|
+
next: async () => (await registry.execute(req, ctx)) ?? undefined,
|
|
472
|
+
});
|
|
467
473
|
// Only activate env isolation in proxy mode (multi-tenant).
|
|
468
474
|
// reqCtx.token indicates the request came through the proxy with auth.
|
|
469
475
|
// Without it (standalone / test), host env must remain accessible.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { RuntimeAdapter } from "../../platform/adapters/base.js";
|
|
2
|
+
import { type MiddlewareFunction } from "../dev-server/middleware.js";
|
|
3
|
+
import type { HandlerContext } from "../../types/index.js";
|
|
4
|
+
type MiddlewareLoader = (projectDir: string, adapter: RuntimeAdapter) => Promise<MiddlewareFunction[]>;
|
|
5
|
+
interface ProjectMiddlewareRuntimeOptions {
|
|
6
|
+
maxEntries?: number;
|
|
7
|
+
loadMiddleware?: MiddlewareLoader;
|
|
8
|
+
registryName?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ProjectMiddlewareRuntimeContext {
|
|
11
|
+
request: Request;
|
|
12
|
+
handlerContext: HandlerContext;
|
|
13
|
+
isSharedProxy: boolean;
|
|
14
|
+
next: () => Promise<Response | undefined>;
|
|
15
|
+
}
|
|
16
|
+
/** Request-scoped root middleware loader for the shared multi-project runtime. */
|
|
17
|
+
export declare class ProjectMiddlewareRuntime {
|
|
18
|
+
#private;
|
|
19
|
+
constructor(options?: ProjectMiddlewareRuntimeOptions);
|
|
20
|
+
get size(): number;
|
|
21
|
+
invalidateProject(projectIdentity: string): number;
|
|
22
|
+
clear(): void;
|
|
23
|
+
execute(input: ProjectMiddlewareRuntimeContext): Promise<Response | undefined>;
|
|
24
|
+
}
|
|
25
|
+
export declare const projectMiddlewareRuntime: ProjectMiddlewareRuntime;
|
|
26
|
+
export declare function invalidateProjectMiddlewareCache(projectSlug: string, projectId?: string): number;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=project-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-middleware.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/project-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAMtE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAO3D,KAAK,gBAAgB,GAAG,CACtB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,cAAc,KACpB,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;AAEnC,UAAU,+BAA+B;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;CAC3C;AAcD,kFAAkF;AAClF,qBAAa,wBAAwB;;gBAIvB,OAAO,GAAE,+BAAoC;IAYzD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,iBAAiB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM;IAYlD,KAAK,IAAI,IAAI;IAIP,OAAO,CAAC,KAAK,EAAE,+BAA+B,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;CAiGrF;AAED,eAAO,MAAM,wBAAwB,0BAEnC,CAAC;AAEH,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAOR"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { isExtendedFSAdapter } from "../../platform/adapters/fs/wrapper.js";
|
|
2
|
+
import { registerLRUCache } from "../../cache/index.js";
|
|
3
|
+
import { MiddlewareContext } from "../../middleware/core/context.js";
|
|
4
|
+
import { MiddlewarePipeline } from "../../middleware/core/pipeline/index.js";
|
|
5
|
+
import { getProjectEnvSnapshot } from "../project-env/index.js";
|
|
6
|
+
import { loadMiddlewareFile, } from "../dev-server/middleware.js";
|
|
7
|
+
import { LRUCache } from "../../utils/lru-wrapper.js";
|
|
8
|
+
import { serverLogger } from "../../utils/index.js";
|
|
9
|
+
const DEFAULT_MAX_ENTRIES = 100;
|
|
10
|
+
const logger = serverLogger.component("project-middleware");
|
|
11
|
+
function cacheSegment(value) {
|
|
12
|
+
return encodeURIComponent(value);
|
|
13
|
+
}
|
|
14
|
+
function resolvedEnvironment(ctx) {
|
|
15
|
+
return ctx.resolvedEnvironment ?? ctx.requestContext?.mode ?? "preview";
|
|
16
|
+
}
|
|
17
|
+
function resolvedBranch(ctx) {
|
|
18
|
+
return ctx.requestContext?.branch ?? ctx.parsedDomain?.branch ?? null;
|
|
19
|
+
}
|
|
20
|
+
/** Request-scoped root middleware loader for the shared multi-project runtime. */
|
|
21
|
+
export class ProjectMiddlewareRuntime {
|
|
22
|
+
#cache;
|
|
23
|
+
#loadMiddleware;
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.#cache = new LRUCache({
|
|
26
|
+
maxEntries: options.maxEntries ?? DEFAULT_MAX_ENTRIES,
|
|
27
|
+
});
|
|
28
|
+
this.#loadMiddleware = options.loadMiddleware ??
|
|
29
|
+
((projectDir, adapter) => loadMiddlewareFile(projectDir, adapter, { throwOnError: true }));
|
|
30
|
+
if (options.registryName) {
|
|
31
|
+
registerLRUCache(options.registryName, this.#cache);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
get size() {
|
|
35
|
+
return this.#cache.size;
|
|
36
|
+
}
|
|
37
|
+
invalidateProject(projectIdentity) {
|
|
38
|
+
const expectedProject = cacheSegment(projectIdentity);
|
|
39
|
+
let deleted = 0;
|
|
40
|
+
for (const key of [...this.#cache.keys()]) {
|
|
41
|
+
if (key.split(":", 1)[0] !== expectedProject)
|
|
42
|
+
continue;
|
|
43
|
+
if (this.#cache.delete(key))
|
|
44
|
+
deleted++;
|
|
45
|
+
}
|
|
46
|
+
return deleted;
|
|
47
|
+
}
|
|
48
|
+
clear() {
|
|
49
|
+
this.#cache.clear();
|
|
50
|
+
}
|
|
51
|
+
async execute(input) {
|
|
52
|
+
const { handlerContext: ctx, isSharedProxy, next, request } = input;
|
|
53
|
+
const fs = ctx.adapter.fs;
|
|
54
|
+
if (!isSharedProxy || ctx.isLocalProject || !ctx.projectSlug || !ctx.proxyToken ||
|
|
55
|
+
!isExtendedFSAdapter(fs) || !fs.isMultiProjectMode()) {
|
|
56
|
+
return next();
|
|
57
|
+
}
|
|
58
|
+
const environment = resolvedEnvironment(ctx);
|
|
59
|
+
const branch = resolvedBranch(ctx);
|
|
60
|
+
return fs.runWithContext(ctx.projectSlug, ctx.proxyToken, async () => {
|
|
61
|
+
const middleware = await this.#getMiddleware(ctx, environment, branch);
|
|
62
|
+
if (middleware.length === 0)
|
|
63
|
+
return next();
|
|
64
|
+
const pipeline = new MiddlewarePipeline();
|
|
65
|
+
for (const handler of middleware)
|
|
66
|
+
pipeline.use(handler);
|
|
67
|
+
const composed = pipeline.compose();
|
|
68
|
+
const middlewareContext = new MiddlewareContext(request, getProjectEnvSnapshot() ?? {});
|
|
69
|
+
return await composed(middlewareContext, next);
|
|
70
|
+
}, ctx.projectId, {
|
|
71
|
+
productionMode: environment === "production",
|
|
72
|
+
releaseId: ctx.releaseId ?? null,
|
|
73
|
+
branch,
|
|
74
|
+
environmentName: ctx.environmentName ?? null,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async #getMiddleware(ctx, environment, branch) {
|
|
78
|
+
const key = this.#buildCacheKey(ctx, environment, branch);
|
|
79
|
+
if (!key)
|
|
80
|
+
return this.#load(ctx);
|
|
81
|
+
let pending = this.#cache.get(key);
|
|
82
|
+
if (!pending) {
|
|
83
|
+
pending = Promise.resolve().then(() => this.#load(ctx));
|
|
84
|
+
this.#cache.set(key, pending);
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
return await pending;
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (this.#cache.get(key) === pending)
|
|
91
|
+
this.#cache.delete(key);
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
#buildCacheKey(ctx, environment, branch) {
|
|
96
|
+
const projectIdentity = ctx.projectId ?? ctx.projectSlug;
|
|
97
|
+
if (!projectIdentity)
|
|
98
|
+
return null;
|
|
99
|
+
const sourceIdentity = environment === "production" ? ctx.releaseId : branch ?? "default";
|
|
100
|
+
if (!sourceIdentity)
|
|
101
|
+
return null;
|
|
102
|
+
const environmentIdentity = ctx.environmentId ?? ctx.environmentName ?? "default";
|
|
103
|
+
return [
|
|
104
|
+
cacheSegment(projectIdentity),
|
|
105
|
+
environment,
|
|
106
|
+
cacheSegment(sourceIdentity),
|
|
107
|
+
cacheSegment(environmentIdentity),
|
|
108
|
+
].join(":");
|
|
109
|
+
}
|
|
110
|
+
async #load(ctx) {
|
|
111
|
+
try {
|
|
112
|
+
return await this.#loadMiddleware(ctx.projectDir, ctx.adapter);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
logger.error("Failed to load project middleware", {
|
|
116
|
+
projectSlug: ctx.projectSlug,
|
|
117
|
+
projectId: ctx.projectId,
|
|
118
|
+
releaseId: ctx.releaseId,
|
|
119
|
+
branch: resolvedBranch(ctx),
|
|
120
|
+
error: error instanceof Error ? error.message : String(error),
|
|
121
|
+
});
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
export const projectMiddlewareRuntime = new ProjectMiddlewareRuntime({
|
|
127
|
+
registryName: "project-middleware-cache",
|
|
128
|
+
});
|
|
129
|
+
export function invalidateProjectMiddlewareCache(projectSlug, projectId) {
|
|
130
|
+
const identities = new Set([projectSlug, projectId].filter((value) => !!value));
|
|
131
|
+
let deleted = 0;
|
|
132
|
+
for (const identity of identities) {
|
|
133
|
+
deleted += projectMiddlewareRuntime.invalidateProject(identity);
|
|
134
|
+
}
|
|
135
|
+
return deleted;
|
|
136
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1059",
|
|
4
4
|
"description": "The simplest way to build AI-powered apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -328,10 +328,10 @@
|
|
|
328
328
|
"@types/react": "19.2.14",
|
|
329
329
|
"@types/react-dom": "19.2.3",
|
|
330
330
|
"ws": "8.21.0",
|
|
331
|
-
"@veryfront/ext-bundler-esbuild": "0.1.
|
|
332
|
-
"@veryfront/ext-content-mdx": "0.1.
|
|
333
|
-
"@veryfront/ext-css-tailwind": "0.1.
|
|
334
|
-
"@veryfront/ext-parser-babel": "0.1.
|
|
331
|
+
"@veryfront/ext-bundler-esbuild": "0.1.1059",
|
|
332
|
+
"@veryfront/ext-content-mdx": "0.1.1059",
|
|
333
|
+
"@veryfront/ext-css-tailwind": "0.1.1059",
|
|
334
|
+
"@veryfront/ext-parser-babel": "0.1.1059"
|
|
335
335
|
},
|
|
336
336
|
"devDependencies": {
|
|
337
337
|
"@types/node": "20.9.0"
|