mlcl 1.0.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/.eslintignore +31 -0
- package/.eslintrc.json +54 -0
- package/LICENSE +200 -0
- package/README.md +101 -0
- package/babel.config.json +10 -0
- package/build/API/authorization.js +107 -0
- package/build/API/client.js +11 -0
- package/build/API/index.js +8 -0
- package/build/API/molecule/index.js +11 -0
- package/build/API/molecule/types.js +6 -0
- package/build/API/resource/device/index.js +12 -0
- package/build/API/resource/device/types.js +6 -0
- package/build/API/resource/device/utilities/confirmSshKey.js +20 -0
- package/build/API/resource/device/utilities/index.js +7 -0
- package/build/API/resource/device/utilities/sendSshKey.js +64 -0
- package/build/API/resource/index.js +10 -0
- package/build/API/resource/molecule/index.js +12 -0
- package/build/API/resource/molecule/types.js +6 -0
- package/build/API/resource/molecule/utilities/getPlan.js +15 -0
- package/build/API/resource/molecule/utilities/index.js +6 -0
- package/build/API/resource/payment/index.js +9 -0
- package/build/API/resource/payment/plans.js +28 -0
- package/build/API/resource/payment/platform.js +7 -0
- package/build/API/resource/payment/platforms/apple/index.js +21 -0
- package/build/API/resource/payment/platforms/apple/plans/appleMonthly.js +17 -0
- package/build/API/resource/payment/platforms/apple/plans/appleYearly.js +18 -0
- package/build/API/resource/payment/platforms/apple/plans/index.js +7 -0
- package/build/API/resource/payment/platforms/google/index.js +21 -0
- package/build/API/resource/payment/platforms/google/plans/googleMonthly.js +17 -0
- package/build/API/resource/payment/platforms/google/plans/googleYearly.js +18 -0
- package/build/API/resource/payment/platforms/google/plans/index.js +7 -0
- package/build/API/resource/payment/platforms/index.js +8 -0
- package/build/API/resource/payment/platforms/stripe/index.js +25 -0
- package/build/API/resource/payment/platforms/stripe/plans/index.js +11 -0
- package/build/API/resource/payment/platforms/stripe/plans/stripeBusinessMolecule.js +21 -0
- package/build/API/resource/payment/platforms/stripe/plans/stripeMonthly.js +18 -0
- package/build/API/resource/payment/platforms/stripe/plans/stripeMonthlyMolecule.js +18 -0
- package/build/API/resource/payment/platforms/stripe/plans/stripeSingleUseMolecule.js +19 -0
- package/build/API/resource/payment/platforms/stripe/plans/stripeUnlimitedUseMolecule.js +19 -0
- package/build/API/resource/payment/platforms/stripe/plans/stripeYearly.js +19 -0
- package/build/API/resource/payment/types.js +6 -0
- package/build/API/resource/types.js +6 -0
- package/build/API/resource/user/index.js +15 -0
- package/build/API/resource/user/types.js +6 -0
- package/build/API/resource/user/utilities/getPlan.js +12 -0
- package/build/API/resource/user/utilities/index.js +7 -0
- package/build/API/resource/user/utilities/logIn.js +60 -0
- package/build/cli.js +15 -0
- package/build/commands/add-ssh-key.js +24 -0
- package/build/commands/buy.js +70 -0
- package/build/commands/clone.js +329 -0
- package/build/commands/index.js +17 -0
- package/build/commands/info.js +33 -0
- package/build/commands/login.js +20 -0
- package/build/commands/logout.js +23 -0
- package/build/commands/molecule/index.js +11 -0
- package/build/commands/molecule/types.js +6 -0
- package/build/commands/remove-ssh-key.js +36 -0
- package/build/commands/select.js +98 -0
- package/build/commands/set-api-key.js +73 -0
- package/build/commands/subscribe.js +74 -0
- package/build/commands/unsubscribe.js +59 -0
- package/build/commands/utilities/asyncExec.js +6 -0
- package/build/commands/utilities/branchExists.js +13 -0
- package/build/commands/utilities/branchExistsSync.js +12 -0
- package/build/commands/utilities/checkoutSelectionBranch.js +49 -0
- package/build/commands/utilities/cherryPick.js +20 -0
- package/build/commands/utilities/cloneRepo.js +9 -0
- package/build/commands/utilities/convertNpmToYarn.js +60 -0
- package/build/commands/utilities/convertTypeDocToJSDoc.js +51 -0
- package/build/commands/utilities/convertTypeScriptToJavaScript.js +451 -0
- package/build/commands/utilities/enableMergeDrivers.js +50 -0
- package/build/commands/utilities/getCurrentBranch.js +11 -0
- package/build/commands/utilities/getCurrentBranchSync.js +7 -0
- package/build/commands/utilities/getRepoRootPath.js +7 -0
- package/build/commands/utilities/getRepoRootPathSync.js +13 -0
- package/build/commands/utilities/index.js +16 -0
- package/build/commands/utilities/npm-to-yarn.js +41 -0
- package/build/commands/utilities/ts-to-js.js +344 -0
- package/build/commands/version.js +14 -0
- package/build/commands/x-merge-json.js +33 -0
- package/build/config.js +5 -0
- package/build/environment.js +80 -0
- package/build/index.js +23 -0
- package/build/logger.js +12 -0
- package/build/pkg.js +5 -0
- package/build/repoConfig copy.js +5 -0
- package/build/repoConfig.js +5 -0
- package/build/types/global.js +6 -0
- package/build/types/index.js +6 -0
- package/build/userConfig.js +5 -0
- package/build/utilities/__tests__/getErrorMessage.js +26 -0
- package/build/utilities/__tests__/timeAgo.js +39 -0
- package/build/utilities/checkVersion.js +29 -0
- package/build/utilities/getErrorMessage.js +15 -0
- package/build/utilities/index.js +13 -0
- package/build/utilities/readConfig.js +19 -0
- package/build/utilities/readRepoConfig copy.js +20 -0
- package/build/utilities/readRepoConfig.js +19 -0
- package/build/utilities/readUserConfig.js +20 -0
- package/build/utilities/synchronizeUserConfig.js +25 -0
- package/build/utilities/timeAgo.js +42 -0
- package/build/utilities/userLoggedIn.js +24 -0
- package/build/utilities/writeConfig.js +8 -0
- package/build/utilities/writeRepoConfig copy.js +9 -0
- package/build/utilities/writeRepoConfig.js +10 -0
- package/build/utilities/writeUserConfig.js +11 -0
- package/jest.config.json +11 -0
- package/package.json +56 -0
- package/src/API/authorization.ts +143 -0
- package/src/API/client.ts +12 -0
- package/src/API/index.ts +10 -0
- package/src/API/resource/device/index.ts +27 -0
- package/src/API/resource/device/types.ts +80 -0
- package/src/API/resource/device/utilities/confirmSshKey.ts +25 -0
- package/src/API/resource/device/utilities/index.ts +8 -0
- package/src/API/resource/device/utilities/sendSshKey.ts +80 -0
- package/src/API/resource/index.ts +12 -0
- package/src/API/resource/molecule/index.ts +30 -0
- package/src/API/resource/molecule/types.ts +332 -0
- package/src/API/resource/molecule/utilities/getPlan.ts +21 -0
- package/src/API/resource/molecule/utilities/index.ts +7 -0
- package/src/API/resource/payment/index.ts +12 -0
- package/src/API/resource/payment/plans.ts +32 -0
- package/src/API/resource/payment/platform.ts +11 -0
- package/src/API/resource/payment/platforms/apple/index.ts +25 -0
- package/src/API/resource/payment/platforms/apple/plans/appleMonthly.ts +19 -0
- package/src/API/resource/payment/platforms/apple/plans/appleYearly.ts +20 -0
- package/src/API/resource/payment/platforms/apple/plans/index.ts +9 -0
- package/src/API/resource/payment/platforms/google/index.ts +25 -0
- package/src/API/resource/payment/platforms/google/plans/googleMonthly.ts +19 -0
- package/src/API/resource/payment/platforms/google/plans/googleYearly.ts +20 -0
- package/src/API/resource/payment/platforms/google/plans/index.ts +9 -0
- package/src/API/resource/payment/platforms/index.ts +10 -0
- package/src/API/resource/payment/platforms/stripe/index.ts +29 -0
- package/src/API/resource/payment/platforms/stripe/plans/index.ts +12 -0
- package/src/API/resource/payment/platforms/stripe/plans/stripeBusinessMolecule.ts +23 -0
- package/src/API/resource/payment/platforms/stripe/plans/stripeMonthly.ts +20 -0
- package/src/API/resource/payment/platforms/stripe/plans/stripeMonthlyMolecule.ts +20 -0
- package/src/API/resource/payment/platforms/stripe/plans/stripeSingleUseMolecule.ts +21 -0
- package/src/API/resource/payment/platforms/stripe/plans/stripeUnlimitedUseMolecule.ts +21 -0
- package/src/API/resource/payment/platforms/stripe/plans/stripeYearly.ts +21 -0
- package/src/API/resource/payment/types.ts +201 -0
- package/src/API/resource/types.ts +169 -0
- package/src/API/resource/user/index.ts +29 -0
- package/src/API/resource/user/types.ts +131 -0
- package/src/API/resource/user/utilities/getPlan.ts +16 -0
- package/src/API/resource/user/utilities/index.ts +8 -0
- package/src/API/resource/user/utilities/logIn.ts +71 -0
- package/src/commands/add-ssh-key.ts +30 -0
- package/src/commands/buy.ts +83 -0
- package/src/commands/clone.ts +363 -0
- package/src/commands/index.ts +19 -0
- package/src/commands/info.ts +40 -0
- package/src/commands/login.ts +24 -0
- package/src/commands/logout.ts +27 -0
- package/src/commands/remove-ssh-key.ts +42 -0
- package/src/commands/subscribe.ts +84 -0
- package/src/commands/unsubscribe.ts +67 -0
- package/src/commands/utilities/asyncExec.ts +7 -0
- package/src/commands/utilities/branchExists.ts +15 -0
- package/src/commands/utilities/checkoutSelectionBranch.ts +62 -0
- package/src/commands/utilities/cherryPick.ts +23 -0
- package/src/commands/utilities/cloneRepo.ts +10 -0
- package/src/commands/utilities/convertNpmToYarn.ts +71 -0
- package/src/commands/utilities/convertTypeDocToJSDoc.ts +55 -0
- package/src/commands/utilities/convertTypeScriptToJavaScript.ts +526 -0
- package/src/commands/utilities/enableMergeDrivers.ts +55 -0
- package/src/commands/utilities/getCurrentBranch.ts +14 -0
- package/src/commands/utilities/getRepoRootPathSync.ts +13 -0
- package/src/commands/utilities/index.ts +17 -0
- package/src/commands/version.ts +22 -0
- package/src/commands/x-merge-json.ts +41 -0
- package/src/environment.ts +88 -0
- package/src/index.ts +30 -0
- package/src/logger.ts +29 -0
- package/src/pkg.ts +11 -0
- package/src/repoConfig.ts +6 -0
- package/src/types/global.ts +26 -0
- package/src/types/index.ts +53 -0
- package/src/types/process.env.d.ts +56 -0
- package/src/userConfig.ts +6 -0
- package/src/utilities/__tests__/getErrorMessage.ts +32 -0
- package/src/utilities/__tests__/timeAgo.ts +46 -0
- package/src/utilities/checkVersion.ts +31 -0
- package/src/utilities/getErrorMessage.ts +30 -0
- package/src/utilities/index.ts +14 -0
- package/src/utilities/readRepoConfig.ts +22 -0
- package/src/utilities/readUserConfig.ts +24 -0
- package/src/utilities/synchronizeUserConfig.ts +26 -0
- package/src/utilities/timeAgo.ts +39 -0
- package/src/utilities/userLoggedIn.ts +28 -0
- package/src/utilities/writeRepoConfig.ts +11 -0
- package/src/utilities/writeUserConfig.ts +13 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { userConfig } from '../userConfig.js'
|
|
5
|
+
import { userLoggedIn } from '../utilities/userLoggedIn.js'
|
|
6
|
+
import { getPlan } from '../API/resource/molecule/utilities/getPlan.js'
|
|
7
|
+
import { confirmSshKey } from '../API/resource/device/utilities/confirmSshKey.js'
|
|
8
|
+
import * as API from '../API/index.js'
|
|
9
|
+
import { logger } from '../logger.js'
|
|
10
|
+
import { getErrorMessage } from '../utilities/getErrorMessage.js'
|
|
11
|
+
import { cloneRepo } from './utilities/cloneRepo.js'
|
|
12
|
+
import { checkoutSelectionBranch } from './utilities/checkoutSelectionBranch.js'
|
|
13
|
+
import { enableMergeDrivers } from './utilities/enableMergeDrivers.js'
|
|
14
|
+
import { cherryPick } from './utilities/cherryPick.js'
|
|
15
|
+
import { asyncExec } from './utilities/asyncExec.js'
|
|
16
|
+
import { convertTypeDocToJSDoc } from './utilities/convertTypeDocToJSDoc.js'
|
|
17
|
+
import { convertNpmToYarn } from './utilities/convertNpmToYarn.js'
|
|
18
|
+
import { convertTypeScriptToJavaScript } from './utilities/convertTypeScriptToJavaScript.js'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Adds the `clone` command to the program.
|
|
22
|
+
*/
|
|
23
|
+
export const clone = (program: Command) => {
|
|
24
|
+
program.command(`clone`)
|
|
25
|
+
.description(`clones your Molecule into \`project-name-api\` and/or \`project-name-app\``)
|
|
26
|
+
.argument(`<id>`, `your Molecule's ID`)
|
|
27
|
+
.argument(`[projectName]`, `a directory for your Molecule, usually something like \`project-name\``)
|
|
28
|
+
.action(async (id = ``, projectName = ``) => {
|
|
29
|
+
if (!id) {
|
|
30
|
+
logger.warn(`Molecule ID is required.`)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const user = await userLoggedIn()
|
|
35
|
+
|
|
36
|
+
if (!user) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
logger.info(`Fetching your Molecule's clone information...`)
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
props,
|
|
45
|
+
apiCommits,
|
|
46
|
+
apiRepoUrl,
|
|
47
|
+
appCommits,
|
|
48
|
+
appRepoUrl,
|
|
49
|
+
} = (await API.resource.molecule.readClone(id)).data
|
|
50
|
+
|
|
51
|
+
const {
|
|
52
|
+
name,
|
|
53
|
+
apiSelectionHash,
|
|
54
|
+
appSelectionHash,
|
|
55
|
+
} = props
|
|
56
|
+
|
|
57
|
+
if (((!apiSelectionHash || !apiRepoUrl)) && (!appSelectionHash || !appRepoUrl)) {
|
|
58
|
+
logger.warn(`Failed to fetch the necessary information to clone your Molecule. Please contact developer-support@molecule.dev so we can fix this.`)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const moleculePlan = getPlan(props)
|
|
63
|
+
|
|
64
|
+
if (moleculePlan?.capabilities.forMolecules && moleculePlan?.capabilities.premium) {
|
|
65
|
+
const deviceId = userConfig.userIdsToDeviceIds?.[user.id] || ``
|
|
66
|
+
|
|
67
|
+
if (!deviceId) {
|
|
68
|
+
logger.warn(`No device ID for the current user. Try logging out and in again.`)
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const confirmedSshKey = await confirmSshKey(deviceId, user.username)
|
|
73
|
+
|
|
74
|
+
if (!confirmedSshKey) {
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
projectName = projectName || name?.replace(/ /gi, `-`).replace(/[^A-z0-9-]/gi, ``).toLowerCase() || `your`
|
|
80
|
+
|
|
81
|
+
const apiRepoPath = `${projectName}-api`
|
|
82
|
+
const appRepoPath = `${projectName}-app`
|
|
83
|
+
let apiError = false
|
|
84
|
+
let appError = false
|
|
85
|
+
|
|
86
|
+
if (apiSelectionHash && apiRepoUrl) {
|
|
87
|
+
let resetMergeDrivers: null | (() => void) = null
|
|
88
|
+
const writeNewRepoConfig = async () => {
|
|
89
|
+
const repoConfigPath = path.join(apiRepoPath, `.mlclrc.json`)
|
|
90
|
+
|
|
91
|
+
logger.info(`Creating ${apiRepoPath}/.mlclrc.json...`)
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
await fs.promises.access(repoConfigPath, fs.constants.R_OK | fs.constants.W_OK)
|
|
95
|
+
logger.info(`${apiRepoPath}/.mlclrc.json already exists on your machine!`)
|
|
96
|
+
apiError = true
|
|
97
|
+
} catch (error) {
|
|
98
|
+
await fs.promises.writeFile(repoConfigPath, JSON.stringify({ id }, null, 2))
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
await fs.promises.access(apiRepoPath, fs.constants.R_OK | fs.constants.W_OK)
|
|
104
|
+
logger.warn(`Error cloning API: ${apiRepoPath} already exists on your machine!`)
|
|
105
|
+
apiError = true
|
|
106
|
+
} catch (error) {
|
|
107
|
+
try {
|
|
108
|
+
await cloneRepo(apiRepoUrl, apiRepoPath)
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logger.warn(`Error cloning API: ${getErrorMessage(error) || `Unknown error`}`)
|
|
111
|
+
apiError = true
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const createdEmptyBranch = await checkoutSelectionBranch(apiSelectionHash, apiRepoPath, `remotes/origin`)
|
|
117
|
+
|
|
118
|
+
if (!createdEmptyBranch) {
|
|
119
|
+
await writeNewRepoConfig()
|
|
120
|
+
logger.info(`API is ready.`)
|
|
121
|
+
} else if (apiCommits?.length) {
|
|
122
|
+
const convert = {
|
|
123
|
+
typeScriptToJavaScript: props.selection.apiLanguage === `JavaScript`,
|
|
124
|
+
typeDocToJSDoc: props.selection.apiDocs === `JSDoc`,
|
|
125
|
+
npmToYarn: props.selection.apiPackageManager === `Yarn`,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
logger.info(`You are the first to assemble this particular Molecule API!`)
|
|
129
|
+
|
|
130
|
+
resetMergeDrivers = enableMergeDrivers(apiRepoPath)
|
|
131
|
+
|
|
132
|
+
logger.info(`Cherry picking the relevant commits...`)
|
|
133
|
+
|
|
134
|
+
while (apiCommits.length) {
|
|
135
|
+
const commit = apiCommits.shift()
|
|
136
|
+
|
|
137
|
+
if (commit?.hash) {
|
|
138
|
+
logger.info(`Cherry picking ${commit.hash.substring(0, 7)} (${commit.subject || `Unknown commit??? This should never happen!!!`})`)
|
|
139
|
+
await cherryPick(commit.hash, apiRepoPath, `-x`)
|
|
140
|
+
} else {
|
|
141
|
+
logger.info(`No commit to cherry pick??? This should never happen!!!`)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
resetMergeDrivers()
|
|
146
|
+
|
|
147
|
+
if (convert.typeDocToJSDoc || convert.npmToYarn || convert.typeScriptToJavaScript) {
|
|
148
|
+
// Delete package-lock.json for any of these.
|
|
149
|
+
try {
|
|
150
|
+
const packageLockPath = path.join(apiRepoPath, `package-lock.json`)
|
|
151
|
+
await fs.promises.unlink(packageLockPath)
|
|
152
|
+
} catch (error) {
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (convert.typeDocToJSDoc) {
|
|
157
|
+
try {
|
|
158
|
+
logger.info(`Converting TypeDoc to JSDoc...`)
|
|
159
|
+
await convertTypeDocToJSDoc(apiRepoPath)
|
|
160
|
+
await asyncExec(`git -C "${apiRepoPath}" add --all`)
|
|
161
|
+
await asyncExec(`git -C "${apiRepoPath}" commit -m "convert typedoc to jsdoc"`)
|
|
162
|
+
} catch (error) {
|
|
163
|
+
logger.warn(`Error: ${getErrorMessage(error)}`)
|
|
164
|
+
throw error
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (convert.npmToYarn) {
|
|
169
|
+
try {
|
|
170
|
+
logger.info(`Converting NPM to Yarn...`)
|
|
171
|
+
await convertNpmToYarn(apiRepoPath)
|
|
172
|
+
await asyncExec(`git -C "${apiRepoPath}" add --all`)
|
|
173
|
+
await asyncExec(`git -C "${apiRepoPath}" commit -m "convert npm to yarn"`)
|
|
174
|
+
} catch (error) {
|
|
175
|
+
logger.warn(`Error: ${getErrorMessage(error)}`)
|
|
176
|
+
throw error
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (convert.typeScriptToJavaScript) {
|
|
181
|
+
try {
|
|
182
|
+
logger.info(`Converting TypeScript to JavaScript...`)
|
|
183
|
+
await convertTypeScriptToJavaScript(apiRepoPath)
|
|
184
|
+
await asyncExec(`git -C "${apiRepoPath}" add --all`)
|
|
185
|
+
await asyncExec(`git -C "${apiRepoPath}" commit -m "convert typescript to javascript"`)
|
|
186
|
+
} catch (error) {
|
|
187
|
+
logger.warn(`Error: ${getErrorMessage(error)}`)
|
|
188
|
+
throw error
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
await writeNewRepoConfig()
|
|
193
|
+
logger.info(`API is ready.`)
|
|
194
|
+
} else {
|
|
195
|
+
logger.warn(`Created an empty branch but no commits to cherry pick.`)
|
|
196
|
+
apiError = true
|
|
197
|
+
}
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (resetMergeDrivers) {
|
|
200
|
+
resetMergeDrivers()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
logger.warn(`Error assembling API: ${getErrorMessage(error) || `Unknown error`}`)
|
|
204
|
+
apiError = true
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (appSelectionHash && appRepoUrl) {
|
|
209
|
+
let resetMergeDrivers: null | (() => void) = null
|
|
210
|
+
const writeNewRepoConfig = async () => {
|
|
211
|
+
const repoConfigPath = path.join(appRepoPath, `.mlclrc.json`)
|
|
212
|
+
|
|
213
|
+
logger.info(`Creating ${appRepoPath}/.mlclrc.json...`)
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
await fs.promises.access(repoConfigPath, fs.constants.R_OK | fs.constants.W_OK)
|
|
217
|
+
logger.info(`${appRepoPath}/.mlclrc.json already exists on your machine!`)
|
|
218
|
+
appError = true
|
|
219
|
+
} catch (error) {
|
|
220
|
+
await fs.promises.writeFile(repoConfigPath, JSON.stringify({ id }, null, 2))
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
await fs.promises.access(appRepoPath, fs.constants.R_OK | fs.constants.W_OK)
|
|
226
|
+
logger.warn(`Error cloning app: ${appRepoPath} already exists on your machine!`)
|
|
227
|
+
appError = true
|
|
228
|
+
} catch (error) {
|
|
229
|
+
try {
|
|
230
|
+
await cloneRepo(appRepoUrl, appRepoPath)
|
|
231
|
+
} catch (error) {
|
|
232
|
+
logger.warn(`Error cloning app: ${getErrorMessage(error) || `Unknown error`}`)
|
|
233
|
+
appError = true
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const createdEmptyBranch = await checkoutSelectionBranch(appSelectionHash, appRepoPath, `remotes/origin`)
|
|
239
|
+
|
|
240
|
+
if (!createdEmptyBranch) {
|
|
241
|
+
await writeNewRepoConfig()
|
|
242
|
+
logger.info(`App is ready.`)
|
|
243
|
+
} else if (appCommits?.length) {
|
|
244
|
+
const convert = {
|
|
245
|
+
typeScriptToJavaScript: props.selection.appLanguage === `JavaScript`,
|
|
246
|
+
typeDocToJSDoc: props.selection.appDocs === `JSDoc`,
|
|
247
|
+
npmToYarn: props.selection.appPackageManager === `Yarn`,
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
logger.info(`You are the first to assemble this particular Molecule app!`)
|
|
251
|
+
|
|
252
|
+
resetMergeDrivers = enableMergeDrivers(appRepoPath)
|
|
253
|
+
|
|
254
|
+
logger.info(`Cherry picking the relevant commits...`)
|
|
255
|
+
|
|
256
|
+
while (appCommits.length) {
|
|
257
|
+
const commit = appCommits.shift()
|
|
258
|
+
|
|
259
|
+
if (commit?.hash) {
|
|
260
|
+
logger.info(`Cherry picking ${commit.hash.substring(0, 7)} (${commit.subject || `Unknown commit??? This should never happen!!!`})`)
|
|
261
|
+
await cherryPick(commit.hash, appRepoPath, `-x`)
|
|
262
|
+
} else {
|
|
263
|
+
logger.info(`No commit to cherry pick??? This should never happen!!!`)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
resetMergeDrivers()
|
|
268
|
+
|
|
269
|
+
if (convert.typeDocToJSDoc || convert.npmToYarn || convert.typeScriptToJavaScript) {
|
|
270
|
+
// Delete package-lock.json for any of these.
|
|
271
|
+
try {
|
|
272
|
+
const packageLockPath = path.join(appRepoPath, `package-lock.json`)
|
|
273
|
+
await fs.promises.unlink(packageLockPath)
|
|
274
|
+
} catch (error) {
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (convert.typeDocToJSDoc) {
|
|
279
|
+
try {
|
|
280
|
+
logger.info(`Converting TypeDoc to JSDoc...`)
|
|
281
|
+
await convertTypeDocToJSDoc(appRepoPath)
|
|
282
|
+
await asyncExec(`git -C "${appRepoPath}" add --all`)
|
|
283
|
+
await asyncExec(`git -C "${appRepoPath}" commit -m "convert typedoc to jsdoc"`)
|
|
284
|
+
} catch (error) {
|
|
285
|
+
logger.warn(`Error: ${getErrorMessage(error)}`)
|
|
286
|
+
throw error
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (convert.npmToYarn) {
|
|
291
|
+
try {
|
|
292
|
+
logger.info(`Converting NPM to Yarn...`)
|
|
293
|
+
await convertNpmToYarn(appRepoPath)
|
|
294
|
+
await asyncExec(`git -C "${appRepoPath}" add --all`)
|
|
295
|
+
await asyncExec(`git -C "${appRepoPath}" commit -m "convert npm to yarn"`)
|
|
296
|
+
} catch (error) {
|
|
297
|
+
logger.warn(`Error: ${getErrorMessage(error)}`)
|
|
298
|
+
throw error
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (convert.typeScriptToJavaScript) {
|
|
303
|
+
try {
|
|
304
|
+
logger.info(`Converting TypeScript to JavaScript...`)
|
|
305
|
+
await convertTypeScriptToJavaScript(appRepoPath)
|
|
306
|
+
await asyncExec(`git -C "${appRepoPath}" add --all`)
|
|
307
|
+
await asyncExec(`git -C "${appRepoPath}" commit -m "convert typescript to javascript"`)
|
|
308
|
+
} catch (error) {
|
|
309
|
+
logger.warn(`Error: ${getErrorMessage(error)}`)
|
|
310
|
+
throw error
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
await writeNewRepoConfig()
|
|
315
|
+
logger.info(`App is ready.`)
|
|
316
|
+
} else {
|
|
317
|
+
logger.warn(`Created an empty branch but no commits to cherry pick.`)
|
|
318
|
+
appError = true
|
|
319
|
+
}
|
|
320
|
+
} catch (error) {
|
|
321
|
+
if (resetMergeDrivers) {
|
|
322
|
+
resetMergeDrivers()
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
logger.warn(`Error assembling app: ${getErrorMessage(error) || `Unknown error`}`)
|
|
326
|
+
appError = true
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (apiSelectionHash && appSelectionHash) {
|
|
331
|
+
if (apiError || appError) {
|
|
332
|
+
logger.info(`There may have been an error cloning your Molecule.`)
|
|
333
|
+
logger.info(`If everything seems good, see \`${apiRepoPath}/README.md\` and \`${appRepoPath}/README.md\` to get started.`)
|
|
334
|
+
logger.info(`If not, please contact developer-support@molecule.dev so we can fix this.`)
|
|
335
|
+
} else {
|
|
336
|
+
logger.info(`Your Molecule was successfully cloned!`)
|
|
337
|
+
logger.info(`See \`${apiRepoPath}/README.md\` and \`${appRepoPath}/README.md\` to get started.`)
|
|
338
|
+
}
|
|
339
|
+
} else if (apiSelectionHash) {
|
|
340
|
+
if (apiError) {
|
|
341
|
+
logger.info(`There may have been an error cloning your Molecule API.`)
|
|
342
|
+
logger.info(`If everything seems good, see \`${apiRepoPath}/README.md\` to get started.`)
|
|
343
|
+
logger.info(`If not, please contact developer-support@molecule.dev so we can fix this.`)
|
|
344
|
+
} else {
|
|
345
|
+
logger.info(`Your Molecule API was successfully cloned!`)
|
|
346
|
+
logger.info(`See \`${apiRepoPath}/README.md\` to get started.`)
|
|
347
|
+
}
|
|
348
|
+
} else if (appSelectionHash) {
|
|
349
|
+
if (appError) {
|
|
350
|
+
logger.info(`There may have been an error cloning your Molecule app.`)
|
|
351
|
+
logger.info(`If everything seems good, see \`${appRepoPath}/README.md\` to get started.`)
|
|
352
|
+
logger.info(`If not, please contact developer-support@molecule.dev so we can fix this.`)
|
|
353
|
+
} else {
|
|
354
|
+
logger.info(`Your Molecule app was successfully cloned!`)
|
|
355
|
+
logger.info(`See \`${appRepoPath}/README.md\` to get started.`)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
} catch (error) {
|
|
359
|
+
logger.warn(`Error cloning Molecule: ${getErrorMessage(error) || `Unknown error`}`)
|
|
360
|
+
logger.warn(`Please contact developer-support@molecule.dev so we can fix this.`)
|
|
361
|
+
}
|
|
362
|
+
})
|
|
363
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every available command and utilities.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export * as utilities from './utilities/index.js'
|
|
8
|
+
|
|
9
|
+
export * from './add-ssh-key.js'
|
|
10
|
+
export * from './buy.js'
|
|
11
|
+
export * from './clone.js'
|
|
12
|
+
export * from './info.js'
|
|
13
|
+
export * from './login.js'
|
|
14
|
+
export * from './logout.js'
|
|
15
|
+
export * from './remove-ssh-key.js'
|
|
16
|
+
export * from './subscribe.js'
|
|
17
|
+
export * from './unsubscribe.js'
|
|
18
|
+
export * from './version.js'
|
|
19
|
+
export * from './x-merge-json.js'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import open from 'open'
|
|
2
|
+
import { Command } from 'commander'
|
|
3
|
+
import { userLoggedIn } from '../utilities/userLoggedIn.js'
|
|
4
|
+
import { repoConfig } from '../repoConfig.js'
|
|
5
|
+
import { logger } from '../logger.js'
|
|
6
|
+
import { getErrorMessage } from '../utilities/getErrorMessage.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adds the `info` command to the program.
|
|
10
|
+
*/
|
|
11
|
+
export const info = (program: Command) => {
|
|
12
|
+
program.command(`info`)
|
|
13
|
+
.description(`opens your Molecule's information on Molecule.dev`)
|
|
14
|
+
.action(async () => {
|
|
15
|
+
const user = await userLoggedIn()
|
|
16
|
+
|
|
17
|
+
if (!user) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!repoConfig?.id) {
|
|
22
|
+
logger.warn(`Not within a Molecule repository or \`.mlclrc.json\` not found.`)
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const url = `${process.env.WEB_ORIGIN}/#/${encodeURIComponent(repoConfig.id)}`
|
|
27
|
+
const childProcess = await open(url)
|
|
28
|
+
|
|
29
|
+
childProcess.on(`error`, error => {
|
|
30
|
+
logger.warn(`Error opening the URL: ${getErrorMessage(error)}`)
|
|
31
|
+
logger.warn(`If your browser didn't open on its own, visit the following URL: ${url}`)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
childProcess.on(`close`, code => {
|
|
35
|
+
if (code !== 0) {
|
|
36
|
+
logger.warn(`If your browser didn't open on its own, visit the following URL: ${url}`)
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { userConfig } from '../userConfig.js'
|
|
3
|
+
import { synchronizeUserConfig } from '../utilities/synchronizeUserConfig.js'
|
|
4
|
+
import * as API from '../API/index.js'
|
|
5
|
+
import { logger } from '../logger.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Adds the `login` command to the program.
|
|
9
|
+
*/
|
|
10
|
+
export const login = (program: Command) => {
|
|
11
|
+
program.command(`login`)
|
|
12
|
+
.description(`log into mlcl with your Molecule.dev account via the browser`)
|
|
13
|
+
.action(async () => {
|
|
14
|
+
await synchronizeUserConfig()
|
|
15
|
+
|
|
16
|
+
if (userConfig.user) {
|
|
17
|
+
logger.info(`Logged in as ${userConfig.user.username}.`)
|
|
18
|
+
logger.info(`If you would like to log in as another user, run \`mlcl logout\` first.`)
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
await API.resource.user.utilities.logIn()
|
|
23
|
+
})
|
|
24
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { userConfig } from '../userConfig.js'
|
|
3
|
+
import { synchronizeUserConfig } from '../utilities/synchronizeUserConfig.js'
|
|
4
|
+
import { writeUserConfig } from '../utilities/writeUserConfig.js'
|
|
5
|
+
import * as API from '../API/index.js'
|
|
6
|
+
import { logger } from '../logger.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adds the `logout` command to the program.
|
|
10
|
+
*/
|
|
11
|
+
export const logout = (program: Command) => {
|
|
12
|
+
program.command(`logout`)
|
|
13
|
+
.description(`logs out of your Molecule.dev account`)
|
|
14
|
+
.action(async () => {
|
|
15
|
+
await synchronizeUserConfig()
|
|
16
|
+
|
|
17
|
+
if (!userConfig?.user?.id) {
|
|
18
|
+
logger.warn(`Not logged in.`)
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
API.resource.user.logOut()
|
|
23
|
+
delete userConfig.user
|
|
24
|
+
writeUserConfig()
|
|
25
|
+
logger.info(`Logged out.`)
|
|
26
|
+
})
|
|
27
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { userConfig } from '../userConfig.js'
|
|
3
|
+
import { userLoggedIn } from '../utilities/userLoggedIn.js'
|
|
4
|
+
import * as API from '../API/index.js'
|
|
5
|
+
import { logger } from '../logger.js'
|
|
6
|
+
import { getErrorMessage } from '../utilities/getErrorMessage.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adds the `remove-ssh-key` command to the program.
|
|
10
|
+
*/
|
|
11
|
+
export const removeSshKey = (program: Command) => {
|
|
12
|
+
program.command(`remove-ssh-key`)
|
|
13
|
+
.description(`removes your current CLI device's SSH key from your Molecule.dev account`)
|
|
14
|
+
.action(async () => {
|
|
15
|
+
const user = await userLoggedIn()
|
|
16
|
+
|
|
17
|
+
if (!user) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const deviceId = userConfig.userIdsToDeviceIds?.[user.id] || ``
|
|
22
|
+
|
|
23
|
+
if (!deviceId) {
|
|
24
|
+
logger.warn(`No device ID for the current user. Try logging out and in again.`)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
logger.info(`Removing public SSH key from the API...`)
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const { props } = (await API.resource.device.update(deviceId, { sshKey: `` })).data
|
|
32
|
+
|
|
33
|
+
if (props.sshKey === ``) {
|
|
34
|
+
logger.info(`SSH key successfully removed!`)
|
|
35
|
+
} else {
|
|
36
|
+
logger.info(`Failed to remove SSH key.`)
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
logger.warn(`Failed to remove SSH key. ${getErrorMessage(error)}`)
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import open from 'open'
|
|
2
|
+
import { Command } from 'commander'
|
|
3
|
+
import { userLoggedIn } from '../utilities/userLoggedIn.js'
|
|
4
|
+
import { repoConfig } from '../repoConfig.js'
|
|
5
|
+
import * as API from '../API/index.js'
|
|
6
|
+
import { logger } from '../logger.js'
|
|
7
|
+
import { getErrorMessage } from '../utilities/getErrorMessage.js'
|
|
8
|
+
import { plans } from '../API/resource/payment/plans.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Adds the `subscribe` command to the program.
|
|
12
|
+
*/
|
|
13
|
+
export const subscribe = (program: Command) => {
|
|
14
|
+
program.command(`subscribe`)
|
|
15
|
+
.description(`enables or renews your Molecule's subscription`)
|
|
16
|
+
.action(async () => {
|
|
17
|
+
const user = await userLoggedIn()
|
|
18
|
+
|
|
19
|
+
if (!user) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!repoConfig?.id) {
|
|
24
|
+
logger.warn(`Not within a Molecule repository or \`.mlclrc.json\` not found.`)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const molecule = (await API.resource.molecule.read(repoConfig.id)).data.props
|
|
30
|
+
|
|
31
|
+
if (!molecule) {
|
|
32
|
+
throw new Error(`Failed to fetch Molecule by ID ${repoConfig.id}.`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const planKey = molecule.planKey || ``
|
|
36
|
+
const planAutoRenews = molecule.planAutoRenews
|
|
37
|
+
const planExpiresAt = molecule.planExpiresAt
|
|
38
|
+
const planExpiresDate = planKey
|
|
39
|
+
? (planAutoRenews || plans[planKey].autoRenews)
|
|
40
|
+
? planExpiresAt && new Date(planExpiresAt)
|
|
41
|
+
: null
|
|
42
|
+
: undefined
|
|
43
|
+
const planExpiresLocaleString = (planExpiresDate && planExpiresDate.toLocaleString()) || `never`
|
|
44
|
+
|
|
45
|
+
if (planKey && (planAutoRenews || !plans[planKey].autoRenews)) {
|
|
46
|
+
if (planAutoRenews) {
|
|
47
|
+
logger.info(`Your Molecule is already subscribed!`)
|
|
48
|
+
logger.info(`Your ${plans[planKey].title} plan will ${planAutoRenews ? `automatically renew` : `expire`} ${planExpiresLocaleString}.`)
|
|
49
|
+
} else {
|
|
50
|
+
logger.info(`You already have a ${plans[planKey].title} license, so no subscription is necessary.`)
|
|
51
|
+
logger.info(`Thank you!`)
|
|
52
|
+
}
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
logger.info(`Subscribing...`)
|
|
57
|
+
|
|
58
|
+
const { props, checkoutUrl } = (await API.resource.molecule.updatePlan(molecule.id, { planKey: `stripeMonthlyMolecule`, quantity: 1 })).data
|
|
59
|
+
|
|
60
|
+
if (checkoutUrl) {
|
|
61
|
+
logger.info(`Opening your browser to complete checkout via Stripe...`)
|
|
62
|
+
|
|
63
|
+
const childProcess = await open(checkoutUrl)
|
|
64
|
+
|
|
65
|
+
childProcess.on(`error`, error => {
|
|
66
|
+
logger.warn(`Error opening the checkout URL: ${getErrorMessage(error)}`)
|
|
67
|
+
logger.warn(`If your browser didn't open on its own, visit the following URL: ${checkoutUrl}`)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
childProcess.on(`close`, code => {
|
|
71
|
+
if (code !== 0) {
|
|
72
|
+
logger.warn(`If your browser didn't open on its own, visit the following URL: ${checkoutUrl}`)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
} else if (props?.planKey) {
|
|
76
|
+
logger.info(`Your Molecule is now subscribed! Thank you!`)
|
|
77
|
+
} else {
|
|
78
|
+
throw new Error(`Unexpected response. Please contact developer-support@molecule.dev so we can fix this.`)
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logger.warn(`Failed to subscribe. ${getErrorMessage(error)}`)
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { userLoggedIn } from '../utilities/userLoggedIn.js'
|
|
3
|
+
import { repoConfig } from '../repoConfig.js'
|
|
4
|
+
import * as API from '../API/index.js'
|
|
5
|
+
import { logger } from '../logger.js'
|
|
6
|
+
import { getErrorMessage } from '../utilities/getErrorMessage.js'
|
|
7
|
+
import { plans } from '../API/resource/payment/plans.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Adds the `unsubscribe` command to the program.
|
|
11
|
+
*/
|
|
12
|
+
export const unsubscribe = (program: Command) => {
|
|
13
|
+
program.command(`unsubscribe`)
|
|
14
|
+
.description(`cancels your Molecule's subscription`)
|
|
15
|
+
.action(async () => {
|
|
16
|
+
const user = await userLoggedIn()
|
|
17
|
+
|
|
18
|
+
if (!user) {
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!repoConfig?.id) {
|
|
23
|
+
logger.warn(`Not within a Molecule repository or \`.mlclrc.json\` not found.`)
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const molecule = (await API.resource.molecule.read(repoConfig.id)).data.props
|
|
29
|
+
|
|
30
|
+
if (!molecule) {
|
|
31
|
+
throw new Error(`Failed to fetch Molecule by ID ${repoConfig.id}.`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const planKey = molecule.planKey || ``
|
|
35
|
+
const planAutoRenews = molecule.planAutoRenews
|
|
36
|
+
const planExpiresAt = molecule.planExpiresAt
|
|
37
|
+
const planExpiresDate = planKey
|
|
38
|
+
? (planAutoRenews || plans[planKey].autoRenews)
|
|
39
|
+
? planExpiresAt && new Date(planExpiresAt)
|
|
40
|
+
: null
|
|
41
|
+
: undefined
|
|
42
|
+
const planExpiresLocaleString = (planExpiresDate && planExpiresDate.toLocaleString()) || `never`
|
|
43
|
+
|
|
44
|
+
if (!planAutoRenews) {
|
|
45
|
+
if (planKey && !plans[planKey].autoRenews) {
|
|
46
|
+
logger.info(`You already have a ${plans[planKey].title} license, so there is nothing to unsubscribe.`)
|
|
47
|
+
logger.info(`Thank you!`)
|
|
48
|
+
} else {
|
|
49
|
+
logger.info(`Your Molecule is not subscribed.`)
|
|
50
|
+
}
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
logger.info(`Unsubscribing...`)
|
|
55
|
+
|
|
56
|
+
const { props } = (await API.resource.molecule.updatePlan(molecule.id, { planKey: `` })).data
|
|
57
|
+
|
|
58
|
+
if (props?.planAutoRenews === false) {
|
|
59
|
+
logger.info(`Your Molecule has been unsubscribed and will expire ${planExpiresLocaleString}.`)
|
|
60
|
+
} else {
|
|
61
|
+
throw new Error(`Unexpected response. Please contact developer-support@molecule.dev so we can fix this.`)
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
logger.warn(`Failed to unsubscribe. ${getErrorMessage(error)}`)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|