@alumbwe/anvil 1.0.40 → 1.0.42

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/anvil.js ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Anvil CLI launcher — the npm `bin` entry for `anvil`.
4
+ *
5
+ * Runs on plain Node.js (Bun is NOT required upfront) and works from cmd,
6
+ * PowerShell and POSIX shells: npm generates a shim for each shell that all
7
+ * execute this file with `node`.
8
+ *
9
+ * Strategy:
10
+ * 1. Already running under Bun (e.g. `bunx anvil`) → import the TS entry directly.
11
+ * 2. Else locate a Bun executable: local node_modules/.bin, global
12
+ * node_modules/.bin, ~/.bun/bin, /usr/local/bin, Homebrew, then PATH.
13
+ * 3. Else install Bun once via `npm i -g bun`, then retry.
14
+ * 4. Spawn `bun src/entry.ts <args>` with inherited stdio and forward the
15
+ * exit code, so the TUI behaves exactly as when run directly.
16
+ */
17
+
18
+ import { spawnSync } from 'child_process'
19
+ import fs from 'fs'
20
+ import os from 'os'
21
+ import path from 'path'
22
+ import { fileURLToPath, pathToFileURL } from 'url'
23
+
24
+ const HERE = path.dirname(fileURLToPath(import.meta.url))
25
+ const PKG_ROOT = path.resolve(HERE, '..')
26
+ const ENTRY = path.join(PKG_ROOT, 'src', 'entry.ts')
27
+ const IS_WIN = process.platform === 'win32'
28
+ const BUN_EXE = IS_WIN ? 'bun.exe' : 'bun'
29
+
30
+ /** Check that a candidate command is a working bun executable. */
31
+ function isBun(cmd) {
32
+ try {
33
+ const r = spawnSync(cmd, ['--version'], {
34
+ encoding: 'utf8',
35
+ timeout: 15000,
36
+ windowsHide: true,
37
+ })
38
+ return !r.error && r.status === 0
39
+ } catch {
40
+ return false
41
+ }
42
+ }
43
+
44
+ /** Locate a usable bun executable without assuming anything about the machine. */
45
+ function findBun() {
46
+ // Already running under Bun itself
47
+ if (process.versions.bun) return process.execPath
48
+
49
+ const candidates = [
50
+ // Local install: <pkg>/node_modules/.bin/bun
51
+ path.join(PKG_ROOT, 'node_modules', '.bin', BUN_EXE),
52
+ // Hoisted install: <prefix>/node_modules/.bin/bun
53
+ // global: <prefix>/node_modules/@alumbwe/anvil → ../../.bin
54
+ // project: <project>/node_modules/@alumbwe/anvil → ../../.bin
55
+ path.join(PKG_ROOT, '..', '..', '.bin', BUN_EXE),
56
+ // Official bun installer location
57
+ path.join(os.homedir(), '.bun', 'bin', BUN_EXE),
58
+ ]
59
+ if (!IS_WIN) {
60
+ candidates.push('/usr/local/bin/bun', '/opt/homebrew/bin/bun')
61
+ }
62
+
63
+ for (const candidate of candidates) {
64
+ if (fs.existsSync(candidate) && isBun(candidate)) return candidate
65
+ }
66
+
67
+ // Last resort: let the OS resolve `bun` from PATH.
68
+ if (isBun(BUN_EXE)) return BUN_EXE
69
+ return null
70
+ }
71
+
72
+ /** One-time automatic install of Bun through npm (same prefix as this CLI). */
73
+ function installBun() {
74
+ console.error('')
75
+ console.error('Anvil runs on the Bun runtime, which was not found on this machine.')
76
+ console.error('Installing Bun via npm (one-time, this may take a minute)...')
77
+ console.error('')
78
+ const r = spawnSync('npm', ['install', '-g', 'bun'], {
79
+ stdio: 'inherit',
80
+ shell: IS_WIN,
81
+ windowsHide: true,
82
+ })
83
+ return r.status === 0
84
+ }
85
+
86
+ function printManualInstructions() {
87
+ console.error('')
88
+ console.error('Could not install Bun automatically. Install it manually, then re-run anvil:')
89
+ if (IS_WIN) {
90
+ console.error(' powershell -c "irm bun.sh/install.ps1 | iex"')
91
+ } else {
92
+ console.error(' curl -fsSL https://bun.sh/install | bash')
93
+ }
94
+ console.error(' (or) npm i -g bun')
95
+ console.error('')
96
+ }
97
+
98
+ // Under Bun: run the TypeScript entry in-process.
99
+ if (process.versions.bun) {
100
+ await import(pathToFileURL(ENTRY).href)
101
+ } else {
102
+ let bun = findBun()
103
+ if (!bun && installBun()) bun = findBun()
104
+ if (!bun) {
105
+ printManualInstructions()
106
+ process.exit(1)
107
+ }
108
+
109
+ const result = spawnSync(bun, [ENTRY, ...process.argv.slice(2)], {
110
+ stdio: 'inherit',
111
+ env: process.env,
112
+ windowsHide: true,
113
+ })
114
+
115
+ if (result.error) {
116
+ console.error(`Failed to start Anvil via Bun (${bun}):`, result.error.message)
117
+ process.exit(1)
118
+ }
119
+ process.exit(result.status ?? (result.signal ? 1 : 0))
120
+ }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@alumbwe/anvil",
3
- "version": "1.0.40",
3
+ "version": "1.0.42",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
7
- "anvil": "./bin/anvil"
7
+ "anvil": "./bin/anvil.js"
8
8
  },
9
9
  "files": [
10
- "bin/anvil",
10
+ "bin/anvil.js",
11
11
  "bin/tree-sitter.wasm",
12
12
  "src/",
13
13
  "vendor/",
package/src/entry.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
+ // MUST be first: applies production defaults for NEXT_PUBLIC_* before any
4
+ // module in the import graph evaluates `@anvil/common/env`, which validates
5
+ // the client env at import time and throws on machines without an env file.
6
+ import './pre-init/client-env'
7
+
3
8
  import {
4
9
  isTerminalCommandBrokerInvocation,
5
10
  serveTerminalCommandBroker,
@@ -0,0 +1,17 @@
1
+ // Apply production defaults for NEXT_PUBLIC_* vars BEFORE anything that
2
+ // imports `@anvil/common/env`, which validates the client env at import time
3
+ // and throws on a machine without an env file (any global npm install).
4
+ //
5
+ // Must be imported before `./index` in `entry.ts`, mirroring the tree-sitter
6
+ // pre-init: both prepare runtime state the eagerly-evaluated import graph
7
+ // needs at module-evaluation time.
8
+ //
9
+ // Gap-filling only: real values already in process.env (from ~/.env.local,
10
+ // the shell, or --define flags baked into compiled binaries) always win.
11
+ import { CLIENT_ENV_DEFAULTS } from '../utils/client-env-defaults'
12
+
13
+ for (const [key, value] of Object.entries(CLIENT_ENV_DEFAULTS)) {
14
+ if (process.env[key] === undefined) {
15
+ process.env[key] = value
16
+ }
17
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Production defaults for the NEXT_PUBLIC_* client env vars.
3
+ *
4
+ * `@anvil/common/env` validates all NEXT_PUBLIC_* vars at *import time*, so a
5
+ * global npm install (no .env.local anywhere on the machine) dies before any
6
+ * of our code runs. These defaults — all public-by-design values, the same
7
+ * set `cli/scripts/build-binary.ts` bakes into compiled binaries — are applied
8
+ * to `process.env` at pre-init so the CLI boots on any machine, cmd or
9
+ * PowerShell, with or without an env file.
10
+ *
11
+ * IMPORTANT: the pre-init loader (`cli/src/pre-init/client-env.ts`) imports
12
+ * this table, and `build-binary.ts` reads it at build time. Keep the two in
13
+ * sync through this module — never duplicate the values.
14
+ *
15
+ * User-set values always win: the loader only fills gaps, it never overwrites
16
+ * anything already in process.env (including values Bun loads from
17
+ * .env.local in dev, and the --define flags baked into binaries).
18
+ */
19
+ export const CLIENT_ENV_DEFAULTS: Readonly<Record<string, string>> = {
20
+ NEXT_PUBLIC_CB_ENVIRONMENT: 'prod',
21
+ NEXT_PUBLIC_ANVIL_APP_URL: 'https://anvil.dev',
22
+ NEXT_PUBLIC_SUPPORT_EMAIL: 'support@anvil.dev',
23
+ NEXT_PUBLIC_POSTHOG_API_KEY: 'phc_placeholder',
24
+ NEXT_PUBLIC_POSTHOG_HOST_URL: 'https://us.i.posthog.com',
25
+ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: 'pk_placeholder',
26
+ NEXT_PUBLIC_STRIPE_CUSTOMER_PORTAL: 'https://billing.stripe.com/portal',
27
+ NEXT_PUBLIC_WEB_PORT: '3000',
28
+ }