+68
-10
@@ -51,3 +51,3 @@ #!/usr/bin/env bun | ||
| import { spawn, spawnSync } from 'node:child_process' | ||
| import { isAbsolute, resolve } from 'node:path' | ||
| import { isAbsolute, join, resolve } from 'node:path' | ||
| import { pathToFileURL } from 'node:url' | ||
@@ -318,16 +318,74 @@ | ||
| if (command === 'build') { | ||
| // Tell library code that this process exists for bundling, not for | ||
| // serving traffic. Eager side-effect modules (Redis subscribers, | ||
| // queue workers, fs watchers, scheduler ticks) read `TEKIR_RUNNER` | ||
| // and short-circuit when it equals `'build'`, the same way they do | ||
| // for `'test'`. Use `??=` so an outer caller can pin the value first | ||
| // (e.g. CI scripts wrapping `tekir build` with their own marker). | ||
| process.env.TEKIR_RUNNER ??= 'build' | ||
| ensureBunOrReexec() | ||
| // From here on we are guaranteed to be running under Bun. | ||
| // | ||
| // Run the entry through `runEntry` (same path as `serve`) so the | ||
| // user's `tekir({...})` instance gets to register `onBuild` hooks | ||
| // before we bundle. Inside `tekir()` core, `argv[2] === 'build'` is | ||
| // detected and triggers `server.build()` (which fires the hooks, | ||
| // e.g. `@tekir/vite` builds the frontend into `dist/client/`) | ||
| // followed by the actual `Bun.build` for the backend bundle. Calling | ||
| // `runBuild` directly from this bin, as we did before, skipped the | ||
| // entry entirely and silently dropped any frontend build. | ||
| // The `tekir build` entry is parsed by `@tekir/core`'s build-entry | ||
| // preparer, which extracts the top-level `await tekir({...})` call | ||
| // and emits a temporary source containing only the imports and | ||
| // declarations reachable from the call's argument expression. That | ||
| // temp file is what gets imported here; `process.argv[1]` keeps the | ||
| // original entry path so the in-app build dispatch bundles the real | ||
| // file (the temp file is throwaway). Everything else in the user | ||
| // entry — registerDir, route handlers, app.start callbacks, eager | ||
| // service constructors, scheduler ticks, fs watchers — is dropped | ||
| // for the duration of the build, so a hostile remote dependency | ||
| // (Redis with no listener, queue server, ...) never blocks the | ||
| // bundle. Entries we cannot parse statically (no literal `tekir()` | ||
| // call, multiple calls, parse error) return `null` from | ||
| // `generateBuildEntry`; in that case the cli falls through to a | ||
| // plain full-entry import as a last resort. | ||
| const entry = await findEntry(entryFlag, tekirCfg) | ||
| const rest = argv.slice(1) | ||
| await runEntry(entry, command, rest) | ||
| const absEntry = isAbsolute(entry) ? entry : resolve(process.cwd(), entry) | ||
| let result = null | ||
| try { | ||
| const { generateBuildEntry } = await import('@tekir/core') | ||
| result = await generateBuildEntry(absEntry) | ||
| } catch { | ||
| // @tekir/core not installed locally; fall through. | ||
| } | ||
| if (result && result.source) { | ||
| const { writeFileSync, unlinkSync } = await import('fs') | ||
| const { dirname } = await import('node:path') | ||
| // Write the build-entry alongside the original file (not into | ||
| // `os.tmpdir()`) so relative imports the user wrote (`import | ||
| // './types'`, `import '../config/env'`, ...) resolve from the | ||
| // expected directory. Bun resolves bare `./` specifiers relative | ||
| // to the importing file; if the temp file lives elsewhere those | ||
| // imports throw `Cannot find module './...'` even though the | ||
| // dependency is right next to the original entry. The leading dot | ||
| // in the filename hides it from most editors, and the | ||
| // `${pid}-${timestamp}` suffix keeps parallel builds from racing | ||
| // on the same path. Cleanup runs in a `finally` block; an orphan | ||
| // would survive a hard crash but the pattern is easy to spot and | ||
| // safe to delete by hand. | ||
| 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] | ||
| try { | ||
| await import(pathToFileURL(buildEntryPath).href) | ||
| } finally { | ||
| try { unlinkSync(buildEntryPath) } catch {} | ||
| } | ||
| } else { | ||
| await runEntry(entry, command, rest) | ||
| } | ||
| } | ||
@@ -334,0 +392,0 @@ |
+2
-9
| { | ||
| "name": "@tekir/cli", | ||
| "version": "0.1.4", | ||
| "version": "0.1.5", | ||
| "description": "tekir command-line tool: serve, build, generate-key, and provider-registered commands.", | ||
@@ -33,12 +33,5 @@ "author": "dev@tekir.io", | ||
| "dependencies": { | ||
| "@tekir/core": "^0.1.28" | ||
| }, | ||
| "peerDependencies": { | ||
| "@tekir/core": "^0.1.29", | ||
| "oxc-parser": ">=0.30.0" | ||
| }, | ||
| "peerDependenciesMeta": { | ||
| "oxc-parser": { | ||
| "optional": true | ||
| } | ||
| }, | ||
| "engines": { | ||
@@ -45,0 +38,0 @@ "node": ">=18.0.0" |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
22702
15.97%416
16.2%8
33.33%+ Added
Updated