rlsbl 0.130.0 → 0.131.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/README.md +19 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Auto-generated by selfdoc from
|
|
1
|
+
<!-- Auto-generated by selfdoc from .strictmetadata/docs/_README.md — do not edit -->
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<img src="logo.svg" alt="rlsbl" width="336" height="105">
|
|
@@ -35,7 +35,7 @@ rlsbl watch <sha> # monitor CI for that release
|
|
|
35
35
|
|
|
36
36
|
## Commands
|
|
37
37
|
|
|
38
|
-
Release orchestration and project scaffolding for npm, PyPI, Go, and [
|
|
38
|
+
Release orchestration and project scaffolding for npm, PyPI, Go, and [1 more release targets](https://rlsbl.smmh.dev/targets).
|
|
39
39
|
|
|
40
40
|
All commands auto-detect targets (versioning) from project files (`package.json`, `pyproject.toml`, `go.mod`) and pipelines (publishing) from `.rlsbl/config.json`. Targets handle version bumps; pipelines handle where releases are published.
|
|
41
41
|
|
|
@@ -58,7 +58,7 @@ All commands auto-detect targets (versioning) from project files (`package.json`
|
|
|
58
58
|
| **release** | Release orchestration commands covering the full release lifecycle. Provides 12 subcommands: run, resume, init, retry, edit, undo, abandon, deprecate, yank, scrub, backfill, reconcile. |
|
|
59
59
|
| `release run` | Bump version, validate the JSONL changelog, run tests and lint, commit, tag, push, and create a GitHub Release. Reads the bump type (patch, minor, major, or infra) and target selection from .rlsbl/releases/unreleased.toml, which can be scaffolded with rlsbl release init. The release runs in the release checkout, a detached checkout of the release branch's committed tip under the repository's git directory: producers, tests, hooks, the version bump and the release commit all happen there, the branch advances only from the commit the release started at, and only the files the release's own commits change are written into the working tree. An uncommitted change to one of those files refuses the release, naming it; every other uncommitted change is listed and left alone. Supports dry-run preview, which reports those changes instead of refusing, and --approve-consequential to skip the confirmation prompt in non-interactive contexts. |
|
|
60
60
|
| `release resume` | Resume a previously failed release from where it left off. Reads the in-progress state file (.rlsbl/releases/in-progress.json, or .rlsbl-monorepo/releasables/<name>/releases/in-progress.json for releasable releases), validates that the current branch matches the saved state, and re-enters the release flow, skipping already-completed steps. Re-pins at the current branch tip, so the fix-forward commit and everything else committed while the release was stopped are adopted into it: the tip is pushed as the candidate and re-judged by CI, and the tag lands on what CI verified. An adopted commit the release did not create and the changelog does not describe is refused before any mutation, named with its subject, alongside the changelog add that records it. |
|
|
61
|
-
| `release init` | Scaffold a .rlsbl/releases/unreleased.toml file by auto-detecting project targets. The generated file contains a default bump type (patch)
|
|
61
|
+
| `release init` | Scaffold a .rlsbl/releases/unreleased.toml file by auto-detecting project targets. The generated file contains a default bump type (patch) and an include list of all detected targets. |
|
|
62
62
|
| `release retry` | Dispatch CI/CD workflows for a completed release via gh workflow run. Reads the dispatch list and ref from .rlsbl/releases/retry.toml, which is written with the release tag as the ref when missing. Verifies the GitHub Release exists before dispatching. Each workflow in the dispatch list is triggered at the release tag; a ref naming anything else, a branch included, is refused before anything is dispatched, because a run started there cannot be tied to the release. |
|
|
63
63
|
| `release edit` | Sync the GitHub Release notes for a given version with the corresponding CHANGELOG.md entry. Defaults to the current version if none is specified. Use --dry-run to preview changes without updating GitHub. |
|
|
64
64
|
| `release undo` | Revert a release. Without --version, reverts the latest release (deletes GitHub Release, removes git tag, reverts version bump commit). With --version, reverts a non-latest release if it is provably unpublished (probes registries for evidence, deletes GitHub Release + tag only, un-finalizes changelog). |
|
|
@@ -75,7 +75,7 @@ All commands auto-detect targets (versioning) from project files (`package.json`
|
|
|
75
75
|
| `changelog edit` | Modify an existing changelog entry in unreleased or released JSONL files. Finds the entry by commit hash or entry ID, applies field changes (type, description, user-facing status), and rewrites the file atomically. For released files, temporarily unlocks the read-only file, regenerates CHANGELOG.md, and syncs GitHub Release notes. |
|
|
76
76
|
| `changelog remove` | Delete one entry from a JSONL changelog file, selected by its ULID identifier or by the commits it covers. The file is rewritten atomically without that line; a released version's file is temporarily unlocked, re-locked, and followed by a CHANGELOG.md regeneration and a GitHub Release notes sync. Exactly one entry is removed: a selector matching several is refused with every match named, and a selector matching none is refused too. |
|
|
77
77
|
| `changelog remap` | Remap stale commit hashes in JSONL changelog files using a mapping of old SHAs to new SHAs. Reads the mapping from a file (--map-file), the safegit rewrite journal (--from-journal), or stdin (--stdin). At least one source is required. Auto-commits with Autogenerated trailer. |
|
|
78
|
-
| **monorepo** | Manage monorepo workspaces with multiple independently-versioned projects. Initialize workspaces, add or remove projects, sync CI workflows, check name availability, and analyze dependency graphs. Provides 17 monorepo subcommands: init, add, remove, list, sync, status, check-names, outdated, snapshot, snapshot-check, mirror, graph, impact, extract, absorb, cleanup, rename-releasable. Plus 1 subgroup: release. Supports all
|
|
78
|
+
| **monorepo** | Manage monorepo workspaces with multiple independently-versioned projects. Initialize workspaces, add or remove projects, sync CI workflows, check name availability, and analyze dependency graphs. Provides 17 monorepo subcommands: init, add, remove, list, sync, status, check-names, outdated, snapshot, snapshot-check, mirror, graph, impact, extract, absorb, cleanup, rename-releasable. Plus 1 subgroup: release. Supports all 4 release targets in a single workspace.toml (the app help enumerates them). |
|
|
79
79
|
| `monorepo init` | Create a new monorepo workspace by generating the .rlsbl-monorepo directory and a workspace.toml at the current directory, carrying the mandatory root member whose kind you declare and a [[releasables]] section. This must be run at the repository root before adding individual projects with the add subcommand. Each workspace tracks multiple independently-versioned projects that share a single git repository. |
|
|
80
80
|
| `monorepo add` | Register a project directory in the monorepo workspace.toml configuration. The path argument specifies the project's location relative to the repo root. Optional settings cover display name, target registry, inter-project dependencies, releasable membership, registry identity, and flags marking the project as a shared library or a dev-only leaf. A --releasable naming a group [[releasables]] does not declare yet creates it, as absorb creates one for an arriving member: a singleton entry whose tag_format is written out explicitly, derived from the member's primary target scheme unless --tag-format states it. The mirror destination is not among them: it is a releasable-level key, declared in workspace.toml beside the releasable it binds. What CI reacts to is not among them: the router's paths filters are derived from the workspace, never declared per project. After writing the entry the command scaffolds the member (unless it already has a .rlsbl/config.json) and runs monorepo sync, then commits what the three wrote as one commit, leaving uncommitted, and naming, a file that had uncommitted changes before the add; if the scaffold, the sync, or the commit fails, the command exits 1 with the working tree as it was before and nothing committed. |
|
|
81
81
|
| `monorepo remove` | Unregister a project from the monorepo workspace.toml by its path. This removes the project entry from the workspace configuration file but does not delete any files, directories, or git history on disk. The project's code remains intact and can be re-added later with the add subcommand if needed. The path must be a member's path as workspace.toml writes it; any other path is refused, naming every member's path. |
|
|
@@ -93,12 +93,16 @@ All commands auto-detect targets (versioning) from project files (`package.json`
|
|
|
93
93
|
| `monorepo absorb` | Absorb an external repository into this workspace as a releasable. The source's history is rewritten under the destination path and merged in (full history, rewritten paths), its version tags are imported under the destination's tag scheme with one boundary alias at the current version, and its whole release state -- changelog, release archives with their release commits, config and version -- moves into a releasable's state directory with every hash and release commit remapped onto the rewritten commits. Without --releasable a singleton releasable named after the member is created, with its tag_format written explicitly. Nothing is fetched as a tag, so a tag this repository already owns is never moved or deleted; a colliding tag name or version is refused before anything is written. A crashed run is completed by re-running it. Use --dry-run to see the whole plan first. |
|
|
94
94
|
| `monorepo cleanup` | Remove per-package release-state residue from releasable member packages: .rlsbl/changes/, .rlsbl/releases/, .rlsbl/bases/, .rlsbl/lint/, .rlsbl/version, per-package CHANGELOG.md, and .rlsbl/config.json when identical to the releasable-level config. Per-package hooks/ directories are preserved (live feature), and members whose path is the workspace root are exempt. Deletions go through saferm (audit trail, recoverable) and are committed automatically unless --no-auto-commit is passed. Detect residue first with `rlsbl check --name releasable-residue`. |
|
|
95
95
|
| `monorepo rename-releasable` | Rename a releasable group. Rewrites the [[releasables]] name and every member's releasable field in workspace.toml (preserving comments), moves the state directory, drops the stale changelog validation cache, re-runs monorepo sync, and commits it all as one commit. That commit carries no Autogenerated trailer, so changelog coverage decides whether it needs an entry: when tag_format contains {name} the commit changes the publish workflow and coverage asks for one, and the closing message prints the runnable rlsbl changelog add --type breaking line, changing into one of the releasable's members first; a name-only rename touches only files rlsbl owns, needs no entry, and the closing message says nothing about the changelog. When tag_format contains {name}, a boundary alias tag for the current version is created at the old tag's commit and pushed; historical releases stay under the old prefix, and each one's archive records that tag in shipped_as so reconcile and release edit/deprecate/yank resolve it there. Idempotent: re-running heals a crash between the commit and the tag push, and records shipped_as on the past releases of a releasable renamed before the rename recorded it. |
|
|
96
|
+
| **monorepo release** | Release commands for monorepo workspaces. Provides 3 subcommands: run (batch release), init (scaffold release file), order (topological release order). |
|
|
97
|
+
| `monorepo release run` | Execute a batch release of multiple monorepo packages in topological order. Reads package configurations from .rlsbl-monorepo/releases/unreleased.toml. Each package is released sequentially using the single-package release flow, with leaves (no dependencies) released first. The whole batch runs in the release checkout, a detached checkout of the release branch's committed tip, as rlsbl release run does: an uncommitted change to a path the batch writes (the workspace's release state, a member's version files, the workspace changelog) refuses it, naming the path, and every other uncommitted change is listed and left alone. Supports --dry-run, which reports those changes instead of refusing, and --approve-consequential. |
|
|
98
|
+
| `monorepo release init` | Scaffold a batch release file for the workspace's releasables by auto-detecting each releasable's release targets and generating one configuration section per releasable. Creates .rlsbl-monorepo/releases/unreleased.toml with a [releasables.<name>] section for each releasable declared in workspace.toml, carrying an empty bump type and description for you to fill in and the detected include list. A releasable with no unreleased commits since its last tag is rendered as a commented-out section; one with no members or no detected targets is skipped with a warning. |
|
|
99
|
+
| `monorepo release order` | Compute and display the topological release order for all projects in the monorepo workspace based on their declared depends-on relationships. Projects with no dependencies are listed first, followed by projects that depend on them, ensuring each project is released only after its dependencies. Detects and reports circular dependency errors. |
|
|
96
100
|
| **dev** | Developer utilities for locally working with rlsbl projects, including editable installs that mirror the project's release target (pypi -> uv tool install -e, npm -> npm link, go -> go install). |
|
|
97
|
-
| `dev install` | Install the project locally for development by running each detected target's own install command. --target is required and names the install mode: global installs onto the machine (pypi via `uv tool install -e`, npm via `npm link`), venv installs into the project's local environment instead; a target that does not support the chosen mode is skipped with a reason. --target global is supported by
|
|
101
|
+
| `dev install` | Install the project locally for development by running each detected target's own install command. --target is required and names the install mode: global installs onto the machine (pypi via `uv tool install -e`, npm via `npm link`), venv installs into the project's local environment instead; a target that does not support the chosen mode is skipped with a reason. --target global is supported by 3 targets: npm, pypi, go. --target venv is supported by 2 targets: npm, pypi. --uninstall reverses a previous install on 2 targets: npm, pypi. In monorepo mode, pair with --all, --include, or --exclude. |
|
|
98
102
|
| `dev sync` | Overlay local editable checkouts of sibling projects onto this project's locked environment. Reads dev-sources.toml.local-only for overlay entries, runs uv sync --inexact excluding overlaid packages, then uv pip install -e per entry. Requires UV_NO_SYNC=1 in the environment to prevent bare uv run from reverting overlays. |
|
|
99
103
|
| `dev status` | Report the state of local dev-sync overlays: for each package recorded in the dev-overlays sentinel, show its declared editable checkout path and version alongside the venv's actual install (editable at the expected path, WIPED back to a registry wheel, or missing entirely). Exits 1 if any overlay drifted so scripts and pre-run guards can detect a silent wipe by a bare uv sync or uv run; exits 0 when all overlays are intact or none are declared. |
|
|
100
104
|
| **rewrite** | Sweeping rewrites of the current working tree, each previewed before it is performed. Every command in this group observes the tree, reports a per-file plan with occurrence counts, and refuses to apply when a count moved between the preview and the write. |
|
|
101
|
-
| `rewrite go-module-path` | Rename one Go module path across the repository. Rewrites the module-path tokens in every go.mod (the module directive plus any require, replace, exclude or retract reference from another module) and every Go import site under the old path, located by the tree-sitter import scanner and rewritten line-scoped. A module nested under the old path with a go.mod of its own is another module: its path and its packages' imports are left alone, since the longest declared module path owns every token and import, and it is renamed by an invocation of its own. The committed strictcli schema dump moves with it: the project_id line in every .
|
|
105
|
+
| `rewrite go-module-path` | Rename one Go module path across the repository. Rewrites the module-path tokens in every go.mod (the module directive plus any require, replace, exclude or retract reference from another module) and every Go import site under the old path, located by the tree-sitter import scanner and rewritten line-scoped. A module nested under the old path with a go.mod of its own is another module: its path and its packages' imports are left alone, since the longest declared module path owns every token and import, and it is renamed by an invocation of its own. The committed strictcli schema dump moves with it: the project_id line in every .strictmetadata/.cli-schema/schema.json under the old module path is rewritten too, so the committed help document names the module the program now reports in `help --json`, not the old one. Containment is boundary-aware, so a neighboring module whose path merely begins with the same letters is left alone. The files considered are what `git ls-files --cached --others --exclude-standard` lists: tracked files plus untracked files that are not ignored, so a gitignored third-party clone is never touched, and neither are comments, vendored trees, or any other kind of file. Use --dry-run to print the per-file plan with occurrence counts. |
|
|
102
106
|
| `rewrite project-name` | Rename a standalone project's published identity. Rewrites the package name in every target manifest whose target renames it (npm package.json "name", PyPI pyproject.toml [project].name), located through the targets' configured paths, and for a Go target moves the module path to one whose last element is --to, through the same rewrite as `rlsbl rewrite go-module-path`. Records one identity-transition event per changed identity in .rlsbl/transitions.jsonl: package-name from --from to --to, and go-module-path from the module path the latest release published (read from go.mod at that release's commit) to the new one. The effective version is the version the next release ships: the current version plus the bump in .rlsbl/releases/unreleased.toml, or the current version as-is for a project that has never released. The rename and the record are committed separately, so a crash between them is completed by re-running; the record commit carries the Autogenerated trailer, and the rename commit does not, so changelog coverage asks for the breaking entry the closing message prints. Nothing else is touched: no source directory is moved, no command name (npm bin, PyPI [project.scripts]) is renamed, no registry is contacted, and no repository is renamed; the closing message lists those remaining steps in order. Refuses, before writing anything, inside a monorepo workspace, when --from is not the name the manifests declare, when --from equals --to or --to is not a valid package name for a target it renames, when a target rlsbl does not rename still declares --from, when the release file is missing, when the next version cannot be decided the way `rlsbl release run` decides it (version files naming a version the release record holds nothing for once a release exists, version files behind the latest release, or a next version the record holds as never released; version files naming an unrecoverable version, which the release refuses until its tag is restored, are bumped from instead), when there is a Go target and the latest release's archive is marked unrecoverable (rlsbl's record cannot establish the module path that release published), and on a dirty working tree. Use --dry-run to print the per-file plan with occurrence counts and the events it would record. |
|
|
103
107
|
| `rewrite uv-path-sources` | Convert path- and workspace-sourced Python dependencies into registry constraints floored at the version uv.lock resolves. Covers [project].dependencies, every [project.optional-dependencies] extra and every PEP 735 [dependency-groups] group, and deletes the matching [tool.uv.sources] entry so it stops overriding the new constraint. Each converted name is added to internal_dep_floors in .rlsbl/config.json, and the rlsbl:dep-floors option that key belongs to is switched on with an options entry (current and ideal error, scoped to the project when it is a workspace member) when it is off. A locked version that is not published on PyPI is a hard error naming the remedy (release that dependency first), and so is a registry probe that fails to answer. Use --dry-run to print the per-dependency plan with entry counts. |
|
|
104
108
|
| **transition** | Record the transition-record facts an operator states. Most events in a repository's transition record are written by the operation that performed them; the ones here are statements about a repository somebody read -- two that nothing can derive at all, and one whose command exists but which a rename performed by hand leaves unrecorded. |
|
|
@@ -121,7 +125,7 @@ description and the target selection, then:
|
|
|
121
125
|
1. Verifies `gh` auth, refuses an uncommitted change to any path the release writes (naming it; every other uncommitted change is listed and left alone), computes the new version and confirms its tag does not exist, and enters the release checkout: a detached checkout of the branch tip under `.git/rlsbl/release-checkout`, where every step below runs, so nothing the release builds, tests or commits reads the working tree
|
|
122
126
|
2. Validates the JSONL changelog and regenerates CHANGELOG.md from it
|
|
123
127
|
3. Runs `.rlsbl/hooks/pre-checks.sh` (user-owned), the strictcli schema dump, `selfdoc gen` and `selfdoc check`, the built-in tests and lint, and `.rlsbl/hooks/pre-release.sh` (scaffold-managed) -- any non-zero aborts
|
|
124
|
-
4. Writes the new version to every detected target file and `.rlsbl/version`, commits it with the tag string as the message, advances the branch to that commit by compare-and-swap (writing into the working tree only the files it changed), and pushes it **untagged**: the release candidate
|
|
128
|
+
4. Writes the new version to every detected target file (and its own rlsbl version to `.rlsbl/version`, which names the rlsbl that last scaffolded or released the project, not the project's version), commits it with the tag string as the message, advances the branch to that commit by compare-and-swap (writing into the working tree only the files it changed), and pushes it **untagged**: the release candidate
|
|
125
129
|
5. Waits in-process for the repository's own push-triggered CI to conclude on that exact commit
|
|
126
130
|
6. Finalizes the changelog (renames `unreleased.jsonl` to the version's file, opens a fresh one, regenerates CHANGELOG.md), archives the release file, tags the **CI-verified commit**, pushes the finalization commits and the tags, and creates the GitHub Release with the version's changelog section as notes
|
|
127
131
|
7. Uploads assets, runs each pipeline's `publish` (configured in `.rlsbl/config.json`), deploys, runs `.rlsbl/hooks/post-release.sh` (non-fatal), and prints `Watch CI: rlsbl watch <sha>`
|
|
@@ -133,7 +137,7 @@ release branch and `rlsbl release resume` completes the *same* version; a failed
|
|
|
133
137
|
release never burns it.
|
|
134
138
|
|
|
135
139
|
The step-by-step pipeline, including what each step does in monorepo and releasable
|
|
136
|
-
mode, is in [
|
|
140
|
+
mode, is in [.strictmetadata/docs/release-workflow.md](.strictmetadata/docs/release-workflow.md).
|
|
137
141
|
|
|
138
142
|
Use `--dry-run` to preview without changes: mutating operations are recorded and printed as a
|
|
139
143
|
would-do log rather than performed. A small set of commands declares itself `consequential`
|
|
@@ -155,7 +159,6 @@ Pre-release versions (e.g. `1.0.0-beta.1`) are supported.
|
|
|
155
159
|
|
|
156
160
|
```
|
|
157
161
|
rlsbl scaffold # create or update CI/CD for all detected registries
|
|
158
|
-
rlsbl scaffold --target plain # also cover a registry auto-detection cannot find
|
|
159
162
|
rlsbl scaffold --no-auto-commit # skip auto-commit of scaffolded files
|
|
160
163
|
rlsbl scaffold --no-auto-tag # skip the rlsbl GitHub topic tag on this run
|
|
161
164
|
```
|
|
@@ -186,30 +189,29 @@ Created files are committed automatically by default.
|
|
|
186
189
|
- `.github/workflows/ci-custom.yml` -- runs alongside `ci.yml`
|
|
187
190
|
- `.github/workflows/publish-custom.yml` -- runs alongside `publish.yml`
|
|
188
191
|
|
|
189
|
-
See [
|
|
192
|
+
See [.strictmetadata/docs/ci-customization.md](.strictmetadata/docs/ci-customization.md) for an example.
|
|
190
193
|
|
|
191
194
|
**Runs config migrations** when `.rlsbl/config-schema.json` exists.
|
|
192
195
|
|
|
193
196
|
## Check system
|
|
194
197
|
|
|
195
|
-
rlsbl includes
|
|
198
|
+
rlsbl includes 93 checks across 8 tags.
|
|
196
199
|
|
|
197
200
|
Checks are grouped by tag -- `--tag` runs one family, `--name` runs a single check, and `--all` runs everything, including the checks that carry no tag:
|
|
198
201
|
|
|
199
202
|
| Tag | Checks |
|
|
200
203
|
| --- | --- |
|
|
201
|
-
| `preflight` |
|
|
204
|
+
| `preflight` | 30 |
|
|
202
205
|
| `project` | 30 |
|
|
203
|
-
| `workspace` |
|
|
204
|
-
| `quality` |
|
|
206
|
+
| `workspace` | 24 |
|
|
207
|
+
| `quality` | 15 |
|
|
205
208
|
| `changelog` | 10 |
|
|
206
209
|
| `preflight-changelog` | 9 |
|
|
207
210
|
| `release` | 8 |
|
|
208
211
|
| `prepush` | 6 |
|
|
209
|
-
| `maven` | 1 |
|
|
210
212
|
| (untagged) | 4 |
|
|
211
213
|
|
|
212
|
-
What each tag's checks actually verify, one row per check with its severity, is in [
|
|
214
|
+
What each tag's checks actually verify, one row per check with its severity, is in [.strictmetadata/docs/checks.md](.strictmetadata/docs/checks.md), which also says which tags the release pipeline runs on its own.
|
|
213
215
|
|
|
214
216
|
```
|
|
215
217
|
rlsbl check --all # run all checks
|
|
@@ -308,6 +310,7 @@ Supports architectural layer rules via `[layers]` in `workspace.toml` for enforc
|
|
|
308
310
|
| Variable | Default | Description |
|
|
309
311
|
|----------|---------|-------------|
|
|
310
312
|
| `RLSBL_VERSION` | -- | Set when running pre-release and post-release hooks; contains the version being released |
|
|
313
|
+
| `RLSBL_RELEASE_BIN` | -- | Set for every hook and step of a release; the release's own directory for binaries, first on `PATH`, where a hook can build a tool the release must run in its unreleased form |
|
|
311
314
|
| `RLSBL_DIST_DIR` | -- | Set when running `custom_assets` build commands; points to the distribution directory for output files |
|
|
312
315
|
| `GITHUB_TOKEN` | -- | Used by `gh` CLI for GitHub API calls; `discover` works unauthenticated for public repos |
|
|
313
316
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rlsbl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.131.0",
|
|
4
4
|
"description": "Release orchestration and project scaffolding CLI that bumps versions, validates a structured JSONL changelog, tags only the commit CI verified, and publishes to npm, PyPI, Go and more",
|
|
5
5
|
"homepage": "https://smmh.dev/rlsbl/",
|
|
6
6
|
"license": "MIT",
|