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.
Files changed (195) hide show
  1. package/.eslintignore +31 -0
  2. package/.eslintrc.json +54 -0
  3. package/LICENSE +200 -0
  4. package/README.md +101 -0
  5. package/babel.config.json +10 -0
  6. package/build/API/authorization.js +107 -0
  7. package/build/API/client.js +11 -0
  8. package/build/API/index.js +8 -0
  9. package/build/API/molecule/index.js +11 -0
  10. package/build/API/molecule/types.js +6 -0
  11. package/build/API/resource/device/index.js +12 -0
  12. package/build/API/resource/device/types.js +6 -0
  13. package/build/API/resource/device/utilities/confirmSshKey.js +20 -0
  14. package/build/API/resource/device/utilities/index.js +7 -0
  15. package/build/API/resource/device/utilities/sendSshKey.js +64 -0
  16. package/build/API/resource/index.js +10 -0
  17. package/build/API/resource/molecule/index.js +12 -0
  18. package/build/API/resource/molecule/types.js +6 -0
  19. package/build/API/resource/molecule/utilities/getPlan.js +15 -0
  20. package/build/API/resource/molecule/utilities/index.js +6 -0
  21. package/build/API/resource/payment/index.js +9 -0
  22. package/build/API/resource/payment/plans.js +28 -0
  23. package/build/API/resource/payment/platform.js +7 -0
  24. package/build/API/resource/payment/platforms/apple/index.js +21 -0
  25. package/build/API/resource/payment/platforms/apple/plans/appleMonthly.js +17 -0
  26. package/build/API/resource/payment/platforms/apple/plans/appleYearly.js +18 -0
  27. package/build/API/resource/payment/platforms/apple/plans/index.js +7 -0
  28. package/build/API/resource/payment/platforms/google/index.js +21 -0
  29. package/build/API/resource/payment/platforms/google/plans/googleMonthly.js +17 -0
  30. package/build/API/resource/payment/platforms/google/plans/googleYearly.js +18 -0
  31. package/build/API/resource/payment/platforms/google/plans/index.js +7 -0
  32. package/build/API/resource/payment/platforms/index.js +8 -0
  33. package/build/API/resource/payment/platforms/stripe/index.js +25 -0
  34. package/build/API/resource/payment/platforms/stripe/plans/index.js +11 -0
  35. package/build/API/resource/payment/platforms/stripe/plans/stripeBusinessMolecule.js +21 -0
  36. package/build/API/resource/payment/platforms/stripe/plans/stripeMonthly.js +18 -0
  37. package/build/API/resource/payment/platforms/stripe/plans/stripeMonthlyMolecule.js +18 -0
  38. package/build/API/resource/payment/platforms/stripe/plans/stripeSingleUseMolecule.js +19 -0
  39. package/build/API/resource/payment/platforms/stripe/plans/stripeUnlimitedUseMolecule.js +19 -0
  40. package/build/API/resource/payment/platforms/stripe/plans/stripeYearly.js +19 -0
  41. package/build/API/resource/payment/types.js +6 -0
  42. package/build/API/resource/types.js +6 -0
  43. package/build/API/resource/user/index.js +15 -0
  44. package/build/API/resource/user/types.js +6 -0
  45. package/build/API/resource/user/utilities/getPlan.js +12 -0
  46. package/build/API/resource/user/utilities/index.js +7 -0
  47. package/build/API/resource/user/utilities/logIn.js +60 -0
  48. package/build/cli.js +15 -0
  49. package/build/commands/add-ssh-key.js +24 -0
  50. package/build/commands/buy.js +70 -0
  51. package/build/commands/clone.js +329 -0
  52. package/build/commands/index.js +17 -0
  53. package/build/commands/info.js +33 -0
  54. package/build/commands/login.js +20 -0
  55. package/build/commands/logout.js +23 -0
  56. package/build/commands/molecule/index.js +11 -0
  57. package/build/commands/molecule/types.js +6 -0
  58. package/build/commands/remove-ssh-key.js +36 -0
  59. package/build/commands/select.js +98 -0
  60. package/build/commands/set-api-key.js +73 -0
  61. package/build/commands/subscribe.js +74 -0
  62. package/build/commands/unsubscribe.js +59 -0
  63. package/build/commands/utilities/asyncExec.js +6 -0
  64. package/build/commands/utilities/branchExists.js +13 -0
  65. package/build/commands/utilities/branchExistsSync.js +12 -0
  66. package/build/commands/utilities/checkoutSelectionBranch.js +49 -0
  67. package/build/commands/utilities/cherryPick.js +20 -0
  68. package/build/commands/utilities/cloneRepo.js +9 -0
  69. package/build/commands/utilities/convertNpmToYarn.js +60 -0
  70. package/build/commands/utilities/convertTypeDocToJSDoc.js +51 -0
  71. package/build/commands/utilities/convertTypeScriptToJavaScript.js +451 -0
  72. package/build/commands/utilities/enableMergeDrivers.js +50 -0
  73. package/build/commands/utilities/getCurrentBranch.js +11 -0
  74. package/build/commands/utilities/getCurrentBranchSync.js +7 -0
  75. package/build/commands/utilities/getRepoRootPath.js +7 -0
  76. package/build/commands/utilities/getRepoRootPathSync.js +13 -0
  77. package/build/commands/utilities/index.js +16 -0
  78. package/build/commands/utilities/npm-to-yarn.js +41 -0
  79. package/build/commands/utilities/ts-to-js.js +344 -0
  80. package/build/commands/version.js +14 -0
  81. package/build/commands/x-merge-json.js +33 -0
  82. package/build/config.js +5 -0
  83. package/build/environment.js +80 -0
  84. package/build/index.js +23 -0
  85. package/build/logger.js +12 -0
  86. package/build/pkg.js +5 -0
  87. package/build/repoConfig copy.js +5 -0
  88. package/build/repoConfig.js +5 -0
  89. package/build/types/global.js +6 -0
  90. package/build/types/index.js +6 -0
  91. package/build/userConfig.js +5 -0
  92. package/build/utilities/__tests__/getErrorMessage.js +26 -0
  93. package/build/utilities/__tests__/timeAgo.js +39 -0
  94. package/build/utilities/checkVersion.js +29 -0
  95. package/build/utilities/getErrorMessage.js +15 -0
  96. package/build/utilities/index.js +13 -0
  97. package/build/utilities/readConfig.js +19 -0
  98. package/build/utilities/readRepoConfig copy.js +20 -0
  99. package/build/utilities/readRepoConfig.js +19 -0
  100. package/build/utilities/readUserConfig.js +20 -0
  101. package/build/utilities/synchronizeUserConfig.js +25 -0
  102. package/build/utilities/timeAgo.js +42 -0
  103. package/build/utilities/userLoggedIn.js +24 -0
  104. package/build/utilities/writeConfig.js +8 -0
  105. package/build/utilities/writeRepoConfig copy.js +9 -0
  106. package/build/utilities/writeRepoConfig.js +10 -0
  107. package/build/utilities/writeUserConfig.js +11 -0
  108. package/jest.config.json +11 -0
  109. package/package.json +56 -0
  110. package/src/API/authorization.ts +143 -0
  111. package/src/API/client.ts +12 -0
  112. package/src/API/index.ts +10 -0
  113. package/src/API/resource/device/index.ts +27 -0
  114. package/src/API/resource/device/types.ts +80 -0
  115. package/src/API/resource/device/utilities/confirmSshKey.ts +25 -0
  116. package/src/API/resource/device/utilities/index.ts +8 -0
  117. package/src/API/resource/device/utilities/sendSshKey.ts +80 -0
  118. package/src/API/resource/index.ts +12 -0
  119. package/src/API/resource/molecule/index.ts +30 -0
  120. package/src/API/resource/molecule/types.ts +332 -0
  121. package/src/API/resource/molecule/utilities/getPlan.ts +21 -0
  122. package/src/API/resource/molecule/utilities/index.ts +7 -0
  123. package/src/API/resource/payment/index.ts +12 -0
  124. package/src/API/resource/payment/plans.ts +32 -0
  125. package/src/API/resource/payment/platform.ts +11 -0
  126. package/src/API/resource/payment/platforms/apple/index.ts +25 -0
  127. package/src/API/resource/payment/platforms/apple/plans/appleMonthly.ts +19 -0
  128. package/src/API/resource/payment/platforms/apple/plans/appleYearly.ts +20 -0
  129. package/src/API/resource/payment/platforms/apple/plans/index.ts +9 -0
  130. package/src/API/resource/payment/platforms/google/index.ts +25 -0
  131. package/src/API/resource/payment/platforms/google/plans/googleMonthly.ts +19 -0
  132. package/src/API/resource/payment/platforms/google/plans/googleYearly.ts +20 -0
  133. package/src/API/resource/payment/platforms/google/plans/index.ts +9 -0
  134. package/src/API/resource/payment/platforms/index.ts +10 -0
  135. package/src/API/resource/payment/platforms/stripe/index.ts +29 -0
  136. package/src/API/resource/payment/platforms/stripe/plans/index.ts +12 -0
  137. package/src/API/resource/payment/platforms/stripe/plans/stripeBusinessMolecule.ts +23 -0
  138. package/src/API/resource/payment/platforms/stripe/plans/stripeMonthly.ts +20 -0
  139. package/src/API/resource/payment/platforms/stripe/plans/stripeMonthlyMolecule.ts +20 -0
  140. package/src/API/resource/payment/platforms/stripe/plans/stripeSingleUseMolecule.ts +21 -0
  141. package/src/API/resource/payment/platforms/stripe/plans/stripeUnlimitedUseMolecule.ts +21 -0
  142. package/src/API/resource/payment/platforms/stripe/plans/stripeYearly.ts +21 -0
  143. package/src/API/resource/payment/types.ts +201 -0
  144. package/src/API/resource/types.ts +169 -0
  145. package/src/API/resource/user/index.ts +29 -0
  146. package/src/API/resource/user/types.ts +131 -0
  147. package/src/API/resource/user/utilities/getPlan.ts +16 -0
  148. package/src/API/resource/user/utilities/index.ts +8 -0
  149. package/src/API/resource/user/utilities/logIn.ts +71 -0
  150. package/src/commands/add-ssh-key.ts +30 -0
  151. package/src/commands/buy.ts +83 -0
  152. package/src/commands/clone.ts +363 -0
  153. package/src/commands/index.ts +19 -0
  154. package/src/commands/info.ts +40 -0
  155. package/src/commands/login.ts +24 -0
  156. package/src/commands/logout.ts +27 -0
  157. package/src/commands/remove-ssh-key.ts +42 -0
  158. package/src/commands/subscribe.ts +84 -0
  159. package/src/commands/unsubscribe.ts +67 -0
  160. package/src/commands/utilities/asyncExec.ts +7 -0
  161. package/src/commands/utilities/branchExists.ts +15 -0
  162. package/src/commands/utilities/checkoutSelectionBranch.ts +62 -0
  163. package/src/commands/utilities/cherryPick.ts +23 -0
  164. package/src/commands/utilities/cloneRepo.ts +10 -0
  165. package/src/commands/utilities/convertNpmToYarn.ts +71 -0
  166. package/src/commands/utilities/convertTypeDocToJSDoc.ts +55 -0
  167. package/src/commands/utilities/convertTypeScriptToJavaScript.ts +526 -0
  168. package/src/commands/utilities/enableMergeDrivers.ts +55 -0
  169. package/src/commands/utilities/getCurrentBranch.ts +14 -0
  170. package/src/commands/utilities/getRepoRootPathSync.ts +13 -0
  171. package/src/commands/utilities/index.ts +17 -0
  172. package/src/commands/version.ts +22 -0
  173. package/src/commands/x-merge-json.ts +41 -0
  174. package/src/environment.ts +88 -0
  175. package/src/index.ts +30 -0
  176. package/src/logger.ts +29 -0
  177. package/src/pkg.ts +11 -0
  178. package/src/repoConfig.ts +6 -0
  179. package/src/types/global.ts +26 -0
  180. package/src/types/index.ts +53 -0
  181. package/src/types/process.env.d.ts +56 -0
  182. package/src/userConfig.ts +6 -0
  183. package/src/utilities/__tests__/getErrorMessage.ts +32 -0
  184. package/src/utilities/__tests__/timeAgo.ts +46 -0
  185. package/src/utilities/checkVersion.ts +31 -0
  186. package/src/utilities/getErrorMessage.ts +30 -0
  187. package/src/utilities/index.ts +14 -0
  188. package/src/utilities/readRepoConfig.ts +22 -0
  189. package/src/utilities/readUserConfig.ts +24 -0
  190. package/src/utilities/synchronizeUserConfig.ts +26 -0
  191. package/src/utilities/timeAgo.ts +39 -0
  192. package/src/utilities/userLoggedIn.ts +28 -0
  193. package/src/utilities/writeRepoConfig.ts +11 -0
  194. package/src/utilities/writeUserConfig.ts +13 -0
  195. package/tsconfig.json +25 -0
@@ -0,0 +1,88 @@
1
+ /**
2
+ * This module helps with various sets of environment variables,
3
+ * using both `dotenv` and `env-cmd`.
4
+ *
5
+ * The `.env` file will be loaded via `dotenv.config()` and extended
6
+ * by the variables for the `NODE_ENV` key specified in `.env-cmdrc.json`.
7
+ *
8
+ * This also allows you to combine various sets of env vars using e.g.:
9
+ * ```
10
+ * ./node_modules/.bin/env-cmd -e development,something,etc npm run dev
11
+ * ```
12
+ *
13
+ * @see https://www.npmjs.com/package/dotenv
14
+ * @see https://www.npmjs.com/package/env-cmd
15
+ *
16
+ * @module
17
+ */
18
+
19
+ import 'dotenv/config'
20
+ import fs from 'fs'
21
+ import path from 'path'
22
+ import { fileURLToPath } from 'url'
23
+ import { pkg } from './pkg.js'
24
+
25
+ const __dirname = fileURLToPath(path.dirname(import.meta.url))
26
+
27
+ const defaultEnv = {
28
+ APP_ID: `mlcl`,
29
+ APP_NAME: pkg.name,
30
+ APP_VERSION: pkg.version,
31
+ APP_PLATFORM: `cli`,
32
+ API_ORIGIN: `https://api.molecule.dev`,
33
+ WEB_ORIGIN: `https://assemble.molecule.dev`,
34
+ LOG_LEVEL: `info`,
35
+ }
36
+
37
+ const defaultEnvCmdrc = {
38
+ 'development': {
39
+ NODE_ENV: 'development',
40
+ API_ORIGIN: 'http://localhost:4000',
41
+ WEB_ORIGIN: 'http://localhost:3000',
42
+ },
43
+ 'production': {
44
+ NODE_ENV: 'production',
45
+ API_ORIGIN: 'https://api.molecule.dev',
46
+ WEB_ORIGIN: 'https://assemble.molecule.dev',
47
+ },
48
+ 'test': {
49
+ NODE_ENV: 'test',
50
+ LOG_LEVEL: 'silent',
51
+ API_ORIGIN: 'http://localhost:4000',
52
+ WEB_ORIGIN: 'http://localhost:3000',
53
+ }
54
+ }
55
+
56
+ if (!process.env.NODE_ENV) {
57
+ process.env.NODE_ENV = `production`
58
+ }
59
+
60
+ for (const key in defaultEnv) {
61
+ process.env[key] = defaultEnv[key]
62
+ }
63
+
64
+ /**
65
+ * Sets the variables specified within each environment within `.env-cmdrc.json`.
66
+ */
67
+ export const setVariables = (environments: string[] = [ process.env.NODE_ENV || `production` ]) => {
68
+ try {
69
+ const envCmdrcPath = path.join(__dirname, '../.env-cmdrc.json')
70
+ const envCmdrc = fs.existsSync(envCmdrcPath)
71
+ ? JSON.parse(fs.readFileSync(envCmdrcPath, 'utf8'))
72
+ : defaultEnvCmdrc
73
+
74
+ for (const environment of environments) {
75
+ const variables = envCmdrc[environment]
76
+
77
+ if (variables) {
78
+ Object.assign(process.env, variables)
79
+ } else {
80
+ console.warn(`Environment "${environment}" not found within .env-cmdrc.`)
81
+ }
82
+ }
83
+ } catch (error) {
84
+ }
85
+ }
86
+
87
+ // Extend the environment variables using `.env-cmdrc.json`.
88
+ setVariables()
package/src/index.ts ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Molecule.dev command line tools.
5
+ *
6
+ * @module
7
+ */
8
+
9
+ import './environment.js'
10
+ import { program } from 'commander'
11
+ import * as commands from './commands/index.js'
12
+
13
+ export * as API from './API/index.js'
14
+ export * as commands from './commands/index.js'
15
+ export * as utilities from './utilities/index.js'
16
+
17
+ export * from './logger.js'
18
+ export * from './pkg.js'
19
+ export * from './repoConfig.js'
20
+ export * from './userConfig.js'
21
+
22
+ export * as types from './types/index.js'
23
+
24
+ for (const commandKey in commands) {
25
+ if (typeof commands[commandKey] === `function`) {
26
+ commands[commandKey](program)
27
+ }
28
+ }
29
+
30
+ program.parse()
package/src/logger.ts ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The logger, using `loglevel`.
3
+ *
4
+ * @see https://www.npmjs.com/package/loglevel
5
+ *
6
+ * @module
7
+ */
8
+
9
+ import logger from 'loglevel'
10
+
11
+ declare global {
12
+ // eslint-disable-next-line @typescript-eslint/no-namespace
13
+ namespace NodeJS {
14
+ export interface ProcessEnv {
15
+ /**
16
+ * The log level.
17
+ *
18
+ * @default warn
19
+ */
20
+ LOG_LEVEL?: `trace` | `debug` | `info` | `warn` | `error` | `silent`
21
+ }
22
+ }
23
+ }
24
+
25
+ if (process.env.LOG_LEVEL) {
26
+ logger.setLevel(process.env.LOG_LEVEL)
27
+ }
28
+
29
+ export { logger }
package/src/pkg.ts ADDED
@@ -0,0 +1,11 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { fileURLToPath } from 'url'
4
+
5
+ const __dirname = fileURLToPath(path.dirname(import.meta.url))
6
+
7
+ export const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, `../package.json`), `utf8`)) as {
8
+ name: string
9
+ description: string
10
+ version: string
11
+ }
@@ -0,0 +1,6 @@
1
+ import { readRepoConfig } from './utilities/readRepoConfig.js'
2
+
3
+ /**
4
+ * The current repo's `mlcl` configuration state.
5
+ */
6
+ export const repoConfig = readRepoConfig()
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Global type definitions.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ declare global {
8
+ /**
9
+ * Useful when specifying that some type is a JSON value.
10
+ */
11
+ export type JSONValue = string | number | boolean | JSONObject | JSONArray | null | undefined
12
+
13
+ /**
14
+ * Useful when specifying that some type is a JSON object.
15
+ */
16
+ export interface JSONObject {
17
+ [key: string]: JSONValue
18
+ }
19
+
20
+ /**
21
+ * Useful when specifying that some type is a JSON array.
22
+ */
23
+ export type JSONArray = Array<JSONValue>
24
+ }
25
+
26
+ export {}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Types used throughout `mlcl`.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import * as userTypes from '../API/resource/user/types.js'
8
+
9
+ export * as global from './global.js'
10
+
11
+ /**
12
+ * Molecule user configuration.
13
+ *
14
+ * Mostly used for authenticating with the Molecule.dev API.
15
+ *
16
+ * We'll be adding some really useful functionality surrounding this in the future.
17
+ */
18
+ export interface UserConfig {
19
+ /**
20
+ * Stored authorization headers used for talking to the Molecule.dev API.
21
+ */
22
+ authorizationHeader?: string
23
+ /**
24
+ * Remember the user's device.
25
+ */
26
+ userIdsToDeviceIds?: { [userId: string]: string }
27
+ /**
28
+ * The current user's properties.
29
+ */
30
+ user?: userTypes.Props
31
+ /**
32
+ * This will be asynchronously set to the latest `mlcl` version number if available
33
+ * on the NPM registry. A message will be shown to the user, suggesting the update.
34
+ */
35
+ newVersion?: string
36
+ }
37
+
38
+ /**
39
+ * Molecule repository configuration.
40
+ *
41
+ * We'll be adding some really useful functionality surrounding this in the future.
42
+ */
43
+ export interface RepoConfig {
44
+ id: string
45
+ }
46
+
47
+ /**
48
+ * Git commit information.
49
+ */
50
+ export interface Commit {
51
+ hash: string
52
+ subject?: string
53
+ }
@@ -0,0 +1,56 @@
1
+ declare namespace NodeJS {
2
+ export interface ProcessEnv {
3
+ /**
4
+ * The current environment.
5
+ */
6
+ NODE_ENV?: `development` | `production` | `test`
7
+
8
+ /**
9
+ * A unique identifier for the app.
10
+ *
11
+ * @default mlcl
12
+ */
13
+ APP_ID?: string
14
+
15
+ /**
16
+ * The name of the app, typically as specified within `package.json`.
17
+ *
18
+ * @default $npm_package_name
19
+ */
20
+ APP_NAME?: string
21
+
22
+ /**
23
+ * The version number of the app, typically as specified within `package.json`.
24
+ *
25
+ * @default $npm_package_version
26
+ */
27
+ APP_VERSION?: string
28
+
29
+ /**
30
+ * The available app platforms.
31
+ */
32
+ APP_PLATFORM?: undefined
33
+ | `android`
34
+ | `ios`
35
+ | `windows`
36
+ | `mac`
37
+ | `linux`
38
+ | `cli`
39
+
40
+ /**
41
+ * The API's origin.
42
+ *
43
+ * @example https://api.molecule.dev
44
+ * @example http://localhost:4000
45
+ */
46
+ API_ORIGIN?: string
47
+
48
+ /**
49
+ * The web app's origin.
50
+ *
51
+ * @example https://app.molecule.dev
52
+ * @example http://localhost:3000
53
+ */
54
+ WEB_ORIGIN?: string
55
+ }
56
+ }
@@ -0,0 +1,6 @@
1
+ import { readUserConfig } from './utilities/readUserConfig.js'
2
+
3
+ /**
4
+ * The current user's `mlcl` configuration state.
5
+ */
6
+ export const userConfig = readUserConfig()
@@ -0,0 +1,32 @@
1
+ import { getErrorMessage } from '../getErrorMessage.js'
2
+
3
+ it(`returns a better "timed out" message`, () => {
4
+ expect(getErrorMessage({ code: `ETIMEDOUT` })).toBe(`Timed out`)
5
+ })
6
+
7
+ it(`returns a better "connection aborted" message`, () => {
8
+ expect(getErrorMessage({ code: `ECONNABORTED` })).toBe(`Connection aborted`)
9
+ })
10
+
11
+ it(`returns response data's error message`, () => {
12
+ expect(getErrorMessage({
13
+ response: {
14
+ data: {
15
+ error: `Username is required.`
16
+ }
17
+ }
18
+ })).toBe(`Username is required.`)
19
+ })
20
+
21
+ it(`returns error message`, () => {
22
+ expect(getErrorMessage(new Error(`Unexpected token`))).toBe(`Unexpected token`)
23
+ })
24
+
25
+ it(`returns the error if already a string`, () => {
26
+ expect(getErrorMessage(`Unexpected token`)).toBe(`Unexpected token`)
27
+ })
28
+
29
+ it(`returns null if no error`, () => {
30
+ expect(getErrorMessage()).toBe(null)
31
+ expect(getErrorMessage(null)).toBe(null)
32
+ })
@@ -0,0 +1,46 @@
1
+ import { timeAgo } from '../timeAgo.js'
2
+
3
+ const MINUTE = 60000 + 1
4
+ const HOUR = 3600000 + 1
5
+ const DAY = 86400000 + 1
6
+ const WEEK = 604800000 + 1
7
+ const YEAR = 31557600000 + 1
8
+
9
+ it(`returns "just now"`, () => {
10
+ expect(timeAgo(new Date().getTime())).toBe(`just now`)
11
+ })
12
+
13
+ it(`returns "minute(s)"`, () => {
14
+ expect(timeAgo(new Date().getTime() - MINUTE)).toBe(`1 minute ago`)
15
+ expect(timeAgo(new Date().getTime() - MINUTE*2)).toBe(`2 minutes ago`)
16
+ expect(timeAgo(new Date().getTime() - MINUTE, true)).toBe(`1m ago`)
17
+ expect(timeAgo(new Date().getTime() - MINUTE*2, true)).toBe(`2m ago`)
18
+ })
19
+
20
+ it(`returns "hour(s)"`, () => {
21
+ expect(timeAgo(new Date().getTime() - HOUR)).toBe(`1 hour ago`)
22
+ expect(timeAgo(new Date().getTime() - HOUR*2)).toBe(`2 hours ago`)
23
+ expect(timeAgo(new Date().getTime() - HOUR, true)).toBe(`1h ago`)
24
+ expect(timeAgo(new Date().getTime() - HOUR*2, true)).toBe(`2h ago`)
25
+ })
26
+
27
+ it(`returns "day(s)"`, () => {
28
+ expect(timeAgo(new Date().getTime() - DAY)).toBe(`1 day ago`)
29
+ expect(timeAgo(new Date().getTime() - DAY*2)).toBe(`2 days ago`)
30
+ expect(timeAgo(new Date().getTime() - DAY, true)).toBe(`1d ago`)
31
+ expect(timeAgo(new Date().getTime() - DAY*2, true)).toBe(`2d ago`)
32
+ })
33
+
34
+ it(`returns "week(s)"`, () => {
35
+ expect(timeAgo(new Date().getTime() - WEEK)).toBe(`1 week ago`)
36
+ expect(timeAgo(new Date().getTime() - WEEK*2)).toBe(`2 weeks ago`)
37
+ expect(timeAgo(new Date().getTime() - WEEK, true)).toBe(`1w ago`)
38
+ expect(timeAgo(new Date().getTime() - WEEK*2, true)).toBe(`2w ago`)
39
+ })
40
+
41
+ it(`returns "year(s)"`, () => {
42
+ expect(timeAgo(new Date().getTime() - YEAR)).toBe(`1 year ago`)
43
+ expect(timeAgo(new Date().getTime() - YEAR*2)).toBe(`2 years ago`)
44
+ expect(timeAgo(new Date().getTime() - YEAR, true)).toBe(`1y ago`)
45
+ expect(timeAgo(new Date().getTime() - YEAR*2, true)).toBe(`2y ago`)
46
+ })
@@ -0,0 +1,31 @@
1
+ import semver from 'semver'
2
+ import axios from 'axios'
3
+ import { pkg } from '../pkg.js'
4
+ import { userConfig } from '../userConfig.js'
5
+ import { writeUserConfig } from '../utilities/writeUserConfig.js'
6
+ import { logger } from '../logger.js'
7
+
8
+ export const checkVersion = async () => {
9
+ if (userConfig.newVersion) {
10
+ if (semver.gt(userConfig.newVersion, pkg.version)) {
11
+ logger.info(`A new version (${userConfig.newVersion}) of \`mlcl\` is available.`)
12
+ logger.info(`You are currently on version ${pkg.version}.`)
13
+ } else {
14
+ delete userConfig.newVersion
15
+ writeUserConfig()
16
+ }
17
+ }
18
+
19
+ try {
20
+ const { version } = (await axios.get(`https://registry.npmjs.org/mlcl/latest`)).data
21
+
22
+ if (version && semver.gt(version, pkg.version)) {
23
+ userConfig.newVersion = version
24
+ writeUserConfig()
25
+ }
26
+ } catch (error) {
27
+ }
28
+ }
29
+
30
+ // Check every time `mlcl` is run.
31
+ checkVersion()
@@ -0,0 +1,30 @@
1
+ export type VariableError = null | string | {
2
+ code?: string | number
3
+ message?: string
4
+ response?: {
5
+ statusText?: string
6
+ data?: {
7
+ error?: string
8
+ }
9
+ }
10
+ }
11
+
12
+ /**
13
+ * Attempts to return a human readable message based on some `Error`.
14
+ */
15
+ export const getErrorMessage = (error?: VariableError): string | null => {
16
+ if (!error) {
17
+ return null
18
+ }
19
+
20
+ if (typeof error === `string`) {
21
+ return error
22
+ }
23
+
24
+ return (
25
+ ({ ETIMEDOUT: `Timed out`, ECONNABORTED: `Connection aborted` })[error.code || -1]
26
+ || error.response?.data?.error
27
+ || error.message
28
+ || null
29
+ )
30
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Various utilities used throughout the application.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ export * from './checkVersion.js'
8
+ export * from './getErrorMessage.js'
9
+ export * from './readRepoConfig.js'
10
+ export * from './readUserConfig.js'
11
+ export * from './synchronizeUserConfig.js'
12
+ export * from './timeAgo.js'
13
+ export * from './writeRepoConfig.js'
14
+ export * from './writeUserConfig.js'
@@ -0,0 +1,22 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { RepoConfig } from '../types/index.js'
4
+ import { getRepoRootPathSync } from '../commands/utilities/getRepoRootPathSync.js'
5
+ import { logger } from '../logger.js'
6
+
7
+ /**
8
+ * Reads and returns the current repo's config.
9
+ */
10
+ export const readRepoConfig = (repoConfigPath = path.join(getRepoRootPathSync(), `.mlclrc.json`)) => {
11
+ try {
12
+ if (fs.existsSync(repoConfigPath)) {
13
+ const config = JSON.parse(fs.readFileSync(repoConfigPath, `utf8`))
14
+
15
+ return config as RepoConfig
16
+ }
17
+ } catch (error) {
18
+ logger.error(error)
19
+ }
20
+
21
+ return {} as RepoConfig
22
+ }
@@ -0,0 +1,24 @@
1
+ import os from 'os'
2
+ import fs from 'fs'
3
+ import path from 'path'
4
+ import { UserConfig } from '../types/index.js'
5
+ import { logger } from '../logger.js'
6
+
7
+ const homeDir = os.homedir()
8
+
9
+ /**
10
+ * Reads and returns the current user's config.
11
+ */
12
+ export const readUserConfig = (userConfigPath = path.join(homeDir, `.mlclrc.json`)) => {
13
+ try {
14
+ if (fs.existsSync(userConfigPath)) {
15
+ const config = JSON.parse(fs.readFileSync(userConfigPath, `utf8`))
16
+
17
+ return config as UserConfig
18
+ }
19
+ } catch (error) {
20
+ logger.error(error)
21
+ }
22
+
23
+ return {} as UserConfig
24
+ }
@@ -0,0 +1,26 @@
1
+ import { read as readUser } from '../API/resource/user/index.js'
2
+ import { userConfig } from '../userConfig.js'
3
+ import { writeUserConfig } from '../utilities/writeUserConfig.js'
4
+ import { logger } from '../logger.js'
5
+ import { getErrorMessage } from '../utilities/getErrorMessage.js'
6
+
7
+ export const synchronizeUserConfig = async () => {
8
+ if (userConfig.user) {
9
+ try {
10
+ const { props: user } = (await readUser(userConfig.user.id)).data
11
+
12
+ if (!user) {
13
+ throw new Error(`No user was returned by the Molecule.dev API.`)
14
+ }
15
+
16
+ if (userConfig.user?.id === user.id) {
17
+ userConfig.user = user
18
+ writeUserConfig()
19
+ } else {
20
+ logger.warn(`The Molecule.dev API returned an unexpected response when synchronizing the user.`)
21
+ }
22
+ } catch (error) {
23
+ logger.warn(`Error fetching user: ${getErrorMessage(error) || `Unknown error`}`)
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,39 @@
1
+ const MINUTE = 60000
2
+ const HOUR = 3600000
3
+ const DAY = 86400000
4
+ const WEEK = 604800000
5
+ const YEAR = 31557600000
6
+
7
+ /**
8
+ * Returns how long ago some time (date) was.
9
+ */
10
+ export const timeAgo = (time: number | string, abbreviate?: boolean): string => {
11
+ if (typeof time !== `number`) {
12
+ try {
13
+ time = new Date(time).getTime()
14
+ } catch (error) {
15
+ }
16
+ }
17
+
18
+ const diff = Math.max(0, Date.now() - Number(time))
19
+ let count = 0
20
+
21
+ if (diff < MINUTE) {
22
+ return `just now`
23
+ } else if (diff < HOUR) {
24
+ count = Math.round(diff / MINUTE)
25
+ return `${count}${abbreviate ? `m` : ` minute${count === 1 ? `` : `s`}`} ago`
26
+ } else if (diff < DAY) {
27
+ count = Math.round(diff / HOUR)
28
+ return `${count}${abbreviate ? `h` : ` hour${count === 1 ? `` : `s`}`} ago`
29
+ } else if (diff < WEEK) {
30
+ count = Math.round(diff / DAY)
31
+ return `${count}${abbreviate ? `d` : ` day${count === 1 ? `` : `s`}`} ago`
32
+ } else if (diff < YEAR) {
33
+ count = Math.round(diff / WEEK)
34
+ return `${count}${abbreviate ? `w` : ` week${count === 1 ? `` : `s`}`} ago`
35
+ } else {
36
+ count = Math.round(diff / YEAR)
37
+ return `${count}${abbreviate ? `y` : ` year${count === 1 ? `` : `s`}`} ago`
38
+ }
39
+ }
@@ -0,0 +1,28 @@
1
+ import { userConfig } from '../userConfig.js'
2
+ import { synchronizeUserConfig } from './synchronizeUserConfig.js'
3
+ import * as API from '../API/index.js'
4
+ import { logger } from '../logger.js'
5
+
6
+ /**
7
+ * Ensures the user is logged in before continuing.
8
+ *
9
+ * If the user is not logged in, automatically runs the `login` command.
10
+ */
11
+ export const userLoggedIn = async () => {
12
+ await synchronizeUserConfig()
13
+
14
+ if (userConfig?.user?.id) {
15
+ logger.info(`Hello, ${userConfig.user.name || userConfig.user.username}!`)
16
+ } else {
17
+ logger.info(`Not logged into \`mlcl\`. Logging in...`)
18
+
19
+ await API.resource.user.utilities.logIn()
20
+
21
+ if (!userConfig.user?.id) {
22
+ logger.warn(`Not logged in. Run \`mlcl login\` first.`)
23
+ return null
24
+ }
25
+ }
26
+
27
+ return userConfig.user
28
+ }
@@ -0,0 +1,11 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { repoConfig as currentRepoConfig } from '../repoConfig.js'
4
+ import { getRepoRootPathSync } from '../commands/utilities/getRepoRootPathSync.js'
5
+
6
+ /**
7
+ * Writes the current repo's config to disk.
8
+ */
9
+ export const writeRepoConfig = (repoConfig = currentRepoConfig, repoConfigPath = path.join(getRepoRootPathSync(), `.mlclrc.json`)) => {
10
+ fs.writeFileSync(repoConfigPath, JSON.stringify(repoConfig, null, 2))
11
+ }
@@ -0,0 +1,13 @@
1
+ import os from 'os'
2
+ import fs from 'fs'
3
+ import path from 'path'
4
+ import { userConfig as currentUserConfig } from '../userConfig.js'
5
+
6
+ const homeDir = os.homedir()
7
+
8
+ /**
9
+ * Writes the current user's config to disk.
10
+ */
11
+ export const writeUserConfig = (userConfig = currentUserConfig, userConfigPath = path.join(homeDir, `.mlclrc.json`)) => {
12
+ fs.writeFileSync(userConfigPath, JSON.stringify(userConfig, null, 2))
13
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "./src",
4
+ "outDir": "./build",
5
+ "target": "ES2020",
6
+ "lib": [
7
+ "ESNext"
8
+ ],
9
+ "module": "ESNext",
10
+ "moduleResolution": "Node",
11
+ "esModuleInterop": true,
12
+ "strictNullChecks": true
13
+ },
14
+ "include": [
15
+ "./src"
16
+ ],
17
+ "exclude": [
18
+ "./node_modules",
19
+ "./build",
20
+ "./docs"
21
+ ],
22
+ "ts-node": {
23
+ "files": true
24
+ }
25
+ }