@actuate-media/cli 0.4.1 → 0.5.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.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +69 -12
- package/CHANGELOG.md +58 -0
- package/dist/__tests__/db-init.test.d.ts +2 -0
- package/dist/__tests__/db-init.test.d.ts.map +1 -0
- package/dist/__tests__/db-init.test.js +127 -0
- package/dist/__tests__/db-init.test.js.map +1 -0
- package/dist/__tests__/db-sync.test.d.ts +2 -0
- package/dist/__tests__/db-sync.test.d.ts.map +1 -0
- package/dist/__tests__/db-sync.test.js +136 -0
- package/dist/__tests__/db-sync.test.js.map +1 -0
- package/dist/__tests__/deployment-diagnostics.test.js.map +1 -1
- package/dist/__tests__/init.test.js.map +1 -1
- package/dist/__tests__/schema-fragment.test.js +1 -1
- package/dist/__tests__/schema-fragment.test.js.map +1 -1
- package/dist/__tests__/seed.test.js.map +1 -1
- package/dist/commands/db-init.d.ts +19 -2
- package/dist/commands/db-init.d.ts.map +1 -1
- package/dist/commands/db-init.js +128 -306
- package/dist/commands/db-init.js.map +1 -1
- package/dist/commands/db-status.d.ts +1 -1
- package/dist/commands/db-status.d.ts.map +1 -1
- package/dist/commands/db-status.js +33 -33
- package/dist/commands/db-status.js.map +1 -1
- package/dist/commands/db-sync.d.ts +31 -0
- package/dist/commands/db-sync.d.ts.map +1 -0
- package/dist/commands/db-sync.js +195 -0
- package/dist/commands/db-sync.js.map +1 -0
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +48 -41
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/export.d.ts +1 -1
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +32 -32
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/generate.d.ts +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +8 -8
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/import.d.ts +1 -1
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/commands/import.js +55 -58
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts +1 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +18 -24
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/seed.d.ts +1 -1
- package/dist/commands/seed.d.ts.map +1 -1
- package/dist/commands/seed.js +156 -157
- package/dist/commands/seed.js.map +1 -1
- package/dist/commands/update-check.d.ts +1 -1
- package/dist/commands/update-check.d.ts.map +1 -1
- package/dist/commands/update-check.js +34 -27
- package/dist/commands/update-check.js.map +1 -1
- package/dist/commands/upgrade.d.ts +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +46 -34
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/deployment/diagnostics.d.ts.map +1 -1
- package/dist/deployment/diagnostics.js +7 -2
- package/dist/deployment/diagnostics.js.map +1 -1
- package/dist/index.js +17 -15
- package/dist/index.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +5 -5
- package/dist/utils/logger.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/db-init.test.ts +155 -0
- package/src/__tests__/db-sync.test.ts +167 -0
- package/src/__tests__/deployment-diagnostics.test.ts +68 -60
- package/src/__tests__/init.test.ts +17 -17
- package/src/__tests__/schema-fragment.test.ts +29 -25
- package/src/__tests__/seed.test.ts +25 -25
- package/src/commands/db-init.ts +146 -319
- package/src/commands/db-status.ts +70 -68
- package/src/commands/db-sync.ts +227 -0
- package/src/commands/doctor.ts +102 -88
- package/src/commands/export.ts +65 -75
- package/src/commands/generate.ts +14 -16
- package/src/commands/import.ts +125 -140
- package/src/commands/init.ts +14 -14
- package/src/commands/migrate.ts +29 -35
- package/src/commands/seed.ts +294 -300
- package/src/commands/update-check.ts +77 -72
- package/src/commands/upgrade.ts +100 -85
- package/src/deployment/diagnostics.ts +86 -72
- package/src/index.ts +32 -30
- package/src/utils/logger.ts +10 -10
package/src/commands/doctor.ts
CHANGED
|
@@ -1,74 +1,83 @@
|
|
|
1
|
-
import { Command } from
|
|
2
|
-
import { access, readdir, readFile } from
|
|
3
|
-
import { resolve } from
|
|
4
|
-
import chalk from
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { access, readdir, readFile } from 'node:fs/promises'
|
|
3
|
+
import { resolve } from 'node:path'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
5
|
import {
|
|
6
6
|
buildDeploymentManifest,
|
|
7
7
|
createDiagnosticReport,
|
|
8
8
|
detectPackageManager,
|
|
9
9
|
type DiagnosticReport,
|
|
10
|
-
} from
|
|
10
|
+
} from '../deployment/diagnostics.js'
|
|
11
11
|
|
|
12
12
|
async function fileExists(path: string): Promise<boolean> {
|
|
13
13
|
try {
|
|
14
|
-
await access(path)
|
|
15
|
-
return true
|
|
14
|
+
await access(path)
|
|
15
|
+
return true
|
|
16
16
|
} catch {
|
|
17
|
-
return false
|
|
17
|
+
return false
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
async function readSchemaModels(schemaPath: string): Promise<Set<string>> {
|
|
22
|
-
if (!(await fileExists(schemaPath))) return new Set()
|
|
23
|
-
const schema = await readFile(schemaPath,
|
|
24
|
-
const models = new Set<string>()
|
|
25
|
-
const modelRegex = /^model\s+(\w+)\s*\{/gm
|
|
26
|
-
let match
|
|
22
|
+
if (!(await fileExists(schemaPath))) return new Set()
|
|
23
|
+
const schema = await readFile(schemaPath, 'utf-8')
|
|
24
|
+
const models = new Set<string>()
|
|
25
|
+
const modelRegex = /^model\s+(\w+)\s*\{/gm
|
|
26
|
+
let match
|
|
27
27
|
while ((match = modelRegex.exec(schema)) !== null) {
|
|
28
|
-
models.add(match[1]!)
|
|
28
|
+
models.add(match[1]!)
|
|
29
29
|
}
|
|
30
|
-
return models
|
|
30
|
+
return models
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
async function readSchemaContent(schemaPath: string): Promise<string> {
|
|
34
|
-
if (!(await fileExists(schemaPath))) return
|
|
35
|
-
return readFile(schemaPath,
|
|
34
|
+
if (!(await fileExists(schemaPath))) return ''
|
|
35
|
+
return readFile(schemaPath, 'utf-8')
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
async function readOptionalFile(path: string): Promise<string> {
|
|
39
|
-
if (!(await fileExists(path))) return
|
|
40
|
-
return readFile(path,
|
|
39
|
+
if (!(await fileExists(path))) return ''
|
|
40
|
+
return readFile(path, 'utf-8')
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
async function detectProjectPackageManager(): Promise<string> {
|
|
44
|
-
const entries = new Set(await readdir(process.cwd()))
|
|
45
|
-
return detectPackageManager(entries)
|
|
44
|
+
const entries = new Set(await readdir(process.cwd()))
|
|
45
|
+
return detectPackageManager(entries)
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function printReport(title: string, report: DiagnosticReport): void {
|
|
49
|
-
console.log()
|
|
50
|
-
console.log(chalk.bold(title))
|
|
51
|
-
console.log(chalk.dim(
|
|
49
|
+
console.log()
|
|
50
|
+
console.log(chalk.bold(title))
|
|
51
|
+
console.log(chalk.dim('─────────────────────────────────'))
|
|
52
52
|
for (const check of report.checks) {
|
|
53
|
-
const icon =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
const icon =
|
|
54
|
+
check.status === 'pass'
|
|
55
|
+
? chalk.green('PASS')
|
|
56
|
+
: check.status === 'warn'
|
|
57
|
+
? chalk.yellow('WARN')
|
|
58
|
+
: chalk.red('FAIL')
|
|
59
|
+
console.log(`${icon} ${check.label}: ${check.message}`)
|
|
60
|
+
if (check.fix) console.log(chalk.dim(` Fix: ${check.fix}`))
|
|
61
|
+
if (check.docs) console.log(chalk.dim(` Docs: ${check.docs}`))
|
|
57
62
|
}
|
|
58
|
-
console.log(chalk.dim(
|
|
59
|
-
console.log(`Status: ${report.status.toUpperCase()}`)
|
|
60
|
-
console.log()
|
|
63
|
+
console.log(chalk.dim('─────────────────────────────────'))
|
|
64
|
+
console.log(`Status: ${report.status.toUpperCase()}`)
|
|
65
|
+
console.log()
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
async function buildReport(
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
async function buildReport(
|
|
69
|
+
schemaPath: string,
|
|
70
|
+
configPath: string,
|
|
71
|
+
mode: 'doctor' | 'deploy' = 'doctor',
|
|
72
|
+
) {
|
|
73
|
+
const resolvedSchema = resolve(process.cwd(), schemaPath)
|
|
74
|
+
const resolvedConfig = resolve(process.cwd(), configPath)
|
|
66
75
|
const [schemaModels, schemaContent, configContent, packageManager] = await Promise.all([
|
|
67
76
|
readSchemaModels(resolvedSchema),
|
|
68
77
|
readSchemaContent(resolvedSchema),
|
|
69
78
|
readOptionalFile(resolvedConfig),
|
|
70
79
|
detectProjectPackageManager(),
|
|
71
|
-
])
|
|
80
|
+
])
|
|
72
81
|
|
|
73
82
|
return createDiagnosticReport({
|
|
74
83
|
schemaModels,
|
|
@@ -78,92 +87,97 @@ async function buildReport(schemaPath: string, configPath: string, mode: "doctor
|
|
|
78
87
|
packageManager,
|
|
79
88
|
schemaPath,
|
|
80
89
|
mode,
|
|
81
|
-
})
|
|
90
|
+
})
|
|
82
91
|
}
|
|
83
92
|
|
|
84
93
|
export function registerDoctorCommand(program: Command): void {
|
|
85
94
|
program
|
|
86
|
-
.command(
|
|
87
|
-
.description(
|
|
88
|
-
.option(
|
|
89
|
-
.option(
|
|
90
|
-
.option(
|
|
95
|
+
.command('doctor')
|
|
96
|
+
.description('Run AI-friendly preflight checks for an Actuate CMS project')
|
|
97
|
+
.option('--schema <path>', 'Path to schema.prisma', 'prisma/schema.prisma')
|
|
98
|
+
.option('--config <path>', 'Path to actuate.config.ts', 'actuate.config.ts')
|
|
99
|
+
.option('--json', 'Print machine-readable JSON')
|
|
91
100
|
.action(async (opts: { schema: string; config: string; json?: boolean }) => {
|
|
92
|
-
const report = await buildReport(opts.schema, opts.config,
|
|
101
|
+
const report = await buildReport(opts.schema, opts.config, 'doctor')
|
|
93
102
|
if (opts.json) {
|
|
94
|
-
console.log(JSON.stringify(report, null, 2))
|
|
103
|
+
console.log(JSON.stringify(report, null, 2))
|
|
95
104
|
} else {
|
|
96
|
-
printReport(
|
|
105
|
+
printReport('Actuate Doctor', report)
|
|
97
106
|
}
|
|
98
|
-
if (report.status ===
|
|
99
|
-
})
|
|
107
|
+
if (report.status === 'fail') process.exitCode = 1
|
|
108
|
+
})
|
|
100
109
|
}
|
|
101
110
|
|
|
102
111
|
export function registerDeployCheckCommand(program: Command): void {
|
|
103
112
|
program
|
|
104
|
-
.command(
|
|
105
|
-
.description(
|
|
106
|
-
.option(
|
|
107
|
-
.option(
|
|
108
|
-
.option(
|
|
113
|
+
.command('deploy:check')
|
|
114
|
+
.description('Check production deployment readiness for Actuate CMS')
|
|
115
|
+
.option('--schema <path>', 'Path to schema.prisma', 'prisma/schema.prisma')
|
|
116
|
+
.option('--config <path>', 'Path to actuate.config.ts', 'actuate.config.ts')
|
|
117
|
+
.option('--json', 'Print machine-readable JSON')
|
|
109
118
|
.action(async (opts: { schema: string; config: string; json?: boolean }) => {
|
|
110
|
-
const report = await buildReport(opts.schema, opts.config,
|
|
119
|
+
const report = await buildReport(opts.schema, opts.config, 'deploy')
|
|
111
120
|
if (opts.json) {
|
|
112
|
-
console.log(JSON.stringify({ ...report, manifest: buildDeploymentManifest() }, null, 2))
|
|
121
|
+
console.log(JSON.stringify({ ...report, manifest: buildDeploymentManifest() }, null, 2))
|
|
113
122
|
} else {
|
|
114
|
-
printReport(
|
|
115
|
-
console.log(chalk.dim(
|
|
123
|
+
printReport('Actuate Deploy Check', report)
|
|
124
|
+
console.log(chalk.dim('Run `actuate verify --full` after deployment succeeds.'))
|
|
116
125
|
}
|
|
117
|
-
if (report.status ===
|
|
118
|
-
})
|
|
126
|
+
if (report.status === 'fail') process.exitCode = 1
|
|
127
|
+
})
|
|
119
128
|
}
|
|
120
129
|
|
|
121
130
|
export function registerVerifyCommand(program: Command): void {
|
|
122
131
|
program
|
|
123
|
-
.command(
|
|
124
|
-
.description(
|
|
125
|
-
.option(
|
|
126
|
-
.option(
|
|
127
|
-
.option(
|
|
132
|
+
.command('verify')
|
|
133
|
+
.description('Verify an installed or deployed Actuate CMS project')
|
|
134
|
+
.option('--full', 'Run the full verification checklist')
|
|
135
|
+
.option('--url <origin>', 'Deployed site origin to verify, for example https://example.com')
|
|
136
|
+
.option('--json', 'Print the deployment manifest as JSON')
|
|
128
137
|
.action(async (opts: { full?: boolean; url?: string; json?: boolean }) => {
|
|
129
|
-
const manifest = buildDeploymentManifest()
|
|
138
|
+
const manifest = buildDeploymentManifest()
|
|
130
139
|
if (opts.json) {
|
|
131
|
-
console.log(JSON.stringify(manifest, null, 2))
|
|
132
|
-
return
|
|
140
|
+
console.log(JSON.stringify(manifest, null, 2))
|
|
141
|
+
return
|
|
133
142
|
}
|
|
134
143
|
|
|
135
|
-
console.log()
|
|
136
|
-
console.log(chalk.bold(opts.full ?
|
|
137
|
-
console.log(chalk.dim(
|
|
138
|
-
console.log(`PASS Manifest generated for ${manifest.packageScope} packages.`)
|
|
144
|
+
console.log()
|
|
145
|
+
console.log(chalk.bold(opts.full ? 'Actuate Full Verification' : 'Actuate Verification'))
|
|
146
|
+
console.log(chalk.dim('─────────────────────────────────'))
|
|
147
|
+
console.log(`PASS Manifest generated for ${manifest.packageScope} packages.`)
|
|
139
148
|
if (!opts.url) {
|
|
140
|
-
console.log(
|
|
141
|
-
console.log(
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
console.log('WARN No --url provided, so remote health/admin endpoints were not checked.')
|
|
150
|
+
console.log(
|
|
151
|
+
chalk.dim('Run `actuate verify --full --url https://your-site.com` after deployment.'),
|
|
152
|
+
)
|
|
153
|
+
console.log()
|
|
154
|
+
return
|
|
144
155
|
}
|
|
145
156
|
|
|
146
|
-
const origin = opts.url.replace(/\/$/,
|
|
147
|
-
const healthUrl = `${origin}${manifest.routes.apiHealth}
|
|
148
|
-
const adminUrl = `${origin}${manifest.routes.adminDefault}
|
|
149
|
-
let failed = false
|
|
157
|
+
const origin = opts.url.replace(/\/$/, '')
|
|
158
|
+
const healthUrl = `${origin}${manifest.routes.apiHealth}`
|
|
159
|
+
const adminUrl = `${origin}${manifest.routes.adminDefault}`
|
|
160
|
+
let failed = false
|
|
150
161
|
|
|
151
|
-
for (const [label, url] of [
|
|
162
|
+
for (const [label, url] of [
|
|
163
|
+
['Health', healthUrl],
|
|
164
|
+
['Admin', adminUrl],
|
|
165
|
+
] as const) {
|
|
152
166
|
try {
|
|
153
|
-
const response = await fetch(url, { method: 'GET' })
|
|
167
|
+
const response = await fetch(url, { method: 'GET' })
|
|
154
168
|
if (response.ok || response.status === 302 || response.status === 401) {
|
|
155
|
-
console.log(`PASS ${label} route responded at ${url}.`)
|
|
169
|
+
console.log(`PASS ${label} route responded at ${url}.`)
|
|
156
170
|
} else {
|
|
157
|
-
failed = true
|
|
158
|
-
console.log(`FAIL ${label} route returned HTTP ${response.status} at ${url}.`)
|
|
171
|
+
failed = true
|
|
172
|
+
console.log(`FAIL ${label} route returned HTTP ${response.status} at ${url}.`)
|
|
159
173
|
}
|
|
160
174
|
} catch (error) {
|
|
161
|
-
failed = true
|
|
162
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
163
|
-
console.log(`FAIL ${label} route could not be reached at ${url}: ${message}`)
|
|
175
|
+
failed = true
|
|
176
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
177
|
+
console.log(`FAIL ${label} route could not be reached at ${url}: ${message}`)
|
|
164
178
|
}
|
|
165
179
|
}
|
|
166
|
-
console.log()
|
|
167
|
-
if (failed) process.exitCode = 1
|
|
168
|
-
})
|
|
180
|
+
console.log()
|
|
181
|
+
if (failed) process.exitCode = 1
|
|
182
|
+
})
|
|
169
183
|
}
|
package/src/commands/export.ts
CHANGED
|
@@ -1,131 +1,121 @@
|
|
|
1
|
-
import { Command } from
|
|
2
|
-
import { writeFile } from
|
|
3
|
-
import ora from
|
|
4
|
-
import { logger } from
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { writeFile } from 'node:fs/promises'
|
|
3
|
+
import ora from 'ora'
|
|
4
|
+
import { logger } from '../utils/logger.js'
|
|
5
5
|
|
|
6
6
|
interface ExportOptions {
|
|
7
|
-
collection?: string
|
|
8
|
-
format: string
|
|
9
|
-
output?: string
|
|
10
|
-
includeDrafts?: boolean
|
|
7
|
+
collection?: string
|
|
8
|
+
format: string
|
|
9
|
+
output?: string
|
|
10
|
+
includeDrafts?: boolean
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function buildOutputPath(options: ExportOptions): string {
|
|
14
|
-
if (options.output) return options.output
|
|
15
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g,
|
|
16
|
-
const ext = options.format ===
|
|
17
|
-
return `actuate-export-${timestamp}.${ext}
|
|
14
|
+
if (options.output) return options.output
|
|
15
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
|
16
|
+
const ext = options.format === 'csv' ? 'csv' : 'json'
|
|
17
|
+
return `actuate-export-${timestamp}.${ext}`
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function jsonToCsv(docs: any[]): string {
|
|
21
|
-
if (docs.length === 0) return
|
|
21
|
+
if (docs.length === 0) return ''
|
|
22
22
|
|
|
23
|
-
const keySet = new Set<string>()
|
|
23
|
+
const keySet = new Set<string>()
|
|
24
24
|
for (const doc of docs) {
|
|
25
|
-
keySet.add(
|
|
26
|
-
keySet.add(
|
|
27
|
-
keySet.add(
|
|
28
|
-
keySet.add(
|
|
29
|
-
keySet.add(
|
|
30
|
-
if (doc.data && typeof doc.data ===
|
|
25
|
+
keySet.add('id')
|
|
26
|
+
keySet.add('collection')
|
|
27
|
+
keySet.add('status')
|
|
28
|
+
keySet.add('createdAt')
|
|
29
|
+
keySet.add('updatedAt')
|
|
30
|
+
if (doc.data && typeof doc.data === 'object') {
|
|
31
31
|
for (const key of Object.keys(doc.data)) {
|
|
32
|
-
keySet.add(`data.${key}`)
|
|
32
|
+
keySet.add(`data.${key}`)
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
const headers = Array.from(keySet)
|
|
36
|
+
const headers = Array.from(keySet)
|
|
37
37
|
|
|
38
38
|
const escapeCsvValue = (val: unknown): string => {
|
|
39
|
-
if (val === null || val === undefined) return
|
|
40
|
-
const str = typeof val ===
|
|
41
|
-
if (str.includes(
|
|
42
|
-
return `"${str.replace(/"/g, '""')}"
|
|
39
|
+
if (val === null || val === undefined) return ''
|
|
40
|
+
const str = typeof val === 'object' ? JSON.stringify(val) : String(val)
|
|
41
|
+
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
|
|
42
|
+
return `"${str.replace(/"/g, '""')}"`
|
|
43
43
|
}
|
|
44
|
-
return str
|
|
45
|
-
}
|
|
44
|
+
return str
|
|
45
|
+
}
|
|
46
46
|
|
|
47
47
|
const rows = docs.map((doc) => {
|
|
48
48
|
return headers
|
|
49
49
|
.map((header) => {
|
|
50
|
-
if (header.startsWith(
|
|
51
|
-
const field = header.slice(5)
|
|
52
|
-
return escapeCsvValue(doc.data?.[field])
|
|
50
|
+
if (header.startsWith('data.')) {
|
|
51
|
+
const field = header.slice(5)
|
|
52
|
+
return escapeCsvValue(doc.data?.[field])
|
|
53
53
|
}
|
|
54
|
-
return escapeCsvValue(doc[header])
|
|
54
|
+
return escapeCsvValue(doc[header])
|
|
55
55
|
})
|
|
56
|
-
.join(
|
|
57
|
-
})
|
|
56
|
+
.join(',')
|
|
57
|
+
})
|
|
58
58
|
|
|
59
|
-
return [headers.join(
|
|
59
|
+
return [headers.join(','), ...rows].join('\n')
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
async function runExport(options: ExportOptions): Promise<void> {
|
|
63
|
-
const target = options.collection ??
|
|
64
|
-
const spinner = ora(`Exporting ${target}…`).start()
|
|
63
|
+
const target = options.collection ?? 'all collections'
|
|
64
|
+
const spinner = ora(`Exporting ${target}…`).start()
|
|
65
65
|
|
|
66
66
|
try {
|
|
67
|
-
const { getDB } = await import(
|
|
68
|
-
const db = getDB<any>()
|
|
67
|
+
const { getDB } = await import('@actuate-media/cms-core')
|
|
68
|
+
const db = getDB<any>()
|
|
69
69
|
|
|
70
|
-
const where: any = { deletedAt: null }
|
|
70
|
+
const where: any = { deletedAt: null }
|
|
71
71
|
|
|
72
72
|
if (options.collection) {
|
|
73
|
-
where.collection = options.collection
|
|
73
|
+
where.collection = options.collection
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
if (!options.includeDrafts) {
|
|
77
|
-
where.status =
|
|
77
|
+
where.status = 'PUBLISHED'
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const docs = await db.document.findMany({
|
|
81
81
|
where,
|
|
82
|
-
orderBy: { createdAt:
|
|
83
|
-
})
|
|
82
|
+
orderBy: { createdAt: 'desc' },
|
|
83
|
+
})
|
|
84
84
|
|
|
85
85
|
if (docs.length === 0) {
|
|
86
|
-
spinner.warn(
|
|
87
|
-
return
|
|
86
|
+
spinner.warn('No documents found matching the criteria.')
|
|
87
|
+
return
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
const outputPath = buildOutputPath(options)
|
|
91
|
-
let content: string
|
|
90
|
+
const outputPath = buildOutputPath(options)
|
|
91
|
+
let content: string
|
|
92
92
|
|
|
93
|
-
if (options.format ===
|
|
94
|
-
content = jsonToCsv(docs)
|
|
93
|
+
if (options.format === 'csv') {
|
|
94
|
+
content = jsonToCsv(docs)
|
|
95
95
|
} else {
|
|
96
|
-
content = JSON.stringify(docs, null, 2)
|
|
96
|
+
content = JSON.stringify(docs, null, 2)
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
await writeFile(outputPath, content,
|
|
100
|
-
spinner.succeed(`Exported ${docs.length} documents to ${outputPath}`)
|
|
99
|
+
await writeFile(outputPath, content, 'utf-8')
|
|
100
|
+
spinner.succeed(`Exported ${docs.length} documents to ${outputPath}`)
|
|
101
101
|
} catch (err) {
|
|
102
|
-
spinner.fail(
|
|
103
|
-
const message = err instanceof Error ? err.message : String(err)
|
|
104
|
-
logger.error(message)
|
|
105
|
-
process.exit(1)
|
|
102
|
+
spinner.fail('Export failed.')
|
|
103
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
104
|
+
logger.error(message)
|
|
105
|
+
process.exit(1)
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
export function registerExportCommand(program: Command): void {
|
|
110
110
|
program
|
|
111
|
-
.command(
|
|
112
|
-
.description(
|
|
113
|
-
.option(
|
|
114
|
-
|
|
115
|
-
"Export only the specified collection (default: all)",
|
|
116
|
-
)
|
|
117
|
-
.option(
|
|
118
|
-
"-f, --format <type>",
|
|
119
|
-
"Output format: json or csv",
|
|
120
|
-
"json",
|
|
121
|
-
)
|
|
122
|
-
.option(
|
|
123
|
-
"-o, --output <path>",
|
|
124
|
-
"Output file path (default: actuate-export-{timestamp}.{format})",
|
|
125
|
-
)
|
|
111
|
+
.command('export')
|
|
112
|
+
.description('Export content to JSON or CSV files')
|
|
113
|
+
.option('-c, --collection <slug>', 'Export only the specified collection (default: all)')
|
|
114
|
+
.option('-f, --format <type>', 'Output format: json or csv', 'json')
|
|
126
115
|
.option(
|
|
127
|
-
|
|
128
|
-
|
|
116
|
+
'-o, --output <path>',
|
|
117
|
+
'Output file path (default: actuate-export-{timestamp}.{format})',
|
|
129
118
|
)
|
|
130
|
-
.
|
|
119
|
+
.option('--include-drafts', 'Include draft documents (default: published only)')
|
|
120
|
+
.action(runExport)
|
|
131
121
|
}
|
package/src/commands/generate.ts
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
|
-
import { Command } from
|
|
2
|
-
import ora from
|
|
3
|
-
import { logger } from
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import ora from 'ora'
|
|
3
|
+
import { logger } from '../utils/logger.js'
|
|
4
4
|
|
|
5
5
|
async function runGenerate(): Promise<void> {
|
|
6
|
-
const spinner = ora(
|
|
6
|
+
const spinner = ora('Generating TypeScript types from schema…').start()
|
|
7
7
|
|
|
8
8
|
try {
|
|
9
9
|
// Dynamically import the codegen module from cms-core
|
|
10
|
-
const { runCodegen } = await import(
|
|
11
|
-
await runCodegen()
|
|
12
|
-
spinner.succeed(
|
|
10
|
+
const { runCodegen } = await import('@actuate-media/cms-core/codegen')
|
|
11
|
+
await runCodegen()
|
|
12
|
+
spinner.succeed('Type generation complete.')
|
|
13
13
|
} catch (err) {
|
|
14
|
-
spinner.fail(
|
|
15
|
-
const message = err instanceof Error ? err.message : String(err)
|
|
16
|
-
logger.error(message)
|
|
17
|
-
process.exitCode = 1
|
|
14
|
+
spinner.fail('Type generation failed.')
|
|
15
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
16
|
+
logger.error(message)
|
|
17
|
+
process.exitCode = 1
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function registerGenerateCommand(program: Command): void {
|
|
22
22
|
program
|
|
23
|
-
.command(
|
|
24
|
-
.description(
|
|
25
|
-
|
|
26
|
-
)
|
|
27
|
-
.action(runGenerate);
|
|
23
|
+
.command('generate')
|
|
24
|
+
.description('Run the TypeScript type generator from the cms-core codegen module')
|
|
25
|
+
.action(runGenerate)
|
|
28
26
|
}
|