@ressjs/cli 0.6.0-experimental.0

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.
Files changed (46) hide show
  1. package/bin/ress.js +2 -0
  2. package/dist/api/app.d.ts +4 -0
  3. package/dist/api/app.js +189 -0
  4. package/dist/api/build.d.ts +8 -0
  5. package/dist/api/build.js +113 -0
  6. package/dist/api/dev.d.ts +3 -0
  7. package/dist/api/dev.js +6 -0
  8. package/dist/api/errors.d.ts +37 -0
  9. package/dist/api/errors.js +119 -0
  10. package/dist/api/extend.d.ts +29 -0
  11. package/dist/api/extend.js +117 -0
  12. package/dist/api/index.d.ts +22 -0
  13. package/dist/api/index.js +16 -0
  14. package/dist/api/inspect.d.ts +75 -0
  15. package/dist/api/inspect.js +170 -0
  16. package/dist/api/options.d.ts +29 -0
  17. package/dist/api/options.js +152 -0
  18. package/dist/api/reporter.d.ts +74 -0
  19. package/dist/api/reporter.js +12 -0
  20. package/dist/api/start.d.ts +3 -0
  21. package/dist/api/start.js +6 -0
  22. package/dist/api/types.d.ts +82 -0
  23. package/dist/api/types.js +1 -0
  24. package/dist/cli/argv.d.ts +63 -0
  25. package/dist/cli/argv.js +148 -0
  26. package/dist/cli/commands/build.d.ts +2 -0
  27. package/dist/cli/commands/build.js +7 -0
  28. package/dist/cli/commands/dev.d.ts +2 -0
  29. package/dist/cli/commands/dev.js +10 -0
  30. package/dist/cli/commands/info.d.ts +2 -0
  31. package/dist/cli/commands/info.js +17 -0
  32. package/dist/cli/commands/start.d.ts +2 -0
  33. package/dist/cli/commands/start.js +10 -0
  34. package/dist/cli/main.d.ts +2 -0
  35. package/dist/cli/main.js +62 -0
  36. package/dist/cli/reporters/format.d.ts +24 -0
  37. package/dist/cli/reporters/format.js +50 -0
  38. package/dist/cli/reporters/info-format.d.ts +15 -0
  39. package/dist/cli/reporters/info-format.js +49 -0
  40. package/dist/cli/reporters/json.d.ts +12 -0
  41. package/dist/cli/reporters/json.js +13 -0
  42. package/dist/cli/reporters/terminal.d.ts +35 -0
  43. package/dist/cli/reporters/terminal.js +118 -0
  44. package/dist/cli/shutdown.d.ts +2 -0
  45. package/dist/cli/shutdown.js +17 -0
  46. package/package.json +59 -0
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Superficie pública de `@ressjs/cli`.
3
+ *
4
+ * Todo lo que hace un comando de la CLI está disponible acá como función. La
5
+ * CLI (`src/cli/**`) es un consumidor de esta superficie, no una segunda
6
+ * implementación.
7
+ */
8
+ export { createRessApp } from './app.js';
9
+ export { startDevServer } from './dev.js';
10
+ export { startProdServer } from './start.js';
11
+ export { buildApp } from './build.js';
12
+ export { inspectProject, cliVersion } from './inspect.js';
13
+ export { resolveOptions } from './options.js';
14
+ export { silentReporter, collectingReporter } from './reporter.js';
15
+ export { RessError } from './errors.js';
16
+ export { findServerFile, resolveServerFilePath } from './extend.js';
@@ -0,0 +1,75 @@
1
+ import { type OptionSource } from './options.js';
2
+ import type { RessAppOptions } from './types.js';
3
+ /**
4
+ * `inspectProject` siempre devuelve el diagnóstico completo: filtrar a
5
+ * `--routes` / `--variants` / `--config-only` es presentación, y por eso vive
6
+ * en el comando de la CLI (`src/cli/commands/info.ts`), no acá.
7
+ */
8
+ export type InspectOptions = RessAppOptions;
9
+ export interface ConfigValidationError {
10
+ message: string;
11
+ }
12
+ export interface ProjectInfo {
13
+ versions: {
14
+ node: string;
15
+ cli: string;
16
+ packages: Record<string, string>;
17
+ vite: string;
18
+ react: string;
19
+ };
20
+ platform: {
21
+ os: string;
22
+ arch: string;
23
+ cwd: string;
24
+ };
25
+ config: {
26
+ file: string | null;
27
+ valid: boolean;
28
+ errors?: ConfigValidationError[];
29
+ resolved: Record<string, unknown> | null;
30
+ sources: Record<string, {
31
+ value: unknown;
32
+ source: OptionSource;
33
+ origin: string;
34
+ }>;
35
+ };
36
+ routes: {
37
+ count: number;
38
+ entries: Array<{
39
+ pattern: string;
40
+ pageId: string;
41
+ kind: string;
42
+ file: string;
43
+ }>;
44
+ conflicts: Array<{
45
+ pattern: string;
46
+ files: string[];
47
+ }>;
48
+ };
49
+ variants: {
50
+ tags: string[];
51
+ byPage: Record<string, string[]>;
52
+ declared: string[];
53
+ };
54
+ build: {
55
+ present: boolean;
56
+ outDir: string;
57
+ manifestVersion: number | null;
58
+ compatible: boolean;
59
+ builtAt: string | null;
60
+ sizes: {
61
+ client: number;
62
+ server: number;
63
+ };
64
+ };
65
+ }
66
+ /**
67
+ * La versión de `@ressjs/cli`, leída de su propio `package.json`.
68
+ *
69
+ * Exportada porque `src/cli/main.ts` también la necesita para `--version` y no
70
+ * puede tocar `node:fs` directamente sin romper la regla de separación —
71
+ * "la CLI no reimplementa" incluye no leer archivos por su cuenta cuando la
72
+ * API ya sabe cómo.
73
+ */
74
+ export declare function cliVersion(): Promise<string>;
75
+ export declare function inspectProject(opts?: InspectOptions): Promise<ProjectInfo>;
@@ -0,0 +1,170 @@
1
+ /**
2
+ * `inspectProject` — el diagnóstico de `ress info`.
3
+ *
4
+ * Nunca lanza por un proyecto roto: un comando de diagnóstico que aborta ante
5
+ * el problema que se está diagnosticando no sirve (UC-4, Flujo D). Reutiliza
6
+ * `resolveOptions` y `scanRoutes`/`getPages`: no tiene lectura propia del
7
+ * proyecto, así que su salida coincide con lo que `ress dev` va a hacer.
8
+ */
9
+ import os from 'node:os';
10
+ import path from 'node:path';
11
+ import { fileURLToPath, URL } from 'node:url';
12
+ import { readFile } from 'node:fs/promises';
13
+ import { buildVariantCatalog, getPages, loadRouteManifest, OUT_DIR, scanRoutes, } from '@ressjs/vite-router';
14
+ import { resolveOptions } from './options.js';
15
+ import { RessError } from './errors.js';
16
+ async function dirBytes(dir) {
17
+ const { readdir, stat } = await import('node:fs/promises');
18
+ let entries;
19
+ try {
20
+ entries = (await readdir(dir, { recursive: true }));
21
+ }
22
+ catch {
23
+ return 0;
24
+ }
25
+ let bytes = 0;
26
+ for (const entry of entries) {
27
+ const info = await stat(path.join(dir, entry)).catch(() => null);
28
+ if (info?.isFile())
29
+ bytes += info.size;
30
+ }
31
+ return bytes;
32
+ }
33
+ async function packageVersion(root, name) {
34
+ try {
35
+ const raw = await readFile(path.join(root, 'node_modules', ...name.split('/'), 'package.json'), 'utf-8');
36
+ return JSON.parse(raw).version;
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ /**
43
+ * La versión de `@ressjs/cli`, leída de su propio `package.json`.
44
+ *
45
+ * Exportada porque `src/cli/main.ts` también la necesita para `--version` y no
46
+ * puede tocar `node:fs` directamente sin romper la regla de separación —
47
+ * "la CLI no reimplementa" incluye no leer archivos por su cuenta cuando la
48
+ * API ya sabe cómo.
49
+ */
50
+ export async function cliVersion() {
51
+ try {
52
+ const raw = await readFile(fileURLToPath(new URL('../../package.json', import.meta.url)), 'utf-8');
53
+ return JSON.parse(raw).version ?? '0.0.0';
54
+ }
55
+ catch {
56
+ return '0.0.0';
57
+ }
58
+ }
59
+ export async function inspectProject(opts = {}) {
60
+ const root = opts.root ? path.resolve(opts.root) : process.cwd();
61
+ const mode = opts.mode ?? 'development';
62
+ let sources = {};
63
+ let resolvedConfig = null;
64
+ let configFile = null;
65
+ let valid = true;
66
+ let errors;
67
+ try {
68
+ const options = await resolveOptions({ ...opts, root, mode }, { mode });
69
+ for (const [key, entry] of Object.entries(options)) {
70
+ if (key === 'config')
71
+ continue;
72
+ const { value, source, origin } = entry;
73
+ sources[key] = { value, source, origin };
74
+ }
75
+ resolvedConfig = options.config;
76
+ configFile = options.configFile.value;
77
+ }
78
+ catch (cause) {
79
+ valid = false;
80
+ const message = cause instanceof RessError ? (cause.reason ?? cause.title) : String(cause);
81
+ errors = [{ message }];
82
+ }
83
+ // Rutas: nunca aborta. `scanRoutes` sin `strict` avisa por consola y sigue
84
+ // con la primera; para el diagnóstico interesa además la lista de conflictos,
85
+ // así que se detectan comparando duplicados de patrón a mano.
86
+ const pagesDir = path.join(root, 'app/pages');
87
+ let entries = [];
88
+ const conflicts = [];
89
+ try {
90
+ const data = await scanRoutes({ pagesDir });
91
+ entries = data.entries;
92
+ conflicts.push(...(data.conflicts ?? []));
93
+ }
94
+ catch {
95
+ // Un proyecto sin `app/pages` no tiene rutas: no es un error del diagnóstico.
96
+ }
97
+ // Variantes: mismo catálogo que `createRessApp` construiría.
98
+ let tags = [];
99
+ const byPage = {};
100
+ try {
101
+ const pages = getPages();
102
+ const catalog = await buildVariantCatalog({ pages });
103
+ const tagSet = new Set();
104
+ for (const [pageId, variants] of catalog.variantsByPage) {
105
+ const suffixes = variants.map((v) => v.suffix).filter(Boolean);
106
+ byPage[pageId] = suffixes;
107
+ for (const suffix of suffixes)
108
+ tagSet.add(suffix);
109
+ }
110
+ tags = [...tagSet].sort();
111
+ }
112
+ catch {
113
+ // Sin páginas, catálogo vacío.
114
+ }
115
+ const platformConfig = resolvedConfig?.platform;
116
+ const declared = platformConfig?.targets ??
117
+ (platformConfig?.values ?? []).map((value) => value.token);
118
+ // Build.
119
+ const outDirAbs = path.join(root, OUT_DIR.base);
120
+ let present = false;
121
+ let manifestVersion = null;
122
+ let compatible = false;
123
+ let builtAt = null;
124
+ const sizes = { client: 0, server: 0 };
125
+ try {
126
+ const raw = await readFile(path.join(outDirAbs, 'route-manifest.json'), 'utf-8');
127
+ present = true;
128
+ const data = JSON.parse(raw);
129
+ manifestVersion = data.version ?? null;
130
+ try {
131
+ loadRouteManifest(raw);
132
+ compatible = true;
133
+ }
134
+ catch {
135
+ compatible = false;
136
+ }
137
+ const stat = await import('node:fs/promises').then((fs) => fs.stat(path.join(outDirAbs, 'route-manifest.json')));
138
+ builtAt = stat.mtime.toISOString();
139
+ sizes.client = await dirBytes(path.join(root, OUT_DIR.client));
140
+ sizes.server = await dirBytes(path.join(root, OUT_DIR.ssr));
141
+ }
142
+ catch {
143
+ present = false;
144
+ }
145
+ return {
146
+ versions: {
147
+ node: process.version,
148
+ cli: await cliVersion(),
149
+ packages: Object.fromEntries((await Promise.all(['@ressjs/vite-router', '@ressjs/config', '@ressjs/cli', '@ressjs/platform'].map(async (name) => [name, await packageVersion(root, name)]))).filter((entry) => Boolean(entry[1]))),
150
+ vite: (await packageVersion(root, 'vite')) ?? 'desconocida',
151
+ react: (await packageVersion(root, 'react')) ?? 'desconocida',
152
+ },
153
+ platform: { os: `${os.platform()} ${os.arch()}`, arch: os.arch(), cwd: root },
154
+ config: { file: configFile, valid, errors, resolved: resolvedConfig, sources },
155
+ routes: {
156
+ count: entries.length,
157
+ entries: entries.map((e) => ({ pattern: e.pattern, pageId: e.pageId, kind: e.kind, file: e.sourceFile })),
158
+ conflicts,
159
+ },
160
+ variants: { tags, byPage, declared },
161
+ build: {
162
+ present,
163
+ outDir: OUT_DIR.base,
164
+ manifestVersion,
165
+ compatible,
166
+ builtAt,
167
+ sizes,
168
+ },
169
+ };
170
+ }
@@ -0,0 +1,29 @@
1
+ import { type ResolvedConfig } from '@ressjs/vite-router';
2
+ import type { LogLevel, RessAppOptions, RessMode } from './types.js';
3
+ export type OptionSource = 'flag' | 'env' | 'config' | 'default';
4
+ export interface ResolvedOption<T> {
5
+ value: T;
6
+ source: OptionSource;
7
+ /** Nombre concreto del origen: '--port', 'PORT', 'config:basePath'. */
8
+ origin: string;
9
+ }
10
+ export interface ResolvedRessOptions {
11
+ root: ResolvedOption<string>;
12
+ configFile: ResolvedOption<string | null>;
13
+ mode: ResolvedOption<RessMode>;
14
+ port: ResolvedOption<number>;
15
+ host: ResolvedOption<string>;
16
+ strictPort: ResolvedOption<boolean>;
17
+ base: ResolvedOption<string>;
18
+ logLevel: ResolvedOption<LogLevel>;
19
+ serverFile: ResolvedOption<string | null>;
20
+ outDir: ResolvedOption<string>;
21
+ /** `ResolvedConfig` de F-008, ya fusionado por modo. */
22
+ config: ResolvedConfig;
23
+ }
24
+ export interface ResolveDefaults {
25
+ mode?: RessMode;
26
+ host?: string;
27
+ port?: number;
28
+ }
29
+ export declare function resolveOptions(opts: RessAppOptions, defaults?: ResolveDefaults): Promise<ResolvedRessOptions>;
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Resolución de opciones con procedencia.
3
+ *
4
+ * Precedencia fija: **argumento > variable de entorno > `config/` (F-008) >
5
+ * default del framework.** No se devuelven valores pelados: cada campo lleva su
6
+ * `source` y su `origin`, porque UC-10 pide que sea observable cuál ganó y
7
+ * `ress info` lo muestra.
8
+ *
9
+ * Variables de entorno leídas, y solo estas: `PORT`, `HOST`, `NODE_ENV`,
10
+ * `RESS_LOG_LEVEL`, `RESS_CONFIG`. `NODE_ENV` sólo se lee para derivar `mode`;
11
+ * nunca se escribe acá.
12
+ *
13
+ * Límite conocido: `loadConfig()` de F-008 resuelve `config/` a partir de
14
+ * `root` + `mode` y no acepta todavía una ruta de archivo explícita. Por eso
15
+ * `--config` / `RESS_CONFIG` quedan registrados con su procedencia pero no
16
+ * cambian qué archivo lee `loadConfig` — es una limitación de F-008, no de esta
17
+ * spec, y se deja documentada en vez de reimplementarse acá.
18
+ */
19
+ import path from 'node:path';
20
+ import { loadConfig } from '@ressjs/vite-router';
21
+ import { RessError } from './errors.js';
22
+ const VALID_MODES = ['development', 'production'];
23
+ const VALID_LOG_LEVELS = ['silent', 'error', 'warn', 'info', 'debug'];
24
+ function opt(value, source, origin) {
25
+ return { value, source, origin };
26
+ }
27
+ export async function resolveOptions(opts, defaults = {}) {
28
+ const env = process.env;
29
+ const root = opts.root
30
+ ? opt(path.resolve(opts.root), 'flag', '--root')
31
+ : opt(process.cwd(), 'default', 'process.cwd()');
32
+ const configFile = opts.configFile
33
+ ? opt(opts.configFile, 'flag', '--config')
34
+ : env.RESS_CONFIG
35
+ ? opt(env.RESS_CONFIG, 'env', 'RESS_CONFIG')
36
+ : opt(null, 'default', 'autodetectado');
37
+ const mode = resolveMode(opts, defaults, env);
38
+ // Config del proyecto: F-008. Se carga con la raíz y el modo ya resueltos —es
39
+ // la misma resolución que usará `createRessApp`, así que lo que reporta
40
+ // `ress info` coincide con lo que el servidor va a hacer.
41
+ let config;
42
+ try {
43
+ config = await loadConfig(root.value, mode.value);
44
+ }
45
+ catch (cause) {
46
+ throw RessError.configInvalid(cause instanceof Error ? cause.message : String(cause), undefined, cause);
47
+ }
48
+ const port = resolvePort(opts, defaults, env);
49
+ const host = resolveHost(opts, defaults, env);
50
+ const strictPort = resolveStrictPort(opts);
51
+ const base = resolveBase(opts, config);
52
+ const logLevel = resolveLogLevel(opts, env);
53
+ const serverFile = resolveServerFile(opts);
54
+ const outDir = opts.outDir
55
+ ? opt(opts.outDir, 'flag', '--out-dir')
56
+ : opt('dist', 'default', 'dist');
57
+ return {
58
+ root,
59
+ configFile,
60
+ mode,
61
+ port,
62
+ host,
63
+ strictPort,
64
+ base,
65
+ logLevel,
66
+ serverFile,
67
+ outDir,
68
+ config,
69
+ };
70
+ }
71
+ function resolveMode(opts, defaults, env) {
72
+ if (opts.mode !== undefined) {
73
+ if (!VALID_MODES.includes(opts.mode)) {
74
+ throw RessError.badUsage(`--mode sólo acepta ${VALID_MODES.join(' o ')}; se recibió "${opts.mode}".`);
75
+ }
76
+ return opt(opts.mode, 'flag', '--mode');
77
+ }
78
+ if (env.NODE_ENV === 'production' || env.NODE_ENV === 'development') {
79
+ return opt(env.NODE_ENV, 'env', 'NODE_ENV');
80
+ }
81
+ return opt(defaults.mode ?? 'development', 'default', 'default del framework');
82
+ }
83
+ function resolvePort(opts, defaults, env) {
84
+ if (opts.port !== undefined) {
85
+ if (!Number.isInteger(opts.port) || opts.port < 0) {
86
+ throw RessError.badUsage(`--port tiene que ser un entero no negativo; se recibió "${opts.port}".`);
87
+ }
88
+ return opt(opts.port, 'flag', '--port');
89
+ }
90
+ if (env.PORT !== undefined) {
91
+ const parsed = Number(env.PORT);
92
+ if (!Number.isInteger(parsed) || parsed < 0) {
93
+ throw RessError.badUsage(`PORT tiene que ser un entero no negativo; se recibió "${env.PORT}".`);
94
+ }
95
+ return opt(parsed, 'env', 'PORT');
96
+ }
97
+ return opt(defaults.port ?? 3000, 'default', 'default del framework');
98
+ }
99
+ function resolveHost(opts, defaults, env) {
100
+ if (opts.host !== undefined) {
101
+ // `--host` sin valor llega como `true`: equivale a exponer en todas las interfaces.
102
+ const value = opts.host === true ? '0.0.0.0' : opts.host === false ? 'localhost' : opts.host;
103
+ return opt(value, 'flag', '--host');
104
+ }
105
+ if (env.HOST !== undefined) {
106
+ return opt(env.HOST, 'env', 'HOST');
107
+ }
108
+ return opt(defaults.host ?? 'localhost', 'default', 'default del framework');
109
+ }
110
+ function resolveStrictPort(opts) {
111
+ if (opts.strictPort !== undefined) {
112
+ return opt(opts.strictPort, 'flag', opts.strictPort ? '--strict-port' : '--no-strict-port');
113
+ }
114
+ return opt(true, 'default', 'default del framework');
115
+ }
116
+ function resolveBase(opts, config) {
117
+ if (opts.base !== undefined) {
118
+ return opt(opts.base, 'flag', '--base');
119
+ }
120
+ if (config.basePath) {
121
+ return opt(config.basePath, 'config', 'config:basePath');
122
+ }
123
+ return opt('/', 'default', 'default del framework');
124
+ }
125
+ function resolveLogLevel(opts, env) {
126
+ if (opts.logLevel !== undefined) {
127
+ if (!VALID_LOG_LEVELS.includes(opts.logLevel)) {
128
+ throw RessError.badUsage(`--log-level sólo acepta ${VALID_LOG_LEVELS.join(', ')}; se recibió "${opts.logLevel}".`);
129
+ }
130
+ return opt(opts.logLevel, 'flag', '--log-level');
131
+ }
132
+ if (env.RESS_LOG_LEVEL !== undefined) {
133
+ const value = env.RESS_LOG_LEVEL;
134
+ if (!VALID_LOG_LEVELS.includes(value)) {
135
+ throw RessError.badUsage(`RESS_LOG_LEVEL sólo acepta ${VALID_LOG_LEVELS.join(', ')}; se recibió "${env.RESS_LOG_LEVEL}".`);
136
+ }
137
+ return opt(value, 'env', 'RESS_LOG_LEVEL');
138
+ }
139
+ return opt('info', 'default', 'default del framework');
140
+ }
141
+ function resolveServerFile(opts) {
142
+ if (opts.serverFile === false) {
143
+ return opt(null, 'flag', '--server=false');
144
+ }
145
+ if (typeof opts.serverFile === 'string') {
146
+ return opt(opts.serverFile, 'flag', '--server');
147
+ }
148
+ // La autodetección real (¿existe server.{ts,tsx,js,mjs} en la raíz?) la hace
149
+ // `extend.ts`, que sí toca el sistema de archivos. Acá sólo se declara que no
150
+ // hubo elección explícita.
151
+ return opt(null, 'default', 'autodetectado');
152
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * El canal de eventos.
3
+ *
4
+ * "La API no imprime" necesita un mecanismo o se convierte en una convención
5
+ * que el primer log de conveniencia por `console` rompe. Cada punto de la API que
6
+ * quiera informar progreso emite un `RessEvent`; quién lo escucha y cómo lo
7
+ * muestra es responsabilidad exclusiva de la CLI (`src/cli/reporters/*`).
8
+ */
9
+ import type { RouteEntry } from '@ressjs/vite-router';
10
+ import type { RessError } from './errors.js';
11
+ import type { RessMode } from './types.js';
12
+ export type VariantTag = string;
13
+ export type RessEvent = {
14
+ type: 'config:loaded';
15
+ file: string | null;
16
+ durationMs: number;
17
+ } | {
18
+ type: 'routes:scanned';
19
+ entries: RouteEntry[];
20
+ durationMs: number;
21
+ } | {
22
+ type: 'routes:conflict';
23
+ pattern: string;
24
+ files: string[];
25
+ } | {
26
+ type: 'routes:changed';
27
+ added: string[];
28
+ removed: string[];
29
+ } | {
30
+ type: 'catalog:built';
31
+ pages: number;
32
+ variants: VariantTag[];
33
+ } | {
34
+ type: 'server:listening';
35
+ url: string;
36
+ mode: RessMode;
37
+ port: number;
38
+ } | {
39
+ type: 'server:extended';
40
+ file: string;
41
+ hook: 'before' | 'after' | 'file';
42
+ } | {
43
+ type: 'build:start';
44
+ target: 'client' | 'ssr';
45
+ } | {
46
+ type: 'build:done';
47
+ target: 'client' | 'ssr';
48
+ durationMs: number;
49
+ bytes: number;
50
+ } | {
51
+ type: 'build:artifact';
52
+ file: string;
53
+ bytes: number;
54
+ } | {
55
+ type: 'config:reload';
56
+ keys: string[];
57
+ requiresRestart: string[];
58
+ } | {
59
+ type: 'warn';
60
+ message: string;
61
+ file?: string;
62
+ } | {
63
+ type: 'error';
64
+ error: RessError;
65
+ };
66
+ export interface RessReporter {
67
+ event(e: RessEvent): void;
68
+ }
69
+ /** Default de toda la API. No hace nada: la API es utilizable sin ensuciar stdout. */
70
+ export declare const silentReporter: RessReporter;
71
+ /** Reporter de conveniencia para tests: acumula todo lo que recibe. */
72
+ export declare function collectingReporter(): RessReporter & {
73
+ events: RessEvent[];
74
+ };
@@ -0,0 +1,12 @@
1
+ /** Default de toda la API. No hace nada: la API es utilizable sin ensuciar stdout. */
2
+ export const silentReporter = { event() { } };
3
+ /** Reporter de conveniencia para tests: acumula todo lo que recibe. */
4
+ export function collectingReporter() {
5
+ const events = [];
6
+ return {
7
+ events,
8
+ event(e) {
9
+ events.push(e);
10
+ },
11
+ };
12
+ }
@@ -0,0 +1,3 @@
1
+ import type { RessAppOptions, RessServer } from './types.js';
2
+ /** `ress start`: construye la app en modo producción y la pone a escuchar. */
3
+ export declare function startProdServer(opts?: RessAppOptions): Promise<RessServer>;
@@ -0,0 +1,6 @@
1
+ import { createRessApp } from './app.js';
2
+ /** `ress start`: construye la app en modo producción y la pone a escuchar. */
3
+ export async function startProdServer(opts = {}) {
4
+ const app = await createRessApp({ ...opts, mode: 'production' });
5
+ return app.listen();
6
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Tipos públicos de la API de `@ressjs/cli`.
3
+ *
4
+ * La CLI y la API programática comparten esta forma. Un comando de la CLI hace
5
+ * poco más que traducir argumentos a `RessAppOptions` e invocar una de estas
6
+ * funciones — ver `src/cli/commands/*`.
7
+ */
8
+ import type { Express } from 'express';
9
+ import type { RessReporter } from './reporter.js';
10
+ import type { ResolvedRessOptions } from './options.js';
11
+ import type { RouteManifest } from '@ressjs/vite-router';
12
+ export type RessMode = 'development' | 'production';
13
+ export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';
14
+ /** Lo que un consumidor puede pedir. Todo opcional: hay default para cada cosa. */
15
+ export interface RessAppOptions {
16
+ /** Raíz del proyecto. Default: `process.cwd()`. */
17
+ root?: string;
18
+ /** Ruta explícita a un archivo de configuración; si falta, se autodetecta `config/` bajo `root` (F-008). */
19
+ configFile?: string;
20
+ /** Default: `'development'` en dev, `'production'` en build y start. */
21
+ mode?: RessMode;
22
+ /** Default: `3000`. */
23
+ port?: number;
24
+ /** Default: `'localhost'`. `true` equivale a `'0.0.0.0'`. */
25
+ host?: string | boolean;
26
+ /** Si el puerto está ocupado: fallar (default) o probar el siguiente. */
27
+ strictPort?: boolean;
28
+ /** Prefijo de servido. Default: `config.basePath ?? '/'`. */
29
+ base?: string;
30
+ /** Default: `'info'`. */
31
+ logLevel?: LogLevel;
32
+ /** Canal de eventos. Default: `silentReporter`. La API nunca imprime. */
33
+ reporter?: RessReporter;
34
+ /**
35
+ * Archivo de servidor del proyecto (vía de escape nivel 2).
36
+ * Default: se autodetecta `server.{ts,tsx,js,mjs}` en la raíz; ausente = no hay.
37
+ */
38
+ serverFile?: string | false;
39
+ /** Raíz de salida del build. Default: `'dist'`. */
40
+ outDir?: string;
41
+ }
42
+ export interface RessServer {
43
+ readonly url: string;
44
+ readonly port: number;
45
+ readonly httpServer: import('node:http').Server;
46
+ close(): Promise<void>;
47
+ }
48
+ export interface RessApp {
49
+ /** La aplicación Express, ya con el enrutador del framework montado. */
50
+ readonly app: Express;
51
+ /** Opciones resueltas, con procedencia. */
52
+ readonly options: ResolvedRessOptions;
53
+ /** Manifest de rutas vigente (F-018). En dev cambia con el watcher. */
54
+ readonly manifest: RouteManifest;
55
+ /** Servidor de desarrollo del bundler. `undefined` en producción. */
56
+ readonly vite?: unknown;
57
+ /** Pone la app a escuchar. Resuelve cuando ya acepta conexiones. */
58
+ listen(): Promise<RessServer>;
59
+ /** Cierra bundler, watchers y `ModuleLoader`. Idempotente. */
60
+ close(): Promise<void>;
61
+ }
62
+ export interface RessServerFileContext {
63
+ app: Express;
64
+ mode: RessMode;
65
+ options: ResolvedRessOptions;
66
+ manifest: RouteManifest;
67
+ vite?: unknown;
68
+ }
69
+ export type RessServerFile = (ctx: RessServerFileContext) => Express | void | Promise<Express | void>;
70
+ export interface BuildTargetResult {
71
+ target: 'client' | 'ssr';
72
+ durationMs: number;
73
+ bytes: number;
74
+ files: string[];
75
+ }
76
+ export interface BuildResult {
77
+ targets: BuildTargetResult[];
78
+ durationMs: number;
79
+ outDir: string;
80
+ routes: number;
81
+ variants: number;
82
+ }
@@ -0,0 +1 @@
1
+ export {};