@beechcms/cli 0.4.0-preview.9 → 0.4.0

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.
@@ -0,0 +1,189 @@
1
+ import pc from 'picocolors'
2
+ import { createInterface } from 'node:readline/promises'
3
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs'
4
+ import { resolve } from 'node:path'
5
+ import type { BranchType } from '@beechcms/core'
6
+
7
+ export type SeedCreateOptions = Record<string, never>
8
+
9
+ const BRANCH_TYPES: BranchType[] = ['text', 'number', 'boolean', 'date', 'richtext', 'file', 'tags']
10
+
11
+ function slugify(str: string): string {
12
+ return str
13
+ .toLowerCase()
14
+ .trim()
15
+ .replace(/\s+/g, '-')
16
+ .replace(/[^a-z0-9-]/g, '')
17
+ }
18
+
19
+ function toConstName(slug: string): string {
20
+ return slug.replace(/-/g, '_').toUpperCase() + '_SEED'
21
+ }
22
+
23
+ function toLabel(alias: string): string {
24
+ return alias
25
+ .replace(/([A-Z])/g, ' $1')
26
+ .replace(/^./, s => s.toUpperCase())
27
+ .trim()
28
+ }
29
+
30
+ interface BranchDef {
31
+ alias: string
32
+ label: string
33
+ type: BranchType
34
+ required: boolean
35
+ }
36
+
37
+ function generateSeedBlock(slug: string, label: string, labelPlural: string, branches: BranchDef[]): string {
38
+ const displayAlias =
39
+ branches.find(b => b.type === 'text')?.alias ??
40
+ branches[0]?.alias ??
41
+ 'name'
42
+
43
+ const cName = toConstName(slug)
44
+ const branchLines = branches.map(b => {
45
+ const parts: string[] = [
46
+ `alias: '${b.alias}'`,
47
+ `label: '${b.label}'`,
48
+ `type: '${b.type}'`,
49
+ ]
50
+ if (b.required) parts.push('requiredOnCreate: true')
51
+ return ` { ${parts.join(', ')} },`
52
+ })
53
+
54
+ return [
55
+ '',
56
+ `export const ${cName} = defineSeed({`,
57
+ ` slug: '${slug}',`,
58
+ ` label: '${label}',`,
59
+ ` labelPlural: '${labelPlural}',`,
60
+ ` displayNameAlias: '${displayAlias}',`,
61
+ ' branches: [',
62
+ ...branchLines,
63
+ ' ],',
64
+ ' dashboard: {',
65
+ " icon: 'Folder',",
66
+ " group: 'Content',",
67
+ ' },',
68
+ '})',
69
+ '',
70
+ ].join('\n')
71
+ }
72
+
73
+ function ensureDefineSeedImport(content: string): string {
74
+ if (content.includes('defineSeed')) return content
75
+ return `import { defineSeed } from '@beechcms/core'\n` + content
76
+ }
77
+
78
+ function tryInsertRegistryEntry(content: string, slug: string, cName: string): string | null {
79
+ const match = content.match(/(SEED_REGISTRY[^{]*\{)([\s\S]*?)(\n\})/m)
80
+ if (!match) return null
81
+ const [full, open, inner, close] = match
82
+ const newEntry = `\n ${slug}: ${cName},`
83
+ return content.replace(full, open + inner + newEntry + close)
84
+ }
85
+
86
+ function findSeedsFile(): string | null {
87
+ const cwd = process.cwd()
88
+ const searchDirs = [cwd, resolve(cwd, 'apps', 'api')]
89
+ for (const dir of searchDirs) {
90
+ for (const name of ['seeds.ts', 'seed.ts']) {
91
+ const p = resolve(dir, name)
92
+ if (existsSync(p)) return p
93
+ }
94
+ }
95
+ return null
96
+ }
97
+
98
+ export async function seedCreate(_args: SeedCreateOptions): Promise<void> {
99
+ const rl = createInterface({ input: process.stdin, output: process.stdout })
100
+
101
+ const ask = async (q: string, fallback = ''): Promise<string> => {
102
+ const hint = fallback ? pc.dim(` [${fallback}]`) : ''
103
+ const answer = await rl.question(` ${q}${hint}: `)
104
+ return answer.trim() || fallback
105
+ }
106
+
107
+ const askYN = async (q: string, defaultYes = true): Promise<boolean> => {
108
+ const hint = defaultYes ? pc.dim(' (Y/n)') : pc.dim(' (y/N)')
109
+ const answer = (await rl.question(` ${q}${hint}: `)).trim().toLowerCase()
110
+ if (!answer) return defaultYes
111
+ return answer === 'y' || answer === 'yes'
112
+ }
113
+
114
+ console.log(pc.cyan('\n beech seed:create — new content type wizard\n'))
115
+
116
+ try {
117
+ const label = await ask('Content type name (singular, e.g. "Article")')
118
+ if (!label) {
119
+ rl.close()
120
+ console.log(pc.red('\n ✗ Name required.\n'))
121
+ process.exit(1)
122
+ }
123
+
124
+ const defaultSlug = slugify(label) + 's'
125
+ const slug = slugify(await ask('Slug (plural, used in URL + table name)', defaultSlug)) || defaultSlug
126
+ const labelPlural = (await ask('Plural label', label + 's')) || label + 's'
127
+
128
+ const branches: BranchDef[] = []
129
+ console.log(pc.dim('\n Now define the fields. Press Enter to accept defaults.\n'))
130
+
131
+ let addMore = true
132
+ while (addMore) {
133
+ console.log(pc.dim(` ─── Field ${branches.length + 1} ───`))
134
+ const alias = await ask(' Alias (camelCase, e.g. "title", "publishedAt")')
135
+ if (!alias) {
136
+ console.log(pc.yellow(' Alias required — skipping.'))
137
+ addMore = await askYN('\n Add a field?')
138
+ continue
139
+ }
140
+
141
+ const fieldLabel = (await ask(' Label', toLabel(alias))) || toLabel(alias)
142
+ const typeList = BRANCH_TYPES.join(' | ')
143
+ const rawType = (await ask(` Type (${typeList})`, 'text')).toLowerCase()
144
+ const type: BranchType = (BRANCH_TYPES.includes(rawType as BranchType) ? rawType : 'text') as BranchType
145
+ const required = await askYN(' Required on create?', false)
146
+
147
+ branches.push({ alias, label: fieldLabel, type, required })
148
+ addMore = await askYN('\n Add another field?')
149
+ }
150
+
151
+ rl.close()
152
+
153
+ if (branches.length === 0) {
154
+ console.log(pc.yellow('\n No fields defined — seed not created.\n'))
155
+ process.exit(0)
156
+ }
157
+
158
+ const seedBlock = generateSeedBlock(slug, label, labelPlural, branches)
159
+ const cName = toConstName(slug)
160
+ const seedsPath = findSeedsFile()
161
+
162
+ if (!seedsPath) {
163
+ console.log(pc.yellow('\n Could not find seeds.ts. Add this to your seeds file manually:\n'))
164
+ console.log(seedBlock)
165
+ process.exit(0)
166
+ }
167
+
168
+ let content = readFileSync(seedsPath, 'utf-8')
169
+ content = ensureDefineSeedImport(content)
170
+
171
+ const withSeed = content + seedBlock
172
+ const withRegistry = tryInsertRegistryEntry(withSeed, slug, cName)
173
+ writeFileSync(seedsPath, withRegistry ?? withSeed, 'utf-8')
174
+
175
+ console.log(pc.green(`\n ✓ Seed "${slug}" appended to ${seedsPath}\n`))
176
+
177
+ if (!withRegistry) {
178
+ console.log(pc.yellow(` ⚠ Could not auto-update SEED_REGISTRY — add this entry manually:\n`))
179
+ console.log(pc.cyan(` ${slug}: ${cName},\n`))
180
+ }
181
+
182
+ console.log(pc.dim(' Next steps:'))
183
+ console.log(pc.cyan(' npx beech seed:load --local'))
184
+ console.log(pc.dim(' → create the new content table in your local D1 database\n'))
185
+ } catch (err) {
186
+ rl.close()
187
+ throw err
188
+ }
189
+ }
@@ -1,128 +1,155 @@
1
- import pc from 'picocolors'
2
- import {
3
- SEED_REGISTRY,
4
- generateCreateTable,
5
- generateDraftTable,
6
- generateIndexes,
7
- generateFtsTable,
8
- generateFtsTriggers,
9
- type Seed,
10
- } from '@beechcms/core'
11
- import { executeD1File, findWranglerConfig, resolveDbName, type WranglerOptions } from '../lib/wrangler.js'
12
- import { diffSeed } from '../lib/schema-diff.js'
13
-
14
- export interface SeedLoadOptions {
15
- dryRun: boolean
16
- diff: boolean
17
- local: boolean
18
- db?: string
19
- registry?: Record<string, Seed> | null
20
- }
21
-
22
- function buildStatements(seed: Seed): string[] {
23
- const stmts: string[] = [generateCreateTable(seed), ...generateIndexes(seed)]
24
-
25
- const draft = generateDraftTable(seed)
26
- if (draft) stmts.push(draft)
27
-
28
- const fts = generateFtsTable(seed)
29
- if (fts) {
30
- stmts.push(fts, ...generateFtsTriggers(seed))
31
- }
32
-
33
- return stmts
34
- }
35
-
36
- async function runDiff(options: WranglerOptions, registry: Record<string, Seed>): Promise<void> {
37
- const seeds = Object.values(registry)
38
- console.log(pc.cyan('\n Diffing schema…\n'))
39
-
40
- let allOk = true
41
- for (const seed of seeds) {
42
- const result = await diffSeed(seed, options)
43
- const tableName = `content_${seed.slug}`
44
-
45
- if (!result.tableExists) {
46
- console.log(pc.red(` ✗ ${tableName} — table missing`))
47
- allOk = false
48
- continue
49
- }
50
-
51
- const problems = result.columns.filter(c => c.status !== 'ok')
52
- if (problems.length === 0) {
53
- console.log(pc.green(` ✓ ${tableName}`))
54
- continue
55
- }
56
-
57
- allOk = false
58
- console.log(pc.yellow(` ⚠ ${tableName}`))
59
- for (const col of problems) {
60
- if (col.status === 'missing') {
61
- console.log(pc.red(` + missing column: ${col.name} ${col.expectedType}`))
62
- } else if (col.status === 'extra') {
63
- console.log(pc.dim(` ~ extra column: ${col.name} ${col.actualType}`))
64
- } else if (col.status === 'type_mismatch') {
65
- console.log(pc.red(` ≠ type mismatch: ${col.name} (expected ${col.expectedType}, got ${col.actualType})`))
66
- }
67
- }
68
- }
69
-
70
- console.log('')
71
- if (allOk) {
72
- console.log(pc.green(' Schema matches seeds. No action needed.\n'))
73
- } else {
74
- console.log(pc.yellow(' Run `beech seed:load` to apply missing tables/columns.\n'))
75
- }
76
- }
77
-
78
- async function runLoad(options: WranglerOptions, dryRun: boolean, registry: Record<string, Seed>): Promise<void> {
79
- const seeds = Object.values(registry)
80
-
81
- if (dryRun) {
82
- console.log(pc.cyan('\n -- dry-run: SQL that would be executed\n'))
83
- for (const seed of seeds) {
84
- const stmts = buildStatements(seed)
85
- console.log(pc.dim(` -- content_${seed.slug}`))
86
- for (const stmt of stmts) {
87
- console.log(stmt + '\n')
88
- }
89
- }
90
- return
91
- }
92
-
93
- console.log(pc.cyan(`\n Loading seeds into ${options.local ? 'local' : 'remote'} D1 (${options.db})…\n`))
94
-
95
- for (const seed of seeds) {
96
- const stmts = buildStatements(seed)
97
- const sql = stmts.join('\n\n') + '\n'
98
- process.stdout.write(` ${pc.dim('')} content_${seed.slug}… `)
99
- executeD1File(sql, options)
100
- console.log(pc.green('done'))
101
- }
102
-
103
- console.log(pc.green('\n All seeds loaded.\n'))
104
- }
105
-
106
- export async function seedLoad(args: SeedLoadOptions): Promise<void> {
107
- const registry = args.registry ?? SEED_REGISTRY
108
-
109
- if (Object.keys(registry).length === 0) {
110
- console.warn(pc.yellow('\n Warning: SEED_REGISTRY is empty. Create a seeds.ts in your project root.\n'))
111
- return
112
- }
113
-
114
- const configPath = findWranglerConfig()
115
- const db = args.db ?? resolveDbName(configPath)
116
-
117
- const options: WranglerOptions = {
118
- db,
119
- local: args.local,
120
- configPath,
121
- }
122
-
123
- if (args.diff) {
124
- await runDiff(options, registry)
125
- } else {
126
- await runLoad(options, args.dryRun, registry)
127
- }
128
- }
1
+ import pc from 'picocolors'
2
+ import {
3
+ SEED_REGISTRY,
4
+ generateCreateTable,
5
+ generateDraftTable,
6
+ generateIndexes,
7
+ generateFtsTable,
8
+ generateFtsTriggers,
9
+ type Seed,
10
+ } from '@beechcms/core'
11
+ import { executeD1File, findWranglerConfig, resolveDbName, type WranglerOptions } from '../lib/wrangler.js'
12
+ import { diffSeed } from '../lib/schema-diff.js'
13
+ import { validateSeeds } from './validate.js'
14
+
15
+ export interface SeedLoadOptions {
16
+ dryRun: boolean
17
+ diff: boolean
18
+ local: boolean
19
+ db?: string
20
+ registry?: Record<string, Seed> | null
21
+ }
22
+
23
+ function buildStatements(seed: Seed): string[] {
24
+ const stmts: string[] = [generateCreateTable(seed), ...generateIndexes(seed)]
25
+
26
+ const draft = generateDraftTable(seed)
27
+ if (draft) stmts.push(draft)
28
+
29
+ const fts = generateFtsTable(seed)
30
+ if (fts) {
31
+ stmts.push(fts, ...generateFtsTriggers(seed))
32
+ }
33
+
34
+ return stmts
35
+ }
36
+
37
+ async function runDiff(options: WranglerOptions, registry: Record<string, Seed>): Promise<void> {
38
+ const seeds = Object.values(registry)
39
+ console.log(pc.cyan('\n Diffing schema…\n'))
40
+
41
+ let allOk = true
42
+ for (const seed of seeds) {
43
+ const result = await diffSeed(seed, options)
44
+ const tableName = `content_${seed.slug}`
45
+
46
+ if (!result.tableExists) {
47
+ console.log(pc.red(` ✗ ${tableName} — table missing`))
48
+ allOk = false
49
+ continue
50
+ }
51
+
52
+ const problems = result.columns.filter(c => c.status !== 'ok')
53
+ if (problems.length === 0) {
54
+ console.log(pc.green(` ✓ ${tableName}`))
55
+ continue
56
+ }
57
+
58
+ allOk = false
59
+ console.log(pc.yellow(` ⚠ ${tableName}`))
60
+ for (const col of problems) {
61
+ if (col.status === 'missing') {
62
+ console.log(pc.red(` + missing column: ${col.name} ${col.expectedType}`))
63
+ } else if (col.status === 'extra') {
64
+ console.log(pc.dim(` ~ orphaned column: "${col.name}" (${col.actualType}) exists in DB but not in seeds.ts`))
65
+ } else if (col.status === 'type_mismatch') {
66
+ console.log(pc.red(` ≠ type mismatch: ${col.name} (expected ${col.expectedType}, got ${col.actualType})`))
67
+ }
68
+ }
69
+ }
70
+
71
+ console.log('')
72
+ if (allOk) {
73
+ console.log(pc.green(' Schema matches seeds. No action needed.\n'))
74
+ } else {
75
+ console.log(pc.yellow(' Run `beech seed:load` to apply missing tables/columns.\n'))
76
+ }
77
+ }
78
+
79
+ async function runLoad(options: WranglerOptions, dryRun: boolean, registry: Record<string, Seed>): Promise<void> {
80
+ const seeds = Object.values(registry)
81
+
82
+ if (dryRun) {
83
+ console.log(pc.cyan('\n -- dry-run: SQL that would be executed\n'))
84
+ for (const seed of seeds) {
85
+ const stmts = buildStatements(seed)
86
+ console.log(pc.dim(` -- content_${seed.slug}`))
87
+ for (const stmt of stmts) {
88
+ console.log(stmt + '\n')
89
+ }
90
+ }
91
+ return
92
+ }
93
+
94
+ console.log(pc.cyan(`\n Loading seeds into ${options.local ? 'local' : 'remote'} D1 (${options.db})…\n`))
95
+
96
+ for (const seed of seeds) {
97
+ const stmts = buildStatements(seed)
98
+ const sql = stmts.join('\n\n') + '\n'
99
+ process.stdout.write(` ${pc.dim('→')} content_${seed.slug}… `)
100
+ const ok = executeD1File(sql, options)
101
+ if (!ok) {
102
+ console.log(pc.red('failed'))
103
+ console.log(pc.red(`\n Failed to apply schema for content_${seed.slug}\n`))
104
+ console.log(pc.dim(' wrangler reported an error above.'))
105
+ console.log(pc.dim(` Most likely causes:`))
106
+ console.log(pc.dim(` - Database "${options.db}" not found or wrong database_id`))
107
+ if (!options.local) {
108
+ console.log(pc.dim(' - Not logged in to Cloudflare'))
109
+ console.log(pc.cyan('\n → Run: npx wrangler login'))
110
+ console.log(pc.cyan(' → Then: npx beech seed:load\n'))
111
+ } else {
112
+ console.log(pc.cyan('\n → Run: npx beech init --db --local # re-initialise local DB'))
113
+ console.log(pc.cyan(' → Then: npx beech seed:load --local\n'))
114
+ }
115
+ process.exit(1)
116
+ }
117
+ console.log(pc.green('done'))
118
+ }
119
+
120
+ console.log(pc.green('\n All seeds loaded.\n'))
121
+ }
122
+
123
+ export async function seedLoad(args: SeedLoadOptions): Promise<void> {
124
+ const registry = args.registry ?? SEED_REGISTRY
125
+
126
+ if (Object.keys(registry).length === 0) {
127
+ console.log(pc.yellow('\n ✗ No seeds found\n'))
128
+ console.log(pc.dim(' Create a seeds.ts file in your project root with at least one content type.'))
129
+ console.log(pc.cyan('\n → Run: npx beech seed:create\n'))
130
+ return
131
+ }
132
+
133
+ const validationErrors = validateSeeds(registry)
134
+ if (validationErrors.length > 0) {
135
+ const total = validationErrors.reduce((n, e) => n + e.messages.length, 0)
136
+ const s = total !== 1 ? 's' : ''
137
+ console.log(pc.yellow(`\n ⚠ Seed validation found ${total} issue${s}. Schema changes will still be applied.\n`))
138
+ console.log(pc.dim(' Run "npx beech validate" for details.\n'))
139
+ }
140
+
141
+ const configPath = findWranglerConfig()
142
+ const db = args.db ?? resolveDbName(configPath)
143
+
144
+ const options: WranglerOptions = {
145
+ db,
146
+ local: args.local,
147
+ configPath,
148
+ }
149
+
150
+ if (args.diff) {
151
+ await runDiff(options, registry)
152
+ } else {
153
+ await runLoad(options, args.dryRun, registry)
154
+ }
155
+ }
@@ -0,0 +1,51 @@
1
+ import pc from 'picocolors'
2
+ import { spawnSync } from 'node:child_process'
3
+
4
+ export interface UpdateOptions {}
5
+
6
+ export async function update(_args: UpdateOptions): Promise<void> {
7
+ console.log(pc.cyan('\n beech update\n'))
8
+
9
+ // Step 1 — install latest BeechCMS packages
10
+ console.log(pc.dim(' [1/2] Installing latest BeechCMS packages…\n'))
11
+ const installResult = spawnSync(
12
+ 'npm',
13
+ ['install', '@beechcms/api@latest', '@beechcms/core@latest'],
14
+ { stdio: 'inherit', cwd: process.cwd(), shell: true }
15
+ )
16
+
17
+ if (installResult.status !== 0) {
18
+ console.log(pc.red('\n ✗ npm install failed\n'))
19
+ console.log(pc.dim(' Check the output above for details.'))
20
+ console.log(pc.dim(' You may need to resolve version conflicts manually.'))
21
+ console.log(pc.cyan('\n → Try: npm install --legacy-peer-deps\n'))
22
+ process.exit(1)
23
+ }
24
+
25
+ console.log(pc.green('\n ✓ Packages updated'))
26
+
27
+ // Step 2 — apply any new system migrations to local DB
28
+ console.log(pc.dim('\n [2/2] Applying system migrations to local database…\n'))
29
+ const initResult = spawnSync(
30
+ 'npx',
31
+ ['beech', 'init', '--db', '--local'],
32
+ { stdio: 'inherit', cwd: process.cwd(), shell: true }
33
+ )
34
+
35
+ if (initResult.status !== 0) {
36
+ console.log(pc.yellow('\n ⚠ Local DB update failed\n'))
37
+ console.log(pc.dim(' Apply system migrations manually:'))
38
+ console.log(pc.cyan(' → Run: npx beech init --db --local\n'))
39
+ } else {
40
+ console.log(pc.green('\n ✓ Local database updated'))
41
+ }
42
+
43
+ console.log(pc.dim('\n Local update complete.\n'))
44
+ console.log(pc.dim(' Next steps:'))
45
+ console.log(pc.cyan(' 1. npx beech seed:load --local'))
46
+ console.log(pc.dim(' → sync content schema to local DB'))
47
+ console.log(pc.cyan(' 2. npm run deploy'))
48
+ console.log(pc.dim(' → deploy updated API + dashboard'))
49
+ console.log(pc.cyan(' 3. npx beech seed:load'))
50
+ console.log(pc.dim(' → sync remote schema\n'))
51
+ }
@@ -0,0 +1,83 @@
1
+ import pc from 'picocolors'
2
+ import type { Seed } from '@beechcms/core'
3
+ import { SEED_REGISTRY } from '@beechcms/core'
4
+
5
+ export interface ValidateOptions {
6
+ registry?: Record<string, Seed> | null
7
+ }
8
+
9
+ export interface SeedValidationError {
10
+ slug: string
11
+ messages: string[]
12
+ }
13
+
14
+ export function validateSeeds(registry: Record<string, Seed>): SeedValidationError[] {
15
+ const result: SeedValidationError[] = []
16
+ const slugsSeen = new Set<string>()
17
+
18
+ for (const seed of Object.values(registry)) {
19
+ const messages: string[] = []
20
+
21
+ if (slugsSeen.has(seed.slug)) {
22
+ messages.push(`duplicate slug "${seed.slug}" — each seed must have a unique slug`)
23
+ }
24
+ slugsSeen.add(seed.slug)
25
+
26
+ const aliasesSeen = new Set<string>()
27
+ for (const branch of seed.branches) {
28
+ if (aliasesSeen.has(branch.alias)) {
29
+ messages.push(`duplicate branch alias "${branch.alias}"`)
30
+ }
31
+ aliasesSeen.add(branch.alias)
32
+ }
33
+
34
+ const allAliases = new Set(seed.branches.map(b => b.alias))
35
+ if (!allAliases.has(seed.displayNameAlias)) {
36
+ messages.push(`displayNameAlias "${seed.displayNameAlias}" not found in branches`)
37
+ }
38
+
39
+ if (messages.length > 0) {
40
+ result.push({ slug: seed.slug, messages })
41
+ }
42
+ }
43
+
44
+ return result
45
+ }
46
+
47
+ export async function validate(args: ValidateOptions): Promise<void> {
48
+ const registry = args.registry ?? SEED_REGISTRY
49
+
50
+ if (Object.keys(registry).length === 0) {
51
+ console.warn(pc.yellow('\n Warning: SEED_REGISTRY is empty. Create a seeds.ts in your project root.\n'))
52
+ return
53
+ }
54
+
55
+ console.log(pc.cyan('\n beech validate — checking seeds\n'))
56
+
57
+ const errors = validateSeeds(registry)
58
+ const errorMap = new Map(errors.map(e => [e.slug, e.messages]))
59
+
60
+ let totalIssues = 0
61
+ for (const seed of Object.values(registry)) {
62
+ const msgs = errorMap.get(seed.slug)
63
+ if (!msgs) {
64
+ console.log(pc.green(` ✓ ${seed.slug}`))
65
+ } else {
66
+ totalIssues += msgs.length
67
+ console.log(pc.red(` ✗ ${seed.slug}`))
68
+ for (const msg of msgs) {
69
+ console.log(pc.red(` → ${msg}`))
70
+ }
71
+ }
72
+ }
73
+
74
+ console.log('')
75
+
76
+ if (totalIssues > 0) {
77
+ const s = totalIssues !== 1 ? 's' : ''
78
+ console.log(pc.red(` Found ${totalIssues} issue${s}. Fix the seeds above before loading.\n`))
79
+ process.exit(1)
80
+ } else {
81
+ console.log(pc.green(' All seeds valid.\n'))
82
+ }
83
+ }
package/src/index.ts CHANGED
@@ -1,2 +1,12 @@
1
- export { seedLoad } from './commands/seed-load.js'
2
- export type { SeedLoadOptions } from './commands/seed-load.js'
1
+ export { seedLoad } from './commands/seed-load.js'
2
+ export type { SeedLoadOptions } from './commands/seed-load.js'
3
+ export { init } from './commands/init.js'
4
+ export type { InitOptions } from './commands/init.js'
5
+ export { validate, validateSeeds } from './commands/validate.js'
6
+ export type { ValidateOptions, SeedValidationError } from './commands/validate.js'
7
+ export { seedCreate } from './commands/seed-create.js'
8
+ export type { SeedCreateOptions } from './commands/seed-create.js'
9
+ export { deploy } from './commands/deploy.js'
10
+ export type { DeployOptions } from './commands/deploy.js'
11
+ export { update } from './commands/update.js'
12
+ export type { UpdateOptions } from './commands/update.js'