hightjs 0.3.4 → 0.4.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.
- package/README.md +1 -1
- package/dist/api/console.js +1 -1
- package/dist/bin/hightjs.js +3 -3
- package/dist/builder.js +60 -5
- package/dist/client/entry.client.js +155 -7
- package/dist/helpers.js +40 -1
- package/dist/hotReload.js +96 -7
- package/dist/index.js +66 -51
- package/dist/renderer.js +137 -18
- package/dist/types.d.ts +39 -0
- package/docs/checklist.md +1 -1
- package/docs/config.md +15 -0
- package/docs/middlewares.md +1 -1
- package/docs/rotas-backend.md +3 -3
- package/docs/websocket.md +1 -1
- package/example/hightjs.config.ts +6 -0
- package/example/package-lock.json +1 -1
- package/example/package.json +2 -2
- package/example/src/backend/routes/auth.ts +3 -0
- package/example/src/{web/backend → backend}/routes/version.ts +1 -1
- package/example/src/web/components/Home.tsx +140 -0
- package/example/src/web/components/LoginPage.tsx +149 -0
- package/example/src/web/layout.tsx +57 -3
- package/example/src/web/routes/index.tsx +1 -141
- package/example/src/web/routes/login.tsx +1 -146
- package/package.json +3 -1
- package/src/api/console.ts +3 -1
- package/src/bin/hightjs.js +3 -3
- package/src/builder.js +66 -5
- package/src/client/entry.client.tsx +192 -10
- package/src/helpers.ts +39 -1
- package/src/hotReload.ts +96 -7
- package/src/index.ts +79 -63
- package/src/renderer.tsx +142 -18
- package/src/router.ts +2 -0
- package/src/types.ts +56 -0
- package/example/src/web/backend/routes/auth.ts +0 -3
- /package/example/src/{auth.ts → backend/auth.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -110,48 +110,49 @@ function isLargeProject(projectDir) {
|
|
|
110
110
|
}
|
|
111
111
|
// Função para gerar o arquivo de entrada para o esbuild
|
|
112
112
|
function createEntryFile(projectDir, routes) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
113
|
+
try {
|
|
114
|
+
const tempDir = path_1.default.join(projectDir, '.hight', 'temp');
|
|
115
|
+
fs_1.default.mkdirSync(tempDir, { recursive: true });
|
|
116
|
+
const entryFilePath = path_1.default.join(tempDir, 'entry.client.js');
|
|
117
|
+
// Verifica se há layout
|
|
118
|
+
const layout = (0, router_1.getLayout)();
|
|
119
|
+
// Verifica se há notFound personalizado
|
|
120
|
+
const notFound = (0, router_1.getNotFound)();
|
|
121
|
+
// Gera imports dinâmicos para cada componente
|
|
122
|
+
const imports = routes
|
|
123
|
+
.map((route, index) => {
|
|
124
|
+
const relativePath = path_1.default.relative(tempDir, route.componentPath).replace(/\\/g, '/');
|
|
125
|
+
return `import route${index} from '${relativePath}';`;
|
|
126
|
+
})
|
|
127
|
+
.join('\n');
|
|
128
|
+
// Import do layout se existir
|
|
129
|
+
const layoutImport = layout
|
|
130
|
+
? `import LayoutComponent from '${path_1.default.relative(tempDir, layout.componentPath).replace(/\\/g, '/')}';`
|
|
131
|
+
: '';
|
|
132
|
+
// Import do notFound se existir
|
|
133
|
+
const notFoundImport = notFound
|
|
134
|
+
? `import NotFoundComponent from '${path_1.default.relative(tempDir, notFound.componentPath).replace(/\\/g, '/')}';`
|
|
135
|
+
: '';
|
|
136
|
+
// Registra os componentes no window para o cliente acessar
|
|
137
|
+
const componentRegistration = routes
|
|
138
|
+
.map((route, index) => ` '${route.componentPath}': route${index}.component || route${index}.default?.component,`)
|
|
139
|
+
.join('\n');
|
|
140
|
+
// Registra o layout se existir
|
|
141
|
+
const layoutRegistration = layout
|
|
142
|
+
? `window.__HWEB_LAYOUT__ = LayoutComponent.default || LayoutComponent;`
|
|
143
|
+
: `window.__HWEB_LAYOUT__ = null;`;
|
|
144
|
+
// Registra o notFound se existir
|
|
145
|
+
const notFoundRegistration = notFound
|
|
146
|
+
? `window.__HWEB_NOT_FOUND__ = NotFoundComponent.default || NotFoundComponent;`
|
|
147
|
+
: `window.__HWEB_NOT_FOUND__ = null;`;
|
|
148
|
+
// Caminho correto para o entry.client.tsx
|
|
149
|
+
const sdkDir = path_1.default.dirname(__dirname); // Vai para a pasta pai de src (onde está o hweb-sdk)
|
|
150
|
+
const entryClientPath = path_1.default.join(sdkDir, 'src', 'client', 'entry.client.tsx');
|
|
151
|
+
const relativeEntryPath = path_1.default.relative(tempDir, entryClientPath).replace(/\\/g, '/');
|
|
152
|
+
// Import do DefaultNotFound do SDK
|
|
153
|
+
const defaultNotFoundPath = path_1.default.join(sdkDir, 'src', 'client', 'DefaultNotFound.tsx');
|
|
154
|
+
const relativeDefaultNotFoundPath = path_1.default.relative(tempDir, defaultNotFoundPath).replace(/\\/g, '/');
|
|
155
|
+
const entryContent = `// Arquivo gerado automaticamente pelo hweb
|
|
155
156
|
${imports}
|
|
156
157
|
${layoutImport}
|
|
157
158
|
${notFoundImport}
|
|
@@ -174,8 +175,18 @@ window.__HWEB_DEFAULT_NOT_FOUND__ = DefaultNotFound;
|
|
|
174
175
|
// Importa e executa o entry.client.tsx
|
|
175
176
|
import '${relativeEntryPath}';
|
|
176
177
|
`;
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
try {
|
|
179
|
+
fs_1.default.writeFileSync(entryFilePath, entryContent);
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
console.error("sdfijnsdfnijfsdijnfsdnijsdfnijfsdnijfsdnijfsdn", e);
|
|
183
|
+
}
|
|
184
|
+
return entryFilePath;
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
console_1.default.error("Error creating entry file:", e);
|
|
188
|
+
throw e;
|
|
189
|
+
}
|
|
179
190
|
}
|
|
180
191
|
function hweb(options) {
|
|
181
192
|
const { dev = true, dir = process.cwd(), port = 3000 } = options;
|
|
@@ -183,7 +194,7 @@ function hweb(options) {
|
|
|
183
194
|
process.hight = options;
|
|
184
195
|
const userWebDir = path_1.default.join(dir, 'src', 'web');
|
|
185
196
|
const userWebRoutesDir = path_1.default.join(userWebDir, 'routes');
|
|
186
|
-
const userBackendRoutesDir = path_1.default.join(
|
|
197
|
+
const userBackendRoutesDir = path_1.default.join(dir, 'src', 'backend', 'routes');
|
|
187
198
|
/**
|
|
188
199
|
* Executa middlewares sequencialmente e depois o handler final
|
|
189
200
|
* @param middlewares Array de middlewares para executar
|
|
@@ -260,7 +271,7 @@ function hweb(options) {
|
|
|
260
271
|
// Carrega layout.tsx ANTES de criar o entry file
|
|
261
272
|
const layout = (0, router_1.loadLayout)(userWebDir);
|
|
262
273
|
const notFound = (0, router_1.loadNotFound)(userWebDir);
|
|
263
|
-
const outDir = path_1.default.join(dir, '
|
|
274
|
+
const outDir = path_1.default.join(dir, '.hight');
|
|
264
275
|
fs_1.default.mkdirSync(outDir, { recursive: true });
|
|
265
276
|
entryPoint = createEntryFile(dir, frontendRoutes);
|
|
266
277
|
clearInterval(spinner1);
|
|
@@ -280,6 +291,10 @@ function hweb(options) {
|
|
|
280
291
|
clearInterval(spinner); // para o spinner
|
|
281
292
|
time.update(""); // limpa a linha
|
|
282
293
|
time.end(` ${console_1.Colors.BgGreen} build ${console_1.Colors.Reset} Client build completed in ${elapsed}ms`);
|
|
294
|
+
// Notifica o hot reload manager que o build foi concluído
|
|
295
|
+
if (hotReloadManager) {
|
|
296
|
+
hotReloadManager.onBuildComplete(true);
|
|
297
|
+
}
|
|
283
298
|
}
|
|
284
299
|
else {
|
|
285
300
|
const time = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} watcher ${console_1.Colors.Reset} Starting client watch`);
|
|
@@ -330,7 +345,7 @@ function hweb(options) {
|
|
|
330
345
|
return;
|
|
331
346
|
}
|
|
332
347
|
// 2. Primeiro verifica se é um arquivo estático da pasta public
|
|
333
|
-
if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('
|
|
348
|
+
if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('/.hight')) {
|
|
334
349
|
const publicDir = path_1.default.join(dir, 'public');
|
|
335
350
|
const filePath = path_1.default.join(publicDir, pathname);
|
|
336
351
|
if (fs_1.default.existsSync(filePath) && fs_1.default.statSync(filePath).isFile()) {
|
|
@@ -372,10 +387,10 @@ function hweb(options) {
|
|
|
372
387
|
return;
|
|
373
388
|
}
|
|
374
389
|
}
|
|
375
|
-
// 3. Verifica se é um arquivo estático do
|
|
376
|
-
if (pathname.startsWith('/
|
|
377
|
-
const staticPath = path_1.default.join(dir, '
|
|
378
|
-
const filePath = path_1.default.join(staticPath, pathname.replace('/
|
|
390
|
+
// 3. Verifica se é um arquivo estático do .hight
|
|
391
|
+
if (pathname.startsWith('/_hight/')) {
|
|
392
|
+
const staticPath = path_1.default.join(dir, '.hight');
|
|
393
|
+
const filePath = path_1.default.join(staticPath, pathname.replace('/_hight/', ''));
|
|
379
394
|
if (fs_1.default.existsSync(filePath)) {
|
|
380
395
|
const ext = path_1.default.extname(filePath).toLowerCase();
|
|
381
396
|
const contentTypes = {
|
package/dist/renderer.js
CHANGED
|
@@ -7,18 +7,123 @@ exports.render = render;
|
|
|
7
7
|
const router_1 = require("./router");
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
-
//
|
|
11
|
-
function
|
|
12
|
-
|
|
10
|
+
// Função para gerar todas as meta tags
|
|
11
|
+
function generateMetaTags(metadata) {
|
|
12
|
+
const tags = [];
|
|
13
|
+
// Charset
|
|
14
|
+
tags.push(`<meta charset="${metadata.charset || 'UTF-8'}">`);
|
|
15
|
+
// Viewport
|
|
16
|
+
tags.push(`<meta name="viewport" content="${metadata.viewport || 'width=device-width, initial-scale=1.0'}">`);
|
|
17
|
+
// Description
|
|
18
|
+
if (metadata.description) {
|
|
19
|
+
tags.push(`<meta name="description" content="${metadata.description}">`);
|
|
20
|
+
}
|
|
21
|
+
// Keywords
|
|
22
|
+
if (metadata.keywords) {
|
|
23
|
+
const keywordsStr = Array.isArray(metadata.keywords)
|
|
24
|
+
? metadata.keywords.join(', ')
|
|
25
|
+
: metadata.keywords;
|
|
26
|
+
tags.push(`<meta name="keywords" content="${keywordsStr}">`);
|
|
27
|
+
}
|
|
28
|
+
// Author
|
|
29
|
+
if (metadata.author) {
|
|
30
|
+
tags.push(`<meta name="author" content="${metadata.author}">`);
|
|
31
|
+
}
|
|
32
|
+
// Theme color
|
|
33
|
+
if (metadata.themeColor) {
|
|
34
|
+
tags.push(`<meta name="theme-color" content="${metadata.themeColor}">`);
|
|
35
|
+
}
|
|
36
|
+
// Robots
|
|
37
|
+
if (metadata.robots) {
|
|
38
|
+
tags.push(`<meta name="robots" content="${metadata.robots}">`);
|
|
39
|
+
}
|
|
40
|
+
// Canonical
|
|
41
|
+
if (metadata.canonical) {
|
|
42
|
+
tags.push(`<link rel="canonical" href="${metadata.canonical}">`);
|
|
43
|
+
}
|
|
44
|
+
// Favicon
|
|
45
|
+
if (metadata.favicon) {
|
|
46
|
+
tags.push(`<link rel="icon" href="${metadata.favicon}">`);
|
|
47
|
+
}
|
|
48
|
+
// Apple Touch Icon
|
|
49
|
+
if (metadata.appleTouchIcon) {
|
|
50
|
+
tags.push(`<link rel="apple-touch-icon" href="${metadata.appleTouchIcon}">`);
|
|
51
|
+
}
|
|
52
|
+
// Manifest
|
|
53
|
+
if (metadata.manifest) {
|
|
54
|
+
tags.push(`<link rel="manifest" href="${metadata.manifest}">`);
|
|
55
|
+
}
|
|
56
|
+
// Open Graph
|
|
57
|
+
if (metadata.openGraph) {
|
|
58
|
+
const og = metadata.openGraph;
|
|
59
|
+
if (og.title)
|
|
60
|
+
tags.push(`<meta property="og:title" content="${og.title}">`);
|
|
61
|
+
if (og.description)
|
|
62
|
+
tags.push(`<meta property="og:description" content="${og.description}">`);
|
|
63
|
+
if (og.type)
|
|
64
|
+
tags.push(`<meta property="og:type" content="${og.type}">`);
|
|
65
|
+
if (og.url)
|
|
66
|
+
tags.push(`<meta property="og:url" content="${og.url}">`);
|
|
67
|
+
if (og.siteName)
|
|
68
|
+
tags.push(`<meta property="og:site_name" content="${og.siteName}">`);
|
|
69
|
+
if (og.locale)
|
|
70
|
+
tags.push(`<meta property="og:locale" content="${og.locale}">`);
|
|
71
|
+
if (og.image) {
|
|
72
|
+
if (typeof og.image === 'string') {
|
|
73
|
+
tags.push(`<meta property="og:image" content="${og.image}">`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
tags.push(`<meta property="og:image" content="${og.image.url}">`);
|
|
77
|
+
if (og.image.width)
|
|
78
|
+
tags.push(`<meta property="og:image:width" content="${og.image.width}">`);
|
|
79
|
+
if (og.image.height)
|
|
80
|
+
tags.push(`<meta property="og:image:height" content="${og.image.height}">`);
|
|
81
|
+
if (og.image.alt)
|
|
82
|
+
tags.push(`<meta property="og:image:alt" content="${og.image.alt}">`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Twitter Card
|
|
87
|
+
if (metadata.twitter) {
|
|
88
|
+
const tw = metadata.twitter;
|
|
89
|
+
if (tw.card)
|
|
90
|
+
tags.push(`<meta name="twitter:card" content="${tw.card}">`);
|
|
91
|
+
if (tw.site)
|
|
92
|
+
tags.push(`<meta name="twitter:site" content="${tw.site}">`);
|
|
93
|
+
if (tw.creator)
|
|
94
|
+
tags.push(`<meta name="twitter:creator" content="${tw.creator}">`);
|
|
95
|
+
if (tw.title)
|
|
96
|
+
tags.push(`<meta name="twitter:title" content="${tw.title}">`);
|
|
97
|
+
if (tw.description)
|
|
98
|
+
tags.push(`<meta name="twitter:description" content="${tw.description}">`);
|
|
99
|
+
if (tw.image)
|
|
100
|
+
tags.push(`<meta name="twitter:image" content="${tw.image}">`);
|
|
101
|
+
if (tw.imageAlt)
|
|
102
|
+
tags.push(`<meta name="twitter:image:alt" content="${tw.imageAlt}">`);
|
|
103
|
+
}
|
|
104
|
+
// Custom meta tags
|
|
105
|
+
if (metadata.other) {
|
|
106
|
+
for (const [key, value] of Object.entries(metadata.other)) {
|
|
107
|
+
tags.push(`<meta name="${key}" content="${value}">`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return tags.join('\n');
|
|
111
|
+
}
|
|
112
|
+
// Função para ofuscar dados (não é criptografia, apenas ofuscação)
|
|
113
|
+
function obfuscateData(data) {
|
|
114
|
+
// 1. Serializa para JSON minificado
|
|
13
115
|
const jsonStr = JSON.stringify(data);
|
|
116
|
+
// 2. Converte para base64
|
|
14
117
|
const base64 = Buffer.from(jsonStr).toString('base64');
|
|
15
|
-
|
|
118
|
+
// 3. Adiciona um hash fake no início para parecer um token
|
|
119
|
+
const hash = Buffer.from(Date.now().toString()).toString('base64').substring(0, 8);
|
|
120
|
+
return `${hash}.${base64}`;
|
|
16
121
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
122
|
+
// Função para criar script ofuscado
|
|
123
|
+
function createInitialDataScript(data) {
|
|
124
|
+
const obfuscated = obfuscateData(data);
|
|
125
|
+
// Usa um atributo data-* ao invés de JSON visível
|
|
126
|
+
return `<script id="__hight_data__" type="text/plain" data-h="${obfuscated}"></script>`;
|
|
22
127
|
}
|
|
23
128
|
async function render({ req, route, params, allRoutes }) {
|
|
24
129
|
const { generateMetadata } = route;
|
|
@@ -43,22 +148,36 @@ async function render({ req, route, params, allRoutes }) {
|
|
|
43
148
|
initialComponentPath: route.componentPath,
|
|
44
149
|
initialParams: params,
|
|
45
150
|
};
|
|
46
|
-
//
|
|
47
|
-
const
|
|
151
|
+
// Cria script JSON limpo
|
|
152
|
+
const initialDataScript = createInitialDataScript(initialData);
|
|
48
153
|
// Script de hot reload apenas em desenvolvimento
|
|
49
154
|
const hotReloadScript = !isProduction && hotReloadManager
|
|
50
155
|
? hotReloadManager.getClientScript()
|
|
51
156
|
: '';
|
|
52
|
-
|
|
157
|
+
// Gera todas as meta tags
|
|
158
|
+
const metaTags = generateMetaTags(metadata);
|
|
53
159
|
// Determina quais arquivos JavaScript carregar
|
|
54
160
|
const jsFiles = getJavaScriptFiles(req);
|
|
161
|
+
const htmlLang = metadata.language || 'pt-BR';
|
|
55
162
|
// HTML base sem SSR - apenas o container e scripts para client-side rendering
|
|
56
|
-
return `<!DOCTYPE html
|
|
163
|
+
return `<!DOCTYPE html>
|
|
164
|
+
<html lang="${htmlLang}">
|
|
165
|
+
<head>
|
|
166
|
+
${metaTags}
|
|
167
|
+
<title>${metadata.title || 'App hweb'}</title>
|
|
168
|
+
</head>
|
|
169
|
+
<body>
|
|
170
|
+
<div id="root"></div>
|
|
171
|
+
${initialDataScript}
|
|
172
|
+
${jsFiles}
|
|
173
|
+
${hotReloadScript}
|
|
174
|
+
</body>
|
|
175
|
+
</html>`;
|
|
57
176
|
}
|
|
58
177
|
// Função para determinar quais arquivos JavaScript carregar
|
|
59
178
|
function getJavaScriptFiles(req) {
|
|
60
179
|
const projectDir = process.cwd();
|
|
61
|
-
const distDir = path_1.default.join(projectDir, '
|
|
180
|
+
const distDir = path_1.default.join(projectDir, '.hight');
|
|
62
181
|
try {
|
|
63
182
|
// Verifica se existe um manifesto de chunks (gerado pelo ESBuild com splitting)
|
|
64
183
|
const manifestPath = path_1.default.join(distDir, 'manifest.json');
|
|
@@ -67,7 +186,7 @@ function getJavaScriptFiles(req) {
|
|
|
67
186
|
const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
|
|
68
187
|
const scripts = Object.values(manifest)
|
|
69
188
|
.filter((file) => file.endsWith('.js'))
|
|
70
|
-
.map((file) => `<script src="/
|
|
189
|
+
.map((file) => `<script src="/_hight/${file}"></script>`)
|
|
71
190
|
.join('');
|
|
72
191
|
return scripts;
|
|
73
192
|
}
|
|
@@ -91,17 +210,17 @@ function getJavaScriptFiles(req) {
|
|
|
91
210
|
if (jsFiles.length >= 1) {
|
|
92
211
|
// Modo chunks sem manifesto
|
|
93
212
|
return jsFiles
|
|
94
|
-
.map(file => `<script src="/
|
|
213
|
+
.map(file => `<script src="/_hight/${file}"></script>`)
|
|
95
214
|
.join('');
|
|
96
215
|
}
|
|
97
216
|
else {
|
|
98
217
|
// Modo tradicional - único arquivo
|
|
99
|
-
return '<script src="/
|
|
218
|
+
return '<script src="/_hight/main.js"></script>';
|
|
100
219
|
}
|
|
101
220
|
}
|
|
102
221
|
}
|
|
103
222
|
catch (error) {
|
|
104
223
|
// Fallback para o modo tradicional
|
|
105
|
-
return '<script src="/
|
|
224
|
+
return '<script src="/_hight/main.js"></script>';
|
|
106
225
|
}
|
|
107
226
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -62,6 +62,11 @@ export interface HightConfig {
|
|
|
62
62
|
* Padrão: 2048
|
|
63
63
|
*/
|
|
64
64
|
maxUrlLength?: number;
|
|
65
|
+
/**
|
|
66
|
+
* Habilita o log de acesso HTTP (ex: GET /api/users 200 15ms).
|
|
67
|
+
* Padrão: false
|
|
68
|
+
*/
|
|
69
|
+
accessLogging?: boolean;
|
|
65
70
|
}
|
|
66
71
|
/**
|
|
67
72
|
* Tipo da função de configuração que pode ser exportada no hightjs.config.js
|
|
@@ -72,7 +77,41 @@ export type HightConfigFunction = (phase: string, context: {
|
|
|
72
77
|
export interface Metadata {
|
|
73
78
|
title?: string;
|
|
74
79
|
description?: string;
|
|
80
|
+
keywords?: string | string[];
|
|
81
|
+
author?: string;
|
|
75
82
|
favicon?: string;
|
|
83
|
+
viewport?: string;
|
|
84
|
+
themeColor?: string;
|
|
85
|
+
canonical?: string;
|
|
86
|
+
robots?: string;
|
|
87
|
+
openGraph?: {
|
|
88
|
+
title?: string;
|
|
89
|
+
description?: string;
|
|
90
|
+
type?: string;
|
|
91
|
+
url?: string;
|
|
92
|
+
image?: string | {
|
|
93
|
+
url: string;
|
|
94
|
+
width?: number;
|
|
95
|
+
height?: number;
|
|
96
|
+
alt?: string;
|
|
97
|
+
};
|
|
98
|
+
siteName?: string;
|
|
99
|
+
locale?: string;
|
|
100
|
+
};
|
|
101
|
+
twitter?: {
|
|
102
|
+
card?: 'summary' | 'summary_large_image' | 'app' | 'player';
|
|
103
|
+
site?: string;
|
|
104
|
+
creator?: string;
|
|
105
|
+
title?: string;
|
|
106
|
+
description?: string;
|
|
107
|
+
image?: string;
|
|
108
|
+
imageAlt?: string;
|
|
109
|
+
};
|
|
110
|
+
language?: string;
|
|
111
|
+
charset?: string;
|
|
112
|
+
appleTouchIcon?: string;
|
|
113
|
+
manifest?: string;
|
|
114
|
+
other?: Record<string, string>;
|
|
76
115
|
}
|
|
77
116
|
export interface RouteConfig {
|
|
78
117
|
pattern: string;
|
package/docs/checklist.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# ✅ Checklist Mental
|
|
2
2
|
|
|
3
3
|
1. Precisa de página? Crie em `src/web/routes/...`
|
|
4
|
-
2. Precisa de endpoint? Crie em `src/
|
|
4
|
+
2. Precisa de endpoint? Crie em `src/backend/routes/...`
|
|
5
5
|
3. Precisa proteger? Use autenticação nas rotas
|
|
6
6
|
4. Precisa middleware? `middleware.ts` ou `middleware: []` na rota
|
|
7
7
|
5. Metadata? `generateMetadata` ou `metadata` no layout
|
package/docs/config.md
CHANGED
|
@@ -63,9 +63,18 @@ export default config;
|
|
|
63
63
|
| `serverTimeout` | `number` | `35000` | Timeout geral do servidor em ms |
|
|
64
64
|
| `individualRequestTimeout` | `number` | `30000` | Timeout por requisição individual em ms |
|
|
65
65
|
| `maxUrlLength` | `number` | `2048` | Tamanho máximo da URL em caracteres |
|
|
66
|
+
| `accessLogging` | `boolean` | `false` | Habilita logs de acesso HTTP (ex: `GET /api/users 200 15ms`) |
|
|
66
67
|
|
|
67
68
|
## 🎯 Casos de Uso
|
|
68
69
|
|
|
70
|
+
### Habilitar Logs de Acesso
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
const config: HightConfig = {
|
|
74
|
+
accessLogging: true, // Mostra: GET /api/users 200 15ms
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
69
78
|
### Aumentar Timeouts para Debugging
|
|
70
79
|
|
|
71
80
|
```typescript
|
|
@@ -153,6 +162,9 @@ const hightConfig: HightConfigFunction = (phase, { defaultConfig }) => {
|
|
|
153
162
|
|
|
154
163
|
// Ajustes por ambiente
|
|
155
164
|
if (phase === 'development') {
|
|
165
|
+
// Habilita logs de acesso em dev
|
|
166
|
+
config.accessLogging = true;
|
|
167
|
+
|
|
156
168
|
// Timeouts maiores para debugging
|
|
157
169
|
config.requestTimeout = 120000;
|
|
158
170
|
config.individualRequestTimeout = 120000;
|
|
@@ -160,6 +172,9 @@ const hightConfig: HightConfigFunction = (phase, { defaultConfig }) => {
|
|
|
160
172
|
// URLs longas para testes
|
|
161
173
|
config.maxUrlLength = 4096;
|
|
162
174
|
} else if (phase === 'production') {
|
|
175
|
+
// Logs de acesso também podem ser úteis em produção
|
|
176
|
+
config.accessLogging = true;
|
|
177
|
+
|
|
163
178
|
// Mais restritivo em produção
|
|
164
179
|
config.maxHeadersCount = 50;
|
|
165
180
|
config.maxUrlLength = 1024;
|
package/docs/middlewares.md
CHANGED
package/docs/rotas-backend.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# 🌐 Rotas Backend
|
|
2
2
|
|
|
3
|
-
Qualquer arquivo em `src/
|
|
3
|
+
Qualquer arquivo em `src/backend/routes` vira endpoint backend.
|
|
4
4
|
O _pattern_ pode ser qualquer caminho, não só `/api/...`!
|
|
5
5
|
|
|
6
6
|
## Exemplo Simples
|
|
7
7
|
|
|
8
|
-
`src/
|
|
8
|
+
`src/backend/routes/version.ts`:
|
|
9
9
|
|
|
10
10
|
```ts
|
|
11
11
|
import { HightJSRequest, HightJSResponse, BackendRouteConfig } from 'hightjs';
|
|
@@ -29,7 +29,7 @@ Defina `GET`, `POST`, `PUT`, `DELETE` (ou só os necessários).
|
|
|
29
29
|
|
|
30
30
|
## Rotas Dinâmicas Backend
|
|
31
31
|
|
|
32
|
-
`src/
|
|
32
|
+
`src/backend/routes/users/[id].ts` → `/users/123`
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
35
|
import { BackendRouteConfig, HightJSResponse } from "hightjs";
|
package/docs/websocket.md
CHANGED
|
@@ -47,6 +47,12 @@ const hightConfig: HightConfigFunction = (phase, { defaultConfig }) => {
|
|
|
47
47
|
* Default: 2048
|
|
48
48
|
*/
|
|
49
49
|
maxUrlLength: 2048,
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Enable HTTP access logging (e.g., GET /api/users 200 15ms)
|
|
53
|
+
* Default: false
|
|
54
|
+
*/
|
|
55
|
+
accessLogging: true,
|
|
50
56
|
};
|
|
51
57
|
|
|
52
58
|
// You can customize settings based on the phase
|
package/example/package.json
CHANGED