@dev-pi2pie/word-counter 0.1.4 → 0.1.5-canary.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/esm/bin.mjs CHANGED
@@ -3,11 +3,10 @@
3
3
  import { Command, Option } from "commander";
4
4
  import { closeSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, statSync } from "node:fs";
5
5
  import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
6
  import os from "node:os";
7
+ import { fileURLToPath } from "node:url";
8
8
  import { parseDocument } from "yaml";
9
9
  import { readFile, readdir, stat } from "node:fs/promises";
10
-
11
10
  //#region \0rolldown/runtime.js
12
11
  var __create = Object.create;
13
12
  var __defProp = Object.defineProperty;
@@ -17,16 +16,12 @@ var __getProtoOf = Object.getPrototypeOf;
17
16
  var __hasOwnProp = Object.prototype.hasOwnProperty;
18
17
  var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
19
18
  var __copyProps = (to, from, except, desc) => {
20
- if (from && typeof from === "object" || typeof from === "function") {
21
- for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
22
- key = keys[i];
23
- if (!__hasOwnProp.call(to, key) && key !== except) {
24
- __defProp(to, key, {
25
- get: ((k) => from[k]).bind(null, key),
26
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
27
- });
28
- }
29
- }
19
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
20
+ key = keys[i];
21
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
22
+ get: ((k) => from[k]).bind(null, key),
23
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
24
+ });
30
25
  }
31
26
  return to;
32
27
  };
@@ -34,7 +29,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
34
29
  value: mod,
35
30
  enumerable: true
36
31
  }) : target, mod));
37
-
38
32
  //#endregion
39
33
  //#region src/cli/debug/channel.ts
40
34
  const NOOP_CLOSE = async () => {};
@@ -144,7 +138,469 @@ function createDebugChannel(options) {
144
138
  }
145
139
  };
146
140
  }
147
-
141
+ //#endregion
142
+ //#region src/cli/program/version-embedded.ts
143
+ var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
144
+ let p = process || {}, argv = p.argv || [], env = p.env || {};
145
+ let isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
146
+ let formatter = (open, close, replace = open) => (input) => {
147
+ let string = "" + input, index = string.indexOf(close, open.length);
148
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
149
+ };
150
+ let replaceClose = (string, close, replace, index) => {
151
+ let result = "", cursor = 0;
152
+ do {
153
+ result += string.substring(cursor, index) + replace;
154
+ cursor = index + close.length;
155
+ index = string.indexOf(close, cursor);
156
+ } while (~index);
157
+ return result + string.substring(cursor);
158
+ };
159
+ let createColors = (enabled = isColorSupported) => {
160
+ let f = enabled ? formatter : () => String;
161
+ return {
162
+ isColorSupported: enabled,
163
+ reset: f("\x1B[0m", "\x1B[0m"),
164
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
165
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
166
+ italic: f("\x1B[3m", "\x1B[23m"),
167
+ underline: f("\x1B[4m", "\x1B[24m"),
168
+ inverse: f("\x1B[7m", "\x1B[27m"),
169
+ hidden: f("\x1B[8m", "\x1B[28m"),
170
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
171
+ black: f("\x1B[30m", "\x1B[39m"),
172
+ red: f("\x1B[31m", "\x1B[39m"),
173
+ green: f("\x1B[32m", "\x1B[39m"),
174
+ yellow: f("\x1B[33m", "\x1B[39m"),
175
+ blue: f("\x1B[34m", "\x1B[39m"),
176
+ magenta: f("\x1B[35m", "\x1B[39m"),
177
+ cyan: f("\x1B[36m", "\x1B[39m"),
178
+ white: f("\x1B[37m", "\x1B[39m"),
179
+ gray: f("\x1B[90m", "\x1B[39m"),
180
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
181
+ bgRed: f("\x1B[41m", "\x1B[49m"),
182
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
183
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
184
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
185
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
186
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
187
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
188
+ blackBright: f("\x1B[90m", "\x1B[39m"),
189
+ redBright: f("\x1B[91m", "\x1B[39m"),
190
+ greenBright: f("\x1B[92m", "\x1B[39m"),
191
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
192
+ blueBright: f("\x1B[94m", "\x1B[39m"),
193
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
194
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
195
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
196
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
197
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
198
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
199
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
200
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
201
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
202
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
203
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
204
+ };
205
+ };
206
+ module.exports = createColors();
207
+ module.exports.createColors = createColors;
208
+ })))(), 1);
209
+ //#endregion
210
+ //#region src/cli/batch/jobs/limits.ts
211
+ const DEFAULT_UV_THREADPOOL_SIZE = 4;
212
+ function parsePositiveInteger(value) {
213
+ if (!value) return;
214
+ const parsed = Number.parseInt(value, 10);
215
+ if (!Number.isFinite(parsed) || parsed <= 0) return;
216
+ return parsed;
217
+ }
218
+ function resolveBatchJobsLimit(env = process.env) {
219
+ const cpuLimit = Math.max(1, os.availableParallelism());
220
+ const uvThreadpool = parsePositiveInteger(env.UV_THREADPOOL_SIZE) ?? DEFAULT_UV_THREADPOOL_SIZE;
221
+ const ioLimit = Math.max(1, uvThreadpool * 2);
222
+ return {
223
+ suggestedMaxJobs: Math.max(1, Math.min(cpuLimit, ioLimit)),
224
+ cpuLimit,
225
+ uvThreadpool,
226
+ ioLimit
227
+ };
228
+ }
229
+ function clampRequestedJobs(requestedJobs, limits) {
230
+ return Math.max(1, Math.min(requestedJobs, limits.suggestedMaxJobs));
231
+ }
232
+ function formatJobsAdvisoryWarning(requestedJobs, effectiveJobs, limits) {
233
+ return [
234
+ `Warning: requested --jobs=${requestedJobs} exceeds suggested host limit (${limits.suggestedMaxJobs}).`,
235
+ `Running with --jobs=${effectiveJobs} as a safety cap.`,
236
+ `Host limits: cpuLimit=${limits.cpuLimit}, uvThreadpool=${limits.uvThreadpool}, ioLimit=${limits.ioLimit}.`
237
+ ].join(" ");
238
+ }
239
+ function isResourceLimitError(error) {
240
+ if (typeof error !== "object" || error === null) return false;
241
+ const code = "code" in error ? error.code : void 0;
242
+ return code === "EMFILE" || code === "ENFILE";
243
+ }
244
+ function createResourceLimitError(path, error, requestedJobs, limits) {
245
+ const message = error instanceof Error ? error.message : String(error);
246
+ const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "UNKNOWN";
247
+ return new Error([
248
+ `Resource limit reached while processing: ${path} (${code}: ${message}).`,
249
+ `Requested --jobs=${requestedJobs}; suggested host limit is ${limits.suggestedMaxJobs}.`,
250
+ "Reduce --jobs or raise OS file descriptor limits before retrying."
251
+ ].join(" "));
252
+ }
253
+ //#endregion
254
+ //#region src/cli/batch/jobs/load-count-worker.ts
255
+ var WorkerRouteUnavailableError = class extends Error {};
256
+ async function resolveWorkerThreadsAvailability() {
257
+ try {
258
+ return typeof (await import("node:worker_threads")).Worker === "function";
259
+ } catch {
260
+ return false;
261
+ }
262
+ }
263
+ function isFallbackFriendlyWorkerError(error) {
264
+ if (typeof error !== "object" || error === null) return false;
265
+ const code = "code" in error ? String(error.code) : "";
266
+ if (code === "ERR_WORKER_PATH" || code === "ERR_WORKER_UNSUPPORTED_EXTENSION" || code === "ERR_UNKNOWN_FILE_EXTENSION" || code === "ERR_MODULE_NOT_FOUND") return true;
267
+ const message = error instanceof Error ? error.message : String(error);
268
+ return message.includes("Unknown file extension") || message.includes("Cannot find module");
269
+ }
270
+ async function resolveWorkerRoutePreflight(env = process.env) {
271
+ const disableWorkerJobsEnv = env.WORD_COUNTER_DISABLE_WORKER_JOBS ?? null;
272
+ const workerRouteDisabledByEnv = disableWorkerJobsEnv === "1";
273
+ const workerThreadsAvailable = await resolveWorkerThreadsAvailability();
274
+ try {
275
+ return {
276
+ workerThreadsAvailable,
277
+ workerRouteDisabledByEnv,
278
+ disableWorkerJobsEnv,
279
+ workerPoolModuleLoadable: true,
280
+ workerEntryFound: (await import("./worker-pool.mjs")).resolveWorkerEntryUrl() !== null
281
+ };
282
+ } catch {
283
+ return {
284
+ workerThreadsAvailable,
285
+ workerRouteDisabledByEnv,
286
+ disableWorkerJobsEnv,
287
+ workerPoolModuleLoadable: false,
288
+ workerEntryFound: false
289
+ };
290
+ }
291
+ }
292
+ async function countBatchInputsWithWorkerJobs(filePaths, options) {
293
+ if (process.env.WORD_COUNTER_DISABLE_WORKER_JOBS === "1") throw new WorkerRouteUnavailableError("Worker route disabled by environment.");
294
+ let workerPoolModule;
295
+ try {
296
+ workerPoolModule = await import("./worker-pool.mjs");
297
+ } catch (error) {
298
+ throw new WorkerRouteUnavailableError(`Worker route unavailable: ${error instanceof Error ? error.message : String(error)}`);
299
+ }
300
+ try {
301
+ return await workerPoolModule.countBatchInputsWithWorkerPool({
302
+ filePaths,
303
+ jobs: options.jobs,
304
+ section: options.section,
305
+ wcOptions: options.wcOptions,
306
+ preserveCollectorSegments: options.preserveCollectorSegments,
307
+ onFileProcessed: options.onFileProcessed
308
+ });
309
+ } catch (error) {
310
+ if (error instanceof workerPoolModule.WorkerPoolTaskFatalError) {
311
+ if (error.code === "EMFILE" || error.code === "ENFILE") throw createResourceLimitError(error.path, {
312
+ code: error.code,
313
+ message: error.message
314
+ }, options.jobs, resolveBatchJobsLimit());
315
+ throw new Error(error.message);
316
+ }
317
+ if (error instanceof workerPoolModule.WorkerPoolUnavailableError || isFallbackFriendlyWorkerError(error)) throw new WorkerRouteUnavailableError(`Worker route unavailable: ${error instanceof Error ? error.message : String(error)}`);
318
+ throw error;
319
+ }
320
+ }
321
+ //#endregion
322
+ //#region src/cli/doctor/checks.ts
323
+ const REQUIRED_NODE_RANGE = ">=20";
324
+ const REQUIRED_NODE_MAJOR = 20;
325
+ const SAMPLE_TEXT = "Hello 世界";
326
+ function normalizePackageVersion(value) {
327
+ const trimmed = value?.trim();
328
+ return trimmed && trimmed.length > 0 ? trimmed : "0.0.0";
329
+ }
330
+ function deriveBuildChannel(packageVersion) {
331
+ const prereleaseMatch = /(?:^|[.-])(alpha|beta|rc|canary)(?:[.-]|$)/i.exec(packageVersion);
332
+ if (!prereleaseMatch) return "stable";
333
+ const channel = prereleaseMatch[1]?.toLowerCase();
334
+ if (channel === "alpha" || channel === "beta" || channel === "rc" || channel === "canary") return channel;
335
+ return "stable";
336
+ }
337
+ function parseNodeMajor(version) {
338
+ const match = /^v?(\d+)(?:\.\d+){0,2}(?:[-+].*)?$/.exec(version.trim());
339
+ if (!match) return null;
340
+ const major = Number.parseInt(match[1] ?? "", 10);
341
+ return Number.isFinite(major) ? major : null;
342
+ }
343
+ function resolveRuntimeSummary(overrides = {}) {
344
+ const packageVersion = normalizePackageVersion(overrides.packageVersion ?? "0.1.5-canary.1");
345
+ const nodeVersion = overrides.nodeVersion ?? process.version;
346
+ const major = parseNodeMajor(nodeVersion);
347
+ return {
348
+ packageVersion,
349
+ buildChannel: deriveBuildChannel(packageVersion),
350
+ requiredNodeRange: REQUIRED_NODE_RANGE,
351
+ nodeVersion,
352
+ meetsProjectRequirement: major !== null && major >= REQUIRED_NODE_MAJOR,
353
+ platform: overrides.platform ?? process.platform,
354
+ arch: overrides.arch ?? process.arch
355
+ };
356
+ }
357
+ function resolveSegmenterHealth(overrides = {}) {
358
+ const Segmenter = (overrides.intl ?? Intl).Segmenter;
359
+ const available = typeof Segmenter === "function";
360
+ let wordGranularity = false;
361
+ let graphemeGranularity = false;
362
+ let sampleWordSegmentation = false;
363
+ if (!available) return {
364
+ available,
365
+ wordGranularity,
366
+ graphemeGranularity,
367
+ sampleWordSegmentation
368
+ };
369
+ try {
370
+ const wordSegmenter = new Segmenter("en", { granularity: "word" });
371
+ wordGranularity = true;
372
+ for (const _segment of wordSegmenter.segment(SAMPLE_TEXT)) {
373
+ sampleWordSegmentation = true;
374
+ break;
375
+ }
376
+ } catch {
377
+ wordGranularity = false;
378
+ sampleWordSegmentation = false;
379
+ }
380
+ try {
381
+ new Segmenter("en", { granularity: "grapheme" });
382
+ graphemeGranularity = true;
383
+ } catch {
384
+ graphemeGranularity = false;
385
+ }
386
+ return {
387
+ available,
388
+ wordGranularity,
389
+ graphemeGranularity,
390
+ sampleWordSegmentation
391
+ };
392
+ }
393
+ function collectWarnings(runtime, segmenter, workerRoute) {
394
+ const warnings = [];
395
+ if (!runtime.meetsProjectRequirement) warnings.push(`Node.js ${runtime.nodeVersion} is outside the supported range ${runtime.requiredNodeRange}.`);
396
+ if (!segmenter.available) warnings.push("Intl.Segmenter is unavailable.");
397
+ else {
398
+ if (!segmenter.wordGranularity) warnings.push("Intl.Segmenter word granularity is unusable.");
399
+ if (!segmenter.graphemeGranularity) warnings.push("Intl.Segmenter grapheme granularity is unusable.");
400
+ if (!segmenter.sampleWordSegmentation) warnings.push("Intl.Segmenter sample segmentation failed.");
401
+ }
402
+ if (!workerRoute.workerThreadsAvailable) warnings.push("Worker threads are unavailable on this runtime.");
403
+ if (workerRoute.workerRouteDisabledByEnv) warnings.push("Worker route is disabled by environment.");
404
+ if (!workerRoute.workerPoolModuleLoadable) warnings.push("Worker route preflight failed: worker-pool module could not be loaded.");
405
+ else if (!workerRoute.workerEntryFound) warnings.push("Worker route preflight failed: count-worker entry file was not found.");
406
+ return warnings;
407
+ }
408
+ function resolveStatus(segmenter, warnings) {
409
+ if (!segmenter.available || !segmenter.wordGranularity || !segmenter.graphemeGranularity || !segmenter.sampleWordSegmentation) return "fail";
410
+ if (warnings.length > 0) return "warn";
411
+ return "ok";
412
+ }
413
+ async function createDoctorReport(overrides = {}) {
414
+ const runtime = resolveRuntimeSummary(overrides);
415
+ const segmenter = resolveSegmenterHealth(overrides);
416
+ const env = overrides.env ?? process.env;
417
+ const jobs = resolveBatchJobsLimit(env);
418
+ const workerRoute = await resolveWorkerRoutePreflight(env);
419
+ const warnings = collectWarnings(runtime, segmenter, workerRoute);
420
+ return {
421
+ status: resolveStatus(segmenter, warnings),
422
+ runtime,
423
+ segmenter,
424
+ jobs,
425
+ workerRoute,
426
+ warnings
427
+ };
428
+ }
429
+ //#endregion
430
+ //#region src/cli/doctor/render.ts
431
+ function colorStatus(status) {
432
+ if (status === "ok") return import_picocolors.default.green(import_picocolors.default.bold(status));
433
+ if (status === "warn") return import_picocolors.default.yellow(import_picocolors.default.bold(status));
434
+ return import_picocolors.default.red(import_picocolors.default.bold(status));
435
+ }
436
+ function renderSection(title, lines) {
437
+ console.log(import_picocolors.default.bold(title));
438
+ for (const line of lines) console.log(`- ${line}`);
439
+ console.log("");
440
+ }
441
+ function colorBoolean(value, yes = "yes", no = "no") {
442
+ return value ? import_picocolors.default.green(yes) : import_picocolors.default.red(no);
443
+ }
444
+ function colorNumber(value) {
445
+ return import_picocolors.default.yellow(String(value));
446
+ }
447
+ function colorStatusWord(value) {
448
+ return value ? import_picocolors.default.green("ok") : import_picocolors.default.red("fail");
449
+ }
450
+ function renderStandardDoctorReport(report) {
451
+ console.log(`Doctor: ${colorStatus(report.status)}`);
452
+ console.log("");
453
+ renderSection("Runtime", [
454
+ `package: ${report.runtime.packageVersion} (${report.runtime.buildChannel})`,
455
+ `node: ${report.runtime.nodeVersion} (supported: ${colorBoolean(report.runtime.meetsProjectRequirement)}; required ${report.runtime.requiredNodeRange})`,
456
+ `platform: ${report.runtime.platform} ${report.runtime.arch}`
457
+ ]);
458
+ renderSection("Segmenter", [
459
+ `Intl.Segmenter: ${colorBoolean(report.segmenter.available, "available", "missing")}`,
460
+ `word granularity: ${colorStatusWord(report.segmenter.wordGranularity)}`,
461
+ `grapheme granularity: ${colorStatusWord(report.segmenter.graphemeGranularity)}`,
462
+ `sample segmentation: ${colorStatusWord(report.segmenter.sampleWordSegmentation)}`
463
+ ]);
464
+ renderSection("Batch jobs", [
465
+ `cpuLimit: ${colorNumber(report.jobs.cpuLimit)}`,
466
+ `uvThreadpool: ${colorNumber(report.jobs.uvThreadpool)}`,
467
+ `ioLimit: ${colorNumber(report.jobs.ioLimit)}`,
468
+ `suggestedMaxJobs: ${colorNumber(report.jobs.suggestedMaxJobs)}`
469
+ ]);
470
+ renderSection("Worker route", [
471
+ `worker threads: ${colorBoolean(report.workerRoute.workerThreadsAvailable, "available", "missing")}`,
472
+ `disabled by env: ${colorBoolean(report.workerRoute.workerRouteDisabledByEnv)}`,
473
+ `disableWorkerJobsEnv: ${report.workerRoute.disableWorkerJobsEnv ?? "null"}`,
474
+ `worker pool module: ${colorBoolean(report.workerRoute.workerPoolModuleLoadable, "loadable", "missing")}`,
475
+ `worker entry: ${colorBoolean(report.workerRoute.workerEntryFound, "found", "missing")}`
476
+ ]);
477
+ if (report.warnings.length > 0) {
478
+ console.log(import_picocolors.default.bold("Warnings"));
479
+ for (const warning of report.warnings) console.log(import_picocolors.default.yellow(`- ${warning}`));
480
+ }
481
+ }
482
+ function renderDoctorReport(report, options) {
483
+ if (options.format === "json") {
484
+ console.log(JSON.stringify(report, null, options.pretty ? 2 : 0));
485
+ return;
486
+ }
487
+ renderStandardDoctorReport(report);
488
+ }
489
+ //#endregion
490
+ //#region src/cli/doctor/run.ts
491
+ const DOCTOR_HELP_LINES = [
492
+ "Usage: word-counter doctor [options]",
493
+ "",
494
+ "report runtime diagnostics for this host",
495
+ "",
496
+ "Options:",
497
+ " --format <format> doctor output format (json)",
498
+ " --pretty pretty print doctor JSON output (default: false)",
499
+ " -h, --help display help for command"
500
+ ];
501
+ function parseDoctorFormat(rawValue) {
502
+ if (rawValue === void 0) return "standard";
503
+ if (rawValue === "json") return "json";
504
+ return null;
505
+ }
506
+ function validateDoctorInvocation(argv) {
507
+ const doctorIndex = argv.findIndex((token, index) => index >= 2 && token === "doctor");
508
+ const tokens = doctorIndex >= 0 ? argv.slice(doctorIndex + 1) : [];
509
+ let expectsFormatValue = false;
510
+ let format = "standard";
511
+ let pretty = false;
512
+ for (const token of tokens) {
513
+ if (token === "-h" || token === "--help") return {
514
+ ok: true,
515
+ help: true
516
+ };
517
+ if (expectsFormatValue) {
518
+ const parsedFormat = parseDoctorFormat(token);
519
+ if (parsedFormat === null) return {
520
+ ok: false,
521
+ message: "`doctor` only supports default text output or `--format json`."
522
+ };
523
+ format = parsedFormat;
524
+ expectsFormatValue = false;
525
+ continue;
526
+ }
527
+ if (token === "--") return {
528
+ ok: false,
529
+ message: "`doctor` does not accept positional inputs."
530
+ };
531
+ if (token === "--format") {
532
+ expectsFormatValue = true;
533
+ continue;
534
+ }
535
+ if (token.startsWith("--format=")) {
536
+ const rawValue = token.slice(9);
537
+ if (rawValue.length === 0) return {
538
+ ok: false,
539
+ message: "`--format` requires a value."
540
+ };
541
+ const parsedFormat = parseDoctorFormat(rawValue);
542
+ if (parsedFormat === null) return {
543
+ ok: false,
544
+ message: "`doctor` only supports default text output or `--format json`."
545
+ };
546
+ format = parsedFormat;
547
+ continue;
548
+ }
549
+ if (token === "--pretty") {
550
+ pretty = true;
551
+ continue;
552
+ }
553
+ if (token.startsWith("-")) return {
554
+ ok: false,
555
+ message: `\`${token}\` is not supported by \`doctor\`.`
556
+ };
557
+ return {
558
+ ok: false,
559
+ message: "`doctor` does not accept positional inputs."
560
+ };
561
+ }
562
+ if (expectsFormatValue) return {
563
+ ok: false,
564
+ message: "`--format` requires a value."
565
+ };
566
+ if (pretty && format !== "json") return {
567
+ ok: false,
568
+ message: "`--pretty` requires `--format json`."
569
+ };
570
+ return {
571
+ ok: true,
572
+ format,
573
+ pretty
574
+ };
575
+ }
576
+ function isExplicitDoctorInvocation(argv) {
577
+ if (argv[2] !== "doctor") return false;
578
+ const trailingTokens = argv.slice(3);
579
+ if (trailingTokens.length === 0) return true;
580
+ return trailingTokens.some((token) => token === "--" || token.startsWith("-"));
581
+ }
582
+ function printDoctorHelp() {
583
+ for (const line of DOCTOR_HELP_LINES) console.log(line);
584
+ }
585
+ async function executeDoctorCommand({ argv, runtime }) {
586
+ const validated = validateDoctorInvocation(argv);
587
+ if (!validated.ok) {
588
+ console.error(import_picocolors.default.red(`error: ${validated.message}`));
589
+ process.exitCode = 1;
590
+ return;
591
+ }
592
+ if ("help" in validated) {
593
+ printDoctorHelp();
594
+ process.exitCode = 0;
595
+ return;
596
+ }
597
+ const report = await createDoctorReport(runtime);
598
+ renderDoctorReport(report, {
599
+ format: validated.format,
600
+ pretty: validated.pretty
601
+ });
602
+ process.exitCode = report.status === "fail" ? 2 : 0;
603
+ }
148
604
  //#endregion
149
605
  //#region src/cli/path/filter.ts
150
606
  const DEFAULT_INCLUDE_EXTENSIONS = Object.freeze([
@@ -221,7 +677,6 @@ function shouldIncludeFromDirectoryRegex(relativePath, filter) {
221
677
  if (!filter.regex) return true;
222
678
  return filter.regex.test(relativePath);
223
679
  }
224
-
225
680
  //#endregion
226
681
  //#region src/cli/total-of.ts
227
682
  const TOTAL_OF_PARTS = Object.freeze([
@@ -316,7 +771,6 @@ function resolveTotalOfOverride(result, parts) {
316
771
  function formatTotalOfParts(parts) {
317
772
  return parts.join(", ");
318
773
  }
319
-
320
774
  //#endregion
321
775
  //#region src/cli/program/options.ts
322
776
  const MODE_CHOICES = [
@@ -355,81 +809,6 @@ function parseJobsOption(value) {
355
809
  function configureProgramOptions(program, parseMode) {
356
810
  program.addOption(new Option("-m, --mode <mode>", "breakdown mode").choices(MODE_CHOICES).argParser(parseMode).default("chunk")).addOption(new Option("-f, --format <format>", "output format").choices(FORMAT_CHOICES).default("standard")).addOption(new Option("--section <section>", "document section mode").choices(SECTION_CHOICES).default("all")).addOption(new Option("--path-mode <mode>", "path resolution mode: auto (default) expands directories; manual treats --path values as literal files").choices(PATH_MODE_CHOICES).default("auto")).option("--latin-language <language>", "hint a language tag for Latin script text").option("--latin-tag <tag>", "hint a BCP 47 tag for Latin script text").option("--latin-locale <locale>", "legacy alias of --latin-language").option("--latin-hint <tag>=<pattern>", "add a custom Latin hint rule (repeatable)", collectLatinHintValue, []).option("--latin-hints-file <path>", "load custom Latin hint rules from a JSON file").option("--no-default-latin-hints", "disable built-in Latin hint rules").option("--han-language <language>", "hint a language tag for Han script text").option("--han-tag <tag>", "hint a BCP 47 tag for Han script text").option("--non-words", "collect emoji, symbols, and punctuation (excludes whitespace)").option("--include-whitespace", "include whitespace counts (implies --non-words; same as --misc)").option("--misc", "collect non-words plus whitespace (alias for --include-whitespace)").option("--total-of <parts>", "override total composition (comma-separated): words,emoji,symbols,punctuation,whitespace", parseTotalOfOption).option("--pretty", "pretty print JSON output", false).option("--debug", "enable debug diagnostics on stderr").option("--verbose", "emit verbose per-file debug diagnostics (requires --debug)").option("--debug-report [path]", "write debug diagnostics to a report file").option("--debug-report-tee", "mirror debug diagnostics to both report file and stderr").option("--debug-tee", "alias of --debug-report-tee").option("--merged", "show merged aggregate output (default)").option("--per-file", "show per-file output plus merged summary").option("--jobs <n>", "batch jobs in --path mode (1=async main-thread, >1=worker load+count)", parseJobsOption, 1).option("--print-jobs-limit", "print host jobs-limit JSON and exit (must be used alone)").option("--no-progress", "disable batch progress indicator").option("--keep-progress", "keep final batch progress line visible in standard mode").option("--no-recursive", "disable recursive directory traversal").option("--quiet-warnings", "suppress non-fatal warning diagnostics").option("--quiet-skips", "suppress debug skip output and per-file json skipped field").option("--include-ext <exts>", "comma-separated extensions to include during directory scanning", collectExtensionOption, []).option("--exclude-ext <exts>", "comma-separated extensions to exclude during directory scanning", collectExtensionOption, []).option("--regex <pattern>", "regex filter for directory-scanned paths (applies to --path directories only)").option("-p, --path <path>", "read input from file or directory (directories expand in auto mode by default)", collectPathValue, []).argument("[text...]", "text to count").showHelpAfterError();
357
811
  }
358
-
359
- //#endregion
360
- //#region node_modules/picocolors/picocolors.js
361
- var require_picocolors = /* @__PURE__ */ __commonJSMin(((exports, module) => {
362
- let p = process || {}, argv = p.argv || [], env = p.env || {};
363
- let isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
364
- let formatter = (open, close, replace = open) => (input) => {
365
- let string = "" + input, index = string.indexOf(close, open.length);
366
- return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
367
- };
368
- let replaceClose = (string, close, replace, index) => {
369
- let result = "", cursor = 0;
370
- do {
371
- result += string.substring(cursor, index) + replace;
372
- cursor = index + close.length;
373
- index = string.indexOf(close, cursor);
374
- } while (~index);
375
- return result + string.substring(cursor);
376
- };
377
- let createColors = (enabled = isColorSupported) => {
378
- let f = enabled ? formatter : () => String;
379
- return {
380
- isColorSupported: enabled,
381
- reset: f("\x1B[0m", "\x1B[0m"),
382
- bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
383
- dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
384
- italic: f("\x1B[3m", "\x1B[23m"),
385
- underline: f("\x1B[4m", "\x1B[24m"),
386
- inverse: f("\x1B[7m", "\x1B[27m"),
387
- hidden: f("\x1B[8m", "\x1B[28m"),
388
- strikethrough: f("\x1B[9m", "\x1B[29m"),
389
- black: f("\x1B[30m", "\x1B[39m"),
390
- red: f("\x1B[31m", "\x1B[39m"),
391
- green: f("\x1B[32m", "\x1B[39m"),
392
- yellow: f("\x1B[33m", "\x1B[39m"),
393
- blue: f("\x1B[34m", "\x1B[39m"),
394
- magenta: f("\x1B[35m", "\x1B[39m"),
395
- cyan: f("\x1B[36m", "\x1B[39m"),
396
- white: f("\x1B[37m", "\x1B[39m"),
397
- gray: f("\x1B[90m", "\x1B[39m"),
398
- bgBlack: f("\x1B[40m", "\x1B[49m"),
399
- bgRed: f("\x1B[41m", "\x1B[49m"),
400
- bgGreen: f("\x1B[42m", "\x1B[49m"),
401
- bgYellow: f("\x1B[43m", "\x1B[49m"),
402
- bgBlue: f("\x1B[44m", "\x1B[49m"),
403
- bgMagenta: f("\x1B[45m", "\x1B[49m"),
404
- bgCyan: f("\x1B[46m", "\x1B[49m"),
405
- bgWhite: f("\x1B[47m", "\x1B[49m"),
406
- blackBright: f("\x1B[90m", "\x1B[39m"),
407
- redBright: f("\x1B[91m", "\x1B[39m"),
408
- greenBright: f("\x1B[92m", "\x1B[39m"),
409
- yellowBright: f("\x1B[93m", "\x1B[39m"),
410
- blueBright: f("\x1B[94m", "\x1B[39m"),
411
- magentaBright: f("\x1B[95m", "\x1B[39m"),
412
- cyanBright: f("\x1B[96m", "\x1B[39m"),
413
- whiteBright: f("\x1B[97m", "\x1B[39m"),
414
- bgBlackBright: f("\x1B[100m", "\x1B[49m"),
415
- bgRedBright: f("\x1B[101m", "\x1B[49m"),
416
- bgGreenBright: f("\x1B[102m", "\x1B[49m"),
417
- bgYellowBright: f("\x1B[103m", "\x1B[49m"),
418
- bgBlueBright: f("\x1B[104m", "\x1B[49m"),
419
- bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
420
- bgCyanBright: f("\x1B[106m", "\x1B[49m"),
421
- bgWhiteBright: f("\x1B[107m", "\x1B[49m")
422
- };
423
- };
424
- module.exports = createColors();
425
- module.exports.createColors = createColors;
426
- }));
427
-
428
- //#endregion
429
- //#region src/cli/program/version-embedded.ts
430
- var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
431
- const EMBEDDED_PACKAGE_VERSION = "0.1.4";
432
-
433
812
  //#endregion
434
813
  //#region src/cli/program/version.ts
435
814
  function* candidateSearchRoots() {
@@ -462,7 +841,7 @@ function normalizeVersion(value) {
462
841
  return trimmed;
463
842
  }
464
843
  function resolvePackageVersion(options = {}) {
465
- const embeddedVersion = normalizeVersion(options.embeddedVersion ?? EMBEDDED_PACKAGE_VERSION);
844
+ const embeddedVersion = normalizeVersion(options.embeddedVersion ?? "0.1.5-canary.1");
466
845
  if (embeddedVersion) return embeddedVersion;
467
846
  const maxLevels = options.maxLevels ?? 8;
468
847
  const resolveFromPath = options.resolveFromPath ?? resolveVersionFromPath;
@@ -480,58 +859,11 @@ function getFormattedVersionLabel() {
480
859
  const version = resolvePackageVersion();
481
860
  return import_picocolors.default.bgBlack(import_picocolors.default.bold(import_picocolors.default.italic(` word-counter ${import_picocolors.default.cyanBright(`ver.${version}`)} `)));
482
861
  }
483
-
484
- //#endregion
485
- //#region src/cli/batch/jobs/limits.ts
486
- const DEFAULT_UV_THREADPOOL_SIZE = 4;
487
- function parsePositiveInteger(value) {
488
- if (!value) return;
489
- const parsed = Number.parseInt(value, 10);
490
- if (!Number.isFinite(parsed) || parsed <= 0) return;
491
- return parsed;
492
- }
493
- function resolveBatchJobsLimit(env = process.env) {
494
- const cpuLimit = Math.max(1, os.availableParallelism());
495
- const uvThreadpool = parsePositiveInteger(env.UV_THREADPOOL_SIZE) ?? DEFAULT_UV_THREADPOOL_SIZE;
496
- const ioLimit = Math.max(1, uvThreadpool * 2);
497
- return {
498
- suggestedMaxJobs: Math.max(1, Math.min(cpuLimit, ioLimit)),
499
- cpuLimit,
500
- uvThreadpool,
501
- ioLimit
502
- };
503
- }
504
- function clampRequestedJobs(requestedJobs, limits) {
505
- return Math.max(1, Math.min(requestedJobs, limits.suggestedMaxJobs));
506
- }
507
- function formatJobsAdvisoryWarning(requestedJobs, effectiveJobs, limits) {
508
- return [
509
- `Warning: requested --jobs=${requestedJobs} exceeds suggested host limit (${limits.suggestedMaxJobs}).`,
510
- `Running with --jobs=${effectiveJobs} as a safety cap.`,
511
- `Host limits: cpuLimit=${limits.cpuLimit}, uvThreadpool=${limits.uvThreadpool}, ioLimit=${limits.ioLimit}.`
512
- ].join(" ");
513
- }
514
- function isResourceLimitError(error) {
515
- if (typeof error !== "object" || error === null) return false;
516
- const code = "code" in error ? error.code : void 0;
517
- return code === "EMFILE" || code === "ENFILE";
518
- }
519
- function createResourceLimitError(path, error, requestedJobs, limits) {
520
- const message = error instanceof Error ? error.message : String(error);
521
- const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "UNKNOWN";
522
- return new Error([
523
- `Resource limit reached while processing: ${path} (${code}: ${message}).`,
524
- `Requested --jobs=${requestedJobs}; suggested host limit is ${limits.suggestedMaxJobs}.`,
525
- "Reduce --jobs or raise OS file descriptor limits before retrying."
526
- ].join(" "));
527
- }
528
-
529
862
  //#endregion
530
863
  //#region src/utils/append-all.ts
531
864
  function appendAll(target, source) {
532
865
  for (const item of source) target.push(item);
533
866
  }
534
-
535
867
  //#endregion
536
868
  //#region src/markdown/toml/arrays.ts
537
869
  function ensureArrayContainer(result, key) {
@@ -547,7 +879,6 @@ function flattenArrayTables(result) {
547
879
  result[key] = value.map((entry) => Object.entries(entry).map(([entryKey, entryValue]) => `${entryKey}=${entryValue}`).join(", ")).join(" | ");
548
880
  }
549
881
  }
550
-
551
882
  //#endregion
552
883
  //#region src/markdown/toml/keys.ts
553
884
  function stripKeyQuotes(key) {
@@ -566,7 +897,6 @@ function normalizeKeyPath(key) {
566
897
  if (segments.some((segment) => !segment)) return null;
567
898
  return segments.join(".");
568
899
  }
569
-
570
900
  //#endregion
571
901
  //#region src/markdown/toml/strings.ts
572
902
  function stripInlineComment(line) {
@@ -615,7 +945,6 @@ function parseStringLiteral(value) {
615
945
  if (value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
616
946
  return null;
617
947
  }
618
-
619
948
  //#endregion
620
949
  //#region src/markdown/toml/values.ts
621
950
  function parsePrimitive(raw) {
@@ -773,7 +1102,6 @@ function toPlainText(value) {
773
1102
  if (Array.isArray(value)) return value.map((item) => String(item)).join(", ");
774
1103
  return String(value);
775
1104
  }
776
-
777
1105
  //#endregion
778
1106
  //#region src/markdown/toml/parse-frontmatter.ts
779
1107
  function parseTomlFrontmatter(frontmatter) {
@@ -857,7 +1185,6 @@ function parseTomlFrontmatter(frontmatter) {
857
1185
  flattenArrayTables(result);
858
1186
  return result;
859
1187
  }
860
-
861
1188
  //#endregion
862
1189
  //#region src/markdown/parse-markdown.ts
863
1190
  const FENCE_TO_TYPE = {
@@ -992,7 +1319,6 @@ function parseMarkdown(input) {
992
1319
  frontmatterType: openingType
993
1320
  };
994
1321
  }
995
-
996
1322
  //#endregion
997
1323
  //#region src/wc/segmenter.ts
998
1324
  const segmenterCache = /* @__PURE__ */ new Map();
@@ -1021,7 +1347,6 @@ function countCharsForLocale(text, locale) {
1021
1347
  for (const _segment of segmenter.segment(text)) count++;
1022
1348
  return count;
1023
1349
  }
1024
-
1025
1350
  //#endregion
1026
1351
  //#region src/wc/non-words.ts
1027
1352
  const emojiRegex = /(?:\p{Extended_Pictographic}|\p{Emoji_Presentation})/u;
@@ -1135,7 +1460,6 @@ function createWhitespaceCounts() {
1135
1460
  other: 0
1136
1461
  };
1137
1462
  }
1138
-
1139
1463
  //#endregion
1140
1464
  //#region src/wc/analyze.ts
1141
1465
  function analyzeChunk(chunk, collectNonWords, includeWhitespace) {
@@ -1235,7 +1559,6 @@ function aggregateByLocale(chunks) {
1235
1559
  }
1236
1560
  return order.map((locale) => map.get(locale));
1237
1561
  }
1238
-
1239
1562
  //#endregion
1240
1563
  //#region src/wc/mode.ts
1241
1564
  const MODE_ALIASES = {
@@ -1303,10 +1626,7 @@ function normalizeMode(input) {
1303
1626
  function resolveMode(input, fallback = "chunk") {
1304
1627
  return normalizeMode(input) ?? fallback;
1305
1628
  }
1306
-
1307
- //#endregion
1308
- //#region src/wc/latin-hints.ts
1309
- const DEFAULT_LATIN_HINT_RULES_SOURCE = [
1629
+ const DEFAULT_LATIN_HINT_RULES = Object.freeze([
1310
1630
  {
1311
1631
  tag: "de",
1312
1632
  pattern: "[äöüÄÖÜß]"
@@ -1343,13 +1663,10 @@ const DEFAULT_LATIN_HINT_RULES_SOURCE = [
1343
1663
  tag: "is",
1344
1664
  pattern: "[ðÐþÞ]"
1345
1665
  }
1346
- ];
1347
- const DEFAULT_LATIN_HINT_RULES = Object.freeze(DEFAULT_LATIN_HINT_RULES_SOURCE.map((rule) => Object.freeze({ ...rule })));
1348
-
1666
+ ].map((rule) => Object.freeze({ ...rule })));
1349
1667
  //#endregion
1350
1668
  //#region src/wc/locale-detect.ts
1351
1669
  const DEFAULT_LOCALE = "und-Latn";
1352
- const DEFAULT_HAN_TAG = "und-Hani";
1353
1670
  const MAX_LATIN_HINT_PATTERN_LENGTH = 256;
1354
1671
  const regex = {
1355
1672
  hiragana: /\p{Script=Hiragana}/u,
@@ -1465,18 +1782,17 @@ function detectLocaleForChar(char, previousLocale, options = {}, context = resol
1465
1782
  if (regex.thai.test(char)) return "th";
1466
1783
  if (regex.han.test(char)) {
1467
1784
  if (allowJapaneseHanCarry && previousLocale && previousLocale.startsWith("ja")) return previousLocale;
1468
- return context.hanHint ?? DEFAULT_HAN_TAG;
1785
+ return context.hanHint ?? "und-Hani";
1469
1786
  }
1470
1787
  if (regex.latin.test(char)) {
1471
1788
  const hintedLocale = detectLatinLocale(char, context);
1472
- if (hintedLocale !== DEFAULT_LOCALE) return hintedLocale;
1473
- if (allowLatinLocaleCarry && previousLocale && isLatinLocale(previousLocale, context) && previousLocale !== DEFAULT_LOCALE) return previousLocale;
1789
+ if (hintedLocale !== "und-Latn") return hintedLocale;
1790
+ if (allowLatinLocaleCarry && previousLocale && isLatinLocale(previousLocale, context) && previousLocale !== "und-Latn") return previousLocale;
1474
1791
  if (context.latinHint) return context.latinHint;
1475
1792
  return DEFAULT_LOCALE;
1476
1793
  }
1477
1794
  return null;
1478
1795
  }
1479
-
1480
1796
  //#endregion
1481
1797
  //#region src/wc/segment.ts
1482
1798
  const HARD_BOUNDARY_REGEX = /[\r\n,.!?;:,、。!?;:.。、]/u;
@@ -1513,7 +1829,7 @@ function segmentTextByLocale(text, options = {}) {
1513
1829
  continue;
1514
1830
  }
1515
1831
  if (targetLocale !== currentLocale && detected !== null) {
1516
- if (currentLocale === DEFAULT_LOCALE && isLatinLocale(targetLocale, context)) {
1832
+ if (currentLocale === "und-Latn" && isLatinLocale(targetLocale, context)) {
1517
1833
  const promotionBreakIndex = findLastLatinPromotionBreakIndex(buffer);
1518
1834
  if (promotionBreakIndex === -1) {
1519
1835
  currentLocale = targetLocale;
@@ -1580,7 +1896,6 @@ function mergeAdjacentChunks(chunks) {
1580
1896
  merged.push(last);
1581
1897
  return merged;
1582
1898
  }
1583
-
1584
1899
  //#endregion
1585
1900
  //#region src/wc/wc.ts
1586
1901
  function wordCounter(text, options = {}) {
@@ -1695,11 +2010,9 @@ function collectNonWordsAggregate(analyzed, enabled) {
1695
2010
  }
1696
2011
  return collection;
1697
2012
  }
1698
-
1699
2013
  //#endregion
1700
2014
  //#region src/wc/index.ts
1701
2015
  var wc_default = wordCounter;
1702
-
1703
2016
  //#endregion
1704
2017
  //#region src/markdown/section-count.ts
1705
2018
  function normalizeText(value) {
@@ -1764,7 +2077,6 @@ function countSections(input, section, options = {}) {
1764
2077
  items
1765
2078
  };
1766
2079
  }
1767
-
1768
2080
  //#endregion
1769
2081
  //#region src/cli/batch/aggregate.ts
1770
2082
  function mergeWordCounterResult(left, right, preserveCollectorSegments) {
@@ -1978,7 +2290,6 @@ function finalizeBatchSummaryFromFileResults(files, section, wcOptions, options
1978
2290
  aggregate: section === "all" ? aggregateWordCounterResults(files.map((file) => file.result), preserveCollectorSegments) : aggregateSectionedResults(files.map((file) => file.result), preserveCollectorSegments)
1979
2291
  };
1980
2292
  }
1981
-
1982
2293
  //#endregion
1983
2294
  //#region src/cli/batch/jobs/queue.ts
1984
2295
  async function runBoundedQueue(total, requestedJobs, worker) {
@@ -1998,7 +2309,6 @@ async function runBoundedQueue(total, requestedJobs, worker) {
1998
2309
  await Promise.all(Array.from({ length: concurrency }, () => runWorker()));
1999
2310
  return results;
2000
2311
  }
2001
-
2002
2312
  //#endregion
2003
2313
  //#region src/cli/path/load.ts
2004
2314
  function isProbablyBinary(buffer) {
@@ -2015,7 +2325,6 @@ function isProbablyBinary(buffer) {
2015
2325
  }
2016
2326
  return suspicious / sampleSize > .3;
2017
2327
  }
2018
-
2019
2328
  //#endregion
2020
2329
  //#region src/cli/batch/jobs/read-input.ts
2021
2330
  async function readBatchInput(path, options) {
@@ -2046,7 +2355,6 @@ async function readBatchInput(path, options) {
2046
2355
  content: buffer.toString("utf8")
2047
2356
  };
2048
2357
  }
2049
-
2050
2358
  //#endregion
2051
2359
  //#region src/cli/batch/jobs/load-count.ts
2052
2360
  async function countBatchInputsWithJobs(filePaths, options) {
@@ -2101,47 +2409,6 @@ async function countBatchInputsWithJobs(filePaths, options) {
2101
2409
  skipped
2102
2410
  };
2103
2411
  }
2104
-
2105
- //#endregion
2106
- //#region src/cli/batch/jobs/load-count-worker.ts
2107
- var WorkerRouteUnavailableError = class extends Error {};
2108
- function isFallbackFriendlyWorkerError(error) {
2109
- if (typeof error !== "object" || error === null) return false;
2110
- const code = "code" in error ? String(error.code) : "";
2111
- if (code === "ERR_WORKER_PATH" || code === "ERR_WORKER_UNSUPPORTED_EXTENSION" || code === "ERR_UNKNOWN_FILE_EXTENSION" || code === "ERR_MODULE_NOT_FOUND") return true;
2112
- const message = error instanceof Error ? error.message : String(error);
2113
- return message.includes("Unknown file extension") || message.includes("Cannot find module");
2114
- }
2115
- async function countBatchInputsWithWorkerJobs(filePaths, options) {
2116
- if (process.env.WORD_COUNTER_DISABLE_WORKER_JOBS === "1" || process.env.WORD_COUNTER_DISABLE_EXPERIMENTAL_WORKERS === "1") throw new WorkerRouteUnavailableError("Worker route disabled by environment.");
2117
- let workerPoolModule;
2118
- try {
2119
- workerPoolModule = await import("./worker-pool.mjs");
2120
- } catch (error) {
2121
- throw new WorkerRouteUnavailableError(`Worker route unavailable: ${error instanceof Error ? error.message : String(error)}`);
2122
- }
2123
- try {
2124
- return await workerPoolModule.countBatchInputsWithWorkerPool({
2125
- filePaths,
2126
- jobs: options.jobs,
2127
- section: options.section,
2128
- wcOptions: options.wcOptions,
2129
- preserveCollectorSegments: options.preserveCollectorSegments,
2130
- onFileProcessed: options.onFileProcessed
2131
- });
2132
- } catch (error) {
2133
- if (error instanceof workerPoolModule.WorkerPoolTaskFatalError) {
2134
- if (error.code === "EMFILE" || error.code === "ENFILE") throw createResourceLimitError(error.path, {
2135
- code: error.code,
2136
- message: error.message
2137
- }, options.jobs, resolveBatchJobsLimit());
2138
- throw new Error(error.message);
2139
- }
2140
- if (error instanceof workerPoolModule.WorkerPoolUnavailableError || isFallbackFriendlyWorkerError(error)) throw new WorkerRouteUnavailableError(`Worker route unavailable: ${error instanceof Error ? error.message : String(error)}`);
2141
- throw error;
2142
- }
2143
- }
2144
-
2145
2412
  //#endregion
2146
2413
  //#region src/cli/batch/jobs/render.ts
2147
2414
  function finalizeBatchJobsSummary(files, section, wcOptions, options = {}) {
@@ -2150,7 +2417,6 @@ function finalizeBatchJobsSummary(files, section, wcOptions, options = {}) {
2150
2417
  preserveCollectorSegments: options.preserveCollectorSegments
2151
2418
  });
2152
2419
  }
2153
-
2154
2420
  //#endregion
2155
2421
  //#region src/cli/path/resolve.ts
2156
2422
  async function expandDirectory(rootPath, directoryPath, recursive, extensionFilter, regexFilter, skipped, recordRegexExcluded, debug, stats) {
@@ -2353,7 +2619,6 @@ async function resolveBatchFilePaths(pathInputs, options) {
2353
2619
  skipped
2354
2620
  };
2355
2621
  }
2356
-
2357
2622
  //#endregion
2358
2623
  //#region src/cli/progress/reporter.ts
2359
2624
  const PROGRESS_BAR_WIDTH = 20;
@@ -2457,7 +2722,6 @@ function createBatchProgressReporter(options) {
2457
2722
  }
2458
2723
  };
2459
2724
  }
2460
-
2461
2725
  //#endregion
2462
2726
  //#region src/cli/batch/run.ts
2463
2727
  async function runBatchCount(options) {
@@ -2609,19 +2873,16 @@ async function runBatchCount(options) {
2609
2873
  });
2610
2874
  return summary;
2611
2875
  }
2612
-
2613
2876
  //#endregion
2614
2877
  //#region src/cli/batch/jobs/strategy.ts
2615
2878
  function resolveBatchJobsStrategy(_jobs) {
2616
2879
  return "load-count";
2617
2880
  }
2618
-
2619
2881
  //#endregion
2620
2882
  //#region src/utils/show-singular-or-plural-word.ts
2621
2883
  function showSingularOrPluralWord(count, word) {
2622
2884
  return `${count} ${word}${count === 1 ? "" : "s"}`;
2623
2885
  }
2624
-
2625
2886
  //#endregion
2626
2887
  //#region src/cli/output/render.ts
2627
2888
  function getCountUnit(mode) {
@@ -2766,7 +3027,6 @@ function renderPerFileStandard(summary, labels, resolveTotalOfOverride) {
2766
3027
  }
2767
3028
  renderStandardResult(summary.aggregate, labels.overall, resolveTotalOfOverride?.(summary.aggregate));
2768
3029
  }
2769
-
2770
3030
  //#endregion
2771
3031
  //#region src/cli/output/normalize-base.ts
2772
3032
  function normalizeWordCounterResultBase(result) {
@@ -2805,7 +3065,6 @@ function normalizeBatchSummaryBase(summary) {
2805
3065
  normalizeResultBase(summary.aggregate);
2806
3066
  return summary;
2807
3067
  }
2808
-
2809
3068
  //#endregion
2810
3069
  //#region src/cli/runtime/options.ts
2811
3070
  function hasPathInput(pathValues) {
@@ -2932,7 +3191,6 @@ function resolveCountRunOptions(options) {
2932
3191
  function formatInputReadError(error) {
2933
3192
  return `Failed to read input: ${error instanceof Error ? error.message : String(error)}`;
2934
3193
  }
2935
-
2936
3194
  //#endregion
2937
3195
  //#region src/cli/runtime/batch.ts
2938
3196
  async function executeBatchCount({ argv, options, runtime, resolved, debug, teeEnabled }) {
@@ -3070,7 +3328,6 @@ async function executeBatchCount({ argv, options, runtime, resolved, debug, teeE
3070
3328
  }
3071
3329
  renderStandardResult(summary.aggregate, labels.overall, aggregateTotalOfOverride);
3072
3330
  }
3073
-
3074
3331
  //#endregion
3075
3332
  //#region src/cli/runtime/input.ts
3076
3333
  async function readStdin() {
@@ -3088,7 +3345,6 @@ async function resolveInput(textTokens) {
3088
3345
  if (textTokens.length > 0) return textTokens.join(" ");
3089
3346
  return readStdin();
3090
3347
  }
3091
-
3092
3348
  //#endregion
3093
3349
  //#region src/cli/runtime/single.ts
3094
3350
  async function executeSingleCount({ textTokens, options, resolved }) {
@@ -3129,17 +3385,23 @@ async function executeSingleCount({ textTokens, options, resolved }) {
3129
3385
  }
3130
3386
  renderStandardResult(displayResult, labels.overall, totalOfOverride);
3131
3387
  }
3132
-
3133
3388
  //#endregion
3134
3389
  //#region src/command.ts
3135
3390
  async function runCli(argv = process.argv, runtime = {}) {
3391
+ if (isExplicitDoctorInvocation(argv)) {
3392
+ await executeDoctorCommand({
3393
+ argv,
3394
+ runtime: runtime.doctor
3395
+ });
3396
+ return;
3397
+ }
3136
3398
  const program = new Command();
3137
3399
  const parseMode = (value) => {
3138
3400
  const normalized = normalizeMode(value);
3139
3401
  if (!normalized) throw new Error(`Invalid mode: ${value}`);
3140
3402
  return normalized;
3141
3403
  };
3142
- program.name("word-counter").description("Locale-aware word counting powered by Intl.Segmenter.").version(getFormattedVersionLabel(), "-v, --version", "output the version number");
3404
+ program.name("word-counter").description("Locale-aware word counting powered by Intl.Segmenter.").version(getFormattedVersionLabel(), "-v, --version", "output the version number").addHelpText("after", "\nCommands:\n doctor [options] report runtime diagnostics for this host");
3143
3405
  configureProgramOptions(program, parseMode);
3144
3406
  program.action(async (textTokens, options) => {
3145
3407
  if (options.printJobsLimit) {
@@ -3226,7 +3488,6 @@ async function runCli(argv = process.argv, runtime = {}) {
3226
3488
  });
3227
3489
  await program.parseAsync(argv);
3228
3490
  }
3229
-
3230
3491
  //#endregion
3231
3492
  //#region src/bin.ts
3232
3493
  runCli().catch((error) => {
@@ -3234,7 +3495,7 @@ runCli().catch((error) => {
3234
3495
  console.error("Failed to run CLI:", message);
3235
3496
  process.exitCode = 1;
3236
3497
  });
3237
-
3238
3498
  //#endregion
3239
- export { };
3499
+ export {};
3500
+
3240
3501
  //# sourceMappingURL=bin.mjs.map