@beechcms/cms 0.4.0-preview.1 → 0.4.0-preview.11

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 (3) hide show
  1. package/bin/cli.mjs +77 -18
  2. package/bin/create.mjs +66 -45
  3. package/package.json +6 -4
package/bin/cli.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  // @ts-check
3
3
 
4
- import { execSync } from 'node:child_process'
4
+ import { spawnSync } from 'node:child_process'
5
5
  import { existsSync } from 'node:fs'
6
6
  import { resolve } from 'node:path'
7
+ import { pathToFileURL } from 'node:url'
7
8
 
8
9
 
9
10
  const [,, command, ...args] = process.argv
@@ -25,29 +26,83 @@ function help() {
25
26
  --remote Execute against remote D1 (default: local)
26
27
  --db <name> Override D1 database name
27
28
 
28
- Run npx beech-cms to scaffold a new project.
29
+ Run npx @beechcms/cms to scaffold a new project.
29
30
  `)
30
31
  }
31
32
 
32
33
  function cmdBuild() {
33
- const corePath = resolve(process.cwd(), 'packages', 'core')
34
-
35
- if (!existsSync(corePath)) {
36
- console.error(
37
- '\nError: packages/core not found.\n' +
38
- 'Make sure you are running this command from the root of a BeechCMS project.\n'
39
- )
40
- process.exit(1)
34
+ console.log(
35
+ '\nNo build step needed for BeechCMS projects.\n' +
36
+ 'Edit seeds.ts then run `npx beech seed:load` to sync schema changes to D1.\n'
37
+ )
38
+ }
39
+
40
+ async function tryLoadLocalRegistry() {
41
+ const cwd = process.cwd()
42
+
43
+ // Try compiled JS first (root or apps/api)
44
+ const searchDirs = [cwd, resolve(cwd, 'apps', 'api')]
45
+ for (const dir of searchDirs) {
46
+ for (const name of ['seeds.js', 'seeds.mjs', 'seed.js', 'seed.mjs']) {
47
+ const p = resolve(dir, name)
48
+ if (existsSync(p)) {
49
+ try {
50
+ const mod = await import(pathToFileURL(p).href)
51
+ if (mod.SEED_REGISTRY && typeof mod.SEED_REGISTRY === 'object') {
52
+ return mod.SEED_REGISTRY
53
+ }
54
+ if (Array.isArray(mod.seeds)) {
55
+ return Object.fromEntries(mod.seeds.map(s => [s.slug, s]))
56
+ }
57
+ if (Array.isArray(mod.default)) {
58
+ return Object.fromEntries(mod.default.map(s => [s.slug, s]))
59
+ }
60
+ } catch {}
61
+ }
62
+ }
63
+ }
64
+
65
+ // Try seeds.ts / seed.ts (root or apps/api)
66
+ let tsPath = null
67
+ for (const dir of searchDirs) {
68
+ const p = existsSync(resolve(dir, 'seeds.ts'))
69
+ ? resolve(dir, 'seeds.ts')
70
+ : resolve(dir, 'seed.ts')
71
+ if (existsSync(p)) {
72
+ tsPath = p
73
+ break
74
+ }
41
75
  }
42
76
 
43
- console.log('\nBuilding @beechcms/core…\n')
44
- try {
45
- execSync('npm run build -w @beechcms/core', { stdio: 'inherit' })
46
- console.log('\n✔ @beechcms/core built — your seed changes are now live.\n')
47
- } catch {
48
- console.error('\nBuild failed. Check the output above for errors.\n')
49
- process.exit(1)
77
+ if (tsPath) {
78
+ const result = spawnSync(process.execPath, [
79
+ '--experimental-strip-types',
80
+ '--input-type=module',
81
+ '--eval',
82
+ `
83
+ import * as mod from ${JSON.stringify(pathToFileURL(tsPath).href)};
84
+ let out = null;
85
+ if (mod.SEED_REGISTRY && typeof mod.SEED_REGISTRY === 'object') out = mod.SEED_REGISTRY;
86
+ else if (Array.isArray(mod.seeds)) out = Object.fromEntries(mod.seeds.map(s => [s.slug, s]));
87
+ else if (Array.isArray(mod.default)) out = Object.fromEntries(mod.default.map(s => [s.slug, s]));
88
+ if (out) process.stdout.write(JSON.stringify(out));
89
+ `.trim(),
90
+ ], { encoding: 'utf-8' })
91
+
92
+ if (result.status === 0 && result.stdout) {
93
+ try { return JSON.parse(result.stdout) } catch (err) {
94
+ console.error(' Failed to parse seeds.ts output:', err)
95
+ }
96
+ } else if (result.status !== 0) {
97
+ console.error(' Error loading seeds.ts:')
98
+ console.error(result.stderr || result.stdout || 'Unknown error')
99
+ if (process.version.slice(1).split('.')[0] < 22) {
100
+ console.warn(' Note: Node.js 22.6+ is required to load .ts files directly. Current version:', process.version)
101
+ }
102
+ }
50
103
  }
104
+
105
+ return null
51
106
  }
52
107
 
53
108
  async function cmdSeedLoad(args) {
@@ -57,14 +112,18 @@ async function cmdSeedLoad(args) {
57
112
  const dbIdx = args.indexOf('--db')
58
113
  const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
59
114
 
115
+ const registry = await tryLoadLocalRegistry()
116
+
60
117
  const { seedLoad } = await import('@beechcms/cli')
61
- await seedLoad({ dryRun, diff, local: !remote, db })
118
+ await seedLoad({ dryRun, diff, local: !remote, db, registry })
62
119
  }
63
120
 
64
121
  const handler = COMMANDS[command]
65
122
  if (!handler) {
66
123
  help()
67
124
  if (command) process.exit(1)
125
+ } else if (args.includes('--help') || args.includes('-h')) {
126
+ help()
68
127
  } else {
69
128
  await handler(args)
70
129
  }
package/bin/create.mjs CHANGED
@@ -85,9 +85,9 @@ function writeFile(path, content) {
85
85
  function buildWorkerTs() {
86
86
  return `/// <reference types="@cloudflare/workers-types" />
87
87
  import { createBeechApp } from '@beechcms/api'
88
- import { seeds } from './seeds'
88
+ import { SEED_REGISTRY } from './seeds'
89
89
 
90
- export default createBeechApp({ seeds })
90
+ export default createBeechApp({ seeds: SEED_REGISTRY })
91
91
  `
92
92
  }
93
93
 
@@ -104,12 +104,14 @@ function buildPackageJson(name) {
104
104
  scripts: {
105
105
  dev: 'wrangler dev --port 8789',
106
106
  deploy: 'wrangler deploy --minify',
107
+ 'seed:load': 'npx beech seed:load',
108
+ 'seed:load:local': 'npx beech seed:load --local',
107
109
  'db:migrate:local': 'wrangler d1 migrations apply ' + name + '-db --local',
108
110
  'db:reset:local': 'node -e "require(\'fs\').rmSync(\'.wrangler/state\',{recursive:true,force:true})" && npm run db:migrate:local',
109
111
  },
110
112
  dependencies: {
111
- '@beechcms/api': '^0.4.0-preview.1',
112
- '@beechcms/core': '^0.4.0-preview.1',
113
+ '@beechcms/api': '^0.4.0-preview.6',
114
+ '@beechcms/core': '^0.4.0-preview.6',
113
115
  },
114
116
  devDependencies: {
115
117
  '@cloudflare/workers-types': '^4.0.0',
@@ -133,6 +135,11 @@ function buildWranglerJsonc(cfg) {
133
135
  "APP_URL": "${cfg.appUrl || 'http://localhost:5173'}"
134
136
  },
135
137
 
138
+ "assets": {
139
+ "binding": "ASSETS",
140
+ "directory": "node_modules/@beechcms/api/assets/dashboard"
141
+ },
142
+
136
143
  "d1_databases": [
137
144
  {
138
145
  "binding": "DB",
@@ -272,54 +279,68 @@ async function askCloudflareConfig(name) {
272
279
  // ── Main ──────────────────────────────────────────────────────────────────────
273
280
 
274
281
  async function main() {
282
+ const argv = process.argv.slice(2)
283
+ const silent = argv.includes('--yes') || argv.includes('-y') || !process.stdout.isTTY
284
+
275
285
  console.log()
276
- p.intro(pc.bgGreen(pc.black(' beech-cms ')))
277
-
278
- // Project name
279
- const projectName = await p.text({
280
- message: 'Project name',
281
- placeholder: 'my-website',
282
- validate: (v) => {
283
- if (!v.trim()) return 'Required'
284
- if (!/^[a-z0-9][a-z0-9-]*$/.test(v.trim())) return 'Lowercase letters, numbers and hyphens only'
285
- },
286
- })
287
- if (p.isCancel(projectName)) { p.cancel('Cancelled'); process.exit(0) }
288
- const name = projectName.trim()
289
- const targetDir = resolve(process.cwd(), name)
286
+ p.intro(pc.bgGreen(pc.black(' @beechcms/cms ')))
287
+
288
+ let name, selectedTemplates, cloudflare
289
+
290
+ if (silent) {
291
+ // Non-interactive: use first positional arg or default name, no templates, skip Cloudflare
292
+ const positional = argv.find((a) => !a.startsWith('-'))
293
+ name = positional ?? 'my-beech-project'
294
+ selectedTemplates = []
295
+ cloudflare = null
296
+ console.log(pc.dim(` Running in non-interactive mode. Project name: ${name}`))
297
+ } else {
298
+ // Project name
299
+ const projectName = await p.text({
300
+ message: 'Project name',
301
+ placeholder: 'my-website',
302
+ validate: (v) => {
303
+ if (!v.trim()) return 'Required'
304
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(v.trim())) return 'Lowercase letters, numbers and hyphens only'
305
+ },
306
+ })
307
+ if (p.isCancel(projectName)) { p.cancel('Cancelled'); process.exit(0) }
308
+ name = projectName.trim()
309
+
310
+ // Content types
311
+ const tmpl = await p.multiselect({
312
+ message: 'Which content types do you need?',
313
+ hint: 'Space to select, Enter to confirm. You can add more later in seeds.ts',
314
+ options: [
315
+ { value: 'blog', label: 'Blog', hint: 'posts with rich text, cover image, tags and authors' },
316
+ { value: 'gallery', label: 'Gallery', hint: 'media items with image, tags and featured flag' },
317
+ { value: 'contact', label: 'Contact', hint: 'public form submissions with masked email and read status' },
318
+ ],
319
+ required: false,
320
+ })
321
+ if (p.isCancel(tmpl)) { p.cancel('Cancelled'); process.exit(0) }
322
+ selectedTemplates = tmpl
323
+
324
+ // Cloudflare now or later?
325
+ const configureNow = await p.confirm({
326
+ message: 'Configure Cloudflare credentials now?',
327
+ hint: 'Choose "No" to scaffold the project and fill in the values later',
328
+ initialValue: true,
329
+ })
330
+ if (p.isCancel(configureNow)) { p.cancel('Cancelled'); process.exit(0) }
331
+
332
+ if (configureNow) {
333
+ cloudflare = await askCloudflareConfig(name)
334
+ if (!cloudflare) { p.cancel('Cancelled'); process.exit(0) }
335
+ }
336
+ }
290
337
 
338
+ const targetDir = resolve(process.cwd(), name)
291
339
  if (existsSync(targetDir)) {
292
340
  p.cancel(`Directory '${name}' already exists. Choose a different name or delete the folder.`)
293
341
  process.exit(1)
294
342
  }
295
343
 
296
- // Content types
297
- const selectedTemplates = await p.multiselect({
298
- message: 'Which content types do you need?',
299
- hint: 'Space to select, Enter to confirm. You can add more later in seeds.ts',
300
- options: [
301
- { value: 'blog', label: 'Blog', hint: 'posts with rich text, cover image, tags and authors' },
302
- { value: 'gallery', label: 'Gallery', hint: 'media items with image, tags and featured flag' },
303
- { value: 'contact', label: 'Contact', hint: 'public form submissions with masked email and read status' },
304
- ],
305
- required: false,
306
- })
307
- if (p.isCancel(selectedTemplates)) { p.cancel('Cancelled'); process.exit(0) }
308
-
309
- // Cloudflare now or later?
310
- const configureNow = await p.confirm({
311
- message: 'Configure Cloudflare credentials now?',
312
- hint: 'Choose "No" to scaffold the project and fill in the values later',
313
- initialValue: true,
314
- })
315
- if (p.isCancel(configureNow)) { p.cancel('Cancelled'); process.exit(0) }
316
-
317
- let cloudflare = null
318
- if (configureNow) {
319
- cloudflare = await askCloudflareConfig(name)
320
- if (!cloudflare) { p.cancel('Cancelled'); process.exit(0) }
321
- }
322
-
323
344
  const jwtSecret = generateSecret(32)
324
345
  const publicReadKey = generateSecret(16)
325
346
  const publicWriteKey = generateSecret(16)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beechcms/cms",
3
- "version": "0.4.0-preview.1",
3
+ "version": "0.4.0-preview.11",
4
4
  "packageManager": "npm@11.9.0",
5
5
  "description": "Edge-Native, Schema-Driven Headless CMS built on Cloudflare Workers, D1, and R2. Features the Botanical Engine for alias-stable field management, a modular widget layer, and a React + Vite admin dashboard.",
6
6
  "keywords": [
@@ -38,14 +38,16 @@
38
38
  "node": ">=18.0.0"
39
39
  },
40
40
  "bin": {
41
- "beech-cms": "bin/create.mjs",
41
+ "beechcms": "bin/create.mjs",
42
42
  "beech": "bin/cli.mjs"
43
43
  },
44
44
  "scripts": {
45
45
  "dev": "turbo run dev --parallel",
46
46
  "build": "turbo run build",
47
47
  "test": "turbo run test",
48
- "test:coverage": "turbo run test:coverage"
48
+ "test:coverage": "turbo run test:coverage",
49
+ "release": "node scripts/release.mjs",
50
+ "release:preview": "node scripts/release.mjs --preview"
49
51
  },
50
52
  "type": "module",
51
53
  "devDependencies": {
@@ -54,7 +56,7 @@
54
56
  "typescript": "^5.9.3"
55
57
  },
56
58
  "dependencies": {
57
- "@beechcms/cli": "^0.4.0-preview.1",
59
+ "@beechcms/cli": "^0.4.0-preview.11",
58
60
  "@clack/prompts": "^0.9.1",
59
61
  "picocolors": "^1.1.1"
60
62
  }