@oyster-lib/cli 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1534 -536
- package/package.json +5 -1
- package/jest.config.ts +0 -23
- package/src/__tests__/templates.test.ts +0 -283
- package/src/commands/deploy.ts +0 -109
- package/src/commands/init.ts +0 -154
- package/src/commands/login.ts +0 -78
- package/src/commands/migrate.ts +0 -81
- package/src/config.ts +0 -86
- package/src/index.ts +0 -22
- package/src/templates/backend.ts +0 -119
- package/src/templates/frontend.ts +0 -139
- package/src/templates/index.ts +0 -149
- package/src/utils/http.ts +0 -131
- package/src/utils/input.ts +0 -89
- package/tsconfig.json +0 -16
package/src/utils/http.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import axios, { type AxiosRequestConfig } from 'axios'
|
|
2
|
-
import FormData from 'form-data'
|
|
3
|
-
import { getApiBaseUrl, requireLogin } from '../config.js'
|
|
4
|
-
|
|
5
|
-
function authHeaders(): Record<string, string> {
|
|
6
|
-
const apiKey = requireLogin()
|
|
7
|
-
return { Authorization: `Bearer ${apiKey}` }
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export async function apiGet(path: string, params?: Record<string, any>) {
|
|
11
|
-
const base = getApiBaseUrl()
|
|
12
|
-
return axios.get(`${base}/${path}`, { params, headers: authHeaders() })
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export async function apiPost(path: string, data?: Record<string, any>) {
|
|
16
|
-
const base = getApiBaseUrl()
|
|
17
|
-
return axios.post(`${base}/${path}`, data, { headers: authHeaders() })
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function apiPostFormData(
|
|
21
|
-
path: string,
|
|
22
|
-
formData: FormData,
|
|
23
|
-
extraConfig?: Partial<AxiosRequestConfig>
|
|
24
|
-
) {
|
|
25
|
-
const base = getApiBaseUrl()
|
|
26
|
-
return axios.post(`${base}/${path}`, formData, {
|
|
27
|
-
...extraConfig,
|
|
28
|
-
headers: {
|
|
29
|
-
...formData.getHeaders(),
|
|
30
|
-
...authHeaders(),
|
|
31
|
-
...(extraConfig?.headers as Record<string, string> ?? {})
|
|
32
|
-
},
|
|
33
|
-
maxContentLength: Infinity,
|
|
34
|
-
maxBodyLength: Infinity
|
|
35
|
-
})
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* SSE(Server-Sent Events)ストリームで POST リクエストを送信し、イベントをコールバックで受信する
|
|
40
|
-
*
|
|
41
|
-
* @param path APIパス
|
|
42
|
-
* @param formData FormDataボディ
|
|
43
|
-
* @param onEvent 各SSEイベントのコールバック
|
|
44
|
-
* @param options タイムアウト設定
|
|
45
|
-
* @returns 完了時に resolve、エラー時に reject
|
|
46
|
-
*/
|
|
47
|
-
export async function apiPostStream(
|
|
48
|
-
path: string,
|
|
49
|
-
formData: FormData,
|
|
50
|
-
onEvent: (data: Record<string, any>) => void,
|
|
51
|
-
options?: { timeout?: number }
|
|
52
|
-
): Promise<void> {
|
|
53
|
-
const base = getApiBaseUrl()
|
|
54
|
-
const url = `${base}/${path}`
|
|
55
|
-
const timeout = options?.timeout ?? 600_000
|
|
56
|
-
|
|
57
|
-
// FormData を Buffer に変換(Node.js の FormData は ReadableStream ではないため)
|
|
58
|
-
const body = await new Promise<Buffer>((resolve, reject) => {
|
|
59
|
-
const chunks: Buffer[] = []
|
|
60
|
-
formData.on('data', (chunk: Buffer) => chunks.push(chunk))
|
|
61
|
-
formData.on('end', () => resolve(Buffer.concat(chunks)))
|
|
62
|
-
formData.on('error', reject)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
const controller = new AbortController()
|
|
66
|
-
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const response = await fetch(url, {
|
|
70
|
-
method: 'POST',
|
|
71
|
-
headers: {
|
|
72
|
-
...formData.getHeaders(),
|
|
73
|
-
...authHeaders()
|
|
74
|
-
},
|
|
75
|
-
body,
|
|
76
|
-
signal: controller.signal
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
if (!response.ok) {
|
|
80
|
-
// 非SSEレスポンス(エラー)の場合
|
|
81
|
-
let errorMessage = 'デプロイに失敗しました'
|
|
82
|
-
try {
|
|
83
|
-
const errorBody = await response.json() as Record<string, any>
|
|
84
|
-
errorMessage = errorBody.message || errorMessage
|
|
85
|
-
} catch {
|
|
86
|
-
// JSONパース失敗時はデフォルトメッセージを使用
|
|
87
|
-
}
|
|
88
|
-
throw new Error(errorMessage)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (!response.body) {
|
|
92
|
-
throw new Error('レスポンスボディがありません')
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// SSEストリームを読み取る
|
|
96
|
-
const reader = response.body.getReader()
|
|
97
|
-
const decoder = new TextDecoder()
|
|
98
|
-
let buffer = ''
|
|
99
|
-
|
|
100
|
-
while (true) {
|
|
101
|
-
const { done, value } = await reader.read()
|
|
102
|
-
if (done) break
|
|
103
|
-
|
|
104
|
-
buffer += decoder.decode(value, { stream: true })
|
|
105
|
-
|
|
106
|
-
// SSEイベントを行ごとにパース
|
|
107
|
-
const lines = buffer.split('\n')
|
|
108
|
-
buffer = lines.pop() || '' // 最後の不完全な行をバッファに残す
|
|
109
|
-
|
|
110
|
-
for (const line of lines) {
|
|
111
|
-
if (line.startsWith('data: ')) {
|
|
112
|
-
let data: Record<string, any>
|
|
113
|
-
try {
|
|
114
|
-
data = JSON.parse(line.slice(6))
|
|
115
|
-
} catch {
|
|
116
|
-
// JSONパースエラーは無視(不完全な行)
|
|
117
|
-
continue
|
|
118
|
-
}
|
|
119
|
-
onEvent(data)
|
|
120
|
-
|
|
121
|
-
// エラーイベントの場合は例外をスロー
|
|
122
|
-
if (data.type === 'error') {
|
|
123
|
-
throw new Error(data.message || 'デプロイに失敗しました')
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
} finally {
|
|
129
|
-
clearTimeout(timeoutId)
|
|
130
|
-
}
|
|
131
|
-
}
|
package/src/utils/input.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import ora from 'ora'
|
|
2
|
-
import prompts from 'prompts'
|
|
3
|
-
import { apiGet } from './http.js'
|
|
4
|
-
|
|
5
|
-
export type Domain = {
|
|
6
|
-
id: number
|
|
7
|
-
name: string
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export type App = {
|
|
11
|
-
name: string
|
|
12
|
-
uuid: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* ドメインを対話的に選択する
|
|
17
|
-
*/
|
|
18
|
-
export async function chooseDomainAsync(): Promise<Domain | null> {
|
|
19
|
-
const spinner = ora('ドメイン一覧を取得中...').start()
|
|
20
|
-
try {
|
|
21
|
-
const response = await apiGet('domains')
|
|
22
|
-
spinner.succeed('ドメイン一覧を取得しました')
|
|
23
|
-
|
|
24
|
-
const domains: Domain[] = response.data.domains
|
|
25
|
-
if (domains.length === 0) {
|
|
26
|
-
console.log('ドメインが登録されていません。')
|
|
27
|
-
return null
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (domains.length === 1) {
|
|
31
|
-
console.log(` ドメイン: ${domains[0].name}`)
|
|
32
|
-
return domains[0]
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const choices = domains.map((d) => ({ title: d.name, value: d.name }))
|
|
36
|
-
const { value } = await prompts({
|
|
37
|
-
type: 'autocomplete',
|
|
38
|
-
name: 'value',
|
|
39
|
-
message: 'ドメインを選択:',
|
|
40
|
-
choices,
|
|
41
|
-
suggest: async (input: string, choices: any[]) =>
|
|
42
|
-
choices.filter((c) => c.title.toLowerCase().includes(input.toLowerCase()))
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
if (!value) return null
|
|
46
|
-
return domains.find((d) => d.name === value) ?? null
|
|
47
|
-
} catch (error: any) {
|
|
48
|
-
spinner.fail('ドメイン一覧の取得に失敗しました')
|
|
49
|
-
throw error
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* ドメイン配下のアプリを対話的に選択する
|
|
55
|
-
*/
|
|
56
|
-
export async function chooseAppAsync(domainName: string): Promise<App | null> {
|
|
57
|
-
const spinner = ora('アプリ一覧を取得中...').start()
|
|
58
|
-
try {
|
|
59
|
-
const response = await apiGet('apps', { domainName })
|
|
60
|
-
spinner.succeed('アプリ一覧を取得しました')
|
|
61
|
-
|
|
62
|
-
const apps: App[] = response.data.apps
|
|
63
|
-
if (apps.length === 0) {
|
|
64
|
-
console.log('アプリが登録されていません。')
|
|
65
|
-
return null
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (apps.length === 1) {
|
|
69
|
-
console.log(` アプリ: ${apps[0].name}`)
|
|
70
|
-
return apps[0]
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const choices = apps.map((a) => ({ title: a.name, value: a.uuid }))
|
|
74
|
-
const { value } = await prompts({
|
|
75
|
-
type: 'autocomplete',
|
|
76
|
-
name: 'value',
|
|
77
|
-
message: 'アプリを選択:',
|
|
78
|
-
choices,
|
|
79
|
-
suggest: async (input: string, choices: any[]) =>
|
|
80
|
-
choices.filter((c) => c.title.toLowerCase().includes(input.toLowerCase()))
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
if (!value) return null
|
|
84
|
-
return apps.find((a) => a.uuid === value) ?? null
|
|
85
|
-
} catch (error: any) {
|
|
86
|
-
spinner.fail('アプリ一覧の取得に失敗しました')
|
|
87
|
-
throw error
|
|
88
|
-
}
|
|
89
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"esModuleInterop": true,
|
|
7
|
-
"strict": true,
|
|
8
|
-
"outDir": "./dist",
|
|
9
|
-
"rootDir": "./src",
|
|
10
|
-
"declaration": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"resolveJsonModule": true
|
|
14
|
-
},
|
|
15
|
-
"include": ["src/**/*"]
|
|
16
|
-
}
|