atl-fetch 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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -0
  3. package/dist/cli/cli.d.ts +61 -0
  4. package/dist/cli/cli.js +131 -0
  5. package/dist/cli/index.d.ts +5 -0
  6. package/dist/cli/index.js +4 -0
  7. package/dist/index.d.ts +8 -0
  8. package/dist/index.js +13 -0
  9. package/dist/ports/file/file-port.d.ts +89 -0
  10. package/dist/ports/file/file-port.js +155 -0
  11. package/dist/ports/file/index.d.ts +1 -0
  12. package/dist/ports/file/index.js +1 -0
  13. package/dist/ports/http/http-port.d.ts +107 -0
  14. package/dist/ports/http/http-port.js +238 -0
  15. package/dist/ports/http/index.d.ts +1 -0
  16. package/dist/ports/http/index.js +1 -0
  17. package/dist/services/auth/auth-service.d.ts +79 -0
  18. package/dist/services/auth/auth-service.js +158 -0
  19. package/dist/services/auth/index.d.ts +1 -0
  20. package/dist/services/auth/index.js +1 -0
  21. package/dist/services/confluence/confluence-service.d.ts +152 -0
  22. package/dist/services/confluence/confluence-service.js +510 -0
  23. package/dist/services/confluence/index.d.ts +1 -0
  24. package/dist/services/confluence/index.js +1 -0
  25. package/dist/services/diff/diff-service.d.ts +84 -0
  26. package/dist/services/diff/diff-service.js +881 -0
  27. package/dist/services/diff/index.d.ts +1 -0
  28. package/dist/services/diff/index.js +1 -0
  29. package/dist/services/fetch/fetch-service.d.ts +112 -0
  30. package/dist/services/fetch/fetch-service.js +302 -0
  31. package/dist/services/fetch/index.d.ts +1 -0
  32. package/dist/services/fetch/index.js +1 -0
  33. package/dist/services/jira/index.d.ts +1 -0
  34. package/dist/services/jira/index.js +1 -0
  35. package/dist/services/jira/jira-service.d.ts +100 -0
  36. package/dist/services/jira/jira-service.js +354 -0
  37. package/dist/services/output/index.d.ts +4 -0
  38. package/dist/services/output/index.js +4 -0
  39. package/dist/services/output/output-service.d.ts +67 -0
  40. package/dist/services/output/output-service.js +228 -0
  41. package/dist/services/storage/index.d.ts +6 -0
  42. package/dist/services/storage/index.js +6 -0
  43. package/dist/services/storage/storage-service.d.ts +77 -0
  44. package/dist/services/storage/storage-service.js +738 -0
  45. package/dist/services/text-converter/index.d.ts +1 -0
  46. package/dist/services/text-converter/index.js +1 -0
  47. package/dist/services/text-converter/text-converter.d.ts +35 -0
  48. package/dist/services/text-converter/text-converter.js +681 -0
  49. package/dist/services/url-parser/index.d.ts +1 -0
  50. package/dist/services/url-parser/index.js +1 -0
  51. package/dist/services/url-parser/url-parser.d.ts +43 -0
  52. package/dist/services/url-parser/url-parser.js +283 -0
  53. package/dist/types/auth.d.ts +25 -0
  54. package/dist/types/auth.js +1 -0
  55. package/dist/types/confluence.d.ts +68 -0
  56. package/dist/types/confluence.js +1 -0
  57. package/dist/types/diff.d.ts +77 -0
  58. package/dist/types/diff.js +7 -0
  59. package/dist/types/fetch.d.ts +65 -0
  60. package/dist/types/fetch.js +1 -0
  61. package/dist/types/file.d.ts +22 -0
  62. package/dist/types/file.js +1 -0
  63. package/dist/types/http.d.ts +45 -0
  64. package/dist/types/http.js +1 -0
  65. package/dist/types/jira.d.ts +90 -0
  66. package/dist/types/jira.js +1 -0
  67. package/dist/types/output.d.ts +55 -0
  68. package/dist/types/output.js +7 -0
  69. package/dist/types/result.d.ts +104 -0
  70. package/dist/types/result.js +119 -0
  71. package/dist/types/storage.d.ts +209 -0
  72. package/dist/types/storage.js +6 -0
  73. package/dist/types/url-parser.d.ts +46 -0
  74. package/dist/types/url-parser.js +1 -0
  75. package/package.json +106 -0
@@ -0,0 +1,107 @@
1
+ import { type Result } from 'neverthrow';
2
+ import type { HttpError, HttpRequestOptions, HttpResponse, ProgressCallback } from '../../types/http.js';
3
+ /**
4
+ * HTTP リクエストを実行する
5
+ *
6
+ * got ライブラリを使用して HTTP リクエストを実行し、Result 型で結果を返す。
7
+ * 認証ヘッダーの付与、リトライ、タイムアウト処理をサポート。
8
+ *
9
+ * @typeParam T - レスポンスデータの型
10
+ * @param url - リクエスト先の URL
11
+ * @param options - リクエストオプション(メソッド、ヘッダー、ボディ、タイムアウト)
12
+ * @returns 成功時は {@link HttpResponse} を含む Ok、失敗時は {@link HttpError} を含む Err
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * // GET リクエスト
17
+ * const result = await httpRequest<{ id: string }>('https://api.example.com/data');
18
+ * if (result.isOk()) {
19
+ * console.log(result.value.data.id);
20
+ * }
21
+ * ```
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * // 認証付き GET リクエスト
26
+ * const result = await httpRequest<{ user: string }>('https://api.example.com/me', {
27
+ * headers: { Authorization: 'Basic xxx' }
28
+ * });
29
+ * ```
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * // POST リクエスト
34
+ * const result = await httpRequest<{ id: string }>('https://api.example.com/create', {
35
+ * method: 'POST',
36
+ * headers: { 'Content-Type': 'application/json' },
37
+ * body: JSON.stringify({ name: 'test' }),
38
+ * });
39
+ * ```
40
+ */
41
+ export declare function httpRequest<T>(url: string, options?: HttpRequestOptions): Promise<Result<HttpResponse<T>, HttpError>>;
42
+ /**
43
+ * エラーを HttpError にマッピングする
44
+ *
45
+ * @param error - キャッチしたエラー
46
+ * @returns 対応する HttpError
47
+ *
48
+ * @internal テスト用に export
49
+ */
50
+ export declare function mapErrorToHttpError(error: unknown): HttpError;
51
+ /**
52
+ * HTTP ステータスコードに対応するテキストを取得する
53
+ *
54
+ * @param status - HTTP ステータスコード
55
+ * @returns ステータステキスト
56
+ *
57
+ * @internal テスト用に export
58
+ */
59
+ export declare function getStatusText(status: number): string;
60
+ /**
61
+ * ファイルをダウンロードして指定パスに保存する
62
+ *
63
+ * got ライブラリを使用してストリームでファイルをダウンロードし、
64
+ * 指定したパスに保存する。大きなファイルでもメモリを節約できる。
65
+ *
66
+ * @param url - ダウンロード対象の URL
67
+ * @param destPath - 保存先ファイルパス
68
+ * @param headers - リクエストヘッダー(認証ヘッダー等)
69
+ * @param onProgress - 進捗コールバック関数
70
+ * @param timeout - タイムアウト(ミリ秒)
71
+ * @returns 成功時は void を含む Ok、失敗時は {@link HttpError} を含む Err
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * // 基本的なダウンロード
76
+ * const result = await httpDownload(
77
+ * 'https://example.com/file.pdf',
78
+ * '/path/to/save/file.pdf'
79
+ * );
80
+ * ```
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // 認証付きダウンロード
85
+ * const result = await httpDownload(
86
+ * 'https://api.example.com/attachments/123',
87
+ * '/path/to/save/attachment.pdf',
88
+ * { Authorization: 'Basic xxx' }
89
+ * );
90
+ * ```
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * // 進捗コールバック付きダウンロード
95
+ * const result = await httpDownload(
96
+ * 'https://example.com/large-file.zip',
97
+ * '/path/to/save/large-file.zip',
98
+ * undefined,
99
+ * (transferred, total) => {
100
+ * if (total) {
101
+ * console.log(`Progress: ${(transferred / total * 100).toFixed(1)}%`);
102
+ * }
103
+ * }
104
+ * );
105
+ * ```
106
+ */
107
+ export declare function httpDownload(url: string, destPath: string, headers?: Record<string, string>, onProgress?: ProgressCallback, timeout?: number): Promise<Result<void, HttpError>>;
@@ -0,0 +1,238 @@
1
+ import { createWriteStream } from 'node:fs';
2
+ import { mkdir } from 'node:fs/promises';
3
+ import { dirname } from 'node:path';
4
+ import { pipeline } from 'node:stream/promises';
5
+ import got, { HTTPError, RequestError, TimeoutError } from 'got';
6
+ import { err, ok } from 'neverthrow';
7
+ /**
8
+ * デフォルトのタイムアウト(ミリ秒)
9
+ */
10
+ const DEFAULT_TIMEOUT = 30000;
11
+ /**
12
+ * リトライ対象のステータスコード
13
+ */
14
+ const RETRY_STATUS_CODES = [408, 429, 500, 502, 503, 504];
15
+ /**
16
+ * リトライ回数
17
+ */
18
+ const DEFAULT_RETRY_LIMIT = 2;
19
+ /**
20
+ * HTTP リクエストを実行する
21
+ *
22
+ * got ライブラリを使用して HTTP リクエストを実行し、Result 型で結果を返す。
23
+ * 認証ヘッダーの付与、リトライ、タイムアウト処理をサポート。
24
+ *
25
+ * @typeParam T - レスポンスデータの型
26
+ * @param url - リクエスト先の URL
27
+ * @param options - リクエストオプション(メソッド、ヘッダー、ボディ、タイムアウト)
28
+ * @returns 成功時は {@link HttpResponse} を含む Ok、失敗時は {@link HttpError} を含む Err
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * // GET リクエスト
33
+ * const result = await httpRequest<{ id: string }>('https://api.example.com/data');
34
+ * if (result.isOk()) {
35
+ * console.log(result.value.data.id);
36
+ * }
37
+ * ```
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // 認証付き GET リクエスト
42
+ * const result = await httpRequest<{ user: string }>('https://api.example.com/me', {
43
+ * headers: { Authorization: 'Basic xxx' }
44
+ * });
45
+ * ```
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * // POST リクエスト
50
+ * const result = await httpRequest<{ id: string }>('https://api.example.com/create', {
51
+ * method: 'POST',
52
+ * headers: { 'Content-Type': 'application/json' },
53
+ * body: JSON.stringify({ name: 'test' }),
54
+ * });
55
+ * ```
56
+ */
57
+ export async function httpRequest(url, options = {}) {
58
+ const { method = 'GET', headers = {}, body, timeout = DEFAULT_TIMEOUT } = options;
59
+ try {
60
+ const response = await got(url, {
61
+ body,
62
+ headers,
63
+ method,
64
+ responseType: 'json',
65
+ retry: {
66
+ limit: DEFAULT_RETRY_LIMIT,
67
+ statusCodes: RETRY_STATUS_CODES,
68
+ },
69
+ timeout: {
70
+ request: timeout,
71
+ },
72
+ });
73
+ // ヘッダーを Record<string, string> に変換
74
+ const responseHeaders = {};
75
+ for (const [key, value] of Object.entries(response.headers)) {
76
+ if (typeof value === 'string') {
77
+ responseHeaders[key] = value;
78
+ }
79
+ else if (Array.isArray(value)) {
80
+ responseHeaders[key] = value.join(', ');
81
+ }
82
+ }
83
+ return ok({
84
+ data: response.body,
85
+ headers: responseHeaders,
86
+ status: response.statusCode,
87
+ });
88
+ }
89
+ catch (error) {
90
+ return err(mapErrorToHttpError(error));
91
+ }
92
+ }
93
+ /**
94
+ * エラーを HttpError にマッピングする
95
+ *
96
+ * @param error - キャッチしたエラー
97
+ * @returns 対応する HttpError
98
+ *
99
+ * @internal テスト用に export
100
+ */
101
+ export function mapErrorToHttpError(error) {
102
+ // タイムアウトエラー
103
+ if (error instanceof TimeoutError) {
104
+ return {
105
+ kind: 'TIMEOUT',
106
+ message: `リクエストがタイムアウトしました: ${error.message}`,
107
+ };
108
+ }
109
+ // HTTP エラー(4xx, 5xx)
110
+ if (error instanceof HTTPError) {
111
+ const status = error.response.statusCode;
112
+ const statusText = getStatusText(status);
113
+ return {
114
+ kind: 'HTTP_ERROR',
115
+ message: `HTTP ${status} ${statusText}: ${error.message}`,
116
+ status,
117
+ };
118
+ }
119
+ // その他の got エラー(ネットワークエラー等)
120
+ if (error instanceof RequestError) {
121
+ return {
122
+ kind: 'NETWORK_ERROR',
123
+ message: `ネットワークエラーが発生しました: ${error.message}`,
124
+ };
125
+ }
126
+ // 不明なエラー
127
+ const message = error instanceof Error ? error.message : '不明なエラーが発生しました';
128
+ return {
129
+ kind: 'NETWORK_ERROR',
130
+ message: `リクエストエラー: ${message}`,
131
+ };
132
+ }
133
+ /**
134
+ * HTTP ステータスコードに対応するテキストを取得する
135
+ *
136
+ * @param status - HTTP ステータスコード
137
+ * @returns ステータステキスト
138
+ *
139
+ * @internal テスト用に export
140
+ */
141
+ export function getStatusText(status) {
142
+ const statusTexts = {
143
+ 400: 'Bad Request',
144
+ 401: 'Unauthorized',
145
+ 403: 'Forbidden',
146
+ 404: 'Not Found',
147
+ 405: 'Method Not Allowed',
148
+ 408: 'Request Timeout',
149
+ 429: 'Too Many Requests',
150
+ 500: 'Internal Server Error',
151
+ 502: 'Bad Gateway',
152
+ 503: 'Service Unavailable',
153
+ 504: 'Gateway Timeout',
154
+ };
155
+ return statusTexts[status] ?? 'Error';
156
+ }
157
+ /**
158
+ * ファイルをダウンロードして指定パスに保存する
159
+ *
160
+ * got ライブラリを使用してストリームでファイルをダウンロードし、
161
+ * 指定したパスに保存する。大きなファイルでもメモリを節約できる。
162
+ *
163
+ * @param url - ダウンロード対象の URL
164
+ * @param destPath - 保存先ファイルパス
165
+ * @param headers - リクエストヘッダー(認証ヘッダー等)
166
+ * @param onProgress - 進捗コールバック関数
167
+ * @param timeout - タイムアウト(ミリ秒)
168
+ * @returns 成功時は void を含む Ok、失敗時は {@link HttpError} を含む Err
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * // 基本的なダウンロード
173
+ * const result = await httpDownload(
174
+ * 'https://example.com/file.pdf',
175
+ * '/path/to/save/file.pdf'
176
+ * );
177
+ * ```
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * // 認証付きダウンロード
182
+ * const result = await httpDownload(
183
+ * 'https://api.example.com/attachments/123',
184
+ * '/path/to/save/attachment.pdf',
185
+ * { Authorization: 'Basic xxx' }
186
+ * );
187
+ * ```
188
+ *
189
+ * @example
190
+ * ```typescript
191
+ * // 進捗コールバック付きダウンロード
192
+ * const result = await httpDownload(
193
+ * 'https://example.com/large-file.zip',
194
+ * '/path/to/save/large-file.zip',
195
+ * undefined,
196
+ * (transferred, total) => {
197
+ * if (total) {
198
+ * console.log(`Progress: ${(transferred / total * 100).toFixed(1)}%`);
199
+ * }
200
+ * }
201
+ * );
202
+ * ```
203
+ */
204
+ export async function httpDownload(url, destPath, headers, onProgress, timeout = DEFAULT_TIMEOUT) {
205
+ try {
206
+ // 保存先ディレクトリを作成
207
+ await mkdir(dirname(destPath), { recursive: true });
208
+ // ダウンロードストリームを作成
209
+ const downloadStream = got.stream(url, {
210
+ ...(headers && { headers }),
211
+ retry: {
212
+ limit: DEFAULT_RETRY_LIMIT,
213
+ statusCodes: RETRY_STATUS_CODES,
214
+ },
215
+ timeout: {
216
+ request: timeout,
217
+ },
218
+ });
219
+ // 進捗追跡
220
+ let transferred = 0;
221
+ let total;
222
+ downloadStream.on('downloadProgress', (progress) => {
223
+ transferred = progress.transferred;
224
+ total = progress.total;
225
+ if (onProgress) {
226
+ onProgress(transferred, total);
227
+ }
228
+ });
229
+ // ファイル書き込みストリームを作成
230
+ const writeStream = createWriteStream(destPath);
231
+ // ストリームをパイプラインで接続
232
+ await pipeline(downloadStream, writeStream);
233
+ return ok(undefined);
234
+ }
235
+ catch (error) {
236
+ return err(mapErrorToHttpError(error));
237
+ }
238
+ }
@@ -0,0 +1 @@
1
+ export { httpDownload, httpRequest } from './http-port.js';
@@ -0,0 +1 @@
1
+ export { httpDownload, httpRequest } from './http-port.js';
@@ -0,0 +1,79 @@
1
+ import { type Result } from 'neverthrow';
2
+ import { type ZodIssue } from 'zod';
3
+ import type { AuthError, Credentials } from '../../types/auth.js';
4
+ /**
5
+ * Zod の issues 配列からエラーの種別を判定し、AuthError を生成する
6
+ *
7
+ * @t3-oss/env-core は onValidationError で ZodError ではなく issues 配列を渡す
8
+ *
9
+ * @param issues - Zod バリデーションエラーの issues 配列
10
+ * @returns 適切な AuthError
11
+ *
12
+ * @internal テスト用に export
13
+ */
14
+ export declare function mapZodIssuesToAuthError(issues: ZodIssue[]): AuthError;
15
+ /**
16
+ * 環境変数から Atlassian Cloud の認証情報を取得する
17
+ *
18
+ * @t3-oss/env-core と zod を使用して環境変数をバリデーションし、
19
+ * 型安全な認証情報を返す。
20
+ *
21
+ * 必要な環境変数:
22
+ * - `ATLASSIAN_EMAIL`: Atlassian アカウントのメールアドレス(有効なメール形式)
23
+ * - `ATLASSIAN_API_TOKEN`: Atlassian API トークン(空でない文字列)
24
+ *
25
+ * @returns 成功時は {@link Credentials} を含む Ok、失敗時は {@link AuthError} を含す Err
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * // 成功例
30
+ * process.env.ATLASSIAN_EMAIL = 'user@example.com';
31
+ * process.env.ATLASSIAN_API_TOKEN = 'your-api-token';
32
+ * const result = getCredentials();
33
+ * if (result.isOk()) {
34
+ * console.log(result.value.email); // 'user@example.com'
35
+ * }
36
+ * ```
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * // 失敗例: 環境変数未設定
41
+ * delete process.env.ATLASSIAN_EMAIL;
42
+ * const result = getCredentials();
43
+ * if (result.isErr()) {
44
+ * console.log(result.error.kind); // 'MISSING_EMAIL'
45
+ * }
46
+ * ```
47
+ */
48
+ export declare function getCredentials(): Result<Credentials, AuthError>;
49
+ /**
50
+ * Basic Auth ヘッダーを生成する
51
+ *
52
+ * 環境変数から認証情報を取得し、`Authorization: Basic <base64>` 形式の
53
+ * ヘッダー値を生成する。email:token を base64 エンコードする。
54
+ *
55
+ * @returns 成功時は `Basic <base64>` 形式の文字列を含む Ok、失敗時は {@link AuthError} を含む Err
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * // 成功例
60
+ * process.env.ATLASSIAN_EMAIL = 'user@example.com';
61
+ * process.env.ATLASSIAN_API_TOKEN = 'your-api-token';
62
+ * const result = getAuthHeader();
63
+ * if (result.isOk()) {
64
+ * // result.value は 'Basic dXNlckBleGFtcGxlLmNvbTp5b3VyLWFwaS10b2tlbg=='
65
+ * fetch(url, { headers: { Authorization: result.value } });
66
+ * }
67
+ * ```
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * // 失敗例: 環境変数未設定
72
+ * delete process.env.ATLASSIAN_EMAIL;
73
+ * const result = getAuthHeader();
74
+ * if (result.isErr()) {
75
+ * console.error(result.error.message);
76
+ * }
77
+ * ```
78
+ */
79
+ export declare function getAuthHeader(): Result<string, AuthError>;
@@ -0,0 +1,158 @@
1
+ import { createEnv } from '@t3-oss/env-core';
2
+ import { err, ok } from 'neverthrow';
3
+ import { z } from 'zod';
4
+ /**
5
+ * 環境変数スキーマの定義
6
+ *
7
+ * @t3-oss/env-core と zod を使用して環境変数をバリデーション
8
+ */
9
+ const envSchema = {
10
+ ATLASSIAN_API_TOKEN: z.string().min(1, 'API トークンは必須です'),
11
+ ATLASSIAN_EMAIL: z.string().min(1, 'メールアドレスは必須です').email('有効なメールアドレスを指定してください'),
12
+ };
13
+ /**
14
+ * Zod の issues 配列からエラーの種別を判定し、AuthError を生成する
15
+ *
16
+ * @t3-oss/env-core は onValidationError で ZodError ではなく issues 配列を渡す
17
+ *
18
+ * @param issues - Zod バリデーションエラーの issues 配列
19
+ * @returns 適切な AuthError
20
+ *
21
+ * @internal テスト用に export
22
+ */
23
+ export function mapZodIssuesToAuthError(issues) {
24
+ // 最初のエラーを取得
25
+ const firstIssue = issues[0];
26
+ if (!firstIssue) {
27
+ return {
28
+ kind: 'INVALID_CREDENTIALS',
29
+ message: '認証情報のバリデーションに失敗しました。',
30
+ };
31
+ }
32
+ const path = firstIssue.path[0];
33
+ const code = firstIssue.code;
34
+ // ATLASSIAN_EMAIL のエラー
35
+ if (path === 'ATLASSIAN_EMAIL') {
36
+ // 未設定または空の場合
37
+ if (code === 'invalid_type' || code === 'too_small') {
38
+ return {
39
+ kind: 'MISSING_EMAIL',
40
+ message: '環境変数 ATLASSIAN_EMAIL が設定されていません。Atlassian アカウントのメールアドレスを設定してください。',
41
+ };
42
+ }
43
+ // メール形式が無効
44
+ return {
45
+ kind: 'INVALID_EMAIL',
46
+ message: `無効なメールアドレス形式です。有効なメールアドレスを ATLASSIAN_EMAIL に設定してください。`,
47
+ };
48
+ }
49
+ // ATLASSIAN_API_TOKEN のエラー
50
+ if (path === 'ATLASSIAN_API_TOKEN') {
51
+ return {
52
+ kind: 'MISSING_TOKEN',
53
+ message: '環境変数 ATLASSIAN_API_TOKEN が設定されていません。Atlassian API トークンを設定してください。トークンは https://id.atlassian.com/manage-profile/security/api-tokens で作成できます。',
54
+ };
55
+ }
56
+ return {
57
+ kind: 'INVALID_CREDENTIALS',
58
+ message: `認証情報のバリデーションに失敗しました: ${firstIssue.message}`,
59
+ };
60
+ }
61
+ /**
62
+ * 環境変数から Atlassian Cloud の認証情報を取得する
63
+ *
64
+ * @t3-oss/env-core と zod を使用して環境変数をバリデーションし、
65
+ * 型安全な認証情報を返す。
66
+ *
67
+ * 必要な環境変数:
68
+ * - `ATLASSIAN_EMAIL`: Atlassian アカウントのメールアドレス(有効なメール形式)
69
+ * - `ATLASSIAN_API_TOKEN`: Atlassian API トークン(空でない文字列)
70
+ *
71
+ * @returns 成功時は {@link Credentials} を含む Ok、失敗時は {@link AuthError} を含す Err
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * // 成功例
76
+ * process.env.ATLASSIAN_EMAIL = 'user@example.com';
77
+ * process.env.ATLASSIAN_API_TOKEN = 'your-api-token';
78
+ * const result = getCredentials();
79
+ * if (result.isOk()) {
80
+ * console.log(result.value.email); // 'user@example.com'
81
+ * }
82
+ * ```
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * // 失敗例: 環境変数未設定
87
+ * delete process.env.ATLASSIAN_EMAIL;
88
+ * const result = getCredentials();
89
+ * if (result.isErr()) {
90
+ * console.log(result.error.kind); // 'MISSING_EMAIL'
91
+ * }
92
+ * ```
93
+ */
94
+ export function getCredentials() {
95
+ let validationError = null;
96
+ try {
97
+ const env = createEnv({
98
+ onValidationError: (issues) => {
99
+ // @t3-oss/env-core は issues 配列を渡す
100
+ validationError = mapZodIssuesToAuthError(issues);
101
+ throw new Error('Validation failed');
102
+ },
103
+ runtimeEnv: process.env,
104
+ server: envSchema,
105
+ });
106
+ return ok({
107
+ apiToken: env.ATLASSIAN_API_TOKEN,
108
+ email: env.ATLASSIAN_EMAIL,
109
+ });
110
+ }
111
+ catch {
112
+ if (validationError) {
113
+ return err(validationError);
114
+ }
115
+ // 予期しないエラー
116
+ return err({
117
+ kind: 'INVALID_CREDENTIALS',
118
+ message: '認証情報の取得中に予期しないエラーが発生しました。',
119
+ });
120
+ }
121
+ }
122
+ /**
123
+ * Basic Auth ヘッダーを生成する
124
+ *
125
+ * 環境変数から認証情報を取得し、`Authorization: Basic <base64>` 形式の
126
+ * ヘッダー値を生成する。email:token を base64 エンコードする。
127
+ *
128
+ * @returns 成功時は `Basic <base64>` 形式の文字列を含む Ok、失敗時は {@link AuthError} を含む Err
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * // 成功例
133
+ * process.env.ATLASSIAN_EMAIL = 'user@example.com';
134
+ * process.env.ATLASSIAN_API_TOKEN = 'your-api-token';
135
+ * const result = getAuthHeader();
136
+ * if (result.isOk()) {
137
+ * // result.value は 'Basic dXNlckBleGFtcGxlLmNvbTp5b3VyLWFwaS10b2tlbg=='
138
+ * fetch(url, { headers: { Authorization: result.value } });
139
+ * }
140
+ * ```
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * // 失敗例: 環境変数未設定
145
+ * delete process.env.ATLASSIAN_EMAIL;
146
+ * const result = getAuthHeader();
147
+ * if (result.isErr()) {
148
+ * console.error(result.error.message);
149
+ * }
150
+ * ```
151
+ */
152
+ export function getAuthHeader() {
153
+ return getCredentials().map((credentials) => {
154
+ const rawCredentials = `${credentials.email}:${credentials.apiToken}`;
155
+ const base64Encoded = Buffer.from(rawCredentials).toString('base64');
156
+ return `Basic ${base64Encoded}`;
157
+ });
158
+ }
@@ -0,0 +1 @@
1
+ export { getAuthHeader, getCredentials } from './auth-service.js';
@@ -0,0 +1 @@
1
+ export { getAuthHeader, getCredentials } from './auth-service.js';