claude-code-jsonl-parser 0.0.0 → 0.1.0-beta.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 ADDED
@@ -0,0 +1,802 @@
1
+ import { createReadStream } from "node:fs";
2
+ import { createInterface } from "node:readline";
3
+ //#region src/result.ts
4
+ /** `OkResult<T>` の実装。 */
5
+ var OkResultImpl = class OkResultImpl {
6
+ value;
7
+ isOk = true;
8
+ isErr = false;
9
+ constructor(value) {
10
+ this.value = value;
11
+ }
12
+ map(fn) {
13
+ return new OkResultImpl(fn(this.value));
14
+ }
15
+ mapErr(_fn) {
16
+ return this;
17
+ }
18
+ andThen(fn) {
19
+ return fn(this.value);
20
+ }
21
+ match(onOk, _onErr) {
22
+ return onOk(this.value);
23
+ }
24
+ unwrapOr(_fallback) {
25
+ return this.value;
26
+ }
27
+ };
28
+ /** `ErrResult<E>` の実装。 */
29
+ var ErrResultImpl = class ErrResultImpl {
30
+ error;
31
+ isOk = false;
32
+ isErr = true;
33
+ constructor(error) {
34
+ this.error = error;
35
+ }
36
+ map(_fn) {
37
+ return this;
38
+ }
39
+ mapErr(fn) {
40
+ return new ErrResultImpl(fn(this.error));
41
+ }
42
+ andThen(_fn) {
43
+ return this;
44
+ }
45
+ match(_onOk, onErr) {
46
+ return onErr(this.error);
47
+ }
48
+ unwrapOr(fallback) {
49
+ return fallback;
50
+ }
51
+ };
52
+ /**
53
+ * 成功 `Result<T, never>` を生成する。
54
+ *
55
+ * @param value - 成功値
56
+ */
57
+ function ok(value) {
58
+ return new OkResultImpl(value);
59
+ }
60
+ /**
61
+ * 失敗 `Result<never, E>` を生成する。
62
+ *
63
+ * @param error - エラー値
64
+ */
65
+ function err(error) {
66
+ return new ErrResultImpl(error);
67
+ }
68
+ /**
69
+ * `await` 可能で `map / mapErr / andThen` を連鎖できる `PromiseLike<Result<T, E>>`。
70
+ */
71
+ var ResultAsync = class ResultAsync {
72
+ _promise;
73
+ constructor(promise) {
74
+ this._promise = promise;
75
+ }
76
+ then(onfulfilled, onrejected) {
77
+ return this._promise.then(onfulfilled, onrejected);
78
+ }
79
+ /**
80
+ * `Promise<T>` を `ResultAsync<T, E>` に包む。reject 時は `onError` で E に写像する。
81
+ *
82
+ * @param promise - 対象 Promise
83
+ * @param onError - エラー写像
84
+ */
85
+ static fromPromise(promise, onError) {
86
+ return new ResultAsync(promise.then((v) => ok(v), (error) => err(onError(error))));
87
+ }
88
+ /**
89
+ * 解決済み `Result<T, E>` を `ResultAsync<T, E>` に包む。
90
+ *
91
+ * @param result - 対象 Result
92
+ */
93
+ static fromResult(result) {
94
+ return new ResultAsync(Promise.resolve(result));
95
+ }
96
+ /**
97
+ * 成功値を変換する。
98
+ *
99
+ * @param fn - 同期写像
100
+ */
101
+ map(fn) {
102
+ return new ResultAsync(this._promise.then((r) => r.map(fn)));
103
+ }
104
+ /**
105
+ * エラー値を変換する。
106
+ *
107
+ * @param fn - 同期エラー写像
108
+ */
109
+ mapErr(fn) {
110
+ return new ResultAsync(this._promise.then((r) => r.mapErr(fn)));
111
+ }
112
+ /**
113
+ * 失敗しうる非同期処理を連鎖する。
114
+ *
115
+ * @param fn - `ResultAsync<U, F>` または `Result<U, F>` を返す写像
116
+ */
117
+ andThen(fn) {
118
+ return new ResultAsync(this._promise.then(async (r) => {
119
+ if (r.isErr) return r;
120
+ const next = fn(r.value);
121
+ if (next instanceof ResultAsync) return next._promise;
122
+ return next;
123
+ }));
124
+ }
125
+ /**
126
+ * 成功 / 失敗でパターンマッチする。
127
+ *
128
+ * @param onOk - 成功値ハンドラ
129
+ * @param onErr - エラー値ハンドラ
130
+ */
131
+ async match(onOk, onErr) {
132
+ const r = await this._promise;
133
+ if (r.isOk) return onOk(r.value);
134
+ return onErr(r.error);
135
+ }
136
+ /**
137
+ * 成功なら value、失敗なら fallback を返す。
138
+ *
139
+ * @param fallback - フォールバック値
140
+ */
141
+ async unwrapOr(fallback) {
142
+ const r = await this._promise;
143
+ if (r.isOk) return r.value;
144
+ return fallback;
145
+ }
146
+ };
147
+ //#endregion
148
+ //#region src/generated/guards.ts
149
+ /** 配列でも null でもないプレーンオブジェクトかどうかを判定する。 */
150
+ function isObject(v) {
151
+ return typeof v === "object" && v !== null && !Array.isArray(v);
152
+ }
153
+ /** `string` かどうかを判定する。 */
154
+ function isString(v) {
155
+ return typeof v === "string";
156
+ }
157
+ /** `number` かどうかを判定する。 */
158
+ function isNumber(v) {
159
+ return typeof v === "number";
160
+ }
161
+ /** `boolean` かどうかを判定する。 */
162
+ function isBoolean(v) {
163
+ return typeof v === "boolean";
164
+ }
165
+ /** `[フィールド名, 検査結果]` の配列から、不一致だったフィールド名をまとめた診断メッセージを作る。 */
166
+ function fieldCheck(fields) {
167
+ const failing = fields.filter(([, ok]) => !ok).map(([name]) => name);
168
+ return failing.length > 0 ? `missing/invalid field(s): ${failing.join(", ")}` : void 0;
169
+ }
170
+ /** フルメタデータ系の共通必須フィールドを検査し、不一致理由を返す (一致なら undefined)。 */
171
+ function entryBaseReason(o) {
172
+ return fieldCheck([
173
+ ["cwd", isString(o.cwd)],
174
+ ["entrypoint", isString(o.entrypoint)],
175
+ ["gitBranch", isString(o.gitBranch)],
176
+ ["isSidechain", isBoolean(o.isSidechain)],
177
+ ["parentUuid", o.parentUuid === null || isString(o.parentUuid)],
178
+ ["sessionId", isString(o.sessionId)],
179
+ ["timestamp", isString(o.timestamp)],
180
+ ["userType", isString(o.userType)],
181
+ ["uuid", isString(o.uuid)],
182
+ ["version", isString(o.version)]
183
+ ]);
184
+ }
185
+ function isAssistant(v) {
186
+ if (!isObject(v)) return {
187
+ ok: false,
188
+ reason: "not an object"
189
+ };
190
+ if (v.type !== "assistant") return {
191
+ ok: false,
192
+ reason: "type is not \"assistant\""
193
+ };
194
+ const baseReason = entryBaseReason(v);
195
+ if (baseReason !== void 0) return {
196
+ ok: false,
197
+ reason: baseReason
198
+ };
199
+ if (!isObject(v.message)) return {
200
+ ok: false,
201
+ reason: "message is not an object"
202
+ };
203
+ const reason = fieldCheck([
204
+ ["message.role", isString(v.message.role)],
205
+ ["message.id", isString(v.message.id)],
206
+ ["message.model", isString(v.message.model)]
207
+ ]);
208
+ return reason === void 0 ? { ok: true } : {
209
+ ok: false,
210
+ reason
211
+ };
212
+ }
213
+ function isUser(v) {
214
+ if (!isObject(v)) return {
215
+ ok: false,
216
+ reason: "not an object"
217
+ };
218
+ if (v.type !== "user") return {
219
+ ok: false,
220
+ reason: "type is not \"user\""
221
+ };
222
+ const baseReason = entryBaseReason(v);
223
+ if (baseReason !== void 0) return {
224
+ ok: false,
225
+ reason: baseReason
226
+ };
227
+ if (!isObject(v.message)) return {
228
+ ok: false,
229
+ reason: "message is not an object"
230
+ };
231
+ const reason = fieldCheck([["message.role", isString(v.message.role)]]);
232
+ return reason === void 0 ? { ok: true } : {
233
+ ok: false,
234
+ reason
235
+ };
236
+ }
237
+ function isAttachment(v) {
238
+ if (!isObject(v)) return {
239
+ ok: false,
240
+ reason: "not an object"
241
+ };
242
+ if (v.type !== "attachment") return {
243
+ ok: false,
244
+ reason: "type is not \"attachment\""
245
+ };
246
+ const baseReason = entryBaseReason(v);
247
+ if (baseReason !== void 0) return {
248
+ ok: false,
249
+ reason: baseReason
250
+ };
251
+ const reason = fieldCheck([["attachment", isObject(v.attachment)]]);
252
+ return reason === void 0 ? { ok: true } : {
253
+ ok: false,
254
+ reason
255
+ };
256
+ }
257
+ function isSystem(v) {
258
+ if (!isObject(v)) return {
259
+ ok: false,
260
+ reason: "not an object"
261
+ };
262
+ if (v.type !== "system") return {
263
+ ok: false,
264
+ reason: "type is not \"system\""
265
+ };
266
+ const baseReason = entryBaseReason(v);
267
+ if (baseReason !== void 0) return {
268
+ ok: false,
269
+ reason: baseReason
270
+ };
271
+ const reason = fieldCheck([["subtype", isString(v.subtype)]]);
272
+ return reason === void 0 ? { ok: true } : {
273
+ ok: false,
274
+ reason
275
+ };
276
+ }
277
+ function isMode(v) {
278
+ if (!isObject(v)) return {
279
+ ok: false,
280
+ reason: "not an object"
281
+ };
282
+ if (v.type !== "mode") return {
283
+ ok: false,
284
+ reason: "type is not \"mode\""
285
+ };
286
+ const reason = fieldCheck([["mode", isString(v.mode)], ["sessionId", isString(v.sessionId)]]);
287
+ return reason === void 0 ? { ok: true } : {
288
+ ok: false,
289
+ reason
290
+ };
291
+ }
292
+ function isPermissionMode(v) {
293
+ if (!isObject(v)) return {
294
+ ok: false,
295
+ reason: "not an object"
296
+ };
297
+ if (v.type !== "permission-mode") return {
298
+ ok: false,
299
+ reason: "type is not \"permission-mode\""
300
+ };
301
+ const reason = fieldCheck([["permissionMode", isString(v.permissionMode)], ["sessionId", isString(v.sessionId)]]);
302
+ return reason === void 0 ? { ok: true } : {
303
+ ok: false,
304
+ reason
305
+ };
306
+ }
307
+ function isAiTitle(v) {
308
+ if (!isObject(v)) return {
309
+ ok: false,
310
+ reason: "not an object"
311
+ };
312
+ if (v.type !== "ai-title") return {
313
+ ok: false,
314
+ reason: "type is not \"ai-title\""
315
+ };
316
+ const reason = fieldCheck([["aiTitle", isString(v.aiTitle)], ["sessionId", isString(v.sessionId)]]);
317
+ return reason === void 0 ? { ok: true } : {
318
+ ok: false,
319
+ reason
320
+ };
321
+ }
322
+ function isAgentName(v) {
323
+ if (!isObject(v)) return {
324
+ ok: false,
325
+ reason: "not an object"
326
+ };
327
+ if (v.type !== "agent-name") return {
328
+ ok: false,
329
+ reason: "type is not \"agent-name\""
330
+ };
331
+ const reason = fieldCheck([["agentName", isString(v.agentName)], ["sessionId", isString(v.sessionId)]]);
332
+ return reason === void 0 ? { ok: true } : {
333
+ ok: false,
334
+ reason
335
+ };
336
+ }
337
+ function isAgentSetting(v) {
338
+ if (!isObject(v)) return {
339
+ ok: false,
340
+ reason: "not an object"
341
+ };
342
+ if (v.type !== "agent-setting") return {
343
+ ok: false,
344
+ reason: "type is not \"agent-setting\""
345
+ };
346
+ const reason = fieldCheck([["agentSetting", isString(v.agentSetting)], ["sessionId", isString(v.sessionId)]]);
347
+ return reason === void 0 ? { ok: true } : {
348
+ ok: false,
349
+ reason
350
+ };
351
+ }
352
+ function isRelocated(v) {
353
+ if (!isObject(v)) return {
354
+ ok: false,
355
+ reason: "not an object"
356
+ };
357
+ if (v.type !== "relocated") return {
358
+ ok: false,
359
+ reason: "type is not \"relocated\""
360
+ };
361
+ const reason = fieldCheck([["relocatedCwd", isString(v.relocatedCwd)], ["sessionId", isString(v.sessionId)]]);
362
+ return reason === void 0 ? { ok: true } : {
363
+ ok: false,
364
+ reason
365
+ };
366
+ }
367
+ function isLastPrompt(v) {
368
+ if (!isObject(v)) return {
369
+ ok: false,
370
+ reason: "not an object"
371
+ };
372
+ if (v.type !== "last-prompt") return {
373
+ ok: false,
374
+ reason: "type is not \"last-prompt\""
375
+ };
376
+ const reason = fieldCheck([["leafUuid", isString(v.leafUuid)], ["sessionId", isString(v.sessionId)]]);
377
+ return reason === void 0 ? { ok: true } : {
378
+ ok: false,
379
+ reason
380
+ };
381
+ }
382
+ function isWorktreeState(v) {
383
+ if (!isObject(v)) return {
384
+ ok: false,
385
+ reason: "not an object"
386
+ };
387
+ if (v.type !== "worktree-state") return {
388
+ ok: false,
389
+ reason: "type is not \"worktree-state\""
390
+ };
391
+ const reason = fieldCheck([["sessionId", isString(v.sessionId)], ["worktreeSession", v.worktreeSession === null || isObject(v.worktreeSession)]]);
392
+ return reason === void 0 ? { ok: true } : {
393
+ ok: false,
394
+ reason
395
+ };
396
+ }
397
+ function isBridgeSession(v) {
398
+ if (!isObject(v)) return {
399
+ ok: false,
400
+ reason: "not an object"
401
+ };
402
+ if (v.type !== "bridge-session") return {
403
+ ok: false,
404
+ reason: "type is not \"bridge-session\""
405
+ };
406
+ const reason = fieldCheck([
407
+ ["bridgeSessionId", isString(v.bridgeSessionId)],
408
+ ["lastSequenceNum", isNumber(v.lastSequenceNum)],
409
+ ["sessionId", isString(v.sessionId)]
410
+ ]);
411
+ return reason === void 0 ? { ok: true } : {
412
+ ok: false,
413
+ reason
414
+ };
415
+ }
416
+ function isQueueOperation(v) {
417
+ if (!isObject(v)) return {
418
+ ok: false,
419
+ reason: "not an object"
420
+ };
421
+ if (v.type !== "queue-operation") return {
422
+ ok: false,
423
+ reason: "type is not \"queue-operation\""
424
+ };
425
+ const reason = fieldCheck([
426
+ ["operation", isString(v.operation)],
427
+ ["sessionId", isString(v.sessionId)],
428
+ ["timestamp", isString(v.timestamp)]
429
+ ]);
430
+ return reason === void 0 ? { ok: true } : {
431
+ ok: false,
432
+ reason
433
+ };
434
+ }
435
+ function isPrLink(v) {
436
+ if (!isObject(v)) return {
437
+ ok: false,
438
+ reason: "not an object"
439
+ };
440
+ if (v.type !== "pr-link") return {
441
+ ok: false,
442
+ reason: "type is not \"pr-link\""
443
+ };
444
+ const reason = fieldCheck([
445
+ ["prNumber", isNumber(v.prNumber)],
446
+ ["prRepository", isString(v.prRepository)],
447
+ ["prUrl", isString(v.prUrl)],
448
+ ["sessionId", isString(v.sessionId)],
449
+ ["timestamp", isString(v.timestamp)]
450
+ ]);
451
+ return reason === void 0 ? { ok: true } : {
452
+ ok: false,
453
+ reason
454
+ };
455
+ }
456
+ function isFrameLink(v) {
457
+ if (!isObject(v)) return {
458
+ ok: false,
459
+ reason: "not an object"
460
+ };
461
+ if (v.type !== "frame-link") return {
462
+ ok: false,
463
+ reason: "type is not \"frame-link\""
464
+ };
465
+ const reason = fieldCheck([
466
+ ["frameUrl", isString(v.frameUrl)],
467
+ ["path", isString(v.path)],
468
+ ["sessionId", isString(v.sessionId)],
469
+ ["timestamp", isString(v.timestamp)]
470
+ ]);
471
+ return reason === void 0 ? { ok: true } : {
472
+ ok: false,
473
+ reason
474
+ };
475
+ }
476
+ function isFileHistorySnapshot(v) {
477
+ if (!isObject(v)) return {
478
+ ok: false,
479
+ reason: "not an object"
480
+ };
481
+ if (v.type !== "file-history-snapshot") return {
482
+ ok: false,
483
+ reason: "type is not \"file-history-snapshot\""
484
+ };
485
+ const reason = fieldCheck([
486
+ ["isSnapshotUpdate", isBoolean(v.isSnapshotUpdate)],
487
+ ["messageId", isString(v.messageId)],
488
+ ["snapshot", isObject(v.snapshot)]
489
+ ]);
490
+ return reason === void 0 ? { ok: true } : {
491
+ ok: false,
492
+ reason
493
+ };
494
+ }
495
+ function isStarted(v) {
496
+ if (!isObject(v)) return {
497
+ ok: false,
498
+ reason: "not an object"
499
+ };
500
+ if (v.type !== "started") return {
501
+ ok: false,
502
+ reason: "type is not \"started\""
503
+ };
504
+ const reason = fieldCheck([["key", isString(v.key)], ["agentId", isString(v.agentId)]]);
505
+ return reason === void 0 ? { ok: true } : {
506
+ ok: false,
507
+ reason
508
+ };
509
+ }
510
+ function isResult(v) {
511
+ if (!isObject(v)) return {
512
+ ok: false,
513
+ reason: "not an object"
514
+ };
515
+ if (v.type !== "result") return {
516
+ ok: false,
517
+ reason: "type is not \"result\""
518
+ };
519
+ const reason = fieldCheck([
520
+ ["key", isString(v.key)],
521
+ ["agentId", isString(v.agentId)],
522
+ ["result", "result" in v]
523
+ ]);
524
+ return reason === void 0 ? { ok: true } : {
525
+ ok: false,
526
+ reason
527
+ };
528
+ }
529
+ /** type 文字列 → ガード関数のマップ。 */
530
+ const guards = {
531
+ assistant: isAssistant,
532
+ user: isUser,
533
+ attachment: isAttachment,
534
+ system: isSystem,
535
+ mode: isMode,
536
+ "permission-mode": isPermissionMode,
537
+ "ai-title": isAiTitle,
538
+ "agent-name": isAgentName,
539
+ "agent-setting": isAgentSetting,
540
+ relocated: isRelocated,
541
+ "last-prompt": isLastPrompt,
542
+ "worktree-state": isWorktreeState,
543
+ "bridge-session": isBridgeSession,
544
+ "queue-operation": isQueueOperation,
545
+ "pr-link": isPrLink,
546
+ "frame-link": isFrameLink,
547
+ "file-history-snapshot": isFileHistorySnapshot,
548
+ started: isStarted,
549
+ result: isResult
550
+ };
551
+ /** 既知 type 名の配列。 */
552
+ const KNOWN_TYPES = Object.keys(guards);
553
+ /**
554
+ * 指定 type のガードを適用する。ガードが存在しない type なら undefined。
555
+ * 一致すれば `{ ok: true }`、不一致なら `{ ok: false, reason }` (reason に
556
+ * 診断メッセージ、どのフィールドが原因で shape 不一致になったかを含む)。
557
+ *
558
+ * @param type - 判別子
559
+ * @param v - 検査対象の生 JSON
560
+ */
561
+ function guardByType(type, v) {
562
+ const g = guards[type];
563
+ if (g === void 0) return void 0;
564
+ return g(v);
565
+ }
566
+ function unknownBlock(raw) {
567
+ return {
568
+ _kind: "unknown",
569
+ raw
570
+ };
571
+ }
572
+ function isTextBlock(v) {
573
+ return v.type === "text" && isString(v.text);
574
+ }
575
+ function isThinkingBlock(v) {
576
+ return v.type === "thinking" && isString(v.thinking) && isString(v.signature);
577
+ }
578
+ function isToolUseBlock(v) {
579
+ return v.type === "tool_use" && isString(v.id) && isString(v.name) && "input" in v;
580
+ }
581
+ function isImageBlock(v) {
582
+ return v.type === "image" && isObject(v.source) && isString(v.source.media_type) && isString(v.source.data);
583
+ }
584
+ function isToolReferenceBlock(v) {
585
+ return v.type === "tool_reference" && isString(v.tool_name);
586
+ }
587
+ /** tool_result の inner content 配列を正規化する。 */
588
+ function normalizeToolResultContent(content) {
589
+ if (isString(content)) return content;
590
+ if (!Array.isArray(content)) return [unknownBlock(content)];
591
+ return content.map((el) => {
592
+ if (!isObject(el)) return unknownBlock(el);
593
+ if (isTextBlock(el) || isImageBlock(el) || isToolReferenceBlock(el)) return {
594
+ ...el,
595
+ _kind: "known"
596
+ };
597
+ return unknownBlock(el);
598
+ });
599
+ }
600
+ /** tool_result ブロック全体を検査・正規化する。 */
601
+ function normalizeToolResultBlock(v) {
602
+ if (v.type !== "tool_result" || !isString(v.tool_use_id)) return unknownBlock(v);
603
+ const block = {
604
+ _kind: "known",
605
+ type: "tool_result",
606
+ tool_use_id: v.tool_use_id,
607
+ content: normalizeToolResultContent(v.content)
608
+ };
609
+ if (isBoolean(v.is_error)) block.is_error = v.is_error;
610
+ return block;
611
+ }
612
+ /**
613
+ * assistant の message.content 配列を正規化する。
614
+ * 既知ブロックはそのまま、未知は UnknownContentBlock に降格する。
615
+ *
616
+ * @param content - 生の content 配列
617
+ */
618
+ function normalizeAssistantContent(content) {
619
+ if (!Array.isArray(content)) return [unknownBlock(content)];
620
+ return content.map((el) => {
621
+ if (!isObject(el)) return unknownBlock(el);
622
+ if (isTextBlock(el) || isThinkingBlock(el) || isToolUseBlock(el)) return {
623
+ ...el,
624
+ _kind: "known"
625
+ };
626
+ return unknownBlock(el);
627
+ });
628
+ }
629
+ /**
630
+ * user の message.content を正規化する。string ならそのまま、配列なら要素ごとに正規化。
631
+ *
632
+ * @param content - 生の content (string または配列)
633
+ */
634
+ function normalizeUserContent(content) {
635
+ if (isString(content)) return content;
636
+ if (!Array.isArray(content)) return [unknownBlock(content)];
637
+ return content.map((el) => {
638
+ if (!isObject(el)) return unknownBlock(el);
639
+ if (isTextBlock(el)) return {
640
+ ...el,
641
+ _kind: "known"
642
+ };
643
+ if (el.type === "tool_result") return normalizeToolResultBlock(el);
644
+ return unknownBlock(el);
645
+ });
646
+ }
647
+ //#endregion
648
+ //#region src/parse-line.ts
649
+ /**
650
+ * JSONL 1 行を ParsedLine に変換する。
651
+ *
652
+ * - JSON 構文エラー: `LineParseError`
653
+ * - 既知 type かつ shape 一致: `KnownEntry` (_kind='known' 付与、content 正規化)
654
+ * - 既知 type だが shape 不一致: `UnknownEntry` (typeHint に type 値)
655
+ * - 未知 type / type 無し: `UnknownEntry` (typeHint 無し)
656
+ *
657
+ * @param rawLine - 1 行分の文字列 (改行を含まない)
658
+ * @param lineNumber - 1 始まりの行番号
659
+ */
660
+ function parseLine(rawLine, lineNumber) {
661
+ let parsed;
662
+ try {
663
+ parsed = JSON.parse(rawLine);
664
+ } catch (error) {
665
+ return {
666
+ _kind: "error",
667
+ lineNumber,
668
+ rawLine,
669
+ message: error instanceof Error ? error.message : String(error)
670
+ };
671
+ }
672
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed) || typeof parsed.type !== "string") return {
673
+ _kind: "unknown",
674
+ lineNumber,
675
+ raw: parsed
676
+ };
677
+ const obj = parsed;
678
+ const type = obj.type;
679
+ const matched = guardByType(type, obj);
680
+ if (matched === void 0) return {
681
+ _kind: "unknown",
682
+ lineNumber,
683
+ raw: parsed
684
+ };
685
+ if (!matched.ok) return {
686
+ _kind: "unknown",
687
+ lineNumber,
688
+ raw: parsed,
689
+ typeHint: type,
690
+ reason: matched.reason
691
+ };
692
+ const entry = {
693
+ ...obj,
694
+ _kind: "known"
695
+ };
696
+ if (entry.type === "assistant") entry.message = {
697
+ ...entry.message,
698
+ content: normalizeAssistantContent(entry.message.content)
699
+ };
700
+ else if (entry.type === "user") entry.message = {
701
+ ...entry.message,
702
+ content: normalizeUserContent(entry.message.content)
703
+ };
704
+ return entry;
705
+ }
706
+ //#endregion
707
+ //#region src/errors.ts
708
+ /**
709
+ * FileReadError を生成する。
710
+ *
711
+ * @param path - 対象パス
712
+ * @param error - 捕捉した例外
713
+ */
714
+ function fileReadError(path, error) {
715
+ return {
716
+ _tag: "FileReadError",
717
+ path,
718
+ code: typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0,
719
+ message: error instanceof Error ? error.message : String(error)
720
+ };
721
+ }
722
+ //#endregion
723
+ //#region src/parse.ts
724
+ /**
725
+ * JSONL 文字列を行単位で逐次パースする同期イテレータ。
726
+ * `text.split('\n')` で全行配列を作らず、`indexOf` で 1 行ずつ切り出すため、
727
+ * 追加メモリは 1 行分にとどまる (全行配列分の追加確保が発生しない)。
728
+ *
729
+ * 空行 (トリム後に空) はスキップし、行番号カウントには含めない。
730
+ *
731
+ * @param text - JSONL テキスト全体
732
+ */
733
+ function* parseJsonlLines(text) {
734
+ let lineNumber = 0;
735
+ let start = 0;
736
+ while (start <= text.length) {
737
+ const nlIndex = text.indexOf("\n", start);
738
+ const end = nlIndex === -1 ? text.length : nlIndex;
739
+ const rawEnd = end > start && text[end - 1] === "\r" ? end - 1 : end;
740
+ const line = text.slice(start, rawEnd);
741
+ if (line.trim() !== "") {
742
+ lineNumber += 1;
743
+ yield parseLine(line, lineNumber);
744
+ }
745
+ if (nlIndex === -1) break;
746
+ start = end + 1;
747
+ }
748
+ }
749
+ /**
750
+ * JSONL 文字列全体を一括パースして配列で返す。
751
+ * 行単位のエラーは配列要素として表現されるため、この関数は Err を返さない。
752
+ *
753
+ * @param text - JSONL テキスト全体
754
+ */
755
+ function parseJsonl(text) {
756
+ return ok([...parseJsonlLines(text)]);
757
+ }
758
+ /**
759
+ * ファイルを行単位で逐次読み込み・パースする非同期イテレータ (定数メモリ)。
760
+ * I/O エラーは反復時に FileReadError を throw する。
761
+ *
762
+ * @param path - JSONL ファイルパス
763
+ */
764
+ async function* parseJsonlFileStream(path) {
765
+ const stream = createReadStream(path, { encoding: "utf8" });
766
+ const rl = createInterface({
767
+ input: stream,
768
+ crlfDelay: Infinity
769
+ });
770
+ let lineNumber = 0;
771
+ try {
772
+ for await (const raw of rl) {
773
+ if (raw.trim() === "") continue;
774
+ lineNumber += 1;
775
+ yield parseLine(raw, lineNumber);
776
+ }
777
+ } catch (error) {
778
+ throw fileReadError(path, error);
779
+ } finally {
780
+ rl.close();
781
+ stream.destroy();
782
+ }
783
+ }
784
+ /**
785
+ * ファイル全体を読み込み一括パースする。I/O エラーのみ Err になる。
786
+ *
787
+ * @param path - JSONL ファイルパス
788
+ */
789
+ function parseJsonlFile(path) {
790
+ return ResultAsync.fromPromise((async () => {
791
+ const out = [];
792
+ for await (const line of parseJsonlFileStream(path)) out.push(line);
793
+ return out;
794
+ })(), (error) => {
795
+ if (typeof error === "object" && error !== null && error._tag === "FileReadError") return error;
796
+ return fileReadError(path, error);
797
+ });
798
+ }
799
+ //#endregion
800
+ export { KNOWN_TYPES, ResultAsync, err, fileReadError, guardByType, guards, normalizeAssistantContent, normalizeUserContent, ok, parseJsonl, parseJsonlFile, parseJsonlFileStream, parseJsonlLines };
801
+
802
+ //# sourceMappingURL=index.js.map