@seamapi/cli 0.7.0 → 0.9.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/bin/cli.js +11 -2
- package/bin/cli.js.map +1 -1
- package/lib/blueprint.d.ts +2 -1
- package/lib/blueprint.js +171 -26
- package/lib/blueprint.js.map +1 -1
- package/lib/config/config-store.d.ts +23 -0
- package/lib/config/config-store.js +113 -0
- package/lib/config/config-store.js.map +1 -0
- package/lib/config/index.d.ts +1 -0
- package/lib/config/index.js +2 -0
- package/lib/config/index.js.map +1 -0
- package/lib/config/migrate.d.ts +2 -0
- package/lib/config/migrate.js +21 -0
- package/lib/config/migrate.js.map +1 -0
- package/lib/get-api-blueprint.d.ts +4 -1
- package/lib/get-api-blueprint.js +3 -4
- package/lib/get-api-blueprint.js.map +1 -1
- package/lib/get-current-workspace-id.js +1 -1
- package/lib/get-current-workspace-id.js.map +1 -1
- package/lib/get-seam.js +1 -1
- package/lib/get-seam.js.map +1 -1
- package/lib/get-server.js +1 -1
- package/lib/get-server.js.map +1 -1
- package/lib/interact-for-login.js +1 -1
- package/lib/interact-for-login.js.map +1 -1
- package/lib/interact-for-server-selection.js +1 -1
- package/lib/interact-for-server-selection.js.map +1 -1
- package/lib/interact-for-use-remote-api-defs.js +1 -1
- package/lib/interact-for-use-remote-api-defs.js.map +1 -1
- package/lib/interact-for-workspace-id.js +1 -1
- package/lib/interact-for-workspace-id.js.map +1 -1
- package/lib/version.d.ts +3 -1
- package/lib/version.js +4 -1
- package/lib/version.js.map +1 -1
- package/package.json +5 -3
- package/src/bin/cli.ts +12 -2
- package/src/lib/blueprint.ts +228 -28
- package/src/lib/config/config-store.ts +158 -0
- package/src/lib/config/index.ts +1 -0
- package/src/lib/config/migrate.ts +39 -0
- package/src/lib/get-api-blueprint.ts +7 -3
- package/src/lib/get-current-workspace-id.ts +1 -1
- package/src/lib/get-seam.ts +1 -1
- package/src/lib/get-server.ts +1 -1
- package/src/lib/interact-for-login.ts +1 -1
- package/src/lib/interact-for-server-selection.ts +1 -1
- package/src/lib/interact-for-use-remote-api-defs.ts +1 -1
- package/src/lib/interact-for-workspace-id.ts +1 -1
- package/src/lib/version.ts +4 -1
- package/lib/get-config-store.d.ts +0 -2
- package/lib/get-config-store.js +0 -5
- package/lib/get-config-store.js.map +0 -1
- package/src/lib/get-config-store.ts +0 -5
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
|
|
3
|
+
import Configstore from 'configstore'
|
|
4
|
+
import envPaths from 'env-paths'
|
|
5
|
+
|
|
6
|
+
import { migrateConfigStore } from './migrate.js'
|
|
7
|
+
|
|
8
|
+
const configFileName = 'cli.json'
|
|
9
|
+
const legacyConfigStoreId = 'seam-cli'
|
|
10
|
+
const currentWorkspaceIdKey = 'current_workspace_id'
|
|
11
|
+
const patKey = 'pat'
|
|
12
|
+
const paths = envPaths('seam', { suffix: '' })
|
|
13
|
+
|
|
14
|
+
export const getConfigStore = () => {
|
|
15
|
+
const settingsStore = new Configstore(legacyConfigStoreId, undefined, {
|
|
16
|
+
configPath: getConfigPath(),
|
|
17
|
+
})
|
|
18
|
+
const stateStore = new Configstore(legacyConfigStoreId, undefined, {
|
|
19
|
+
configPath: getStateConfigPath(),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
migrateConfigStore(
|
|
23
|
+
settingsStore,
|
|
24
|
+
stateStore,
|
|
25
|
+
new Configstore(legacyConfigStoreId),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
return new SeamConfigStore(settingsStore, stateStore)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const mergeConfig = (
|
|
32
|
+
baseConfig: Record<string, unknown>,
|
|
33
|
+
overrideConfig: Record<string, unknown>,
|
|
34
|
+
): Record<string, unknown> => {
|
|
35
|
+
const mergedConfig = { ...baseConfig }
|
|
36
|
+
|
|
37
|
+
for (const [key, value] of Object.entries(overrideConfig)) {
|
|
38
|
+
const baseValue = mergedConfig[key]
|
|
39
|
+
mergedConfig[key] =
|
|
40
|
+
isRecord(baseValue) && isRecord(value)
|
|
41
|
+
? mergeConfig(baseValue, value)
|
|
42
|
+
: value
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return mergedConfig
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const splitConfig = (
|
|
49
|
+
config: Record<string, unknown>,
|
|
50
|
+
): {
|
|
51
|
+
settings: Record<string, unknown>
|
|
52
|
+
state: Record<string, unknown>
|
|
53
|
+
} => {
|
|
54
|
+
const settings: Record<string, unknown> = {}
|
|
55
|
+
const state: Record<string, unknown> = {}
|
|
56
|
+
|
|
57
|
+
for (const [key, value] of Object.entries(config)) {
|
|
58
|
+
if (isStateKey(key)) {
|
|
59
|
+
state[key] = value
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (isRecord(value)) {
|
|
64
|
+
const splitValue = splitConfig(value)
|
|
65
|
+
if (Object.keys(splitValue.settings).length > 0) {
|
|
66
|
+
settings[key] = splitValue.settings
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (Object.keys(splitValue.state).length > 0) {
|
|
70
|
+
state[key] = splitValue.state
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
continue
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
settings[key] = value
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { settings, state }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
class SeamConfigStore {
|
|
83
|
+
readonly path: string
|
|
84
|
+
|
|
85
|
+
constructor(
|
|
86
|
+
private readonly settingsStore: Configstore,
|
|
87
|
+
private readonly stateStore: Configstore,
|
|
88
|
+
) {
|
|
89
|
+
this.path = settingsStore.path
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get all(): Record<string, unknown> {
|
|
93
|
+
return mergeConfig(this.settingsStore.all, this.stateStore.all)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
set all(value: Record<string, unknown>) {
|
|
97
|
+
const { settings, state } = splitConfig(value)
|
|
98
|
+
this.settingsStore.all = settings
|
|
99
|
+
this.stateStore.all = state
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get size(): number {
|
|
103
|
+
return Object.keys(this.all).length
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get(key: string): unknown {
|
|
107
|
+
return this.getStore(key).get(key)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
set(key: string | Record<string, unknown>, value?: unknown): void {
|
|
111
|
+
if (isRecord(key)) {
|
|
112
|
+
for (const [configKey, configValue] of Object.entries(key)) {
|
|
113
|
+
this.set(configKey, configValue)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.getStore(key).set(key, value)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
has(key: string): boolean {
|
|
123
|
+
return this.getStore(key).has(key)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
delete(key: string): void {
|
|
127
|
+
this.getStore(key).delete(key)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
clear(): void {
|
|
131
|
+
this.settingsStore.clear()
|
|
132
|
+
this.stateStore.clear()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private getStore(key: string): Configstore {
|
|
136
|
+
return isStateKey(key) ? this.stateStore : this.settingsStore
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const getConfigPath = (): string => {
|
|
141
|
+
return join(paths.config, configFileName)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const getStateConfigPath = (): string => {
|
|
145
|
+
return join(paths.log, configFileName)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const isStateKey = (key: string): boolean => {
|
|
149
|
+
return (
|
|
150
|
+
key === currentWorkspaceIdKey ||
|
|
151
|
+
key === patKey ||
|
|
152
|
+
key.endsWith(`.${patKey}`)
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const isRecord = (value: unknown): value is Record<string, unknown> => {
|
|
157
|
+
return value != null && typeof value === 'object' && !Array.isArray(value)
|
|
158
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getConfigStore } from './config-store.js'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { existsSync, rmSync } from 'node:fs'
|
|
2
|
+
|
|
3
|
+
import type Configstore from 'configstore'
|
|
4
|
+
|
|
5
|
+
import { mergeConfig, splitConfig } from './config-store.js'
|
|
6
|
+
|
|
7
|
+
export const migrateConfigStore = (
|
|
8
|
+
settingsStore: Configstore,
|
|
9
|
+
stateStore: Configstore,
|
|
10
|
+
legacyStore: Configstore,
|
|
11
|
+
): void => {
|
|
12
|
+
if (!existsSync(legacyStore.path)) return
|
|
13
|
+
|
|
14
|
+
migrateLegacyConfig(settingsStore, stateStore, legacyStore.all)
|
|
15
|
+
|
|
16
|
+
rmSync(legacyStore.path, { force: true })
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const migrateLegacyConfig = (
|
|
20
|
+
settingsStore: Configstore,
|
|
21
|
+
stateStore: Configstore,
|
|
22
|
+
legacyConfig: Record<string, unknown>,
|
|
23
|
+
): void => {
|
|
24
|
+
if (Object.keys(legacyConfig).length === 0) return
|
|
25
|
+
|
|
26
|
+
const { settings, state } = splitConfig(legacyConfig)
|
|
27
|
+
|
|
28
|
+
writeIfChanged(settingsStore, mergeConfig(settings, settingsStore.all))
|
|
29
|
+
writeIfChanged(stateStore, mergeConfig(state, stateStore.all))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const writeIfChanged = (
|
|
33
|
+
store: Configstore,
|
|
34
|
+
nextConfig: Record<string, unknown>,
|
|
35
|
+
): void => {
|
|
36
|
+
if (JSON.stringify(store.all) !== JSON.stringify(nextConfig)) {
|
|
37
|
+
store.all = nextConfig
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -5,15 +5,19 @@ import { getServer } from './get-server.js'
|
|
|
5
5
|
|
|
6
6
|
export type ApiBlueprint = Blueprint
|
|
7
7
|
|
|
8
|
+
export interface GetApiBlueprintOptions {
|
|
9
|
+
update?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
8
12
|
export const getApiBlueprint = async (
|
|
9
13
|
useRemoteDefinitions: boolean,
|
|
14
|
+
options: GetApiBlueprintOptions = {},
|
|
10
15
|
): Promise<ApiBlueprint> => {
|
|
11
16
|
// Remote definitions describe whatever the server is currently running, so
|
|
12
|
-
// build them directly from the server's OpenAPI document.
|
|
13
|
-
// does not load @seamapi/types.
|
|
17
|
+
// build them directly from the server's OpenAPI document.
|
|
14
18
|
if (useRemoteDefinitions) return await createRemoteBlueprint()
|
|
15
19
|
|
|
16
|
-
return await getBlueprint()
|
|
20
|
+
return await getBlueprint(options)
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
const createRemoteBlueprint = async (): Promise<Blueprint> => {
|
package/src/lib/get-seam.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
SeamHttpWithoutWorkspace,
|
|
6
6
|
} from '@seamapi/http/connect'
|
|
7
7
|
|
|
8
|
-
import { getConfigStore } from './
|
|
8
|
+
import { getConfigStore } from './config/index.js'
|
|
9
9
|
import { getServer } from './get-server.js'
|
|
10
10
|
|
|
11
11
|
export const getSeam = async (): Promise<SeamHttp> => {
|
package/src/lib/get-server.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { isApiKey, isPersonalAccessToken } from '@seamapi/http/connect'
|
|
|
2
2
|
import chalk from 'chalk'
|
|
3
3
|
import prompts from 'prompts'
|
|
4
4
|
|
|
5
|
-
import { getConfigStore } from './
|
|
5
|
+
import { getConfigStore } from './config/index.js'
|
|
6
6
|
import { getServer } from './get-server.js'
|
|
7
7
|
import { interactForWorkspaceId } from './interact-for-workspace-id.js'
|
|
8
8
|
import { withLoading } from './util/with-loading.js'
|
|
@@ -2,7 +2,7 @@ import { randomBytes } from 'node:crypto'
|
|
|
2
2
|
|
|
3
3
|
import prompts from 'prompts'
|
|
4
4
|
|
|
5
|
-
import { getConfigStore } from './
|
|
5
|
+
import { getConfigStore } from './config/index.js'
|
|
6
6
|
import { getServer } from './get-server.js'
|
|
7
7
|
|
|
8
8
|
export async function interactForServerSelection() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SeamHttpWithoutWorkspace } from '@seamapi/http/connect'
|
|
2
2
|
import prompts from 'prompts'
|
|
3
3
|
|
|
4
|
-
import { getConfigStore } from './
|
|
4
|
+
import { getConfigStore } from './config/index.js'
|
|
5
5
|
import { getSeamMultiWorkspace } from './get-seam.js'
|
|
6
6
|
import { getServer } from './get-server.js'
|
|
7
7
|
import { withLoading } from './util/with-loading.js'
|
package/src/lib/version.ts
CHANGED
package/lib/get-config-store.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-config-store.js","sourceRoot":"","sources":["../src/lib/get-config-store.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,aAAa,CAAA;AAErC,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,EAAE;IACjC,OAAO,IAAI,WAAW,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;AACxC,CAAC,CAAA"}
|