@fiyuu/runtime 0.5.0 → 0.5.1
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/dist/bundler.d.ts +10 -0
- package/dist/bundler.d.ts.map +1 -0
- package/dist/bundler.js +125 -0
- package/dist/bundler.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +26 -0
- package/dist/cli.js.map +1 -0
- package/dist/client-runtime.d.ts +16 -0
- package/dist/client-runtime.d.ts.map +1 -0
- package/dist/client-runtime.js +528 -0
- package/dist/client-runtime.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/inspector.d.ts +39 -0
- package/dist/inspector.d.ts.map +1 -0
- package/dist/inspector.js +262 -0
- package/dist/inspector.js.map +1 -0
- package/dist/server-devtools.d.ts +14 -0
- package/dist/server-devtools.d.ts.map +1 -0
- package/dist/server-devtools.js +123 -0
- package/dist/server-devtools.js.map +1 -0
- package/dist/server-loader.d.ts +40 -0
- package/dist/server-loader.d.ts.map +1 -0
- package/dist/server-loader.js +255 -0
- package/dist/server-loader.js.map +1 -0
- package/dist/server-middleware.d.ts +8 -0
- package/dist/server-middleware.d.ts.map +1 -0
- package/dist/server-middleware.js +50 -0
- package/dist/server-middleware.js.map +1 -0
- package/dist/server-private.d.ts +81 -0
- package/dist/server-private.d.ts.map +1 -0
- package/dist/server-private.js +225 -0
- package/dist/server-private.js.map +1 -0
- package/dist/server-renderer.d.ts +42 -0
- package/dist/server-renderer.d.ts.map +1 -0
- package/dist/server-renderer.js +255 -0
- package/dist/server-renderer.js.map +1 -0
- package/dist/server-router.d.ts +15 -0
- package/dist/server-router.d.ts.map +1 -0
- package/dist/server-router.js +68 -0
- package/dist/server-router.js.map +1 -0
- package/dist/server-types.d.ts +172 -0
- package/dist/server-types.d.ts.map +1 -0
- package/dist/server-types.js +6 -0
- package/dist/server-types.js.map +1 -0
- package/dist/server-utils.d.ts +17 -0
- package/dist/server-utils.d.ts.map +1 -0
- package/dist/server-utils.js +103 -0
- package/dist/server-utils.js.map +1 -0
- package/dist/server-websocket.d.ts +8 -0
- package/dist/server-websocket.d.ts.map +1 -0
- package/dist/server-websocket.js +56 -0
- package/dist/server-websocket.js.map +1 -0
- package/dist/server.d.ts +69 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +859 -0
- package/dist/server.js.map +1 -0
- package/dist/service.d.ts +29 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +74 -0
- package/dist/service.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module loading, layout stacking, meta merging, query caching,
|
|
3
|
+
* and GEA component rendering for the Fiyuu runtime server.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, mkdirSync, statSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
import { buildSync } from "esbuild";
|
|
9
|
+
import { QUERY_CACHE_MAX_ENTRIES, QUERY_CACHE_SWEEP_INTERVAL_MS } from "./server-router.js";
|
|
10
|
+
// ── TypeScript compilation cache ──────────────────────────────────────────────
|
|
11
|
+
const tsxCacheDir = path.join(process.cwd(), ".fiyuu", "dev", "tsx-cache");
|
|
12
|
+
const tsxCache = new Map();
|
|
13
|
+
function getCompiledPath(originalPath) {
|
|
14
|
+
const hash = Buffer.from(originalPath).toString("base64").replace(/[^a-zA-Z0-9]/g, "").slice(0, 16);
|
|
15
|
+
return path.join(tsxCacheDir, `${hash}.js`);
|
|
16
|
+
}
|
|
17
|
+
function compileTsxFile(tsxPath) {
|
|
18
|
+
const compiledPath = getCompiledPath(tsxPath);
|
|
19
|
+
// Check cache
|
|
20
|
+
const cached = tsxCache.get(tsxPath);
|
|
21
|
+
if (cached && cached === compiledPath) {
|
|
22
|
+
try {
|
|
23
|
+
const tsxStat = statSync(tsxPath);
|
|
24
|
+
const jsStat = statSync(compiledPath);
|
|
25
|
+
if (jsStat.mtimeMs >= tsxStat.mtimeMs) {
|
|
26
|
+
return compiledPath;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Cache miss, recompile
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Ensure cache directory exists
|
|
34
|
+
mkdirSync(tsxCacheDir, { recursive: true });
|
|
35
|
+
// Compile with esbuild
|
|
36
|
+
const result = buildSync({
|
|
37
|
+
entryPoints: [tsxPath],
|
|
38
|
+
bundle: false,
|
|
39
|
+
format: "esm",
|
|
40
|
+
platform: "node",
|
|
41
|
+
target: "node18",
|
|
42
|
+
jsx: "automatic",
|
|
43
|
+
jsxImportSource: "@geajs/core",
|
|
44
|
+
outfile: compiledPath,
|
|
45
|
+
sourcemap: false,
|
|
46
|
+
});
|
|
47
|
+
if (result.errors.length > 0) {
|
|
48
|
+
throw new Error(`Failed to compile ${tsxPath}: ${result.errors.map(e => e.text).join(", ")}`);
|
|
49
|
+
}
|
|
50
|
+
tsxCache.set(tsxPath, compiledPath);
|
|
51
|
+
return compiledPath;
|
|
52
|
+
}
|
|
53
|
+
// ── Dynamic module import ─────────────────────────────────────────────────────
|
|
54
|
+
export async function importModule(modulePath, mode, serverDirectory) {
|
|
55
|
+
// In production mode, use compiled .js files from server directory
|
|
56
|
+
let resolvedPath = modulePath;
|
|
57
|
+
if (mode === "start" && serverDirectory) {
|
|
58
|
+
// Convert .ts/.tsx to .js and map to server directory
|
|
59
|
+
const relativePath = path.relative(process.cwd(), modulePath);
|
|
60
|
+
if (relativePath.startsWith("app/")) {
|
|
61
|
+
const jsPath = relativePath.replace(/\.tsx?$/, ".js");
|
|
62
|
+
resolvedPath = path.join(serverDirectory, jsPath.replace(/^app\//, ""));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (mode === "dev" && (modulePath.endsWith(".tsx") || modulePath.endsWith(".ts"))) {
|
|
66
|
+
// In dev mode, compile .tsx/.ts files on the fly
|
|
67
|
+
resolvedPath = compileTsxFile(modulePath);
|
|
68
|
+
}
|
|
69
|
+
const fileUrl = pathToFileURL(resolvedPath).href;
|
|
70
|
+
try {
|
|
71
|
+
return await import(mode === "dev" ? `${fileUrl}?t=${Date.now()}` : fileUrl);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
75
|
+
const shortPath = modulePath.replace(process.cwd(), ".");
|
|
76
|
+
let hint = "";
|
|
77
|
+
if (message.includes("Cannot find package") || message.includes("ERR_MODULE_NOT_FOUND")) {
|
|
78
|
+
const match = message.match(/Cannot find (?:package|module) '([^']+)'/);
|
|
79
|
+
const missing = match ? match[1] : "a dependency";
|
|
80
|
+
hint = `\n → Missing package: "${missing}". Run \`npm install\` in the project root.`;
|
|
81
|
+
}
|
|
82
|
+
else if (message.includes("SyntaxError") || message.includes("Unexpected token")) {
|
|
83
|
+
hint = `\n → Syntax error in ${shortPath}. Check for typos or invalid TypeScript.`;
|
|
84
|
+
}
|
|
85
|
+
else if (message.includes("ERR_INVALID_URL")) {
|
|
86
|
+
hint = `\n → Invalid file path: ${shortPath}`;
|
|
87
|
+
}
|
|
88
|
+
const enhanced = new Error(`Failed to load module: ${shortPath}\n ${message}${hint}`);
|
|
89
|
+
enhanced.stack = err instanceof Error ? err.stack : undefined;
|
|
90
|
+
throw enhanced;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// ── API route resolution ──────────────────────────────────────────────────────
|
|
94
|
+
export function resolveApiRouteModule(appDirectory, pathname) {
|
|
95
|
+
const relativePath = pathname.replace(/^\//, "");
|
|
96
|
+
const normalizedRoot = path.resolve(appDirectory) + path.sep;
|
|
97
|
+
const directModule = path.resolve(appDirectory, relativePath, "route.ts");
|
|
98
|
+
if (!directModule.startsWith(normalizedRoot))
|
|
99
|
+
return null; // path traversal guard
|
|
100
|
+
if (existsSync(directModule))
|
|
101
|
+
return directModule;
|
|
102
|
+
const rootModule = path.resolve(appDirectory, relativePath + ".ts");
|
|
103
|
+
if (!rootModule.startsWith(normalizedRoot))
|
|
104
|
+
return null; // path traversal guard
|
|
105
|
+
return existsSync(rootModule) ? rootModule : null;
|
|
106
|
+
}
|
|
107
|
+
// ── Meta loading & merging ────────────────────────────────────────────────────
|
|
108
|
+
export async function loadMetaFile(filePath, mode, serverDirectory) {
|
|
109
|
+
if (!existsSync(filePath)) {
|
|
110
|
+
return { intent: "" };
|
|
111
|
+
}
|
|
112
|
+
const module = (await importModule(filePath, mode, serverDirectory));
|
|
113
|
+
return module.default ?? { intent: "" };
|
|
114
|
+
}
|
|
115
|
+
export async function loadLayoutMeta(directory, mode, serverDirectory) {
|
|
116
|
+
return loadMetaFile(path.join(directory, "layout.meta.ts"), mode, serverDirectory);
|
|
117
|
+
}
|
|
118
|
+
export function mergeMetaDefinitions(...definitions) {
|
|
119
|
+
return definitions.reduce((current, item) => ({
|
|
120
|
+
...current,
|
|
121
|
+
...item,
|
|
122
|
+
seo: {
|
|
123
|
+
...current.seo,
|
|
124
|
+
...item.seo,
|
|
125
|
+
},
|
|
126
|
+
}), { intent: "" });
|
|
127
|
+
}
|
|
128
|
+
export async function loadLayoutStack(appDirectory, feature, mode, serverDirectory, providers) {
|
|
129
|
+
const parts = feature.feature ? feature.feature.split("/") : [];
|
|
130
|
+
const directories = [appDirectory];
|
|
131
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
132
|
+
directories.push(path.join(appDirectory, ...parts.slice(0, index + 1)));
|
|
133
|
+
}
|
|
134
|
+
const stack = [];
|
|
135
|
+
// First, load global providers (if any)
|
|
136
|
+
if (providers && providers.length > 0) {
|
|
137
|
+
for (const provider of providers.filter((p) => p.target === "global" || p.target === "layout")) {
|
|
138
|
+
try {
|
|
139
|
+
const module = (await importModule(provider.filePath, mode, serverDirectory));
|
|
140
|
+
const component = module.default ?? module.Provider;
|
|
141
|
+
if (component) {
|
|
142
|
+
stack.push({
|
|
143
|
+
component,
|
|
144
|
+
meta: { intent: provider.intent ?? `${provider.name} provider` },
|
|
145
|
+
isProvider: true,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
console.warn(`[fiyuu] Failed to load provider ${provider.name}:`, err);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Then load layouts
|
|
155
|
+
for (const directory of directories) {
|
|
156
|
+
const layoutFile = path.join(directory, "layout.tsx");
|
|
157
|
+
const metaFile = path.join(directory, "layout.meta.ts");
|
|
158
|
+
if (existsSync(layoutFile)) {
|
|
159
|
+
const module = (await importModule(layoutFile, mode, serverDirectory));
|
|
160
|
+
if (module.default) {
|
|
161
|
+
stack.push({ component: module.default, meta: await loadMetaFile(metaFile, mode, serverDirectory) });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Finally, load page-specific providers
|
|
166
|
+
if (providers && providers.length > 0) {
|
|
167
|
+
for (const provider of providers.filter((p) => p.target === "page")) {
|
|
168
|
+
try {
|
|
169
|
+
const module = (await importModule(provider.filePath, mode, serverDirectory));
|
|
170
|
+
const component = module.default ?? module.Provider;
|
|
171
|
+
if (component) {
|
|
172
|
+
stack.push({
|
|
173
|
+
component,
|
|
174
|
+
meta: { intent: provider.intent ?? `${provider.name} provider` },
|
|
175
|
+
isProvider: true,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
console.warn(`[fiyuu] Failed to load provider ${provider.name}:`, err);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return stack;
|
|
185
|
+
}
|
|
186
|
+
export async function loadFeatureMeta(feature, mode, serverDirectory) {
|
|
187
|
+
return feature.files["meta.ts"]
|
|
188
|
+
? loadMetaFile(feature.files["meta.ts"], mode, serverDirectory)
|
|
189
|
+
: { intent: feature.intent ?? "" };
|
|
190
|
+
}
|
|
191
|
+
// ── Cached layout stack (production only) ────────────────────────────────────
|
|
192
|
+
export async function getCachedLayoutStack(state, appDirectory, feature, mode, providers) {
|
|
193
|
+
const cached = state.layoutStackCache.get(feature.route);
|
|
194
|
+
if (cached)
|
|
195
|
+
return cached;
|
|
196
|
+
const layoutStack = await loadLayoutStack(appDirectory, feature, mode, state.serverDirectory, providers);
|
|
197
|
+
state.layoutStackCache.set(feature.route, layoutStack);
|
|
198
|
+
return layoutStack;
|
|
199
|
+
}
|
|
200
|
+
export async function getCachedMergedMeta(state, feature, layoutStack, mode) {
|
|
201
|
+
const cached = state.mergedMetaCache.get(feature.route);
|
|
202
|
+
if (cached)
|
|
203
|
+
return cached;
|
|
204
|
+
let featureMeta = state.featureMetaCache.get(feature.route);
|
|
205
|
+
if (!featureMeta) {
|
|
206
|
+
featureMeta = await loadFeatureMeta(feature, mode, state.serverDirectory);
|
|
207
|
+
state.featureMetaCache.set(feature.route, featureMeta);
|
|
208
|
+
}
|
|
209
|
+
const merged = mergeMetaDefinitions(...layoutStack.map((item) => item.meta), featureMeta);
|
|
210
|
+
state.mergedMetaCache.set(feature.route, merged);
|
|
211
|
+
return merged;
|
|
212
|
+
}
|
|
213
|
+
// ── Query cache ───────────────────────────────────────────────────────────────
|
|
214
|
+
export function pruneQueryCache(state, now) {
|
|
215
|
+
if (now - state.queryCacheLastPruneAt < QUERY_CACHE_SWEEP_INTERVAL_MS &&
|
|
216
|
+
state.queryCache.size < QUERY_CACHE_MAX_ENTRIES) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
state.queryCacheLastPruneAt = now;
|
|
220
|
+
for (const [key, entry] of state.queryCache) {
|
|
221
|
+
if (entry.expiresAt <= now) {
|
|
222
|
+
state.queryCache.delete(key);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (state.queryCache.size <= QUERY_CACHE_MAX_ENTRIES)
|
|
226
|
+
return;
|
|
227
|
+
const survivors = [...state.queryCache.entries()].sort((left, right) => left[1].expiresAt - right[1].expiresAt);
|
|
228
|
+
const overflowCount = survivors.length - QUERY_CACHE_MAX_ENTRIES;
|
|
229
|
+
for (let index = 0; index < overflowCount; index += 1) {
|
|
230
|
+
state.queryCache.delete(survivors[index][0]);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// ── GEA component rendering ───────────────────────────────────────────────────
|
|
234
|
+
const geaComponentModeCache = new WeakMap();
|
|
235
|
+
export function renderGeaComponent(component, props) {
|
|
236
|
+
if (typeof component !== "function") {
|
|
237
|
+
throw new Error("Route module default export must be a Gea component class or function.");
|
|
238
|
+
}
|
|
239
|
+
const candidate = component;
|
|
240
|
+
const componentKey = candidate;
|
|
241
|
+
const cachedMode = geaComponentModeCache.get(componentKey);
|
|
242
|
+
const mode = cachedMode ?? (typeof candidate.prototype?.template === "function" ? "class" : "function");
|
|
243
|
+
if (!cachedMode) {
|
|
244
|
+
geaComponentModeCache.set(componentKey, mode);
|
|
245
|
+
}
|
|
246
|
+
if (mode === "class") {
|
|
247
|
+
const instance = new candidate(props);
|
|
248
|
+
if (typeof instance.template === "function") {
|
|
249
|
+
return String(instance.template(instance.props ?? props));
|
|
250
|
+
}
|
|
251
|
+
return String(instance.toString());
|
|
252
|
+
}
|
|
253
|
+
return String(candidate(props));
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=server-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-loader.js","sourceRoot":"","sources":["../src/server-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware runner for the Fiyuu runtime server.
|
|
3
|
+
* Loads the app-level middleware.ts module and chains handlers.
|
|
4
|
+
*/
|
|
5
|
+
import type { IncomingMessage } from "node:http";
|
|
6
|
+
import type { MiddlewareResult, StartServerOptions } from "./server-types.js";
|
|
7
|
+
export declare function runMiddleware(options: StartServerOptions, url: URL, request: IncomingMessage, mode: "dev" | "start", stateWarnings?: string[], requestId?: string, serverDirectory?: string): Promise<MiddlewareResult | undefined>;
|
|
8
|
+
//# sourceMappingURL=server-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-middleware.d.ts","sourceRoot":"","sources":["../src/server-middleware.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAIV,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAG3B,wBAAsB,aAAa,CACjC,OAAO,EAAE,kBAAkB,EAC3B,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,KAAK,GAAG,OAAO,EACrB,aAAa,GAAE,MAAM,EAAO,EAC5B,SAAS,SAAK,EACd,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CA8CvC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware runner for the Fiyuu runtime server.
|
|
3
|
+
* Loads the app-level middleware.ts module and chains handlers.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { importModule } from "./server-loader.js";
|
|
8
|
+
export async function runMiddleware(options, url, request, mode, stateWarnings = [], requestId = "", serverDirectory) {
|
|
9
|
+
if (options.config?.middleware?.enabled === false) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const middlewarePath = path.join(options.appDirectory, "middleware.ts");
|
|
13
|
+
if (!existsSync(middlewarePath)) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const module = (await importModule(middlewarePath, mode, serverDirectory));
|
|
17
|
+
const handlers = Array.isArray(module.middleware)
|
|
18
|
+
? module.middleware
|
|
19
|
+
: module.middleware
|
|
20
|
+
? [module.middleware]
|
|
21
|
+
: [];
|
|
22
|
+
const context = {
|
|
23
|
+
request,
|
|
24
|
+
url,
|
|
25
|
+
responseHeaders: {},
|
|
26
|
+
requestId,
|
|
27
|
+
warnings: stateWarnings,
|
|
28
|
+
};
|
|
29
|
+
let shortCircuit;
|
|
30
|
+
let index = -1;
|
|
31
|
+
async function dispatch(position) {
|
|
32
|
+
if (position <= index) {
|
|
33
|
+
throw new Error("Middleware next() called multiple times.");
|
|
34
|
+
}
|
|
35
|
+
index = position;
|
|
36
|
+
const handler = handlers[position];
|
|
37
|
+
if (!handler)
|
|
38
|
+
return;
|
|
39
|
+
const result = await handler(context, async () => dispatch(position + 1));
|
|
40
|
+
if (result?.headers) {
|
|
41
|
+
Object.assign(context.responseHeaders, result.headers);
|
|
42
|
+
}
|
|
43
|
+
if (result?.response) {
|
|
44
|
+
shortCircuit = result.response;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
await dispatch(0);
|
|
48
|
+
return { headers: context.responseHeaders, response: shortCircuit };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=server-middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-middleware.js","sourceRoot":"","sources":["../src/server-middleware.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAS7B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAA2B,EAC3B,GAAQ,EACR,OAAwB,EACxB,IAAqB,EACrB,gBAA0B,EAAE,EAC5B,SAAS,GAAG,EAAE,EACd,eAAwB;IAExB,IAAI,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;QAClD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IACxE,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAChC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,MAAM,YAAY,CAAC,cAAc,EAAE,IAAI,EAAE,eAAe,CAAC,CAAqB,CAAC;IAC/F,MAAM,QAAQ,GAAwB,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;QACpE,CAAC,CAAC,MAAM,CAAC,UAAU;QACnB,CAAC,CAAC,MAAM,CAAC,UAAU;YACjB,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;YACrB,CAAC,CAAC,EAAE,CAAC;IAET,MAAM,OAAO,GAAsB;QACjC,OAAO;QACP,GAAG;QACH,eAAe,EAAE,EAAE;QACnB,SAAS;QACT,QAAQ,EAAE,aAAa;KACxB,CAAC;IACF,IAAI,YAA0C,CAAC;IAC/C,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;IAEf,KAAK,UAAU,QAAQ,CAAC,QAAgB;QACtC,IAAI,QAAQ,IAAI,KAAK,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC9D,CAAC;QACD,KAAK,GAAG,QAAQ,CAAC;QACjB,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1E,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,MAAM,EAAE,QAAQ,EAAE,CAAC;YACrB,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjC,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClB,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Private assets management for Fiyuu.
|
|
3
|
+
*
|
|
4
|
+
* The private/ directory contains files that should only be accessible
|
|
5
|
+
* server-side. These files are NEVER served to the client via HTTP.
|
|
6
|
+
* They can only be accessed through server-side code (actions, queries, services).
|
|
7
|
+
*/
|
|
8
|
+
import type { ServerResponse } from "node:http";
|
|
9
|
+
export interface PrivateAsset {
|
|
10
|
+
/** Asset name/path relative to private directory */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Full file path */
|
|
13
|
+
filePath: string;
|
|
14
|
+
/** File size in bytes */
|
|
15
|
+
size: number;
|
|
16
|
+
/** MIME type */
|
|
17
|
+
mimeType: string;
|
|
18
|
+
/** Last modified timestamp */
|
|
19
|
+
modifiedAt: Date;
|
|
20
|
+
}
|
|
21
|
+
export interface PrivateDirectoryConfig {
|
|
22
|
+
/** Root directory for private assets */
|
|
23
|
+
rootPath: string;
|
|
24
|
+
/** Allowed MIME types (undefined = allow all) */
|
|
25
|
+
allowedMimeTypes?: string[];
|
|
26
|
+
/** Maximum file size in bytes (default: 100MB) */
|
|
27
|
+
maxFileSize?: number;
|
|
28
|
+
/** Whether to enable server-side caching */
|
|
29
|
+
enableCache?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get MIME type for a file based on extension.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getPrivateAssetMimeType(filePath: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Check if a file is allowed to be accessed from private directory.
|
|
37
|
+
* Blocks access to sensitive paths.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isAllowedPrivatePath(filePath: string, rootPath: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Read a private asset as string (for text files).
|
|
42
|
+
* This can only be called from server-side code.
|
|
43
|
+
*/
|
|
44
|
+
export declare function readPrivateAsset(rootPath: string, assetName: string, encoding?: BufferEncoding): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Read a private asset as Buffer (for binary files).
|
|
47
|
+
* This can only be called from server-side code.
|
|
48
|
+
*/
|
|
49
|
+
export declare function readPrivateAssetBuffer(rootPath: string, assetName: string): Promise<Buffer>;
|
|
50
|
+
/**
|
|
51
|
+
* Get metadata about a private asset without reading content.
|
|
52
|
+
*/
|
|
53
|
+
export declare function getPrivateAssetInfo(rootPath: string, assetName: string): Promise<PrivateAsset | null>;
|
|
54
|
+
/**
|
|
55
|
+
* List all assets in the private directory.
|
|
56
|
+
*/
|
|
57
|
+
export declare function listPrivateAssets(rootPath: string, subDirectory?: string): Promise<PrivateAsset[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Check if a request is trying to access private assets via HTTP.
|
|
60
|
+
* This should be blocked - private assets are server-side only.
|
|
61
|
+
*/
|
|
62
|
+
export declare function isPrivatePathRequest(pathname: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Block a request trying to access private assets.
|
|
65
|
+
* Returns true if request was blocked.
|
|
66
|
+
*/
|
|
67
|
+
export declare function blockPrivateAccess(response: ServerResponse, pathname: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Helper to safely access private assets in server code.
|
|
70
|
+
* Returns null if asset doesn't exist or access is denied.
|
|
71
|
+
*/
|
|
72
|
+
export declare function safeReadPrivateAsset<T = string>(rootPath: string, assetName: string, parser?: (content: string) => T): Promise<T | null>;
|
|
73
|
+
/**
|
|
74
|
+
* Parse a JSON private asset.
|
|
75
|
+
*/
|
|
76
|
+
export declare function readPrivateJson<T = unknown>(rootPath: string, assetName: string): Promise<T | null>;
|
|
77
|
+
/**
|
|
78
|
+
* Parse a CSV private asset into array of objects.
|
|
79
|
+
*/
|
|
80
|
+
export declare function readPrivateCsv(rootPath: string, assetName: string): Promise<Record<string, string>[] | null>;
|
|
81
|
+
//# sourceMappingURL=server-private.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-private.d.ts","sourceRoot":"","sources":["../src/server-private.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGhD,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA4BD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CA2BhF;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,cAAuB,GAChC,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAoB9B;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,YAAY,GAAE,MAAW,GACxB,OAAO,CAAC,YAAY,EAAE,CAAC,CAczB;AAiCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAS9D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAeT;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,GAAG,MAAM,EACnD,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,CAAC,GAC9B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAOnB;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,CAAC,GAAG,OAAO,EAC/C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAEnB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,CAc1C"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Private assets management for Fiyuu.
|
|
3
|
+
*
|
|
4
|
+
* The private/ directory contains files that should only be accessible
|
|
5
|
+
* server-side. These files are NEVER served to the client via HTTP.
|
|
6
|
+
* They can only be accessed through server-side code (actions, queries, services).
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { promises as fs } from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
const DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
|
|
12
|
+
const MIME_TYPES = {
|
|
13
|
+
".json": "application/json",
|
|
14
|
+
".csv": "text/csv",
|
|
15
|
+
".xml": "application/xml",
|
|
16
|
+
".pdf": "application/pdf",
|
|
17
|
+
".txt": "text/plain",
|
|
18
|
+
".md": "text/markdown",
|
|
19
|
+
".yaml": "application/yaml",
|
|
20
|
+
".yml": "application/yaml",
|
|
21
|
+
".env": "text/plain",
|
|
22
|
+
".key": "text/plain",
|
|
23
|
+
".pem": "text/plain",
|
|
24
|
+
".crt": "text/plain",
|
|
25
|
+
".p12": "application/x-pkcs12",
|
|
26
|
+
".pfx": "application/x-pkcs12",
|
|
27
|
+
".sqlite": "application/x-sqlite3",
|
|
28
|
+
".db": "application/x-sqlite3",
|
|
29
|
+
".zip": "application/zip",
|
|
30
|
+
".tar": "application/x-tar",
|
|
31
|
+
".gz": "application/gzip",
|
|
32
|
+
".sql": "text/plain",
|
|
33
|
+
".log": "text/plain",
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Get MIME type for a file based on extension.
|
|
37
|
+
*/
|
|
38
|
+
export function getPrivateAssetMimeType(filePath) {
|
|
39
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
40
|
+
return MIME_TYPES[ext] ?? "application/octet-stream";
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if a file is allowed to be accessed from private directory.
|
|
44
|
+
* Blocks access to sensitive paths.
|
|
45
|
+
*/
|
|
46
|
+
export function isAllowedPrivatePath(filePath, rootPath) {
|
|
47
|
+
const resolvedPath = path.resolve(filePath);
|
|
48
|
+
const resolvedRoot = path.resolve(rootPath);
|
|
49
|
+
// Path traversal protection
|
|
50
|
+
if (!resolvedPath.startsWith(resolvedRoot)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
// Block access to node_modules
|
|
54
|
+
if (resolvedPath.includes("node_modules")) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
// Block access to .git
|
|
58
|
+
if (resolvedPath.includes(".git")) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
// Block access to .env files directly (should use config instead)
|
|
62
|
+
const basename = path.basename(resolvedPath);
|
|
63
|
+
if (basename.startsWith(".env") && !basename.endsWith(".example")) {
|
|
64
|
+
// Allow reading but log warning - actual env should be in .fiyuu/
|
|
65
|
+
console.warn(`[fiyuu] Warning: Accessing .env file from private/: ${basename}`);
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Read a private asset as string (for text files).
|
|
71
|
+
* This can only be called from server-side code.
|
|
72
|
+
*/
|
|
73
|
+
export async function readPrivateAsset(rootPath, assetName, encoding = "utf8") {
|
|
74
|
+
const filePath = path.join(rootPath, assetName);
|
|
75
|
+
if (!isAllowedPrivatePath(filePath, rootPath)) {
|
|
76
|
+
throw new Error(`Access denied to private asset: ${assetName}`);
|
|
77
|
+
}
|
|
78
|
+
if (!existsSync(filePath)) {
|
|
79
|
+
throw new Error(`Private asset not found: ${assetName}`);
|
|
80
|
+
}
|
|
81
|
+
return fs.readFile(filePath, encoding);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Read a private asset as Buffer (for binary files).
|
|
85
|
+
* This can only be called from server-side code.
|
|
86
|
+
*/
|
|
87
|
+
export async function readPrivateAssetBuffer(rootPath, assetName) {
|
|
88
|
+
const filePath = path.join(rootPath, assetName);
|
|
89
|
+
if (!isAllowedPrivatePath(filePath, rootPath)) {
|
|
90
|
+
throw new Error(`Access denied to private asset: ${assetName}`);
|
|
91
|
+
}
|
|
92
|
+
if (!existsSync(filePath)) {
|
|
93
|
+
throw new Error(`Private asset not found: ${assetName}`);
|
|
94
|
+
}
|
|
95
|
+
return fs.readFile(filePath);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get metadata about a private asset without reading content.
|
|
99
|
+
*/
|
|
100
|
+
export async function getPrivateAssetInfo(rootPath, assetName) {
|
|
101
|
+
const filePath = path.join(rootPath, assetName);
|
|
102
|
+
if (!isAllowedPrivatePath(filePath, rootPath)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
if (!existsSync(filePath)) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const stats = await fs.stat(filePath);
|
|
109
|
+
return {
|
|
110
|
+
name: assetName,
|
|
111
|
+
filePath: normalizePath(filePath),
|
|
112
|
+
size: stats.size,
|
|
113
|
+
mimeType: getPrivateAssetMimeType(filePath),
|
|
114
|
+
modifiedAt: stats.mtime,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* List all assets in the private directory.
|
|
119
|
+
*/
|
|
120
|
+
export async function listPrivateAssets(rootPath, subDirectory = "") {
|
|
121
|
+
const targetDir = path.join(rootPath, subDirectory);
|
|
122
|
+
if (!isAllowedPrivatePath(targetDir, rootPath)) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
if (!existsSync(targetDir)) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
const assets = [];
|
|
129
|
+
await walkPrivateDirectory(rootPath, targetDir, assets);
|
|
130
|
+
return assets;
|
|
131
|
+
}
|
|
132
|
+
async function walkPrivateDirectory(rootPath, currentDir, assets) {
|
|
133
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
134
|
+
for (const entry of entries) {
|
|
135
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
136
|
+
if (entry.isDirectory()) {
|
|
137
|
+
// Skip hidden directories and node_modules
|
|
138
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
await walkPrivateDirectory(rootPath, fullPath, assets);
|
|
142
|
+
}
|
|
143
|
+
else if (entry.isFile()) {
|
|
144
|
+
const stats = await fs.stat(fullPath);
|
|
145
|
+
const relativeName = path.relative(rootPath, fullPath);
|
|
146
|
+
assets.push({
|
|
147
|
+
name: normalizePath(relativeName),
|
|
148
|
+
filePath: normalizePath(fullPath),
|
|
149
|
+
size: stats.size,
|
|
150
|
+
mimeType: getPrivateAssetMimeType(fullPath),
|
|
151
|
+
modifiedAt: stats.mtime,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Check if a request is trying to access private assets via HTTP.
|
|
158
|
+
* This should be blocked - private assets are server-side only.
|
|
159
|
+
*/
|
|
160
|
+
export function isPrivatePathRequest(pathname) {
|
|
161
|
+
// Block any path starting with /private/ or containing /private/
|
|
162
|
+
const normalizedPath = pathname.replace(/\\/g, "/");
|
|
163
|
+
return (normalizedPath.startsWith("/private/") ||
|
|
164
|
+
normalizedPath === "/private" ||
|
|
165
|
+
normalizedPath.includes("/../private/") ||
|
|
166
|
+
normalizedPath.includes("/./private/"));
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Block a request trying to access private assets.
|
|
170
|
+
* Returns true if request was blocked.
|
|
171
|
+
*/
|
|
172
|
+
export function blockPrivateAccess(response, pathname) {
|
|
173
|
+
if (!isPrivatePathRequest(pathname)) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
response.statusCode = 403;
|
|
177
|
+
response.setHeader("content-type", "text/plain; charset=utf-8");
|
|
178
|
+
response.setHeader("x-fiyuu-private-blocked", "true");
|
|
179
|
+
response.end(`Access denied: Private assets cannot be accessed via HTTP.\n` +
|
|
180
|
+
`Path: ${pathname}\n\n` +
|
|
181
|
+
`Private assets are server-side only. ` +
|
|
182
|
+
`Use server actions, queries, or services to access them.`);
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Helper to safely access private assets in server code.
|
|
187
|
+
* Returns null if asset doesn't exist or access is denied.
|
|
188
|
+
*/
|
|
189
|
+
export async function safeReadPrivateAsset(rootPath, assetName, parser) {
|
|
190
|
+
try {
|
|
191
|
+
const content = await readPrivateAsset(rootPath, assetName);
|
|
192
|
+
return parser ? parser(content) : content;
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Parse a JSON private asset.
|
|
200
|
+
*/
|
|
201
|
+
export async function readPrivateJson(rootPath, assetName) {
|
|
202
|
+
return safeReadPrivateAsset(rootPath, assetName, (content) => JSON.parse(content));
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Parse a CSV private asset into array of objects.
|
|
206
|
+
*/
|
|
207
|
+
export async function readPrivateCsv(rootPath, assetName) {
|
|
208
|
+
return safeReadPrivateAsset(rootPath, assetName, (content) => {
|
|
209
|
+
const lines = content.trim().split("\n");
|
|
210
|
+
if (lines.length === 0)
|
|
211
|
+
return [];
|
|
212
|
+
const headers = lines[0].split(",").map((h) => h.trim());
|
|
213
|
+
return lines.slice(1).map((line) => {
|
|
214
|
+
const values = line.split(",").map((v) => v.trim());
|
|
215
|
+
return headers.reduce((obj, header, i) => {
|
|
216
|
+
obj[header] = values[i] ?? "";
|
|
217
|
+
return obj;
|
|
218
|
+
}, {});
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function normalizePath(filePath) {
|
|
223
|
+
return filePath.split(path.sep).join("/");
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=server-private.js.map
|