@beechcms/cli 0.5.0 → 0.6.0-preview.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.
Files changed (45) hide show
  1. package/coverage/base.css +224 -0
  2. package/coverage/block-navigation.js +87 -0
  3. package/coverage/favicon.png +0 -0
  4. package/coverage/index.html +116 -0
  5. package/coverage/lcov-report/base.css +224 -0
  6. package/coverage/lcov-report/block-navigation.js +87 -0
  7. package/coverage/lcov-report/favicon.png +0 -0
  8. package/coverage/lcov-report/index.html +116 -0
  9. package/coverage/lcov-report/prettify.css +1 -0
  10. package/coverage/lcov-report/prettify.js +2 -0
  11. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  12. package/coverage/lcov-report/sorter.js +210 -0
  13. package/coverage/lcov-report/validate.ts.html +325 -0
  14. package/coverage/lcov.info +80 -0
  15. package/coverage/prettify.css +1 -0
  16. package/coverage/prettify.js +2 -0
  17. package/coverage/sort-arrow-sprite.png +0 -0
  18. package/coverage/sorter.js +210 -0
  19. package/coverage/validate.ts.html +325 -0
  20. package/dist/commands/seed-load.d.ts +8 -0
  21. package/dist/commands/seed-load.d.ts.map +1 -0
  22. package/dist/commands/seed-load.js +89 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +241 -72
  26. package/dist/lib/schema-diff.d.ts +15 -0
  27. package/dist/lib/schema-diff.d.ts.map +1 -0
  28. package/dist/lib/schema-diff.js +37 -0
  29. package/dist/lib/wrangler.d.ts +17 -0
  30. package/dist/lib/wrangler.d.ts.map +1 -0
  31. package/dist/lib/wrangler.js +65 -0
  32. package/package.json +9 -5
  33. package/src/commands/deploy.ts +126 -123
  34. package/src/commands/init.ts +599 -557
  35. package/src/commands/onboard.ts +32 -0
  36. package/src/commands/seed-create.ts +192 -189
  37. package/src/commands/seed-load.ts +235 -155
  38. package/src/commands/update.ts +54 -51
  39. package/src/commands/validate.ts +80 -83
  40. package/src/index.ts +17 -12
  41. package/src/lib/schema-diff.ts +150 -64
  42. package/src/lib/wrangler.ts +129 -106
  43. package/tsconfig.json +16 -16
  44. package/tsconfig.tsbuildinfo +1 -1
  45. package/vitest.config.ts +33 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beechcms/cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.0-preview.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -8,14 +8,18 @@
8
8
  },
9
9
  "scripts": {
10
10
  "build": "tsc --noEmit && esbuild src/index.ts --bundle --packages=external --platform=node --format=esm --outfile=dist/index.js",
11
- "dev": "esbuild src/index.ts --bundle --packages=external --platform=node --format=esm --outfile=dist/index.js --watch"
11
+ "dev": "esbuild src/index.ts --bundle --packages=external --platform=node --format=esm --outfile=dist/index.js --watch",
12
+ "test": "vitest run",
13
+ "test:coverage": "vitest run --coverage"
12
14
  },
13
15
  "dependencies": {
14
- "@beechcms/core": "^0.5.0",
16
+ "@beechcms/core": "^0.6.0-preview.2",
15
17
  "picocolors": "^1.1.1"
16
18
  },
17
19
  "devDependencies": {
18
20
  "esbuild": "^0.27.3",
19
- "typescript": "^5.9.3"
20
- }
21
+ "typescript": "^5.9.3",
22
+ "vitest": "^4.1.0"
23
+ },
24
+ "license": "MIT"
21
25
  }
@@ -1,123 +1,126 @@
1
- import pc from 'picocolors'
2
- import { spawnSync } from 'node:child_process'
3
- import { readFileSync } from 'node:fs'
4
- import { findWranglerConfig } from '../lib/wrangler.js'
5
-
6
- export interface DeployOptions {
7
- skipSeed?: boolean
8
- skipCheck?: boolean
9
- }
10
-
11
- function readWorkerName(configPath: string | null): string | null {
12
- if (!configPath) return null
13
- try {
14
- const raw = readFileSync(configPath, 'utf-8')
15
- const stripped = raw
16
- .replace(/\/\/[^\n]*/g, '')
17
- .replace(/\/\*[\s\S]*?\*\//g, '')
18
- const parsed = JSON.parse(stripped)
19
- return (parsed?.name as string) ?? null
20
- } catch {
21
- return null
22
- }
23
- }
24
-
25
- // Extracts the first workers.dev URL from wrangler deploy stdout.
26
- // wrangler writes progress to stderr (shown live) and the summary to stdout (captured).
27
- function extractWorkerUrl(output: string): string | null {
28
- const match = output.match(/https:\/\/[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+\.workers\.dev\b/)
29
- return match?.[0] ?? null
30
- }
31
-
32
- async function checkAdmin(url: string): Promise<{ ok: boolean; status: number | null }> {
33
- try {
34
- const res = await fetch(`${url}/admin`, {
35
- method: 'HEAD',
36
- redirect: 'follow',
37
- signal: AbortSignal.timeout(12_000),
38
- })
39
- return { ok: res.status < 500, status: res.status }
40
- } catch {
41
- return { ok: false, status: null }
42
- }
43
- }
44
-
45
- export async function deploy(args: DeployOptions): Promise<void> {
46
- console.log(pc.cyan('\n beech deploy\n'))
47
-
48
- // Step 1: wrangler deploy via npm run deploy.
49
- // stdout captured to extract the deployed URL; stderr stays on the terminal for live progress.
50
- console.log(pc.dim(' [1/3] Deploying Worker…\n'))
51
- const deployResult = spawnSync('npm', ['run', 'deploy'], {
52
- stdio: ['inherit', 'pipe', 'inherit'],
53
- encoding: 'utf-8',
54
- cwd: process.cwd(),
55
- shell: true,
56
- })
57
-
58
- const deployStdout = deployResult.stdout ?? ''
59
- if (deployStdout) process.stdout.write(deployStdout)
60
-
61
- if (deployResult.status !== 0) {
62
- console.log(pc.red('\n ✗ Worker deploy failed\n'))
63
- console.log(pc.dim(' Check the wrangler output above for details.'))
64
- console.log(pc.cyan('\n → Run: npx wrangler login # if not authenticated'))
65
- console.log(pc.cyan(' Or: Update wrangler.jsonc # if database_id is wrong\n'))
66
- process.exit(1)
67
- }
68
-
69
- const deployedUrl = extractWorkerUrl(deployStdout)
70
- console.log(pc.green('\n ✓ Worker deployed'))
71
-
72
- // Step 2: seed:load --remote as a subprocess so that wrangler failures
73
- // (which call process.exit internally) don't abort our own process.
74
- if (args.skipSeed) {
75
- console.log(pc.dim('\n [2/3] Skipping seed:load (--skip-seed)'))
76
- } else {
77
- console.log(pc.dim('\n [2/3] Syncing content schema to remote D1…\n'))
78
- const seedResult = spawnSync('npx', ['beech', 'seed:load'], {
79
- stdio: 'inherit',
80
- cwd: process.cwd(),
81
- shell: true,
82
- })
83
- if (seedResult.status !== 0) {
84
- console.log(pc.yellow('\n ⚠ seed:load failed\n'))
85
- console.log(pc.dim(' Sync the remote content schema manually:'))
86
- console.log(pc.cyan(' → Run: npx beech seed:load\n'))
87
- } else {
88
- console.log(pc.green('\n Content schema synced'))
89
- }
90
- }
91
-
92
- // Step 3: check /admin reachability.
93
- // Use URL extracted from deploy output; fall back to worker name from wrangler.jsonc.
94
- if (args.skipCheck) {
95
- console.log(pc.dim('\n [3/3] Skipping admin check (--skip-check)\n'))
96
- return
97
- }
98
-
99
- const adminBase = deployedUrl ?? (() => {
100
- const workerName = readWorkerName(findWranglerConfig())
101
- // We can't reliably construct the full workers.dev subdomain without knowing the account,
102
- // so only use the name-based URL as a fallback when nothing better is available.
103
- return workerName ? `https://${workerName}.workers.dev` : null
104
- })()
105
-
106
- if (!adminBase) {
107
- console.log(pc.dim('\n [3/3] Could not determine worker URL — skipping admin check\n'))
108
- console.log(pc.dim(' The deployed URL is printed by wrangler above. Open <url>/admin to verify.\n'))
109
- return
110
- }
111
-
112
- console.log(pc.dim(`\n [3/3] Checking ${adminBase}/admin…\n`))
113
- const { ok, status } = await checkAdmin(adminBase)
114
-
115
- if (ok) {
116
- console.log(pc.green(` Admin reachable at: ${adminBase}/admin\n`))
117
- } else {
118
- const statusStr = status != null ? ` (HTTP ${status})` : ''
119
- console.log(pc.yellow(` Admin returned an error${statusStr} at: ${adminBase}/admin\n`))
120
- console.log(pc.dim(' The database may not be fully initialized.'))
121
- console.log(pc.cyan(' → Run: npx beech init --db --remote\n'))
122
- }
123
- }
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+
4
+ import pc from 'picocolors'
5
+ import { spawnSync } from 'node:child_process'
6
+ import { readFileSync } from 'node:fs'
7
+ import { findWranglerConfig } from '../lib/wrangler.js'
8
+
9
+ export interface DeployOptions {
10
+ skipSeed?: boolean
11
+ skipCheck?: boolean
12
+ }
13
+
14
+ function readWorkerName(configPath: string | null): string | null {
15
+ if (!configPath) return null
16
+ try {
17
+ const raw = readFileSync(configPath, 'utf-8')
18
+ const stripped = raw
19
+ .replace(/\/\/[^\n]*/g, '')
20
+ .replace(/\/\*[\s\S]*?\*\//g, '')
21
+ const parsed = JSON.parse(stripped)
22
+ return (parsed?.name as string) ?? null
23
+ } catch {
24
+ return null
25
+ }
26
+ }
27
+
28
+ // Extracts the first workers.dev URL from wrangler deploy stdout.
29
+ // wrangler writes progress to stderr (shown live) and the summary to stdout (captured).
30
+ function extractWorkerUrl(output: string): string | null {
31
+ const match = output.match(/https:\/\/[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+\.workers\.dev\b/)
32
+ return match?.[0] ?? null
33
+ }
34
+
35
+ async function checkAdmin(url: string): Promise<{ ok: boolean; status: number | null }> {
36
+ try {
37
+ const res = await fetch(`${url}/admin`, {
38
+ method: 'HEAD',
39
+ redirect: 'follow',
40
+ signal: AbortSignal.timeout(12_000),
41
+ })
42
+ return { ok: res.status < 500, status: res.status }
43
+ } catch {
44
+ return { ok: false, status: null }
45
+ }
46
+ }
47
+
48
+ export async function deploy(args: DeployOptions): Promise<void> {
49
+ console.log(pc.cyan('\n beech deploy\n'))
50
+
51
+ // Step 1: wrangler deploy via npm run deploy.
52
+ // stdout captured to extract the deployed URL; stderr stays on the terminal for live progress.
53
+ console.log(pc.dim(' [1/3] Deploying Worker…\n'))
54
+ const deployResult = spawnSync('npm', ['run', 'deploy'], {
55
+ stdio: ['inherit', 'pipe', 'inherit'],
56
+ encoding: 'utf-8',
57
+ cwd: process.cwd(),
58
+ shell: true,
59
+ })
60
+
61
+ const deployStdout = deployResult.stdout ?? ''
62
+ if (deployStdout) process.stdout.write(deployStdout)
63
+
64
+ if (deployResult.status !== 0) {
65
+ console.log(pc.red('\n Worker deploy failed\n'))
66
+ console.log(pc.dim(' Check the wrangler output above for details.'))
67
+ console.log(pc.cyan('\n → Run: npx wrangler login # if not authenticated'))
68
+ console.log(pc.cyan(' → Or: Update wrangler.jsonc # if database_id is wrong\n'))
69
+ process.exit(1)
70
+ }
71
+
72
+ const deployedUrl = extractWorkerUrl(deployStdout)
73
+ console.log(pc.green('\n ✓ Worker deployed'))
74
+
75
+ // Step 2: seed:load --remote as a subprocess so that wrangler failures
76
+ // (which call process.exit internally) don't abort our own process.
77
+ if (args.skipSeed) {
78
+ console.log(pc.dim('\n [2/3] Skipping seed:load (--skip-seed)'))
79
+ } else {
80
+ console.log(pc.dim('\n [2/3] Syncing content schema to remote D1…\n'))
81
+ const seedResult = spawnSync('npx', ['beech', 'seed:load'], {
82
+ stdio: 'inherit',
83
+ cwd: process.cwd(),
84
+ shell: true,
85
+ })
86
+ if (seedResult.status !== 0) {
87
+ console.log(pc.yellow('\n ⚠ seed:load failed\n'))
88
+ console.log(pc.dim(' Sync the remote content schema manually:'))
89
+ console.log(pc.cyan(' → Run: npx beech seed:load\n'))
90
+ } else {
91
+ console.log(pc.green('\n ✓ Content schema synced'))
92
+ }
93
+ }
94
+
95
+ // Step 3: check /admin reachability.
96
+ // Use URL extracted from deploy output; fall back to worker name from wrangler.jsonc.
97
+ if (args.skipCheck) {
98
+ console.log(pc.dim('\n [3/3] Skipping admin check (--skip-check)\n'))
99
+ return
100
+ }
101
+
102
+ const adminBase = deployedUrl ?? (() => {
103
+ const workerName = readWorkerName(findWranglerConfig())
104
+ // We can't reliably construct the full workers.dev subdomain without knowing the account,
105
+ // so only use the name-based URL as a fallback when nothing better is available.
106
+ return workerName ? `https://${workerName}.workers.dev` : null
107
+ })()
108
+
109
+ if (!adminBase) {
110
+ console.log(pc.dim('\n [3/3] Could not determine worker URL — skipping admin check\n'))
111
+ console.log(pc.dim(' The deployed URL is printed by wrangler above. Open <url>/admin to verify.\n'))
112
+ return
113
+ }
114
+
115
+ console.log(pc.dim(`\n [3/3] Checking ${adminBase}/admin…\n`))
116
+ const { ok, status } = await checkAdmin(adminBase)
117
+
118
+ if (ok) {
119
+ console.log(pc.green(` Admin reachable at: ${adminBase}/admin\n`))
120
+ } else {
121
+ const statusStr = status != null ? ` (HTTP ${status})` : ''
122
+ console.log(pc.yellow(` ⚠ Admin returned an error${statusStr} at: ${adminBase}/admin\n`))
123
+ console.log(pc.dim(' The database may not be fully initialized.'))
124
+ console.log(pc.cyan(' → Run: npx beech init --db --remote\n'))
125
+ }
126
+ }