Radish alpha
r
rad:z3trNYnLWS11cJWC6BbxDs5niGo82
Radicle Improvement Proposals (RIPs)
Radicle
Git
RIP X: URI scheme
Open levitte opened 8 months ago

Building on the work that Lorenz Leutgeb did in patch rad:z3trNYnLWS11cJWC6BbxDs5niGo82/patch/19ba16634fbde97156255ed97069d0aa3ab2bf12, the URI scheme is brought to a final RIP document, with full ABNF and Nearley grammars as well as examples, and a couple of scripts to extract and test those grammars against those examples.

Compared to the URL scheme in RIP 3, this RIP brings a subtly different URL form, while also supporting the RIP 3 scheme (now considered legacy). In short, these two forms are supported by this RIP:

  • rad://{rid}/{nid} (considered legacy)
  • rad://{rad-auth}/{rid}{object-and-query}

(rad-auth is the authority part, which can take the form of {nid}@{host-and-port} or parts thereof)

This works, because the {rid} and the {nid} are distinguishable, as a {nid} always starts with z6Mk, while a {rid} does start with a z, but never with z6Mk, and they have different lengths (a {rid} is always 28 or 29 characters long, which a {nid} is always 48 characters long).

The test scripts are found in tests/0004-uri-scheme

65 files changed +4943 -4 7e993a9b bdadd44d
added .gitignore
@@ -0,0 +1,2 @@
+
node_modules/
+
result
added .linkspector.yml
@@ -0,0 +1,8 @@
+
dirs:
+
  - .
+
useGitIgnore: true
+
ignorePatterns:
+
  - pattern: "^https?://.*$"
+
  - pattern: "^web\\+rad:.*$"
+
  - pattern: "^rad:.*$"
+
  - pattern: "^vscode:.*$"
modified 0003-storage-layout.md
@@ -265,7 +265,7 @@ specify the namespace that is being used for the operation. This can be
achieved using `git --namespace=<nid>` or `GIT_NAMESPACE=<nid> git`.
Unfortunately, this is somewhat cumbersome for the user and does not prevent
pushing to namespaces belonging to a non-local peer. This is remedied in
-
[Remote Helper](#Remote-Helper).
+
[Remote Helper](#remote-helper).

### Example

@@ -286,14 +286,14 @@ namespace, a configuration like the following can be used:
In the above configuration, `git pull rad` would pull the canonical references
while `git push rad` would push to the local user's namespace.

-
For a more thorough example, see the [Appendix](#Appendix).
+
For a more thorough example, see the [Appendix](#appendix).

### Remote Helper

The remote helper is what allows Git to interpret URLs with the `rad://`
scheme.

-
As mentioned in the [Working Copy](#Working-Copy) section, there is currently
+
As mentioned in the [Working Copy](#working-copy) section, there is currently
no way to configure a Git remote to be aware of additional logic, such as the
appropriate `refs/namespaces` to use (to avoid having to use `--namespace`) or
to prevent pushing to other peers' namespaces.
@@ -318,7 +318,7 @@ is specified in the URL, as no authorization is required to fetch.

Future Work
-----------
-
You may have noticed that in this [layout](#Layout) the top-level namespace
+
You may have noticed that in this [layout](#layout) the top-level namespace
is left for canonical references. The definition and verification of canonicity
is left for a future RIP.

added 000X-general-uri-scheme/data/rad-uri.abnf
@@ -0,0 +1,208 @@
+
; This grammar is written using ABNF, as defined in RFC 5234
+
; (https://datatracker.ietf.org/doc/html/rfc5234), from which ALPHA, DIGIT
+
; and HEXDIG are borrowed
+
;
+
; Furthermore, this grammar is based on and specialises the generic URI
+
; specification (https://datatracker.ietf.org/doc/html/rfc3986) for Radicle,
+
; from which 'query', 'fragment', 'host', 'port', 'unreserved', 'pchar', ...
+
; are borrowed
+

+
; tag::overview[]
+
; RFC 3986 defines the syntax for 'query' and 'fragment'
+
rad                     = rad-scheme ":" rad-auth-and-resource rad-resource-type-and-identity [ "?" query ] [ "#" fragment ]
+
                        / rad-scheme ":" rad-legacy
+

+
rad-scheme              = "rad"
+

+
; RFC 3986 allows an absolute resource path without authority, i.e. a URI
+
; that looks like this: rad:/{RID}/{NID}
+
; We don't include that sort of URI in this scheme, because it's, strictly
+
; speaking, unnecessary in this context, and may seem confusing.
+
rad-auth-and-resource   = "//" [ rad-auth ] "/" rad-resource    ; abs. resource "path" with authority
+
                        / rad-resource                          ; rel. resource "path"
+

+
; rad-legacy are URLs defined by RIP #3.  That URL doesn't quite hold up
+
; against the general URI syntax defined in RFC3986, in so far that a node
+
; (identified with NID or NID@host) is the conceptual authority, not
+
; a repository.
+
; To be noted is that RIDs and NIDs are distinct enough that there should not
+
; be any ambiguity between rad-auth-and-resource and rad-legacy.
+
; Of all current implementations, this mostly impacts the git remote helper
+
; git-remote-rad.
+
rad-legacy              = "//" rad-resource
+
; end::overview[]
+

+
; The NID is conceptually ambiguous from an RFC 3986 syntax perspective:
+
;
+
;       authority   = [ userinfo "@" ] host [ ":" port ]
+
;
+
; Is the NID 'userinfo' or 'host'?  In a sense, it currently is a little bit
+
; of both.  Furthermore, Radicle requires that if a hostname (+ port) is
+
; given, the NID must be given too.  Therefore, instead of using 'authority'
+
; strictly as given by RFC 3986, we allow ourselves a slightly different
+
; syntax, which is fine.  Do note also, that a 'host' can still be made to
+
; look like an NID, so programs dealing directly with the Radicle network
+
; will have to be prepared to handle that ambiguity appropriately.
+
; tag::authority[]
+
;
+
; TODO: There already are ways to resolve its NID from a hostname.
+
; For example, if `radicle-httpd` runs next to the node, then it can be
+
; queried for its NID via HTTPS. Also, there are ways to use DNS TXT records,
+
; which is also done for DNS-SD.
+
; We would therefore likely make the `rad-node` optional, or even omit it.
+
; If `rad-node` is sufficiently different from a DNS name (it probably is
+
; not), then we can even omit "@".
+
rad-auth                = rad-node [ "@" rad-host-and-port ]
+

+
; TODO: Port might be optional in the future, once we have convinced IANA
+
; to assign one to us. There currently is no default port.
+
rad-host-and-port       = host ":" port
+

+
; rad-node is a semantic symbol, to signify intent
+
rad-node                = rad-nid
+

+
; An NID is an ID that is ultimately a public key in the Radicle network.
+
; This is used to uniquely represent a node, or a user (also a node), or a
+
; namespace within a repository.
+
; It is a [multibase] and [multicodec] encoded string, using [base58btc] as the multibase
+
; and the codec for the underlying Ed25519 public key.
+
;
+
; It is always 48 characters long, including the initial "z6Mk",
+
; the `z` corresponding to the base, and `6Mk` corresponding to the codec.
+
;
+
; [base58btc]: https://digitalbazaar.github.io/base58-spec/
+
; [multibase]: https://www.ietf.org/archive/id/draft-multiformats-multibase-07.html
+
; [multicodec]: https://github.com/multiformats/multicodec/
+
;
+
; TODO: New NIDs in the future? Even this might change, as we allow rotating
+
; keys for nodes. But this is even further out than stable IDs for users.
+
rad-nid                 = "z6Mk" 44( base58btc )
+
; end::authority[]
+

+
; tag::resource[]
+
rad-resource            = rad-repository [ "/" rad-namespace ]
+

+
; rad-repository is a semantic symbol, to signify intent
+
rad-repository          = rad-rid
+

+
; rad-namespace is a semantic symbol, to signify intent
+
rad-namespace           = rad-nid
+

+
; An RID uniquely represents a git repository in the Radicle network.
+
; It is a [multibase] encoded string of a Git OID, using [base58btc] as the multibase.
+
;
+
; It is 28 or 29 characters long, including the initial multibase identifier "z".
+
;
+
; [base58btc]: https://digitalbazaar.github.io/base58-spec/
+
; [multibase]: https://www.ietf.org/archive/id/draft-multiformats-multibase-07.html
+
;
+
; TODO: In the near future (as soon as Git repositories with SHA-256 object
+
; format become popular, which will be in late 2026), we will have to extend
+
; the syntax of RIDs. Leave a note here so that implementors know this and
+
; make design decisions accordingly.
+
rad-rid                 = "z" 27*28( base58btc )
+
; end::resource[]
+

+
; tag::resource-type-and-identity[]
+
; 'rad-resource-type-and-identity' is empty by default, and more resource
+
; types and identities will be added further on, using the ABNF =/ operator,
+
; see RFC 5234 "3.3. Incremental Alternatives: Rule1 =/ Rule2"
+
rad-resource-type-and-identity = ""
+

+
; tag::git-type-and-identity[]
+
; For git specific resource types
+
rad-resource-type-and-identity =/ git-commit / git-tree / git-blob / git-tag
+

+
git-commit              = "/" "commit" "/" git-obj-or-ref
+
git-tree                = "/" "tree" "/" git-obj
+
git-blob                = "/" "blob" "/" git-obj
+
git-tag                 = "/" "tag" "/" git-obj-or-ref
+

+
; Note: this rule will always be ambiguous, as something that looks like a
+
; git object ID could very well be a branch or a tag.
+
; Some git commands will warn when such refs are used, but not all.
+
git-obj-or-ref          = git-obj / git-ref
+
; Currently, only sha1 identities are supported.
+
git-obj                 = git-sha1
+
; HEXDIG is defined in RFC 5234
+
git-sha1                = 40HEXDIG
+

+
; unreserved is defined in RFC 3986.  Not that this syntax allows much
+
; more than what can be specified for a git reference.  It's left to
+
; the implementation to parse and apply semantics properly.
+
; Our best reference for how to interpret and parse git references is
+
; https://git-scm.com/docs/protocol-common.html
+
git-ref                 = 1*unreserved *( "/" 1*unreserved )
+
; end::git-type-and-identity[]
+

+
; tag::rad-cob-type-and-identity[]
+
; For Radicle collaborative object resource types
+
rad-resource-type-and-identity =/ rad-cob / rad-cobs
+

+
rad-cob                 = "/" "cob" "/" rad-cob-type "/" rad-cob-obj
+
rad-cobs                = "/" "cob" "/" rad-cob-type
+

+
; The COB type is like a reversed FQDN.  The syntax spec is derived from
+
; https://datatracker.ietf.org/doc/html/rfc1034#section-3.5
+
rad-cob-type            = rad-cob-label 1*( "." rad-cob-label )
+
rad-cob-label           = 1*( ALPHA / DIGIT ) *( "-" 1*( ALPHA / DIGIT ) )
+

+
; syntactically, radicle COB objects and object revisions are git objects
+
; semantically, they are as well, at least for now
+
rad-cob-obj             = git-obj
+
; end::rad-cob-type-and-identity[]
+
; end::resource-type-and-identity[]
+

+
; tag::rfc5234[]
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
; Copied from RFC 5234
+

+
ALPHA          =  %x41-5A / %x61-7A     ; A-Z / a-z
+
DIGIT          =  %x30-39               ; 0-9
+
HEXDIG         =  DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
+

+
; Copied from RFC 3986
+
host          = IP-literal / IPv4address / reg-name
+
port          = *DIGIT
+

+
IP-literal    = "[" ( IPv6address / IPvFuture  ) "]"
+

+
IPvFuture     = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
+

+
IPv6address   =                            6( h16 ":" ) ls32
+
              /                       "::" 5( h16 ":" ) ls32
+
              / [               h16 ] "::" 4( h16 ":" ) ls32
+
              / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
+
              / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
+
              / [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
+
              / [ *4( h16 ":" ) h16 ] "::"              ls32
+
              / [ *5( h16 ":" ) h16 ] "::"              h16
+
              / [ *6( h16 ":" ) h16 ] "::"
+

+
h16           = 1*4HEXDIG
+
ls32          = ( h16 ":" h16 ) / IPv4address
+
IPv4address   = dec-octet "." dec-octet "." dec-octet "." dec-octet
+
dec-octet     = DIGIT                 ; 0-9
+
              / %x31-39 DIGIT         ; 10-99
+
              / "1" 2DIGIT            ; 100-199
+
              / "2" %x30-34 DIGIT     ; 200-249
+
              / "25" %x30-35          ; 250-255
+

+
reg-name      = *( unreserved / pct-encoded / sub-delims )
+

+
pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
+

+
query         = *( pchar / "/" / "?" )
+

+
fragment      = *( pchar / "/" / "?" )
+

+
pct-encoded   = "%" HEXDIG HEXDIG
+

+
unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
+
sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
+
              / "*" / "+" / "," / ";" / "="
+
; end::rfc5234[]
+

+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
; Base58 Bitcoin Alphabet
+
base58btc = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

\ No newline at end of file
added 000X-general-uri-scheme/data/rad-uri.ne
@@ -0,0 +1,245 @@
+
@{%
+
    const lstr = function(d) { return d.flat(5).join(''); }
+
%}
+

+
# In the ABNF grammar, 'rad-resource-type-and-identity' defaults to empty,
+
# which effectively renders it optional.  Nearley doesn't allow empty rules,
+
# and multiple rules with the same name can be added anyway, so we make
+
# resource_type_and_identity optional here to get the same essential semantics.
+
rad                     -> scheme ":" auth_and_resource resource_type_and_identity:?
+
                           ( "?" rad_query ):? ( "#" fragment ):?
+
                           {% ([s, , aar, rtai, q, f]) =>
+
                               ({ scheme: s,
+
                                  ...aar,
+
                                  ...(rtai ? { resource: rtai} : {}),
+
                                  ...(q ? { query: q[1]} : {}),
+
                                  ...(f ? { fragment: f[1]} : {}) }) %}
+
                         | scheme ":" legacy
+
                           {% ([s, , l]) => ({ scheme: s, ...l }) %}
+

+
scheme                  -> "rad" {% lstr %}
+

+
auth_and_resource       -> "//" auth:? "/" resource             # absolute resource "path"
+
                           {% ([, auth = {}, , res]) => ({ ...auth, ...res }) %}
+
                         | resource                             # relative resource "path"
+
                           {% ([res]) => res %}
+

+
# 'legacy' are URLs defined by RIP #3.  That URL doesn't quite hold up
+
# against the general URI syntax defined in RFC3986, in so far that a node
+
# (identified with NID or NID@host) is the conceptual authority, not
+
# a repository.
+
# To be noted is that RIDs and NIDs are distinct enough that there shouldn't
+
# be any ambiguity between rad-auth-and-resource and rad-legacy.
+
# Of all current implementations, this mostly impacts the git remote helper
+
# git-remote-rad.
+
legacy                  -> "//" resource
+
                           {% ([, res]) => res %}
+

+
# The NID is conceptually ambiguous from an RFC 3986 syntax perspective:
+
#
+
#       authority   = [ userinfo "@" ] host [ ":" port ]
+
#
+
# Is the NID 'userinfo' or 'host'?  In a sense, it currently is a little bit
+
# of both.  Furthermore, Radicle requires that if a hostname (+ port) is
+
# given, the NID must be given too.  Therefore, instead of using 'authority'
+
# strictly as given by RFC 3986, we allow ourselves a slightly different
+
# syntax, which is fine.  Do note also, that a 'host' can still be made to
+
# look like an NID, so programs dealing directly with the Radicle network
+
# will have to be prepared to handle that ambiguity appropriately.
+
#
+
# TODO: There already are ways to resolve its NID from a hostname.
+
# For example, if `radicle-httpd` runs next to the node, then it can be
+
# queried for its NID via HTTPS. Also, there are ways to use DNS TXT records,
+
# which is also done for DNS-SD.
+
# We would therefore likely make the `node` optional, or even omit it.
+
# If `node` is sufficiently different from a DNS name (it probably is
+
# not), then we can even omit "@".
+
auth                    -> node ("@" host_and_port):?
+
                           {% ([node, hap]) => ({ node: node, ...(hap ? { host: hap[1] } : {}) }) %}
+

+
host_and_port           -> host ":" port {% lstr %}
+

+
# rad-node is a semantic symbol, to signify intent
+
node                    -> nid {% id %}
+

+
# An NID is an ID that is ultimately a public key in the Radicle network.
+
# This is used to uniquely represent a node, or a user (also a node), or a
+
# namespace within a repository.
+
# It is a [multibase] and [multicodec] encoded string, using [base58btc] as the multibase
+
# and the codec for the underlying Ed25519 public key.
+
#
+
# It is always 48 characters long, including the initial "z6Mk",
+
# the `z` corresponding to the base, and `6Mk` corresponding to the codec.
+
#
+
# [base58btc]: https://digitalbazaar.github.io/base58-spec/
+
# [multibase]: https://www.ietf.org/archive/id/draft-multiformats-multibase-07.html
+
# [multicodec]: https://github.com/multiformats/multicodec/
+
#
+
# TODO: New NIDs in the future? Even this might change, as we allow rotating
+
# keys for nodes. But this is even further out than stable IDs for users.
+
nid                     -> "z6Mk" b58x25 b58x10 b58x5 base58btc base58btc base58btc base58btc {% lstr %}
+

+
resource                -> repository ( "/" namespace ):?
+
                           {% ([repo, ns]) => ({ repo: repo, ...(ns ? { namespace: ns[1] } : {}) }) %}
+

+
# repository is a semantic symbol, to signify intent
+
repository              -> rid {% id %}
+

+
# namespace is a semantic symbol, to signify intent
+
namespace               -> nid {% id %}
+

+
# An RID uniquely represents a git repository in the Radicle network.
+
# It is a [multibase] encoded string of a Git OID, using [base58btc] as the multibase.
+
#
+
# It is 28 or 29 characters long, including the initial multibase identifier "z".
+
#
+
# [base58btc]: https://digitalbazaar.github.io/base58-spec/
+
# [multibase]: https://www.ietf.org/archive/id/draft-multiformats-multibase-07.html
+
#
+
# TODO: In the near future (as soon as Git repositories with SHA-256 object
+
# format become popular, which will be in late 2026), we will have to extend
+
# the syntax of RIDs. Leave a note here so that implementors know this and
+
# make design decisions accordingly.
+
rid                     -> "z" b58x25 base58btc base58btc base58btc:?
+
                           {% (d,l,r) => {
+
                                   s = d.join('')
+
                                   // Check that the RID doesn't starts
+
                                   // with "z6Mk", as that can only be
+
                                   // an NID.  Reject it if it is.
+
                                   if (s.startsWith("z6Mk")) { return r; }
+
                                   return s;
+
                               }
+
                           %}
+

+
# For git specific resource types
+
resource_type_and_identity -> git_commit {% id %}
+
resource_type_and_identity -> git_tree {% id %}
+
resource_type_and_identity -> git_blob {% id %}
+
resource_type_and_identity -> git_tag {% id %}
+

+
git_commit              -> "/" "commit" "/" git_obj_or_ref {% ([, , , o]) => ({ type: "commit", ...o }) %}
+
git_tree                -> "/" "tree" "/" git_obj {% ([, , , o]) => ({ type: "tree", obj: o }) %}
+
git_blob                -> "/" "blob" "/" git_obj {% ([, , , o]) => ({ type: "blob", ref: o }) %}
+
git_tag                 -> "/" "tag" "/" git_obj_or_ref {% ([, , , o]) => ({ type: "tag", ...o }) %}
+

+
# Note: this rule will always be ambiguous, as something that looks like a
+
# git object ID could very well be a branch or a tag.
+
# Some git commands will warn when such refs are used, but not all.
+
git_obj_or_ref          -> git_obj {% ([o]) => ({ obj: o }) %}
+
                         | git_ref {% ([r]) => ({ ref: r }) %}
+

+
# Currently, only sha1 identities are supported.
+
git_obj                 -> git_sha1 {% id %}
+
# a sha1 ID is 40 hex digits
+
git_sha1                -> hex8 hex8 hex8 hex8 hex8 {% lstr %}
+

+
# unreserved is defined in RFC 3986.  Not that this syntax allows much
+
# more than what can be specified for a git reference.  It's left to
+
# the implementation to parse and apply semantics properly.
+
# Our best reference for how to interpret and parse git references is
+
# https://git-scm.com/docs/protocol-common.html
+
git_ref                 -> unreserved:+ ( "/" unreserved:+ ):* {% lstr %}
+

+
# For Radicle collaborative object resource types
+
resource_type_and_identity -> rad_cob {% id %}
+
resource_type_and_identity -> rad_cobs {% id %}
+

+
rad_cob                 -> "/" "cob" "/" rad_cob_type "/" rad_cob_obj
+
                           {% ([, , , type, , obj]) =>
+
                              ({ type: "cob", id: { type: type, obj: obj } }) %}
+
rad_cobs                -> "/" "cob" "/" rad_cob_type
+
                           {% ([, , , type]) =>
+
                              ({ type: "cobs", id: { type: type } }) %}
+

+
# The COB type is like a reversed FQDN.  The syntax spec is derived from
+
# https://datatracker.ietf.org/doc/html/rfc1034#section-3.5
+
rad_cob_type            -> rad_cob_label ( "." rad_cob_label ):+ {% lstr %}
+
rad_cob_label           -> alnum:+ ( "-" alnum:+ ):* {% lstr %}
+

+
# syntactically, radicle COB objects and object revisions are git objects
+
# semantically, they are as well, at least for now
+
rad_cob_obj             -> git_obj {% id %}
+

+
# While the ABNF doesn't say anything special about 'query', we can allow
+
# it for ourselves here, since nearley does a bit more than just checking
+
# syntax.
+
rad_query               -> rad_query_param ( "&" rad_query_param ):*
+
                           {% ([first, rest]) => {
+
                                  newrest = rest ? rest : [];
+
                                  newrest = newrest.reduce((tot, val) => { tot.push(val[1]); return tot }, []);
+
                                  return ([first, ...newrest].reduce((tot, val) => {
+
                                      if (tot[val['name']]) {
+
                                          tot[val['name']].push(val['value']);
+
                                      } else {
+
                                          tot[val['name']] = [ val['value'] ];
+
                                      }
+
                                      return tot;
+
                                  }, {}))
+
                              } %}
+
rad_query_param         -> ( alpha alnum:* ) "=" ( rad_query_pchar | "/" | "?" ):*
+
                           {% ([name, , value]) => ({ name: lstr(name), value: lstr(value) }) %}
+
rad_query_pchar         -> unreserved | pct_encoded | rad_query_sub_delims | ":" | "@"
+
rad_query_sub_delims    -> "!" | "$" | "'" | "(" | ")"
+
                         | "*" | "+" | "," | ";" | "="
+

+
# Helper symbols
+
alnum                   -> [a-zA-Z0-9] {% id %}
+
alnum5                  -> alnum alnum alnum alnum alnum {% function(d) { return d.join(''); } %}
+
alnum10                 -> alnum5 alnum5 {% function(d) { return d.join(''); } %}
+
alnum25                 -> alnum10 alnum10 alnum5 {% function(d) { return d.join(''); } %}
+

+
# Base58 Bitcoin Alphabet (excludes 0, O, I, l)
+
base58btc               -> [1-9A-HJ-NP-Za-km-z] {% id %}
+
b58x5                   -> base58btc base58btc base58btc base58btc base58btc {% function(d) { return d.join(''); } %}
+
b58x10                  -> b58x5 b58x5 {% function(d) { return d.join(''); } %}
+
b58x25                  -> b58x10 b58x10 b58x5 {% function(d) { return d.join(''); } %}
+

+
hex2                    -> hexdig hexdig {% function(d) { return d.join(''); } %}
+
hex4                    -> hex2 hex2 {% function(d) { return d.join(''); } %}
+
hex8                    -> hex4 hex4 {% function(d) { return d.join(''); } %}
+

+
# ABNF defines ALPHA, DIGIT and HEXDIG
+
alpha           -> [a-zA-Z] {% id %}
+
digit           -> [0-9] {% id %}
+
hexdig          -> [a-f0-9] {% id %}
+

+
# RFC 3986 defines the following (translated to nearley)
+
host            -> IP_literal | IPv4address | reg_name
+
port            -> [0-9]:+
+

+
IP_literal      -> "[" ( IPv6address | IPvFuture ) "]"
+

+
IPvFuture       -> "v" hexdig:+ "." ( unreserved | sub_delims | ":" ):+
+

+
IPv6address     ->                                                          h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" ls32
+
                 |                                                     "::" h16 ":" h16 ":" h16 ":" h16 ":" h16 ":"         ls32
+
                 |                                                 h16 "::" h16 ":" h16 ":" h16 ":" h16 ":"                 ls32
+
                 |                                         h16 ":" h16 "::" h16 ":" h16 ":" h16 ":"                         ls32
+
                 |                                 h16 ":" h16 ":" h16 "::" h16 ":" h16 ":"                                 ls32
+
                 |                         h16 ":" h16 ":" h16 ":" h16 "::" h16 ":"                                         ls32
+
                 |                 h16 ":" h16 ":" h16 ":" h16 ":" h16 "::"                                                 ls32
+
                 |         h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" h16 "::"                                                 h16
+
                 | h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" h16 "::"
+

+
h16             -> hexdig | hex2 | hex2 hexdig | hex4
+
ls32            -> ( h16 ":" h16 ) | IPv4address
+
IPv4address     -> dec_octet "." dec_octet "." dec_octet "." dec_octet
+
dec_octet       -> digit                # 0-9
+
                 | [1-9] digit          # 10-99
+
                 | "1" digit digit      # 100-199
+
                 | "2" [0-4] digit      # 200-249
+
                 | "25" [0-5]           # 250-255
+

+
reg_name        -> ( unreserved | pct_encoded | sub_delims ):*
+

+
pchar           -> unreserved | pct_encoded | sub_delims | ":" | "@"
+

+
query           -> ( pchar | "/" | "?" ):*
+

+
fragment        -> ( pchar | "/" | "?" ):*
+

+
pct_encoded     -> "%" hex2
+

+
unreserved      -> alnum | "-" | "." | "_" | "~"
+
sub_delims      -> "!" | "$" | "&" | "'" | "(" | ")"
+
                 | "*" | "+" | "," | ";" | "="
added 000X-general-uri-scheme/data/test/tc00a
@@ -0,0 +1 @@
+
rad:z3gqcJUbbbbbbbbbbbbbbbCSGazv5

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc00a-expected.json
@@ -0,0 +1,6 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "z3gqcJUbbbbbbbbbbbbbbbCSGazv5"
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc00b
@@ -0,0 +1 @@
+
rad://z3gqcJUbbbbbbbbbbbbbbbCSGazv5

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc00b-expected.json
@@ -0,0 +1,6 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "z3gqcJUbbbbbbbbbbbbbbbCSGazv5"
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc00c
@@ -0,0 +1 @@
+
rad:///z3gqcJUbbbbbbbbbbbbbbbCSGazv5

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc00c-expected.json
@@ -0,0 +1,6 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "z3gqcJUbbbbbbbbbbbbbbbCSGazv5"
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc01a
@@ -0,0 +1 @@
+
rad:zk1arLZbbbbbbbbbbbbbbb26us45E/z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc01a-expected.json
@@ -0,0 +1,7 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "namespace": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT"
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc01b
@@ -0,0 +1 @@
+
rad://zk1arLZbbbbbbbbbbbbbbb26us45E/z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc01b-expected.json
@@ -0,0 +1,7 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "namespace": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT"
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc01c
@@ -0,0 +1 @@
+
rad:///zk1arLZbbbbbbbbbbbbbbb26us45E/z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc01c-expected.json
@@ -0,0 +1,7 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "namespace": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT"
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc02
@@ -0,0 +1 @@
+
rad://z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT/zk1arLZbbbbbbbbbbbbbbb26us45E

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc02-expected.json
@@ -0,0 +1,7 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "node": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E"
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc03
@@ -0,0 +1 @@
+
rad://z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT/zk1arLZbbbbbbbbbbbbbbb26us45E/z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc03-expected.json
@@ -0,0 +1,8 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "node": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "namespace": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT"
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc04
@@ -0,0 +1 @@
+
rad://z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT@seed.example.com:8776/zk1arLZbbbbbbbbbbbbbbb26us45E

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc04-expected.json
@@ -0,0 +1,8 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "node": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT",
+
    "host": "seed.example.com:8776",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E"
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc05
@@ -0,0 +1 @@
+
rad://z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT@seed.example.com:8776/zk1arLZbbbbbbbbbbbbbbb26us45E/z6Mku8bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbo9XVAx

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc05-expected.json
@@ -0,0 +1,9 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "node": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT",
+
    "host": "seed.example.com:8776",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "namespace": "z6Mku8bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbo9XVAx"
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc10a
@@ -0,0 +1 @@
+
rad:z3gqcJUbbbbbbbbbbbbbbbCSGazv5/commit/72db6dffffffffffffffffffffffffffffffffff

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc10a-expected.json
@@ -0,0 +1,18 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "z3gqcJUbbbbbbbbbbbbbbbCSGazv5",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "72db6dffffffffffffffffffffffffffffffffff"
+
    }
+
  },
+
  {
+
    "scheme": "rad",
+
    "repo": "z3gqcJUbbbbbbbbbbbbbbbCSGazv5",
+
    "resource": {
+
      "type": "commit",
+
      "obj": "72db6dffffffffffffffffffffffffffffffffff"
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc10b
@@ -0,0 +1 @@
+
rad:///z3gqcJUbbbbbbbbbbbbbbbCSGazv5/commit/72db6dffffffffffffffffffffffffffffffffff

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc10b-expected.json
@@ -0,0 +1,18 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "z3gqcJUbbbbbbbbbbbbbbbCSGazv5",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "72db6dffffffffffffffffffffffffffffffffff"
+
    }
+
  },
+
  {
+
    "scheme": "rad",
+
    "repo": "z3gqcJUbbbbbbbbbbbbbbbCSGazv5",
+
    "resource": {
+
      "type": "commit",
+
      "obj": "72db6dffffffffffffffffffffffffffffffffff"
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc12
@@ -0,0 +1 @@
+
rad://z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT/zk1arLZbbbbbbbbbbbbbbb26us45E/commit/e9cf5263ffffffffffffffffffffffffffffffff

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc12-expected.json
@@ -0,0 +1,20 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "node": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "e9cf5263ffffffffffffffffffffffffffffffff"
+
    }
+
  },
+
  {
+
    "scheme": "rad",
+
    "node": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "commit",
+
      "obj": "e9cf5263ffffffffffffffffffffffffffffffff"
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc13
@@ -0,0 +1 @@
+
rad://z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT@seed.example.com:8776/zk1arLZbbbbbbbbbbbbbbb26us45E/z6Mku8bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbo9XVAx/commit/72db6dffffffffffffffffffffffffffffffffff

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc13-expected.json
@@ -0,0 +1,24 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "node": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT",
+
    "host": "seed.example.com:8776",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "namespace": "z6Mku8bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbo9XVAx",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "72db6dffffffffffffffffffffffffffffffffff"
+
    }
+
  },
+
  {
+
    "scheme": "rad",
+
    "node": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT",
+
    "host": "seed.example.com:8776",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "namespace": "z6Mku8bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbo9XVAx",
+
    "resource": {
+
      "type": "commit",
+
      "obj": "72db6dffffffffffffffffffffffffffffffffff"
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc30
@@ -0,0 +1 @@
+
rad:zk1arLZbbbbbbbbbbbbbbb26us45E/cob/org.example/e9cf5263ffffffffffffffffffffffffffffffff

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc30-expected.json
@@ -0,0 +1,13 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "cob",
+
      "id": {
+
        "type": "org.example",
+
        "obj": "e9cf5263ffffffffffffffffffffffffffffffff"
+
      }
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc31
@@ -0,0 +1 @@
+
rad://z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT/zk1arLZbbbbbbbbbbbbbbb26us45E/cob/org.example/e9cf5263ffffffffffffffffffffffffffffffff

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc31-expected.json
@@ -0,0 +1,14 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "node": "z6MksFbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbS9wzpT",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "cob",
+
      "id": {
+
        "type": "org.example",
+
        "obj": "e9cf5263ffffffffffffffffffffffffffffffff"
+
      }
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc32
@@ -0,0 +1 @@
+
rad:zk1arLZbbbbbbbbbbbbbbb26us45E/cob/org.example
added 000X-general-uri-scheme/data/test/tc32-expected.json
@@ -0,0 +1,12 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "cobs",
+
      "id": {
+
        "type": "org.example"
+
      }
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc40a
@@ -0,0 +1 @@
+
rad:zk1arLZbbbbbbbbbbbbbbb26us45E/tree/3eb47e9fffffffffffffffffffffffffffffffff?path=src

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc40a-expected.json
@@ -0,0 +1,15 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "tree",
+
      "obj": "3eb47e9fffffffffffffffffffffffffffffffff"
+
    },
+
    "query": {
+
      "path": [
+
        "src"
+
      ]
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc40b
@@ -0,0 +1 @@
+
rad:///zk1arLZbbbbbbbbbbbbbbb26us45E/tree/3eb47e9fffffffffffffffffffffffffffffffff?path=src

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc40b-expected.json
@@ -0,0 +1,15 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "tree",
+
      "obj": "3eb47e9fffffffffffffffffffffffffffffffff"
+
    },
+
    "query": {
+
      "path": [
+
        "src"
+
      ]
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc41a
@@ -0,0 +1 @@
+
rad:zk1arLZbbbbbbbbbbbbbbb26us45E/commit/master?blob=README.md

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc41a-expected.json
@@ -0,0 +1,15 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "master"
+
    },
+
    "query": {
+
      "blob": [
+
        "README.md"
+
      ]
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc41b
@@ -0,0 +1 @@
+
rad:///zk1arLZbbbbbbbbbbbbbbb26us45E/commit/master?blob=README.md

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc41b-expected.json
@@ -0,0 +1,15 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "master"
+
    },
+
    "query": {
+
      "blob": [
+
        "README.md"
+
      ]
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc42a
@@ -0,0 +1 @@
+
rad:zk1arLZbbbbbbbbbbbbbbb26us45E/commit/baz?path=foo/doc

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc42a-expected.json
@@ -0,0 +1,15 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "baz"
+
    },
+
    "query": {
+
      "path": [
+
        "foo/doc"
+
      ]
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc42b
@@ -0,0 +1 @@
+
rad:///zk1arLZbbbbbbbbbbbbbbb26us45E/commit/baz?path=foo/doc

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc42b-expected.json
@@ -0,0 +1,15 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "baz"
+
    },
+
    "query": {
+
      "path": [
+
        "foo/doc"
+
      ]
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc43a
@@ -0,0 +1 @@
+
rad:zk1arLZbbbbbbbbbbbbbbb26us45E/commit/baz/foo?path=doc&path=src

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc43a-expected.json
@@ -0,0 +1,16 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "baz/foo"
+
    },
+
    "query": {
+
      "path": [
+
        "doc",
+
        "src"
+
      ]
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc43b
@@ -0,0 +1 @@
+
rad:///zk1arLZbbbbbbbbbbbbbbb26us45E/commit/baz/foo?path=doc&path=src

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc43b-expected.json
@@ -0,0 +1,16 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "baz/foo"
+
    },
+
    "query": {
+
      "path": [
+
        "doc",
+
        "src"
+
      ]
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/tc44
@@ -0,0 +1 @@
+
rad:///zk1arLZbbbbbbbbbbbbbbb26us45E/commit/refs/notes/commit?blob=e9cf5263ffffffffffffffffffffffffffffffff

\ No newline at end of file
added 000X-general-uri-scheme/data/test/tc44-expected.json
@@ -0,0 +1,15 @@
+
[
+
  {
+
    "scheme": "rad",
+
    "repo": "zk1arLZbbbbbbbbbbbbbbb26us45E",
+
    "resource": {
+
      "type": "commit",
+
      "ref": "refs/notes/commit"
+
    },
+
    "query": {
+
      "blob": [
+
        "e9cf5263ffffffffffffffffffffffffffffffff"
+
      ]
+
    }
+
  }
+
]
added 000X-general-uri-scheme/data/test/uri-scheme-tester.sh
@@ -0,0 +1,35 @@
+
#! /bin/bash
+

+
HERE=$(dirname $0)
+
TEST_DATA=$HERE
+
RAD_URI_NE=$HERE/../rad-uri.ne
+
TMPDIR=${TMPDIR:-$(mktemp -d)}
+
RAD_URI_JS=$TMPDIR/rad-uri.js
+

+
if ! command -v nearley-test > /dev/null; then
+
    echo >&2 'You must install nearley: npm install -g nearley'
+
    exit 2
+
fi
+

+
if ! nearleyc -o $RAD_URI_JS $RAD_URI_NE; then
+
    exit 1
+
fi
+

+
failed=0
+
for expected in ${TEST_DATA}/tc*-expected.json; do
+
    input=${expected%-expected.json}
+
    name=${input##*/}
+

+
    actual=$(nearley-test -q -i "$(cat "$input")" "$RAD_URI_JS" | \
+
             node -e "process.stdout.write(JSON.stringify(
+
                 eval(require('fs').readFileSync('/dev/stdin','utf8')),null,2)+'\n')")
+

+
    if ! diff -u "$expected" <(printf '%s\n' "$actual") >&2; then
+
        echo "FAIL: $name" >&2
+
        failed=1
+
    else
+
        echo "PASS: $name"
+
    fi
+
done
+

+
[ "$failed" -eq 0 ]
added 000X-general-uri-scheme/general-uri-scheme.adoc
@@ -0,0 +1,857 @@
+
= RIP #X: General `rad:` URI Scheme
+
Lorenz Leutgeb <lorenz@leutgeb.xyz>; Richard Levitte <richard@levitte.org>
+
:doctype: article
+
:toc: preamble
+
:toclevels: 3
+
:status: Draft
+
:copyright: CC0-1.0
+
:lang: en
+

+
--
+
In <<RIP2>>, a `rad:` URN is briefly discussed as a simple Radicle
+
identifier. In <<RIP3>>, the `rad://` URL is introduced, made to allow
+
`git-remote-rad` to be a fruitful interface between Git and Radicle.
+

+
In this RIP, we discuss some modifications that are needed to have a general
+
URI scheme that fits in the syntax defined by <<URI>>, as well as further
+
extensions that allow reference to more aspects of Radicle as well as Git on a
+
Radicle network.
+

+
All extensions and modifications are intended not to introduce impossible
+
clashes with the existing schemes.
+
--
+

+
== Status Quo
+

+
References to resources on Radicle (both traditional Git objects as well as
+
collaborative objects, such as patches and issues) are communicated between
+
individuals mostly by relying on installations of <<radicle-explorer>>,
+
a web application that provides an HTTPS interface to the Radicle network.
+

+
When Alice wants to direct Bob's attention to a specific issue that she filed
+
on Radicle, you will likely see her send a URI of the form
+
`https://app.example.com/nodes/seed.example.com/rad:…/issues/…` to Bob.
+
URIs using the `rad:` schema are exchanged more rarely, and almost exclusively
+
to refer to repositories as a whole, e.g. in order to invoke `rad clone`,
+
`git clone`, etc.
+

+
As of 2024-04-23, the Radicle team also opted to have `rad:…`
+
URIs in Zulip messages automatically replaced by links to
+
`https://app.radicle.xyz/nodes/garden.radicle.xyz/rad:…`, limiting the
+
usefulness of exchange of `rad:` URIs.<<zulip-mangle>>
+

+
== Challenges
+

+
:url-cgit: https://git.zx2c4.com/cgit/
+
:url-sourcegraph: https://sourcegraph.com/
+

+
Every user should be able to individually control how they want to interact
+
with the network. Users may prefer to interact with Radicle via…
+

+
* a command line interface in their shell.
+
* a specific client program, such as:
+
** VSCode with a suitable extension, <<radicle-vscode>>
+
** a JetBrains IDE with a suitable plugin, <<radicle-jetbrains>>.
+
* <<radicle-explorer>>, hosted locally or by relying on a specific hosting
+
  provider.
+
* another custom program or handler, e.g. redirection to an instance of
+
  {url-cgit}[cgit] or {url-sourcegraph}[Sourcegraph].
+

+
Exchanging `https:` URIs that point at a specific hosting provider of
+
<<radicle-explorer>> is not portable. Individuals that read/receive such a URI
+
might prefer to access the repository with another method and then must modify
+
the URI (extract repository address, etc.) before access.
+

+
For the case of a reference to a repository, this is rather simple, but as
+
soon as references to resources _within_ the repositories are exchanged, there
+
are currently no standardized options. The two most common ones are referring
+
to an instance of `radicle-explorer` by means of a `https:`-URI, or
+
communicating in natural language, often in a context where the repository ID
+
is clear to all communicating parties. They will simply say "patch c863fe".
+

+
Automatic rewriting of `rad:` URIs to a specific hosting provider of
+
`radicle-explorer` contributes to additional centralization of access to the
+
network. In case the specific hosting provider goes offline, is censored, or
+
censors content, the impact is multiplied.
+

+
== Intention
+

+
The intention with this RIP is to define a `rad:` URI scheme with which one
+
can refer to:
+

+
. a Git repository in the Radicle network, which also includes the namespace
+
  for the contributions of a particular user (currently represented with a NID).
+
. optionally, a repository resource of some sort; to properly identify
+
  the resource:
+
.. the resource's type
+
.. a resource identity, specific to the resource type
+

+
****
+
We recognise that the concept of namespace and how that relates to a user is
+
a bit ambiguous.  A more detailed explanation should appear in another RIP.
+

+
For the moment, the user and their local node are identified with the same
+
identity, the NID.  Since the namespace for a user uses its identity, it may
+
appear confusing.  However, the fact remains that a namespace stores a user's
+
contribution to the repository it lives within.
+

+
In a future, users may be identified differently, and when that happens, the
+
namespace is expected to change accordingly, so that it's clear that it's the
+
user's contributions.
+
****
+

+
This can be seen as a hierarchy, expressed like this:
+

+
 repo-id [ "/" namespace ] [ "/" resource-type "/" resource-id ]
+

+
The `rad:` URI scheme is intended to be used to refer to / represent
+
repositories and resources within repositories, in such a way that it
+
can be used by applications, but also be understood by humans wishing
+
to get to the resource being referred to with what knowledge tools
+
they have available (such as the commands `git` and `rad`).
+

+
It does not imply any particular network protocol, or even that all
+
the parts of the scheme must be implemeted by all applications, and
+
applications may also make some parts of the scheme more mandatory
+
than what this RIP implies.
+

+
As a very concrete example of an application that handles `rad:` URIs,
+
`git-remote-rad` can probably never be expected to handle more than a
+
repository and namespace, a `rad:` URI with more than that should be
+
considered an error.  On the other hand, `git-remote-rad` requires the
+
presence of a namespace, i.e. makes it mandatory.
+

+
== Formalization and general extension of the `rad:` URI scheme
+

+
In this RIP, we denote the `rad:` scheme as a _URI_ scheme and try to
+
generally follow the nomenclature from <<URI>>.
+

+
=== The `web+rad:` URI scheme
+

+
<<HTML>> defines that unless a URL scheme is known and on their safelist, it
+
may be registered as a custom URL handler if prefixed with `web+`.  See their
+
section on <<HTML-registerhandler,registering handlers>>.
+

+
For this RIP, it means that `web+rad:` must be compatible with `rad:` in every
+
way, i.e prepending the string `"web+"` to a valid `rad:` URI must result in a
+
valid `web+rad:`-URI, and removing the first four symbols from a `web+rad:`
+
URI must result in a valid `rad:` URI.
+

+
This should make it as easy as possible for users to register URI handlers for
+
both `web+rad:` and `rad:` on their system, according to their preferences.
+

+
=== The old `rad:` URI scheme
+

+
The scheme as defined in <<RIP2>> and <<RIP3>> was really pretty simple, and could
+
be summarized with just a few lines of <<ABNF>>:
+

+
// [source,abnf]
+
[%nowrap]
+
----
+
rad             = rad-scheme ":" rad-rid                       ; (RIP #2)
+
                / rad-scheme ":" "//" rad-rid [ "/" rad-nid ]  ; (RIP #3)
+
rad-scheme      = "rad"
+
rad-rid         = "z" 27*28(ALPHA / DIGIT)
+
rad-nid         = "z6Mk" 44(ALPHA / DIGIT)
+
----
+

+
This is workable in practice, but the URL form is arguably not aligning with
+
the ideas behind the 'authority' as described in <<URI-Authority>>.  The
+
'authority' carries with it the idea of a host, possibly with some user
+
information.  In a Radicle network, it can be argued that the NID represents
+
that much more than the RID does, but the NID used in the old `rad:` URI
+
scheme doesn't represent that either; it represents a namespace within a
+
repository.
+

+
=== The new `rad:` URI scheme, a general approach
+

+
To align with <<URI, RFC 3986>>, the `rad:` scheme is renewed to include a
+
possible authority, which is the particular node / host that the data should
+
be found on.  In most cases, (much like in `file:` URIs), the authority is
+
expected to be empty or not there at all.
+

+
Other than that, the new URI scheme is extended with additional syntax to
+
meet the <<Challenges, challenges>> given above.
+

+
// [source,abnf]
+
[%nowrap]
+
----
+
include::data/rad-uri.abnf[tag=overview]
+
----
+

+
==== The authority
+

+
The NID is conceptually ambiguous from an <<URI, RFC 3986>> syntax perspective:
+

+
// [source,abnf]
+
[%nowrap]
+
----
+
authority   = [ userinfo "@" ] host [ ":" port ]
+
----
+

+
This doesn't quite align semantically with how Radicle nodes are identified.
+
Radicle nodes are identified with their NID, or with `{NID}@{hostname}[:{port}]`.
+
This does align with <<URI, RFC 3986>> syntax well enough, with just a little
+
bit of redefinition of the authority for use in Radicle context.
+

+
****
+
The authority is most closely related to the address given to the Radicle CLI
+
command `rad node connect`.
+
****
+

+
// [source,abnf]
+
[%nowrap]
+
----
+
include::data/rad-uri.abnf[tag=authority]
+
----
+

+
==== The repository
+

+
After the authority (if there is one) comes the repository to refer to.
+

+
With Radicle, the main anchoring resource is the repository, so its ID must
+
always be present, and specifying it alone refers to the synthesis of all
+
participants' contributions.
+

+
To refer to individual participants' separate contribution, their namespace
+
must also be included in the URI, separate from the repository ID with a slash
+
("/").  It shall be noted, though, that the namespace is only meaningful in
+
some specific circumstances, to be defined later.
+

+
****
+
Radicle repositories are _currently_ Git repositories.  We expect that other
+
version control (VCS) implementations may be used as well, and in that case,
+
we expect the VCS implementation type to be reflected in the repository
+
identity.
+
****
+

+
// [source,abnf]
+
[%nowrap]
+
----
+
include::data/rad-uri.abnf[tag=resource]
+
----
+

+
Here are a few examples of what a reference to a whole repository could look
+
like:
+

+
:tc00a: pass:c,q[footnote:tc00a[nearley-test result in link:data/test/tc00a-expected.json[]]]
+
:tc00b: pass:c,q[footnote:tc00b[nearley-test result in link:data/test/tc00b-expected.json[]]]
+
:tc00c: pass:c,q[footnote:tc00c[nearley-test result in link:data/test/tc00c-expected.json[]]]
+
:tc01a: pass:c,q[footnote:tc01a[nearley-test result in link:data/test/tc01a-expected.json[]]]
+
:tc01b: pass:c,q[footnote:tc01b[nearley-test result in link:data/test/tc01b-expected.json[]]]
+
:tc01c: pass:c,q[footnote:tc01c[nearley-test result in link:data/test/tc01c-expected.json[]]]
+
:tc02: pass:c,q[footnote:tc02[nearley-test result in link:data/test/tc02-expected.json[]]]
+
:tc03: pass:c,q[footnote:tc03[nearley-test result in link:data/test/tc03-expected.json[]]]
+
:tc04: pass:c,q[footnote:tc04[nearley-test result in link:data/test/tc04-expected.json[]]]
+
:tc05: pass:c,q[footnote:tc05[nearley-test result in link:data/test/tc05-expected.json[]]]
+

+
[options="header",cols="2a,1"]
+
|===
+
|URI
+
|Refers to
+

+
|
+
....
+
include::data/test/tc00a[]
+
....
+
{tc00a}
+
|Specific repo (RIP #2 URN form, still valid)
+

+
|
+
....
+
include::data/test/tc00b[]
+
....
+
{tc00b}
+
|Specific repo (RIP #3 URL form, considered legacy)
+

+
|
+
....
+
include::data/test/tc00c[]
+
....
+
{tc00c}
+
|Specific repo (new URI form with empty authority)
+

+
|
+
....
+
include::data/test/tc01a[]
+
....
+
{tc01a}
+
|Specific repo and namespace (new URI form, relative)
+

+
|
+
....
+
include::data/test/tc01b[]
+
....
+
{tc01b}
+
|Specific repo and namespace (RIP #3 URL form, considered legacy)
+

+
|
+
....
+
include::data/test/tc01c[]
+
....
+
{tc01c}
+
|Specific repo and namespace (new URI form with empty authority)
+

+
|
+
....
+
include::data/test/tc02[]
+
....
+
{tc02}
+
|Specific repo, which, if not available locally, should be fetched from the node `z6MksF…S9wzpT`
+

+
|
+
....
+
include::data/test/tc03[]
+
....
+
{tc03}
+
|Specific repo and namespace, which, if not available locally, should be fetched from the node `z6MksF…S9wzpT`
+

+
|
+
....
+
include::data/test/tc04[]
+
....
+
{tc04}
+
|Specific repo, which, if not available locally, should be fetched from the node `z6MksF…S9wzpT`, available on the host `seed.example.com:8776`
+

+
|
+
....
+
include::data/test/tc05[]
+
....
+
{tc05}
+
|Specific repo and namespace, which, if not available locally, should be fetched from the node `z6MksF…S9wzpT`, available on the host `seed.example.com:8776`
+
|===
+

+
(to test these URIs against a grammar, see <<Testing>> below)
+

+
==== The resource type and identity
+

+
While <<The repository,the repository>> allows you to refer to a Git repository,
+
it may also be desirable to refer to one or more specific resources within
+
that repository.  To do this, <<The repository,the repository>> may be followed
+
by a "/", a resource type and identity.
+

+
By default, the resource type and identity is nothing, left to be added
+
to further on in this RIP, or in future RIPs:
+

+
// [source,abnf]
+
[%nowrap]
+
----
+
include::data/rad-uri.abnf[tags=resource-type-and-identity;!*]
+
----
+

+
==== The query
+

+
The query part of the `rad:` URI inherits the syntax from <<URI>>.
+
The semantics of the query are to be defined with each resource type.
+

+
==== The fragment
+

+
The fragment part of the URI inherits the syntax and semantics from <<URI>>.
+

+
=== Relative URIs
+

+
<<URI-Reference-Resolution>> discusses the resolution of relative (absolute
+
or rootless alike) URIs, based on a base URI.
+

+
This RIP has nothing specific to say about this, apart from supporting it.
+

+
== Resource types and identities
+

+
These are the resource types and associated identities defined in this
+
RIP.  Future RIPs may amend them, as well as define new resource types
+
with associated identities.
+

+
=== Git specific resource types
+

+
There are four Git specific resource types:
+

+
* `commit`
+
* `tree`
+
* `blob`
+
* `tag`
+

+
In a `rad:` URI, these resource types are used to refer to Git object types
+
with the same name in a repository.  These resource types must be followed by
+
an identifier that refers to a specific Git object of that type.
+

+
Some of these resource types may also be accompanied by <<The Query,query>>
+
parameters to further refine what the `rad:` URI refers to.  This RIP defines
+
the following <<The Query,query>> parameters, all of which may be given
+
multiple times:
+

+
* `tree=dir1/dir2/…`, to "dig out" a tree given by the path.
+
* `blob=dir1/dir2/…`, to "dig out" a blob given by the path.
+
* `path=dir1/dir2/…`, to "dig out" a tree or a blob given by the path.
+

+
****
+
Applications are by and large allowed to define other <<The Query,query>>
+
parameters than what is defined here.  We hope that future RIPs will help
+
standardise at least the most common ones.
+

+
A few possible examples that may be useful:
+

+
* `rad:{rid}/commit/{git-obj-or-ref}?history`, to view the history of a commit.
+
* `rad:{rid}/blob/{git-obj}?raw`, to view/download the raw contents of a blob,
+
  where one might otherwise reasonably expected it to be wrapped in some
+
  markup, such as HTML.
+
****
+

+
`commit`::
+
+
+
--
+
The identifier that follows this resource type may be:
+

+
* a Git object ID
+
* a Git reference (typically, a branch or a tag name)
+

+
In all cases, this resource type refers to a single commit object.
+

+
With this resource type, it makes sense to use the <<The Query,query>>
+
parameters `tree`, `blob` and `path` to refine the `rad:` URI.
+
--
+

+
`tree`::
+
+
+
--
+
The identifier that follows this type may be must be a Git object identifier.
+

+
With this resource type, it makes sense to use the <<The Query,query>>
+
parameters `tree`, `blob` and `path` to refine the `rad:` URI.
+
--
+

+
`blob`::
+
+
+
--
+
The identifier that follows this type must be a Git object identifier.
+

+
With this resource type, it makes no sense to use any of the <<The
+
Query,query>> defined above.
+
--
+

+
`tag`::
+
+
+
--
+
The identifier that follows this type may be:
+

+
* a Git object ID
+
* a Git reference (i.e. a tag name)
+

+
In all cases, this resource type refers to a single tag object.
+

+
With this resource type, it makes sense to use the <<The Query,query>>
+
parameters `tree`, `blob` and `path` to refine the `rad:` URI.
+
--
+

+
In <<ABNF>> terms, the Git specific resource types are defined like this:
+

+
// [source,abnf]
+
[%nowrap]
+
----
+
include::data/rad-uri.abnf[tag=git-type-and-identity]
+
----
+

+
Here are a few examples of what a reference to a Git specific resource could
+
look like:
+

+
:tc10a: pass:c,q[footnote:tc10a[nearley-test result in link:data/test/tc10a-expected.json[]]]
+
:tc10b: pass:c,q[footnote:tc10b[nearley-test result in link:data/test/tc10b-expected.json[]]]
+
:tc12: pass:c,q[footnote:tc12[nearley-test result in link:data/test/tc12-expected.json[]]]
+
:tc13: pass:c,q[footnote:tc13[nearley-test result in link:data/test/tc13-expected.json[]]]
+

+
[options="header",cols="2a,1"]
+
|===
+
|URI
+
|Refers to
+

+
|
+
....
+
include::data/test/tc10a[]
+
....
+
{tc10a}
+
|Specific commit in repo
+

+
|
+
....
+
include::data/test/tc10b[]
+
....
+
{tc10b}
+
|Specific commit in repo
+

+
|
+
....
+
include::data/test/tc12[]
+
....
+
{tc12}
+
|Specific commit on a specific node, using the explicit name for the commit object
+

+
|
+
....
+
include::data/test/tc13[]
+
....
+
{tc13}
+
|Specific commit in a specific repo and namespace, which, if not available locally, should be fetched from the node `z6MksF…S9wzpT`, available on the host `seed.example.com:8776`
+
|===
+

+
(to test these URIs against a grammar, see <<Testing>> below)
+

+
And with queries:
+

+
:tc40a: pass:c,q[footnote:tc40a[nearley-test result in link:data/test/tc40a-expected.json[]]]
+
:tc40b: pass:c,q[footnote:tc40b[nearley-test result in link:data/test/tc40b-expected.json[]]]
+
:tc41a: pass:c,q[footnote:tc41a[nearley-test result in link:data/test/tc41a-expected.json[]]]
+
:tc41b: pass:c,q[footnote:tc41b[nearley-test result in link:data/test/tc41b-expected.json[]]]
+
:tc42a: pass:c,q[footnote:tc42a[nearley-test result in link:data/test/tc42a-expected.json[]]]
+
:tc42b: pass:c,q[footnote:tc42b[nearley-test result in link:data/test/tc42b-expected.json[]]]
+
:tc43a: pass:c,q[footnote:tc43a[nearley-test result in link:data/test/tc43a-expected.json[]]]
+
:tc43b: pass:c,q[footnote:tc43b[nearley-test result in link:data/test/tc43b-expected.json[]]]
+
:tc44: pass:c,q[footnote:tc44[nearley-test result in link:data/test/tc44-expected.json[]]]
+

+
[options="header",cols="2a,1"]
+
|===
+
|URI
+
|Refers to
+

+
|
+
....
+
include::data/test/tc40a[]
+
....
+
{tc40a}
+
|The `src` directory of the repo at tree object `3eb47e9`
+

+
|
+
....
+
include::data/test/tc40b[]
+
....
+
{tc40b}
+
|The `src` directory of the repo at tree object `3eb47e9`
+

+
|
+
....
+
include::data/test/tc41a[]
+
....
+
{tc41a}
+
|The `README.md` file of the repo at the "master" reference
+

+
|
+
....
+
include::data/test/tc41b[]
+
....
+
{tc41b}
+
|The `README.md` file of the repo at the "master" reference
+

+
|
+
....
+
include::data/test/tc42a[]
+
....
+
{tc42a}
+
|The `foo/doc` directory of the repo at the "baz" reference
+

+
|
+
....
+
include::data/test/tc42b[]
+
....
+
{tc42b}
+
|The `foo/doc` directory of the repo at the "baz" reference
+

+
|
+
....
+
include::data/test/tc43a[]
+
....
+
{tc43a}
+
|The `doc` and `src` directories of the repo at the "baz/foo" reference.
+

+
|
+
....
+
include::data/test/tc43b[]
+
....
+
{tc43b}
+
|The `doc` and `src` directories directory of the repo at the "baz/foo" reference.
+

+
|
+
....
+
include::data/test/tc44[]
+
....
+
{tc44}
+
|The commit notes for commit `e9cf5263…`.
+
|===
+

+
(to test these URIs against a grammar, see <<Testing>> below)
+

+
****
+
// Git version that is current at the time of writing.
+
:gitversion: 2.54.0
+

+
// Base URL of Git documentation.
+
:gitdocs: https://git-scm.com/docs
+

+
// Allows for convenient reference to definitions in the Git glossary.
+
// "gitgloss" is just short for "a definition in the Git glossary".
+
:gitgloss: {gitdocs}/gitglossary/{gitversion}#Documentation/gitglossary.txt-
+

+
:gitrevparse: {gitdocs}/git-rev-parse/{gitversion}#Documentation/git-rev-parse.txt-
+

+
[discrete]
+
==== Addressing resources by reference names
+

+
[discrete]
+
===== Types of resources that may be addressed
+

+
Resources of the types "commit" and "tag", may, in Git jargon, be
+
"committish". There are at least two definitions in the Git documentation
+
that differ slightly:
+

+
[quote,'{gitgloss}commit-ishalsocommittish[Git glossary entry for "committish"]']
+
A {gitgloss}commit_object[commit object] or an {gitgloss}object[object]
+
that can be recursively {gitgloss}dereference[dereferenced] to a commit
+
object.  The following are all commit-ishes:
+
a commit object,
+
a {gitgloss}tag_object[tag object] that points to a commit object,
+
a tag object that points to a tag object that points to a commit object,
+
etc.
+

+
[quote,'{gitrevparse}--verify[Git documentation of `git rev-parse --verify`]']
+
a commit-ish (i.e. a commit, or an annotated tag that points at a commit)
+

+
We thus restrict ourselves to only refer to commits and tags via references,
+
since no tree or blob object may ever be committish.
+

+
[discrete]
+
===== Ambiguity in addressing by reference name
+

+
Note that Git references are ambiguous, even within one repository (or reference
+
namespace).
+

+
It is possible to create the following set of references in one namespace:
+

+
* `refs/heads/main`
+
* `refs/tags/main`
+
* `refs/heads/refs/heads/main`
+

+
We do not define how such ambiguity is to be resolved, but note that applications
+
will likely want to implement some mechanism for disambiguation.
+

+
See also {gitrevparse}refnameegmasterheadsmasterrefsheadsmaster[the section
+
_Specifying Revisions_ in the documentation of `git rev-parse`].
+

+
****
+

+
=== Radicle collaborative object resource types
+

+
Alongside the usual Git objects, Radicle also implements a generalized
+
collaborative object (usually referred to as COB), identified with a COB type
+
and a COB object.
+

+
For general COB references in a `rad:` URI, these resource types are defined
+
to refer to a Radicle COB / multiple COBs:
+

+
`cob`::
+
+
+
--
+
In a `rad:` URI, this must be followed a slash (`/`) and the COB type.  The
+
COB type _may_ be followed by another slash (`/`) and the COB object.
+

+
If the `rad:` URI includes the COB object, then it refers to a single COB.
+

+
If the `rad:` URI does not include the COB object, then it refers to the set
+
of COB objects of the given COB type.
+
--
+

+
`cob` resources may also be accompanied by a set of <<The Query,query>>
+
parameters to further refine what the `rad:` URI refers to.
+

+
****
+
Because we realise that we can't know all future COB types and how they
+
function, we can't do too much in terms of definitions.  We leave it very much
+
to the COB owners as well as applications to work out what query params they
+
will handle, and hopefully, some of them can be standardised in future RIPs.
+

+
A hypothetical example: `rad:{RID}/cob/example.radicle.sparkle?q=glitter`.
+
****
+

+
In <<ABNF>> terms, the COB resource types are defined like this:
+

+
// [source, abnf]
+
[%nowrap]
+
----
+
include::data/rad-uri.abnf[tag=rad-cob-type-and-identity]
+
----
+

+
Here are a few examples of what a reference to other collaborative objects
+
could look like:
+

+
:tc30: pass:c,q[footnote:tc30[nearley-test result in link:data/test/tc30-expected.json[]]]
+
:tc31: pass:c,q[footnote:tc31[nearley-test result in link:data/test/tc31-expected.json[]]]
+
:tc32: pass:c,q[footnote:tc32[nearley-test result in link:data/test/tc32-expected.json[]]]
+

+
[options="header",cols="2a,1"]
+
|===
+
|URI
+
|Refers to
+

+
|
+
....
+
include::data/test/tc30[]
+
....
+
{tc30}
+
|Specific COB of the type `org.example`
+

+
|
+
....
+
include::data/test/tc31[]
+
....
+
{tc31}
+
|Specific COB of the type `org.example` on specific node
+

+
|
+
....
+
include::data/test/tc32[]
+
....
+
{tc32}
+
|The set of COBs of the type `org.example`
+
|===
+

+
== Implementation requirements and recommendations
+

+
* Implementations may implement only part of this specification, depending on
+
what makes sense in the context of what they do.  It is advisable, however, if
+
they can parse enough of a valid `rad:` URI to allow for <<Future work,future work>>.
+
For example, it's hard to imagine that `git-remote-rad` would ever support
+
more elaborate URLs than those involving an RID and an NID:
+

+
** `rad:{RID}`
+
** `rad:///{RID}`
+
** `rad:{RID}/{NID}`
+
** `rad:///{RID}/{NID}`
+
** `rad://{RID}/{NID}` (legacy)
+

+
* Implementations that receive a URI they cannot handle - because of
+
unrecognised resource type, identifier, or query params - should signal an
+
error.
+
+
+
For fragments, there is no recommendation, that's left entirely to the
+
authors of those softwares.
+

+
* Software for human interaction on the web should be able to automatically
+
convert between `rad:` and `web+rad:` to create links.  For example, in a
+
markdown context, it would be quite viable for this:
+
+
+
`rad:z3gqcJU…CSGazv5/commit/72db6d…`
+
+
+
to be converted to:
+
+
+
`[rad:z3gqcJU…CSGazv5/commit/72db6d…](web+rad:z3gqcJU…CSGazv5/commit/72db6d…)`
+

+
* Implementations that are aware of the context may support relative URIs as
+
they see fit.  For example, a program that handles Radicle patches and issues
+
could assume the base URI `rad:///{RID}` (given that `{RID}` is the ID of
+
the repository that's being handled), allowing comments to refer to issue and
+
other patches in the same repository with `patch/{PATCH-ID}` or `issue/{ISSUE-ID}`.
+

+
== Future work
+

+
Future work may include:
+

+
* amending <<The authority,the authority>> with Radicle network
+
  specification syntax (pending future formalization of that idea).
+
* amending <<The authority,the authority>> with multi-device IDs (also
+
  pending formalization of that idea).
+
* adding more resource types, and their respective syntaxes
+
* extending existing resource types.
+
* adding other query types
+
* viewing the history/graph of the underlying Git repository (in analogy to `git log`/`git log --graph`).
+
* viewing revision ranges of the underlying Git repository (in analogy to `git diff`, `git log`).
+

+
== Closing thoughts
+

+
This RIP started with a status quo and challenges.  It is the hope of the
+
authors that the challenges are sufficiently addressed, while also leaving
+
doors open for more extensions in the future.
+

+
== Credits
+

+
* Lorenz Leutgeb, who wrote the original text and grammar leading up to this RIP.
+

+
== Copyright
+

+
This document is licensed under the Creative Commons CC0 1.0 Universal license.
+

+
[bibliography]
+
== References
+

+
// https://docs.asciidoctor.org/asciidoc/latest/syntax-quick-reference/#bibliography
+
// says that anchors like [[[ABNF]]] should render to [ANBF], but experience
+
// says that this isn't quite true.  However, [[ABNF]][ABNF] seems to render
+
// what I hoped for.  /RL
+

+
[[ABNF]][ABNF]:: https://datatracker.ietf.org/doc/html/rfc5234[RFC 5234 - Augmented BNF for Syntax Specifications: ABNF]
+
[[HTML]][HTML]:: Web Hypertext Application Technology Working Group: _HTML Living Standard_ (<https://html.spec.whatwg.org>)
+
[[HTML-registerhandler]][HTML-registerhandler]:: Sec. 8.9.1.4, _Custom scheme handlers: the `registerProtocolHandler()` method_ (<https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers>)
+
[[Nearley]][Nearley]:: Kartik Chandra's Nearley grammar (<https://nearley.js.org/>)
+
[[radicle-explorer]][radicle-explorer]:: Radicle Web Interface - link:web+rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5[rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5] +
+
  (<https://app.radicle.xyz/nodes/iris.radicle.xyz/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5>)
+
[[radicle-jetbrains]][radicle-jetbrains]:: Radicle JetBrains plugin - link:web+rad:z3WHS4GSf8hChLjGYfPkJY7vCxsBK[rad:z3WHS4GSf8hChLjGYfPkJY7vCxsBK] +
+
  (<https://app.radicle.xyz/nodes/iris.radicle.xyz/rad:z3WHS4GSf8hChLjGYfPkJY7vCxsBK>)
+
[[radicle-vscode]][radicle-vscode]:: Radicle VSCode extension - link:web+rad:z3Makm6fsQQXmpSFE43DZqwupaEhk[rad:z3Makm6fsQQXmpSFE43DZqwupaEhk] +
+
  (<https://app.radicle.xyz/nodes/iris.radicle.xyz/rad:z3Makm6fsQQXmpSFE43DZqwupaEhk>), +
+
  link:web+vscode:extension/radicle-ide-plugins-team.radicle[vscode:extension/radicle-ide-plugins-team.radicle] +
+
  (<https://marketplace.visualstudio.com/items?itemName=radicle-ide-plugins-team.radicle>)
+
[[RIP2]][RIP2]:: @cloudhead: Identity - link:web+rad:z3trNYnLWS11cJWC6BbxDs5niGo82/blob/1c402116983be19e754fb14aa7ce38145f0a4b09?path=0002-identity.md[rad:z3trNYnLWS11cJWC6BbxDs5niGo82/blob/1c402116983be19e754fb14aa7ce38145f0a4b09?path=0002-identity.md] +
+
  (<https://app.radicle.xyz/nodes/iris.radicle.xyz/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/1c402116983be19e754fb14aa7ce38145f0a4b09/0002-identity.md>)
+
[[RIP3]][RIP3]:: @fintohaps: Radicle Improvement Proposal #3: Storage Layout - link:web+rad:z3trNYnLWS11cJWC6BbxDs5niGo82/blob/329dee9a4b65169ea3889a7da239892b705d0d68?path=0003-storage-layout.md#url[rad:z3trNYnLWS11cJWC6BbxDs5niGo82/blob/329dee9a4b65169ea3889a7da239892b705d0d68?path=0003-storage-layout.md#url] +
+
  (<https://app.radicle.xyz/nodes/iris.radicle.xyz/rad:z3trNYnLWS11cJWC6BbxDs5niGo82/tree/329dee9a4b65169ea3889a7da239892b705d0d68/0003-storage-layout.md#url>)
+
[[URI]][URI]:: https://datatracker.ietf.org/doc/html/rfc3986[RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax]
+
[[URI-Authority]][URI-Authority]:: https://datatracker.ietf.org/doc/html/rfc3986#section-3.2[RFC 3986 - 3.2. Authority]
+
[[URI-Reference-Resolution]][URI-Reference-Resolution]:: https://datatracker.ietf.org/doc/html/rfc3986#section-5[RFC 3986 - 5. Reference Resolution]
+
[[zulip-mangle]][zulip-mangle]:: Zulip Topic: _`rad:` gets mangled by Zulip_ (#general, 2024-04-23) (<https://radicle.zulipchat.com/#narrow/stream/369274-general/topic/.60rad.3A.60.20gets.20mangled.20by.20Zulip>)
+

+
[appendix]
+
== Full <<ABNF>> grammar
+

+
[%nowrap]
+
----
+
include::data/rad-uri.abnf[tag=!rfc5234]
+
----
+

+
[appendix]
+
== Full <<Nearley>> grammar
+

+
[%nowrap]
+
----
+
include::data/rad-uri.ne[]
+
----
+

+
[appendix]
+
== Testing
+

+
All the examples sprinkled in this RIP are tested against the <<Nearley>>
+
grammar.  Test inputs and expected parse results are in `data/test/`, where
+
each `tc__` file contains a valid URI and the corresponding
+
`tc__-expected.json` contains the expected parse result as JSON.
+

+
The test suite can be run with:
+

+
[,shell]
+
----
+
nix flake check
+
----
+

+
This compiles the grammar with `nearleyc`, runs `nearley-test` on each test
+
input, and compares the JSON output against the expected result.
+

+
To test a single URI manually:
+

+
[,shell]
+
[%nowrap]
+
----
+
# [one time] Compile the grammar into javascript
+
nearleyc -o rad-uri.js data/rad-uri.ne
+

+
# Test the chosen URI string ($URI)
+
nearley-test -q -i "$URI" rad-uri.js
+
----
+

+
`nearley-test` will return all possible results for the URI, which means that
+
where there are syntactic ambiguities, there will be more than one result.
+
For testing purposes, it's enough if one of the results match the expected
+
results linked from the footnotes associated with the examples in the tables
+
above.
added 000X-general-uri-scheme/tests/.gitignore
@@ -0,0 +1,4 @@
+
*.js
+
*.testcases-*
+
*.testresult-*
+
tmp_test_result.txt
added 000X-general-uri-scheme/tests/uri-scheme-extractor.sh
@@ -0,0 +1,24 @@
+
#! /bin/sh
+

+
HERE=$(dirname $0)
+
URI_SCHEME_MD=$HERE/../general-uri-scheme.adoc
+
RAD_URI_ABNF=$HERE/../data/rad-uri.abnf
+
RAD_URI_NE=$HERE/../data/rad-uri.ne
+
TESTCASES_PREFIX=$HERE/uri-scheme.testcases-
+
TESTRESULT_PREFIX=$HERE/uri-scheme.testresult-
+

+
rm -f $TESTCASES_PREFIX*
+
sed -E -n 's/^\|`(rad:.*?)` \{tc([0-9]+)\}.*$/\2 : \1/p' < $URI_SCHEME_MD \
+
    | while read TC _ URI; do
+
    echo "$URI" >> $TESTCASES_PREFIX$TC
+
    echo "Extracted a test URI into $TESTCASES_PREFIX$TC"
+
done
+

+
rm -f $TESTRESULT_PREFIX*
+
sed -E -n 's/^.*?footnote:tc([0-9]+)\[nearley-test +result: *`(.*?)`.*?\]$/\1 : \2/p' < $URI_SCHEME_MD \
+
    | while read TC _ RESULT; do
+
    # Because asciidoc needs to have some characters in the result string escaped,
+
    # we remove the escape character here.
+
    echo "$RESULT" | sed -E 's/\\//g' >> $TESTRESULT_PREFIX$TC
+
    echo "Extracted a test result into $TESTRESULT_PREFIX$TC"
+
done
added flake.lock
@@ -0,0 +1,61 @@
+
{
+
  "nodes": {
+
    "flake-utils": {
+
      "inputs": {
+
        "systems": "systems"
+
      },
+
      "locked": {
+
        "lastModified": 1731533236,
+
        "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+
        "owner": "numtide",
+
        "repo": "flake-utils",
+
        "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "numtide",
+
        "repo": "flake-utils",
+
        "type": "github"
+
      }
+
    },
+
    "nixpkgs": {
+
      "locked": {
+
        "lastModified": 1774855581,
+
        "narHash": "sha256-YkreHeMgTCYvJ5fESV0YyqQK49bHGe2B51tH6claUh4=",
+
        "owner": "NixOS",
+
        "repo": "nixpkgs",
+
        "rev": "15c6719d8c604779cf59e03c245ea61d3d7ab69b",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "NixOS",
+
        "ref": "nixpkgs-unstable",
+
        "repo": "nixpkgs",
+
        "type": "github"
+
      }
+
    },
+
    "root": {
+
      "inputs": {
+
        "flake-utils": "flake-utils",
+
        "nixpkgs": "nixpkgs"
+
      }
+
    },
+
    "systems": {
+
      "locked": {
+
        "lastModified": 1681028828,
+
        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+
        "owner": "nix-systems",
+
        "repo": "default",
+
        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+
        "type": "github"
+
      },
+
      "original": {
+
        "owner": "nix-systems",
+
        "repo": "default",
+
        "type": "github"
+
      }
+
    }
+
  },
+
  "root": "root",
+
  "version": 7
+
}
added flake.nix
@@ -0,0 +1,78 @@
+
{
+
  description = "RIPs development environment";
+

+
  inputs = {
+
    nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+
    flake-utils.url = "github:numtide/flake-utils";
+
  };
+

+
  outputs = {
+
    nixpkgs,
+
    flake-utils,
+
    ...
+
  }:
+
    flake-utils.lib.eachDefaultSystem (system: let
+
      pkgs = nixpkgs.legacyPackages.${system};
+

+
      node-deps = pkgs.buildNpmPackage {
+
        pname = "rips-node-deps";
+
        version = "0.0.0";
+
        src = pkgs.lib.sources.sourceByRegex ./. ["^package\\.json$" "^package-lock\\.json$"];
+
        nodejs = pkgs.nodejs_22;
+
        npmDepsHash = "sha256-VS398KrZ5HQYJqH50EP1ESIh5lDwA0YTttkTY/qOpTE=";
+
        dontNpmBuild = true;
+
        dontNpmPrune = true;
+
        makeCacheWritable = true;
+
        env.PUPPETEER_SKIP_DOWNLOAD = "1";
+
        installPhase = ''
+
          runHook preInstall
+
          mkdir -p $out/lib
+
          cp -r node_modules $out/lib/node_modules
+
          runHook postInstall
+
        '';
+
      };
+
      node-bin = "${node-deps}/lib/node_modules/.bin";
+
      grammarTest = ./000X-general-uri-scheme/data/test/uri-scheme-tester.sh
+
    in {
+
      devShells.default = pkgs.mkShell {
+
        buildInputs = [
+
          pkgs.asciidoctor
+
          pkgs.nodejs_22
+
          pkgs.chromium
+
        ];
+

+
        shellHook = ''
+
          export PUPPETEER_EXECUTABLE_PATH="${pkgs.chromium}/bin/chromium"
+
          export PUPPETEER_SKIP_DOWNLOAD=1
+

+
          if [ ! -d node_modules ]; then
+
            echo "Installing npm dependencies..."
+
            npm install
+
          fi
+

+
          export PATH="$PWD/node_modules/.bin:$PATH"
+
        '';
+
      };
+

+
      checks.grammar =
+
        pkgs.runCommand "grammar-check" {
+
          nativeBuildInputs = [pkgs.nodejs_22];
+
        } ''
+
          ${grammarTest} || exit 1
+
          touch $out
+
        '';
+

+
      checks.links = let
+
        src = pkgs.lib.sources.sourceFilesBySuffices ./. [".md" ".adoc" ".yml"];
+
      in
+
        pkgs.runCommand "links-check" {
+
          nativeBuildInputs = [pkgs.nodejs_22];
+
          CI = "true";
+
        } ''
+
          cp -r ${src}/. work
+
          cd work
+
          ${node-bin}/linkspector check
+
          touch $out
+
        '';
+
    });
+
}
added package-lock.json
@@ -0,0 +1,3056 @@
+
{
+
  "name": "rips",
+
  "lockfileVersion": 3,
+
  "requires": true,
+
  "packages": {
+
    "": {
+
      "name": "rips",
+
      "devDependencies": {
+
        "@umbrelladocs/linkspector": "^0.4.8",
+
        "nearley": "^2.20.1"
+
      }
+
    },
+
    "node_modules/@babel/code-frame": {
+
      "version": "7.29.0",
+
      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+
      "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@babel/helper-validator-identifier": "^7.28.5",
+
        "js-tokens": "^4.0.0",
+
        "picocolors": "^1.1.1"
+
      },
+
      "engines": {
+
        "node": ">=6.9.0"
+
      }
+
    },
+
    "node_modules/@babel/helper-validator-identifier": {
+
      "version": "7.28.5",
+
      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+
      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=6.9.0"
+
      }
+
    },
+
    "node_modules/@hapi/address": {
+
      "version": "5.1.1",
+
      "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz",
+
      "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==",
+
      "dev": true,
+
      "license": "BSD-3-Clause",
+
      "dependencies": {
+
        "@hapi/hoek": "^11.0.2"
+
      },
+
      "engines": {
+
        "node": ">=14.0.0"
+
      }
+
    },
+
    "node_modules/@hapi/formula": {
+
      "version": "3.0.2",
+
      "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz",
+
      "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==",
+
      "dev": true,
+
      "license": "BSD-3-Clause"
+
    },
+
    "node_modules/@hapi/hoek": {
+
      "version": "11.0.7",
+
      "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz",
+
      "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==",
+
      "dev": true,
+
      "license": "BSD-3-Clause"
+
    },
+
    "node_modules/@hapi/pinpoint": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz",
+
      "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==",
+
      "dev": true,
+
      "license": "BSD-3-Clause"
+
    },
+
    "node_modules/@hapi/tlds": {
+
      "version": "1.1.6",
+
      "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.6.tgz",
+
      "integrity": "sha512-xdi7A/4NZokvV0ewovme3aUO5kQhW9pQ2YD1hRqZGhhSi5rBv4usHYidVocXSi9eihYsznZxLtAiEYYUL6VBGw==",
+
      "dev": true,
+
      "license": "BSD-3-Clause",
+
      "engines": {
+
        "node": ">=14.0.0"
+
      }
+
    },
+
    "node_modules/@hapi/topo": {
+
      "version": "6.0.2",
+
      "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz",
+
      "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==",
+
      "dev": true,
+
      "license": "BSD-3-Clause",
+
      "dependencies": {
+
        "@hapi/hoek": "^11.0.2"
+
      }
+
    },
+
    "node_modules/@puppeteer/browsers": {
+
      "version": "2.13.0",
+
      "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz",
+
      "integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "debug": "^4.4.3",
+
        "extract-zip": "^2.0.1",
+
        "progress": "^2.0.3",
+
        "proxy-agent": "^6.5.0",
+
        "semver": "^7.7.4",
+
        "tar-fs": "^3.1.1",
+
        "yargs": "^17.7.2"
+
      },
+
      "bin": {
+
        "browsers": "lib/cjs/main-cli.js"
+
      },
+
      "engines": {
+
        "node": ">=18"
+
      }
+
    },
+
    "node_modules/@standard-schema/spec": {
+
      "version": "1.1.0",
+
      "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
+
      "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/@tootallnate/quickjs-emscripten": {
+
      "version": "0.23.0",
+
      "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
+
      "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/@types/debug": {
+
      "version": "4.1.13",
+
      "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
+
      "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/ms": "*"
+
      }
+
    },
+
    "node_modules/@types/mdast": {
+
      "version": "4.0.4",
+
      "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+
      "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/unist": "*"
+
      }
+
    },
+
    "node_modules/@types/ms": {
+
      "version": "2.1.0",
+
      "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+
      "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/@types/node": {
+
      "version": "25.5.0",
+
      "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
+
      "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "optional": true,
+
      "dependencies": {
+
        "undici-types": "~7.18.0"
+
      }
+
    },
+
    "node_modules/@types/unist": {
+
      "version": "3.0.3",
+
      "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+
      "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/@types/yauzl": {
+
      "version": "2.10.3",
+
      "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+
      "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+
      "dev": true,
+
      "license": "MIT",
+
      "optional": true,
+
      "dependencies": {
+
        "@types/node": "*"
+
      }
+
    },
+
    "node_modules/@umbrelladocs/linkspector": {
+
      "version": "0.4.8",
+
      "resolved": "https://registry.npmjs.org/@umbrelladocs/linkspector/-/linkspector-0.4.8.tgz",
+
      "integrity": "sha512-JYubJdwMUaJHRRNZNRdNtf59sDZ+T0+cci+qZPUnITuxG5ERhJo08Zsa7e3tfys8R/E2La+4J3SvPFKRAn6RBw==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "@umbrelladocs/linkspector": "github:UmbrellaDocs/linkspector#asciidoc-updates",
+
        "@umbrelladocs/rdformat-validator": "^1.0.0",
+
        "commander": "^14.0.3",
+
        "github-slugger": "^2.0.0",
+
        "glob": "^13.0.6",
+
        "ignore": "^7.0.5",
+
        "joi": "^18.0.2",
+
        "js-yaml": "^4.1.1",
+
        "kleur": "^4.1.5",
+
        "ora": "^9.3.0",
+
        "puppeteer": "^24.37.5",
+
        "remark-gfm": "^4.0.1",
+
        "remark-parse": "^11.0.0",
+
        "unified": "^11.0.5",
+
        "unist-util-visit": "^5.1.0"
+
      },
+
      "bin": {
+
        "linkspector": "index.js"
+
      }
+
    },
+
    "node_modules/@umbrelladocs/linkspector/node_modules/@umbrelladocs/linkspector": {
+
      "version": "0.4.7",
+
      "resolved": "git+ssh://git@github.com/UmbrellaDocs/linkspector.git#f1091ba57af3d2a85e60513dc367426262d11928",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "@umbrelladocs/linkspector": "github:UmbrellaDocs/linkspector#asciidoc-updates",
+
        "@umbrelladocs/rdformat-validator": "^1.0.0",
+
        "commander": "^14.0.3",
+
        "github-slugger": "^2.0.0",
+
        "glob": "^13.0.6",
+
        "ignore": "^7.0.5",
+
        "joi": "^18.0.2",
+
        "js-yaml": "^4.1.1",
+
        "kleur": "^4.1.5",
+
        "ora": "^9.3.0",
+
        "puppeteer": "^24.37.5",
+
        "remark-gfm": "^4.0.1",
+
        "remark-parse": "^11.0.0",
+
        "unified": "^11.0.5",
+
        "unist-util-visit": "^5.1.0"
+
      },
+
      "bin": {
+
        "linkspector": "index.js"
+
      }
+
    },
+
    "node_modules/@umbrelladocs/rdformat-validator": {
+
      "version": "1.0.0",
+
      "resolved": "https://registry.npmjs.org/@umbrelladocs/rdformat-validator/-/rdformat-validator-1.0.0.tgz",
+
      "integrity": "sha512-HRQI1QbbQJoVIlPhn/P30aW2fEyLj2gvZQt/Gbkae/dtCEeB9fEvWdpoI6GMY0eVRzg9KWL16I9Q7Awg3M77hA==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "commander": "^14.0.0"
+
      },
+
      "bin": {
+
        "rdformat-validator": "bin/rdformat-validator.js"
+
      },
+
      "engines": {
+
        "node": ">=14.0.0",
+
        "npm": ">=6.0.0"
+
      },
+
      "funding": {
+
        "type": "github",
+
        "url": "https://github.com/sponsors/UmbrellaDocs"
+
      }
+
    },
+
    "node_modules/agent-base": {
+
      "version": "7.1.4",
+
      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+
      "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">= 14"
+
      }
+
    },
+
    "node_modules/ansi-regex": {
+
      "version": "6.2.2",
+
      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+
      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=12"
+
      },
+
      "funding": {
+
        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+
      }
+
    },
+
    "node_modules/ansi-styles": {
+
      "version": "4.3.0",
+
      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+
      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "color-convert": "^2.0.1"
+
      },
+
      "engines": {
+
        "node": ">=8"
+
      },
+
      "funding": {
+
        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+
      }
+
    },
+
    "node_modules/argparse": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+
      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+
      "dev": true,
+
      "license": "Python-2.0"
+
    },
+
    "node_modules/ast-types": {
+
      "version": "0.13.4",
+
      "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
+
      "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "tslib": "^2.0.1"
+
      },
+
      "engines": {
+
        "node": ">=4"
+
      }
+
    },
+
    "node_modules/b4a": {
+
      "version": "1.8.0",
+
      "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz",
+
      "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "peerDependencies": {
+
        "react-native-b4a": "*"
+
      },
+
      "peerDependenciesMeta": {
+
        "react-native-b4a": {
+
          "optional": true
+
        }
+
      }
+
    },
+
    "node_modules/bail": {
+
      "version": "2.0.2",
+
      "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+
      "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "funding": {
+
        "type": "github",
+
        "url": "https://github.com/sponsors/wooorm"
+
      }
+
    },
+
    "node_modules/balanced-match": {
+
      "version": "4.0.4",
+
      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+
      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": "18 || 20 || >=22"
+
      }
+
    },
+
    "node_modules/bare-events": {
+
      "version": "2.8.2",
+
      "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz",
+
      "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "peerDependencies": {
+
        "bare-abort-controller": "*"
+
      },
+
      "peerDependenciesMeta": {
+
        "bare-abort-controller": {
+
          "optional": true
+
        }
+
      }
+
    },
+
    "node_modules/bare-fs": {
+
      "version": "4.5.6",
+
      "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.6.tgz",
+
      "integrity": "sha512-1QovqDrR80Pmt5HPAsMsXTCFcDYr+NSUKW6nd6WO5v0JBmnItc/irNRzm2KOQ5oZ69P37y+AMujNyNtG+1Rggw==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "bare-events": "^2.5.4",
+
        "bare-path": "^3.0.0",
+
        "bare-stream": "^2.6.4",
+
        "bare-url": "^2.2.2",
+
        "fast-fifo": "^1.3.2"
+
      },
+
      "engines": {
+
        "bare": ">=1.16.0"
+
      },
+
      "peerDependencies": {
+
        "bare-buffer": "*"
+
      },
+
      "peerDependenciesMeta": {
+
        "bare-buffer": {
+
          "optional": true
+
        }
+
      }
+
    },
+
    "node_modules/bare-os": {
+
      "version": "3.8.6",
+
      "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.8.6.tgz",
+
      "integrity": "sha512-l8xaNWWb/bXuzgsrlF5jaa5QYDJ9S0ddd54cP6CH+081+5iPrbJiCfBWQqrWYzmUhCbsH+WR6qxo9MeHVCr0MQ==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "engines": {
+
        "bare": ">=1.14.0"
+
      }
+
    },
+
    "node_modules/bare-path": {
+
      "version": "3.0.0",
+
      "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz",
+
      "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "bare-os": "^3.0.1"
+
      }
+
    },
+
    "node_modules/bare-stream": {
+
      "version": "2.11.0",
+
      "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.11.0.tgz",
+
      "integrity": "sha512-Y/+iQ49fL3rIn6w/AVxI/2+BRrpmzJvdWt5Jv8Za6Ngqc6V227c+pYjYYgLdpR3MwQ9ObVXD0ZrqoBztakM0rw==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "streamx": "^2.25.0",
+
        "teex": "^1.0.1"
+
      },
+
      "peerDependencies": {
+
        "bare-abort-controller": "*",
+
        "bare-buffer": "*",
+
        "bare-events": "*"
+
      },
+
      "peerDependenciesMeta": {
+
        "bare-abort-controller": {
+
          "optional": true
+
        },
+
        "bare-buffer": {
+
          "optional": true
+
        },
+
        "bare-events": {
+
          "optional": true
+
        }
+
      }
+
    },
+
    "node_modules/bare-url": {
+
      "version": "2.4.0",
+
      "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.0.tgz",
+
      "integrity": "sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "bare-path": "^3.0.0"
+
      }
+
    },
+
    "node_modules/basic-ftp": {
+
      "version": "5.2.0",
+
      "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz",
+
      "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=10.0.0"
+
      }
+
    },
+
    "node_modules/brace-expansion": {
+
      "version": "5.0.5",
+
      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+
      "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "balanced-match": "^4.0.2"
+
      },
+
      "engines": {
+
        "node": "18 || 20 || >=22"
+
      }
+
    },
+
    "node_modules/buffer-crc32": {
+
      "version": "0.2.13",
+
      "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+
      "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": "*"
+
      }
+
    },
+
    "node_modules/callsites": {
+
      "version": "3.1.0",
+
      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+
      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=6"
+
      }
+
    },
+
    "node_modules/ccount": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+
      "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "funding": {
+
        "type": "github",
+
        "url": "https://github.com/sponsors/wooorm"
+
      }
+
    },
+
    "node_modules/chalk": {
+
      "version": "5.6.2",
+
      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
+
      "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+
      },
+
      "funding": {
+
        "url": "https://github.com/chalk/chalk?sponsor=1"
+
      }
+
    },
+
    "node_modules/character-entities": {
+
      "version": "2.0.2",
+
      "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+
      "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "funding": {
+
        "type": "github",
+
        "url": "https://github.com/sponsors/wooorm"
+
      }
+
    },
+
    "node_modules/chromium-bidi": {
+
      "version": "14.0.0",
+
      "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz",
+
      "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "mitt": "^3.0.1",
+
        "zod": "^3.24.1"
+
      },
+
      "peerDependencies": {
+
        "devtools-protocol": "*"
+
      }
+
    },
+
    "node_modules/cli-cursor": {
+
      "version": "5.0.0",
+
      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+
      "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "restore-cursor": "^5.0.0"
+
      },
+
      "engines": {
+
        "node": ">=18"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/cli-spinners": {
+
      "version": "3.4.0",
+
      "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz",
+
      "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=18.20"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/cliui": {
+
      "version": "8.0.1",
+
      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+
      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+
      "dev": true,
+
      "license": "ISC",
+
      "dependencies": {
+
        "string-width": "^4.2.0",
+
        "strip-ansi": "^6.0.1",
+
        "wrap-ansi": "^7.0.0"
+
      },
+
      "engines": {
+
        "node": ">=12"
+
      }
+
    },
+
    "node_modules/cliui/node_modules/ansi-regex": {
+
      "version": "5.0.1",
+
      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+
      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=8"
+
      }
+
    },
+
    "node_modules/cliui/node_modules/string-width": {
+
      "version": "4.2.3",
+
      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+
      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "emoji-regex": "^8.0.0",
+
        "is-fullwidth-code-point": "^3.0.0",
+
        "strip-ansi": "^6.0.1"
+
      },
+
      "engines": {
+
        "node": ">=8"
+
      }
+
    },
+
    "node_modules/cliui/node_modules/strip-ansi": {
+
      "version": "6.0.1",
+
      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+
      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "ansi-regex": "^5.0.1"
+
      },
+
      "engines": {
+
        "node": ">=8"
+
      }
+
    },
+
    "node_modules/color-convert": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+
      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "color-name": "~1.1.4"
+
      },
+
      "engines": {
+
        "node": ">=7.0.0"
+
      }
+
    },
+
    "node_modules/color-name": {
+
      "version": "1.1.4",
+
      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+
      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/commander": {
+
      "version": "14.0.3",
+
      "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+
      "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=20"
+
      }
+
    },
+
    "node_modules/cosmiconfig": {
+
      "version": "9.0.1",
+
      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz",
+
      "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "env-paths": "^2.2.1",
+
        "import-fresh": "^3.3.0",
+
        "js-yaml": "^4.1.0",
+
        "parse-json": "^5.2.0"
+
      },
+
      "engines": {
+
        "node": ">=14"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/d-fischer"
+
      },
+
      "peerDependencies": {
+
        "typescript": ">=4.9.5"
+
      },
+
      "peerDependenciesMeta": {
+
        "typescript": {
+
          "optional": true
+
        }
+
      }
+
    },
+
    "node_modules/data-uri-to-buffer": {
+
      "version": "6.0.2",
+
      "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
+
      "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">= 14"
+
      }
+
    },
+
    "node_modules/debug": {
+
      "version": "4.4.3",
+
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+
      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "ms": "^2.1.3"
+
      },
+
      "engines": {
+
        "node": ">=6.0"
+
      },
+
      "peerDependenciesMeta": {
+
        "supports-color": {
+
          "optional": true
+
        }
+
      }
+
    },
+
    "node_modules/decode-named-character-reference": {
+
      "version": "1.3.0",
+
      "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
+
      "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "character-entities": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "github",
+
        "url": "https://github.com/sponsors/wooorm"
+
      }
+
    },
+
    "node_modules/degenerator": {
+
      "version": "5.0.1",
+
      "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
+
      "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "ast-types": "^0.13.4",
+
        "escodegen": "^2.1.0",
+
        "esprima": "^4.0.1"
+
      },
+
      "engines": {
+
        "node": ">= 14"
+
      }
+
    },
+
    "node_modules/dequal": {
+
      "version": "2.0.3",
+
      "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+
      "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=6"
+
      }
+
    },
+
    "node_modules/devlop": {
+
      "version": "1.1.0",
+
      "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+
      "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "dequal": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "github",
+
        "url": "https://github.com/sponsors/wooorm"
+
      }
+
    },
+
    "node_modules/devtools-protocol": {
+
      "version": "0.0.1581282",
+
      "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1581282.tgz",
+
      "integrity": "sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ==",
+
      "dev": true,
+
      "license": "BSD-3-Clause"
+
    },
+
    "node_modules/discontinuous-range": {
+
      "version": "1.0.0",
+
      "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
+
      "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/emoji-regex": {
+
      "version": "8.0.0",
+
      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+
      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/end-of-stream": {
+
      "version": "1.4.5",
+
      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+
      "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "once": "^1.4.0"
+
      }
+
    },
+
    "node_modules/env-paths": {
+
      "version": "2.2.1",
+
      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+
      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=6"
+
      }
+
    },
+
    "node_modules/error-ex": {
+
      "version": "1.3.4",
+
      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
+
      "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "is-arrayish": "^0.2.1"
+
      }
+
    },
+
    "node_modules/escalade": {
+
      "version": "3.2.0",
+
      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+
      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=6"
+
      }
+
    },
+
    "node_modules/escape-string-regexp": {
+
      "version": "5.0.0",
+
      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+
      "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=12"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/escodegen": {
+
      "version": "2.1.0",
+
      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+
      "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+
      "dev": true,
+
      "license": "BSD-2-Clause",
+
      "dependencies": {
+
        "esprima": "^4.0.1",
+
        "estraverse": "^5.2.0",
+
        "esutils": "^2.0.2"
+
      },
+
      "bin": {
+
        "escodegen": "bin/escodegen.js",
+
        "esgenerate": "bin/esgenerate.js"
+
      },
+
      "engines": {
+
        "node": ">=6.0"
+
      },
+
      "optionalDependencies": {
+
        "source-map": "~0.6.1"
+
      }
+
    },
+
    "node_modules/esprima": {
+
      "version": "4.0.1",
+
      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+
      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+
      "dev": true,
+
      "license": "BSD-2-Clause",
+
      "bin": {
+
        "esparse": "bin/esparse.js",
+
        "esvalidate": "bin/esvalidate.js"
+
      },
+
      "engines": {
+
        "node": ">=4"
+
      }
+
    },
+
    "node_modules/estraverse": {
+
      "version": "5.3.0",
+
      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+
      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+
      "dev": true,
+
      "license": "BSD-2-Clause",
+
      "engines": {
+
        "node": ">=4.0"
+
      }
+
    },
+
    "node_modules/esutils": {
+
      "version": "2.0.3",
+
      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+
      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+
      "dev": true,
+
      "license": "BSD-2-Clause",
+
      "engines": {
+
        "node": ">=0.10.0"
+
      }
+
    },
+
    "node_modules/events-universal": {
+
      "version": "1.0.1",
+
      "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
+
      "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "bare-events": "^2.7.0"
+
      }
+
    },
+
    "node_modules/extend": {
+
      "version": "3.0.2",
+
      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+
      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/extract-zip": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+
      "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+
      "dev": true,
+
      "license": "BSD-2-Clause",
+
      "dependencies": {
+
        "debug": "^4.1.1",
+
        "get-stream": "^5.1.0",
+
        "yauzl": "^2.10.0"
+
      },
+
      "bin": {
+
        "extract-zip": "cli.js"
+
      },
+
      "engines": {
+
        "node": ">= 10.17.0"
+
      },
+
      "optionalDependencies": {
+
        "@types/yauzl": "^2.9.1"
+
      }
+
    },
+
    "node_modules/fast-fifo": {
+
      "version": "1.3.2",
+
      "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+
      "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/fd-slicer": {
+
      "version": "1.1.0",
+
      "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+
      "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "pend": "~1.2.0"
+
      }
+
    },
+
    "node_modules/get-caller-file": {
+
      "version": "2.0.5",
+
      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+
      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+
      "dev": true,
+
      "license": "ISC",
+
      "engines": {
+
        "node": "6.* || 8.* || >= 10.*"
+
      }
+
    },
+
    "node_modules/get-east-asian-width": {
+
      "version": "1.5.0",
+
      "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz",
+
      "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=18"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/get-stream": {
+
      "version": "5.2.0",
+
      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+
      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "pump": "^3.0.0"
+
      },
+
      "engines": {
+
        "node": ">=8"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/get-uri": {
+
      "version": "6.0.5",
+
      "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
+
      "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "basic-ftp": "^5.0.2",
+
        "data-uri-to-buffer": "^6.0.2",
+
        "debug": "^4.3.4"
+
      },
+
      "engines": {
+
        "node": ">= 14"
+
      }
+
    },
+
    "node_modules/github-slugger": {
+
      "version": "2.0.0",
+
      "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz",
+
      "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
+
      "dev": true,
+
      "license": "ISC"
+
    },
+
    "node_modules/glob": {
+
      "version": "13.0.6",
+
      "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
+
      "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
+
      "dev": true,
+
      "license": "BlueOak-1.0.0",
+
      "dependencies": {
+
        "minimatch": "^10.2.2",
+
        "minipass": "^7.1.3",
+
        "path-scurry": "^2.0.2"
+
      },
+
      "engines": {
+
        "node": "18 || 20 || >=22"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/isaacs"
+
      }
+
    },
+
    "node_modules/http-proxy-agent": {
+
      "version": "7.0.2",
+
      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+
      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "agent-base": "^7.1.0",
+
        "debug": "^4.3.4"
+
      },
+
      "engines": {
+
        "node": ">= 14"
+
      }
+
    },
+
    "node_modules/https-proxy-agent": {
+
      "version": "7.0.6",
+
      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+
      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "agent-base": "^7.1.2",
+
        "debug": "4"
+
      },
+
      "engines": {
+
        "node": ">= 14"
+
      }
+
    },
+
    "node_modules/ignore": {
+
      "version": "7.0.5",
+
      "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+
      "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">= 4"
+
      }
+
    },
+
    "node_modules/import-fresh": {
+
      "version": "3.3.1",
+
      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+
      "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "parent-module": "^1.0.0",
+
        "resolve-from": "^4.0.0"
+
      },
+
      "engines": {
+
        "node": ">=6"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/ip-address": {
+
      "version": "10.1.0",
+
      "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
+
      "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">= 12"
+
      }
+
    },
+
    "node_modules/is-arrayish": {
+
      "version": "0.2.1",
+
      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+
      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/is-fullwidth-code-point": {
+
      "version": "3.0.0",
+
      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+
      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=8"
+
      }
+
    },
+
    "node_modules/is-interactive": {
+
      "version": "2.0.0",
+
      "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
+
      "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=12"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/is-plain-obj": {
+
      "version": "4.1.0",
+
      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+
      "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=12"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/is-unicode-supported": {
+
      "version": "2.1.0",
+
      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+
      "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=18"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/joi": {
+
      "version": "18.1.2",
+
      "resolved": "https://registry.npmjs.org/joi/-/joi-18.1.2.tgz",
+
      "integrity": "sha512-rF5MAmps5esSlhCA+N1b6IYHDw9j/btzGaqfgie522jS02Ju/HXBxamlXVlKEHAxoMKQL77HWI8jlqWsFuekZA==",
+
      "dev": true,
+
      "license": "BSD-3-Clause",
+
      "dependencies": {
+
        "@hapi/address": "^5.1.1",
+
        "@hapi/formula": "^3.0.2",
+
        "@hapi/hoek": "^11.0.7",
+
        "@hapi/pinpoint": "^2.0.1",
+
        "@hapi/tlds": "^1.1.1",
+
        "@hapi/topo": "^6.0.2",
+
        "@standard-schema/spec": "^1.1.0"
+
      },
+
      "engines": {
+
        "node": ">= 20"
+
      }
+
    },
+
    "node_modules/js-tokens": {
+
      "version": "4.0.0",
+
      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+
      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/js-yaml": {
+
      "version": "4.1.1",
+
      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+
      "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "argparse": "^2.0.1"
+
      },
+
      "bin": {
+
        "js-yaml": "bin/js-yaml.js"
+
      }
+
    },
+
    "node_modules/json-parse-even-better-errors": {
+
      "version": "2.3.1",
+
      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+
      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/kleur": {
+
      "version": "4.1.5",
+
      "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
+
      "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=6"
+
      }
+
    },
+
    "node_modules/lines-and-columns": {
+
      "version": "1.2.4",
+
      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+
      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/log-symbols": {
+
      "version": "7.0.1",
+
      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz",
+
      "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "is-unicode-supported": "^2.0.0",
+
        "yoctocolors": "^2.1.1"
+
      },
+
      "engines": {
+
        "node": ">=18"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/longest-streak": {
+
      "version": "3.1.0",
+
      "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+
      "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+
      "dev": true,
+
      "license": "MIT",
+
      "funding": {
+
        "type": "github",
+
        "url": "https://github.com/sponsors/wooorm"
+
      }
+
    },
+
    "node_modules/lru-cache": {
+
      "version": "11.2.7",
+
      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz",
+
      "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==",
+
      "dev": true,
+
      "license": "BlueOak-1.0.0",
+
      "engines": {
+
        "node": "20 || >=22"
+
      }
+
    },
+
    "node_modules/markdown-table": {
+
      "version": "3.0.4",
+
      "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+
      "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "funding": {
+
        "type": "github",
+
        "url": "https://github.com/sponsors/wooorm"
+
      }
+
    },
+
    "node_modules/mdast-util-find-and-replace": {
+
      "version": "3.0.2",
+
      "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+
      "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "escape-string-regexp": "^5.0.0",
+
        "unist-util-is": "^6.0.0",
+
        "unist-util-visit-parents": "^6.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/mdast-util-from-markdown": {
+
      "version": "2.0.3",
+
      "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz",
+
      "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "@types/unist": "^3.0.0",
+
        "decode-named-character-reference": "^1.0.0",
+
        "devlop": "^1.0.0",
+
        "mdast-util-to-string": "^4.0.0",
+
        "micromark": "^4.0.0",
+
        "micromark-util-decode-numeric-character-reference": "^2.0.0",
+
        "micromark-util-decode-string": "^2.0.0",
+
        "micromark-util-normalize-identifier": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0",
+
        "unist-util-stringify-position": "^4.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/mdast-util-gfm": {
+
      "version": "3.1.0",
+
      "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+
      "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "mdast-util-from-markdown": "^2.0.0",
+
        "mdast-util-gfm-autolink-literal": "^2.0.0",
+
        "mdast-util-gfm-footnote": "^2.0.0",
+
        "mdast-util-gfm-strikethrough": "^2.0.0",
+
        "mdast-util-gfm-table": "^2.0.0",
+
        "mdast-util-gfm-task-list-item": "^2.0.0",
+
        "mdast-util-to-markdown": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/mdast-util-gfm-autolink-literal": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+
      "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "ccount": "^2.0.0",
+
        "devlop": "^1.0.0",
+
        "mdast-util-find-and-replace": "^3.0.0",
+
        "micromark-util-character": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/mdast-util-gfm-footnote": {
+
      "version": "2.1.0",
+
      "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+
      "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "devlop": "^1.1.0",
+
        "mdast-util-from-markdown": "^2.0.0",
+
        "mdast-util-to-markdown": "^2.0.0",
+
        "micromark-util-normalize-identifier": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/mdast-util-gfm-strikethrough": {
+
      "version": "2.0.0",
+
      "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+
      "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "mdast-util-from-markdown": "^2.0.0",
+
        "mdast-util-to-markdown": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/mdast-util-gfm-table": {
+
      "version": "2.0.0",
+
      "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+
      "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "devlop": "^1.0.0",
+
        "markdown-table": "^3.0.0",
+
        "mdast-util-from-markdown": "^2.0.0",
+
        "mdast-util-to-markdown": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/mdast-util-gfm-task-list-item": {
+
      "version": "2.0.0",
+
      "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+
      "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "devlop": "^1.0.0",
+
        "mdast-util-from-markdown": "^2.0.0",
+
        "mdast-util-to-markdown": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/mdast-util-phrasing": {
+
      "version": "4.1.0",
+
      "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+
      "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "unist-util-is": "^6.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/mdast-util-to-markdown": {
+
      "version": "2.1.2",
+
      "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+
      "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "@types/unist": "^3.0.0",
+
        "longest-streak": "^3.0.0",
+
        "mdast-util-phrasing": "^4.0.0",
+
        "mdast-util-to-string": "^4.0.0",
+
        "micromark-util-classify-character": "^2.0.0",
+
        "micromark-util-decode-string": "^2.0.0",
+
        "unist-util-visit": "^5.0.0",
+
        "zwitch": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/mdast-util-to-string": {
+
      "version": "4.0.0",
+
      "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+
      "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/micromark": {
+
      "version": "4.0.2",
+
      "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
+
      "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/debug": "^4.0.0",
+
        "debug": "^4.0.0",
+
        "decode-named-character-reference": "^1.0.0",
+
        "devlop": "^1.0.0",
+
        "micromark-core-commonmark": "^2.0.0",
+
        "micromark-factory-space": "^2.0.0",
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-chunked": "^2.0.0",
+
        "micromark-util-combine-extensions": "^2.0.0",
+
        "micromark-util-decode-numeric-character-reference": "^2.0.0",
+
        "micromark-util-encode": "^2.0.0",
+
        "micromark-util-normalize-identifier": "^2.0.0",
+
        "micromark-util-resolve-all": "^2.0.0",
+
        "micromark-util-sanitize-uri": "^2.0.0",
+
        "micromark-util-subtokenize": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-core-commonmark": {
+
      "version": "2.0.3",
+
      "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+
      "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "decode-named-character-reference": "^1.0.0",
+
        "devlop": "^1.0.0",
+
        "micromark-factory-destination": "^2.0.0",
+
        "micromark-factory-label": "^2.0.0",
+
        "micromark-factory-space": "^2.0.0",
+
        "micromark-factory-title": "^2.0.0",
+
        "micromark-factory-whitespace": "^2.0.0",
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-chunked": "^2.0.0",
+
        "micromark-util-classify-character": "^2.0.0",
+
        "micromark-util-html-tag-name": "^2.0.0",
+
        "micromark-util-normalize-identifier": "^2.0.0",
+
        "micromark-util-resolve-all": "^2.0.0",
+
        "micromark-util-subtokenize": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-extension-gfm": {
+
      "version": "3.0.0",
+
      "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+
      "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-extension-gfm-autolink-literal": "^2.0.0",
+
        "micromark-extension-gfm-footnote": "^2.0.0",
+
        "micromark-extension-gfm-strikethrough": "^2.0.0",
+
        "micromark-extension-gfm-table": "^2.0.0",
+
        "micromark-extension-gfm-tagfilter": "^2.0.0",
+
        "micromark-extension-gfm-task-list-item": "^2.0.0",
+
        "micromark-util-combine-extensions": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/micromark-extension-gfm-autolink-literal": {
+
      "version": "2.1.0",
+
      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+
      "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-sanitize-uri": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/micromark-extension-gfm-footnote": {
+
      "version": "2.1.0",
+
      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+
      "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "devlop": "^1.0.0",
+
        "micromark-core-commonmark": "^2.0.0",
+
        "micromark-factory-space": "^2.0.0",
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-normalize-identifier": "^2.0.0",
+
        "micromark-util-sanitize-uri": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/micromark-extension-gfm-strikethrough": {
+
      "version": "2.1.0",
+
      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+
      "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "devlop": "^1.0.0",
+
        "micromark-util-chunked": "^2.0.0",
+
        "micromark-util-classify-character": "^2.0.0",
+
        "micromark-util-resolve-all": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/micromark-extension-gfm-table": {
+
      "version": "2.1.1",
+
      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+
      "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "devlop": "^1.0.0",
+
        "micromark-factory-space": "^2.0.0",
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/micromark-extension-gfm-tagfilter": {
+
      "version": "2.0.0",
+
      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+
      "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-types": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/micromark-extension-gfm-task-list-item": {
+
      "version": "2.1.0",
+
      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+
      "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "devlop": "^1.0.0",
+
        "micromark-factory-space": "^2.0.0",
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/micromark-factory-destination": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+
      "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-factory-label": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+
      "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "devlop": "^1.0.0",
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-factory-space": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+
      "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-factory-title": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+
      "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-factory-space": "^2.0.0",
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-factory-whitespace": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+
      "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-factory-space": "^2.0.0",
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-character": {
+
      "version": "2.1.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+
      "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-chunked": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+
      "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-symbol": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-classify-character": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+
      "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-combine-extensions": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+
      "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-chunked": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-decode-numeric-character-reference": {
+
      "version": "2.0.2",
+
      "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+
      "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-symbol": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-decode-string": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+
      "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "decode-named-character-reference": "^1.0.0",
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-decode-numeric-character-reference": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-encode": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+
      "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT"
+
    },
+
    "node_modules/micromark-util-html-tag-name": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+
      "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT"
+
    },
+
    "node_modules/micromark-util-normalize-identifier": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+
      "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-symbol": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-resolve-all": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+
      "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-sanitize-uri": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+
      "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "micromark-util-character": "^2.0.0",
+
        "micromark-util-encode": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-subtokenize": {
+
      "version": "2.1.0",
+
      "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+
      "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT",
+
      "dependencies": {
+
        "devlop": "^1.0.0",
+
        "micromark-util-chunked": "^2.0.0",
+
        "micromark-util-symbol": "^2.0.0",
+
        "micromark-util-types": "^2.0.0"
+
      }
+
    },
+
    "node_modules/micromark-util-symbol": {
+
      "version": "2.0.1",
+
      "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+
      "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT"
+
    },
+
    "node_modules/micromark-util-types": {
+
      "version": "2.0.2",
+
      "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+
      "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+
      "dev": true,
+
      "funding": [
+
        {
+
          "type": "GitHub Sponsors",
+
          "url": "https://github.com/sponsors/unifiedjs"
+
        },
+
        {
+
          "type": "OpenCollective",
+
          "url": "https://opencollective.com/unified"
+
        }
+
      ],
+
      "license": "MIT"
+
    },
+
    "node_modules/mimic-function": {
+
      "version": "5.0.1",
+
      "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+
      "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=18"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/minimatch": {
+
      "version": "10.2.5",
+
      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+
      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+
      "dev": true,
+
      "license": "BlueOak-1.0.0",
+
      "dependencies": {
+
        "brace-expansion": "^5.0.5"
+
      },
+
      "engines": {
+
        "node": "18 || 20 || >=22"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/isaacs"
+
      }
+
    },
+
    "node_modules/minipass": {
+
      "version": "7.1.3",
+
      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+
      "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+
      "dev": true,
+
      "license": "BlueOak-1.0.0",
+
      "engines": {
+
        "node": ">=16 || 14 >=14.17"
+
      }
+
    },
+
    "node_modules/mitt": {
+
      "version": "3.0.1",
+
      "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+
      "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/moo": {
+
      "version": "0.5.3",
+
      "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.3.tgz",
+
      "integrity": "sha512-m2fmM2dDm7GZQsY7KK2cme8agi+AAljILjQnof7p1ZMDe6dQ4bdnSMx0cPppudoeNv5hEFQirN6u+O4fDE0IWA==",
+
      "dev": true,
+
      "license": "BSD-3-Clause"
+
    },
+
    "node_modules/ms": {
+
      "version": "2.1.3",
+
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+
      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/nearley": {
+
      "version": "2.20.1",
+
      "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz",
+
      "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "commander": "^2.19.0",
+
        "moo": "^0.5.0",
+
        "railroad-diagrams": "^1.0.0",
+
        "randexp": "0.4.6"
+
      },
+
      "bin": {
+
        "nearley-railroad": "bin/nearley-railroad.js",
+
        "nearley-test": "bin/nearley-test.js",
+
        "nearley-unparse": "bin/nearley-unparse.js",
+
        "nearleyc": "bin/nearleyc.js"
+
      },
+
      "funding": {
+
        "type": "individual",
+
        "url": "https://nearley.js.org/#give-to-nearley"
+
      }
+
    },
+
    "node_modules/nearley/node_modules/commander": {
+
      "version": "2.20.3",
+
      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+
      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/netmask": {
+
      "version": "2.0.2",
+
      "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
+
      "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">= 0.4.0"
+
      }
+
    },
+
    "node_modules/once": {
+
      "version": "1.4.0",
+
      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+
      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+
      "dev": true,
+
      "license": "ISC",
+
      "dependencies": {
+
        "wrappy": "1"
+
      }
+
    },
+
    "node_modules/onetime": {
+
      "version": "7.0.0",
+
      "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+
      "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "mimic-function": "^5.0.0"
+
      },
+
      "engines": {
+
        "node": ">=18"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/ora": {
+
      "version": "9.3.0",
+
      "resolved": "https://registry.npmjs.org/ora/-/ora-9.3.0.tgz",
+
      "integrity": "sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "chalk": "^5.6.2",
+
        "cli-cursor": "^5.0.0",
+
        "cli-spinners": "^3.2.0",
+
        "is-interactive": "^2.0.0",
+
        "is-unicode-supported": "^2.1.0",
+
        "log-symbols": "^7.0.1",
+
        "stdin-discarder": "^0.3.1",
+
        "string-width": "^8.1.0"
+
      },
+
      "engines": {
+
        "node": ">=20"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/pac-proxy-agent": {
+
      "version": "7.2.0",
+
      "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz",
+
      "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@tootallnate/quickjs-emscripten": "^0.23.0",
+
        "agent-base": "^7.1.2",
+
        "debug": "^4.3.4",
+
        "get-uri": "^6.0.1",
+
        "http-proxy-agent": "^7.0.0",
+
        "https-proxy-agent": "^7.0.6",
+
        "pac-resolver": "^7.0.1",
+
        "socks-proxy-agent": "^8.0.5"
+
      },
+
      "engines": {
+
        "node": ">= 14"
+
      }
+
    },
+
    "node_modules/pac-resolver": {
+
      "version": "7.0.1",
+
      "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
+
      "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "degenerator": "^5.0.0",
+
        "netmask": "^2.0.2"
+
      },
+
      "engines": {
+
        "node": ">= 14"
+
      }
+
    },
+
    "node_modules/parent-module": {
+
      "version": "1.0.1",
+
      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+
      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "callsites": "^3.0.0"
+
      },
+
      "engines": {
+
        "node": ">=6"
+
      }
+
    },
+
    "node_modules/parse-json": {
+
      "version": "5.2.0",
+
      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+
      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@babel/code-frame": "^7.0.0",
+
        "error-ex": "^1.3.1",
+
        "json-parse-even-better-errors": "^2.3.0",
+
        "lines-and-columns": "^1.1.6"
+
      },
+
      "engines": {
+
        "node": ">=8"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/path-scurry": {
+
      "version": "2.0.2",
+
      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
+
      "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+
      "dev": true,
+
      "license": "BlueOak-1.0.0",
+
      "dependencies": {
+
        "lru-cache": "^11.0.0",
+
        "minipass": "^7.1.2"
+
      },
+
      "engines": {
+
        "node": "18 || 20 || >=22"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/isaacs"
+
      }
+
    },
+
    "node_modules/pend": {
+
      "version": "1.2.0",
+
      "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+
      "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/picocolors": {
+
      "version": "1.1.1",
+
      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+
      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+
      "dev": true,
+
      "license": "ISC"
+
    },
+
    "node_modules/progress": {
+
      "version": "2.0.3",
+
      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+
      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=0.4.0"
+
      }
+
    },
+
    "node_modules/proxy-agent": {
+
      "version": "6.5.0",
+
      "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
+
      "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "agent-base": "^7.1.2",
+
        "debug": "^4.3.4",
+
        "http-proxy-agent": "^7.0.1",
+
        "https-proxy-agent": "^7.0.6",
+
        "lru-cache": "^7.14.1",
+
        "pac-proxy-agent": "^7.1.0",
+
        "proxy-from-env": "^1.1.0",
+
        "socks-proxy-agent": "^8.0.5"
+
      },
+
      "engines": {
+
        "node": ">= 14"
+
      }
+
    },
+
    "node_modules/proxy-agent/node_modules/lru-cache": {
+
      "version": "7.18.3",
+
      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+
      "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+
      "dev": true,
+
      "license": "ISC",
+
      "engines": {
+
        "node": ">=12"
+
      }
+
    },
+
    "node_modules/proxy-from-env": {
+
      "version": "1.1.0",
+
      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+
      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/pump": {
+
      "version": "3.0.4",
+
      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
+
      "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "end-of-stream": "^1.1.0",
+
        "once": "^1.3.1"
+
      }
+
    },
+
    "node_modules/puppeteer": {
+
      "version": "24.40.0",
+
      "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.40.0.tgz",
+
      "integrity": "sha512-IxQbDq93XHVVLWHrAkFP7F7iHvb9o0mgfsSIMlhHb+JM+JjM1V4v4MNSQfcRWJopx9dsNOr9adYv0U5fm9BJBQ==",
+
      "dev": true,
+
      "hasInstallScript": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "@puppeteer/browsers": "2.13.0",
+
        "chromium-bidi": "14.0.0",
+
        "cosmiconfig": "^9.0.0",
+
        "devtools-protocol": "0.0.1581282",
+
        "puppeteer-core": "24.40.0",
+
        "typed-query-selector": "^2.12.1"
+
      },
+
      "bin": {
+
        "puppeteer": "lib/cjs/puppeteer/node/cli.js"
+
      },
+
      "engines": {
+
        "node": ">=18"
+
      }
+
    },
+
    "node_modules/puppeteer-core": {
+
      "version": "24.40.0",
+
      "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.40.0.tgz",
+
      "integrity": "sha512-MWL3XbUCfVgGR0gRsidzT6oKJT2QydPLhMITU6HoVWiiv4gkb6gJi3pcdAa8q4HwjBTbqISOWVP4aJiiyUJvag==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "@puppeteer/browsers": "2.13.0",
+
        "chromium-bidi": "14.0.0",
+
        "debug": "^4.4.3",
+
        "devtools-protocol": "0.0.1581282",
+
        "typed-query-selector": "^2.12.1",
+
        "webdriver-bidi-protocol": "0.4.1",
+
        "ws": "^8.19.0"
+
      },
+
      "engines": {
+
        "node": ">=18"
+
      }
+
    },
+
    "node_modules/railroad-diagrams": {
+
      "version": "1.0.0",
+
      "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
+
      "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==",
+
      "dev": true,
+
      "license": "CC0-1.0"
+
    },
+
    "node_modules/randexp": {
+
      "version": "0.4.6",
+
      "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
+
      "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "discontinuous-range": "1.0.0",
+
        "ret": "~0.1.10"
+
      },
+
      "engines": {
+
        "node": ">=0.12"
+
      }
+
    },
+
    "node_modules/remark-gfm": {
+
      "version": "4.0.1",
+
      "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+
      "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "mdast-util-gfm": "^3.0.0",
+
        "micromark-extension-gfm": "^3.0.0",
+
        "remark-parse": "^11.0.0",
+
        "remark-stringify": "^11.0.0",
+
        "unified": "^11.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/remark-parse": {
+
      "version": "11.0.0",
+
      "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+
      "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "mdast-util-from-markdown": "^2.0.0",
+
        "micromark-util-types": "^2.0.0",
+
        "unified": "^11.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/remark-stringify": {
+
      "version": "11.0.0",
+
      "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+
      "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/mdast": "^4.0.0",
+
        "mdast-util-to-markdown": "^2.0.0",
+
        "unified": "^11.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/require-directory": {
+
      "version": "2.1.1",
+
      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+
      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=0.10.0"
+
      }
+
    },
+
    "node_modules/resolve-from": {
+
      "version": "4.0.0",
+
      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+
      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=4"
+
      }
+
    },
+
    "node_modules/restore-cursor": {
+
      "version": "5.1.0",
+
      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+
      "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "onetime": "^7.0.0",
+
        "signal-exit": "^4.1.0"
+
      },
+
      "engines": {
+
        "node": ">=18"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/ret": {
+
      "version": "0.1.15",
+
      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+
      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=0.12"
+
      }
+
    },
+
    "node_modules/semver": {
+
      "version": "7.7.4",
+
      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+
      "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+
      "dev": true,
+
      "license": "ISC",
+
      "bin": {
+
        "semver": "bin/semver.js"
+
      },
+
      "engines": {
+
        "node": ">=10"
+
      }
+
    },
+
    "node_modules/signal-exit": {
+
      "version": "4.1.0",
+
      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+
      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+
      "dev": true,
+
      "license": "ISC",
+
      "engines": {
+
        "node": ">=14"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/isaacs"
+
      }
+
    },
+
    "node_modules/smart-buffer": {
+
      "version": "4.2.0",
+
      "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+
      "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">= 6.0.0",
+
        "npm": ">= 3.0.0"
+
      }
+
    },
+
    "node_modules/socks": {
+
      "version": "2.8.7",
+
      "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
+
      "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "ip-address": "^10.0.1",
+
        "smart-buffer": "^4.2.0"
+
      },
+
      "engines": {
+
        "node": ">= 10.0.0",
+
        "npm": ">= 3.0.0"
+
      }
+
    },
+
    "node_modules/socks-proxy-agent": {
+
      "version": "8.0.5",
+
      "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+
      "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "agent-base": "^7.1.2",
+
        "debug": "^4.3.4",
+
        "socks": "^2.8.3"
+
      },
+
      "engines": {
+
        "node": ">= 14"
+
      }
+
    },
+
    "node_modules/source-map": {
+
      "version": "0.6.1",
+
      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+
      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+
      "dev": true,
+
      "license": "BSD-3-Clause",
+
      "optional": true,
+
      "engines": {
+
        "node": ">=0.10.0"
+
      }
+
    },
+
    "node_modules/stdin-discarder": {
+
      "version": "0.3.1",
+
      "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.1.tgz",
+
      "integrity": "sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=18"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/streamx": {
+
      "version": "2.25.0",
+
      "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz",
+
      "integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "events-universal": "^1.0.0",
+
        "fast-fifo": "^1.3.2",
+
        "text-decoder": "^1.1.0"
+
      }
+
    },
+
    "node_modules/string-width": {
+
      "version": "8.2.0",
+
      "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz",
+
      "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "get-east-asian-width": "^1.5.0",
+
        "strip-ansi": "^7.1.2"
+
      },
+
      "engines": {
+
        "node": ">=20"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/strip-ansi": {
+
      "version": "7.2.0",
+
      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+
      "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "ansi-regex": "^6.2.2"
+
      },
+
      "engines": {
+
        "node": ">=12"
+
      },
+
      "funding": {
+
        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+
      }
+
    },
+
    "node_modules/tar-fs": {
+
      "version": "3.1.2",
+
      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz",
+
      "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "pump": "^3.0.0",
+
        "tar-stream": "^3.1.5"
+
      },
+
      "optionalDependencies": {
+
        "bare-fs": "^4.0.1",
+
        "bare-path": "^3.0.0"
+
      }
+
    },
+
    "node_modules/tar-stream": {
+
      "version": "3.1.8",
+
      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz",
+
      "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "b4a": "^1.6.4",
+
        "bare-fs": "^4.5.5",
+
        "fast-fifo": "^1.2.0",
+
        "streamx": "^2.15.0"
+
      }
+
    },
+
    "node_modules/teex": {
+
      "version": "1.0.1",
+
      "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
+
      "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "streamx": "^2.12.5"
+
      }
+
    },
+
    "node_modules/text-decoder": {
+
      "version": "1.2.7",
+
      "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
+
      "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
+
      "dev": true,
+
      "license": "Apache-2.0",
+
      "dependencies": {
+
        "b4a": "^1.6.4"
+
      }
+
    },
+
    "node_modules/trough": {
+
      "version": "2.2.0",
+
      "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+
      "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "funding": {
+
        "type": "github",
+
        "url": "https://github.com/sponsors/wooorm"
+
      }
+
    },
+
    "node_modules/tslib": {
+
      "version": "2.8.1",
+
      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+
      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+
      "dev": true,
+
      "license": "0BSD"
+
    },
+
    "node_modules/typed-query-selector": {
+
      "version": "2.12.1",
+
      "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.1.tgz",
+
      "integrity": "sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==",
+
      "dev": true,
+
      "license": "MIT"
+
    },
+
    "node_modules/undici-types": {
+
      "version": "7.18.2",
+
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+
      "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
+
      "dev": true,
+
      "license": "MIT",
+
      "optional": true
+
    },
+
    "node_modules/unified": {
+
      "version": "11.0.5",
+
      "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+
      "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/unist": "^3.0.0",
+
        "bail": "^2.0.0",
+
        "devlop": "^1.0.0",
+
        "extend": "^3.0.0",
+
        "is-plain-obj": "^4.0.0",
+
        "trough": "^2.0.0",
+
        "vfile": "^6.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/unist-util-is": {
+
      "version": "6.0.1",
+
      "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+
      "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/unist": "^3.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/unist-util-stringify-position": {
+
      "version": "4.0.0",
+
      "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+
      "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/unist": "^3.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/unist-util-visit": {
+
      "version": "5.1.0",
+
      "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+
      "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/unist": "^3.0.0",
+
        "unist-util-is": "^6.0.0",
+
        "unist-util-visit-parents": "^6.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/unist-util-visit-parents": {
+
      "version": "6.0.2",
+
      "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+
      "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/unist": "^3.0.0",
+
        "unist-util-is": "^6.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/vfile": {
+
      "version": "6.0.3",
+
      "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+
      "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/unist": "^3.0.0",
+
        "vfile-message": "^4.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/vfile-message": {
+
      "version": "4.0.3",
+
      "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+
      "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "@types/unist": "^3.0.0",
+
        "unist-util-stringify-position": "^4.0.0"
+
      },
+
      "funding": {
+
        "type": "opencollective",
+
        "url": "https://opencollective.com/unified"
+
      }
+
    },
+
    "node_modules/webdriver-bidi-protocol": {
+
      "version": "0.4.1",
+
      "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz",
+
      "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==",
+
      "dev": true,
+
      "license": "Apache-2.0"
+
    },
+
    "node_modules/wrap-ansi": {
+
      "version": "7.0.0",
+
      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+
      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "ansi-styles": "^4.0.0",
+
        "string-width": "^4.1.0",
+
        "strip-ansi": "^6.0.0"
+
      },
+
      "engines": {
+
        "node": ">=10"
+
      },
+
      "funding": {
+
        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+
      }
+
    },
+
    "node_modules/wrap-ansi/node_modules/ansi-regex": {
+
      "version": "5.0.1",
+
      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+
      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=8"
+
      }
+
    },
+
    "node_modules/wrap-ansi/node_modules/string-width": {
+
      "version": "4.2.3",
+
      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+
      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "emoji-regex": "^8.0.0",
+
        "is-fullwidth-code-point": "^3.0.0",
+
        "strip-ansi": "^6.0.1"
+
      },
+
      "engines": {
+
        "node": ">=8"
+
      }
+
    },
+
    "node_modules/wrap-ansi/node_modules/strip-ansi": {
+
      "version": "6.0.1",
+
      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+
      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "ansi-regex": "^5.0.1"
+
      },
+
      "engines": {
+
        "node": ">=8"
+
      }
+
    },
+
    "node_modules/wrappy": {
+
      "version": "1.0.2",
+
      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+
      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+
      "dev": true,
+
      "license": "ISC"
+
    },
+
    "node_modules/ws": {
+
      "version": "8.20.0",
+
      "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
+
      "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=10.0.0"
+
      },
+
      "peerDependencies": {
+
        "bufferutil": "^4.0.1",
+
        "utf-8-validate": ">=5.0.2"
+
      },
+
      "peerDependenciesMeta": {
+
        "bufferutil": {
+
          "optional": true
+
        },
+
        "utf-8-validate": {
+
          "optional": true
+
        }
+
      }
+
    },
+
    "node_modules/y18n": {
+
      "version": "5.0.8",
+
      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+
      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+
      "dev": true,
+
      "license": "ISC",
+
      "engines": {
+
        "node": ">=10"
+
      }
+
    },
+
    "node_modules/yargs": {
+
      "version": "17.7.2",
+
      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+
      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "cliui": "^8.0.1",
+
        "escalade": "^3.1.1",
+
        "get-caller-file": "^2.0.5",
+
        "require-directory": "^2.1.1",
+
        "string-width": "^4.2.3",
+
        "y18n": "^5.0.5",
+
        "yargs-parser": "^21.1.1"
+
      },
+
      "engines": {
+
        "node": ">=12"
+
      }
+
    },
+
    "node_modules/yargs-parser": {
+
      "version": "21.1.1",
+
      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+
      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+
      "dev": true,
+
      "license": "ISC",
+
      "engines": {
+
        "node": ">=12"
+
      }
+
    },
+
    "node_modules/yargs/node_modules/ansi-regex": {
+
      "version": "5.0.1",
+
      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+
      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=8"
+
      }
+
    },
+
    "node_modules/yargs/node_modules/string-width": {
+
      "version": "4.2.3",
+
      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+
      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "emoji-regex": "^8.0.0",
+
        "is-fullwidth-code-point": "^3.0.0",
+
        "strip-ansi": "^6.0.1"
+
      },
+
      "engines": {
+
        "node": ">=8"
+
      }
+
    },
+
    "node_modules/yargs/node_modules/strip-ansi": {
+
      "version": "6.0.1",
+
      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+
      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "ansi-regex": "^5.0.1"
+
      },
+
      "engines": {
+
        "node": ">=8"
+
      }
+
    },
+
    "node_modules/yauzl": {
+
      "version": "2.10.0",
+
      "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+
      "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+
      "dev": true,
+
      "license": "MIT",
+
      "dependencies": {
+
        "buffer-crc32": "~0.2.3",
+
        "fd-slicer": "~1.1.0"
+
      }
+
    },
+
    "node_modules/yoctocolors": {
+
      "version": "2.1.2",
+
      "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
+
      "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
+
      "dev": true,
+
      "license": "MIT",
+
      "engines": {
+
        "node": ">=18"
+
      },
+
      "funding": {
+
        "url": "https://github.com/sponsors/sindresorhus"
+
      }
+
    },
+
    "node_modules/zod": {
+
      "version": "3.25.76",
+
      "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+
      "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+
      "dev": true,
+
      "license": "MIT",
+
      "funding": {
+
        "url": "https://github.com/sponsors/colinhacks"
+
      }
+
    },
+
    "node_modules/zwitch": {
+
      "version": "2.0.4",
+
      "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+
      "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+
      "dev": true,
+
      "license": "MIT",
+
      "funding": {
+
        "type": "github",
+
        "url": "https://github.com/sponsors/wooorm"
+
      }
+
    }
+
  }
+
}
added package.json
@@ -0,0 +1,8 @@
+
{
+
  "name": "rips",
+
  "private": true,
+
  "devDependencies": {
+
    "nearley": "^2.20.1",
+
    "@umbrelladocs/linkspector": "^0.4.8"
+
  }
+
}