@webq/cli 1.0.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/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +403 -0
- package/dist/index.js +417 -0
- package/package.json +200 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Crylan Software
|
|
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,403 @@
|
|
|
1
|
+
# WebQ CLI
|
|
2
|
+
|
|
3
|
+
A CLI tool for querying and validating Custom Elements Manifest (CEM) JSON files. It can also run as an MCP (Model Context Protocol) server for AI assistants.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Requires [Bun](https://bun.sh) 1.0+.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Pre-built Binaries
|
|
14
|
+
|
|
15
|
+
Download the appropriate binary for your platform from the [releases page](https://github.com/blueprintui/webq/releases).
|
|
16
|
+
|
|
17
|
+
| Platform | Architecture | Download |
|
|
18
|
+
| -------- | --------------------- | ---------------------- |
|
|
19
|
+
| macOS | Apple Silicon (arm64) | `webq-macos-arm64` |
|
|
20
|
+
| macOS | Intel (x64) | `webq-macos-x64` |
|
|
21
|
+
| Linux | arm64 | `webq-linux-arm64` |
|
|
22
|
+
| Linux | x86_64 (x64) | `webq-linux-x64` |
|
|
23
|
+
| Windows | x86_64 (x64) | `webq-windows-x64.exe` |
|
|
24
|
+
|
|
25
|
+
## Configuration
|
|
26
|
+
|
|
27
|
+
### Config File
|
|
28
|
+
|
|
29
|
+
Create a `webq.config.json` in your project root to configure paths and per-rule settings:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"global": {
|
|
34
|
+
"path": ["./node_modules/@org/components", "./node_modules/@org/icons"]
|
|
35
|
+
},
|
|
36
|
+
"validate-html": {
|
|
37
|
+
"rules": {
|
|
38
|
+
"no-unknown-element": ["error", { "tags": ["my-extra-tag"] }],
|
|
39
|
+
"no-unknown-event": ["error", { "events": ["custom-event"] }],
|
|
40
|
+
"no-deprecated-element": "warn",
|
|
41
|
+
"no-boolean-attr-value": "off"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Rules use ESLint-style config: either a severity string (`"error"`, `"warn"`, `"off"`) or a tuple `["severity", { ...options }]`.
|
|
48
|
+
|
|
49
|
+
The `no-unknown-element` rule accepts a `tags` allowlist and the `no-unknown-event` rule accepts an `events` allowlist.
|
|
50
|
+
|
|
51
|
+
### Path Resolution Priority
|
|
52
|
+
|
|
53
|
+
Schema paths are resolved in this order:
|
|
54
|
+
|
|
55
|
+
1. `--path` CLI flag
|
|
56
|
+
2. `webq.config.json` `global.path` array
|
|
57
|
+
3. `WEBQ_PATH` environment variable
|
|
58
|
+
|
|
59
|
+
### Config Flag
|
|
60
|
+
|
|
61
|
+
Use `--config` to specify a config file path explicitly:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
webq validate-html '<my-tag></my-tag>' --config ./path/to/webq.config.json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Without `--config`, webq auto-discovers `webq.config.json` in the current directory.
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
### CLI Commands
|
|
72
|
+
|
|
73
|
+
The `--path` flag accepts directories (searched recursively for `custom-elements.json`).
|
|
74
|
+
|
|
75
|
+
| Command | Parameters | Description |
|
|
76
|
+
| ------------------------ | ------------ | ---------------------------------------------------------- |
|
|
77
|
+
| `element.list` | | List all custom elements |
|
|
78
|
+
| `element` | `<tag-name>` | Get full details for a specific element |
|
|
79
|
+
| `element.attributes` | `<tag-name>` | Get attributes for an element |
|
|
80
|
+
| `element.properties` | `<tag-name>` | Get properties/fields for an element |
|
|
81
|
+
| `element.methods` | `<tag-name>` | Get methods for an element |
|
|
82
|
+
| `element.events` | `<tag-name>` | Get events for an element |
|
|
83
|
+
| `element.commands` | `<tag-name>` | Get invoker commands for an element |
|
|
84
|
+
| `element.slots` | `<tag-name>` | Get slots for an element |
|
|
85
|
+
| `element.css-properties` | `<tag-name>` | Get CSS custom properties for an element |
|
|
86
|
+
| `element.css-parts` | `<tag-name>` | Get CSS parts for an element |
|
|
87
|
+
| `pattern.list` | | List all compositional patterns |
|
|
88
|
+
| `pattern` | `<name>` | Get full details for a specific pattern |
|
|
89
|
+
| `attribute.list` | | List all custom attributes |
|
|
90
|
+
| `attribute` | `<name>` | Get details for a custom attribute |
|
|
91
|
+
| `style.property.list` | | List all CSS custom properties |
|
|
92
|
+
| `style.property` | `<name>` | Get details for a CSS custom property |
|
|
93
|
+
| `validate-manifest` | | Validate a Custom Elements Manifest file |
|
|
94
|
+
| `validate-html` | `<html>` | Validate HTML against CEM definitions |
|
|
95
|
+
| `mcp` | | Start the MCP server on STDIO transport |
|
|
96
|
+
| `setup-mcp` | | Add webq MCP server to `.mcp.json` |
|
|
97
|
+
| `setup-skill` | | Create Claude Code skill at `.claude/skills/webq/SKILL.md` |
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# List all custom elements
|
|
101
|
+
webq element.list --path .
|
|
102
|
+
|
|
103
|
+
# Search an entire node_modules directory
|
|
104
|
+
webq element.list --path ./node_modules
|
|
105
|
+
|
|
106
|
+
# Multiple paths (comma-separated)
|
|
107
|
+
webq element.list --path ./lib1,./node_modules/@org
|
|
108
|
+
|
|
109
|
+
# Get full details for a specific element
|
|
110
|
+
webq element bp-button --path ./node_modules
|
|
111
|
+
|
|
112
|
+
# Get specific element information
|
|
113
|
+
webq element.attributes bp-button --path ./node_modules
|
|
114
|
+
webq element.properties bp-button --path ./node_modules
|
|
115
|
+
webq element.methods bp-button --path ./node_modules
|
|
116
|
+
webq element.events bp-button --path ./node_modules
|
|
117
|
+
webq element.commands bp-button --path ./node_modules
|
|
118
|
+
webq element.slots bp-button --path ./node_modules
|
|
119
|
+
webq element.css-properties bp-button --path ./node_modules
|
|
120
|
+
webq element.css-parts bp-button --path ./node_modules
|
|
121
|
+
|
|
122
|
+
# List patterns, attributes, styles
|
|
123
|
+
webq pattern.list --path ./node_modules
|
|
124
|
+
webq attribute.list --path ./node_modules
|
|
125
|
+
webq style.property.list --path ./node_modules
|
|
126
|
+
|
|
127
|
+
# Validate manifest structure
|
|
128
|
+
webq validate-manifest --path .
|
|
129
|
+
webq validate-manifest --path ./node_modules
|
|
130
|
+
|
|
131
|
+
# Validate HTML against the manifest
|
|
132
|
+
webq validate-html '<bp-button variant="primary">Click</bp-button>' --path ./node_modules
|
|
133
|
+
|
|
134
|
+
# Validate HTML with JSON output (ESLint-compatible format)
|
|
135
|
+
webq validate-html '<bp-button unknown-attr></bp-button>' --path ./node_modules --json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### MCP Server
|
|
139
|
+
|
|
140
|
+
Start the MCP server on STDIO transport:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
webq mcp --path .
|
|
144
|
+
|
|
145
|
+
# Or scan all manifests in node_modules
|
|
146
|
+
webq mcp --path ./node_modules
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Setup with Claude Code
|
|
150
|
+
|
|
151
|
+
Run the setup commands to configure the MCP server and Claude Code skill:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Add webq MCP server to .mcp.json
|
|
155
|
+
webq setup-mcp
|
|
156
|
+
|
|
157
|
+
# Create Claude Code skill at .claude/skills/webq/SKILL.md
|
|
158
|
+
webq setup-skill
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Both commands are safe to run in existing projects. If the configuration already exists, they will notify you and offer a `--force` flag to update.
|
|
162
|
+
|
|
163
|
+
### Configure with Cursor
|
|
164
|
+
|
|
165
|
+
Add the following to your Cursor configuration `.cursor/mcp.json`:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"mcpServers": {
|
|
170
|
+
"webq": {
|
|
171
|
+
"command": "webq",
|
|
172
|
+
"description": "Search and query for Web Components APIs via Custom Elements Manifest",
|
|
173
|
+
"args": ["mcp", "--path", "./node_modules"]
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## MCP Tools
|
|
180
|
+
|
|
181
|
+
The server exposes the following tools:
|
|
182
|
+
|
|
183
|
+
| Tool | Description | Parameters |
|
|
184
|
+
| ---------------------------- | -------------------------------------------- | ----------------- |
|
|
185
|
+
| `element_get_list` | List all custom elements with tag names | none |
|
|
186
|
+
| `element_get` | Get full details for a specific element | `tagName: string` |
|
|
187
|
+
| `element_get_attributes` | Get attributes for an element | `tagName: string` |
|
|
188
|
+
| `element_get_properties` | Get properties/fields for an element | `tagName: string` |
|
|
189
|
+
| `element_get_methods` | Get methods with parameters and return types | `tagName: string` |
|
|
190
|
+
| `element_get_events` | Get events for an element | `tagName: string` |
|
|
191
|
+
| `element_get_commands` | Get invoker commands for an element | `tagName: string` |
|
|
192
|
+
| `element_get_slots` | Get slots for an element | `tagName: string` |
|
|
193
|
+
| `element_get_css_properties` | Get CSS custom properties | `tagName: string` |
|
|
194
|
+
| `element_get_css_parts` | Get CSS parts | `tagName: string` |
|
|
195
|
+
| `pattern_get_list` | List all compositional patterns | none |
|
|
196
|
+
| `pattern_get` | Get full details for a specific pattern | `name: string` |
|
|
197
|
+
| `attribute_get_list` | List all custom attributes | none |
|
|
198
|
+
| `attribute_get` | Get details for a custom attribute | `name: string` |
|
|
199
|
+
| `style_property_list` | List all CSS custom properties | none |
|
|
200
|
+
| `style_property_get` | Get details for a CSS custom property | `name: string` |
|
|
201
|
+
| `validate_html` | Validate HTML against CEM definitions | `html: string` |
|
|
202
|
+
|
|
203
|
+
## MCP Resources
|
|
204
|
+
|
|
205
|
+
The server exposes the following resources:
|
|
206
|
+
|
|
207
|
+
| Resource | URI Pattern | Description |
|
|
208
|
+
| -------- | ----------------- | ------------------ |
|
|
209
|
+
| Manifest | `webq://manifest` | Full manifest JSON |
|
|
210
|
+
|
|
211
|
+
## HTML Validation
|
|
212
|
+
|
|
213
|
+
The `validate-html` command and `validate_html` MCP tool check HTML against the Custom Elements Manifest, running 18 validation rules:
|
|
214
|
+
|
|
215
|
+
| Rule | Severity | Description |
|
|
216
|
+
| -------------------------------- | -------- | -------------------------------------------------------------------------------------------------------- |
|
|
217
|
+
| `no-unknown-element` | error | Custom elements must exist in the manifest |
|
|
218
|
+
| `no-unknown-attr` | error | Attributes must be defined (global attributes like `class`, `id`, `data-*`, `aria-*` are always allowed) |
|
|
219
|
+
| `no-unknown-attr-value` | error | Values must match string literal union types (e.g., `'primary' \| 'secondary'`) |
|
|
220
|
+
| `no-unknown-event` | error | Event bindings (`@event`, `(event)`, `on-event`, `onevent`) must match defined events |
|
|
221
|
+
| `no-unknown-slot` | error | Slot attribute values must match the parent element's defined slots |
|
|
222
|
+
| `no-unknown-command` | error | Command/commandfor pairs must match the target element's defined commands |
|
|
223
|
+
| `no-unknown-css-part` | error | `::part()` selectors in `<style>` must match defined CSS parts |
|
|
224
|
+
| `no-unknown-css-custom-property` | error | CSS custom property declarations must match defined properties |
|
|
225
|
+
| `no-missing-required-child` | error | Pattern root elements must have all required children present |
|
|
226
|
+
| `no-missing-sibling-binding` | error | Sibling trigger/target pairs must have matching attribute bindings |
|
|
227
|
+
| `no-deprecated-element` | warn | Warn on usage of deprecated elements |
|
|
228
|
+
| `no-deprecated-attr` | warn | Warn on usage of deprecated attributes |
|
|
229
|
+
| `no-deprecated-event` | warn | Warn on usage of deprecated events |
|
|
230
|
+
| `no-deprecated-slot` | warn | Warn on usage of deprecated slots |
|
|
231
|
+
| `no-deprecated-command` | warn | Warn on usage of deprecated commands |
|
|
232
|
+
| `no-boolean-attr-value` | warn | Warn when boolean attributes have explicit values (e.g., `disabled="true"`) |
|
|
233
|
+
| `no-unknown-style-value` | warn | `var()` references must match custom styles or element CSS properties |
|
|
234
|
+
| `no-unknown-custom-attr-value` | warn | Custom attribute token/enum values must match definitions in `custom-attributes.json` |
|
|
235
|
+
|
|
236
|
+
### Output Formats
|
|
237
|
+
|
|
238
|
+
**Human-readable (default):**
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
1:12 error Unknown attribute "foo" on <bp-button>. Valid attributes: type, disabled, variant, size, loading no-unknown-attr
|
|
242
|
+
|
|
243
|
+
1 error(s), 0 warning(s)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**JSON (`--json` flag):**
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"messages": [
|
|
251
|
+
{
|
|
252
|
+
"ruleId": "no-unknown-attr",
|
|
253
|
+
"severity": 2,
|
|
254
|
+
"message": "Unknown attribute \"foo\" on <bp-button>. Valid attributes: type, disabled, variant, size, loading",
|
|
255
|
+
"line": 1,
|
|
256
|
+
"column": 12
|
|
257
|
+
}
|
|
258
|
+
],
|
|
259
|
+
"errorCount": 1,
|
|
260
|
+
"warningCount": 0
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Custom Elements Manifest
|
|
265
|
+
|
|
266
|
+
This tool works with the [Custom Elements Manifest](https://github.com/webcomponents/custom-elements-manifest) format, a JSON schema for documenting Web Components. Example:
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"schemaVersion": "1.0.0",
|
|
271
|
+
"modules": [
|
|
272
|
+
{
|
|
273
|
+
"kind": "javascript-module",
|
|
274
|
+
"path": "src/button.js",
|
|
275
|
+
"declarations": [
|
|
276
|
+
{
|
|
277
|
+
"kind": "class",
|
|
278
|
+
"name": "MyButton",
|
|
279
|
+
"tagName": "my-button",
|
|
280
|
+
"attributes": [{ "name": "disabled", "type": { "text": "boolean" } }],
|
|
281
|
+
"members": [{ "kind": "field", "name": "value" }],
|
|
282
|
+
"events": [{ "name": "change" }],
|
|
283
|
+
"slots": [{ "name": "", "description": "Default slot" }],
|
|
284
|
+
"cssProperties": [{ "name": "--button-color" }],
|
|
285
|
+
"cssParts": [{ "name": "base" }]
|
|
286
|
+
}
|
|
287
|
+
]
|
|
288
|
+
}
|
|
289
|
+
]
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Custom Patterns Manifest
|
|
294
|
+
|
|
295
|
+
WebQ supports an optional companion file `custom-patterns.json` that defines compositional patterns — how custom elements are used together. This enables pattern querying and structural validation.
|
|
296
|
+
|
|
297
|
+
Place a `custom-patterns.json` alongside your `custom-elements.json` and WebQ will auto-discover it. Or set the path explicitly in config:
|
|
298
|
+
|
|
299
|
+
```json
|
|
300
|
+
{
|
|
301
|
+
"global": {
|
|
302
|
+
"path": ["./node_modules/@org/components"],
|
|
303
|
+
"patternsPath": "./custom-patterns.json"
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Patterns File
|
|
309
|
+
|
|
310
|
+
Patterns define structural rules (required children, sibling bindings) and usage examples:
|
|
311
|
+
|
|
312
|
+
```json
|
|
313
|
+
{
|
|
314
|
+
"schemaVersion": "1.0.0",
|
|
315
|
+
"patterns": [
|
|
316
|
+
{
|
|
317
|
+
"name": "form-field",
|
|
318
|
+
"description": "Standard form field with label and validation messaging.",
|
|
319
|
+
"tags": ["form", "input"],
|
|
320
|
+
"structure": {
|
|
321
|
+
"root": "bp-field",
|
|
322
|
+
"children": [
|
|
323
|
+
{ "rule": "required", "element": "label", "description": "Visible label" },
|
|
324
|
+
{ "rule": "oneOf", "options": ["bp-input", "bp-select", "bp-textarea"], "description": "Form control" },
|
|
325
|
+
{ "rule": "optional", "element": "bp-field-message", "description": "Validation message" }
|
|
326
|
+
]
|
|
327
|
+
},
|
|
328
|
+
"examples": [
|
|
329
|
+
{
|
|
330
|
+
"name": "text-input",
|
|
331
|
+
"html": "<bp-field>\n <label>Name</label>\n <bp-input required></bp-input>\n</bp-field>"
|
|
332
|
+
}
|
|
333
|
+
],
|
|
334
|
+
"relatedPatterns": ["form-group"]
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"name": "dialog-trigger",
|
|
338
|
+
"description": "A button that opens a dialog using the Popover API.",
|
|
339
|
+
"structure": {
|
|
340
|
+
"siblings": [
|
|
341
|
+
{
|
|
342
|
+
"trigger": { "tag": "bp-button", "attributes": [{ "name": "popovertarget", "required": true }] },
|
|
343
|
+
"target": { "tag": "bp-dialog", "attributes": [{ "name": "id", "required": true }] },
|
|
344
|
+
"bindings": [{ "triggerAttribute": "popovertarget", "targetAttribute": "id" }]
|
|
345
|
+
}
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
]
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Child Rules
|
|
354
|
+
|
|
355
|
+
| Rule | Description |
|
|
356
|
+
| ------------ | ------------------------------------------------------- |
|
|
357
|
+
| `required` | Child must be present |
|
|
358
|
+
| `optional` | Child may be present |
|
|
359
|
+
| `oneOf` | Exactly one of the listed `options` must be present |
|
|
360
|
+
| `oneOrMore` | At least one child of the given element must be present |
|
|
361
|
+
| `zeroOrMore` | Any number of children (including none) |
|
|
362
|
+
|
|
363
|
+
Elements can be a tag name string (`"label"`) or an object with constraints (`{ "tag": "*", "slot": "header" }`). Use `*` to match any tag.
|
|
364
|
+
|
|
365
|
+
### Sibling Bindings
|
|
366
|
+
|
|
367
|
+
Sibling rules define trigger/target pairs with attribute bindings. For example, a button's `popovertarget` must reference a dialog's `id`. The `no-missing-sibling-binding` validation rule checks these relationships.
|
|
368
|
+
|
|
369
|
+
## Building
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
# Install dependencies
|
|
373
|
+
bun install
|
|
374
|
+
|
|
375
|
+
# Build for current platform
|
|
376
|
+
bun run build
|
|
377
|
+
|
|
378
|
+
# Build for all platforms
|
|
379
|
+
bun run build:all
|
|
380
|
+
|
|
381
|
+
# Run tests
|
|
382
|
+
bun test
|
|
383
|
+
|
|
384
|
+
# Run linter
|
|
385
|
+
bun run lint
|
|
386
|
+
|
|
387
|
+
# Run full CI (format, lint, test, build)
|
|
388
|
+
bun run ci
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Development
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
# Run CLI directly via Bun
|
|
395
|
+
bun src/index.ts element.list --path ./testdata
|
|
396
|
+
|
|
397
|
+
# Debug MCP server with inspector
|
|
398
|
+
bun run debug
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## License
|
|
402
|
+
|
|
403
|
+
MIT
|