@doubledigit/cli 0.1.0 → 0.2.1
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/LICENSE +1 -1
- package/README.md +102 -0
- package/dist/codegen.d.ts +6 -2
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +81 -13
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +36 -3
- package/dist/commands/create.js +2 -2
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +24 -11
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +46 -12
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +163 -0
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +6 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +5 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +25 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -5
- package/dist/lib/marketplace-schema.d.ts +24 -24
- package/dist/lib/onboarding.d.ts +3 -3
- package/dist/lib/onboarding.d.ts.map +1 -1
- package/dist/lib/onboarding.js +282 -237
- package/dist/lib/package-entry.d.ts +6 -0
- package/dist/lib/package-entry.d.ts.map +1 -0
- package/dist/lib/package-entry.js +63 -0
- package/dist/lib/scoped-migrations.d.ts +11 -0
- package/dist/lib/scoped-migrations.d.ts.map +1 -0
- package/dist/lib/scoped-migrations.js +156 -0
- package/dist/lib/validators.d.ts.map +1 -1
- package/dist/lib/validators.js +12 -49
- package/dist/paths.d.ts +4 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +2 -0
- package/dist/scanner.d.ts +4 -0
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +21 -0
- package/package.json +12 -5
package/LICENSE
CHANGED
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# @doubledigit/cli
|
|
2
|
+
|
|
3
|
+
CLI for Double Digit local setup, project bootstrapping, extension management, and database workflows.
|
|
4
|
+
|
|
5
|
+
Double Digit is a pluggable Next.js/Payload developer control plane. The CLI is the supported command surface for creating a new Double Digit project and managing an existing local workspace.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
Run the published package directly:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx @doubledigit/cli init my-project
|
|
13
|
+
pnpm dlx @doubledigit/cli init my-project
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Inside a Double Digit project, use the repo-local shortcut:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm dd doctor
|
|
20
|
+
pnpm dd onboard
|
|
21
|
+
pnpm dd run
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If installed globally, the binary is available as `dd`:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
dd doctor
|
|
28
|
+
dd onboard
|
|
29
|
+
dd run
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
- Node.js 20 or newer
|
|
35
|
+
- pnpm 9 or newer
|
|
36
|
+
|
|
37
|
+
Enable pnpm through Corepack when needed:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
corepack enable
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Core Commands
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
dd init <project-name> # scaffold a new Double Digit project
|
|
47
|
+
dd doctor # check local prerequisites and project health
|
|
48
|
+
dd onboard # prepare env, dependencies, DB, migrations, and types
|
|
49
|
+
dd run # bootstrap the runtime DB and start the app
|
|
50
|
+
dd db status # inspect migration state
|
|
51
|
+
dd db migrate # run migrations
|
|
52
|
+
dd db create <target> <name> # create a migration for shared or micro-app schema
|
|
53
|
+
dd add <source> # install an extension
|
|
54
|
+
dd sync # regenerate workspace registries
|
|
55
|
+
dd list # list discovered micro-apps
|
|
56
|
+
dd enable <name> # enable a micro-app
|
|
57
|
+
dd disable <name> # disable a micro-app
|
|
58
|
+
dd remove <name> # remove a micro-app package and wiring
|
|
59
|
+
dd reconcile # detect registry, lock file, and package drift
|
|
60
|
+
dd marketplace <subcommand> # manage marketplace registrations
|
|
61
|
+
dd browse # browse marketplace extensions
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`add` is also available as `install`, and `remove` is also available as `uninstall`.
|
|
65
|
+
|
|
66
|
+
## New Project Bootstrap
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx @doubledigit/cli init my-project --yes
|
|
70
|
+
cd my-project
|
|
71
|
+
pnpm dd run
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Useful `init` options:
|
|
75
|
+
|
|
76
|
+
- `--yes` skips prompts and uses defaults.
|
|
77
|
+
- `--run` bootstraps and starts the app after scaffolding.
|
|
78
|
+
- `--skip-install` skips dependency installation.
|
|
79
|
+
- `--no-git` removes the cloned `.git` directory from the generated project.
|
|
80
|
+
|
|
81
|
+
## Existing Project Setup
|
|
82
|
+
|
|
83
|
+
Use `onboard` for setup-only work:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pnpm dd onboard
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Use `run` when you want the CLI to prepare the runtime database and then start the app:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pnpm dd run
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Leave `DATABASE_URL` empty in `apps/main-app/.env` to use the default CLI-managed embedded PostgreSQL flow. Provide `DATABASE_URL` through the environment or `.env` when using your own PostgreSQL instance.
|
|
96
|
+
|
|
97
|
+
## Links
|
|
98
|
+
|
|
99
|
+
- Repository: https://github.com/crystalphantom/double-digit
|
|
100
|
+
- Issues: https://github.com/crystalphantom/double-digit/issues
|
|
101
|
+
- Getting started: https://github.com/crystalphantom/double-digit/blob/main/docs/getting-started.md
|
|
102
|
+
- Architecture: https://github.com/crystalphantom/double-digit/blob/main/docs/architecture.md
|
package/dist/codegen.d.ts
CHANGED
|
@@ -5,8 +5,12 @@
|
|
|
5
5
|
* that the Next.js bundler can tree-shake and analyze.
|
|
6
6
|
*/
|
|
7
7
|
import type { DiscoveredApp, DiscoveredPayloadPlugin } from './scanner.js';
|
|
8
|
-
export declare function generateMicroAppsModule(enabledApps: DiscoveredApp[]): string;
|
|
8
|
+
export declare function generateMicroAppsModule(outputPath: string, enabledApps: DiscoveredApp[]): string;
|
|
9
9
|
export declare function writeMicroAppsModule(outputPath: string, enabledApps: DiscoveredApp[]): void;
|
|
10
|
-
export declare function
|
|
10
|
+
export declare function generateMicroAppTranspilePackagesModule(apps: DiscoveredApp[]): string;
|
|
11
|
+
export declare function writeMicroAppTranspilePackagesModule(outputPath: string, apps: DiscoveredApp[]): void;
|
|
12
|
+
export declare function generatePayloadPluginsModule(outputPath: string, plugins: DiscoveredPayloadPlugin[]): string;
|
|
11
13
|
export declare function writePayloadPluginsModule(outputPath: string, plugins: DiscoveredPayloadPlugin[]): void;
|
|
14
|
+
export declare function generateScopedPayloadConfigModule(app: DiscoveredApp): string;
|
|
15
|
+
export declare function writeScopedPayloadConfigs(configsDir: string, apps: DiscoveredApp[]): void;
|
|
12
16
|
//# sourceMappingURL=codegen.d.ts.map
|
package/dist/codegen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAe3E,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,aAAa,EAAE,GAC3B,MAAM,CAmER;AAED,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,aAAa,EAAE,GAC3B,IAAI,CAIN;AAED,wBAAgB,uCAAuC,CACrD,IAAI,EAAE,aAAa,EAAE,GACpB,MAAM,CAcR;AAED,wBAAgB,oCAAoC,CAClD,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,aAAa,EAAE,GACpB,IAAI,CAIN;AAID,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,uBAAuB,EAAE,GACjC,MAAM,CAuBR;AAED,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,uBAAuB,EAAE,GACjC,IAAI,CAIN;AAID,wBAAgB,iCAAiC,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CA6B5E;AAED,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,aAAa,EAAE,GACpB,IAAI,CAuBN"}
|
package/dist/codegen.js
CHANGED
|
@@ -5,16 +5,22 @@
|
|
|
5
5
|
* that the Next.js bundler can tree-shake and analyze.
|
|
6
6
|
*/
|
|
7
7
|
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
8
9
|
const HEADER = `// ⚠️ AUTO-GENERATED by @doubledigit/cli sync
|
|
9
10
|
// Do not edit manually. Run \`dd sync\` or \`npx @doubledigit/cli sync\` to regenerate.
|
|
10
|
-
//
|
|
11
|
-
// Generated at: {{TIMESTAMP}}
|
|
12
11
|
`;
|
|
13
|
-
|
|
14
|
-
const
|
|
12
|
+
function toImportSpecifier(outputPath, entryFile) {
|
|
13
|
+
const relativePath = path.relative(path.dirname(outputPath), entryFile);
|
|
14
|
+
const normalized = relativePath
|
|
15
|
+
.split(path.sep)
|
|
16
|
+
.join('/')
|
|
17
|
+
.replace(/\.(cts|mts|ts|tsx|js|jsx)$/, '');
|
|
18
|
+
return normalized.startsWith('.') ? normalized : `./${normalized}`;
|
|
19
|
+
}
|
|
20
|
+
export function generateMicroAppsModule(outputPath, enabledApps) {
|
|
15
21
|
const lines = [];
|
|
16
22
|
// Header
|
|
17
|
-
lines.push(HEADER
|
|
23
|
+
lines.push(HEADER);
|
|
18
24
|
// Type + config imports
|
|
19
25
|
lines.push("import type { DDApp } from '@doubledigit/micro-app-sdk';");
|
|
20
26
|
lines.push("import { isAppEnabled } from '@doubledigit/micro-app-sdk';");
|
|
@@ -74,20 +80,37 @@ export function generateMicroAppsModule(enabledApps) {
|
|
|
74
80
|
return lines.join('\n');
|
|
75
81
|
}
|
|
76
82
|
export function writeMicroAppsModule(outputPath, enabledApps) {
|
|
77
|
-
const content = generateMicroAppsModule(enabledApps);
|
|
78
|
-
fs.mkdirSync(
|
|
83
|
+
const content = generateMicroAppsModule(outputPath, enabledApps);
|
|
84
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
85
|
+
fs.writeFileSync(outputPath, content, 'utf-8');
|
|
86
|
+
}
|
|
87
|
+
export function generateMicroAppTranspilePackagesModule(apps) {
|
|
88
|
+
const lines = [];
|
|
89
|
+
lines.push(HEADER);
|
|
90
|
+
lines.push('// Packages statically imported by the generated micro-app registry.');
|
|
91
|
+
lines.push('// next.config.ts includes these in transpilePackages for source-first workspace dev.');
|
|
92
|
+
lines.push('export const microAppTranspilePackages = [');
|
|
93
|
+
for (const app of apps) {
|
|
94
|
+
lines.push(` '${app.npmName}',`);
|
|
95
|
+
}
|
|
96
|
+
lines.push('] as const;');
|
|
97
|
+
lines.push('');
|
|
98
|
+
return lines.join('\n');
|
|
99
|
+
}
|
|
100
|
+
export function writeMicroAppTranspilePackagesModule(outputPath, apps) {
|
|
101
|
+
const content = generateMicroAppTranspilePackagesModule(apps);
|
|
102
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
79
103
|
fs.writeFileSync(outputPath, content, 'utf-8');
|
|
80
104
|
}
|
|
81
105
|
// ─── Payload Plugins codegen ───────────────────────────────────────────
|
|
82
|
-
export function generatePayloadPluginsModule(plugins) {
|
|
83
|
-
const timestamp = new Date().toISOString();
|
|
106
|
+
export function generatePayloadPluginsModule(outputPath, plugins) {
|
|
84
107
|
const lines = [];
|
|
85
|
-
lines.push(HEADER
|
|
108
|
+
lines.push(HEADER);
|
|
86
109
|
lines.push("import type { Plugin } from 'payload';");
|
|
87
110
|
lines.push('');
|
|
88
111
|
if (plugins.length > 0) {
|
|
89
112
|
for (const plugin of plugins) {
|
|
90
|
-
lines.push(`import ${plugin.importName} from '${plugin.
|
|
113
|
+
lines.push(`import ${plugin.importName} from '${toImportSpecifier(outputPath, plugin.entryFile)}';`);
|
|
91
114
|
}
|
|
92
115
|
}
|
|
93
116
|
lines.push('');
|
|
@@ -101,7 +124,52 @@ export function generatePayloadPluginsModule(plugins) {
|
|
|
101
124
|
return lines.join('\n');
|
|
102
125
|
}
|
|
103
126
|
export function writePayloadPluginsModule(outputPath, plugins) {
|
|
104
|
-
const content = generatePayloadPluginsModule(plugins);
|
|
105
|
-
fs.mkdirSync(
|
|
127
|
+
const content = generatePayloadPluginsModule(outputPath, plugins);
|
|
128
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
106
129
|
fs.writeFileSync(outputPath, content, 'utf-8');
|
|
107
130
|
}
|
|
131
|
+
// ─── Scoped Payload configs codegen ─────────────────────────────────────
|
|
132
|
+
export function generateScopedPayloadConfigModule(app) {
|
|
133
|
+
const lines = [];
|
|
134
|
+
lines.push(HEADER);
|
|
135
|
+
lines.push("import path from 'node:path';");
|
|
136
|
+
lines.push('');
|
|
137
|
+
lines.push(`import ${app.importName}MicroApp from '${app.npmName}';`);
|
|
138
|
+
lines.push('');
|
|
139
|
+
lines.push("import { createScopedPayloadConfig } from './_factory';");
|
|
140
|
+
lines.push("import { getScopedMigrationTables } from './scoped-migration-tables';");
|
|
141
|
+
lines.push('');
|
|
142
|
+
lines.push(`export const scopedMigrationTables = getScopedMigrationTables(${app.importName}MicroApp);`);
|
|
143
|
+
lines.push('');
|
|
144
|
+
lines.push('export default createScopedPayloadConfig(');
|
|
145
|
+
lines.push(` [${app.importName}MicroApp],`);
|
|
146
|
+
lines.push(' {');
|
|
147
|
+
lines.push(' migrationDir: path.resolve(');
|
|
148
|
+
lines.push(' process.cwd(),');
|
|
149
|
+
lines.push(` '../../extensions/micro-apps/${app.key}/src/migrations',`);
|
|
150
|
+
lines.push(' ),');
|
|
151
|
+
lines.push(' mcp: false,');
|
|
152
|
+
lines.push(' tablesFilter: scopedMigrationTables,');
|
|
153
|
+
lines.push(' },');
|
|
154
|
+
lines.push(');');
|
|
155
|
+
lines.push('');
|
|
156
|
+
return lines.join('\n');
|
|
157
|
+
}
|
|
158
|
+
export function writeScopedPayloadConfigs(configsDir, apps) {
|
|
159
|
+
fs.mkdirSync(configsDir, { recursive: true });
|
|
160
|
+
const expectedConfigFiles = new Set(apps.map((app) => `${app.key}.config.ts`));
|
|
161
|
+
for (const app of apps) {
|
|
162
|
+
fs.writeFileSync(path.join(configsDir, `${app.key}.config.ts`), generateScopedPayloadConfigModule(app), 'utf-8');
|
|
163
|
+
}
|
|
164
|
+
for (const entry of fs.readdirSync(configsDir, { withFileTypes: true })) {
|
|
165
|
+
if (!entry.isFile())
|
|
166
|
+
continue;
|
|
167
|
+
if (!entry.name.endsWith('.config.ts'))
|
|
168
|
+
continue;
|
|
169
|
+
if (entry.name.startsWith('_') || entry.name === 'shared.config.ts')
|
|
170
|
+
continue;
|
|
171
|
+
if (expectedConfigFiles.has(entry.name))
|
|
172
|
+
continue;
|
|
173
|
+
fs.rmSync(path.join(configsDir, entry.name));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;
|
|
1
|
+
{"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAopBH,wBAAsB,GAAG,CACvB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3C,OAAO,CAAC,IAAI,CAAC,CA4Ff"}
|
package/dist/commands/add.js
CHANGED
|
@@ -107,6 +107,32 @@ function restoreSnapshots(snapshots) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
+
function snapshotDirectory(dirPath) {
|
|
111
|
+
if (!fs.existsSync(dirPath)) {
|
|
112
|
+
return { path: dirPath, existed: false };
|
|
113
|
+
}
|
|
114
|
+
const backupRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'dd-add-configs-'));
|
|
115
|
+
const backupDir = path.join(backupRoot, 'payload-configs');
|
|
116
|
+
fs.cpSync(dirPath, backupDir, { recursive: true });
|
|
117
|
+
return { path: dirPath, existed: true, backupDir };
|
|
118
|
+
}
|
|
119
|
+
function restoreDirectorySnapshot(snapshot) {
|
|
120
|
+
if (fs.existsSync(snapshot.path)) {
|
|
121
|
+
fs.rmSync(snapshot.path, { recursive: true, force: true });
|
|
122
|
+
}
|
|
123
|
+
if (snapshot.existed && snapshot.backupDir) {
|
|
124
|
+
fs.mkdirSync(path.dirname(snapshot.path), { recursive: true });
|
|
125
|
+
fs.cpSync(snapshot.backupDir, snapshot.path, { recursive: true });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function cleanupDirectorySnapshot(snapshot) {
|
|
129
|
+
if (snapshot.backupDir) {
|
|
130
|
+
const backupRoot = path.dirname(snapshot.backupDir);
|
|
131
|
+
if (fs.existsSync(backupRoot)) {
|
|
132
|
+
fs.rmSync(backupRoot, { recursive: true, force: true });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
110
136
|
// ---------------------------------------------------------------------------
|
|
111
137
|
// npm source handler
|
|
112
138
|
// ---------------------------------------------------------------------------
|
|
@@ -300,8 +326,10 @@ async function addFromGitHub(raw, options, marketplaceCtx) {
|
|
|
300
326
|
paths.configPath,
|
|
301
327
|
paths.lockFilePath,
|
|
302
328
|
paths.microAppsOutputPath,
|
|
329
|
+
paths.microAppTranspilePackagesOutputPath,
|
|
303
330
|
paths.payloadPluginsOutputPath,
|
|
304
331
|
]);
|
|
332
|
+
const payloadConfigsSnapshot = snapshotDirectory(paths.payloadConfigsDir);
|
|
305
333
|
try {
|
|
306
334
|
// Ensure parent directory exists (extensions/<kind>/ may not yet exist)
|
|
307
335
|
fs.mkdirSync(path.dirname(targetDir), { recursive: true });
|
|
@@ -421,6 +449,7 @@ async function addFromGitHub(raw, options, marketplaceCtx) {
|
|
|
421
449
|
console.error(' ✘ Install failed — rolling back workspace changes');
|
|
422
450
|
try {
|
|
423
451
|
restoreSnapshots(rollbackSnapshots);
|
|
452
|
+
restoreDirectorySnapshot(payloadConfigsSnapshot);
|
|
424
453
|
if (fs.existsSync(targetDir)) {
|
|
425
454
|
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
426
455
|
}
|
|
@@ -437,6 +466,9 @@ async function addFromGitHub(raw, options, marketplaceCtx) {
|
|
|
437
466
|
}
|
|
438
467
|
throw installErr;
|
|
439
468
|
}
|
|
469
|
+
finally {
|
|
470
|
+
cleanupDirectorySnapshot(payloadConfigsSnapshot);
|
|
471
|
+
}
|
|
440
472
|
for (const backup of backups) {
|
|
441
473
|
if (fs.existsSync(backup.backupDir)) {
|
|
442
474
|
fs.rmSync(backup.backupDir, { recursive: true, force: true });
|
|
@@ -455,9 +487,10 @@ async function addFromGitHub(raw, options, marketplaceCtx) {
|
|
|
455
487
|
|
|
456
488
|
Next steps:
|
|
457
489
|
1. Review the installed code: ${targetRelPath}/
|
|
458
|
-
2. Run: pnpm db:migrate:create
|
|
459
|
-
3. Run: pnpm db:migrate
|
|
460
|
-
4. Run: pnpm
|
|
490
|
+
2. ${isPayloadPlugin ? 'Run: pnpm db:migrate:create:shared <migration-name>' : `Run: pnpm db:migrate:create:app ${appName} <migration-name>`}
|
|
491
|
+
3. ${isPayloadPlugin ? 'Run: pnpm db:migrate:shared' : `Run: pnpm db:migrate:app ${appName}`}
|
|
492
|
+
4. Run: pnpm payload:generate-types:main
|
|
493
|
+
5. Run: pnpm dev
|
|
461
494
|
`);
|
|
462
495
|
}
|
|
463
496
|
finally {
|
package/dist/commands/create.js
CHANGED
|
@@ -210,8 +210,8 @@ export async function create(name) {
|
|
|
210
210
|
Next steps:
|
|
211
211
|
1. Edit ${targetRelPath}/src/micro-app/index.tsx to define your routes, widgets, and API entrypoints
|
|
212
212
|
2. Edit ${targetRelPath}/src/collections to define your collections
|
|
213
|
-
3. Run: pnpm db:migrate:create
|
|
214
|
-
4. Run: pnpm db:migrate
|
|
213
|
+
3. Run: pnpm db:migrate:create:app ${name} <migration-name>
|
|
214
|
+
4. Run: pnpm db:migrate:app ${name}
|
|
215
215
|
5. Run: pnpm payload:generate-types:main
|
|
216
216
|
6. Run: pnpm dev
|
|
217
217
|
`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/commands/db.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/commands/db.ts"],"names":[],"mappings":"AA8EA,wBAAsB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBtD"}
|
package/dist/commands/db.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveWorkspacePaths } from '../paths.js';
|
|
2
|
-
import { applyRuntimeDatabaseUrl,
|
|
3
|
-
|
|
2
|
+
import { applyRuntimeDatabaseUrl, checkDatabaseReachability, inspectLocalEnv, parseDatabaseUrl, resolveDatabasePreference, databaseRuntimeToEnvOptions, ensureDatabaseReady, ensureLocalEnv, } from '../lib/onboarding.js';
|
|
3
|
+
import { runScopedMigrations } from '../lib/scoped-migrations.js';
|
|
4
|
+
async function dbStatus(args) {
|
|
4
5
|
const paths = resolveWorkspacePaths();
|
|
5
6
|
const envInspection = inspectLocalEnv(paths);
|
|
6
7
|
const databasePreference = resolveDatabasePreference(envInspection.env);
|
|
@@ -18,38 +19,50 @@ async function dbStatus() {
|
|
|
18
19
|
console.log(`Port: ${info.port}`);
|
|
19
20
|
console.log(`Database: ${info.database}`);
|
|
20
21
|
console.log(`Reachable: ${reachable ? 'yes' : 'no'}`);
|
|
21
|
-
|
|
22
|
+
process.env.DATABASE_URL = databaseUrl;
|
|
22
23
|
console.log('\nMigration status:\n');
|
|
23
|
-
|
|
24
|
-
if (!reachable
|
|
24
|
+
runScopedMigrations(paths, 'status', args);
|
|
25
|
+
if (!reachable) {
|
|
25
26
|
process.exitCode = 1;
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
|
-
async function dbMigrate() {
|
|
29
|
+
async function dbMigrate(args) {
|
|
29
30
|
const paths = resolveWorkspacePaths();
|
|
30
31
|
const runtime = await ensureDatabaseReady(paths, {
|
|
31
32
|
yes: true,
|
|
32
33
|
allowDockerFallback: true,
|
|
33
34
|
});
|
|
34
35
|
applyRuntimeDatabaseUrl(ensureLocalEnv(paths, databaseRuntimeToEnvOptions(runtime)).env, runtime);
|
|
35
|
-
|
|
36
|
+
runScopedMigrations(paths, 'migrate', args);
|
|
36
37
|
console.log('✔ Migrations applied');
|
|
37
38
|
}
|
|
39
|
+
function dbCreate(args) {
|
|
40
|
+
runScopedMigrations(resolveWorkspacePaths(), 'create', args);
|
|
41
|
+
}
|
|
38
42
|
const HELP = `
|
|
39
43
|
dd db — Database helpers
|
|
40
44
|
|
|
41
45
|
Subcommands:
|
|
42
|
-
status
|
|
43
|
-
migrate
|
|
46
|
+
status [target] Show database connectivity and migration status
|
|
47
|
+
migrate [target] Apply committed migrations
|
|
48
|
+
create [target] [name] Create a scoped migration
|
|
49
|
+
|
|
50
|
+
Targets:
|
|
51
|
+
--all Shared migrations, then every micro-app config (default)
|
|
52
|
+
--shared Shared migrations only
|
|
53
|
+
<micro-app> One micro-app, for example meal-planner
|
|
44
54
|
`;
|
|
45
55
|
export async function db(args) {
|
|
46
56
|
const subcommand = args[0];
|
|
47
57
|
switch (subcommand) {
|
|
48
58
|
case 'status':
|
|
49
|
-
await dbStatus();
|
|
59
|
+
await dbStatus(args.slice(1));
|
|
50
60
|
break;
|
|
51
61
|
case 'migrate':
|
|
52
|
-
await dbMigrate();
|
|
62
|
+
await dbMigrate(args.slice(1));
|
|
63
|
+
break;
|
|
64
|
+
case 'create':
|
|
65
|
+
dbCreate(args.slice(1));
|
|
53
66
|
break;
|
|
54
67
|
case '--help':
|
|
55
68
|
case '-h':
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAyBA,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAqI5C"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
2
3
|
import { resolveWorkspacePaths } from '../paths.js';
|
|
3
|
-
import { captureCommand, checkDatabaseReachability, commandExists, dockerAvailable, getGeneratedTypesPath, probeEmbeddedPostgresSupport, inspectLocalEnv, resolveDatabasePreference, requiredEnvKeysMissing, trimOutput, } from '../lib/onboarding.js';
|
|
4
|
+
import { captureCommand, checkDatabaseReachability, commandExists, DEFAULT_APP_URL, DEFAULT_EMBEDDED_POSTGRES_DATA_DIR, dockerAvailable, getGeneratedTypesPath, probeEmbeddedPostgresSupport, inspectLocalEnv, resolveDatabasePreference, requiredEnvKeysMissing, trimOutput, } from '../lib/onboarding.js';
|
|
4
5
|
export async function doctor() {
|
|
5
6
|
const paths = resolveWorkspacePaths();
|
|
6
7
|
const checks = [];
|
|
@@ -29,10 +30,24 @@ export async function doctor() {
|
|
|
29
30
|
: 'Missing apps/main-app/.env.'
|
|
30
31
|
: `Missing keys: ${missingKeys.join(', ')}`,
|
|
31
32
|
});
|
|
33
|
+
const dbReasonLabels = {
|
|
34
|
+
'DATABASE_URL': 'via DATABASE_URL env var',
|
|
35
|
+
'apps/main-app/.env': 'via .env',
|
|
36
|
+
'DD_DATABASE_MODE': 'via DD_DATABASE_MODE',
|
|
37
|
+
'embedded-managed-url': 'auto-detected from database URL',
|
|
38
|
+
'no-database-url': 'default — no DATABASE_URL set',
|
|
39
|
+
'legacy-default': 'migrated from legacy default',
|
|
40
|
+
};
|
|
41
|
+
let dbModeDetail = `${databaseMode} (${dbReasonLabels[databasePreference.reason] || databasePreference.reason})`;
|
|
42
|
+
if (databaseMode === 'embedded') {
|
|
43
|
+
const dataDir = (envInspection.env.DD_EMBEDDED_POSTGRES_DATA_DIR?.trim() || DEFAULT_EMBEDDED_POSTGRES_DATA_DIR)
|
|
44
|
+
.replace(os.homedir(), '~');
|
|
45
|
+
dbModeDetail += `\n Data dir: ${dataDir}`;
|
|
46
|
+
}
|
|
32
47
|
checks.push({
|
|
33
48
|
label: 'Database mode',
|
|
34
49
|
ok: true,
|
|
35
|
-
detail:
|
|
50
|
+
detail: dbModeDetail,
|
|
36
51
|
});
|
|
37
52
|
const dbReachable = databasePreference.databaseUrl
|
|
38
53
|
? await checkDatabaseReachability(databasePreference.databaseUrl)
|
|
@@ -55,14 +70,23 @@ export async function doctor() {
|
|
|
55
70
|
? 'Database is not reachable yet. Run `pnpm dd onboard` or start Docker/Postgres manually.'
|
|
56
71
|
: 'Database is not reachable. Start PostgreSQL or update DATABASE_URL.',
|
|
57
72
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
if (databaseMode === 'embedded' && embeddedSupport.supported && !dbReachable) {
|
|
74
|
+
checks.push({
|
|
75
|
+
label: 'Migration status',
|
|
76
|
+
ok: true,
|
|
77
|
+
detail: 'Skipped until embedded PostgreSQL starts. `pnpm dev` or `dd run` will start it automatically.',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const migrationStatus = captureCommand('pnpm', ['db:migrate:status'], paths.root, databasePreference.databaseUrl ? { DATABASE_URL: databasePreference.databaseUrl } : {});
|
|
82
|
+
checks.push({
|
|
83
|
+
label: 'Migration status',
|
|
84
|
+
ok: migrationStatus.ok,
|
|
85
|
+
detail: migrationStatus.ok
|
|
86
|
+
? trimOutput(migrationStatus.output)
|
|
87
|
+
: trimOutput(migrationStatus.output) || 'Could not read migration status.',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
66
90
|
const generatedTypesPath = getGeneratedTypesPath(paths);
|
|
67
91
|
const generatedTypesExists = fs.existsSync(generatedTypesPath);
|
|
68
92
|
checks.push({
|
|
@@ -72,6 +96,15 @@ export async function doctor() {
|
|
|
72
96
|
? 'packages/shared/src/types/payload-types.ts exists.'
|
|
73
97
|
: 'Run `pnpm payload:generate-types:main`.',
|
|
74
98
|
});
|
|
99
|
+
const appPort = envInspection.env.APP_PORT?.trim() || process.env.APP_PORT?.trim();
|
|
100
|
+
const appUrl = appPort ? `http://localhost:${appPort}` : DEFAULT_APP_URL;
|
|
101
|
+
checks.push({
|
|
102
|
+
label: 'App URL',
|
|
103
|
+
ok: true,
|
|
104
|
+
detail: appPort
|
|
105
|
+
? `${appUrl} (APP_PORT override)`
|
|
106
|
+
: `${appUrl} (default — override with APP_PORT)`,
|
|
107
|
+
});
|
|
75
108
|
console.log('\n🩺 Double Digit doctor\n');
|
|
76
109
|
for (const check of checks) {
|
|
77
110
|
console.log(`${check.ok ? '✔' : '✘'} ${check.label}`);
|
|
@@ -79,8 +112,9 @@ export async function doctor() {
|
|
|
79
112
|
}
|
|
80
113
|
const failed = checks.filter((check) => !check.ok);
|
|
81
114
|
if (failed.length > 0) {
|
|
82
|
-
console.log('\nDoctor found issues. Recommended next
|
|
83
|
-
console.log(' pnpm dd onboard
|
|
115
|
+
console.log('\nDoctor found issues. Recommended next steps:\n');
|
|
116
|
+
console.log(' pnpm dd onboard # fix setup issues');
|
|
117
|
+
console.log(' pnpm dev # then start the app\n');
|
|
84
118
|
process.exitCode = 1;
|
|
85
119
|
return;
|
|
86
120
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA0JA,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+ExD"}
|