@lark-apaas/fullstack-cli 1.1.6-alpha.13 → 1.1.6-alpha.14

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 (2) hide show
  1. package/dist/index.js +844 -7
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.ts
2
- import fs15 from "fs";
3
- import path15 from "path";
2
+ import fs18 from "fs";
3
+ import path16 from "path";
4
4
  import { fileURLToPath as fileURLToPath3 } from "url";
5
5
  import { config as dotenvConfig } from "dotenv";
6
6
 
@@ -3024,22 +3024,859 @@ var migrationCommand = {
3024
3024
  }
3025
3025
  };
3026
3026
 
3027
+ // src/commands/read-logs/index.ts
3028
+ import path15 from "path";
3029
+
3030
+ // src/commands/read-logs/std-utils.ts
3031
+ import fs15 from "fs";
3032
+ function formatStdPrefixTime(localTime) {
3033
+ const match = localTime.match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
3034
+ if (!match) return localTime;
3035
+ const year = Number(match[1]);
3036
+ const month = Number(match[2]);
3037
+ const day = Number(match[3]);
3038
+ const hour = Number(match[4]);
3039
+ const minute = Number(match[5]);
3040
+ const second = Number(match[6]);
3041
+ const date = new Date(year, month - 1, day, hour, minute, second, 0);
3042
+ if (Number.isNaN(date.getTime())) return localTime;
3043
+ const pad2 = (n) => String(n).padStart(2, "0");
3044
+ const tzOffsetMinutes = -date.getTimezoneOffset();
3045
+ const sign = tzOffsetMinutes >= 0 ? "+" : "-";
3046
+ const abs = Math.abs(tzOffsetMinutes);
3047
+ const offsetHH = pad2(Math.floor(abs / 60));
3048
+ const offsetMM = pad2(abs % 60);
3049
+ return `${localTime.replace(" ", "T")}${sign}${offsetHH}:${offsetMM}`;
3050
+ }
3051
+ function stripPrefixFromStdLine(line) {
3052
+ const match = line.match(/^(\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] \[(server|client)\] )(.*)$/);
3053
+ if (!match) {
3054
+ return line;
3055
+ }
3056
+ const time = formatStdPrefixTime(match[2] || "");
3057
+ const content = match[4] || "";
3058
+ return `[${time}] ${content}`;
3059
+ }
3060
+ function readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, isMarker) {
3061
+ const stat = fs15.statSync(filePath);
3062
+ if (stat.size === 0) {
3063
+ return { lines: [], markerFound: false, totalLinesCount: 0 };
3064
+ }
3065
+ const fd = fs15.openSync(filePath, "r");
3066
+ const chunkSize = 64 * 1024;
3067
+ let position = stat.size;
3068
+ let remainder = "";
3069
+ let markerFound = false;
3070
+ let finished = false;
3071
+ let totalLinesCount = 0;
3072
+ let skipped = 0;
3073
+ const collected = [];
3074
+ try {
3075
+ while (position > 0 && !finished) {
3076
+ const length = Math.min(chunkSize, position);
3077
+ position -= length;
3078
+ const buffer = Buffer.alloc(length);
3079
+ fs15.readSync(fd, buffer, 0, length, position);
3080
+ let chunk = buffer.toString("utf8");
3081
+ if (remainder) {
3082
+ chunk += remainder;
3083
+ remainder = "";
3084
+ }
3085
+ const parts = chunk.split("\n");
3086
+ remainder = parts.shift() ?? "";
3087
+ for (let i = parts.length - 1; i >= 0; i -= 1) {
3088
+ const rawLine = parts[i];
3089
+ const line = stripPrefixFromStdLine(rawLine);
3090
+ if (totalLinesCount === 0 && line === "") {
3091
+ continue;
3092
+ }
3093
+ totalLinesCount += 1;
3094
+ if (skipped < offset) {
3095
+ skipped += 1;
3096
+ } else if (collected.length < maxLines) {
3097
+ collected.push(line);
3098
+ }
3099
+ if (isMarker(line)) {
3100
+ markerFound = true;
3101
+ finished = true;
3102
+ break;
3103
+ }
3104
+ }
3105
+ }
3106
+ if (!finished && remainder) {
3107
+ const line = stripPrefixFromStdLine(remainder);
3108
+ if (!(totalLinesCount === 0 && line === "")) {
3109
+ totalLinesCount += 1;
3110
+ if (skipped < offset) {
3111
+ skipped += 1;
3112
+ } else if (collected.length < maxLines) {
3113
+ collected.push(line);
3114
+ }
3115
+ }
3116
+ if (isMarker(line)) {
3117
+ markerFound = true;
3118
+ }
3119
+ }
3120
+ } finally {
3121
+ fs15.closeSync(fd);
3122
+ }
3123
+ return { lines: collected.reverse(), markerFound, totalLinesCount };
3124
+ }
3125
+
3126
+ // src/commands/read-logs/server-std.ts
3127
+ function readServerStdSegment(filePath, maxLines, offset) {
3128
+ const marker = (line) => {
3129
+ if (!line) return false;
3130
+ if (/\bdev:server\b/.test(line)) return true;
3131
+ if (line.includes("Starting compilation in watch mode")) return true;
3132
+ if (line.includes("File change detected. Starting incremental compilation")) return true;
3133
+ if (line.includes("Starting Nest application")) return true;
3134
+ if (line.includes("Nest application successfully started")) return true;
3135
+ return false;
3136
+ };
3137
+ const segment = readStdLinesTailFromLastMarkerPaged(filePath, maxLines, offset, marker);
3138
+ return { lines: segment.lines, totalLinesCount: segment.totalLinesCount };
3139
+ }
3140
+
3141
+ // src/commands/read-logs/tail.ts
3142
+ import fs16 from "fs";
3143
+ function fileExists(filePath) {
3144
+ try {
3145
+ fs16.accessSync(filePath, fs16.constants.F_OK | fs16.constants.R_OK);
3146
+ return true;
3147
+ } catch {
3148
+ return false;
3149
+ }
3150
+ }
3151
+ function readFileTailLines(filePath, maxLines) {
3152
+ const stat = fs16.statSync(filePath);
3153
+ if (stat.size === 0) {
3154
+ return [];
3155
+ }
3156
+ const fd = fs16.openSync(filePath, "r");
3157
+ const chunkSize = 64 * 1024;
3158
+ const chunks = [];
3159
+ let position = stat.size;
3160
+ let collectedLines = 0;
3161
+ try {
3162
+ while (position > 0 && collectedLines <= maxLines) {
3163
+ const length = Math.min(chunkSize, position);
3164
+ position -= length;
3165
+ const buffer = Buffer.alloc(length);
3166
+ fs16.readSync(fd, buffer, 0, length, position);
3167
+ chunks.unshift(buffer.toString("utf8"));
3168
+ const chunkLines = buffer.toString("utf8").split("\n").length - 1;
3169
+ collectedLines += chunkLines;
3170
+ }
3171
+ } finally {
3172
+ fs16.closeSync(fd);
3173
+ }
3174
+ const content = chunks.join("");
3175
+ const allLines = content.split("\n");
3176
+ if (allLines.length === 0) {
3177
+ return [];
3178
+ }
3179
+ if (allLines[allLines.length - 1] === "") {
3180
+ allLines.pop();
3181
+ }
3182
+ if (allLines.length <= maxLines) {
3183
+ return allLines;
3184
+ }
3185
+ return allLines.slice(allLines.length - maxLines);
3186
+ }
3187
+ function readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset) {
3188
+ const stat = fs16.statSync(filePath);
3189
+ if (stat.size === 0) {
3190
+ return { lines: [], totalLinesCount: 0 };
3191
+ }
3192
+ const fd = fs16.openSync(filePath, "r");
3193
+ const chunkSize = 64 * 1024;
3194
+ let position = stat.size;
3195
+ let remainder = "";
3196
+ let totalLinesCount = 0;
3197
+ let skipped = 0;
3198
+ const collected = [];
3199
+ try {
3200
+ while (position > 0) {
3201
+ const length = Math.min(chunkSize, position);
3202
+ position -= length;
3203
+ const buffer = Buffer.alloc(length);
3204
+ fs16.readSync(fd, buffer, 0, length, position);
3205
+ let chunk = buffer.toString("utf8");
3206
+ if (remainder) {
3207
+ chunk += remainder;
3208
+ remainder = "";
3209
+ }
3210
+ const parts = chunk.split("\n");
3211
+ remainder = parts.shift() ?? "";
3212
+ for (let i = parts.length - 1; i >= 0; i -= 1) {
3213
+ const line = parts[i].trim();
3214
+ if (!line) continue;
3215
+ totalLinesCount += 1;
3216
+ if (skipped < offset) {
3217
+ skipped += 1;
3218
+ } else if (collected.length < maxLines) {
3219
+ collected.push(line);
3220
+ }
3221
+ }
3222
+ }
3223
+ if (remainder) {
3224
+ const line = remainder.trim();
3225
+ if (line) {
3226
+ totalLinesCount += 1;
3227
+ if (skipped < offset) {
3228
+ skipped += 1;
3229
+ } else if (collected.length < maxLines) {
3230
+ collected.push(line);
3231
+ }
3232
+ }
3233
+ }
3234
+ } finally {
3235
+ fs16.closeSync(fd);
3236
+ }
3237
+ return { lines: collected.reverse(), totalLinesCount };
3238
+ }
3239
+
3240
+ // src/commands/read-logs/client-std.ts
3241
+ function readClientStdSegment(filePath, maxLines, offset) {
3242
+ const lines = readFileTailLines(filePath, Math.max((maxLines + offset) * 5, 2e3));
3243
+ return extractClientStdSegment(lines, maxLines, offset);
3244
+ }
3245
+ function extractClientStdSegment(lines, maxLines, offset) {
3246
+ const bodyLines = lines.map(stripPrefixFromStdLine);
3247
+ const hotStartMarkers = [
3248
+ /file change detected\..*incremental compilation/i,
3249
+ /starting incremental compilation/i,
3250
+ /starting compilation/i,
3251
+ /\bcompiling\b/i,
3252
+ /\brecompil/i
3253
+ ];
3254
+ const hotEndMarkers = [
3255
+ /file change detected\..*incremental compilation/i,
3256
+ /\bwebpack compiled\b/i,
3257
+ /compiled successfully/i,
3258
+ /compiled with warnings/i,
3259
+ /compiled with errors/i,
3260
+ /failed to compile/i,
3261
+ /fast refresh/i,
3262
+ /\bhmr\b/i,
3263
+ /hot update/i,
3264
+ /\bhot reload\b/i,
3265
+ /\bhmr update\b/i
3266
+ ];
3267
+ const compiledMarkers = [
3268
+ /\bwebpack compiled\b/i,
3269
+ /compiled successfully/i,
3270
+ /compiled with warnings/i,
3271
+ /compiled with errors/i,
3272
+ /failed to compile/i
3273
+ ];
3274
+ let startIndex = -1;
3275
+ for (let i = bodyLines.length - 1; i >= 0; i -= 1) {
3276
+ const line = bodyLines[i];
3277
+ if (!line) continue;
3278
+ if (hotStartMarkers.some((re) => re.test(line))) {
3279
+ startIndex = i;
3280
+ break;
3281
+ }
3282
+ }
3283
+ if (startIndex === -1) {
3284
+ let pivotIndex = -1;
3285
+ for (let i = bodyLines.length - 1; i >= 0; i -= 1) {
3286
+ const line = bodyLines[i];
3287
+ if (!line) continue;
3288
+ if (hotEndMarkers.some((re) => re.test(line))) {
3289
+ pivotIndex = i;
3290
+ break;
3291
+ }
3292
+ }
3293
+ if (pivotIndex !== -1) {
3294
+ if (compiledMarkers.some((re) => re.test(bodyLines[pivotIndex] ?? ""))) {
3295
+ startIndex = pivotIndex;
3296
+ } else {
3297
+ const searchLimit = 80;
3298
+ const from = Math.max(0, pivotIndex - searchLimit);
3299
+ for (let i = pivotIndex; i >= from; i -= 1) {
3300
+ const line = bodyLines[i];
3301
+ if (!line) continue;
3302
+ if (compiledMarkers.some((re) => re.test(line))) {
3303
+ startIndex = i;
3304
+ break;
3305
+ }
3306
+ }
3307
+ if (startIndex === -1) {
3308
+ startIndex = pivotIndex;
3309
+ }
3310
+ }
3311
+ }
3312
+ }
3313
+ if (startIndex === -1) {
3314
+ for (let i = bodyLines.length - 1; i >= 0; i -= 1) {
3315
+ const line = bodyLines[i];
3316
+ if (!line) continue;
3317
+ if (/\bdev:client\b/.test(line)) {
3318
+ startIndex = i;
3319
+ break;
3320
+ }
3321
+ }
3322
+ }
3323
+ const segment = startIndex === -1 ? bodyLines : bodyLines.slice(startIndex);
3324
+ if (segment.length === 0) {
3325
+ return { lines: [], totalLinesCount: 0 };
3326
+ }
3327
+ const totalLinesCount = segment.length;
3328
+ const endExclusive = Math.max(0, segment.length - offset);
3329
+ const start = Math.max(0, endExclusive - maxLines);
3330
+ return {
3331
+ lines: segment.slice(start, endExclusive),
3332
+ totalLinesCount
3333
+ };
3334
+ }
3335
+
3336
+ // src/commands/read-logs/json-lines.ts
3337
+ import fs17 from "fs";
3338
+ function normalizePid(value) {
3339
+ if (typeof value === "number") {
3340
+ return String(value);
3341
+ }
3342
+ if (typeof value === "string" && value.length > 0) {
3343
+ return value;
3344
+ }
3345
+ return "unknown";
3346
+ }
3347
+ function normalizeLevelName(value) {
3348
+ if (typeof value === "string") {
3349
+ const trimmed = value.trim().toLowerCase();
3350
+ if (!trimmed) return null;
3351
+ if (trimmed === "warning") return "warn";
3352
+ if (trimmed === "err") return "error";
3353
+ const allowed = ["trace", "debug", "info", "warn", "error", "fatal"];
3354
+ return allowed.includes(trimmed) ? trimmed : null;
3355
+ }
3356
+ if (typeof value === "number" && Number.isFinite(value)) {
3357
+ const num = Math.floor(value);
3358
+ if (num <= 10) return "trace";
3359
+ if (num <= 20) return "debug";
3360
+ if (num <= 30) return "info";
3361
+ if (num <= 40) return "warn";
3362
+ if (num <= 50) return "error";
3363
+ return "fatal";
3364
+ }
3365
+ return null;
3366
+ }
3367
+ function extractLevelName(obj) {
3368
+ if (!obj || typeof obj !== "object") return null;
3369
+ const record = obj;
3370
+ const direct = normalizeLevelName(record["level"]);
3371
+ if (direct) return direct;
3372
+ const meta = record["meta"];
3373
+ if (meta && typeof meta === "object") {
3374
+ return normalizeLevelName(meta["level"]);
3375
+ }
3376
+ return null;
3377
+ }
3378
+ function buildWantedLevelSet(levels) {
3379
+ if (!levels || levels.length === 0) return null;
3380
+ const set = /* @__PURE__ */ new Set();
3381
+ for (const raw of levels) {
3382
+ const norm = normalizeLevelName(raw);
3383
+ if (norm) set.add(norm);
3384
+ }
3385
+ return set.size > 0 ? set : null;
3386
+ }
3387
+ function readJsonLinesLastPid(filePath, maxLines, offset, levels) {
3388
+ const stat = fs17.statSync(filePath);
3389
+ if (stat.size === 0) {
3390
+ return { lines: [], totalLinesCount: 0 };
3391
+ }
3392
+ const fd = fs17.openSync(filePath, "r");
3393
+ const chunkSize = 64 * 1024;
3394
+ let position = stat.size;
3395
+ let remainder = "";
3396
+ let targetPid = null;
3397
+ let finished = false;
3398
+ let totalLinesCount = 0;
3399
+ let skipped = 0;
3400
+ const collected = [];
3401
+ const wantedLevelSet = buildWantedLevelSet(levels);
3402
+ try {
3403
+ while (position > 0 && !finished) {
3404
+ const length = Math.min(chunkSize, position);
3405
+ position -= length;
3406
+ const buffer = Buffer.alloc(length);
3407
+ fs17.readSync(fd, buffer, 0, length, position);
3408
+ let chunk = buffer.toString("utf8");
3409
+ if (remainder) {
3410
+ chunk += remainder;
3411
+ remainder = "";
3412
+ }
3413
+ const parts = chunk.split("\n");
3414
+ remainder = parts.shift() ?? "";
3415
+ for (let i = parts.length - 1; i >= 0; i -= 1) {
3416
+ const line = parts[i].trim();
3417
+ if (!line) continue;
3418
+ let parsed = null;
3419
+ try {
3420
+ parsed = JSON.parse(line);
3421
+ } catch {
3422
+ continue;
3423
+ }
3424
+ const pid = normalizePid(parsed?.pid);
3425
+ if (targetPid === null) {
3426
+ targetPid = pid;
3427
+ }
3428
+ if (pid !== targetPid) {
3429
+ finished = true;
3430
+ break;
3431
+ }
3432
+ const levelName = extractLevelName(parsed);
3433
+ if (!wantedLevelSet || levelName && wantedLevelSet.has(levelName)) {
3434
+ totalLinesCount += 1;
3435
+ if (skipped < offset) {
3436
+ skipped += 1;
3437
+ } else if (collected.length < maxLines) {
3438
+ collected.push(line);
3439
+ }
3440
+ }
3441
+ }
3442
+ }
3443
+ if (!finished && remainder) {
3444
+ const line = remainder.trim();
3445
+ if (line) {
3446
+ try {
3447
+ const parsed = JSON.parse(line);
3448
+ const pid = normalizePid(parsed?.pid);
3449
+ if (targetPid === null) {
3450
+ targetPid = pid;
3451
+ }
3452
+ if (pid === targetPid) {
3453
+ const levelName = extractLevelName(parsed);
3454
+ if (!wantedLevelSet || levelName && wantedLevelSet.has(levelName)) {
3455
+ totalLinesCount += 1;
3456
+ if (skipped < offset) {
3457
+ skipped += 1;
3458
+ } else if (collected.length < maxLines) {
3459
+ collected.push(line);
3460
+ }
3461
+ }
3462
+ }
3463
+ } catch {
3464
+ return { lines: [], totalLinesCount: 0 };
3465
+ }
3466
+ }
3467
+ }
3468
+ } finally {
3469
+ fs17.closeSync(fd);
3470
+ }
3471
+ return { lines: collected.reverse(), totalLinesCount };
3472
+ }
3473
+ function normalizeTraceId(value) {
3474
+ if (typeof value === "string") {
3475
+ const trimmed = value.trim();
3476
+ return trimmed ? trimmed : null;
3477
+ }
3478
+ if (typeof value === "number" && Number.isFinite(value)) {
3479
+ return String(value);
3480
+ }
3481
+ return null;
3482
+ }
3483
+ function extractTraceId(obj) {
3484
+ if (!obj || typeof obj !== "object") return null;
3485
+ const record = obj;
3486
+ const directKeys = ["trace_id", "traceId", "traceID", "traceid"];
3487
+ for (const key of directKeys) {
3488
+ const value = normalizeTraceId(record[key]);
3489
+ if (value) return value;
3490
+ }
3491
+ const meta = record["meta"];
3492
+ if (meta && typeof meta === "object") {
3493
+ const metaRecord = meta;
3494
+ for (const key of directKeys) {
3495
+ const value = normalizeTraceId(metaRecord[key]);
3496
+ if (value) return value;
3497
+ }
3498
+ }
3499
+ const attributes = record["attributes"];
3500
+ if (attributes && typeof attributes === "object") {
3501
+ const attrRecord = attributes;
3502
+ for (const key of ["traceID", "trace_id", "traceId", "traceid"]) {
3503
+ const value = normalizeTraceId(attrRecord[key]);
3504
+ if (value) return value;
3505
+ }
3506
+ }
3507
+ return null;
3508
+ }
3509
+ function readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels) {
3510
+ const wanted = traceId.trim();
3511
+ if (!wanted) return { lines: [], totalLinesCount: 0 };
3512
+ const stat = fs17.statSync(filePath);
3513
+ if (stat.size === 0) {
3514
+ return { lines: [], totalLinesCount: 0 };
3515
+ }
3516
+ const fd = fs17.openSync(filePath, "r");
3517
+ const chunkSize = 64 * 1024;
3518
+ let position = stat.size;
3519
+ let remainder = "";
3520
+ let totalLinesCount = 0;
3521
+ let skipped = 0;
3522
+ const collected = [];
3523
+ const wantedLevelSet = buildWantedLevelSet(levels);
3524
+ try {
3525
+ while (position > 0) {
3526
+ const length = Math.min(chunkSize, position);
3527
+ position -= length;
3528
+ const buffer = Buffer.alloc(length);
3529
+ fs17.readSync(fd, buffer, 0, length, position);
3530
+ let chunk = buffer.toString("utf8");
3531
+ if (remainder) {
3532
+ chunk += remainder;
3533
+ remainder = "";
3534
+ }
3535
+ const parts = chunk.split("\n");
3536
+ remainder = parts.shift() ?? "";
3537
+ for (let i = parts.length - 1; i >= 0; i -= 1) {
3538
+ const line = parts[i].trim();
3539
+ if (!line) continue;
3540
+ try {
3541
+ const parsed = JSON.parse(line);
3542
+ const lineTraceId = extractTraceId(parsed);
3543
+ if (lineTraceId === wanted) {
3544
+ const levelName = extractLevelName(parsed);
3545
+ if (!wantedLevelSet || levelName && wantedLevelSet.has(levelName)) {
3546
+ totalLinesCount += 1;
3547
+ if (skipped < offset) {
3548
+ skipped += 1;
3549
+ } else if (collected.length < maxLines) {
3550
+ collected.push(line);
3551
+ }
3552
+ }
3553
+ }
3554
+ } catch {
3555
+ continue;
3556
+ }
3557
+ }
3558
+ }
3559
+ if (remainder) {
3560
+ const line = remainder.trim();
3561
+ if (line) {
3562
+ try {
3563
+ const parsed = JSON.parse(line);
3564
+ const lineTraceId = extractTraceId(parsed);
3565
+ if (lineTraceId === wanted) {
3566
+ const levelName = extractLevelName(parsed);
3567
+ if (!wantedLevelSet || levelName && wantedLevelSet.has(levelName)) {
3568
+ totalLinesCount += 1;
3569
+ if (skipped < offset) {
3570
+ skipped += 1;
3571
+ } else if (collected.length < maxLines) {
3572
+ collected.push(line);
3573
+ }
3574
+ }
3575
+ }
3576
+ } catch {
3577
+ return { lines: [], totalLinesCount: 0 };
3578
+ }
3579
+ }
3580
+ }
3581
+ } finally {
3582
+ fs17.closeSync(fd);
3583
+ }
3584
+ return { lines: collected.reverse(), totalLinesCount };
3585
+ }
3586
+ function readJsonLinesTailByLevel(filePath, maxLines, offset, levels) {
3587
+ const wantedLevelSet = buildWantedLevelSet(levels);
3588
+ if (!wantedLevelSet) {
3589
+ return { lines: [], totalLinesCount: 0 };
3590
+ }
3591
+ const stat = fs17.statSync(filePath);
3592
+ if (stat.size === 0) {
3593
+ return { lines: [], totalLinesCount: 0 };
3594
+ }
3595
+ const fd = fs17.openSync(filePath, "r");
3596
+ const chunkSize = 64 * 1024;
3597
+ let position = stat.size;
3598
+ let remainder = "";
3599
+ let totalLinesCount = 0;
3600
+ let skipped = 0;
3601
+ const collected = [];
3602
+ try {
3603
+ while (position > 0) {
3604
+ const length = Math.min(chunkSize, position);
3605
+ position -= length;
3606
+ const buffer = Buffer.alloc(length);
3607
+ fs17.readSync(fd, buffer, 0, length, position);
3608
+ let chunk = buffer.toString("utf8");
3609
+ if (remainder) {
3610
+ chunk += remainder;
3611
+ remainder = "";
3612
+ }
3613
+ const parts = chunk.split("\n");
3614
+ remainder = parts.shift() ?? "";
3615
+ for (let i = parts.length - 1; i >= 0; i -= 1) {
3616
+ const line = parts[i].trim();
3617
+ if (!line) continue;
3618
+ try {
3619
+ const parsed = JSON.parse(line);
3620
+ const levelName = extractLevelName(parsed);
3621
+ if (levelName && wantedLevelSet.has(levelName)) {
3622
+ totalLinesCount += 1;
3623
+ if (skipped < offset) {
3624
+ skipped += 1;
3625
+ } else if (collected.length < maxLines) {
3626
+ collected.push(line);
3627
+ }
3628
+ }
3629
+ } catch {
3630
+ continue;
3631
+ }
3632
+ }
3633
+ }
3634
+ if (remainder) {
3635
+ const line = remainder.trim();
3636
+ if (line) {
3637
+ try {
3638
+ const parsed = JSON.parse(line);
3639
+ const levelName = extractLevelName(parsed);
3640
+ if (levelName && wantedLevelSet.has(levelName)) {
3641
+ totalLinesCount += 1;
3642
+ if (skipped < offset) {
3643
+ skipped += 1;
3644
+ } else if (collected.length < maxLines) {
3645
+ collected.push(line);
3646
+ }
3647
+ }
3648
+ } catch {
3649
+ return { lines: [], totalLinesCount: 0 };
3650
+ }
3651
+ }
3652
+ }
3653
+ } finally {
3654
+ fs17.closeSync(fd);
3655
+ }
3656
+ return { lines: collected.reverse(), totalLinesCount };
3657
+ }
3658
+
3659
+ // src/commands/read-logs/index.ts
3660
+ var LOG_TYPES = ["server", "trace", "server-std", "client-std", "browser"];
3661
+ function sanitizeStructuredLog(value) {
3662
+ if (!value || typeof value !== "object") return value;
3663
+ if (Array.isArray(value)) return value;
3664
+ const obj = value;
3665
+ const sanitized = { ...obj };
3666
+ delete sanitized.tenant_id;
3667
+ delete sanitized.app_id;
3668
+ delete sanitized.pid;
3669
+ return sanitized;
3670
+ }
3671
+ function hasErrorInStdLines(lines) {
3672
+ const combined = lines.join("\n");
3673
+ if (!combined) return false;
3674
+ const strong = [
3675
+ /compiled with errors/i,
3676
+ /failed to compile/i,
3677
+ /error: \w+/i,
3678
+ /uncaught/i,
3679
+ /unhandled/i,
3680
+ /eaddrinuse/i,
3681
+ /cannot find module/i,
3682
+ /module not found/i,
3683
+ /ts\d{3,5}:/i
3684
+ ];
3685
+ if (strong.some((re) => re.test(combined))) return true;
3686
+ const weakLine = /\b(error|fatal|exception)\b/i;
3687
+ const ignorePatterns = [
3688
+ /\b0\s+errors?\b/i,
3689
+ /Server Error \d{3}/i
3690
+ ];
3691
+ return lines.some((line) => {
3692
+ const text = line.trim();
3693
+ if (!text) return false;
3694
+ if (ignorePatterns.some((re) => re.test(text))) return false;
3695
+ return weakLine.test(text);
3696
+ });
3697
+ }
3698
+ function hasErrorInLogObject(value) {
3699
+ if (!value || typeof value !== "object") return false;
3700
+ const obj = value;
3701
+ const rawLevel = obj.level;
3702
+ const level = typeof rawLevel === "string" ? rawLevel.toLowerCase() : "";
3703
+ if (level === "error" || level === "fatal") return true;
3704
+ if (level === "err") return true;
3705
+ if (typeof rawLevel === "number" && Number.isFinite(rawLevel) && rawLevel >= 50) return true;
3706
+ if (level === "warn" && typeof obj.stack === "string" && obj.stack.length > 0) return true;
3707
+ if (typeof obj.stack === "string" && obj.stack.length > 0) return true;
3708
+ const statusCode = obj.statusCode;
3709
+ const status_code = obj.status_code;
3710
+ const meta = obj.meta;
3711
+ const metaObj = meta && typeof meta === "object" ? meta : null;
3712
+ const metaStatusCode = metaObj?.statusCode;
3713
+ const metaStatus_code = metaObj?.status_code;
3714
+ const candidates = [statusCode, status_code, metaStatusCode, metaStatus_code];
3715
+ for (const candidate of candidates) {
3716
+ if (typeof candidate === "number" && Number.isFinite(candidate) && candidate >= 400) {
3717
+ return true;
3718
+ }
3719
+ }
3720
+ const message = typeof obj.message === "string" ? obj.message : "";
3721
+ if (message && hasErrorInStdLines([message])) return true;
3722
+ return false;
3723
+ }
3724
+ async function readLatestLogLinesMeta(options) {
3725
+ const maxLines = options.maxLines ?? 30;
3726
+ const offset = options.offset ?? 0;
3727
+ const filePath = resolveLogFilePath(options.logDir, options.type);
3728
+ const levels = Array.isArray(options.levels) ? options.levels : void 0;
3729
+ if (!fileExists(filePath)) {
3730
+ throw new Error(`Log file not found: ${filePath}`);
3731
+ }
3732
+ if (options.type === "server-std") {
3733
+ return readServerStdSegment(filePath, maxLines, offset);
3734
+ }
3735
+ if (options.type === "client-std") {
3736
+ return readClientStdSegment(filePath, maxLines, offset);
3737
+ }
3738
+ const traceId = typeof options.traceId === "string" ? options.traceId.trim() : "";
3739
+ if (traceId) {
3740
+ return readJsonLinesByTraceId(filePath, traceId, maxLines, offset, levels);
3741
+ }
3742
+ if (options.type === "server" || options.type === "trace") {
3743
+ return readJsonLinesLastPid(filePath, maxLines, offset, levels);
3744
+ }
3745
+ if (options.type === "browser") {
3746
+ if (levels && levels.length > 0) {
3747
+ return readJsonLinesTailByLevel(filePath, maxLines, offset, levels);
3748
+ }
3749
+ return readFileTailNonEmptyLinesWithOffset(filePath, maxLines, offset);
3750
+ }
3751
+ return { lines: [], totalLinesCount: 0 };
3752
+ }
3753
+ async function readLogsJsonResult(options) {
3754
+ const { lines, totalLinesCount } = await readLatestLogLinesMeta(options);
3755
+ if (options.type === "server-std" || options.type === "client-std") {
3756
+ return {
3757
+ hasError: hasErrorInStdLines(lines),
3758
+ totalLinesCount,
3759
+ logs: lines
3760
+ };
3761
+ }
3762
+ const logs = [];
3763
+ let hasError = false;
3764
+ for (const line of lines) {
3765
+ if (!hasError && hasErrorInStdLines([line])) {
3766
+ hasError = true;
3767
+ }
3768
+ try {
3769
+ const parsed = JSON.parse(line);
3770
+ logs.push(sanitizeStructuredLog(parsed));
3771
+ if (!hasError && hasErrorInLogObject(parsed)) {
3772
+ hasError = true;
3773
+ }
3774
+ } catch {
3775
+ continue;
3776
+ }
3777
+ }
3778
+ return {
3779
+ hasError,
3780
+ totalLinesCount,
3781
+ logs
3782
+ };
3783
+ }
3784
+ function resolveLogFilePath(logDir, type) {
3785
+ const base = path15.isAbsolute(logDir) ? logDir : path15.join(process.cwd(), logDir);
3786
+ if (type === "server") {
3787
+ return path15.join(base, "server.log");
3788
+ }
3789
+ if (type === "trace") {
3790
+ return path15.join(base, "trace.log");
3791
+ }
3792
+ if (type === "server-std") {
3793
+ return path15.join(base, "server.std.log");
3794
+ }
3795
+ if (type === "client-std") {
3796
+ return path15.join(base, "client.std.log");
3797
+ }
3798
+ if (type === "browser") {
3799
+ return path15.join(base, "browser.log");
3800
+ }
3801
+ throw new Error(`Unsupported log type: ${type}`);
3802
+ }
3803
+ async function run4(options) {
3804
+ try {
3805
+ const result = await readLogsJsonResult(options);
3806
+ process.stdout.write(JSON.stringify(result) + "\n");
3807
+ } catch (error) {
3808
+ const message = error instanceof Error ? error.message : String(error);
3809
+ const result = { hasError: true, totalLinesCount: 0, logs: [message] };
3810
+ process.stdout.write(JSON.stringify(result) + "\n");
3811
+ process.exitCode = 1;
3812
+ }
3813
+ }
3814
+ function parseLogType(input) {
3815
+ const value = typeof input === "string" ? input.trim() : "";
3816
+ if (LOG_TYPES.includes(value)) {
3817
+ return value;
3818
+ }
3819
+ throw new Error(`Invalid --type: ${String(input)}. Expected one of: ${LOG_TYPES.join(", ")}`);
3820
+ }
3821
+ function parsePositiveInt(input, flagName) {
3822
+ const value = typeof input === "number" ? input : Number.parseInt(String(input), 10);
3823
+ if (!Number.isFinite(value) || value <= 0) {
3824
+ throw new Error(`Invalid ${flagName}: ${String(input)}. Expected a positive integer.`);
3825
+ }
3826
+ return value;
3827
+ }
3828
+ function parseNonNegativeInt(input, flagName) {
3829
+ const value = typeof input === "number" ? input : Number.parseInt(String(input), 10);
3830
+ if (!Number.isFinite(value) || value < 0) {
3831
+ throw new Error(`Invalid ${flagName}: ${String(input)}. Expected a non-negative integer.`);
3832
+ }
3833
+ return value;
3834
+ }
3835
+ function parseCommaSeparatedList(input) {
3836
+ const value = typeof input === "string" ? input : "";
3837
+ const items = value.split(",").map((s) => s.trim()).filter(Boolean);
3838
+ return items.length > 0 ? items : void 0;
3839
+ }
3840
+ var readLogsCommand = {
3841
+ name: "read-logs",
3842
+ description: "Read latest logs from log files",
3843
+ register(program) {
3844
+ program.command(this.name).description(this.description).option("--dir <path>", "Logs directory", "logs").option("--type <type>", `Log type: ${LOG_TYPES.join("|")}`, "server-std").option("--max-lines <lines>", "Max lines to return", "30").option("--offset <lines>", "Skip latest N lines for pagination", "0").option("--trace-id <id>", "Filter structured logs by trace id").option("--level <levels>", "Filter structured logs by level (comma-separated)").action(async (rawOptions) => {
3845
+ try {
3846
+ const logDir = typeof rawOptions.dir === "string" ? rawOptions.dir : "logs";
3847
+ const type = parseLogType(rawOptions.type);
3848
+ const maxLines = parsePositiveInt(rawOptions.maxLines, "--max-lines");
3849
+ const offset = parseNonNegativeInt(rawOptions.offset, "--offset");
3850
+ const traceId = typeof rawOptions.traceId === "string" ? rawOptions.traceId : void 0;
3851
+ const levels = parseCommaSeparatedList(rawOptions.level);
3852
+ await run4({ logDir, type, maxLines, offset, traceId, levels });
3853
+ } catch (error) {
3854
+ const message = error instanceof Error ? error.message : String(error);
3855
+ const result = { hasError: true, totalLinesCount: 0, logs: [message] };
3856
+ process.stdout.write(JSON.stringify(result) + "\n");
3857
+ process.exitCode = 1;
3858
+ }
3859
+ });
3860
+ }
3861
+ };
3862
+
3027
3863
  // src/commands/index.ts
3028
3864
  var commands = [
3029
3865
  genDbSchemaCommand,
3030
3866
  syncCommand,
3031
3867
  actionPluginCommandGroup,
3032
3868
  capabilityCommandGroup,
3033
- migrationCommand
3869
+ migrationCommand,
3870
+ readLogsCommand
3034
3871
  ];
3035
3872
 
3036
3873
  // src/index.ts
3037
- var envPath = path15.join(process.cwd(), ".env");
3038
- if (fs15.existsSync(envPath)) {
3874
+ var envPath = path16.join(process.cwd(), ".env");
3875
+ if (fs18.existsSync(envPath)) {
3039
3876
  dotenvConfig({ path: envPath });
3040
3877
  }
3041
- var __dirname = path15.dirname(fileURLToPath3(import.meta.url));
3042
- var pkg = JSON.parse(fs15.readFileSync(path15.join(__dirname, "../package.json"), "utf-8"));
3878
+ var __dirname = path16.dirname(fileURLToPath3(import.meta.url));
3879
+ var pkg = JSON.parse(fs18.readFileSync(path16.join(__dirname, "../package.json"), "utf-8"));
3043
3880
  var cli = new FullstackCLI(pkg.version);
3044
3881
  cli.useAll(commands);
3045
3882
  cli.run();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/fullstack-cli",
3
- "version": "1.1.6-alpha.13",
3
+ "version": "1.1.6-alpha.14",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "access": "public"
32
32
  },
33
33
  "dependencies": {
34
- "@lark-apaas/devtool-kits": "^1.2.12",
34
+ "@lark-apaas/devtool-kits": "1.2.11-alpha.3",
35
35
  "@lark-apaas/http-client": "0.1.2",
36
36
  "@vercel/nft": "^0.30.3",
37
37
  "commander": "^13.0.0",