@gmb/bitmark-parser-generator 5.15.0 → 5.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/main.js CHANGED
@@ -10876,7 +10876,7 @@ var instance2 = new Config();
10876
10876
  // src/generated/package_info.ts
10877
10877
  var PACKAGE_INFO = {
10878
10878
  "name": "@gmb/bitmark-parser-generator",
10879
- "version": "5.15.0",
10879
+ "version": "5.17.0",
10880
10880
  "license": "ISC"};
10881
10881
  var Environment = {
10882
10882
  unknown: "",
@@ -11107,6 +11107,16 @@ var TextMarkType = {
11107
11107
  light: "light",
11108
11108
  italic: "italic",
11109
11109
  highlight: "highlight",
11110
+ highlightOrange: "highlightOrange",
11111
+ highlightYellow: "highlightYellow",
11112
+ highlightGreen: "highlightGreen",
11113
+ highlightBlue: "highlightBlue",
11114
+ highlightPurple: "highlightPurple",
11115
+ highlightPink: "highlightPink",
11116
+ highlightBrown: "highlightBrown",
11117
+ highlightBlack: "highlightBlack",
11118
+ highlightWhite: "highlightWhite",
11119
+ highlightGray: "highlightGray",
11110
11120
  // Inline only styles
11111
11121
  strike: "strike",
11112
11122
  subscript: "subscript",
@@ -12601,6 +12611,16 @@ var STANDARD_MARK_TYPES = [
12601
12611
  TextMarkType.highlight
12602
12612
  ];
12603
12613
  var INLINE_MARK_TYPES = [
12614
+ TextMarkType.highlightOrange,
12615
+ TextMarkType.highlightYellow,
12616
+ TextMarkType.highlightGreen,
12617
+ TextMarkType.highlightBlue,
12618
+ TextMarkType.highlightPurple,
12619
+ TextMarkType.highlightPink,
12620
+ TextMarkType.highlightBrown,
12621
+ TextMarkType.highlightBlack,
12622
+ TextMarkType.highlightWhite,
12623
+ TextMarkType.highlightGray,
12604
12624
  TextMarkType.strike,
12605
12625
  TextMarkType.subscript,
12606
12626
  TextMarkType.superscript,
@@ -39294,6 +39314,265 @@ var JsonFileGenerator = class {
39294
39314
  this.generator.generateSync(_ast);
39295
39315
  }
39296
39316
  };
39317
+
39318
+ // src/generator/plainText/PlainTextGenerator.ts
39319
+ var TEXT_NODE_TYPES = new Set(Object.values(TextNodeType));
39320
+ var PlainTextGenerator = class {
39321
+ /**
39322
+ * Generate plain text from a string or JSON object.
39323
+ *
39324
+ * @param input - A string (plain or JSON-encoded) or a parsed JSON value.
39325
+ * @returns The extracted plain text.
39326
+ */
39327
+ generate(input) {
39328
+ let data = input;
39329
+ if (typeof data === "string") {
39330
+ try {
39331
+ data = JSON.parse(data);
39332
+ } catch (_e) {
39333
+ return data;
39334
+ }
39335
+ }
39336
+ return this.walk(data).trim();
39337
+ }
39338
+ // ---------------------------------------------------------------------------
39339
+ // Private helpers
39340
+ // ---------------------------------------------------------------------------
39341
+ walk(value) {
39342
+ if (value == null) return "";
39343
+ if (typeof value === "string") return value;
39344
+ if (typeof value !== "object") return "";
39345
+ if (Array.isArray(value)) {
39346
+ if (value.length === 0) return "";
39347
+ if (this.isTextAst(value)) {
39348
+ return this.textAstToPlainText(value);
39349
+ }
39350
+ return value.map((v) => this.walk(v)).filter(Boolean).join("\n");
39351
+ }
39352
+ const obj = value;
39353
+ if (this.isTextNode(obj)) {
39354
+ return this.textNodeToPlainText(obj);
39355
+ }
39356
+ if (this.isBitWrapper(obj)) {
39357
+ return this.walk(obj["bit"]);
39358
+ }
39359
+ const parts = [];
39360
+ for (const val of Object.values(obj)) {
39361
+ if (val == null || typeof val !== "object") continue;
39362
+ const text = this.walk(val);
39363
+ if (text) parts.push(text);
39364
+ }
39365
+ return parts.join("\n");
39366
+ }
39367
+ // -- Type guards -----------------------------------------------------------
39368
+ isTextNode(obj) {
39369
+ return typeof obj["type"] === "string" && TEXT_NODE_TYPES.has(obj["type"]);
39370
+ }
39371
+ isTextAst(arr) {
39372
+ const first = arr[0];
39373
+ return typeof first === "object" && first !== null && !Array.isArray(first) && this.isTextNode(first);
39374
+ }
39375
+ isBitWrapper(obj) {
39376
+ return "bit" in obj && typeof obj["bit"] === "object" && obj["bit"] !== null && !Array.isArray(obj["bit"]);
39377
+ }
39378
+ // -- TextNode extraction ---------------------------------------------------
39379
+ textAstToPlainText(ast) {
39380
+ return ast.map((node) => this.textNodeToPlainText(node)).join("\n");
39381
+ }
39382
+ textNodeToPlainText(node) {
39383
+ const { type, text, content } = node;
39384
+ switch (type) {
39385
+ case TextNodeType.text:
39386
+ return this.textWithMarks(node);
39387
+ case TextNodeType.hardBreak:
39388
+ return "\n";
39389
+ // Block elements whose children are joined without extra separator
39390
+ case TextNodeType.paragraph:
39391
+ case TextNodeType.heading:
39392
+ case TextNodeType.section:
39393
+ case TextNodeType.gap:
39394
+ case TextNodeType.select:
39395
+ case TextNodeType.highlight:
39396
+ case TextNodeType.mark:
39397
+ case TextNodeType.codeBlock:
39398
+ return content ? content.map((c) => this.textNodeToPlainText(c)).join("") : text ?? "";
39399
+ // List items are handled by listToPlainText with indent context
39400
+ case TextNodeType.listItem:
39401
+ return content ? content.map((c) => this.textNodeToPlainText(c)).join("") : text ?? "";
39402
+ // Task item – handled by taskListToPlainText, but fallback if encountered standalone
39403
+ case TextNodeType.taskItem: {
39404
+ const checked = node.attrs?.checked ?? false;
39405
+ const prefix = checked ? "[x] " : "[ ] ";
39406
+ const itemText = content ? content.map((c) => this.textNodeToPlainText(c)).join("") : text ?? "";
39407
+ return `${prefix}${itemText}`;
39408
+ }
39409
+ // List containers – rendered with indent-aware helper
39410
+ case TextNodeType.noBulletList:
39411
+ case TextNodeType.bulletList:
39412
+ case TextNodeType.orderedList:
39413
+ case TextNodeType.orderedListRoman:
39414
+ case TextNodeType.orderedListRomanLower:
39415
+ case TextNodeType.letteredList:
39416
+ case TextNodeType.letteredListLower:
39417
+ return this.listToPlainText(node, 0);
39418
+ // Task list – rendered with indent-aware helper
39419
+ case TextNodeType.taskList:
39420
+ return this.taskListToPlainText(node, 0);
39421
+ // Images – return alt text when available
39422
+ case TextNodeType.image:
39423
+ case TextNodeType.imageInline: {
39424
+ const attrs = node.attrs;
39425
+ return attrs?.alt ?? "";
39426
+ }
39427
+ // LaTeX – return the formula source
39428
+ case TextNodeType.latex: {
39429
+ const latexAttrs = node.attrs;
39430
+ return latexAttrs?.formula ?? "";
39431
+ }
39432
+ default:
39433
+ return content ? content.map((c) => this.textNodeToPlainText(c)).join("") : text ?? "";
39434
+ }
39435
+ }
39436
+ listToPlainText(node, depth) {
39437
+ const { type, content } = node;
39438
+ if (!content || content.length === 0) return "";
39439
+ const indent = " ".repeat(depth);
39440
+ const start = node.attrs?.start ?? 1;
39441
+ const displayStart = start < 1 ? start + 1 : start;
39442
+ return content.map((child, i) => {
39443
+ const { inline, nested } = this.splitListItemContent(child, depth);
39444
+ const prefix = this.listItemPrefix(type, displayStart + i);
39445
+ const line = `${indent}${prefix}${inline}`;
39446
+ return nested ? `${line}
39447
+ ${nested}` : line;
39448
+ }).join("\n");
39449
+ }
39450
+ taskListToPlainText(node, depth) {
39451
+ const { content } = node;
39452
+ if (!content || content.length === 0) return "";
39453
+ const indent = " ".repeat(depth);
39454
+ return content.map((child) => {
39455
+ const checked = child.attrs?.checked ?? false;
39456
+ const prefix = checked ? "[x] " : "[ ] ";
39457
+ const { inline, nested } = this.splitListItemContent(child, depth);
39458
+ const line = `${indent}${prefix}${inline}`;
39459
+ return nested ? `${line}
39460
+ ${nested}` : line;
39461
+ }).join("\n");
39462
+ }
39463
+ splitListItemContent(item, depth) {
39464
+ const children = item.content ?? [];
39465
+ const inlineParts = [];
39466
+ const nestedParts = [];
39467
+ for (const child of children) {
39468
+ if (this.isListType(child.type)) {
39469
+ nestedParts.push(this.renderNestedList(child, depth + 1));
39470
+ } else {
39471
+ inlineParts.push(this.textNodeToPlainText(child));
39472
+ }
39473
+ }
39474
+ return {
39475
+ inline: inlineParts.join(""),
39476
+ nested: nestedParts.join("\n")
39477
+ };
39478
+ }
39479
+ isListType(type) {
39480
+ return type === TextNodeType.bulletList || type === TextNodeType.orderedList || type === TextNodeType.orderedListRoman || type === TextNodeType.orderedListRomanLower || type === TextNodeType.letteredList || type === TextNodeType.letteredListLower || type === TextNodeType.noBulletList || type === TextNodeType.taskList;
39481
+ }
39482
+ renderNestedList(node, depth) {
39483
+ if (node.type === TextNodeType.taskList) {
39484
+ return this.taskListToPlainText(node, depth);
39485
+ }
39486
+ return this.listToPlainText(node, depth);
39487
+ }
39488
+ listItemPrefix(listType, index) {
39489
+ switch (listType) {
39490
+ case TextNodeType.bulletList:
39491
+ return "\u2022 ";
39492
+ case TextNodeType.orderedList:
39493
+ return `${index}. `;
39494
+ case TextNodeType.orderedListRoman:
39495
+ return `${this.toRoman(index)}. `;
39496
+ case TextNodeType.orderedListRomanLower:
39497
+ return `${this.toRoman(index).toLowerCase()}. `;
39498
+ case TextNodeType.letteredList:
39499
+ return `${this.toLetter(index)}. `;
39500
+ case TextNodeType.letteredListLower:
39501
+ return `${this.toLetter(index).toLowerCase()}. `;
39502
+ case TextNodeType.noBulletList:
39503
+ default:
39504
+ return "";
39505
+ }
39506
+ }
39507
+ toRoman(num) {
39508
+ const romanNumerals = [
39509
+ [1e3, "M"],
39510
+ [900, "CM"],
39511
+ [500, "D"],
39512
+ [400, "CD"],
39513
+ [100, "C"],
39514
+ [90, "XC"],
39515
+ [50, "L"],
39516
+ [40, "XL"],
39517
+ [10, "X"],
39518
+ [9, "IX"],
39519
+ [5, "V"],
39520
+ [4, "IV"],
39521
+ [1, "I"]
39522
+ ];
39523
+ let result = "";
39524
+ let remaining = num;
39525
+ for (const [value, numeral] of romanNumerals) {
39526
+ while (remaining >= value) {
39527
+ result += numeral;
39528
+ remaining -= value;
39529
+ }
39530
+ }
39531
+ return result;
39532
+ }
39533
+ toLetter(num) {
39534
+ let result = "";
39535
+ let remaining = num;
39536
+ while (remaining > 0) {
39537
+ remaining--;
39538
+ result = String.fromCharCode(65 + remaining % 26) + result;
39539
+ remaining = Math.floor(remaining / 26);
39540
+ }
39541
+ return result;
39542
+ }
39543
+ textWithMarks(node) {
39544
+ const { text, marks } = node;
39545
+ const parts = [];
39546
+ const linkMark = marks?.find((m) => m.type === "link");
39547
+ const href = linkMark?.attrs?.href;
39548
+ if (text && href && text !== href) {
39549
+ const hrefBare = href.replace(/^https?:\/\//, "");
39550
+ if (text.includes(hrefBare)) {
39551
+ parts.push(text.replace(hrefBare, href));
39552
+ } else if (text.includes(href)) {
39553
+ parts.push(text);
39554
+ } else {
39555
+ parts.push(`${text} ${href}`);
39556
+ }
39557
+ } else if (text) {
39558
+ parts.push(text);
39559
+ } else if (href) {
39560
+ parts.push(href);
39561
+ }
39562
+ if (marks) {
39563
+ for (const mark of marks) {
39564
+ if (mark.type === "footnote") {
39565
+ const footnote = mark;
39566
+ if (footnote.attrs?.content) {
39567
+ const footnoteText = footnote.attrs.content.map((c) => this.textNodeToPlainText(c)).join("");
39568
+ if (footnoteText) parts.push(footnoteText);
39569
+ }
39570
+ }
39571
+ }
39572
+ }
39573
+ return parts.join(" ");
39574
+ }
39575
+ };
39297
39576
  var normalizeCardKey = (cardSetKey) => stringUtils.camelToKebab(cardSetKey);
39298
39577
  var ConfigBuilder = class {
39299
39578
  build(options) {
@@ -39995,7 +40274,25 @@ var Output = {
39995
40274
  /**
39996
40275
  * Output AST as a plain JS object, or a file
39997
40276
  */
39998
- ast: "ast"
40277
+ ast: "ast",
40278
+ /**
40279
+ * Output plain text as a string, or a file
40280
+ */
40281
+ text: "text"
40282
+ };
40283
+ var InputFormat = {
40284
+ /**
40285
+ * Input is bitmark
40286
+ */
40287
+ bitmark: "bitmark",
40288
+ /**
40289
+ * Input is bitmarkText
40290
+ */
40291
+ bitmarkText: "bitmarkText",
40292
+ /**
40293
+ * Input is plain text
40294
+ */
40295
+ plainText: "plainText"
39999
40296
  };
40000
40297
  var BitmarkParserGenerator = class {
40001
40298
  ast = new Ast();
@@ -40079,6 +40376,12 @@ var BitmarkParserGenerator = class {
40079
40376
  * - input(JSON/AST) ==> output(bitmark)
40080
40377
  * - input(bitmark) ==> output(JSON)
40081
40378
  *
40379
+ * Output type can be overridden to one of the following:
40380
+ * - bitmark: output bitmark string
40381
+ * - json: output JSON as a plain JS object, or a file
40382
+ * - ast: output AST as a plain JS object, or a file
40383
+ * - text: output plain text as a string, or a file
40384
+ *
40082
40385
  * By default, the result is returned as a string for bitmark, or a plain JS object for JSON/AST.
40083
40386
  *
40084
40387
  * The options can be used to write the output to a file and to set conversion options or override defaults.
@@ -40101,6 +40404,7 @@ var BitmarkParserGenerator = class {
40101
40404
  const outputBitmark = outputFormat === Output.bitmark;
40102
40405
  const outputJson = outputFormat === Output.json;
40103
40406
  const outputAst = outputFormat === Output.ast;
40407
+ const outputText = outputFormat === Output.text;
40104
40408
  const bitmarkParserType = BitmarkParserType.peggy;
40105
40409
  let inStr = input;
40106
40410
  const inputIsString = typeof input === "string";
@@ -40147,6 +40451,22 @@ var BitmarkParserGenerator = class {
40147
40451
  }
40148
40452
  }
40149
40453
  };
40454
+ const bitmarkToText = (bitmarkStr) => {
40455
+ ast = this.bitmarkParser.toAst(bitmarkStr, {
40456
+ parserType: bitmarkParserType
40457
+ });
40458
+ const jsonGenerator = new JsonObjectGenerator(opts);
40459
+ const json = jsonGenerator.generateSync(ast);
40460
+ const textGenerator = new PlainTextGenerator();
40461
+ const str = textGenerator.generate(json);
40462
+ if (opts.outputFile) {
40463
+ fs3.writeFileSync(opts.outputFile, str, {
40464
+ encoding: "utf8"
40465
+ });
40466
+ } else {
40467
+ res = str;
40468
+ }
40469
+ };
40150
40470
  const astToBitmark = (astJson) => {
40151
40471
  if (opts.outputFile) {
40152
40472
  const generator = new BitmarkFileGenerator(opts.outputFile, opts);
@@ -40169,6 +40489,19 @@ var BitmarkParserGenerator = class {
40169
40489
  res = this.jsonStringifyPrettify(json, jsonOptions);
40170
40490
  }
40171
40491
  };
40492
+ const astToText = (astJson) => {
40493
+ const jsonGenerator = new JsonObjectGenerator(opts);
40494
+ const json = jsonGenerator.generateSync(astJson);
40495
+ const textGenerator = new PlainTextGenerator();
40496
+ const str = textGenerator.generate(json);
40497
+ if (opts.outputFile) {
40498
+ fs3.writeFileSync(opts.outputFile, str, {
40499
+ encoding: "utf8"
40500
+ });
40501
+ } else {
40502
+ res = str;
40503
+ }
40504
+ };
40172
40505
  const jsonToBitmark = (astJson) => {
40173
40506
  if (opts.outputFile) {
40174
40507
  const generator = new BitmarkFileGenerator(opts.outputFile, opts);
@@ -40181,6 +40514,19 @@ var BitmarkParserGenerator = class {
40181
40514
  const jsonToAst = (astJson) => {
40182
40515
  res = this.jsonStringifyPrettify(astJson, jsonOptions);
40183
40516
  };
40517
+ const jsonToText = (astJson) => {
40518
+ const jsonGenerator = new JsonObjectGenerator(opts);
40519
+ const json = jsonGenerator.generateSync(astJson);
40520
+ const textGenerator = new PlainTextGenerator();
40521
+ const str = textGenerator.generate(json);
40522
+ if (opts.outputFile) {
40523
+ fs3.writeFileSync(opts.outputFile, str, {
40524
+ encoding: "utf8"
40525
+ });
40526
+ } else {
40527
+ res = str;
40528
+ }
40529
+ };
40184
40530
  const jsonToJson = (astJson) => {
40185
40531
  astToJson(astJson);
40186
40532
  };
@@ -40189,6 +40535,8 @@ var BitmarkParserGenerator = class {
40189
40535
  bitmarkToBitmark(inStr);
40190
40536
  } else if (outputAst) {
40191
40537
  bitmarkToAst(inStr);
40538
+ } else if (outputText) {
40539
+ bitmarkToText(inStr);
40192
40540
  } else {
40193
40541
  bitmarkToJson(inStr);
40194
40542
  }
@@ -40198,6 +40546,8 @@ var BitmarkParserGenerator = class {
40198
40546
  astToAst(ast);
40199
40547
  } else if (outputJson) {
40200
40548
  astToJson(ast);
40549
+ } else if (outputText) {
40550
+ astToText(ast);
40201
40551
  } else {
40202
40552
  astToBitmark(ast);
40203
40553
  }
@@ -40207,6 +40557,8 @@ var BitmarkParserGenerator = class {
40207
40557
  jsonToJson(ast);
40208
40558
  } else if (outputAst) {
40209
40559
  jsonToAst(ast);
40560
+ } else if (outputText) {
40561
+ jsonToText(ast);
40210
40562
  } else {
40211
40563
  jsonToBitmark(ast);
40212
40564
  }
@@ -40335,7 +40687,7 @@ var BitmarkParserGenerator = class {
40335
40687
  return res;
40336
40688
  }
40337
40689
  /**
40338
- * Convert bitmark text from JSON, or JSON to bitmark text.
40690
+ * Convert bitmark text to JSON, or JSON to bitmark text.
40339
40691
  *
40340
40692
  * Input type is detected automatically and may be:
40341
40693
  * - string: bitmark text or JSON
@@ -40409,6 +40761,54 @@ var BitmarkParserGenerator = class {
40409
40761
  }
40410
40762
  return res;
40411
40763
  }
40764
+ extractPlainText(input, options) {
40765
+ const dataIn = input;
40766
+ const inputFormat = options?.inputFormat;
40767
+ const isString2 = typeof input === "string";
40768
+ let data;
40769
+ const parseAutomatically = () => {
40770
+ let dataOut = dataIn;
40771
+ if (typeof dataIn === "string") {
40772
+ try {
40773
+ dataOut = JSON.parse(dataIn);
40774
+ } catch (_e) {
40775
+ let isBitmark = false;
40776
+ const bitmarkData = this.convert(dataIn, {
40777
+ outputFormat: Output.json
40778
+ });
40779
+ if (bitmarkData.length > 0) {
40780
+ const isError = bitmarkData[0].bit.type === BitType._error;
40781
+ if (!isError) {
40782
+ isBitmark = true;
40783
+ dataOut = bitmarkData;
40784
+ }
40785
+ }
40786
+ if (!isBitmark) {
40787
+ dataOut = this.convertText(dataIn, {
40788
+ textFormat: TextFormat.bitmarkText
40789
+ });
40790
+ }
40791
+ }
40792
+ }
40793
+ return dataOut;
40794
+ };
40795
+ if (inputFormat === InputFormat.bitmark) {
40796
+ data = this.convert(dataIn, {
40797
+ outputFormat: Output.json
40798
+ });
40799
+ } else if (inputFormat === InputFormat.bitmarkText) {
40800
+ data = this.convertText(dataIn, {
40801
+ textFormat: TextFormat.bitmarkText
40802
+ });
40803
+ } else if (inputFormat === InputFormat.plainText) {
40804
+ if (isString2) data = String(input);
40805
+ } else {
40806
+ data = parseAutomatically();
40807
+ }
40808
+ const generator = new PlainTextGenerator();
40809
+ const res = generator.generate(data);
40810
+ return res;
40811
+ }
40412
40812
  /**
40413
40813
  * Breakscape bitmark text.
40414
40814
  *
@@ -40516,6 +40916,16 @@ var BitmarkParserGenerator = class {
40516
40916
  }
40517
40917
  return;
40518
40918
  }
40919
+ textAstToPlainText(textAst, _options) {
40920
+ const textGenerator = new TextGenerator(BitmarkVersion.v3, {
40921
+ //
40922
+ });
40923
+ const res = textGenerator.generateSync(textAst, TextFormat.bitmarkText, TextLocation.body, {
40924
+ noBreakscaping: true,
40925
+ noMarkup: true
40926
+ });
40927
+ return res;
40928
+ }
40519
40929
  /**
40520
40930
  * Stringify / prettify a plain JS object to a JSON string, depending on the JSON options
40521
40931
  *
@@ -40773,6 +41183,42 @@ Examples:
40773
41183
  );
40774
41184
  return cmd;
40775
41185
  }
41186
+ var INPUT_FORMAT_CHOICES = enumChoices(InputFormat);
41187
+ function createExtractPlainTextCommand() {
41188
+ const bpg = new BitmarkParserGenerator();
41189
+ const cmd = new Command("extractPlainText").description("Extract plain text from bitmark, bitmark text, or JSON").argument(
41190
+ "[input]",
41191
+ "file to read, or text or json string. If not specified, input will be from <stdin>"
41192
+ ).addOption(
41193
+ new Option(
41194
+ "-f, --inputFormat <format>",
41195
+ "force input format (auto-detected by default)"
41196
+ ).choices([...INPUT_FORMAT_CHOICES])
41197
+ ).option("-a, --append", "append to the output file (default is to overwrite)").option("-o, --output <file>", "output file. If not specified, output will be to <stdout>").action(async (input, options) => {
41198
+ try {
41199
+ const dataIn = await readInput(input);
41200
+ const result = bpg.extractPlainText(dataIn, {
41201
+ inputFormat: Enum(InputFormat).fromValue(options.inputFormat)
41202
+ });
41203
+ await writeOutput(result ?? "", options.output, options.append);
41204
+ } catch (error) {
41205
+ console.error("Error:", error instanceof Error ? error.message : String(error));
41206
+ process.exit(1);
41207
+ }
41208
+ }).addHelpText(
41209
+ "after",
41210
+ `
41211
+ Examples:
41212
+ $ bitmark-parser extractPlainText '[{"type":"paragraph","content":[{"text":"Hello World","type":"text"}],"attrs":{}}]'
41213
+
41214
+ $ bitmark-parser extractPlainText input.json
41215
+
41216
+ $ bitmark-parser extractPlainText input.json -o output.txt
41217
+
41218
+ $ bitmark-parser extractPlainText -f bitmark input.bitmark`
41219
+ );
41220
+ return cmd;
41221
+ }
40776
41222
  var INFO_TYPE_CHOICES = (() => {
40777
41223
  const choices = new Set(enumChoices(InfoType));
40778
41224
  const ordered = [];
@@ -40900,6 +41346,7 @@ async function asyncInit() {
40900
41346
  });
40901
41347
  program.addCommand(createConvertCommand());
40902
41348
  program.addCommand(createConvertTextCommand());
41349
+ program.addCommand(createExtractPlainTextCommand());
40903
41350
  program.addCommand(createBreakscapeCommand());
40904
41351
  program.addCommand(createUnbreakscapeCommand());
40905
41352
  program.addCommand(createInfoCommand());