cli-kiss 0.2.2 → 0.2.4
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/README.md +1 -1
- package/dist/index.d.ts +711 -1046
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/docs/.vitepress/config.mts +1 -0
- package/docs/.vitepress/theme/index.ts +4 -0
- package/docs/.vitepress/theme/style.css +4 -0
- package/docs/guide/01_getting_started.md +4 -4
- package/docs/guide/02_commands.md +72 -41
- package/docs/guide/03_options.md +13 -14
- package/docs/guide/04_positionals.md +6 -8
- package/docs/guide/05_types.md +9 -11
- package/docs/guide/06_run.md +4 -5
- package/docs/index.md +8 -3
- package/docs/public/hero.png +0 -0
- package/package.json +1 -1
- package/src/lib/Command.ts +151 -275
- package/src/lib/Operation.ts +57 -95
- package/src/lib/Option.ts +194 -181
- package/src/lib/Positional.ts +54 -112
- package/src/lib/Reader.ts +155 -156
- package/src/lib/Run.ts +64 -69
- package/src/lib/Type.ts +89 -145
- package/src/lib/Typo.ts +131 -195
- package/src/lib/Usage.ts +203 -69
- package/tests/unit.Reader.aliases.ts +31 -15
- package/tests/unit.Reader.commons.ts +99 -43
- package/tests/unit.Reader.shortBig.ts +75 -31
- package/tests/unit.command.execute.ts +146 -91
- package/tests/unit.command.usage.ts +235 -114
- package/tests/unit.runner.cycle.ts +50 -20
- package/tests/unit.runner.errors.ts +19 -3
|
@@ -8,7 +8,7 @@ npm install cli-kiss
|
|
|
8
8
|
|
|
9
9
|
## Your first CLI
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Minimal "greet" CLI with:
|
|
12
12
|
|
|
13
13
|
- a required `NAME` positional
|
|
14
14
|
- optional `--loud` flag:
|
|
@@ -60,7 +60,7 @@ greet Alice
|
|
|
60
60
|
Hello, Alice!
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
Pass
|
|
63
|
+
Pass a flag:
|
|
64
64
|
|
|
65
65
|
```sh
|
|
66
66
|
greet --loud Alice
|
|
@@ -70,7 +70,7 @@ greet --loud Alice
|
|
|
70
70
|
HELLO, ALICE!
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
Help (built-in):
|
|
74
74
|
|
|
75
75
|
```sh
|
|
76
76
|
greet --help
|
|
@@ -88,7 +88,7 @@ Options:
|
|
|
88
88
|
--loud[=no] Print in uppercase
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
Version (built-in):
|
|
92
92
|
|
|
93
93
|
```sh
|
|
94
94
|
greet --version
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
# Commands
|
|
2
2
|
|
|
3
|
-
Commands are the building blocks of a `cli-kiss` CLI.
|
|
4
|
-
|
|
5
3
|
Three factory functions cover every use-case.
|
|
6
4
|
|
|
7
5
|
## `command` — leaf command
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
No subcommands — directly runs an operation.
|
|
10
8
|
|
|
11
9
|
```ts
|
|
12
10
|
import { command, operation, positionalRequired, typeString } from "cli-kiss";
|
|
@@ -18,40 +16,16 @@ const greet = command(
|
|
|
18
16
|
options: {},
|
|
19
17
|
positionals: [positionalRequired({ type: typeString, label: "NAME" })],
|
|
20
18
|
},
|
|
21
|
-
async (_ctx, { positionals: [name] })
|
|
19
|
+
async function (_ctx, { positionals: [name] }) {
|
|
22
20
|
console.log(`Hello, ${name}!`);
|
|
23
21
|
},
|
|
24
22
|
),
|
|
25
23
|
);
|
|
26
24
|
```
|
|
27
25
|
|
|
28
|
-
### `CommandInformation`
|
|
29
|
-
|
|
30
|
-
Every command accepts a metadata object:
|
|
31
|
-
|
|
32
|
-
| Field | Type | Description |
|
|
33
|
-
| ------------- | ----------- | ------------------------------------------------- |
|
|
34
|
-
| `description` | `string` | Short description shown in help output |
|
|
35
|
-
| `hint` | `string?` | Note shown in parentheses next to the description |
|
|
36
|
-
| `details` | `string[]?` | Extra lines printed below the description |
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
command(
|
|
40
|
-
{
|
|
41
|
-
description: "Deploy the application",
|
|
42
|
-
hint: "experimental",
|
|
43
|
-
details: [
|
|
44
|
-
"Pushes to the configured remote.",
|
|
45
|
-
"Runs migrations after push.",
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
deployOperation,
|
|
49
|
-
);
|
|
50
|
-
```
|
|
51
|
-
|
|
52
26
|
## `commandWithSubcommands` — dispatch to a subcommand
|
|
53
27
|
|
|
54
|
-
|
|
28
|
+
User must pick one of several sub-actions.
|
|
55
29
|
|
|
56
30
|
```ts
|
|
57
31
|
import {
|
|
@@ -65,19 +39,19 @@ const rootCmd = commandWithSubcommands(
|
|
|
65
39
|
{ description: "My deployment CLI" },
|
|
66
40
|
// This operation runs before the subcommand is selected.
|
|
67
41
|
// Its return value becomes the subcommand's context.
|
|
68
|
-
operation({ options: {}, positionals: [] }, async (_ctx)
|
|
69
|
-
db: "postgres://localhost/mydb"
|
|
70
|
-
})
|
|
42
|
+
operation({ options: {}, positionals: [] }, async function (_ctx) {
|
|
43
|
+
return { db: "postgres://localhost/mydb" };
|
|
44
|
+
}),
|
|
71
45
|
{
|
|
72
46
|
deploy: command(
|
|
73
47
|
{ description: "Deploy the latest build" },
|
|
74
|
-
operation({ options: {}, positionals: [] }, async (ctx)
|
|
48
|
+
operation({ options: {}, positionals: [] }, async function (ctx) {
|
|
75
49
|
console.log(`Deploying with DB: ${ctx.db}`);
|
|
76
50
|
}),
|
|
77
51
|
),
|
|
78
52
|
rollback: command(
|
|
79
53
|
{ description: "Rollback to the previous release" },
|
|
80
|
-
operation({ options: {}, positionals: [] }, async (ctx)
|
|
54
|
+
operation({ options: {}, positionals: [] }, async function (ctx) {
|
|
81
55
|
console.log(`Rolling back, DB: ${ctx.db}`);
|
|
82
56
|
}),
|
|
83
57
|
),
|
|
@@ -105,13 +79,11 @@ Subcommands:
|
|
|
105
79
|
|
|
106
80
|
### Subcommand names
|
|
107
81
|
|
|
108
|
-
|
|
109
|
-
lowercase strings.
|
|
82
|
+
Keys are the tokens users type — must be lowercase strings.
|
|
110
83
|
|
|
111
84
|
## `commandChained` — sequential stages
|
|
112
85
|
|
|
113
|
-
|
|
114
|
-
user-visible subcommand token.
|
|
86
|
+
Splits a command into reusable steps with no extra user-visible token.
|
|
115
87
|
|
|
116
88
|
```ts
|
|
117
89
|
import {
|
|
@@ -132,7 +104,7 @@ const authenticatedDeploy = commandChained(
|
|
|
132
104
|
long: "token",
|
|
133
105
|
type: typeString,
|
|
134
106
|
description: "API token",
|
|
135
|
-
default: ()
|
|
107
|
+
default: function () {
|
|
136
108
|
const t = process.env.API_TOKEN;
|
|
137
109
|
if (!t) throw new Error("API_TOKEN env var is required");
|
|
138
110
|
return t;
|
|
@@ -153,5 +125,64 @@ const authenticatedDeploy = commandChained(
|
|
|
153
125
|
);
|
|
154
126
|
```
|
|
155
127
|
|
|
156
|
-
|
|
157
|
-
|
|
128
|
+
All stages share a single flat usage — users see one combined command.
|
|
129
|
+
|
|
130
|
+
## `CommandInformation`
|
|
131
|
+
|
|
132
|
+
Every command accepts a metadata object:
|
|
133
|
+
|
|
134
|
+
| Field | Type | Description |
|
|
135
|
+
| ------------- | ------------ | ------------------------------------------------- |
|
|
136
|
+
| `description` | `string` | Short description shown in help output |
|
|
137
|
+
| `hint` | `string?` | Note shown in parentheses next to the description |
|
|
138
|
+
| `details` | `string[]?` | Extra lines printed below the description |
|
|
139
|
+
| `examples` | `Example[]?` | Usage examples shown in the `Examples:` section |
|
|
140
|
+
|
|
141
|
+
Each `Example` entry has:
|
|
142
|
+
|
|
143
|
+
| Field | Type | Description |
|
|
144
|
+
| ------------- | -------------- | ------------------------------------------------------- |
|
|
145
|
+
| `explanation` | `string` | Comment line shown above the example command |
|
|
146
|
+
| `commandArgs` | `CommandArg[]` | Ordered list of arguments to render on the command line |
|
|
147
|
+
|
|
148
|
+
Each `CommandArg` is one of:
|
|
149
|
+
|
|
150
|
+
| Shape | Renders as |
|
|
151
|
+
| ----------------------------------------------- | ---------------- |
|
|
152
|
+
| `string` | literal text |
|
|
153
|
+
| `{ positional: string }` | positional label |
|
|
154
|
+
| `{ subcommand: string }` | subcommand name |
|
|
155
|
+
| `{ option: { long: string; value?: string } }` | `--long[=value]` |
|
|
156
|
+
| `{ option: { short: string; value?: string } }` | `-s[=value]` |
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
command(
|
|
160
|
+
{
|
|
161
|
+
description: "Deploy the application",
|
|
162
|
+
hint: "experimental",
|
|
163
|
+
details: [
|
|
164
|
+
"Pushes to the configured remote.",
|
|
165
|
+
"Runs migrations after push.",
|
|
166
|
+
],
|
|
167
|
+
examples: [
|
|
168
|
+
{
|
|
169
|
+
explanation: "Deploy with a specific tag",
|
|
170
|
+
commandArgs: [
|
|
171
|
+
{ positional: "v1.2.3" },
|
|
172
|
+
{ option: { long: "dry-run" } },
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
deployOperation,
|
|
178
|
+
);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The `Examples:` section in `--help` output renders each entry as a comment
|
|
182
|
+
followed by the reconstructed command line:
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
Examples:
|
|
186
|
+
# Deploy with a specific tag
|
|
187
|
+
deploy v1.2.3 --dry-run
|
|
188
|
+
```
|
package/docs/guide/03_options.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# Options
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Named `--` arguments (or `-` for short forms). Declared in the `options` map of
|
|
4
|
+
[`operation`](/guide/02_commands).
|
|
5
5
|
|
|
6
6
|
## `optionFlag` — boolean toggle
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
`--flag=no`.
|
|
8
|
+
Present or absent. Also accepts `--flag=true` / `--flag=no` / `--no-flag`.
|
|
10
9
|
|
|
11
10
|
```ts
|
|
12
11
|
import { optionFlag } from "cli-kiss";
|
|
@@ -22,14 +21,14 @@ const verbose = optionFlag({
|
|
|
22
21
|
// (absent) → false
|
|
23
22
|
```
|
|
24
23
|
|
|
25
|
-
| Parameter | Type | Description
|
|
26
|
-
| ------------- | --------------------- |
|
|
27
|
-
| `long` | `Lowercase<string>` | Long flag name (without `--`)
|
|
28
|
-
| `short` | `string?` | Short flag name (without `-`)
|
|
29
|
-
| `description` | `string?` | Help text
|
|
30
|
-
| `hint` | `string?` | Short note in parentheses
|
|
31
|
-
| `default` | `
|
|
32
|
-
| `aliases` | `{ longs?, shorts? }` | Additional names for the flag
|
|
24
|
+
| Parameter | Type | Description |
|
|
25
|
+
| ------------- | --------------------- | ------------------------------------ |
|
|
26
|
+
| `long` | `Lowercase<string>` | Long flag name (without `--`) |
|
|
27
|
+
| `short` | `string?` | Short flag name (without `-`) |
|
|
28
|
+
| `description` | `string?` | Help text |
|
|
29
|
+
| `hint` | `string?` | Short note in parentheses |
|
|
30
|
+
| `default` | `boolean? ` | Value when absent (default: `false`) |
|
|
31
|
+
| `aliases` | `{ longs?, shorts? }` | Additional names for the flag |
|
|
33
32
|
|
|
34
33
|
::: tip A flag specified more than once triggers a parse error. Use
|
|
35
34
|
[`optionRepeatable`](#optionrepeatable-collect-multiple-values) if you need
|
|
@@ -39,7 +38,7 @@ multiple values.
|
|
|
39
38
|
|
|
40
39
|
## `optionSingleValue` — one typed value
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
Exactly one typed value.
|
|
43
42
|
|
|
44
43
|
```ts
|
|
45
44
|
import { optionSingleValue, typeString } from "cli-kiss";
|
|
@@ -71,7 +70,7 @@ const output = optionSingleValue({
|
|
|
71
70
|
|
|
72
71
|
## `optionRepeatable` — collect multiple values
|
|
73
72
|
|
|
74
|
-
Collects every occurrence into an array.
|
|
73
|
+
Collects every occurrence into an array.
|
|
75
74
|
|
|
76
75
|
```ts
|
|
77
76
|
import { optionRepeatable, typeString } from "cli-kiss";
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# Positionals
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
order in the `positionals` array of [`operation`](/guide/02_commands).
|
|
3
|
+
Bare (non-`--`) arguments, consumed in order. Declared in the `positionals` array of [`operation`](/guide/02_commands).
|
|
5
4
|
|
|
6
5
|
## `positionalRequired` — must be present
|
|
7
6
|
|
|
8
|
-
Fails
|
|
7
|
+
Fails if missing.
|
|
9
8
|
|
|
10
9
|
```ts
|
|
11
10
|
import { positionalRequired, typeString } from "cli-kiss";
|
|
@@ -28,7 +27,7 @@ const name = positionalRequired({
|
|
|
28
27
|
|
|
29
28
|
## `positionalOptional` — may be absent
|
|
30
29
|
|
|
31
|
-
Falls back to a default
|
|
30
|
+
Falls back to a default when absent.
|
|
32
31
|
|
|
33
32
|
```ts
|
|
34
33
|
import { positionalOptional, typeString } from "cli-kiss";
|
|
@@ -53,7 +52,7 @@ const greeting = positionalOptional({
|
|
|
53
52
|
|
|
54
53
|
## `positionalVariadics` — zero or more
|
|
55
54
|
|
|
56
|
-
|
|
55
|
+
Consumes all remaining tokens into an array.
|
|
57
56
|
|
|
58
57
|
```ts
|
|
59
58
|
import { positionalVariadics, typeString } from "cli-kiss";
|
|
@@ -78,7 +77,7 @@ const args = positionalVariadics({
|
|
|
78
77
|
endDelimiter: "STOP",
|
|
79
78
|
description: "Arguments (end with STOP)",
|
|
80
79
|
});
|
|
81
|
-
// my-cli foo bar STOP
|
|
80
|
+
// my-cli foo bar STOP → ["foo", "bar"]
|
|
82
81
|
```
|
|
83
82
|
|
|
84
83
|
| Parameter | Type | Description |
|
|
@@ -91,8 +90,7 @@ const args = positionalVariadics({
|
|
|
91
90
|
|
|
92
91
|
## Ordering rules
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
come first; variadics should be last.
|
|
93
|
+
Consumed **in declaration order** — required first, variadics last.
|
|
96
94
|
|
|
97
95
|
```ts
|
|
98
96
|
operation(
|
package/docs/guide/05_types.md
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
# Types
|
|
2
2
|
|
|
3
|
-
A `Type<Value>`
|
|
4
|
-
function. It tells cli-kiss how to convert a raw CLI string into a typed value.
|
|
3
|
+
A `Type<Value>` converts a raw CLI string into a typed value: a `content` label paired with a `decoder`.
|
|
5
4
|
|
|
6
5
|
## Built-in types
|
|
7
6
|
|
|
8
7
|
| Export | TypeScript type | Accepts |
|
|
9
8
|
| ------------- | --------------- | ---------------------------------------------------------- |
|
|
10
9
|
| `typeString` | `string` | Any string |
|
|
11
|
-
| `typeBoolean` | `boolean` | `true
|
|
10
|
+
| `typeBoolean` | `boolean` | `true/yes/on/1/y/t` → true, `false/no/off/0/n/f` → false (case-insensitive) |
|
|
12
11
|
| `typeNumber` | `number` | Integers, floats, scientific notation |
|
|
13
12
|
| `typeInteger` | `bigint` | Integer strings only |
|
|
14
13
|
| `typeDate` | `Date` | Any format accepted by `Date.parse` (ISO 8601 recommended) |
|
|
@@ -34,7 +33,7 @@ typeUrl.decoder("https://example.com/path"); // → URL object
|
|
|
34
33
|
|
|
35
34
|
## `typeOneOf` — string enum
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
Accepts only a fixed set of strings:
|
|
38
37
|
|
|
39
38
|
```ts
|
|
40
39
|
import { typeOneOf } from "cli-kiss";
|
|
@@ -46,14 +45,14 @@ typeEnv.decoder("unknown");
|
|
|
46
45
|
// Error: Invalid value: "unknown" (expected one of: "dev" | "staging" | "prod")
|
|
47
46
|
```
|
|
48
47
|
|
|
49
|
-
## `
|
|
48
|
+
## `typeMapped` — transform an existing type
|
|
50
49
|
|
|
51
50
|
Chain a `before` type with an `after` transformation:
|
|
52
51
|
|
|
53
52
|
```ts
|
|
54
|
-
import {
|
|
53
|
+
import { typeMapped, typeNumber } from "cli-kiss";
|
|
55
54
|
|
|
56
|
-
const typePort =
|
|
55
|
+
const typePort = typeMapped(typeNumber, {
|
|
57
56
|
content: "Port",
|
|
58
57
|
decoder: (n) => {
|
|
59
58
|
if (n < 1 || n > 65535) throw new Error("Out of range");
|
|
@@ -64,12 +63,11 @@ const typePort = typeConverted(typeNumber, {
|
|
|
64
63
|
// "--port 99999" → Error: --port: <PORT>: Port: Out of range
|
|
65
64
|
```
|
|
66
65
|
|
|
67
|
-
Errors from the `before` decoder are
|
|
68
|
-
`from: <content>` for easy debugging.
|
|
66
|
+
Errors from the `before` decoder are prefixed with `from: <content>`.
|
|
69
67
|
|
|
70
68
|
## `typeTuple` — fixed-length delimited value
|
|
71
69
|
|
|
72
|
-
|
|
70
|
+
Splits a string into a fixed-length typed tuple:
|
|
73
71
|
|
|
74
72
|
```ts
|
|
75
73
|
import { typeTuple, typeNumber } from "cli-kiss";
|
|
@@ -89,7 +87,7 @@ typeTuple([typeString, typeNumber], ":");
|
|
|
89
87
|
|
|
90
88
|
## `typeList` — variable-length delimited value
|
|
91
89
|
|
|
92
|
-
|
|
90
|
+
Splits a string into an array of typed values:
|
|
93
91
|
|
|
94
92
|
```ts
|
|
95
93
|
import { typeList, typeNumber } from "cli-kiss";
|
package/docs/guide/06_run.md
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## `runAndExit`
|
|
4
4
|
|
|
5
|
-
`runAndExit`
|
|
6
|
-
runs the matched command, and exits the process.
|
|
5
|
+
`runAndExit` parses arguments, runs the matched command, and exits.
|
|
7
6
|
|
|
8
7
|
```ts
|
|
9
8
|
await runAndExit(cliName, cliArgs, context, command, options?);
|
|
@@ -25,7 +24,7 @@ await runAndExit(cliName, cliArgs, context, command, options?);
|
|
|
25
24
|
| `usageOnHelp` | `boolean?` | `true` | Enables `--help` flag |
|
|
26
25
|
| `usageOnError` | `boolean?` | `true` | Prints usage to stderr when parsing fails |
|
|
27
26
|
| `useTtyColors` | `boolean \| "mock"?` | auto | Controls ANSI color output |
|
|
28
|
-
| `onError` | `(error: unknown) => void` | — | Custom handler for execution errors
|
|
27
|
+
| `onError` | `(error: unknown) => void` | — | Custom handler for parse and execution errors |
|
|
29
28
|
| `onExit` | `(code: number) => never` | `process.exit` | Override for testing |
|
|
30
29
|
|
|
31
30
|
### Exit codes
|
|
@@ -127,7 +126,7 @@ my-cli deploy --dry-run
|
|
|
127
126
|
|
|
128
127
|
## Color control
|
|
129
128
|
|
|
130
|
-
|
|
129
|
+
Colors are auto-detected. Override:
|
|
131
130
|
|
|
132
131
|
```ts
|
|
133
132
|
// Force colors on
|
|
@@ -142,7 +141,7 @@ await runAndExit("my-cli", args, ctx, cmd, { useTtyColors: "mock" });
|
|
|
142
141
|
|
|
143
142
|
## Testing your CLI
|
|
144
143
|
|
|
145
|
-
Override `onExit`
|
|
144
|
+
Override `onExit` to prevent process exit during tests:
|
|
146
145
|
|
|
147
146
|
```ts
|
|
148
147
|
import { runAndExit } from "cli-kiss";
|
package/docs/index.md
CHANGED
|
@@ -4,10 +4,12 @@ layout: home
|
|
|
4
4
|
hero:
|
|
5
5
|
name: CLI-kiss
|
|
6
6
|
text: CLI for TypeScript.
|
|
7
|
+
|
|
7
8
|
tagline:
|
|
8
|
-
No bloat, no
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
No bloat, no dependencies.<br/>Standard behavior users expect.<br/>Keep It Simple and Stupid, it just does the job.
|
|
10
|
+
|
|
11
|
+
image:
|
|
12
|
+
src: /hero.png
|
|
11
13
|
|
|
12
14
|
actions:
|
|
13
15
|
- theme: brand
|
|
@@ -19,12 +21,15 @@ hero:
|
|
|
19
21
|
|
|
20
22
|
features:
|
|
21
23
|
- title: Zero dependencies
|
|
24
|
+
icon: 📦
|
|
22
25
|
details:
|
|
23
26
|
Ships with no runtime dependencies.<br/>Pure TypeScript, 5kb bundled.
|
|
24
27
|
- title: Fully typed
|
|
28
|
+
icon: 🧠
|
|
25
29
|
details:
|
|
26
30
|
TypeScript first.<br/>Options and positionals inputs strongly typed.
|
|
27
31
|
- title: Composable
|
|
32
|
+
icon: 🧩
|
|
28
33
|
details:
|
|
29
34
|
Easily create nested subcommands.<br/>Build complex CLIs by nesting logic.
|
|
30
35
|
---
|
|
Binary file
|