@optique/core 1.0.0-dev.1129 → 1.0.0-dev.1155
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/completion.cjs +48 -29
- package/dist/completion.js +48 -29
- package/dist/facade.cjs +61 -0
- package/dist/facade.d.cts +4 -1
- package/dist/facade.d.ts +4 -1
- package/dist/facade.js +61 -0
- package/dist/valueparser.cjs +20 -2
- package/dist/valueparser.d.cts +2 -0
- package/dist/valueparser.d.ts +2 -0
- package/dist/valueparser.js +20 -2
- package/package.json +1 -1
package/dist/completion.cjs
CHANGED
|
@@ -40,6 +40,9 @@ function validateProgramName(programName) {
|
|
|
40
40
|
function encodePattern(pattern) {
|
|
41
41
|
return pattern.replace(/%/g, "%25").replace(/:/g, "%3A");
|
|
42
42
|
}
|
|
43
|
+
function encodeExtensions(extensions) {
|
|
44
|
+
return extensions?.map((ext) => ext.replace(/^\./, "")).join(",") ?? "";
|
|
45
|
+
}
|
|
43
46
|
/**
|
|
44
47
|
* Replaces control characters that would corrupt shell completion protocols.
|
|
45
48
|
* Shell completion formats use tabs as field delimiters and newlines as record
|
|
@@ -114,17 +117,25 @@ function _${programName} () {
|
|
|
114
117
|
# Generate file completions based on type
|
|
115
118
|
case "$type" in
|
|
116
119
|
file)
|
|
117
|
-
# Complete files
|
|
120
|
+
# Complete files and directories (directories for navigation)
|
|
118
121
|
if [[ -n "$extensions" ]]; then
|
|
119
|
-
#
|
|
122
|
+
# Files with extension filtering + directories
|
|
120
123
|
local ext_pattern="\${extensions//,/|}"
|
|
121
|
-
for
|
|
122
|
-
[[ -
|
|
124
|
+
for item in "$__glob_current"*; do
|
|
125
|
+
if [[ -d "$item" ]]; then
|
|
126
|
+
COMPREPLY+=("$item/")
|
|
127
|
+
elif [[ -f "$item" && "$item" =~ \\.($ext_pattern)$ ]]; then
|
|
128
|
+
COMPREPLY+=("$item")
|
|
129
|
+
fi
|
|
123
130
|
done
|
|
124
131
|
else
|
|
125
|
-
# Complete files
|
|
132
|
+
# Complete files and directories for navigation
|
|
126
133
|
for item in "$__glob_current"*; do
|
|
127
|
-
[[ -
|
|
134
|
+
if [[ -d "$item" ]]; then
|
|
135
|
+
COMPREPLY+=("$item/")
|
|
136
|
+
elif [[ -f "$item" ]]; then
|
|
137
|
+
COMPREPLY+=("$item")
|
|
138
|
+
fi
|
|
128
139
|
done
|
|
129
140
|
fi
|
|
130
141
|
;;
|
|
@@ -203,7 +214,7 @@ complete -F _${programName} -- ${programName}
|
|
|
203
214
|
if (i > 0) yield "\n";
|
|
204
215
|
if (suggestion.kind === "literal") yield `${suggestion.text}`;
|
|
205
216
|
else {
|
|
206
|
-
const extensions = suggestion.extensions
|
|
217
|
+
const extensions = encodeExtensions(suggestion.extensions);
|
|
207
218
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
208
219
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
209
220
|
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}`;
|
|
@@ -271,11 +282,11 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
271
282
|
case "\$type" in
|
|
272
283
|
file)
|
|
273
284
|
if [[ -n "\$extensions" ]]; then
|
|
274
|
-
# Complete files with extension filtering
|
|
285
|
+
# Complete files with extension filtering + directories for navigation
|
|
275
286
|
local ext_pattern="*.(\$\{extensions//,/|\})"
|
|
276
|
-
_files -g "\$ext_pattern"
|
|
287
|
+
_files -g "\$ext_pattern"; _directories
|
|
277
288
|
else
|
|
278
|
-
_files
|
|
289
|
+
_files
|
|
279
290
|
fi
|
|
280
291
|
;;
|
|
281
292
|
directory)
|
|
@@ -285,7 +296,7 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
285
296
|
if [[ -n "\$extensions" ]]; then
|
|
286
297
|
# Complete both files and directories, with extension filtering for files
|
|
287
298
|
local ext_pattern="*.(\$\{extensions//,/|\})"
|
|
288
|
-
_files -g "\$ext_pattern"
|
|
299
|
+
_files -g "\$ext_pattern"; _directories
|
|
289
300
|
else
|
|
290
301
|
_files
|
|
291
302
|
fi
|
|
@@ -328,7 +339,7 @@ compdef _${programName.replace(/[^a-zA-Z0-9]/g, "_")} ${programName}
|
|
|
328
339
|
const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
329
340
|
yield `${suggestion.text}\0${description}\0`;
|
|
330
341
|
} else {
|
|
331
|
-
const extensions = suggestion.extensions
|
|
342
|
+
const extensions = encodeExtensions(suggestion.extensions);
|
|
332
343
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
333
344
|
const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
334
345
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
@@ -375,9 +386,11 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
375
386
|
set -l items
|
|
376
387
|
switch $type
|
|
377
388
|
case file
|
|
378
|
-
# Complete files
|
|
389
|
+
# Complete files and directories (directories for navigation)
|
|
379
390
|
for item in $current*
|
|
380
|
-
if test -
|
|
391
|
+
if test -d $item
|
|
392
|
+
set -a items $item/
|
|
393
|
+
else if test -f $item
|
|
381
394
|
set -a items $item
|
|
382
395
|
end
|
|
383
396
|
end
|
|
@@ -390,7 +403,9 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
390
403
|
if test "$hidden" = "1"
|
|
391
404
|
if test -z "$current"; or string match -q '*/' -- "$current"
|
|
392
405
|
for item in $current.*
|
|
393
|
-
if test -
|
|
406
|
+
if test -d $item
|
|
407
|
+
set -a items $item/
|
|
408
|
+
else if test -f $item
|
|
394
409
|
set -a items $item
|
|
395
410
|
end
|
|
396
411
|
end
|
|
@@ -496,7 +511,7 @@ complete -c ${programName} -f -a '(${functionName})'
|
|
|
496
511
|
const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
497
512
|
yield `${suggestion.text}\t${description}`;
|
|
498
513
|
} else {
|
|
499
|
-
const extensions = suggestion.extensions
|
|
514
|
+
const extensions = encodeExtensions(suggestion.extensions);
|
|
500
515
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
501
516
|
const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
502
517
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
@@ -630,13 +645,16 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
630
645
|
match $type {
|
|
631
646
|
"file" => {
|
|
632
647
|
if ($extensions | is-empty) {
|
|
633
|
-
(if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }) | where type == file
|
|
648
|
+
(if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }) | where type == file or type == dir
|
|
634
649
|
} else {
|
|
635
650
|
let ext_list = ($extensions | split row ',')
|
|
636
|
-
(if $hidden { ls -a $ls_pattern } else { ls $ls_pattern })
|
|
651
|
+
let all_items = (if $hidden { ls -a $ls_pattern } else { ls $ls_pattern })
|
|
652
|
+
let dirs = $all_items | where type == dir
|
|
653
|
+
let files = $all_items | where type == file | where {|f|
|
|
637
654
|
let ext = ($f.name | path parse | get extension)
|
|
638
655
|
$ext in $ext_list
|
|
639
656
|
}
|
|
657
|
+
$dirs | append $files
|
|
640
658
|
}
|
|
641
659
|
},
|
|
642
660
|
"directory" => {
|
|
@@ -647,8 +665,9 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
647
665
|
if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }
|
|
648
666
|
} else {
|
|
649
667
|
let ext_list = ($extensions | split row ',')
|
|
650
|
-
let
|
|
651
|
-
let
|
|
668
|
+
let all_items = (if $hidden { ls -a $ls_pattern } else { ls $ls_pattern })
|
|
669
|
+
let dirs = $all_items | where type == dir
|
|
670
|
+
let files = $all_items | where type == file | where {|f|
|
|
652
671
|
let ext = ($f.name | path parse | get extension)
|
|
653
672
|
$ext in $ext_list
|
|
654
673
|
}
|
|
@@ -740,7 +759,7 @@ ${functionName}-external
|
|
|
740
759
|
const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
741
760
|
yield `${suggestion.text}\t${description}`;
|
|
742
761
|
} else {
|
|
743
|
-
const extensions = suggestion.extensions
|
|
762
|
+
const extensions = encodeExtensions(suggestion.extensions);
|
|
744
763
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
745
764
|
const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
746
765
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
@@ -828,15 +847,16 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
828
847
|
switch (\$type) {
|
|
829
848
|
'file' {
|
|
830
849
|
if (\$extensions) {
|
|
831
|
-
# Filter by extensions
|
|
850
|
+
# Filter by extensions, always include directories
|
|
832
851
|
\$extList = \$extensions -split ','
|
|
833
|
-
\$items = Get-ChildItem @forceParam -
|
|
852
|
+
\$items = Get-ChildItem @forceParam -Path "\${prefix}*" -ErrorAction SilentlyContinue |
|
|
834
853
|
Where-Object {
|
|
854
|
+
if (\$_.PSIsContainer) { return \$true }
|
|
835
855
|
\$ext = \$_.Extension
|
|
836
856
|
\$extList | ForEach-Object { if (\$ext -eq ".\$_") { return \$true } }
|
|
837
857
|
}
|
|
838
858
|
} else {
|
|
839
|
-
\$items = Get-ChildItem @forceParam -
|
|
859
|
+
\$items = Get-ChildItem @forceParam -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
840
860
|
}
|
|
841
861
|
}
|
|
842
862
|
'directory' {
|
|
@@ -844,15 +864,14 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
844
864
|
}
|
|
845
865
|
'any' {
|
|
846
866
|
if (\$extensions) {
|
|
847
|
-
#
|
|
848
|
-
\$dirs = Get-ChildItem @forceParam -Directory -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
867
|
+
# Filter by extensions, always include directories
|
|
849
868
|
\$extList = \$extensions -split ','
|
|
850
|
-
\$
|
|
869
|
+
\$items = Get-ChildItem @forceParam -Path "\${prefix}*" -ErrorAction SilentlyContinue |
|
|
851
870
|
Where-Object {
|
|
871
|
+
if (\$_.PSIsContainer) { return \$true }
|
|
852
872
|
\$ext = \$_.Extension
|
|
853
873
|
\$extList | ForEach-Object { if (\$ext -eq ".\$_") { return \$true } }
|
|
854
874
|
}
|
|
855
|
-
\$items = \$dirs + \$files
|
|
856
875
|
} else {
|
|
857
876
|
\$items = Get-ChildItem @forceParam -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
858
877
|
}
|
|
@@ -910,7 +929,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
910
929
|
const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
911
930
|
yield `${suggestion.text}\t${suggestion.text}\t${description}`;
|
|
912
931
|
} else {
|
|
913
|
-
const extensions = suggestion.extensions
|
|
932
|
+
const extensions = encodeExtensions(suggestion.extensions);
|
|
914
933
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
915
934
|
const description = suggestion.description == null ? "" : sanitizeDescription(require_message.formatMessage(suggestion.description, { colors: false }));
|
|
916
935
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
package/dist/completion.js
CHANGED
|
@@ -40,6 +40,9 @@ function validateProgramName(programName) {
|
|
|
40
40
|
function encodePattern(pattern) {
|
|
41
41
|
return pattern.replace(/%/g, "%25").replace(/:/g, "%3A");
|
|
42
42
|
}
|
|
43
|
+
function encodeExtensions(extensions) {
|
|
44
|
+
return extensions?.map((ext) => ext.replace(/^\./, "")).join(",") ?? "";
|
|
45
|
+
}
|
|
43
46
|
/**
|
|
44
47
|
* Replaces control characters that would corrupt shell completion protocols.
|
|
45
48
|
* Shell completion formats use tabs as field delimiters and newlines as record
|
|
@@ -114,17 +117,25 @@ function _${programName} () {
|
|
|
114
117
|
# Generate file completions based on type
|
|
115
118
|
case "$type" in
|
|
116
119
|
file)
|
|
117
|
-
# Complete files
|
|
120
|
+
# Complete files and directories (directories for navigation)
|
|
118
121
|
if [[ -n "$extensions" ]]; then
|
|
119
|
-
#
|
|
122
|
+
# Files with extension filtering + directories
|
|
120
123
|
local ext_pattern="\${extensions//,/|}"
|
|
121
|
-
for
|
|
122
|
-
[[ -
|
|
124
|
+
for item in "$__glob_current"*; do
|
|
125
|
+
if [[ -d "$item" ]]; then
|
|
126
|
+
COMPREPLY+=("$item/")
|
|
127
|
+
elif [[ -f "$item" && "$item" =~ \\.($ext_pattern)$ ]]; then
|
|
128
|
+
COMPREPLY+=("$item")
|
|
129
|
+
fi
|
|
123
130
|
done
|
|
124
131
|
else
|
|
125
|
-
# Complete files
|
|
132
|
+
# Complete files and directories for navigation
|
|
126
133
|
for item in "$__glob_current"*; do
|
|
127
|
-
[[ -
|
|
134
|
+
if [[ -d "$item" ]]; then
|
|
135
|
+
COMPREPLY+=("$item/")
|
|
136
|
+
elif [[ -f "$item" ]]; then
|
|
137
|
+
COMPREPLY+=("$item")
|
|
138
|
+
fi
|
|
128
139
|
done
|
|
129
140
|
fi
|
|
130
141
|
;;
|
|
@@ -203,7 +214,7 @@ complete -F _${programName} -- ${programName}
|
|
|
203
214
|
if (i > 0) yield "\n";
|
|
204
215
|
if (suggestion.kind === "literal") yield `${suggestion.text}`;
|
|
205
216
|
else {
|
|
206
|
-
const extensions = suggestion.extensions
|
|
217
|
+
const extensions = encodeExtensions(suggestion.extensions);
|
|
207
218
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
208
219
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
209
220
|
yield `__FILE__:${suggestion.type}:${extensions}:${pattern}:${hidden}`;
|
|
@@ -271,11 +282,11 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
271
282
|
case "\$type" in
|
|
272
283
|
file)
|
|
273
284
|
if [[ -n "\$extensions" ]]; then
|
|
274
|
-
# Complete files with extension filtering
|
|
285
|
+
# Complete files with extension filtering + directories for navigation
|
|
275
286
|
local ext_pattern="*.(\$\{extensions//,/|\})"
|
|
276
|
-
_files -g "\$ext_pattern"
|
|
287
|
+
_files -g "\$ext_pattern"; _directories
|
|
277
288
|
else
|
|
278
|
-
_files
|
|
289
|
+
_files
|
|
279
290
|
fi
|
|
280
291
|
;;
|
|
281
292
|
directory)
|
|
@@ -285,7 +296,7 @@ function _${programName.replace(/[^a-zA-Z0-9]/g, "_")} () {
|
|
|
285
296
|
if [[ -n "\$extensions" ]]; then
|
|
286
297
|
# Complete both files and directories, with extension filtering for files
|
|
287
298
|
local ext_pattern="*.(\$\{extensions//,/|\})"
|
|
288
|
-
_files -g "\$ext_pattern"
|
|
299
|
+
_files -g "\$ext_pattern"; _directories
|
|
289
300
|
else
|
|
290
301
|
_files
|
|
291
302
|
fi
|
|
@@ -328,7 +339,7 @@ compdef _${programName.replace(/[^a-zA-Z0-9]/g, "_")} ${programName}
|
|
|
328
339
|
const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
|
|
329
340
|
yield `${suggestion.text}\0${description}\0`;
|
|
330
341
|
} else {
|
|
331
|
-
const extensions = suggestion.extensions
|
|
342
|
+
const extensions = encodeExtensions(suggestion.extensions);
|
|
332
343
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
333
344
|
const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
|
|
334
345
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
@@ -375,9 +386,11 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
375
386
|
set -l items
|
|
376
387
|
switch $type
|
|
377
388
|
case file
|
|
378
|
-
# Complete files
|
|
389
|
+
# Complete files and directories (directories for navigation)
|
|
379
390
|
for item in $current*
|
|
380
|
-
if test -
|
|
391
|
+
if test -d $item
|
|
392
|
+
set -a items $item/
|
|
393
|
+
else if test -f $item
|
|
381
394
|
set -a items $item
|
|
382
395
|
end
|
|
383
396
|
end
|
|
@@ -390,7 +403,9 @@ ${escapedArgs ? ` set -l output (${programName} ${escapedArgs} $prev $current
|
|
|
390
403
|
if test "$hidden" = "1"
|
|
391
404
|
if test -z "$current"; or string match -q '*/' -- "$current"
|
|
392
405
|
for item in $current.*
|
|
393
|
-
if test -
|
|
406
|
+
if test -d $item
|
|
407
|
+
set -a items $item/
|
|
408
|
+
else if test -f $item
|
|
394
409
|
set -a items $item
|
|
395
410
|
end
|
|
396
411
|
end
|
|
@@ -496,7 +511,7 @@ complete -c ${programName} -f -a '(${functionName})'
|
|
|
496
511
|
const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
|
|
497
512
|
yield `${suggestion.text}\t${description}`;
|
|
498
513
|
} else {
|
|
499
|
-
const extensions = suggestion.extensions
|
|
514
|
+
const extensions = encodeExtensions(suggestion.extensions);
|
|
500
515
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
501
516
|
const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
|
|
502
517
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
@@ -630,13 +645,16 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
630
645
|
match $type {
|
|
631
646
|
"file" => {
|
|
632
647
|
if ($extensions | is-empty) {
|
|
633
|
-
(if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }) | where type == file
|
|
648
|
+
(if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }) | where type == file or type == dir
|
|
634
649
|
} else {
|
|
635
650
|
let ext_list = ($extensions | split row ',')
|
|
636
|
-
(if $hidden { ls -a $ls_pattern } else { ls $ls_pattern })
|
|
651
|
+
let all_items = (if $hidden { ls -a $ls_pattern } else { ls $ls_pattern })
|
|
652
|
+
let dirs = $all_items | where type == dir
|
|
653
|
+
let files = $all_items | where type == file | where {|f|
|
|
637
654
|
let ext = ($f.name | path parse | get extension)
|
|
638
655
|
$ext in $ext_list
|
|
639
656
|
}
|
|
657
|
+
$dirs | append $files
|
|
640
658
|
}
|
|
641
659
|
},
|
|
642
660
|
"directory" => {
|
|
@@ -647,8 +665,9 @@ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete |
|
|
|
647
665
|
if $hidden { ls -a $ls_pattern } else { ls $ls_pattern }
|
|
648
666
|
} else {
|
|
649
667
|
let ext_list = ($extensions | split row ',')
|
|
650
|
-
let
|
|
651
|
-
let
|
|
668
|
+
let all_items = (if $hidden { ls -a $ls_pattern } else { ls $ls_pattern })
|
|
669
|
+
let dirs = $all_items | where type == dir
|
|
670
|
+
let files = $all_items | where type == file | where {|f|
|
|
652
671
|
let ext = ($f.name | path parse | get extension)
|
|
653
672
|
$ext in $ext_list
|
|
654
673
|
}
|
|
@@ -740,7 +759,7 @@ ${functionName}-external
|
|
|
740
759
|
const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
|
|
741
760
|
yield `${suggestion.text}\t${description}`;
|
|
742
761
|
} else {
|
|
743
|
-
const extensions = suggestion.extensions
|
|
762
|
+
const extensions = encodeExtensions(suggestion.extensions);
|
|
744
763
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
745
764
|
const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
|
|
746
765
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
|
@@ -828,15 +847,16 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
828
847
|
switch (\$type) {
|
|
829
848
|
'file' {
|
|
830
849
|
if (\$extensions) {
|
|
831
|
-
# Filter by extensions
|
|
850
|
+
# Filter by extensions, always include directories
|
|
832
851
|
\$extList = \$extensions -split ','
|
|
833
|
-
\$items = Get-ChildItem @forceParam -
|
|
852
|
+
\$items = Get-ChildItem @forceParam -Path "\${prefix}*" -ErrorAction SilentlyContinue |
|
|
834
853
|
Where-Object {
|
|
854
|
+
if (\$_.PSIsContainer) { return \$true }
|
|
835
855
|
\$ext = \$_.Extension
|
|
836
856
|
\$extList | ForEach-Object { if (\$ext -eq ".\$_") { return \$true } }
|
|
837
857
|
}
|
|
838
858
|
} else {
|
|
839
|
-
\$items = Get-ChildItem @forceParam -
|
|
859
|
+
\$items = Get-ChildItem @forceParam -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
840
860
|
}
|
|
841
861
|
}
|
|
842
862
|
'directory' {
|
|
@@ -844,15 +864,14 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
844
864
|
}
|
|
845
865
|
'any' {
|
|
846
866
|
if (\$extensions) {
|
|
847
|
-
#
|
|
848
|
-
\$dirs = Get-ChildItem @forceParam -Directory -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
867
|
+
# Filter by extensions, always include directories
|
|
849
868
|
\$extList = \$extensions -split ','
|
|
850
|
-
\$
|
|
869
|
+
\$items = Get-ChildItem @forceParam -Path "\${prefix}*" -ErrorAction SilentlyContinue |
|
|
851
870
|
Where-Object {
|
|
871
|
+
if (\$_.PSIsContainer) { return \$true }
|
|
852
872
|
\$ext = \$_.Extension
|
|
853
873
|
\$extList | ForEach-Object { if (\$ext -eq ".\$_") { return \$true } }
|
|
854
874
|
}
|
|
855
|
-
\$items = \$dirs + \$files
|
|
856
875
|
} else {
|
|
857
876
|
\$items = Get-ChildItem @forceParam -Path "\${prefix}*" -ErrorAction SilentlyContinue
|
|
858
877
|
}
|
|
@@ -910,7 +929,7 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
|
|
|
910
929
|
const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
|
|
911
930
|
yield `${suggestion.text}\t${suggestion.text}\t${description}`;
|
|
912
931
|
} else {
|
|
913
|
-
const extensions = suggestion.extensions
|
|
932
|
+
const extensions = encodeExtensions(suggestion.extensions);
|
|
914
933
|
const hidden = suggestion.includeHidden ? "1" : "0";
|
|
915
934
|
const description = suggestion.description == null ? "" : sanitizeDescription(formatMessage(suggestion.description, { colors: false }));
|
|
916
935
|
const pattern = encodePattern(suggestion.pattern ?? "");
|
package/dist/facade.cjs
CHANGED
|
@@ -727,6 +727,61 @@ function validateVersionValue(value$1) {
|
|
|
727
727
|
if (/[\x00-\x1f\x7f]/.test(value$1)) throw new TypeError("Version value must not contain control characters.");
|
|
728
728
|
return value$1;
|
|
729
729
|
}
|
|
730
|
+
/**
|
|
731
|
+
* Escapes control characters in a string for display in error messages.
|
|
732
|
+
*
|
|
733
|
+
* @param value The string to escape.
|
|
734
|
+
* @returns The escaped string with control characters replaced by escape
|
|
735
|
+
* sequences.
|
|
736
|
+
*/
|
|
737
|
+
function escapeControlChars(value$1) {
|
|
738
|
+
return value$1.replace(/[\x00-\x1f\x7f]/g, (ch) => {
|
|
739
|
+
const code = ch.charCodeAt(0);
|
|
740
|
+
switch (code) {
|
|
741
|
+
case 9: return "\\t";
|
|
742
|
+
case 10: return "\\n";
|
|
743
|
+
case 13: return "\\r";
|
|
744
|
+
default: return `\\x${code.toString(16).padStart(2, "0")}`;
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Validates meta option names at runtime.
|
|
750
|
+
*
|
|
751
|
+
* @param names The option names to validate.
|
|
752
|
+
* @param label A human-readable label for error messages (e.g.,
|
|
753
|
+
* `"Help option"`).
|
|
754
|
+
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
755
|
+
* lacks a valid prefix, or contains whitespace or control characters.
|
|
756
|
+
*/
|
|
757
|
+
function validateOptionNames(names, label) {
|
|
758
|
+
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
759
|
+
for (const name of names) {
|
|
760
|
+
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
761
|
+
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
762
|
+
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
763
|
+
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
764
|
+
if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Validates meta command names at runtime.
|
|
769
|
+
*
|
|
770
|
+
* @param names The command names to validate.
|
|
771
|
+
* @param label A human-readable label for error messages (e.g.,
|
|
772
|
+
* `"Help command"`).
|
|
773
|
+
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
774
|
+
* whitespace-only, or contains whitespace or control characters.
|
|
775
|
+
*/
|
|
776
|
+
function validateCommandNames(names, label) {
|
|
777
|
+
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
778
|
+
for (const name of names) {
|
|
779
|
+
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
780
|
+
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
781
|
+
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
782
|
+
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
730
785
|
function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsParam) {
|
|
731
786
|
const isProgram = typeof programNameOrArgs !== "string";
|
|
732
787
|
let parser;
|
|
@@ -770,6 +825,12 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
770
825
|
const onCompletion = options.completion?.onShow ?? (() => ({}));
|
|
771
826
|
const onCompletionResult = (code) => onCompletion(code);
|
|
772
827
|
const onErrorResult = (code) => onError(code);
|
|
828
|
+
if (helpOptionConfig?.names) validateOptionNames(helpOptionConfig.names, "Help option");
|
|
829
|
+
if (helpCommandConfig?.names) validateCommandNames(helpCommandConfig.names, "Help command");
|
|
830
|
+
if (versionOptionConfig?.names) validateOptionNames(versionOptionConfig.names, "Version option");
|
|
831
|
+
if (versionCommandConfig?.names) validateCommandNames(versionCommandConfig.names, "Version command");
|
|
832
|
+
if (completionOptionConfig?.names) validateOptionNames(completionOptionConfig.names, "Completion option");
|
|
833
|
+
if (completionCommandConfig?.names) validateCommandNames(completionCommandConfig.names, "Completion command");
|
|
773
834
|
const helpOptionNames = helpOptionConfig?.names ?? ["--help"];
|
|
774
835
|
const helpCommandNames = helpCommandConfig?.names ?? ["help"];
|
|
775
836
|
const versionOptionNames = versionOptionConfig?.names ?? ["--version"];
|
package/dist/facade.d.cts
CHANGED
|
@@ -273,7 +273,10 @@ interface RunOptions<THelp, TError> {
|
|
|
273
273
|
* @returns The parsed result value, or the return value of `onHelp`/`onError`
|
|
274
274
|
* callbacks.
|
|
275
275
|
* @throws {TypeError} If `options.version.value` is not a non-empty string
|
|
276
|
-
* without ASCII control characters
|
|
276
|
+
* without ASCII control characters, or if any meta command/option
|
|
277
|
+
* name is empty, whitespace-only, contains whitespace or control
|
|
278
|
+
* characters, or (for option names) lacks a valid prefix (`--`,
|
|
279
|
+
* `-`, `/`, or `+`).
|
|
277
280
|
* @throws {RunParserError} When parsing fails and no `onError` callback is
|
|
278
281
|
* provided.
|
|
279
282
|
* @since 0.10.0 Added support for {@link Program} objects.
|
package/dist/facade.d.ts
CHANGED
|
@@ -273,7 +273,10 @@ interface RunOptions<THelp, TError> {
|
|
|
273
273
|
* @returns The parsed result value, or the return value of `onHelp`/`onError`
|
|
274
274
|
* callbacks.
|
|
275
275
|
* @throws {TypeError} If `options.version.value` is not a non-empty string
|
|
276
|
-
* without ASCII control characters
|
|
276
|
+
* without ASCII control characters, or if any meta command/option
|
|
277
|
+
* name is empty, whitespace-only, contains whitespace or control
|
|
278
|
+
* characters, or (for option names) lacks a valid prefix (`--`,
|
|
279
|
+
* `-`, `/`, or `+`).
|
|
277
280
|
* @throws {RunParserError} When parsing fails and no `onError` callback is
|
|
278
281
|
* provided.
|
|
279
282
|
* @since 0.10.0 Added support for {@link Program} objects.
|
package/dist/facade.js
CHANGED
|
@@ -727,6 +727,61 @@ function validateVersionValue(value$1) {
|
|
|
727
727
|
if (/[\x00-\x1f\x7f]/.test(value$1)) throw new TypeError("Version value must not contain control characters.");
|
|
728
728
|
return value$1;
|
|
729
729
|
}
|
|
730
|
+
/**
|
|
731
|
+
* Escapes control characters in a string for display in error messages.
|
|
732
|
+
*
|
|
733
|
+
* @param value The string to escape.
|
|
734
|
+
* @returns The escaped string with control characters replaced by escape
|
|
735
|
+
* sequences.
|
|
736
|
+
*/
|
|
737
|
+
function escapeControlChars(value$1) {
|
|
738
|
+
return value$1.replace(/[\x00-\x1f\x7f]/g, (ch) => {
|
|
739
|
+
const code = ch.charCodeAt(0);
|
|
740
|
+
switch (code) {
|
|
741
|
+
case 9: return "\\t";
|
|
742
|
+
case 10: return "\\n";
|
|
743
|
+
case 13: return "\\r";
|
|
744
|
+
default: return `\\x${code.toString(16).padStart(2, "0")}`;
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Validates meta option names at runtime.
|
|
750
|
+
*
|
|
751
|
+
* @param names The option names to validate.
|
|
752
|
+
* @param label A human-readable label for error messages (e.g.,
|
|
753
|
+
* `"Help option"`).
|
|
754
|
+
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
755
|
+
* lacks a valid prefix, or contains whitespace or control characters.
|
|
756
|
+
*/
|
|
757
|
+
function validateOptionNames(names, label) {
|
|
758
|
+
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
759
|
+
for (const name of names) {
|
|
760
|
+
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
761
|
+
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
762
|
+
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
763
|
+
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
764
|
+
if (!/^(--|[-/+])/.test(name)) throw new TypeError(`${label} name must start with "--", "-", "/", or "+": "${name}".`);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Validates meta command names at runtime.
|
|
769
|
+
*
|
|
770
|
+
* @param names The command names to validate.
|
|
771
|
+
* @param label A human-readable label for error messages (e.g.,
|
|
772
|
+
* `"Help command"`).
|
|
773
|
+
* @throws {TypeError} If the names array is empty, or any name is empty,
|
|
774
|
+
* whitespace-only, or contains whitespace or control characters.
|
|
775
|
+
*/
|
|
776
|
+
function validateCommandNames(names, label) {
|
|
777
|
+
if (names.length === 0) throw new TypeError(`Expected at least one ${label.toLowerCase()} name.`);
|
|
778
|
+
for (const name of names) {
|
|
779
|
+
if (name === "") throw new TypeError(`${label} name must not be empty.`);
|
|
780
|
+
if (/^\s+$/.test(name)) throw new TypeError(`${label} name must not be whitespace-only: "${escapeControlChars(name)}".`);
|
|
781
|
+
if (/[\x00-\x1f\x7f]/.test(name)) throw new TypeError(`${label} name must not contain control characters: "${escapeControlChars(name)}".`);
|
|
782
|
+
if (/\s/.test(name)) throw new TypeError(`${label} name must not contain whitespace: "${escapeControlChars(name)}".`);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
730
785
|
function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsParam) {
|
|
731
786
|
const isProgram = typeof programNameOrArgs !== "string";
|
|
732
787
|
let parser;
|
|
@@ -770,6 +825,12 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
|
|
|
770
825
|
const onCompletion = options.completion?.onShow ?? (() => ({}));
|
|
771
826
|
const onCompletionResult = (code) => onCompletion(code);
|
|
772
827
|
const onErrorResult = (code) => onError(code);
|
|
828
|
+
if (helpOptionConfig?.names) validateOptionNames(helpOptionConfig.names, "Help option");
|
|
829
|
+
if (helpCommandConfig?.names) validateCommandNames(helpCommandConfig.names, "Help command");
|
|
830
|
+
if (versionOptionConfig?.names) validateOptionNames(versionOptionConfig.names, "Version option");
|
|
831
|
+
if (versionCommandConfig?.names) validateCommandNames(versionCommandConfig.names, "Version command");
|
|
832
|
+
if (completionOptionConfig?.names) validateOptionNames(completionOptionConfig.names, "Completion option");
|
|
833
|
+
if (completionCommandConfig?.names) validateCommandNames(completionCommandConfig.names, "Completion command");
|
|
773
834
|
const helpOptionNames = helpOptionConfig?.names ?? ["--help"];
|
|
774
835
|
const helpCommandNames = helpCommandConfig?.names ?? ["help"];
|
|
775
836
|
const versionOptionNames = versionOptionConfig?.names ?? ["--version"];
|
package/dist/valueparser.cjs
CHANGED
|
@@ -453,10 +453,28 @@ function float(options = {}) {
|
|
|
453
453
|
* object.
|
|
454
454
|
* @param options Configuration options for the URL parser.
|
|
455
455
|
* @returns A {@link ValueParser} that converts string input to `URL` objects.
|
|
456
|
+
* @throws {TypeError} If any `allowedProtocols` entry is not a valid protocol
|
|
457
|
+
* string ending with a colon (e.g., `"https:"`).
|
|
456
458
|
*/
|
|
457
459
|
function url(options = {}) {
|
|
458
|
-
const
|
|
459
|
-
const
|
|
460
|
+
const originalProtocolsList = [];
|
|
461
|
+
const normalizedProtocolsList = [];
|
|
462
|
+
if (options.allowedProtocols != null) {
|
|
463
|
+
const seen = /* @__PURE__ */ new Set();
|
|
464
|
+
for (const protocol of options.allowedProtocols) {
|
|
465
|
+
if (typeof protocol !== "string" || !/^[a-z][a-z0-9+\-.]*:$/i.test(protocol)) {
|
|
466
|
+
const rendered = typeof protocol === "string" ? JSON.stringify(protocol) : String(protocol);
|
|
467
|
+
throw new TypeError(`Each allowed protocol must be a valid protocol ending with a colon (e.g., "https:"), got: ${rendered}.`);
|
|
468
|
+
}
|
|
469
|
+
const normalized = protocol.toLowerCase();
|
|
470
|
+
if (seen.has(normalized)) continue;
|
|
471
|
+
seen.add(normalized);
|
|
472
|
+
originalProtocolsList.push(protocol);
|
|
473
|
+
normalizedProtocolsList.push(normalized);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const originalProtocols = options.allowedProtocols != null ? Object.freeze(originalProtocolsList) : void 0;
|
|
477
|
+
const allowedProtocols = options.allowedProtocols != null ? Object.freeze(normalizedProtocolsList) : void 0;
|
|
460
478
|
const metavar = options.metavar ?? "URL";
|
|
461
479
|
require_nonempty.ensureNonEmptyString(metavar);
|
|
462
480
|
const invalidUrl = options.errors?.invalidUrl;
|
package/dist/valueparser.d.cts
CHANGED
|
@@ -491,6 +491,8 @@ interface UrlOptions {
|
|
|
491
491
|
* object.
|
|
492
492
|
* @param options Configuration options for the URL parser.
|
|
493
493
|
* @returns A {@link ValueParser} that converts string input to `URL` objects.
|
|
494
|
+
* @throws {TypeError} If any `allowedProtocols` entry is not a valid protocol
|
|
495
|
+
* string ending with a colon (e.g., `"https:"`).
|
|
494
496
|
*/
|
|
495
497
|
declare function url(options?: UrlOptions): ValueParser<"sync", URL>;
|
|
496
498
|
/**
|
package/dist/valueparser.d.ts
CHANGED
|
@@ -491,6 +491,8 @@ interface UrlOptions {
|
|
|
491
491
|
* object.
|
|
492
492
|
* @param options Configuration options for the URL parser.
|
|
493
493
|
* @returns A {@link ValueParser} that converts string input to `URL` objects.
|
|
494
|
+
* @throws {TypeError} If any `allowedProtocols` entry is not a valid protocol
|
|
495
|
+
* string ending with a colon (e.g., `"https:"`).
|
|
494
496
|
*/
|
|
495
497
|
declare function url(options?: UrlOptions): ValueParser<"sync", URL>;
|
|
496
498
|
/**
|
package/dist/valueparser.js
CHANGED
|
@@ -453,10 +453,28 @@ function float(options = {}) {
|
|
|
453
453
|
* object.
|
|
454
454
|
* @param options Configuration options for the URL parser.
|
|
455
455
|
* @returns A {@link ValueParser} that converts string input to `URL` objects.
|
|
456
|
+
* @throws {TypeError} If any `allowedProtocols` entry is not a valid protocol
|
|
457
|
+
* string ending with a colon (e.g., `"https:"`).
|
|
456
458
|
*/
|
|
457
459
|
function url(options = {}) {
|
|
458
|
-
const
|
|
459
|
-
const
|
|
460
|
+
const originalProtocolsList = [];
|
|
461
|
+
const normalizedProtocolsList = [];
|
|
462
|
+
if (options.allowedProtocols != null) {
|
|
463
|
+
const seen = /* @__PURE__ */ new Set();
|
|
464
|
+
for (const protocol of options.allowedProtocols) {
|
|
465
|
+
if (typeof protocol !== "string" || !/^[a-z][a-z0-9+\-.]*:$/i.test(protocol)) {
|
|
466
|
+
const rendered = typeof protocol === "string" ? JSON.stringify(protocol) : String(protocol);
|
|
467
|
+
throw new TypeError(`Each allowed protocol must be a valid protocol ending with a colon (e.g., "https:"), got: ${rendered}.`);
|
|
468
|
+
}
|
|
469
|
+
const normalized = protocol.toLowerCase();
|
|
470
|
+
if (seen.has(normalized)) continue;
|
|
471
|
+
seen.add(normalized);
|
|
472
|
+
originalProtocolsList.push(protocol);
|
|
473
|
+
normalizedProtocolsList.push(normalized);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const originalProtocols = options.allowedProtocols != null ? Object.freeze(originalProtocolsList) : void 0;
|
|
477
|
+
const allowedProtocols = options.allowedProtocols != null ? Object.freeze(normalizedProtocolsList) : void 0;
|
|
460
478
|
const metavar = options.metavar ?? "URL";
|
|
461
479
|
ensureNonEmptyString(metavar);
|
|
462
480
|
const invalidUrl = options.errors?.invalidUrl;
|