@oyster-lib/cli 2.0.0 → 2.0.2

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/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
- }
@@ -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
- }