@symbo.ls/cli 2.33.12 ā 2.33.13
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/bin/collab.js +334 -0
- package/bin/fetch.js +77 -54
- package/bin/fs.js +150 -61
- package/bin/index.js +1 -0
- package/bin/install.js +6 -19
- package/bin/login.js +82 -62
- package/bin/push.js +157 -117
- package/bin/require.js +140 -6
- package/bin/socket-server.js +5 -10
- package/bin/sync.js +313 -112
- package/helpers/apiUtils.js +60 -58
- package/helpers/buildMessages.js +76 -0
- package/helpers/changesUtils.js +474 -0
- package/helpers/compareUtils.js +136 -2
- package/helpers/config.js +131 -18
- package/helpers/credentialManager.js +66 -0
- package/helpers/fileUtils.js +8 -2
- package/helpers/symbolsConfig.js +35 -0
- package/helpers/transportUtils.js +58 -0
- package/package.json +7 -5
package/bin/fs.js
CHANGED
|
@@ -18,24 +18,90 @@ const {
|
|
|
18
18
|
} = utils.default || utils
|
|
19
19
|
|
|
20
20
|
let singleFileKeys = ['designSystem', 'state', 'files', 'dependencies']
|
|
21
|
-
const directoryKeys = [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
21
|
+
const directoryKeys = ['components', 'snippets', 'pages', 'functions', 'methods']
|
|
22
|
+
|
|
23
|
+
const defaultExports = ['pages', 'designSystem', 'state', 'files', 'dependencies', 'schema']
|
|
24
|
+
|
|
25
|
+
// Minimal reserved identifier set to avoid invalid named exports like "export const default"
|
|
26
|
+
const RESERVED_IDENTIFIERS = new Set(['default'])
|
|
27
|
+
function isReservedIdentifier (name) {
|
|
28
|
+
return RESERVED_IDENTIFIERS.has(name)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Keys that should never be materialized as files inside collection directories
|
|
32
|
+
const SKIP_ENTRY_KEYS = new Set(['__order', 'schema'])
|
|
33
|
+
function shouldSkipEntryKey (name) {
|
|
34
|
+
return SKIP_ENTRY_KEYS.has(name) || isReservedIdentifier(name)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function reorderWithOrderKeys (input) {
|
|
38
|
+
if (Array.isArray(input)) {
|
|
39
|
+
return input.map(reorderWithOrderKeys)
|
|
40
|
+
}
|
|
41
|
+
if (!input || typeof input !== 'object') return input
|
|
42
|
+
const hasOrder = Array.isArray(input.__order)
|
|
43
|
+
const originalKeys = Object.keys(input)
|
|
44
|
+
const orderedKeys = []
|
|
45
|
+
if (hasOrder) {
|
|
46
|
+
for (let i = 0; i < input.__order.length; i++) {
|
|
47
|
+
const k = input.__order[i]
|
|
48
|
+
if (k === '__order') continue
|
|
49
|
+
if (originalKeys.includes(k) && !orderedKeys.includes(k)) {
|
|
50
|
+
orderedKeys.push(k)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (let i = 0; i < originalKeys.length; i++) {
|
|
55
|
+
const k = originalKeys[i]
|
|
56
|
+
if (k === '__order') continue
|
|
57
|
+
if (!orderedKeys.includes(k)) orderedKeys.push(k)
|
|
58
|
+
}
|
|
59
|
+
const out = {}
|
|
60
|
+
for (let i = 0; i < orderedKeys.length; i++) {
|
|
61
|
+
const k = orderedKeys[i]
|
|
62
|
+
out[k] = reorderWithOrderKeys(input[k])
|
|
63
|
+
}
|
|
64
|
+
if (hasOrder) {
|
|
65
|
+
out.__order = input.__order.slice()
|
|
66
|
+
} else if (Object.prototype.hasOwnProperty.call(input, '__order')) {
|
|
67
|
+
// Preserve explicit empty/non-array __order semantics at the end
|
|
68
|
+
out.__order = input.__order
|
|
69
|
+
}
|
|
70
|
+
return out
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function removeStaleFiles(body, targetDir) {
|
|
74
|
+
for (const key of directoryKeys) {
|
|
75
|
+
const dirPath = path.join(targetDir, key)
|
|
76
|
+
if (!fs.existsSync(dirPath)) continue
|
|
77
|
+
|
|
78
|
+
const existingFiles = await fs.promises.readdir(dirPath)
|
|
79
|
+
const currentEntries = body[key] ? Object.keys(body[key])
|
|
80
|
+
// Drop meta/reserved identifiers like "__order" and "default"
|
|
81
|
+
.filter(entry => !shouldSkipEntryKey(entry))
|
|
82
|
+
.map(entry => {
|
|
83
|
+
// Apply the same transformations as in createKeyDirectoryAndFiles
|
|
84
|
+
let fileName = entry
|
|
85
|
+
if (fileName.startsWith('/')) fileName = fileName.slice(1)
|
|
86
|
+
if (fileName === '') fileName = 'main'
|
|
87
|
+
if (fileName.includes('*')) fileName = 'fallback'
|
|
88
|
+
return `${fileName.replace('/', '-')}.js`
|
|
89
|
+
}) : []
|
|
90
|
+
|
|
91
|
+
// Don't remove index.js
|
|
92
|
+
const filesToCheck = existingFiles.filter(file => file !== 'index.js')
|
|
93
|
+
|
|
94
|
+
for (const file of filesToCheck) {
|
|
95
|
+
if (!currentEntries.includes(file)) {
|
|
96
|
+
const filePath = path.join(dirPath, file)
|
|
97
|
+
console.log(chalk.yellow(`Removing stale file: ${path.join(key, file)}`))
|
|
98
|
+
await fs.promises.unlink(filePath)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function createFs (
|
|
39
105
|
body,
|
|
40
106
|
distDir = path.join(process.cwd(), 'smbls'),
|
|
41
107
|
opts = {}
|
|
@@ -84,54 +150,72 @@ export async function createFs(
|
|
|
84
150
|
|
|
85
151
|
if (filesExist) {
|
|
86
152
|
const cacheDir = path.join(distDir, '.cache')
|
|
87
|
-
await fs.promises.mkdir(cacheDir, { recursive: true })
|
|
88
153
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
createKeyDirectoryAndFiles(key, body, cacheDir, true)
|
|
92
|
-
),
|
|
93
|
-
...singleFileKeys.map((key) => {
|
|
94
|
-
if (body[key] && typeof body[key] === 'object') {
|
|
95
|
-
return createSingleFileFolderAndFile(key, body[key], cacheDir, true)
|
|
96
|
-
}
|
|
97
|
-
return undefined
|
|
98
|
-
})
|
|
99
|
-
]
|
|
154
|
+
try {
|
|
155
|
+
await fs.promises.mkdir(cacheDir, { recursive: true })
|
|
100
156
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
cacheDir,
|
|
105
|
-
'root'
|
|
106
|
-
)
|
|
157
|
+
if (update) {
|
|
158
|
+
await removeStaleFiles(body, targetDir)
|
|
159
|
+
}
|
|
107
160
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
161
|
+
const cachePromises = [
|
|
162
|
+
...directoryKeys.map((key) =>
|
|
163
|
+
createKeyDirectoryAndFiles(key, body, cacheDir, true)
|
|
164
|
+
),
|
|
165
|
+
...singleFileKeys.map((key) => {
|
|
166
|
+
if (body[key] && typeof body[key] === 'object') {
|
|
167
|
+
return createSingleFileFolderAndFile(key, body[key], cacheDir, true)
|
|
168
|
+
}
|
|
169
|
+
return undefined
|
|
170
|
+
})
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
await Promise.all(cachePromises)
|
|
174
|
+
await generateIndexjsFile(
|
|
175
|
+
joinArrays(directoryKeys, singleFileKeys),
|
|
176
|
+
cacheDir,
|
|
177
|
+
'root'
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
const diffs = await findDiff(cacheDir, targetDir)
|
|
181
|
+
if (diffs.length > 0) {
|
|
182
|
+
console.log('Differences found:')
|
|
183
|
+
diffs.forEach((diff) => {
|
|
184
|
+
console.log(chalk.green(`File: ${diff.file}`))
|
|
185
|
+
console.log(chalk.yellow('Diff:'))
|
|
186
|
+
console.log(chalk.yellow(diff.diff))
|
|
187
|
+
console.log('---')
|
|
188
|
+
})
|
|
189
|
+
if (!update) {
|
|
190
|
+
const { consent } = await askForConsent()
|
|
191
|
+
if (consent) {
|
|
192
|
+
await overrideFiles(cacheDir, targetDir)
|
|
193
|
+
console.log('Files overridden successfully.')
|
|
194
|
+
} else {
|
|
195
|
+
console.log('Files not overridden.')
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
120
198
|
await overrideFiles(cacheDir, targetDir)
|
|
121
199
|
console.log('Files overridden successfully.')
|
|
122
|
-
|
|
123
|
-
console.log(
|
|
200
|
+
console.log()
|
|
201
|
+
console.log(chalk.dim('\n----------------\n'))
|
|
124
202
|
}
|
|
125
203
|
} else {
|
|
126
|
-
|
|
127
|
-
console.log('Files overridden successfully.')
|
|
204
|
+
console.log('No differences found.')
|
|
128
205
|
console.log()
|
|
129
206
|
console.log(chalk.dim('\n----------------\n'))
|
|
130
207
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
208
|
+
|
|
209
|
+
// Clean up cache directory
|
|
210
|
+
await fs.promises.rm(cacheDir, { recursive: true, force: true })
|
|
211
|
+
} catch (error) {
|
|
212
|
+
// Make sure we clean up even if there's an error
|
|
213
|
+
try {
|
|
214
|
+
await fs.promises.rm(cacheDir, { recursive: true, force: true })
|
|
215
|
+
} catch (cleanupError) {
|
|
216
|
+
// Ignore cleanup errors
|
|
217
|
+
}
|
|
218
|
+
throw error // Re-throw the original error
|
|
135
219
|
}
|
|
136
220
|
}
|
|
137
221
|
|
|
@@ -142,7 +226,10 @@ export async function createFs(
|
|
|
142
226
|
const dirs = []
|
|
143
227
|
|
|
144
228
|
if (body[key] && isObject(body[key])) {
|
|
145
|
-
const promises = Object.entries(body[key])
|
|
229
|
+
const promises = Object.entries(body[key])
|
|
230
|
+
// Skip meta/reserved identifier entries (e.g. "__order", "default")
|
|
231
|
+
.filter(([entryKey]) => !shouldSkipEntryKey(entryKey))
|
|
232
|
+
.map(
|
|
146
233
|
async ([entryKey, value]) => {
|
|
147
234
|
// if pages
|
|
148
235
|
if (entryKey.startsWith('/')) entryKey = entryKey.slice(1)
|
|
@@ -165,7 +252,9 @@ export async function createFs(
|
|
|
165
252
|
childKey.includes('-') || childKey.includes('/')
|
|
166
253
|
? removeChars(toCamelCase(childKey))
|
|
167
254
|
: childKey
|
|
168
|
-
|
|
255
|
+
// Avoid reserved identifiers that break ESM syntax, e.g. "export const default"
|
|
256
|
+
const safeItemKey = isReservedIdentifier(itemKey) ? `_${itemKey}` : itemKey
|
|
257
|
+
const filePath = path.join(dirPath, `${childKey.replace('/', '-')}.js`)
|
|
169
258
|
|
|
170
259
|
if (!update && fs.existsSync(filePath)) {
|
|
171
260
|
return
|
|
@@ -174,13 +263,13 @@ export async function createFs(
|
|
|
174
263
|
const itemKeyInvalid = itemKey.includes('.')
|
|
175
264
|
const validKey = itemKeyInvalid
|
|
176
265
|
? `const ${removeChars(toTitleCase(itemKey))}`
|
|
177
|
-
: `export const ${
|
|
266
|
+
: `export const ${safeItemKey}`
|
|
178
267
|
|
|
179
268
|
let stringifiedContent
|
|
180
269
|
if (isString(value)) {
|
|
181
270
|
stringifiedContent = `${validKey} = ${value}`
|
|
182
271
|
} else {
|
|
183
|
-
const content = deepDestringifyFunctions(value)
|
|
272
|
+
const content = reorderWithOrderKeys(deepDestringifyFunctions(value))
|
|
184
273
|
// console.log('ON DEEPDESTR:')
|
|
185
274
|
// console.log(content.components.Configuration)
|
|
186
275
|
stringifiedContent = `${validKey} = ${objectToString(content)};`
|
|
@@ -203,7 +292,7 @@ export { ${removeChars(toTitleCase(itemKey))} as '${itemKey}' }`
|
|
|
203
292
|
}
|
|
204
293
|
|
|
205
294
|
if (isString(data)) data = { default: data }
|
|
206
|
-
const content = deepDestringifyFunctions(data)
|
|
295
|
+
const content = reorderWithOrderKeys(deepDestringifyFunctions(data))
|
|
207
296
|
const stringifiedContent = `export default ${objectToString(content)};`
|
|
208
297
|
|
|
209
298
|
await fs.promises.writeFile(filePath, stringifiedContent, 'utf8')
|
package/bin/index.js
CHANGED
package/bin/install.js
CHANGED
|
@@ -4,20 +4,10 @@ import chalk from 'chalk'
|
|
|
4
4
|
import { loadModule } from './require.js'
|
|
5
5
|
import { exec } from 'child_process'
|
|
6
6
|
import { program } from './program.js'
|
|
7
|
-
|
|
7
|
+
import { loadSymbolsConfig } from '../helpers/symbolsConfig.js'
|
|
8
8
|
const PACKAGE_PATH = process.cwd() + '/package.json'
|
|
9
|
-
const RC_PATH = process.cwd() + '/symbols.json'
|
|
10
|
-
const LOCAL_CONFIG_PATH = process.cwd() + '/node_modules/@symbo.ls/init/dynamic.json'
|
|
11
|
-
const DEFAULT_REMOTE_CONFIG_PATH = 'https://api.symbols.app/' // eslint-disable-line
|
|
12
|
-
|
|
13
|
-
const pkg = loadModule(PACKAGE_PATH)
|
|
14
|
-
const rcFile = loadModule(RC_PATH) // eslint-disable-line
|
|
15
|
-
const localConfig = loadModule(LOCAL_CONFIG_PATH) // eslint-disable-line
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
rc = loadModule(RC_PATH) // eslint-disable-line
|
|
20
|
-
} catch (e) { console.error('Please include symbols.json to your root of respository') }
|
|
10
|
+
const pkg = await loadModule(PACKAGE_PATH, { json: true, silent: true })
|
|
21
11
|
|
|
22
12
|
const makeCommand = (packageManager, packageName) => {
|
|
23
13
|
return packageManager === 'yarn'
|
|
@@ -28,13 +18,10 @@ const makeCommand = (packageManager, packageName) => {
|
|
|
28
18
|
}
|
|
29
19
|
|
|
30
20
|
export const installFromCli = async (options) => {
|
|
31
|
-
|
|
32
|
-
console.error('symbols.json not found in the root of the repository')
|
|
33
|
-
return
|
|
34
|
-
}
|
|
21
|
+
const symbolsConfig = await loadSymbolsConfig()
|
|
35
22
|
|
|
36
|
-
const framework =
|
|
37
|
-
const packageManager =
|
|
23
|
+
const framework = symbolsConfig.framework || options.framework
|
|
24
|
+
const packageManager = symbolsConfig.packageManager || options.packageManager
|
|
38
25
|
|
|
39
26
|
// const packageName = `@symbo.ls/${mode || 'uikit'}`
|
|
40
27
|
const packageName = framework === 'react' ? '@symbo.ls/react' : 'smbls'
|
|
@@ -63,7 +50,7 @@ export const installFromCli = async (options) => {
|
|
|
63
50
|
}
|
|
64
51
|
|
|
65
52
|
program
|
|
66
|
-
.version(pkg.version
|
|
53
|
+
.version(pkg && pkg.version ? pkg.version : 'unknown')
|
|
67
54
|
|
|
68
55
|
program
|
|
69
56
|
.command('install')
|
package/bin/login.js
CHANGED
|
@@ -1,56 +1,25 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
import inquirer from 'inquirer'
|
|
4
|
-
import fs from 'fs'
|
|
5
|
-
import path from 'path'
|
|
6
|
-
import os from 'os'
|
|
7
4
|
import chalk from 'chalk'
|
|
8
5
|
import { program } from './program.js'
|
|
9
|
-
import { getApiUrl } from '../helpers/config.js'
|
|
6
|
+
import { getApiUrl, saveCliConfig, loadCliConfig } from '../helpers/config.js'
|
|
7
|
+
import { CredentialManager } from '../helpers/credentialManager.js'
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return {}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Save credentials to rc file
|
|
30
|
-
saveCredentials (credentials) {
|
|
31
|
-
try {
|
|
32
|
-
fs.writeFileSync(this.rcPath, JSON.stringify(credentials, null, 2))
|
|
33
|
-
return true
|
|
34
|
-
} catch (err) {
|
|
35
|
-
console.error('Failed to save credentials:', err)
|
|
36
|
-
return false
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Get stored auth token
|
|
41
|
-
getAuthToken () {
|
|
42
|
-
const creds = this.loadCredentials()
|
|
43
|
-
return creds.authToken
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Clear stored credentials
|
|
47
|
-
clearCredentials () {
|
|
48
|
-
try {
|
|
49
|
-
fs.unlinkSync(this.rcPath)
|
|
50
|
-
return true
|
|
51
|
-
} catch (err) {
|
|
52
|
-
return false
|
|
53
|
-
}
|
|
9
|
+
function websiteFromApi(apiBaseUrl) {
|
|
10
|
+
try {
|
|
11
|
+
const u = new URL(apiBaseUrl)
|
|
12
|
+
const host = u.host
|
|
13
|
+
if (apiBaseUrl.startsWith('http://localhost')) return 'http://localhost:1024'
|
|
14
|
+
if (host === 'api.dev.symbols.app') return 'https://dev.symbols.app'
|
|
15
|
+
if (host === 'api.staging.symbols.app') return 'https://staging.symbols.app'
|
|
16
|
+
if (host === 'api.test.symbols.app') return 'https://test.symbols.app'
|
|
17
|
+
if (host === 'api.symbols.app') return 'https://symbols.app'
|
|
18
|
+
// Fallback: strip leading api.
|
|
19
|
+
if (host.startsWith('api.')) return `https://${host.replace(/^api\./, '')}`
|
|
20
|
+
return `${u.protocol}//${host}`
|
|
21
|
+
} catch (_) {
|
|
22
|
+
return 'https://symbols.app'
|
|
54
23
|
}
|
|
55
24
|
}
|
|
56
25
|
|
|
@@ -58,29 +27,38 @@ program
|
|
|
58
27
|
.command('login')
|
|
59
28
|
.description('Sign in to Symbols')
|
|
60
29
|
.action(async () => {
|
|
61
|
-
console.log('
|
|
62
|
-
|
|
63
|
-
console.log(
|
|
30
|
+
console.log(chalk.cyan('\nš Welcome to Symbols CLI'))
|
|
31
|
+
console.log(chalk.white('\nPlease sign in with your Symbols account:'))
|
|
32
|
+
console.log(chalk.dim('Don\'t have an account? Visit https://symbols.app/signup\n'))
|
|
64
33
|
|
|
65
34
|
// Prompt for credentials
|
|
35
|
+
const currentConfig = loadCliConfig()
|
|
66
36
|
const answers = await inquirer.prompt([
|
|
37
|
+
{
|
|
38
|
+
type: 'input',
|
|
39
|
+
name: 'apiBaseUrl',
|
|
40
|
+
message: 'API Base URL:',
|
|
41
|
+
default: currentConfig.apiBaseUrl || getApiUrl(),
|
|
42
|
+
validate: input => /^https?:\/\//.test(input) || 'ā Please enter a valid URL'
|
|
43
|
+
},
|
|
67
44
|
{
|
|
68
45
|
type: 'input',
|
|
69
46
|
name: 'email',
|
|
70
|
-
message: '
|
|
71
|
-
validate: input => input.includes('@') || 'Please enter a valid email'
|
|
47
|
+
message: 'Email:',
|
|
48
|
+
validate: input => input.includes('@') || 'ā Please enter a valid email address'
|
|
72
49
|
},
|
|
73
50
|
{
|
|
74
51
|
type: 'password',
|
|
75
52
|
name: 'password',
|
|
76
|
-
message: '
|
|
77
|
-
validate: input => input.length >= 6 || 'Password must be at least 6 characters'
|
|
53
|
+
message: 'Password:',
|
|
54
|
+
validate: input => input.length >= 6 || 'ā Password must be at least 6 characters'
|
|
78
55
|
}
|
|
79
56
|
])
|
|
80
57
|
|
|
81
58
|
try {
|
|
82
59
|
// Make login request
|
|
83
|
-
|
|
60
|
+
console.log(chalk.dim('\nAuthenticating...'))
|
|
61
|
+
const response = await fetch(`${answers.apiBaseUrl}/core/auth/login`, {
|
|
84
62
|
method: 'POST',
|
|
85
63
|
headers: {
|
|
86
64
|
'Content-Type': 'application/json'
|
|
@@ -94,21 +72,63 @@ program
|
|
|
94
72
|
const data = await response.json()
|
|
95
73
|
|
|
96
74
|
if (!response.ok) {
|
|
97
|
-
|
|
75
|
+
const msg = data?.message || data?.error || `Authentication failed (${response.status})`
|
|
76
|
+
const err = new Error(msg)
|
|
77
|
+
err.response = { status: response.status, data }
|
|
78
|
+
throw err
|
|
98
79
|
}
|
|
99
80
|
|
|
81
|
+
// Extract token from various possible shapes
|
|
82
|
+
const user = data?.data?.user || data?.user
|
|
83
|
+
const tokens = data?.data?.tokens || data?.tokens || {}
|
|
84
|
+
const token =
|
|
85
|
+
tokens?.accessToken ||
|
|
86
|
+
data?.token ||
|
|
87
|
+
data?.accessToken ||
|
|
88
|
+
data?.jwt ||
|
|
89
|
+
data?.data?.token ||
|
|
90
|
+
data?.data?.accessToken ||
|
|
91
|
+
data?.data?.jwt
|
|
92
|
+
if (!token) {
|
|
93
|
+
throw new Error('Login succeeded but no token was returned by the server')
|
|
94
|
+
}
|
|
95
|
+
const refreshToken = tokens?.refreshToken || null
|
|
96
|
+
const accessTokenExp = tokens?.accessTokenExp?.expiresAt || null
|
|
97
|
+
|
|
100
98
|
// Save credentials
|
|
101
99
|
const credManager = new CredentialManager()
|
|
102
100
|
credManager.saveCredentials({
|
|
103
|
-
authToken:
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
authToken: token,
|
|
102
|
+
refreshToken,
|
|
103
|
+
authTokenExpiresAt: accessTokenExp,
|
|
104
|
+
userId: user?.id || data?.userId,
|
|
105
|
+
email: user?.email || answers.email
|
|
106
106
|
})
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
// Persist API base URL to local config
|
|
109
|
+
saveCliConfig({ apiBaseUrl: answers.apiBaseUrl })
|
|
110
|
+
|
|
111
|
+
console.log(chalk.green('\n⨠Successfully logged in!'))
|
|
112
|
+
console.log(chalk.white('\nYou can now use Symbols CLI commands:'))
|
|
113
|
+
console.log(chalk.cyan(' smbls fetch ') + chalk.dim('Fetch your design system'))
|
|
114
|
+
console.log(chalk.cyan(' smbls sync ') + chalk.dim('Sync local changes'))
|
|
115
|
+
console.log(chalk.cyan(' smbls push ') + chalk.dim('Push updates to Symbols'))
|
|
116
|
+
console.log(chalk.cyan(' smbls collab ') + chalk.dim('Connect to team on platform in realtime'))
|
|
117
|
+
|
|
118
|
+
console.log(chalk.dim(`\nCredentials saved to ${credManager.rcPath}`))
|
|
119
|
+
console.log(chalk.dim('For more commands, run: smbls --help\n'))
|
|
120
|
+
|
|
110
121
|
} catch (error) {
|
|
111
|
-
|
|
122
|
+
const website = websiteFromApi(answers.apiBaseUrl)
|
|
123
|
+
console.log(chalk.red('\nā Login failed'))
|
|
124
|
+
console.log(chalk.white('\nError:'))
|
|
125
|
+
console.log(chalk.yellow(error.message || 'Unknown error'))
|
|
126
|
+
|
|
127
|
+
console.log(chalk.white('\nNeed help?'))
|
|
128
|
+
console.log(chalk.dim(`⢠Reset password: ${website}/reset-password`))
|
|
129
|
+
console.log(chalk.dim('⢠Contact support: support@symbols.app'))
|
|
130
|
+
console.log(chalk.dim(`⢠Documentation: ${website}/docs/cli\n`))
|
|
131
|
+
|
|
112
132
|
process.exit(1)
|
|
113
133
|
}
|
|
114
134
|
})
|