@actuate-media/cli 0.4.2 → 0.6.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 +64 -18
- package/CHANGELOG.md +46 -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__/seed.test.js +20 -1
- package/dist/__tests__/seed.test.js.map +1 -1
- package/dist/commands/db-init.d.ts +17 -0
- package/dist/commands/db-init.d.ts.map +1 -1
- package/dist/commands/db-init.js +100 -278
- package/dist/commands/db-init.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/seed.d.ts +10 -0
- package/dist/commands/seed.d.ts.map +1 -1
- package/dist/commands/seed.js +38 -4
- package/dist/commands/seed.js.map +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +5 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/db-init.test.ts +155 -0
- package/src/__tests__/db-sync.test.ts +167 -0
- package/src/__tests__/seed.test.ts +27 -1
- package/src/commands/db-init.ts +93 -266
- package/src/commands/db-sync.ts +227 -0
- package/src/commands/seed.ts +40 -4
- package/src/commands/upgrade.ts +8 -0
- package/src/index.ts +2 -0
package/dist/commands/db-init.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { readFile, writeFile, access } from 'node:fs/promises';
|
|
2
|
-
import { resolve } from 'node:path';
|
|
2
|
+
import { resolve, join, dirname } from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
import { execSync } from 'node:child_process';
|
|
4
5
|
import ora from 'ora';
|
|
5
6
|
import { logger } from '../utils/logger.js';
|
|
6
7
|
const CMS_SCHEMA_MARKER = '// ── Actuate CMS models';
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
7
9
|
export let runDbInitCommand = (command, options) => {
|
|
8
10
|
execSync(command, options);
|
|
9
11
|
};
|
|
@@ -14,293 +16,104 @@ export function setDbInitCommandRunner(runner) {
|
|
|
14
16
|
export function resetDbInitCommandRunner() {
|
|
15
17
|
runDbInitCommand = defaultDbInitCommandRunner;
|
|
16
18
|
}
|
|
17
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Reads the canonical Prisma schema shipped by the installed
|
|
21
|
+
* `@actuate-media/cms-core` package. This is the single source of truth — it has
|
|
22
|
+
* the correct `@@map("actuate_*")` table names and every model the API handlers
|
|
23
|
+
* expect. Previously `db:init` injected a hand-maintained copy that drifted
|
|
24
|
+
* (missing `@@map`, missing models), producing a database the runtime couldn't
|
|
25
|
+
* query. Overridable for tests.
|
|
26
|
+
*/
|
|
27
|
+
export let readCanonicalCmsSchema = async () => {
|
|
28
|
+
const candidates = [];
|
|
18
29
|
try {
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
// `./prisma/schema` is an exported subpath, so this resolves inside the
|
|
31
|
+
// package's `prisma/` directory without depending on the build output.
|
|
32
|
+
const exported = require.resolve('@actuate-media/cms-core/prisma/schema');
|
|
33
|
+
candidates.push(join(dirname(exported), 'schema.prisma'));
|
|
21
34
|
}
|
|
22
35
|
catch {
|
|
23
|
-
|
|
36
|
+
/* exports map missing the subpath — fall through to the main entry */
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const mainEntry = require.resolve('@actuate-media/cms-core');
|
|
40
|
+
candidates.push(join(dirname(mainEntry), '..', 'prisma', 'schema.prisma'));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
/* package not installed */
|
|
44
|
+
}
|
|
45
|
+
for (const candidate of candidates) {
|
|
46
|
+
try {
|
|
47
|
+
return await readFile(candidate, 'utf-8');
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
/* try the next candidate */
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
};
|
|
55
|
+
const defaultSchemaReader = readCanonicalCmsSchema;
|
|
56
|
+
export function setCanonicalSchemaReader(reader) {
|
|
57
|
+
readCanonicalCmsSchema = reader;
|
|
58
|
+
}
|
|
59
|
+
export function resetCanonicalSchemaReader() {
|
|
60
|
+
readCanonicalCmsSchema = defaultSchemaReader;
|
|
61
|
+
}
|
|
62
|
+
function netBraces(line) {
|
|
63
|
+
return (line.match(/\{/g)?.length ?? 0) - (line.match(/\}/g)?.length ?? 0);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Strips the top-level `generator` and `datasource` blocks from a full Prisma
|
|
67
|
+
* schema, leaving only the `enum` and `model` definitions. The consumer keeps
|
|
68
|
+
* their own datasource/generator; we only contribute models.
|
|
69
|
+
*/
|
|
70
|
+
export function extractModelsFragment(fullSchema) {
|
|
71
|
+
const lines = fullSchema.split(/\r?\n/);
|
|
72
|
+
const kept = [];
|
|
73
|
+
let skipping = false;
|
|
74
|
+
let depth = 0;
|
|
75
|
+
for (const line of lines) {
|
|
76
|
+
if (!skipping) {
|
|
77
|
+
if (/^\s*(generator|datasource)\s+[A-Za-z0-9_]+\s*\{/.test(line)) {
|
|
78
|
+
skipping = true;
|
|
79
|
+
depth = netBraces(line);
|
|
80
|
+
if (depth <= 0)
|
|
81
|
+
skipping = false;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
kept.push(line);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
depth += netBraces(line);
|
|
88
|
+
if (depth <= 0)
|
|
89
|
+
skipping = false;
|
|
90
|
+
}
|
|
24
91
|
}
|
|
92
|
+
return kept.join('\n').trim();
|
|
25
93
|
}
|
|
26
|
-
function
|
|
94
|
+
function wrapFragment(models) {
|
|
27
95
|
return `
|
|
28
|
-
|
|
29
|
-
// Auto-injected by \`actuate db:init
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
enum DocumentStatus {
|
|
33
|
-
DRAFT
|
|
34
|
-
PUBLISHED
|
|
35
|
-
ARCHIVED
|
|
36
|
-
SCHEDULED
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
model User {
|
|
40
|
-
id String @id @default(cuid())
|
|
41
|
-
email String @unique
|
|
42
|
-
name String @default("")
|
|
43
|
-
role String @default("EDITOR")
|
|
44
|
-
passwordHash String?
|
|
45
|
-
isActive Boolean @default(true)
|
|
46
|
-
isApproved Boolean @default(false)
|
|
47
|
-
emailVerified Boolean @default(false)
|
|
48
|
-
totpEnabled Boolean @default(false)
|
|
49
|
-
totpSecret String?
|
|
50
|
-
backupCodes Json?
|
|
51
|
-
oauthProvider String?
|
|
52
|
-
oauthId String?
|
|
53
|
-
createdAt DateTime @default(now())
|
|
54
|
-
updatedAt DateTime @updatedAt
|
|
55
|
-
|
|
56
|
-
sessions Session[]
|
|
57
|
-
documentsCreated Document[] @relation("DocumentCreatedBy")
|
|
58
|
-
documentsUpdated Document[] @relation("DocumentUpdatedBy")
|
|
59
|
-
documentsReviewed Document[] @relation("DocumentReviewer")
|
|
60
|
-
versions Version[]
|
|
61
|
-
mediaUploaded Media[] @relation("MediaUploadedBy")
|
|
62
|
-
auditLogs AuditLog[]
|
|
63
|
-
|
|
64
|
-
@@index([role])
|
|
65
|
-
@@index([isActive])
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
model Session {
|
|
69
|
-
id String @id @default(cuid())
|
|
70
|
-
userId String
|
|
71
|
-
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
72
|
-
token String? @unique
|
|
73
|
-
expiresAt DateTime
|
|
74
|
-
revokedAt DateTime?
|
|
75
|
-
ipAddress String?
|
|
76
|
-
userAgent String?
|
|
77
|
-
createdAt DateTime @default(now())
|
|
78
|
-
|
|
79
|
-
@@index([userId])
|
|
80
|
-
@@index([expiresAt])
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
model Document {
|
|
84
|
-
id String @id @default(cuid())
|
|
85
|
-
collection String
|
|
86
|
-
slug String?
|
|
87
|
-
title String?
|
|
88
|
-
data Json
|
|
89
|
-
status DocumentStatus @default(DRAFT)
|
|
90
|
-
plainText String? @db.Text
|
|
91
|
-
locale String?
|
|
92
|
-
folderId String?
|
|
93
|
-
structuredData Json?
|
|
94
|
-
workflowStage String?
|
|
95
|
-
reviewerId String?
|
|
96
|
-
reviewNote String? @db.Text
|
|
97
|
-
publishedAt DateTime?
|
|
98
|
-
scheduledAt DateTime?
|
|
99
|
-
scheduledUnpublishAt DateTime?
|
|
100
|
-
deletedAt DateTime?
|
|
101
|
-
contentHash String?
|
|
102
|
-
siteId String?
|
|
103
|
-
templateId String?
|
|
104
|
-
createdById String
|
|
105
|
-
updatedById String
|
|
106
|
-
createdAt DateTime @default(now())
|
|
107
|
-
updatedAt DateTime @updatedAt
|
|
108
|
-
|
|
109
|
-
createdBy User @relation("DocumentCreatedBy", fields: [createdById], references: [id], onDelete: Restrict)
|
|
110
|
-
updatedBy User @relation("DocumentUpdatedBy", fields: [updatedById], references: [id], onDelete: Restrict)
|
|
111
|
-
reviewer User? @relation("DocumentReviewer", fields: [reviewerId], references: [id], onDelete: SetNull)
|
|
112
|
-
folder Folder? @relation(fields: [folderId], references: [id], onDelete: SetNull)
|
|
113
|
-
versions Version[]
|
|
114
|
-
formSubmissions FormSubmission[]
|
|
115
|
-
|
|
116
|
-
@@unique([collection, slug], name: "collection_slug")
|
|
117
|
-
@@index([collection])
|
|
118
|
-
@@index([status])
|
|
119
|
-
@@index([deletedAt])
|
|
120
|
-
@@index([publishedAt])
|
|
121
|
-
@@index([folderId])
|
|
122
|
-
@@index([locale])
|
|
123
|
-
@@index([scheduledAt])
|
|
124
|
-
@@index([scheduledUnpublishAt])
|
|
125
|
-
@@index([createdById])
|
|
126
|
-
@@index([updatedById])
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
model Media {
|
|
130
|
-
id String @id @default(cuid())
|
|
131
|
-
filename String
|
|
132
|
-
storageKey String @unique
|
|
133
|
-
mimeType String
|
|
134
|
-
fileSize Int
|
|
135
|
-
width Int?
|
|
136
|
-
height Int?
|
|
137
|
-
altText String?
|
|
138
|
-
title String?
|
|
139
|
-
blurHash String?
|
|
140
|
-
focalPointX Float?
|
|
141
|
-
focalPointY Float?
|
|
142
|
-
folderId String?
|
|
143
|
-
uploadedById String
|
|
144
|
-
createdAt DateTime @default(now())
|
|
145
|
-
updatedAt DateTime @updatedAt
|
|
146
|
-
|
|
147
|
-
uploadedBy User @relation("MediaUploadedBy", fields: [uploadedById], references: [id], onDelete: Restrict)
|
|
148
|
-
folder Folder? @relation(fields: [folderId], references: [id], onDelete: SetNull)
|
|
149
|
-
|
|
150
|
-
@@index([folderId])
|
|
151
|
-
@@index([mimeType])
|
|
152
|
-
@@index([createdAt])
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
model Version {
|
|
156
|
-
id String @id @default(cuid())
|
|
157
|
-
documentId String
|
|
158
|
-
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
|
159
|
-
data Json
|
|
160
|
-
changedById String
|
|
161
|
-
changedBy User @relation(fields: [changedById], references: [id], onDelete: Restrict)
|
|
162
|
-
changeType String @default("UPDATE")
|
|
163
|
-
createdAt DateTime @default(now())
|
|
164
|
-
|
|
165
|
-
@@index([documentId])
|
|
166
|
-
@@index([createdAt])
|
|
167
|
-
}
|
|
96
|
+
${CMS_SCHEMA_MARKER} ─────────────────────────────────────────────────────────
|
|
97
|
+
// Auto-injected by \`actuate db:init\` from the installed @actuate-media/cms-core
|
|
98
|
+
// package's canonical prisma/schema.prisma (the single source of truth). Do not
|
|
99
|
+
// edit by hand — re-run \`actuate db:init --force\` after upgrading cms-core.
|
|
168
100
|
|
|
169
|
-
|
|
170
|
-
id String @id @default(cuid())
|
|
171
|
-
name String
|
|
172
|
-
scope String
|
|
173
|
-
parentId String?
|
|
174
|
-
position Int @default(0)
|
|
175
|
-
createdAt DateTime @default(now())
|
|
176
|
-
|
|
177
|
-
parent Folder? @relation("FolderTree", fields: [parentId], references: [id], onDelete: Cascade)
|
|
178
|
-
children Folder[] @relation("FolderTree")
|
|
179
|
-
documents Document[]
|
|
180
|
-
media Media[]
|
|
181
|
-
|
|
182
|
-
@@index([scope])
|
|
183
|
-
@@index([parentId])
|
|
184
|
-
@@index([scope, parentId, position])
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
model Redirect {
|
|
188
|
-
id String @id @default(cuid())
|
|
189
|
-
source String
|
|
190
|
-
destination String
|
|
191
|
-
statusCode Int @default(301)
|
|
192
|
-
isRegex Boolean @default(false)
|
|
193
|
-
notes String?
|
|
194
|
-
createdAt DateTime @default(now())
|
|
195
|
-
updatedAt DateTime @updatedAt
|
|
196
|
-
|
|
197
|
-
@@unique([source])
|
|
198
|
-
@@index([createdAt])
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
model FormSubmission {
|
|
202
|
-
id String @id @default(cuid())
|
|
203
|
-
formId String
|
|
204
|
-
form Document @relation(fields: [formId], references: [id], onDelete: Cascade)
|
|
205
|
-
data Json
|
|
206
|
-
attribution Json?
|
|
207
|
-
submittedAt DateTime @default(now())
|
|
208
|
-
createdAt DateTime @default(now())
|
|
209
|
-
|
|
210
|
-
@@index([formId])
|
|
211
|
-
@@index([submittedAt])
|
|
212
|
-
@@index([createdAt])
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
model AuditLog {
|
|
216
|
-
id String @id @default(cuid())
|
|
217
|
-
event String
|
|
218
|
-
userId String?
|
|
219
|
-
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
|
220
|
-
details Json?
|
|
221
|
-
ipAddress String?
|
|
222
|
-
userAgent String?
|
|
223
|
-
createdAt DateTime @default(now())
|
|
224
|
-
|
|
225
|
-
@@index([event])
|
|
226
|
-
@@index([userId])
|
|
227
|
-
@@index([createdAt])
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
model PasswordResetToken {
|
|
231
|
-
id String @id @default(cuid())
|
|
232
|
-
userId String
|
|
233
|
-
tokenHash String
|
|
234
|
-
expiresAt DateTime
|
|
235
|
-
usedAt DateTime?
|
|
236
|
-
createdAt DateTime @default(now())
|
|
237
|
-
|
|
238
|
-
@@index([tokenHash])
|
|
239
|
-
@@index([userId])
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
model MediaUsage {
|
|
243
|
-
id String @id @default(cuid())
|
|
244
|
-
mediaId String
|
|
245
|
-
documentId String
|
|
246
|
-
fieldPath String?
|
|
247
|
-
createdAt DateTime @default(now())
|
|
248
|
-
|
|
249
|
-
@@unique([mediaId, documentId, fieldPath])
|
|
250
|
-
@@index([mediaId])
|
|
251
|
-
@@index([documentId])
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
model ScriptTag {
|
|
255
|
-
id String @id @default(cuid())
|
|
256
|
-
name String
|
|
257
|
-
code String @db.Text
|
|
258
|
-
placement String
|
|
259
|
-
scope String
|
|
260
|
-
targetPaths String[]
|
|
261
|
-
priority Int @default(100)
|
|
262
|
-
enabled Boolean @default(true)
|
|
263
|
-
createdAt DateTime @default(now())
|
|
264
|
-
updatedAt DateTime @updatedAt
|
|
265
|
-
|
|
266
|
-
@@index([enabled])
|
|
267
|
-
@@index([placement])
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
model PageTemplate {
|
|
271
|
-
id String @id @default(cuid())
|
|
272
|
-
name String
|
|
273
|
-
description String?
|
|
274
|
-
category String @default("content")
|
|
275
|
-
tree Json
|
|
276
|
-
thumbnail String?
|
|
277
|
-
builtIn Boolean @default(false)
|
|
278
|
-
createdAt DateTime @default(now())
|
|
279
|
-
updatedAt DateTime @updatedAt
|
|
280
|
-
|
|
281
|
-
@@index([category])
|
|
282
|
-
@@index([builtIn])
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
model SavedSection {
|
|
286
|
-
id String @id @default(cuid())
|
|
287
|
-
name String
|
|
288
|
-
description String?
|
|
289
|
-
category String @default("content")
|
|
290
|
-
tree Json
|
|
291
|
-
thumbnail String?
|
|
292
|
-
usageCount Int @default(0)
|
|
293
|
-
createdAt DateTime @default(now())
|
|
294
|
-
updatedAt DateTime @updatedAt
|
|
295
|
-
|
|
296
|
-
@@index([category])
|
|
297
|
-
}
|
|
101
|
+
${models}
|
|
298
102
|
`;
|
|
299
103
|
}
|
|
104
|
+
async function fileExists(filePath) {
|
|
105
|
+
try {
|
|
106
|
+
await access(filePath);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
300
113
|
export function registerDbInitCommand(program) {
|
|
301
114
|
program
|
|
302
115
|
.command('db:init')
|
|
303
|
-
.description('Add Actuate CMS models to your Prisma schema
|
|
116
|
+
.description('Add Actuate CMS models (from the installed cms-core) to your Prisma schema')
|
|
304
117
|
.option('--schema <path>', 'Path to schema.prisma', 'prisma/schema.prisma')
|
|
305
118
|
.option('--migrate', 'Run prisma migrate dev after adding models')
|
|
306
119
|
.option('--force', 'Overwrite existing CMS models if present')
|
|
@@ -312,7 +125,16 @@ export function registerDbInitCommand(program) {
|
|
|
312
125
|
process.exitCode = 1;
|
|
313
126
|
return;
|
|
314
127
|
}
|
|
315
|
-
const spinner = ora('Reading
|
|
128
|
+
const spinner = ora('Reading canonical Actuate schema...').start();
|
|
129
|
+
const canonical = await readCanonicalCmsSchema();
|
|
130
|
+
if (!canonical) {
|
|
131
|
+
spinner.fail('Could not locate @actuate-media/cms-core.');
|
|
132
|
+
logger.info('Install it first: `npm install @actuate-media/cms-core`.');
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const fragment = wrapFragment(extractModelsFragment(canonical));
|
|
137
|
+
spinner.text = 'Reading Prisma schema...';
|
|
316
138
|
let content;
|
|
317
139
|
try {
|
|
318
140
|
content = await readFile(schemaPath, 'utf-8');
|
|
@@ -333,7 +155,7 @@ export function registerDbInitCommand(program) {
|
|
|
333
155
|
content = content.substring(0, markerIndex).trimEnd() + '\n';
|
|
334
156
|
}
|
|
335
157
|
spinner.text = 'Adding Actuate CMS models...';
|
|
336
|
-
const updatedContent = content.trimEnd() + '\n' +
|
|
158
|
+
const updatedContent = content.trimEnd() + '\n' + fragment;
|
|
337
159
|
try {
|
|
338
160
|
await writeFile(schemaPath, updatedContent);
|
|
339
161
|
spinner.succeed('Actuate CMS models added to schema.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-init.js","sourceRoot":"","sources":["../../src/commands/db-init.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"db-init.js","sourceRoot":"","sources":["../../src/commands/db-init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Build the consumer's `schema.prisma` from cms-core's canonical schema: strip
|
|
4
|
+
* cms-core's own generator/datasource blocks and prepend the consumer header
|
|
5
|
+
* (client output `../generated/prisma`, no datasource `url` — supplied by
|
|
6
|
+
* `prisma.config.ts`). Pure for testing. Mirrors the scaffolder's builder.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildConsumerSchema(coreSchemaSource: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the installed `@actuate-media/cms-core` package's `prisma/` directory
|
|
11
|
+
* (the source of truth for schema + migrations). Overridable for tests.
|
|
12
|
+
*/
|
|
13
|
+
export declare let resolveCmsCorePrismaDir: () => string | null;
|
|
14
|
+
export declare function setCmsCorePrismaDirResolver(resolver: typeof resolveCmsCorePrismaDir): void;
|
|
15
|
+
export declare function resetCmsCorePrismaDirResolver(): void;
|
|
16
|
+
export interface DbSyncResult {
|
|
17
|
+
schemaWritten: boolean;
|
|
18
|
+
migrationsAdded: string[];
|
|
19
|
+
skippedReason?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Core sync logic, separated from CLI plumbing for testing. Additive for
|
|
23
|
+
* migrations (never deletes existing ones — they are immutable history), and
|
|
24
|
+
* guarded for the schema (won't overwrite a non-auto-synced schema without
|
|
25
|
+
* `force`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function syncPrismaAssets(consumerSchemaPath: string, corePrismaDir: string, opts?: {
|
|
28
|
+
force?: boolean;
|
|
29
|
+
}): Promise<DbSyncResult>;
|
|
30
|
+
export declare function registerDbSyncCommand(program: Command): void;
|
|
31
|
+
//# sourceMappingURL=db-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-sync.d.ts","sourceRoot":"","sources":["../../src/commands/db-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAmCnC;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAcpE;AAED;;;GAGG;AACH,eAAO,IAAI,uBAAuB,QAAO,MAAM,GAAG,IAajD,CAAA;AAID,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,OAAO,uBAAuB,GAAG,IAAI,CAE1F;AAED,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD;AAgBD,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,OAAO,CAAA;IACtB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,kBAAkB,EAAE,MAAM,EAC1B,aAAa,EAAE,MAAM,EACrB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,YAAY,CAAC,CAiDvB;AAsDD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAO5D"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { access, cp, mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
// Marker that identifies a schema this CLI/scaffolder owns (auto-synced from
|
|
9
|
+
// cms-core). We refuse to overwrite a schema lacking it unless --force, so a
|
|
10
|
+
// hand-customized schema is never silently clobbered.
|
|
11
|
+
const AUTO_SYNCED_MARKER = 'AUTO-SYNCED from @actuate-media/cms-core';
|
|
12
|
+
// Must match create-actuate-cms/scripts/sync-prisma-assets.ts `SCAFFOLD_SCHEMA_HEADER`
|
|
13
|
+
// so re-syncing a scaffolded project is idempotent (no spurious diffs).
|
|
14
|
+
const SCHEMA_HEADER = `// ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
// Actuate CMS — Prisma schema
|
|
16
|
+
//
|
|
17
|
+
// AUTO-SYNCED from @actuate-media/cms-core. Do NOT edit the model definitions
|
|
18
|
+
// by hand — they must match the bundled migrations and cms-core's API layer.
|
|
19
|
+
// (Generated by \`actuate db:sync\`)
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
generator client {
|
|
23
|
+
provider = "prisma-client"
|
|
24
|
+
output = "../generated/prisma"
|
|
25
|
+
previewFeatures = ["fullTextSearchPostgres", "relationJoins"]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
datasource db {
|
|
29
|
+
provider = "postgresql"
|
|
30
|
+
}`;
|
|
31
|
+
/**
|
|
32
|
+
* Build the consumer's `schema.prisma` from cms-core's canonical schema: strip
|
|
33
|
+
* cms-core's own generator/datasource blocks and prepend the consumer header
|
|
34
|
+
* (client output `../generated/prisma`, no datasource `url` — supplied by
|
|
35
|
+
* `prisma.config.ts`). Pure for testing. Mirrors the scaffolder's builder.
|
|
36
|
+
*/
|
|
37
|
+
export function buildConsumerSchema(coreSchemaSource) {
|
|
38
|
+
const datasourceMatch = coreSchemaSource.match(/datasource\s+\w+\s*\{[\s\S]*?\}/);
|
|
39
|
+
if (!datasourceMatch || datasourceMatch.index === undefined) {
|
|
40
|
+
throw new Error('Could not locate the `datasource` block in cms-core schema.prisma');
|
|
41
|
+
}
|
|
42
|
+
const body = coreSchemaSource
|
|
43
|
+
.slice(datasourceMatch.index + datasourceMatch[0].length)
|
|
44
|
+
.replace(/^\s+/, '');
|
|
45
|
+
if (!/@@map\("actuate_users"\)/.test(body)) {
|
|
46
|
+
throw new Error('cms-core schema body is missing `@@map("actuate_users")` — aborting sync');
|
|
47
|
+
}
|
|
48
|
+
return `${SCHEMA_HEADER}\n\n${body.trimEnd()}\n`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the installed `@actuate-media/cms-core` package's `prisma/` directory
|
|
52
|
+
* (the source of truth for schema + migrations). Overridable for tests.
|
|
53
|
+
*/
|
|
54
|
+
export let resolveCmsCorePrismaDir = () => {
|
|
55
|
+
try {
|
|
56
|
+
const exported = require.resolve('@actuate-media/cms-core/prisma/schema');
|
|
57
|
+
return dirname(exported);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
/* exports map missing the subpath — try the main entry */
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const mainEntry = require.resolve('@actuate-media/cms-core');
|
|
64
|
+
return join(dirname(mainEntry), '..', 'prisma');
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const defaultPrismaDirResolver = resolveCmsCorePrismaDir;
|
|
71
|
+
export function setCmsCorePrismaDirResolver(resolver) {
|
|
72
|
+
resolveCmsCorePrismaDir = resolver;
|
|
73
|
+
}
|
|
74
|
+
export function resetCmsCorePrismaDirResolver() {
|
|
75
|
+
resolveCmsCorePrismaDir = defaultPrismaDirResolver;
|
|
76
|
+
}
|
|
77
|
+
async function listMigrationDirs(dir) {
|
|
78
|
+
try {
|
|
79
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
80
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Core sync logic, separated from CLI plumbing for testing. Additive for
|
|
88
|
+
* migrations (never deletes existing ones — they are immutable history), and
|
|
89
|
+
* guarded for the schema (won't overwrite a non-auto-synced schema without
|
|
90
|
+
* `force`).
|
|
91
|
+
*/
|
|
92
|
+
export async function syncPrismaAssets(consumerSchemaPath, corePrismaDir, opts = {}) {
|
|
93
|
+
const coreSchemaPath = join(corePrismaDir, 'schema.prisma');
|
|
94
|
+
const coreSchema = await readFile(coreSchemaPath, 'utf-8');
|
|
95
|
+
const nextSchema = buildConsumerSchema(coreSchema);
|
|
96
|
+
const consumerDir = dirname(consumerSchemaPath);
|
|
97
|
+
const consumerMigrationsDir = join(consumerDir, 'migrations');
|
|
98
|
+
const coreMigrationsDir = join(corePrismaDir, 'migrations');
|
|
99
|
+
// Guard the schema overwrite.
|
|
100
|
+
let schemaWritten = false;
|
|
101
|
+
let skippedReason;
|
|
102
|
+
const existing = existsSync(consumerSchemaPath)
|
|
103
|
+
? await readFile(consumerSchemaPath, 'utf-8')
|
|
104
|
+
: null;
|
|
105
|
+
if (existing && !existing.includes(AUTO_SYNCED_MARKER) && !opts.force) {
|
|
106
|
+
skippedReason =
|
|
107
|
+
'schema.prisma is not an auto-synced Actuate schema (no AUTO-SYNCED marker). Re-run with --force to overwrite it.';
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
await mkdir(consumerDir, { recursive: true });
|
|
111
|
+
if (existing !== nextSchema) {
|
|
112
|
+
await writeFile(consumerSchemaPath, nextSchema);
|
|
113
|
+
schemaWritten = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Additively copy any cms-core migrations the consumer doesn't already have.
|
|
117
|
+
const coreMigrations = await listMigrationDirs(coreMigrationsDir);
|
|
118
|
+
const existingMigrations = new Set(await listMigrationDirs(consumerMigrationsDir));
|
|
119
|
+
const migrationsAdded = [];
|
|
120
|
+
if (coreMigrations.length > 0) {
|
|
121
|
+
await mkdir(consumerMigrationsDir, { recursive: true });
|
|
122
|
+
const lockSrc = join(coreMigrationsDir, 'migration_lock.toml');
|
|
123
|
+
const lockDest = join(consumerMigrationsDir, 'migration_lock.toml');
|
|
124
|
+
if (existsSync(lockSrc) && !existsSync(lockDest)) {
|
|
125
|
+
await cp(lockSrc, lockDest);
|
|
126
|
+
}
|
|
127
|
+
for (const name of coreMigrations) {
|
|
128
|
+
if (existingMigrations.has(name))
|
|
129
|
+
continue;
|
|
130
|
+
await cp(join(coreMigrationsDir, name), join(consumerMigrationsDir, name), {
|
|
131
|
+
recursive: true,
|
|
132
|
+
});
|
|
133
|
+
migrationsAdded.push(name);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { schemaWritten, migrationsAdded, skippedReason };
|
|
137
|
+
}
|
|
138
|
+
async function runDbSync(options) {
|
|
139
|
+
const consumerSchemaPath = resolve(process.cwd(), options.schema);
|
|
140
|
+
const spinner = ora('Locating @actuate-media/cms-core…').start();
|
|
141
|
+
const corePrismaDir = resolveCmsCorePrismaDir();
|
|
142
|
+
if (!corePrismaDir) {
|
|
143
|
+
spinner.fail('Could not locate @actuate-media/cms-core.');
|
|
144
|
+
logger.info('Install it first: `npm install @actuate-media/cms-core`.');
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
await access(join(corePrismaDir, 'schema.prisma'));
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
spinner.fail(`cms-core does not ship a Prisma schema at ${corePrismaDir}.`);
|
|
153
|
+
process.exitCode = 1;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
spinner.text = 'Syncing schema + migrations…';
|
|
157
|
+
let result;
|
|
158
|
+
try {
|
|
159
|
+
result = await syncPrismaAssets(consumerSchemaPath, corePrismaDir, { force: options.force });
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
spinner.fail('Failed to sync Prisma assets.');
|
|
163
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
164
|
+
process.exitCode = 1;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
spinner.succeed('Prisma assets synced from cms-core.');
|
|
168
|
+
if (result.skippedReason) {
|
|
169
|
+
logger.warn(`Schema not updated: ${result.skippedReason}`);
|
|
170
|
+
}
|
|
171
|
+
else if (result.schemaWritten) {
|
|
172
|
+
logger.success('schema.prisma refreshed.');
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
logger.info('schema.prisma already up to date.');
|
|
176
|
+
}
|
|
177
|
+
if (result.migrationsAdded.length > 0) {
|
|
178
|
+
logger.success(`Added ${result.migrationsAdded.length} new migration(s).`);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
logger.info('No new migrations to add.');
|
|
182
|
+
}
|
|
183
|
+
if (result.schemaWritten || result.migrationsAdded.length > 0) {
|
|
184
|
+
logger.info('Next: run `npx prisma migrate deploy` then `npx prisma generate`.');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
export function registerDbSyncCommand(program) {
|
|
188
|
+
program
|
|
189
|
+
.command('db:sync')
|
|
190
|
+
.description('Sync the canonical Prisma schema + migrations from the installed cms-core')
|
|
191
|
+
.option('--schema <path>', 'Path to schema.prisma', 'prisma/schema.prisma')
|
|
192
|
+
.option('--force', 'Overwrite schema.prisma even if it lacks the AUTO-SYNCED marker')
|
|
193
|
+
.action(runDbSync);
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=db-sync.js.map
|