@optique/core 1.0.0-dev.1116 → 1.0.0-dev.1122

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.
@@ -41,6 +41,17 @@ function encodePattern(pattern) {
41
41
  return pattern.replace(/%/g, "%25").replace(/:/g, "%3A");
42
42
  }
43
43
  /**
44
+ * Replaces control characters that would corrupt shell completion protocols.
45
+ * Shell completion formats use tabs as field delimiters and newlines as record
46
+ * delimiters. Null bytes are used as delimiters in zsh's format.
47
+ * @param description The description string to sanitize.
48
+ * @returns The sanitized description with control characters replaced by spaces.
49
+ * @internal
50
+ */
51
+ function sanitizeDescription(description) {
52
+ return description.replace(/[\t\n\r\0]/g, " ");
53
+ }
54
+ /**
44
55
  * The Bash shell completion generator.
45
56
  * @since 0.6.0
46
57
  */
@@ -314,12 +325,12 @@ compdef _${programName.replace(/[^a-zA-Z0-9]/g, "_")} ${programName}
314
325
  },
315
326
  *encodeSuggestions(suggestions) {
316
327
  for (const suggestion of suggestions) if (suggestion.kind === "literal") {
317
- const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
328
+ const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
318
329
  yield `${suggestion.text}\0${description}\0`;
319
330
  } else {
320
331
  const extensions = suggestion.extensions?.join(",") || "";
321
332
  const hidden = suggestion.includeHidden ? "1" : "0";
322
- const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
333
+ const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
323
334
  const pattern = encodePattern(suggestion.pattern ?? "");
324
335
  yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\0${description}\0`;
325
336
  }
@@ -482,12 +493,12 @@ complete -c ${programName} -f -a '(${functionName})'
482
493
  for (const suggestion of suggestions) {
483
494
  if (i > 0) yield "\n";
484
495
  if (suggestion.kind === "literal") {
485
- const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
496
+ const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
486
497
  yield `${suggestion.text}\t${description}`;
487
498
  } else {
488
499
  const extensions = suggestion.extensions?.join(",") || "";
489
500
  const hidden = suggestion.includeHidden ? "1" : "0";
490
- const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
501
+ const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
491
502
  const pattern = encodePattern(suggestion.pattern ?? "");
492
503
  yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t${description}`;
493
504
  }
@@ -726,12 +737,12 @@ ${functionName}-external
726
737
  for (const suggestion of suggestions) {
727
738
  if (i > 0) yield "\n";
728
739
  if (suggestion.kind === "literal") {
729
- const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
740
+ const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
730
741
  yield `${suggestion.text}\t${description}`;
731
742
  } else {
732
743
  const extensions = suggestion.extensions?.join(",") || "";
733
744
  const hidden = suggestion.includeHidden ? "1" : "0";
734
- const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
745
+ const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
735
746
  const pattern = encodePattern(suggestion.pattern ?? "");
736
747
  yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t${description}`;
737
748
  }
@@ -896,12 +907,12 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
896
907
  for (const suggestion of suggestions) {
897
908
  if (i > 0) yield "\n";
898
909
  if (suggestion.kind === "literal") {
899
- const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
910
+ const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
900
911
  yield `${suggestion.text}\t${suggestion.text}\t${description}`;
901
912
  } else {
902
913
  const extensions = suggestion.extensions?.join(",") || "";
903
914
  const hidden = suggestion.includeHidden ? "1" : "0";
904
- const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
915
+ const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
905
916
  const pattern = encodePattern(suggestion.pattern ?? "");
906
917
  yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t[file]\t${description}`;
907
918
  }
@@ -41,6 +41,17 @@ function encodePattern(pattern) {
41
41
  return pattern.replace(/%/g, "%25").replace(/:/g, "%3A");
42
42
  }
43
43
  /**
44
+ * Replaces control characters that would corrupt shell completion protocols.
45
+ * Shell completion formats use tabs as field delimiters and newlines as record
46
+ * delimiters. Null bytes are used as delimiters in zsh's format.
47
+ * @param description The description string to sanitize.
48
+ * @returns The sanitized description with control characters replaced by spaces.
49
+ * @internal
50
+ */
51
+ function sanitizeDescription(description) {
52
+ return description.replace(/[\t\n\r\0]/g, " ");
53
+ }
54
+ /**
44
55
  * The Bash shell completion generator.
45
56
  * @since 0.6.0
46
57
  */
@@ -314,12 +325,12 @@ compdef _${programName.replace(/[^a-zA-Z0-9]/g, "_")} ${programName}
314
325
  },
315
326
  *encodeSuggestions(suggestions) {
316
327
  for (const suggestion of suggestions) if (suggestion.kind === "literal") {
317
- const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
328
+ const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
318
329
  yield `${suggestion.text}\0${description}\0`;
319
330
  } else {
320
331
  const extensions = suggestion.extensions?.join(",") || "";
321
332
  const hidden = suggestion.includeHidden ? "1" : "0";
322
- const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
333
+ const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
323
334
  const pattern = encodePattern(suggestion.pattern ?? "");
324
335
  yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\0${description}\0`;
325
336
  }
@@ -482,12 +493,12 @@ complete -c ${programName} -f -a '(${functionName})'
482
493
  for (const suggestion of suggestions) {
483
494
  if (i > 0) yield "\n";
484
495
  if (suggestion.kind === "literal") {
485
- const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
496
+ const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
486
497
  yield `${suggestion.text}\t${description}`;
487
498
  } else {
488
499
  const extensions = suggestion.extensions?.join(",") || "";
489
500
  const hidden = suggestion.includeHidden ? "1" : "0";
490
- const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
501
+ const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
491
502
  const pattern = encodePattern(suggestion.pattern ?? "");
492
503
  yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t${description}`;
493
504
  }
@@ -726,12 +737,12 @@ ${functionName}-external
726
737
  for (const suggestion of suggestions) {
727
738
  if (i > 0) yield "\n";
728
739
  if (suggestion.kind === "literal") {
729
- const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
740
+ const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
730
741
  yield `${suggestion.text}\t${description}`;
731
742
  } else {
732
743
  const extensions = suggestion.extensions?.join(",") || "";
733
744
  const hidden = suggestion.includeHidden ? "1" : "0";
734
- const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
745
+ const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
735
746
  const pattern = encodePattern(suggestion.pattern ?? "");
736
747
  yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t${description}`;
737
748
  }
@@ -896,12 +907,12 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
896
907
  for (const suggestion of suggestions) {
897
908
  if (i > 0) yield "\n";
898
909
  if (suggestion.kind === "literal") {
899
- const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
910
+ const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
900
911
  yield `${suggestion.text}\t${suggestion.text}\t${description}`;
901
912
  } else {
902
913
  const extensions = suggestion.extensions?.join(",") || "";
903
914
  const hidden = suggestion.includeHidden ? "1" : "0";
904
- const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
915
+ const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
905
916
  const pattern = encodePattern(suggestion.pattern ?? "");
906
917
  yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}\t[file]\t${description}`;
907
918
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1116+6084dd3a",
3
+ "version": "1.0.0-dev.1122+85e536cf",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",