Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@tekir/cli

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tekir/cli - npm Package Compare versions

Comparing version
0.1.5
to
0.1.6
+76
-27
bin/tekir.mjs

@@ -51,3 +51,3 @@ #!/usr/bin/env bun

import { spawn, spawnSync } from 'node:child_process'
import { isAbsolute, join, resolve } from 'node:path'
import { isAbsolute, join, resolve, relative, sep } from 'node:path'
import { pathToFileURL } from 'node:url'

@@ -57,3 +57,29 @@

// Marker passed to a re-exec'd child so it doesn't re-load env files the
// parent already applied (the child inherits process.env, so a second load
// would re-emit "not found" warnings and re-apply the same values).
const ENV_LOADED_MARKER = 'TEKIR_ENV_LOADED'
/**
* Resolve an entry path against cwd and confine it under cwd. The entry is
* `import()`-executed, and it can come from `--entry` or `package.json`
* (`tekir.entry`), so a value pointing outside the project (e.g.
* `../../evil.ts`) must be rejected rather than run.
*
* @param {string} entry - The raw entry path.
* @returns {string} The absolute, confined entry path.
*/
function resolveEntryPath(entry) {
const cwd = process.cwd()
const abs = isAbsolute(entry) ? entry : resolve(cwd, entry)
const rel = relative(cwd, abs)
if (rel === '' || rel.startsWith('..' + sep) || rel === '..' || isAbsolute(rel)) {
console.error(`[tekir] Refusing to load entry outside the project directory: ${entry}`)
console.error(` The entry must live under ${cwd}.`)
process.exit(1)
}
return abs
}
/**
* Pull `--entry`, `--envfile`, and `--env-file` (multi for the env

@@ -157,8 +183,12 @@ * variants) out of argv. Everything else stays in `rest` in original

let value = line.slice(eq + 1).trim()
if (value.length >= 2) {
const f = value[0]
const l = value[value.length - 1]
if ((f === '"' && l === '"') || (f === "'" && l === "'")) {
value = value.slice(1, -1)
}
if (value.length >= 2 && ((value[0] === '"' && value[value.length - 1] === '"') ||
(value[0] === "'" && value[value.length - 1] === "'"))) {
// Quoted value: take the content verbatim (a `#` inside quotes is part
// of the value, not a comment).
value = value.slice(1, -1)
} else {
// Unquoted value: strip a trailing inline `# comment` (must be preceded
// by whitespace so a `#` inside a token like `pa#ss` stays intact).
const hashIdx = value.search(/\s#/)
if (hashIdx !== -1) value = value.slice(0, hashIdx).trimEnd()
}

@@ -251,3 +281,3 @@ if (!shellKeys.has(key)) process.env[key] = value

async function runEntry(entry, command, args) {
const absEntry = isAbsolute(entry) ? entry : resolve(process.cwd(), entry)
const absEntry = resolveEntryPath(entry)
if (!existsSync(absEntry)) {

@@ -308,5 +338,9 @@ console.error(`[tekir] Entry file not found: ${absEntry}`)

const allEnvFiles = [...jsonEnvFiles, ...cliEnvFiles]
if (allEnvFiles.length > 0) {
// Skip loading when a parent process already did it (re-exec under Bun for
// `tekir build`). The child inherits process.env, so re-loading would only
// re-emit "not found" warnings and re-apply identical values.
if (allEnvFiles.length > 0 && !process.env[ENV_LOADED_MARKER]) {
const shellKeys = new Set(Object.keys(process.env))
for (const f of allEnvFiles) loadEnvFile(f, shellKeys)
process.env[ENV_LOADED_MARKER] = '1'
}

@@ -349,3 +383,3 @@

const rest = argv.slice(1)
const absEntry = isAbsolute(entry) ? entry : resolve(process.cwd(), entry)
const absEntry = resolveEntryPath(entry)
let result = null

@@ -374,20 +408,33 @@ try {

const buildEntryPath = join(dirname(absEntry), `.tekir-build-entry-${process.pid}-${Date.now()}.ts`)
writeFileSync(buildEntryPath, result.source)
// The in-app build dispatcher calls `process.exit(0)` once
// `Bun.build` resolves, which short-circuits any `try/finally`
// unlink we'd schedule below. `process.on('exit')` runs
// synchronously at the very end of the exit sequence and is
// allowed to do filesystem work, so the temp file goes away on
// every successful build. The handler also covers a non-zero exit
// (build failure) since `process.exit(N)` fires the same event.
process.on('exit', () => { try { unlinkSync(buildEntryPath) } catch {} })
// `process.argv[1]` MUST stay on the original entry so the in-app
// build dispatcher bundles the real file. We import the temp file
// only to fire the `tekir({...})` call.
process.argv = [process.argv[0], absEntry, command, ...rest]
let wrote = false
try {
await import(pathToFileURL(buildEntryPath).href)
} finally {
try { unlinkSync(buildEntryPath) } catch {}
writeFileSync(buildEntryPath, result.source)
wrote = true
} catch (err) {
// A read-only source directory (CI, read-only mount) can't host the
// temp build entry; fall back to a plain full-entry import instead of
// crashing mid-build with a raw filesystem error.
console.warn(`[tekir] Could not write temp build entry (${err.message}); building from the full entry instead.`)
}
if (wrote) {
// The in-app build dispatcher calls `process.exit(0)` once
// `Bun.build` resolves, which short-circuits any `try/finally`
// unlink we'd schedule below. `process.on('exit')` runs
// synchronously at the very end of the exit sequence and is
// allowed to do filesystem work, so the temp file goes away on
// every successful build. The handler also covers a non-zero exit
// (build failure) since `process.exit(N)` fires the same event.
process.on('exit', () => { try { unlinkSync(buildEntryPath) } catch {} })
// `process.argv[1]` MUST stay on the original entry so the in-app
// build dispatcher bundles the real file. We import the temp file
// only to fire the `tekir({...})` call.
process.argv = [process.argv[0], absEntry, command, ...rest]
try {
await import(pathToFileURL(buildEntryPath).href)
} finally {
try { unlinkSync(buildEntryPath) } catch {}
}
} else {
await runEntry(entry, command, rest)
}
} else {

@@ -410,3 +457,5 @@ await runEntry(entry, command, rest)

const runner = isBun ? 'bun' : 'npx'
const runnerArgs = isBun ? ['test', ...argv.slice(1)] : ['vitest', 'run', ...argv.slice(1)]
// `--no-install` so a missing local vitest fails fast instead of npx
// silently fetching it from the registry (unexpected network / wrong pkg).
const runnerArgs = isBun ? ['test', ...argv.slice(1)] : ['--no-install', 'vitest', 'run', ...argv.slice(1)]
const proc = spawn(runner, runnerArgs, {

@@ -413,0 +462,0 @@ stdio: 'inherit',

{
"name": "@tekir/cli",
"version": "0.1.5",
"version": "0.1.6",
"description": "tekir command-line tool: serve, build, generate-key, and provider-registered commands.",

@@ -33,3 +33,3 @@ "author": "dev@tekir.io",

"dependencies": {
"@tekir/core": "^0.1.29",
"@tekir/core": "^0.1.34",
"oxc-parser": ">=0.30.0"

@@ -36,0 +36,0 @@ },