tova 0.9.8 → 0.9.10
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/bin/tova.js +108 -18
- package/package.json +1 -1
- package/src/parser/auth-parser.js +2 -2
- package/src/version.js +1 -1
package/bin/tova.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { resolve, basename, dirname, join, relative, sep, extname } from 'path';
|
|
4
4
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync, copyFileSync, rmSync, chmodSync, renameSync, watch as fsWatch } from 'fs';
|
|
5
|
-
import { spawn } from 'child_process';
|
|
5
|
+
import { spawn, spawnSync as _spawnSync } from 'child_process';
|
|
6
6
|
import { createHash as _cryptoHash } from 'crypto';
|
|
7
7
|
import { createRequire as _createRequire } from 'module';
|
|
8
8
|
import { Lexer } from '../src/lexer/lexer.js';
|
|
@@ -25,6 +25,78 @@ import { addToSection, removeFromSection } from '../src/config/edit-toml.js';
|
|
|
25
25
|
import { stringifyTOML } from '../src/config/toml.js';
|
|
26
26
|
|
|
27
27
|
import { VERSION } from '../src/version.js';
|
|
28
|
+
import { createServer as _createHttpServer } from 'http';
|
|
29
|
+
|
|
30
|
+
const _hasBun = typeof Bun !== 'undefined';
|
|
31
|
+
|
|
32
|
+
// ─── Compat: Bun.serve() fallback to Node http.createServer ─
|
|
33
|
+
function _compatServe({ port, fetch: fetchHandler }) {
|
|
34
|
+
if (_hasBun) {
|
|
35
|
+
return Bun.serve({ port, fetch: fetchHandler });
|
|
36
|
+
}
|
|
37
|
+
// Node.js fallback using http.createServer
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const server = _createHttpServer(async (req, res) => {
|
|
40
|
+
try {
|
|
41
|
+
const url = `http://localhost:${port}${req.url}`;
|
|
42
|
+
const headers = new Headers();
|
|
43
|
+
for (let i = 0; i < req.rawHeaders.length; i += 2) {
|
|
44
|
+
headers.append(req.rawHeaders[i], req.rawHeaders[i + 1]);
|
|
45
|
+
}
|
|
46
|
+
const request = new Request(url, {
|
|
47
|
+
method: req.method,
|
|
48
|
+
headers,
|
|
49
|
+
...(req.method !== 'GET' && req.method !== 'HEAD' ? { body: req, duplex: 'half' } : {}),
|
|
50
|
+
});
|
|
51
|
+
const response = await fetchHandler(request);
|
|
52
|
+
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
53
|
+
if (response.body) {
|
|
54
|
+
const reader = response.body.getReader();
|
|
55
|
+
const pump = async () => {
|
|
56
|
+
while (true) {
|
|
57
|
+
const { done, value } = await reader.read();
|
|
58
|
+
if (done) { res.end(); return; }
|
|
59
|
+
res.write(value);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
pump().catch(() => res.end());
|
|
63
|
+
} else {
|
|
64
|
+
const buf = Buffer.from(await response.arrayBuffer());
|
|
65
|
+
res.end(buf);
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
res.writeHead(500);
|
|
69
|
+
res.end('Internal Server Error');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
server.listen(port, () => resolve(server));
|
|
73
|
+
server.on('error', reject);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Compat: Bun.spawnSync fallback to child_process.spawnSync ─
|
|
78
|
+
// Accepts Bun-style opts: { stdout: 'pipe', stderr: 'pipe', cwd, timeout }
|
|
79
|
+
function _compatSpawnSync(cmd, args, opts) {
|
|
80
|
+
if (_hasBun) return Bun.spawnSync([cmd, ...args], opts);
|
|
81
|
+
// Translate Bun-style stdout/stderr to Node-style stdio
|
|
82
|
+
const nodeOpts = { ...opts };
|
|
83
|
+
if (!nodeOpts.stdio) {
|
|
84
|
+
nodeOpts.stdio = [
|
|
85
|
+
'pipe',
|
|
86
|
+
nodeOpts.stdout === 'pipe' ? 'pipe' : (nodeOpts.stdout || 'pipe'),
|
|
87
|
+
nodeOpts.stderr === 'pipe' ? 'pipe' : (nodeOpts.stderr || 'pipe'),
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
delete nodeOpts.stdout;
|
|
91
|
+
delete nodeOpts.stderr;
|
|
92
|
+
const result = _spawnSync(cmd, args, nodeOpts);
|
|
93
|
+
return {
|
|
94
|
+
...result,
|
|
95
|
+
exitCode: result.status,
|
|
96
|
+
stdout: result.stdout ? (typeof result.stdout === 'string' ? result.stdout : result.stdout.toString()) : '',
|
|
97
|
+
stderr: result.stderr ? (typeof result.stderr === 'string' ? result.stderr : result.stderr.toString()) : '',
|
|
98
|
+
};
|
|
99
|
+
}
|
|
28
100
|
|
|
29
101
|
// ─── CLI Color Helpers ──────────────────────────────────────
|
|
30
102
|
const isTTY = process.stdout?.isTTY;
|
|
@@ -1417,10 +1489,17 @@ function cleanBuild(args) {
|
|
|
1417
1489
|
|
|
1418
1490
|
async function devServer(args) {
|
|
1419
1491
|
const config = resolveConfig(process.cwd());
|
|
1420
|
-
|
|
1421
|
-
const srcDir = resolve(explicitSrc || config.project.entry || '.');
|
|
1492
|
+
// Parse --port value first, then filter positional args (skip flag values)
|
|
1422
1493
|
const explicitPort = args.find((_, i, a) => a[i - 1] === '--port');
|
|
1423
|
-
const basePort = parseInt(explicitPort || config.dev
|
|
1494
|
+
const basePort = parseInt(explicitPort || config.dev?.port || '3000');
|
|
1495
|
+
const flagsWithValues = new Set(['--port']);
|
|
1496
|
+
const positional = [];
|
|
1497
|
+
for (let i = 0; i < args.length; i++) {
|
|
1498
|
+
if (args[i].startsWith('--')) { if (flagsWithValues.has(args[i])) i++; continue; }
|
|
1499
|
+
positional.push(args[i]);
|
|
1500
|
+
}
|
|
1501
|
+
const explicitSrc = positional[0];
|
|
1502
|
+
const srcDir = resolve(explicitSrc || config.project?.entry || '.');
|
|
1424
1503
|
const buildStrict = args.includes('--strict');
|
|
1425
1504
|
const buildStrictSecurity = args.includes('--strict-security');
|
|
1426
1505
|
|
|
@@ -1438,7 +1517,7 @@ async function devServer(args) {
|
|
|
1438
1517
|
let actualReloadPort = reloadPort;
|
|
1439
1518
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
1440
1519
|
try {
|
|
1441
|
-
reloadServer =
|
|
1520
|
+
reloadServer = await _compatServe({
|
|
1442
1521
|
port: actualReloadPort,
|
|
1443
1522
|
fetch(req) {
|
|
1444
1523
|
return handleReloadFetch(req);
|
|
@@ -1624,7 +1703,7 @@ async function devServer(args) {
|
|
|
1624
1703
|
'.map': 'application/json',
|
|
1625
1704
|
};
|
|
1626
1705
|
|
|
1627
|
-
const staticServer =
|
|
1706
|
+
const staticServer = await _compatServe({
|
|
1628
1707
|
port: basePort,
|
|
1629
1708
|
async fetch(req) {
|
|
1630
1709
|
const url = new URL(req.url);
|
|
@@ -3373,8 +3452,8 @@ tova add github.com/yourname/${projectName}
|
|
|
3373
3452
|
|
|
3374
3453
|
// git init (silent, only if git is available)
|
|
3375
3454
|
try {
|
|
3376
|
-
const gitProc =
|
|
3377
|
-
if (gitProc.exitCode === 0) {
|
|
3455
|
+
const gitProc = _compatSpawnSync('git', ['init'], { cwd: projectDir, stdout: 'pipe', stderr: 'pipe' });
|
|
3456
|
+
if ((gitProc.exitCode ?? gitProc.status) === 0) {
|
|
3378
3457
|
console.log(` ${color.green('✓')} Initialized git repository`);
|
|
3379
3458
|
}
|
|
3380
3459
|
} catch {}
|
|
@@ -4162,6 +4241,9 @@ function hasNpmImports(code) {
|
|
|
4162
4241
|
}
|
|
4163
4242
|
|
|
4164
4243
|
async function bundleClientCode(clientCode, srcDir) {
|
|
4244
|
+
if (!_hasBun) {
|
|
4245
|
+
throw new Error('Client bundling with npm imports requires Bun. Install from https://bun.sh and run with: bun tova build --production');
|
|
4246
|
+
}
|
|
4165
4247
|
const tmpDir = join(srcDir, '.tova-out', '.tmp-bundle');
|
|
4166
4248
|
try {
|
|
4167
4249
|
mkdirSync(tmpDir, { recursive: true });
|
|
@@ -4897,7 +4979,10 @@ async function productionBuild(srcDir, outDir, isStatic = false) {
|
|
|
4897
4979
|
const allSharedCode = sharedParts.join('\n');
|
|
4898
4980
|
|
|
4899
4981
|
// Generate content hash for cache busting
|
|
4900
|
-
const hashCode = (s) =>
|
|
4982
|
+
const hashCode = (s) => {
|
|
4983
|
+
if (_hasBun) return Bun.hash(s).toString(16).slice(0, 12);
|
|
4984
|
+
return _cryptoHash('md5').update(s).digest('hex').slice(0, 12);
|
|
4985
|
+
};
|
|
4901
4986
|
|
|
4902
4987
|
// Write server bundle
|
|
4903
4988
|
if (allServerCode.trim()) {
|
|
@@ -6024,12 +6109,13 @@ async function doctorCommand() {
|
|
|
6024
6109
|
|
|
6025
6110
|
// 2. Bun availability
|
|
6026
6111
|
try {
|
|
6027
|
-
const bunProc =
|
|
6028
|
-
const bunVer = bunProc.stdout.toString().trim();
|
|
6029
|
-
if (bunProc.exitCode === 0 && bunVer) {
|
|
6112
|
+
const bunProc = _compatSpawnSync('bun', ['--version'], { stdout: 'pipe', stderr: 'pipe' });
|
|
6113
|
+
const bunVer = (bunProc.stdout || '').toString().trim();
|
|
6114
|
+
if ((bunProc.exitCode ?? bunProc.status) === 0 && bunVer) {
|
|
6030
6115
|
const major = parseInt(bunVer.split('.')[0], 10);
|
|
6031
6116
|
if (major >= 1) {
|
|
6032
|
-
|
|
6117
|
+
const whichProc = _compatSpawnSync('which', ['bun'], { stdout: 'pipe', stderr: 'pipe' });
|
|
6118
|
+
pass(`Bun v${bunVer}`, (whichProc.stdout || '').toString().trim());
|
|
6033
6119
|
} else {
|
|
6034
6120
|
warn(`Bun v${bunVer}`, 'Bun >= 1.0 recommended');
|
|
6035
6121
|
}
|
|
@@ -6077,9 +6163,9 @@ async function doctorCommand() {
|
|
|
6077
6163
|
|
|
6078
6164
|
// 5. git
|
|
6079
6165
|
try {
|
|
6080
|
-
const gitProc =
|
|
6081
|
-
if (gitProc.exitCode === 0) {
|
|
6082
|
-
const gitVer = gitProc.stdout.toString().trim();
|
|
6166
|
+
const gitProc = _compatSpawnSync('git', ['--version'], { stdout: 'pipe', stderr: 'pipe' });
|
|
6167
|
+
if ((gitProc.exitCode ?? gitProc.status) === 0) {
|
|
6168
|
+
const gitVer = (gitProc.stdout || '').toString().trim();
|
|
6083
6169
|
pass('git available', gitVer);
|
|
6084
6170
|
} else {
|
|
6085
6171
|
warn('git', 'not found');
|
|
@@ -6323,6 +6409,10 @@ function detectInstallMethod() {
|
|
|
6323
6409
|
const execPath = process.execPath || process.argv[0];
|
|
6324
6410
|
const scriptPath = process.argv[1] || '';
|
|
6325
6411
|
if (execPath.includes('.tova/bin') || scriptPath.includes('.tova/')) return 'binary';
|
|
6412
|
+
// Check if ~/.tova/bin/tova exists — indicates binary/wrapper install even if
|
|
6413
|
+
// the wrapper points to a local repo checkout
|
|
6414
|
+
const wrapperPath = join(process.env.HOME || '', '.tova', 'bin', 'tova');
|
|
6415
|
+
if (existsSync(wrapperPath)) return 'binary';
|
|
6326
6416
|
return 'npm';
|
|
6327
6417
|
}
|
|
6328
6418
|
|
|
@@ -6584,8 +6674,8 @@ async function infoCommand() {
|
|
|
6584
6674
|
// Bun version
|
|
6585
6675
|
let bunVersion = 'not found';
|
|
6586
6676
|
try {
|
|
6587
|
-
const proc =
|
|
6588
|
-
bunVersion = proc.stdout.toString().trim();
|
|
6677
|
+
const proc = _compatSpawnSync('bun', ['--version'], { stdout: 'pipe', stderr: 'pipe' });
|
|
6678
|
+
bunVersion = (proc.stdout || '').toString().trim();
|
|
6589
6679
|
} catch {}
|
|
6590
6680
|
console.log(` Runtime: Bun v${bunVersion}`);
|
|
6591
6681
|
console.log(` Platform: ${process.platform} ${process.arch}`);
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import { TokenType } from '../lexer/tokens.js';
|
|
|
5
5
|
import * as AST from './ast.js';
|
|
6
6
|
import { AuthConfigField, AuthProviderDeclaration, AuthHookDeclaration, AuthProtectedRoute } from './auth-ast.js';
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const AUTH_CONFIG_KEY_TOKENS = new Set([
|
|
9
9
|
TokenType.IDENTIFIER, TokenType.TYPE, TokenType.STORE,
|
|
10
10
|
TokenType.FN, TokenType.MATCH, TokenType.IF,
|
|
11
11
|
]);
|
|
@@ -15,7 +15,7 @@ export function installAuthParser(ParserClass) {
|
|
|
15
15
|
ParserClass.prototype._authParserInstalled = true;
|
|
16
16
|
|
|
17
17
|
ParserClass.prototype._expectAuthConfigKey = function(context) {
|
|
18
|
-
if (
|
|
18
|
+
if (AUTH_CONFIG_KEY_TOKENS.has(this.current().type)) {
|
|
19
19
|
return this.advance().value;
|
|
20
20
|
}
|
|
21
21
|
this.error(`Expected ${context} config key`);
|
package/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by scripts/embed-runtime.js — do not edit
|
|
2
|
-
export const VERSION = "0.9.
|
|
2
|
+
export const VERSION = "0.9.10";
|