@actuate-media/cli 0.4.0 → 0.4.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 (79) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-test.log +21 -10
  3. package/CHANGELOG.md +34 -0
  4. package/dist/__tests__/deployment-diagnostics.test.js +40 -0
  5. package/dist/__tests__/deployment-diagnostics.test.js.map +1 -1
  6. package/dist/__tests__/init.test.js.map +1 -1
  7. package/dist/__tests__/schema-fragment.test.js +1 -1
  8. package/dist/__tests__/schema-fragment.test.js.map +1 -1
  9. package/dist/__tests__/seed.test.js.map +1 -1
  10. package/dist/commands/db-init.d.ts +2 -2
  11. package/dist/commands/db-init.d.ts.map +1 -1
  12. package/dist/commands/db-init.js +32 -32
  13. package/dist/commands/db-init.js.map +1 -1
  14. package/dist/commands/db-status.d.ts +1 -1
  15. package/dist/commands/db-status.d.ts.map +1 -1
  16. package/dist/commands/db-status.js +33 -33
  17. package/dist/commands/db-status.js.map +1 -1
  18. package/dist/commands/doctor.d.ts +1 -1
  19. package/dist/commands/doctor.d.ts.map +1 -1
  20. package/dist/commands/doctor.js +55 -38
  21. package/dist/commands/doctor.js.map +1 -1
  22. package/dist/commands/export.d.ts +1 -1
  23. package/dist/commands/export.d.ts.map +1 -1
  24. package/dist/commands/export.js +32 -32
  25. package/dist/commands/export.js.map +1 -1
  26. package/dist/commands/generate.d.ts +1 -1
  27. package/dist/commands/generate.d.ts.map +1 -1
  28. package/dist/commands/generate.js +8 -8
  29. package/dist/commands/generate.js.map +1 -1
  30. package/dist/commands/import.d.ts +1 -1
  31. package/dist/commands/import.d.ts.map +1 -1
  32. package/dist/commands/import.js +55 -58
  33. package/dist/commands/import.js.map +1 -1
  34. package/dist/commands/init.d.ts.map +1 -1
  35. package/dist/commands/init.js.map +1 -1
  36. package/dist/commands/migrate.d.ts +1 -1
  37. package/dist/commands/migrate.d.ts.map +1 -1
  38. package/dist/commands/migrate.js +18 -24
  39. package/dist/commands/migrate.js.map +1 -1
  40. package/dist/commands/seed.d.ts +1 -1
  41. package/dist/commands/seed.d.ts.map +1 -1
  42. package/dist/commands/seed.js +156 -157
  43. package/dist/commands/seed.js.map +1 -1
  44. package/dist/commands/update-check.d.ts +1 -1
  45. package/dist/commands/update-check.d.ts.map +1 -1
  46. package/dist/commands/update-check.js +34 -27
  47. package/dist/commands/update-check.js.map +1 -1
  48. package/dist/commands/upgrade.d.ts +1 -1
  49. package/dist/commands/upgrade.d.ts.map +1 -1
  50. package/dist/commands/upgrade.js +41 -34
  51. package/dist/commands/upgrade.js.map +1 -1
  52. package/dist/deployment/diagnostics.d.ts +2 -0
  53. package/dist/deployment/diagnostics.d.ts.map +1 -1
  54. package/dist/deployment/diagnostics.js +50 -1
  55. package/dist/deployment/diagnostics.js.map +1 -1
  56. package/dist/index.js +15 -15
  57. package/dist/index.js.map +1 -1
  58. package/dist/utils/logger.d.ts.map +1 -1
  59. package/dist/utils/logger.js +5 -5
  60. package/dist/utils/logger.js.map +1 -1
  61. package/package.json +3 -3
  62. package/src/__tests__/deployment-diagnostics.test.ts +100 -50
  63. package/src/__tests__/init.test.ts +17 -17
  64. package/src/__tests__/schema-fragment.test.ts +29 -25
  65. package/src/__tests__/seed.test.ts +25 -25
  66. package/src/commands/db-init.ts +59 -59
  67. package/src/commands/db-status.ts +70 -68
  68. package/src/commands/doctor.ts +110 -86
  69. package/src/commands/export.ts +65 -75
  70. package/src/commands/generate.ts +14 -16
  71. package/src/commands/import.ts +125 -140
  72. package/src/commands/init.ts +14 -14
  73. package/src/commands/migrate.ts +29 -35
  74. package/src/commands/seed.ts +294 -300
  75. package/src/commands/update-check.ts +77 -72
  76. package/src/commands/upgrade.ts +92 -85
  77. package/src/deployment/diagnostics.ts +124 -61
  78. package/src/index.ts +30 -30
  79. package/src/utils/logger.ts +10 -10
@@ -1,31 +1,33 @@
1
- import { describe, expect, it } from 'vitest';
1
+ import { describe, expect, it } from 'vitest'
2
2
 
3
3
  import {
4
4
  buildDeploymentManifest,
5
5
  createDiagnosticReport,
6
6
  REQUIRED_CMS_MODELS,
7
7
  REQUIRED_ENV_VARS,
8
- } from '../deployment/diagnostics.js';
8
+ } from '../deployment/diagnostics.js'
9
9
 
10
10
  describe('deployment diagnostics', () => {
11
11
  it('tracks deploy-critical models used by first-run admin features', () => {
12
- expect(REQUIRED_CMS_MODELS).toEqual(expect.arrayContaining([
13
- 'User',
14
- 'Session',
15
- 'Document',
16
- 'Media',
17
- 'Version',
18
- 'Folder',
19
- 'Redirect',
20
- 'FormSubmission',
21
- 'AuditLog',
22
- 'PasswordResetToken',
23
- 'MediaUsage',
24
- 'ScriptTag',
25
- 'PageTemplate',
26
- 'SavedSection',
27
- ]));
28
- });
12
+ expect(REQUIRED_CMS_MODELS).toEqual(
13
+ expect.arrayContaining([
14
+ 'User',
15
+ 'Session',
16
+ 'Document',
17
+ 'Media',
18
+ 'Version',
19
+ 'Folder',
20
+ 'Redirect',
21
+ 'FormSubmission',
22
+ 'AuditLog',
23
+ 'PasswordResetToken',
24
+ 'MediaUsage',
25
+ 'ScriptTag',
26
+ 'PageTemplate',
27
+ 'SavedSection',
28
+ ]),
29
+ )
30
+ })
29
31
 
30
32
  it('reports missing models and env vars with exact fix commands', () => {
31
33
  const report = createDiagnosticReport({
@@ -36,22 +38,24 @@ describe('deployment diagnostics', () => {
36
38
  },
37
39
  packageManager: 'pnpm',
38
40
  schemaPath: 'prisma/schema.prisma',
39
- });
41
+ })
40
42
 
41
- expect(report.status).toBe('fail');
42
- expect(report.checks).toEqual(expect.arrayContaining([
43
- expect.objectContaining({
44
- id: 'schema-models',
45
- status: 'fail',
46
- fix: 'Run `actuate db:init --schema prisma/schema.prisma` for new schemas, or update the existing Actuate block from the database setup docs, then create and apply a Prisma migration.',
47
- }),
48
- expect.objectContaining({
49
- id: 'environment',
50
- status: 'fail',
51
- fix: 'Set missing environment variables before deploying: CMS_SECRET, CMS_ENCRYPTION_KEY, NEXT_PUBLIC_SITE_URL.',
52
- }),
53
- ]));
54
- });
43
+ expect(report.status).toBe('fail')
44
+ expect(report.checks).toEqual(
45
+ expect.arrayContaining([
46
+ expect.objectContaining({
47
+ id: 'schema-models',
48
+ status: 'fail',
49
+ fix: 'Run `actuate db:init --schema prisma/schema.prisma` for new schemas, or update the existing Actuate block from the database setup docs, then create and apply a Prisma migration.',
50
+ }),
51
+ expect.objectContaining({
52
+ id: 'environment',
53
+ status: 'fail',
54
+ fix: 'Set missing environment variables before deploying: CMS_SECRET, CMS_ENCRYPTION_KEY, NEXT_PUBLIC_SITE_URL.',
55
+ }),
56
+ ]),
57
+ )
58
+ })
55
59
 
56
60
  it('requires migration connection details for deploy checks', () => {
57
61
  const report = createDiagnosticReport({
@@ -66,23 +70,69 @@ describe('deployment diagnostics', () => {
66
70
  packageManager: 'pnpm',
67
71
  schemaPath: 'prisma/schema.prisma',
68
72
  mode: 'deploy',
69
- });
73
+ })
70
74
 
71
- expect(report.status).toBe('fail');
72
- expect(report.checks).toEqual(expect.arrayContaining([
73
- expect.objectContaining({
74
- id: 'environment',
75
- message: expect.stringContaining('DIRECT_DATABASE_URL'),
76
- }),
77
- ]));
78
- });
75
+ expect(report.status).toBe('fail')
76
+ expect(report.checks).toEqual(
77
+ expect.arrayContaining([
78
+ expect.objectContaining({
79
+ id: 'environment',
80
+ message: expect.stringContaining('DIRECT_DATABASE_URL'),
81
+ }),
82
+ ]),
83
+ )
84
+ })
85
+
86
+ it('warns when designed marketing pages appear to use flat fields instead of page-builder content', () => {
87
+ const report = createDiagnosticReport({
88
+ schemaModels: new Set(REQUIRED_CMS_MODELS),
89
+ schemaContent: '',
90
+ configContent: `
91
+ export default defineConfig({
92
+ collections: {
93
+ pages: {
94
+ slug: 'pages',
95
+ fields: {
96
+ title: { type: 'text' },
97
+ slug: { type: 'slug' },
98
+ heroHeadline: { type: 'text' },
99
+ heroSubheadline: { type: 'text' },
100
+ heroImage: { type: 'media' },
101
+ bodyContent: { type: 'richText' },
102
+ ctaButtonText: { type: 'text' }
103
+ }
104
+ }
105
+ }
106
+ })
107
+ `,
108
+ env: {
109
+ DATABASE_URL: 'postgresql://example',
110
+ CMS_SECRET: 'secret',
111
+ CMS_ENCRYPTION_KEY: 'a'.repeat(64),
112
+ NEXT_PUBLIC_SITE_URL: 'https://example.com',
113
+ },
114
+ packageManager: 'pnpm',
115
+ schemaPath: 'prisma/schema.prisma',
116
+ })
117
+
118
+ expect(report.status).toBe('warn')
119
+ expect(report.checks).toEqual(
120
+ expect.arrayContaining([
121
+ expect.objectContaining({
122
+ id: 'design-first-page-builder',
123
+ status: 'warn',
124
+ docs: 'https://actuatecms.dev/docs/design-first-page-builder',
125
+ }),
126
+ ]),
127
+ )
128
+ })
79
129
 
80
130
  it('exposes machine-readable deployment metadata', () => {
81
- const manifest = buildDeploymentManifest();
131
+ const manifest = buildDeploymentManifest()
82
132
 
83
- expect(manifest.requiredModels).toEqual(REQUIRED_CMS_MODELS);
84
- expect(manifest.requiredEnv).toEqual(REQUIRED_ENV_VARS);
85
- expect(manifest.routes.apiHealth).toBe('/api/cms/health');
86
- expect(manifest.crons.scheduledPublish).toBe('/api/cms/cron/publish');
87
- });
88
- });
133
+ expect(manifest.requiredModels).toEqual(REQUIRED_CMS_MODELS)
134
+ expect(manifest.requiredEnv).toEqual(REQUIRED_ENV_VARS)
135
+ expect(manifest.routes.apiHealth).toBe('/api/cms/health')
136
+ expect(manifest.crons.scheduledPublish).toBe('/api/cms/cron/publish')
137
+ })
138
+ })
@@ -1,30 +1,30 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { Command } from 'commander';
3
- import { describe, expect, it } from 'vitest';
1
+ import { readFile } from 'node:fs/promises'
2
+ import { Command } from 'commander'
3
+ import { describe, expect, it } from 'vitest'
4
4
 
5
- import { buildCreateActuateArgs, registerInitCommand } from '../commands/init.js';
5
+ import { buildCreateActuateArgs, registerInitCommand } from '../commands/init.js'
6
6
 
7
7
  describe('init command', () => {
8
8
  it('registers an init command that delegates to create-actuate-cms', () => {
9
- const program = new Command();
10
- registerInitCommand(program);
9
+ const program = new Command()
10
+ registerInitCommand(program)
11
11
 
12
- const command = program.commands.find((candidate) => candidate.name() === 'init');
12
+ const command = program.commands.find((candidate) => candidate.name() === 'init')
13
13
 
14
- expect(command).toBeDefined();
15
- expect(buildCreateActuateArgs()).toEqual(['create', 'actuate-cms@latest']);
14
+ expect(command).toBeDefined()
15
+ expect(buildCreateActuateArgs()).toEqual(['create', 'actuate-cms@latest'])
16
16
  expect(buildCreateActuateArgs('maidpro-site')).toEqual([
17
17
  'create',
18
18
  'actuate-cms@latest',
19
19
  'maidpro-site',
20
- ]);
21
- });
20
+ ])
21
+ })
22
22
 
23
23
  it('exposes an actuate-cms bin alias for installed CLI usage', async () => {
24
- const raw = await readFile(new URL('../../package.json', import.meta.url), 'utf-8');
25
- const pkg = JSON.parse(raw) as { bin?: Record<string, string> };
24
+ const raw = await readFile(new URL('../../package.json', import.meta.url), 'utf-8')
25
+ const pkg = JSON.parse(raw) as { bin?: Record<string, string> }
26
26
 
27
- expect(pkg.bin?.actuate).toBe('dist/index.js');
28
- expect(pkg.bin?.['actuate-cms']).toBe('dist/index.js');
29
- });
30
- });
27
+ expect(pkg.bin?.actuate).toBe('dist/index.js')
28
+ expect(pkg.bin?.['actuate-cms']).toBe('dist/index.js')
29
+ })
30
+ })
@@ -1,11 +1,15 @@
1
- import { Command } from 'commander';
2
- import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
3
- import { tmpdir } from 'node:os';
4
- import path from 'node:path';
5
- import { afterEach, describe, expect, it, vi } from 'vitest';
6
-
7
- import { registerDbInitCommand, resetDbInitCommandRunner, setDbInitCommandRunner } from '../commands/db-init.js';
8
- import { REQUIRED_CMS_MODELS } from '../deployment/diagnostics.js';
1
+ import { Command } from 'commander'
2
+ import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
3
+ import { tmpdir } from 'node:os'
4
+ import path from 'node:path'
5
+ import { afterEach, describe, expect, it, vi } from 'vitest'
6
+
7
+ import {
8
+ registerDbInitCommand,
9
+ resetDbInitCommandRunner,
10
+ setDbInitCommandRunner,
11
+ } from '../commands/db-init.js'
12
+ import { REQUIRED_CMS_MODELS } from '../deployment/diagnostics.js'
9
13
 
10
14
  function baseSchema() {
11
15
  return `generator client {
@@ -16,32 +20,32 @@ datasource db {
16
20
  provider = "postgresql"
17
21
  url = env("DATABASE_URL")
18
22
  }
19
- `;
23
+ `
20
24
  }
21
25
 
22
26
  describe('db:init schema fragment', () => {
23
27
  afterEach(() => {
24
- resetDbInitCommandRunner();
25
- vi.restoreAllMocks();
26
- });
28
+ resetDbInitCommandRunner()
29
+ vi.restoreAllMocks()
30
+ })
27
31
 
28
32
  it('injects all deploy-critical Actuate models', async () => {
29
- const tempRoot = await mkdtemp(path.join(tmpdir(), 'actuate-db-init-'));
30
- const schemaPath = path.join(tempRoot, 'schema.prisma');
31
- await writeFile(schemaPath, baseSchema());
33
+ const tempRoot = await mkdtemp(path.join(tmpdir(), 'actuate-db-init-'))
34
+ const schemaPath = path.join(tempRoot, 'schema.prisma')
35
+ await writeFile(schemaPath, baseSchema())
32
36
 
33
- vi.spyOn(process, 'cwd').mockReturnValue(tempRoot);
34
- setDbInitCommandRunner(() => undefined);
35
- const command = new Command();
36
- registerDbInitCommand(command);
37
+ vi.spyOn(process, 'cwd').mockReturnValue(tempRoot)
38
+ setDbInitCommandRunner(() => undefined)
39
+ const command = new Command()
40
+ registerDbInitCommand(command)
37
41
 
38
- await command.parseAsync(['node', 'actuate', 'db:init', '--schema', 'schema.prisma']);
42
+ await command.parseAsync(['node', 'actuate', 'db:init', '--schema', 'schema.prisma'])
39
43
 
40
- const schema = await readFile(schemaPath, 'utf-8');
44
+ const schema = await readFile(schemaPath, 'utf-8')
41
45
  for (const model of REQUIRED_CMS_MODELS) {
42
- expect(schema).toContain(`model ${model} `);
46
+ expect(schema).toContain(`model ${model} `)
43
47
  }
44
48
 
45
- await rm(tempRoot, { recursive: true, force: true });
46
- });
47
- });
49
+ await rm(tempRoot, { recursive: true, force: true })
50
+ })
51
+ })
@@ -1,17 +1,17 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
1
+ import { afterEach, describe, expect, it, vi } from 'vitest'
2
2
 
3
3
  vi.mock('@actuate-media/cms-core', () => ({
4
4
  extractPlainText: vi.fn((value: string) => value.replace(/<[^>]+>/g, ' ')),
5
5
  hashContent: vi.fn(async () => 'hash-1'),
6
6
  sanitizeHtml: vi.fn((value: string) => value.replace(/<script[\s\S]*?<\/script>/gi, '')),
7
7
  createInitialAdmin: vi.fn(),
8
- }));
8
+ }))
9
9
 
10
- import { createSeedDocument, ensureSeedAdmin, normalizeSeedPayload } from '../commands/seed.js';
10
+ import { createSeedDocument, ensureSeedAdmin, normalizeSeedPayload } from '../commands/seed.js'
11
11
 
12
12
  afterEach(() => {
13
- vi.unstubAllEnvs();
14
- });
13
+ vi.unstubAllEnvs()
14
+ })
15
15
 
16
16
  describe('seed payload normalization', () => {
17
17
  it('separates globals from collection documents', () => {
@@ -30,38 +30,38 @@ describe('seed payload normalization', () => {
30
30
  },
31
31
  ],
32
32
  },
33
- });
33
+ })
34
34
 
35
35
  expect(normalized.globals).toEqual([
36
36
  {
37
37
  slug: 'site-settings',
38
38
  data: { siteName: 'MaidPro', phone: '555-0100' },
39
39
  },
40
- ]);
40
+ ])
41
41
  expect(normalized.documents).toEqual([
42
42
  {
43
43
  collection: 'pages',
44
44
  data: { title: 'Home', slug: 'home' },
45
45
  status: 'PUBLISHED',
46
46
  },
47
- ]);
48
- });
47
+ ])
48
+ })
49
49
 
50
50
  it('keeps backwards-compatible collection-key seed files', () => {
51
51
  const normalized = normalizeSeedPayload({
52
52
  pages: [{ title: 'Home', slug: 'home' }],
53
- });
53
+ })
54
54
 
55
- expect(normalized.globals).toEqual([]);
55
+ expect(normalized.globals).toEqual([])
56
56
  expect(normalized.documents).toEqual([
57
57
  {
58
58
  collection: 'pages',
59
59
  data: { title: 'Home', slug: 'home' },
60
60
  status: 'DRAFT',
61
61
  },
62
- ]);
63
- });
64
- });
62
+ ])
63
+ })
64
+ })
65
65
 
66
66
  describe('seed admin handling', () => {
67
67
  it('refuses to create a first admin without credentials', async () => {
@@ -69,11 +69,11 @@ describe('seed admin handling', () => {
69
69
  user: {
70
70
  findFirst: vi.fn().mockResolvedValue(null),
71
71
  },
72
- };
72
+ }
73
73
 
74
- await expect(ensureSeedAdmin(db)).rejects.toThrow('CMS_ADMIN_EMAIL and CMS_ADMIN_PASSWORD');
75
- });
76
- });
74
+ await expect(ensureSeedAdmin(db)).rejects.toThrow('CMS_ADMIN_EMAIL and CMS_ADMIN_PASSWORD')
75
+ })
76
+ })
77
77
 
78
78
  describe('seed document creation', () => {
79
79
  it('creates documents with versions, content hashes, and sanitized HTML', async () => {
@@ -84,10 +84,10 @@ describe('seed document creation', () => {
84
84
  version: {
85
85
  create: vi.fn(),
86
86
  },
87
- };
87
+ }
88
88
  const db = {
89
89
  $transaction: vi.fn(async (fn) => fn(tx)),
90
- };
90
+ }
91
91
 
92
92
  await createSeedDocument(db, 'admin-1', {
93
93
  collection: 'pages',
@@ -97,7 +97,7 @@ describe('seed document creation', () => {
97
97
  slug: 'home',
98
98
  content: '<p>Hello</p><script>alert(1)</script>',
99
99
  },
100
- });
100
+ })
101
101
 
102
102
  expect(tx.document.create).toHaveBeenCalledWith({
103
103
  data: expect.objectContaining({
@@ -114,13 +114,13 @@ describe('seed document creation', () => {
114
114
  content: '<p>Hello</p>',
115
115
  }),
116
116
  }),
117
- });
117
+ })
118
118
  expect(tx.version.create).toHaveBeenCalledWith({
119
119
  data: expect.objectContaining({
120
120
  documentId: 'doc-1',
121
121
  changedById: 'admin-1',
122
122
  changeType: 'CREATE',
123
123
  }),
124
- });
125
- });
126
- });
124
+ })
125
+ })
126
+ })
@@ -1,32 +1,32 @@
1
- import { Command } from "commander";
2
- import { readFile, writeFile, access } from "node:fs/promises";
3
- import { resolve, join } from "node:path";
4
- import { execSync, type ExecSyncOptions } from "node:child_process";
5
- import ora from "ora";
6
- import { logger } from "../utils/logger.js";
1
+ import { Command } from 'commander'
2
+ import { readFile, writeFile, access } from 'node:fs/promises'
3
+ import { resolve, join } from 'node:path'
4
+ import { execSync, type ExecSyncOptions } from 'node:child_process'
5
+ import ora from 'ora'
6
+ import { logger } from '../utils/logger.js'
7
7
 
8
- const CMS_SCHEMA_MARKER = "// ── Actuate CMS models";
8
+ const CMS_SCHEMA_MARKER = '// ── Actuate CMS models'
9
9
 
10
10
  export let runDbInitCommand = (command: string, options: ExecSyncOptions): void => {
11
- execSync(command, options);
12
- };
11
+ execSync(command, options)
12
+ }
13
13
 
14
- const defaultDbInitCommandRunner = runDbInitCommand;
14
+ const defaultDbInitCommandRunner = runDbInitCommand
15
15
 
16
16
  export function setDbInitCommandRunner(runner: typeof runDbInitCommand): void {
17
- runDbInitCommand = runner;
17
+ runDbInitCommand = runner
18
18
  }
19
19
 
20
20
  export function resetDbInitCommandRunner(): void {
21
- runDbInitCommand = defaultDbInitCommandRunner;
21
+ runDbInitCommand = defaultDbInitCommandRunner
22
22
  }
23
23
 
24
24
  async function fileExists(filePath: string): Promise<boolean> {
25
25
  try {
26
- await access(filePath);
27
- return true;
26
+ await access(filePath)
27
+ return true
28
28
  } catch {
29
- return false;
29
+ return false
30
30
  }
31
31
  }
32
32
 
@@ -302,83 +302,83 @@ model SavedSection {
302
302
 
303
303
  @@index([category])
304
304
  }
305
- `;
305
+ `
306
306
  }
307
307
 
308
308
  export function registerDbInitCommand(program: Command): void {
309
309
  program
310
- .command("db:init")
311
- .description("Add Actuate CMS models to your Prisma schema and generate the client")
312
- .option("--schema <path>", "Path to schema.prisma", "prisma/schema.prisma")
313
- .option("--migrate", "Run prisma migrate dev after adding models")
314
- .option("--force", "Overwrite existing CMS models if present")
310
+ .command('db:init')
311
+ .description('Add Actuate CMS models to your Prisma schema and generate the client')
312
+ .option('--schema <path>', 'Path to schema.prisma', 'prisma/schema.prisma')
313
+ .option('--migrate', 'Run prisma migrate dev after adding models')
314
+ .option('--force', 'Overwrite existing CMS models if present')
315
315
  .action(async (opts: { schema: string; migrate?: boolean; force?: boolean }) => {
316
- const schemaPath = resolve(process.cwd(), opts.schema);
316
+ const schemaPath = resolve(process.cwd(), opts.schema)
317
317
 
318
318
  if (!(await fileExists(schemaPath))) {
319
- logger.error(`Schema file not found at ${schemaPath}`);
320
- logger.info("Run `npx prisma init` first, or specify --schema <path>.");
321
- process.exitCode = 1;
322
- return;
319
+ logger.error(`Schema file not found at ${schemaPath}`)
320
+ logger.info('Run `npx prisma init` first, or specify --schema <path>.')
321
+ process.exitCode = 1
322
+ return
323
323
  }
324
324
 
325
- const spinner = ora("Reading Prisma schema...").start();
325
+ const spinner = ora('Reading Prisma schema...').start()
326
326
 
327
- let content: string;
327
+ let content: string
328
328
  try {
329
- content = await readFile(schemaPath, "utf-8");
329
+ content = await readFile(schemaPath, 'utf-8')
330
330
  } catch (err) {
331
- spinner.fail("Failed to read schema file.");
332
- logger.error(err instanceof Error ? err.message : String(err));
333
- process.exitCode = 1;
334
- return;
331
+ spinner.fail('Failed to read schema file.')
332
+ logger.error(err instanceof Error ? err.message : String(err))
333
+ process.exitCode = 1
334
+ return
335
335
  }
336
336
 
337
337
  if (content.includes(CMS_SCHEMA_MARKER)) {
338
338
  if (!opts.force) {
339
- spinner.info("Actuate CMS models already present in schema. Use --force to overwrite.");
340
- return;
339
+ spinner.info('Actuate CMS models already present in schema. Use --force to overwrite.')
340
+ return
341
341
  }
342
- spinner.text = "Removing existing CMS models...";
343
- const markerIndex = content.indexOf(CMS_SCHEMA_MARKER);
344
- content = content.substring(0, markerIndex).trimEnd() + "\n";
342
+ spinner.text = 'Removing existing CMS models...'
343
+ const markerIndex = content.indexOf(CMS_SCHEMA_MARKER)
344
+ content = content.substring(0, markerIndex).trimEnd() + '\n'
345
345
  }
346
346
 
347
- spinner.text = "Adding Actuate CMS models...";
348
- const updatedContent = content.trimEnd() + "\n" + getCmsSchemaFragment();
347
+ spinner.text = 'Adding Actuate CMS models...'
348
+ const updatedContent = content.trimEnd() + '\n' + getCmsSchemaFragment()
349
349
 
350
350
  try {
351
- await writeFile(schemaPath, updatedContent);
352
- spinner.succeed("Actuate CMS models added to schema.");
351
+ await writeFile(schemaPath, updatedContent)
352
+ spinner.succeed('Actuate CMS models added to schema.')
353
353
  } catch (err) {
354
- spinner.fail("Failed to write schema file.");
355
- logger.error(err instanceof Error ? err.message : String(err));
356
- process.exitCode = 1;
357
- return;
354
+ spinner.fail('Failed to write schema file.')
355
+ logger.error(err instanceof Error ? err.message : String(err))
356
+ process.exitCode = 1
357
+ return
358
358
  }
359
359
 
360
- const execOpts: ExecSyncOptions = { stdio: "inherit", cwd: process.cwd() };
360
+ const execOpts: ExecSyncOptions = { stdio: 'inherit', cwd: process.cwd() }
361
361
 
362
- const genSpinner = ora("Running prisma generate...").start();
362
+ const genSpinner = ora('Running prisma generate...').start()
363
363
  try {
364
- genSpinner.stop();
365
- runDbInitCommand("npx prisma generate", execOpts);
366
- logger.success("Prisma client generated.");
364
+ genSpinner.stop()
365
+ runDbInitCommand('npx prisma generate', execOpts)
366
+ logger.success('Prisma client generated.')
367
367
  } catch {
368
- logger.warn("prisma generate failed. You may need to set DATABASE_URL first.");
368
+ logger.warn('prisma generate failed. You may need to set DATABASE_URL first.')
369
369
  }
370
370
 
371
371
  if (opts.migrate) {
372
- const migSpinner = ora("Running prisma migrate dev...").start();
372
+ const migSpinner = ora('Running prisma migrate dev...').start()
373
373
  try {
374
- migSpinner.stop();
375
- runDbInitCommand("npx prisma migrate dev --name actuate-cms-init", execOpts);
376
- logger.success("Migration created and applied.");
374
+ migSpinner.stop()
375
+ runDbInitCommand('npx prisma migrate dev --name actuate-cms-init', execOpts)
376
+ logger.success('Migration created and applied.')
377
377
  } catch {
378
- logger.warn("prisma migrate dev failed. Run it manually after setting DATABASE_URL.");
378
+ logger.warn('prisma migrate dev failed. Run it manually after setting DATABASE_URL.')
379
379
  }
380
380
  } else {
381
- logger.info("Run `npx prisma migrate dev --name actuate-cms` to create the migration.");
381
+ logger.info('Run `npx prisma migrate dev --name actuate-cms` to create the migration.')
382
382
  }
383
- });
383
+ })
384
384
  }