@clidoc/core 0.1.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/LICENSE +21 -0
- package/README.md +69 -0
- package/THIRD_PARTY_NOTICES.md +25 -0
- package/dist/completion.d.ts +10 -0
- package/dist/completion.d.ts.map +1 -0
- package/dist/completion.js +255 -0
- package/dist/completion.js.map +1 -0
- package/dist/discovery.d.ts +11 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +42 -0
- package/dist/discovery.js.map +1 -0
- package/dist/docgen.d.ts +22 -0
- package/dist/docgen.d.ts.map +1 -0
- package/dist/docgen.js +44 -0
- package/dist/docgen.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +326 -0
- package/dist/index.js.map +1 -0
- package/dist/logical.d.ts +9 -0
- package/dist/logical.d.ts.map +1 -0
- package/dist/logical.js +112 -0
- package/dist/logical.js.map +1 -0
- package/dist/merge.d.ts +44 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +59 -0
- package/dist/merge.js.map +1 -0
- package/dist/schema.d.ts +2 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +393 -0
- package/dist/schema.js.map +1 -0
- package/dist/types.d.ts +131 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
- package/schema/spec.schema.json +537 -0
- package/src/completion.ts +285 -0
- package/src/discovery.ts +45 -0
- package/src/docgen.ts +61 -0
- package/src/index.ts +359 -0
- package/src/logical.ts +158 -0
- package/src/merge.ts +96 -0
- package/src/schema.ts +433 -0
- package/src/types.ts +105 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ben Houston
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @clidoc/core
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@clidoc/core)
|
|
4
|
+
[](https://www.npmjs.com/package/@clidoc/core)
|
|
5
|
+
[](https://github.com/bhouston/clidoc/actions/workflows/ci.yml)
|
|
6
|
+
[](https://codecov.io/gh/bhouston/clidoc)
|
|
7
|
+
[](https://clidoc.dev)
|
|
8
|
+
|
|
9
|
+
TypeScript types, offline JSON Schema validation, YAML/JSON parsing, and Markdown documentation generation for [OpenCLI 1.0.0-alpha.14](https://github.com/bcdxn/opencli). The published package bundles the exact schema pinned in `upstream/opencli/spec.schema.json`.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { parse, validate, renderMarkdown, generatePages } from '@clidoc/core';
|
|
13
|
+
|
|
14
|
+
const document = parse(sourceText); // JSON or YAML; throws on invalid input
|
|
15
|
+
const result = validate(document); // { valid, errors }
|
|
16
|
+
const markdown = renderMarkdown(document);
|
|
17
|
+
const pages = generatePages(document, { basePath: '/cli' });
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`generatePages` returns a landing page and one page per visible command. Each command page includes a generated usage synopsis: required values use `<value>`, optional elements use `[element]`, and repeatable elements use `...`. The synopsis includes visible command flags and inherited global flags. Each page has `id`, `title`, `path`, and Markdown `content`. A command named `<binary> foo-bar` keeps the readable `/commands/foo-bar` route when its suffix is lowercase ASCII letters, digits, and single hyphens (up to 64 characters). Other names use a normalized readable prefix (up to 64 characters), a reserved `~`, and a SHA-256 digest of the full command name. Each route depends only on its command name, so adding unrelated commands does not change existing URLs. The generator rejects a digest collision before returning any pages. This replaces the previous collision-dependent hash suffixes, so URLs for names outside the readable form may change. Landing links use the generated base path. The bundled JSON Schema is also available at `@clidoc/core/schema`.
|
|
21
|
+
|
|
22
|
+
`validate` checks documents against the vendored JSON Schema and, once that passes, against the logical rules ported from upstream's `validate/validate.go`: positional arguments must not place a required argument after an optional one, `minItems`/`maxItems` are only valid on variadic arguments and flags (with `minItems <= maxItems`), `$FILE` alternative sources must have a matching file declared in `global.config`, flag names and aliases must be unique per command, variadic flags cannot be `required`, and group commands cannot declare `args` or `flags`. Errors use the same `instancePath message` shape as schema errors.
|
|
23
|
+
|
|
24
|
+
## Adding author-supplied metadata
|
|
25
|
+
|
|
26
|
+
Adapters generate a document from what your CLI's argument parser already knows, so things like
|
|
27
|
+
`examples`, `exitCodes`, `info.license`/`contact`, `install`, and `global` config are usually
|
|
28
|
+
missing. Add them with `mergeDocument`, then re-validate:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { mergeDocument } from '@clidoc/core';
|
|
32
|
+
|
|
33
|
+
const documented = mergeDocument(document, {
|
|
34
|
+
info: { license: { name: 'MIT', spdxId: 'MIT' } },
|
|
35
|
+
install: [{ name: 'npm', command: 'npm i -g my-cli' }],
|
|
36
|
+
commands: {
|
|
37
|
+
'my-cli greet': {
|
|
38
|
+
examples: [{ title: 'Basic', content: 'my-cli greet Ada' }],
|
|
39
|
+
exitCodes: [{ code: 1, status: 'BAD_USER_INPUT_ERROR', summary: 'Missing name' }],
|
|
40
|
+
flags: [{ name: 'language', alternativeSources: [{ type: '$ENV', property: 'LANG' }] }],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Plain objects (`info`, `global`, each command) merge recursively. Arrays replace the base array,
|
|
47
|
+
except `examples` and `exitCodes`, which append, and `flags`/`args`, which are merged item-by-item
|
|
48
|
+
matched by `name` — so you can add `alternativeSources` or a `summary` to one generated flag
|
|
49
|
+
without repeating the rest of it. Commands not present in the generated document are added as-is.
|
|
50
|
+
`mergeDocument` throws an `Error` listing every problem if the merged result fails schema
|
|
51
|
+
validation.
|
|
52
|
+
|
|
53
|
+
## Shell completion generation
|
|
54
|
+
|
|
55
|
+
`generateCompletion(document, { shell: 'bash' | 'zsh' | 'fish', binary?: string })`
|
|
56
|
+
validates an OpenCLI document and returns a standalone completion script. It
|
|
57
|
+
supports nested commands, aliases, global/local flags, option and positional
|
|
58
|
+
choices, and hidden entries, with no runtime dependency on Node or the target CLI.
|
|
59
|
+
The optional `binary` overrides only the registered executable name. See the
|
|
60
|
+
[completion guide](https://clidoc.dev/docs/guides/completion) for activation,
|
|
61
|
+
supported syntax, and limitations.
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT. See [LICENSE](../../LICENSE).
|
|
66
|
+
|
|
67
|
+
## Author
|
|
68
|
+
|
|
69
|
+
[Ben Houston](https://ben3d.ca), Sponsored by [Land of Assets](https://landofassets.com).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
The OpenCLI 1.0.0-alpha.14 JSON Schema in `schema/spec.schema.json` and the matching compiled schema in `src/schema.ts` are copied from [bcdxn/opencli](https://github.com/bcdxn/opencli), pinned at submodule commit `683d0ca92fc37ccc2626e64db0a8c32f3c4063c0`. They are distributed under the following license.
|
|
4
|
+
|
|
5
|
+
MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 bcdxn
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { OpenCliDocument } from './types.js';
|
|
2
|
+
/** Shells supported by standalone completion scripts. */
|
|
3
|
+
export type CompletionShell = 'bash' | 'zsh' | 'fish';
|
|
4
|
+
export type CompletionOptions = {
|
|
5
|
+
shell: CompletionShell;
|
|
6
|
+
binary?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Generate a validated, deterministic script with no runtime dependency on clidoc or the target CLI. */
|
|
9
|
+
export declare function generateCompletion(document: OpenCliDocument, options: CompletionOptions): string;
|
|
10
|
+
//# sourceMappingURL=completion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.d.ts","sourceRoot":"","sources":["../src/completion.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAqC,eAAe,EAAE,MAAM,YAAY,CAAC;AAErF,yDAAyD;AACzD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AACtD,MAAM,MAAM,iBAAiB,GAAG;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA4E5E,yGAAyG;AACzG,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CA8BhG"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { validate } from './index.js';
|
|
3
|
+
function compile(document) {
|
|
4
|
+
const binary = document.info.binary;
|
|
5
|
+
const nodes = [{ command: {}, children: new Map() }];
|
|
6
|
+
const paths = new Map([[binary, 0]]);
|
|
7
|
+
const commands = Object.entries(document.commands ?? {}).toSorted(([a], [b]) => a.localeCompare(b));
|
|
8
|
+
for (const [path, command] of commands) {
|
|
9
|
+
if (path !== binary && !path.startsWith(`${binary} `))
|
|
10
|
+
throw new Error(`Completion command must start with ${binary}: ${path}`);
|
|
11
|
+
let parent = 0;
|
|
12
|
+
let full = binary;
|
|
13
|
+
for (const part of path.slice(binary.length).trim().split(/\s+/).filter(Boolean)) {
|
|
14
|
+
full += ` ${part}`;
|
|
15
|
+
let id = paths.get(full);
|
|
16
|
+
if (id === undefined) {
|
|
17
|
+
id = nodes.length;
|
|
18
|
+
nodes.push({ command: {}, children: new Map() });
|
|
19
|
+
nodes[parent].children.set(part, id);
|
|
20
|
+
paths.set(full, id);
|
|
21
|
+
}
|
|
22
|
+
parent = id;
|
|
23
|
+
}
|
|
24
|
+
nodes[parent].command = command;
|
|
25
|
+
}
|
|
26
|
+
// Aliases identify a sibling command and share its complete descendant tree.
|
|
27
|
+
for (const node of nodes) {
|
|
28
|
+
for (const id of new Set(node.children.values())) {
|
|
29
|
+
for (const alias of nodes[id].command.aliases ?? []) {
|
|
30
|
+
if (!alias || /\s/.test(alias))
|
|
31
|
+
throw new Error('Completion command aliases must be single words');
|
|
32
|
+
const existing = node.children.get(alias);
|
|
33
|
+
if (existing !== undefined && existing !== id)
|
|
34
|
+
throw new Error(`Ambiguous completion alias: ${alias}`);
|
|
35
|
+
node.children.set(alias, id);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const table = new Map();
|
|
40
|
+
const put = (key, mode, values = []) => table.set(key, { mode, values });
|
|
41
|
+
for (const [id, node] of nodes.entries()) {
|
|
42
|
+
const children = [...node.children].filter(([, child]) => !nodes[child].command.hidden);
|
|
43
|
+
put(`s:${id}`, '', children.map(([name]) => name));
|
|
44
|
+
for (const [name, child] of children)
|
|
45
|
+
put(`c:${id}:${name}`, String(child));
|
|
46
|
+
const flags = new Map();
|
|
47
|
+
for (const flag of [...(document.global?.flags ?? []), ...(node.command.flags ?? [])]) {
|
|
48
|
+
flags.set(`--${flag.name}`, flag);
|
|
49
|
+
for (const alias of flag.aliases ?? [])
|
|
50
|
+
flags.set(`${alias.length === 1 ? '-' : '--'}${alias}`, flag);
|
|
51
|
+
}
|
|
52
|
+
put(`o:${id}`, '', [...flags].filter(([, flag]) => !flag.hidden).map(([name]) => name));
|
|
53
|
+
for (const [name, flag] of flags)
|
|
54
|
+
put(`f:${id}:${name}`, flag.type === 'boolean' ? 'boolean' : 'value', (flag.choices ?? []).map((choice) => String(choice.value)));
|
|
55
|
+
for (const [position, arg] of (node.command.args ?? []).entries())
|
|
56
|
+
put(`a:${id}:${position}`, arg.variadic ? 'repeat' : 'argument', (arg.choices ?? []).map((choice) => String(choice.value)));
|
|
57
|
+
}
|
|
58
|
+
return table;
|
|
59
|
+
}
|
|
60
|
+
/** Generate a validated, deterministic script with no runtime dependency on clidoc or the target CLI. */
|
|
61
|
+
export function generateCompletion(document, options) {
|
|
62
|
+
const result = validate(document);
|
|
63
|
+
if (!result.valid)
|
|
64
|
+
throw new Error(`Invalid OpenCLI document: ${result.errors.join('; ')}`);
|
|
65
|
+
if (!['bash', 'zsh', 'fish'].includes(options.shell))
|
|
66
|
+
throw new Error(`Unsupported completion shell: ${options.shell}`);
|
|
67
|
+
const binary = options.binary ?? document.info.binary;
|
|
68
|
+
if (!/^[a-zA-Z0-9_][a-zA-Z0-9_.+-]*$/.test(binary))
|
|
69
|
+
throw new Error('Completion binary must be a single executable name (letters, digits, _, ., +, -)');
|
|
70
|
+
const table = compile(document);
|
|
71
|
+
const text = [
|
|
72
|
+
...Object.keys(document.commands ?? {}),
|
|
73
|
+
...[...table].flatMap(([key, entry]) => [key, ...entry.values]),
|
|
74
|
+
];
|
|
75
|
+
// eslint-disable-next-line no-control-regex -- Shell completion data must not contain control characters.
|
|
76
|
+
if (text.some((value) => /[\x00-\x1f\x7f]/.test(value)))
|
|
77
|
+
throw new Error('Completion names and choices cannot contain control characters');
|
|
78
|
+
const name = `_clidoc_${createHash('sha256').update(binary).digest('hex').slice(0, 16)}`;
|
|
79
|
+
const shell = options.shell;
|
|
80
|
+
const fish = shell === 'fish';
|
|
81
|
+
const quote = (value) => `'${fish ? value.replace(/\\/g, '\\\\').replace(/'/g, "\\'") : value.replace(/'/g, "'\\''")}'`;
|
|
82
|
+
const cases = [...table]
|
|
83
|
+
.map(([key, { mode, values }]) => fish
|
|
84
|
+
? ` case ${quote(key.replace(/[\\*?[\]]/g, '\\$&'))}\n set mode ${quote(mode)}\n set reply ${values.map(quote).join(' ')}`
|
|
85
|
+
: ` ${quote(key)}) mode=${quote(mode)}; reply=(${values.map(quote).join(' ')});;`)
|
|
86
|
+
.join('\n');
|
|
87
|
+
const header = '# Generated by clidoc. Regenerate after changing the CLI schema.\n';
|
|
88
|
+
return header + (fish ? fishScript(name, binary, cases) : shScript(name, binary, cases, shell));
|
|
89
|
+
}
|
|
90
|
+
function shScript(name, binary, cases, shell) {
|
|
91
|
+
const bash = shell === 'bash';
|
|
92
|
+
return `${name}_lookup() {
|
|
93
|
+
mode=''; reply=()
|
|
94
|
+
case "$1" in
|
|
95
|
+
${cases}
|
|
96
|
+
esac
|
|
97
|
+
}
|
|
98
|
+
${name}() {
|
|
99
|
+
${bash ? '' : 'emulate -L zsh'}
|
|
100
|
+
local state=0 pos=0 ended=0 pending='' cur word key mode prefix='' candidate i
|
|
101
|
+
local -a reply candidates tokens
|
|
102
|
+
${bash
|
|
103
|
+
? `COMPREPLY=()
|
|
104
|
+
# Bash splits '=' at COMP_WORDBREAKS; reconstruct tokens up to the cursor.
|
|
105
|
+
for ((i=1; i<=COMP_CWORD; i++)); do
|
|
106
|
+
word="\${COMP_WORDS[i]}"
|
|
107
|
+
if [[ "$word" == '=' && \${#tokens[@]} -gt 0 ]]; then
|
|
108
|
+
tokens[\${#tokens[@]}-1]+='='
|
|
109
|
+
elif [[ \${#tokens[@]} -gt 0 && "\${tokens[\${#tokens[@]}-1]}" == *= ]]; then
|
|
110
|
+
tokens[\${#tokens[@]}-1]+="$word"
|
|
111
|
+
else
|
|
112
|
+
tokens+=("$word")
|
|
113
|
+
fi
|
|
114
|
+
done
|
|
115
|
+
cur="\${tokens[\${#tokens[@]}-1]}"
|
|
116
|
+
unset "tokens[\${#tokens[@]}-1]"`
|
|
117
|
+
: `tokens=("\${(@)words[2,CURRENT-1]}")
|
|
118
|
+
(( CURRENT > 2 )) || tokens=()
|
|
119
|
+
cur="\${words[CURRENT]}"`}
|
|
120
|
+
for word in "\${tokens[@]}"; do
|
|
121
|
+
if [[ -n "$pending" ]]; then pending=''; continue; fi
|
|
122
|
+
if [[ "$ended" == 0 && "$word" == '--' ]]; then ended=1; continue; fi
|
|
123
|
+
if [[ "$ended" == 0 && "$word" == -* ]]; then
|
|
124
|
+
${name}_lookup "f:$state:\${word%%=*}"
|
|
125
|
+
if [[ "$mode" == value && "$word" != *=* ]]; then pending="f:$state:$word"; fi
|
|
126
|
+
continue
|
|
127
|
+
fi
|
|
128
|
+
if [[ "$ended" == 0 && "$pos" == 0 ]]; then
|
|
129
|
+
${name}_lookup "c:$state:$word"
|
|
130
|
+
if [[ -n "$mode" ]]; then state="$mode"; continue; fi
|
|
131
|
+
fi
|
|
132
|
+
${name}_lookup "a:$state:$pos"
|
|
133
|
+
[[ "$mode" == repeat ]] || pos=$((pos + 1))
|
|
134
|
+
done
|
|
135
|
+
if [[ -n "$pending" ]]; then
|
|
136
|
+
${name}_lookup "$pending"
|
|
137
|
+
candidates=("\${reply[@]}")
|
|
138
|
+
elif [[ "$ended" == 0 && "$cur" == --*=* ]]; then
|
|
139
|
+
key="\${cur%%=*}"
|
|
140
|
+
prefix="$key="
|
|
141
|
+
cur="\${cur#*=}"
|
|
142
|
+
${name}_lookup "f:$state:$key"
|
|
143
|
+
candidates=("\${reply[@]}")
|
|
144
|
+
elif [[ "$ended" == 0 && "$cur" == -* ]]; then
|
|
145
|
+
${name}_lookup "o:$state"
|
|
146
|
+
candidates=("\${reply[@]}")
|
|
147
|
+
else
|
|
148
|
+
${name}_lookup "a:$state:$pos"
|
|
149
|
+
candidates=("\${reply[@]}")
|
|
150
|
+
if [[ "$ended" == 0 && "$pos" == 0 ]]; then
|
|
151
|
+
${name}_lookup "s:$state"
|
|
152
|
+
candidates+=("\${reply[@]}")
|
|
153
|
+
fi
|
|
154
|
+
fi
|
|
155
|
+
${bash
|
|
156
|
+
? `for candidate in "\${candidates[@]}"; do
|
|
157
|
+
[[ "$candidate" == "$cur"* ]] && COMPREPLY+=("$prefix$candidate")
|
|
158
|
+
done
|
|
159
|
+
if [[ \${#candidates[@]} == 0 && "$cur" != -* ]]; then
|
|
160
|
+
while IFS= read -r candidate; do COMPREPLY+=("$prefix$candidate"); done < <(compgen -f -- "$cur")
|
|
161
|
+
fi
|
|
162
|
+
# Readline replaces only the value when '=' is a word break.
|
|
163
|
+
if [[ -n "$prefix" && "$COMP_WORDBREAKS" == *'='* ]]; then
|
|
164
|
+
for ((i=0; i<\${#COMPREPLY[@]}; i++)); do COMPREPLY[i]="\${COMPREPLY[i]#*=}"; done
|
|
165
|
+
fi`
|
|
166
|
+
: `if (( \${#candidates[@]} )); then
|
|
167
|
+
[[ -z "$prefix" ]] || compset -P '*='
|
|
168
|
+
compadd -- "\${candidates[@]}"
|
|
169
|
+
elif [[ "$cur" != -* ]]; then
|
|
170
|
+
[[ -z "$prefix" ]] || compset -P '*='
|
|
171
|
+
_files
|
|
172
|
+
fi`}
|
|
173
|
+
return 0
|
|
174
|
+
}
|
|
175
|
+
${bash ? `complete -o filenames -F ${name} -- '${binary}'` : `compdef ${name} '${binary}'`}
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
function fishScript(name, binary, cases) {
|
|
179
|
+
// Fish's no-scope-shadowing allows the helper to update caller-local reply/mode.
|
|
180
|
+
const lookup = (key) => `${name}_lookup ${key}`;
|
|
181
|
+
return `function ${name}_lookup --no-scope-shadowing
|
|
182
|
+
set mode ''; set reply
|
|
183
|
+
switch "$argv[1]"
|
|
184
|
+
${cases}
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
function ${name}
|
|
188
|
+
set -l tokens (commandline -opc)
|
|
189
|
+
set -e tokens[1]
|
|
190
|
+
set -l cur (commandline -ct)
|
|
191
|
+
set -l state 0
|
|
192
|
+
set -l pos 0
|
|
193
|
+
set -l ended 0
|
|
194
|
+
set -l pending ''
|
|
195
|
+
set -l mode ''
|
|
196
|
+
set -l reply
|
|
197
|
+
set -l candidates
|
|
198
|
+
set -l prefix ''
|
|
199
|
+
for word in $tokens
|
|
200
|
+
if test -n "$pending"
|
|
201
|
+
set pending ''; continue
|
|
202
|
+
end
|
|
203
|
+
if test $ended = 0; and test "$word" = --
|
|
204
|
+
set ended 1; continue
|
|
205
|
+
end
|
|
206
|
+
if test $ended = 0; and string match -q -- '-*' "$word"
|
|
207
|
+
set -l flag (string split -m 1 '=' -- "$word")[1]
|
|
208
|
+
${lookup('"f:$state:$flag"')}
|
|
209
|
+
if test "$mode" = value; and not string match -q '*=*' -- "$word"
|
|
210
|
+
set pending "f:$state:$word"
|
|
211
|
+
end
|
|
212
|
+
continue
|
|
213
|
+
end
|
|
214
|
+
if test $ended = 0; and test $pos = 0
|
|
215
|
+
${lookup('"c:$state:$word"')}
|
|
216
|
+
if test -n "$mode"
|
|
217
|
+
set state $mode; continue
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
${lookup('"a:$state:$pos"')}
|
|
221
|
+
if test "$mode" != repeat; set pos (math $pos + 1); end
|
|
222
|
+
end
|
|
223
|
+
if test -n "$pending"
|
|
224
|
+
${lookup('"$pending"')}
|
|
225
|
+
set candidates $reply
|
|
226
|
+
else if test $ended = 0; and string match -q -- '--*=*' "$cur"
|
|
227
|
+
set -l pair (string split -m 1 '=' -- "$cur")
|
|
228
|
+
set prefix "$pair[1]="
|
|
229
|
+
set cur "$pair[2]"
|
|
230
|
+
${lookup('"f:$state:$pair[1]"')}
|
|
231
|
+
set candidates $reply
|
|
232
|
+
else if test $ended = 0; and string match -q -- '-*' "$cur"
|
|
233
|
+
${lookup('"o:$state"')}
|
|
234
|
+
set candidates $reply
|
|
235
|
+
else
|
|
236
|
+
${lookup('"a:$state:$pos"')}
|
|
237
|
+
set candidates $reply
|
|
238
|
+
if test $ended = 0; and test $pos = 0
|
|
239
|
+
${lookup('"s:$state"')}
|
|
240
|
+
set -a candidates $reply
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
for candidate in $candidates
|
|
244
|
+
printf '%s\\n' "$prefix$candidate"
|
|
245
|
+
end
|
|
246
|
+
if test (count $candidates) = 0; and not string match -q -- '-*' "$cur"
|
|
247
|
+
for candidate in (__fish_complete_path "$cur")
|
|
248
|
+
printf '%s\\n' "$prefix$candidate"
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
complete -c '${binary}' -f -a '(${name})'
|
|
253
|
+
`;
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=completion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.js","sourceRoot":"","sources":["../src/completion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { OpenCliDocument } from './types.js';
|
|
2
|
+
/** Hidden subcommand upstream OpenCLI adapters use for machine discovery. */
|
|
3
|
+
export declare const OPENCLI_DISCOVERY_COMMAND: '__opencli';
|
|
4
|
+
/**
|
|
5
|
+
* If argv requests the OpenCLI document (`__opencli` subcommand, optionally with upstream's
|
|
6
|
+
* `-o`/`--out <file>` flag), write it — JSON, 2-space indented, single trailing newline — to that
|
|
7
|
+
* file or to stdout, and return true. Otherwise return false without writing anything, so the
|
|
8
|
+
* caller can continue parsing argv as usual.
|
|
9
|
+
*/
|
|
10
|
+
export declare function handleOpenCliRequest(argv: readonly string[], document: () => OpenCliDocument, write?: (chunk: string) => void): Promise<boolean>;
|
|
11
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,6EAA6E;AAC7E,eAAO,MAAM,yBAAyB,EAAG,WAAoB,CAAC;AAoB9D;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,QAAQ,EAAE,MAAM,eAAe,EAC/B,KAAK,GAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAA6C,GACtE,OAAO,CAAC,OAAO,CAAC,CAUlB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { writeOpenCliDocument } from './docgen.js';
|
|
2
|
+
/** Hidden subcommand upstream OpenCLI adapters use for machine discovery. */
|
|
3
|
+
export const OPENCLI_DISCOVERY_COMMAND = '__opencli';
|
|
4
|
+
/** Sentinel returned by {@link parseOutArg} for argv this module doesn't recognize. */
|
|
5
|
+
const INVALID = Symbol('invalid');
|
|
6
|
+
/**
|
|
7
|
+
* Parse `-o <file>` / `--out <file>` / `--out=<file>` from the args following `__opencli`,
|
|
8
|
+
* matching upstream OpenCLI's `ocobra` adapter flag. Returns `undefined` for no args (write to
|
|
9
|
+
* stdout), the file path for a recognized flag, or {@link INVALID} for anything else.
|
|
10
|
+
*/
|
|
11
|
+
function parseOutArg(args) {
|
|
12
|
+
if (args.length === 0)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (args.length === 1) {
|
|
15
|
+
const match = /^(?:-o|--out)=(.+)$/.exec(args[0]);
|
|
16
|
+
return match ? match[1] : INVALID;
|
|
17
|
+
}
|
|
18
|
+
if (args.length === 2 && (args[0] === '-o' || args[0] === '--out'))
|
|
19
|
+
return args[1];
|
|
20
|
+
return INVALID;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* If argv requests the OpenCLI document (`__opencli` subcommand, optionally with upstream's
|
|
24
|
+
* `-o`/`--out <file>` flag), write it — JSON, 2-space indented, single trailing newline — to that
|
|
25
|
+
* file or to stdout, and return true. Otherwise return false without writing anything, so the
|
|
26
|
+
* caller can continue parsing argv as usual.
|
|
27
|
+
*/
|
|
28
|
+
export async function handleOpenCliRequest(argv, document, write = (chunk) => process.stdout.write(chunk)) {
|
|
29
|
+
if (argv[0] !== OPENCLI_DISCOVERY_COMMAND)
|
|
30
|
+
return false;
|
|
31
|
+
const outFile = parseOutArg(argv.slice(1));
|
|
32
|
+
if (outFile === INVALID)
|
|
33
|
+
return false;
|
|
34
|
+
if (outFile === undefined) {
|
|
35
|
+
write(`${JSON.stringify(document(), null, 2)}\n`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
await writeOpenCliDocument(document(), outFile);
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGnD,6EAA6E;AAC7E,MAAM,CAAC,MAAM,yBAAyB,GAAG,WAAoB,CAAC;AAE9D,uFAAuF;AACvF,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAElC;;;;GAIG;AACH,SAAS,WAAW,CAAC,IAAuB;IAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;QACnD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IACrC,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACnF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAuB,EACvB,QAA+B,EAC/B,KAAK,GAA4B,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;IAEvE,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,yBAAyB;QAAE,OAAO,KAAK,CAAC;IACxD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC;SAAM,CAAC;QACN,MAAM,oBAAoB,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/docgen.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { InfoObject, OpenCliDocument } from './types.js';
|
|
2
|
+
/** The minimal shape of `package.json` used to derive an {@link InfoObject}. */
|
|
3
|
+
export type PackageJsonLike = {
|
|
4
|
+
name?: string;
|
|
5
|
+
version?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
bin?: string | Record<string, string>;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Derive an {@link InfoObject} from a parsed `package.json`, so adapters don't each need their
|
|
11
|
+
* own `title`/`binary`/`version` bookkeeping. `overrides` wins over anything derived from `pkg`,
|
|
12
|
+
* and is required for whatever `pkg` cannot express (e.g. a package that exposes several binaries).
|
|
13
|
+
*/
|
|
14
|
+
export declare function infoFromPackageJson(pkg: PackageJsonLike, overrides?: Partial<InfoObject>): InfoObject;
|
|
15
|
+
/** Output format for {@link writeOpenCliDocument}. */
|
|
16
|
+
export type DocumentFormat = 'json' | 'yaml' | 'markdown';
|
|
17
|
+
/**
|
|
18
|
+
* Render `document` (JSON, YAML, or Markdown) and write it to `output`, or to stdout if `output`
|
|
19
|
+
* is omitted. Shared by every adapter's docgen command and by `handleOpenCliRequest`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function writeOpenCliDocument(document: OpenCliDocument, output?: string, format?: DocumentFormat): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=docgen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docgen.d.ts","sourceRoot":"","sources":["../src/docgen.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE9D,gFAAgF;AAChF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC,CAAC;AAOF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,EAAE,SAAS,GAAE,OAAO,CAAC,UAAU,CAAM,GAAG,UAAU,CAYzG;AAED,sDAAsD;AACtD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAE1D;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,eAAe,EACzB,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,GAAE,cAAuB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAYf"}
|
package/dist/docgen.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import { stringify as stringifyYaml } from 'yaml';
|
|
3
|
+
import { renderMarkdown } from './index.js';
|
|
4
|
+
/** Strip a npm scope (`@scope/name` -> `name`) for use as a default binary name. */
|
|
5
|
+
function unscopedName(name) {
|
|
6
|
+
return name.replace(/^@[^/]+\//, '');
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Derive an {@link InfoObject} from a parsed `package.json`, so adapters don't each need their
|
|
10
|
+
* own `title`/`binary`/`version` bookkeeping. `overrides` wins over anything derived from `pkg`,
|
|
11
|
+
* and is required for whatever `pkg` cannot express (e.g. a package that exposes several binaries).
|
|
12
|
+
*/
|
|
13
|
+
export function infoFromPackageJson(pkg, overrides = {}) {
|
|
14
|
+
const binary = overrides.binary ??
|
|
15
|
+
(typeof pkg.bin === 'object' && pkg.bin !== null ? Object.keys(pkg.bin)[0] : undefined) ??
|
|
16
|
+
(pkg.name ? unscopedName(pkg.name) : undefined);
|
|
17
|
+
if (!binary)
|
|
18
|
+
throw new Error('infoFromPackageJson: could not determine a binary name; pass overrides.binary');
|
|
19
|
+
const version = overrides.version ?? pkg.version;
|
|
20
|
+
if (!version)
|
|
21
|
+
throw new Error('infoFromPackageJson: package.json has no version; pass overrides.version');
|
|
22
|
+
const title = overrides.title ?? pkg.name ?? binary;
|
|
23
|
+
const info = { ...overrides, title, binary, version };
|
|
24
|
+
if (info.summary === undefined && pkg.description !== undefined)
|
|
25
|
+
info.summary = pkg.description;
|
|
26
|
+
return info;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Render `document` (JSON, YAML, or Markdown) and write it to `output`, or to stdout if `output`
|
|
30
|
+
* is omitted. Shared by every adapter's docgen command and by `handleOpenCliRequest`.
|
|
31
|
+
*/
|
|
32
|
+
export async function writeOpenCliDocument(document, output, format = 'json') {
|
|
33
|
+
const content = format === 'markdown'
|
|
34
|
+
? renderMarkdown(document)
|
|
35
|
+
: format === 'yaml'
|
|
36
|
+
? stringifyYaml(document)
|
|
37
|
+
: `${JSON.stringify(document, null, 2)}\n`;
|
|
38
|
+
if (output === undefined) {
|
|
39
|
+
process.stdout.write(content);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
await writeFile(output, content);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=docgen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docgen.js","sourceRoot":"","sources":["../src/docgen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,MAAM,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAW5C,oFAAoF;AACpF,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAoB,EAAE,SAAS,GAAwB,EAAE;IAC3F,MAAM,MAAM,GACV,SAAS,CAAC,MAAM;QAChB,CAAC,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvF,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC;IAC9G,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC;IACjD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC1G,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC;IACpD,MAAM,IAAI,GAAe,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAClE,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,GAAG,CAAC,WAAW,KAAK,SAAS;QAAE,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,WAAW,CAAC;IAChG,OAAO,IAAI,CAAC;AACd,CAAC;AAKD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAyB,EACzB,MAAe,EACf,MAAM,GAAmB,MAAM;IAE/B,MAAM,OAAO,GACX,MAAM,KAAK,UAAU;QACnB,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC;QAC1B,CAAC,CAAC,MAAM,KAAK,MAAM;YACjB,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC;YACzB,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IACjD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,MAAM,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACnC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export * from './discovery.js';
|
|
2
|
+
export * from './merge.js';
|
|
3
|
+
export * from './docgen.js';
|
|
4
|
+
export * from './completion.js';
|
|
5
|
+
import type { OpenCliDocument } from './types.js';
|
|
6
|
+
export type * from './types.js';
|
|
7
|
+
/** Supported version of the vendored OpenCLI schema. */
|
|
8
|
+
export declare const OPENCLI_VERSION: '1.0.0-alpha.14';
|
|
9
|
+
/** Vendored JSON Schema used to validate OpenCLI documents. */
|
|
10
|
+
export declare const openCliSchema: object;
|
|
11
|
+
/** Validate against the exact vendored upstream JSON Schema, plus upstream's logical checks. */
|
|
12
|
+
export declare function validate(document: unknown): {
|
|
13
|
+
valid: boolean;
|
|
14
|
+
errors: string[];
|
|
15
|
+
};
|
|
16
|
+
/** Parse JSON or YAML and reject documents that do not match the specification. */
|
|
17
|
+
export declare function parse(input: string, options?: {
|
|
18
|
+
format?: 'json' | 'yaml';
|
|
19
|
+
}): OpenCliDocument;
|
|
20
|
+
/** Render a complete Markdown reference, preserving spec-authored Markdown prose. */
|
|
21
|
+
export declare function renderMarkdown(document: OpenCliDocument): string;
|
|
22
|
+
export type GeneratedPage = {
|
|
23
|
+
id: string;
|
|
24
|
+
title: string;
|
|
25
|
+
path: string;
|
|
26
|
+
content: string;
|
|
27
|
+
};
|
|
28
|
+
/** Produce a landing page and one page per visible command with safe, stable routes. */
|
|
29
|
+
export declare function generatePages(document: OpenCliDocument, options?: {
|
|
30
|
+
basePath?: string;
|
|
31
|
+
}): GeneratedPage[];
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAMhC,OAAO,KAAK,EACV,eAAe,EAQhB,MAAM,YAAY,CAAC;AACpB,mBAAmB,YAAY,CAAC;AAEhC,wDAAwD;AACxD,eAAO,MAAM,eAAe,EAAG,gBAAyB,CAAC;AACzD,+DAA+D;AAC/D,eAAO,MAAM,aAAa,QAAS,CAAC;AAKpC,gGAAgG;AAChG,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAUhF;AAED,mFAAmF;AACnF,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAO,GAAG,eAAe,CAwBhG;AAyND,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAOhE;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AA4BzF,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,aAAa,EAAE,CAkC7G"}
|