dbdiagram 0.1.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/COPYRIGHT.md +3 -0
- package/README.md +189 -0
- package/dist/actions/auth/auth-login.action.js +44 -0
- package/dist/actions/auth/auth-logout.action.js +13 -0
- package/dist/actions/auth/auth-status.action.js +38 -0
- package/dist/actions/build/build-document.action.js +142 -0
- package/dist/actions/delete.action.js +67 -0
- package/dist/actions/init.action.js +105 -0
- package/dist/actions/list/list-document.action.js +130 -0
- package/dist/actions/list/list.action.js +103 -0
- package/dist/actions/pull.action.js +58 -0
- package/dist/actions/push.action.js +91 -0
- package/dist/actions/tokens/token-delete.action.js +50 -0
- package/dist/actions/tokens/token-generate.action.js +52 -0
- package/dist/actions/tokens/token-list.action.js +52 -0
- package/dist/assets/callback-error.html +109 -0
- package/dist/assets/callback-success.html +73 -0
- package/dist/commands/auth/auth-login.command.js +7 -0
- package/dist/commands/auth/auth-logout.command.js +7 -0
- package/dist/commands/auth/auth-status.command.js +8 -0
- package/dist/commands/auth/auth.command.js +16 -0
- package/dist/commands/build/build-document.command.js +23 -0
- package/dist/commands/build/build.command.js +10 -0
- package/dist/commands/command.loader.js +18 -0
- package/dist/commands/delete.command.js +12 -0
- package/dist/commands/init.command.js +14 -0
- package/dist/commands/list/list-document.command.js +11 -0
- package/dist/commands/list/list.command.js +15 -0
- package/dist/commands/pull.command.js +11 -0
- package/dist/commands/push.command.js +15 -0
- package/dist/commands/tokens/token-delete.command.js +9 -0
- package/dist/commands/tokens/token-generate.command.js +9 -0
- package/dist/commands/tokens/token-list.command.js +8 -0
- package/dist/commands/tokens/tokens.command.js +15 -0
- package/dist/config/credential-manager.js +52 -0
- package/dist/config/settings-manager.js +44 -0
- package/dist/config.js +14 -0
- package/dist/config.staging.js +14 -0
- package/dist/constants/auth-message.constant.js +1 -0
- package/dist/constants/document.constant.js +8 -0
- package/dist/constants/dot-dbdiagram-dir.constant.js +9 -0
- package/dist/constants/viz-read-error.constant.js +6 -0
- package/dist/errors/portal-api.error.js +37 -0
- package/dist/errors/portal-error-codes.js +23 -0
- package/dist/hooks/build-document.hook.js +30 -0
- package/dist/hooks/delete.hook.js +12 -0
- package/dist/hooks/global.hook.js +6 -0
- package/dist/hooks/list.hook.js +23 -0
- package/dist/hooks/pull.hook.js +15 -0
- package/dist/hooks/push.hook.js +19 -0
- package/dist/index.js +12 -0
- package/dist/integrations/portal/portal-http.integration.js +27 -0
- package/dist/integrations/portal/portal.integration.js +68 -0
- package/dist/libs/portal/client.js +114 -0
- package/dist/libs/portal/errors.js +20 -0
- package/dist/libs/portal/index.js +2 -0
- package/dist/libs/portal/server.js +70 -0
- package/dist/program.js +16 -0
- package/dist/services/dbml/dbml.service.js +30 -0
- package/dist/services/dbml/dbml.worker.js +40 -0
- package/dist/services/file.service.js +6 -0
- package/dist/services/viz/diagram-viz-converter.service.js +67 -0
- package/dist/services/viz/diagram-viz-file.service.js +43 -0
- package/dist/types/config.type.js +1 -0
- package/dist/types/diagram-viz.type.js +6 -0
- package/dist/types/integrations/portal.type.js +1 -0
- package/dist/utils/dbml.util.js +30 -0
- package/dist/utils/diagram-viz-error.util.js +11 -0
- package/dist/utils/logger.util.js +3 -0
- package/dist/utils/output.util.js +18 -0
- package/dist/utils/portal-error.util.js +18 -0
- package/dist/utils/table.util.js +10 -0
- package/dist/utils/validation.util.js +32 -0
- package/package.json +56 -0
package/COPYRIGHT.md
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Copyright (C) 2015-present Holistics Software Pte Ltd - All Rights Reserved
|
|
2
|
+
|
|
3
|
+
The content of this repository is protected by International copyright laws. Unauthorized reproduction and distribution of any part of this repository, via any medium without permission is strictly prohibited.
|
package/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# dbdiagram-cli
|
|
2
|
+
|
|
3
|
+
A CLI tool for dbdiagram.io.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install -g dbdiagram-cli
|
|
9
|
+
# or
|
|
10
|
+
yarn global add dbdiagram-cli
|
|
11
|
+
# or
|
|
12
|
+
pnpm add -g dbdiagram-cli
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Authentication
|
|
18
|
+
|
|
19
|
+
Manage login state for dbdiagram/dbdocs.
|
|
20
|
+
|
|
21
|
+
**Options:**
|
|
22
|
+
|
|
23
|
+
- `--json` — Output status as JSON (`auth status` only)
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
dbdiagram auth login # log in via browser
|
|
27
|
+
dbdiagram auth logout # clear stored credentials
|
|
28
|
+
dbdiagram auth status # show who you're logged in as
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
> **Tip:** Set `DBDIAGRAM_TOKEN=<token>` to authenticate without a browser (see [Tokens](#tokens)).
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### Init
|
|
36
|
+
|
|
37
|
+
Creates or updates `.dbdiagram/settings.json` so you don't need to repeat flags on every command.
|
|
38
|
+
|
|
39
|
+
**Options:**
|
|
40
|
+
|
|
41
|
+
- `--entry <filepath>` — Path to your DBML entry file
|
|
42
|
+
- `--diagram-id <id>` — Diagram ID (from `dbdiagram.io/d/<id>`)
|
|
43
|
+
- `--workspace <id>` — Workspace ID (omit for personal workspace)
|
|
44
|
+
- `--document-source <mode>` — DBML source for `build document`: `file` or `diagram`
|
|
45
|
+
- `--workspace-url <name>` — dbdocs workspace URL name (from `dbdocs.io/<name>`)
|
|
46
|
+
- `--project-url <name>` — dbdocs project URL name (from `dbdocs.io/<workspace>/<name>`)
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
# set up a diagram project
|
|
50
|
+
dbdiagram init --entry schema.dbml --diagram-id <id>
|
|
51
|
+
|
|
52
|
+
# set up a dbdocs project
|
|
53
|
+
dbdiagram init --entry schema.dbml --workspace-url myteam --project-url myteam/myproject
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### Push
|
|
59
|
+
|
|
60
|
+
Push a local DBML file to create or update a diagram on dbdiagram.io.
|
|
61
|
+
|
|
62
|
+
**Options:**
|
|
63
|
+
|
|
64
|
+
- `--name <name>` — Name for the diagram
|
|
65
|
+
- `--diagram-id <id>` — Update an existing diagram (omit to create new)
|
|
66
|
+
- `--workspace <id>` — Create in a specific workspace (ignored when `--diagram-id` is set)
|
|
67
|
+
- `--new` — Force-create a new diagram, ignoring `diagram-id` from settings
|
|
68
|
+
- `--json` — Output result as JSON
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
dbdiagram push schema.dbml --name "My Diagram" # create new diagram
|
|
72
|
+
dbdiagram push schema.dbml --diagram-id <id> # update existing diagram
|
|
73
|
+
dbdiagram push schema.dbml --workspace <id> # create in a workspace
|
|
74
|
+
dbdiagram push schema.dbml --new # force-create, ignoring settings
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### Pull
|
|
80
|
+
|
|
81
|
+
Pull DBML content from a diagram on dbdiagram.io.
|
|
82
|
+
|
|
83
|
+
**Options:**
|
|
84
|
+
|
|
85
|
+
- `--diagram-id <id>` — The diagram to pull from
|
|
86
|
+
- `-o, --out-file <filepath>` — Save to a file instead of printing to stdout
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
dbdiagram pull --diagram-id <id> # print DBML to stdout
|
|
90
|
+
dbdiagram pull --diagram-id <id> -o schema.dbml # save to file
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
### List
|
|
96
|
+
|
|
97
|
+
List diagrams or documents.
|
|
98
|
+
|
|
99
|
+
**Options:**
|
|
100
|
+
|
|
101
|
+
- `--workspace <id>` — Filter by workspace (omit for personal workspace)
|
|
102
|
+
- `--json` — Output as JSON
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
dbdiagram list # list your diagrams
|
|
106
|
+
dbdiagram list --workspace <id> # list diagrams in a workspace
|
|
107
|
+
dbdiagram list document # list your dbdocs documents
|
|
108
|
+
dbdiagram list document --workspace <name> # list documents in a workspace
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### Delete
|
|
114
|
+
|
|
115
|
+
Delete a diagram.
|
|
116
|
+
|
|
117
|
+
**Options:**
|
|
118
|
+
|
|
119
|
+
- `--diagram-id <id>` — The diagram to delete
|
|
120
|
+
- `-f, --force` — Skip confirmation prompt
|
|
121
|
+
- `--json` — Output result as JSON
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
dbdiagram delete --diagram-id <id> # delete with confirmation
|
|
125
|
+
dbdiagram delete --diagram-id <id> --force # delete without confirmation
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### Build
|
|
131
|
+
|
|
132
|
+
Publish a dbdocs document from a local DBML file or a cloud diagram.
|
|
133
|
+
|
|
134
|
+
**Options:**
|
|
135
|
+
|
|
136
|
+
- `--from-file <filepath>` — Use a local DBML file as source _(required if not using `--from-diagram`)_
|
|
137
|
+
- `--from-diagram <id>` — Use a cloud diagram as source _(required if not using `--from-file`)_
|
|
138
|
+
- `--project <name>` — Target project as `<workspace>/<project>` or just `<project>`
|
|
139
|
+
- `--version-name <name>` — Version label for this publish
|
|
140
|
+
- `--json` — Output result as JSON
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
dbdiagram build document --from-file schema.dbml # publish from local file
|
|
144
|
+
dbdiagram build document --from-diagram <id> # publish from cloud diagram
|
|
145
|
+
dbdiagram build document --from-file schema.dbml --project myteam/myproject # publish to a specific project
|
|
146
|
+
dbdiagram build document --from-file schema.dbml --version-name "v1.2.0" # publish with a version label
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### Tokens
|
|
152
|
+
|
|
153
|
+
Manage CLI tokens for non-interactive authentication.
|
|
154
|
+
|
|
155
|
+
**Options:**
|
|
156
|
+
|
|
157
|
+
- `--name <name>` — Name for the token (`generate` only)
|
|
158
|
+
- `-f, --force` — Skip confirmation (`delete` only)
|
|
159
|
+
- `--json` — Output as JSON
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
dbdiagram tokens generate --name "CI token" # create a named token
|
|
163
|
+
dbdiagram tokens list # list all tokens
|
|
164
|
+
dbdiagram tokens delete <token_id> # delete a token (with confirmation)
|
|
165
|
+
dbdiagram tokens delete <token_id> --force # delete without confirmation
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Set the generated token as `DBDIAGRAM_TOKEN=<value>` in your environment to authenticate without browser login.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
yarn install
|
|
176
|
+
yarn dev # run with tsx (no build needed)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
yarn build # compile to dist/
|
|
181
|
+
yarn start # run compiled output
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Test
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
yarn test # run tests for changed files with coverage
|
|
188
|
+
yarn test:all # run all tests
|
|
189
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { spinner, log } from '@clack/prompts';
|
|
3
|
+
import open from 'open';
|
|
4
|
+
import { storeCredential } from '../../config/credential-manager.js';
|
|
5
|
+
import { PORTAL_LOGIN_CONFIGS, PORTAL_URLS } from '../../config.js';
|
|
6
|
+
import { initAuthClient, PORTAL_AUTH_ERROR_CODES, PortalAuthenticationError, } from '../../libs/portal/index.js';
|
|
7
|
+
const ERROR_MESSAGES = {
|
|
8
|
+
[PORTAL_AUTH_ERROR_CODES.loginTimeout]: 'Login timed out. Please try again.',
|
|
9
|
+
[PORTAL_AUTH_ERROR_CODES.loginCancelled]: 'Login cancelled.',
|
|
10
|
+
[PORTAL_AUTH_ERROR_CODES.stateMismatch]: 'Authentication failed: security validation error.',
|
|
11
|
+
[PORTAL_AUTH_ERROR_CODES.exchangeFailed]: 'Authentication failed: could not exchange token.',
|
|
12
|
+
[PORTAL_AUTH_ERROR_CODES.invalidCallback]: 'Authentication failed: invalid callback received.',
|
|
13
|
+
[PORTAL_AUTH_ERROR_CODES.loopbackServerError]: 'Authentication failed: could not start local server.',
|
|
14
|
+
[PORTAL_AUTH_ERROR_CODES.unknownError]: 'Authentication failed: an unexpected error occurred.',
|
|
15
|
+
};
|
|
16
|
+
export async function loginAction() {
|
|
17
|
+
try {
|
|
18
|
+
const authClient = await initAuthClient({
|
|
19
|
+
loginUrl: PORTAL_URLS.loginUrl,
|
|
20
|
+
loginApiUrl: PORTAL_URLS.apiUrl,
|
|
21
|
+
clientId: PORTAL_LOGIN_CONFIGS.clientId,
|
|
22
|
+
loginTimeoutMs: PORTAL_LOGIN_CONFIGS.loginTimeoutMs,
|
|
23
|
+
callbackHtmlPaths: {
|
|
24
|
+
success: path.join(import.meta.dirname, '../../assets/callback-success.html'),
|
|
25
|
+
error: path.join(import.meta.dirname, '../../assets/callback-error.html'),
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
const s = spinner();
|
|
29
|
+
await open(authClient.getLoginUrl());
|
|
30
|
+
s.start('Waiting for browser login...');
|
|
31
|
+
const { accessToken } = await authClient.obtainTokens();
|
|
32
|
+
storeCredential(accessToken);
|
|
33
|
+
s.stop('Login successful. Credentials saved.');
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (error instanceof PortalAuthenticationError) {
|
|
37
|
+
const message = ERROR_MESSAGES[error.errorCode] || ERROR_MESSAGES[PORTAL_AUTH_ERROR_CODES.unknownError];
|
|
38
|
+
log.error(message);
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { log } from '@clack/prompts';
|
|
2
|
+
import { clearCredential } from '../../config/credential-manager.js';
|
|
3
|
+
export async function logoutAction() {
|
|
4
|
+
try {
|
|
5
|
+
clearCredential();
|
|
6
|
+
log.success('Logout successful.');
|
|
7
|
+
}
|
|
8
|
+
catch (error) {
|
|
9
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10
|
+
log.error(`Failed to log out: ${message}`);
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { log, spinner } from '@clack/prompts';
|
|
2
|
+
import { getCredential } from '../../config/credential-manager.js';
|
|
3
|
+
import { NOT_LOGGED_IN_MESSAGE } from '../../constants/auth-message.constant.js';
|
|
4
|
+
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
5
|
+
import { isPortalApiError, getPortalApiErrorMessage } from '../../utils/portal-error.util.js';
|
|
6
|
+
import { printJson } from '../../utils/output.util.js';
|
|
7
|
+
export async function statusAction(options = {}) {
|
|
8
|
+
const credential = getCredential();
|
|
9
|
+
if (!credential) {
|
|
10
|
+
log.warn(NOT_LOGGED_IN_MESSAGE);
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const s = spinner();
|
|
15
|
+
s.start('Checking login status...');
|
|
16
|
+
try {
|
|
17
|
+
const user = await portalIntegration.getCurrentUser();
|
|
18
|
+
if (options.json) {
|
|
19
|
+
s.stop();
|
|
20
|
+
printJson({ email: user.email, name: user.name ?? null });
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const display = user.name ? `${user.name} (${user.email})` : user.email;
|
|
24
|
+
s.stop(`Logged in as ${display}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
s.stop('Failed to check login status.');
|
|
29
|
+
if (isPortalApiError(error)) {
|
|
30
|
+
log.error(getPortalApiErrorMessage(error));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
34
|
+
log.error(message);
|
|
35
|
+
}
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { log, spinner } from '@clack/prompts';
|
|
3
|
+
import { getCredential } from '../../config/credential-manager.js';
|
|
4
|
+
import { NOT_LOGGED_IN_MESSAGE } from '../../constants/auth-message.constant.js';
|
|
5
|
+
import { prepareBuildDocumentData } from '../../services/dbml/dbml.service.js';
|
|
6
|
+
import { formatDbmlError, getDbmlVersion } from '../../utils/dbml.util.js';
|
|
7
|
+
import { portalIntegration } from '../../integrations/portal/portal.integration.js';
|
|
8
|
+
import { DBDOCS_CONFIGS } from '../../config.js';
|
|
9
|
+
import { isPortalApiError, getPortalApiErrorMessage } from '../../utils/portal-error.util.js';
|
|
10
|
+
import { printJson } from '../../utils/output.util.js';
|
|
11
|
+
import { validateProjectUrlName, validateVersionName, validateWorkspaceUrlName, } from '../../utils/validation.util.js';
|
|
12
|
+
import { DOCUMENT_SOURCE_TYPE } from '../../constants/document.constant.js';
|
|
13
|
+
const DOCUMENT_SOURCE_CLIENT_TYPE = 'dbdiagram-cli';
|
|
14
|
+
export async function documentAction(options) {
|
|
15
|
+
const credential = getCredential();
|
|
16
|
+
if (!credential) {
|
|
17
|
+
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (options.versionName !== undefined) {
|
|
22
|
+
const versionError = validateVersionName(options.versionName);
|
|
23
|
+
if (versionError) {
|
|
24
|
+
log.error(versionError);
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (!options.workspaceUrlName) {
|
|
30
|
+
log.error('Workspace url name is required. Use --project <workspace>/<project> '
|
|
31
|
+
+ 'or configure workspaceUrlName in settings.json');
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const workspaceError = validateWorkspaceUrlName(options.workspaceUrlName);
|
|
36
|
+
if (workspaceError) {
|
|
37
|
+
log.error(workspaceError);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!options.projectUrlOrName) {
|
|
42
|
+
log.error('Project name/url name is required. Use --project <workspace>/<project> '
|
|
43
|
+
+ 'or configure projectUrlName in settings.json');
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (options.projectUrlOrName.includes('/')) {
|
|
48
|
+
log.error('Invalid --project value. Expected <workspace>/<project> or <project>.');
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const projectError = validateProjectUrlName(options.projectUrlOrName);
|
|
53
|
+
if (projectError) {
|
|
54
|
+
log.error(projectError);
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (options.source === undefined) {
|
|
59
|
+
log.error('One source must be specified: --from-file or --from-diagram');
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
let content;
|
|
64
|
+
if (options.source.type === DOCUMENT_SOURCE_TYPE.FILE) {
|
|
65
|
+
try {
|
|
66
|
+
content = await readFile(options.source.value, 'utf8');
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
70
|
+
log.error(`Failed to read file: ${message}`);
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
try {
|
|
77
|
+
const diagram = await portalIntegration.getDiagram(options.source.value);
|
|
78
|
+
content = diagram.content;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (isPortalApiError(error)) {
|
|
82
|
+
log.error(getPortalApiErrorMessage(error));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86
|
+
log.error(`Failed to fetch diagram: ${message}`);
|
|
87
|
+
}
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!content || !content.trim()) {
|
|
93
|
+
log.warn('Source content is empty. Skipping document build.');
|
|
94
|
+
process.exitCode = 0;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let preparedDocumentData;
|
|
98
|
+
try {
|
|
99
|
+
preparedDocumentData = await prepareBuildDocumentData(content);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
log.error(`Failed to parse DBML: ${formatDbmlError(error)}`);
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const s = spinner();
|
|
107
|
+
s.start('Building document on dbdocs...');
|
|
108
|
+
try {
|
|
109
|
+
const { project } = await portalIntegration.createDocument({
|
|
110
|
+
projectName: options.projectUrlOrName,
|
|
111
|
+
projectDescription: preparedDocumentData.description,
|
|
112
|
+
workspaceUrlName: options.workspaceUrlName,
|
|
113
|
+
clientType: DOCUMENT_SOURCE_CLIENT_TYPE,
|
|
114
|
+
doc: {
|
|
115
|
+
content,
|
|
116
|
+
name: options.versionName ?? null,
|
|
117
|
+
},
|
|
118
|
+
shallowSchema: preparedDocumentData.shallowSchema,
|
|
119
|
+
normalizedDatabase: preparedDocumentData.normalizedDatabase,
|
|
120
|
+
dbmlVersion: getDbmlVersion(),
|
|
121
|
+
});
|
|
122
|
+
const url = `${DBDOCS_CONFIGS.baseUrl}/${project.workspaceUrl}/${project.urlName}`;
|
|
123
|
+
if (options.json) {
|
|
124
|
+
s.stop();
|
|
125
|
+
printJson({ projectUrlName: `${project.workspaceUrl}/${project.urlName}`, url });
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
s.stop(`Document built successfully. Visit: ${url}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
s.stop('Failed to build document.');
|
|
133
|
+
if (isPortalApiError(error)) {
|
|
134
|
+
log.error(getPortalApiErrorMessage(error));
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
138
|
+
log.error(message);
|
|
139
|
+
}
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { confirm, isCancel, log, spinner } from '@clack/prompts';
|
|
2
|
+
import { getCredential } from '../config/credential-manager.js';
|
|
3
|
+
import { NOT_LOGGED_IN_MESSAGE } from '../constants/auth-message.constant.js';
|
|
4
|
+
import { getSettings, writeSettings } from '../config/settings-manager.js';
|
|
5
|
+
import { portalIntegration } from '../integrations/portal/portal.integration.js';
|
|
6
|
+
import { isPortalApiError, getPortalApiErrorMessage } from '../utils/portal-error.util.js';
|
|
7
|
+
import { printJson } from '../utils/output.util.js';
|
|
8
|
+
export async function deleteAction(options) {
|
|
9
|
+
const credential = getCredential();
|
|
10
|
+
if (!credential) {
|
|
11
|
+
log.error(NOT_LOGGED_IN_MESSAGE);
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (!options.diagramId) {
|
|
16
|
+
log.error('Missing required flag: --diagram-id');
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!options.force) {
|
|
21
|
+
const shouldDelete = await confirm({
|
|
22
|
+
message: `Delete diagram '${options.diagramId}'?`,
|
|
23
|
+
initialValue: false,
|
|
24
|
+
});
|
|
25
|
+
if (isCancel(shouldDelete) || !shouldDelete) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const s = spinner();
|
|
30
|
+
s.start('Deleting diagram...');
|
|
31
|
+
try {
|
|
32
|
+
await portalIntegration.deleteDiagram(options.diagramId);
|
|
33
|
+
const settings = getSettings();
|
|
34
|
+
let isRemoveInSettings = false;
|
|
35
|
+
if (settings && settings.diagram.id === options.diagramId) {
|
|
36
|
+
settings.diagram.id = '';
|
|
37
|
+
writeSettings(settings);
|
|
38
|
+
isRemoveInSettings = true;
|
|
39
|
+
}
|
|
40
|
+
if (options.json) {
|
|
41
|
+
s.stop();
|
|
42
|
+
printJson({
|
|
43
|
+
diagramId: options.diagramId,
|
|
44
|
+
deleted: true,
|
|
45
|
+
...(isRemoveInSettings ? {} : { diagramIdSettingCleared: true }),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
s.stop(`Diagram '${options.diagramId}' deleted successfully.`);
|
|
50
|
+
if (isRemoveInSettings) {
|
|
51
|
+
log.info('The diagram.id has been cleared from .dbdiagram/settings.json');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
s.error('Failed to delete diagram.');
|
|
57
|
+
s.stop();
|
|
58
|
+
if (isPortalApiError(error)) {
|
|
59
|
+
log.error(getPortalApiErrorMessage(error));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
63
|
+
log.error(message);
|
|
64
|
+
}
|
|
65
|
+
process.exitCode = 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { isCancel, log, select, text } from '@clack/prompts';
|
|
2
|
+
import { defaultSettings, readSettings, settingsExists, writeSettings, } from '../config/settings-manager.js';
|
|
3
|
+
function hasFieldFlags(options) {
|
|
4
|
+
return [
|
|
5
|
+
options.entry,
|
|
6
|
+
options.diagramId,
|
|
7
|
+
options.workspace,
|
|
8
|
+
options.documentSource,
|
|
9
|
+
options.workspaceUrl,
|
|
10
|
+
options.projectUrl,
|
|
11
|
+
].some((v) => v !== undefined);
|
|
12
|
+
}
|
|
13
|
+
function persist(data, message) {
|
|
14
|
+
try {
|
|
15
|
+
writeSettings(data);
|
|
16
|
+
log.success(message);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
20
|
+
log.error(errorMessage);
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function flagFlow(options) {
|
|
25
|
+
const existed = settingsExists();
|
|
26
|
+
const existing = readSettings() ?? defaultSettings();
|
|
27
|
+
if (options.entry !== undefined)
|
|
28
|
+
existing.dbml.entry = options.entry;
|
|
29
|
+
if (options.diagramId !== undefined)
|
|
30
|
+
existing.diagram.id = options.diagramId;
|
|
31
|
+
if (options.workspace !== undefined)
|
|
32
|
+
existing.diagram.workspaceId = options.workspace;
|
|
33
|
+
if (options.documentSource !== undefined)
|
|
34
|
+
existing.document.source = options.documentSource;
|
|
35
|
+
if (options.workspaceUrl !== undefined)
|
|
36
|
+
existing.document.workspaceUrlName = options.workspaceUrl;
|
|
37
|
+
if (options.projectUrl !== undefined)
|
|
38
|
+
existing.document.projectUrlName = options.projectUrl;
|
|
39
|
+
persist(existing, existed ? 'Updated .dbdiagram/settings.json' : 'Created .dbdiagram/settings.json');
|
|
40
|
+
}
|
|
41
|
+
async function interactiveFlow() {
|
|
42
|
+
const existed = settingsExists();
|
|
43
|
+
const existing = readSettings() ?? defaultSettings();
|
|
44
|
+
const entryResult = await text({
|
|
45
|
+
message: 'DBML entry file',
|
|
46
|
+
placeholder: './schema.dbml',
|
|
47
|
+
defaultValue: existing.dbml.entry || undefined,
|
|
48
|
+
});
|
|
49
|
+
if (isCancel(entryResult))
|
|
50
|
+
return;
|
|
51
|
+
const diagramIdResult = await text({
|
|
52
|
+
message: 'Diagram ID (from dbdiagram.io/d/<id>)',
|
|
53
|
+
defaultValue: existing.diagram.id || undefined,
|
|
54
|
+
});
|
|
55
|
+
if (isCancel(diagramIdResult))
|
|
56
|
+
return;
|
|
57
|
+
const workspaceIdResult = await text({
|
|
58
|
+
message: 'dbdiagram Workspace ID (leave empty for personal)',
|
|
59
|
+
defaultValue: existing.diagram.workspaceId || undefined,
|
|
60
|
+
});
|
|
61
|
+
if (isCancel(workspaceIdResult))
|
|
62
|
+
return;
|
|
63
|
+
const sourceResult = await select({
|
|
64
|
+
message: 'Document source',
|
|
65
|
+
options: [
|
|
66
|
+
{ value: 'file', label: 'file' },
|
|
67
|
+
{ value: 'diagram', label: 'diagram' },
|
|
68
|
+
],
|
|
69
|
+
initialValue: existing.document.source || 'file',
|
|
70
|
+
});
|
|
71
|
+
if (isCancel(sourceResult))
|
|
72
|
+
return;
|
|
73
|
+
const wsUrlResult = await text({
|
|
74
|
+
message: 'dbdocs Workspace URL name (from dbdocs.io/<name>)',
|
|
75
|
+
defaultValue: existing.document.workspaceUrlName || undefined,
|
|
76
|
+
});
|
|
77
|
+
if (isCancel(wsUrlResult))
|
|
78
|
+
return;
|
|
79
|
+
const projUrlResult = await text({
|
|
80
|
+
message: 'dbdocs Project URL name (from dbdocs.io/<workspace>/<name>)',
|
|
81
|
+
defaultValue: existing.document.projectUrlName || undefined,
|
|
82
|
+
});
|
|
83
|
+
if (isCancel(projUrlResult))
|
|
84
|
+
return;
|
|
85
|
+
const data = {
|
|
86
|
+
dbml: { entry: entryResult || '' },
|
|
87
|
+
diagram: {
|
|
88
|
+
id: diagramIdResult || '',
|
|
89
|
+
workspaceId: workspaceIdResult || '',
|
|
90
|
+
},
|
|
91
|
+
document: {
|
|
92
|
+
source: sourceResult || 'file',
|
|
93
|
+
workspaceUrlName: wsUrlResult || '',
|
|
94
|
+
projectUrlName: projUrlResult || '',
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
persist(data, existed ? 'Updated .dbdiagram/settings.json' : 'Created .dbdiagram/settings.json');
|
|
98
|
+
}
|
|
99
|
+
export async function initAction(options) {
|
|
100
|
+
if (hasFieldFlags(options)) {
|
|
101
|
+
flagFlow(options);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
await interactiveFlow();
|
|
105
|
+
}
|