atl-fetch 1.0.0 → 1.2.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/README.md +54 -43
- package/dist/cli/cli.d.ts +8 -0
- package/dist/cli/cli.js +157 -4
- package/dist/cli/spinner.d.ts +189 -0
- package/dist/cli/spinner.js +247 -0
- package/dist/services/fetch/fetch-service.js +4 -5
- package/dist/services/jira/jira-service.js +2 -0
- package/dist/services/storage/storage-service.d.ts +5 -3
- package/dist/services/storage/storage-service.js +297 -36
- package/dist/services/text-converter/text-converter.d.ts +8 -0
- package/dist/services/text-converter/text-converter.js +293 -20
- package/dist/types/jira.d.ts +5 -1
- package/dist/types/storage.d.ts +3 -3
- package/package.json +3 -1
- package/dist/types/result.d.ts +0 -104
- package/dist/types/result.js +0 -119
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI スピナー・進捗表示ユーティリティ
|
|
3
|
+
*
|
|
4
|
+
* ora と picocolors を使用した統一的な進捗表示を提供する。
|
|
5
|
+
*/
|
|
6
|
+
import ora, {} from 'ora';
|
|
7
|
+
import pc from 'picocolors';
|
|
8
|
+
/**
|
|
9
|
+
* 事前定義されたスピナーステップ
|
|
10
|
+
*/
|
|
11
|
+
export const SPINNER_STEPS = {
|
|
12
|
+
AUTH_CHECK: {
|
|
13
|
+
name: 'auth_check',
|
|
14
|
+
successText: '認証情報を確認しました',
|
|
15
|
+
text: '認証情報を確認中...',
|
|
16
|
+
},
|
|
17
|
+
DOWNLOAD_ATTACHMENTS: {
|
|
18
|
+
name: 'download_attachments',
|
|
19
|
+
successText: '添付ファイルをダウンロードしました',
|
|
20
|
+
text: '添付ファイルをダウンロード中...',
|
|
21
|
+
},
|
|
22
|
+
FETCH_CONFLUENCE: {
|
|
23
|
+
name: 'fetch_confluence',
|
|
24
|
+
successText: 'Confluence ページを取得しました',
|
|
25
|
+
text: 'Confluence ページを取得中...',
|
|
26
|
+
},
|
|
27
|
+
FETCH_DATA: {
|
|
28
|
+
name: 'fetch_data',
|
|
29
|
+
successText: 'データを取得しました',
|
|
30
|
+
text: 'データを取得中...',
|
|
31
|
+
},
|
|
32
|
+
FETCH_JIRA: {
|
|
33
|
+
name: 'fetch_jira',
|
|
34
|
+
successText: 'Jira Issue を取得しました',
|
|
35
|
+
text: 'Jira Issue を取得中...',
|
|
36
|
+
},
|
|
37
|
+
FORMAT_OUTPUT: {
|
|
38
|
+
name: 'format_output',
|
|
39
|
+
successText: '出力を整形しました',
|
|
40
|
+
text: '出力を整形中...',
|
|
41
|
+
},
|
|
42
|
+
SAVE_FILES: {
|
|
43
|
+
name: 'save_files',
|
|
44
|
+
successText: 'ファイルを保存しました',
|
|
45
|
+
text: 'ファイルを保存中...',
|
|
46
|
+
},
|
|
47
|
+
URL_PARSE: {
|
|
48
|
+
name: 'url_parse',
|
|
49
|
+
successText: 'URLを解析しました',
|
|
50
|
+
text: 'URLを解析中...',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* CLI 出力のカラーヘルパー
|
|
55
|
+
*/
|
|
56
|
+
export const colors = {
|
|
57
|
+
/** 太字 */
|
|
58
|
+
bold: (text) => pc.bold(text),
|
|
59
|
+
/** シアン(ファイルパスなど) */
|
|
60
|
+
cyan: (text) => pc.cyan(text),
|
|
61
|
+
/** 薄いテキスト(グレー) */
|
|
62
|
+
dim: (text) => pc.dim(text),
|
|
63
|
+
/** エラーメッセージ(赤) */
|
|
64
|
+
error: (text) => pc.red(text),
|
|
65
|
+
/** 情報メッセージ(青) */
|
|
66
|
+
info: (text) => pc.blue(text),
|
|
67
|
+
/** マゼンタ(URL など) */
|
|
68
|
+
magenta: (text) => pc.magenta(text),
|
|
69
|
+
/** 成功メッセージ(緑) */
|
|
70
|
+
success: (text) => pc.green(text),
|
|
71
|
+
/** 警告メッセージ(黄) */
|
|
72
|
+
warn: (text) => pc.yellow(text),
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* CLI スピナー管理クラス
|
|
76
|
+
*
|
|
77
|
+
* 処理の進捗状況をユーザーに表示するためのスピナーを管理する。
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* const spinner = new CliSpinner();
|
|
82
|
+
* spinner.start('データを取得中...');
|
|
83
|
+
* // ... 処理 ...
|
|
84
|
+
* spinner.succeed('データを取得しました');
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export class CliSpinner {
|
|
88
|
+
spinner = null;
|
|
89
|
+
enabled;
|
|
90
|
+
/**
|
|
91
|
+
* CliSpinner インスタンスを作成する
|
|
92
|
+
*
|
|
93
|
+
* @param enabled スピナーを有効にするかどうか(CI環境などでは無効にする)
|
|
94
|
+
*/
|
|
95
|
+
constructor(enabled = true) {
|
|
96
|
+
// CI環境やテスト環境ではスピナーを無効化
|
|
97
|
+
this.enabled = enabled && !process.env['CI'] && process.stdout.isTTY === true;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* スピナーを開始する
|
|
101
|
+
*
|
|
102
|
+
* @param text 表示するテキスト
|
|
103
|
+
*/
|
|
104
|
+
start(text) {
|
|
105
|
+
if (!this.enabled) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.spinner = ora({
|
|
109
|
+
spinner: 'dots',
|
|
110
|
+
text,
|
|
111
|
+
}).start();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* スピナーのテキストを更新する
|
|
115
|
+
*
|
|
116
|
+
* @param text 新しいテキスト
|
|
117
|
+
*/
|
|
118
|
+
update(text) {
|
|
119
|
+
if (this.spinner) {
|
|
120
|
+
this.spinner.text = text;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* スピナーを成功状態で終了する
|
|
125
|
+
*
|
|
126
|
+
* @param text 成功時に表示するテキスト
|
|
127
|
+
*/
|
|
128
|
+
succeed(text) {
|
|
129
|
+
if (this.spinner) {
|
|
130
|
+
this.spinner.succeed(text);
|
|
131
|
+
this.spinner = null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* スピナーを失敗状態で終了する
|
|
136
|
+
*
|
|
137
|
+
* @param text 失敗時に表示するテキスト
|
|
138
|
+
*/
|
|
139
|
+
fail(text) {
|
|
140
|
+
if (this.spinner) {
|
|
141
|
+
this.spinner.fail(text);
|
|
142
|
+
this.spinner = null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* スピナーを警告状態で終了する
|
|
147
|
+
*
|
|
148
|
+
* @param text 警告時に表示するテキスト
|
|
149
|
+
*/
|
|
150
|
+
warn(text) {
|
|
151
|
+
if (this.spinner) {
|
|
152
|
+
this.spinner.warn(text);
|
|
153
|
+
this.spinner = null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* スピナーを情報状態で終了する
|
|
158
|
+
*
|
|
159
|
+
* @param text 情報として表示するテキスト
|
|
160
|
+
*/
|
|
161
|
+
info(text) {
|
|
162
|
+
if (this.spinner) {
|
|
163
|
+
this.spinner.info(text);
|
|
164
|
+
this.spinner = null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* スピナーを停止する(状態なし)
|
|
169
|
+
*/
|
|
170
|
+
stop() {
|
|
171
|
+
if (this.spinner) {
|
|
172
|
+
this.spinner.stop();
|
|
173
|
+
this.spinner = null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 事前定義されたステップでスピナーを開始する
|
|
178
|
+
*
|
|
179
|
+
* @param step スピナーステップ定義
|
|
180
|
+
*/
|
|
181
|
+
startStep(step) {
|
|
182
|
+
this.start(step.text);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* 事前定義されたステップを成功状態で終了する
|
|
186
|
+
*
|
|
187
|
+
* @param step スピナーステップ定義
|
|
188
|
+
*/
|
|
189
|
+
succeedStep(step) {
|
|
190
|
+
this.succeed(step.successText ?? step.text);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* エラーメッセージを整形して出力する
|
|
195
|
+
*
|
|
196
|
+
* @param code エラーコード
|
|
197
|
+
* @param message エラーメッセージ
|
|
198
|
+
* @param cause 原因の説明(オプション)
|
|
199
|
+
* @param solution 解決策の説明(オプション)
|
|
200
|
+
* @returns 整形されたエラーメッセージ
|
|
201
|
+
*/
|
|
202
|
+
export function formatError(code, message, cause, solution) {
|
|
203
|
+
const lines = [];
|
|
204
|
+
lines.push(colors.error(`${colors.bold(code)}: ${message}`));
|
|
205
|
+
if (cause) {
|
|
206
|
+
lines.push(colors.dim(` 原因: ${cause}`));
|
|
207
|
+
}
|
|
208
|
+
if (solution) {
|
|
209
|
+
lines.push(colors.info(` 解決策: ${solution}`));
|
|
210
|
+
}
|
|
211
|
+
return lines.join('\n');
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 成功メッセージを整形する
|
|
215
|
+
*
|
|
216
|
+
* @param message メッセージ
|
|
217
|
+
* @param details 詳細情報(オプション)
|
|
218
|
+
* @returns 整形されたメッセージ
|
|
219
|
+
*/
|
|
220
|
+
export function formatSuccess(message, details) {
|
|
221
|
+
const lines = [];
|
|
222
|
+
lines.push(colors.success(`✓ ${message}`));
|
|
223
|
+
if (details) {
|
|
224
|
+
for (const [key, value] of Object.entries(details)) {
|
|
225
|
+
lines.push(colors.dim(` ${key}: ${colors.cyan(value)}`));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return lines.join('\n');
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* 情報メッセージを整形する
|
|
232
|
+
*
|
|
233
|
+
* @param message メッセージ
|
|
234
|
+
* @returns 整形されたメッセージ
|
|
235
|
+
*/
|
|
236
|
+
export function formatInfo(message) {
|
|
237
|
+
return colors.info(`ℹ ${message}`);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* 警告メッセージを整形する
|
|
241
|
+
*
|
|
242
|
+
* @param message メッセージ
|
|
243
|
+
* @returns 整形されたメッセージ
|
|
244
|
+
*/
|
|
245
|
+
export function formatWarn(message) {
|
|
246
|
+
return colors.warn(`⚠ ${message}`);
|
|
247
|
+
}
|
|
@@ -3,7 +3,7 @@ import { fetchConfluencePage } from '../confluence/confluence-service.js';
|
|
|
3
3
|
import { fetchJiraIssue } from '../jira/jira-service.js';
|
|
4
4
|
import { formatConfluencePage, formatJiraIssue, writeToFile } from '../output/output-service.js';
|
|
5
5
|
import { saveConfluencePage, saveConfluenceVersions, saveJiraIssue } from '../storage/storage-service.js';
|
|
6
|
-
import {
|
|
6
|
+
import { convertStorageFormatToPlainText } from '../text-converter/text-converter.js';
|
|
7
7
|
import { parseUrl } from '../url-parser/url-parser.js';
|
|
8
8
|
/**
|
|
9
9
|
* Jira エラーを FetchError に変換する
|
|
@@ -230,15 +230,14 @@ export async function fetchAndSave(url, options) {
|
|
|
230
230
|
return err(mapJiraErrorToFetchError(issueResult.error));
|
|
231
231
|
}
|
|
232
232
|
const issue = issueResult.value;
|
|
233
|
-
// ADF をプレーンテキストに変換
|
|
234
|
-
const descriptionPlainText = issue.description !== null ? convertAdfToPlainText(issue.description) : null;
|
|
235
233
|
// Jira Issue をディレクトリ構造で保存
|
|
234
|
+
// description は ADF 形式で保存、descriptionPlainText は後方互換性のため維持
|
|
236
235
|
const saveResult = await saveJiraIssue({
|
|
237
236
|
attachments: issue.attachments,
|
|
238
237
|
changelog: issue.changelog,
|
|
239
238
|
comments: issue.comments,
|
|
240
|
-
description: issue.
|
|
241
|
-
descriptionPlainText,
|
|
239
|
+
description: issue.descriptionAdf,
|
|
240
|
+
descriptionPlainText: issue.description,
|
|
242
241
|
key: issue.key,
|
|
243
242
|
summary: issue.summary,
|
|
244
243
|
}, {
|
|
@@ -108,6 +108,7 @@ function mapApiCommentToJiraComment(apiComment) {
|
|
|
108
108
|
return {
|
|
109
109
|
author: apiComment.author.displayName,
|
|
110
110
|
body: extractTextFromAdf(apiComment.body),
|
|
111
|
+
bodyAdf: apiComment.body,
|
|
111
112
|
created: apiComment.created,
|
|
112
113
|
id: apiComment.id,
|
|
113
114
|
updated: apiComment.updated,
|
|
@@ -254,6 +255,7 @@ export async function fetchJiraIssue(organization, issueKey) {
|
|
|
254
255
|
changelog: (apiResponse.changelog?.histories ?? []).map(mapApiChangelogEntryToJiraChangelogEntry),
|
|
255
256
|
comments: (apiResponse.fields.comment?.comments ?? []).map(mapApiCommentToJiraComment),
|
|
256
257
|
description: apiResponse.fields.description ? extractTextFromAdf(apiResponse.fields.description) : null,
|
|
258
|
+
descriptionAdf: apiResponse.fields.description,
|
|
257
259
|
key: apiResponse.key,
|
|
258
260
|
summary: apiResponse.fields.summary,
|
|
259
261
|
};
|
|
@@ -16,9 +16,11 @@ import type { ConfluenceSaveData, ConfluenceSaveResult, ConfluenceStorageOptions
|
|
|
16
16
|
* ├── manifest.json # 取得メタデータ
|
|
17
17
|
* ├── issue.json # Issue 全データ(JSON 形式)
|
|
18
18
|
* ├── description.txt # 説明文のプレーンテキスト
|
|
19
|
-
* ├── content.md # Markdown
|
|
20
|
-
* ├──
|
|
21
|
-
* ├──
|
|
19
|
+
* ├── content.md # Markdown 形式(Description + Attachments)
|
|
20
|
+
* ├── comments.md # コメント一覧(Markdown 形式)
|
|
21
|
+
* ├── changelog.md # 変更履歴(Markdown 形式)
|
|
22
|
+
* ├── changelog.json # 変更履歴(JSON 形式)
|
|
23
|
+
* ├── comments.json # コメント一覧(JSON 形式)
|
|
22
24
|
* ├── attachments.json # 添付ファイル一覧メタデータ
|
|
23
25
|
* └── attachments/ # 添付ファイル実体
|
|
24
26
|
* └── {id}_{filename}
|