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.
Files changed (74) hide show
  1. package/COPYRIGHT.md +3 -0
  2. package/README.md +189 -0
  3. package/dist/actions/auth/auth-login.action.js +44 -0
  4. package/dist/actions/auth/auth-logout.action.js +13 -0
  5. package/dist/actions/auth/auth-status.action.js +38 -0
  6. package/dist/actions/build/build-document.action.js +142 -0
  7. package/dist/actions/delete.action.js +67 -0
  8. package/dist/actions/init.action.js +105 -0
  9. package/dist/actions/list/list-document.action.js +130 -0
  10. package/dist/actions/list/list.action.js +103 -0
  11. package/dist/actions/pull.action.js +58 -0
  12. package/dist/actions/push.action.js +91 -0
  13. package/dist/actions/tokens/token-delete.action.js +50 -0
  14. package/dist/actions/tokens/token-generate.action.js +52 -0
  15. package/dist/actions/tokens/token-list.action.js +52 -0
  16. package/dist/assets/callback-error.html +109 -0
  17. package/dist/assets/callback-success.html +73 -0
  18. package/dist/commands/auth/auth-login.command.js +7 -0
  19. package/dist/commands/auth/auth-logout.command.js +7 -0
  20. package/dist/commands/auth/auth-status.command.js +8 -0
  21. package/dist/commands/auth/auth.command.js +16 -0
  22. package/dist/commands/build/build-document.command.js +23 -0
  23. package/dist/commands/build/build.command.js +10 -0
  24. package/dist/commands/command.loader.js +18 -0
  25. package/dist/commands/delete.command.js +12 -0
  26. package/dist/commands/init.command.js +14 -0
  27. package/dist/commands/list/list-document.command.js +11 -0
  28. package/dist/commands/list/list.command.js +15 -0
  29. package/dist/commands/pull.command.js +11 -0
  30. package/dist/commands/push.command.js +15 -0
  31. package/dist/commands/tokens/token-delete.command.js +9 -0
  32. package/dist/commands/tokens/token-generate.command.js +9 -0
  33. package/dist/commands/tokens/token-list.command.js +8 -0
  34. package/dist/commands/tokens/tokens.command.js +15 -0
  35. package/dist/config/credential-manager.js +52 -0
  36. package/dist/config/settings-manager.js +44 -0
  37. package/dist/config.js +14 -0
  38. package/dist/config.staging.js +14 -0
  39. package/dist/constants/auth-message.constant.js +1 -0
  40. package/dist/constants/document.constant.js +8 -0
  41. package/dist/constants/dot-dbdiagram-dir.constant.js +9 -0
  42. package/dist/constants/viz-read-error.constant.js +6 -0
  43. package/dist/errors/portal-api.error.js +37 -0
  44. package/dist/errors/portal-error-codes.js +23 -0
  45. package/dist/hooks/build-document.hook.js +30 -0
  46. package/dist/hooks/delete.hook.js +12 -0
  47. package/dist/hooks/global.hook.js +6 -0
  48. package/dist/hooks/list.hook.js +23 -0
  49. package/dist/hooks/pull.hook.js +15 -0
  50. package/dist/hooks/push.hook.js +19 -0
  51. package/dist/index.js +12 -0
  52. package/dist/integrations/portal/portal-http.integration.js +27 -0
  53. package/dist/integrations/portal/portal.integration.js +68 -0
  54. package/dist/libs/portal/client.js +114 -0
  55. package/dist/libs/portal/errors.js +20 -0
  56. package/dist/libs/portal/index.js +2 -0
  57. package/dist/libs/portal/server.js +70 -0
  58. package/dist/program.js +16 -0
  59. package/dist/services/dbml/dbml.service.js +30 -0
  60. package/dist/services/dbml/dbml.worker.js +40 -0
  61. package/dist/services/file.service.js +6 -0
  62. package/dist/services/viz/diagram-viz-converter.service.js +67 -0
  63. package/dist/services/viz/diagram-viz-file.service.js +43 -0
  64. package/dist/types/config.type.js +1 -0
  65. package/dist/types/diagram-viz.type.js +6 -0
  66. package/dist/types/integrations/portal.type.js +1 -0
  67. package/dist/utils/dbml.util.js +30 -0
  68. package/dist/utils/diagram-viz-error.util.js +11 -0
  69. package/dist/utils/logger.util.js +3 -0
  70. package/dist/utils/output.util.js +18 -0
  71. package/dist/utils/portal-error.util.js +18 -0
  72. package/dist/utils/table.util.js +10 -0
  73. package/dist/utils/validation.util.js +32 -0
  74. 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
+ }