@dashnex/cli 0.5.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.
@@ -0,0 +1,262 @@
1
+ import inquirer from "inquirer";
2
+ import chalk from "chalk";
3
+ import fs from "fs-extra";
4
+ import path from "path";
5
+ import { debug, debugJson, debugError } from "../lib/debug.js";
6
+ import { getApiBase, apiFetch } from "../lib/api.js";
7
+ import { ensureLoggedIn } from "../services/auth.js";
8
+ const extractErrorMessage = (body) => {
9
+ if (typeof body.error === "string") return body.error;
10
+ if (typeof body.message === "string") return body.message;
11
+ return void 0;
12
+ };
13
+ const handleApiError = (response, body, defaultMessage) => {
14
+ const apiMessage = extractErrorMessage(body);
15
+ let message = defaultMessage;
16
+ if (response.status === 429) {
17
+ message = "Too many requests. Please wait a moment and try again.";
18
+ } else if (response.status >= 500) {
19
+ message = "DashNex API is temporarily unavailable. Please try again later.";
20
+ } else if (apiMessage) {
21
+ message = apiMessage;
22
+ }
23
+ console.error(chalk.red(message));
24
+ process.exit(1);
25
+ };
26
+ class LoginCommand {
27
+ async execute() {
28
+ debug("Login flow started");
29
+ const cwd = process.cwd();
30
+ const dashnexPath = path.join(cwd, ".dashnex");
31
+ const session = await ensureLoggedIn({ exitOnFailure: false, dashnexPath });
32
+ if (session) {
33
+ console.log(chalk.green(`Already logged in as ${session.userName ?? "user"}`));
34
+ return;
35
+ }
36
+ try {
37
+ const { username, password } = await inquirer.prompt([
38
+ {
39
+ type: "input",
40
+ name: "username",
41
+ message: "Email:",
42
+ validate: (input) => input.trim() ? true : "Email is required"
43
+ },
44
+ {
45
+ type: "password",
46
+ name: "password",
47
+ message: "Password:",
48
+ mask: "*",
49
+ validate: (input) => input ? true : "Password is required"
50
+ }
51
+ ]);
52
+ const trimmedUsername = username.trim();
53
+ debug("Email provided");
54
+ debug(`POST ${getApiBase()}/auth/v1/login`);
55
+ const { response: loginResponse, body: loginBody } = await apiFetch(
56
+ `${getApiBase()}/auth/v1/login`,
57
+ {
58
+ method: "POST",
59
+ body: JSON.stringify({ username: trimmedUsername, password })
60
+ }
61
+ );
62
+ debug(`Response: ${loginResponse.status}`);
63
+ debugJson("Login response", loginBody);
64
+ if (!loginResponse.ok) {
65
+ debugError(new Error(`Login failed: ${loginResponse.status} ${JSON.stringify(loginBody)}`));
66
+ if (loginResponse.status === 401) {
67
+ console.error(chalk.red("Invalid username or password."));
68
+ process.exit(1);
69
+ }
70
+ handleApiError(
71
+ loginResponse,
72
+ loginBody,
73
+ "Login failed. Please try again."
74
+ );
75
+ }
76
+ const loginData = loginBody;
77
+ if (loginData.two_fa_required && loginData.token) {
78
+ debug(`2FA required, type: ${loginData.type || "unknown"}`);
79
+ const token = await this.handle2FA(loginData.token, loginData.type);
80
+ if (!token) process.exit(1);
81
+ await this.completeLogin(token, dashnexPath);
82
+ } else if (loginData.token && loginData.refreshToken) {
83
+ await this.completeLogin(
84
+ { token: loginData.token, refreshToken: loginData.refreshToken },
85
+ dashnexPath
86
+ );
87
+ } else {
88
+ console.error(chalk.red("Invalid response from server. Please try again."));
89
+ process.exit(1);
90
+ }
91
+ } catch (error) {
92
+ debugError(error);
93
+ console.error(chalk.red("Could not reach DashNex API. Check your connection and try again."));
94
+ process.exit(1);
95
+ }
96
+ }
97
+ async handle2FA(interimToken, type) {
98
+ if (type === "sms") {
99
+ debug(`POST ${getApiBase()}/auth/v1/two-fa/sms`);
100
+ try {
101
+ const { response } = await apiFetch(`${getApiBase()}/auth/v1/two-fa/sms`, {
102
+ method: "POST",
103
+ headers: { Authorization: `Bearer ${interimToken}` },
104
+ body: JSON.stringify({})
105
+ });
106
+ debug(`Response: ${response.status}`);
107
+ } catch (err) {
108
+ debugError(err);
109
+ console.error(chalk.red("Could not send SMS code. Check your connection and try again."));
110
+ return null;
111
+ }
112
+ }
113
+ const typeLabel = type === "sms" ? "Check your phone for the code" : "Check your authenticator app for the code";
114
+ const maxAttempts = 3;
115
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
116
+ const { auth_code } = await inquirer.prompt([
117
+ {
118
+ type: "input",
119
+ name: "auth_code",
120
+ message: `Enter code (${typeLabel}):`,
121
+ validate: (input) => input.trim() ? true : "Code is required"
122
+ }
123
+ ]);
124
+ debug(`POST ${getApiBase()}/auth/v1/2fa-check`);
125
+ try {
126
+ const { response, body } = await apiFetch(`${getApiBase()}/auth/v1/2fa-check`, {
127
+ method: "POST",
128
+ headers: { Authorization: `Bearer ${interimToken}` },
129
+ body: JSON.stringify({ auth_code: auth_code.trim() })
130
+ });
131
+ debug(`Response: ${response.status}`);
132
+ debugJson("2FA check response", body);
133
+ if (response.ok) {
134
+ const data = body;
135
+ if (data.token && data.refreshToken) {
136
+ return { token: data.token, refreshToken: data.refreshToken };
137
+ }
138
+ }
139
+ if (response.status >= 400 && response.status < 500) {
140
+ console.error(chalk.red("Invalid code. Please try again."));
141
+ if (attempt < maxAttempts) {
142
+ const { retry } = await inquirer.prompt([
143
+ {
144
+ type: "confirm",
145
+ name: "retry",
146
+ message: "Try again?",
147
+ default: true
148
+ }
149
+ ]);
150
+ if (!retry) return null;
151
+ }
152
+ continue;
153
+ }
154
+ handleApiError(response, body, "2FA verification failed. Please try again.");
155
+ } catch (err) {
156
+ debugError(err);
157
+ console.error(chalk.red("Could not reach DashNex API. Check your connection and try again."));
158
+ return null;
159
+ }
160
+ }
161
+ return null;
162
+ }
163
+ async completeLogin(tokens, dashnexPath) {
164
+ debug(`GET ${getApiBase()}/users/v1/business/my`);
165
+ const { response: bizResponse, body: bizBody } = await apiFetch(
166
+ `${getApiBase()}/users/v1/business/my`,
167
+ {
168
+ headers: { Authorization: `Bearer ${tokens.token}` }
169
+ }
170
+ );
171
+ debug(`Response: ${bizResponse.status}`);
172
+ debugJson("Businesses response", bizBody);
173
+ if (!bizResponse.ok) {
174
+ debugError(new Error(`Business fetch failed: ${bizResponse.status}`));
175
+ handleApiError(
176
+ bizResponse,
177
+ bizBody,
178
+ "Failed to fetch businesses. Please try again."
179
+ );
180
+ }
181
+ const businesses = this.parseBusinesses(bizBody);
182
+ debug(`Businesses: ${businesses.length}`);
183
+ if (businesses.length === 0) {
184
+ console.error(chalk.red("No businesses found for your account."));
185
+ process.exit(1);
186
+ }
187
+ let selected;
188
+ if (businesses.length === 1) {
189
+ selected = businesses[0];
190
+ debug(`Selected business: ${selected.id} ${selected.name}`);
191
+ } else {
192
+ const { businessId } = await inquirer.prompt([
193
+ {
194
+ type: "select",
195
+ name: "businessId",
196
+ message: "Select business:",
197
+ choices: businesses.map((b) => ({ name: b.name, value: b.id }))
198
+ }
199
+ ]);
200
+ selected = businesses.find((b) => b.id === businessId) || businesses[0];
201
+ debug(`Selected business: ${selected.id} ${selected.name}`);
202
+ }
203
+ debug(`GET ${getApiBase()}/business/v1/login?_switch_business=${selected.id}`);
204
+ const { response: switchResponse, body: switchBody } = await apiFetch(
205
+ `${getApiBase()}/business/v1/login?_switch_business=${selected.id}`,
206
+ {
207
+ headers: { Authorization: `Bearer ${tokens.token}` }
208
+ }
209
+ );
210
+ debug(`Response: ${switchResponse.status}`);
211
+ debugJson("Business login response", switchBody);
212
+ if (!switchResponse.ok) {
213
+ debugError(new Error(`Business login failed: ${switchResponse.status}`));
214
+ handleApiError(
215
+ switchResponse,
216
+ switchBody,
217
+ "Failed to switch to business. Please try again."
218
+ );
219
+ }
220
+ const switchData = switchBody;
221
+ if (!switchData.token || !switchData.refreshToken) {
222
+ console.error(chalk.red("Invalid response from server. Please try again."));
223
+ process.exit(1);
224
+ }
225
+ const config = {
226
+ token: switchData.token,
227
+ refreshToken: switchData.refreshToken,
228
+ businessId: selected.id
229
+ };
230
+ await fs.writeJson(dashnexPath, config, { spaces: 2 });
231
+ debug("Saved credentials to .dashnex");
232
+ this.ensureGitignore();
233
+ console.log(chalk.green(`Logged in as ${selected.name}`));
234
+ }
235
+ parseBusinesses(body) {
236
+ const arr = Array.isArray(body) ? body : body.items ?? body.data ?? body.businesses ?? [];
237
+ if (!Array.isArray(arr)) return [];
238
+ return arr.map((item) => {
239
+ if (item && typeof item === "object") {
240
+ const o = item;
241
+ const id = String(o.id ?? o.business_id ?? "").trim();
242
+ const name = String(o.name ?? o.companyName ?? o.business_name ?? "Unknown").trim();
243
+ if (id) return { id, name };
244
+ }
245
+ return null;
246
+ }).filter((b) => b !== null);
247
+ }
248
+ async ensureGitignore() {
249
+ const gitignorePath = path.join(process.cwd(), ".gitignore");
250
+ if (!await fs.pathExists(gitignorePath)) return;
251
+ let content = await fs.readFile(gitignorePath, "utf8");
252
+ if (content.includes(".dashnex")) return;
253
+ content = content.trimEnd();
254
+ if (!content.endsWith("\n")) content += "\n";
255
+ content += "\n.dashnex\n";
256
+ await fs.writeFile(gitignorePath, content);
257
+ }
258
+ }
259
+ export {
260
+ LoginCommand
261
+ };
262
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sources":["../../src/commands/login.ts"],"sourcesContent":["import inquirer from \"inquirer\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"path\";\nimport type { CliCommand } from \"@dashnex/types\";\nimport { debug, debugJson, debugError } from \"../lib/debug.js\";\nimport { getApiBase, apiFetch } from \"../lib/api.js\";\nimport { ensureLoggedIn } from \"../services/auth.js\";\n\nconst extractErrorMessage = (body: Record<string, unknown>): string | undefined => {\n if (typeof body.error === \"string\") return body.error;\n if (typeof body.message === \"string\") return body.message;\n return undefined;\n};\n\ninterface LoginResponse {\n token?: string;\n refreshToken?: string;\n two_fa_required?: boolean;\n type?: string;\n}\n\ninterface Business {\n id: string;\n name: string;\n}\n\nconst handleApiError = (\n response: Response,\n body: Record<string, unknown>,\n defaultMessage: string\n): never => {\n const apiMessage = extractErrorMessage(body);\n let message = defaultMessage;\n if (response.status === 429) {\n message = \"Too many requests. Please wait a moment and try again.\";\n } else if (response.status >= 500) {\n message = \"DashNex API is temporarily unavailable. Please try again later.\";\n } else if (apiMessage) {\n message = apiMessage;\n }\n console.error(chalk.red(message));\n process.exit(1);\n};\n\nexport class LoginCommand implements CliCommand {\n async execute(): Promise<void> {\n debug(\"Login flow started\");\n\n const cwd = process.cwd();\n const dashnexPath = path.join(cwd, \".dashnex\");\n\n const session = await ensureLoggedIn({ exitOnFailure: false, dashnexPath });\n if (session) {\n console.log(chalk.green(`Already logged in as ${session.userName ?? \"user\"}`));\n return;\n }\n\n try {\n const { username, password } = await inquirer.prompt<{ username: string; password: string }>([\n {\n type: \"input\",\n name: \"username\",\n message: \"Email:\",\n validate: (input: string) => (input.trim() ? true : \"Email is required\"),\n },\n {\n type: \"password\",\n name: \"password\",\n message: \"Password:\",\n mask: \"*\",\n validate: (input: string) => (input ? true : \"Password is required\"),\n },\n ]);\n\n const trimmedUsername = username.trim();\n debug(\"Email provided\");\n\n debug(`POST ${getApiBase()}/auth/v1/login`);\n const { response: loginResponse, body: loginBody } = await apiFetch(\n `${getApiBase()}/auth/v1/login`,\n {\n method: \"POST\",\n body: JSON.stringify({ username: trimmedUsername, password }),\n }\n );\n debug(`Response: ${loginResponse.status}`);\n debugJson(\"Login response\", loginBody);\n\n if (!loginResponse.ok) {\n debugError(new Error(`Login failed: ${loginResponse.status} ${JSON.stringify(loginBody)}`));\n if (loginResponse.status === 401) {\n console.error(chalk.red(\"Invalid username or password.\"));\n process.exit(1);\n }\n handleApiError(\n loginResponse,\n loginBody,\n \"Login failed. Please try again.\"\n );\n }\n\n const loginData = loginBody as LoginResponse;\n\n if (loginData.two_fa_required && loginData.token) {\n debug(`2FA required, type: ${loginData.type || \"unknown\"}`);\n const token = await this.handle2FA(loginData.token, loginData.type);\n if (!token) process.exit(1);\n await this.completeLogin(token, dashnexPath);\n } else if (loginData.token && loginData.refreshToken) {\n await this.completeLogin(\n { token: loginData.token, refreshToken: loginData.refreshToken },\n dashnexPath\n );\n } else {\n console.error(chalk.red(\"Invalid response from server. Please try again.\"));\n process.exit(1);\n }\n } catch (error) {\n debugError(error);\n console.error(chalk.red(\"Could not reach DashNex API. Check your connection and try again.\"));\n process.exit(1);\n }\n }\n\n private async handle2FA(\n interimToken: string,\n type?: string\n ): Promise<{ token: string; refreshToken: string } | null> {\n if (type === \"sms\") {\n debug(`POST ${getApiBase()}/auth/v1/two-fa/sms`);\n try {\n const { response } = await apiFetch(`${getApiBase()}/auth/v1/two-fa/sms`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${interimToken}` },\n body: JSON.stringify({}),\n });\n debug(`Response: ${response.status}`);\n } catch (err) {\n debugError(err);\n console.error(chalk.red(\"Could not send SMS code. Check your connection and try again.\"));\n return null;\n }\n }\n\n const typeLabel = type === \"sms\" ? \"Check your phone for the code\" : \"Check your authenticator app for the code\";\n const maxAttempts = 3;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const { auth_code } = await inquirer.prompt<{ auth_code: string }>([\n {\n type: \"input\",\n name: \"auth_code\",\n message: `Enter code (${typeLabel}):`,\n validate: (input: string) => (input.trim() ? true : \"Code is required\"),\n },\n ]);\n\n debug(`POST ${getApiBase()}/auth/v1/2fa-check`);\n try {\n const { response, body } = await apiFetch(`${getApiBase()}/auth/v1/2fa-check`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${interimToken}` },\n body: JSON.stringify({ auth_code: auth_code.trim() }),\n });\n debug(`Response: ${response.status}`);\n debugJson(\"2FA check response\", body);\n\n if (response.ok) {\n const data = body as { token?: string; refreshToken?: string };\n if (data.token && data.refreshToken) {\n return { token: data.token, refreshToken: data.refreshToken };\n }\n }\n\n if (response.status >= 400 && response.status < 500) {\n console.error(chalk.red(\"Invalid code. Please try again.\"));\n if (attempt < maxAttempts) {\n const { retry } = await inquirer.prompt<{ retry: boolean }>([\n {\n type: \"confirm\",\n name: \"retry\",\n message: \"Try again?\",\n default: true,\n },\n ]);\n if (!retry) return null;\n }\n continue;\n }\n\n handleApiError(response, body, \"2FA verification failed. Please try again.\");\n } catch (err) {\n debugError(err);\n console.error(chalk.red(\"Could not reach DashNex API. Check your connection and try again.\"));\n return null;\n }\n }\n\n return null;\n }\n\n private async completeLogin(\n tokens: { token: string; refreshToken: string },\n dashnexPath: string\n ): Promise<void> {\n debug(`GET ${getApiBase()}/users/v1/business/my`);\n const { response: bizResponse, body: bizBody } = await apiFetch(\n `${getApiBase()}/users/v1/business/my`,\n {\n headers: { Authorization: `Bearer ${tokens.token}` },\n }\n );\n debug(`Response: ${bizResponse.status}`);\n debugJson(\"Businesses response\", bizBody);\n\n if (!bizResponse.ok) {\n debugError(new Error(`Business fetch failed: ${bizResponse.status}`));\n handleApiError(\n bizResponse,\n bizBody,\n \"Failed to fetch businesses. Please try again.\"\n );\n }\n\n const businesses = this.parseBusinesses(bizBody);\n debug(`Businesses: ${businesses.length}`);\n\n if (businesses.length === 0) {\n console.error(chalk.red(\"No businesses found for your account.\"));\n process.exit(1);\n }\n\n let selected: Business;\n if (businesses.length === 1) {\n selected = businesses[0];\n debug(`Selected business: ${selected.id} ${selected.name}`);\n } else {\n const { businessId } = await inquirer.prompt<{ businessId: string }>([\n {\n type: \"select\",\n name: \"businessId\",\n message: \"Select business:\",\n choices: businesses.map((b) => ({ name: b.name, value: b.id })),\n },\n ]);\n selected = businesses.find((b) => b.id === businessId) || businesses[0];\n debug(`Selected business: ${selected.id} ${selected.name}`);\n }\n\n debug(`GET ${getApiBase()}/business/v1/login?_switch_business=${selected.id}`);\n const { response: switchResponse, body: switchBody } = await apiFetch(\n `${getApiBase()}/business/v1/login?_switch_business=${selected.id}`,\n {\n headers: { Authorization: `Bearer ${tokens.token}` },\n }\n );\n debug(`Response: ${switchResponse.status}`);\n debugJson(\"Business login response\", switchBody);\n\n if (!switchResponse.ok) {\n debugError(new Error(`Business login failed: ${switchResponse.status}`));\n handleApiError(\n switchResponse,\n switchBody,\n \"Failed to switch to business. Please try again.\"\n );\n }\n\n const switchData = switchBody as { token?: string; refreshToken?: string };\n if (!switchData.token || !switchData.refreshToken) {\n console.error(chalk.red(\"Invalid response from server. Please try again.\"));\n process.exit(1);\n }\n\n const config = {\n token: switchData.token,\n refreshToken: switchData.refreshToken,\n businessId: selected.id,\n };\n\n await fs.writeJson(dashnexPath, config, { spaces: 2 });\n debug(\"Saved credentials to .dashnex\");\n\n this.ensureGitignore();\n\n console.log(chalk.green(`Logged in as ${selected.name}`));\n }\n\n private parseBusinesses(body: Record<string, unknown>): Business[] {\n const arr = Array.isArray(body)\n ? body\n : (body.items as unknown[]) ??\n (body.data as unknown[]) ??\n (body.businesses as unknown[]) ??\n [];\n if (!Array.isArray(arr)) return [];\n\n return arr\n .map((item) => {\n if (item && typeof item === \"object\") {\n const o = item as Record<string, unknown>;\n const id = String(o.id ?? o.business_id ?? \"\").trim();\n const name = String(o.name ?? o.companyName ?? o.business_name ?? \"Unknown\").trim();\n if (id) return { id, name };\n }\n return null;\n })\n .filter((b): b is Business => b !== null);\n }\n\n private async ensureGitignore(): Promise<void> {\n const gitignorePath = path.join(process.cwd(), \".gitignore\");\n if (!(await fs.pathExists(gitignorePath))) return;\n\n let content = await fs.readFile(gitignorePath, \"utf8\");\n if (content.includes(\".dashnex\")) return;\n\n content = content.trimEnd();\n if (!content.endsWith(\"\\n\")) content += \"\\n\";\n content += \"\\n.dashnex\\n\";\n await fs.writeFile(gitignorePath, content);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import chalk from "chalk";
4
+ class LogoutCommand {
5
+ async execute() {
6
+ const dashnexPath = path.join(process.cwd(), ".dashnex");
7
+ if (await fs.pathExists(dashnexPath)) {
8
+ await fs.remove(dashnexPath);
9
+ }
10
+ console.log(chalk.green("Logged out."));
11
+ }
12
+ }
13
+ export {
14
+ LogoutCommand
15
+ };
16
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.js","sources":["../../src/commands/logout.ts"],"sourcesContent":["import fs from \"fs-extra\";\nimport path from \"path\";\nimport chalk from \"chalk\";\nimport type { CliCommand } from \"@dashnex/types\";\n\nexport class LogoutCommand implements CliCommand {\n async execute(): Promise<void> {\n const dashnexPath = path.join(process.cwd(), \".dashnex\");\n if (await fs.pathExists(dashnexPath)) {\n await fs.remove(dashnexPath);\n }\n console.log(chalk.green(\"Logged out.\"));\n }\n}\n"],"names":[],"mappings":";;;AAKO,MAAM,cAAoC;AAAA,EAC/C,MAAM,UAAyB;AAC7B,UAAM,cAAc,KAAK,KAAK,QAAQ,IAAA,GAAO,UAAU;AACvD,QAAI,MAAM,GAAG,WAAW,WAAW,GAAG;AACpC,YAAM,GAAG,OAAO,WAAW;AAAA,IAC7B;AACA,YAAQ,IAAI,MAAM,MAAM,aAAa,CAAC;AAAA,EACxC;AACF;"}
@@ -0,0 +1,102 @@
1
+ import chalk from "chalk";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ import os from "os";
5
+ import inquirer from "inquirer";
6
+ import AdmZip from "adm-zip";
7
+ import { debug, debugError } from "../lib/debug.js";
8
+ import { getBusinessApiBase } from "../lib/api.js";
9
+ import { ensureLoggedIn } from "../services/auth.js";
10
+ const extractErrorMessage = (body) => {
11
+ if (typeof body.error === "string") return body.error;
12
+ if (typeof body.message === "string") return body.message;
13
+ return void 0;
14
+ };
15
+ const SUCCESS_MESSAGE_NEW = 'Application is successfully pulled. Run "npm install" or "pnpm install" to install dependencies and then "npx dashnex dev" to run it';
16
+ const getSuccessMessageNew = (targetDir, cwd) => {
17
+ if (path.resolve(targetDir) === path.resolve(cwd)) {
18
+ return SUCCESS_MESSAGE_NEW;
19
+ }
20
+ const folder = path.relative(cwd, targetDir);
21
+ return `Application is successfully pulled. Run 'cd ${folder}' then "pnpm install" or "npm install" to install dependencies and then "npx dashnex dev" to run it`;
22
+ };
23
+ class PullCommand {
24
+ async execute() {
25
+ debug("Pull flow started");
26
+ const session = await ensureLoggedIn();
27
+ if (!session) return;
28
+ const cwd = process.cwd();
29
+ const dashnexJsonPath = path.join(cwd, "dashnex.json");
30
+ const hasApp = await fs.pathExists(dashnexJsonPath);
31
+ debug(`Application check (dashnex.json): ${hasApp ? "present" : "absent"}`);
32
+ let targetDir;
33
+ if (hasApp) {
34
+ targetDir = cwd;
35
+ } else {
36
+ debug("No application yet, prompting for folder");
37
+ const { folder } = await inquirer.prompt([
38
+ {
39
+ type: "input",
40
+ name: "folder",
41
+ message: "Which folder to pull the application to?",
42
+ default: "."
43
+ }
44
+ ]);
45
+ const resolved = path.resolve(cwd, folder.trim() || ".");
46
+ targetDir = resolved;
47
+ await fs.ensureDir(targetDir);
48
+ debug(`Target folder created/resolved: ${targetDir}`);
49
+ }
50
+ const url = `${getBusinessApiBase()}/business/v1/cli/pull`;
51
+ debug(`GET ${url}`);
52
+ const tempZipPath = path.join(
53
+ os.tmpdir(),
54
+ `dashnex-pull-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`
55
+ );
56
+ try {
57
+ const response = await fetch(url, {
58
+ headers: { Authorization: `Bearer ${session.token}` }
59
+ });
60
+ debug(`Response: ${response.status}`);
61
+ if (!response.ok) {
62
+ const contentType = response.headers.get("content-type") ?? "";
63
+ let message = "Failed to pull application.";
64
+ if (response.status === 401 || response.status === 403) {
65
+ message = "Please run 'npx dashnex login' to authenticate.";
66
+ } else if (response.status === 404) {
67
+ message = "Business has no application. Run 'npx dashnex create' to create one.";
68
+ } else if (contentType.includes("application/json")) {
69
+ const body = await response.json().catch(() => ({}));
70
+ const apiMessage = extractErrorMessage(body);
71
+ if (apiMessage) message = apiMessage;
72
+ }
73
+ console.error(chalk.red(message));
74
+ process.exit(1);
75
+ }
76
+ const buffer = Buffer.from(await response.arrayBuffer());
77
+ await fs.writeFile(tempZipPath, buffer);
78
+ debug("Zip written to temp file");
79
+ const zip = new AdmZip(tempZipPath);
80
+ zip.extractAllTo(targetDir, true);
81
+ debug("Extracted to target folder");
82
+ if (hasApp) {
83
+ console.log(chalk.green(`Application pulled to ${targetDir}`));
84
+ } else {
85
+ console.log(chalk.green(getSuccessMessageNew(targetDir, cwd)));
86
+ }
87
+ } catch (error) {
88
+ debugError(error);
89
+ if (error instanceof Error && error.message.startsWith("EXIT:")) throw error;
90
+ console.error(chalk.red("Could not reach DashNex API. Check your connection and try again."));
91
+ process.exit(1);
92
+ } finally {
93
+ await fs.remove(tempZipPath).catch(() => {
94
+ });
95
+ debug("Temp zip removed");
96
+ }
97
+ }
98
+ }
99
+ export {
100
+ PullCommand
101
+ };
102
+ //# sourceMappingURL=pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.js","sources":["../../src/commands/pull.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"path\";\nimport os from \"os\";\nimport inquirer from \"inquirer\";\nimport AdmZip from \"adm-zip\";\nimport type { CliCommand } from \"@dashnex/types\";\nimport { debug, debugError } from \"../lib/debug.js\";\nimport { getBusinessApiBase } from \"../lib/api.js\";\nimport { ensureLoggedIn } from \"../services/auth.js\";\n\nconst extractErrorMessage = (body: Record<string, unknown>): string | undefined => {\n if (typeof body.error === \"string\") return body.error;\n if (typeof body.message === \"string\") return body.message;\n return undefined;\n};\n\nconst SUCCESS_MESSAGE_NEW =\n 'Application is successfully pulled. Run \"npm install\" or \"pnpm install\" to install dependencies and then \"npx dashnex dev\" to run it';\n\nconst getSuccessMessageNew = (targetDir: string, cwd: string): string => {\n if (path.resolve(targetDir) === path.resolve(cwd)) {\n return SUCCESS_MESSAGE_NEW;\n }\n const folder = path.relative(cwd, targetDir);\n return `Application is successfully pulled. Run 'cd ${folder}' then \"pnpm install\" or \"npm install\" to install dependencies and then \"npx dashnex dev\" to run it`;\n};\n\nexport class PullCommand implements CliCommand {\n async execute(): Promise<void> {\n debug(\"Pull flow started\");\n\n const session = await ensureLoggedIn();\n if (!session) return;\n\n const cwd = process.cwd();\n const dashnexJsonPath = path.join(cwd, \"dashnex.json\");\n const hasApp = await fs.pathExists(dashnexJsonPath);\n debug(`Application check (dashnex.json): ${hasApp ? \"present\" : \"absent\"}`);\n\n let targetDir: string;\n if (hasApp) {\n targetDir = cwd;\n } else {\n debug(\"No application yet, prompting for folder\");\n const { folder } = await inquirer.prompt<{ folder: string }>([\n {\n type: \"input\",\n name: \"folder\",\n message: \"Which folder to pull the application to?\",\n default: \".\",\n },\n ]);\n const resolved = path.resolve(cwd, folder.trim() || \".\");\n targetDir = resolved;\n await fs.ensureDir(targetDir);\n debug(`Target folder created/resolved: ${targetDir}`);\n }\n\n const url = `${getBusinessApiBase()}/business/v1/cli/pull`;\n debug(`GET ${url}`);\n\n const tempZipPath = path.join(\n os.tmpdir(),\n `dashnex-pull-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`\n );\n\n try {\n const response = await fetch(url, {\n headers: { Authorization: `Bearer ${session.token}` },\n });\n\n debug(`Response: ${response.status}`);\n\n if (!response.ok) {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n let message = \"Failed to pull application.\";\n if (response.status === 401 || response.status === 403) {\n message = \"Please run 'npx dashnex login' to authenticate.\";\n } else if (response.status === 404) {\n message = \"Business has no application. Run 'npx dashnex create' to create one.\";\n } else if (contentType.includes(\"application/json\")) {\n const body = (await response.json().catch(() => ({}))) as Record<string, unknown>;\n const apiMessage = extractErrorMessage(body);\n if (apiMessage) message = apiMessage;\n }\n console.error(chalk.red(message));\n process.exit(1);\n }\n\n const buffer = Buffer.from(await response.arrayBuffer());\n await fs.writeFile(tempZipPath, buffer);\n debug(\"Zip written to temp file\");\n\n const zip = new AdmZip(tempZipPath);\n zip.extractAllTo(targetDir, true);\n debug(\"Extracted to target folder\");\n\n if (hasApp) {\n console.log(chalk.green(`Application pulled to ${targetDir}`));\n } else {\n console.log(chalk.green(getSuccessMessageNew(targetDir, cwd)));\n }\n } catch (error) {\n debugError(error);\n if (error instanceof Error && error.message.startsWith(\"EXIT:\")) throw error;\n console.error(chalk.red(\"Could not reach DashNex API. Check your connection and try again.\"));\n process.exit(1);\n } finally {\n await fs.remove(tempZipPath).catch(() => {});\n debug(\"Temp zip removed\");\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ import chalk from "chalk";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ import os from "os";
5
+ import AdmZip from "adm-zip";
6
+ import ignore from "ignore";
7
+ import { debug, debugError } from "../lib/debug.js";
8
+ import { getBusinessApiBase } from "../lib/api.js";
9
+ import { ensureLoggedIn } from "../services/auth.js";
10
+ const extractErrorMessage = (body) => {
11
+ if (typeof body.error === "string") return body.error;
12
+ if (typeof body.message === "string") return body.message;
13
+ return void 0;
14
+ };
15
+ const ALWAYS_EXCLUDE = [".dashnex", ".git"];
16
+ const collectFilesToInclude = async (dir, ig) => {
17
+ const result = [];
18
+ const entries = await fs.readdir(dir, { withFileTypes: true });
19
+ for (const entry of entries) {
20
+ const relativePath = path.relative(process.cwd(), path.join(dir, entry.name));
21
+ const normalized = relativePath.split(path.sep).join("/");
22
+ if (ALWAYS_EXCLUDE.some((ex) => normalized === ex || normalized.startsWith(`${ex}/`))) {
23
+ continue;
24
+ }
25
+ if (ig.ignores(normalized)) {
26
+ continue;
27
+ }
28
+ const fullPath = path.join(dir, entry.name);
29
+ if (entry.isDirectory()) {
30
+ const nested = await collectFilesToInclude(fullPath, ig);
31
+ result.push(...nested);
32
+ } else if (entry.isFile()) {
33
+ result.push(fullPath);
34
+ }
35
+ }
36
+ return result;
37
+ };
38
+ class PushCommand {
39
+ async execute() {
40
+ debug("Push flow started");
41
+ const session = await ensureLoggedIn();
42
+ if (!session) return;
43
+ const cwd = process.cwd();
44
+ const gitignorePath = path.join(cwd, ".gitignore");
45
+ const ig = ignore();
46
+ ig.add(ALWAYS_EXCLUDE);
47
+ if (await fs.pathExists(gitignorePath)) {
48
+ const content = await fs.readFile(gitignorePath, "utf8");
49
+ ig.add(content);
50
+ }
51
+ debug("Building file list");
52
+ const files = await collectFilesToInclude(cwd, ig);
53
+ debug(`Including ${files.length} files`);
54
+ const zip = new AdmZip();
55
+ for (const filePath of files) {
56
+ const relativePath = path.relative(cwd, filePath);
57
+ const entryPath = relativePath.split(path.sep).join("/");
58
+ const zipPath = path.dirname(entryPath) === "." ? "" : path.dirname(entryPath);
59
+ const zipName = path.basename(entryPath);
60
+ zip.addLocalFile(filePath, zipPath, zipName);
61
+ }
62
+ const tempZipPath = path.join(
63
+ os.tmpdir(),
64
+ `dashnex-push-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`
65
+ );
66
+ try {
67
+ zip.writeZip(tempZipPath);
68
+ debug(`Zip written to ${tempZipPath}`);
69
+ const url = `${getBusinessApiBase()}/business/v1/cli/push`;
70
+ debug(`POST ${url}`);
71
+ const formData = new FormData();
72
+ formData.append("file", new Blob([await fs.readFile(tempZipPath)]), "archive.zip");
73
+ const response = await fetch(url, {
74
+ method: "POST",
75
+ headers: { Authorization: `Bearer ${session.token}` },
76
+ body: formData
77
+ });
78
+ debug(`Response: ${response.status}`);
79
+ if (!response.ok) {
80
+ const contentType = response.headers.get("content-type") ?? "";
81
+ let message = "Failed to push application.";
82
+ if (response.status === 401 || response.status === 403) {
83
+ message = "Please run 'npx dashnex login' to authenticate.";
84
+ } else if (contentType.includes("application/json")) {
85
+ const body = await response.json().catch(() => ({}));
86
+ const apiMessage = extractErrorMessage(body);
87
+ if (apiMessage) message = apiMessage;
88
+ }
89
+ console.error(chalk.red(message));
90
+ process.exit(1);
91
+ }
92
+ console.log(chalk.green("Application pushed successfully."));
93
+ } catch (error) {
94
+ debugError(error);
95
+ if (error instanceof Error && error.message.startsWith("EXIT:")) throw error;
96
+ console.error(chalk.red("Could not reach DashNex API. Check your connection and try again."));
97
+ process.exit(1);
98
+ } finally {
99
+ await fs.remove(tempZipPath).catch(() => {
100
+ });
101
+ debug("Temp zip removed");
102
+ }
103
+ }
104
+ }
105
+ export {
106
+ PushCommand
107
+ };
108
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sources":["../../src/commands/push.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"path\";\nimport os from \"os\";\nimport AdmZip from \"adm-zip\";\nimport ignore from \"ignore\";\nimport type { CliCommand } from \"@dashnex/types\";\nimport { debug, debugError } from \"../lib/debug.js\";\nimport { getBusinessApiBase } from \"../lib/api.js\";\nimport { ensureLoggedIn } from \"../services/auth.js\";\n\nconst extractErrorMessage = (body: Record<string, unknown>): string | undefined => {\n if (typeof body.error === \"string\") return body.error;\n if (typeof body.message === \"string\") return body.message;\n return undefined;\n};\n\nconst ALWAYS_EXCLUDE = [\".dashnex\", \".git\"];\n\nconst collectFilesToInclude = async (\n dir: string,\n ig: ReturnType<typeof ignore>\n): Promise<string[]> => {\n const result: string[] = [];\n const entries = await fs.readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const relativePath = path.relative(process.cwd(), path.join(dir, entry.name));\n const normalized = relativePath.split(path.sep).join(\"/\");\n\n if (ALWAYS_EXCLUDE.some((ex) => normalized === ex || normalized.startsWith(`${ex}/`))) {\n continue;\n }\n if (ig.ignores(normalized)) {\n continue;\n }\n\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n const nested = await collectFilesToInclude(fullPath, ig);\n result.push(...nested);\n } else if (entry.isFile()) {\n result.push(fullPath);\n }\n }\n\n return result;\n};\n\nexport class PushCommand implements CliCommand {\n async execute(): Promise<void> {\n debug(\"Push flow started\");\n\n const session = await ensureLoggedIn();\n if (!session) return;\n\n const cwd = process.cwd();\n const gitignorePath = path.join(cwd, \".gitignore\");\n\n const ig = ignore();\n ig.add(ALWAYS_EXCLUDE);\n if (await fs.pathExists(gitignorePath)) {\n const content = await fs.readFile(gitignorePath, \"utf8\");\n ig.add(content);\n }\n\n debug(\"Building file list\");\n const files = await collectFilesToInclude(cwd, ig);\n debug(`Including ${files.length} files`);\n\n const zip = new AdmZip();\n for (const filePath of files) {\n const relativePath = path.relative(cwd, filePath);\n const entryPath = relativePath.split(path.sep).join(\"/\");\n const zipPath = path.dirname(entryPath) === \".\" ? \"\" : path.dirname(entryPath);\n const zipName = path.basename(entryPath);\n zip.addLocalFile(filePath, zipPath, zipName);\n }\n\n const tempZipPath = path.join(\n os.tmpdir(),\n `dashnex-push-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`\n );\n\n try {\n zip.writeZip(tempZipPath);\n debug(`Zip written to ${tempZipPath}`);\n\n const url = `${getBusinessApiBase()}/business/v1/cli/push`;\n debug(`POST ${url}`);\n\n const formData = new FormData();\n formData.append(\"file\", new Blob([await fs.readFile(tempZipPath)]), \"archive.zip\");\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${session.token}` },\n body: formData,\n });\n\n debug(`Response: ${response.status}`);\n\n if (!response.ok) {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n let message = \"Failed to push application.\";\n if (response.status === 401 || response.status === 403) {\n message = \"Please run 'npx dashnex login' to authenticate.\";\n } else if (contentType.includes(\"application/json\")) {\n const body = (await response.json().catch(() => ({}))) as Record<string, unknown>;\n const apiMessage = extractErrorMessage(body);\n if (apiMessage) message = apiMessage;\n }\n console.error(chalk.red(message));\n process.exit(1);\n }\n\n console.log(chalk.green(\"Application pushed successfully.\"));\n } catch (error) {\n debugError(error);\n if (error instanceof Error && error.message.startsWith(\"EXIT:\")) throw error;\n console.error(chalk.red(\"Could not reach DashNex API. Check your connection and try again.\"));\n process.exit(1);\n } finally {\n await fs.remove(tempZipPath).catch(() => {});\n debug(\"Temp zip removed\");\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ class VersionCommand {
2
+ async execute(options) {
3
+ const packageJson = await import("../package.json.js");
4
+ console.log(packageJson.default.version);
5
+ }
6
+ }
7
+ export {
8
+ VersionCommand as default
9
+ };
10
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sources":["../../src/commands/version.ts"],"sourcesContent":["import { CliCommand } from \"@dashnex/types\";\n\nclass VersionCommand implements CliCommand {\n async execute(options: any) {\n const packageJson = await import(\"../../package.json\", { with: { type: \"json\" } });\n console.log(packageJson.default.version);\n }\n}\n\nexport default VersionCommand;"],"names":[],"mappings":"AAEA,MAAM,eAAqC;AAAA,EACzC,MAAM,QAAQ,SAAc;AAC1B,UAAM,cAAc,MAAM,OAAO,oBAAgD;AACjF,YAAQ,IAAI,YAAY,QAAQ,OAAO;AAAA,EACzC;AACF;"}
@@ -0,0 +1,52 @@
1
+ import chalk from "chalk";
2
+ import { debug, debugError } from "../lib/debug.js";
3
+ import { getApiBase } from "../lib/api.js";
4
+ import { ensureLoggedIn } from "../services/auth.js";
5
+ const extractErrorMessage = (body) => {
6
+ if (typeof body.error === "string") return body.error;
7
+ if (typeof body.message === "string") return body.message;
8
+ return void 0;
9
+ };
10
+ class WhoamiCommand {
11
+ async execute() {
12
+ debug("Whoami flow started");
13
+ const session = await ensureLoggedIn();
14
+ if (!session) return;
15
+ const url = `${getApiBase()}/users/v1/`;
16
+ debug(`GET ${url}`);
17
+ try {
18
+ const response = await fetch(url, {
19
+ headers: { Authorization: `Bearer ${session.token}` }
20
+ });
21
+ debug(`Response: ${response.status}`);
22
+ if (!response.ok) {
23
+ const contentType = response.headers.get("content-type") ?? "";
24
+ let message = "Failed to fetch user info.";
25
+ if (response.status === 401 || response.status === 403) {
26
+ message = "Please run 'npx dashnex login' to authenticate.";
27
+ } else if (contentType.includes("application/json")) {
28
+ const body = await response.json().catch(() => ({}));
29
+ const apiMessage = extractErrorMessage(body);
30
+ if (apiMessage) message = apiMessage;
31
+ }
32
+ console.error(chalk.red(message));
33
+ process.exit(1);
34
+ }
35
+ const data = await response.json().catch(() => ({}));
36
+ const businessName = typeof data.name === "string" ? data.name : typeof data.companyName === "string" ? data.companyName : "Unknown";
37
+ const memberUser = data.memberUser;
38
+ const userName = memberUser && typeof memberUser.fullName === "string" ? memberUser.fullName : memberUser && typeof memberUser.name === "string" ? memberUser.name : "Unknown";
39
+ debug(`Whoami: ${businessName} (${userName})`);
40
+ console.log(chalk.green(`You are logged in as ${businessName} (${userName})`));
41
+ } catch (error) {
42
+ debugError(error);
43
+ if (error instanceof Error && error.message.startsWith("EXIT:")) throw error;
44
+ console.error(chalk.red("Could not reach DashNex API. Check your connection and try again."));
45
+ process.exit(1);
46
+ }
47
+ }
48
+ }
49
+ export {
50
+ WhoamiCommand
51
+ };
52
+ //# sourceMappingURL=whoami.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.js","sources":["../../src/commands/whoami.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport type { CliCommand } from \"@dashnex/types\";\nimport { debug, debugError } from \"../lib/debug.js\";\nimport { getApiBase } from \"../lib/api.js\";\nimport { ensureLoggedIn } from \"../services/auth.js\";\n\nconst extractErrorMessage = (body: Record<string, unknown>): string | undefined => {\n if (typeof body.error === \"string\") return body.error;\n if (typeof body.message === \"string\") return body.message;\n return undefined;\n};\n\ninterface MemberUser {\n fullName?: string;\n name?: string;\n}\n\ninterface WhoamiResponse {\n name?: string;\n companyName?: string;\n memberUser?: MemberUser;\n}\n\nexport class WhoamiCommand implements CliCommand {\n async execute(): Promise<void> {\n debug(\"Whoami flow started\");\n\n const session = await ensureLoggedIn();\n if (!session) return;\n\n const url = `${getApiBase()}/users/v1/`;\n debug(`GET ${url}`);\n\n try {\n const response = await fetch(url, {\n headers: { Authorization: `Bearer ${session.token}` },\n });\n\n debug(`Response: ${response.status}`);\n\n if (!response.ok) {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n let message = \"Failed to fetch user info.\";\n if (response.status === 401 || response.status === 403) {\n message = \"Please run 'npx dashnex login' to authenticate.\";\n } else if (contentType.includes(\"application/json\")) {\n const body = (await response.json().catch(() => ({}))) as Record<string, unknown>;\n const apiMessage = extractErrorMessage(body);\n if (apiMessage) message = apiMessage;\n }\n console.error(chalk.red(message));\n process.exit(1);\n }\n\n const data = (await response.json().catch(() => ({}))) as WhoamiResponse;\n const businessName =\n typeof data.name === \"string\"\n ? data.name\n : typeof data.companyName === \"string\"\n ? data.companyName\n : \"Unknown\";\n const memberUser = data.memberUser;\n const userName =\n memberUser && typeof memberUser.fullName === \"string\"\n ? memberUser.fullName\n : memberUser && typeof memberUser.name === \"string\"\n ? memberUser.name\n : \"Unknown\";\n\n debug(`Whoami: ${businessName} (${userName})`);\n console.log(chalk.green(`You are logged in as ${businessName} (${userName})`));\n } catch (error) {\n debugError(error);\n if (error instanceof Error && error.message.startsWith(\"EXIT:\")) throw error;\n console.error(chalk.red(\"Could not reach DashNex API. Check your connection and try again.\"));\n process.exit(1);\n }\n }\n}\n"],"names":[],"mappings":";;;;AAMA,MAAM,sBAAsB,CAAC,SAAsD;AACjF,MAAI,OAAO,KAAK,UAAU,iBAAiB,KAAK;AAChD,MAAI,OAAO,KAAK,YAAY,iBAAiB,KAAK;AAClD,SAAO;AACT;AAaO,MAAM,cAAoC;AAAA,EAC/C,MAAM,UAAyB;AAC7B,UAAM,qBAAqB;AAE3B,UAAM,UAAU,MAAM,eAAA;AACtB,QAAI,CAAC,QAAS;AAEd,UAAM,MAAM,GAAG,WAAA,CAAY;AAC3B,UAAM,OAAO,GAAG,EAAE;AAElB,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,KAAK;AAAA,QAChC,SAAS,EAAE,eAAe,UAAU,QAAQ,KAAK,GAAA;AAAA,MAAG,CACrD;AAED,YAAM,aAAa,SAAS,MAAM,EAAE;AAEpC,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,YAAI,UAAU;AACd,YAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,oBAAU;AAAA,QACZ,WAAW,YAAY,SAAS,kBAAkB,GAAG;AACnD,gBAAM,OAAQ,MAAM,SAAS,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AACpD,gBAAM,aAAa,oBAAoB,IAAI;AAC3C,cAAI,WAAY,WAAU;AAAA,QAC5B;AACA,gBAAQ,MAAM,MAAM,IAAI,OAAO,CAAC;AAChC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAEA,YAAM,OAAQ,MAAM,SAAS,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AACpD,YAAM,eACJ,OAAO,KAAK,SAAS,WACjB,KAAK,OACL,OAAO,KAAK,gBAAgB,WAC1B,KAAK,cACL;AACR,YAAM,aAAa,KAAK;AACxB,YAAM,WACJ,cAAc,OAAO,WAAW,aAAa,WACzC,WAAW,WACX,cAAc,OAAO,WAAW,SAAS,WACvC,WAAW,OACX;AAER,YAAM,WAAW,YAAY,KAAK,QAAQ,GAAG;AAC7C,cAAQ,IAAI,MAAM,MAAM,wBAAwB,YAAY,KAAK,QAAQ,GAAG,CAAC;AAAA,IAC/E,SAAS,OAAO;AACd,iBAAW,KAAK;AAChB,UAAI,iBAAiB,SAAS,MAAM,QAAQ,WAAW,OAAO,EAAG,OAAM;AACvE,cAAQ,MAAM,MAAM,IAAI,mEAAmE,CAAC;AAC5F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF;"}
@@ -0,0 +1,5 @@
1
+ const dashnexConfig = {};
2
+ export {
3
+ dashnexConfig as default
4
+ };
5
+ //# sourceMappingURL=dashnex.json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashnex.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}