fluxion-ts 0.0.5 → 0.0.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxion-ts",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "author": {
5
5
  "name": "Kasukabe Tsumugi",
6
6
  "email": "futami16237@gmail.com"
@@ -9,6 +9,11 @@
9
9
  "description": "Fluxion is able to start a server that can dynamically load routers and js files. It is designed to be used to replace PHP",
10
10
  "description_zh": "Fluxion可以启动一个能够动态加载路由和js文件的服务器,旨在替代PHP",
11
11
  "type": "module",
12
+ "exports": {
13
+ "import": "./index.mjs",
14
+ "default": "./index.mjs",
15
+ "types": "./index.d.ts"
16
+ },
12
17
  "homepage": "https://github.com/baendlorel/fluxion#readme",
13
18
  "repository": {
14
19
  "type": "git",
@@ -131,11 +131,7 @@ function normalizeDatabaseRuntimeConfigItem(
131
131
  }
132
132
 
133
133
  const rawDriver =
134
- typeof input.driver === 'string'
135
- ? input.driver
136
- : typeof input.type === 'string'
137
- ? input.type
138
- : undefined;
134
+ typeof input.driver === 'string' ? input.driver : typeof input.type === 'string' ? input.type : undefined;
139
135
 
140
136
  if (rawDriver === undefined) {
141
137
  throw new Error(`Missing db driver for "${dbName}" in ${source}`);
@@ -209,11 +205,7 @@ function normalizeDatabaseConfigMap(input: unknown, source: string): protocol.Wo
209
205
  throw new Error(`Invalid db config for "${name}" in "${source}"`);
210
206
  }
211
207
 
212
- normalized[name] = normalizeDatabaseRuntimeConfigItem(
213
- name,
214
- rawConfig as FluxionDatabaseRuntimeConfigInput,
215
- source,
216
- );
208
+ normalized[name] = normalizeDatabaseRuntimeConfigItem(name, rawConfig as FluxionDatabaseRuntimeConfigInput, source);
217
209
  }
218
210
 
219
211
  return normalized;
@@ -372,7 +364,7 @@ export function fluxion(options: FluxionOptions): http.Server {
372
364
 
373
365
  const bodyCapture = createBodyPreviewCapture(req);
374
366
 
375
- logger.write('INFO', 'RequestReceived', { method, ip, path: url.pathname });
367
+ logger.write('INFO', 'Req', { method, ip, path: url.pathname });
376
368
 
377
369
  const start = performance.now();
378
370
  res.once('finish', () => {
@@ -395,7 +387,7 @@ export function fluxion(options: FluxionOptions): http.Server {
395
387
  fields.bodyTruncated = bodyPreview.truncated;
396
388
  }
397
389
 
398
- logger.write('INFO', 'RequestCompleted', fields);
390
+ logger.write('INFO', 'Res', fields);
399
391
  });
400
392
 
401
393
  void metaApi
@@ -532,6 +532,25 @@ function buildHandlerCandidates(dynamicDirectory: string, segments: readonly str
532
532
  return [path.resolve(routePath, 'index.mjs'), `${routePath}.mjs`];
533
533
  }
534
534
 
535
+ /**
536
+ * Builds ordered static file candidates for one route.
537
+ */
538
+ function buildStaticCandidates(dynamicDirectory: string, segments: readonly string[]): string[] {
539
+ if (segments.length === 0) {
540
+ return [path.resolve(dynamicDirectory, 'index.html')];
541
+ }
542
+
543
+ const routePath = path.resolve(dynamicDirectory, ...segments);
544
+ const candidates = [routePath];
545
+ const lastSegment = segments[segments.length - 1];
546
+
547
+ if (path.extname(lastSegment).length === 0) {
548
+ candidates.push(path.resolve(routePath, 'index.html'));
549
+ }
550
+
551
+ return candidates;
552
+ }
553
+
535
554
  /**
536
555
  * Streams static file to response.
537
556
  */
@@ -893,38 +912,40 @@ export function createFileRuntime(dir: string, options: FileRuntimeOptions = {})
893
912
  return HandlerResult.NotFound;
894
913
  }
895
914
 
896
- if (parsedPath.segments.length === 0) {
897
- return HandlerResult.NotFound;
898
- }
915
+ const candidates = buildStaticCandidates(dir, parsedPath.segments);
899
916
 
900
- const filePath = path.resolve(dir, ...parsedPath.segments);
917
+ for (let i = 0; i < candidates.length; i++) {
918
+ const filePath = candidates[i];
901
919
 
902
- if (!isUnderDirectory(filePath, dir)) {
903
- return HandlerResult.NotFound;
904
- }
920
+ if (!isUnderDirectory(filePath, dir)) {
921
+ continue;
922
+ }
905
923
 
906
- if (path.extname(filePath).toLowerCase() === '.mjs') {
907
- return HandlerResult.NotFound;
908
- }
924
+ if (path.extname(filePath).toLowerCase() === '.mjs') {
925
+ continue;
926
+ }
909
927
 
910
- try {
911
- const stat = await fs.promises.stat(filePath);
928
+ try {
929
+ const stat = await fs.promises.stat(filePath);
912
930
 
913
- if (!stat.isFile()) {
914
- return HandlerResult.NotFound;
915
- }
931
+ if (!stat.isFile()) {
932
+ continue;
933
+ }
916
934
 
917
- await streamStaticFile(filePath, stat, method, res);
918
- return HandlerResult.Handled;
919
- } catch (error) {
920
- const code = (error as NodeJS.ErrnoException).code;
935
+ await streamStaticFile(filePath, stat, method, res);
936
+ return HandlerResult.Handled;
937
+ } catch (error) {
938
+ const code = (error as NodeJS.ErrnoException).code;
921
939
 
922
- if (code === 'ENOENT' || code === 'ENOTDIR') {
923
- return HandlerResult.NotFound;
924
- }
940
+ if (code === 'ENOENT' || code === 'ENOTDIR') {
941
+ continue;
942
+ }
925
943
 
926
- throw error;
944
+ throw error;
945
+ }
927
946
  }
947
+
948
+ return HandlerResult.NotFound;
928
949
  };
929
950
 
930
951
  /**
@@ -169,6 +169,33 @@ describe('file-runtime', () => {
169
169
  expect(await hiddenStaticResponse.text()).toBe('not_found');
170
170
  });
171
171
 
172
+ it('falls back to route index.html when direct file is not matched', async () => {
173
+ const dynamicDirectory = await createTempDirectory('fluxion-runtime-static-index-');
174
+ tempDirectories.push(dynamicDirectory);
175
+
176
+ await writeFile(
177
+ path.join(dynamicDirectory, 'docs', 'index.html'),
178
+ '<html><body><h1>docs-home</h1></body></html>',
179
+ );
180
+ await writeFile(
181
+ path.join(dynamicDirectory, 'index.html'),
182
+ '<html><body><h1>root-home</h1></body></html>',
183
+ );
184
+
185
+ const { server, baseUrl } = await startRuntimeServer(dynamicDirectory);
186
+ servers.push(server);
187
+
188
+ const docsResponse = await fetch(`${baseUrl}/docs`);
189
+ expect(docsResponse.status).toBe(200);
190
+ expect(docsResponse.headers.get('content-type')).toContain('text/html');
191
+ expect(await docsResponse.text()).toContain('docs-home');
192
+
193
+ const rootResponse = await fetch(`${baseUrl}/`);
194
+ expect(rootResponse.status).toBe(200);
195
+ expect(rootResponse.headers.get('content-type')).toContain('text/html');
196
+ expect(await rootResponse.text()).toContain('root-home');
197
+ });
198
+
172
199
  it('creates route snapshot from .mjs handlers and static files', async () => {
173
200
  const dynamicDirectory = await createTempDirectory('fluxion-runtime-snapshot-');
174
201
  tempDirectories.push(dynamicDirectory);
package/Dockerfile DELETED
@@ -1,13 +0,0 @@
1
- FROM node:24
2
- WORKDIR /app
3
- COPY package.json pnpm-lock.yaml ./
4
- RUN npm install -g pnpm && pnpm install --frozen-lockfile
5
- RUN pnpm install
6
- COPY ./dist /app/
7
- WORKDIR /app
8
-
9
- RUN curl -fsSL https://code-server.dev/install.sh | sh
10
-
11
- ENV NODE_ENV=production
12
- EXPOSE 3000
13
- CMD ["pnpm", "start"]
@@ -1,13 +0,0 @@
1
- import { execSync } from 'node:child_process';
2
- import { readFileSync } from 'node:fs';
3
- import path from 'node:path';
4
- import { bumpVersion } from './bump-version';
5
-
6
- const pkgPath = path.resolve(import.meta.dirname, '..', '.image.json');
7
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as { name: string; version: string };
8
- const imageName = `${pkg.name}:${pkg.version}`;
9
-
10
- execSync('pnpm build', { stdio: 'inherit' });
11
- execSync(`docker build -t ${imageName} .`, { stdio: 'inherit' });
12
-
13
- bumpVersion(pkgPath);
@@ -1,79 +0,0 @@
1
- import path from 'node:path';
2
- import fs from 'node:fs';
3
-
4
- import type { RollupReplaceOptions } from '@rollup/plugin-replace';
5
-
6
- export const getTSConfig = () => {
7
- const tsconfigBuildPath = path.join(import.meta.dirname, '..', 'tsconfig.build.json');
8
- const tsconfigPath = path.join(import.meta.dirname, '..', 'tsconfig.json');
9
- return fs.existsSync(tsconfigBuildPath) ? tsconfigBuildPath : tsconfigPath;
10
- };
11
-
12
- // #region replace options
13
-
14
- interface CommonPackageJson {
15
- name: string;
16
- version: string;
17
- description: string;
18
- description_zh: string;
19
- author: {
20
- name: string;
21
- email: string;
22
- };
23
- license: string;
24
- repository: {
25
- type: string;
26
- url: string;
27
- };
28
- }
29
-
30
- export function replaceOpts(): RollupReplaceOptions {
31
- const pkg = JSON.parse(
32
- fs.readFileSync(path.join(import.meta.dirname, '..', 'package.json'), 'utf-8'),
33
- ) as CommonPackageJson;
34
- function formatDateFull(dt = new Date()) {
35
- const y = dt.getFullYear();
36
- const m = String(dt.getMonth() + 1).padStart(2, '0');
37
- const d = String(dt.getDate()).padStart(2, '0');
38
- const hh = String(dt.getHours()).padStart(2, '0');
39
- const mm = String(dt.getMinutes()).padStart(2, '0');
40
- const ss = String(dt.getSeconds()).padStart(2, '0');
41
- const ms = String(dt.getMilliseconds()).padStart(3, '0');
42
- return `${y}.${m}.${d} ${hh}:${mm}:${ss}.${ms}`;
43
- }
44
-
45
- const __KEBAB_NAME__ = pkg.name.replace('rollup-plugin-', '');
46
- const __VERSION__ = pkg.version;
47
- const __NAME__ = __KEBAB_NAME__.replace(/(^|-)(\w)/g, (_, __, c) => c.toUpperCase());
48
-
49
- const __PKG_INFO__ = `## About
50
- * @package ${__NAME__}
51
- * @author ${pkg.author.name} <${pkg.author.email}>
52
- * @version ${pkg.version} (Last Update: ${formatDateFull()})
53
- * @license ${pkg.license}
54
- * @link ${pkg.repository.url}
55
- * @link https://baendlorel.github.io/ Welcome to my site!
56
- * @description ${pkg.description.replace(/\n/g, '\n * \n * ')}
57
- * @copyright Copyright (c) ${new Date().getFullYear()} ${pkg.author.name}. All rights reserved.`;
58
-
59
- return {
60
- preventAssignment: true,
61
- delimiters: ['', ''],
62
- values: {
63
- __IS_DEV__: 'false',
64
- __NAME__,
65
- __KEBAB_NAME__,
66
- __PKG_INFO__,
67
- __VERSION__,
68
-
69
- // global error/warn/debug
70
- "$throw('": `throw new Error('[${__NAME__} error] `,
71
- '$throw(`': `throw new Error(\`[${__NAME__} error] `,
72
- '$throw("': `throw new Error("[${__NAME__} error] `,
73
- '$warn(': `console.warn('[${__NAME__} warn]',`,
74
- '$error(': `console.error('[${__NAME__} error]',`,
75
- '$debug(': `console.debug('[${__NAME__} debug]',`,
76
- },
77
- };
78
- }
79
- // #endregion