tova 0.4.0 → 0.4.2
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 +138 -63
- package/package.json +1 -1
- package/src/version.js +1 -1
package/bin/tova.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { resolve, basename, dirname, join, relative } from 'path';
|
|
4
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync, copyFileSync, rmSync, watch as fsWatch } from 'fs';
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync, copyFileSync, rmSync, chmodSync, renameSync, watch as fsWatch } from 'fs';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
// Bun.hash used instead of crypto.createHash for faster hashing
|
|
7
7
|
import { Lexer } from '../src/lexer/lexer.js';
|
|
@@ -4397,6 +4397,66 @@ function detectInstallMethod() {
|
|
|
4397
4397
|
return 'npm';
|
|
4398
4398
|
}
|
|
4399
4399
|
|
|
4400
|
+
function compareSemver(a, b) {
|
|
4401
|
+
// Returns: -1 if a < b, 0 if a === b, 1 if a > b
|
|
4402
|
+
const pa = a.split('.').map(Number);
|
|
4403
|
+
const pb = b.split('.').map(Number);
|
|
4404
|
+
for (let i = 0; i < 3; i++) {
|
|
4405
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return -1;
|
|
4406
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return 1;
|
|
4407
|
+
}
|
|
4408
|
+
return 0;
|
|
4409
|
+
}
|
|
4410
|
+
|
|
4411
|
+
function formatBytes(bytes) {
|
|
4412
|
+
if (bytes < 1024) return bytes + ' B';
|
|
4413
|
+
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
|
|
4414
|
+
return (bytes / 1048576).toFixed(1) + ' MB';
|
|
4415
|
+
}
|
|
4416
|
+
|
|
4417
|
+
async function downloadWithProgress(url, destPath) {
|
|
4418
|
+
const res = await fetch(url);
|
|
4419
|
+
if (!res.ok) return null;
|
|
4420
|
+
|
|
4421
|
+
const contentLength = parseInt(res.headers.get('content-length'), 10) || 0;
|
|
4422
|
+
const reader = res.body.getReader();
|
|
4423
|
+
const chunks = [];
|
|
4424
|
+
let received = 0;
|
|
4425
|
+
|
|
4426
|
+
const barWidth = 20;
|
|
4427
|
+
|
|
4428
|
+
while (true) {
|
|
4429
|
+
const { done, value } = await reader.read();
|
|
4430
|
+
if (done) break;
|
|
4431
|
+
chunks.push(value);
|
|
4432
|
+
received += value.length;
|
|
4433
|
+
|
|
4434
|
+
if (isTTY) {
|
|
4435
|
+
if (contentLength > 0) {
|
|
4436
|
+
const pct = Math.min(100, Math.round((received / contentLength) * 100));
|
|
4437
|
+
const filled = Math.round((pct / 100) * barWidth);
|
|
4438
|
+
const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(barWidth - filled);
|
|
4439
|
+
process.stdout.write(`\r Downloading... [${bar}] ${pct}% (${formatBytes(received)} / ${formatBytes(contentLength)})`);
|
|
4440
|
+
} else {
|
|
4441
|
+
process.stdout.write(`\r Downloading... ${formatBytes(received)}`);
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
|
|
4446
|
+
if (isTTY) process.stdout.write('\n');
|
|
4447
|
+
|
|
4448
|
+
// Combine chunks into a single Uint8Array
|
|
4449
|
+
const result = new Uint8Array(received);
|
|
4450
|
+
let offset = 0;
|
|
4451
|
+
for (const chunk of chunks) {
|
|
4452
|
+
result.set(chunk, offset);
|
|
4453
|
+
offset += chunk.length;
|
|
4454
|
+
}
|
|
4455
|
+
|
|
4456
|
+
writeFileSync(destPath, result);
|
|
4457
|
+
return { compressed: url.endsWith('.gz'), size: received };
|
|
4458
|
+
}
|
|
4459
|
+
|
|
4400
4460
|
async function upgradeCommand() {
|
|
4401
4461
|
console.log(`\n Current version: ${color.bold('Tova v' + VERSION)}\n`);
|
|
4402
4462
|
console.log(' Checking for updates...');
|
|
@@ -4404,102 +4464,98 @@ async function upgradeCommand() {
|
|
|
4404
4464
|
const installMethod = detectInstallMethod();
|
|
4405
4465
|
|
|
4406
4466
|
try {
|
|
4467
|
+
// Always check npm registry as the source of truth for latest version
|
|
4468
|
+
const res = await fetch('https://registry.npmjs.org/tova/latest');
|
|
4469
|
+
if (!res.ok) {
|
|
4470
|
+
console.error(color.red(' Could not reach the npm registry. Check your network connection.'));
|
|
4471
|
+
process.exit(1);
|
|
4472
|
+
}
|
|
4473
|
+
const data = await res.json();
|
|
4474
|
+
const latestVersion = data.version;
|
|
4475
|
+
|
|
4476
|
+
if (compareSemver(VERSION, latestVersion) >= 0) {
|
|
4477
|
+
console.log(` ${color.green('Already on the latest version')} (v${VERSION}).\n`);
|
|
4478
|
+
return;
|
|
4479
|
+
}
|
|
4480
|
+
|
|
4481
|
+
console.log(` New version available: ${color.green('v' + latestVersion)}\n`);
|
|
4482
|
+
|
|
4407
4483
|
if (installMethod === 'binary') {
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4484
|
+
console.log(' Upgrading via binary...');
|
|
4485
|
+
|
|
4486
|
+
// Check GitHub releases for the matching binary
|
|
4487
|
+
const ghRes = await fetch('https://api.github.com/repos/tova-lang/tova-lang/releases/latest');
|
|
4488
|
+
let ghTag = null;
|
|
4489
|
+
if (ghRes.ok) {
|
|
4490
|
+
const ghData = await ghRes.json();
|
|
4491
|
+
const ghVersion = (ghData.tag_name || '').replace(/^v/, '');
|
|
4492
|
+
if (compareSemver(ghVersion, VERSION) > 0) {
|
|
4493
|
+
ghTag = ghData.tag_name;
|
|
4494
|
+
}
|
|
4413
4495
|
}
|
|
4414
|
-
const data = await res.json();
|
|
4415
|
-
const latestVersion = (data.tag_name || '').replace(/^v/, '');
|
|
4416
4496
|
|
|
4417
|
-
if (
|
|
4418
|
-
|
|
4497
|
+
if (!ghTag) {
|
|
4498
|
+
// No newer binary release available — fall back to npm install
|
|
4499
|
+
console.log(` ${color.dim('No binary release for v' + latestVersion + ' yet. Falling back to npm...')}\n`);
|
|
4500
|
+
await npmUpgrade(latestVersion);
|
|
4419
4501
|
return;
|
|
4420
4502
|
}
|
|
4421
4503
|
|
|
4422
|
-
console.log(` New version available: ${color.green('v' + latestVersion)}\n`);
|
|
4423
|
-
console.log(' Upgrading via binary...');
|
|
4424
|
-
|
|
4425
4504
|
// Detect platform
|
|
4426
4505
|
const platform = process.platform === 'darwin' ? 'darwin' : process.platform === 'linux' ? 'linux' : 'windows';
|
|
4427
4506
|
const arch = process.arch === 'arm64' ? 'arm64' : 'x64';
|
|
4428
4507
|
const assetName = `tova-${platform}-${arch}`;
|
|
4429
|
-
const downloadUrl = `https://github.com/tova-lang/tova-lang/releases/download/${
|
|
4508
|
+
const downloadUrl = `https://github.com/tova-lang/tova-lang/releases/download/${ghTag}/${assetName}.gz`;
|
|
4430
4509
|
|
|
4431
4510
|
const installDir = join(process.env.HOME || '', '.tova', 'bin');
|
|
4432
4511
|
const tmpPath = join(installDir, 'tova.download');
|
|
4433
4512
|
const binPath = join(installDir, 'tova');
|
|
4434
4513
|
|
|
4435
|
-
//
|
|
4436
|
-
|
|
4437
|
-
|
|
4514
|
+
// Ensure install directory exists
|
|
4515
|
+
mkdirSync(installDir, { recursive: true });
|
|
4516
|
+
|
|
4517
|
+
// Download compressed binary with progress
|
|
4518
|
+
let dlResult = await downloadWithProgress(downloadUrl, tmpPath);
|
|
4519
|
+
if (!dlResult) {
|
|
4438
4520
|
// Fall back to uncompressed
|
|
4439
|
-
|
|
4440
|
-
if (!
|
|
4441
|
-
console.
|
|
4442
|
-
|
|
4521
|
+
dlResult = await downloadWithProgress(downloadUrl.replace('.gz', ''), tmpPath);
|
|
4522
|
+
if (!dlResult) {
|
|
4523
|
+
console.log(` ${color.dim('Binary download failed. Falling back to npm...')}\n`);
|
|
4524
|
+
await npmUpgrade(latestVersion);
|
|
4525
|
+
return;
|
|
4443
4526
|
}
|
|
4444
|
-
|
|
4445
|
-
|
|
4527
|
+
}
|
|
4528
|
+
|
|
4529
|
+
if (dlResult.compressed) {
|
|
4446
4530
|
// Decompress gzip
|
|
4447
|
-
|
|
4448
|
-
const
|
|
4531
|
+
console.log(' Decompressing...');
|
|
4532
|
+
const compressed = readFileSync(tmpPath);
|
|
4533
|
+
const { gunzipSync } = await import('zlib');
|
|
4534
|
+
const decompressed = gunzipSync(compressed);
|
|
4449
4535
|
writeFileSync(tmpPath, decompressed);
|
|
4450
4536
|
}
|
|
4451
4537
|
|
|
4452
4538
|
// Make executable
|
|
4453
|
-
const { chmodSync } = await import('fs');
|
|
4454
4539
|
chmodSync(tmpPath, 0o755);
|
|
4455
4540
|
|
|
4456
4541
|
// Verify the new binary works
|
|
4457
|
-
|
|
4458
|
-
|
|
4542
|
+
console.log(' Verifying binary...');
|
|
4543
|
+
const { spawnSync } = await import('child_process');
|
|
4544
|
+
const verifyProc = spawnSync(tmpPath, ['--version'], { timeout: 10000 });
|
|
4545
|
+
if (verifyProc.status !== 0) {
|
|
4459
4546
|
rmSync(tmpPath, { force: true });
|
|
4460
|
-
console.error(color.red(' Downloaded binary verification failed.'));
|
|
4461
|
-
|
|
4547
|
+
console.error(color.red(' Downloaded binary verification failed. Falling back to npm...'));
|
|
4548
|
+
await npmUpgrade(latestVersion);
|
|
4549
|
+
return;
|
|
4462
4550
|
}
|
|
4463
4551
|
|
|
4464
4552
|
// Atomic rename
|
|
4465
|
-
const { renameSync } = await import('fs');
|
|
4466
4553
|
renameSync(tmpPath, binPath);
|
|
4467
4554
|
|
|
4468
4555
|
console.log(`\n ${color.green('✓')} Upgraded: v${VERSION} -> ${color.bold('v' + latestVersion)}\n`);
|
|
4469
4556
|
} else {
|
|
4470
|
-
// npm/bun install: check npm registry
|
|
4471
|
-
const res = await fetch('https://registry.npmjs.org/tova/latest');
|
|
4472
|
-
if (!res.ok) {
|
|
4473
|
-
console.error(color.red(' Could not reach the npm registry. Check your network connection.'));
|
|
4474
|
-
process.exit(1);
|
|
4475
|
-
}
|
|
4476
|
-
const data = await res.json();
|
|
4477
|
-
const latestVersion = data.version;
|
|
4478
|
-
|
|
4479
|
-
if (latestVersion === VERSION) {
|
|
4480
|
-
console.log(` ${color.green('Already on the latest version')} (v${VERSION}).\n`);
|
|
4481
|
-
return;
|
|
4482
|
-
}
|
|
4483
|
-
|
|
4484
|
-
console.log(` New version available: ${color.green('v' + latestVersion)}\n`);
|
|
4485
4557
|
console.log(' Upgrading...');
|
|
4486
|
-
|
|
4487
|
-
const pm = detectPackageManager();
|
|
4488
|
-
const installCmd = pm === 'bun' ? ['bun', ['add', '-g', 'tova@latest']]
|
|
4489
|
-
: pm === 'pnpm' ? ['pnpm', ['add', '-g', 'tova@latest']]
|
|
4490
|
-
: pm === 'yarn' ? ['yarn', ['global', 'add', 'tova@latest']]
|
|
4491
|
-
: ['npm', ['install', '-g', 'tova@latest']];
|
|
4492
|
-
|
|
4493
|
-
const proc = spawn(installCmd[0], installCmd[1], { stdio: 'inherit' });
|
|
4494
|
-
const exitCode = await new Promise(res => proc.on('close', res));
|
|
4495
|
-
|
|
4496
|
-
if (exitCode === 0) {
|
|
4497
|
-
console.log(`\n ${color.green('✓')} Upgraded to Tova v${latestVersion}.\n`);
|
|
4498
|
-
} else {
|
|
4499
|
-
console.error(color.red(`\n Upgrade failed (exit code ${exitCode}).`));
|
|
4500
|
-
console.error(` Try manually: ${installCmd[0]} ${installCmd[1].join(' ')}\n`);
|
|
4501
|
-
process.exit(1);
|
|
4502
|
-
}
|
|
4558
|
+
await npmUpgrade(latestVersion);
|
|
4503
4559
|
}
|
|
4504
4560
|
} catch (err) {
|
|
4505
4561
|
console.error(color.red(` Upgrade failed: ${err.message}`));
|
|
@@ -4512,6 +4568,25 @@ async function upgradeCommand() {
|
|
|
4512
4568
|
}
|
|
4513
4569
|
}
|
|
4514
4570
|
|
|
4571
|
+
async function npmUpgrade(latestVersion) {
|
|
4572
|
+
const pm = detectPackageManager();
|
|
4573
|
+
const installCmd = pm === 'bun' ? ['bun', ['add', '-g', 'tova@latest']]
|
|
4574
|
+
: pm === 'pnpm' ? ['pnpm', ['add', '-g', 'tova@latest']]
|
|
4575
|
+
: pm === 'yarn' ? ['yarn', ['global', 'add', 'tova@latest']]
|
|
4576
|
+
: ['npm', ['install', '-g', 'tova@latest']];
|
|
4577
|
+
|
|
4578
|
+
const proc = spawn(installCmd[0], installCmd[1], { stdio: 'inherit' });
|
|
4579
|
+
const exitCode = await new Promise(res => proc.on('close', res));
|
|
4580
|
+
|
|
4581
|
+
if (exitCode === 0) {
|
|
4582
|
+
console.log(`\n ${color.green('✓')} Upgraded to Tova v${latestVersion}.\n`);
|
|
4583
|
+
} else {
|
|
4584
|
+
console.error(color.red(`\n Upgrade failed (exit code ${exitCode}).`));
|
|
4585
|
+
console.error(` Try manually: ${installCmd[0]} ${installCmd[1].join(' ')}\n`);
|
|
4586
|
+
process.exit(1);
|
|
4587
|
+
}
|
|
4588
|
+
}
|
|
4589
|
+
|
|
4515
4590
|
function detectPackageManager() {
|
|
4516
4591
|
if (typeof Bun !== 'undefined') return 'bun';
|
|
4517
4592
|
const ua = process.env.npm_config_user_agent || '';
|
package/package.json
CHANGED
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.4.
|
|
2
|
+
export const VERSION = "0.4.2";
|