@fastang/core 0.1.0-alpha.4 → 0.1.0-alpha.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0-alpha.5
4
+
5
+ - Default development Fastify/Vite logging to warn, retain the startup address and allow FASTANG_LOG_LEVEL overrides. Suppress successful dev check summaries with check --quiet.
6
+
7
+ - Preserve relative dependency symlinks in Vercel functions for pnpm installations.
8
+
3
9
  ## 0.1.0-alpha.4
4
10
 
5
11
  - Add source-only `fastang check` diagnostics for API route/contract correspondence, with tsconfig resolution, JSON and strict mode.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @fastang/core
2
2
 
3
3
  Experimental FastAng runtime and tooling for Angular + Fastify applications.
4
- Version 0.1.0-alpha.4 is an experimental alpha. Public APIs may change between alpha releases.
4
+ Version 0.1.0-alpha.5 is an experimental alpha. Public APIs may change between alpha releases.
5
5
 
6
6
  The package contains compiled JavaScript and TypeScript declarations. Angular,
7
7
  Fastify, Vite and the Angular compiler are explicit peer dependencies so the app
@@ -124,3 +124,20 @@ overlay on route/contract edits; fixing them triggers a full reload. `fastang de
124
124
  checks before restarting the backend and keeps an existing server during an invalid
125
125
  edit. The standalone CLI delegates `check` to the app's installed core: both need
126
126
  a release containing this command. Use `0.1.0-alpha.4` or later for both packages.
127
+
128
+ ## Development logging
129
+
130
+ From `0.1.0-alpha.5`, development defaults to `warn` for both Fastify and Vite during `fastang dev`.
131
+ The listening address remains visible. Routine HTTP request/completion logs and
132
+ successful route-check summaries are omitted; warnings, errors and failed checks
133
+ remain visible. Production logger configuration is unchanged.
134
+
135
+ Set `FASTANG_LOG_LEVEL=info` or `FASTANG_LOG_LEVEL=debug` in the process environment
136
+ when investigating requests. Accepted values are `trace`, `debug`, `info`, `warn`,
137
+ `error`, `fatal` and `silent`. Vite maps trace/debug to info and fatal to error.
138
+ This controls framework loggers, not application `console.log` calls or package
139
+ manager output. The startup address is retained even with `silent`.
140
+
141
+ `fastang check --quiet` suppresses only a successful text summary; diagnostics
142
+ still print, and `--json` always returns the JSON report. The dev supervisor uses
143
+ this option for checks before backend restarts. These options are available from `0.1.0-alpha.5`.
@@ -1,5 +1,5 @@
1
- import { copyFile, mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
2
- import { dirname, resolve } from 'node:path';
1
+ import { copyFile, mkdir, readFile, rm, stat, lstat, readlink, symlink, writeFile } from 'node:fs/promises';
2
+ import { dirname, resolve, relative, isAbsolute } from 'node:path';
3
3
  import { createRequire } from 'node:module';
4
4
  import { nodeFileTrace } from '@vercel/nft';
5
5
  import { staticFile } from "../server.js";
@@ -15,7 +15,22 @@ export async function buildVercelOutput(plan, root = process.cwd()) {
15
15
  await rm(output, { recursive: true, force: true });
16
16
  const copy = async (file, destination) => {
17
17
  await mkdir(dirname(destination), { recursive: true });
18
- await copyFile(resolve(root, file), destination);
18
+ const source = resolve(root, file);
19
+ const info = await lstat(source);
20
+ if (info.isSymbolicLink()) {
21
+ const target = await readlink(source);
22
+ const sourceTarget = relative(root, resolve(dirname(source), target));
23
+ if (isAbsolute(target) || sourceTarget === '..' || sourceTarget.startsWith('../') || sourceTarget.startsWith('..\\'))
24
+ throw new Error(`Dependency symlink outside project: ${file}`);
25
+ const directory = (await stat(source)).isDirectory();
26
+ if (directory)
27
+ await mkdir(resolve(dirname(destination), target), { recursive: true });
28
+ await symlink(target, destination, directory ? 'dir' : 'file');
29
+ }
30
+ else if (info.isDirectory())
31
+ await mkdir(destination, { recursive: true });
32
+ else
33
+ await copyFile(source, destination);
19
34
  };
20
35
  const { cp } = await import('node:fs/promises');
21
36
  await cp(resolve(root, 'dist/client/assets'), resolve(output, 'static/assets'), { recursive: true });
@@ -39,11 +54,16 @@ export async function buildVercelOutput(plan, root = process.cwd()) {
39
54
  traces.set(entryFile, trace);
40
55
  for (const warning of trace.warnings)
41
56
  console.warn(`[vercel:${name}] ${warning.message}`);
42
- for (const file of trace.fileList) {
57
+ const files = await Promise.all([...trace.fileList].map(async (file) => {
43
58
  if (file.startsWith('../') || file.startsWith('/'))
44
59
  throw new Error(`Dependency outside project: ${file}`);
60
+ return { file, link: (await lstat(resolve(root, file))).isSymbolicLink() };
61
+ }));
62
+ // pnpm exposes dependencies through relative directory symlinks. Keep that
63
+ // topology in the function, creating links before copying traced files.
64
+ files.sort((a, b) => Number(b.link) - Number(a.link) || a.file.length - b.file.length);
65
+ for (const { file } of files)
45
66
  await copy(file, resolve(directory, file));
46
- }
47
67
  if (kind === 'ssr')
48
68
  await copy('dist/client/index.html', resolve(directory, dirname(entryFile), '../client/index.html'));
49
69
  await mkdir(directory, { recursive: true });
package/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ try {
12
12
  else if (command === 'build')
13
13
  await import("./commands/build.js");
14
14
  else if (!command || command === '--help' || command === '-h')
15
- console.log('Usage: fastang <dev|generate|build|routes|check>\n build [--vercel]\n routes [--json]\n check [--project tsconfig.node.json] [--json] [--strict]');
15
+ console.log('Usage: fastang <dev|generate|build|routes|check>\n build [--vercel]\n routes [--json]\n check [--project tsconfig.node.json] [--json] [--strict] [--quiet]');
16
16
  else
17
17
  throw new Error(`Unknown command: ${command}`);
18
18
  }
@@ -4,10 +4,13 @@ let project;
4
4
  for (let index = 0; index < args.length; index++) {
5
5
  if (args[index] === '--project' && args[index + 1] && !args[index + 1].startsWith('-'))
6
6
  project = args[++index];
7
- else if (!['--json', '--strict'].includes(args[index]))
8
- throw new Error('Usage: fastang check [--project tsconfig.node.json] [--json] [--strict]');
7
+ else if (!['--json', '--strict', '--quiet'].includes(args[index]))
8
+ throw new Error('Usage: fastang check [--project tsconfig.node.json] [--json] [--strict] [--quiet]');
9
9
  }
10
10
  const result = await checkRoutes({ project, strict: args.includes('--strict') });
11
- console.log(args.includes('--json') ? JSON.stringify(result, null, 2) : formatCheck(result));
11
+ if (args.includes('--json'))
12
+ console.log(JSON.stringify(result, null, 2));
13
+ else if (!args.includes('--quiet') || result.diagnostics.length)
14
+ console.log(formatCheck(result));
12
15
  if (result.diagnostics.some(diagnostic => diagnostic.severity === 'error'))
13
16
  process.exitCode = 1;
@@ -0,0 +1,10 @@
1
+ import type { LogLevel } from 'vite';
2
+ declare const levels: readonly ["trace", "debug", "info", "warn", "error", "fatal", "silent"];
3
+ type Level = typeof levels[number];
4
+ /** Development only: production servers keep their own logger configuration. */
5
+ export declare function developmentLogging(value?: string | undefined): {
6
+ level: Level;
7
+ vite: LogLevel;
8
+ banner: boolean;
9
+ };
10
+ export {};
@@ -0,0 +1,13 @@
1
+ const levels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal', 'silent'];
2
+ /** Development only: production servers keep their own logger configuration. */
3
+ export function developmentLogging(value = process.env['FASTANG_LOG_LEVEL']) {
4
+ const level = value ?? 'warn';
5
+ if (!levels.includes(level))
6
+ throw new Error(`FASTANG_LOG_LEVEL must be one of: ${levels.join(', ')}`);
7
+ return {
8
+ level: level,
9
+ vite: ['trace', 'debug', 'info'].includes(level) ? 'info' : level === 'fatal' ? 'error' : level,
10
+ // Fastify already announces the address at info; preserve it at quieter levels.
11
+ banner: !['trace', 'debug', 'info'].includes(level),
12
+ };
13
+ }
@@ -6,16 +6,18 @@ import { listen } from "../listen.js";
6
6
  import { tsImport } from 'tsx/esm/api';
7
7
  import { resolve } from 'node:path';
8
8
  import { pathToFileURL } from 'node:url';
9
+ import { developmentLogging } from "./dev-logging.js";
9
10
  import { loadFastangConfig } from "./load-config.js";
11
+ const logging = developmentLogging();
10
12
  await loadFastangConfig('serve');
11
13
  const { configureServer } = await tsImport(pathToFileURL(resolve('src/server/plugins.ts')).href, import.meta.url);
12
14
  const { registerApi } = await tsImport(pathToFileURL(resolve('.fastang/api.ts')).href, import.meta.url);
13
15
  let vite;
14
16
  try {
15
17
  const app = await createServer({
16
- logger: true, registerApi,
18
+ logger: { level: logging.level }, registerApi,
17
19
  async configure(server) {
18
- vite = await createViteServer({ server: { middlewareMode: true, hmr: { server: server.server } }, appType: 'custom' });
20
+ vite = await createViteServer({ logLevel: logging.vite, server: { middlewareMode: true, hmr: { server: server.server } }, appType: 'custom' });
19
21
  server.addHook('onClose', async () => { await vite?.close(); });
20
22
  await server.register(middie);
21
23
  // Middie gestisce next(), risposte completate ed errori del middleware Connect.
@@ -36,6 +38,13 @@ try {
36
38
  },
37
39
  });
38
40
  await listen(app);
41
+ if (logging.banner) {
42
+ const address = app.server.address();
43
+ if (address && typeof address !== 'string') {
44
+ const host = address.family === 'IPv6' ? `[${address.address}]` : address.address;
45
+ console.log(`Server listening at http://${host}:${address.port}`);
46
+ }
47
+ }
39
48
  }
40
49
  catch (error) {
41
50
  await vite?.close();
@@ -12,7 +12,7 @@ async function restart() {
12
12
  if (stopping)
13
13
  return;
14
14
  // Keep the previous server available for Vite diagnostics while an edit is invalid.
15
- const checker = spawn(process.execPath, [fileURLToPath(new URL('../cli.js', import.meta.url)), 'check'], { stdio: 'inherit', env: process.env });
15
+ const checker = spawn(process.execPath, [fileURLToPath(new URL('../cli.js', import.meta.url)), 'check', '--quiet'], { stdio: 'inherit', env: process.env });
16
16
  const [checked] = await once(checker, 'exit');
17
17
  if (checked !== 0 || stopping)
18
18
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastang/core",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "0.1.0-alpha.5",
4
4
  "type": "module",
5
5
  "description": "Angular rendering, Fastify APIs and file-based routing.",
6
6
  "license": "MIT",