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