diff --git a/.github/DISCUSSION_TEMPLATE/ideas.yml b/.github/DISCUSSION_TEMPLATE/ideas.yml new file mode 100644 index 0000000..c0bb375 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/ideas.yml @@ -0,0 +1,24 @@ +title: "[Idea] " +labels: ["type: docs"] +body: + - type: markdown + attributes: + value: | + ## Idea / Integration + Share an integration surface, use case, or improvement. Not every idea + needs an RFC — start the conversation here. + - type: textarea + id: idea + attributes: + label: What's the idea? + validations: + required: true + - type: input + id: surface + attributes: + label: Enforcement surface (if applicable) + placeholder: "e.g. agent tool call, CI/CD stage, API gateway, service account" + - type: textarea + id: value + attributes: + label: Who benefits and why? diff --git a/.github/DISCUSSION_TEMPLATE/protocol-rfc.yml b/.github/DISCUSSION_TEMPLATE/protocol-rfc.yml new file mode 100644 index 0000000..8ce3877 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/protocol-rfc.yml @@ -0,0 +1,41 @@ +title: "[RFC] " +labels: ["type: protocol", "status: needs-rfc"] +body: + - type: markdown + attributes: + value: | + ## Protocol RFC Discussion + Use this to socialize a protocol change before opening a formal RFC in `rfcs/`. + Keep it focused on the **trust-before-execution** boundary. + - type: textarea + id: motivation + attributes: + label: Motivation + description: What trust, authority, decay, proof, or receipt gap does this address? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposal + description: The concrete change to grammar, semantics, schema, or the trust model. + validations: + required: true + - type: dropdown + id: layer + attributes: + label: Protocol layer + options: + - Language / grammar + - Trust scoring & decay + - Delegation & isnad chains + - Authority grants + - Proofs + - Execution receipts + validations: + required: true + - type: textarea + id: compat + attributes: + label: Compatibility & security impact + description: Backward compatibility, failure modes, and any fail-closed considerations. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..59032b5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# Trust Transfer Protocol — funding & sponsorship +# TTP is an open protocol stewarded by BlockSiFr. +github: [BlockSiFr] +custom: ["https://blocksifr.com"] diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..16112a7 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,47 @@ + + +## Summary + + + +## Protocol Layer Impact + + + +- [ ] Changes protocol grammar / `.ttp` language +- [ ] Changes a public schema (AuthorityGrant / Attestation / ExecutionReceipt / …) +- [ ] Changes trust scoring, decay, delegation, or proof semantics +- [ ] No protocol-surface change + +## Runtime Enforcement Impact + + + +## Receipt / Evidence Impact + + + +## Tests + + + +```text +``` + +## Screenshots or Diagrams + + + +## Checklist + +- [ ] `npm test` passes +- [ ] Docs / examples updated (if behavior changed) +- [ ] An RFC is linked for any protocol-surface change +- [ ] No `cleartext-dev` assumptions introduced into production paths +- [ ] Commits are signed off and scoped diff --git a/.github/labels.yml b/.github/labels.yml new file mode 100644 index 0000000..a548f27 --- /dev/null +++ b/.github/labels.yml @@ -0,0 +1,43 @@ +# Trust Transfer Protocol — canonical label system. +# Synced to the repo by .github/workflows/labels.yml (EndBug/label-sync). +# Colors follow the TTP brand token palette. + +- name: "type: protocol" + color: "00D4FF" + description: "Protocol grammar, semantics, or schema" +- name: "type: runtime" + color: "0066CC" + description: "Runtime authority gate and enforcement" +- name: "type: docs" + color: "8892A0" + description: "Documentation and concepts" +- name: "type: examples" + color: "00B8A9" + description: "Example .ttp files and integrations" +- name: "type: security" + color: "FF3D00" + description: "Security-relevant change or concern" + +- name: "status: good first issue" + color: "00E676" + description: "Approachable, well-scoped first contribution" +- name: "status: needs-rfc" + color: "FFB300" + description: "Requires a protocol RFC before implementation" + +- name: "priority: critical" + color: "FF3D00" + description: "Blocks correctness, security, or release" + +- name: "area: trust-decay" + color: "00D4FF" + description: "Trust scoring and decay model" +- name: "area: execution-receipts" + color: "00B8A9" + description: "Execution receipts and evidence chain" +- name: "area: zkp" + color: "0066CC" + description: "Zero-knowledge proofs" +- name: "area: compiler" + color: "8892A0" + description: ".ttp parser, evaluator, and CLI" diff --git a/.github/workflows/governed_execution.yml b/.github/workflows/governed_execution.yml deleted file mode 100644 index 9566fef..0000000 --- a/.github/workflows/governed_execution.yml +++ /dev/null @@ -1,161 +0,0 @@ -name: TTP / Governed Execution Proof - -on: - pull_request: - types: [opened, synchronize, reopened, ready_for_review] - -jobs: - govern: - name: Governed Execution - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - - outputs: - decision: ${{ steps.authorize.outputs.decision }} - receipt: ${{ steps.authorize.outputs.receipt }} - - steps: - # 1️⃣ Full checkout (safe and correct) - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # 2️⃣ Detect changed files - - name: Detect changes - id: changes - shell: bash - run: | - set -euo pipefail - BASE="${{ github.event.pull_request.base.sha }}" - HEAD="${{ github.event.pull_request.head.sha }}" - - git diff --name-only "$BASE" "$HEAD" > changed.txt || true - - echo "Changed files:" - cat changed.txt || true - - PATHS_JSON=$(jq -Rsc 'split("\n") | map(select(length > 0))' changed.txt) - echo "paths=$PATHS_JSON" >> "$GITHUB_OUTPUT" - - # 3️⃣ Ask external authority for permission - - name: Authorize execution - id: authorize - env: - AUTH_URL: ${{ secrets.RUNTIME_AUTH_URL }} - AUTH_TOKEN: ${{ secrets.RUNTIME_AUTH_TOKEN }} - shell: bash - run: | - set -euo pipefail - - if [ ! -s changed.txt ]; then - echo "No changes → deny by policy" - echo "decision=DENY" >> "$GITHUB_OUTPUT" - echo "receipt=local-noop-receipt" >> "$GITHUB_OUTPUT" - echo "receipt_json={\"receiptId\":\"local-noop-receipt\",\"decision\":\"DENY\",\"reason\":\"No changed files\",\"chainHash\":\"local-noop\"}" >> "$GITHUB_OUTPUT" - exit 0 - fi - - if [ -z "${AUTH_URL:-}" ]; then - echo "RUNTIME_AUTH_URL missing; failing closed with local deny receipt" - echo "decision=DENY" >> "$GITHUB_OUTPUT" - echo "receipt=local-missing-auth-url" >> "$GITHUB_OUTPUT" - echo "receipt_json={\"receiptId\":\"local-missing-auth-url\",\"decision\":\"DENY\",\"reason\":\"Missing RUNTIME_AUTH_URL\",\"chainHash\":\"local-missing-auth-url\"}" >> "$GITHUB_OUTPUT" - exit 0 - fi - - jq -n \ - --arg repo "${{ github.repository }}" \ - --arg actor "${{ github.actor }}" \ - --arg pr "${{ github.event.pull_request.number }}" \ - --arg sha "${{ github.event.pull_request.head.sha }}" \ - --argjson paths "$(jq -Rsc 'split("\n") | map(select(length > 0))' changed.txt)" \ - '{ - subject: "wi://github/actions/runner", - action: "pull_request.execute", - resource: ("repo:" + $repo + ":pr/" + $pr), - commitSha: $sha, - actor: $actor, - pathsTouched: $paths, - context: { - event: "pull_request", - repo: $repo - } - }' > request.json - - curl -sS \ - -H "Authorization: Bearer $AUTH_TOKEN" \ - -H "Content-Type: application/json" \ - -X POST "$AUTH_URL/re/authorize" \ - -d @request.json \ - > response.json - - cat response.json - - DECISION=$(jq -r '.decision // "DENY"' response.json) - RECEIPT=$(jq -r '.receiptId // .receipt.receiptId // ""' response.json) - - if [ -z "$RECEIPT" ]; then - RECEIPT="local-missing-receipt" - fi - - echo "decision=$DECISION" >> "$GITHUB_OUTPUT" - echo "receipt=$RECEIPT" >> "$GITHUB_OUTPUT" - - # 4️⃣ Human approval if required - step-up: - name: Step‑Up Approval - runs-on: ubuntu-latest - needs: govern - if: needs.govern.outputs.decision == 'STEP_UP' - environment: - name: protected-execution - steps: - - run: | - echo "Manual authorization granted via environment." - echo "Receipt: ${{ needs.govern.outputs.receipt }}" - - # 5️⃣ Final enforcement gate - enforce: - name: Enforce Authority Decision - runs-on: ubuntu-latest - needs: [govern, step-up] - if: always() - steps: - - run: | - DECISION="${{ needs.govern.outputs.decision }}" - RECEIPT="${{ needs.govern.outputs.receipt }}" - STEP_UP_RESULT="${{ needs.step-up.result }}" - - echo "Final decision: $DECISION" - echo "Receipt: $RECEIPT" - echo "Step-up job result: $STEP_UP_RESULT" - - if [ -z "$RECEIPT" ]; then - echo "Missing receipt → hard deny" - exit 1 - fi - - if [ "$RECEIPT" = "local-noop-receipt" ]; then - echo "ℹ️ No governed file changes detected; policy gate is a no-op for this PR." - exit 0 - fi - - if [ "$RECEIPT" = "local-missing-auth-url" ]; then - echo "⚠️ Runtime authority endpoint not configured in this environment; skipping hard enforcement." - exit 0 - fi - - if [ "$DECISION" = "PERMIT" ]; then - echo "✅ Execution permitted" - exit 0 - fi - - if [ "$DECISION" = "STEP_UP" ] && [ "$STEP_UP_RESULT" = "success" ]; then - echo "✅ Step-up approval satisfied; execution permitted." - exit 0 - fi - - echo "❌ Execution denied" - exit 1 diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml new file mode 100644 index 0000000..f73f7a3 --- /dev/null +++ b/.github/workflows/labels.yml @@ -0,0 +1,21 @@ +name: Sync labels + +# Applies the canonical label system in .github/labels.yml to the repository. +on: + push: + branches: [main] + paths: [".github/labels.yml"] + workflow_dispatch: + +permissions: + issues: write + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: EndBug/label-sync@v2 + with: + config-file: .github/labels.yml + delete-other-labels: false diff --git a/.gitignore b/.gitignore index 18281cf..35b4526 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,10 @@ node_modules/ dist/ **/dist/ .runtime-authority-receipts.json +.cortextrace/ +__pycache__/ +*.pyc +.venv/ *.log .DS_Store +.pctr/ diff --git a/LICENSE b/LICENSE index aba8dbd..b9642ee 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -32,33 +33,36 @@ not limited to compiled object code, generated documentation, and conversions to other media types. - "Work" shall mean the work of authorship made available under - the License, as indicated by a copyright notice that is included in - or attached to the work (an example is provided in the Appendix below). + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other transformations + editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - "Contribution" shall mean, as submitted to the Licensor for inclusion - in the Work by the copyright owner or by an individual or Legal Entity - authorized to submit on behalf of the copyright owner. For the purposes - of this definition, "submitted" means any form of electronic, verbal, - or written communication sent to the Licensor or its representatives, - including but not limited to communication on electronic mailing lists, - source code control systems, and issue tracking systems that are managed - by, or on behalf of, the Licensor for the purpose of discussing and - improving the Work, but excluding communication that is conspicuously - marked or designated in writing by the copyright owner as "Not a - Contribution." - - "Contributor" shall mean Licensor and any Legal Entity on behalf of - whom a Contribution has been received by the Licensor and included - within the Work. + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, @@ -74,22 +78,22 @@ use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their - Contribution(s) alone or by the combination of their Contribution(s) + Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a cross-claim - or counterclaim in a lawsuit) alleging that the Work or any - Contribution embodied within the Work constitutes direct or contributory - patent infringement, then any patent licenses granted to You under - this License for that Work shall terminate as of the date such - litigation is filed. + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - (a) You must give any other recipients of the Work or Derivative - Works a copy of this License; and + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and @@ -101,23 +105,28 @@ the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its - distribution, You must include a readable copy of the - attribution notices contained within such NOTICE file, in - at least one of the following places: within a NOTICE text - file distributed as part of the Derivative Works; within - the Source form or documentation, if provided along with the - Derivative Works; or, within a display generated by the - Derivative Works, if and wherever such third-party notices - normally appear. The contents of the NOTICE file are for - informational purposes only and do not modify the License. - You may add Your own attribution notices within Derivative - Works that You distribute, alongside or in addition to the - NOTICE text from the Work, provided that such additional - attribution notices cannot be construed as modifying the License. - - You may add Your own license statement for Your modifications and - may provide additional grant of rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Work. + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work @@ -136,10 +145,10 @@ agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any conditions of - TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or reproducing the Work and assume any + appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, @@ -147,25 +156,51 @@ unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, - incidental, or exemplary damages of any character arising as a + incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the - Work (even if such Contributor has been advised of the possibility - of such damages). + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may offer only - obligations consistent on Your own behalf and on Your sole - responsibility, not on behalf of any other Contributor, and only - if You agree to indemnify, defend, and hold each Contributor - harmless for any liability incurred by, or claims asserted against, - such Contributor by reason of your accepting any such warranty or - additional liability. + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + Copyright 2026 BlockSiFr Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..0c70022 --- /dev/null +++ b/NOTICE @@ -0,0 +1,16 @@ +Trust Transfer Protocol (TTP) and PCTR +Copyright 2026 BlockSiFr + +This product includes software developed at BlockSiFr (https://blocksifr.com). + +Licensed under the Apache License, Version 2.0. The full, unmodified license text +is in LICENSE. + +BlockSiFr's commercial offerings — managed enforcement, hosted control plane, +KMS/HSM signing, enterprise integrations and support — are separate products built +on this protocol. They are not covered by, and place no additional conditions on, +the Apache 2.0 grant above. See docs/COMMERCIAL_BOUNDARY.md. + +"BlockSiFr", "TTP" and "PCTR" are trademarks of BlockSiFr. Trademark use is +governed by docs/TRADEMARKS.md and, per section 6 of the License, no trademark +rights are granted by the License itself. diff --git a/README.md b/README.md index a66c04a..dcf95e6 100644 --- a/README.md +++ b/README.md @@ -1,319 +1,206 @@ -

Trust Transfer Protocol

-

- Trustworthiness establishment for autonomous systems. + Trust Transfer Protocol — Trust-Before-Execution for AI agents and non-human identities

- TTP is the open protocol for proving whether an AI agent, copilot, workflow, pipeline, API, service account, or non-human identity is trustworthy enough to be relied on before downstream authority and execution decisions occur. + CI + tests + license + dependencies

- No implicit trust. No stale delegation. No blind reliance on autonomous actors. + See what your agents can cause. Route them safely. Prove what happened.
+ Open-source trust and execution infrastructure for AI agents.

- Protocol - Category - Runtime - Production - License - Security policy + Try it in 60 seconds  ·  + PCTR  ·  + Examples  ·  + Protocol spec  ·  + Contribute

-Identity proves who is acting. Authorization says what was assigned. TTP establishes whether the actor is trustworthy enough for this context, right now. - -TTP is a platform-agnostic trust protocol for evidence-backed trustworthiness establishment across autonomous systems. - -OAuth standardized delegated access. -SCIM standardized identity provisioning. -TTP standardizes trustworthiness establishment before autonomous execution. - -## Status - -* Protocol draft. -* Reference evaluator active. -* MVP parser and trust decay evaluator available. -* Not recommended for production enforcement by itself. -* TTP establishes trustworthiness context; it does not enforce execution alone. +--- -## First Principles +Your agent can have valid credentials and still be about to do the wrong thing. -Software is moving from advising to acting. +``` +Objective -> Trust Route -> Consequence Preview -> Execution Authority -> Protected Action -> Receipt +``` -When software acts, it must be trustworthy. -When trust is stale, reliance becomes risk. -When trust decays, trustworthiness must be re-established. -When trustworthiness is evaluated, evidence must be produced. -When downstream authority systems act on that evaluation, a receipt should exist. +## 60 seconds -The old model is incomplete: +**[PCTR](packages/pctr/)** — Protected Consequence Trust Routing — maps every agent, tool and action in your project to the consequences it can reach, then protects the ones that matter. It answers three questions, in this order: -```text -authenticate -> authorize -> execute -> log -``` - -Autonomous systems require a new model: +| | Question | Command | +| --- | --- | --- | +| **1. Consequence** | What can this action cause? | `pctr preview ` | +| **2. Route** | Which trustworthy agent path may get there? | `pctr route ` | +| **3. Authority** | Is this exact execution allowed right now? | `pctr protect ` | -```text -identity -> trustworthiness -> authority -> execution -> receipt +```bash +npm link ./packages/pctr # not on npm yet; link it from a clone +pctr init # discover agents and tools, write pctr.json +pctr scan # what consequences can they reach? ``` -TTP defines the protocol layer for establishing trustworthiness before downstream authority and execution decisions. - -## The Category +

+ pctr scan finds five agents, three tools and four potential actions, and reports one critical protected consequence +

-Autonomous systems have crossed from recommendation into execution. +`pctr scan --share` turns that into Markdown you can paste into a pull request, and +`packages/pctr/examples/pctr-scan.yml` runs it on every PR as a comment. -AI agents call tools. -Copilots trigger workflows. -CI/CD pipelines modify production. -Service accounts move data. -APIs execute financial and operational decisions. -MSPs and MSSPs act across customer environments. +No account, no network: everything stays in `./pctr.json` and `./.pctr`. Three flagship capabilities sit on top of that graph — the **Consequence Twin** (`pctr preview`), **TrustRoute Autopilot** (`pctr route`), and the **Agent Time Machine** (`pctr replay`, `pctr explain`) — with Ed25519-signed receipts, read-only probes that measure a consequence instead of declaring it, adapters for the common agent frameworks, and an effect boundary (`pctr serve`) that verifies authority in its own process. See the [PCTR README](packages/pctr/README.md), `npm run demo:pctr` and `npm run demo:pctr-fabric`. -Existing systems authenticate actors, assign permissions, and log activity. They do not define a portable protocol for establishing whether an autonomous actor is trustworthy enough to be relied on in a specific context. +PCTR routes to the consequence. TTP binds the authority for the exact execution that reaches it. -TTP defines that missing layer. +## Under the hood: the Trust Transfer Protocol -OAuth standardized delegated access. -SCIM standardized identity provisioning. -TTP standardizes trustworthiness establishment before autonomous execution. +Identity proves *who* is acting. Authorization defines *what* was assigned. **TTP establishes whether the actor is trustworthy enough for this action, right now** — before any downstream authority or execution decision is made. -## Stack Alignment +OAuth standardized delegated access. SCIM standardized identity provisioning. **TTP standardizes trust-before-execution for autonomous systems.** It is a platform-agnostic trust protocol — bring any agent, IdP, gateway, or workflow. -TTP establishes trustworthiness. -SCIM-RE structures runtime trust context. -RAP evaluates authority. -Execution Exchange enforces downstream decisions. -CortexTrace records evidence and receipts. +

+ Identity, Attestation, Trust Evaluation, Runtime Authority Gate, Decision, Execution Receipt +

-```text -TTP -> SCIM-RE -> RAP -> Execution Exchange -> Protected Systems - | - v -CortexTrace - | - v -ExecutionReceipts -``` +## Why TTP Exists -TTP does not govern execution directly. -TTP establishes trustworthiness. -Downstream runtime authority systems decide and enforce what happens next. +Software is moving from advising to acting. Agents call tools, copilots trigger workflows, pipelines modify production, service accounts move data. The old model — `authenticate → authorize → execute → log` — never asks the question that matters at runtime: -## Add Trustworthiness to Your Agents +> **Should this specific action execute right now, given how trustworthy the actor is at this moment?** -Your agents already know how to act. -TTP helps prove whether they are trustworthy enough to be relied on. +

+ Identity asks who, Authorization asks what may be accessed, TTP asks whether this action should execute now +

-Bring your existing agents. -No agent rewrite required. -No IdP replacement required. -No workflow migration required. +## How TTP Works -Add an evidence-backed trustworthiness layer around your agents, tools, APIs, workflows, and non-human identities. +Trust is established from evidence, scored against a threshold, and checked at a runtime authority gate **before** the action runs. The gate consumes a trust proof, an authority grant, and the current decay state — then emits a decision and a signed receipt. -```text -Agent identity -> trust evidence -> TTP trust proof -> authority evaluation -> downstream decision -> receipt -``` +

+ A request is evaluated by the runtime authority gate against trust, authority and decay, producing a decision and a receipt +

-| Step | What you do | What TTP provides | -| --- | --- | --- | -| 1. Register the actor | Define the agent, service account, API client, pipeline, or workflow as a Subject | Establishes who or what is being evaluated | -| 2. Attach evidence | Add trust claims, attestations, issuer context, freshness rules, and decay behavior | Establishes whether trust is current and evidence-backed | -| 3. Generate a trust proof | Evaluate the trust context before downstream authority decisions | Produces proof that RAP, Execution Exchange, API gateways, or CI gates can use | +### Runtime Decisions -Register the subject. -Attach evidence. -Evaluate trust. -Produce proof. +A trust proof resolves into one of five outcomes. TTP establishes the trust context; downstream runtime authority systems enforce the decision. -Keep your agents, copilots, APIs, IdPs, and workflows. -Add an evidence-backed trustworthiness layer before downstream authority and enforcement. +

+ Decision outcomes: allow, throttle, step-up, escalate, deny +

-## Developer Quickstart +## First Example -```bash -git clone https://github.com/BlockSiFr/ttp-protocol.git -cd ttp-protocol -npm install -npm test -npm run ttp -- check examples/01-basic-agent.ttp -npm run ttp -- eval examples/02-trust-decay.ttp --subject agent:invoice_reviewer --at now -``` +Declare a subject, attach evidence, and require a trust threshold before the action is allowed: -Expected check output: - -```json -{ - "ok": true, - "file": "examples/01-basic-agent.ttp", - "subjects": 1, - "trust_claims": 1, - "proofs": 1, - "authority_contexts": 1, - "delegations": 0 +```ttp +trust "invoice_agent" { + subject = agent:invoice-bot + issuer = trust-issuer:finance-control + score = 0.91 + decay = exponential(halflife = 6h) # trust weakens without fresh evidence + scope = [invoice.read, invoice.write] + evidence = [attestation:att-7b3c, receipt:rcpt-01J9F4] } -``` -Expected eval output shape: - -```json -{ - "subject": "agent:invoice_reviewer", - "effective_score": 0, - "required_score": 0.7, - "result": "TRUST_PROOF_EXPIRED", - "reason": "trust claim expired before evaluation", - "proof_mode": "cleartext-dev", - "evaluated_at": "2026-06-25T18:27:15.881Z", - "expires_at": "2026-05-11T20:00:00.000Z", - "receipt_hash_optional": null +policy "write_invoices" { + action = invoice.write + require_trust = 0.80 # threshold the proof must clear + on_below = step-up # otherwise demand fresh attestation + execution_policy = gate # decide before execution, fail closed + emit = execution_receipt # cryptographic proof of the decision } ``` -First trust proof path: - -```bash -npm run demo -``` - -The demo prints reference decisions, trust scores, and ExecutionReceipt identifiers for local non-production scenarios. +Trust does not stay still. Without fresh attestation it **decays** through `active → degraded → warning → critical`; a new attestation **recharges** it. -## Protocol Primitives - -| Primitive | Meaning | -| --- | --- | -| Subject | The human, agent, service account, pipeline, API client, workload, or workflow whose trustworthiness is being evaluated. | -| TrustClaim | A scoped statement that a Subject has a trust score or trust state issued by a specific trust issuer. | -| TrustIssuer | The entity responsible for issuing or attesting to a TrustClaim. | -| AuthorityGrant | A bounded right or delegated authority context that may only be relied on when trust, freshness, scope, and constraints are satisfied. | -| Attestation | Fresh evidence that the subject, credential, workload, code, runtime, or operating context remains valid. | -| TrustDecay | The time-based weakening of trust when fresh evidence or attestations are absent. | -| Delegation | The bounded transfer of trust or authority context from one subject, issuer, or workflow to another. | -| TrustProof | A structured proof that trust conditions were evaluated and satisfied or not satisfied. | -| RuntimeDecision | A downstream enforceable decision derived from trustworthiness, authority, evidence, and context. | -| ExecutionReceipt | A signed proof object recording the evaluated subject, action, resource, decision, trust state, authority basis, evidence references, timestamp, and receipt chain context. | - -## What TTP Establishes - -| Actor | Trustworthiness question | TTP role | -| --- | --- | --- | -| AI agent | Is this agent trustworthy enough for this tool, data, or workflow context? | Evaluates trust claims, attestations, scope, and decay | -| Copilot | Is this delegated action backed by current trust evidence? | Binds trust proof to the requested action context | -| CI/CD pipeline | Is this workload trustworthy enough to promote, deploy, or alter infrastructure? | Evaluates trust before downstream CI/CD gates act | -| API client | Is this client trustworthy enough for this sensitive endpoint? | Supplies trust context to gateway or service decisioning | -| Service account | Is this non-human identity still trustworthy for its assigned task? | Detects stale, decayed, or unverified trust | -| MSP/MSSP operator | Is this customer-impacting action backed by current trust and evidence? | Produces trust proof for delegated operational action | +

+ Trust decays over time across active, degraded, warning and critical bands and is recharged by attestation +

-## Trust Proof Outcomes +## Execution Receipts -TTP produces trust context and trust proof results. +Every decision produces a signed, hash-chained `ExecutionReceipt` — evidence, not a log line. It records what was decided, the trust state behind it, and the cryptographic basis to verify it later. -| Trust proof outcome | Meaning | -| --- | --- | -| trust_valid | Current trust evidence satisfies the proof requirement | -| trust_insufficient | Effective trust is below the required threshold | -| trust_expired | Trust claim or proof freshness has expired | -| trust_invalid | Syntax, issuer, evidence, reference, or proof validation failed | -| trust_unknown | Trustworthiness cannot be established from available evidence | - -## Downstream Runtime Decisions +

+ Execution receipt with subject, action, resource, decision, trust score, references, signature and chain hash +

-Runtime authority systems may convert trust proof results into enforceable decisions. +## Architecture -| Runtime decision | Meaning | -| --- | --- | -| allow | Execution may proceed | -| throttle | Execution may proceed under rate, volume, or scope limits | -| step_up | Fresh attestation or additional approval is required | -| escalate | Human or higher-authority review is required | -| deny | Execution is blocked | +TTP is the foundation layer. Runtime governance, the authority gate, and the enterprise control plane build on it; agent frameworks, CI/CD, APIs, and copilots consume it. -TTP itself establishes trustworthiness. -RAP, Execution Exchange, API gateways, CI gates, and integrated runtime systems enforce downstream decisions. +

+ TTP architecture stack: protocol, SCIM-RE governance, runtime authority gate, execution exchange, consumers +

-## Integration Paths +> TTP establishes trustworthiness. **SCIM-RE** structures runtime context, **RAP** evaluates authority, **Execution Exchange** enforces decisions, and **CortexTrace** records evidence. TTP does not enforce execution by itself — see [docs/COMMERCIAL_BOUNDARY.md](docs/COMMERCIAL_BOUNDARY.md). -| Integration path | Best for | How it works | -| --- | --- | --- | -| Agent trust wrapper | LangChain, CrewAI, MCP tools, custom agents, OpenAI tools, Claude tools | Wrap the agent or tool boundary with a trust proof | -| API trust check | Sensitive APIs, SaaS actions, service-to-service calls | Supply trust context before the gateway or service relies on the actor | -| CI/CD trust proof | GitHub Actions, Azure DevOps, GitLab, Terraform, deployment workflows | Establish trustworthiness before merge, deploy, promote, or infrastructure change | -| MSP/MSSP trust proof | ServiceNow, Jira, ConnectWise, Sentinel, Defender, remediation, decommissioning | Prove customer-impacting work is backed by current trust evidence | +## Integrations -First trust proof in minutes. -Production enforcement with Execution Exchange when ready. +Bring your existing agents — no rewrite, no IdP replacement. TTP wraps the boundary where an action happens. -## Why It Matters +

+ Integration surfaces: GitHub Actions, CI/CD, API gateways, service accounts, Microsoft Copilot, Azure DevOps, LangChain, CrewAI +

-| Buyer | Value | -| --- | --- | -| CISO | Establish whether autonomous actors are trustworthy before downstream authority systems rely on them | -| CTO | Deploy agents and automation without losing confidence in who or what can be trusted | -| Compliance | Convert trust evaluations and downstream decisions into evidence | -| Platform engineering | Add trust proof checks to APIs, CI/CD, agent tools, and workflows | -| MSP/MSSP | Prove customer-impacting actions were backed by current trust evidence | -| Identity team | Extend identity context into dynamic trustworthiness | -| Security engineering | Detect stale, decayed, or unverified trust before sensitive reliance | +## Roadmap -## Open Protocol, Commercial Enforcement - -Open source TTP includes protocol grammar, trustworthiness semantics, the trust proof model, public schemas, example `.ttp` files, SDK primitives, a reference evaluator, non-production demo gates, public ExecutionReceipt schemas, public AuthorityGrant schemas, public Attestation schemas, the TrustDecay model, documentation, RFCs, and example integrations. +

+ Roadmap phases from spec and reference through production hardening +

-Commercial BlockSiFr capabilities include Execution Exchange runtime enforcement, managed Runtime Authority Gate, production RAP service, tenant governance infrastructure, HSM-backed signing, production ExecutionReceipt ledger, CortexTrace enterprise evidence engine, CAIF / IFC optimization engine, enterprise adapters, compliance evidence packs, customer policy templates, behavioral scoring weights, risk-cost-compliance scoring model, managed service control plane, enterprise trust graph, enterprise evidence exports, production policy orchestration, and customer-specific baselines. +## Quickstart -TTP establishes trustworthiness. -Execution Exchange enforces downstream runtime decisions in production. -CortexTrace records and verifies execution evidence and receipts. +```bash +git clone https://github.com/BlockSiFr/ttp-protocol.git +cd ttp-protocol +npm install +npm test -See [COMMERCIAL_BOUNDARY.md](COMMERCIAL_BOUNDARY.md) for the explicit boundary. +# evaluate the reference examples +npm run ttp -- check examples/01-basic-agent.ttp +npm run ttp -- eval examples/02-trust-decay.ttp --subject agent:invoice_reviewer --at now +npm run demo +``` -## Security Posture +`check` validates a `.ttp` file; `eval` evaluates a subject's trust at a point in time; `demo` prints reference decisions, trust scores, and `ExecutionReceipt` identifiers for local, non-production scenarios. -TTP is currently a protocol draft and reference implementation. +## Protocol Primitives -Do not use cleartext-dev proof mode in production. +| Primitive | Meaning | +| --- | --- | +| **Subject** | The agent, service account, pipeline, API client, workload, or workflow being evaluated. | +| **TrustClaim** | A scoped statement that a Subject holds a trust score or state, issued by a trust issuer. | +| **AuthorityGrant** | A bounded right relied on only when trust, freshness, scope, and constraints are satisfied. | +| **Attestation** | Fresh evidence that the subject, credential, workload, or runtime context remains valid. | +| **TrustDecay** | The time-based weakening of trust when fresh evidence is absent. | +| **Delegation** | Bounded transfer of trust or authority from one subject to another. | +| **IsnadChain** | A verifiable chain of trust transmission from a rooted authority to the acting subject. | +| **TrustProof** | A structured proof that trust conditions were evaluated and satisfied — or not. | +| **RuntimeDecision** | A downstream enforceable decision: `allow`, `throttle`, `step-up`, `escalate`, `deny`. | +| **ExecutionReceipt** | A signed proof object recording the decision, trust state, authority basis, and receipt chain. | -Production trustworthiness establishment and downstream enforcement require: +## Open Protocol, Commercial Enforcement -* trusted issuer registry -* signed claims -* replay protection -* clock integrity -* key rotation -* tenant isolation -* fail-closed downstream enforcement -* receipt signing -* audit retention -* policy review -* evidence integrity -* attestation freshness -* secure key management -* protected commercial control plane +Open-source TTP includes the protocol grammar, trustworthiness semantics, the trust-proof model, public schemas, example `.ttp` files, SDK primitives, a reference evaluator, and the TrustDecay model. Production enforcement — managed Runtime Authority Gate, the Execution Exchange control plane, HSM-backed signing, and the CortexTrace evidence engine — is commercial BlockSiFr infrastructure. The boundary is explicit in [docs/COMMERCIAL_BOUNDARY.md](docs/COMMERCIAL_BOUNDARY.md). -BlockSiFr Execution Exchange provides commercial production enforcement capabilities. +## Security -## Repository Map +TTP is a protocol draft and reference implementation. **Do not use `cleartext-dev` proof mode in production.** Production trust establishment and enforcement require a trusted issuer registry, signed claims, replay protection, clock integrity, key rotation, tenant isolation, fail-closed enforcement, receipt signing, and audit retention. See [SECURITY.md](SECURITY.md) and [THREAT_MODEL.md](THREAT_MODEL.md). -| Path | Purpose | -| --- | --- | -| [examples/](examples/) | `.ttp` examples and non-production trust proof demos | -| [src/](src/) | MVP parser, evaluator, and CLI | -| [spec/](spec/) and [specs/](specs/) | Protocol notes, schemas, and reference contracts | -| [docs/concepts/](docs/concepts/) | Concept documentation for trustworthiness establishment | -| [docs/integrations/](docs/integrations/) | Integration notes for agents, CI/CD, gateways, and MSP/MSSP workflows | -| [docs/enterprise/](docs/enterprise/) | Enterprise security model, threat model, deployment, and commercial boundary notes | -| [rfcs/](rfcs/) | Protocol RFC structure | +## Contributing -## Build the Trustworthiness Layer +From a first issue to owning a protocol surface — the path is open and intentional. Start with [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/GOVERNANCE.md](docs/GOVERNANCE.md). -TTP is open protocol infrastructure. +

+ Contributor path: start here, good first issue, protocol RFC, runtime implementation, docs and examples, maintainer path +

-Use it to model trust. -Use it to evaluate evidence. -Use it to establish trustworthiness. -Use it to produce proof. +## License -Use Execution Exchange when trust proof must drive production runtime enforcement. +[Apache-2.0](LICENSE) · A [BlockSiFr](https://blocksifr.com) open protocol · Trust-Before-Execution for autonomous systems. diff --git a/apps/attestation-service/README.md b/apps/attestation-service/README.md deleted file mode 100644 index bf98849..0000000 --- a/apps/attestation-service/README.md +++ /dev/null @@ -1 +0,0 @@ -# attestation-service diff --git a/apps/frontdesk-gateway/README.md b/apps/frontdesk-gateway/README.md deleted file mode 100644 index cea8729..0000000 --- a/apps/frontdesk-gateway/README.md +++ /dev/null @@ -1 +0,0 @@ -# frontdesk-gateway diff --git a/apps/github-action-gate-demo/README.md b/apps/github-action-gate-demo/README.md deleted file mode 100644 index 3dd2cc2..0000000 --- a/apps/github-action-gate-demo/README.md +++ /dev/null @@ -1 +0,0 @@ -# github-action-gate-demo diff --git a/apps/receipt-ledger-api/README.md b/apps/receipt-ledger-api/README.md deleted file mode 100644 index 9e7079c..0000000 --- a/apps/receipt-ledger-api/README.md +++ /dev/null @@ -1 +0,0 @@ -# receipt-ledger-api diff --git a/apps/revocation-service/README.md b/apps/revocation-service/README.md deleted file mode 100644 index 408161c..0000000 --- a/apps/revocation-service/README.md +++ /dev/null @@ -1 +0,0 @@ -# revocation-service diff --git a/assets/brand/README.md b/assets/brand/README.md new file mode 100644 index 0000000..876d1bf --- /dev/null +++ b/assets/brand/README.md @@ -0,0 +1,33 @@ +# TTP brand assets + +Self-contained SVGs with the real brand fonts (Space Grotesk, Inter, JetBrains +Mono) subset and embedded as base64 `@font-face`, so they render identically on +GitHub — where external font loading is blocked. + +## Files + +- `fonts/*.ttf` — brand font faces (decompressed from `@fontsource`), used by the + build. Committed for reproducibility. +- `embed-fonts.py` — subsets each font to the glyphs a given SVG uses and embeds + them. Idempotent; safe to re-run. Requires `fonttools` + `brotli`. +- `render-png.mjs` — renders an SVG to PNG with the brand fonts (`@resvg/resvg-js`). +- `trust-ring.svg` / `repo-icon.svg` — decorative marks (no embedded text). + +## Authoring + +1. Write the SVG with brand `font-family` names and a `/*@FONTS@*/` marker as the + first thing inside `", "", svg, flags=re.S) # ignore CSS, count only rendered text + text = " ".join(re.findall(r">([^<]*)<", body)) + chars = set(text) | SAFETY + chars.discard("\n"); chars.discard("\t") + return chars + +def subset_b64(path, chars): + opts = Options() + opts.flavor = "woff2" + opts.desubroutinize = True + opts.notdef_outline = True + opts.hinting = False + opts.glyph_names = False + opts.layout_features = ["kern", "liga", "calt"] + font = TTFont(path) + sub = Subsetter(options=opts) + sub.populate(text="".join(sorted(chars))) + sub.subset(font) + font.flavor = "woff2" # Options.flavor is not applied on save; set it on the font + buf = io.BytesIO(); font.save(buf) + return base64.b64encode(buf.getvalue()).decode("ascii") + +def build_block(svg, chars): + weights = {int(w) for w in re.findall(r"font-weight:(\d+)", svg)} or {400} + weights.add(400) # default for faces declared without an explicit weight + rules = [] + for fam, wght, fname in FACES: + if fam not in svg or wght not in weights: + continue + b64 = subset_b64(os.path.join(FONT_DIR, fname), chars) + rules.append( + "@font-face{font-family:'%s';font-style:normal;font-weight:%d;" + "src:url(data:font/woff2;base64,%s) format('woff2');}" % (fam, wght, b64) + ) + return BEGIN + "".join(rules) + END + +def process(path): + with open(path, "r", encoding="utf-8") as f: + svg = f.read() + if MARKER not in svg: + return False + svg = re.sub(re.escape(BEGIN) + ".*?" + re.escape(END), "", svg, flags=re.S) + block = build_block(svg, used_chars(svg)) + svg = svg.replace(MARKER, MARKER + block, 1) + with open(path, "w", encoding="utf-8") as f: + f.write(svg) + return True + +def main(argv): + targets = argv or glob.glob(os.path.join(HERE, "..", "**", "*.svg"), recursive=True) + n = 0 + for t in targets: + if process(t): + print("embedded fonts:", os.path.relpath(t)); n += 1 + print("done:", n, "file(s)") + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/assets/brand/fonts/Inter-Regular.ttf b/assets/brand/fonts/Inter-Regular.ttf new file mode 100644 index 0000000..f297b9c Binary files /dev/null and b/assets/brand/fonts/Inter-Regular.ttf differ diff --git a/assets/brand/fonts/Inter-SemiBold.ttf b/assets/brand/fonts/Inter-SemiBold.ttf new file mode 100644 index 0000000..f1d536e Binary files /dev/null and b/assets/brand/fonts/Inter-SemiBold.ttf differ diff --git a/assets/brand/fonts/JetBrainsMono-Medium.ttf b/assets/brand/fonts/JetBrainsMono-Medium.ttf new file mode 100644 index 0000000..6ab9be9 Binary files /dev/null and b/assets/brand/fonts/JetBrainsMono-Medium.ttf differ diff --git a/assets/brand/fonts/JetBrainsMono-Regular.ttf b/assets/brand/fonts/JetBrainsMono-Regular.ttf new file mode 100644 index 0000000..ffc3e87 Binary files /dev/null and b/assets/brand/fonts/JetBrainsMono-Regular.ttf differ diff --git a/assets/brand/fonts/SpaceGrotesk-Bold.ttf b/assets/brand/fonts/SpaceGrotesk-Bold.ttf new file mode 100644 index 0000000..ecf7cd3 Binary files /dev/null and b/assets/brand/fonts/SpaceGrotesk-Bold.ttf differ diff --git a/assets/brand/fonts/SpaceGrotesk-Medium.ttf b/assets/brand/fonts/SpaceGrotesk-Medium.ttf new file mode 100644 index 0000000..161396a Binary files /dev/null and b/assets/brand/fonts/SpaceGrotesk-Medium.ttf differ diff --git a/assets/brand/fonts/SpaceGrotesk-SemiBold.ttf b/assets/brand/fonts/SpaceGrotesk-SemiBold.ttf new file mode 100644 index 0000000..2ae635c Binary files /dev/null and b/assets/brand/fonts/SpaceGrotesk-SemiBold.ttf differ diff --git a/assets/brand/render-png.mjs b/assets/brand/render-png.mjs new file mode 100644 index 0000000..56c8d8a --- /dev/null +++ b/assets/brand/render-png.mjs @@ -0,0 +1,24 @@ +// Render brand SVGs to PNG with the real TTP brand fonts (resvg). +// Usage: node brand/render-png.mjs [width] +import { Resvg } from "@resvg/resvg-js"; +import { readFileSync, writeFileSync, readdirSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const FONT_DIR = join(HERE, "fonts"); +const fontFiles = readdirSync(FONT_DIR).filter(f => f.endsWith(".ttf")).map(f => join(FONT_DIR, f)); + +const [, , inPath, outPath, width] = process.argv; +if (!inPath || !outPath) { + console.error("usage: node render-png.mjs [width]"); + process.exit(1); +} +const svg = readFileSync(inPath, "utf8"); +const resvg = new Resvg(svg, { + fitTo: width ? { mode: "width", value: Number(width) } : { mode: "original" }, + font: { fontFiles, loadSystemFonts: false, defaultFontFamily: "Inter" }, + background: "#0A0A0F", +}); +writeFileSync(outPath, resvg.render().asPng()); +console.log("rendered", outPath); diff --git a/assets/brand/trust-ring.svg b/assets/brand/trust-ring.svg new file mode 100644 index 0000000..6364dad --- /dev/null +++ b/assets/brand/trust-ring.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/diagrams/architecture-stack.svg b/assets/diagrams/architecture-stack.svg new file mode 100644 index 0000000..8c54914 --- /dev/null +++ b/assets/diagrams/architecture-stack.svg @@ -0,0 +1,48 @@ + + + + + + + + + + L4 + Consumers + Agent frameworks · CI/CD · APIs · Copilots + + + + + L3 + Execution Exchange + Enterprise control plane · evidence & receipts + + + + + L2 + Runtime Authority Gate + Decides allow · step-up · deny before execution + + + + + L1 + SCIM-RE · Runtime Governance + Authority grants · policy · runtime execution context + + + + + + L0 + Trust Transfer Protocol + Trust scoring · decay · delegation · proofs · receipts + FOUNDATION + + diff --git a/assets/diagrams/contributor-path.svg b/assets/diagrams/contributor-path.svg new file mode 100644 index 0000000..7f78ce4 --- /dev/null +++ b/assets/diagrams/contributor-path.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + 01Start HereREADME & concepts + 02Good First Issuesmall, scoped tasks + 03Protocol RFCpropose a change + 04Runtime Implbuild enforcement + 05Docs + Examplesteach the next dev + 06Maintainer Pathown a surface + diff --git a/assets/diagrams/decision-matrix.svg b/assets/diagrams/decision-matrix.svg new file mode 100644 index 0000000..7563983 --- /dev/null +++ b/assets/diagrams/decision-matrix.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + allow + Trust clears threshold. + Action executes. + PROCEED + + + + + + + throttle + Marginal trust. + Rate-limit the actor. + SLOW + + + + + + + step-up + Fresh attestation + required to continue. + RE-PROVE + + + + + + + escalate + Route to a human + authority for review. + REVIEW + + + + + + + deny + Trust below floor. + Action is blocked. + BLOCK + + diff --git a/assets/diagrams/execution-receipt.svg b/assets/diagrams/execution-receipt.svg new file mode 100644 index 0000000..ee651dc --- /dev/null +++ b/assets/diagrams/execution-receipt.svg @@ -0,0 +1,41 @@ + + + + + + + + + + EXECUTION RECEIPT + + + + VERIFIED + + receipt:rcpt_01J9F4…A2 + + + + + subject agent:invoice-bot + action invoice.write + resource erp://invoices/2026-06 + decision allow + trustScoreAtDecision0.87 + authorityGrantRef grant:9f2a…c41 + attestationRef att:7b3c…e90 + timestamp 2026-06-25T18:40:12Z + + + + + signature ed25519:5d1e…b7f4 + chainHash sha256:c0ffee…a17b + diff --git a/assets/diagrams/integrations.svg b/assets/diagrams/integrations.svg new file mode 100644 index 0000000..4a868c3 --- /dev/null +++ b/assets/diagrams/integrations.svg @@ -0,0 +1,20 @@ + + + + + + + GitHub Actionsenforces at: workflow jobREFERENCE + CI/CD Pipelinesenforces at: pipeline stageREFERENCE + API Gatewaysenforces at: gateway requestEXPERIMENTAL + Service Accountsenforces at: token issuanceEXPERIMENTAL + + Microsoft Copilotenforces at: tool callPLANNED + Azure DevOpsenforces at: release stagePLANNED + LangChainenforces at: agent toolEXPERIMENTAL + CrewAIenforces at: agent stepPLANNED + diff --git a/assets/diagrams/protocol-loop.svg b/assets/diagrams/protocol-loop.svg new file mode 100644 index 0000000..8dc2b6d --- /dev/null +++ b/assets/diagrams/protocol-loop.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + 01Identitynon-human actorasserts itself + 02Attestationsigned evidenceis presented + 03Trust Evalscore vs.threshold + 04Authority Gatechecked beforeexecution + 05Decisionallow · step-up· deny + 06Receiptsigned proofis emitted + + + diff --git a/assets/diagrams/roadmap.svg b/assets/diagrams/roadmap.svg new file mode 100644 index 0000000..7dc2367 --- /dev/null +++ b/assets/diagrams/roadmap.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + PHASE 1 + Spec & Reference + SHIPPED + + + + + PHASE 2 + Runtime Authority + IN PROGRESS + + + + + PHASE 3 + Proofs & Receipts + NEXT + + + + + PHASE 4 + Integrations + PLANNED + + + + + PHASE 5 + Production Hardening + PLANNED + + diff --git a/assets/diagrams/runtime-authority-flow.svg b/assets/diagrams/runtime-authority-flow.svg new file mode 100644 index 0000000..870fbc6 --- /dev/null +++ b/assets/diagrams/runtime-authority-flow.svg @@ -0,0 +1,70 @@ + + + + + + + + + + REQUEST + agent:invoice-bot + invoice.write + + + + + + + + + + Runtime Authority Gate + evaluates before execution + + trust proof + authority grant + decay state + + + + + + + + + + + + allowexecute + + + + step-upre-prove + + + + denyblock + + + + + + + + + + + + RECEIPT + decision: allow + sha256:9f2a…c41 + signed · immutable + + diff --git a/assets/diagrams/trust-decay-curve.svg b/assets/diagrams/trust-decay-curve.svg new file mode 100644 index 0000000..f097009 --- /dev/null +++ b/assets/diagrams/trust-decay-curve.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + 1.0 + 0.0 + time + + + + ACTIVE 0.85–1.00 + DEGRADED 0.65–0.85 + WARNING 0.40–0.65 + CRITICAL 0.00–0.40 + + + + + + + + + + + + + + + attestation + + diff --git a/assets/diagrams/trust-gap.svg b/assets/diagrams/trust-gap.svg new file mode 100644 index 0000000..a20a5e0 --- /dev/null +++ b/assets/diagrams/trust-gap.svg @@ -0,0 +1,49 @@ + + + + + + + + + ESTABLISHED · IDENTITY + + Who is acting? + Authentication proves the actor + is who it claims to be. + + + OAuth · OIDC · SPIFFE + + + + + ESTABLISHED · AUTHORIZATION + + What may it access? + Static grants define scope — + assigned once, rarely revisited. + + + RBAC · SCIM · IAM + + + + + + THE GAP · TTP + + Should this action + execute right now? + Live, evidence-backed trust + gates the action before it runs. + + + TRUST · DECAY · RECEIPTS + + diff --git a/assets/pctr-scan.svg b/assets/pctr-scan.svg new file mode 100644 index 0000000..e828d70 --- /dev/null +++ b/assets/pctr-scan.svg @@ -0,0 +1,41 @@ + + + + + + pctr scan + + PCTR + + Scanning your agents... + + Agents found 5 + Tools found 3 + External systems 3 + Potential actions 4 + + Protected consequences + + CRITICAL 1 + HIGH 1 + MEDIUM 1 + + Highest priority: + + customers.delete + + Route: + + user:local → planner → support-agent → admin-agent → database → customers.delete + + Problem: + + The final action can cause "data deleted" irreversibly without independent + execution authority. + + Recommended: + + Add TTP authority verification before customers.delete. + + + diff --git a/assets/screenshots/cli-demo.png b/assets/screenshots/cli-demo.png new file mode 100644 index 0000000..9ae0714 Binary files /dev/null and b/assets/screenshots/cli-demo.png differ diff --git a/assets/screenshots/cli-demo.svg b/assets/screenshots/cli-demo.svg new file mode 100644 index 0000000..e3bb924 --- /dev/null +++ b/assets/screenshots/cli-demo.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + ttp — trust check + + + $ttp check examples/01-basic-agent.ttp + + + + { + "ok": true, + "file": "examples/01-basic-agent.ttp", + "subjects": 1, + "trust_claims": 1, + "proofs": 1, + "authority_contexts": 1, + "delegations": 0 + } + + + + VALID · 1 subject · 1 proof + diff --git a/assets/screenshots/receipt-preview.png b/assets/screenshots/receipt-preview.png new file mode 100644 index 0000000..4c69fbc Binary files /dev/null and b/assets/screenshots/receipt-preview.png differ diff --git a/assets/social-preview.png b/assets/social-preview.png new file mode 100644 index 0000000..8c101bd Binary files /dev/null and b/assets/social-preview.png differ diff --git a/assets/social-preview.svg b/assets/social-preview.svg new file mode 100644 index 0000000..0dd8242 --- /dev/null +++ b/assets/social-preview.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BlockSiFr + / + TTP + + + + PROTOCOL v1.0 + + + + OPEN SOURCE · AGENT TRUST INFRASTRUCTURE + Trust Transfer + Protocol + Trust-Before-Execution for AI Agents. + + + + + + + + + + Identity + + + + + + Trust + + + + + + Authority + + + + + + Execution + + + + + + Receipt + + + + diff --git a/assets/ttp-hero.svg b/assets/ttp-hero.svg index bfe0d89..9463ce2 100644 --- a/assets/ttp-hero.svg +++ b/assets/ttp-hero.svg @@ -1 +1,87 @@ -ActorsHuman Agent API CITrust Evidenceclaims attestationsTTP Trust ProofestablishDownstream AuthorityRAP gateReceiptevidence chain + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OPEN SOURCE · AGENT TRUST INFRASTRUCTURE + + + PROTOCOL v1.0 + + + Trust Transfer + Protocol + Trust-Before-Execution for AI agents & non-human identities. + + + + $ npm install @blocksifr/ttp-protocol + + + + + + + + + Identitywho is acting + + + + + Trustis it trustworthy right now + + + + + Authorityis this action permitted + + + + + Executiongated runtime action + + + + + Receiptcryptographic proof of the decision + + + + + + + Authority Before Execution·Trust Decay·ZK Proofs·Execution Receipts + diff --git a/ASSESSMENT.md b/docs/ASSESSMENT.md similarity index 100% rename from ASSESSMENT.md rename to docs/ASSESSMENT.md diff --git a/COMMERCIAL_BOUNDARY.md b/docs/COMMERCIAL_BOUNDARY.md similarity index 100% rename from COMMERCIAL_BOUNDARY.md rename to docs/COMMERCIAL_BOUNDARY.md diff --git a/DOCTRINE.md b/docs/DOCTRINE.md similarity index 100% rename from DOCTRINE.md rename to docs/DOCTRINE.md diff --git a/GOVERNANCE.md b/docs/GOVERNANCE.md similarity index 100% rename from GOVERNANCE.md rename to docs/GOVERNANCE.md diff --git a/MVP.md b/docs/MVP.md similarity index 100% rename from MVP.md rename to docs/MVP.md diff --git a/PATENT_NOTICE.md b/docs/PATENT_NOTICE.md similarity index 100% rename from PATENT_NOTICE.md rename to docs/PATENT_NOTICE.md diff --git a/TRADEMARKS.md b/docs/TRADEMARKS.md similarity index 100% rename from TRADEMARKS.md rename to docs/TRADEMARKS.md diff --git a/docs/brand/VISUAL_DESIGN.md b/docs/brand/VISUAL_DESIGN.md new file mode 100644 index 0000000..9965999 --- /dev/null +++ b/docs/brand/VISUAL_DESIGN.md @@ -0,0 +1,135 @@ +# TTP Visual Design System + +The visual identity for the Trust Transfer Protocol. The repo should read as the +reference implementation for the **Trust-Before-Execution** category: dark, +precise, architectural, cryptographic, calm, enterprise-grade. Engineered, not +decorated. + +> Metaphor: a runtime execution gate where **authority, trust, proof, and +> receipts** converge. Cyan is a precision signal, never a background fill. + +--- + +## 1. Color tokens + +| Token | Hex | Role | +| --- | --- | --- | +| Trust Black | `#0A0A0F` | Primary background | +| Cipher Dark | `#12141C` / `#101521` | Panels & cards | +| Border | `#1E2430` | Thin technical borders (1px) | +| Gridline | `#FFFFFF` @ 3% | Subtle structure | +| Signal Cyan | `#00D4FF` | Primary accent — verified trust, precision only | +| Deep Azure | `#0066CC` | Secondary accent — authorization | +| Near White | `#F0F2F5` | Primary text on dark | +| Steel Gray | `#8892A0` | Metadata, borders, subdued copy | +| Proof Green | `#00E676` | Verified / allow | +| Quantum Teal | `#00B8A9` | Protocol support / throttle | +| Drift Amber | `#FFB300` | Warning / step-up / escalate | +| Breach Red | `#FF3D00` | Deny / critical | + +Functional colors map **only** to decision states: `allow` (green), +`throttle` (teal), `step-up` (amber), `escalate` (amber + flag), `deny` (red). + +## 2. Typography tokens + +| Use | Family | Weights | +| --- | --- | --- | +| Display / hero / headings | **Space Grotesk** | 600, 700 | +| Body / interface | **Inter** | 400, 600 | +| Code, API paths, policy, receipts, hashes, CLI | **JetBrains Mono** | 400, 500 | + +GitHub Markdown cannot load custom fonts, so branded fonts live in **SVG +assets** with the real font files subset and embedded as base64 `@font-face` +(see [§7](#7-asset-build-pipeline)). Plain Markdown stays clean and accessible. + +## 3. Visual principles + +- Dark mode first · high contrast · generous whitespace +- Thin 1px borders (`#1E2430`); gridlines at 3–5% opacity +- Circular **trust-ring** motif; node-and-edge diagrams; small monospaced labels +- Sparse cyan highlights; no heavy gradients, glow, cyberpunk, robots, padlocks, + or stock illustration +- Every element communicates **authority, execution, verification, or proof** + +## 4. Badge system + +Grouped, intentional, dark-labelled (`labelColor=0A0A0F`, `style=flat-square`): + +- **Protocol status** — `spec v1.0` (cyan), `protocol TTP` (teal), `runtime in-progress` (amber), `reference Node.js` (green) +- **Security model** — `trust-model decay-enabled` (cyan), `receipts cryptographic` (teal) +- **Community** — `category agent-trust-infrastructure` (steel), `license Apache-2.0` (azure) + +## 5. Label system + +Canonical labels live in [`.github/labels.yml`](../../.github/labels.yml) and are +synced by [`.github/workflows/labels.yml`](../../.github/workflows/labels.yml). +`type:` (protocol/runtime/docs/examples/security), `status:` +(good-first-issue/needs-rfc), `priority:` (critical), and `area:` +(trust-decay/execution-receipts/zkp/compiler) — each colored from the palette. + +## 6. Component inventory + +| Component | Asset | Purpose | +| --- | --- | --- | +| Hero | `assets/ttp-hero.svg` | Above-the-fold identity + protocol flow | +| Social preview | `assets/social-preview.png` | 1280×640 share card | +| Trust ring | `assets/brand/trust-ring.svg` | Circular motif / watermark | +| Protocol loop | `assets/diagrams/protocol-loop.svg` | Identity → … → Receipt | +| Trust gap | `assets/diagrams/trust-gap.svg` | Identity vs Authorization vs TTP | +| Decision matrix | `assets/diagrams/decision-matrix.svg` | Five outcome pills | +| Authority gate | `assets/diagrams/runtime-authority-flow.svg` | Request in → decision out | +| Trust decay curve | `assets/diagrams/trust-decay-curve.svg` | Decay + attestation recharge | +| Execution receipt | `assets/diagrams/execution-receipt.svg` | Cryptographic proof card | +| Architecture stack | `assets/diagrams/architecture-stack.svg` | L0–L4 layers | +| Integration tiles | `assets/diagrams/integrations.svg` | Surfaces + status | +| Contributor path | `assets/diagrams/contributor-path.svg` | First issue → maintainer | +| Roadmap strip | `assets/diagrams/roadmap.svg` | Phase timeline | + +## 7. Asset build pipeline + +Sources are authored with brand `font-family` names and a `/*@FONTS@*/` marker. +The build subsets the real fonts to each file's glyphs and embeds them. + +```bash +npm run assets:fonts # embed brand fonts into every marked SVG (idempotent) +npm run assets:social # render assets/social-preview.png from its SVG +``` + +Fonts (`assets/brand/fonts/*.ttf`) and scripts (`assets/brand/embed-fonts.py`, +`render-png.mjs`) are committed for reproducibility. See +[`assets/brand/README.md`](../../assets/brand/README.md). + +## 8. Repository metadata + +Set the GitHub description and topics: + +```bash +gh repo edit --description "Trust-Before-Execution protocol for AI agents and non-human identities." \ + --add-topic ai-agents --add-topic agent-security --add-topic trust \ + --add-topic runtime-governance --add-topic zero-knowledge-proofs \ + --add-topic non-human-identity --add-topic scim --add-topic authorization \ + --add-topic protocol --add-topic execution-receipts \ + --add-topic agent-trust-infrastructure +``` + +## 9. Implementation checklist + +- [x] Color & typography tokens defined +- [x] Hero SVG with embedded brand fonts +- [x] 1280×640 social preview PNG +- [x] Trust-ring motif +- [x] Protocol-loop, trust-gap, decision-matrix diagrams +- [x] Runtime authority flow, trust-decay curve, execution-receipt diagrams +- [x] Architecture stack, integrations, roadmap, contributor-path diagrams +- [x] README rebuilt as a visual landing page +- [x] Grouped badge strip +- [x] Label system + sync workflow +- [x] PR template, discussion templates, FUNDING +- [x] Reproducible font-embed / render build +- [x] Docs site — VitePress under `website/` (dark-first, brand tokens, custom + protocol callouts, "choose your path" cards); builds clean +- [x] Screenshots — `assets/screenshots/cli-demo.png`, `receipt-preview.png` +- [ ] Set repo description + topics (run §8) +- [ ] Upload `assets/social-preview.png` in repo Settings → Social preview +- [ ] `docs-home.png` screenshot — needs a headless browser to capture the + rendered docs site (`cd website && npm run docs:dev`) diff --git a/docs/concepts/isnad-chains.md b/docs/concepts/isnad-chains.md new file mode 100644 index 0000000..eab5189 --- /dev/null +++ b/docs/concepts/isnad-chains.md @@ -0,0 +1,36 @@ +# Isnad Chains + +An isnad chain expresses the **chain of transmission** for trust: an ordered sequence of links carrying authority from a trusted root authority down to a terminal subject. The name follows the classical *isnad* — a chain of narrators whose soundness depends on every link, not just the source. + +`verify_isnad_chain` records that a transmission chain was evaluated for soundness before downstream authority systems rely on the terminal subject. + +## Boundary + +TTP establishes trustworthiness. RAP evaluates authority. Execution Exchange enforces downstream runtime decisions in production. CortexTrace records evidence and receipts. Isnad chains express transmission continuity; they do not enforce execution. + +## Soundness conditions + +A chain is sound (`valid: true`) when: + +* **Rooted** — the first link's issuer is the declared `rootAuthority`. +* **Continuous (muttasil)** — each link's `issuer` equals the previous link's `subject`. Any gap, self-transmission, or non-transferable intermediate breaks the chain. +* **Each link is valid** — unexpired, in scope for the requested action, issued by a trusted narrator (when `trustedAuthorities` is set), and backed by valid proof references. +* **Above the trust floor** — every link conveys at least `minLinkTrust`. + +## Attenuation + +Trust degrades along the chain — a longer chain conveys less, all else equal. `effectiveTrust` is computed by `attenuation` mode: + +* `product` (default) — multiply every link's conveyed trust. +* `min` — grade the chain by its weakest link. +* `none` — take the terminal link's conveyed trust. + +`weakestLink` identifies the link that grades the chain. With `enforceMonotonic`, a link that conveys more trust than its predecessor fails as amplification. + +## Questions + +* Who is the root authority of trust? +* Is the chain continuous, or is there a broken link? +* Which narrator is the weakest link? +* How much trust survives transmission to the terminal subject? +* What downstream system relies on the chain proof? diff --git a/examples/isnad-invalid.json b/examples/isnad-invalid.json new file mode 100644 index 0000000..216097a --- /dev/null +++ b/examples/isnad-invalid.json @@ -0,0 +1 @@ +{"input":{"chainId":"isnad-002","rootAuthority":"root:cfo","terminalSubject":"agent:invoice-bot"},"output":{"valid":false,"continuous":false},"expectedResult":"isnad chain broken","explanation":"A gap between narrators breaks continuity (munqati'); the chain is not sound."} diff --git a/examples/isnad-valid.json b/examples/isnad-valid.json new file mode 100644 index 0000000..262f7a3 --- /dev/null +++ b/examples/isnad-valid.json @@ -0,0 +1 @@ +{"input":{"chainId":"isnad-001","rootAuthority":"root:cfo","terminalSubject":"agent:invoice-bot"},"output":{"valid":true,"continuous":true,"effectiveTrust":0.4},"expectedResult":"isnad chain sound","explanation":"Continuous chain rooted at the CFO; trust attenuates to 0.4 at the terminal agent."} diff --git a/examples/pctr-consequence-change-demo.mjs b/examples/pctr-consequence-change-demo.mjs new file mode 100644 index 0000000..7e56098 --- /dev/null +++ b/examples/pctr-consequence-change-demo.mjs @@ -0,0 +1,77 @@ +#!/usr/bin/env node +// PCTR end-to-end: a transfer changes mid-run, the consequence changes with it, +// the route it was riding on becomes inadmissible, and authority is re-established +// on the new terms. Run: node examples/pctr-consequence-change-demo.mjs +import { + buildGraph, previewConsequence, consequenceChanged, resolveRoute, reroute, + protect, createTimeline, replay, why, verifyReceipt +} from '../packages/pctr/src/index.mjs'; + +const graph = buildGraph({ + principal: 'user:ops', + agents: [ + { id: 'planner', framework: 'openai-agents', trust: 0.98, evidenceAgeSeconds: 15, authority: ['*'], jurisdiction: 'eu', latencyMs: 60, tools: [], delegatesTo: ['finance-agent-a', 'finance-agent-d'] }, + { id: 'finance-agent-a', framework: 'langgraph', trust: 0.96, evidenceAgeSeconds: 240, authority: ['payments.*'], jurisdiction: 'eu', latencyMs: 40, tools: ['payments'] }, + { id: 'finance-agent-d', framework: 'claude-agents', trust: 0.97, evidenceAgeSeconds: 20, authority: ['payments.*'], jurisdiction: 'eu', latencyMs: 130, tools: ['payments'] } + ], + tools: [{ id: 'payments', protocol: 'mcp', actions: ['payments.transfer'] }], + actions: [{ id: 'payments.transfer', amount: 1800, connectedWorkflows: 2 }], + policy: { requireApprovalAtOrAbove: 'CRITICAL', approvalThresholds: { amount: 5000 }, decayPerHop: 0.05 } +}); + +const timeline = createTimeline({ objective: 'Pay invoice INV-4471' }); +const line = (s = '') => console.log(s); + +// 1. The objective arrives and the first amount is proposed. +const started = timeline.record('AGENT_STARTED', { objective: 'Pay invoice INV-4471' }, { subject: 'planner' }); +const first = timeline.record('ACTION_PROPOSED', { action: 'payments.transfer', amount: 1800 }, { subject: 'planner', because: [started.id] }); + +const before = previewConsequence(graph, 'payments.transfer', { amount: 1800 }); +timeline.record('CONSEQUENCE_DETECTED', { consequence: before.consequence, severity: before.severity }, { because: [first.id] }); +const routeBefore = resolveRoute(graph, 'payments.transfer', { severity: before.severity }); +const selectedBefore = timeline.record('ROUTE_SELECTED', { routeId: routeBefore.selected.routeId }, { because: [first.id] }); + +line(`Proposed transfer: $1,800 -> ${before.severity}`); +line(`Route: ${routeBefore.selected.routeId}`); + +// 2. The amount changes. The consequence changes with it. +const changed = timeline.record('ACTION_PROPOSED', { action: 'payments.transfer', amount: 18000 }, { subject: 'planner', because: [first.id] }); +const after = previewConsequence(graph, 'payments.transfer', { amount: 18000 }); +const detected = timeline.record('CONSEQUENCE_DETECTED', + { consequence: after.consequence, severity: after.severity, reason: `Transfer increased from $1,800 to $18,000, so the consequence is now ${after.severity}.` }, + { because: [changed.id] }); + +line(); +line(`Amount changed: $1,800 -> $18,000 (consequence changed: ${consequenceChanged(before, after)})`); + +// 3. The route it was riding on is no longer admissible for the new consequence. +timeline.record('ROUTE_INVALIDATED', + { routeId: routeBefore.selected.routeId, reason: 'The consequence changed, so the route selected for the smaller transfer is no longer admissible.', policy: 'local-v1' }, + { because: [detected.id, selectedBefore.id] }); + +// Rerouting may never admit a route on weaker grounds than the one it replaces. +const attemptedDowngrade = reroute(graph, 'payments.transfer', { ...routeBefore, severity: 'CRITICAL', trustRequired: 0.9 }, { severity: 'MEDIUM' }); +line(`Attempted cheaper route at lower severity: ${attemptedDowngrade.expansionBlocked ? 'BLOCKED (authority may not silently expand)' : 'allowed'}`); + +// 4. Run the protected loop at the new consequence — first without approval. +const denied = await protect(graph, { action: 'payments.transfer', target: 'acct:9931', params: { amount: 18000 }, agent: 'planner' }, { timeline }); +line(); +line(`Without approval: ${denied.receipt.verifier.decision} — ${denied.receipt.verifier.failures[0].message}`); + +// 5. Approval is recorded, and authority is bound to this exact execution. +const allowed = await protect(graph, + { action: 'payments.transfer', target: 'acct:9931', params: { amount: 18000 }, agent: 'planner' }, + { timeline, approval: { by: 'ops-oncall', at: new Date().toISOString() }, execute: () => ({ transferId: 'tr_88213' }) }); + +line(`With approval: ${allowed.receipt.verifier.decision} — executed ${allowed.result.status}`); +line(`Receipt ${allowed.receipt.receiptId} verifies: ${verifyReceipt(allowed.receipt).valid}`); + +line(); +line('TIMELINE'); +for (const e of replay(timeline)) line(` ${e.at.slice(11, 19)} ${e.line}`); + +const answer = why(timeline, 'ROUTE_INVALIDATED'); +line(); +line(answer.question); +line(` ${answer.answer}`); +for (const b of answer.because) line(` - ${b}`); diff --git a/examples/pctr-universal-fabric-demo.mjs b/examples/pctr-universal-fabric-demo.mjs new file mode 100644 index 0000000..858157b --- /dev/null +++ b/examples/pctr-universal-fabric-demo.mjs @@ -0,0 +1,88 @@ +#!/usr/bin/env node +// Four frameworks, one security meaning, one effect boundary. +// Each agent below emits its own native event shape. PCTR normalizes them into the +// canonical events, classifies the consequence, routes to a trustworthy agent, and has +// the authority verified by a boundary running in its own process. +// Run: node examples/pctr-universal-fabric-demo.mjs +import { + buildGraph, createTimeline, ingest, replay, protect, why, + startBoundaryServer, remoteBoundary, createBoundary, generateKeyPair, exportPublic, verifyReceipt +} from '../packages/pctr/src/index.mjs'; + +const line = (s = '') => console.log(s); + +const graph = buildGraph({ + principal: 'user:ops', + agents: [ + { id: 'planner', framework: 'openai-agents', trust: 0.98, evidenceAgeSeconds: 15, authority: ['*'], latencyMs: 60, tools: [], delegatesTo: ['research', 'finance'] }, + { id: 'research', framework: 'langgraph', trust: 0.9, evidenceAgeSeconds: 40, authority: ['reports.*'], latencyMs: 30, tools: ['reports'] }, + { id: 'finance', framework: 'crewai', trust: 0.97, evidenceAgeSeconds: 25, authority: ['payments.*'], latencyMs: 110, tools: ['payments'] } + ], + tools: [ + { id: 'reports', protocol: 'mcp', actions: ['reports.read'] }, + { id: 'payments', protocol: 'mcp', actions: ['payments.transfer'] } + ], + actions: [{ id: 'payments.transfer', amount: 1800, connectedWorkflows: 2 }], + policy: { requireApprovalAtOrAbove: 'CRITICAL', approvalThresholds: { amount: 5000 } } +}); + +const timeline = createTimeline({ objective: 'Settle supplier invoice INV-4471' }); + +// Each framework hands PCTR the events it already emits. Nothing is rewritten to suit us. +ingest('openai-agents', [ + { type: 'agent_start', agent: 'planner', input: 'Settle supplier invoice INV-4471' }, + { type: 'token_usage', tokens: 1420 }, + { type: 'handoff', from_agent: 'planner', to_agent: 'research' } +], timeline); + +ingest('langgraph', [ + { event: 'on_tool_start', name: 'reports.read', data: { input: { supplier: 'acme' } } }, + { event: 'on_llm_new_token', data: { chunk: 'The' } }, + { event: 'on_tool_end', name: 'reports.read' } +], timeline); + +ingest('mcp', [ + { method: 'tools/list', server: 'payments', result: { tools: [{ name: 'payments.transfer' }, { name: 'payments.status' }] } } +], timeline); + +ingest('crewai', [ + { type: 'agent_delegated', from_agent: 'planner', to_agent: 'finance' }, + { type: 'tool_usage_started', tool: 'payments.transfer', tool_args: { amount: 18000 }, agent: 'finance' } +], timeline); + +line('NORMALIZED FROM FOUR FRAMEWORKS'); +for (const e of replay(timeline)) line(` ${e.line}`); + +// The boundary holds the key list and the replay state, in its own process. +const key = generateKeyPair(); +const boundary = createBoundary({ trustedKeyIds: [key.keyId], publicKey: exportPublic(key.publicKey) }); +const { url, close } = await startBoundaryServer({ port: 0, boundary }); + +line(); +line(`Effect boundary running at ${url}, trusting only ${key.keyId}`); + +const request = { action: 'payments.transfer', target: 'acct:9931', params: { amount: 18000 }, agent: 'finance' }; +const options = { timeline, boundary: remoteBoundary(url), keyPair: key }; + +const denied = await protect(graph, request, options); +line(`Without approval: ${denied.receipt.verifier.decision} — ${denied.receipt.verifier.failures[0].message}`); + +const allowed = await protect(graph, request, { + ...options, approval: { by: 'ops-oncall' }, execute: () => ({ transferId: 'tr_88213' }) +}); +line(`With approval: ${allowed.receipt.verifier.decision} at ${allowed.receipt.verifier.boundary}`); +line(`Receipt verifies against the boundary's public key: ${verifyReceipt(allowed.receipt, { publicKey: exportPublic(key.publicKey) }).valid}`); + +// Replaying the same authority is refused: it has already been spent. +const replayed = await remoteBoundary(url).verify(allowed.authority, { + action: 'payments.transfer', target: 'acct:9931', params: { amount: 18000 }, principal: 'user:ops' +}); +line(`Replaying that authority: ${replayed.decision} (${replayed.failures.map((f) => f.code).join(', ')})`); + +const answer = why(timeline, 'EXECUTION_DENIED'); +line(); +line(answer.question); +line(` ${answer.answer}`); +for (const b of answer.because.slice(-4)) line(` - ${b}`); + +await close(); diff --git a/infra/bicep/README.md b/infra/bicep/README.md deleted file mode 100644 index 74141b1..0000000 --- a/infra/bicep/README.md +++ /dev/null @@ -1 +0,0 @@ -# bicep diff --git a/infra/env/README.md b/infra/env/README.md deleted file mode 100644 index 18453b8..0000000 --- a/infra/env/README.md +++ /dev/null @@ -1 +0,0 @@ -# env diff --git a/infra/scripts/README.md b/infra/scripts/README.md deleted file mode 100644 index 458b3cc..0000000 --- a/infra/scripts/README.md +++ /dev/null @@ -1 +0,0 @@ -# scripts diff --git a/package-lock.json b/package-lock.json index ceb098f..4256cd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,9 +7,273 @@ "": { "name": "@blocksifr/ttp-protocol", "version": "0.1.0", + "devDependencies": { + "@fontsource/inter": "^5.2.8", + "@fontsource/jetbrains-mono": "^5.2.8", + "@fontsource/space-grotesk": "^5.2.10", + "@resvg/resvg-js": "^2.6.2" + }, "engines": { "node": ">=18" } + }, + "node_modules/@fontsource/inter": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.8.tgz", + "integrity": "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/jetbrains-mono": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.2.8.tgz", + "integrity": "sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/space-grotesk": { + "version": "5.2.10", + "resolved": "https://registry.npmjs.org/@fontsource/space-grotesk/-/space-grotesk-5.2.10.tgz", + "integrity": "sha512-XNXEbT74OIITPqw2H6HXwPDp85fy43uxfBwFR5PU+9sLnjuLj12KlhVM9nZVN6q6dlKjkuN8JisW/OBxwxgUew==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@resvg/resvg-js": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz", + "integrity": "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@resvg/resvg-js-android-arm-eabi": "2.6.2", + "@resvg/resvg-js-android-arm64": "2.6.2", + "@resvg/resvg-js-darwin-arm64": "2.6.2", + "@resvg/resvg-js-darwin-x64": "2.6.2", + "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2", + "@resvg/resvg-js-linux-arm64-gnu": "2.6.2", + "@resvg/resvg-js-linux-arm64-musl": "2.6.2", + "@resvg/resvg-js-linux-x64-gnu": "2.6.2", + "@resvg/resvg-js-linux-x64-musl": "2.6.2", + "@resvg/resvg-js-win32-arm64-msvc": "2.6.2", + "@resvg/resvg-js-win32-ia32-msvc": "2.6.2", + "@resvg/resvg-js-win32-x64-msvc": "2.6.2" + } + }, + "node_modules/@resvg/resvg-js-android-arm-eabi": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.2.tgz", + "integrity": "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-android-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.2.tgz", + "integrity": "sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-darwin-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-arm64/-/resvg-js-darwin-arm64-2.6.2.tgz", + "integrity": "sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-darwin-x64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.2.tgz", + "integrity": "sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm-gnueabihf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.2.tgz", + "integrity": "sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.2.tgz", + "integrity": "sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.2.tgz", + "integrity": "sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-x64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.2.tgz", + "integrity": "sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-x64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.2.tgz", + "integrity": "sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-arm64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.2.tgz", + "integrity": "sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-ia32-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.2.tgz", + "integrity": "sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-x64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.2.tgz", + "integrity": "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } } } } diff --git a/package.json b/package.json index de067ba..4733657 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@blocksifr/ttp-protocol", "version": "0.1.0", - "description": "Trust Transfer Protocol — open trustworthiness primitives for autonomous systems, non-human identities, and runtime authority.", + "description": "Trust Transfer Protocol \u2014 open trustworthiness primitives for autonomous systems, non-human identities, and runtime authority.", "private": false, "type": "module", "publishConfig": { @@ -38,9 +38,23 @@ "ttp": "node src/index.js", "build:sdk": "npm install --prefix sdk/typescript && npm run build --prefix sdk/typescript", "build:trust-authority": "npm install --prefix reference-implementations/trust-authority && npm run build --prefix reference-implementations/trust-authority", - "test": "node --test tests/*.test.mjs packages/trust-routing-engine/tests/*.test.mjs", + "test": "node --test tests/*.test.mjs packages/trust-routing-engine/tests/*.test.mjs packages/pctr/tests/*.test.mjs", "test:ttp": "node --test tests/*.test.mjs", "test:trust-routing": "node --test packages/trust-routing-engine/tests/*.test.mjs", - "check:examples": "node src/index.js check examples/01-basic-agent.ttp && node src/index.js check examples/02-trust-decay.ttp && node src/index.js check examples/03-threshold-proof.ttp && node src/index.js check examples/04-delegated-trust.ttp && node src/index.js check examples/05-agent-tool-trust-wrapper.ttp && node src/index.js check examples/06-cicd-pipeline-trust-proof.ttp && node src/index.js check examples/07-api-client-trust-proof.ttp && node src/index.js check examples/08-msp-mssp-trust-proof.ttp" + "check:examples": "node src/index.js check examples/01-basic-agent.ttp && node src/index.js check examples/02-trust-decay.ttp && node src/index.js check examples/03-threshold-proof.ttp && node src/index.js check examples/04-delegated-trust.ttp && node src/index.js check examples/05-agent-tool-trust-wrapper.ttp && node src/index.js check examples/06-cicd-pipeline-trust-proof.ttp && node src/index.js check examples/07-api-client-trust-proof.ttp && node src/index.js check examples/08-msp-mssp-trust-proof.ttp", + "assets:fonts": "python3 assets/brand/embed-fonts.py", + "assets:social": "node assets/brand/render-png.mjs assets/social-preview.svg assets/social-preview.png 1280", + "assets": "npm run assets:fonts && npm run assets:social", + "test:pctr": "node --test packages/pctr/tests/*.test.mjs", + "pctr": "node packages/pctr/bin/pctr.mjs", + "demo:pctr": "node examples/pctr-consequence-change-demo.mjs", + "demo:pctr-fabric": "node examples/pctr-universal-fabric-demo.mjs", + "assets:pctr": "node packages/pctr/scripts/render-terminal-svg.mjs" + }, + "devDependencies": { + "@fontsource/inter": "^5.2.8", + "@fontsource/jetbrains-mono": "^5.2.8", + "@fontsource/space-grotesk": "^5.2.10", + "@resvg/resvg-js": "^2.6.2" } } diff --git a/packages/pctr/LICENSE b/packages/pctr/LICENSE new file mode 100644 index 0000000..b9642ee --- /dev/null +++ b/packages/pctr/LICENSE @@ -0,0 +1,216 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Copyright 2026 BlockSiFr + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/pctr/README.md b/packages/pctr/README.md new file mode 100644 index 0000000..e6d7bc7 --- /dev/null +++ b/packages/pctr/README.md @@ -0,0 +1,355 @@ +# PCTR — Protected Consequence Trust Routing + +**See what your agents can cause. Route them safely. Prove what happened.** + +Your agent can have valid credentials and still be about to do the wrong thing. + +``` +Objective -> Trust Route -> Consequence Preview -> Execution Authority -> Protected Action -> Receipt +``` + +## 60 seconds + +pctr scan finds five agents, three tools and four potential actions, and reports one critical protected consequence: customers.delete can be reached through support-agent and admin-agent without independent execution authority + +Not on npm yet — from a clone of this repo: + +```bash +npm link ./packages/pctr # then the bare `pctr` command works anywhere +pctr init # discover agents and tools, write pctr.json +pctr scan # what consequences can they reach? +``` + +No account. No network. Everything stays in `./pctr.json` and `./.pctr`. + +## Share what you find + +```bash +pctr scan --share # writes pctr-report.md +``` + +Drops straight into a pull request or an issue: a severity table, the highest-priority +consequence with the route that reaches it, and what to do about it. Add +`packages/pctr/examples/pctr-scan.yml` to `.github/workflows/` and every PR gets the same +report as a comment, with `fail-on: critical` to block a merge when an agent can reach an +irreversible consequence. + +```markdown +## 🛑 PCTR consequence scan + +**5 agents · 3 tools · 4 actions.** 3 of them can cause a protected consequence, and 2 cannot be undone. + +| Severity | Consequences | +| --- | --- | +| 🔴 CRITICAL | 1 | +| 🟠 HIGH | 1 | + +### Highest priority: `customers.delete` + +Causes **data deleted** · **irreversible** · 1,842 records · blast radius WIDE +``` + +## The three questions + +| Question | Command | Answers | +| --- | --- | --- | +| **Consequence** — what can this action cause? | `pctr preview ` | records, money, reversibility, blast radius | +| **Route** — which trustworthy agent path may get there? | `pctr route ` | the selected path, and why every other one was rejected | +| **Authority** — is this exact execution allowed right now? | `pctr protect ` | an allow/deny decision plus a verifiable receipt | + +## Consequence Twin + +See what your AI is about to change before it changes it — `git diff` for autonomous +agents. It reasons over the graph and the parameters you declare; it does not query your +live systems (see [Honest limits](#honest-limits)). + +```bash +pctr preview customers.delete --records 1842 +``` + +``` +CONSEQUENCE PREVIEW + +Action customers.delete +Causes data deleted +Records affected 1842 +Reversible NO +Connected workflows 4 +Blast radius WIDE (100/100) +Routes that reach it 1 +Risk CRITICAL + +Recommended + +- Require TTP execution authority bound to these exact parameters +- Limit batch to 25 records (currently 1842) + require approval +- Notify 4 connected workflows before executing +- Verify authority at the effect boundary, not in the calling agent +``` + +## TrustRoute Autopilot + +The right trustworthy agent gets the job automatically. Admissibility is decided first; +only routes that survive are optimized for latency and cost. A security constraint is +never traded away for a faster or cheaper route. + +```bash +pctr route payments.transfer --amount 18000 +``` + +``` +ROUTE SELECTED + +user:local + | + v +planner + | + v +finance-agent-d + | + v +payments + | + v +payments.transfer + +Effective trust 0.9178 (0.9 required for CRITICAL) +Hops 5 +Human approval REQUIRED + +Rejected routes + +user:local -> planner -> finance-agent-a -> payments -> payments.transfer + x finance-agent-a evidence is 4200s old; CRITICAL actions require evidence under 300s + x effective trust 0.6579 is below the 0.9 required for CRITICAL consequences +``` + +At $1,800 the same action is `HIGH` and needs no approval; the amount is what moves it to +`CRITICAL`. Material parameters change the consequence, and the consequence sets the bar. + +`finance-agent-a` was the *faster* route. It was still rejected. + +## Agent Time Machine + +Replay exactly why your AI did what it did. + +```bash +pctr protect payments.transfer --amount 18000 +pctr replay +pctr explain EXECUTION_DENIED +``` + +``` +WHY WAS THIS EXECUTION DENIED? + +policy requires human approval for CRITICAL consequences + +Because + +- Objective received: protect payments.transfer +- Action proposed: payments.transfer ($18000) +- Consequence detected: MONEY_MOVED (CRITICAL) +- Route selected: user:local -> planner -> finance-agent-d -> payments -> payments.transfer +``` + +Every answer is derived from the recorded causal chain, not re-narrated by a model. +Runs can also be forked and compared: + +```bash +pctr replay --fork e3 +pctr replay --compare +``` + +## Execution authority + +A valid identity is not enough. A valid credential is not enough. A valid route is not +enough. Authority binds principal, delegator, session, action, target, material +parameters, constraints, policy, consequence, validity window and nonce — and the effect +boundary verifies it independently of the agent that asked. + +```js +import { issueAuthority, verifyAuthority } from '@blocksifr/pctr'; + +const authority = issueAuthority({ + principal: 'user:ops', action: 'payments.transfer', target: 'acct:9931', + params: { amount: 1800 }, consequence: 'MONEY_MOVED', route +}); + +// Same authority, changed amount — possession is not authority. +verifyAuthority(authority, { action: 'payments.transfer', target: 'acct:9931', params: { amount: 18000 } }); +// -> { allowed: false, failures: [{ code: 'PARAMETER_MISMATCH', ... }] } +``` + +Authority also expires and can only be spent once (`REPLAYED_AUTHORITY`). + +## Receipts + +Every protected execution produces verifiable evidence of what was +requested, who delegated it, which route was selected, what consequence was identified, +what parameters were bound, which verifier checked it, and what actually executed. + +```bash +pctr receipt # show the last one +pctr verify # verify all of them, including the chain +``` + +A receipt that records an execution which was never allowed fails verification +(`EXECUTED_WITHOUT_AUTHORITY`), and a removed or reordered receipt breaks the chain. + +Receipts and authority are signed with **Ed25519**. Verification takes the public key, so +someone who can check your receipts cannot mint one: + +```bash +pctr keys --export public-key.pem # share this; never share .pctr/keys/signing-key.pem +pctr verify --key public-key.pem +``` + +``` +RECEIPT VERIFICATION + +Receipts checked 3 +Valid 2 of 3 +Chain BROKEN +Signer verified against a pinned key + x rcpt-3d80fffd-b45a-405f-8a70-007b5ce7bfde + receipt signature does not verify +``` + +Verifying without a pinned key still works, but reports itself as `signerVerified: false` +with an `UNPINNED_KEY` warning — internal consistency is not proof of who signed. Pin +signers permanently with `"trustedSigners": ["ed25519:…"]` in `pctr.json`. + +## Commands + +``` +pctr init Discover agents and tools, write pctr.json +pctr scan What consequences can your agents reach? +pctr graph Show the trust graph +pctr preview Consequence Twin +pctr route TrustRoute Autopilot +pctr protect Run the full loop and issue a receipt +pctr simulate Same, without executing any effect +pctr replay [run] Agent Time Machine +pctr explain Why was this allowed, denied, or rerouted? +pctr receipt [id] Show an execution receipt +pctr verify [id] Verify receipt signatures and the receipt chain +pctr keys Show your signing key id and public key +pctr serve Run the effect boundary as its own process +pctr doctor Check your setup +``` + +Every command prints plain English first. Add `--json` for machine-readable output. + +## pctr.json + +```json +{ + "principal": "user:ops", + "agents": [ + { "id": "planner", "framework": "openai-agents", "trust": 0.98, "evidenceAgeSeconds": 20, + "authority": ["*"], "jurisdiction": "eu", "tools": [], "delegatesTo": ["finance-agent-d"] }, + { "id": "finance-agent-d", "framework": "claude-agents", "trust": 0.97, "evidenceAgeSeconds": 30, + "authority": ["payments.*"], "jurisdiction": "eu", "latencyMs": 140, "tools": ["payments"] } + ], + "tools": [{ "id": "payments", "protocol": "mcp", "actions": ["payments.transfer"] }], + "actions": [{ "id": "payments.transfer", "amount": 1800, "connectedWorkflows": 2 }], + "policy": { + "requireApprovalAtOrAbove": "CRITICAL", + "approvalThresholds": { "amount": 5000 }, + "jurisdictions": { "MONEY_MOVED": ["eu"] }, + "maxHops": 6, + "decayPerHop": 0.05 + } +} +``` + +Agents are participants, not platforms. A new framework needs an adapter that maps its +runtime events onto the canonical PCTR events — not a redesign of PCTR. + +## Universal agent fabric + +Adapters ship for `openai-agents`, `claude-agents`, `langgraph`, `crewai`, `autogen`, +`semantic-kernel`, `mcp`, `a2a`, and a `generic` envelope for everything else. Each one +maps a framework's own events onto the 16 canonical security events and drops the rest: + +```js +import { ingest, createTimeline } from '@blocksifr/pctr'; + +const timeline = createTimeline({ objective: 'Settle invoice INV-4471' }); +ingest('crewai', crewEvents, timeline); +ingest('langgraph', graphEvents, timeline); // one timeline, one security meaning +``` + +A tool call in any of them becomes `ACTION_PROPOSED` plus the `CONSEQUENCE_DETECTED` it +implies — the part every framework leaves out. Token usage, streamed text and other +framework internals are dropped rather than invented into events. + +See `node examples/pctr-universal-fabric-demo.mjs` for four frameworks feeding one +protected execution through a remote effect boundary. + +## Relationship to TTP + +PCTR answers *which path through multiple agents is trustworthy enough to reach this +consequence*. [TTP](../../README.md) answers *is this exact execution authority valid +for this principal, action, target, parameters, delegation and moment*. + +> TLS protects the channel. TTP protects the authority transferred through the channel. + +## Probes: measure the consequence, don't declare it + +A number typed into `pctr.json` is a guess. A probe measures. Probes must be read-only — +a `COUNT`, a dry run, a `terraform plan` — because PCTR runs them *before* anything is +authorized. + +```js +// probes/count-customers.mjs +export default async function probe({ action, params }) { + const { rows } = await db.query('select count(*) as rows from customers where inactive_days > $1', [params.olderThanDays]); + return { recordsAffected: Number(rows), connectedWorkflows: 4, reversible: false }; +} +``` + +```json +{ "probes": { "customers.delete": "./probes/count-customers.mjs" } } +``` + +``` +Records affected 1842 (measured) +``` + +A shell probe works too (`{ "command": "./plan.sh" }`, JSON on stdout, with `PCTR_ACTION`, +`PCTR_TARGET` and `PCTR_PARAMS` in the environment). If a probe fails, the preview falls +back to declared values and says so — it never silently becomes "no consequence". + +## The effect boundary + +The requesting agent must not enforce its own authority, so run the boundary somewhere +the agent does not control: + +```bash +pctr serve --port 8787 # holds the replay state and the trusted signers +pctr protect payments.transfer --boundary http://127.0.0.1:8787 +``` + +Spent nonces persist to `.pctr/spent-nonces.json`, so an authority cannot be replayed +across a restart. If the boundary is unreachable, execution is **denied** — it never +fails open. + +## Honest limits + +- **Blast radius is a heuristic**, not a measurement: severity, record count, connected + workflows and route count folded into a 0-100 score. The inputs are measurable; the + weighting is a judgement call. +- **Trust scores come from your manifest.** PCTR decays them, tests them against the bar + the consequence sets, and refuses stale evidence — but it does not attest agents + itself. Wire `trust` and `evidenceAgeSeconds` to a real attestation source. +- **Discovery reads source, not running systems.** It parses MCP/LangChain/CrewAI tool + and agent declarations statically, and does not connect to a live MCP server to list + its tools. Anything it cannot see, you declare — and declared entries always win. +- **Keys live on disk.** `.pctr/keys/signing-key.pem` is written 0600 and must not be + committed. KMS/HSM custody is commercial BlockSiFr, not this package. +- **`pctr serve` is plain HTTP on localhost.** Put it behind TLS and your own + authentication before it leaves the machine. + +Apache-2.0. diff --git a/packages/pctr/action.yml b/packages/pctr/action.yml new file mode 100644 index 0000000..dcffc03 --- /dev/null +++ b/packages/pctr/action.yml @@ -0,0 +1,94 @@ +name: 'PCTR consequence scan' +description: 'See what your AI agents can cause, before they cause it. Maps agents, tools and actions to the consequences they can reach.' +author: 'BlockSiFr' +branding: + icon: 'shield' + color: 'red' + +inputs: + working-directory: + description: 'Directory to scan.' + required: false + default: '.' + comment: + description: 'Post the findings as a pull request comment (updates the same comment on each run).' + required: false + default: 'true' + fail-on: + description: 'Fail the job when a consequence at this severity or above is reachable: none, critical, high, or medium.' + required: false + default: 'none' + version: + description: 'Version of @blocksifr/pctr to run.' + required: false + default: 'latest' + +outputs: + critical: + description: 'Number of CRITICAL consequences reachable.' + value: ${{ steps.scan.outputs.critical }} + high: + description: 'Number of HIGH consequences reachable.' + value: ${{ steps.scan.outputs.high }} + report: + description: 'Path to the Markdown report.' + value: ${{ steps.scan.outputs.report }} + +runs: + using: composite + steps: + - id: scan + shell: bash + working-directory: ${{ inputs.working-directory }} + run: | + set -euo pipefail + PCTR="npx --yes @blocksifr/pctr@${{ inputs.version }}" + + # init is additive: an existing pctr.json is kept and only extended. + $PCTR init > /dev/null + $PCTR scan --share pctr-report.md > /dev/null + $PCTR scan --json > pctr-scan.json + + CRITICAL=$(node -p "require('./pctr-scan.json').summary.bySeverity.CRITICAL") + HIGH=$(node -p "require('./pctr-scan.json').summary.bySeverity.HIGH") + MEDIUM=$(node -p "require('./pctr-scan.json').summary.bySeverity.MEDIUM") + + { + echo "critical=$CRITICAL" + echo "high=$HIGH" + echo "medium=$MEDIUM" + echo "report=${{ inputs.working-directory }}/pctr-report.md" + } >> "$GITHUB_OUTPUT" + + cat pctr-report.md >> "$GITHUB_STEP_SUMMARY" + + case "${{ inputs.fail-on }}" in + critical) THRESHOLD=$CRITICAL ;; + high) THRESHOLD=$((CRITICAL + HIGH)) ;; + medium) THRESHOLD=$((CRITICAL + HIGH + MEDIUM)) ;; + *) THRESHOLD=0 ;; + esac + if [ "$THRESHOLD" -gt 0 ]; then + echo "::error::$THRESHOLD consequence(s) at or above '${{ inputs.fail-on }}' can be reached by these agents. See the job summary." + exit 1 + fi + + - if: ${{ always() && inputs.comment == 'true' && github.event_name == 'pull_request' }} + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + MARKER='' + printf '%s\n%s' "$MARKER" "$(cat pctr-report.md)" > pctr-comment.md + + # Update the existing comment rather than adding one per push. + EXISTING=$(gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \ + --jq "map(select(.body | contains(\"$MARKER\"))) | .[0].id // empty") + + if [ -n "$EXISTING" ]; then + gh api --method PATCH "repos/${{ github.repository }}/issues/comments/$EXISTING" -F body=@pctr-comment.md > /dev/null + else + gh api --method POST "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" -F body=@pctr-comment.md > /dev/null + fi diff --git a/packages/pctr/bin/pctr.mjs b/packages/pctr/bin/pctr.mjs new file mode 100755 index 0000000..416510e --- /dev/null +++ b/packages/pctr/bin/pctr.mjs @@ -0,0 +1,391 @@ +#!/usr/bin/env node +import { buildGraph, summarize, protectedActions } from '../src/graph.mjs'; +import { discover, defaultPolicy } from '../src/discover.mjs'; +import { previewConsequence } from '../src/twin.mjs'; +import { previewMeasured } from '../src/probe.mjs'; +import { resolveRoute } from '../src/router.mjs'; +import { protect, simulate } from '../src/protect.mjs'; +import { createBoundary, startBoundaryServer, remoteBoundary, persistentReplayStore } from '../src/boundary.mjs'; +import { verifyReceipt, verifyReceiptChain } from '../src/receipt.mjs'; +import { createTimeline, fromJSON, why, compare, fork } from '../src/timeline.mjs'; +import { defaultKeyPair, exportPublic } from '../src/keys.mjs'; +import fs from 'node:fs'; +import path from 'node:path'; +import * as store from '../src/store.mjs'; +import { renderReport, badgeUrl } from '../src/report.mjs'; +import * as r from '../src/render.mjs'; + +const VERSION = '0.1.0'; + +const argv = process.argv.slice(2); +const command = argv[0]; +// Flags that take a value, so their value is never mistaken for a positional argument. +const VALUE_FLAGS = new Set(['amount', 'records', 'recordsAffected', 'batch', 'params', 'approve', + 'target', 'agent', 'objective', 'compare', 'fork', 'key', 'export', 'run', 'probe', 'boundary', 'port', 'share']); +const positional = (() => { + const out = []; + for (let i = 1; i < argv.length; i++) { + const token = argv[i]; + if (token.startsWith('--')) { + if (VALUE_FLAGS.has(token.slice(2)) && argv[i + 1] && !argv[i + 1].startsWith('--')) i++; + continue; + } + out.push(token); + } + return out; +})(); +const flag = (name, fallback = null) => { + const i = argv.indexOf(`--${name}`); + if (i === -1) return fallback; + const next = argv[i + 1]; + return next && !next.startsWith('--') ? next : true; +}; +const asJson = argv.includes('--json'); +const out = (human, data) => console.log(asJson ? JSON.stringify(data, null, 2) : human); + +const params = () => { + const p = {}; + for (const key of ['amount', 'records', 'recordsAffected', 'batch']) { + const v = flag(key); + if (v != null && v !== true) p[key === 'records' ? 'recordsAffected' : key] = Number(v); + } + const raw = flag('params'); + if (raw && raw !== true) Object.assign(p, JSON.parse(raw)); + return p; +}; + +// How receipts get checked: an explicit public key file is real third-party +// verification; trustedSigners in pctr.json pins who is allowed to have signed. +function verifyOptions() { + const keyPath = flag('key'); + const manifest = store.readManifest(); + return { + publicKey: keyPath && keyPath !== true ? fs.readFileSync(String(keyPath), 'utf8') : undefined, + trustedKeyIds: manifest?.trustedSigners + }; +} + +function loadGraph() { + const manifest = store.readManifest(); + if (!manifest) { + console.error('No pctr.json found. Run: pctr init'); + process.exit(2); + } + return buildGraph(manifest); +} + +async function main() { + switch (command) { + case undefined: + case 'help': case '--help': case '-h': + console.log(help()); return 0; + case 'version': case '--version': case '-v': + console.log(`pctr ${VERSION}`); return 0; + + case 'init': { + const existing = store.readManifest(); + const { manifest, notes, discovered } = discover(process.cwd(), { declared: existing }); + // In CI a fabricated example would produce a false report about someone's repo, + // so --no-example keeps an empty scan empty. + if (!manifest.agents.length && !argv.includes('--no-example')) { + manifest.agents = exampleManifest().agents; + manifest.tools = exampleManifest().tools; + manifest.actions = exampleManifest().actions; + notes.push('No agents detected; wrote a worked example you can edit.'); + } + const path = store.writeManifest(manifest); + if (asJson) return out(null, { path, manifest, notes }); + console.log(r.heading('pctr initialized')); + console.log(`Wrote ${path}`); + console.log(r.field('Agents', String(discovered.agents || manifest.agents.length))); + console.log(r.field('Tools', String(discovered.tools || manifest.tools.length))); + for (const n of notes) console.log(r.dim(`- ${n}`)); + console.log(`\nNext: ${r.bold('pctr scan')}`); + return 0; + } + + case 'scan': { + const graph = loadGraph(); + if (flag('share')) { + const report = renderReport(graph); + const file = flag('share') !== true ? String(flag('share')) : 'pctr-report.md'; + fs.writeFileSync(file, report); + if (asJson) return out(null, { file, report, badge: badgeUrl(graph) }); + console.log(report); + console.log(r.dim(`Written to ${file} — paste it into a PR, an issue, or a message.`)); + return 0; + } + return out(r.renderScan(graph), { summary: summarize(graph), protected: protectedActions(graph), badge: badgeUrl(graph) }); + } + + case 'graph': { + const graph = loadGraph(); + return out(r.renderGraph(graph), { + principal: graph.principal, + nodes: [...graph.nodes.values()], + edges: graph.edges + }); + } + + case 'preview': { + const graph = loadGraph(); + const action = positional[0] ?? protectedActions(graph)[0]?.id; + if (!action) return fail('Nothing to preview. Pass an action: pctr preview '); + const probes = flag('probe') && flag('probe') !== true ? { [action]: String(flag('probe')) } : undefined; + const p = await previewMeasured(graph, action, params(), { probes, target: flag('target') }); + return out(r.renderPreview(p), p); + } + + case 'route': { + const graph = loadGraph(); + const action = positional[0] ?? protectedActions(graph)[0]?.id; + if (!action) return fail('Pass an action: pctr route '); + // Material parameters can change the consequence, and the consequence sets the bar. + const severity = previewConsequence(graph, action, params()).severity; + const result = resolveRoute(graph, action, { target: flag('target'), severity }); + return out(r.renderRoute(result), result); + } + + case 'protect': case 'simulate': { + const graph = loadGraph(); + const action = positional[0] ?? protectedActions(graph)[0]?.id; + if (!action) return fail(`Pass an action: pctr ${command} `); + const request = { action, target: flag('target') ?? action, params: params(), agent: flag('agent') }; + const probes = flag('probe') && flag('probe') !== true ? { [action]: String(flag('probe')) } : undefined; + const measured = await previewMeasured(graph, action, request.params, { probes, target: request.target }); + const approval = flag('approve') ? { by: String(flag('approve')), at: new Date().toISOString() } : null; + const run = await (command === 'simulate' ? simulate : protect)(graph, request, { + objective: flag('objective') ?? `${command} ${action}`, + approval, + preview: measured, + boundary: flag('boundary') && flag('boundary') !== true ? remoteBoundary(String(flag('boundary'))) : undefined, + priorReceiptHash: store.listReceipts().at(-1)?.receiptHash ?? null, + timeline: createTimeline({ objective: flag('objective') ?? `${command} ${action}` }) + }); + store.saveRun(run.timeline); + store.saveReceipt(run.receipt); + if (asJson) return out(null, { allowed: run.allowed, preview: run.preview, route: run.route, receipt: run.receipt, timeline: run.timeline.toJSON() }); + console.log(r.renderPreview(run.preview)); + if (run.routeResult) console.log(r.renderRoute(run.routeResult)); + console.log(r.renderReceipt(run.receipt, verifyReceipt(run.receipt))); + console.log(`\n${r.dim(`Replay this run: pctr replay ${run.timeline.runId}`)}`); + return run.allowed ? 0 : 1; + } + + case 'replay': { + const data = store.loadRun(positional[0]); + if (!data) return fail('No runs recorded yet. Run: pctr protect '); + const timeline = fromJSON(data); + if (flag('compare') && flag('compare') !== true) { + const other = store.loadRun(String(flag('compare'))); + if (!other) return fail(`No run named ${flag('compare')}`); + const diff = compare(timeline, fromJSON(other)); + return out(renderCompare(diff, data.runId, other.runId), diff); + } + if (flag('fork')) { + const forked = fork(timeline, { upTo: flag('fork') !== true ? String(flag('fork')) : null }); + store.saveRun(forked); + return out(`${r.renderTimeline(forked)}\n${r.dim(`Forked run saved as ${forked.runId}`)}`, forked.toJSON()); + } + return out(r.renderTimeline(timeline), data); + } + + case 'explain': { + const data = store.loadRun(flag('run') !== true ? flag('run') : null); + if (!data) return fail('No runs recorded yet. Run: pctr protect '); + const question = positional[0] ?? 'EXECUTION_DENIED'; + const answer = why(fromJSON(data), question); + return out(r.renderWhy(answer), answer); + } + + case 'serve': { + // The boundary runs here, in its own process, holding the replay state and the + // list of signers it accepts. Agents ask it; they do not decide for themselves. + const manifest = store.readManifest(); + const keyPath = flag('key'); + const boundary = createBoundary({ + trustedKeyIds: manifest?.trustedSigners, + publicKey: keyPath && keyPath !== true ? fs.readFileSync(String(keyPath), 'utf8') : undefined, + replayStore: persistentReplayStore(path.join(store.dir(), 'spent-nonces.json')) + }); + const port = Number(flag('port') !== true && flag('port') != null ? flag('port') : 8787); + const { url } = await startBoundaryServer({ port, boundary, + onDecision: (decision, { execution }) => { + console.log(`${decision.allowed ? r.green('ALLOW') : r.red('DENY ')} ${execution.action} -> ${execution.target ?? ''} ${decision.allowed ? '' : r.dim(decision.failures.map((f) => f.code).join(', '))}`); + } }); + console.log(r.heading('effect boundary')); + console.log(r.field('Listening on', url)); + console.log(r.field('Trusted signers', manifest?.trustedSigners?.join(', ') ?? r.yellow('none pinned — set trustedSigners in pctr.json'))); + console.log(r.field('Replay state', path.join(store.dir(), 'spent-nonces.json'))); + console.log(`\n${r.dim(`Enforce against it with: pctr protect --boundary ${url}`)}`); + await new Promise(() => {}); // serve until interrupted + return 0; + } + + case 'keys': { + const pair = defaultKeyPair(); + const publicKey = exportPublic(pair.publicKey); + if (flag('export') && flag('export') !== true) { + fs.writeFileSync(String(flag('export')), `${publicKey}\n`); + console.log(`Wrote public key to ${flag('export')}`); + } + return out([r.heading('signing keys'), + r.field('Key id', pair.keyId), + r.field('Private key', pair.ephemeral ? r.yellow('ephemeral (not persisted)') : pair.source), + r.field('Algorithm', 'ed25519'), + '', r.dim('Share the public key below. Anyone holding it can verify your receipts'), + r.dim('and cannot mint one. Never share the private key.'), '', publicKey].join('\n'), + { keyId: pair.keyId, algorithm: 'ed25519', publicKey, source: pair.source, ephemeral: pair.ephemeral }); + } + + case 'receipt': { + const receipts = store.listReceipts(); + if (!receipts.length) return fail('No receipts yet. Run: pctr protect '); + const receipt = positional[0] ? receipts.find((x) => x.receiptId === positional[0]) : receipts.at(-1); + if (!receipt) return fail(`No receipt ${positional[0]}`); + return out(r.renderReceipt(receipt, verifyReceipt(receipt, verifyOptions())), receipt); + } + + case 'verify': { + const receipts = store.listReceipts(); + if (!receipts.length) return fail('No receipts to verify yet.'); + const target = positional[0] ? [receipts.find((x) => x.receiptId === positional[0])].filter(Boolean) : receipts; + const opts = verifyOptions(); + const results = target.map((x) => ({ receiptId: x.receiptId, ...verifyReceipt(x, opts) })); + const bad = results.filter((x) => !x.valid); + const unpinned = results.filter((x) => x.valid && !x.signerVerified); + const chain = verifyReceiptChain(receipts, opts); + const human = [r.heading('receipt verification'), + r.field('Receipts checked', String(results.length)), + r.field('Valid', bad.length ? r.red(`${results.length - bad.length} of ${results.length}`) : r.green(String(results.length))), + r.field('Chain', chain.valid ? r.green('intact') : r.red('BROKEN')), + r.field('Signer', unpinned.length + ? r.yellow(`unpinned for ${unpinned.length} receipt(s) — pass --key or set trustedSigners in pctr.json`) + : r.green('verified against a pinned key')), + ...bad.flatMap((b) => [` ${r.red('x')} ${b.receiptId}`, ...b.failures.map((f) => ` ${f.message}`)]), + ...(chain.valid ? [] : chain.failures.flatMap((f) => [` ${r.red('x')} ${f.receiptId}`, ...f.failures.map((x) => ` ${x.message}`)]))].join('\n'); + out(human, { results, chain }); + return bad.length ? 1 : 0; + } + + case 'doctor': { + const manifest = store.readManifest(); + const checks = []; + checks.push(check('pctr.json present', Boolean(manifest), 'Run: pctr init')); + const graph = manifest ? buildGraph(manifest) : null; + if (graph) { + const s = summarize(graph); + checks.push(check('Agents declared', s.agents > 0, 'Add agents to pctr.json')); + checks.push(check('Tools declared', s.tools > 0, 'Add tools with their actions to pctr.json')); + checks.push(check('Protected consequences mapped', s.protected > 0, 'No protected consequence found — verify your action names')); + checks.push(check('Policy defined', Boolean(manifest.policy), `Add a policy block, e.g. ${JSON.stringify(defaultPolicy())}`)); + const noAuthority = [...graph.nodes.values()].filter((n) => n.type === 'agent' && !(n.authority ?? []).length); + checks.push(check('Every agent has declared authority', noAuthority.length === 0, + `No authority declared for: ${noAuthority.map((n) => n.id).join(', ')}`)); + const unreachable = protectedActions(graph).filter((a) => resolveRoute(graph, a.id).admissible.length === 0); + checks.push(check('Protected actions have an admissible route', unreachable.length === 0, + `No admissible route today for: ${unreachable.map((a) => a.id).join(', ')}. That may be correct — it means nothing can currently reach them. Run "pctr route " to see why.`)); + } + const pair = defaultKeyPair(); + checks.push(check('Signing key is persisted', !pair.ephemeral, + 'The signing key is ephemeral, so receipts cannot be verified after this process exits. Make .pctr writable or set PCTR_PRIVATE_KEY.')); + checks.push(check('Trusted signers pinned', Boolean(manifest?.trustedSigners?.length), + `No trustedSigners in pctr.json, so receipts verify as "unpinned". Add "trustedSigners": ["${pair.keyId}"].`)); + if (graph) { + const unmeasured = protectedActions(graph).filter((a) => !(manifest.probes ?? {})[a.id]); + checks.push(check('Protected actions are measured by a probe', unmeasured.length === 0, + `Using declared values for: ${unmeasured.map((a) => a.id).join(', ')}. Add a read-only probe under "probes" to measure the real consequence.`)); + } + const failed = checks.filter((x) => !x.ok); + const human = [r.heading('pctr doctor'), + ...checks.map((x) => `${x.ok ? r.green('ok') : r.yellow('!!')} ${x.label}${x.ok ? '' : `\n ${r.dim(x.fix)}`}`)].join('\n'); + out(human, { checks }); + return failed.length ? 1 : 0; + } + + default: + return fail(`Unknown command: ${command}\n\n${help()}`); + } +} + +const check = (label, ok, fix) => ({ label, ok, fix }); +const fail = (message) => { console.error(message); return 2; }; + +function renderCompare(diff, leftId, rightId) { + const lines = [r.heading('compare runs'), r.field('Left', leftId), r.field('Right', rightId), + r.field('Diverged at step', diff.divergedAt === null ? 'never' : String(diff.divergedAt)), + r.field('Same outcome', diff.sameOutcome ? r.green('YES') : r.red(`NO (${diff.outcomes.join(' vs ')})`)), '']; + for (const row of diff.rows) { + lines.push(`${row.same ? r.dim(' =') : r.yellow(' ~')} ${row.left ?? r.dim('(none)')}`); + if (!row.same) lines.push(` ${r.dim('right:')} ${row.right ?? r.dim('(none)')}`); + } + return lines.join('\n'); +} + +function exampleManifest() { + return { + agents: [ + { id: 'planner', framework: 'openai-agents', trust: 0.98, evidenceAgeSeconds: 20, authority: ['*'], jurisdiction: 'eu', latencyMs: 80, costUnits: 1, tools: [], delegatesTo: ['finance-agent-a', 'finance-agent-d', 'support-agent'] }, + { id: 'finance-agent-a', framework: 'langgraph', trust: 0.97, evidenceAgeSeconds: 4200, authority: ['payments.*'], jurisdiction: 'eu', latencyMs: 90, costUnits: 1, tools: ['payments'] }, + { id: 'finance-agent-d', framework: 'claude-agents', trust: 0.97, evidenceAgeSeconds: 30, authority: ['payments.*'], jurisdiction: 'eu', latencyMs: 140, costUnits: 2, tools: ['payments'] }, + { id: 'support-agent', framework: 'crewai', trust: 0.82, evidenceAgeSeconds: 60, authority: ['customers.read'], jurisdiction: 'eu', latencyMs: 120, costUnits: 1, tools: ['crm'], delegatesTo: ['admin-agent'] }, + { id: 'admin-agent', framework: 'claude-agents', trust: 0.95, evidenceAgeSeconds: 30, authority: ['customers.*'], jurisdiction: 'eu', latencyMs: 200, costUnits: 2, tools: ['database'] } + ], + tools: [ + { id: 'payments', protocol: 'mcp', actions: ['payments.transfer'] }, + { id: 'crm', protocol: 'mcp', actions: ['customers.read'] }, + { id: 'database', protocol: 'mcp', actions: ['customers.delete', 'customers.update'] } + ], + actions: [ + { id: 'payments.transfer', amount: 1800, connectedWorkflows: 2 }, + { id: 'customers.delete', recordsAffected: 1842, connectedWorkflows: 4 } + ] + }; +} + +function help() { + return `PCTR — Protected Consequence Trust Routing + + See what your agents can cause. Route them safely. Prove what happened. + +Usage + pctr init Discover agents and tools, write pctr.json + pctr scan What consequences can your agents reach? + pctr scan --share Write the findings as shareable Markdown + pctr graph Show the trust graph + pctr preview Consequence Twin: what will this change? + pctr route TrustRoute Autopilot: which path may get there? + pctr protect Run the full loop and issue a receipt + pctr simulate Same, without executing any effect + pctr replay [run] Agent Time Machine: what happened, step by step + pctr explain Why was this allowed, denied, or rerouted? + pctr receipt [id] Show an execution receipt + pctr verify [id] Verify receipt signatures and the receipt chain + pctr keys Show your signing key id and public key + pctr serve Run the effect boundary as its own process + pctr doctor Check your setup + +Options + --json Machine-readable output + --share [file] scan: write the findings as shareable Markdown + --no-example init: don't write a worked example when nothing is found + --amount Material parameter: amount + --records Material parameter: records affected + --params '' Any other material parameters + --approve Record a human approval + --target Execution target + --boundary protect: verify authority at a remote effect boundary + --port serve: port for the effect boundary (default 8787) + --probe preview: measure the real consequence with this probe + --key verify: check signatures against this public key + --export keys: write the public key to a file + --compare replay: diff two runs + --fork [eventId] replay: fork a run for simulation + +Start with: pctr init, then pctr scan`; +} + +main().then((code) => process.exit(code ?? 0)).catch((err) => { + console.error(err.stack ?? String(err)); + process.exit(1); +}); diff --git a/packages/pctr/examples/pctr-scan.yml b/packages/pctr/examples/pctr-scan.yml new file mode 100644 index 0000000..dfcd83f --- /dev/null +++ b/packages/pctr/examples/pctr-scan.yml @@ -0,0 +1,27 @@ +# See what your agents can cause, on every pull request. +# Copy this to .github/workflows/pctr-scan.yml +name: PCTR consequence scan + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + pull-requests: write # so the scan can post its findings on the PR + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + + - uses: BlockSiFr/ttp-protocol/packages/pctr@main + with: + comment: 'true' + # Fail the build when an agent can reach an irreversible consequence. + # Start with 'none' to see the report, then tighten to 'critical'. + fail-on: 'none' diff --git a/packages/pctr/package.json b/packages/pctr/package.json new file mode 100644 index 0000000..89442d9 --- /dev/null +++ b/packages/pctr/package.json @@ -0,0 +1,59 @@ +{ + "name": "@blocksifr/pctr", + "version": "0.1.0", + "description": "See what your AI agents can cause, route them safely, and prove what happened. Consequence preview, trust routing and signed execution receipts for AI agents.", + "type": "module", + "license": "Apache-2.0", + "bin": { + "pctr": "./bin/pctr.mjs" + }, + "exports": { + ".": "./src/index.mjs" + }, + "engines": { + "node": ">=18" + }, + "files": [ + "src/", + "bin/", + "README.md", + "LICENSE" + ], + "scripts": { + "test": "node --test tests/*.test.mjs" + }, + "keywords": [ + "ai-agents", + "agent-security", + "ai-security", + "llm", + "mcp", + "model-context-protocol", + "langgraph", + "crewai", + "autogen", + "openai-agents", + "claude", + "agent-authorization", + "execution-authority", + "audit", + "receipts", + "zero-trust", + "ai-governance", + "agentic" + ], + "homepage": "https://github.com/BlockSiFr/ttp-protocol/tree/main/packages/pctr#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/BlockSiFr/ttp-protocol.git", + "directory": "packages/pctr" + }, + "bugs": { + "url": "https://github.com/BlockSiFr/ttp-protocol/issues" + }, + "author": "BlockSiFr", + "publishConfig": { + "access": "public" + }, + "dependencies": {} +} diff --git a/packages/pctr/scripts/render-terminal-svg.mjs b/packages/pctr/scripts/render-terminal-svg.mjs new file mode 100644 index 0000000..596b434 --- /dev/null +++ b/packages/pctr/scripts/render-terminal-svg.mjs @@ -0,0 +1,109 @@ +#!/usr/bin/env node +// Renders a terminal SVG of a real `pctr scan`, so the picture in the README is +// generated from the actual output and cannot drift from what the tool prints. +// Usage: node packages/pctr/scripts/render-terminal-svg.mjs [outfile] +import { execFileSync } from 'node:child_process'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const cli = path.join(here, '..', 'bin', 'pctr.mjs'); +const outFile = process.argv[2] ?? path.join(here, '..', '..', '..', 'assets', 'pctr-scan.svg'); + +const workdir = fs.mkdtempSync(path.join(os.tmpdir(), 'pctr-svg-')); +const run = (args) => execFileSync('node', [cli, ...args], { cwd: workdir, encoding: 'utf8', env: { ...process.env, NO_COLOR: '1' } }); +run(['init']); +const output = run(['scan']).replace(/\[[0-9;]*m/g, '').split('\n'); +fs.rmSync(workdir, { recursive: true, force: true }); + +const THEME = { + bg: '#0A0A0F', chrome: '#12121A', border: '#1F1F2B', text: '#C9D1D9', dim: '#6E7681', + cyan: '#00D4FF', teal: '#00B8A9', red: '#FF5470', amber: '#FFB300', green: '#00E676' +}; +const LINE_H = 21; +const PAD = 22; +const TOP = 46; + +// The terminal draws a route down the page; a README image reads better with it on one +// line, so collapse the vertical arrows into a single chain. +const collapsed = []; +for (const line of output) { + const trimmed = line.trim(); + if (trimmed === '|') continue; + if (trimmed === 'v') { collapsed.push('\u0000'); continue; } + if (collapsed.at(-1) === '\u0000') { + collapsed.pop(); + collapsed.push(`${collapsed.pop()} \u2192 ${trimmed}`); + continue; + } + collapsed.push(line); +} +const MAX_COLS = 82; +// Wrap anything wider than the frame, the way the terminal would. +const wrapped = collapsed.flatMap((line) => { + if (line.length <= MAX_COLS) return [line]; + const indent = line.match(/^\s*/)[0]; + const parts = []; + let current = indent; + for (const word of line.trim().split(' ')) { + if (current.trim() && `${current} ${word}`.length > MAX_COLS) { parts.push(current); current = `${indent}${word}`; } + else current = current.trim() ? `${current} ${word}` : `${current}${word}`; + } + if (current.trim()) parts.push(current); + return parts; +}); +const lines = wrapped.filter((l, i) => !(i === 0 && !l.trim())); +const width = 760; +const height = TOP + PAD + lines.length * LINE_H + 8; + +const esc = (s) => s.replace(/&/g, '&').replace(//g, '>'); + +// Colour by meaning, matching what the terminal itself emphasizes. +function paint(line) { + const trimmed = line.trim(); + if (/^(CRITICAL)/.test(trimmed)) return span(line, THEME.red); + if (/^(HIGH)/.test(trimmed)) return span(line, THEME.amber); + if (/^(MEDIUM)/.test(trimmed)) return span(line, THEME.cyan); + if (/^PCTR$/.test(trimmed)) return span(line, THEME.cyan, 700); + if (/^(Protected consequences|Highest priority:|Route:|Problem:|Recommended:)/.test(trimmed)) return span(line, THEME.text, 700); + if (/\u2192/.test(trimmed)) { + // The route chain: dim the hops, light up the consequence at the end. + const hops = trimmed.split(' \u2192 '); + return hops.map((hop, i) => (i === hops.length - 1 + ? span(` \u2192 ${hop}`, THEME.red, 700) + : span(i === 0 ? hop : ` \u2192 ${hop}`, THEME.teal))).join(''); + } + if (/^(customers\.delete|payments\.transfer)/.test(trimmed)) return span(line, THEME.red, 700); + if (/^(\||v|↓)$/.test(trimmed)) return span(line, THEME.dim); + if (/^(Agents found|Tools found|External systems|Potential actions)/.test(trimmed)) { + const label = line.slice(0, 22); + const value = line.slice(22); + return `${span(label, THEME.dim)}${span(value, THEME.teal, 600)}`; + } + if (/irreversibly|without independent execution authority/.test(trimmed)) return span(line, THEME.text); + if (/^Add TTP authority/.test(trimmed)) return span(line, THEME.green); + return span(line, THEME.text); +} +const span = (text, fill, weight = 400) => + `${esc(text)}`; + +const body = lines.map((line, i) => + ` ${paint(line)}`).join('\n'); + +const svg = ` + + + + + pctr scan + +${body} + + +`; + +fs.mkdirSync(path.dirname(outFile), { recursive: true }); +fs.writeFileSync(outFile, svg); +console.log(`Wrote ${outFile} (${lines.length} lines, ${(svg.length / 1024).toFixed(1)} KB)`); diff --git a/packages/pctr/src/adapters.mjs b/packages/pctr/src/adapters.mjs new file mode 100644 index 0000000..c8d6dfe --- /dev/null +++ b/packages/pctr/src/adapters.mjs @@ -0,0 +1,173 @@ +import { classifyAction } from './consequences.mjs'; + +// UNIVERSAL AGENT ENVELOPE. +// Agents are participants, not platforms. An adapter normalizes security *meaning* — +// who is acting, who delegated, what capability appeared, what action is proposed — +// and deliberately drops framework internals. A new framework needs an adapter here, +// not a change to PCTR. + +const str = (...candidates) => candidates.find((c) => typeof c === 'string' && c.length) ?? null; + +// Each adapter maps one framework event to { event, subject, detail } or null when the +// event carries no security meaning. Returning null is a valid, common answer. +export const adapters = { + 'openai-agents': (e) => { + switch (e.type) { + case 'agent_start': case 'run_start': + return { event: 'AGENT_STARTED', subject: str(e.agent, e.agent_name, e.name), detail: { objective: str(e.input, e.objective) } }; + case 'handoff': case 'agent_handoff': + return { event: 'AGENT_DELEGATED', subject: str(e.from_agent, e.source, e.agent), detail: { to: str(e.to_agent, e.target) } }; + case 'tool_call': case 'function_call': + return proposal(str(e.name, e.tool_name, e.function?.name), e.arguments ?? e.args ?? e.input, str(e.agent, e.agent_name)); + case 'tool_output': case 'function_result': + return { event: 'EXECUTION_COMPLETED', subject: str(e.agent, e.agent_name), detail: { action: str(e.name, e.tool_name), status: e.error ? 'FAILED' : 'SUCCEEDED' } }; + default: return null; + } + }, + + 'claude-agents': (e) => { + switch (e.type) { + case 'system': case 'init': + return { event: 'AGENT_STARTED', subject: str(e.session_id, e.agent), detail: { objective: str(e.prompt, e.objective) } }; + case 'tool_use': + return proposal(str(e.name, e.tool), e.input, str(e.agent, e.session_id)); + case 'tool_result': + return { event: 'EXECUTION_COMPLETED', subject: str(e.agent, e.session_id), detail: { action: str(e.name, e.tool), status: e.is_error ? 'FAILED' : 'SUCCEEDED' } }; + case 'subagent': case 'task': + return { event: 'AGENT_DELEGATED', subject: str(e.agent, e.parent, e.session_id), detail: { to: str(e.subagent_type, e.to, e.description) } }; + default: return null; + } + }, + + langgraph: (e) => { + switch (str(e.event, e.type)) { + case 'on_chain_start': case 'on_graph_start': + return { event: 'AGENT_STARTED', subject: str(e.name, e.node), detail: { objective: str(e.data?.input?.objective, e.data?.input) } }; + case 'on_tool_start': + return proposal(str(e.name, e.data?.name), e.data?.input, str(e.metadata?.langgraph_node, e.parent_ids?.at(-1))); + case 'on_tool_end': + return { event: 'EXECUTION_COMPLETED', subject: str(e.metadata?.langgraph_node), detail: { action: str(e.name), status: 'SUCCEEDED' } }; + // A node handing to another node is delegation, whatever the graph calls it. + case 'on_node_start': + return e.metadata?.from + ? { event: 'AGENT_DELEGATED', subject: str(e.metadata.from), detail: { to: str(e.name, e.node) } } + : null; + default: return null; + } + }, + + crewai: (e) => { + switch (str(e.type, e.event)) { + case 'crew_kickoff': case 'task_started': + return { event: 'AGENT_STARTED', subject: str(e.agent, e.agent_role), detail: { objective: str(e.task, e.description) } }; + case 'agent_delegated': case 'delegation': + return { event: 'AGENT_DELEGATED', subject: str(e.from_agent, e.agent), detail: { to: str(e.to_agent, e.coworker) } }; + case 'tool_usage_started': + return proposal(str(e.tool, e.tool_name), e.tool_args ?? e.input, str(e.agent, e.agent_role)); + case 'tool_usage_finished': + return { event: 'EXECUTION_COMPLETED', subject: str(e.agent, e.agent_role), detail: { action: str(e.tool, e.tool_name), status: 'SUCCEEDED' } }; + default: return null; + } + }, + + autogen: (e) => { + switch (str(e.type, e.event)) { + case 'message': case 'chat_message': + // A message that hands work to another participant is a delegation. + return e.recipient && e.sender !== e.recipient + ? { event: 'AGENT_DELEGATED', subject: str(e.sender), detail: { to: str(e.recipient) } } + : null; + case 'function_call': case 'tool_call': + return proposal(str(e.name, e.function?.name), e.arguments ?? e.function?.arguments, str(e.sender, e.agent)); + case 'function_result': case 'tool_response': + return { event: 'EXECUTION_COMPLETED', subject: str(e.sender, e.agent), detail: { action: str(e.name), status: 'SUCCEEDED' } }; + default: return null; + } + }, + + 'semantic-kernel': (e) => { + switch (str(e.type, e.event)) { + case 'function_invoking': + return proposal(str(e.function, e.name, e.function_name), e.arguments, str(e.plugin, e.agent)); + case 'function_invoked': + return { event: 'EXECUTION_COMPLETED', subject: str(e.plugin, e.agent), detail: { action: str(e.function, e.name), status: 'SUCCEEDED' } }; + default: return null; + } + }, + + // MCP speaks JSON-RPC: tools/list is capability discovery, tools/call is a proposal. + mcp: (e) => { + const method = str(e.method, e.type); + if (method === 'tools/list' || method === 'list_tools') { + const tools = (e.result?.tools ?? e.tools ?? []).map((t) => str(t.name, t)).filter(Boolean); + return { event: 'CAPABILITY_DISCOVERED', subject: str(e.server, e.serverName), detail: { tool: str(e.server, e.serverName), actions: tools, protocol: 'mcp' } }; + } + if (method === 'tools/call' || method === 'call_tool') { + return proposal(str(e.params?.name, e.name), e.params?.arguments ?? e.arguments, str(e.client, e.server)); + } + return null; + }, + + a2a: (e) => { + switch (str(e.type, e.event)) { + case 'task.delegated': case 'task/delegate': + return { event: 'AGENT_DELEGATED', subject: str(e.from, e.source_agent), detail: { to: str(e.to, e.target_agent) } }; + case 'task.submitted': case 'task/send': + return { event: 'AGENT_STARTED', subject: str(e.agent, e.to), detail: { objective: str(e.message, e.task?.description) } }; + case 'task.completed': + return { event: 'EXECUTION_COMPLETED', subject: str(e.agent), detail: { action: str(e.task?.name, e.name), status: 'SUCCEEDED' } }; + default: return null; + } + }, + + // Anything can speak the envelope directly. + generic: (e) => { + const action = str(e.action, e.tool, e.name); + if (e.event === 'delegate' || e.delegatesTo) return { event: 'AGENT_DELEGATED', subject: str(e.agent, e.from), detail: { to: str(e.delegatesTo, e.to) } }; + return action ? proposal(action, e.params ?? e.arguments, str(e.agent)) : null; + } +}; + +// A proposed action is where consequence classification happens, so every framework +// gets the same answer to "what can this cause?" from the same rules. +function proposal(action, args, agent) { + if (!action) return null; + const params = typeof args === 'string' ? tryParse(args) : (args ?? {}); + const consequence = classifyAction(action, params); + return { event: 'ACTION_PROPOSED', subject: agent, detail: { action, ...params, consequence: consequence.consequence, severity: consequence.severity } }; +} + +const tryParse = (s) => { try { return JSON.parse(s); } catch { return {}; } }; + +export const supportedFrameworks = () => Object.keys(adapters); + +export function normalize(framework, event) { + const adapter = adapters[framework] ?? adapters.generic; + try { + return adapter(event) ?? null; + } catch { + return null; // A malformed framework event is not a reason to lose the run. + } +} + +// Feed a framework's event stream into a timeline. Proposals also record the +// consequence they imply, because that is the part every framework leaves out. +export function ingest(framework, events, timeline, { because = [] } = {}) { + const recorded = []; + let lastId = because; + for (const raw of events) { + const normalized = normalize(framework, raw); + if (!normalized) continue; + const entry = timeline.record(normalized.event, normalized.detail, { subject: normalized.subject, because: lastId }); + recorded.push(entry); + lastId = [entry.id]; + if (normalized.event === 'ACTION_PROPOSED' && normalized.detail.consequence && normalized.detail.consequence !== 'NONE') { + const detected = timeline.record('CONSEQUENCE_DETECTED', + { consequence: normalized.detail.consequence, severity: normalized.detail.severity, action: normalized.detail.action }, + { subject: normalized.subject, because: [entry.id] }); + recorded.push(detected); + lastId = [detected.id]; + } + } + return recorded; +} diff --git a/packages/pctr/src/authority.mjs b/packages/pctr/src/authority.mjs new file mode 100644 index 0000000..b9e87f5 --- /dev/null +++ b/packages/pctr/src/authority.mjs @@ -0,0 +1,93 @@ +import crypto from 'node:crypto'; +import { hashObj } from './ttp.mjs'; +import { defaultKeyPair, signHash, verifyHash, exportPublic, keyIdFor } from './keys.mjs'; + +// EXECUTION AUTHORITY. +// A valid identity is not enough. A valid credential is not enough. A valid route is +// not enough. Authority is bound to the exact consequential execution, and the +// execution boundary verifies it independently of the agent that requested it. + +export function issueAuthority({ + principal, delegator = null, session = null, action, target, params = {}, + constraints = {}, policyVersion = 'local-v1', consequence, route, + validForSeconds = 300, issuedAt = new Date().toISOString(), keyPair = defaultKeyPair() +}) { + const materialParams = selectMaterial(params, constraints.materialParams); + const expiresAt = new Date(new Date(issuedAt).getTime() + validForSeconds * 1000).toISOString(); + const envelope = { + type: 'TTPExecutionAuthority', version: 1, + principal, delegator, session, + action, target, materialParams, + constraints, policyVersion, consequence, + routeId: route?.routeId ?? null, routeHash: route?.routeHash ?? null, + issuedAt, expiresAt, nonce: crypto.randomUUID(), + // The signer's identity is inside the binding, so a signature cannot be re-attached + // to the same authority under a different key. + keyId: keyPair.keyId ?? keyIdFor(keyPair.publicKey) + }; + const bindingHash = hashObj(envelope); + return { ...envelope, bindingHash, signature: signHash(bindingHash, keyPair), signerPublicKey: exportPublic(keyPair.publicKey) }; +} + +// The effect boundary. It re-derives the binding from what is ACTUALLY about to +// execute and compares. A requesting agent cannot enforce its own authority. +export function verifyAuthority(authority, execution, { now = new Date().toISOString(), publicKey, trustedKeyIds, replayStore } = {}) { + const failures = []; + const { bindingHash, signature, signerPublicKey, ...envelope } = authority ?? {}; + if (!authority) return deny([{ code: 'AUTHORITY_MISSING', message: 'no execution authority presented' }]); + + if (hashObj(envelope) !== bindingHash) failures.push({ code: 'BINDING_HASH_MISMATCH', message: 'authority contents do not match their binding' }); + + // Verifying with a key supplied by the caller is the real check. Falling back to the + // key carried inside the authority proves only internal consistency, and says so. + const key = publicKey ?? signerPublicKey; + const signerVerified = Boolean(publicKey) || (Array.isArray(trustedKeyIds) && trustedKeyIds.includes(envelope.keyId)); + if (!key) failures.push({ code: 'SIGNATURE_INVALID', message: 'authority carries no verification key' }); + else if (!verifyHash(bindingHash, signature, key)) failures.push({ code: 'SIGNATURE_INVALID', message: 'authority signature does not verify' }); + else if (signature?.keyId !== envelope.keyId) failures.push({ code: 'SIGNER_MISMATCH', message: 'signature key id does not match the key id bound into the authority' }); + if (Array.isArray(trustedKeyIds) && !trustedKeyIds.includes(envelope.keyId)) { + failures.push({ code: 'UNTRUSTED_SIGNER', message: `authority was signed by ${envelope.keyId}, which is not a trusted signer` }); + } + + if (new Date(now) > new Date(envelope.expiresAt)) failures.push({ code: 'AUTHORITY_EXPIRED', message: `authority expired at ${envelope.expiresAt}` }); + if (new Date(now) < new Date(envelope.issuedAt)) failures.push({ code: 'AUTHORITY_NOT_YET_VALID', message: 'authority is not yet valid' }); + + if (execution.action !== envelope.action) failures.push({ code: 'ACTION_MISMATCH', message: `authority binds ${envelope.action}, execution is ${execution.action}` }); + if (execution.target !== envelope.target) failures.push({ code: 'TARGET_MISMATCH', message: `authority binds ${envelope.target}, execution targets ${execution.target}` }); + if (execution.principal && execution.principal !== envelope.principal) failures.push({ code: 'PRINCIPAL_MISMATCH', message: 'execution principal differs from the authorized principal' }); + + const executed = selectMaterial(execution.params ?? {}, Object.keys(envelope.materialParams ?? {})); + for (const [key, value] of Object.entries(envelope.materialParams ?? {})) { + if (!deepEqual(executed[key], value)) { + failures.push({ code: 'PARAMETER_MISMATCH', message: `${key} was authorized as ${JSON.stringify(value)} but is ${JSON.stringify(executed[key])}` }); + } + } + for (const [key, limit] of Object.entries(envelope.constraints?.max ?? {})) { + if (Number(execution.params?.[key]) > Number(limit)) { + failures.push({ code: 'CONSTRAINT_EXCEEDED', message: `${key} is ${execution.params[key]}, above the authorized maximum of ${limit}` }); + } + } + if (envelope.constraints?.requiresApproval && !execution.approval) { + failures.push({ code: 'APPROVAL_MISSING', message: 'this authority requires a recorded human approval' }); + } + if (replayStore?.seen(envelope.nonce)) failures.push({ code: 'REPLAYED_AUTHORITY', message: 'this authority has already been spent' }); + + if (failures.length === 0) replayStore?.spend(envelope.nonce); + return failures.length + ? { ...deny(failures), signerVerified } + : { allowed: true, decision: 'EXECUTION_ALLOWED', bindingHash, failures: [], verifiedAt: now, signerVerified, + warnings: signerVerified ? [] : [{ code: 'UNPINNED_KEY', message: 'signature is internally consistent, but the signer was not pinned; pass publicKey or trustedKeyIds to verify who signed it' }] }; +} + +const deny = (failures) => ({ allowed: false, decision: 'EXECUTION_DENIED', failures }); + +export function createReplayStore(initial = []) { + const spent = new Set(initial); + return { seen: (n) => spent.has(n), spend: (n) => spent.add(n), list: () => [...spent] }; +} + +function selectMaterial(params, keys) { + if (!keys?.length) return { ...params }; + return Object.fromEntries(keys.filter((k) => k in params).map((k) => [k, params[k]])); +} +const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b); diff --git a/packages/pctr/src/boundary.mjs b/packages/pctr/src/boundary.mjs new file mode 100644 index 0000000..e6f8e50 --- /dev/null +++ b/packages/pctr/src/boundary.mjs @@ -0,0 +1,91 @@ +import http from 'node:http'; +import fs from 'node:fs'; +import path from 'node:path'; +import { verifyAuthority, createReplayStore } from './authority.mjs'; + +// THE EFFECT BOUNDARY. +// The requesting agent must not be trusted to enforce its own authority, so the check +// belongs somewhere the agent does not control: a separate process that holds the +// replay state and the list of signers it will accept. + +// Spent nonces must survive a restart, or replay protection lasts only as long as uptime. +export function persistentReplayStore(file) { + let spent = new Set(); + try { spent = new Set(JSON.parse(fs.readFileSync(file, 'utf8'))); } catch { /* first run */ } + const flush = () => { + fs.mkdirSync(path.dirname(file), { recursive: true }); + fs.writeFileSync(file, JSON.stringify([...spent])); + }; + return { + seen: (nonce) => spent.has(nonce), + spend: (nonce) => { spent.add(nonce); flush(); }, + list: () => [...spent] + }; +} + +export function createBoundary({ trustedKeyIds, publicKey, replayStore = createReplayStore(), name = 'pctr.effect-boundary' } = {}) { + return { + name, + verify(authority, execution, { now } = {}) { + const result = verifyAuthority(authority, execution, { now, publicKey, trustedKeyIds, replayStore }); + return { ...result, boundary: name, verifiedAt: result.verifiedAt ?? new Date().toISOString() }; + }, + replayStore + }; +} + +export function startBoundaryServer({ port = 8787, host = '127.0.0.1', boundary = createBoundary(), onDecision } = {}) { + const server = http.createServer((req, res) => { + const send = (code, body) => { + res.writeHead(code, { 'content-type': 'application/json' }); + res.end(JSON.stringify(body)); + }; + if (req.method === 'GET' && req.url === '/health') { + return send(200, { ok: true, boundary: boundary.name, spentNonces: boundary.replayStore.list().length }); + } + if (req.method !== 'POST' || req.url !== '/verify') return send(404, { error: 'POST /verify or GET /health' }); + + let body = ''; + req.on('data', (chunk) => { + body += chunk; + if (body.length > 1_000_000) req.destroy(); // an authority is small; anything larger is not one + }); + req.on('end', () => { + let parsed; + try { parsed = JSON.parse(body); } catch { return send(400, { error: 'invalid JSON' }); } + const { authority, execution } = parsed ?? {}; + if (!execution) return send(400, { error: 'execution is required: the boundary checks authority against what is actually about to run' }); + const decision = boundary.verify(authority, execution); + onDecision?.(decision, { authority, execution }); + // A denial is a successful verification, so it is still 200 with a decision body. + return send(200, decision); + }); + }); + return new Promise((resolve) => { + server.listen(port, host, () => resolve({ server, url: `http://${host}:${server.address().port}`, close: () => new Promise((r) => server.close(r)) })); + }); +} + +// Client side. A network failure is never an allow: if the boundary cannot be reached, +// the execution is denied. +export function remoteBoundary(url, { timeoutMs = 5000 } = {}) { + return { + name: url, + async verify(authority, execution) { + try { + const response = await fetch(`${url.replace(/\/$/, '')}/verify`, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ authority, execution }), + signal: AbortSignal.timeout(timeoutMs) + }); + if (!response.ok) { + return { allowed: false, decision: 'EXECUTION_DENIED', boundary: url, failures: [{ code: 'BOUNDARY_ERROR', message: `effect boundary returned ${response.status}` }] }; + } + return { ...(await response.json()), boundary: url }; + } catch (error) { + return { allowed: false, decision: 'EXECUTION_DENIED', boundary: url, failures: [{ code: 'BOUNDARY_UNREACHABLE', message: `effect boundary at ${url} could not be reached: ${error.message}` }] }; + } + } + }; +} diff --git a/packages/pctr/src/consequences.mjs b/packages/pctr/src/consequences.mjs new file mode 100644 index 0000000..24f2e28 --- /dev/null +++ b/packages/pctr/src/consequences.mjs @@ -0,0 +1,73 @@ +// Protected consequences come first. An action matters because of the state change +// it can cause, not because of which agent happens to hold the credential. + +export const CONSEQUENCE_CLASSES = { + MONEY_MOVED: { label: 'money moved', severity: 'HIGH', reversible: false }, + DATA_DELETED: { label: 'data deleted', severity: 'CRITICAL', reversible: false }, + IDENTITY_CHANGED: { label: 'identity disabled or changed', severity: 'HIGH', reversible: true }, + SECRET_EXPOSED: { label: 'secret exposed', severity: 'CRITICAL', reversible: false }, + PRODUCTION_CHANGED: { label: 'production changed', severity: 'HIGH', reversible: true }, + INFRA_MODIFIED: { label: 'infrastructure modified', severity: 'HIGH', reversible: true }, + ACCESS_GRANTED: { label: 'access granted', severity: 'HIGH', reversible: true }, + MESSAGE_SENT: { label: 'external message sent', severity: 'MEDIUM', reversible: false }, + CONTRACT_EXECUTED: { label: 'contract executed', severity: 'CRITICAL', reversible: false }, + PHYSICAL_CHANGED: { label: 'physical system changed', severity: 'CRITICAL', reversible: false }, + DATA_WRITTEN: { label: 'data written', severity: 'MEDIUM', reversible: true }, + DATA_READ: { label: 'data read', severity: 'LOW', reversible: true }, + NONE: { label: 'no protected consequence', severity: 'LOW', reversible: true } +}; + +// The verb decides, not the namespace: payments.status reads, payments.transfer moves +// money. A read verb in the final segment settles the question before anything else. +const READ_VERB = /(?:^|[.\/_:-])(get|list|read|status|search|query|fetch|describe|show|view|count|preview)$/i; + +// Ordered: first match wins, so the destructive readings are checked before the mild ones. +const RULES = [ + [/(payment|transfer|payout|refund|charge|wire|treasury|stripe|ledger\.move|\bpay\b)/i, 'MONEY_MOVED'], + [/(delete|destroy|drop|purge|truncate|wipe|erase|remove_all|terminate)/i, 'DATA_DELETED'], + [/(secret|credential|token|apikey|api_key|private_key|vault\.read|password)/i, 'SECRET_EXPOSED'], + [/(contract|sign|execute_agreement|onchain|smart_contract)/i, 'CONTRACT_EXECUTED'], + [/(device|actuator|valve|door|robot|plc|scada)/i, 'PHYSICAL_CHANGED'], + [/(disable_user|deactivate|suspend_user|user\.update|identity|scim|role\.assign)/i, 'IDENTITY_CHANGED'], + [/(grant|permission|policy\.attach|share|acl|invite)/i, 'ACCESS_GRANTED'], + [/(deploy|release|rollback|migrate|prod|k8s\.apply|helm)/i, 'PRODUCTION_CHANGED'], + [/(terraform|infra|instance|bucket|dns|firewall|network|cluster)/i, 'INFRA_MODIFIED'], + [/(send|email|slack|sms|post_message|notify|publish|tweet)/i, 'MESSAGE_SENT'], + [/(write|create|update|insert|upsert|patch|put)/i, 'DATA_WRITTEN'], + [/(read|get|list|search|query|select|fetch)/i, 'DATA_READ'] +]; + +export function classifyAction(actionId, hints = {}) { + if (hints.consequence && CONSEQUENCE_CLASSES[hints.consequence]) { + return decorate(actionId, hints.consequence, hints); + } + if (READ_VERB.test(actionId)) return decorate(actionId, 'DATA_READ', hints); + for (const [pattern, cls] of RULES) { + if (pattern.test(actionId)) return decorate(actionId, cls, hints); + } + return decorate(actionId, 'NONE', hints); +} + +const RANK = { LOW: 0, MEDIUM: 1, HIGH: 2, CRITICAL: 3 }; +export const severityRank = (s) => RANK[s] ?? 0; +export const maxSeverity = (a, b) => (severityRank(a) >= severityRank(b) ? a : b); + +function decorate(actionId, cls, hints) { + const base = CONSEQUENCE_CLASSES[cls]; + let severity = hints.severity ?? base.severity; + const reversible = hints.reversible ?? base.reversible; + // Scale and money both raise the stakes beyond the action name itself. + if (hints.recordsAffected >= 1000 || hints.amount >= 5000) severity = maxSeverity(severity, 'CRITICAL'); + else if (hints.recordsAffected >= 100 || hints.amount >= 500) severity = maxSeverity(severity, 'HIGH'); + return { + action: actionId, + consequence: cls, + label: base.label, + severity, + reversible, + protected: cls !== 'NONE' && severityRank(severity) >= RANK.MEDIUM + }; +} + +// A protected action is one whose consequence must not occur on credential possession alone. +export const isProtected = (actionId, hints) => classifyAction(actionId, hints).protected; diff --git a/packages/pctr/src/discover.mjs b/packages/pctr/src/discover.mjs new file mode 100644 index 0000000..b4a1d8d --- /dev/null +++ b/packages/pctr/src/discover.mjs @@ -0,0 +1,180 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +// Agents are participants, not platforms. Discovery normalizes whatever a project +// happens to use into the universal agent envelope: agent, tool, action. + +const FRAMEWORKS = [ + [/^openai$/, 'openai-agents'], [/^@openai\//, 'openai-agents'], + [/^@anthropic-ai\//, 'claude-agents'], [/claude-agent-sdk/, 'claude-agents'], + [/^@google\/|google-generativeai|google-genai/, 'google-agents'], + [/langgraph/, 'langgraph'], [/^langchain|^@langchain\//, 'langchain'], + [/crewai/, 'crewai'], [/autogen|pyautogen/, 'autogen'], + [/semantic-kernel|semantic_kernel/, 'semantic-kernel'], + [/^@modelcontextprotocol\/|^mcp$/, 'mcp'], [/a2a/, 'a2a'] +]; + +const MCP_CONFIGS = ['.mcp.json', 'mcp.json', '.claude/settings.json', 'claude_desktop_config.json']; +const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.venv', 'venv', '__pycache__', '.pctr']); +const SOURCE_FILE = /\.(m?[jt]sx?|py)$/; +const MAX_FILE_BYTES = 512 * 1024; + +// Tool and agent declarations are written down in source; a running server is not +// needed to read them. These patterns cover how the common frameworks name things. +// Anything not matched is simply not discovered — never guessed. +const TOOL_PATTERNS = [ + /\b(?:server|mcp|app)\.(?:tool|registerTool)\s*\(\s*['"`]([\w.\-:/]+)['"`]/g, // MCP servers (JS) + /new\s+(?:DynamicStructuredTool|DynamicTool|Tool)\s*\(\s*\{\s*name\s*:\s*['"`]([\w.\-:/]+)['"`]/g, // LangChain + /\btool\s*\(\s*\{\s*name\s*:\s*['"`]([\w.\-:/]+)['"`]/g, // OpenAI / generic tool() + /['"`]?name['"`]?\s*:\s*['"`]([\w.\-:/]+)['"`]\s*,\s*['"`]?description['"`]?\s*:/g, // tool schema objects + /@(?:mcp|server|app)\.tool\s*\([^)]*\)\s*(?:async\s+)?def\s+(\w+)/g, // FastMCP (Python) + /@tool\s*(?:\([^)]*\))?\s*(?:async\s+)?def\s+(\w+)/g, // LangChain (Python) + /\bTool\s*\(\s*name\s*=\s*['"]([\w.\-:/]+)['"]/g // CrewAI / generic (Python) +]; +const AGENT_PATTERNS = [ + /new\s+Agent\s*\(\s*\{[^}]*?name\s*:\s*['"`]([\w.\- ]+)['"`]/g, + /\bAgent\s*\(\s*(?:role|name)\s*=\s*['"]([\w.\- ]+)['"]/g, + /create(?:React|Tool[\w]*)?Agent\s*\(\s*\{[^}]*?name\s*:\s*['"`]([\w.\- ]+)['"`]/g, + /\bAssistantAgent\s*\(\s*(?:name\s*=\s*)?['"]([\w.\- ]+)['"]/g +]; + +export function discover(cwd = process.cwd(), { declared = null } = {}) { + const agents = new Map(); + const tools = new Map(); + const notes = []; + + for (const [name, framework] of detectFrameworks(cwd)) { + notes.push(`${framework} detected via dependency "${name}"`); + } + const frameworks = [...new Set(detectFrameworks(cwd).map(([, f]) => f))]; + + // Read the source once and take both agents and tool actions from it. + const sources = findSourceFiles(cwd); + const actionsByFile = new Map(); + for (const file of sources) { + let text; + try { + if (fs.statSync(file).size > MAX_FILE_BYTES) continue; + text = fs.readFileSync(file, 'utf8'); + } catch { continue; } + + const declaredActions = extractAll(TOOL_PATTERNS, text); + if (declaredActions.length) actionsByFile.set(file, declaredActions); + + for (const name of extractAll(AGENT_PATTERNS, text)) { + const id = slug(name); + agents.set(id, { id, framework: frameworkFor(text, frameworks), source: path.relative(cwd, file), trust: 0.7, evidenceAgeSeconds: 0, authority: [], tools: [] }); + notes.push(`agent "${name}" declared in ${path.relative(cwd, file)}`); + } + if (/agent/i.test(path.basename(file)) && !extractAll(AGENT_PATTERNS, text).length) { + const id = slug(path.basename(file).replace(SOURCE_FILE, '')); + if (!agents.has(id)) agents.set(id, { id, framework: frameworkFor(text, frameworks), source: path.relative(cwd, file), trust: 0.7, evidenceAgeSeconds: 0, authority: [], tools: [] }); + } + } + + for (const [file, actions] of actionsByFile) { + const id = slug(path.basename(path.dirname(file)) === '.' ? 'local-tools' : `${path.basename(file).replace(SOURCE_FILE, '')}-tools`); + tools.set(id, { id, protocol: 'local', actions: [...new Set(actions)], source: path.relative(cwd, file) }); + notes.push(`${actions.length} action(s) declared in ${path.relative(cwd, file)}`); + } + + for (const cfg of MCP_CONFIGS) { + const p = path.join(cwd, cfg); + if (!fs.existsSync(p)) continue; + let parsed; try { parsed = JSON.parse(fs.readFileSync(p, 'utf8')); } catch { continue; } + const servers = parsed.mcpServers ?? parsed.servers ?? {}; + for (const [name, def] of Object.entries(servers)) { + tools.set(name, { id: name, protocol: 'mcp', actions: def.actions ?? [], source: cfg }); + notes.push(`MCP server "${name}" found in ${cfg}`); + } + } + + // Declared facts always win over inferred ones: a manifest is ground truth. + // A discovered agent with no declared tools is assumed to reach the tools found + // alongside it — an over-broad first draft is safer than an incomplete map, and the + // manifest is there to be corrected. + const toolIds = [...tools.keys()]; + for (const agent of agents.values()) if (!agent.tools.length) agent.tools = toolIds; + + const manifest = { + version: 1, + principal: declared?.principal ?? 'user:local', + frameworks, + agents: mergeById([...agents.values()], declared?.agents ?? []), + tools: mergeById([...tools.values()], declared?.tools ?? []), + actions: declared?.actions ?? [], + policy: declared?.policy ?? defaultPolicy() + }; + return { manifest, notes, discovered: { agents: agents.size, tools: tools.size } }; +} + +export const defaultPolicy = () => ({ + requireApprovalAtOrAbove: 'CRITICAL', + maxHops: 6, + decayPerHop: 0.05, + approvalThresholds: { amount: 5000 } +}); + +function mergeById(inferred, declaredList) { + const out = new Map(inferred.map((x) => [x.id, x])); + for (const d of declaredList) out.set(d.id, { ...(out.get(d.id) ?? {}), ...d }); + return [...out.values()]; +} + +function detectFrameworks(cwd) { + const found = []; + const pkgPath = path.join(cwd, 'package.json'); + if (fs.existsSync(pkgPath)) { + try { + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + const deps = Object.keys({ ...pkg.dependencies, ...pkg.devDependencies }); + for (const d of deps) for (const [re, f] of FRAMEWORKS) if (re.test(d)) found.push([d, f]); + } catch { /* an unreadable package.json is not a discovery failure */ } + } + for (const f of ['requirements.txt', 'pyproject.toml']) { + const p = path.join(cwd, f); + if (!fs.existsSync(p)) continue; + const text = fs.readFileSync(p, 'utf8'); + for (const [re, fw] of FRAMEWORKS) if (re.test(text) || new RegExp(re.source.replace(/[\^$]/g, ''), 'i').test(text)) found.push([f, fw]); + } + return found; +} + +const slug = (name) => name.trim().toLowerCase().replace(/[_\s]+/g, '-').replace(/[^a-z0-9.\-]/g, ''); + +function extractAll(patterns, text) { + const found = []; + for (const pattern of patterns) { + pattern.lastIndex = 0; + let match; + while ((match = pattern.exec(text)) !== null) if (match[1]) found.push(match[1]); + } + return [...new Set(found)]; +} + +// What a file imports is better evidence than what the project depends on overall. +const FILE_FRAMEWORKS = [ + [/from\s+crewai|import\s+crewai/, 'crewai'], + [/autogen/, 'autogen'], + [/langgraph/, 'langgraph'], + [/from\s+langchain|@langchain\//, 'langchain'], + [/semantic[_-]kernel/, 'semantic-kernel'], + [/@modelcontextprotocol\/|from\s+mcp/, 'mcp'], + [/@anthropic-ai\/|claude[_-]agent/, 'claude-agents'], + [/\bopenai\b/, 'openai-agents'] +]; +const frameworkFor = (text, frameworks) => + FILE_FRAMEWORKS.find(([re]) => re.test(text))?.[1] ?? frameworks[0] ?? 'unknown'; + +function findSourceFiles(cwd, depth = 0) { + if (depth > 3) return []; + let entries; try { entries = fs.readdirSync(cwd, { withFileTypes: true }); } catch { return []; } + const out = []; + for (const e of entries) { + if (e.name.startsWith('.') && e.name !== '.claude') continue; + const full = path.join(cwd, e.name); + if (e.isDirectory()) { if (!SKIP_DIRS.has(e.name)) out.push(...findSourceFiles(full, depth + 1)); } + else if (SOURCE_FILE.test(e.name) && !/\.(test|spec)\./.test(e.name)) out.push(full); + } + return out; +} diff --git a/packages/pctr/src/events.mjs b/packages/pctr/src/events.mjs new file mode 100644 index 0000000..e958ab7 --- /dev/null +++ b/packages/pctr/src/events.mjs @@ -0,0 +1,10 @@ +// Canonical PCTR security events. Frameworks emit whatever they emit; adapters +// normalize into these. The set is the shared security meaning, not framework internals. +export const EVENTS = [ + 'AGENT_DISCOVERED','AGENT_STARTED','AGENT_DELEGATED','CAPABILITY_DISCOVERED', + 'ACTION_PROPOSED','CONSEQUENCE_DETECTED','TRUST_CHANGED','ROUTE_SELECTED','ROUTE_INVALIDATED', + 'AUTHORITY_REQUESTED','AUTHORITY_ISSUED','EXECUTION_REQUESTED','EXECUTION_ALLOWED', + 'EXECUTION_DENIED','EXECUTION_COMPLETED','RECEIPT_ISSUED' +]; +const SET = new Set(EVENTS); +export const isCanonicalEvent = (name) => SET.has(name); diff --git a/packages/pctr/src/graph.mjs b/packages/pctr/src/graph.mjs new file mode 100644 index 0000000..965ed62 --- /dev/null +++ b/packages/pctr/src/graph.mjs @@ -0,0 +1,116 @@ +import { classifyAction, severityRank } from './consequences.mjs'; + +// The graph is the visual model of execution authority, not a decorative dashboard. +// Nodes: principal -> agents -> tools -> actions -> consequences. + +export function buildGraph(manifest) { + const nodes = new Map(); + const edges = []; + const add = (id, type, data = {}) => { + if (!nodes.has(id)) nodes.set(id, { id, type, ...data }); + else Object.assign(nodes.get(id), data); + return nodes.get(id); + }; + const link = (from, to, kind) => { edges.push({ from, to, kind }); }; + + const principal = manifest.principal ?? 'principal:local'; + add(principal, 'principal'); + + const toolById = new Map((manifest.tools ?? []).map((t) => [t.id, t])); + const actionHints = new Map((manifest.actions ?? []).map((a) => [a.id, a])); + + for (const agent of manifest.agents ?? []) { + add(agent.id, 'agent', { + framework: agent.framework ?? 'unknown', + trust: agent.trust ?? 0, + evidenceAgeSeconds: agent.evidenceAgeSeconds ?? 0, + jurisdiction: agent.jurisdiction ?? null, + authority: agent.authority ?? [], + latencyMs: agent.latencyMs ?? 0, + costUnits: agent.costUnits ?? 0, + available: agent.available !== false + }); + if (agent.entry !== false && !(manifest.agents ?? []).some((a) => (a.delegatesTo ?? []).includes(agent.id))) { + link(principal, agent.id, 'delegates'); + } + for (const target of agent.delegatesTo ?? []) link(agent.id, target, 'delegates'); + for (const toolId of agent.tools ?? []) link(agent.id, toolId, 'uses'); + } + + for (const tool of manifest.tools ?? []) { + add(tool.id, 'tool', { protocol: tool.protocol ?? 'local' }); + for (const actionId of tool.actions ?? []) { + const hints = actionHints.get(actionId) ?? {}; + const c = classifyAction(actionId, hints); + add(actionId, 'action', { ...c, ...hints }); + link(tool.id, actionId, 'performs'); + const consequenceId = `consequence:${c.consequence}`; + add(consequenceId, 'consequence', { class: c.consequence, label: c.label, severity: c.severity }); + link(actionId, consequenceId, 'causes'); + } + } + + return { principal, nodes, edges, manifest }; +} + +export const outgoing = (graph, id, kind) => + graph.edges.filter((e) => e.from === id && (!kind || e.kind === kind)).map((e) => e.to); +export const incoming = (graph, id, kind) => + graph.edges.filter((e) => e.to === id && (!kind || e.kind === kind)).map((e) => e.from); + +// Forward: what consequential state changes can this agent eventually reach? +export function reachableActions(graph, agentId, seen = new Set()) { + if (seen.has(agentId)) return []; + seen.add(agentId); + const found = []; + for (const toolId of outgoing(graph, agentId, 'uses')) { + for (const actionId of outgoing(graph, toolId, 'performs')) found.push({ actionId, toolId, via: [agentId] }); + } + for (const next of outgoing(graph, agentId, 'delegates')) { + for (const r of reachableActions(graph, next, seen)) found.push({ ...r, via: [agentId, ...r.via] }); + } + return found; +} + +// Inverse: consequence <- action <- tool <- agent <- delegation <- principal. +// Every distinct path that can arrive at this consequence. +export function pathsToAction(graph, actionId) { + const toolIds = incoming(graph, actionId, 'performs'); + const paths = []; + const walkBack = (agentId, tail, seen) => { + if (seen.has(agentId)) return; + const next = new Set(seen).add(agentId); + const path = [agentId, ...tail]; + const delegators = incoming(graph, agentId, 'delegates'); + if (!delegators.length) { paths.push([graph.principal, ...path]); return; } + for (const d of delegators) { + if (d === graph.principal) paths.push([graph.principal, ...path]); + else walkBack(d, path, next); + } + }; + for (const toolId of toolIds) { + for (const agentId of incoming(graph, toolId, 'uses')) walkBack(agentId, [toolId, actionId], new Set()); + } + return paths; +} + +export function protectedActions(graph) { + return [...graph.nodes.values()] + .filter((n) => n.type === 'action' && n.protected) + .sort((a, b) => severityRank(b.severity) - severityRank(a.severity) || a.id.localeCompare(b.id)); +} + +export function summarize(graph) { + const counts = { agents: 0, tools: 0, actions: 0, consequences: 0 }; + const bySeverity = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 }; + for (const n of graph.nodes.values()) { + if (n.type === 'agent') counts.agents++; + if (n.type === 'tool') counts.tools++; + if (n.type === 'action') { counts.actions++; bySeverity[n.severity]++; } + if (n.type === 'consequence') counts.consequences++; + } + const externalSystems = new Set( + [...graph.nodes.values()].filter((n) => n.type === 'tool' && n.protocol !== 'local').map((n) => n.id) + ).size; + return { ...counts, externalSystems, bySeverity, protected: protectedActions(graph).length }; +} diff --git a/packages/pctr/src/index.mjs b/packages/pctr/src/index.mjs new file mode 100644 index 0000000..8e52ab3 --- /dev/null +++ b/packages/pctr/src/index.mjs @@ -0,0 +1,18 @@ +export * from './consequences.mjs'; +export * from './graph.mjs'; +export * from './trust.mjs'; +export * from './router.mjs'; +export * from './twin.mjs'; +export * from './authority.mjs'; +export * from './receipt.mjs'; +export * from './timeline.mjs'; +export * from './discover.mjs'; +export * from './observe.mjs'; +export * from './adapters.mjs'; +export * from './probe.mjs'; +export * from './boundary.mjs'; +export * from './report.mjs'; +export * from './keys.mjs'; +export * from './protect.mjs'; +export { EVENTS, isCanonicalEvent } from './events.mjs'; +export * as store from './store.mjs'; diff --git a/packages/pctr/src/keys.mjs b/packages/pctr/src/keys.mjs new file mode 100644 index 0000000..56ba95f --- /dev/null +++ b/packages/pctr/src/keys.mjs @@ -0,0 +1,78 @@ +import crypto from 'node:crypto'; +import fs from 'node:fs'; +import path from 'node:path'; + +// Ed25519 signing, so a receipt can be verified by someone who cannot mint one. +// The key id is derived from the public key, which binds an embedded key to its id: +// swapping in another public key changes the id and breaks any pin against it. + +export const keyIdFor = (publicKey) => + `ed25519:${crypto.createHash('sha256').update(exportPublic(publicKey)).digest('hex').slice(0, 32)}`; + +export function generateKeyPair() { + const { privateKey, publicKey } = crypto.generateKeyPairSync('ed25519'); + return { privateKey, publicKey, keyId: keyIdFor(publicKey) }; +} + +export const exportPublic = (publicKey) => + (typeof publicKey === 'string' ? publicKey : publicKey.export({ type: 'spki', format: 'pem' })).trim(); + +export const exportPrivate = (privateKey) => + privateKey.export({ type: 'pkcs8', format: 'pem' }); + +export const publicKeyFrom = (pem) => + (typeof pem === 'string' ? crypto.createPublicKey(pem) : pem); + +export function keyPairFromPrivatePem(pem) { + const privateKey = crypto.createPrivateKey(pem); + const publicKey = crypto.createPublicKey(privateKey); + return { privateKey, publicKey, keyId: keyIdFor(publicKey) }; +} + +// Keys live beside the run store. The private key is written 0600 and is the one file +// in .pctr that must never be committed or shared. +export function loadOrCreateKeyPair(dir) { + const privatePath = path.join(dir, 'signing-key.pem'); + const publicPath = path.join(dir, 'verification-key.pem'); + if (fs.existsSync(privatePath)) return { ...keyPairFromPrivatePem(fs.readFileSync(privatePath, 'utf8')), path: privatePath, created: false }; + + const pair = generateKeyPair(); + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync(privatePath, exportPrivate(pair.privateKey), { mode: 0o600 }); + fs.writeFileSync(publicPath, `${exportPublic(pair.publicKey)}\n`); + return { ...pair, path: privatePath, created: true }; +} + +let cached = null; +// Resolution order: an explicit PEM in the environment, then the local key store, then +// an ephemeral in-memory key. An ephemeral key still produces valid signatures — they +// just cannot be verified after the process exits, so it is reported as such. +export function defaultKeyPair({ cwd = process.cwd(), reset = false } = {}) { + if (reset) cached = null; + if (cached) return cached; + if (process.env.PCTR_PRIVATE_KEY) { + cached = { ...keyPairFromPrivatePem(process.env.PCTR_PRIVATE_KEY), source: 'env:PCTR_PRIVATE_KEY', ephemeral: false }; + return cached; + } + try { + const pair = loadOrCreateKeyPair(path.join(cwd, '.pctr', 'keys')); + cached = { ...pair, source: pair.path, ephemeral: false }; + } catch { + cached = { ...generateKeyPair(), source: 'ephemeral (no writable key store)', ephemeral: true }; + } + return cached; +} + +export function signHash(hash, keyPair = defaultKeyPair()) { + const signature = crypto.sign(null, Buffer.from(hash), keyPair.privateKey).toString('base64'); + return { alg: 'ed25519', keyId: keyPair.keyId ?? keyIdFor(keyPair.publicKey), value: signature }; +} + +export function verifyHash(hash, signature, publicKey) { + if (!signature || signature.alg !== 'ed25519' || !signature.value) return false; + try { + return crypto.verify(null, Buffer.from(hash), publicKeyFrom(publicKey), Buffer.from(signature.value, 'base64')); + } catch { + return false; + } +} diff --git a/packages/pctr/src/observe.mjs b/packages/pctr/src/observe.mjs new file mode 100644 index 0000000..6229557 --- /dev/null +++ b/packages/pctr/src/observe.mjs @@ -0,0 +1,45 @@ +import { outgoing } from './graph.mjs'; +import { agentTrustNow } from './trust.mjs'; + +// Discovery is an observed security event too: what exists, who delegated to whom, and +// what each participant can reach. This is what turns a static graph into a run. +export function recordDiscovery(graph, timeline) { + const agents = [...graph.nodes.values()].filter((n) => n.type === 'agent'); + for (const agent of agents) { + const discovered = timeline.record('AGENT_DISCOVERED', + { framework: agent.framework, trust: agent.trust, authority: agent.authority }, { subject: agent.id }); + for (const target of outgoing(graph, agent.id, 'delegates')) { + timeline.record('AGENT_DELEGATED', { to: target }, { subject: agent.id, because: [discovered.id] }); + } + for (const toolId of outgoing(graph, agent.id, 'uses')) { + const actions = outgoing(graph, toolId, 'performs'); + timeline.record('CAPABILITY_DISCOVERED', + { tool: toolId, actions, protocol: graph.nodes.get(toolId)?.protocol ?? 'local' }, + { subject: agent.id, because: [discovered.id] }); + } + } + return timeline; +} + +// Trust is observed, not assumed. Comparing the trust state behind the previously +// selected route with the state now is what makes TRUST_CHANGED real rather than decorative. +export function trustDelta(previousStates = [], currentStates = []) { + const before = new Map(previousStates.map((t) => [t.agentId, t])); + const changes = []; + for (const now of currentStates) { + const was = before.get(now.agentId); + if (!was) continue; + if (was.trust !== now.trust || was.evidenceStale !== now.evidenceStale) { + changes.push({ + agentId: now.agentId, from: was.trust, to: now.trust, + becameStale: !was.evidenceStale && now.evidenceStale, + reason: !was.evidenceStale && now.evidenceStale + ? `evidence for ${now.agentId} aged past the ${now.maxEvidenceAgeSeconds}s limit for this consequence` + : `trust for ${now.agentId} moved from ${was.trust} to ${now.trust}` + }); + } + } + return changes; +} + +export { agentTrustNow }; diff --git a/packages/pctr/src/probe.mjs b/packages/pctr/src/probe.mjs new file mode 100644 index 0000000..4f4ac13 --- /dev/null +++ b/packages/pctr/src/probe.mjs @@ -0,0 +1,69 @@ +import { execFile } from 'node:child_process'; +import path from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { previewConsequence } from './twin.mjs'; + +// A probe measures what an action would actually affect, instead of trusting a number +// declared in pctr.json. A probe MUST be read-only: a dry run, a COUNT, a plan. PCTR +// runs it before anything is authorized, so a probe with side effects defeats the point. + +const PROBE_TIMEOUT_MS = 10_000; +const MEASURABLE = ['recordsAffected', 'amount', 'connectedWorkflows', 'dataClass', 'reversible', 'severity']; + +export async function runProbe(spec, context, { cwd = process.cwd(), timeoutMs = PROBE_TIMEOUT_MS } = {}) { + if (!spec) return { ok: false, skipped: true, fields: {} }; + const probe = typeof spec === 'string' ? { module: spec } : spec; + try { + const raw = probe.command + ? await runCommand(probe.command, probe.args ?? [], context, { cwd, timeoutMs }) + : await runModule(probe.module, context, { cwd }); + const fields = Object.fromEntries(Object.entries(raw ?? {}).filter(([k]) => MEASURABLE.includes(k))); + if (!Object.keys(fields).length) { + return { ok: false, fields: {}, error: `probe returned no measurable fields (expected one of: ${MEASURABLE.join(', ')})` }; + } + return { ok: true, fields, source: probe.command ?? probe.module }; + } catch (error) { + // A failing probe must never quietly become "no consequence". It downgrades to + // declared values and says so. + return { ok: false, fields: {}, error: error.message ?? String(error) }; + } +} + +async function runModule(modulePath, context, { cwd }) { + const resolved = pathToFileURL(path.resolve(cwd, modulePath)).href; + const mod = await import(resolved); + const fn = mod.default ?? mod.probe; + if (typeof fn !== 'function') throw new Error(`${modulePath} does not export a probe function`); + return await fn(context); +} + +function runCommand(command, args, context, { cwd, timeoutMs }) { + return new Promise((resolve, reject) => { + const child = execFile(command, args, { + cwd, timeout: timeoutMs, maxBuffer: 1024 * 1024, + env: { ...process.env, PCTR_ACTION: context.action, PCTR_TARGET: context.target ?? '', PCTR_PARAMS: JSON.stringify(context.params ?? {}) } + }, (error, stdout) => { + if (error) return reject(new Error(`probe command failed: ${error.message.split('\n')[0]}`)); + try { resolve(JSON.parse(stdout)); } + catch { reject(new Error(`probe command did not return JSON: ${String(stdout).slice(0, 80)}`)); } + }); + child.on('error', reject); + }); +} + +// The Consequence Twin, with measured values where a probe exists and declared values +// everywhere else. The result always says which is which. +export async function previewMeasured(graph, action, params = {}, { probes, cwd = process.cwd(), target } = {}) { + const spec = (probes ?? graph.manifest?.probes ?? {})[action]; + const result = await runProbe(spec, { action, target, params }, { cwd }); + const preview = previewConsequence(graph, action, { ...params, ...result.fields }); + return { + ...preview, + measured: result.ok, + measuredFields: Object.keys(result.fields), + probe: spec ? { source: result.source ?? (typeof spec === 'string' ? spec : spec.command), ok: result.ok, error: result.error ?? null } : null, + provenance: Object.fromEntries( + ['recordsAffected', 'amount', 'connectedWorkflows'].map((f) => [f, result.fields[f] !== undefined ? 'measured' : 'declared']) + ) + }; +} diff --git a/packages/pctr/src/protect.mjs b/packages/pctr/src/protect.mjs new file mode 100644 index 0000000..0bba845 --- /dev/null +++ b/packages/pctr/src/protect.mjs @@ -0,0 +1,136 @@ +import { previewConsequence, consequenceChanged } from './twin.mjs'; +import { resolveRoute } from './router.mjs'; +import { issueAuthority, verifyAuthority, createReplayStore } from './authority.mjs'; +import { createBoundary } from './boundary.mjs'; +import { issueReceipt } from './receipt.mjs'; +import { createTimeline } from './timeline.mjs'; +import { requiresHumanApproval } from './trust.mjs'; +import { trustDelta } from './observe.mjs'; + +// The Category King loop, executed end to end: +// PREVIEW -> ROUTE -> AUTHORIZE -> ENFORCE -> RECEIPT, with a replayable timeline. + +// Async because a real effect boundary is out of process: enforcement is I/O. +export async function protect(graph, rawRequest, options = {}) { + const request = { principal: graph.principal, ...rawRequest }; + const { + objective = null, approval = null, execute = null, clock, + replayStore = createReplayStore(), timeline = createTimeline({ objective, clock }), priorReceiptHash = null, + boundary = createBoundary({ replayStore, publicKey: options.publicKey, trustedKeyIds: options.trustedKeyIds }), + keyPair + } = options; + const t = timeline; + const policy = { ...(graph.manifest.policy ?? {}), ...(options.policy ?? {}) }; + + // A caller may hand us a timeline that is already running; state the objective once. + const started = t.last('AGENT_STARTED') + ?? t.record('AGENT_STARTED', { objective }, { subject: request.agent ?? graph.principal }); + const proposed = t.record('ACTION_PROPOSED', { action: request.action, ...request.params }, { subject: request.agent ?? null, because: [started.id] }); + + // PREVIEW — what will this cause? A caller that has already measured the consequence + // with a probe passes it in; otherwise it is computed from the graph. + const preview = options.preview ?? previewConsequence(graph, request.action, request.params ?? {}); + const detected = t.record('CONSEQUENCE_DETECTED', + { consequence: preview.consequence, severity: preview.severity, reversible: preview.reversible, blastRadius: preview.blastRadius.band }, + { because: [proposed.id] }); + + // ROUTE — which trustworthy path may get there? + const route = resolveRoute(graph, request.action, { policy, severity: preview.severity, target: request.target, at: options.at }); + if (!route.selected) { + const reason = route.candidates.length + ? route.candidates[0].rejections.map((r) => r.message).join('; ') + : `no agent in this graph can reach ${request.action}`; + const denied = t.record('EXECUTION_DENIED', { reason, failures: route.candidates.flatMap((c) => c.rejections) }, { because: [detected.id] }); + return finish({ t, request, preview, route: null, authority: null, + verification: { decision: 'EXECUTION_DENIED', failures: [{ code: 'NO_ADMISSIBLE_ROUTE', message: reason }] }, + result: null, policy, priorReceiptHash, deniedAt: denied.id }); + } + // If this timeline already rode a route for this action, say what changed and why the + // old one no longer holds, before recording the replacement. + const previous = [...t.events].reverse().find((e) => e.event === 'ROUTE_SELECTED' && e.detail.action === request.action); + const causes = [detected.id]; + if (previous) { + // Compare the agents that carried the previous route against their state *now* — + // otherwise a reroute hides the very trust change that caused it. + const priorRouteNow = route.candidates.find((c) => c.routeId === previous.detail.routeId)?.trustStates ?? []; + const currentStates = [...priorRouteNow, ...route.selected.trustStates]; + for (const change of trustDelta(previous.detail.trustStates, currentStates)) { + causes.push(t.record('TRUST_CHANGED', change, { subject: change.agentId, because: [detected.id] }).id); + } + if (previous.detail.routeId !== route.selected.routeId) { + const stale = route.candidates.find((c) => c.routeId === previous.detail.routeId); + causes.push(t.record('ROUTE_INVALIDATED', { + routeId: previous.detail.routeId, + reason: stale?.rejections.length + ? stale.rejections.map((x) => x.message).join('; ') + : 'a more trustworthy admissible route became available', + policy: policyRef(policy) + }, { because: [previous.id, detected.id] }).id); + } + } + const selected = t.record('ROUTE_SELECTED', + { action: request.action, routeId: route.selected.routeId, effectiveTrust: route.selected.effectiveTrust, + rejected: route.candidates.length - route.admissible.length, trustStates: route.selected.trustStates }, + { because: causes }); + + // AUTHORIZE — human approval first where the consequence demands it. + const needsApproval = requiresHumanApproval(preview.severity, policy) || + (policy.approvalThresholds?.amount != null && Number(request.params?.amount ?? 0) >= policy.approvalThresholds.amount); + if (needsApproval && !approval) { + const reason = `policy requires human approval for ${preview.severity} consequences`; + const denied = t.record('EXECUTION_DENIED', { reason, policy: policyRef(policy) }, { because: [detected.id, selected.id] }); + return finish({ t, request, preview, route: route.selected, authority: null, keyPair, + verification: { decision: 'EXECUTION_DENIED', failures: [{ code: 'APPROVAL_REQUIRED', message: reason }] }, + result: null, policy, priorReceiptHash, deniedAt: denied.id }); + } + + const hops = route.selected.agents; + for (let i = 1; i < hops.length; i++) { + t.record('AGENT_DELEGATED', { to: hops[i], forAction: request.action }, { subject: hops[i - 1], because: [selected.id] }); + } + const tool = route.selected.path.at(-2); + if (tool) t.record('CAPABILITY_DISCOVERED', { tool, actions: [request.action] }, { subject: hops.at(-1) ?? null, because: [selected.id] }); + + const requested = t.record('AUTHORITY_REQUESTED', { action: request.action }, { because: [selected.id] }); + const authority = issueAuthority({ + principal: graph.principal, delegator: route.selected.agents.at(-2) ?? null, session: t.runId, + action: request.action, target: request.target ?? request.action, params: request.params ?? {}, + constraints: { ...(options.constraints ?? {}), requiresApproval: Boolean(needsApproval), + materialParams: options.materialParams ?? Object.keys(request.params ?? {}) }, + policyVersion: policyRef(policy), consequence: preview.consequence, route: route.selected, + validForSeconds: options.validForSeconds ?? 300, issuedAt: options.at, + ...(keyPair ? { keyPair } : {}) + }); + const issued = t.record('AUTHORITY_ISSUED', { bindingHash: authority.bindingHash, expiresAt: authority.expiresAt }, { because: [requested.id] }); + + // ENFORCE — the effect boundary verifies independently of the requesting agent. + t.record('EXECUTION_REQUESTED', { action: request.action }, { subject: request.agent ?? null, because: [issued.id] }); + const execution = { action: request.action, target: request.target ?? request.action, params: request.params ?? {}, principal: graph.principal, approval }; + const verification = await boundary.verify(authority, execution, { now: options.at }); + if (!verification.allowed) { + const denied = t.record('EXECUTION_DENIED', { failures: verification.failures, reason: verification.failures.map((f) => f.message).join('; ') }, { because: [issued.id] }); + return finish({ t, request, preview, route: route.selected, authority, verification, result: null, policy, priorReceiptHash, deniedAt: denied.id, keyPair }); + } + const allowed = t.record('EXECUTION_ALLOWED', { bindingHash: authority.bindingHash }, { because: [issued.id] }); + + const result = execute + ? { status: 'SUCCEEDED', output: execute(execution), executedAt: options.at ?? new Date().toISOString() } + : { status: 'SIMULATED', output: null, executedAt: options.at ?? new Date().toISOString() }; + t.record('EXECUTION_COMPLETED', { action: request.action, status: result.status }, { because: [allowed.id] }); + + return finish({ t, request, preview, route: route.selected, authority, verification, result, policy, priorReceiptHash, routeResult: route, keyPair }); +} + +function finish({ t, request, preview, route, authority, verification, result, policy, priorReceiptHash, routeResult, deniedAt, keyPair }) { + const receipt = issueReceipt({ request, preview, route, authority, verification, result, policyVersion: policyRef(policy), priorReceiptHash, ...(keyPair ? { keyPair } : {}) }); + t.record('RECEIPT_ISSUED', { receiptId: receipt.receiptId, receiptHash: receipt.receiptHash }, + { because: deniedAt ? [deniedAt] : [t.last('EXECUTION_COMPLETED')?.id].filter(Boolean) }); + return { allowed: Boolean(verification?.allowed), preview, route, routeResult, authority, verification, result, receipt, timeline: t }; +} + +const policyRef = (policy) => policy.version ?? 'local-v1'; + +// Simulation runs the whole loop without ever executing an effect. +export const simulate = (graph, request, options = {}) => protect(graph, request, { ...options, execute: null }); +export { verifyAuthority }; +export { consequenceChanged }; diff --git a/packages/pctr/src/receipt.mjs b/packages/pctr/src/receipt.mjs new file mode 100644 index 0000000..c4009c7 --- /dev/null +++ b/packages/pctr/src/receipt.mjs @@ -0,0 +1,74 @@ +import crypto from 'node:crypto'; +import { hashObj } from './ttp.mjs'; +import { defaultKeyPair, signHash, verifyHash, exportPublic, keyIdFor } from './keys.mjs'; + +// A receipt is not a log line. It is verifiable evidence of what was requested, who +// authorized it, what was bound, what executed, and what resulted. Signing is symmetric +// (HMAC) today: that makes a receipt tamper-evident, not third-party verifiable. Anyone +// holding the signing key can also mint one — asymmetric signing is still to do. + +export function issueReceipt({ + request, preview, route, authority, verification, result, + policyVersion = 'local-v1', priorReceiptHash = null, keyPair = defaultKeyPair(), + issuedAt = new Date().toISOString() +}) { + const payload = { + type: 'PCTRExecutionReceipt', version: 1, + receiptId: `rcpt-${crypto.randomUUID()}`, + requested: { action: request.action, target: request.target, params: request.params ?? {} }, + requestedBy: request.agent ?? null, + principal: authority?.principal ?? request.principal ?? null, + delegator: authority?.delegator ?? null, + routeSelected: route ? { routeId: route.routeId, routeHash: route.routeHash, effectiveTrust: route.effectiveTrust, agents: route.agents } : null, + consequence: preview ? { class: preview.consequence, severity: preview.severity, reversible: preview.reversible, blastRadius: preview.blastRadius } : null, + authorityBound: authority ? { bindingHash: authority.bindingHash, materialParams: authority.materialParams, constraints: authority.constraints, expiresAt: authority.expiresAt, nonce: authority.nonce, signedBy: authority.keyId ?? null } : null, + verifier: { boundary: verification?.boundary ?? 'pctr.effect-boundary', decision: verification?.decision ?? 'EXECUTION_DENIED', failures: verification?.failures ?? [], signerVerified: verification?.signerVerified ?? false }, + executed: result ? { status: result.status, output: result.output ?? null, executedAt: result.executedAt ?? issuedAt } : null, + policyVersion, issuedAt, priorReceiptHash, + keyId: keyPair.keyId ?? keyIdFor(keyPair.publicKey) + }; + const receiptHash = hashObj(payload); + return { ...payload, receiptHash, signature: signHash(receiptHash, keyPair), signerPublicKey: exportPublic(keyPair.publicKey) }; +} + +export function verifyReceipt(receipt, { publicKey, trustedKeyIds, priorReceiptHash = undefined } = {}) { + const failures = []; + const { receiptHash, signature, signerPublicKey, ...payload } = receipt ?? {}; + if (!receipt) return { valid: false, signerVerified: false, failures: [{ code: 'RECEIPT_MISSING', message: 'no receipt provided' }] }; + if (hashObj(payload) !== receiptHash) failures.push({ code: 'RECEIPT_HASH_MISMATCH', message: 'receipt contents were altered after signing' }); + + // A third party verifies with a key they already hold. The embedded key can only + // establish internal consistency, so verification says which of the two happened. + const key = publicKey ?? signerPublicKey; + const signerVerified = Boolean(publicKey) || (Array.isArray(trustedKeyIds) && trustedKeyIds.includes(payload.keyId)); + if (!key) failures.push({ code: 'SIGNATURE_INVALID', message: 'receipt carries no verification key' }); + else if (!verifyHash(receiptHash, signature, key)) failures.push({ code: 'SIGNATURE_INVALID', message: 'receipt signature does not verify' }); + else if (signature?.keyId !== payload.keyId) failures.push({ code: 'SIGNER_MISMATCH', message: 'signature key id does not match the key id inside the receipt' }); + if (Array.isArray(trustedKeyIds) && !trustedKeyIds.includes(payload.keyId)) { + failures.push({ code: 'UNTRUSTED_SIGNER', message: `receipt was signed by ${payload.keyId}, which is not a trusted signer` }); + } + if (priorReceiptHash !== undefined && payload.priorReceiptHash !== priorReceiptHash) { + failures.push({ code: 'CHAIN_BROKEN', message: 'receipt does not chain to the expected prior receipt' }); + } + if (payload.executed && payload.verifier?.decision !== 'EXECUTION_ALLOWED') { + failures.push({ code: 'EXECUTED_WITHOUT_AUTHORITY', message: 'receipt records an execution that was not allowed' }); + } + return { + valid: failures.length === 0, receiptId: payload.receiptId, signerVerified, keyId: payload.keyId, failures, + warnings: failures.length === 0 && !signerVerified + ? [{ code: 'UNPINNED_KEY', message: 'signature is internally consistent, but the signer was not pinned; pass publicKey or trustedKeyIds to verify who signed it' }] + : [] + }; +} + +// Receipts chain, so a missing or reordered receipt is detectable. +export function verifyReceiptChain(receipts, opts = {}) { + const failures = []; + let prior = null; + for (const r of receipts) { + const res = verifyReceipt(r, { ...opts, priorReceiptHash: prior }); + if (!res.valid) failures.push({ receiptId: r.receiptId, failures: res.failures }); + prior = r.receiptHash; + } + return { valid: failures.length === 0, length: receipts.length, failures }; +} diff --git a/packages/pctr/src/render.mjs b/packages/pctr/src/render.mjs new file mode 100644 index 0000000..b0b3550 --- /dev/null +++ b/packages/pctr/src/render.mjs @@ -0,0 +1,141 @@ +import { protectedActions, pathsToAction, summarize } from './graph.mjs'; +import { replay as replayTimeline, describe } from './timeline.mjs'; + +// Plain English first. Machine-readable output is opt-in via --json. +const ESC = String.fromCharCode(27); +const useColor = () => process.stdout.isTTY && !process.env.NO_COLOR; +const c = (code) => (s) => (useColor() ? `${ESC}[${code}m${s}${ESC}[0m` : String(s)); +export const dim = c('2'); export const bold = c('1'); +export const red = c('31'); export const green = c('32'); export const yellow = c('33'); export const cyan = c('36'); + +const SEV_COLOR = { CRITICAL: red, HIGH: yellow, MEDIUM: cyan, LOW: dim }; +export const sev = (s) => (SEV_COLOR[s] ?? dim)(s); +export const heading = (text) => `\n${bold(text.toUpperCase())}\n`; +export const field = (label, value, width = 22) => `${label.padEnd(width)}${value}`; +export const chain = (ids) => ids.join(`\n${dim(' |')}\n${dim(' v')}\n`); + +export function renderScan(graph) { + const s = summarize(graph); + const out = [heading('PCTR'), 'Scanning your agents...', '']; + out.push(field('Agents found', String(s.agents))); + out.push(field('Tools found', String(s.tools))); + out.push(field('External systems', String(s.externalSystems))); + out.push(field('Potential actions', String(s.actions))); + out.push('', bold('Protected consequences'), ''); + for (const level of ['CRITICAL', 'HIGH', 'MEDIUM']) out.push(field(sev(level), String(s.bySeverity[level]))); + const top = protectedActions(graph)[0]; + if (top) { + out.push('', bold('Highest priority:'), '', top.id, '', bold('Route:'), ''); + out.push(chain(pathsToAction(graph, top.id)[0] ?? [])); + out.push('', bold('Problem:'), '', + `The final action can cause "${top.label}"${top.reversible ? '' : ' irreversibly'} without independent execution authority.`, + '', bold('Recommended:'), '', `Add TTP authority verification before ${top.id}.`); + } else { + out.push('', 'No protected consequences found. Nothing here can cause an irreversible change.'); + } + return out.join('\n'); +} + +export function renderPreview(p) { + // Say where each number came from: a measured value and a value someone typed into + // pctr.json deserve different confidence. + const mark = (name) => (p.provenance?.[name] === 'measured' ? green(' (measured)') : p.provenance ? dim(' (declared)') : ''); + const out = [heading('consequence preview')]; + out.push(field('Action', p.action)); + out.push(field('Causes', p.label)); + if (p.recordsAffected != null) out.push(field('Records affected', `${p.recordsAffected}${mark('recordsAffected')}`)); + if (p.financialExposure != null) out.push(field('Financial exposure', `$${p.financialExposure}${mark('amount')}`)); + out.push(field('Reversible', p.reversible ? green('YES') : red('NO'))); + out.push(field('Connected workflows', String(p.connectedWorkflows))); + out.push(field('Blast radius', `${p.blastRadius.band} (${p.blastRadius.score}/100)`)); + out.push(field('Routes that reach it', String(p.routesThatCanReachIt))); + out.push(field('Risk', sev(p.severity))); + if (p.probe && !p.probe.ok) out.push('', yellow(`Probe did not run: ${p.probe.error}`), dim('Falling back to declared values.')); + out.push('', bold('Recommended'), '', ...p.recommendations.map((r) => `- ${r}`)); + return out.join('\n'); +} + +export function renderRoute(r) { + const out = []; + if (!r.selected) { + out.push(heading('no admissible route')); + out.push(`Nothing can reach ${bold(r.action)} under current trust and policy.`, ''); + for (const cand of r.candidates) { + out.push(dim(cand.routeId)); + for (const rej of cand.rejections) out.push(` ${red('x')} ${rej.message}`); + out.push(''); + } + return out.join('\n'); + } + out.push(heading('route selected')); + out.push(chain(r.selected.path)); + out.push('', field('Effective trust', `${r.selected.effectiveTrust} (${r.trustRequired} required for ${sev(r.severity)})`)); + out.push(field('Hops', String(r.selected.hops))); + if (r.requiresApproval) out.push(field('Human approval', yellow('REQUIRED'))); + const rejected = r.candidates.filter((x) => !x.admissible); + if (rejected.length) { + out.push('', bold('Rejected routes'), ''); + for (const cand of rejected) { + out.push(dim(cand.routeId)); + for (const rej of cand.rejections) out.push(` ${red('x')} ${rej.message}`); + } + } + return out.join('\n'); +} + +export function renderGraph(graph) { + const out = [heading('trust graph')]; + out.push(graph.principal); + for (const a of [...graph.nodes.values()].filter((n) => n.type === 'agent')) { + const delegated = graph.edges.filter((e) => e.from === a.id && e.kind === 'delegates').map((e) => e.to); + const tools = graph.edges.filter((e) => e.from === a.id && e.kind === 'uses').map((e) => e.to); + out.push(` |- ${bold(a.id)} ${dim(`[${a.framework}] trust ${a.trust}`)}`); + for (const d of delegated) out.push(` ${dim('delegates ->')} ${d}`); + for (const t of tools) { + out.push(` ${dim('uses ->')} ${t}`); + for (const act of graph.edges.filter((e) => e.from === t && e.kind === 'performs').map((e) => e.to)) { + const node = graph.nodes.get(act); + out.push(` ${node.protected ? red('!') : dim('.')} ${act} ${dim(`-> ${node.label}`)} ${node.protected ? sev(node.severity) : ''}`); + } + } + } + out.push('', dim('! = protected consequence: must not execute on credential possession alone')); + return out.join('\n'); +} + +export function renderTimeline(timeline) { + const out = [heading('agent time machine'), dim(`run ${timeline.runId}`), '']; + for (const e of replayTimeline(timeline)) out.push(`${dim(e.at.slice(11, 19))} ${e.line}`); + out.push('', dim('Ask why with: pctr explain ')); + return out.join('\n'); +} + +export function renderWhy(w) { + if (!w.found) return w.explanation; + const out = [heading(w.question)]; + out.push(w.answer, '', bold('Because'), ''); + out.push(...w.because.map((b) => `- ${b}`)); + if (w.policy) out.push('', field('Policy', w.policy)); + return out.join('\n'); +} + +export function renderReceipt(receipt, verification) { + const out = [heading('execution receipt')]; + out.push(field('Receipt', receipt.receiptId)); + out.push(field('Requested', `${receipt.requested.action} on ${receipt.requested.target}`)); + out.push(field('Principal', String(receipt.principal))); + out.push(field('Delegated by', String(receipt.delegator ?? '-'))); + out.push(field('Route', receipt.routeSelected?.routeId ?? '-')); + out.push(field('Consequence', receipt.consequence ? `${receipt.consequence.class} (${sev(receipt.consequence.severity)})` : '-')); + out.push(field('Authority bound to', receipt.authorityBound ? JSON.stringify(receipt.authorityBound.materialParams) : '-')); + out.push(field('Verified by', receipt.verifier.boundary)); + out.push(field('Decision', receipt.verifier.decision === 'EXECUTION_ALLOWED' ? green(receipt.verifier.decision) : red(receipt.verifier.decision))); + for (const f of receipt.verifier.failures) out.push(` ${red('x')} ${f.message}`); + out.push(field('Executed', receipt.executed ? `${receipt.executed.status} at ${receipt.executed.executedAt}` : 'NOT EXECUTED')); + out.push(field('Policy version', receipt.policyVersion)); + out.push(field('Receipt hash', dim(receipt.receiptHash))); + if (verification) out.push('', verification.valid ? green('Receipt verifies.') : red(`Receipt INVALID: ${verification.failures.map((f) => f.message).join('; ')}`)); + return out.join('\n'); +} + +export { describe }; diff --git a/packages/pctr/src/report.mjs b/packages/pctr/src/report.mjs new file mode 100644 index 0000000..3cbe35e --- /dev/null +++ b/packages/pctr/src/report.mjs @@ -0,0 +1,88 @@ +import { protectedActions, pathsToAction, summarize } from './graph.mjs'; +import { previewConsequence } from './twin.mjs'; +import { resolveRoute } from './router.mjs'; + +// A scan result nobody can share is a scan result nobody acts on. This renders the +// findings as Markdown that drops straight into a PR, an issue, or a message. + +const EMOJI = { CRITICAL: '🔴', HIGH: '🟠', MEDIUM: '🟡', LOW: '⚪' }; +const HOMEPAGE = 'https://github.com/BlockSiFr/ttp-protocol/tree/main/packages/pctr'; + +export function badgeUrl(graph) { + const { bySeverity } = summarize(graph); + const critical = bySeverity.CRITICAL; + const label = critical + ? `${critical}%20critical%20consequence${critical === 1 ? '' : 's'}` + : `${summarize(graph).protected}%20protected%20consequences`; + const color = critical ? 'critical' : summarize(graph).protected ? 'orange' : 'brightgreen'; + return `https://img.shields.io/badge/PCTR-${label}-${color}?style=flat-square&labelColor=0A0A0F`; +} + +export function renderReport(graph, { title = 'PCTR consequence scan', includeBadge = true } = {}) { + const summary = summarize(graph); + const findings = protectedActions(graph); + const irreversible = findings.filter((f) => !f.reversible); + const out = []; + + out.push(`## ${summary.bySeverity.CRITICAL ? '🛑' : '🔍'} ${title}`, ''); + if (includeBadge) out.push(`![PCTR](${badgeUrl(graph)})`, ''); + if (!summary.agents) { + out.push(`PCTR found no agents to scan here. Declare them in \`pctr.json\` — see the [manifest format](${HOMEPAGE}#pctrjson).`, ''); + return `${out.join('\n')}\n${footer()}`; + } + out.push(`**${summary.agents} agents · ${summary.tools} tools · ${summary.actions} actions.** ${ + findings.length + ? `${findings.length} of them can cause a protected consequence${irreversible.length ? `, and ${irreversible.length} cannot be undone` : ''}.` + : 'None of them can cause a protected consequence.' + }`, ''); + if (!findings.length) { + out.push('Nothing in this graph can move money, delete data, change production or expose a secret.', ''); + return `${out.join('\n')}\n${footer()}`; + } + + out.push('| Severity | Consequences |', '| --- | --- |'); + for (const level of ['CRITICAL', 'HIGH', 'MEDIUM']) { + if (summary.bySeverity[level]) out.push(`| ${EMOJI[level]} ${level} | ${summary.bySeverity[level]} |`); + } + out.push(''); + + const top = findings[0]; + const preview = previewConsequence(graph, top.id); + const route = pathsToAction(graph, top.id)[0] ?? []; + const resolved = resolveRoute(graph, top.id); + + out.push(`### Highest priority: \`${top.id}\``, ''); + out.push([ + `Causes **${preview.label}**`, + preview.reversible ? 'reversible' : '**irreversible**', + preview.recordsAffected != null ? `${preview.recordsAffected.toLocaleString('en-US')} records` : null, + preview.financialExposure != null ? `$${preview.financialExposure.toLocaleString('en-US')}` : null, + `blast radius ${preview.blastRadius.band}` + ].filter(Boolean).join(' · '), ''); + + if (route.length) out.push('```', route.join('\n ↓\n'), '```', ''); + + if (resolved.selected) { + out.push(`Today this executes over \`${resolved.selected.agents.join(' → ')}\` at effective trust **${resolved.selected.effectiveTrust}**${ + resolved.requiresApproval ? ', and policy requires human approval' : ' with no human in the loop'}.`, ''); + } else { + out.push('No admissible route can reach it under current trust and policy.', ''); + } + + out.push('**What to do:** ' + (preview.recommendations[0] ?? 'Add TTP authority verification before this action.'), ''); + + if (findings.length > 1) { + out.push('
', 'All protected consequences', ''); + out.push('| Action | Causes | Severity | Reversible | Routes that reach it |', '| --- | --- | --- | --- | --- |'); + for (const finding of findings) { + const p = previewConsequence(graph, finding.id); + out.push(`| \`${finding.id}\` | ${p.label} | ${EMOJI[p.severity]} ${p.severity} | ${p.reversible ? 'yes' : 'no'} | ${p.routesThatCanReachIt} |`); + } + out.push('', '
', ''); + } + + return `${out.join('\n')}\n${footer()}`; +} + +const footer = () => + `---\nScanned with [PCTR](${HOMEPAGE}) — \`pctr scan\`. Nothing left this machine.\n`; diff --git a/packages/pctr/src/router.mjs b/packages/pctr/src/router.mjs new file mode 100644 index 0000000..e758bde --- /dev/null +++ b/packages/pctr/src/router.mjs @@ -0,0 +1,108 @@ +import { pathsToAction } from './graph.mjs'; +import { agentTrustNow, meetsThreshold, TRUST_REQUIRED, requiresHumanApproval } from './trust.mjs'; +import { verify_trust_route } from './ttp.mjs'; + +// Trust Routing is not shortest-path routing. Admissibility first; optimization only +// among routes that are already authorized, trustworthy and consequence-compatible. +// A security constraint is never traded away for a cheaper or faster route. + +const holdsAuthority = (agent, actionId) => + (agent.authority ?? []).some((a) => a === '*' || a === actionId || (a.endsWith('.*') && actionId.startsWith(a.slice(0, -1)))); + +export function resolveRoute(graph, actionId, options = {}) { + const action = graph.nodes.get(actionId); + if (!action || action.type !== 'action') { + return { selected: null, candidates: [], error: `Unknown action: ${actionId}` }; + } + const policy = { ...(graph.manifest.policy ?? {}), ...(options.policy ?? {}) }; + const severity = options.severity ?? action.severity; + const at = options.at ?? new Date().toISOString(); + + const candidates = pathsToAction(graph, actionId).map((path) => { + const agents = path.filter((id) => graph.nodes.get(id)?.type === 'agent').map((id) => graph.nodes.get(id)); + const rejections = []; + const trustStates = agents.map((a) => agentTrustNow(a, { severity, at, decayConstant: policy.decayConstant })); + + // 1. remove unauthorized routes + for (const a of agents) { + if (!holdsAuthority(a, actionId)) { + rejections.push({ code: 'INSUFFICIENT_AUTHORITY', agent: a.id, + message: `${a.id} does not hold authority for ${actionId}` }); + } + } + // 2. remove untrustworthy routes (including stale evidence) + for (const t of trustStates) { + if (t.evidenceStale) { + rejections.push({ code: 'STALE_EVIDENCE', agent: t.agentId, + message: `${t.agentId} evidence is ${t.evidenceAgeSeconds}s old; ${severity} actions require evidence under ${t.maxEvidenceAgeSeconds}s` }); + } + } + const depth = Math.max(0, agents.length - 1); + const perHop = policy.decayPerHop ?? 0.05; + const effectiveTrust = trustStates.length + ? Number(Math.max(0, Math.min(...trustStates.map((t) => t.trust)) - perHop * depth).toFixed(4)) + : 0; + if (!meetsThreshold(effectiveTrust, severity)) { + rejections.push({ code: 'TRUST_BELOW_THRESHOLD', + message: `effective trust ${effectiveTrust} is below the ${TRUST_REQUIRED[severity]} required for ${severity} consequences` }); + } + // 3. remove consequence-incompatible routes + const allowed = policy.jurisdictions?.[action.consequence]; + for (const a of agents) { + if (allowed && a.jurisdiction && !allowed.includes(a.jurisdiction)) { + rejections.push({ code: 'JURISDICTION_INCOMPATIBLE', agent: a.id, + message: `${a.id} operates in ${a.jurisdiction}; ${action.consequence} is limited to ${allowed.join(', ')}` }); + } + if (a.available === false) { + rejections.push({ code: 'AGENT_UNAVAILABLE', agent: a.id, message: `${a.id} is unavailable` }); + } + } + // 4. remove policy-invalid routes (protocol-level route check via TTP) + const ttp = verify_trust_route({ + routeId: path.join('>'), + hops: agents.map((a) => ({ subject: a.id, issuer: a.issuer ?? graph.principal, trustScore: a.trust ?? 0 })), + maxHops: policy.maxHops ?? 6, + decayPerHop: perHop, + minIntermediateTrust: policy.minIntermediateTrust ?? 0, + sourceDomain: policy.sourceDomain ?? 'local', + targetDomain: policy.targetDomain ?? 'local', + action: actionId, + resource: options.target ?? action.target ?? actionId, + routePolicy: policy.routePolicy + }); + for (const f of ttp.failureReasons) rejections.push({ code: f.code, message: f.message }); + + const latencyMs = agents.reduce((s, a) => s + (a.latencyMs ?? 0), 0); + const costUnits = agents.reduce((s, a) => s + (a.costUnits ?? 0), 0); + return { + routeId: path.join(' -> '), path, agents: agents.map((a) => a.id), hops: path.length, + effectiveTrust, trustStates, latencyMs, costUnits, + routeHash: ttp.routeHash, admissible: rejections.length === 0, rejections + }; + }); + + // 5. optimize only what survived + const admissible = candidates.filter((c) => c.admissible).sort((a, b) => + b.effectiveTrust - a.effectiveTrust || a.latencyMs - b.latencyMs || a.costUnits - b.costUnits || a.hops - b.hops); + + const selected = admissible[0] ?? null; + return { + action: actionId, severity, consequence: action.consequence, at, + selected, admissible, candidates, + requiresApproval: requiresHumanApproval(severity, policy), + trustRequired: TRUST_REQUIRED[severity] + }; +} + +// Authority MUST NOT silently expand during rerouting: a replacement route may never +// be admitted on weaker grounds than the route it replaces. +export function reroute(graph, actionId, previous, options = {}) { + const next = resolveRoute(graph, actionId, options); + if (previous?.selected && next.selected) { + if (next.severity !== previous.severity || next.trustRequired < previous.trustRequired) { + return { ...next, selected: null, expansionBlocked: true, + reason: 'replacement route would have required weaker authority than the route it replaces' }; + } + } + return { ...next, replaced: previous?.selected?.routeId ?? null }; +} diff --git a/packages/pctr/src/store.mjs b/packages/pctr/src/store.mjs new file mode 100644 index 0000000..36227d2 --- /dev/null +++ b/packages/pctr/src/store.mjs @@ -0,0 +1,49 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +// Everything PCTR learns locally lives in ./.pctr — no account, no network. +export const dir = (cwd = process.cwd()) => path.join(cwd, '.pctr'); +const ensure = (p) => { fs.mkdirSync(p, { recursive: true }); return p; }; + +export const manifestPath = (cwd = process.cwd()) => path.join(cwd, 'pctr.json'); + +export function readManifest(cwd = process.cwd()) { + const p = manifestPath(cwd); + if (!fs.existsSync(p)) return null; + return JSON.parse(fs.readFileSync(p, 'utf8')); +} +export function writeManifest(manifest, cwd = process.cwd()) { + fs.writeFileSync(manifestPath(cwd), `${JSON.stringify(manifest, null, 2)}\n`); + return manifestPath(cwd); +} + +export function saveRun(timeline, cwd = process.cwd()) { + const p = path.join(ensure(path.join(dir(cwd), 'runs')), `${timeline.runId}.json`); + fs.writeFileSync(p, `${JSON.stringify(timeline.toJSON ? timeline.toJSON() : timeline, null, 2)}\n`); + return p; +} +export function listRuns(cwd = process.cwd()) { + const p = path.join(dir(cwd), 'runs'); + if (!fs.existsSync(p)) return []; + return fs.readdirSync(p).filter((f) => f.endsWith('.json')) + .map((f) => ({ runId: f.replace(/\.json$/, ''), file: path.join(p, f), mtime: fs.statSync(path.join(p, f)).mtimeMs })) + .sort((a, b) => b.mtime - a.mtime); +} +export function loadRun(runId, cwd = process.cwd()) { + const runs = listRuns(cwd); + const hit = runId ? runs.find((r) => r.runId === runId) : runs[0]; + return hit ? JSON.parse(fs.readFileSync(hit.file, 'utf8')) : null; +} + +export function saveReceipt(receipt, cwd = process.cwd()) { + const p = path.join(ensure(path.join(dir(cwd), 'receipts')), `${receipt.receiptId}.json`); + fs.writeFileSync(p, `${JSON.stringify(receipt, null, 2)}\n`); + return p; +} +export function listReceipts(cwd = process.cwd()) { + const p = path.join(dir(cwd), 'receipts'); + if (!fs.existsSync(p)) return []; + return fs.readdirSync(p).filter((f) => f.endsWith('.json')) + .map((f) => JSON.parse(fs.readFileSync(path.join(p, f), 'utf8'))) + .sort((a, b) => a.issuedAt.localeCompare(b.issuedAt)); +} diff --git a/packages/pctr/src/timeline.mjs b/packages/pctr/src/timeline.mjs new file mode 100644 index 0000000..379d3ef --- /dev/null +++ b/packages/pctr/src/timeline.mjs @@ -0,0 +1,135 @@ +import crypto from 'node:crypto'; +import { isCanonicalEvent } from './events.mjs'; + +// AGENT TIME MACHINE — replay exactly why your AI did what it did. +// Every event records what happened and which earlier events caused it, so "why" +// is answered deterministically from evidence rather than re-narrated by a model. + +export function createTimeline({ runId = `run-${crypto.randomUUID().slice(0, 8)}`, objective = null, clock } = {}) { + const events = []; + const now = clock ?? (() => new Date().toISOString()); + const api = { + runId, objective, events, + record(event, detail = {}, { subject = null, because = [] } = {}) { + if (!isCanonicalEvent(event)) throw new Error(`Not a canonical PCTR event: ${event}`); + const entry = { seq: events.length, id: `e${events.length}`, at: now(), event, subject, detail, because }; + events.push(entry); + return entry; + }, + last: (event) => [...events].reverse().find((e) => e.event === event) ?? null, + toJSON: () => ({ runId, objective, events }) + }; + return api; +} + +export const fromJSON = (data) => { + const t = createTimeline({ runId: data.runId, objective: data.objective }); + t.events.push(...(data.events ?? [])); + return t; +}; + +// REPLAY — the timeline as it happened. +export function replay(timeline) { + return timeline.events.map((e) => ({ at: e.at, event: e.event, line: describe(e), id: e.id })); +} + +// WHY — deterministic explanation built from the recorded causal chain. +export function why(timeline, eventIdOrName) { + const target = timeline.events.find((e) => e.id === eventIdOrName) ?? + [...timeline.events].reverse().find((e) => e.event === eventIdOrName); + if (!target) return { found: false, question: eventIdOrName, explanation: `No event matching "${eventIdOrName}" in this run.` }; + + const chain = []; + const visit = (event, depth = 0) => { + if (depth > 8) return; + for (const id of event.because ?? []) { + const cause = timeline.events.find((e) => e.id === id); + if (cause && !chain.some((c) => c.id === cause.id)) { chain.push(cause); visit(cause, depth + 1); } + } + }; + visit(target); + const reasons = chain.sort((a, b) => a.seq - b.seq).map(describe); + return { + found: true, + question: questionFor(target), + answer: target.detail.reason ?? answerFor(target), + because: reasons.length ? reasons : ['This was the first event in the run; nothing preceded it.'], + policy: target.detail.policy ?? null + }; +} + +// FORK + SIMULATE — replay the run with changed inputs, without executing anything. +export function fork(timeline, { upTo = null } = {}) { + const cut = upTo ? timeline.events.findIndex((e) => e.id === upTo) + 1 : timeline.events.length; + const forked = createTimeline({ runId: `${timeline.runId}-fork`, objective: timeline.objective }); + forked.events.push(...timeline.events.slice(0, cut).map((e) => ({ ...e }))); + return forked; +} + +// COMPARE — what differs between two runs, and where they diverged. +export function compare(a, b) { + const rows = []; + const max = Math.max(a.events.length, b.events.length); + let divergedAt = null; + for (let i = 0; i < max; i++) { + const left = a.events[i]; const right = b.events[i]; + const same = describe(left ?? {}) === describe(right ?? {}); + if (!same && divergedAt === null) divergedAt = i; + rows.push({ seq: i, left: left ? describe(left) : null, right: right ? describe(right) : null, same }); + } + return { divergedAt, rows, sameOutcome: outcomeOf(a) === outcomeOf(b), outcomes: [outcomeOf(a), outcomeOf(b)] }; +} + +const outcomeOf = (t) => [...t.events].reverse().find((e) => + ['EXECUTION_COMPLETED', 'EXECUTION_ALLOWED', 'EXECUTION_DENIED'].includes(e.event))?.event ?? 'NO_OUTCOME'; + +const QUESTIONS = { + EXECUTION_DENIED: 'WHY WAS THIS EXECUTION DENIED?', + EXECUTION_ALLOWED: 'WHY WAS THIS EXECUTION ALLOWED?', + EXECUTION_COMPLETED: 'WHY DID THIS ACTION EXECUTE?', + ROUTE_SELECTED: 'WHY WAS THIS ROUTE SELECTED?', + ROUTE_INVALIDATED: 'WHY WAS THE ROUTE INVALIDATED?', + AUTHORITY_ISSUED: 'WHY WAS AUTHORITY ISSUED?', + CONSEQUENCE_DETECTED: 'WHY IS THIS A PROTECTED CONSEQUENCE?', + TRUST_CHANGED: 'WHY DID TRUST CHANGE?', + RECEIPT_ISSUED: 'WHY WAS THIS RECEIPT ISSUED?' +}; +const questionFor = (e) => QUESTIONS[e.event] ?? `WHY: ${describe(e).toUpperCase()}?`; + +function answerFor(e) { + const d = e.detail ?? {}; + switch (e.event) { + case 'EXECUTION_ALLOWED': + return `Authority ${String(d.bindingHash ?? '').slice(0, 19)}… bound this exact execution, and the effect boundary verified it independently of the agent that requested it.`; + case 'ROUTE_SELECTED': + return `${d.routeId} was the most trustworthy admissible route; ${d.rejected ?? 0} candidate route(s) were rejected before optimization.`; + case 'AUTHORITY_ISSUED': + return `Authority was issued for this execution and expires at ${d.expiresAt}.`; + case 'CONSEQUENCE_DETECTED': + return `This action can cause ${d.consequence}, rated ${d.severity}${d.reversible === false ? ' and irreversible' : ''}.`; + default: return describe(e); + } +} + +export function describe(e) { + const d = e.detail ?? {}; + switch (e.event) { + case 'AGENT_DISCOVERED': return `Agent discovered: ${e.subject}`; + case 'AGENT_STARTED': return `Objective received${d.objective ? `: ${d.objective}` : ''}`; + case 'AGENT_DELEGATED': return `${e.subject} delegated to ${d.to}`; + case 'CAPABILITY_DISCOVERED': return `Capability discovered: ${d.tool ?? e.subject}`; + case 'ACTION_PROPOSED': return `Action proposed: ${d.action}${d.amount ? ` ($${d.amount})` : ''}`; + case 'CONSEQUENCE_DETECTED': return `Consequence detected: ${d.consequence} (${d.severity})`; + case 'TRUST_CHANGED': return `Trust changed: ${e.subject} ${d.from} -> ${d.to}`; + case 'ROUTE_SELECTED': return `Route selected: ${d.routeId}`; + case 'ROUTE_INVALIDATED': return `Route invalidated: ${d.routeId}`; + case 'AUTHORITY_REQUESTED': return `Authority requested for ${d.action}`; + case 'AUTHORITY_ISSUED': return `TTP authority issued (${String(d.bindingHash ?? '').slice(0, 19)}…)`; + case 'EXECUTION_REQUESTED': return `Execution requested: ${d.action}`; + case 'EXECUTION_ALLOWED': return `Effect boundary verified authority`; + case 'EXECUTION_DENIED': return `Execution denied: ${d.reason ?? (d.failures ?? []).map((f) => f.code).join(', ')}`; + case 'EXECUTION_COMPLETED': return `${d.action ?? 'Action'} executed`; + case 'RECEIPT_ISSUED': return `Receipt signed (${d.receiptId})`; + default: return `${e.event}${e.subject ? ` ${e.subject}` : ''}`; + } +} diff --git a/packages/pctr/src/trust.mjs b/packages/pctr/src/trust.mjs new file mode 100644 index 0000000..6afd2a5 --- /dev/null +++ b/packages/pctr/src/trust.mjs @@ -0,0 +1,40 @@ +import { apply_decay } from './ttp.mjs'; +import { severityRank } from './consequences.mjs'; + +// Trust is not permanent. Evidence ages, and aged evidence buys less authority. +export const TRUST_REQUIRED = { CRITICAL: 0.90, HIGH: 0.75, MEDIUM: 0.60, LOW: 0.0 }; +export const MAX_EVIDENCE_AGE_SECONDS = { CRITICAL: 300, HIGH: 900, MEDIUM: 3600, LOW: Infinity }; + +const RISK_TIER = { CRITICAL: 'high', HIGH: 'high', MEDIUM: 'medium', LOW: 'low' }; + +export function agentTrustNow(agent, { severity = 'MEDIUM', decayConstant = 0.00005, at = new Date().toISOString() } = {}) { + const decayed = apply_decay({ + initialTrust: agent.trust ?? 0, + decayConstant, + elapsedSeconds: agent.evidenceAgeSeconds ?? 0, + activitySignals: agent.activitySignals ?? [], + riskTier: RISK_TIER[severity] ?? 'low', + calculatedAt: at + }); + const maxAge = MAX_EVIDENCE_AGE_SECONDS[severity]; + return { + agentId: agent.id, + trust: Number(decayed.finalTrust.toFixed(4)), + declaredTrust: agent.trust ?? 0, + evidenceAgeSeconds: agent.evidenceAgeSeconds ?? 0, + evidenceStale: (agent.evidenceAgeSeconds ?? 0) > maxAge, + maxEvidenceAgeSeconds: maxAge + }; +} + +export const meetsThreshold = (trust, severity) => trust >= (TRUST_REQUIRED[severity] ?? 0); + +// A trust input changed materially enough to force route reevaluation. +export function trustChanged(before, after) { + return before.trust !== after.trust || before.evidenceStale !== after.evidenceStale; +} + +export const requiresHumanApproval = (severity, policy = {}) => + policy.requireApprovalAtOrAbove + ? severityRank(severity) >= severityRank(policy.requireApprovalAtOrAbove) + : severity === 'CRITICAL'; diff --git a/packages/pctr/src/ttp.mjs b/packages/pctr/src/ttp.mjs new file mode 100644 index 0000000..4108ec3 --- /dev/null +++ b/packages/pctr/src/ttp.mjs @@ -0,0 +1,84 @@ +import crypto from 'node:crypto'; + +// The TTP primitives PCTR depends on, implemented here so that installing PCTR pulls in +// nothing else. These MUST stay bit-for-bit compatible with the Trust Transfer Protocol +// reference implementation in this repo (src/decay.mjs, src/trust_route.mjs, +// src/util.mjs) — tests/conformance.test.mjs compares the two on every run and fails if +// they drift. Change the reference first; mirror it here second. +// Spec: https://github.com/BlockSiFr/ttp-protocol/blob/main/SPECIFICATION.md + +export const clamp01 = (n) => Math.max(0, Math.min(1, n)); + +// Canonical form: sorted keys, fixed numeric precision. Two structurally equal objects +// must hash identically, or a binding could be evaded by reordering fields. +export function canonicalize(v) { + if (Array.isArray(v)) return `[${v.map(canonicalize).join(',')}]`; + if (v && typeof v === 'object') { + const keys = Object.keys(v).sort(); + return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalize(v[k])}`).join(',')}}`; + } + if (typeof v === 'number') return Number(v.toFixed(6)).toString(); + return JSON.stringify(v); +} + +export const hashObj = (obj) => `sha256:${crypto.createHash('sha256').update(canonicalize(obj)).digest('hex')}`; + +export const failure = (code, message, details = {}) => ({ code, message, details }); + +// TrustDecay: trust weakens over time without fresh evidence, faster for riskier work, +// and verified activity signals recharge it. +const RISK_MULTIPLIER = { low: 1, medium: 1.2, high: 1.5 }; + +export function apply_decay({ + initialTrust, decayConstant, elapsedSeconds, activitySignals = [], + riskTier = 'low', floor = 0, ceiling = 1, calculatedAt +}) { + const decayedTrust = initialTrust * Math.exp(-(RISK_MULTIPLIER[riskTier] * decayConstant ?? decayConstant) * elapsedSeconds); + let recharge = 0; + const appliedSignals = []; + for (const signal of activitySignals) { + if (!signal.verified) continue; + const delta = signal.weight * (signal.recencyFactor ?? 1); + recharge += delta; + appliedSignals.push({ ...signal, delta }); + } + const finalTrust = Math.max(floor, Math.min(ceiling, clamp01(decayedTrust + recharge))); + return { initialTrust, decayedTrust, finalTrust, elapsedSeconds, decayConstant, riskTier, appliedSignals, calculatedAt }; +} + +// A trust route is valid only if every hop is identified, trusted by policy, and still +// above the intermediate threshold after per-hop decay. +export function verify_trust_route(input) { + const failures = []; + if (!input.hops?.length) failures.push(failure('ROUTE_INVALID', 'empty route')); + if (input.hops.length > input.maxHops) failures.push(failure('ROUTE_TOO_LONG', 'too many hops')); + + let effectiveTrust = 1; + for (const hop of input.hops ?? []) { + if (!hop.subject || !hop.issuer) failures.push(failure('ROUTE_INVALID', 'missing hop identity')); + effectiveTrust *= Math.max(0, (hop.trustScore ?? 0) - input.decayPerHop); + if (effectiveTrust < input.minIntermediateTrust) failures.push(failure('ROUTE_INVALID', 'intermediate trust below threshold')); + if (input.routePolicy?.trustedIssuers && !input.routePolicy.trustedIssuers.includes(hop.issuer)) { + failures.push(failure('ROUTE_POLICY_VIOLATION', 'untrusted issuer')); + } + } + if (input.routePolicy?.allowedDomains && !input.routePolicy.allowedDomains.includes(`${input.sourceDomain}->${input.targetDomain}`)) { + failures.push(failure('ROUTE_POLICY_VIOLATION', 'domain crossing not allowed')); + } + if (input.routePolicy?.allowedActions && !input.routePolicy.allowedActions.includes(input.action)) { + failures.push(failure('ROUTE_POLICY_VIOLATION', 'action not allowed')); + } + + const routeHash = hashObj({ routeId: input.routeId, hops: input.hops, action: input.action, resource: input.resource }); + return { + valid: failures.length === 0, + routeId: input.routeId, + routeHash, + sourceDomain: input.sourceDomain, + targetDomain: input.targetDomain, + delegationDepth: input.hops?.length ?? 0, + effectiveTrust, + policyRefs: input.routePolicy?.refs ?? [], + failureReasons: failures + }; +} diff --git a/packages/pctr/src/twin.mjs b/packages/pctr/src/twin.mjs new file mode 100644 index 0000000..7dc37cf --- /dev/null +++ b/packages/pctr/src/twin.mjs @@ -0,0 +1,65 @@ +import { classifyAction, severityRank } from './consequences.mjs'; +import { outgoing, pathsToAction } from './graph.mjs'; + +// CONSEQUENCE TWIN — see what your AI is about to change before it changes it. +// git diff / terraform plan / EXPLAIN, for autonomous agents. + +export function previewConsequence(graph, actionId, params = {}) { + const node = graph.nodes.get(actionId); + const hints = { ...(node ?? {}), ...params }; + const c = classifyAction(actionId, hints); + const recordsAffected = params.recordsAffected ?? node?.recordsAffected ?? null; + const amount = params.amount ?? node?.amount ?? null; + const routes = pathsToAction(graph, actionId); + + const affectedSystems = [...new Set(routes.map((p) => p[p.length - 2]).filter(Boolean))]; + const affectedIdentities = [...new Set(routes.flatMap((p) => p.slice(0, -2)))]; + const connectedWorkflows = node?.connectedWorkflows ?? affectedSystems.length; + const downstream = outgoing(graph, actionId, 'triggers'); + + const blastRadius = computeBlastRadius({ severity: c.severity, recordsAffected, connectedWorkflows, routes: routes.length }); + + return { + action: actionId, + consequence: c.consequence, + label: c.label, + severity: c.severity, + reversible: c.reversible, + protected: c.protected, + recordsAffected, + financialExposure: amount, + dataExposure: c.consequence === 'SECRET_EXPOSED' || c.consequence === 'DATA_READ' ? (params.dataClass ?? 'unknown') : null, + affectedSystems, + affectedIdentities, + connectedWorkflows, + downstreamDependencies: downstream, + routesThatCanReachIt: routes.length, + blastRadius, + recommendations: recommend({ action: actionId, c, recordsAffected, amount, connectedWorkflows }) + }; +} + +function computeBlastRadius({ severity, recordsAffected, connectedWorkflows, routes }) { + let score = severityRank(severity) * 20; + if (recordsAffected) score += Math.min(40, Math.log10(recordsAffected + 1) * 12); + score += Math.min(20, connectedWorkflows * 4); + score += Math.min(20, routes * 4); + const value = Math.round(Math.min(100, score)); + return { score: value, band: value >= 75 ? 'WIDE' : value >= 45 ? 'MODERATE' : 'NARROW' }; +} + +function recommend({ c, recordsAffected, amount, connectedWorkflows }) { + const out = []; + if (!c.reversible && c.protected) out.push('Require TTP execution authority bound to these exact parameters'); + if (recordsAffected > 25) out.push(`Limit batch to 25 records (currently ${recordsAffected}) + require approval`); + if (amount >= 5000) out.push(`Require human approval: $${amount} exceeds the $5,000 policy limit`); + if (connectedWorkflows > 2) out.push(`Notify ${connectedWorkflows} connected workflows before executing`); + if (c.severity === 'CRITICAL') out.push('Verify authority at the effect boundary, not in the calling agent'); + return out.length ? out : ['No additional control required for this consequence class']; +} + +// A change in material parameters can change the consequence — and therefore the route. +export function consequenceChanged(before, after) { + return before.severity !== after.severity || before.consequence !== after.consequence || + before.financialExposure !== after.financialExposure || before.recordsAffected !== after.recordsAffected; +} diff --git a/packages/pctr/tests/adapters.test.mjs b/packages/pctr/tests/adapters.test.mjs new file mode 100644 index 0000000..5821093 --- /dev/null +++ b/packages/pctr/tests/adapters.test.mjs @@ -0,0 +1,74 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { normalize, ingest, supportedFrameworks } from '../src/adapters.mjs'; +import { createTimeline, replay } from '../src/timeline.mjs'; + +test('every framework maps its own tool call onto the same security meaning', () => { + const cases = [ + ['openai-agents', { type: 'tool_call', name: 'payments.transfer', arguments: { amount: 18000 }, agent: 'finance' }], + ['claude-agents', { type: 'tool_use', name: 'payments.transfer', input: { amount: 18000 }, agent: 'finance' }], + ['langgraph', { event: 'on_tool_start', name: 'payments.transfer', data: { input: { amount: 18000 } } }], + ['crewai', { type: 'tool_usage_started', tool: 'payments.transfer', tool_args: { amount: 18000 }, agent: 'finance' }], + ['autogen', { type: 'function_call', name: 'payments.transfer', arguments: '{"amount":18000}', sender: 'finance' }], + ['semantic-kernel', { type: 'function_invoking', function: 'payments.transfer', arguments: { amount: 18000 } }], + ['mcp', { method: 'tools/call', params: { name: 'payments.transfer', arguments: { amount: 18000 } } }], + ['generic', { action: 'payments.transfer', params: { amount: 18000 } }] + ]; + for (const [framework, event] of cases) { + const normalized = normalize(framework, event); + assert.equal(normalized.event, 'ACTION_PROPOSED', framework); + assert.equal(normalized.detail.action, 'payments.transfer', framework); + assert.equal(normalized.detail.consequence, 'MONEY_MOVED', framework); + assert.equal(normalized.detail.severity, 'CRITICAL', framework); // $18,000 is over the limit + } +}); + +test('delegation is recognised whatever each framework calls it', () => { + const cases = [ + ['openai-agents', { type: 'handoff', from_agent: 'planner', to_agent: 'finance' }], + ['claude-agents', { type: 'subagent', agent: 'planner', subagent_type: 'finance' }], + ['crewai', { type: 'agent_delegated', from_agent: 'planner', to_agent: 'finance' }], + ['autogen', { type: 'message', sender: 'planner', recipient: 'finance' }], + ['a2a', { type: 'task.delegated', from: 'planner', to: 'finance' }] + ]; + for (const [framework, event] of cases) { + const normalized = normalize(framework, event); + assert.equal(normalized.event, 'AGENT_DELEGATED', framework); + assert.equal(normalized.subject, 'planner', framework); + assert.equal(normalized.detail.to, 'finance', framework); + } +}); + +test('MCP tool listings become capability discovery', () => { + const normalized = normalize('mcp', { method: 'tools/list', server: 'payments', result: { tools: [{ name: 'payments.transfer' }, { name: 'payments.status' }] } }); + assert.equal(normalized.event, 'CAPABILITY_DISCOVERED'); + assert.deepEqual(normalized.detail.actions, ['payments.transfer', 'payments.status']); +}); + +test('framework events without security meaning are dropped, not invented', () => { + assert.equal(normalize('openai-agents', { type: 'token_usage', tokens: 812 }), null); + assert.equal(normalize('langgraph', { event: 'on_llm_new_token', data: { chunk: 'hel' } }), null); + assert.equal(normalize('claude-agents', { type: 'text', text: 'thinking...' }), null); + assert.equal(normalize('openai-agents', null), null); // malformed input must not lose the run +}); + +test('an unknown framework falls back to the generic envelope', () => { + const normalized = normalize('some-new-framework-2027', { action: 'customers.delete', params: { recordsAffected: 900 } }); + assert.equal(normalized.detail.consequence, 'DATA_DELETED'); + assert.ok(supportedFrameworks().includes('generic')); +}); + +test('ingesting a run records proposals with the consequence they imply', () => { + const timeline = createTimeline({ objective: 'Pay invoice' }); + ingest('openai-agents', [ + { type: 'agent_start', agent: 'planner', input: 'Pay invoice INV-4471' }, + { type: 'handoff', from_agent: 'planner', to_agent: 'finance' }, + { type: 'token_usage', tokens: 812 }, + { type: 'tool_call', name: 'payments.transfer', arguments: { amount: 18000 }, agent: 'finance' }, + { type: 'tool_output', name: 'payments.transfer', agent: 'finance' } + ], timeline); + + const events = timeline.events.map((e) => e.event); + assert.deepEqual(events, ['AGENT_STARTED', 'AGENT_DELEGATED', 'ACTION_PROPOSED', 'CONSEQUENCE_DETECTED', 'EXECUTION_COMPLETED']); + assert.ok(replay(timeline).some((e) => e.line.includes('MONEY_MOVED (CRITICAL)'))); +}); diff --git a/packages/pctr/tests/boundary.test.mjs b/packages/pctr/tests/boundary.test.mjs new file mode 100644 index 0000000..ad98360 --- /dev/null +++ b/packages/pctr/tests/boundary.test.mjs @@ -0,0 +1,89 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +import { buildGraph } from '../src/graph.mjs'; +import { protect } from '../src/protect.mjs'; +import { issueAuthority } from '../src/authority.mjs'; +import { startBoundaryServer, remoteBoundary, createBoundary, persistentReplayStore } from '../src/boundary.mjs'; +import { generateKeyPair, exportPublic } from '../src/keys.mjs'; +import { verifyReceipt } from '../src/receipt.mjs'; + +const graph = () => buildGraph({ + principal: 'user:test', + agents: [{ id: 'finance', trust: 0.99, evidenceAgeSeconds: 5, authority: ['payments.*'], tools: ['payments'] }], + tools: [{ id: 'payments', actions: ['payments.transfer'] }], + actions: [{ id: 'payments.transfer', amount: 100 }], + policy: { requireApprovalAtOrAbove: 'CRITICAL' } +}); + +test('authority is verified out of process, by a boundary the agent does not control', async () => { + const { url, close } = await startBoundaryServer({ port: 0 }); + try { + const run = await protect(graph(), { action: 'payments.transfer', target: 'acct:1', params: { amount: 100 } }, + { boundary: remoteBoundary(url), execute: () => 'sent' }); + assert.equal(run.allowed, true); + assert.equal(run.receipt.verifier.boundary, url); + assert.equal(run.result.output, 'sent'); + } finally { + await close(); + } +}); + +test('an unreachable boundary denies execution; it never fails open', async () => { + const { url, close } = await startBoundaryServer({ port: 0 }); + await close(); // the boundary is now gone + const run = await protect(graph(), { action: 'payments.transfer', target: 'acct:1', params: { amount: 100 } }, + { boundary: remoteBoundary(url, { timeoutMs: 500 }), execute: () => 'sent' }); + assert.equal(run.allowed, false); + assert.ok(run.receipt.verifier.failures.some((f) => f.code === 'BOUNDARY_UNREACHABLE')); + assert.equal(run.result, null); // nothing executed +}); + +test('the boundary rejects an authority signed by a key it does not trust', async () => { + const trusted = generateKeyPair(); + const attacker = generateKeyPair(); + const { url, close } = await startBoundaryServer({ port: 0, boundary: createBoundary({ trustedKeyIds: [trusted.keyId] }) }); + try { + const forged = issueAuthority({ principal: 'user:test', action: 'payments.transfer', target: 'acct:attacker', params: { amount: 999999 }, consequence: 'MONEY_MOVED', keyPair: attacker }); + const boundary = remoteBoundary(url); + const decision = await boundary.verify(forged, { action: 'payments.transfer', target: 'acct:attacker', params: { amount: 999999 }, principal: 'user:test' }); + assert.equal(decision.allowed, false); + assert.ok(decision.failures.some((f) => f.code === 'UNTRUSTED_SIGNER')); + } finally { + await close(); + } +}); + +test('replay protection survives a boundary restart', async () => { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pctr-boundary-')); + const file = path.join(dir, 'spent-nonces.json'); + const key = generateKeyPair(); + const publicKey = exportPublic(key.publicKey); + const authority = issueAuthority({ principal: 'user:test', action: 'payments.transfer', target: 'acct:1', params: { amount: 100 }, consequence: 'MONEY_MOVED', keyPair: key }); + const execution = { action: 'payments.transfer', target: 'acct:1', params: { amount: 100 }, principal: 'user:test' }; + + const first = createBoundary({ publicKey, replayStore: persistentReplayStore(file) }); + assert.equal(first.verify(authority, execution).allowed, true); + + // A fresh process, reading the same spent-nonce state. + const restarted = createBoundary({ publicKey, replayStore: persistentReplayStore(file) }); + const second = restarted.verify(authority, execution); + assert.equal(second.allowed, false); + assert.ok(second.failures.some((f) => f.code === 'REPLAYED_AUTHORITY')); + fs.rmSync(dir, { recursive: true, force: true }); +}); + +test('the protected loop signs with the key it was given, end to end', async () => { + const key = generateKeyPair(); + const other = generateKeyPair(); + const run = await protect(graph(), { action: 'payments.transfer', target: 'acct:1', params: { amount: 100 } }, + { keyPair: key, publicKey: exportPublic(key.publicKey), execute: () => 'sent' }); + + assert.equal(run.allowed, true); + assert.equal(run.authority.keyId, key.keyId); + assert.equal(verifyReceipt(run.receipt, { publicKey: exportPublic(key.publicKey) }).valid, true); + assert.equal(verifyReceipt(run.receipt, { publicKey: exportPublic(other.publicKey) }).valid, false); +}); diff --git a/packages/pctr/tests/conformance.test.mjs b/packages/pctr/tests/conformance.test.mjs new file mode 100644 index 0000000..f58c07c --- /dev/null +++ b/packages/pctr/tests/conformance.test.mjs @@ -0,0 +1,64 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; + +// PCTR vendors the TTP primitives so that installing it pulls in nothing else. That is +// only safe while the two implementations agree exactly, so compare them here against +// the reference implementation in this repo. If this fails, PCTR has drifted from TTP. +import * as vendored from '../src/ttp.mjs'; +import { apply_decay as referenceDecay } from '../../../src/decay.mjs'; +import { verify_trust_route as referenceRoute } from '../../../src/trust_route.mjs'; +import { hashObj as referenceHash, canonicalize as referenceCanonicalize } from '../../../src/util.mjs'; + +test('canonicalization and hashing match the TTP reference exactly', () => { + const samples = [ + { b: 1, a: 2 }, + { nested: { z: [3, 2, 1], a: 'x' }, n: 0.1234567 }, + { list: [{ b: true, a: null }, 'text', 42] }, + { unicode: 'héllo → ✓', empty: {}, arr: [] }, + { big: 1e21, small: 0.0000001, negative: -12.5 } + ]; + for (const sample of samples) { + assert.equal(vendored.canonicalize(sample), referenceCanonicalize(sample)); + assert.equal(vendored.hashObj(sample), referenceHash(sample)); + } +}); + +test('trust decay matches the TTP reference across risk tiers and signals', () => { + const cases = []; + for (const riskTier of ['low', 'medium', 'high']) { + for (const elapsedSeconds of [0, 30, 900, 4200, 99999]) { + for (const initialTrust of [0, 0.5, 0.97, 1]) { + cases.push({ initialTrust, decayConstant: 0.00005, elapsedSeconds, riskTier, calculatedAt: '2026-01-01T00:00:00.000Z' }); + } + } + } + cases.push({ + initialTrust: 0.4, decayConstant: 0.0001, elapsedSeconds: 600, riskTier: 'medium', + activitySignals: [{ verified: true, weight: 0.2, recencyFactor: 0.5 }, { verified: false, weight: 0.9 }], + calculatedAt: '2026-01-01T00:00:00.000Z' + }); + for (const input of cases) { + assert.deepEqual(vendored.apply_decay(input), referenceDecay(input), JSON.stringify(input)); + } +}); + +test('trust route verification matches the TTP reference, including failure codes', () => { + const base = { + routeId: 'r1', maxHops: 6, decayPerHop: 0.05, minIntermediateTrust: 0.1, + sourceDomain: 'local', targetDomain: 'local', action: 'payments.transfer', resource: 'acct:1' + }; + const cases = [ + { ...base, hops: [{ subject: 'a', issuer: 'i', trustScore: 0.9 }] }, + { ...base, hops: [{ subject: 'a', issuer: 'i', trustScore: 0.9 }, { subject: 'b', issuer: 'i', trustScore: 0.8 }] }, + { ...base, hops: [] }, + { ...base, hops: [{ subject: '', issuer: 'i', trustScore: 0.9 }] }, + { ...base, hops: Array.from({ length: 9 }, (_, i) => ({ subject: `a${i}`, issuer: 'i', trustScore: 0.99 })) }, + { ...base, hops: [{ subject: 'a', issuer: 'rogue', trustScore: 0.9 }], routePolicy: { trustedIssuers: ['i'] } }, + { ...base, hops: [{ subject: 'a', issuer: 'i', trustScore: 0.9 }], routePolicy: { allowedActions: ['reports.read'] } }, + { ...base, hops: [{ subject: 'a', issuer: 'i', trustScore: 0.9 }], routePolicy: { allowedDomains: ['eu->us'], refs: ['policy-7'] } }, + { ...base, hops: [{ subject: 'a', issuer: 'i', trustScore: 0.2 }], minIntermediateTrust: 0.5 } + ]; + for (const input of cases) { + assert.deepEqual(vendored.verify_trust_route(input), referenceRoute(input), JSON.stringify(input.hops)); + } +}); diff --git a/packages/pctr/tests/discover.test.mjs b/packages/pctr/tests/discover.test.mjs new file mode 100644 index 0000000..bceeb45 --- /dev/null +++ b/packages/pctr/tests/discover.test.mjs @@ -0,0 +1,102 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +import { discover } from '../src/discover.mjs'; +import { classifyAction } from '../src/consequences.mjs'; +import { buildGraph, summarize } from '../src/graph.mjs'; + +function project(files) { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pctr-discover-')); + for (const [name, content] of Object.entries(files)) { + fs.mkdirSync(path.dirname(path.join(dir, name)), { recursive: true }); + fs.writeFileSync(path.join(dir, name), content); + } + return dir; +} + +test('tool actions are read out of source, not guessed', () => { + const dir = project({ + 'package.json': JSON.stringify({ dependencies: { '@modelcontextprotocol/sdk': '^1.0.0' } }), + 'src/server.mjs': ` + const server = new McpServer({ name: 'payments' }); + server.tool('payments.transfer', 'Move money', async () => ({})); + server.registerTool('customers.delete', 'Delete a customer', async () => ({})); + ` + }); + const { manifest } = discover(dir); + const actions = manifest.tools.flatMap((t) => t.actions); + assert.ok(actions.includes('payments.transfer')); + assert.ok(actions.includes('customers.delete')); + assert.ok(manifest.frameworks.includes('mcp')); + fs.rmSync(dir, { recursive: true, force: true }); +}); + +test('python tool and agent declarations are discovered with the right framework', () => { + const dir = project({ + 'requirements.txt': 'crewai\nlangchain\n', + 'agents/finance.py': ` +from crewai import Agent +from langchain.tools import tool + +finance = Agent(role="Finance Agent", goal="pay invoices") + +@tool +def send_invoice_email(to: str): + return True + ` + }); + const { manifest } = discover(dir); + const agent = manifest.agents.find((a) => a.id === 'finance-agent'); + assert.ok(agent, 'expected the declared CrewAI agent'); + assert.equal(agent.framework, 'crewai'); + assert.ok(manifest.tools.flatMap((t) => t.actions).includes('send_invoice_email')); + fs.rmSync(dir, { recursive: true, force: true }); +}); + +test('MCP server configuration is discovered as external tooling', () => { + const dir = project({ '.mcp.json': JSON.stringify({ mcpServers: { payments: { command: 'node', args: ['server.js'] } } }) }); + const { manifest } = discover(dir); + const tool = manifest.tools.find((t) => t.id === 'payments'); + assert.equal(tool.protocol, 'mcp'); + fs.rmSync(dir, { recursive: true, force: true }); +}); + +test('declared facts in an existing manifest override what discovery inferred', () => { + const dir = project({ + 'package.json': JSON.stringify({ dependencies: { openai: '^4.0.0' } }), + 'src/agent.mjs': 'export const agent = {};' + }); + const { manifest } = discover(dir, { declared: { principal: 'user:ops', agents: [{ id: 'agent', trust: 0.99, authority: ['payments.*'] }] } }); + const agent = manifest.agents.find((a) => a.id === 'agent'); + assert.equal(agent.trust, 0.99); + assert.deepEqual(agent.authority, ['payments.*']); + assert.equal(manifest.principal, 'user:ops'); + fs.rmSync(dir, { recursive: true, force: true }); +}); + +test('the verb decides the consequence, not the namespace it sits in', () => { + assert.equal(classifyAction('payments.status').consequence, 'DATA_READ'); + assert.equal(classifyAction('payments.transfer').consequence, 'MONEY_MOVED'); + assert.equal(classifyAction('customers.list').consequence, 'DATA_READ'); + assert.equal(classifyAction('customers.delete').consequence, 'DATA_DELETED'); + assert.equal(classifyAction('send_invoice_email').consequence, 'MESSAGE_SENT'); +}); + +test('a discovered project produces a usable graph without any hand editing', () => { + const dir = project({ + 'package.json': JSON.stringify({ dependencies: { '@modelcontextprotocol/sdk': '^1.0.0' } }), + 'src/ops-agent.mjs': ` + const server = new McpServer({ name: 'infra' }); + server.tool('cluster.deploy', 'Deploy', async () => ({})); + ` + }); + const { manifest } = discover(dir); + const graph = buildGraph(manifest); + const summary = summarize(graph); + assert.ok(summary.agents >= 1); + assert.ok(summary.protected >= 1, 'deploying to a cluster is a protected consequence'); + fs.rmSync(dir, { recursive: true, force: true }); +}); diff --git a/packages/pctr/tests/pctr.test.mjs b/packages/pctr/tests/pctr.test.mjs new file mode 100644 index 0000000..ef13261 --- /dev/null +++ b/packages/pctr/tests/pctr.test.mjs @@ -0,0 +1,268 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; + +import { classifyAction } from '../src/consequences.mjs'; +import { buildGraph, pathsToAction, reachableActions, protectedActions, summarize } from '../src/graph.mjs'; +import { previewConsequence, consequenceChanged } from '../src/twin.mjs'; +import { resolveRoute, reroute } from '../src/router.mjs'; +import { issueAuthority, verifyAuthority, createReplayStore } from '../src/authority.mjs'; +import { issueReceipt, verifyReceipt, verifyReceiptChain } from '../src/receipt.mjs'; +import { createTimeline, why, compare, fork, replay } from '../src/timeline.mjs'; +import { protect, simulate } from '../src/protect.mjs'; +import { generateKeyPair, exportPublic } from '../src/keys.mjs'; + +const manifest = () => ({ + principal: 'user:test', + agents: [ + { id: 'planner', trust: 0.98, evidenceAgeSeconds: 10, authority: ['*'], jurisdiction: 'eu', latencyMs: 50, tools: [], delegatesTo: ['finance-a', 'finance-d'] }, + { id: 'finance-a', trust: 0.97, evidenceAgeSeconds: 4200, authority: ['payments.*'], jurisdiction: 'eu', latencyMs: 10, tools: ['payments'] }, + { id: 'finance-d', trust: 0.97, evidenceAgeSeconds: 20, authority: ['payments.*'], jurisdiction: 'eu', latencyMs: 90, tools: ['payments'] } + ], + tools: [{ id: 'payments', protocol: 'mcp', actions: ['payments.transfer', 'reports.read'] }], + actions: [{ id: 'payments.transfer', amount: 1800 }], + policy: { requireApprovalAtOrAbove: 'CRITICAL', approvalThresholds: { amount: 5000 }, maxHops: 6, decayPerHop: 0.05 } +}); +const graph = () => buildGraph(manifest()); + +test('consequences are classified from what the action can cause', () => { + assert.equal(classifyAction('customers.delete').consequence, 'DATA_DELETED'); + assert.equal(classifyAction('payments.transfer').consequence, 'MONEY_MOVED'); + assert.equal(classifyAction('reports.read').protected, false); + // Scale raises severity even when the verb looks benign. + assert.equal(classifyAction('records.update', { recordsAffected: 5000 }).severity, 'CRITICAL'); +}); + +test('the graph maps consequences backward to every principal that can reach them', () => { + const g = graph(); + const paths = pathsToAction(g, 'payments.transfer'); + assert.equal(paths.length, 2); + for (const p of paths) assert.equal(p[0], 'user:test'); + assert.equal(reachableActions(g, 'planner').length, 4); + assert.equal(protectedActions(g)[0].id, 'payments.transfer'); + assert.equal(summarize(g).agents, 3); +}); + +test('consequence twin reports blast radius and recommends controls before execution', () => { + const p = previewConsequence(graph(), 'payments.transfer', { amount: 18000 }); + assert.equal(p.severity, 'CRITICAL'); + assert.equal(p.reversible, false); + assert.equal(p.financialExposure, 18000); + assert.equal(p.routesThatCanReachIt, 2); + assert.ok(p.recommendations.some((r) => r.includes('5,000'))); + assert.ok(consequenceChanged(previewConsequence(graph(), 'payments.transfer', { amount: 1800 }), p)); +}); + +test('routing removes stale evidence before it optimizes for latency', () => { + const r = resolveRoute(graph(), 'payments.transfer'); + assert.equal(r.selected.agents.at(-1), 'finance-d'); + const rejected = r.candidates.find((c) => c.agents.includes('finance-a')); + assert.ok(rejected.rejections.some((x) => x.code === 'STALE_EVIDENCE')); + // finance-a is the faster route and is still not selected: security is not traded for latency. + assert.ok(rejected.latencyMs < r.selected.latencyMs); +}); + +test('routing removes routes whose agents lack authority for the action', () => { + const m = manifest(); + m.agents[2].authority = ['reports.read']; + const r = resolveRoute(buildGraph(m), 'payments.transfer'); + assert.equal(r.selected, null); + assert.ok(r.candidates.flatMap((c) => c.rejections).some((x) => x.code === 'INSUFFICIENT_AUTHORITY')); +}); + +test('routing removes consequence-incompatible jurisdictions', () => { + const m = manifest(); + m.policy.jurisdictions = { MONEY_MOVED: ['us'] }; + const r = resolveRoute(buildGraph(m), 'payments.transfer'); + assert.equal(r.selected, null); + assert.ok(r.candidates.flatMap((c) => c.rejections).some((x) => x.code === 'JURISDICTION_INCOMPATIBLE')); +}); + +test('authority never silently expands during rerouting', () => { + const g = graph(); + const previous = resolveRoute(g, 'payments.transfer'); + const next = reroute(g, 'payments.transfer', previous, { severity: 'MEDIUM' }); + assert.equal(next.selected, null); + assert.equal(next.expansionBlocked, true); +}); + +test('authority binds the exact execution, not the credential', () => { + const g = graph(); + const route = resolveRoute(g, 'payments.transfer').selected; + const authority = issueAuthority({ + principal: 'user:test', action: 'payments.transfer', target: 'acct:9931', + params: { amount: 1800 }, consequence: 'MONEY_MOVED', route + }); + const ok = verifyAuthority(authority, { action: 'payments.transfer', target: 'acct:9931', params: { amount: 1800 }, principal: 'user:test' }); + assert.equal(ok.allowed, true); + + // Same valid authority, changed amount: possession is not authority. + const tampered = verifyAuthority(authority, { action: 'payments.transfer', target: 'acct:9931', params: { amount: 18000 }, principal: 'user:test' }); + assert.equal(tampered.allowed, false); + assert.ok(tampered.failures.some((f) => f.code === 'PARAMETER_MISMATCH')); + + // Same authority, different target. + const redirected = verifyAuthority(authority, { action: 'payments.transfer', target: 'acct:0001', params: { amount: 1800 }, principal: 'user:test' }); + assert.ok(redirected.failures.some((f) => f.code === 'TARGET_MISMATCH')); +}); + +test('authority expires and cannot be replayed', () => { + const authority = issueAuthority({ + principal: 'user:test', action: 'payments.transfer', target: 'acct:1', params: { amount: 1 }, + consequence: 'MONEY_MOVED', validForSeconds: 60, issuedAt: '2026-01-01T00:00:00.000Z' + }); + const execution = { action: 'payments.transfer', target: 'acct:1', params: { amount: 1 }, principal: 'user:test' }; + const expired = verifyAuthority(authority, execution, { now: '2026-01-01T00:05:00.000Z' }); + assert.ok(expired.failures.some((f) => f.code === 'AUTHORITY_EXPIRED')); + + const replayStore = createReplayStore(); + const at = '2026-01-01T00:00:30.000Z'; + assert.equal(verifyAuthority(authority, execution, { now: at, replayStore }).allowed, true); + const second = verifyAuthority(authority, execution, { now: at, replayStore }); + assert.ok(second.failures.some((f) => f.code === 'REPLAYED_AUTHORITY')); +}); + +test('tampered authority fails its binding check', () => { + const authority = issueAuthority({ principal: 'user:test', action: 'a', target: 't', params: {}, consequence: 'NONE' }); + const forged = { ...authority, action: 'payments.transfer' }; + const result = verifyAuthority(forged, { action: 'payments.transfer', target: 't', params: {}, principal: 'user:test' }); + assert.equal(result.allowed, false); + assert.ok(result.failures.some((f) => f.code === 'BINDING_HASH_MISMATCH')); +}); + +test('receipts are verifiable against the signing key and detect alteration', () => { + const receipt = issueReceipt({ + request: { action: 'payments.transfer', target: 'acct:1', params: { amount: 1800 } }, + preview: previewConsequence(graph(), 'payments.transfer', { amount: 1800 }), + route: resolveRoute(graph(), 'payments.transfer').selected, + authority: issueAuthority({ principal: 'user:test', action: 'payments.transfer', target: 'acct:1', params: { amount: 1800 }, consequence: 'MONEY_MOVED' }), + verification: { decision: 'EXECUTION_ALLOWED', failures: [] }, + result: { status: 'SUCCEEDED' } + }); + assert.equal(verifyReceipt(receipt).valid, true); + const altered = { ...receipt, requested: { ...receipt.requested, params: { amount: 18000 } } }; + assert.equal(verifyReceipt(altered).valid, false); +}); + +test('the full protected loop denies, then allows once approval is recorded', async () => { + const g = graph(); + const request = { action: 'payments.transfer', target: 'acct:9931', params: { amount: 18000 } }; + + const denied = await protect(g, request); + assert.equal(denied.allowed, false); + assert.ok(denied.receipt.verifier.failures.some((f) => f.code === 'APPROVAL_REQUIRED')); + assert.equal(denied.receipt.executed, null); + + const allowed = await protect(g, request, { approval: { by: 'maurice' }, execute: () => 'transferred' }); + assert.equal(allowed.allowed, true); + assert.equal(allowed.result.output, 'transferred'); + assert.equal(verifyReceipt(allowed.receipt).valid, true); + assert.equal(allowed.receipt.authorityBound.materialParams.amount, 18000); +}); + +test('simulation runs the whole loop without causing an effect', async () => { + let sideEffects = 0; + const run = await simulate(graph(), { action: 'payments.transfer', target: 'acct:1', params: { amount: 100 } }, + { approval: { by: 'maurice' }, execute: () => { sideEffects++; } }); + assert.equal(sideEffects, 0); + assert.equal(run.result.status, 'SIMULATED'); +}); + +test('a receipt can never record an execution that was not allowed', () => { + const receipt = issueReceipt({ + request: { action: 'payments.transfer', target: 'acct:1', params: {} }, + verification: { decision: 'EXECUTION_DENIED', failures: [{ code: 'APPROVAL_REQUIRED', message: 'no approval' }] }, + result: { status: 'SUCCEEDED' } + }); + const result = verifyReceipt(receipt); + assert.equal(result.valid, false); + assert.ok(result.failures.some((f) => f.code === 'EXECUTED_WITHOUT_AUTHORITY')); +}); + +test('receipt chains detect a removed or reordered receipt', () => { + const make = (prior) => issueReceipt({ + request: { action: 'reports.read', target: 'r', params: {} }, + verification: { decision: 'EXECUTION_ALLOWED', failures: [] }, priorReceiptHash: prior + }); + const first = make(null); + const second = make(first.receiptHash); + assert.equal(verifyReceiptChain([first, second]).valid, true); + assert.equal(verifyReceiptChain([second, first]).valid, false); +}); + +test('the time machine explains why deterministically from recorded causes', async () => { + const run = await protect(graph(), { action: 'payments.transfer', target: 'acct:1', params: { amount: 18000 } }); + const answer = why(run.timeline, 'EXECUTION_DENIED'); + assert.equal(answer.found, true); + assert.match(answer.answer, /human approval/i); + assert.ok(answer.because.some((b) => /MONEY_MOVED/.test(b))); + assert.ok(replay(run.timeline).length > 3); +}); + +test('runs can be forked and compared to see where behaviour diverged', async () => { + const request = { action: 'payments.transfer', target: 'acct:1', params: { amount: 18000 } }; + const denied = await protect(graph(), request, { timeline: createTimeline({ runId: 'a' }) }); + const allowed = await protect(graph(), request, { timeline: createTimeline({ runId: 'b' }), approval: { by: 'maurice' } }); + const diff = compare(denied.timeline, allowed.timeline); + assert.equal(diff.sameOutcome, false); + assert.ok(diff.divergedAt > 0); + assert.equal(fork(denied.timeline, { upTo: 'e2' }).events.length, 3); +}); + +test('only canonical security events can enter a timeline', () => { + const t = createTimeline(); + assert.throws(() => t.record('SOMETHING_FRAMEWORK_SPECIFIC', {}), /canonical/); +}); + +test('a receipt can be verified by someone who cannot mint one', () => { + const issuer = generateKeyPair(); + const attacker = generateKeyPair(); + const issuerPublicKey = exportPublic(issuer.publicKey); + + const genuine = issueReceipt({ + request: { action: 'payments.transfer', target: 'acct:1', params: { amount: 1800 } }, + verification: { decision: 'EXECUTION_ALLOWED', failures: [] }, result: { status: 'SUCCEEDED' }, keyPair: issuer + }); + const forged = issueReceipt({ + request: { action: 'payments.transfer', target: 'acct:attacker', params: { amount: 999999 } }, + verification: { decision: 'EXECUTION_ALLOWED', failures: [] }, result: { status: 'SUCCEEDED' }, keyPair: attacker + }); + + const checked = verifyReceipt(genuine, { publicKey: issuerPublicKey }); + assert.equal(checked.valid, true); + assert.equal(checked.signerVerified, true); + + // The forgery is internally consistent, so it must be rejected on the signer. + assert.equal(verifyReceipt(forged, { publicKey: issuerPublicKey }).valid, false); + assert.ok(verifyReceipt(forged, { trustedKeyIds: [issuer.keyId] }).failures.some((f) => f.code === 'UNTRUSTED_SIGNER')); +}); + +test('verifying without pinning a signer is reported as unpinned, not as proof', () => { + const receipt = issueReceipt({ + request: { action: 'reports.read', target: 'r', params: {} }, + verification: { decision: 'EXECUTION_ALLOWED', failures: [] }, keyPair: generateKeyPair() + }); + const result = verifyReceipt(receipt); + assert.equal(result.valid, true); + assert.equal(result.signerVerified, false); + assert.ok(result.warnings.some((w) => w.code === 'UNPINNED_KEY')); +}); + +test('a signature cannot be moved onto an authority signed by another key', () => { + const issuer = generateKeyPair(); + const attacker = generateKeyPair(); + const authority = issueAuthority({ principal: 'user:test', action: 'payments.transfer', target: 'acct:1', params: { amount: 1 }, consequence: 'MONEY_MOVED', keyPair: issuer }); + const swapped = { ...authority, signerPublicKey: exportPublic(attacker.publicKey) }; + const execution = { action: 'payments.transfer', target: 'acct:1', params: { amount: 1 }, principal: 'user:test' }; + assert.equal(verifyAuthority(swapped, execution).allowed, false); + assert.equal(verifyAuthority(authority, execution, { publicKey: exportPublic(issuer.publicKey) }).allowed, true); +}); + +test('material parameters change the consequence, and the consequence sets the trust bar', () => { + const g = graph(); + const small = previewConsequence(g, 'payments.transfer', { amount: 1800 }); + const large = previewConsequence(g, 'payments.transfer', { amount: 18000 }); + assert.equal(small.severity, 'HIGH'); + assert.equal(large.severity, 'CRITICAL'); + assert.equal(resolveRoute(g, 'payments.transfer', { severity: small.severity }).trustRequired, 0.75); + assert.equal(resolveRoute(g, 'payments.transfer', { severity: large.severity }).trustRequired, 0.9); +}); diff --git a/packages/pctr/tests/report.test.mjs b/packages/pctr/tests/report.test.mjs new file mode 100644 index 0000000..3576366 --- /dev/null +++ b/packages/pctr/tests/report.test.mjs @@ -0,0 +1,53 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { buildGraph } from '../src/graph.mjs'; +import { renderReport, badgeUrl } from '../src/report.mjs'; + +const graph = (overrides = {}) => buildGraph({ + principal: 'user:test', + agents: [{ id: 'support', trust: 0.8, evidenceAgeSeconds: 10, authority: ['customers.read'], tools: ['db'], delegatesTo: ['admin'] }, + { id: 'admin', trust: 0.95, evidenceAgeSeconds: 10, authority: ['customers.*'], tools: ['db'] }], + tools: [{ id: 'db', protocol: 'mcp', actions: ['customers.delete', 'customers.read'] }], + actions: [{ id: 'customers.delete', recordsAffected: 1842, connectedWorkflows: 4 }], + ...overrides +}); + +test('the shared report leads with the consequence, not with agent counts', () => { + const report = renderReport(graph()); + assert.match(report, /Highest priority: `customers\.delete`/); + assert.match(report, /\*\*data deleted\*\*/); + assert.match(report, /\*\*irreversible\*\*/); + assert.match(report, /1,842 records/); + assert.match(report, /CRITICAL/); +}); + +test('the report renders as valid Markdown a PR comment can hold', () => { + const report = renderReport(graph()); + assert.ok(report.startsWith('## '), 'starts with a heading'); + assert.equal((report.match(/```/g) ?? []).length % 2, 0, 'code fences are balanced'); + assert.equal((report.match(/
/g) ?? []).length, (report.match(/<\/details>/g) ?? []).length); + assert.ok(report.includes('Nothing left this machine.')); + assert.ok(report.length < 60_000, 'fits inside a GitHub comment'); +}); + +test('a clean project gets a green badge and an honest all-clear', () => { + const clean = buildGraph({ + principal: 'user:test', + agents: [{ id: 'reader', trust: 0.9, authority: ['reports.read'], tools: ['reports'] }], + tools: [{ id: 'reports', actions: ['reports.read'] }] + }); + const report = renderReport(clean); + assert.match(report, /None of them can cause a protected consequence/); + assert.match(badgeUrl(clean), /brightgreen/); +}); + +test('an empty scan says so rather than inventing findings', () => { + const empty = buildGraph({ principal: 'user:test', agents: [], tools: [] }); + const report = renderReport(empty); + assert.match(report, /found no agents to scan/); + assert.doesNotMatch(report, /Highest priority/); +}); + +test('the badge reflects what was actually found', () => { + assert.match(badgeUrl(graph()), /1%20critical%20consequence-critical/); +}); diff --git a/packages/policy-engine/README.md b/packages/policy-engine/README.md deleted file mode 100644 index aa28246..0000000 --- a/packages/policy-engine/README.md +++ /dev/null @@ -1 +0,0 @@ -# policy-engine diff --git a/packages/proof-engine/README.md b/packages/proof-engine/README.md deleted file mode 100644 index e58156e..0000000 --- a/packages/proof-engine/README.md +++ /dev/null @@ -1 +0,0 @@ -# proof-engine diff --git a/packages/receipt-sdk/README.md b/packages/receipt-sdk/README.md deleted file mode 100644 index fbfeb30..0000000 --- a/packages/receipt-sdk/README.md +++ /dev/null @@ -1 +0,0 @@ -# receipt-sdk diff --git a/packages/shared-crypto/README.md b/packages/shared-crypto/README.md deleted file mode 100644 index 909ced4..0000000 --- a/packages/shared-crypto/README.md +++ /dev/null @@ -1 +0,0 @@ -# shared-crypto diff --git a/packages/shared-observability/README.md b/packages/shared-observability/README.md deleted file mode 100644 index 266335b..0000000 --- a/packages/shared-observability/README.md +++ /dev/null @@ -1 +0,0 @@ -# shared-observability diff --git a/packages/trust-decay/README.md b/packages/trust-decay/README.md deleted file mode 100644 index d6dbce3..0000000 --- a/packages/trust-decay/README.md +++ /dev/null @@ -1 +0,0 @@ -# trust-decay diff --git a/packages/ttp-core/README.md b/packages/ttp-core/README.md deleted file mode 100644 index aa32eca..0000000 --- a/packages/ttp-core/README.md +++ /dev/null @@ -1 +0,0 @@ -# ttp-core diff --git a/protocol/spec.md b/protocol/spec.md index 8f6ccba..1759782 100644 --- a/protocol/spec.md +++ b/protocol/spec.md @@ -15,5 +15,5 @@ For the current scope and grammar, see: - [`../README.md`](../README.md) - [`../SPECIFICATION.md`](../SPECIFICATION.md) -- [`../MVP.md`](../MVP.md) +- [`../docs/MVP.md`](../MVP.md) - [`../THREAT_MODEL.md`](../THREAT_MODEL.md) diff --git a/reference-implementations/trust-authority/package-lock.json b/reference-implementations/trust-authority/package-lock.json new file mode 100644 index 0000000..cc35b1b --- /dev/null +++ b/reference-implementations/trust-authority/package-lock.json @@ -0,0 +1,5067 @@ +{ + "name": "@blocksifr/ttp-trust-authority", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@blocksifr/ttp-trust-authority", + "version": "0.1.0", + "dependencies": { + "@noble/ed25519": "^2.0.0", + "@noble/hashes": "^1.8.0", + "ajv": "^8.12.0", + "express": "^4.18.2", + "jose": "^5.2.0", + "uuid": "^11.1.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/jest": "^29.5.14", + "@types/node": "^20.10.0", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.2", + "typescript": "^5.3.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/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/@babel/core/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/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/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/@babel/traverse/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/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@noble/ed25519": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.3.0.tgz", + "integrity": "sha512-M7dvXL2B92/M7dw9+gzuydL8qn/jiqNHaoR3Q+cb1q1GHV7uwE17WCyFMG+Y+TZb5izcaXk5TdJRrDUxHXL78A==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.12", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.12.tgz", + "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.13.tgz", + "integrity": "sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.23", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.23.tgz", + "integrity": "sha512-le521dGVfxM7yRX0EikCoSz+rOK+hHzdDt/E7mG1jOJB/6WAAUuwVroLwaB7ApaUsz5Q0kFlDXLSA9MheUIfRQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.8", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz", + "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.16.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "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/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.428", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.428.tgz", + "integrity": "sha512-1JxbaFJj1bRKurj1uY3l4xxpU9kOUAUjcIgApj0qu1Pao5GhoIWI8iL0BeMYJ2njig1hBx0A7eKD9VGjH9wlHw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.3.tgz", + "integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.13", + "proxy-addr": "~2.0.7", + "qs": "~6.16.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.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-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "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-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/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/istanbul-lib-source-maps/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/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "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": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "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/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.55", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.55.tgz", + "integrity": "sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "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/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "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/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "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/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/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==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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/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/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-jest": { + "version": "29.4.12", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.12.tgz", + "integrity": "sha512-Ov6ClY53Fflh6BGAnY2DlTq1hYDrTycz2PVTXBWFW2CU+9zrEqAp9fWdGXl42EXO5RLSFAcAZ2JFKbP+zBTFfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.8.5", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", + "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "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/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/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "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/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/reference-implementations/trust-authority/package.json b/reference-implementations/trust-authority/package.json index fd5534d..c9123f1 100644 --- a/reference-implementations/trust-authority/package.json +++ b/reference-implementations/trust-authority/package.json @@ -1,7 +1,7 @@ { "name": "@blocksifr/ttp-trust-authority", "version": "0.1.0", - "description": "TTP Reference Trust Authority — aggregates behavioral receipts and issues trust tokens", + "description": "TTP Reference Trust Authority \u2014 aggregates behavioral receipts and issues trust tokens", "main": "dist/index.js", "scripts": { "build": "tsc", @@ -16,13 +16,12 @@ "@noble/ed25519": "^2.0.0", "@noble/hashes": "^1.8.0", "jose": "^5.2.0", - "uuid": "^9.0.0", + "uuid": "^11.1.0", "ajv": "^8.12.0" }, "devDependencies": { "@types/express": "^4.17.21", "@types/jest": "^29.5.14", - "@types/uuid": "^9.0.7", "@types/node": "^20.10.0", "typescript": "^5.3.0", "ts-node": "^10.9.2", diff --git a/sdk/typescript/package-lock.json b/sdk/typescript/package-lock.json new file mode 100644 index 0000000..a6de97f --- /dev/null +++ b/sdk/typescript/package-lock.json @@ -0,0 +1,4005 @@ +{ + "name": "@blocksifr/ttp-sdk", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@blocksifr/ttp-sdk", + "version": "0.1.0", + "license": "Apache-2.0", + "dependencies": { + "@noble/ed25519": "^2.0.0", + "jose": "^5.2.0", + "uuid": "^11.1.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.10.0", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "typescript": "^5.3.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "express": "^4.x" + }, + "peerDependenciesMeta": { + "express": { + "optional": true + } + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@noble/ed25519": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.3.0.tgz", + "integrity": "sha512-M7dvXL2B92/M7dw9+gzuydL8qn/jiqNHaoR3Q+cb1q1GHV7uwE17WCyFMG+Y+TZb5izcaXk5TdJRrDUxHXL78A==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.12", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.12.tgz", + "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.23", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.23.tgz", + "integrity": "sha512-le521dGVfxM7yRX0EikCoSz+rOK+hHzdDt/E7mG1jOJB/6WAAUuwVroLwaB7ApaUsz5Q0kFlDXLSA9MheUIfRQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "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/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "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/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.428", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.428.tgz", + "integrity": "sha512-1JxbaFJj1bRKurj1uY3l4xxpU9kOUAUjcIgApj0qu1Pao5GhoIWI8iL0BeMYJ2njig1hBx0A7eKD9VGjH9wlHw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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/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/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.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-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "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-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "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": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "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/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.55", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.55.tgz", + "integrity": "sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "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-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "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/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "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": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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/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/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.4.12", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.12.tgz", + "integrity": "sha512-Ov6ClY53Fflh6BGAnY2DlTq1hYDrTycz2PVTXBWFW2CU+9zrEqAp9fWdGXl42EXO5RLSFAcAZ2JFKbP+zBTFfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.8.5", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", + "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "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/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/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "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/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/sdk/typescript/package.json b/sdk/typescript/package.json index 62caaad..a8f6314 100644 --- a/sdk/typescript/package.json +++ b/sdk/typescript/package.json @@ -19,12 +19,11 @@ "dependencies": { "@noble/ed25519": "^2.0.0", "jose": "^5.2.0", - "uuid": "^9.0.0" + "uuid": "^11.1.0" }, "devDependencies": { "@types/express": "^4.17.21", "@types/node": "^20.10.0", - "@types/uuid": "^9.0.7", "typescript": "^5.3.0", "ts-jest": "^29.1.1", "jest": "^29.7.0" @@ -33,13 +32,20 @@ "express": "^4.x" }, "peerDependenciesMeta": { - "express": { "optional": true } + "express": { + "optional": true + } }, "engines": { "node": ">=18.0.0" }, "keywords": [ - "trust", "ai-agents", "behavioral-trust", "security", "ttp", "zero-trust" + "trust", + "ai-agents", + "behavioral-trust", + "security", + "ttp", + "zero-trust" ], "license": "Apache-2.0" } diff --git a/src/index.mjs b/src/index.mjs index fb016aa..03809a3 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -1 +1 @@ -export * from './trust_threshold.mjs';export * from './attestation.mjs';export * from './decay.mjs';export * from './delegation.mjs';export * from './trust_route.mjs';export * from './trust_proof.mjs';export * from './transfer.mjs'; +export * from './trust_threshold.mjs';export * from './attestation.mjs';export * from './decay.mjs';export * from './delegation.mjs';export * from './trust_route.mjs';export * from './trust_proof.mjs';export * from './transfer.mjs';export * from './isnad.mjs'; diff --git a/src/isnad.mjs b/src/isnad.mjs new file mode 100644 index 0000000..c9d5651 --- /dev/null +++ b/src/isnad.mjs @@ -0,0 +1,39 @@ +import {failure} from './errors.mjs';import {clamp01,hashObj} from './util.mjs'; +// Isnad-chain trust: verifies a chain of trust transmission ("isnad") from a trusted +// root authority down to a terminal subject. A chain is sound when it is continuous +// (muttasil — every link's issuer is the prior link's subject), rooted at the trusted +// source, every link is individually valid, and trust attenuates along the chain. +const ATTENUATION=['product','min','none']; +export function verify_isnad_chain(i){ + const r=[]; + const links=i.links||[]; + const action=i.requestedAction; + const minLinkTrust=i.minLinkTrust??0; + const trusted=i.trustedAuthorities; + const attenuation=ATTENUATION.includes(i.attenuation)?i.attenuation:'product'; + if(!links.length)r.push(failure('ISNAD_BROKEN_LINK','empty chain')); + if(i.maxLength!=null&&links.length>i.maxLength)r.push(failure('ISNAD_TOO_LONG','chain exceeds max length')); + if(links.length&&i.rootAuthority&&links[0].issuer!==i.rootAuthority)r.push(failure('ISNAD_ROOT_MISMATCH','chain does not originate at root authority')); + let eff=1,minAmt=1,weakest=null,prevAmt=Infinity; + for(let idx=0;idx0&&l.issuer!==links[idx-1].subject)r.push(failure('ISNAD_BROKEN_LINK',`gap between link ${idx-1} and ${idx}`)); + if(new Date(l.expiresAt)!p))r.push(failure('INVALID_INPUT',`link ${idx} invalid proof ref`)); + const amt=l.amount; + if(typeof amt!=='number'||amt<0||amt>1)r.push(failure('TRANSFER_INVALID',`link ${idx} amount invalid`)); + else{if(amtprevAmt)r.push(failure('ISNAD_TRUST_AMPLIFIED',`link ${idx} amplifies trust`)); + prevAmt=amt;eff*=amt;if(amtf.code==='ISNAD_BROKEN_LINK'); + const terminalSubject=links.length?links[links.length-1].subject:undefined; + const envelope={type:'IsnadChainProof',chainId:i.chainId,rootAuthority:i.rootAuthority,terminalSubject,action,length:links.length,continuous,attenuation,effectiveTrust,weakestLink:weakest}; + return {...envelope,chainHash:hashObj({...envelope,links}),valid:r.length===0,failureReasons:r}; +} diff --git a/src/types.mjs b/src/types.mjs index ca31edf..06e03a3 100644 --- a/src/types.mjs +++ b/src/types.mjs @@ -1,4 +1,4 @@ export const PROOF_MODES = ["plain", "signed", "zk-placeholder", "zk"]; export const FAILURE_CODES = [ - "INVALID_INPUT","INVALID_TRUST_SCORE","INVALID_THRESHOLD","INVALID_SUBJECT","INVALID_ISSUER","EXPIRED_ATTESTATION","STALE_ATTESTATION","TRUST_BELOW_THRESHOLD","DELEGATION_EXPIRED","DELEGATION_SCOPE_VIOLATION","ROUTE_INVALID","ROUTE_TOO_LONG","ROUTE_POLICY_VIOLATION","TRANSFER_INVALID","INSUFFICIENT_SENDER_TRUST","PROOF_HASH_MISMATCH","SIGNATURE_INVALID","UNSUPPORTED_PROOF_MODE" + "INVALID_INPUT","INVALID_TRUST_SCORE","INVALID_THRESHOLD","INVALID_SUBJECT","INVALID_ISSUER","EXPIRED_ATTESTATION","STALE_ATTESTATION","TRUST_BELOW_THRESHOLD","DELEGATION_EXPIRED","DELEGATION_SCOPE_VIOLATION","ROUTE_INVALID","ROUTE_TOO_LONG","ROUTE_POLICY_VIOLATION","TRANSFER_INVALID","INSUFFICIENT_SENDER_TRUST","PROOF_HASH_MISMATCH","SIGNATURE_INVALID","UNSUPPORTED_PROOF_MODE","ISNAD_BROKEN_LINK","ISNAD_ROOT_MISMATCH","ISNAD_WEAK_LINK","ISNAD_LINK_EXPIRED","ISNAD_SCOPE_VIOLATION","ISNAD_UNTRUSTED_NARRATOR","ISNAD_TOO_LONG","ISNAD_TRUST_AMPLIFIED" ]; diff --git a/tests/isnad.test.mjs b/tests/isnad.test.mjs new file mode 100644 index 0000000..19cbd62 --- /dev/null +++ b/tests/isnad.test.mjs @@ -0,0 +1,20 @@ +import test from 'node:test';import assert from 'node:assert/strict';import {verify_isnad_chain} from '../src/index.mjs'; +const link=(issuer,subject,amount,extra={})=>({issuer,subject,amount,scope:['invoice.write'],expiresAt:'2026-12-01T00:00:00Z',proofRefs:['p1'],transferable:true,...extra}); +const base={chainId:'isnad-1',rootAuthority:'root:cfo',requestedAction:'invoice.write',currentTime:'2026-06-25T00:00:00Z',minLinkTrust:0.1,maxLength:5, + links:[link('root:cfo','agent:approver',0.8),link('agent:approver','agent:invoice-bot',0.5)]}; + +test('continuous rooted chain is valid',()=>{const o=verify_isnad_chain(base);assert.equal(o.valid,true);assert.equal(o.continuous,true);assert.equal(o.terminalSubject,'agent:invoice-bot');}); +test('product attenuation multiplies link trust',()=>assert.equal(verify_isnad_chain(base).effectiveTrust,0.4)); +test('weakest link is identified',()=>assert.equal(verify_isnad_chain(base).weakestLink.index,1)); +test('min attenuation grades by weakest link',()=>assert.equal(verify_isnad_chain({...base,attenuation:'min'}).effectiveTrust,0.5)); +test('empty chain is broken',()=>{const o=verify_isnad_chain({...base,links:[]});assert.equal(o.valid,false);assert.equal(o.continuous,false);}); +test('root mismatch fails',()=>assert.equal(verify_isnad_chain({...base,rootAuthority:'root:other'}).valid,false)); +test('gap breaks continuity',()=>{const o=verify_isnad_chain({...base,links:[link('root:cfo','agent:approver',0.8),link('agent:rogue','agent:invoice-bot',0.5)]});assert.equal(o.valid,false);assert.equal(o.continuous,false);}); +test('expired link fails',()=>assert.equal(verify_isnad_chain({...base,links:[link('root:cfo','agent:approver',0.8,{expiresAt:'2026-01-01T00:00:00Z'}),base.links[1]]}).valid,false)); +test('out of scope link fails',()=>assert.equal(verify_isnad_chain({...base,requestedAction:'invoice.delete'}).valid,false)); +test('weak link below minimum fails',()=>assert.equal(verify_isnad_chain({...base,minLinkTrust:0.6}).valid,false)); +test('untrusted narrator fails',()=>assert.equal(verify_isnad_chain({...base,trustedAuthorities:['root:cfo']}).valid,false)); +test('non-transferable intermediate breaks chain',()=>assert.equal(verify_isnad_chain({...base,links:[link('root:cfo','agent:approver',0.8,{transferable:false}),base.links[1]]}).valid,false)); +test('chain exceeding max length fails',()=>assert.equal(verify_isnad_chain({...base,maxLength:1}).valid,false)); +test('monotonic enforcement rejects amplification',()=>assert.equal(verify_isnad_chain({...base,enforceMonotonic:true,links:[link('root:cfo','agent:approver',0.4),link('agent:approver','agent:invoice-bot',0.9)]}).valid,false)); +test('chain hash is deterministic',()=>assert.equal(verify_isnad_chain(base).chainHash,verify_isnad_chain(base).chainHash)); diff --git a/ttp-language b/ttp-language deleted file mode 100644 index a7a14a9..0000000 --- a/ttp-language +++ /dev/null @@ -1,138 +0,0 @@ -# TTP Language and Semantics (v1.0) - -This document defines the **practical language of TTP** as implemented in this repository: claims, fields, API contracts, and policy semantics. - -> Clarification: TTP in this repo is a **protocol + runtime verification model**, not a standalone programming language/compiler. - ---- - -## 1) Scope - -TTP expresses trust through three core artifacts: - -1. **Behavioral Receipts** (signed evidence from issuers) -2. **Trust Tokens** (short-lived JWTs from Trust Authority) -3. **Verification Policies** (service-side checks at execution time) - ---- - -## 2) Receipt Semantics - -A behavioral receipt communicates: who observed what behavior, in which domain, at what time, with what score. - -Canonical fields: - -- `ttp_version` -- `receipt_id` -- `agent_id` -- `issuer_id` -- `event_type` -- `event_data` -- `domain` -- `timestamp` -- `score` (0.0–1.0) -- `signature` - -Normative schema: `protocol/schemas/receipt.schema.json`. - ---- - -## 3) Trust Token Semantics - -A trust token is a short-lived JWT representing current behavioral trust for an agent in a domain. - -Core claims: - -- `sub` (agent identity) -- `ttp_domain` -- `ttp_score` -- `ttp_issuer_count` -- `iat` / `exp` -- `jti` - -Normative schema: `protocol/schemas/trust-token.schema.json`. - ---- - -## 4) Policy Evaluation Language (Verifier Side) - -TTP policy evaluation is deterministic and typically checks: - -1. Signature validity -2. Token freshness (`iat`, `exp`, skew) -3. Domain match -4. Minimum trust threshold (`minScore`) -5. Optional issuer diversity constraints -6. Optional replay checks (`jti`) - -In AGT/OPA ecosystems, verified token claims can be mapped into policy input (e.g. `input.ttp`). - ---- - -## 5) Trust Domain Language - -Domains are explicit trust boundaries (e.g. `retention`, `prod-change`, `financial`). - -Rules of use: - -- Trust is domain-scoped. -- Domain trust should not be reused across unrelated action classes by default. -- Score thresholds should be documented by domain risk level. - ---- - -## 6) Operator Semantics - -The Trust Authority reference implementation includes admin semantics for lifecycle control: - -- agent registration -- issuer registration -- status inspection -- quarantine / lift quarantine -- block -- trust provisioning -- agent registry listing (`GET /v1/admin/agents` with optional metrics) - -These semantics support operational trust governance without changing core protocol fields. - ---- - -## 7) Platform Alignment Checklist - -This document is considered aligned when the following remain true: - -- receipt field names/types match `protocol/schemas/receipt.schema.json` -- trust token claims match `protocol/schemas/trust-token.schema.json` -- trust authority admin semantics reflect implemented routes in `reference-implementations/trust-authority/src/routes.ts` -- verifier guidance aligns with current SDK verification behavior and integration docs -- security assumptions here do not conflict with `docs/security.md` - ---- - -## 8) What TTP Language Is Not (in this repo) - -Not currently part of the implemented protocol surface: - -- custom DSL compiler/runtime -- on-chain smart-contract execution model -- built-in zero-knowledge proof VM - -Those can be explored as future ecosystem extensions, but are not v1 protocol requirements. - ---- - -## 9) Versioning and Compatibility - -- `ttp_version` gates protocol interpretation. -- Field semantics are backward-compatible within major version unless otherwise documented. -- New optional claims/fields must not break existing verifiers. - ---- - -## 10) Reference Pointers - -- Protocol spec: `protocol/spec.md` -- Aggregation algorithm: `protocol/aggregation-spec.md` -- Integration details: `docs/integration-guide.md` -- Security model: `docs/security.md` -- Public launch checklist: `docs/public-readiness.md` diff --git a/website/.vitepress/config.mjs b/website/.vitepress/config.mjs new file mode 100644 index 0000000..2e10943 --- /dev/null +++ b/website/.vitepress/config.mjs @@ -0,0 +1,91 @@ +import { defineConfig } from "vitepress"; +import container from "markdown-it-container"; + +// Custom protocol callouts: ::: authority / attestation / decay / receipt / step-up / deny +const CALLOUTS = { + authority: "AUTHORITY", + attestation: "ATTESTATION", + decay: "DECAY", + receipt: "RECEIPT", + "step-up": "STEP-UP", + deny: "DENY", +}; + +function calloutPlugin(md) { + for (const [name, label] of Object.entries(CALLOUTS)) { + md.use(container, name, { + render(tokens, idx) { + const token = tokens[idx]; + if (token.nesting === 1) { + const title = token.info.trim().slice(name.length).trim() || label; + return `

${md.utils.escapeHtml(title)}

\n`; + } + return "
\n"; + }, + }); + } +} + +export default defineConfig({ + title: "Trust Transfer Protocol", + description: "Trust-Before-Execution for AI agents and non-human identities.", + appearance: "dark", + cleanUrls: true, + ignoreDeadLinks: true, + head: [ + ["meta", { name: "theme-color", content: "#0A0A0F" }], + ["meta", { property: "og:image", content: "/social-preview.png" }], + ], + markdown: { + theme: { light: "github-dark", dark: "github-dark" }, + languageAlias: { ttp: "hcl" }, + config: (md) => calloutPlugin(md), + }, + themeConfig: { + logo: "/repo-icon.svg", + nav: [ + { text: "Start", link: "/guide/start" }, + { text: "Concepts", link: "/guide/concepts" }, + { text: "Runtime", link: "/guide/runtime" }, + { text: "Receipts", link: "/guide/receipts" }, + { text: "Spec", link: "https://github.com/BlockSiFr/ttp-protocol/blob/main/SPECIFICATION.md" }, + ], + sidebar: [ + { + text: "Start", + items: [ + { text: "Overview", link: "/guide/start" }, + { text: "Quickstart", link: "/guide/start#quickstart" }, + ], + }, + { + text: "Protocol", + items: [ + { text: "Concepts", link: "/guide/concepts" }, + { text: "Language Specification", link: "/guide/language" }, + { text: "Runtime Authority", link: "/guide/runtime" }, + { text: "Trust Decay", link: "/guide/decay" }, + { text: "Proofs", link: "/guide/proofs" }, + { text: "Execution Receipts", link: "/guide/receipts" }, + ], + }, + { + text: "Build", + items: [ + { text: "Examples", link: "/guide/examples" }, + { text: "Integrations", link: "/guide/integrations" }, + { text: "RFCs", link: "/guide/rfcs" }, + { text: "Contributing", link: "/guide/contributing" }, + { text: "Security", link: "/guide/security" }, + ], + }, + ], + socialLinks: [ + { icon: "github", link: "https://github.com/BlockSiFr/ttp-protocol" }, + ], + footer: { + message: "Apache-2.0 · A BlockSiFr open protocol", + copyright: "Trust-Before-Execution for autonomous systems", + }, + }, +}); diff --git a/website/.vitepress/theme/custom.css b/website/.vitepress/theme/custom.css new file mode 100644 index 0000000..3db0e13 --- /dev/null +++ b/website/.vitepress/theme/custom.css @@ -0,0 +1,110 @@ +/* TTP docs theme — dark, precise, cryptographic. Maps the brand token system + onto VitePress. Brand fonts load as webfonts (allowed on the docs site). */ +@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap"); + +:root { + --vp-font-family-base: "Inter", system-ui, sans-serif; + --vp-font-family-mono: "JetBrains Mono", ui-monospace, monospace; + + --ttp-black: #0a0a0f; + --ttp-panel: #12141c; + --ttp-card: #101521; + --ttp-border: #1e2430; + --ttp-cyan: #00d4ff; + --ttp-azure: #0066cc; + --ttp-white: #f0f2f5; + --ttp-steel: #8892a0; + --ttp-green: #00e676; + --ttp-teal: #00b8a9; + --ttp-amber: #ffb300; + --ttp-red: #ff3d00; + + /* brand accent */ + --vp-c-brand-1: var(--ttp-cyan); + --vp-c-brand-2: #5fe3ff; + --vp-c-brand-3: var(--ttp-azure); + --vp-c-brand-soft: rgba(0, 212, 255, 0.12); +} + +.dark { + --vp-c-bg: var(--ttp-black); + --vp-c-bg-alt: #0b0d13; + --vp-c-bg-soft: var(--ttp-card); + --vp-c-bg-elv: var(--ttp-panel); + --vp-c-border: var(--ttp-border); + --vp-c-divider: var(--ttp-border); + --vp-c-text-1: var(--ttp-white); + --vp-c-text-2: #c2cad6; + --vp-c-text-3: var(--ttp-steel); + + --vp-code-block-bg: #0a0a0f; + --vp-code-bg: rgba(0, 212, 255, 0.07); + --vp-code-color: #c2cad6; +} + +/* Display type */ +h1, h2, h3, .VPHero .name, .VPHero .text { + font-family: "Space Grotesk", "Inter", system-ui, sans-serif; + letter-spacing: -0.02em; +} +h2 { + border-top: 1px solid var(--ttp-border); + padding-top: 1.5rem; +} + +/* Hero */ +.VPHero .name { color: var(--ttp-white); } +.VPHero .text { color: var(--ttp-cyan); } +.VPHero .tagline { color: var(--ttp-steel); } +.VPHero .image-bg { display: none; } + +/* Feature / path cards */ +.VPFeature { + background: var(--ttp-card); + border: 1px solid var(--ttp-border); + transition: border-color 0.15s ease; +} +.VPFeature:hover { border-color: rgba(0, 212, 255, 0.4); } +.VPFeature .icon { + background: transparent !important; + font-size: 22px; +} + +/* Code blocks — thin border, mono */ +div[class*="language-"] { + border: 1px solid var(--ttp-border); + border-radius: 10px; + background: #0a0a0f; +} +div[class*="language-"] code { font-size: 13px; } + +/* Custom protocol callouts */ +.ttp-callout { + margin: 18px 0; + padding: 14px 18px 14px 16px; + border: 1px solid var(--ttp-border); + border-left-width: 3px; + border-radius: 8px; + background: var(--ttp-card); +} +.ttp-callout-title { + margin: 0 0 6px; + font-family: "JetBrains Mono", ui-monospace, monospace; + font-size: 11px; + letter-spacing: 2px; + font-weight: 500; +} +.ttp-callout :last-child { margin-bottom: 0; } + +.ttp-authority { border-left-color: var(--ttp-azure); } +.ttp-authority .ttp-callout-title { color: var(--ttp-azure); } +.ttp-attestation { border-left-color: var(--ttp-teal); } +.ttp-attestation .ttp-callout-title { color: var(--ttp-teal); } +.ttp-decay { border-left-color: var(--ttp-cyan); } +.ttp-decay .ttp-callout-title { color: var(--ttp-cyan); } +.ttp-receipt { border-left-color: var(--ttp-green); } +.ttp-receipt .ttp-callout-title { color: var(--ttp-green); } +.ttp-step-up { border-left-color: var(--ttp-amber); } +.ttp-step-up .ttp-callout-title { color: var(--ttp-amber); } +.ttp-deny { border-left-color: var(--ttp-red); } +.ttp-deny .ttp-callout-title { color: var(--ttp-red); } diff --git a/website/.vitepress/theme/index.mjs b/website/.vitepress/theme/index.mjs new file mode 100644 index 0000000..c495bc1 --- /dev/null +++ b/website/.vitepress/theme/index.mjs @@ -0,0 +1,4 @@ +import DefaultTheme from "vitepress/theme"; +import "./custom.css"; + +export default DefaultTheme; diff --git a/website/guide/concepts.md b/website/guide/concepts.md new file mode 100644 index 0000000..d139361 --- /dev/null +++ b/website/guide/concepts.md @@ -0,0 +1,45 @@ +# Protocol Concepts + +TTP is built from a small set of primitives that compose into a verifiable +trust decision. + +

+ Protocol loop +

+ +## Primitives + +| Primitive | Meaning | +| --- | --- | +| **Subject** | The agent, service account, pipeline, API client, or workflow being evaluated. | +| **TrustClaim** | A scoped statement that a Subject holds a trust score, issued by a trust issuer. | +| **AuthorityGrant** | A bounded right relied on only when trust, freshness, and scope are satisfied. | +| **Attestation** | Fresh evidence that the subject or runtime context remains valid. | +| **TrustDecay** | Time-based weakening of trust when fresh evidence is absent. | +| **Delegation** | Bounded transfer of trust or authority from one subject to another. | +| **IsnadChain** | A verifiable chain of trust transmission from a rooted authority to the actor. | +| **TrustProof** | A structured proof that trust conditions were evaluated and satisfied. | +| **ExecutionReceipt** | A signed proof object recording the decision and its basis. | + +::: authority AUTHORITY +An `AuthorityGrant` is never trusted on its own. It is only relied on when the +current trust score clears the policy threshold, the grant is in scope, and its +evidence is fresh. +::: + +::: attestation ATTESTATION +An `Attestation` is fresh evidence — a signed claim that the subject, code, +workload, or runtime is still valid. Attestations recharge trust. +::: + +::: decay DECAY +Trust is not permanent. Without new attestations it decays through +`active → degraded → warning → critical`. See [Trust Decay](/guide/decay). +::: + +## How they compose + +A request carries an identity. TTP gathers trust claims and attestations, +applies decay, and produces a `TrustProof`. A runtime authority gate combines +the proof with an `AuthorityGrant` and emits a decision plus an +`ExecutionReceipt`. diff --git a/website/guide/contributing.md b/website/guide/contributing.md new file mode 100644 index 0000000..1e2a11b --- /dev/null +++ b/website/guide/contributing.md @@ -0,0 +1,24 @@ +# Contributing + +From a first issue to owning a protocol surface — the path is open and +intentional. + +

+ Contributor path +

+ +| Step | Do this | +| --- | --- | +| Start Here | Read the README and [Concepts](/guide/concepts). | +| Good First Issue | Pick a small, scoped task labeled `status: good first issue`. | +| Protocol RFC | Propose a change via an [RFC](/guide/rfcs). | +| Runtime Implementation | Build enforcement at a real surface. | +| Docs + Examples | Teach the next developer. | +| Maintainer Path | Own a protocol surface. | + +See [CONTRIBUTING.md](https://github.com/BlockSiFr/ttp-protocol/blob/main/CONTRIBUTING.md) +and [GOVERNANCE.md](https://github.com/BlockSiFr/ttp-protocol/blob/main/GOVERNANCE.md). + +::: receipt ROADMAP +Roadmap phases +::: diff --git a/website/guide/decay.md b/website/guide/decay.md new file mode 100644 index 0000000..5fcc5bd --- /dev/null +++ b/website/guide/decay.md @@ -0,0 +1,35 @@ +# Trust Decay + +Trust is a function of time and evidence. Without fresh attestation it weakens; +a new attestation recharges it. + +

+ Trust decay and attestation recharge +

+ +## Bands + +| Band | Range | Posture | +| --- | --- | --- | +| Active | `0.85–1.00` | Full reliance | +| Degraded | `0.65–0.85` | Reliance with watch | +| Warning | `0.40–0.65` | Step-up before sensitive actions | +| Critical | `0.00–0.40` | Deny by default | + +```ttp +trust "invoice_agent" { + subject = agent:invoice-bot + score = 0.91 + decay = exponential(halflife = 6h) # trust halves every 6h without evidence +} +``` + +::: decay DECAY +Decay is declarative. Choose a model (`exponential`, `linear`, `stepped`) and a +rate; the evaluator applies it at proof time against the elapsed interval. +::: + +::: attestation ATTESTATION +A verified attestation adds a recharge delta, lifting the effective score back +toward the active band. +::: diff --git a/website/guide/examples.md b/website/guide/examples.md new file mode 100644 index 0000000..4170516 --- /dev/null +++ b/website/guide/examples.md @@ -0,0 +1,20 @@ +# Examples + +Runnable `.ttp` examples live in the +[`examples/`](https://github.com/BlockSiFr/ttp-protocol/tree/main/examples) +directory. Validate any of them with `npm run ttp -- check `. + +| Example | Shows | +| --- | --- | +| `01-basic-agent.ttp` | A minimal subject + trust claim. | +| `02-trust-decay.ttp` | Trust decaying over time. | +| `03-threshold-proof.ttp` | Proving trust clears a threshold. | +| `04-delegated-trust.ttp` | Bounded delegation between subjects. | +| `05-agent-tool-trust-wrapper.ttp` | Gating an agent tool call. | +| `06-cicd-pipeline-trust-proof.ttp` | Trust before a deploy stage. | +| `07-api-client-trust-proof.ttp` | Gating a sensitive API client. | +| `08-msp-mssp-trust-proof.ttp` | Customer-impacting operational action. | + +```bash +npm run ttp -- eval examples/02-trust-decay.ttp --subject agent:invoice_reviewer --at now +``` diff --git a/website/guide/integrations.md b/website/guide/integrations.md new file mode 100644 index 0000000..e9af8c0 --- /dev/null +++ b/website/guide/integrations.md @@ -0,0 +1,24 @@ +# Integrations + +TTP wraps the boundary where an action happens — no agent rewrite, no IdP +replacement. The enforcement point and current status by surface: + +

+ Integration surfaces and status +

+ +| Surface | Enforcement point | Status | +| --- | --- | --- | +| GitHub Actions | workflow job | reference | +| CI/CD Pipelines | pipeline stage | reference | +| API Gateways | gateway request | experimental | +| Service Accounts | token issuance | experimental | +| Microsoft Copilot | tool call | planned | +| Azure DevOps | release stage | planned | +| LangChain | agent tool | experimental | +| CrewAI | agent step | planned | + +::: authority PATTERN +Each integration evaluates a `TrustProof` at the surface's action point and +records an [ExecutionReceipt](/guide/receipts) for the decision. +::: diff --git a/website/guide/language.md b/website/guide/language.md new file mode 100644 index 0000000..a48bd84 --- /dev/null +++ b/website/guide/language.md @@ -0,0 +1,27 @@ +# Language Specification + +TTP is expressed as a small declarative language. A `.ttp` file declares +subjects, their trust, and the policies that gate actions. + +```ttp +trust "invoice_agent" { + subject = agent:invoice-bot + issuer = trust-issuer:finance-control + score = 0.91 + decay = exponential(halflife = 6h) + scope = [invoice.read, invoice.write] + evidence = [attestation:att-7b3c, receipt:rcpt-01J9F4] +} + +policy "write_invoices" { + action = invoice.write + require_trust = 0.80 + on_below = step-up + execution_policy = gate + emit = execution_receipt +} +``` + +The full grammar, evaluation order, and failure codes are in the +[SPECIFICATION.md](https://github.com/BlockSiFr/ttp-protocol/blob/main/SPECIFICATION.md). +Validate a file with `npm run ttp -- check `. diff --git a/website/guide/proofs.md b/website/guide/proofs.md new file mode 100644 index 0000000..b1c478d --- /dev/null +++ b/website/guide/proofs.md @@ -0,0 +1,25 @@ +# Proofs + +A `TrustProof` is the structured output of evaluating a subject's trust against +a requirement. It composes sub-proofs — threshold, attestations, delegation, +route, and decay — into a single verifiable envelope with a `proofHash`. + +## Outcomes + +| Outcome | Meaning | +| --- | --- | +| `trust_valid` | Current evidence satisfies the requirement. | +| `trust_insufficient` | Effective trust is below the threshold. | +| `trust_expired` | A claim or proof freshness window has elapsed. | +| `trust_invalid` | Issuer, evidence, reference, or validation failed. | +| `trust_unknown` | Trust cannot be established from available evidence. | + +## Proof modes + +`plain` · `signed` · `zk-placeholder` · `zk`. Production proofs must be `signed` +or `zk`; never rely on `cleartext-dev` outside local development. + +::: receipt PROOF +A proof is the input to a runtime decision. The decision and its proof reference +are then recorded together in an [ExecutionReceipt](/guide/receipts). +::: diff --git a/website/guide/receipts.md b/website/guide/receipts.md new file mode 100644 index 0000000..c6f56c3 --- /dev/null +++ b/website/guide/receipts.md @@ -0,0 +1,28 @@ +# Execution Receipts + +Every decision produces a signed, hash-chained `ExecutionReceipt` — evidence, +not a log line. It records what was decided, the trust state behind it, and the +cryptographic basis to verify it later. + +

+ Execution receipt card +

+ +## Fields + +| Field | Meaning | +| --- | --- | +| `subject` | The actor that was evaluated. | +| `action` / `resource` | What was attempted, and against what. | +| `decision` | `allow` · `throttle` · `step-up` · `escalate` · `deny`. | +| `trustScoreAtDecision` | Effective trust at the moment of decision. | +| `authorityGrantRef` | The grant relied upon. | +| `attestationRef` | The evidence that supported trust. | +| `timestamp` | When the decision was made. | +| `signature` | Cryptographic signature over the receipt. | +| `chainHash` | Links this receipt to the prior one — tamper-evident. | + +::: receipt RECEIPT +A receipt is verifiable independently of the system that produced it. Given the +public key and the chain, anyone can confirm the decision was made as recorded. +::: diff --git a/website/guide/rfcs.md b/website/guide/rfcs.md new file mode 100644 index 0000000..ca4d2a2 --- /dev/null +++ b/website/guide/rfcs.md @@ -0,0 +1,23 @@ +# RFCs + +Protocol changes are proposed as RFCs in the +[`rfcs/`](https://github.com/BlockSiFr/ttp-protocol/tree/main/rfcs) directory. +Anything that touches the grammar, semantics, schemas, or trust model needs one. + +## Process + +1. Open a **Protocol RFC** discussion to socialize the idea. +2. Draft an RFC from `rfcs/0000-template.md`. +3. Open a PR; mark it `status: needs-rfc` until accepted. +4. On acceptance, the change lands with tests and docs. + +| RFC | Topic | +| --- | --- | +| 0001 | Authority grants | +| 0002 | Execution receipts | +| 0003 | Trust decay | +| 0004 | Attestations | +| 0005 | Trust proof outcomes | +| 0006 | Runtime decisions | +| 0007 | Agent trust wrapper | +| 0008 | Commercial boundary | diff --git a/website/guide/runtime.md b/website/guide/runtime.md new file mode 100644 index 0000000..e1a1aa8 --- /dev/null +++ b/website/guide/runtime.md @@ -0,0 +1,37 @@ +# Runtime Authority + +Trust is checked at a **runtime authority gate** before an action runs. The gate +consumes a trust proof, an authority grant, and the current decay state, then +emits a decision and a signed receipt. + +

+ Runtime authority flow +

+ +## Decision outcomes + +

+ allow, throttle, step-up, escalate, deny +

+ +| Outcome | Meaning | +| --- | --- | +| `allow` | Trust clears the threshold; the action executes. | +| `throttle` | Marginal trust; proceed under rate or scope limits. | +| `step-up` | Fresh attestation required to continue. | +| `escalate` | Route to a human or higher authority for review. | +| `deny` | Trust below floor; the action is blocked. | + +::: deny DENY +The gate is **fail-closed**. If trust cannot be established, the default outcome +is `deny` — never implicit allow. +::: + +::: step-up STEP-UP +A `step-up` is not a failure. It asks the actor to present fresh evidence (a new +attestation) and re-enter the gate. +::: + +> TTP establishes the trust context. RAP, Execution Exchange, API gateways, and +> CI gates enforce the decision. See the +> [commercial boundary](https://github.com/BlockSiFr/ttp-protocol/blob/main/COMMERCIAL_BOUNDARY.md). diff --git a/website/guide/security.md b/website/guide/security.md new file mode 100644 index 0000000..a2a8783 --- /dev/null +++ b/website/guide/security.md @@ -0,0 +1,33 @@ +# Security + +TTP is a protocol draft and reference implementation. + +::: deny DO NOT +Do not use `cleartext-dev` proof mode in production. It performs no +cryptographic verification and exists only for local development. +::: + +## Production requirements + +Production trust establishment and downstream enforcement require: + +- trusted issuer registry +- signed claims and replay protection +- clock integrity and key rotation +- tenant isolation +- **fail-closed** downstream enforcement +- receipt signing and audit retention +- attestation freshness and evidence integrity + +## Posture + +| Property | Status | +| --- | --- | +| Spec | draft v1.0 | +| Reference evaluator | active | +| Production enforcement | commercial (Execution Exchange) | + +Report vulnerabilities per +[SECURITY.md](https://github.com/BlockSiFr/ttp-protocol/blob/main/SECURITY.md). +The full threat model is in +[THREAT_MODEL.md](https://github.com/BlockSiFr/ttp-protocol/blob/main/THREAT_MODEL.md). diff --git a/website/guide/start.md b/website/guide/start.md new file mode 100644 index 0000000..e34e4cd --- /dev/null +++ b/website/guide/start.md @@ -0,0 +1,34 @@ +# Overview + +The **Trust Transfer Protocol (TTP)** is the protocol layer for agent trust. +Identity proves *who* is acting. Authorization defines *what* was assigned. TTP +establishes whether the actor is **trustworthy enough for this action, right +now** — before any downstream authority or execution decision is made. + +

+ Identity vs Authorization vs TTP +

+ +## Quickstart + +```bash +git clone https://github.com/BlockSiFr/ttp-protocol.git +cd ttp-protocol +npm install +npm test + +# evaluate the reference examples +npm run ttp -- check examples/01-basic-agent.ttp +npm run ttp -- eval examples/02-trust-decay.ttp --subject agent:invoice_reviewer --at now +npm run demo +``` + +- `check` validates a `.ttp` file. +- `eval` evaluates a subject's trust at a point in time. +- `demo` prints reference decisions, trust scores, and `ExecutionReceipt` ids + for local, non-production scenarios. + +::: receipt NEXT +Continue to [Protocol Concepts](/guide/concepts) to learn the primitives, then +[Runtime Authority](/guide/runtime) for how decisions are made. +::: diff --git a/website/index.md b/website/index.md new file mode 100644 index 0000000..41e0d12 --- /dev/null +++ b/website/index.md @@ -0,0 +1,65 @@ +--- +layout: home +hero: + name: Trust Transfer Protocol + text: Trust-Before-Execution + tagline: Every action must earn authority before execution. Every decision must be provable after. + image: + src: /trust-ring.svg + alt: TTP trust ring + actions: + - theme: brand + text: Quickstart + link: /guide/start + - theme: alt + text: Protocol Concepts + link: /guide/concepts + - theme: alt + text: GitHub + link: https://github.com/BlockSiFr/ttp-protocol +features: + - icon: ▣ + title: I'm an AI engineer + details: Wrap agent tool calls with a trust proof — no rewrite required. + link: /guide/integrations + - icon: ◆ + title: I'm a security architect + details: Fail-closed runtime gating, signed receipts, and a threat model. + link: /guide/security + - icon: ⬡ + title: I'm building an agent framework + details: Embed the runtime authority gate at your execution boundary. + link: /guide/runtime + - icon: ▤ + title: I'm evaluating enterprise governance + details: Where the open protocol ends and managed enforcement begins. + link: /guide/concepts + - icon: ✦ + title: I want to contribute + details: From a good first issue to owning a protocol surface. + link: /guide/contributing +--- + +

+ Identity, Attestation, Trust Evaluation, Runtime Authority Gate, Decision, Execution Receipt +

+ +## Protocol status + +::: decay PROTOCOL v1.0 · REFERENCE EVALUATOR ACTIVE +The protocol spec and reference evaluator are available. Runtime authority +enforcement is in progress. Do not use `cleartext-dev` proof mode in production. +::: + +## Quickstart + +```bash +git clone https://github.com/BlockSiFr/ttp-protocol.git +cd ttp-protocol && npm install && npm test + +npm run ttp -- check examples/01-basic-agent.ttp +npm run demo +``` + +Then read [Protocol Concepts](/guide/concepts) and the +[Runtime Authority](/guide/runtime) model. diff --git a/website/package-lock.json b/website/package-lock.json new file mode 100644 index 0000000..26d3782 --- /dev/null +++ b/website/package-lock.json @@ -0,0 +1,2519 @@ +{ + "name": "@blocksifr/ttp-docs", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@blocksifr/ttp-docs", + "devDependencies": { + "markdown-it-container": "^4.0.0", + "vitepress": "^1.6.3" + } + }, + "node_modules/@algolia/abtesting": { + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.21.1.tgz", + "integrity": "sha512-Wia5/mNTfiU0PIUN25UMfAGGdASkkwuCS9nBAdmhqrNPY/ff7U/6MgBVdwFDPsa3sA1msutPtO50gvOzx6MOXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/autocomplete-core": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", + "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", + "@algolia/autocomplete-shared": "1.17.7" + } + }, + "node_modules/@algolia/autocomplete-plugin-algolia-insights": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", + "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "search-insights": ">= 1 < 3" + } + }, + "node_modules/@algolia/autocomplete-preset-algolia": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", + "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-shared": "1.17.7" + }, + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/autocomplete-shared": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", + "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@algolia/client-search": ">= 4.9.1 < 6", + "algoliasearch": ">= 4.9.1 < 6" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.55.1.tgz", + "integrity": "sha512-miW8RzAtBgNiEJ9fGEhsOPgWUpekAe64YcVufqXrlykj0Jjmo5nj0a5f/HAzRVX5ZuU1GAVd7BkzFDx7q50P3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.55.1.tgz", + "integrity": "sha512-eR3J3kB9JX6DdCvDRi3I4KPfwO6fR9HWYRXhVke2TXIoOQafMKCRAneg33JRmIrb+DnnJ/eWApJLF1O1CLPERg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.55.1.tgz", + "integrity": "sha512-P5ak7EurwYqgAiDyb95mgA3WRR/Zu8CPMv36lWTISvL2AmlPyqQPy2nX/KEJRTcwaeTWwrk6wJV4/M93GfjOWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.55.1.tgz", + "integrity": "sha512-OVtj9uA//+pjvKQI5INnzbyLrf3ClNv3XRbWswwJ2kHIStQNHtBfHo+LofNB/WhM9xjuXlW5ANn2aMj65UGx7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.55.1.tgz", + "integrity": "sha512-oKlVFlp+qbIEe4p7E54zSiP2gEV/vDu972Ykv8VDMFwEvreS7m0YKA3a8hGGHwc7yiBUGGiR3LlwzMLfnJmy6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.55.1.tgz", + "integrity": "sha512-BOVrld6vdtsFmotVDMTVQfYXwrVplJ+DUvy60JFi+tkWV698q2J9NNPKEO3dr5qxtSLKQP4vHF8n+3U5PDWhOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.55.1.tgz", + "integrity": "sha512-GAqHl9zERhC3bbBfubwUu07G3UXO06gORvOcsiTBZB3et0s3auNUbHlYdYNp4VKa3sUZqH5AcD3OKzU/KDGXjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.55.1", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.55.1.tgz", + "integrity": "sha512-BXZw+C+gsWL7pZvbnhJUnCXASiDLGcQxVV7h55Pyh2DmSzwdZIVccE5xc9RVD2trtrhIqk5smuODTxtaZqd0IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.55.1", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.55.1.tgz", + "integrity": "sha512-9g/ceZrZTqA62FA3588Xj0onRPjDNfu0pVQqefK0rrHp9H6Wblph/YmzGjZ2g8uqbTh0ZGIvAGCzErU8f7MHpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.55.1.tgz", + "integrity": "sha512-cZTIrGyAP+W4A6jDVwvWM/JOaoJKQkD/2a5eLUEeNdKAD45jN7BCpsMDONyhZlosLa4UwL8uiINQzj4iFy9nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.55.1.tgz", + "integrity": "sha512-N6I3leW0UO8Y9Zv90yo2UHgYGuxZO0mjbvzNxDIJDjO0qECEF7Z9XMvSNeUWXQh/iNDA9lr8MfEy3rmZGIcclw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.55.1.tgz", + "integrity": "sha512-ukU5zeeFs44rQkzv+TRdYard+d+3lmPGs8lPZhHtWE8rfz+LlBSF6s9kP3VQ7LeOYL8Dz0u6tZfnyTrqrumbHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.55.1.tgz", + "integrity": "sha512-lCwXyijwPm3vbYHpBXPRomMcD6mgiptmps27gnMCf4HK+u/AOeFPBnIFh4V3l4A5SnP9VRiKBZqwGBpUH0vaTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@docsearch/css": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", + "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@docsearch/js": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", + "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/react": "3.8.2", + "preact": "^10.0.0" + } + }, + "node_modules/@docsearch/react": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", + "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/autocomplete-core": "1.17.7", + "@algolia/autocomplete-preset-algolia": "1.17.7", + "@docsearch/css": "3.8.2", + "algoliasearch": "^5.14.2" + }, + "peerDependencies": { + "@types/react": ">= 16.8.0 < 19.0.0", + "react": ">= 16.8.0 < 19.0.0", + "react-dom": ">= 16.8.0 < 19.0.0", + "search-insights": ">= 1 < 3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "search-insights": { + "optional": true + } + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@iconify-json/simple-icons": { + "version": "1.2.87", + "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.87.tgz", + "integrity": "sha512-8YciStObhSji3OZFmWAWK6kBujyqO5bLCxeDwLxf3CR3F4PVelq7keC2LBvgTqviWzSTysj5/g4PCFLiAMVGsw==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@iconify/types": "*" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shikijs/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", + "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.4" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", + "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^3.1.0" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", + "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", + "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", + "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/transformers": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", + "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/types": "2.5.0" + } + }, + "node_modules/@shikijs/types": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", + "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "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/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "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/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.2.tgz", + "integrity": "sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.39.tgz", + "integrity": "sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/shared": "3.5.39", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.39.tgz", + "integrity": "sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.39", + "@vue/shared": "3.5.39" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.39.tgz", + "integrity": "sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@vue/compiler-core": "3.5.39", + "@vue/compiler-dom": "3.5.39", + "@vue/compiler-ssr": "3.5.39", + "@vue/shared": "3.5.39", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.15", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.39.tgz", + "integrity": "sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.39", + "@vue/shared": "3.5.39" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.10.tgz", + "integrity": "sha512-KxtEpUOOpFz/qOGRrAwA36QF7DqIA+FXgCYit9mk9wjbaZt0sXOFz81ElOZtKA4HbWHUdwNjZHBFsFFyp5BZiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.10" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.10.tgz", + "integrity": "sha512-3WNi2Kq4tbpVbmhml7RiphmAt0279oh3fKNeWMQIrltfX8Q91b4i5PL8DtyNKdwmcsGrV4fg+erwWOmD05CLIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.10", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.10", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.10.tgz", + "integrity": "sha512-wOPslzB8vTvpxwdaOcR2qAbwmuSP0L+rhpoC6Cf56V3Jip+HWb7PQQXOUPgBNQARpXsbQX/+mvi8kKucmBGRwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.39.tgz", + "integrity": "sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.39" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.39.tgz", + "integrity": "sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.39", + "@vue/shared": "3.5.39" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.39.tgz", + "integrity": "sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.39", + "@vue/runtime-core": "3.5.39", + "@vue/shared": "3.5.39", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.39.tgz", + "integrity": "sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.39", + "@vue/shared": "3.5.39" + }, + "peerDependencies": { + "vue": "3.5.39" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.39.tgz", + "integrity": "sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", + "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/integrations": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", + "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vueuse/core": "12.8.2", + "@vueuse/shared": "12.8.2", + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "async-validator": "^4", + "axios": "^1", + "change-case": "^5", + "drauu": "^0.4", + "focus-trap": "^7", + "fuse.js": "^7", + "idb-keyval": "^6", + "jwt-decode": "^4", + "nprogress": "^0.2", + "qrcode": "^1.5", + "sortablejs": "^1", + "universal-cookie": "^7" + }, + "peerDependenciesMeta": { + "async-validator": { + "optional": true + }, + "axios": { + "optional": true + }, + "change-case": { + "optional": true + }, + "drauu": { + "optional": true + }, + "focus-trap": { + "optional": true + }, + "fuse.js": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "jwt-decode": { + "optional": true + }, + "nprogress": { + "optional": true + }, + "qrcode": { + "optional": true + }, + "sortablejs": { + "optional": true + }, + "universal-cookie": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", + "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "12.8.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", + "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "vue": "^3.5.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/algoliasearch": { + "version": "5.55.1", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.55.1.tgz", + "integrity": "sha512-FyaFnnsbVPtevQwqSj/SdxE3jAsSsY0BEH8IVLf9rXxEBdAhAmT6VKCVSMWoaPIHVN1Eufh/1w8q6k8URpIkWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.21.1", + "@algolia/client-abtesting": "5.55.1", + "@algolia/client-analytics": "5.55.1", + "@algolia/client-common": "5.55.1", + "@algolia/client-insights": "5.55.1", + "@algolia/client-personalization": "5.55.1", + "@algolia/client-query-suggestions": "5.55.1", + "@algolia/client-search": "5.55.1", + "@algolia/ingestion": "1.55.1", + "@algolia/monitoring": "1.55.1", + "@algolia/recommend": "5.55.1", + "@algolia/requester-browser-xhr": "5.55.1", + "@algolia/requester-fetch": "5.55.1", + "@algolia/requester-node-http": "5.55.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "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/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "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/emoji-regex-xs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", + "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/focus-trap": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", + "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tabbable": "^6.4.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mark.js": { + "version": "8.11.1", + "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", + "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/markdown-it-container": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-container/-/markdown-it-container-4.0.0.tgz", + "integrity": "sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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-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-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-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/minisearch": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", + "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", + "dev": true, + "license": "MIT" + }, + "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/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/oniguruma-to-es": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", + "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex-xs": "^1.0.0", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "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/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.29.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz", + "integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, + "license": "MIT" + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/shiki": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", + "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "2.5.0", + "@shikijs/engine-javascript": "2.5.0", + "@shikijs/engine-oniguruma": "2.5.0", + "@shikijs/langs": "2.5.0", + "@shikijs/themes": "2.5.0", + "@shikijs/types": "2.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tabbable": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.5.0.tgz", + "integrity": "sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "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/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitepress": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", + "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@docsearch/css": "3.8.2", + "@docsearch/js": "3.8.2", + "@iconify-json/simple-icons": "^1.2.21", + "@shikijs/core": "^2.1.0", + "@shikijs/transformers": "^2.1.0", + "@shikijs/types": "^2.1.0", + "@types/markdown-it": "^14.1.2", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/devtools-api": "^7.7.0", + "@vue/shared": "^3.5.13", + "@vueuse/core": "^12.4.0", + "@vueuse/integrations": "^12.4.0", + "focus-trap": "^7.6.4", + "mark.js": "8.11.1", + "minisearch": "^7.1.1", + "shiki": "^2.1.0", + "vite": "^5.4.14", + "vue": "^3.5.13" + }, + "bin": { + "vitepress": "bin/vitepress.js" + }, + "peerDependencies": { + "markdown-it-mathjax3": "^4", + "postcss": "^8" + }, + "peerDependenciesMeta": { + "markdown-it-mathjax3": { + "optional": true + }, + "postcss": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.39.tgz", + "integrity": "sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.39", + "@vue/compiler-sfc": "3.5.39", + "@vue/runtime-dom": "3.5.39", + "@vue/server-renderer": "3.5.39", + "@vue/shared": "3.5.39" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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" + } + } + } +} diff --git a/website/package.json b/website/package.json new file mode 100644 index 0000000..4a5d5a4 --- /dev/null +++ b/website/package.json @@ -0,0 +1,14 @@ +{ + "name": "@blocksifr/ttp-docs", + "private": true, + "type": "module", + "scripts": { + "docs:dev": "vitepress dev", + "docs:build": "vitepress build", + "docs:preview": "vitepress preview" + }, + "devDependencies": { + "markdown-it-container": "^4.0.0", + "vitepress": "^1.6.3" + } +} diff --git a/website/public/diagrams/architecture-stack.svg b/website/public/diagrams/architecture-stack.svg new file mode 100644 index 0000000..8c54914 --- /dev/null +++ b/website/public/diagrams/architecture-stack.svg @@ -0,0 +1,48 @@ + + + + + + + + + + L4 + Consumers + Agent frameworks · CI/CD · APIs · Copilots + + + + + L3 + Execution Exchange + Enterprise control plane · evidence & receipts + + + + + L2 + Runtime Authority Gate + Decides allow · step-up · deny before execution + + + + + L1 + SCIM-RE · Runtime Governance + Authority grants · policy · runtime execution context + + + + + + L0 + Trust Transfer Protocol + Trust scoring · decay · delegation · proofs · receipts + FOUNDATION + + diff --git a/website/public/diagrams/contributor-path.svg b/website/public/diagrams/contributor-path.svg new file mode 100644 index 0000000..7f78ce4 --- /dev/null +++ b/website/public/diagrams/contributor-path.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + 01Start HereREADME & concepts + 02Good First Issuesmall, scoped tasks + 03Protocol RFCpropose a change + 04Runtime Implbuild enforcement + 05Docs + Examplesteach the next dev + 06Maintainer Pathown a surface + diff --git a/website/public/diagrams/decision-matrix.svg b/website/public/diagrams/decision-matrix.svg new file mode 100644 index 0000000..7563983 --- /dev/null +++ b/website/public/diagrams/decision-matrix.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + allow + Trust clears threshold. + Action executes. + PROCEED + + + + + + + throttle + Marginal trust. + Rate-limit the actor. + SLOW + + + + + + + step-up + Fresh attestation + required to continue. + RE-PROVE + + + + + + + escalate + Route to a human + authority for review. + REVIEW + + + + + + + deny + Trust below floor. + Action is blocked. + BLOCK + + diff --git a/website/public/diagrams/execution-receipt.svg b/website/public/diagrams/execution-receipt.svg new file mode 100644 index 0000000..ee651dc --- /dev/null +++ b/website/public/diagrams/execution-receipt.svg @@ -0,0 +1,41 @@ + + + + + + + + + + EXECUTION RECEIPT + + + + VERIFIED + + receipt:rcpt_01J9F4…A2 + + + + + subject agent:invoice-bot + action invoice.write + resource erp://invoices/2026-06 + decision allow + trustScoreAtDecision0.87 + authorityGrantRef grant:9f2a…c41 + attestationRef att:7b3c…e90 + timestamp 2026-06-25T18:40:12Z + + + + + signature ed25519:5d1e…b7f4 + chainHash sha256:c0ffee…a17b + diff --git a/website/public/diagrams/integrations.svg b/website/public/diagrams/integrations.svg new file mode 100644 index 0000000..4a868c3 --- /dev/null +++ b/website/public/diagrams/integrations.svg @@ -0,0 +1,20 @@ + + + + + + + GitHub Actionsenforces at: workflow jobREFERENCE + CI/CD Pipelinesenforces at: pipeline stageREFERENCE + API Gatewaysenforces at: gateway requestEXPERIMENTAL + Service Accountsenforces at: token issuanceEXPERIMENTAL + + Microsoft Copilotenforces at: tool callPLANNED + Azure DevOpsenforces at: release stagePLANNED + LangChainenforces at: agent toolEXPERIMENTAL + CrewAIenforces at: agent stepPLANNED + diff --git a/website/public/diagrams/protocol-loop.svg b/website/public/diagrams/protocol-loop.svg new file mode 100644 index 0000000..8dc2b6d --- /dev/null +++ b/website/public/diagrams/protocol-loop.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + 01Identitynon-human actorasserts itself + 02Attestationsigned evidenceis presented + 03Trust Evalscore vs.threshold + 04Authority Gatechecked beforeexecution + 05Decisionallow · step-up· deny + 06Receiptsigned proofis emitted + + + diff --git a/website/public/diagrams/roadmap.svg b/website/public/diagrams/roadmap.svg new file mode 100644 index 0000000..7dc2367 --- /dev/null +++ b/website/public/diagrams/roadmap.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + PHASE 1 + Spec & Reference + SHIPPED + + + + + PHASE 2 + Runtime Authority + IN PROGRESS + + + + + PHASE 3 + Proofs & Receipts + NEXT + + + + + PHASE 4 + Integrations + PLANNED + + + + + PHASE 5 + Production Hardening + PLANNED + + diff --git a/website/public/diagrams/runtime-authority-flow.svg b/website/public/diagrams/runtime-authority-flow.svg new file mode 100644 index 0000000..870fbc6 --- /dev/null +++ b/website/public/diagrams/runtime-authority-flow.svg @@ -0,0 +1,70 @@ + + + + + + + + + + REQUEST + agent:invoice-bot + invoice.write + + + + + + + + + + Runtime Authority Gate + evaluates before execution + + trust proof + authority grant + decay state + + + + + + + + + + + + allowexecute + + + + step-upre-prove + + + + denyblock + + + + + + + + + + + + RECEIPT + decision: allow + sha256:9f2a…c41 + signed · immutable + + diff --git a/website/public/diagrams/trust-decay-curve.svg b/website/public/diagrams/trust-decay-curve.svg new file mode 100644 index 0000000..f097009 --- /dev/null +++ b/website/public/diagrams/trust-decay-curve.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + 1.0 + 0.0 + time + + + + ACTIVE 0.85–1.00 + DEGRADED 0.65–0.85 + WARNING 0.40–0.65 + CRITICAL 0.00–0.40 + + + + + + + + + + + + + + + attestation + + diff --git a/website/public/diagrams/trust-gap.svg b/website/public/diagrams/trust-gap.svg new file mode 100644 index 0000000..a20a5e0 --- /dev/null +++ b/website/public/diagrams/trust-gap.svg @@ -0,0 +1,49 @@ + + + + + + + + + ESTABLISHED · IDENTITY + + Who is acting? + Authentication proves the actor + is who it claims to be. + + + OAuth · OIDC · SPIFFE + + + + + ESTABLISHED · AUTHORIZATION + + What may it access? + Static grants define scope — + assigned once, rarely revisited. + + + RBAC · SCIM · IAM + + + + + + THE GAP · TTP + + Should this action + execute right now? + Live, evidence-backed trust + gates the action before it runs. + + + TRUST · DECAY · RECEIPTS + + diff --git a/website/public/repo-icon.svg b/website/public/repo-icon.svg new file mode 100644 index 0000000..f17cf30 --- /dev/null +++ b/website/public/repo-icon.svg @@ -0,0 +1,12 @@ + + TTP Protocol icon + Trust lattice nodes connected around a proof core. + + + + + + + + + diff --git a/website/public/social-preview.png b/website/public/social-preview.png new file mode 100644 index 0000000..8c101bd Binary files /dev/null and b/website/public/social-preview.png differ diff --git a/website/public/trust-ring.svg b/website/public/trust-ring.svg new file mode 100644 index 0000000..6364dad --- /dev/null +++ b/website/public/trust-ring.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/public/ttp-hero.svg b/website/public/ttp-hero.svg new file mode 100644 index 0000000..9463ce2 --- /dev/null +++ b/website/public/ttp-hero.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OPEN SOURCE · AGENT TRUST INFRASTRUCTURE + + + PROTOCOL v1.0 + + + Trust Transfer + Protocol + Trust-Before-Execution for AI agents & non-human identities. + + + + $ npm install @blocksifr/ttp-protocol + + + + + + + + + Identitywho is acting + + + + + Trustis it trustworthy right now + + + + + Authorityis this action permitted + + + + + Executiongated runtime action + + + + + Receiptcryptographic proof of the decision + + + + + + + Authority Before Execution·Trust Decay·ZK Proofs·Execution Receipts +