mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-04 09:06:01 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5f591fc4a | |||
| 6bbc922710 |
-267
@@ -1,267 +0,0 @@
|
|||||||
# Contributing to bknd
|
|
||||||
|
|
||||||
Thank you for your interest in contributing to bknd. This guide will help you get started, understand the codebase, and submit contributions that align with the project's direction.
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
- [Before You Start](#before-you-start)
|
|
||||||
- [Important Resources](#important-resources)
|
|
||||||
- [Development Setup](#development-setup)
|
|
||||||
- [Project Structure](#project-structure)
|
|
||||||
- [Understanding the Codebase](#understanding-the-codebase)
|
|
||||||
- [Running Tests](#running-tests)
|
|
||||||
- [Code Style](#code-style)
|
|
||||||
- [Submitting Changes](#submitting-changes)
|
|
||||||
- [AI-Generated Code](#ai-generated-code)
|
|
||||||
- [Contributing to Documentation](#contributing-to-documentation)
|
|
||||||
- [Reporting Bugs](#reporting-bugs)
|
|
||||||
- [Getting Help](#getting-help)
|
|
||||||
- [Contributors](#contributors)
|
|
||||||
- [Maintainers](#maintainers)
|
|
||||||
|
|
||||||
## Before You Start
|
|
||||||
|
|
||||||
**Open a GitHub Issue before writing code.** This is the preferred way to propose any change. It lets maintainers and the community align on the approach before time is spent on implementation. If you have discussed a contribution in the [Discord server](https://discord.com/invite/952SFk8Tb8) instead, include a link to the relevant Discord thread in your pull request. Pull requests submitted without a corresponding issue or Discord discussion may be closed.
|
|
||||||
|
|
||||||
**Unsolicited architectural changes will be closed.** The internal architecture of bknd is intentional. Refactors, restructuring, or changes to core patterns must be discussed and approved before any code is written.
|
|
||||||
|
|
||||||
Contributions that are generally welcome (after opening an issue):
|
|
||||||
|
|
||||||
- Bug fixes
|
|
||||||
- New adapters, strategies, or storage backends
|
|
||||||
- Documentation improvements
|
|
||||||
- New examples
|
|
||||||
- Test coverage improvements
|
|
||||||
|
|
||||||
**A note on versioning**: bknd is pre-1.0 and under active development. Full backward compatibility is not guaranteed before v1.0.0. Contributors should be aware that APIs and internal interfaces may change between releases.
|
|
||||||
|
|
||||||
## Important Resources
|
|
||||||
|
|
||||||
- **Documentation**: https://docs.bknd.io
|
|
||||||
- **Issue Tracker**: https://github.com/bknd-io/bknd/issues
|
|
||||||
- **Discord**: https://discord.com/invite/952SFk8Tb8
|
|
||||||
- **FAQ / Search**: https://www.answeroverflow.com/c/1308395750564302952
|
|
||||||
|
|
||||||
## Development Setup
|
|
||||||
|
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
- Node.js 22.13 or higher
|
|
||||||
- Bun 1.3.3
|
|
||||||
|
|
||||||
### Getting Started
|
|
||||||
|
|
||||||
1. Fork the repository and clone your fork.
|
|
||||||
|
|
||||||
2. Install dependencies from the repo root:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun install
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Navigate to the main app package:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd app
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Copy the example environment file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cp .env.example .env
|
|
||||||
```
|
|
||||||
|
|
||||||
The default `.env.example` includes a file-based SQLite database (`file:.db/dev.db`). You can change `VITE_DB_URL` to `:memory:` for an in-memory database instead.
|
|
||||||
|
|
||||||
5. Start the dev server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
The dev server runs on `http://localhost:28623` using Vite with Hono.
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
This is a Bun monorepo using workspaces. The vast majority of the code lives in `app/`.
|
|
||||||
|
|
||||||
```
|
|
||||||
bknd/
|
|
||||||
app/ # Main "bknd" npm package (this is where most work happens)
|
|
||||||
src/
|
|
||||||
adapter/ # Runtime/framework adapters (node, bun, cloudflare, nextjs, astro, etc.)
|
|
||||||
auth/ # Authentication module (strategies, sessions, user pool)
|
|
||||||
cli/ # CLI commands
|
|
||||||
core/ # Shared utilities, event system, drivers, server helpers
|
|
||||||
data/ # Data module (entities, fields, relations, queries, schema)
|
|
||||||
flows/ # Workflow engine and tasks
|
|
||||||
media/ # Media module (storage adapters, uploads)
|
|
||||||
modes/ # Configuration modes (code, hybrid, db)
|
|
||||||
modules/ # Module system, MCP server, permissions framework
|
|
||||||
plugins/ # Built-in plugins (auth, data, cloudflare, dev)
|
|
||||||
ui/ # All frontend code
|
|
||||||
client/ # TypeScript SDK and React hooks (exported as bknd/client)
|
|
||||||
elements/ # React components for auth/media (exported as bknd/elements)
|
|
||||||
(everything else) # Admin UI (exported as bknd/ui)
|
|
||||||
App.ts # Central application orchestrator
|
|
||||||
index.ts # Main package exports
|
|
||||||
__test__/ # Unit tests (mirrors src/ structure)
|
|
||||||
e2e/ # End-to-end tests (Playwright)
|
|
||||||
build.ts # Build script (tsup/esbuild)
|
|
||||||
build.cli.ts # CLI build script
|
|
||||||
packages/ # Small satellite packages
|
|
||||||
cli/ # Standalone CLI package (bknd-cli)
|
|
||||||
plasmic/ # Plasmic integration
|
|
||||||
postgres/ # Postgres helper
|
|
||||||
sqlocal/ # SQLocal helper
|
|
||||||
docs/ # Documentation site (Next.js + Fumadocs, deployed to docs.bknd.io)
|
|
||||||
examples/ # Example projects across runtimes and frameworks
|
|
||||||
docker/ # Docker configuration
|
|
||||||
```
|
|
||||||
|
|
||||||
## Understanding the Codebase
|
|
||||||
|
|
||||||
### Path Aliases
|
|
||||||
|
|
||||||
The project uses TypeScript path aliases defined in `app/tsconfig.json`. Imports like `core/utils`, `data/connection`, or `auth/authenticate` resolve to directories under `app/src/`. When reading source code, keep this in mind -- an import like `import { Connection } from "data/connection"` refers to `app/src/data/connection`, not an external package.
|
|
||||||
|
|
||||||
### Module System
|
|
||||||
|
|
||||||
bknd is built around four core modules, each with its own schema, API routes, and permissions:
|
|
||||||
|
|
||||||
- **Data** -- entity definitions, field types, relations, queries (backed by Kysely)
|
|
||||||
- **Auth** -- authentication strategies, sessions, user management
|
|
||||||
- **Media** -- file storage with pluggable adapters (S3, Cloudinary, local filesystem, etc.)
|
|
||||||
- **Flows** -- workflow automation and task execution
|
|
||||||
|
|
||||||
These modules are managed by the `ModuleManager` (in `app/src/modules/`), which handles configuration, building, and lifecycle.
|
|
||||||
|
|
||||||
### Adapter Pattern
|
|
||||||
|
|
||||||
Adapters in `app/src/adapter/` allow bknd to run on different runtimes and frameworks. Each adapter provides the glue between bknd's Hono-based server and a specific environment (Node, Bun, Cloudflare Workers, Next.js, Astro, etc.).
|
|
||||||
|
|
||||||
### Plugin System
|
|
||||||
|
|
||||||
Plugins (in `app/src/plugins/`) hook into the app lifecycle via callbacks like `beforeBuild`, `onBuilt`, `onServerInit`, and `schema`. They can add entities, register routes, and extend behavior.
|
|
||||||
|
|
||||||
### Build System
|
|
||||||
|
|
||||||
The build is handled by a custom `app/build.ts` script using tsup (esbuild under the hood). It builds four targets in parallel:
|
|
||||||
|
|
||||||
- **API** -- the core backend
|
|
||||||
- **UI** -- the admin interface
|
|
||||||
- **Elements** -- standalone React components
|
|
||||||
- **Adapters** -- all runtime/framework adapters
|
|
||||||
|
|
||||||
## Running Tests
|
|
||||||
|
|
||||||
All test commands run from the `app/` directory.
|
|
||||||
|
|
||||||
| Command | Runner | What it runs |
|
|
||||||
|---|---|---|
|
|
||||||
| `bun run test` | Bun test | Unit tests (`*.spec.ts` files) |
|
|
||||||
| `bun run test:node` | Vitest | Node-specific tests (`*.vi-test.ts`, `*.vitest.ts`) |
|
|
||||||
| `bun run test:e2e` | Playwright | End-to-end tests (`*.e2e-spec.ts` in `e2e/`) |
|
|
||||||
| `bun run types` | TypeScript | Type checking (no emit) |
|
|
||||||
|
|
||||||
CI runs both Bun and Node tests, with a Postgres 17 service for database integration tests.
|
|
||||||
|
|
||||||
## Code Style
|
|
||||||
|
|
||||||
The project uses **Biome** for formatting and linting.
|
|
||||||
|
|
||||||
- 3-space indentation for JavaScript/TypeScript
|
|
||||||
- 100-character line width
|
|
||||||
- Spaces (not tabs)
|
|
||||||
|
|
||||||
To format and lint, run from the repo root:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bunx biome format --write ./app
|
|
||||||
bunx biome lint --changed --write ./app
|
|
||||||
```
|
|
||||||
|
|
||||||
Run both before submitting a PR. CI will catch style issues, but it is better to fix them locally first.
|
|
||||||
|
|
||||||
## Submitting Changes
|
|
||||||
|
|
||||||
1. Open a GitHub Issue describing your proposed change, or link to a Discord thread where it was discussed. Wait for feedback before writing code.
|
|
||||||
2. Fork the repo and create a branch from `main`.
|
|
||||||
3. Keep changes focused and minimal. One PR per issue.
|
|
||||||
4. Add or update tests if applicable.
|
|
||||||
5. Run the test suite and linter before pushing.
|
|
||||||
6. Open a pull request against `main`. Reference the issue number in the PR description.
|
|
||||||
|
|
||||||
Expect a response from maintainers, but be patient -- this is an actively developed project and review may take some time.
|
|
||||||
|
|
||||||
## AI-Generated Code
|
|
||||||
|
|
||||||
If you use AI tools to write or assist with your code, you must:
|
|
||||||
|
|
||||||
- **Fully review all AI-generated code yourself** before submitting. You are responsible for understanding and standing behind every line in your PR.
|
|
||||||
- **Include the prompts used** in the PR description. This gives maintainers additional context for reviewing the code.
|
|
||||||
|
|
||||||
Pull requests with unreviewed AI-generated code will be closed.
|
|
||||||
|
|
||||||
## Contributing to Documentation
|
|
||||||
|
|
||||||
The documentation site lives in the `docs/` directory and is a **separate Next.js application** built with [Fumadocs](https://fumadocs.dev). It uses **npm** (not Bun) as its package manager.
|
|
||||||
|
|
||||||
### Docs Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd docs
|
|
||||||
npm install
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
The docs dev server runs on `http://localhost:3000`.
|
|
||||||
|
|
||||||
### Where Documentation Lives
|
|
||||||
|
|
||||||
Documentation content is written in MDX and located in `docs/content/docs/`. The directory is organized into:
|
|
||||||
|
|
||||||
- `(documentation)/` -- core documentation pages
|
|
||||||
- `guide/` -- guides and tutorials
|
|
||||||
- `api-reference/` -- API reference (partially auto-generated from OpenAPI)
|
|
||||||
|
|
||||||
To add or edit a page, create or modify an `.mdx` file in the appropriate directory. Page metadata is defined via frontmatter at the top of each file. Navigation order is controlled by `meta.json` files in each directory.
|
|
||||||
|
|
||||||
### Building the Docs
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
This generates the OpenAPI and MCP reference pages before building the Next.js site. Make sure the build succeeds locally before submitting a docs PR.
|
|
||||||
|
|
||||||
## Reporting Bugs
|
|
||||||
|
|
||||||
Open a GitHub Issue with:
|
|
||||||
|
|
||||||
- A clear title describing the problem.
|
|
||||||
- Steps to reproduce the bug.
|
|
||||||
- Expected behavior vs. actual behavior.
|
|
||||||
- Your environment (runtime, database, adapter, bknd version).
|
|
||||||
- Any relevant error messages or logs.
|
|
||||||
|
|
||||||
## Getting Help
|
|
||||||
|
|
||||||
- **Discord**: https://discord.com/invite/952SFk8Tb8
|
|
||||||
- **FAQ / Search**: https://www.answeroverflow.com/c/1308395750564302952
|
|
||||||
- **GitHub Issues**: https://github.com/bknd-io/bknd/issues
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
|
|
||||||
Thank you to everyone who has contributed to bknd.
|
|
||||||
|
|
||||||
<a href="https://github.com/bknd-io/bknd/graphs/contributors">
|
|
||||||
<img src="https://contrib.rocks/image?repo=bknd-io/bknd" />
|
|
||||||
</a>
|
|
||||||
|
|
||||||
Made with [contrib.rocks](https://contrib.rocks).
|
|
||||||
|
|
||||||
## Maintainers
|
|
||||||
|
|
||||||
- **Dennis** ([@dswbx](https://github.com/dswbx)) -- Creator and maintainer of bknd
|
|
||||||
- **Cameron Pak** ([@cameronapak](https://github.com/cameronapak)) -- Maintainer of bknd docs
|
|
||||||
+77
-169
@@ -1,202 +1,110 @@
|
|||||||
|
# Functional Source License, Version 1.1, MIT Future License
|
||||||
|
|
||||||
Apache License
|
## Abbreviation
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
FSL-1.1-MIT
|
||||||
|
|
||||||
1. Definitions.
|
## Notice
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
Copyright 2025 Dennis Senn
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
## Terms and Conditions
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
### Licensor ("We")
|
||||||
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
|
The party offering the Software under these Terms and Conditions.
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
### The Software
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
The "Software" is each version of the software that we make available under
|
||||||
transformation or translation of a Source form, including but
|
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
||||||
not limited to compiled object code, generated documentation,
|
Conditions with the Software.
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
### License Grant
|
||||||
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
|
Subject to your compliance with this License Grant and the Patents,
|
||||||
form, that is based on (or derived from) the Work and for which the
|
Redistribution and Trademark clauses below, we hereby grant you the right to
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
use, copy, modify, create derivative works, publicly perform, publicly display
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
and redistribute the Software for any Permitted Purpose identified below.
|
||||||
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
|
### Permitted Purpose
|
||||||
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
|
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
means making the Software available to others in a commercial product or
|
||||||
subsequently incorporated within the Work.
|
service that:
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
1. substitutes for the Software;
|
||||||
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
|
2. substitutes for any other product or service we offer using the Software
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
that exists as of the date we make the Software available; or
|
||||||
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
|
3. offers the same or substantially similar functionality as the Software.
|
||||||
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
|
Permitted Purposes specifically include using the Software:
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
1. for your internal use and access;
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
2. for non-commercial education;
|
||||||
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
|
3. for non-commercial research; and
|
||||||
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
|
4. in connection with professional services that you provide to a licensee
|
||||||
may provide additional or different license terms and conditions
|
using the Software in accordance with these 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,
|
### Patents
|
||||||
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
|
To the extent your use for a Permitted Purpose would necessarily infringe our
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
patents, the license grant above includes a license under our patents. If you
|
||||||
except as required for reasonable and customary use in describing the
|
make a claim against any party that the Software infringes or contributes to
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
the infringement of any patent, then your patent license to the Software ends
|
||||||
|
immediately.
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
### Redistribution
|
||||||
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,
|
The Terms and Conditions apply to all copies, modifications and derivatives of
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
the Software.
|
||||||
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
|
If you redistribute any copies, modifications or derivatives of the Software,
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
you must include a copy of or a link to these Terms and Conditions and not
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
remove any copyright notices provided in or with the Software.
|
||||||
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
|
### Disclaimer
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
||||||
replaced with your own identifying information. (Don't include
|
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
||||||
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 2026 Dennis Senn
|
### Trademarks
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Except for displaying the License Details and identifying us as the origin of
|
||||||
you may not use this file except in compliance with the License.
|
the Software, you have no right under these Terms and Conditions to use our
|
||||||
You may obtain a copy of the License at
|
trademarks, trade names, service marks or product names.
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
## Grant of Future License
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
We hereby irrevocably grant you an additional license to use the Software under
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
the MIT license that is effective on the second anniversary of the date we make
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
the Software available. On or after that date, you may use the Software under
|
||||||
See the License for the specific language governing permissions and
|
the MIT license, in which case the following will apply:
|
||||||
limitations under the License.
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -165,19 +165,3 @@ npx bknd run
|
|||||||
```bash
|
```bash
|
||||||
npm install bknd
|
npm install bknd
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
We welcome contributions! Please read our [CONTRIBUTING.md](CONTRIBUTING.md) before getting started. It covers dev setup, project structure, testing, code style, and how to submit changes.
|
|
||||||
|
|
||||||
Before writing code, please open a GitHub Issue or start a conversation in [Discord](https://discord.com/invite/952SFk8Tb8) so we can align on the approach.
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
|
|
||||||
Thank you to everyone who has contributed to bknd.
|
|
||||||
|
|
||||||
<a href="https://github.com/bknd-io/bknd/graphs/contributors">
|
|
||||||
<img src="https://contrib.rocks/image?repo=bknd-io/bknd" />
|
|
||||||
</a>
|
|
||||||
|
|
||||||
Made with [contrib.rocks](https://contrib.rocks).
|
|
||||||
|
|||||||
@@ -2,4 +2,3 @@ playwright-report
|
|||||||
test-results
|
test-results
|
||||||
bknd.config.*
|
bknd.config.*
|
||||||
__test__/helper.d.ts
|
__test__/helper.d.ts
|
||||||
.env.local
|
|
||||||
@@ -1,49 +1,44 @@
|
|||||||
import { expect, describe, it, beforeAll, afterAll, mock } from "bun:test";
|
import { expect, describe, it, beforeAll, afterAll, mock } from "bun:test";
|
||||||
import * as adapter from "adapter";
|
import * as adapter from "adapter";
|
||||||
import { disableConsoleLog, enableConsoleLog, omitKeys } from "core/utils";
|
import { disableConsoleLog, enableConsoleLog } from "core/utils";
|
||||||
import { adapterTestSuite } from "adapter/adapter-test-suite";
|
import { adapterTestSuite } from "adapter/adapter-test-suite";
|
||||||
import { bunTestRunner } from "adapter/bun/test";
|
import { bunTestRunner } from "adapter/bun/test";
|
||||||
|
import { omitKeys } from "core/utils";
|
||||||
const stripConnection = <T extends Record<string, any>>(cfg: T) =>
|
|
||||||
omitKeys(cfg, ["connection"]);
|
|
||||||
|
|
||||||
beforeAll(disableConsoleLog);
|
beforeAll(disableConsoleLog);
|
||||||
afterAll(enableConsoleLog);
|
afterAll(enableConsoleLog);
|
||||||
|
|
||||||
describe("adapter", () => {
|
describe("adapter", () => {
|
||||||
describe("makeConfig", () => {
|
it("makes config", async () => {
|
||||||
it("returns empty config for empty inputs", async () => {
|
expect(omitKeys(await adapter.makeConfig({}), ["connection"])).toEqual({});
|
||||||
const cases: Array<Parameters<typeof adapter.makeConfig>> = [
|
expect(
|
||||||
[{}],
|
omitKeys(await adapter.makeConfig({}, { env: { TEST: "test" } }), ["connection"]),
|
||||||
[{}, { env: { TEST: "test" } }],
|
).toEqual({});
|
||||||
];
|
|
||||||
|
|
||||||
for (const args of cases) {
|
// merges everything returned from `app` with the config
|
||||||
const cfg = await adapter.makeConfig(...(args as any));
|
expect(
|
||||||
expect(stripConnection(cfg)).toEqual({});
|
omitKeys(
|
||||||
}
|
await adapter.makeConfig(
|
||||||
});
|
|
||||||
|
|
||||||
it("merges app output into config", async () => {
|
|
||||||
const cfg = await adapter.makeConfig(
|
|
||||||
{ app: (a) => ({ config: { server: { cors: { origin: a.env.TEST } } } }) },
|
{ app: (a) => ({ config: { server: { cors: { origin: a.env.TEST } } } }) },
|
||||||
{ env: { TEST: "test" } },
|
{ env: { TEST: "test" } },
|
||||||
);
|
),
|
||||||
|
["connection"],
|
||||||
expect(stripConnection(cfg)).toEqual({
|
),
|
||||||
|
).toEqual({
|
||||||
config: { server: { cors: { origin: "test" } } },
|
config: { server: { cors: { origin: "test" } } },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows all properties in app() result", async () => {
|
it("allows all properties in app function", async () => {
|
||||||
const called = mock(() => null);
|
const called = mock(() => null);
|
||||||
|
const config = await adapter.makeConfig(
|
||||||
const cfg = await adapter.makeConfig(
|
|
||||||
{
|
{
|
||||||
app: (env) => ({
|
app: (env) => ({
|
||||||
connection: { url: "test" },
|
connection: { url: "test" },
|
||||||
config: { server: { cors: { origin: "test" } } },
|
config: { server: { cors: { origin: "test" } } },
|
||||||
options: { mode: "db" as const },
|
options: {
|
||||||
|
mode: "db",
|
||||||
|
},
|
||||||
onBuilt: () => {
|
onBuilt: () => {
|
||||||
called();
|
called();
|
||||||
expect(env).toEqual({ foo: "bar" });
|
expect(env).toEqual({ foo: "bar" });
|
||||||
@@ -52,17 +47,13 @@ describe("adapter", () => {
|
|||||||
},
|
},
|
||||||
{ foo: "bar" },
|
{ foo: "bar" },
|
||||||
);
|
);
|
||||||
|
expect(config.connection).toEqual({ url: "test" });
|
||||||
expect(cfg.connection).toEqual({ url: "test" });
|
expect(config.config).toEqual({ server: { cors: { origin: "test" } } });
|
||||||
expect(cfg.config).toEqual({ server: { cors: { origin: "test" } } });
|
expect(config.options).toEqual({ mode: "db" });
|
||||||
expect(cfg.options).toEqual({ mode: "db" });
|
await config.onBuilt?.(null as any);
|
||||||
|
expect(called).toHaveBeenCalled();
|
||||||
await cfg.onBuilt?.({} as any);
|
|
||||||
expect(called).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("adapter test suites", () => {
|
|
||||||
adapterTestSuite(bunTestRunner, {
|
adapterTestSuite(bunTestRunner, {
|
||||||
makeApp: adapter.createFrameworkApp,
|
makeApp: adapter.createFrameworkApp,
|
||||||
label: "framework app",
|
label: "framework app",
|
||||||
@@ -72,5 +63,4 @@ describe("adapter", () => {
|
|||||||
makeApp: adapter.createRuntimeApp,
|
makeApp: adapter.createRuntimeApp,
|
||||||
label: "runtime app",
|
label: "runtime app",
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -70,10 +70,4 @@ describe("Api", async () => {
|
|||||||
expect(params.token_transport).toBe("header");
|
expect(params.token_transport).toBe("header");
|
||||||
expect(params.host).toBe("http://another.com");
|
expect(params.host).toBe("http://another.com");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should extract tokens case insensitive", async () => {
|
|
||||||
const token = await sign({ sub: "test" }, "1234");
|
|
||||||
expect(new Api({ headers: new Headers({ Authorization: `Bearer ${token}` }) }).getAuthState().token).toBe(token);
|
|
||||||
expect(new Api({ headers: new Headers({ Authorization: `bearer ${token}` }) }).getAuthState().token).toBe(token);
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/// <reference types="@types/bun" />
|
/// <reference types="@types/bun" />
|
||||||
import { describe, expect, it, mock } from "bun:test";
|
import { describe, expect, it } from "bun:test";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { getFileFromContext, isFile, isReadableStream, s, jsc } from "core/utils";
|
import { getFileFromContext, isFile, isReadableStream } from "core/utils";
|
||||||
import { MediaApi } from "media/api/MediaApi";
|
import { MediaApi } from "media/api/MediaApi";
|
||||||
import { assetsPath, assetsTmpPath } from "../helper";
|
import { assetsPath, assetsTmpPath } from "../helper";
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ describe("MediaApi", () => {
|
|||||||
expect(isReadableStream(res.body)).toBe(true);
|
expect(isReadableStream(res.body)).toBe(true);
|
||||||
expect(isReadableStream(res.res.body)).toBe(true);
|
expect(isReadableStream(res.res.body)).toBe(true);
|
||||||
|
|
||||||
const blob = (await res.res.blob()) as File;
|
const blob = await res.res.blob();
|
||||||
expect(isFile(blob)).toBe(true);
|
expect(isFile(blob)).toBe(true);
|
||||||
expect(blob.size).toBeGreaterThan(0);
|
expect(blob.size).toBeGreaterThan(0);
|
||||||
expect(blob.type).toBe("image/png");
|
expect(blob.type).toBe("image/png");
|
||||||
@@ -110,11 +110,10 @@ describe("MediaApi", () => {
|
|||||||
const api = new MediaApi({}, mockedBackend.request);
|
const api = new MediaApi({}, mockedBackend.request);
|
||||||
|
|
||||||
const name = "image.png";
|
const name = "image.png";
|
||||||
const res = await api.getFile(name);
|
const res = await api.getFileStream(name);
|
||||||
const stream = await api.getFileStream(name);
|
expect(isReadableStream(res)).toBe(true);
|
||||||
expect(isReadableStream(stream)).toBe(true);
|
|
||||||
|
|
||||||
const blob = (await res.res.blob()) as File;
|
const blob = await new Response(res).blob();
|
||||||
expect(isFile(blob)).toBe(true);
|
expect(isFile(blob)).toBe(true);
|
||||||
expect(blob.size).toBeGreaterThan(0);
|
expect(blob.size).toBeGreaterThan(0);
|
||||||
expect(blob.type).toBe("image/png");
|
expect(blob.type).toBe("image/png");
|
||||||
@@ -163,30 +162,4 @@ describe("MediaApi", () => {
|
|||||||
await matches(api.upload(response.body!, { filename: "readable.png" }), "readable.png");
|
await matches(api.upload(response.body!, { filename: "readable.png" }), "readable.png");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add overwrite query for entity upload", async (c) => {
|
|
||||||
const call = mock(() => null);
|
|
||||||
const hono = new Hono().post(
|
|
||||||
"/api/media/entity/:entity/:id/:field",
|
|
||||||
jsc("query", s.object({ overwrite: s.boolean().optional() })),
|
|
||||||
async (c) => {
|
|
||||||
const { overwrite } = c.req.valid("query");
|
|
||||||
expect(overwrite).toBe(true);
|
|
||||||
call();
|
|
||||||
return c.json({ ok: true });
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const api = new MediaApi(
|
|
||||||
{
|
|
||||||
upload_fetcher: hono.request,
|
|
||||||
},
|
|
||||||
hono.request,
|
|
||||||
);
|
|
||||||
const file = Bun.file(`${assetsPath}/image.png`);
|
|
||||||
const res = await api.uploadToEntity("posts", 1, "cover", file as any, {
|
|
||||||
overwrite: true,
|
|
||||||
});
|
|
||||||
expect(res.ok).toBe(true);
|
|
||||||
expect(call).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,27 +38,4 @@ describe("Authenticator", async () => {
|
|||||||
expect(cookie).toStartWith("auth=");
|
expect(cookie).toStartWith("auth=");
|
||||||
expect(cookie).toEndWith("HttpOnly; Secure; SameSite=Strict");
|
expect(cookie).toEndWith("HttpOnly; Secure; SameSite=Strict");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("bearer token is case insensitive", async () => {
|
|
||||||
const auth = new Authenticator({}, null as any, {
|
|
||||||
jwt: {
|
|
||||||
secret: "secret",
|
|
||||||
fields: ["sub"],
|
|
||||||
},
|
|
||||||
cookie: {
|
|
||||||
sameSite: "strict",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const token = await auth.jwt({ sub: "test" });
|
|
||||||
|
|
||||||
const res = await auth.resolveAuthFromRequest(new Headers({
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
}));
|
|
||||||
expect((res as any).sub).toBe("test")
|
|
||||||
|
|
||||||
const res2 = await auth.resolveAuthFromRequest(new Headers({
|
|
||||||
Authorization: `bearer ${token}`,
|
|
||||||
}));
|
|
||||||
expect((res2 as any).sub).toBe("test")
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
import { PasswordStrategy } from "auth/authenticate/strategies/PasswordStrategy";
|
|
||||||
import { describe, expect, it } from "bun:test";
|
|
||||||
|
|
||||||
describe("PasswordStrategy", () => {
|
|
||||||
it("should enforce provided minimum length", async () => {
|
|
||||||
const strategy = new PasswordStrategy({ minLength: 8, hashing: "plain" });
|
|
||||||
|
|
||||||
expect(strategy.verify("password")({} as any)).rejects.toThrow();
|
|
||||||
expect(
|
|
||||||
strategy.verify("password1234")({ strategy_value: "password1234" } as any),
|
|
||||||
).resolves.toBeUndefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -41,12 +41,12 @@ describe("postgres", () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
if (!(await isPostgresRunning())) {
|
if (!(await isPostgresRunning())) {
|
||||||
await $`docker run --rm --name bknd-test-postgres -d -e POSTGRES_PASSWORD=${credentials.password} -e POSTGRES_USER=${credentials.user} -e POSTGRES_DB=${credentials.database} -p ${credentials.port}:5432 postgres:17`;
|
await $`docker run --rm --name bknd-test-postgres -d -e POSTGRES_PASSWORD=${credentials.password} -e POSTGRES_USER=${credentials.user} -e POSTGRES_DB=${credentials.database} -p ${credentials.port}:5432 postgres:17`;
|
||||||
await $waitUntil("Postgres is running", isPostgresRunning, 500, 20);
|
await $waitUntil("Postgres is running", isPostgresRunning);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
}
|
}
|
||||||
|
|
||||||
disableConsoleLog();
|
disableConsoleLog();
|
||||||
}, 30000);
|
});
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
if (await isPostgresRunning()) {
|
if (await isPostgresRunning()) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ describe("[data] WithBuilder", async () => {
|
|||||||
const res2 = qb2.compile();
|
const res2 = qb2.compile();
|
||||||
|
|
||||||
expect(res2.sql).toBe(
|
expect(res2.sql).toBe(
|
||||||
'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username") from (select "author"."id" as "id", "author"."username" as "username" from "users" as "author" where "author"."id" = "posts"."author_id" order by "author"."id" asc limit ?) as obj) as "author" from "posts"',
|
'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username") from (select "users"."id" as "id", "users"."username" as "username" from "users" as "author" where "author"."id" = "posts"."author_id" order by "users"."id" asc limit ?) as obj) as "author" from "posts"',
|
||||||
);
|
);
|
||||||
expect(res2.parameters).toEqual([1]);
|
expect(res2.parameters).toEqual([1]);
|
||||||
});
|
});
|
||||||
@@ -192,7 +192,9 @@ describe("[data] WithBuilder", async () => {
|
|||||||
{ single: {} },
|
{ single: {} },
|
||||||
);
|
);
|
||||||
const res = qb.compile();
|
const res = qb.compile();
|
||||||
expect(res.sql).toMatchSnapshot();
|
expect(res.sql).toBe(
|
||||||
|
'select (select json_object(\'id\', "obj"."id", \'path\', "obj"."path") from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" order by "media"."id" asc limit ?) as obj) as "single" from "categories"',
|
||||||
|
);
|
||||||
expect(res.parameters).toEqual(["categories.single", 1]);
|
expect(res.parameters).toEqual(["categories.single", 1]);
|
||||||
|
|
||||||
const qb2 = WithBuilder.addClause(
|
const qb2 = WithBuilder.addClause(
|
||||||
@@ -202,7 +204,9 @@ describe("[data] WithBuilder", async () => {
|
|||||||
{ multiple: {} },
|
{ multiple: {} },
|
||||||
);
|
);
|
||||||
const res2 = qb2.compile();
|
const res2 = qb2.compile();
|
||||||
expect(res2.sql).toMatchSnapshot();
|
expect(res2.sql).toBe(
|
||||||
|
'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'path\', "agg"."path")), \'[]\') from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" order by "media"."id" asc limit ? offset ?) as agg) as "multiple" from "categories"',
|
||||||
|
);
|
||||||
expect(res2.parameters).toEqual(["categories.multiple", 10, 0]);
|
expect(res2.parameters).toEqual(["categories.multiple", 10, 0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -267,7 +271,9 @@ describe("[data] WithBuilder", async () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
//prettyPrintQb(qb);
|
//prettyPrintQb(qb);
|
||||||
expect(qb.compile().sql).toMatchSnapshot();
|
expect(qb.compile().sql).toBe(
|
||||||
|
'select (select json_object(\'id\', "obj"."id", \'username\', "obj"."username", \'avatar\', "obj"."avatar") from (select "users"."id" as "id", "users"."username" as "username", (select json_object(\'id\', "obj"."id", \'path\', "obj"."path") from (select "media"."id" as "id", "media"."path" as "path" from "media" where "media"."reference" = ? and "users"."id" = "media"."entity_id" order by "media"."id" asc limit ?) as obj) as "avatar" from "users" as "users" where "users"."id" = "posts"."users_id" order by "users"."username" asc limit ?) as obj) as "users" from "posts"',
|
||||||
|
);
|
||||||
expect(qb.compile().parameters).toEqual(["users.avatar", 1, 1]);
|
expect(qb.compile().parameters).toEqual(["users.avatar", 1, 1]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -307,7 +313,9 @@ describe("[data] WithBuilder", async () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(qb.compile().sql).toMatchSnapshot();
|
expect(qb.compile().sql).toBe(
|
||||||
|
'select (select coalesce(json_group_array(json_object(\'id\', "agg"."id", \'posts_id\', "agg"."posts_id", \'users_id\', "agg"."users_id", \'users\', "agg"."users")), \'[]\') from (select "comments"."id" as "id", "comments"."posts_id" as "posts_id", "comments"."users_id" as "users_id", (select json_object(\'username\', "obj"."username") from (select "users"."username" as "username" from "users" as "users" where "users"."id" = "comments"."users_id" order by "users"."id" asc limit ?) as obj) as "users" from "comments" as "comments" where "comments"."posts_id" = "posts"."id" order by "comments"."id" asc limit ? offset ?) as agg) as "comments" from "posts"',
|
||||||
|
);
|
||||||
expect(qb.compile().parameters).toEqual([1, 12, 0]);
|
expect(qb.compile().parameters).toEqual([1, 12, 0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots
|
|
||||||
|
|
||||||
exports[`[data] WithBuilder recursive compiles with singles 1`] = `"select (select json_object('id', "obj"."id", 'username', "obj"."username", 'avatar', "obj"."avatar") from (select "users"."id" as "id", "users"."username" as "username", (select json_object('id', "obj"."id", 'path', "obj"."path") from (select "avatar"."id" as "id", "avatar"."path" as "path" from "media" where "media"."reference" = ? and "users"."id" = "media"."entity_id" order by "avatar"."id" asc limit ?) as obj) as "avatar" from "users" as "users" where "users"."id" = "posts"."users_id" order by "users"."username" asc limit ?) as obj) as "users" from "posts""`;
|
|
||||||
|
|
||||||
exports[`[data] WithBuilder recursive compiles with many 1`] = `"select (select coalesce(json_group_array(json_object('id', "agg"."id", 'posts_id', "agg"."posts_id", 'users_id', "agg"."users_id", 'users', "agg"."users")), '[]') from (select "comments"."id" as "id", "comments"."posts_id" as "posts_id", "comments"."users_id" as "users_id", (select json_object('username', "obj"."username") from (select "users"."username" as "username" from "users" as "users" where "users"."id" = "comments"."users_id" order by "users"."id" asc limit ?) as obj) as "users" from "comments" as "comments" where "comments"."posts_id" = "posts"."id" order by "comments"."id" asc limit ? offset ?) as agg) as "comments" from "posts""`;
|
|
||||||
|
|
||||||
exports[`[data] WithBuilder polymorphic 1`] = `"select (select json_object('id', "obj"."id", 'path', "obj"."path") from (select "single"."id" as "id", "single"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" order by "single"."id" asc limit ?) as obj) as "single" from "categories""`;
|
|
||||||
|
|
||||||
exports[`[data] WithBuilder polymorphic 2`] = `"select (select coalesce(json_group_array(json_object('id', "agg"."id", 'path', "agg"."path")), '[]') from (select "multiple"."id" as "id", "multiple"."path" as "path" from "media" where "media"."reference" = ? and "categories"."id" = "media"."entity_id" order by "multiple"."id" asc limit ? offset ?) as agg) as "multiple" from "categories""`;
|
|
||||||
@@ -1,15 +1,9 @@
|
|||||||
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
import { afterAll, beforeAll, describe, expect, it } from "bun:test";
|
||||||
import { App, createApp, type AuthResponse } from "../../src";
|
import { App, createApp, type AuthResponse } from "../../src";
|
||||||
import { auth } from "../../src/modules/middlewares";
|
import { auth } from "../../src/modules/middlewares";
|
||||||
import {
|
import { randomString, secureRandomString, withDisabledConsole } from "../../src/core/utils";
|
||||||
mergeObject,
|
|
||||||
randomString,
|
|
||||||
secureRandomString,
|
|
||||||
withDisabledConsole,
|
|
||||||
} from "../../src/core/utils";
|
|
||||||
import { disableConsoleLog, enableConsoleLog } from "core/utils/test";
|
import { disableConsoleLog, enableConsoleLog } from "core/utils/test";
|
||||||
import { getDummyConnection } from "../helper";
|
import { getDummyConnection } from "../helper";
|
||||||
import type { AppAuthSchema } from "auth/auth-schema";
|
|
||||||
|
|
||||||
beforeAll(disableConsoleLog);
|
beforeAll(disableConsoleLog);
|
||||||
afterAll(enableConsoleLog);
|
afterAll(enableConsoleLog);
|
||||||
@@ -68,12 +62,12 @@ const configs = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function createAuthApp(config?: Partial<AppAuthSchema>) {
|
function createAuthApp() {
|
||||||
const { dummyConnection } = getDummyConnection();
|
const { dummyConnection } = getDummyConnection();
|
||||||
const app = createApp({
|
const app = createApp({
|
||||||
connection: dummyConnection,
|
connection: dummyConnection,
|
||||||
config: {
|
config: {
|
||||||
auth: mergeObject(configs.auth, config ?? {}),
|
auth: configs.auth,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -138,16 +132,6 @@ const fns = <Mode extends "cookie" | "token" = "token">(app: App, mode?: Mode) =
|
|||||||
|
|
||||||
return { res, data };
|
return { res, data };
|
||||||
},
|
},
|
||||||
register: async (user: any): Promise<{ res: Response; data: AuthResponse }> => {
|
|
||||||
const res = (await app.server.request("/api/auth/password/register", {
|
|
||||||
method: "POST",
|
|
||||||
headers: headers(),
|
|
||||||
body: body(user),
|
|
||||||
})) as Response;
|
|
||||||
const data = mode === "cookie" ? getCookie(res, "auth") : await res.json();
|
|
||||||
|
|
||||||
return { res, data };
|
|
||||||
},
|
|
||||||
me: async (token?: string): Promise<Pick<AuthResponse, "user">> => {
|
me: async (token?: string): Promise<Pick<AuthResponse, "user">> => {
|
||||||
const res = (await app.server.request("/api/auth/me", {
|
const res = (await app.server.request("/api/auth/me", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@@ -261,61 +245,4 @@ describe("integration auth", () => {
|
|||||||
expect(await $fns.me()).toEqual({ user: null as any });
|
expect(await $fns.me()).toEqual({ user: null as any });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should register users with default role", async () => {
|
|
||||||
const app = createAuthApp({ default_role_register: "guest" });
|
|
||||||
await app.build();
|
|
||||||
const $fns = fns(app);
|
|
||||||
|
|
||||||
// takes default role
|
|
||||||
expect(
|
|
||||||
await app
|
|
||||||
.createUser({
|
|
||||||
email: "test@bknd.io",
|
|
||||||
password: "12345678",
|
|
||||||
})
|
|
||||||
.then((r) => r.role),
|
|
||||||
).toBe("guest");
|
|
||||||
|
|
||||||
// throws error if role doesn't exist
|
|
||||||
expect(
|
|
||||||
app.createUser({
|
|
||||||
email: "test@bknd.io",
|
|
||||||
password: "12345678",
|
|
||||||
role: "doesnt exist",
|
|
||||||
}),
|
|
||||||
).rejects.toThrow();
|
|
||||||
|
|
||||||
// takes role if provided
|
|
||||||
expect(
|
|
||||||
await app
|
|
||||||
.createUser({
|
|
||||||
email: "test2@bknd.io",
|
|
||||||
password: "12345678",
|
|
||||||
role: "admin",
|
|
||||||
})
|
|
||||||
.then((r) => r.role),
|
|
||||||
).toBe("admin");
|
|
||||||
|
|
||||||
// registering with role is not allowed
|
|
||||||
expect(
|
|
||||||
await $fns
|
|
||||||
.register({
|
|
||||||
email: "test3@bknd.io",
|
|
||||||
password: "12345678",
|
|
||||||
role: "admin",
|
|
||||||
})
|
|
||||||
.then((r) => r.res.ok),
|
|
||||||
).toBe(false);
|
|
||||||
|
|
||||||
// takes default role
|
|
||||||
expect(
|
|
||||||
await $fns
|
|
||||||
.register({
|
|
||||||
email: "test3@bknd.io",
|
|
||||||
password: "12345678",
|
|
||||||
})
|
|
||||||
.then((r) => r.data.user.role),
|
|
||||||
).toBe("guest");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import type { TAppMediaConfig } from "../../src/media/media-schema";
|
|||||||
import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter";
|
import { StorageLocalAdapter } from "adapter/node/storage/StorageLocalAdapter";
|
||||||
import { assetsPath, assetsTmpPath } from "../helper";
|
import { assetsPath, assetsTmpPath } from "../helper";
|
||||||
import { disableConsoleLog, enableConsoleLog } from "core/utils/test";
|
import { disableConsoleLog, enableConsoleLog } from "core/utils/test";
|
||||||
import * as proto from "data/prototype";
|
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
disableConsoleLog();
|
disableConsoleLog();
|
||||||
@@ -129,87 +128,4 @@ describe("MediaController", () => {
|
|||||||
expect(destFile.exists()).resolves.toBe(true);
|
expect(destFile.exists()).resolves.toBe(true);
|
||||||
await destFile.delete();
|
await destFile.delete();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("entity upload with max_items and overwrite", async () => {
|
|
||||||
const app = createApp({
|
|
||||||
config: {
|
|
||||||
media: mergeObject(
|
|
||||||
{
|
|
||||||
enabled: true,
|
|
||||||
adapter: {
|
|
||||||
type: "local",
|
|
||||||
config: {
|
|
||||||
path: assetsTmpPath,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
),
|
|
||||||
data: {
|
|
||||||
entities: {
|
|
||||||
posts: proto
|
|
||||||
.entity("posts", {
|
|
||||||
title: proto.text(),
|
|
||||||
cover: proto.medium(),
|
|
||||||
})
|
|
||||||
.toJSON(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await app.build();
|
|
||||||
|
|
||||||
// create a post first
|
|
||||||
const createRes = await app.server.request("/api/data/entity/posts", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ title: "Test Post" }),
|
|
||||||
});
|
|
||||||
expect(createRes.status).toBe(201);
|
|
||||||
const { data: post } = (await createRes.json()) as any;
|
|
||||||
|
|
||||||
const file = Bun.file(path);
|
|
||||||
const uploadedFiles: string[] = [];
|
|
||||||
|
|
||||||
// upload first file to entity (should succeed)
|
|
||||||
const res1 = await app.server.request(`/api/media/entity/posts/${post.id}/cover`, {
|
|
||||||
method: "POST",
|
|
||||||
body: file,
|
|
||||||
});
|
|
||||||
expect(res1.status).toBe(201);
|
|
||||||
const result1 = (await res1.json()) as any;
|
|
||||||
uploadedFiles.push(result1.name);
|
|
||||||
|
|
||||||
// upload second file without overwrite (should fail - max_items reached)
|
|
||||||
const res2 = await app.server.request(`/api/media/entity/posts/${post.id}/cover`, {
|
|
||||||
method: "POST",
|
|
||||||
body: file,
|
|
||||||
});
|
|
||||||
expect(res2.status).toBe(400);
|
|
||||||
const result2 = (await res2.json()) as any;
|
|
||||||
expect(result2.error).toContain("Max items");
|
|
||||||
|
|
||||||
// upload third file with overwrite=true (should succeed and delete old file)
|
|
||||||
const res3 = await app.server.request(
|
|
||||||
`/api/media/entity/posts/${post.id}/cover?overwrite=true`,
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
body: file,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
expect(res3.status).toBe(201);
|
|
||||||
const result3 = (await res3.json()) as any;
|
|
||||||
uploadedFiles.push(result3.name);
|
|
||||||
|
|
||||||
// verify old file was deleted from storage
|
|
||||||
const oldFile = Bun.file(assetsTmpPath + "/" + uploadedFiles[0]);
|
|
||||||
expect(await oldFile.exists()).toBe(false);
|
|
||||||
|
|
||||||
// verify new file exists
|
|
||||||
const newFile = Bun.file(assetsTmpPath + "/" + uploadedFiles[1]);
|
|
||||||
expect(await newFile.exists()).toBe(true);
|
|
||||||
|
|
||||||
// cleanup
|
|
||||||
await newFile.delete();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ describe("AppAuth", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await app.build();
|
await app.build();
|
||||||
app.registerAdminController({ forceDev: true });
|
app.registerAdminController();
|
||||||
const spy = spyOn(app.module.auth.authenticator, "requestCookieRefresh");
|
const spy = spyOn(app.module.auth.authenticator, "requestCookieRefresh");
|
||||||
|
|
||||||
// register custom route
|
// register custom route
|
||||||
@@ -223,32 +223,4 @@ describe("AppAuth", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("default role for registration must be a valid role", async () => {
|
|
||||||
const app = createApp({
|
|
||||||
config: {
|
|
||||||
auth: {
|
|
||||||
enabled: true,
|
|
||||||
jwt: {
|
|
||||||
secret: "123456",
|
|
||||||
},
|
|
||||||
allow_register: true,
|
|
||||||
roles: {
|
|
||||||
guest: {
|
|
||||||
is_default: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await app.build();
|
|
||||||
|
|
||||||
const auth = app.module.auth;
|
|
||||||
// doesn't allow invalid role
|
|
||||||
expect(auth.schema().patch("default_role_register", "admin")).rejects.toThrow();
|
|
||||||
// allows valid role
|
|
||||||
await auth.schema().patch("default_role_register", "guest");
|
|
||||||
expect(auth.toJSON().default_role_register).toBe("guest");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,284 +0,0 @@
|
|||||||
import { describe, it, expect, beforeEach } from "vitest";
|
|
||||||
import { AppReduced, type AppType } from "ui/client/utils/AppReduced";
|
|
||||||
import type { BkndAdminProps } from "ui/Admin";
|
|
||||||
|
|
||||||
// Import the normalizeAdminPath function for testing
|
|
||||||
// Note: This assumes the function is exported or we need to test it indirectly through public methods
|
|
||||||
|
|
||||||
describe("AppReduced", () => {
|
|
||||||
let mockAppJson: AppType;
|
|
||||||
let appReduced: AppReduced;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
mockAppJson = {
|
|
||||||
data: {
|
|
||||||
entities: {},
|
|
||||||
relations: {},
|
|
||||||
},
|
|
||||||
flows: {
|
|
||||||
flows: {},
|
|
||||||
},
|
|
||||||
auth: {},
|
|
||||||
} as AppType;
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("getSettingsPath", () => {
|
|
||||||
it("should return settings path with basepath", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getSettingsPath();
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/settings");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return settings path with empty basepath", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getSettingsPath();
|
|
||||||
|
|
||||||
expect(result).toBe("~/settings");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should append additional path segments", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getSettingsPath(["user", "profile"]);
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/settings/user/profile");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should normalize multiple slashes", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "//admin//",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getSettingsPath(["//user//"]);
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/settings/user");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle basepath without leading slash", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getSettingsPath();
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/settings");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("getAbsolutePath", () => {
|
|
||||||
it("should return absolute path with basepath", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getAbsolutePath("dashboard");
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/dashboard");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return base path when no path provided", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getAbsolutePath();
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should normalize paths correctly", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "//admin//",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getAbsolutePath("//dashboard//");
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/dashboard");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("options getter", () => {
|
|
||||||
it("should return merged options with defaults", () => {
|
|
||||||
const customOptions: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/custom-admin",
|
|
||||||
logo_return_path: "/custom-home",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, customOptions);
|
|
||||||
const options = appReduced.options;
|
|
||||||
|
|
||||||
expect(options).toEqual({
|
|
||||||
logo_return_path: "/custom-home",
|
|
||||||
basepath: "/custom-admin",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should use default logo_return_path when not provided", () => {
|
|
||||||
const customOptions: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, customOptions);
|
|
||||||
const options = appReduced.options;
|
|
||||||
|
|
||||||
expect(options.logo_return_path).toBe("/");
|
|
||||||
expect(options.basepath).toBe("/admin");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("path normalization behavior", () => {
|
|
||||||
it("should normalize duplicate slashes in settings path", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getSettingsPath(["//nested//path//"]);
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/settings/nested/path");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle root path normalization", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getAbsolutePath();
|
|
||||||
|
|
||||||
// The normalizeAdminPath function removes trailing slashes except for root "/"
|
|
||||||
// When basepath is "/", the result is "~/" which becomes "~" after normalization
|
|
||||||
expect(result).toBe("~");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should preserve entity paths ending with slash", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getAbsolutePath("entity/");
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/entity");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should remove trailing slashes from non-entity paths", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getAbsolutePath("dashboard/");
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/dashboard");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("withBasePath - double slash fix (admin_basepath with trailing slash)", () => {
|
|
||||||
it("should not produce double slashes when admin_basepath has trailing slash", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.withBasePath("/data");
|
|
||||||
|
|
||||||
expect(result).toBe("/admin/data");
|
|
||||||
expect(result).not.toContain("//");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should work correctly when admin_basepath has no trailing slash", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.withBasePath("/data");
|
|
||||||
|
|
||||||
expect(result).toBe("/admin/data");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle absolute paths with admin_basepath trailing slash", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getAbsolutePath("data");
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/data");
|
|
||||||
expect(result).not.toContain("//");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle settings path with admin_basepath trailing slash", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getSettingsPath(["general"]);
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/settings/general");
|
|
||||||
expect(result).not.toContain("//");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("edge cases", () => {
|
|
||||||
it("should handle undefined basepath", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getSettingsPath();
|
|
||||||
|
|
||||||
// When basepath is undefined, it defaults to empty string
|
|
||||||
expect(result).toBe("~/settings");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle null path segments", () => {
|
|
||||||
const options: BkndAdminProps["config"] = {
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/",
|
|
||||||
};
|
|
||||||
|
|
||||||
appReduced = new AppReduced(mockAppJson, options);
|
|
||||||
const result = appReduced.getSettingsPath(["", "valid", ""]);
|
|
||||||
|
|
||||||
expect(result).toBe("~/admin/settings/valid");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+12
-1
@@ -3,7 +3,17 @@ import c from "picocolors";
|
|||||||
import { formatNumber } from "bknd/utils";
|
import { formatNumber } from "bknd/utils";
|
||||||
|
|
||||||
const deps = Object.keys(pkg.dependencies);
|
const deps = Object.keys(pkg.dependencies);
|
||||||
const external = ["jsonv-ts/*", "wrangler", "bknd", "bknd/*", ...deps];
|
const external = [
|
||||||
|
"jsonv-ts/*",
|
||||||
|
"wrangler",
|
||||||
|
"bknd",
|
||||||
|
"bknd/*",
|
||||||
|
"@vitejs/plugin-react",
|
||||||
|
"vite",
|
||||||
|
"@tailwindcss/vite",
|
||||||
|
"@cloudflare/vite-plugin",
|
||||||
|
...deps,
|
||||||
|
];
|
||||||
|
|
||||||
const result = await Bun.build({
|
const result = await Bun.build({
|
||||||
entrypoints: ["./src/cli/index.ts"],
|
entrypoints: ["./src/cli/index.ts"],
|
||||||
@@ -11,6 +21,7 @@ const result = await Bun.build({
|
|||||||
outdir: "./dist/cli",
|
outdir: "./dist/cli",
|
||||||
env: "PUBLIC_*",
|
env: "PUBLIC_*",
|
||||||
minify: true,
|
minify: true,
|
||||||
|
banner: `const __originalLog=console.log;console.log=(...o)=>{const n=o[0];"string"==typeof n&&n.includes("[dotenv@")||__originalLog.apply(console,o)};`,
|
||||||
external,
|
external,
|
||||||
define: {
|
define: {
|
||||||
__isDev: "0",
|
__isDev: "0",
|
||||||
|
|||||||
+8
-106
@@ -2,8 +2,6 @@ import { $ } from "bun";
|
|||||||
import * as tsup from "tsup";
|
import * as tsup from "tsup";
|
||||||
import pkg from "./package.json" with { type: "json" };
|
import pkg from "./package.json" with { type: "json" };
|
||||||
import c from "picocolors";
|
import c from "picocolors";
|
||||||
import { watch as fsWatch, readdirSync, rmSync } from "node:fs";
|
|
||||||
import { join } from "node:path";
|
|
||||||
|
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
const watch = args.includes("--watch");
|
const watch = args.includes("--watch");
|
||||||
@@ -27,18 +25,7 @@ const define = {
|
|||||||
|
|
||||||
if (clean) {
|
if (clean) {
|
||||||
console.info("Cleaning dist (w/o static)");
|
console.info("Cleaning dist (w/o static)");
|
||||||
// Cross-platform clean: remove all files/folders in dist except static
|
await $`find dist -mindepth 1 ! -path "dist/static/*" ! -path "dist/static" -exec rm -rf {} +`;
|
||||||
const distPath = join(import.meta.dir, "dist");
|
|
||||||
try {
|
|
||||||
const entries = readdirSync(distPath);
|
|
||||||
for (const entry of entries) {
|
|
||||||
if (entry === "static") continue;
|
|
||||||
const entryPath = join(distPath, entry);
|
|
||||||
rmSync(entryPath, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// dist may not exist yet, ignore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let types_running = false;
|
let types_running = false;
|
||||||
@@ -96,8 +83,7 @@ async function buildApi() {
|
|||||||
await tsup.build({
|
await tsup.build({
|
||||||
minify,
|
minify,
|
||||||
sourcemap,
|
sourcemap,
|
||||||
// don't use tsup's broken watch, we'll handle it ourselves
|
watch,
|
||||||
watch: false,
|
|
||||||
define,
|
define,
|
||||||
entry: [
|
entry: [
|
||||||
"src/index.ts",
|
"src/index.ts",
|
||||||
@@ -125,10 +111,7 @@ async function buildApi() {
|
|||||||
|
|
||||||
async function rewriteClient(path: string) {
|
async function rewriteClient(path: string) {
|
||||||
const bundle = await Bun.file(path).text();
|
const bundle = await Bun.file(path).text();
|
||||||
await Bun.write(
|
await Bun.write(path, '"use client";\n' + bundle.replaceAll("ui/client", "bknd/client"));
|
||||||
path,
|
|
||||||
'"use client";\n' + bundle.replaceAll("ui/client", "bknd/client"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,7 +121,7 @@ async function buildUi() {
|
|||||||
const base = {
|
const base = {
|
||||||
minify,
|
minify,
|
||||||
sourcemap,
|
sourcemap,
|
||||||
watch: false,
|
watch,
|
||||||
define,
|
define,
|
||||||
external: [
|
external: [
|
||||||
...external,
|
...external,
|
||||||
@@ -197,7 +180,7 @@ async function buildUiElements() {
|
|||||||
await tsup.build({
|
await tsup.build({
|
||||||
minify,
|
minify,
|
||||||
sourcemap,
|
sourcemap,
|
||||||
watch: false,
|
watch,
|
||||||
define,
|
define,
|
||||||
entry: ["src/ui/elements/index.ts"],
|
entry: ["src/ui/elements/index.ts"],
|
||||||
outDir: "dist/ui/elements",
|
outDir: "dist/ui/elements",
|
||||||
@@ -238,14 +221,11 @@ async function buildUiElements() {
|
|||||||
/**
|
/**
|
||||||
* Building adapters
|
* Building adapters
|
||||||
*/
|
*/
|
||||||
function baseConfig(
|
function baseConfig(adapter: string, overrides: Partial<tsup.Options> = {}): tsup.Options {
|
||||||
adapter: string,
|
|
||||||
overrides: Partial<tsup.Options> = {},
|
|
||||||
): tsup.Options {
|
|
||||||
return {
|
return {
|
||||||
minify,
|
minify,
|
||||||
sourcemap,
|
sourcemap,
|
||||||
watch: false,
|
watch,
|
||||||
entry: [`src/adapter/${adapter}/index.ts`],
|
entry: [`src/adapter/${adapter}/index.ts`],
|
||||||
format: ["esm"],
|
format: ["esm"],
|
||||||
platform: "neutral",
|
platform: "neutral",
|
||||||
@@ -325,26 +305,6 @@ async function buildAdapters() {
|
|||||||
platform: "node",
|
platform: "node",
|
||||||
}),
|
}),
|
||||||
|
|
||||||
tsup.build({
|
|
||||||
...baseConfig("tanstack-start"),
|
|
||||||
platform: "node",
|
|
||||||
}),
|
|
||||||
|
|
||||||
tsup.build({
|
|
||||||
...baseConfig("universal"),
|
|
||||||
platform: "neutral",
|
|
||||||
}),
|
|
||||||
|
|
||||||
tsup.build({
|
|
||||||
...baseConfig("sveltekit"),
|
|
||||||
platform: "node",
|
|
||||||
}),
|
|
||||||
|
|
||||||
tsup.build({
|
|
||||||
...baseConfig("nuxt"),
|
|
||||||
platform: "node",
|
|
||||||
}),
|
|
||||||
|
|
||||||
tsup.build({
|
tsup.build({
|
||||||
...baseConfig("node"),
|
...baseConfig("node"),
|
||||||
platform: "node",
|
platform: "node",
|
||||||
@@ -372,65 +332,7 @@ async function buildAdapters() {
|
|||||||
metafile: false,
|
metafile: false,
|
||||||
external: [/^bun\:.*/],
|
external: [/^bun\:.*/],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildAll() {
|
await Promise.all([buildApi(), buildUi(), buildUiElements(), buildAdapters()]);
|
||||||
await Promise.all([
|
|
||||||
buildApi(),
|
|
||||||
buildUi(),
|
|
||||||
buildUiElements(),
|
|
||||||
buildAdapters(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// initial build
|
|
||||||
await buildAll();
|
|
||||||
|
|
||||||
// custom watcher since tsup's watch is broken in 8.3.5+
|
|
||||||
if (watch) {
|
|
||||||
oldConsole.log(c.cyan("[Watch]"), "watching for changes in src/...");
|
|
||||||
|
|
||||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
||||||
let isBuilding = false;
|
|
||||||
|
|
||||||
const rebuild = async () => {
|
|
||||||
if (isBuilding) return;
|
|
||||||
isBuilding = true;
|
|
||||||
oldConsole.log(c.cyan("[Watch]"), "rebuilding...");
|
|
||||||
try {
|
|
||||||
await buildAll();
|
|
||||||
oldConsole.log(c.cyan("[Watch]"), c.green("done"));
|
|
||||||
} catch (e) {
|
|
||||||
oldConsole.warn(c.cyan("[Watch]"), c.red("build failed"), e);
|
|
||||||
}
|
|
||||||
isBuilding = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const debouncedRebuild = () => {
|
|
||||||
if (debounceTimer) clearTimeout(debounceTimer);
|
|
||||||
debounceTimer = setTimeout(rebuild, 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
// watch src directory recursively
|
|
||||||
fsWatch(
|
|
||||||
join(import.meta.dir, "src"),
|
|
||||||
{ recursive: true },
|
|
||||||
(event, filename) => {
|
|
||||||
if (!filename) return;
|
|
||||||
// ignore non-source files
|
|
||||||
if (
|
|
||||||
!filename.endsWith(".ts") &&
|
|
||||||
!filename.endsWith(".tsx") &&
|
|
||||||
!filename.endsWith(".css")
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
oldConsole.log(c.cyan("[Watch]"), c.dim(`${event}: ${filename}`));
|
|
||||||
debouncedRebuild();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// keep process alive
|
|
||||||
await new Promise(() => {});
|
|
||||||
}
|
|
||||||
|
|||||||
+2
-3
@@ -3,11 +3,10 @@ import path from "node:path";
|
|||||||
import c from "picocolors";
|
import c from "picocolors";
|
||||||
|
|
||||||
const basePath = new URL(import.meta.resolve("../../")).pathname.slice(0, -1);
|
const basePath = new URL(import.meta.resolve("../../")).pathname.slice(0, -1);
|
||||||
type RunOptions = Omit<Bun.SpawnOptions.SpawnOptions<"ignore", "pipe", "pipe">, "stdout" | "stderr">;
|
|
||||||
|
|
||||||
async function run(
|
async function run(
|
||||||
cmd: string[] | string,
|
cmd: string[] | string,
|
||||||
opts: RunOptions,
|
opts: Bun.SpawnOptions.OptionsObject & {},
|
||||||
onChunk: (chunk: string, resolve: (data: any) => void, reject: (err: Error) => void) => void,
|
onChunk: (chunk: string, resolve: (data: any) => void, reject: (err: Error) => void) => void,
|
||||||
): Promise<{ proc: Bun.Subprocess; data: any }> {
|
): Promise<{ proc: Bun.Subprocess; data: any }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -18,7 +17,7 @@ async function run(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Read from stdout
|
// Read from stdout
|
||||||
const reader = proc.stdout.getReader();
|
const reader = (proc.stdout as ReadableStream).getReader();
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
// Function to read chunks
|
// Function to read chunks
|
||||||
|
|||||||
@@ -15,12 +15,6 @@ const configs = {
|
|||||||
nextjs: {
|
nextjs: {
|
||||||
base_path: "/admin",
|
base_path: "/admin",
|
||||||
},
|
},
|
||||||
meta: {
|
|
||||||
base_path: "/admin",
|
|
||||||
},
|
|
||||||
nuxt: {
|
|
||||||
base_path: "/admin",
|
|
||||||
},
|
|
||||||
astro: {
|
astro: {
|
||||||
base_path: "/admin",
|
base_path: "/admin",
|
||||||
},
|
},
|
||||||
@@ -30,9 +24,6 @@ const configs = {
|
|||||||
bun: {
|
bun: {
|
||||||
base_path: "",
|
base_path: "",
|
||||||
},
|
},
|
||||||
"tanstack-start": {
|
|
||||||
base_path: "/admin",
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getAdapterConfig(): typeof default_config {
|
export function getAdapterConfig(): typeof default_config {
|
||||||
|
|||||||
+5
-31
@@ -3,7 +3,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"version": "0.21.0-rc.1",
|
"version": "0.20.0-rc.1",
|
||||||
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
|
"description": "Lightweight Firebase/Supabase alternative built to run anywhere — incl. Next.js, React Router, Astro, Cloudflare, Bun, Node, AWS Lambda & more.",
|
||||||
"homepage": "https://bknd.io",
|
"homepage": "https://bknd.io",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
"test:e2e:report": "VITE_DB_URL=:memory: playwright show-report",
|
"test:e2e:report": "VITE_DB_URL=:memory: playwright show-report",
|
||||||
"docs:build-assets": "bun internal/docs.build-assets.ts"
|
"docs:build-assets": "bun internal/docs.build-assets.ts"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "FSL-1.1-MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cfworker/json-schema": "^4.1.1",
|
"@cfworker/json-schema": "^4.1.1",
|
||||||
"@codemirror/lang-html": "^6.4.11",
|
"@codemirror/lang-html": "^6.4.11",
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"@xyflow/react": "^12.9.2",
|
"@xyflow/react": "^12.9.2",
|
||||||
"aws4fetch": "^1.0.20",
|
"aws4fetch": "^1.0.20",
|
||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
"dayjs": "^1.11.20",
|
"dayjs": "^1.11.19",
|
||||||
"fast-xml-parser": "^5.3.1",
|
"fast-xml-parser": "^5.3.1",
|
||||||
"hono": "4.10.4",
|
"hono": "4.10.4",
|
||||||
"json-schema-library": "10.0.0-rc7",
|
"json-schema-library": "10.0.0-rc7",
|
||||||
@@ -80,6 +80,7 @@
|
|||||||
"@aws-sdk/client-s3": "^3.922.0",
|
"@aws-sdk/client-s3": "^3.922.0",
|
||||||
"@bluwy/giget-core": "^0.1.6",
|
"@bluwy/giget-core": "^0.1.6",
|
||||||
"@clack/prompts": "^0.11.0",
|
"@clack/prompts": "^0.11.0",
|
||||||
|
"@cloudflare/vite-plugin": "^1.15.3",
|
||||||
"@cloudflare/vitest-pool-workers": "^0.10.4",
|
"@cloudflare/vitest-pool-workers": "^0.10.4",
|
||||||
"@cloudflare/workers-types": "^4.20251014.0",
|
"@cloudflare/workers-types": "^4.20251014.0",
|
||||||
"@dagrejs/dagre": "^1.1.4",
|
"@dagrejs/dagre": "^1.1.4",
|
||||||
@@ -142,7 +143,7 @@
|
|||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "3.0.9",
|
"vitest": "3.0.9",
|
||||||
"wouter": "^3.7.1",
|
"wouter": "^3.7.1",
|
||||||
"wrangler": "^4.45.4"
|
"wrangler": "^4.52.1"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@hono/node-server": "^1.19.6"
|
"@hono/node-server": "^1.19.6"
|
||||||
@@ -233,16 +234,6 @@
|
|||||||
"import": "./dist/adapter/nextjs/index.js",
|
"import": "./dist/adapter/nextjs/index.js",
|
||||||
"require": "./dist/adapter/nextjs/index.js"
|
"require": "./dist/adapter/nextjs/index.js"
|
||||||
},
|
},
|
||||||
"./adapter/universal": {
|
|
||||||
"types": "./dist/types/adapter/universal/index.d.ts",
|
|
||||||
"import": "./dist/adapter/universal/index.js",
|
|
||||||
"require": "./dist/adapter/universal/index.js"
|
|
||||||
},
|
|
||||||
"./adapter/nuxt": {
|
|
||||||
"types": "./dist/types/adapter/nuxt/index.d.ts",
|
|
||||||
"import": "./dist/adapter/nuxt/index.js",
|
|
||||||
"require": "./dist/adapter/nuxt/index.js"
|
|
||||||
},
|
|
||||||
"./adapter/react-router": {
|
"./adapter/react-router": {
|
||||||
"types": "./dist/types/adapter/react-router/index.d.ts",
|
"types": "./dist/types/adapter/react-router/index.d.ts",
|
||||||
"import": "./dist/adapter/react-router/index.js",
|
"import": "./dist/adapter/react-router/index.js",
|
||||||
@@ -263,11 +254,6 @@
|
|||||||
"import": "./dist/adapter/astro/index.js",
|
"import": "./dist/adapter/astro/index.js",
|
||||||
"require": "./dist/adapter/astro/index.js"
|
"require": "./dist/adapter/astro/index.js"
|
||||||
},
|
},
|
||||||
"./adapter/sveltekit": {
|
|
||||||
"types": "./dist/types/adapter/sveltekit/index.d.ts",
|
|
||||||
"import": "./dist/adapter/sveltekit/index.js",
|
|
||||||
"require": "./dist/adapter/sveltekit/index.js"
|
|
||||||
},
|
|
||||||
"./adapter/aws": {
|
"./adapter/aws": {
|
||||||
"types": "./dist/types/adapter/aws/index.d.ts",
|
"types": "./dist/types/adapter/aws/index.d.ts",
|
||||||
"import": "./dist/adapter/aws/index.js",
|
"import": "./dist/adapter/aws/index.js",
|
||||||
@@ -278,11 +264,6 @@
|
|||||||
"import": "./dist/adapter/browser/index.js",
|
"import": "./dist/adapter/browser/index.js",
|
||||||
"require": "./dist/adapter/browser/index.js"
|
"require": "./dist/adapter/browser/index.js"
|
||||||
},
|
},
|
||||||
"./adapter/tanstack-start": {
|
|
||||||
"types": "./dist/types/adapter/tanstack-start/index.d.ts",
|
|
||||||
"import": "./dist/adapter/tanstack-start/index.js",
|
|
||||||
"require": "./dist/adapter/tanstack-start/index.js"
|
|
||||||
},
|
|
||||||
"./dist/main.css": "./dist/ui/main.css",
|
"./dist/main.css": "./dist/ui/main.css",
|
||||||
"./dist/styles.css": "./dist/ui/styles.css",
|
"./dist/styles.css": "./dist/ui/styles.css",
|
||||||
"./dist/manifest.json": "./dist/static/.vite/manifest.json",
|
"./dist/manifest.json": "./dist/static/.vite/manifest.json",
|
||||||
@@ -297,13 +278,9 @@
|
|||||||
"adapter/cloudflare": ["./dist/types/adapter/cloudflare/index.d.ts"],
|
"adapter/cloudflare": ["./dist/types/adapter/cloudflare/index.d.ts"],
|
||||||
"adapter/vite": ["./dist/types/adapter/vite/index.d.ts"],
|
"adapter/vite": ["./dist/types/adapter/vite/index.d.ts"],
|
||||||
"adapter/nextjs": ["./dist/types/adapter/nextjs/index.d.ts"],
|
"adapter/nextjs": ["./dist/types/adapter/nextjs/index.d.ts"],
|
||||||
"adapter/universal": ["./dist/types/adapter/universal/index.d.ts"],
|
|
||||||
"adapter/nuxt": ["./dist/types/adapter/nuxt/index.d.ts"],
|
|
||||||
"adapter/react-router": ["./dist/types/adapter/react-router/index.d.ts"],
|
"adapter/react-router": ["./dist/types/adapter/react-router/index.d.ts"],
|
||||||
"adapter/bun": ["./dist/types/adapter/bun/index.d.ts"],
|
"adapter/bun": ["./dist/types/adapter/bun/index.d.ts"],
|
||||||
"adapter/node": ["./dist/types/adapter/node/index.d.ts"],
|
"adapter/node": ["./dist/types/adapter/node/index.d.ts"],
|
||||||
"adapter/sveltekit": ["./dist/types/adapter/sveltekit/index.d.ts"],
|
|
||||||
"adapter/tanstack-start": ["./dist/types/adapter/tanstack-start/index.d.ts"],
|
|
||||||
"adapter/sqlite": ["./dist/types/adapter/sqlite/edge.d.ts"]
|
"adapter/sqlite": ["./dist/types/adapter/sqlite/edge.d.ts"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -330,12 +307,9 @@
|
|||||||
"serverless",
|
"serverless",
|
||||||
"cloudflare",
|
"cloudflare",
|
||||||
"nextjs",
|
"nextjs",
|
||||||
"nuxt",
|
|
||||||
"remix",
|
"remix",
|
||||||
"react-router",
|
"react-router",
|
||||||
"astro",
|
"astro",
|
||||||
"sveltekit",
|
|
||||||
"svelte",
|
|
||||||
"bun",
|
"bun",
|
||||||
"node"
|
"node"
|
||||||
]
|
]
|
||||||
|
|||||||
+1
-1
@@ -121,7 +121,7 @@ export class Api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try authorization header
|
// try authorization header
|
||||||
const headerToken = this.options.headers.get("authorization")?.replace(/^Bearer\s+/i, "");
|
const headerToken = this.options.headers.get("authorization")?.replace("Bearer ", "");
|
||||||
if (headerToken) {
|
if (headerToken) {
|
||||||
this.token_transport = "header";
|
this.token_transport = "header";
|
||||||
this.updateToken(headerToken);
|
this.updateToken(headerToken);
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ export async function createAdapterApp<Config extends BkndConfig = BkndConfig, A
|
|||||||
if (Connection.isConnection(config.connection)) {
|
if (Connection.isConnection(config.connection)) {
|
||||||
connection = config.connection;
|
connection = config.connection;
|
||||||
} else {
|
} else {
|
||||||
|
if (connection) {
|
||||||
|
$console.warn(
|
||||||
|
"Connection is not a valid connection object, using default SQLite connection",
|
||||||
|
);
|
||||||
|
}
|
||||||
const sqlite = (await import("bknd/adapter/sqlite")).sqlite;
|
const sqlite = (await import("bknd/adapter/sqlite")).sqlite;
|
||||||
const conf = appConfig.connection ?? { url: "file:data.db" };
|
const conf = appConfig.connection ?? { url: "file:data.db" };
|
||||||
connection = sqlite(conf) as any;
|
connection = sqlite(conf) as any;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./nuxt.adapter";
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { afterAll, beforeAll, describe } from "bun:test";
|
|
||||||
import * as nuxt from "./nuxt.adapter";
|
|
||||||
import { disableConsoleLog, enableConsoleLog } from "core/utils";
|
|
||||||
import { adapterTestSuite } from "adapter/adapter-test-suite";
|
|
||||||
import { bunTestRunner } from "adapter/bun/test";
|
|
||||||
|
|
||||||
beforeAll(disableConsoleLog);
|
|
||||||
afterAll(enableConsoleLog);
|
|
||||||
|
|
||||||
describe("nuxt adapter", () => {
|
|
||||||
adapterTestSuite(bunTestRunner, {
|
|
||||||
makeApp: nuxt.getApp,
|
|
||||||
makeHandler: nuxt.serve,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { createRuntimeApp, type RuntimeBkndConfig } from "bknd/adapter";
|
|
||||||
|
|
||||||
export type NuxtEnv = NodeJS.ProcessEnv;
|
|
||||||
export type NuxtBkndConfig<Env = NuxtEnv> = RuntimeBkndConfig<Env>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get bknd app instance
|
|
||||||
* @param config - bknd configuration
|
|
||||||
* @param args - environment variables
|
|
||||||
*/
|
|
||||||
export async function getApp<Env>(
|
|
||||||
config: NuxtBkndConfig<Env> = {} as NuxtBkndConfig<Env>,
|
|
||||||
args: Env,
|
|
||||||
) {
|
|
||||||
return await createRuntimeApp(config, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create middleware handler for Nuxt
|
|
||||||
* @param config - bknd configuration
|
|
||||||
* @param args - environment variables
|
|
||||||
*/
|
|
||||||
export function serve<Env>(
|
|
||||||
config: NuxtBkndConfig<Env> = {} as NuxtBkndConfig<Env>,
|
|
||||||
args: Env,
|
|
||||||
) {
|
|
||||||
return async (request: Request) => {
|
|
||||||
return (await getApp(config, args)).fetch(request);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./sveltekit.adapter";
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { afterAll, beforeAll, describe } from "bun:test";
|
|
||||||
import * as sveltekit from "./sveltekit.adapter";
|
|
||||||
import { disableConsoleLog, enableConsoleLog } from "core/utils";
|
|
||||||
import { adapterTestSuite } from "adapter/adapter-test-suite";
|
|
||||||
import { bunTestRunner } from "adapter/bun/test";
|
|
||||||
|
|
||||||
beforeAll(disableConsoleLog);
|
|
||||||
afterAll(enableConsoleLog);
|
|
||||||
|
|
||||||
describe("sveltekit adapter", () => {
|
|
||||||
adapterTestSuite(bunTestRunner, {
|
|
||||||
makeApp: (c, a) => sveltekit.getApp(c as any, a ?? ({} as any)),
|
|
||||||
makeHandler: (c, a) => (request: Request) =>
|
|
||||||
sveltekit.serve(c as any, a ?? ({} as any))({ request }),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { createRuntimeApp, type RuntimeBkndConfig } from "bknd/adapter";
|
|
||||||
|
|
||||||
type TSvelteKit = {
|
|
||||||
request: Request;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SvelteKitBkndConfig<Env> = Pick<RuntimeBkndConfig<Env>, "adminOptions">;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get bknd app instance
|
|
||||||
* @param config - bknd configuration
|
|
||||||
* @param args - environment variables (use $env/dynamic/private for universal runtime support)
|
|
||||||
*/
|
|
||||||
export async function getApp<Env>(
|
|
||||||
config: SvelteKitBkndConfig<Env> = {} as SvelteKitBkndConfig<Env>,
|
|
||||||
args: Env,
|
|
||||||
) {
|
|
||||||
return await createRuntimeApp(config, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create request handler for hooks.server.ts
|
|
||||||
* @param config - bknd configuration
|
|
||||||
* @param args - environment variables (use $env/dynamic/private for universal runtime support)
|
|
||||||
*/
|
|
||||||
export function serve<Env>(
|
|
||||||
config: SvelteKitBkndConfig<Env> = {} as SvelteKitBkndConfig<Env>,
|
|
||||||
args: Env,
|
|
||||||
) {
|
|
||||||
return async (fnArgs: TSvelteKit) => {
|
|
||||||
return (await getApp(config, args)).fetch(fnArgs.request);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./tanstack-start.adapter";
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { afterAll, beforeAll, describe } from "bun:test";
|
|
||||||
import * as tanstackStart from "./tanstack-start.adapter";
|
|
||||||
import { disableConsoleLog, enableConsoleLog } from "core/utils";
|
|
||||||
import { adapterTestSuite } from "adapter/adapter-test-suite";
|
|
||||||
import { bunTestRunner } from "adapter/bun/test";
|
|
||||||
import type { TanstackStartConfig } from "./tanstack-start.adapter";
|
|
||||||
|
|
||||||
beforeAll(disableConsoleLog);
|
|
||||||
afterAll(enableConsoleLog);
|
|
||||||
|
|
||||||
describe("tanstack start adapter", () => {
|
|
||||||
adapterTestSuite<TanstackStartConfig>(bunTestRunner, {
|
|
||||||
makeApp: tanstackStart.getApp,
|
|
||||||
makeHandler: tanstackStart.serve,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { createFrameworkApp, type FrameworkBkndConfig } from "bknd/adapter";
|
|
||||||
|
|
||||||
export type TanstackStartEnv = NodeJS.ProcessEnv;
|
|
||||||
|
|
||||||
export type TanstackStartConfig<Env = TanstackStartEnv> =
|
|
||||||
FrameworkBkndConfig<Env>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get bknd app instance
|
|
||||||
* @param config - bknd configuration
|
|
||||||
* @param args - environment variables
|
|
||||||
*/
|
|
||||||
export async function getApp<Env = TanstackStartEnv>(
|
|
||||||
config: TanstackStartConfig<Env> = {},
|
|
||||||
args: Env = process.env as Env,
|
|
||||||
) {
|
|
||||||
return await createFrameworkApp(config, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create request handler for src/routes/api.$.ts
|
|
||||||
* @param config - bknd configuration
|
|
||||||
* @param args - environment variables
|
|
||||||
*/
|
|
||||||
export function serve<Env = TanstackStartEnv>(
|
|
||||||
config: TanstackStartConfig<Env> = {},
|
|
||||||
args: Env = process.env as Env,
|
|
||||||
) {
|
|
||||||
return async (request: Request) => {
|
|
||||||
const app = await getApp(config, args);
|
|
||||||
return app.fetch(request);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./universal.adapter";
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
import { afterAll, beforeAll, describe, test, expect } from "bun:test";
|
|
||||||
import { createBknd } from "./universal.adapter";
|
|
||||||
import { disableConsoleLog, enableConsoleLog } from "core/utils";
|
|
||||||
import { adapterTestSuite } from "adapter/adapter-test-suite";
|
|
||||||
import { bunTestRunner } from "adapter/bun/test";
|
|
||||||
|
|
||||||
beforeAll(disableConsoleLog);
|
|
||||||
afterAll(enableConsoleLog);
|
|
||||||
|
|
||||||
describe("universal adapter via createBknd", () => {
|
|
||||||
adapterTestSuite(bunTestRunner, {
|
|
||||||
makeApp: (options, args) => createBknd({ mode: "headless", options }, args).getApp(),
|
|
||||||
makeHandler: (options, args) => createBknd({ mode: "headless", options: options ?? {} }, args).serve(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// ------------------------ MODE HEADLESS ------------------------
|
|
||||||
test("caches app instance", async () => {
|
|
||||||
const bknd = createBknd({ mode: "headless", options: { connection: { url: ":memory:" } } });
|
|
||||||
const app1 = await bknd.getApp();
|
|
||||||
const app2 = await bknd.getApp();
|
|
||||||
expect(app1).toBe(app2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getApi returns api", async () => {
|
|
||||||
const bknd = createBknd({ mode: "headless", options: { connection: { url: ":memory:" } } });
|
|
||||||
const api = await bknd.getApi();
|
|
||||||
expect(api).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("uses createFrameworkApp in headless mode", async () => {
|
|
||||||
const bknd = createBknd({ mode: "headless", options: { connection: { url: ":memory:" } } });
|
|
||||||
const app = await bknd.getApp();
|
|
||||||
expect(app).toBeDefined();
|
|
||||||
expect(app.isBuilt()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("serve returns a fetch handler", async () => {
|
|
||||||
const bknd = createBknd({ mode: "headless", options: { connection: { url: ":memory:" } } });
|
|
||||||
const handler = bknd.serve();
|
|
||||||
const res = await handler(new Request("http://localhost:3000/api/system/config"));
|
|
||||||
expect(res.status).toBe(200);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// ------------------------ MODE ADMIN ------------------------
|
|
||||||
describe("universal adapter via createBknd in admin mode", () => {
|
|
||||||
adapterTestSuite(bunTestRunner, {
|
|
||||||
makeApp: (options, args) => createBknd({ mode: "admin", options }, args).getApp(),
|
|
||||||
makeHandler: (options, args) => createBknd({ mode: "admin", options: options ?? {} }, args).serve(),
|
|
||||||
});
|
|
||||||
|
|
||||||
test("caches app instance", async () => {
|
|
||||||
const bknd = createBknd({ mode: "admin", options: { connection: { url: ":memory:" } } });
|
|
||||||
const app1 = await bknd.getApp();
|
|
||||||
const app2 = await bknd.getApp();
|
|
||||||
expect(app1).toBe(app2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getApi returns api", async () => {
|
|
||||||
const bknd = createBknd({ mode: "admin", options: { connection: { url: ":memory:" } } });
|
|
||||||
const api = await bknd.getApi();
|
|
||||||
expect(api).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("uses createRuntimeApp in admin mode", async () => {
|
|
||||||
const bknd = createBknd({
|
|
||||||
mode: "admin",
|
|
||||||
options: {
|
|
||||||
connection: { url: ":memory:" },
|
|
||||||
adminOptions: { adminBasepath: "/admin" },
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const app = await bknd.getApp();
|
|
||||||
expect(app).toBeDefined();
|
|
||||||
expect(app.isBuilt()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("serve returns a fetch handler", async () => {
|
|
||||||
const bknd = createBknd({
|
|
||||||
mode: "admin",
|
|
||||||
options: {
|
|
||||||
connection: { url: ":memory:" },
|
|
||||||
adminOptions: { adminBasepath: "/admin" },
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const handler = bknd.serve();
|
|
||||||
const res = await handler(new Request("http://localhost:3000/api/system/config"));
|
|
||||||
expect(res.status).toBe(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("check admin route", async () => {
|
|
||||||
const bknd = createBknd({
|
|
||||||
mode: "admin",
|
|
||||||
options: {
|
|
||||||
connection: { url: ":memory:" },
|
|
||||||
adminOptions: { adminBasepath: "/admin" },
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const handler = bknd.serve();
|
|
||||||
const res = await handler(new Request("http://localhost:3000/admin"));
|
|
||||||
expect(res.status).toBe(200);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
import {
|
|
||||||
createFrameworkApp,
|
|
||||||
createRuntimeApp,
|
|
||||||
type FrameworkBkndConfig,
|
|
||||||
type RuntimeBkndConfig,
|
|
||||||
} from "bknd/adapter";
|
|
||||||
import { $console } from "core/utils";
|
|
||||||
import type { App } from "App";
|
|
||||||
|
|
||||||
export type AdapterModeWithOptions<Env = Record<string, string | undefined>> =
|
|
||||||
| {
|
|
||||||
/** Serves the API along with the admin UI and static assets */
|
|
||||||
mode: "admin";
|
|
||||||
options: RuntimeBkndConfig<Env>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
/** Serves the API only, without the admin UI — use when your framework handles rendering */
|
|
||||||
mode: "headless";
|
|
||||||
options: FrameworkBkndConfig<Env>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function createBknd<Env>(config: AdapterModeWithOptions<Env>, env?: Env) {
|
|
||||||
let appPromise: Promise<App> | undefined;
|
|
||||||
|
|
||||||
const { mode, options } = config;
|
|
||||||
|
|
||||||
async function getApp(): Promise<App> {
|
|
||||||
if (!appPromise) {
|
|
||||||
if (mode === "admin") {
|
|
||||||
if (options.adminOptions && !options.serveStatic) {
|
|
||||||
$console.warn(
|
|
||||||
"adminOptions provided without serveStatic — admin UI assets may not be served. " +
|
|
||||||
"See `serveStatic`, `serveStaticViaImport`, or add a `package.json` script that runs `bknd copy-assets --out {relative_static_assets_directory_path}`.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
appPromise = createRuntimeApp(options, env);
|
|
||||||
} else {
|
|
||||||
appPromise = createFrameworkApp(options, env);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return appPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getApi(opts?: { headers?: Headers; verify?: boolean }) {
|
|
||||||
const app = await getApp();
|
|
||||||
if (opts?.verify) {
|
|
||||||
const api = app.getApi({ headers: opts.headers });
|
|
||||||
await api.verifyAuth();
|
|
||||||
return api;
|
|
||||||
}
|
|
||||||
return app.getApi(opts?.headers ? { headers: opts.headers } : undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
function serve() {
|
|
||||||
return async (req: Request) => {
|
|
||||||
const app = await getApp();
|
|
||||||
return app.fetch(req);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return { getApp, getApi, serve };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility type to determine the config type based on mode,
|
|
||||||
* Usage `Config<"standalone">` or `Config<"api">`
|
|
||||||
*/
|
|
||||||
export type Config<T extends AdapterModeWithOptions["mode"]> = Extract<
|
|
||||||
Parameters<typeof createBknd>[0],
|
|
||||||
{ mode: T }
|
|
||||||
>['options'];
|
|
||||||
+1
-29
@@ -46,22 +46,6 @@ export class AppAuth extends Module<AppAuthSchema> {
|
|||||||
to.strategies!.password!.enabled = true;
|
to.strategies!.password!.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to.default_role_register && to.default_role_register?.length > 0) {
|
|
||||||
const valid_to_role = Object.keys(to.roles ?? {}).includes(to.default_role_register);
|
|
||||||
|
|
||||||
if (!valid_to_role) {
|
|
||||||
const msg = `Default role for registration not found: ${to.default_role_register}`;
|
|
||||||
// if changing to a new value
|
|
||||||
if (from.default_role_register !== to.default_role_register) {
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// resetting gracefully, since role doesn't exist anymore
|
|
||||||
$console.warn(`${msg}, resetting to undefined`);
|
|
||||||
to.default_role_register = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +82,6 @@ export class AppAuth extends Module<AppAuthSchema> {
|
|||||||
this._authenticator = new Authenticator(strategies, new AppUserPool(this), {
|
this._authenticator = new Authenticator(strategies, new AppUserPool(this), {
|
||||||
jwt: this.config.jwt,
|
jwt: this.config.jwt,
|
||||||
cookie: this.config.cookie,
|
cookie: this.config.cookie,
|
||||||
default_role_register: this.config.default_role_register,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.registerEntities();
|
this.registerEntities();
|
||||||
@@ -188,20 +171,10 @@ export class AppAuth extends Module<AppAuthSchema> {
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
async createUser({
|
async createUser({ email, password, ...additional }: CreateUserPayload): Promise<DB["users"]> {
|
||||||
email,
|
|
||||||
password,
|
|
||||||
role,
|
|
||||||
...additional
|
|
||||||
}: CreateUserPayload): Promise<DB["users"]> {
|
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
throw new Error("Cannot create user, auth not enabled");
|
throw new Error("Cannot create user, auth not enabled");
|
||||||
}
|
}
|
||||||
if (role) {
|
|
||||||
if (!Object.keys(this.config.roles ?? {}).includes(role)) {
|
|
||||||
throw new Error(`Role "${role}" not found`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const strategy = "password" as const;
|
const strategy = "password" as const;
|
||||||
const pw = this.authenticator.strategy(strategy) as PasswordStrategy;
|
const pw = this.authenticator.strategy(strategy) as PasswordStrategy;
|
||||||
@@ -210,7 +183,6 @@ export class AppAuth extends Module<AppAuthSchema> {
|
|||||||
mutator.__unstable_toggleSystemEntityCreation(false);
|
mutator.__unstable_toggleSystemEntityCreation(false);
|
||||||
const { data: created } = await mutator.insertOne({
|
const { data: created } = await mutator.insertOne({
|
||||||
...(additional as any),
|
...(additional as any),
|
||||||
role: role || this.config.default_role_register || undefined,
|
|
||||||
email,
|
email,
|
||||||
strategy,
|
strategy,
|
||||||
strategy_value,
|
strategy_value,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
InvalidSchemaError,
|
InvalidSchemaError,
|
||||||
transformObject,
|
transformObject,
|
||||||
mcpTool,
|
mcpTool,
|
||||||
$console,
|
|
||||||
} from "bknd/utils";
|
} from "bknd/utils";
|
||||||
import type { PasswordStrategy } from "auth/authenticate/strategies";
|
import type { PasswordStrategy } from "auth/authenticate/strategies";
|
||||||
|
|
||||||
@@ -199,12 +198,7 @@ export class AuthController extends Controller {
|
|||||||
for (const [name, strategy] of Object.entries(strategies)) {
|
for (const [name, strategy] of Object.entries(strategies)) {
|
||||||
if (!this.auth.isStrategyEnabled(strategy)) continue;
|
if (!this.auth.isStrategyEnabled(strategy)) continue;
|
||||||
|
|
||||||
hono.route(
|
hono.route(`/${name}`, strategy.getController(this.auth.authenticator));
|
||||||
`/${name}`,
|
|
||||||
strategy.getController(this.auth.authenticator, {
|
|
||||||
allow_register: this.auth.config.allow_register,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
this.registerStrategyActions(strategy, hono);
|
this.registerStrategyActions(strategy, hono);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +210,7 @@ export class AuthController extends Controller {
|
|||||||
const idType = s.anyOf([s.number({ title: "Integer" }), s.string({ title: "UUID" })]);
|
const idType = s.anyOf([s.number({ title: "Integer" }), s.string({ title: "UUID" })]);
|
||||||
|
|
||||||
const getUser = async (params: { id?: string | number; email?: string }) => {
|
const getUser = async (params: { id?: string | number; email?: string }) => {
|
||||||
let user: DB["users"] | undefined;
|
let user: DB["users"] | undefined = undefined;
|
||||||
if (params.id) {
|
if (params.id) {
|
||||||
const { data } = await this.userRepo.findId(params.id);
|
const { data } = await this.userRepo.findId(params.id);
|
||||||
user = data;
|
user = data;
|
||||||
@@ -231,16 +225,13 @@ export class AuthController extends Controller {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const roles = Object.keys(this.auth.config.roles ?? {});
|
const roles = Object.keys(this.auth.config.roles ?? {});
|
||||||
try {
|
|
||||||
const actions = this.auth.authenticator.strategy("password").getActions();
|
|
||||||
if (actions.create) {
|
|
||||||
const schema = actions.create.schema;
|
|
||||||
mcp.tool(
|
mcp.tool(
|
||||||
"auth_user_create",
|
"auth_user_create",
|
||||||
{
|
{
|
||||||
description: "Create a new user",
|
description: "Create a new user",
|
||||||
inputSchema: s.object({
|
inputSchema: s.object({
|
||||||
...schema.properties,
|
email: s.string({ format: "email" }),
|
||||||
|
password: s.string({ minLength: 8 }),
|
||||||
role: s
|
role: s
|
||||||
.string({
|
.string({
|
||||||
enum: roles.length > 0 ? roles : undefined,
|
enum: roles.length > 0 ? roles : undefined,
|
||||||
@@ -254,10 +245,6 @@ export class AuthController extends Controller {
|
|||||||
return c.json(await this.auth.createUser(params));
|
return c.json(await this.auth.createUser(params));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
$console.warn("error creating auth_user_create tool", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
mcp.tool(
|
mcp.tool(
|
||||||
"auth_user_token",
|
"auth_user_token",
|
||||||
@@ -310,9 +297,7 @@ export class AuthController extends Controller {
|
|||||||
await c.context.ctx().helper.granted(c, AuthPermissions.testPassword);
|
await c.context.ctx().helper.granted(c, AuthPermissions.testPassword);
|
||||||
|
|
||||||
const pw = this.auth.authenticator.strategy("password") as PasswordStrategy;
|
const pw = this.auth.authenticator.strategy("password") as PasswordStrategy;
|
||||||
const controller = pw.getController(this.auth.authenticator, {
|
const controller = pw.getController(this.auth.authenticator);
|
||||||
allow_register: this.auth.config.allow_register,
|
|
||||||
});
|
|
||||||
|
|
||||||
const res = await controller.request(
|
const res = await controller.request(
|
||||||
new Request("https://localhost/login", {
|
new Request("https://localhost/login", {
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ export const authConfigSchema = $object(
|
|||||||
basepath: s.string({ default: "/api/auth" }),
|
basepath: s.string({ default: "/api/auth" }),
|
||||||
entity_name: s.string({ default: "users" }),
|
entity_name: s.string({ default: "users" }),
|
||||||
allow_register: s.boolean({ default: true }).optional(),
|
allow_register: s.boolean({ default: true }).optional(),
|
||||||
default_role_register: s.string().optional(),
|
|
||||||
jwt: jwtConfig,
|
jwt: jwtConfig,
|
||||||
cookie: cookieConfig,
|
cookie: cookieConfig,
|
||||||
strategies: $record(
|
strategies: $record(
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ export const jwtConfig = s.strictObject(
|
|||||||
export const authenticatorConfig = s.object({
|
export const authenticatorConfig = s.object({
|
||||||
jwt: jwtConfig,
|
jwt: jwtConfig,
|
||||||
cookie: cookieConfig,
|
cookie: cookieConfig,
|
||||||
default_role_register: s.string().optional(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type AuthConfig = s.Static<typeof authenticatorConfig>;
|
type AuthConfig = s.Static<typeof authenticatorConfig>;
|
||||||
@@ -165,13 +164,9 @@ export class Authenticator<
|
|||||||
if (!("strategy_value" in profile)) {
|
if (!("strategy_value" in profile)) {
|
||||||
throw new InvalidConditionsException("Profile must have a strategy value");
|
throw new InvalidConditionsException("Profile must have a strategy value");
|
||||||
}
|
}
|
||||||
if ("role" in profile) {
|
|
||||||
throw new InvalidConditionsException("Role cannot be provided during registration");
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await this.userPool.create(strategy.getName(), {
|
const user = await this.userPool.create(strategy.getName(), {
|
||||||
...profile,
|
...profile,
|
||||||
role: this.config.default_role_register,
|
|
||||||
strategy_value: profile.strategy_value,
|
strategy_value: profile.strategy_value,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -430,7 +425,7 @@ export class Authenticator<
|
|||||||
let token: string | undefined;
|
let token: string | undefined;
|
||||||
if (headers.has("Authorization")) {
|
if (headers.has("Authorization")) {
|
||||||
const bearerHeader = String(headers.get("Authorization"));
|
const bearerHeader = String(headers.get("Authorization"));
|
||||||
token = bearerHeader.replace(/^Bearer\s+/i, "");
|
token = bearerHeader.replace("Bearer ", "");
|
||||||
} else {
|
} else {
|
||||||
const context = is_context ? (c as Context) : ({ req: { raw: { headers } } } as Context);
|
const context = is_context ? (c as Context) : ({ req: { raw: { headers } } } as Context);
|
||||||
token = await this.getAuthCookie(context);
|
token = await this.getAuthCookie(context);
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ const schema = s
|
|||||||
.object({
|
.object({
|
||||||
hashing: s.string({ enum: ["plain", "sha256", "bcrypt"], default: "sha256" }),
|
hashing: s.string({ enum: ["plain", "sha256", "bcrypt"], default: "sha256" }),
|
||||||
rounds: s.number({ minimum: 1, maximum: 10 }).optional(),
|
rounds: s.number({ minimum: 1, maximum: 10 }).optional(),
|
||||||
minLength: s.number({ default: 8, minimum: 1 }).optional(),
|
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@ export class PasswordStrategy extends AuthStrategy<typeof schema> {
|
|||||||
format: "email",
|
format: "email",
|
||||||
}),
|
}),
|
||||||
password: s.string({
|
password: s.string({
|
||||||
minLength: this.config.minLength,
|
minLength: 8, // @todo: this should be configurable
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -66,28 +65,19 @@ export class PasswordStrategy extends AuthStrategy<typeof schema> {
|
|||||||
return await bcryptCompare(compare, actual);
|
return await bcryptCompare(compare, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
return actual === compare;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(password: string) {
|
verify(password: string) {
|
||||||
return async (user: User) => {
|
return async (user: User) => {
|
||||||
if (!user || !user.strategy_value) {
|
const compare = await this.compare(user?.strategy_value!, password);
|
||||||
throw new InvalidCredentialsException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.getPayloadSchema().properties.password.validate(password).valid) {
|
|
||||||
$console.debug("PasswordStrategy: Invalid password", password);
|
|
||||||
throw new InvalidCredentialsException();
|
|
||||||
}
|
|
||||||
|
|
||||||
const compare = await this.compare(user.strategy_value, password);
|
|
||||||
if (compare !== true) {
|
if (compare !== true) {
|
||||||
throw new InvalidCredentialsException();
|
throw new InvalidCredentialsException();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getController(authenticator: Authenticator, opts: { allow_register?: boolean }): Hono<any> {
|
getController(authenticator: Authenticator): Hono<any> {
|
||||||
const hono = new Hono();
|
const hono = new Hono();
|
||||||
const redirectQuerySchema = s.object({
|
const redirectQuerySchema = s.object({
|
||||||
redirect: s.string().optional(),
|
redirect: s.string().optional(),
|
||||||
@@ -120,7 +110,6 @@ export class PasswordStrategy extends AuthStrategy<typeof schema> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (opts.allow_register) {
|
|
||||||
hono.post(
|
hono.post(
|
||||||
"/register",
|
"/register",
|
||||||
describeRoute({
|
describeRoute({
|
||||||
@@ -156,7 +145,6 @@ export class PasswordStrategy extends AuthStrategy<typeof schema> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return hono;
|
return hono;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export abstract class AuthStrategy<Schema extends s.Schema = s.Schema> {
|
|||||||
|
|
||||||
protected abstract getSchema(): Schema;
|
protected abstract getSchema(): Schema;
|
||||||
|
|
||||||
abstract getController(auth: Authenticator, opts: { allow_register?: boolean }): Hono;
|
abstract getController(auth: Authenticator): Hono;
|
||||||
|
|
||||||
getType(): string {
|
getType(): string {
|
||||||
return this.type;
|
return this.type;
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ export class OAuthStrategy extends AuthStrategy<typeof schemaProvided> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getController(auth: Authenticator, opts: { allow_register?: boolean }): Hono<any> {
|
getController(auth: Authenticator): Hono<any> {
|
||||||
const hono = new Hono();
|
const hono = new Hono();
|
||||||
const secret = "secret";
|
const secret = "secret";
|
||||||
const cookie_name = "_challenge";
|
const cookie_name = "_challenge";
|
||||||
@@ -379,10 +379,6 @@ export class OAuthStrategy extends AuthStrategy<typeof schemaProvided> {
|
|||||||
return c.notFound();
|
return c.notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === "register" && !opts.allow_register) {
|
|
||||||
return c.notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = new URL(c.req.url);
|
const url = new URL(c.req.url);
|
||||||
const path = url.pathname.replace(`/${action}`, "");
|
const path = url.pathname.replace(`/${action}`, "");
|
||||||
const redirect_uri = url.origin + path + "/callback";
|
const redirect_uri = url.origin + path + "/callback";
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import type { CliCommand } from "cli/types";
|
||||||
|
import { Option } from "commander";
|
||||||
|
import { withConfigOptions, type WithConfigOptions } from "cli/utils/options";
|
||||||
|
import * as utils from "./utils";
|
||||||
|
import { $console } from "core/utils";
|
||||||
|
import { Project } from "./lib/Project";
|
||||||
|
|
||||||
|
export const dev: CliCommand = (program) =>
|
||||||
|
withConfigOptions(program.command("dev"))
|
||||||
|
.description("dev server")
|
||||||
|
.addOption(new Option("--build", "build the project"))
|
||||||
|
.action(action);
|
||||||
|
|
||||||
|
async function action(options: WithConfigOptions<{ build?: boolean }>) {
|
||||||
|
console.log("options", options);
|
||||||
|
const project = new Project({
|
||||||
|
templatePath: utils.TEMPLATE_PATH,
|
||||||
|
});
|
||||||
|
|
||||||
|
await project.init();
|
||||||
|
|
||||||
|
if (options.build) {
|
||||||
|
await project.build();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
await project.listen();
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import { RelativeFS } from "./RelativeFS";
|
||||||
|
//import { $console } from "bknd/utils";
|
||||||
|
import { type ViteDevServer, createServer, type UserConfig, createBuilder } from "vite";
|
||||||
|
import { readdir, copyFile, stat } from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import { cloudflare } from "@cloudflare/vite-plugin";
|
||||||
|
import { getRelativeDistPath } from "cli/utils/sys";
|
||||||
|
import { injectMain } from "./vite/inject-main";
|
||||||
|
import { devFsVitePlugin } from "adapter/cloudflare";
|
||||||
|
|
||||||
|
export type ProjectOptions = {
|
||||||
|
userPath?: string;
|
||||||
|
templatePath?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// @todo: add multiple public dirs
|
||||||
|
// @todo: add npm install (first time)
|
||||||
|
// @todo: add package.json
|
||||||
|
export class Project {
|
||||||
|
public userFs: RelativeFS;
|
||||||
|
public templateFs: RelativeFS;
|
||||||
|
private _server?: ViteDevServer;
|
||||||
|
|
||||||
|
constructor(public options: ProjectOptions) {
|
||||||
|
this.userFs = new RelativeFS(options.userPath ?? process.cwd());
|
||||||
|
this.templateFs = new RelativeFS(options.templatePath ?? "src/cli/commands/dev/template");
|
||||||
|
}
|
||||||
|
|
||||||
|
get server() {
|
||||||
|
if (!this._server) {
|
||||||
|
throw new Error("Server not initialized");
|
||||||
|
}
|
||||||
|
return this._server!;
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
const source = this.templateFs.root;
|
||||||
|
const destination = this.userFs.root;
|
||||||
|
|
||||||
|
// recursively copy all files and directories from source to dest
|
||||||
|
const copyRecursive = async (src: string, dst: string) => {
|
||||||
|
const entries = await readdir(src, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const srcPath = path.join(src, entry.name);
|
||||||
|
const dstPath = path.join(dst, entry.name);
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
await this.userFs.makeDir(path.relative(destination, dstPath));
|
||||||
|
await copyRecursive(srcPath, dstPath);
|
||||||
|
} else if (entry.isFile()) {
|
||||||
|
const exists = await stat(dstPath)
|
||||||
|
.then((s) => s.isFile())
|
||||||
|
.catch(() => null);
|
||||||
|
|
||||||
|
// only copy everything from ".bknd" with override
|
||||||
|
if (!exists || (dstPath.includes(".bknd") && !dstPath.includes("bknd-types.d.ts"))) {
|
||||||
|
await copyFile(srcPath, dstPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await copyRecursive(source, destination);
|
||||||
|
this._server = await createServer(this.getViteConfig());
|
||||||
|
}
|
||||||
|
|
||||||
|
private getViteConfig(): UserConfig {
|
||||||
|
return {
|
||||||
|
clearScreen: false,
|
||||||
|
publicDir: getRelativeDistPath() + "/static",
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
tailwindcss(),
|
||||||
|
devFsVitePlugin({ configFile: ".bknd/bknd.config.ts" }) as any,
|
||||||
|
cloudflare({
|
||||||
|
configPath: this.userFs.path(".bknd/wrangler.json"),
|
||||||
|
persistState: {
|
||||||
|
path: this.userFs.path(".bknd/state"),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
injectMain({
|
||||||
|
appPath: "./src/App.tsx",
|
||||||
|
rootId: "root",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
build: {
|
||||||
|
minify: true,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
dedupe: ["react", "react-dom"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async build() {
|
||||||
|
const builder = await createBuilder(this.getViteConfig());
|
||||||
|
await builder.buildApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
async listen() {
|
||||||
|
await this.server.listen();
|
||||||
|
this.server.printUrls();
|
||||||
|
this.server.bindCLIShortcuts({ print: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import {
|
||||||
|
mkdir,
|
||||||
|
stat,
|
||||||
|
readFile as nodeReadFile,
|
||||||
|
writeFile as nodeWriteFile,
|
||||||
|
} from "node:fs/promises";
|
||||||
|
import { getRootPath } from "cli/utils/sys";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
export class RelativeFS {
|
||||||
|
public root: string;
|
||||||
|
|
||||||
|
constructor(p: string) {
|
||||||
|
this.root = path.resolve(getRootPath(), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
path(p: string) {
|
||||||
|
return path.join(this.root, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
async hasFile(path: string) {
|
||||||
|
try {
|
||||||
|
const s = await stat(this.path(path));
|
||||||
|
return s.isFile();
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async hasDir(path: string) {
|
||||||
|
try {
|
||||||
|
const s = await stat(this.path(path));
|
||||||
|
return s.isDirectory();
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async readFile(p: string) {
|
||||||
|
return await nodeReadFile(this.path(p), "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
async writeFile(p: string, content: string) {
|
||||||
|
return await nodeWriteFile(this.path(p), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
async makeDir(p: string) {
|
||||||
|
return await mkdir(this.path(p), { recursive: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import type { PluginOption } from "vite";
|
||||||
|
|
||||||
|
export function injectMain(options?: { appPath?: string; rootId?: string }): PluginOption {
|
||||||
|
const appPath = options?.appPath ?? "/App.tsx";
|
||||||
|
const rootId = options?.rootId ?? "root";
|
||||||
|
const publicId = "/@virtual/react-main.js";
|
||||||
|
const internalId = "\0virtual-react-main.js";
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: "bknd-virtual-react-entry",
|
||||||
|
transformIndexHtml(html) {
|
||||||
|
return {
|
||||||
|
html,
|
||||||
|
tags: [
|
||||||
|
{
|
||||||
|
tag: "script",
|
||||||
|
injectTo: "body",
|
||||||
|
attrs: {
|
||||||
|
type: "module",
|
||||||
|
src: publicId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
resolveId(id) {
|
||||||
|
if (id === publicId) {
|
||||||
|
return internalId;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
load(id) {
|
||||||
|
if (id === internalId) {
|
||||||
|
return `
|
||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import App from "${appPath}";
|
||||||
|
import { ClientProvider } from "bknd/client";
|
||||||
|
|
||||||
|
const container = document.getElementById("${rootId}");
|
||||||
|
if (!container) {
|
||||||
|
throw new Error("Cannot find #${rootId} element");
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(container);
|
||||||
|
root.render(
|
||||||
|
React.createElement(
|
||||||
|
React.StrictMode,
|
||||||
|
null,
|
||||||
|
React.createElement(ClientProvider, null, React.createElement(App, null))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import type { DB } from "bknd";
|
||||||
|
import type { Insertable, Selectable, Updateable } from "kysely";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
type BkndEntity<T extends keyof DB> = Selectable<DB[T]>;
|
||||||
|
type BkndEntityCreate<T extends keyof DB> = Insertable<DB[T]>;
|
||||||
|
type BkndEntityUpdate<T extends keyof DB> = Updateable<DB[T]>;
|
||||||
|
}
|
||||||
+9
-14
@@ -1,27 +1,22 @@
|
|||||||
{
|
{
|
||||||
"include": ["**/*.ts", "**/*.tsx"],
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"jsx": "react-jsx",
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
"skipLibCheck": true,
|
||||||
"types": ["vite/client"],
|
|
||||||
|
|
||||||
/* Bundler mode */
|
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"verbatimModuleSyntax": false,
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
/* Linting */
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUncheckedSideEffectImports": true,
|
"noUncheckedSideEffectImports": true,
|
||||||
"paths": {
|
"types": ["vite/client"]
|
||||||
"@/*": ["./src/*"]
|
},
|
||||||
}
|
"include": ["bknd-types.d.ts"]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { serve } from "bknd/adapter/cloudflare";
|
||||||
|
import config from "./bknd.config";
|
||||||
|
|
||||||
|
export default serve(config);
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "bknd-dev",
|
||||||
|
"main": "./worker.ts",
|
||||||
|
"compatibility_date": "2025-10-08",
|
||||||
|
"compatibility_flags": ["nodejs_compat"],
|
||||||
|
"observability": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"assets": {
|
||||||
|
"binding": "ASSETS",
|
||||||
|
"directory": "../dist/client",
|
||||||
|
"not_found_handling": "single-page-application",
|
||||||
|
"run_worker_first": ["!/", "/admin*", "/api*", "!/assets/*"]
|
||||||
|
},
|
||||||
|
"vars": {
|
||||||
|
"ENVIRONMENT": "development"
|
||||||
|
},
|
||||||
|
"d1_databases": [
|
||||||
|
{
|
||||||
|
"binding": "DB"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"r2_buckets": [
|
||||||
|
{
|
||||||
|
"binding": "BUCKET"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>bknd + Vite + Cloudflare + React + TS</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import "./styles.css";
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return <div>Hello World</div>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Hono } from "hono";
|
||||||
|
import type { ServerEnv } from "bknd";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add custom routes to the API here. Base path is `/api`.
|
||||||
|
*/
|
||||||
|
export default new Hono<ServerEnv>().get("/", (c) => {
|
||||||
|
// const app = c.var.app;
|
||||||
|
// const api = app.getApi();
|
||||||
|
return c.json({ message: "Hello, world!" });
|
||||||
|
});
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { AppEvents, DatabaseEvents, type EventManager } from "bknd";
|
||||||
|
|
||||||
|
export default function (emgr: EventManager) {
|
||||||
|
// emgr.onEvent(AppEvents.AppRequest, async (event) => {
|
||||||
|
// console.log("Request received", event.params.request.url);
|
||||||
|
// });
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { em, entity, text, boolean, number, datetime, json, jsonSchema, enumm } from "bknd";
|
||||||
|
|
||||||
|
export default em({
|
||||||
|
// todos: entity("todos", {
|
||||||
|
// title: text(),
|
||||||
|
// done: boolean(),
|
||||||
|
// }),
|
||||||
|
});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./.bknd/tsconfig.json",
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
import {
|
||||||
|
mkdir,
|
||||||
|
stat,
|
||||||
|
readFile as nodeReadFile,
|
||||||
|
writeFile as nodeWriteFile,
|
||||||
|
} from "node:fs/promises";
|
||||||
|
import { getRootPath } from "cli/utils/sys";
|
||||||
|
|
||||||
|
export const TEMPLATE_PATH = "src/cli/commands/dev/template";
|
||||||
|
export const currentDir = process.cwd();
|
||||||
|
|
||||||
|
export const fs = (dir: string) => {
|
||||||
|
const PATH = path.resolve(getRootPath(), dir);
|
||||||
|
return {
|
||||||
|
PATH,
|
||||||
|
path: (_path: string) => path.join(PATH, _path),
|
||||||
|
hasFile: async (file: string) => {
|
||||||
|
try {
|
||||||
|
const s = await stat(path.join(PATH, file));
|
||||||
|
return s.isFile();
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasDir: async (_dir: string) => {
|
||||||
|
try {
|
||||||
|
const s = await stat(path.join(PATH, _dir));
|
||||||
|
return s.isDirectory();
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
readFile: (file: string) => nodeReadFile(path.join(PATH, file), "utf-8"),
|
||||||
|
readJsonFile: async (file: string) =>
|
||||||
|
JSON.parse(await nodeReadFile(path.join(PATH, file), "utf-8")),
|
||||||
|
writeFile: (file: string, content: string) => nodeWriteFile(path.join(PATH, file), content),
|
||||||
|
makeDir: (_newDir: string) => mkdir(path.join(PATH, _newDir)),
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -9,3 +9,4 @@ export { types } from "./types";
|
|||||||
export { mcp } from "./mcp/mcp";
|
export { mcp } from "./mcp/mcp";
|
||||||
export { sync } from "./sync";
|
export { sync } from "./sync";
|
||||||
export { secrets } from "./secrets";
|
export { secrets } from "./secrets";
|
||||||
|
export { dev } from "./dev/dev";
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ export const sync: CliCommand = (program) => {
|
|||||||
console.info("");
|
console.info("");
|
||||||
if (stmts.length === 0) {
|
if (stmts.length === 0) {
|
||||||
console.info(c.yellow("No changes to sync"));
|
console.info(c.yellow("No changes to sync"));
|
||||||
} else {
|
process.exit(0);
|
||||||
|
}
|
||||||
// @todo: currently assuming parameters aren't used
|
// @todo: currently assuming parameters aren't used
|
||||||
const sql = stmts.map((d) => d.sql).join(";\n") + ";";
|
const sql = stmts.map((d) => d.sql).join(";\n") + ";";
|
||||||
|
|
||||||
@@ -31,6 +32,20 @@ export const sync: CliCommand = (program) => {
|
|||||||
|
|
||||||
console.info(`\n${c.dim(`Executed ${c.cyan(stmts.length)} statement(s)`)}`);
|
console.info(`\n${c.dim(`Executed ${c.cyan(stmts.length)} statement(s)`)}`);
|
||||||
console.info(`${c.green("Database synced")}`);
|
console.info(`${c.green("Database synced")}`);
|
||||||
|
|
||||||
|
if (options.seed) {
|
||||||
|
console.info(c.dim("\nExecuting seed..."));
|
||||||
|
const seed = app.options?.seed;
|
||||||
|
if (seed) {
|
||||||
|
await app.options?.seed?.({
|
||||||
|
...app.modules.ctx(),
|
||||||
|
app: app,
|
||||||
|
});
|
||||||
|
console.info(c.green("Seed executed"));
|
||||||
|
} else {
|
||||||
|
console.info(c.yellow("No seed function provided"));
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (options.out) {
|
if (options.out) {
|
||||||
const output = options.sql ? sql : JSON.stringify(stmts, null, 2);
|
const output = options.sql ? sql : JSON.stringify(stmts, null, 2);
|
||||||
@@ -46,22 +61,6 @@ export const sync: CliCommand = (program) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (options.seed) {
|
|
||||||
console.info(c.dim("\nExecuting seed..."));
|
|
||||||
const seed = app.options?.seed;
|
|
||||||
if (seed) {
|
|
||||||
await seed({
|
|
||||||
...app.modules.ctx(),
|
|
||||||
app: app,
|
|
||||||
});
|
|
||||||
console.info(c.green("Seed executed"));
|
|
||||||
} else {
|
|
||||||
console.info(c.yellow("No seed function provided"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { Command } from "commander";
|
import { Command } from "commander";
|
||||||
import color from "picocolors";
|
import color from "picocolors";
|
||||||
import * as commands from "./commands";
|
import * as commands from "./commands";
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ export function isDebug(): boolean {
|
|||||||
try {
|
try {
|
||||||
// @ts-expect-error - this is a global variable in dev
|
// @ts-expect-error - this is a global variable in dev
|
||||||
return is_toggled(__isDev);
|
return is_toggled(__isDev);
|
||||||
} catch (_e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export class WithBuilder {
|
|||||||
if (query) {
|
if (query) {
|
||||||
subQuery = em.repo(other.entity).addOptionsToQueryBuilder(subQuery, query as any, {
|
subQuery = em.repo(other.entity).addOptionsToQueryBuilder(subQuery, query as any, {
|
||||||
ignore: ["with", cardinality === 1 ? "limit" : undefined].filter(Boolean) as any,
|
ignore: ["with", cardinality === 1 ? "limit" : undefined].filter(Boolean) as any,
|
||||||
alias: other.reference,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,12 +71,11 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected uploadFile<T extends FileUploadedEventData>(
|
protected uploadFile<T extends FileUploadedEventData>(
|
||||||
body: BodyInit,
|
body: File | Blob | ReadableStream | Buffer<ArrayBufferLike>,
|
||||||
opts?: {
|
opts?: {
|
||||||
filename?: string;
|
filename?: string;
|
||||||
path?: TInput;
|
path?: TInput;
|
||||||
_init?: Omit<RequestInit, "body">;
|
_init?: Omit<RequestInit, "body">;
|
||||||
query?: Record<string, any>;
|
|
||||||
},
|
},
|
||||||
): FetchPromise<ResponseObject<T>> {
|
): FetchPromise<ResponseObject<T>> {
|
||||||
const headers = {
|
const headers = {
|
||||||
@@ -103,22 +102,14 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
|||||||
headers,
|
headers,
|
||||||
};
|
};
|
||||||
if (opts?.path) {
|
if (opts?.path) {
|
||||||
return this.request<T>(opts.path, opts?.query, {
|
return this.post(opts.path, body, init);
|
||||||
...init,
|
|
||||||
body,
|
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name || name.length === 0) {
|
if (!name || name.length === 0) {
|
||||||
throw new Error("Invalid filename");
|
throw new Error("Invalid filename");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.request<T>(opts?.path ?? ["upload", name], opts?.query, {
|
return this.post<T>(opts?.path ?? ["upload", name], body, init);
|
||||||
...init,
|
|
||||||
body,
|
|
||||||
method: "POST",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload<T extends FileUploadedEventData>(
|
async upload<T extends FileUploadedEventData>(
|
||||||
@@ -128,7 +119,6 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
|||||||
_init?: Omit<RequestInit, "body">;
|
_init?: Omit<RequestInit, "body">;
|
||||||
path?: TInput;
|
path?: TInput;
|
||||||
fetcher?: ApiFetcher;
|
fetcher?: ApiFetcher;
|
||||||
query?: Record<string, any>;
|
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
if (item instanceof Request || typeof item === "string") {
|
if (item instanceof Request || typeof item === "string") {
|
||||||
@@ -154,7 +144,7 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.uploadFile<T>(item as BodyInit, opts);
|
return this.uploadFile<T>(item, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadToEntity(
|
async uploadToEntity(
|
||||||
@@ -165,14 +155,11 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
|||||||
opts?: {
|
opts?: {
|
||||||
_init?: Omit<RequestInit, "body">;
|
_init?: Omit<RequestInit, "body">;
|
||||||
fetcher?: typeof fetch;
|
fetcher?: typeof fetch;
|
||||||
overwrite?: boolean;
|
|
||||||
},
|
},
|
||||||
): Promise<ResponseObject<FileUploadedEventData & { result: DB["media"] }>> {
|
): Promise<ResponseObject<FileUploadedEventData & { result: DB["media"] }>> {
|
||||||
const query = opts?.overwrite !== undefined ? { overwrite: opts.overwrite } : undefined;
|
|
||||||
return this.upload(item, {
|
return this.upload(item, {
|
||||||
...opts,
|
...opts,
|
||||||
path: ["entity", entity, id, field],
|
path: ["entity", entity, id, field],
|
||||||
query,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { css, Style } from "hono/css";
|
|||||||
import { Controller } from "modules/Controller";
|
import { Controller } from "modules/Controller";
|
||||||
import * as SystemPermissions from "modules/permissions";
|
import * as SystemPermissions from "modules/permissions";
|
||||||
import type { TApiUser } from "Api";
|
import type { TApiUser } from "Api";
|
||||||
import type { AppTheme } from "ui/client/use-theme";
|
|
||||||
import type { Manifest } from "vite";
|
import type { Manifest } from "vite";
|
||||||
|
|
||||||
const htmlBkndContextReplace = "<!-- BKND_CONTEXT -->";
|
const htmlBkndContextReplace = "<!-- BKND_CONTEXT -->";
|
||||||
@@ -21,7 +20,7 @@ export type AdminBkndWindowContext = {
|
|||||||
logout_route: string;
|
logout_route: string;
|
||||||
admin_basepath: string;
|
admin_basepath: string;
|
||||||
logo_return_path?: string;
|
logo_return_path?: string;
|
||||||
theme?: AppTheme;
|
theme?: "dark" | "light" | "system";
|
||||||
};
|
};
|
||||||
|
|
||||||
// @todo: add migration to remove admin path from config
|
// @todo: add migration to remove admin path from config
|
||||||
@@ -32,7 +31,7 @@ export type AdminControllerOptions = {
|
|||||||
html?: string;
|
html?: string;
|
||||||
forceDev?: boolean | { mainPath: string };
|
forceDev?: boolean | { mainPath: string };
|
||||||
debugRerenders?: boolean;
|
debugRerenders?: boolean;
|
||||||
theme?: AppTheme;
|
theme?: "dark" | "light" | "system";
|
||||||
logoReturnPath?: string;
|
logoReturnPath?: string;
|
||||||
manifest?: Manifest;
|
manifest?: Manifest;
|
||||||
};
|
};
|
||||||
@@ -123,7 +122,7 @@ export class AdminController extends Controller {
|
|||||||
const obj: AdminBkndWindowContext = {
|
const obj: AdminBkndWindowContext = {
|
||||||
user: c.get("auth")?.user,
|
user: c.get("auth")?.user,
|
||||||
logout_route: authRoutes.logout,
|
logout_route: authRoutes.logout,
|
||||||
admin_basepath: this.options.adminBasepath.replace(/\/+$/, ""),
|
admin_basepath: this.options.adminBasepath,
|
||||||
theme: this.options.theme,
|
theme: this.options.theme,
|
||||||
logo_return_path: this.options.logoReturnPath,
|
logo_return_path: this.options.logoReturnPath,
|
||||||
};
|
};
|
||||||
@@ -210,9 +209,8 @@ export class AdminController extends Controller {
|
|||||||
},
|
},
|
||||||
}).then((res) => res.json());
|
}).then((res) => res.json());
|
||||||
} else {
|
} else {
|
||||||
const manifestPath = "bknd/dist/manifest.json";
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
manifest = await import(/* @vite-ignore */ manifestPath, {
|
manifest = await import("bknd/dist/manifest.json", {
|
||||||
with: { type: "json" },
|
with: { type: "json" },
|
||||||
}).then((res) => res.default);
|
}).then((res) => res.default);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export function emailOTP({
|
|||||||
"/login",
|
"/login",
|
||||||
jsc(
|
jsc(
|
||||||
"json",
|
"json",
|
||||||
s.strictObject({
|
s.object({
|
||||||
email: s.string({ format: "email" }),
|
email: s.string({ format: "email" }),
|
||||||
code: s.string({ minLength: 1 }).optional(),
|
code: s.string({ minLength: 1 }).optional(),
|
||||||
}),
|
}),
|
||||||
@@ -213,7 +213,7 @@ export function emailOTP({
|
|||||||
),
|
),
|
||||||
jsc("query", s.object({ redirect: s.string().optional() })),
|
jsc("query", s.object({ redirect: s.string().optional() })),
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const { email, code, ...rest } = c.req.valid("json");
|
const { email, code } = c.req.valid("json");
|
||||||
const { redirect } = c.req.valid("query");
|
const { redirect } = c.req.valid("query");
|
||||||
|
|
||||||
// throw if user exists
|
// throw if user exists
|
||||||
@@ -232,7 +232,6 @@ export function emailOTP({
|
|||||||
await em.mutator(entityName).updateOne(otpData.id, { used_at: new Date() });
|
await em.mutator(entityName).updateOne(otpData.id, { used_at: new Date() });
|
||||||
|
|
||||||
const user = await app.createUser({
|
const user = await app.createUser({
|
||||||
...rest,
|
|
||||||
email,
|
email,
|
||||||
password: randomString(32, true),
|
password: randomString(32, true),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -71,54 +71,4 @@ describe("timestamps plugin", () => {
|
|||||||
expect(updatedData.updated_at).toBeDefined();
|
expect(updatedData.updated_at).toBeDefined();
|
||||||
expect(updatedData.updated_at).toBeInstanceOf(Date);
|
expect(updatedData.updated_at).toBeInstanceOf(Date);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("index strategy", async () => {
|
|
||||||
const app = createApp({
|
|
||||||
config: {
|
|
||||||
data: em({
|
|
||||||
posts: entity("posts", {
|
|
||||||
title: text(),
|
|
||||||
}),
|
|
||||||
}).toJSON(),
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
plugins: [timestamps({ entities: ["posts"] })],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await app.build();
|
|
||||||
expect(app.em.indices.length).toBe(0);
|
|
||||||
{
|
|
||||||
const app = createApp({
|
|
||||||
config: {
|
|
||||||
data: em({
|
|
||||||
posts: entity("posts", {
|
|
||||||
title: text(),
|
|
||||||
}),
|
|
||||||
}).toJSON(),
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
plugins: [timestamps({ entities: ["posts"], indexStrategy: "composite" })],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await app.build();
|
|
||||||
expect(app.em.indices.length).toBe(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const app = createApp({
|
|
||||||
config: {
|
|
||||||
data: em({
|
|
||||||
posts: entity("posts", {
|
|
||||||
title: text(),
|
|
||||||
}),
|
|
||||||
}).toJSON(),
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
plugins: [timestamps({ entities: ["posts"], indexStrategy: "individual" })],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await app.build();
|
|
||||||
expect(app.em.indices.length).toBe(2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { $console } from "bknd/utils";
|
|||||||
export type TimestampsPluginOptions = {
|
export type TimestampsPluginOptions = {
|
||||||
entities: string[];
|
entities: string[];
|
||||||
setUpdatedOnCreate?: boolean;
|
setUpdatedOnCreate?: boolean;
|
||||||
indexStrategy?: "composite" | "individual";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +19,6 @@ export type TimestampsPluginOptions = {
|
|||||||
export function timestamps({
|
export function timestamps({
|
||||||
entities = [],
|
entities = [],
|
||||||
setUpdatedOnCreate = true,
|
setUpdatedOnCreate = true,
|
||||||
indexStrategy,
|
|
||||||
}: TimestampsPluginOptions): AppPlugin {
|
}: TimestampsPluginOptions): AppPlugin {
|
||||||
return (app: App) => ({
|
return (app: App) => ({
|
||||||
name: "timestamps",
|
name: "timestamps",
|
||||||
@@ -31,11 +29,12 @@ export function timestamps({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const appEntities = app.em.entities.map((e) => e.name);
|
const appEntities = app.em.entities.map((e) => e.name);
|
||||||
const actualEntities = entities.filter((e) => appEntities.includes(e));
|
|
||||||
|
|
||||||
return em(
|
return em(
|
||||||
Object.fromEntries(
|
Object.fromEntries(
|
||||||
actualEntities.map((e) => [
|
entities
|
||||||
|
.filter((e) => appEntities.includes(e))
|
||||||
|
.map((e) => [
|
||||||
e,
|
e,
|
||||||
entity(e, {
|
entity(e, {
|
||||||
created_at: datetime(),
|
created_at: datetime(),
|
||||||
@@ -43,23 +42,6 @@ export function timestamps({
|
|||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
(fns, schema) => {
|
|
||||||
if (indexStrategy) {
|
|
||||||
for (const entity of actualEntities) {
|
|
||||||
if (entity in schema) {
|
|
||||||
switch (indexStrategy) {
|
|
||||||
case "composite":
|
|
||||||
fns.index(schema[entity]!).on(["created_at", "updated_at"]);
|
|
||||||
break;
|
|
||||||
case "individual":
|
|
||||||
fns.index(schema[entity]!).on(["created_at"]);
|
|
||||||
fns.index(schema[entity]!).on(["updated_at"]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onBuilt: async () => {
|
onBuilt: async () => {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export class AppReduced {
|
|||||||
protected appJson: AppType,
|
protected appJson: AppType,
|
||||||
protected _options: BkndAdminProps["config"] = {},
|
protected _options: BkndAdminProps["config"] = {},
|
||||||
) {
|
) {
|
||||||
|
//console.log("received appjson", appJson);
|
||||||
|
|
||||||
this._entities = Object.entries(this.appJson.data.entities ?? {}).map(([name, entity]) => {
|
this._entities = Object.entries(this.appJson.data.entities ?? {}).map(([name, entity]) => {
|
||||||
return constructEntity(name, entity);
|
return constructEntity(name, entity);
|
||||||
@@ -69,27 +70,20 @@ export class AppReduced {
|
|||||||
|
|
||||||
get options() {
|
get options() {
|
||||||
return {
|
return {
|
||||||
basepath: "/",
|
basepath: "",
|
||||||
logo_return_path: "/",
|
logo_return_path: "/",
|
||||||
...this._options,
|
...this._options,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
withBasePath(path: string | string[], absolute = false): string {
|
|
||||||
const paths = Array.isArray(path) ? path : [path];
|
|
||||||
return [absolute ? "~" : null, this.options.basepath, ...paths]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join("/")
|
|
||||||
.replace(/\/+/g, "/")
|
|
||||||
.replace(/\/$/, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
getSettingsPath(path: string[] = []): string {
|
getSettingsPath(path: string[] = []): string {
|
||||||
return this.withBasePath(["settings", ...path], true);
|
const base = `~/${this.options.basepath}/settings`.replace(/\/+/g, "/");
|
||||||
|
return [base, ...path].join("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
getAbsolutePath(path?: string): string {
|
getAbsolutePath(path?: string): string {
|
||||||
return this.withBasePath(path ?? [], true);
|
const { basepath } = this.options;
|
||||||
|
return (path ? `~/${basepath}/${path}` : `~/${basepath}`).replace(/\/+/g, "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
getAuthConfig() {
|
getAuthConfig() {
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export function AuthForm({
|
|||||||
</Group>
|
</Group>
|
||||||
<Group>
|
<Group>
|
||||||
<Label htmlFor="password">Password</Label>
|
<Label htmlFor="password">Password</Label>
|
||||||
<Password name="password" required minLength={1} />
|
<Password name="password" required minLength={8} />
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -474,7 +474,6 @@ type SectionHeaderAccordionItemProps = {
|
|||||||
ActiveIcon?: any;
|
ActiveIcon?: any;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
renderHeaderRight?: (props: { open: boolean }) => React.ReactNode;
|
renderHeaderRight?: (props: { open: boolean }) => React.ReactNode;
|
||||||
scrollContainerRef?: React.RefObject<HTMLDivElement>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SectionHeaderAccordionItem = ({
|
export const SectionHeaderAccordionItem = ({
|
||||||
@@ -484,7 +483,6 @@ export const SectionHeaderAccordionItem = ({
|
|||||||
ActiveIcon = IconChevronUp,
|
ActiveIcon = IconChevronUp,
|
||||||
children,
|
children,
|
||||||
renderHeaderRight,
|
renderHeaderRight,
|
||||||
scrollContainerRef,
|
|
||||||
}: SectionHeaderAccordionItemProps) => (
|
}: SectionHeaderAccordionItemProps) => (
|
||||||
<div
|
<div
|
||||||
style={{ minHeight: 49 }}
|
style={{ minHeight: 49 }}
|
||||||
@@ -495,8 +493,6 @@ export const SectionHeaderAccordionItem = ({
|
|||||||
: "flex-initial cursor-pointer hover:bg-primary/5",
|
: "flex-initial cursor-pointer hover:bg-primary/5",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/** biome-ignore lint/a11y/noStaticElementInteractions: . */}
|
|
||||||
{/** biome-ignore lint/a11y/useKeyWithClickEvents: . */}
|
|
||||||
<div
|
<div
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"flex flex-row bg-muted/10 border-muted border-b h-14 py-4 pr-4 pl-2 items-center gap-2",
|
"flex flex-row bg-muted/10 border-muted border-b h-14 py-4 pr-4 pl-2 items-center gap-2",
|
||||||
@@ -505,12 +501,14 @@ export const SectionHeaderAccordionItem = ({
|
|||||||
>
|
>
|
||||||
<IconButton Icon={open ? ActiveIcon : IconChevronDown} disabled={open} />
|
<IconButton Icon={open ? ActiveIcon : IconChevronDown} disabled={open} />
|
||||||
<h2 className="text-lg dark:font-bold font-semibold select-text">{title}</h2>
|
<h2 className="text-lg dark:font-bold font-semibold select-text">{title}</h2>
|
||||||
<div className="flex grow" />
|
<div className="flex flex-grow" />
|
||||||
{renderHeaderRight?.({ open })}
|
{renderHeaderRight?.({ open })}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
ref={scrollContainerRef}
|
className={twMerge(
|
||||||
className={twMerge("overflow-y-scroll transition-all", open ? " grow" : "h-0 opacity-0")}
|
"overflow-y-scroll transition-all",
|
||||||
|
open ? " flex-grow" : "h-0 opacity-0",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
@@ -520,25 +518,14 @@ export const SectionHeaderAccordionItem = ({
|
|||||||
export const RouteAwareSectionHeaderAccordionItem = ({
|
export const RouteAwareSectionHeaderAccordionItem = ({
|
||||||
routePattern,
|
routePattern,
|
||||||
identifier,
|
identifier,
|
||||||
renderHeaderRight,
|
|
||||||
...props
|
...props
|
||||||
}: Omit<SectionHeaderAccordionItemProps, "open" | "toggle" | "renderHeaderRight"> & {
|
}: Omit<SectionHeaderAccordionItemProps, "open" | "toggle"> & {
|
||||||
renderHeaderRight?: (props: { open: boolean; active: boolean }) => React.ReactNode;
|
|
||||||
// it's optional because it could be provided using the context
|
// it's optional because it could be provided using the context
|
||||||
routePattern?: string;
|
routePattern?: string;
|
||||||
identifier: string;
|
identifier: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { active, toggle } = useRoutePathState(routePattern, identifier);
|
const { active, toggle } = useRoutePathState(routePattern, identifier);
|
||||||
return (
|
return <SectionHeaderAccordionItem {...props} open={active} toggle={toggle} />;
|
||||||
<SectionHeaderAccordionItem
|
|
||||||
{...props}
|
|
||||||
open={active}
|
|
||||||
toggle={toggle}
|
|
||||||
renderHeaderRight={
|
|
||||||
renderHeaderRight && ((props) => renderHeaderRight?.({ open: props.open, active }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Separator = ({ className, ...props }: ComponentPropsWithoutRef<"hr">) => (
|
export const Separator = ({ className, ...props }: ComponentPropsWithoutRef<"hr">) => (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { SegmentedControl } from "@mantine/core";
|
import { SegmentedControl, Tooltip } from "@mantine/core";
|
||||||
import { IconApi, IconBook, IconKeyOff, IconSettings, IconUser } from "@tabler/icons-react";
|
import { IconApi, IconBook, IconKeyOff, IconSettings, IconUser } from "@tabler/icons-react";
|
||||||
import {
|
import {
|
||||||
TbDatabase,
|
TbDatabase,
|
||||||
@@ -24,35 +24,34 @@ import { useLocation } from "wouter";
|
|||||||
import { NavLink } from "./AppShell";
|
import { NavLink } from "./AppShell";
|
||||||
import { autoFormatString } from "core/utils";
|
import { autoFormatString } from "core/utils";
|
||||||
import { appShellStore } from "ui/store";
|
import { appShellStore } from "ui/store";
|
||||||
import { getVersion, isDebug } from "core/env";
|
import { getVersion } from "core/env";
|
||||||
import { McpIcon } from "ui/routes/tools/mcp/components/mcp-icon";
|
import { McpIcon } from "ui/routes/tools/mcp/components/mcp-icon";
|
||||||
import { useAppShellAdminOptions } from "ui/options";
|
import { useAppShellAdminOptions } from "ui/options";
|
||||||
|
|
||||||
export function HeaderNavigation() {
|
export function HeaderNavigation() {
|
||||||
const [location, navigate] = useLocation();
|
const [location, navigate] = useLocation();
|
||||||
const { config } = useBknd();
|
|
||||||
|
|
||||||
const items: {
|
const items: {
|
||||||
label: string;
|
label: string;
|
||||||
href: string;
|
href: string;
|
||||||
Icon?: any;
|
Icon: any;
|
||||||
exact?: boolean;
|
exact?: boolean;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}[] = [
|
}[] = [
|
||||||
|
/*{
|
||||||
|
label: "Base",
|
||||||
|
href: "#",
|
||||||
|
exact: true,
|
||||||
|
Icon: TbLayoutDashboard,
|
||||||
|
disabled: true,
|
||||||
|
tooltip: "Coming soon"
|
||||||
|
},*/
|
||||||
{ label: "Data", href: "/data", Icon: TbDatabase },
|
{ label: "Data", href: "/data", Icon: TbDatabase },
|
||||||
{ label: "Auth", href: "/auth", Icon: TbFingerprint },
|
{ label: "Auth", href: "/auth", Icon: TbFingerprint },
|
||||||
{ label: "Media", href: "/media", Icon: TbPhoto },
|
{ label: "Media", href: "/media", Icon: TbPhoto },
|
||||||
|
{ label: "Flows", href: "/flows", Icon: TbHierarchy2 },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isDebug() || Object.keys(config.flows?.flows ?? {}).length > 0) {
|
|
||||||
items.push({ label: "Flows", href: "/flows", Icon: TbHierarchy2 });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.server.mcp.enabled) {
|
|
||||||
items.push({ label: "MCP", href: "/tools/mcp", Icon: McpIcon });
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeItem = items.find((item) =>
|
const activeItem = items.find((item) =>
|
||||||
item.exact ? location === item.href : location.startsWith(item.href),
|
item.exact ? location === item.href : location.startsWith(item.href),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ReactDOM from "react-dom/client";
|
import * as ReactDOM from "react-dom/client";
|
||||||
import Admin from "./Admin";
|
import Admin from "./Admin";
|
||||||
|
//import "./main.css";
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
|
|||||||
@@ -378,7 +378,11 @@ function replaceEntitiesEnum(schema: Record<string, any>, entities: string[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const Policy = ({ permission }: { permission: TPermission }) => {
|
const Policy = ({
|
||||||
|
permission,
|
||||||
|
}: {
|
||||||
|
permission: TPermission;
|
||||||
|
}) => {
|
||||||
const { value } = useDerivedFieldContext("", ({ value }) => ({
|
const { value } = useDerivedFieldContext("", ({ value }) => ({
|
||||||
effect: (value?.effect ?? "allow") as "allow" | "deny" | "filter",
|
effect: (value?.effect ?? "allow") as "allow" | "deny" | "filter",
|
||||||
}));
|
}));
|
||||||
@@ -499,11 +503,10 @@ const CustomFieldWrapper = ({
|
|||||||
className: "max-w-none",
|
className: "max-w-none",
|
||||||
}}
|
}}
|
||||||
position="bottom-end"
|
position="bottom-end"
|
||||||
target={() => (
|
target={() =>
|
||||||
<div className="w-auto max-w-[80vw] md:max-w-120 bg-background overflow-scroll">
|
typeof schema.content === "object" ? (
|
||||||
{typeof schema.content === "object" ? (
|
|
||||||
<JsonViewer
|
<JsonViewer
|
||||||
className="pr-3 text-sm"
|
className="w-auto max-w-120 bg-background pr-3 text-sm"
|
||||||
json={schema.content}
|
json={schema.content}
|
||||||
title={schema.name}
|
title={schema.name}
|
||||||
expand={5}
|
expand={5}
|
||||||
@@ -512,11 +515,10 @@ const CustomFieldWrapper = ({
|
|||||||
<CodePreview
|
<CodePreview
|
||||||
code={schema.content}
|
code={schema.content}
|
||||||
lang="typescript"
|
lang="typescript"
|
||||||
className="p-3 text-sm"
|
className="w-auto max-w-120 bg-background p-3 text-sm"
|
||||||
/>
|
/>
|
||||||
)}
|
)
|
||||||
</div>
|
}
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<Button variant="ghost" size="smaller" IconLeft={TbCodeDots}>
|
<Button variant="ghost" size="smaller" IconLeft={TbCodeDots}>
|
||||||
{autoFormatString(schema.name)}
|
{autoFormatString(schema.name)}
|
||||||
|
|||||||
@@ -15,12 +15,7 @@ import { useBkndWindowContext } from "bknd/client";
|
|||||||
import ToolsRoutes from "./tools";
|
import ToolsRoutes from "./tools";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let TestRoutes: any;
|
const TestRoutes = lazy(() => import("./test"));
|
||||||
try {
|
|
||||||
if (import.meta.env.DEV) {
|
|
||||||
TestRoutes = lazy(() => import("./test"));
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
export function Routes({
|
export function Routes({
|
||||||
BkndWrapper,
|
BkndWrapper,
|
||||||
@@ -33,7 +28,7 @@ export function Routes({
|
|||||||
}) {
|
}) {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const ctx = useBkndWindowContext();
|
const ctx = useBkndWindowContext();
|
||||||
const actualBasePath = (basePath || ctx.admin_basepath).replace(/\/+$/, "");
|
const actualBasePath = basePath || ctx.admin_basepath;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="bknd-admin" className={theme + " antialiased"}>
|
<div id="bknd-admin" className={theme + " antialiased"}>
|
||||||
@@ -48,13 +43,11 @@ export function Routes({
|
|||||||
<Route path="/" nest>
|
<Route path="/" nest>
|
||||||
<Root>
|
<Root>
|
||||||
<Switch>
|
<Switch>
|
||||||
{TestRoutes && (
|
|
||||||
<Route path="/test*" nest>
|
<Route path="/test*" nest>
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<TestRoutes />
|
<TestRoutes />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Route>
|
</Route>
|
||||||
)}
|
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
|
|||||||
@@ -49,21 +49,19 @@ export const AuthSettings = ({ schema: _unsafe_copy, config }) => {
|
|||||||
try {
|
try {
|
||||||
const user_entity = config.entity_name ?? "users";
|
const user_entity = config.entity_name ?? "users";
|
||||||
const entities = _s.config.data.entities ?? {};
|
const entities = _s.config.data.entities ?? {};
|
||||||
|
console.log("entities", entities, user_entity);
|
||||||
const user_fields = Object.entries(entities[user_entity]?.fields ?? {})
|
const user_fields = Object.entries(entities[user_entity]?.fields ?? {})
|
||||||
.map(([name, field]) => (!field.config?.virtual ? name : undefined))
|
.map(([name, field]) => (!field.config?.virtual ? name : undefined))
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
if (user_fields.length > 0) {
|
if (user_fields.length > 0) {
|
||||||
|
console.log("user_fields", user_fields);
|
||||||
_schema.properties.jwt.properties.fields.items.enum = user_fields;
|
_schema.properties.jwt.properties.fields.items.enum = user_fields;
|
||||||
_schema.properties.jwt.properties.fields.uniqueItems = true;
|
_schema.properties.jwt.properties.fields.uniqueItems = true;
|
||||||
uiSchema.jwt.fields["ui:widget"] = "checkboxes";
|
uiSchema.jwt.fields["ui:widget"] = "checkboxes";
|
||||||
}
|
}
|
||||||
|
} catch (e) {}
|
||||||
const roles = Object.keys(config.roles ?? {});
|
console.log("_s", _s);
|
||||||
if (roles.length > 0) {
|
|
||||||
_schema.properties.default_role_register.enum = roles;
|
|
||||||
}
|
|
||||||
} catch (_e) {}
|
|
||||||
const roleSchema = _schema.properties.roles?.additionalProperties ?? { type: "object" };
|
const roleSchema = _schema.properties.roles?.additionalProperties ?? { type: "object" };
|
||||||
/* if (_s.permissions) {
|
/* if (_s.permissions) {
|
||||||
roleSchema.properties.permissions.items.enum = _s.permissions;
|
roleSchema.properties.permissions.items.enum = _s.permissions;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default function ToolsRoutes() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Route path="/" component={ToolsIndex} />
|
<Route path="/" component={ToolsIndex} />
|
||||||
<Route path="/mcp*" component={ToolsMcp} nest />
|
<Route path="/mcp" component={ToolsMcp} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ import { Empty } from "ui/components/display/Empty";
|
|||||||
import { Button } from "ui/components/buttons/Button";
|
import { Button } from "ui/components/buttons/Button";
|
||||||
import { appShellStore } from "ui/store";
|
import { appShellStore } from "ui/store";
|
||||||
import { useBrowserTitle } from "ui/hooks/use-browser-title";
|
import { useBrowserTitle } from "ui/hooks/use-browser-title";
|
||||||
import { RoutePathStateProvider } from "ui/hooks/use-route-path-state";
|
|
||||||
import { Route, Switch } from "wouter";
|
|
||||||
|
|
||||||
export default function ToolsMcp() {
|
export default function ToolsMcp() {
|
||||||
useBrowserTitle(["MCP UI"]);
|
useBrowserTitle(["MCP UI"]);
|
||||||
|
|
||||||
const { config } = useBknd();
|
const { config } = useBknd();
|
||||||
|
const feature = useMcpStore((state) => state.feature);
|
||||||
|
const setFeature = useMcpStore((state) => state.setFeature);
|
||||||
|
const content = useMcpStore((state) => state.content);
|
||||||
const openSidebar = appShellStore((store) => store.toggleSidebar("default"));
|
const openSidebar = appShellStore((store) => store.toggleSidebar("default"));
|
||||||
const mcpPath = config.server.mcp.path;
|
const mcpPath = config.server.mcp.path;
|
||||||
|
|
||||||
@@ -28,7 +29,6 @@ export default function ToolsMcp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RoutePathStateProvider path={"/:type?"} defaultIdentifier="tools">
|
|
||||||
<div className="flex flex-col flex-grow max-w-screen">
|
<div className="flex flex-col flex-grow max-w-screen">
|
||||||
<AppShell.SectionHeader>
|
<AppShell.SectionHeader>
|
||||||
<div className="flex flex-row gap-4 items-center">
|
<div className="flex flex-row gap-4 items-center">
|
||||||
@@ -47,26 +47,23 @@ export default function ToolsMcp() {
|
|||||||
</div>
|
</div>
|
||||||
</AppShell.SectionHeader>
|
</AppShell.SectionHeader>
|
||||||
|
|
||||||
<div className="flex grow h-full">
|
<div className="flex h-full">
|
||||||
<AppShell.Sidebar>
|
<AppShell.Sidebar>
|
||||||
<Tools.Sidebar />
|
<Tools.Sidebar open={feature === "tools"} toggle={() => setFeature("tools")} />
|
||||||
<AppShell.RouteAwareSectionHeaderAccordionItem
|
<AppShell.SectionHeaderAccordionItem
|
||||||
title="Resources"
|
title="Resources"
|
||||||
identifier="resources"
|
open={feature === "resources"}
|
||||||
|
toggle={() => setFeature("resources")}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col flex-grow p-3 gap-3 justify-center items-center opacity-40">
|
<div className="flex flex-col flex-grow p-3 gap-3 justify-center items-center opacity-40">
|
||||||
<i>Resources</i>
|
<i>Resources</i>
|
||||||
</div>
|
</div>
|
||||||
</AppShell.RouteAwareSectionHeaderAccordionItem>
|
</AppShell.SectionHeaderAccordionItem>
|
||||||
</AppShell.Sidebar>
|
</AppShell.Sidebar>
|
||||||
|
{feature === "tools" && <Tools.Content />}
|
||||||
|
|
||||||
<Switch>
|
{!content && (
|
||||||
<Route path="/tools/:toolName?" component={Tools.Content} />
|
<Empty title="No tool selected" description="Please select a tool to continue.">
|
||||||
<Route path="*">
|
|
||||||
<Empty
|
|
||||||
title="No tool selected"
|
|
||||||
description="Please select a tool to continue."
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
onClick={() => openSidebar()}
|
onClick={() => openSidebar()}
|
||||||
@@ -75,10 +72,8 @@ export default function ToolsMcp() {
|
|||||||
Open Tools
|
Open Tools
|
||||||
</Button>
|
</Button>
|
||||||
</Empty>
|
</Empty>
|
||||||
</Route>
|
)}
|
||||||
</Switch>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</RoutePathStateProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,23 @@ import { combine } from "zustand/middleware";
|
|||||||
|
|
||||||
import type { ToolJson } from "jsonv-ts/mcp";
|
import type { ToolJson } from "jsonv-ts/mcp";
|
||||||
|
|
||||||
|
const FEATURES = ["tools", "resources"] as const;
|
||||||
|
export type Feature = (typeof FEATURES)[number];
|
||||||
|
|
||||||
export const useMcpStore = create(
|
export const useMcpStore = create(
|
||||||
combine(
|
combine(
|
||||||
{
|
{
|
||||||
tools: [] as ToolJson[],
|
tools: [] as ToolJson[],
|
||||||
|
feature: "tools" as Feature | null,
|
||||||
|
content: null as ToolJson | null,
|
||||||
history: [] as { type: "request" | "response"; data: any }[],
|
history: [] as { type: "request" | "response"; data: any }[],
|
||||||
historyLimit: 50,
|
historyLimit: 50,
|
||||||
historyVisible: false,
|
historyVisible: false,
|
||||||
},
|
},
|
||||||
(set) => ({
|
(set) => ({
|
||||||
setTools: (tools: ToolJson[]) => set({ tools }),
|
setTools: (tools: ToolJson[]) => set({ tools }),
|
||||||
|
setFeature: (feature: Feature) => set({ feature }),
|
||||||
|
setContent: (content: ToolJson | null) => set({ content }),
|
||||||
addHistory: (type: "request" | "response", data: any) =>
|
addHistory: (type: "request" | "response", data: any) =>
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
history: [{ type, data }, ...state.history.slice(0, state.historyLimit - 1)],
|
history: [{ type, data }, ...state.history.slice(0, state.historyLimit - 1)],
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { AppShell } from "ui/layouts/AppShell";
|
|||||||
import { TbHistory, TbHistoryOff, TbRefresh } from "react-icons/tb";
|
import { TbHistory, TbHistoryOff, TbRefresh } from "react-icons/tb";
|
||||||
import { IconButton } from "ui/components/buttons/IconButton";
|
import { IconButton } from "ui/components/buttons/IconButton";
|
||||||
import { JsonViewer, JsonViewerTabs, type JsonViewerTabsRef } from "ui/components/code/JsonViewer";
|
import { JsonViewer, JsonViewerTabs, type JsonViewerTabsRef } from "ui/components/code/JsonViewer";
|
||||||
|
import { twMerge } from "ui/elements/mocks/tailwind-merge";
|
||||||
import { Field, Form } from "ui/components/form/json-schema-form";
|
import { Field, Form } from "ui/components/form/json-schema-form";
|
||||||
import { Button } from "ui/components/buttons/Button";
|
import { Button } from "ui/components/buttons/Button";
|
||||||
import * as Formy from "ui/components/form/Formy";
|
import * as Formy from "ui/components/form/Formy";
|
||||||
@@ -12,18 +13,17 @@ import { appShellStore } from "ui/store";
|
|||||||
import { Icon } from "ui/components/display/Icon";
|
import { Icon } from "ui/components/display/Icon";
|
||||||
import { useMcpClient } from "./hooks/use-mcp-client";
|
import { useMcpClient } from "./hooks/use-mcp-client";
|
||||||
import { Tooltip } from "@mantine/core";
|
import { Tooltip } from "@mantine/core";
|
||||||
import { Link } from "ui/components/wouter/Link";
|
|
||||||
import { useParams } from "wouter";
|
|
||||||
|
|
||||||
export function Sidebar() {
|
export function Sidebar({ open, toggle }) {
|
||||||
const client = useMcpClient();
|
const client = useMcpClient();
|
||||||
const closeSidebar = appShellStore((store) => store.closeSidebar("default"));
|
const closeSidebar = appShellStore((store) => store.closeSidebar("default"));
|
||||||
const tools = useMcpStore((state) => state.tools);
|
const tools = useMcpStore((state) => state.tools);
|
||||||
const setTools = useMcpStore((state) => state.setTools);
|
const setTools = useMcpStore((state) => state.setTools);
|
||||||
|
const setContent = useMcpStore((state) => state.setContent);
|
||||||
|
const content = useMcpStore((state) => state.content);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [query, setQuery] = useState<string>("");
|
const [query, setQuery] = useState<string>("");
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const scrollContainerRef = useRef<HTMLDivElement>(undefined!);
|
|
||||||
|
|
||||||
const handleRefresh = useCallback(async () => {
|
const handleRefresh = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -39,22 +39,15 @@ export function Sidebar() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleRefresh().then(() => {
|
handleRefresh();
|
||||||
if (scrollContainerRef.current) {
|
|
||||||
const selectedTool = scrollContainerRef.current.querySelector(".active");
|
|
||||||
if (selectedTool) {
|
|
||||||
selectedTool.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppShell.RouteAwareSectionHeaderAccordionItem
|
<AppShell.SectionHeaderAccordionItem
|
||||||
scrollContainerRef={scrollContainerRef}
|
|
||||||
title="Tools"
|
title="Tools"
|
||||||
identifier="tools"
|
open={open}
|
||||||
renderHeaderRight={({ active }) => (
|
toggle={toggle}
|
||||||
|
renderHeaderRight={() => (
|
||||||
<div className="flex flex-row gap-2 items-center">
|
<div className="flex flex-row gap-2 items-center">
|
||||||
{error && (
|
{error && (
|
||||||
<Tooltip label={error}>
|
<Tooltip label={error}>
|
||||||
@@ -64,7 +57,7 @@ export function Sidebar() {
|
|||||||
<span className="flex-inline bg-primary/10 px-2 py-1.5 rounded-xl text-sm font-mono leading-none">
|
<span className="flex-inline bg-primary/10 px-2 py-1.5 rounded-xl text-sm font-mono leading-none">
|
||||||
{tools.length}
|
{tools.length}
|
||||||
</span>
|
</span>
|
||||||
<IconButton Icon={TbRefresh} disabled={!active || loading} onClick={handleRefresh} />
|
<IconButton Icon={TbRefresh} disabled={!open || loading} onClick={handleRefresh} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -83,11 +76,12 @@ export function Sidebar() {
|
|||||||
return (
|
return (
|
||||||
<AppShell.SidebarLink
|
<AppShell.SidebarLink
|
||||||
key={tool.name}
|
key={tool.name}
|
||||||
className="flex flex-col items-start h-auto py-3 gap-px"
|
className={twMerge(
|
||||||
as={Link}
|
"flex flex-col items-start h-auto py-3 gap-px",
|
||||||
href={`/tools/${tool.name}`}
|
content?.name === tool.name ? "active" : "",
|
||||||
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
//setContent(tool);
|
setContent(tool);
|
||||||
closeSidebar();
|
closeSidebar();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -98,34 +92,32 @@ export function Sidebar() {
|
|||||||
})}
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</AppShell.RouteAwareSectionHeaderAccordionItem>
|
</AppShell.SectionHeaderAccordionItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Content() {
|
export function Content() {
|
||||||
const { toolName } = useParams();
|
const content = useMcpStore((state) => state.content);
|
||||||
const tools = useMcpStore((state) => state.tools);
|
|
||||||
const tool = tools.find((tool) => tool.name === toolName);
|
|
||||||
const addHistory = useMcpStore((state) => state.addHistory);
|
const addHistory = useMcpStore((state) => state.addHistory);
|
||||||
const [payload, setPayload] = useState<object>(getTemplate(tool?.inputSchema));
|
const [payload, setPayload] = useState<object>(getTemplate(content?.inputSchema));
|
||||||
const [result, setResult] = useState<object | null>(null);
|
const [result, setResult] = useState<object | null>(null);
|
||||||
const historyVisible = useMcpStore((state) => state.historyVisible);
|
const historyVisible = useMcpStore((state) => state.historyVisible);
|
||||||
const setHistoryVisible = useMcpStore((state) => state.setHistoryVisible);
|
const setHistoryVisible = useMcpStore((state) => state.setHistoryVisible);
|
||||||
const client = useMcpClient();
|
const client = useMcpClient();
|
||||||
const jsonViewerTabsRef = useRef<JsonViewerTabsRef>(null);
|
const jsonViewerTabsRef = useRef<JsonViewerTabsRef>(null);
|
||||||
const hasInputSchema =
|
const hasInputSchema =
|
||||||
tool?.inputSchema && Object.keys(tool.inputSchema.properties ?? {}).length > 0;
|
content?.inputSchema && Object.keys(content.inputSchema.properties ?? {}).length > 0;
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPayload(getTemplate(tool?.inputSchema));
|
setPayload(getTemplate(content?.inputSchema));
|
||||||
setResult(null);
|
setResult(null);
|
||||||
}, [toolName]);
|
}, [content]);
|
||||||
|
|
||||||
const handleSubmit = useCallback(async () => {
|
const handleSubmit = useCallback(async () => {
|
||||||
if (!tool?.name) return;
|
if (!content?.name) return;
|
||||||
const request = {
|
const request = {
|
||||||
name: tool.name,
|
name: content.name,
|
||||||
arguments: payload,
|
arguments: payload,
|
||||||
};
|
};
|
||||||
startTransition(async () => {
|
startTransition(async () => {
|
||||||
@@ -139,7 +131,7 @@ export function Content() {
|
|||||||
});
|
});
|
||||||
}, [payload]);
|
}, [payload]);
|
||||||
|
|
||||||
if (!tool) return null;
|
if (!content) return null;
|
||||||
|
|
||||||
let readableResult = result;
|
let readableResult = result;
|
||||||
try {
|
try {
|
||||||
@@ -152,11 +144,11 @@ export function Content() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
className="flex grow flex-col min-w-0 max-w-screen"
|
className="flex flex-grow flex-col min-w-0 max-w-screen"
|
||||||
key={tool.name}
|
key={content.name}
|
||||||
schema={{
|
schema={{
|
||||||
title: "InputSchema",
|
title: "InputSchema",
|
||||||
...tool?.inputSchema,
|
...content?.inputSchema,
|
||||||
}}
|
}}
|
||||||
validateOn="submit"
|
validateOn="submit"
|
||||||
initialValues={payload}
|
initialValues={payload}
|
||||||
@@ -171,12 +163,12 @@ export function Content() {
|
|||||||
right={
|
right={
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
<IconButton
|
<IconButton
|
||||||
Icon={historyVisible ? TbHistoryOff : TbHistory}
|
Icon={historyVisible ? TbHistory : TbHistoryOff}
|
||||||
onClick={() => setHistoryVisible(!historyVisible)}
|
onClick={() => setHistoryVisible(!historyVisible)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={!tool?.name || isPending}
|
disabled={!content?.name || isPending}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
className="whitespace-nowrap"
|
className="whitespace-nowrap"
|
||||||
>
|
>
|
||||||
@@ -189,19 +181,19 @@ export function Content() {
|
|||||||
<span className="opacity-50">
|
<span className="opacity-50">
|
||||||
Tools <span className="opacity-70">/</span>
|
Tools <span className="opacity-70">/</span>
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
<span className="truncate">{tool?.name}</span>
|
<span className="truncate">{content?.name}</span>
|
||||||
</AppShell.SectionHeaderTitle>
|
</AppShell.SectionHeaderTitle>
|
||||||
</AppShell.SectionHeader>
|
</AppShell.SectionHeader>
|
||||||
<div className="flex grow flex-row w-vw">
|
<div className="flex flex-grow flex-row w-vw">
|
||||||
<div
|
<div
|
||||||
className="flex grow flex-col max-w-full"
|
className="flex flex-grow flex-col max-w-full"
|
||||||
style={{
|
style={{
|
||||||
width: "calc(100% - var(--sidebar-width-right) - 1px)",
|
width: "calc(100% - var(--sidebar-width-right) - 1px)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppShell.Scrollable>
|
<AppShell.Scrollable>
|
||||||
<div key={JSON.stringify(tool)} className="flex flex-col py-4 px-5 gap-4">
|
<div key={JSON.stringify(content)} className="flex flex-col py-4 px-5 gap-4">
|
||||||
<p className="text-primary/80">{tool?.description}</p>
|
<p className="text-primary/80">{content?.description}</p>
|
||||||
|
|
||||||
{hasInputSchema && <Field name="" />}
|
{hasInputSchema && <Field name="" />}
|
||||||
<JsonViewerTabs
|
<JsonViewerTabs
|
||||||
@@ -217,7 +209,7 @@ export function Content() {
|
|||||||
},
|
},
|
||||||
Result: { json: readableResult, title: "Result" },
|
Result: { json: readableResult, title: "Result" },
|
||||||
Configuration: {
|
Configuration: {
|
||||||
json: tool ?? null,
|
json: content ?? null,
|
||||||
title: "Configuration",
|
title: "Configuration",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
@@ -242,7 +234,7 @@ const History = () => {
|
|||||||
<>
|
<>
|
||||||
<AppShell.SectionHeader>History</AppShell.SectionHeader>
|
<AppShell.SectionHeader>History</AppShell.SectionHeader>
|
||||||
<AppShell.Scrollable>
|
<AppShell.Scrollable>
|
||||||
<div className="flex grow flex-col p-3 gap-1">
|
<div className="flex flex-col flex-grow p-3 gap-1">
|
||||||
{history.map((item, i) => (
|
{history.map((item, i) => (
|
||||||
<JsonViewer
|
<JsonViewer
|
||||||
key={`${item.type}-${i}`}
|
key={`${item.type}-${i}`}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
declare module '*.css';
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./dist/types",
|
"outDir": "./dist/types",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
|
"baseUrl": ".",
|
||||||
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
"tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo"
|
||||||
},
|
},
|
||||||
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
|
"include": ["./src/**/*.ts", "./src/**/*.tsx"],
|
||||||
|
|||||||
+3
-2
@@ -2,11 +2,11 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"module": "esnext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"allowImportingTsExtensions": false,
|
"allowImportingTsExtensions": false,
|
||||||
"target": "es2023",
|
"target": "ES2022",
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
|
"baseUrl": ".",
|
||||||
"outDir": "./dist/types",
|
"outDir": "./dist/types",
|
||||||
"paths": {
|
"paths": {
|
||||||
"*": ["./src/*"],
|
"*": ["./src/*"],
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
{
|
{
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"configVersion": 1,
|
"configVersion": 0,
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bknd",
|
"name": "bknd",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.3.3",
|
"@biomejs/biome": "2.3.3",
|
||||||
"@tsconfig/strictest": "^2.0.8",
|
"@tsconfig/strictest": "^2.0.7",
|
||||||
"@types/bun": "^1.3.1",
|
"@types/bun": "^1.3.1",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"miniflare": "^3.20250718.2",
|
"miniflare": "^3.20250718.2",
|
||||||
"typescript": "^6.0.2",
|
"typescript": "^5.9.3",
|
||||||
"verdaccio": "^6.2.1",
|
"verdaccio": "^6.2.1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"name": "bknd",
|
"name": "bknd",
|
||||||
"version": "0.20.0",
|
"version": "0.20.0-rc.1",
|
||||||
"bin": "./dist/cli/index.js",
|
"bin": "./dist/cli/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cfworker/json-schema": "^4.1.1",
|
"@cfworker/json-schema": "^4.1.1",
|
||||||
@@ -51,6 +51,7 @@
|
|||||||
"@aws-sdk/client-s3": "^3.922.0",
|
"@aws-sdk/client-s3": "^3.922.0",
|
||||||
"@bluwy/giget-core": "^0.1.6",
|
"@bluwy/giget-core": "^0.1.6",
|
||||||
"@clack/prompts": "^0.11.0",
|
"@clack/prompts": "^0.11.0",
|
||||||
|
"@cloudflare/vite-plugin": "^1.15.3",
|
||||||
"@cloudflare/vitest-pool-workers": "^0.10.4",
|
"@cloudflare/vitest-pool-workers": "^0.10.4",
|
||||||
"@cloudflare/workers-types": "^4.20251014.0",
|
"@cloudflare/workers-types": "^4.20251014.0",
|
||||||
"@dagrejs/dagre": "^1.1.4",
|
"@dagrejs/dagre": "^1.1.4",
|
||||||
@@ -113,7 +114,7 @@
|
|||||||
"vite-tsconfig-paths": "^5.1.4",
|
"vite-tsconfig-paths": "^5.1.4",
|
||||||
"vitest": "3.0.9",
|
"vitest": "3.0.9",
|
||||||
"wouter": "^3.7.1",
|
"wouter": "^3.7.1",
|
||||||
"wrangler": "^4.45.4",
|
"wrangler": "^4.52.1",
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@hono/node-server": "^1.19.6",
|
"@hono/node-server": "^1.19.6",
|
||||||
@@ -482,9 +483,11 @@
|
|||||||
|
|
||||||
"@clack/prompts": ["@clack/prompts@0.11.0", "https://registry.npmmirror.com/@clack/prompts/-/prompts-0.11.0.tgz", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
|
"@clack/prompts": ["@clack/prompts@0.11.0", "https://registry.npmmirror.com/@clack/prompts/-/prompts-0.11.0.tgz", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
|
||||||
|
|
||||||
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],
|
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.1", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-Nu8ahitGFFJztxUml9oD/DLb7Z28C8cd8F46IVQ7y5Btz575pvMY8AqZsXkX7Gds29eCKdMgIHjIvzskHgPSFg=="],
|
||||||
|
|
||||||
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.9", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20250927.0" }, "optionalPeers": ["workerd"] }, "sha512-Drm7qlTKnvncEv+DANiQNEonq0H0LyIsoFZYJ6tJ8OhAoy5udIE8yp6BsVDYcIjcYLIybp4M7c/P7ly/56SoHg=="],
|
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.11", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20251106.1" }, "optionalPeers": ["workerd"] }, "sha512-se23f1D4PxKrMKOq+Stz+Yn7AJ9ITHcEecXo2Yjb+UgbUDCEBch1FXQC6hx6uT5fNA3kmX3mfzeZiUmpK1W9IQ=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.15.3", "", { "dependencies": { "@cloudflare/unenv-preset": "2.7.11", "@remix-run/node-fetch-server": "^0.8.0", "get-port": "^7.1.0", "miniflare": "4.20251125.0", "picocolors": "^1.1.1", "tinyglobby": "^0.2.12", "unenv": "2.0.0-rc.24", "wrangler": "4.51.0", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0" } }, "sha512-o199VPWhPoKolIW7bfdk1Vzfpa/gE+1wA+J/B8Is8MH/pAVhNQG1rVxAXjNsOAuRMwfJxRwBKDScwVVZFuUUlw=="],
|
||||||
|
|
||||||
"@cloudflare/vitest-pool-workers": ["@cloudflare/vitest-pool-workers@0.10.4", "", { "dependencies": { "birpc": "0.2.14", "cjs-module-lexer": "^1.2.3", "devalue": "^5.3.2", "miniflare": "4.20251011.2", "semver": "^7.7.1", "wrangler": "4.45.4", "zod": "^3.22.3" }, "peerDependencies": { "@vitest/runner": "2.0.x - 3.2.x", "@vitest/snapshot": "2.0.x - 3.2.x", "vitest": "2.0.x - 3.2.x" } }, "sha512-PY9mexikRXEgJIXuFtr0ZWThbuBjq7VepC2sx8LpYizOLqoZ2NsUrJW+T+nojGxYNuzjulI8D14+mFezg7MhRQ=="],
|
"@cloudflare/vitest-pool-workers": ["@cloudflare/vitest-pool-workers@0.10.4", "", { "dependencies": { "birpc": "0.2.14", "cjs-module-lexer": "^1.2.3", "devalue": "^5.3.2", "miniflare": "4.20251011.2", "semver": "^7.7.1", "wrangler": "4.45.4", "zod": "^3.22.3" }, "peerDependencies": { "@vitest/runner": "2.0.x - 3.2.x", "@vitest/snapshot": "2.0.x - 3.2.x", "vitest": "2.0.x - 3.2.x" } }, "sha512-PY9mexikRXEgJIXuFtr0ZWThbuBjq7VepC2sx8LpYizOLqoZ2NsUrJW+T+nojGxYNuzjulI8D14+mFezg7MhRQ=="],
|
||||||
|
|
||||||
@@ -538,7 +541,7 @@
|
|||||||
|
|
||||||
"@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.3", "", {}, "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw=="],
|
"@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.3", "", {}, "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw=="],
|
||||||
|
|
||||||
"@cypress/request": ["@cypress/request@3.0.10", "", { "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~4.0.4", "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", "qs": "~6.14.1", "safe-buffer": "^5.1.2", "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" } }, "sha512-hauBrOdvu08vOsagkZ/Aju5XuiZx6ldsLfByg1htFeldhex+PeMrYauANzFsMJeAA0+dyPLbDoX2OYuvVoLDkQ=="],
|
"@cypress/request": ["@cypress/request@3.0.9", "", { "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~4.0.4", "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", "qs": "6.14.0", "safe-buffer": "^5.1.2", "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" } }, "sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw=="],
|
||||||
|
|
||||||
"@dagrejs/dagre": ["@dagrejs/dagre@1.1.8", "", { "dependencies": { "@dagrejs/graphlib": "2.2.4" } }, "sha512-5SEDlndt4W/LaVzPYJW+bSmSEZc9EzTf8rJ20WCKvjS5EAZAN0b+x0Yww7VMT4R3Wootkg+X9bUfUxazYw6Blw=="],
|
"@dagrejs/dagre": ["@dagrejs/dagre@1.1.8", "", { "dependencies": { "@dagrejs/graphlib": "2.2.4" } }, "sha512-5SEDlndt4W/LaVzPYJW+bSmSEZc9EzTf8rJ20WCKvjS5EAZAN0b+x0Yww7VMT4R3Wootkg+X9bUfUxazYw6Blw=="],
|
||||||
|
|
||||||
@@ -588,6 +591,8 @@
|
|||||||
|
|
||||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="],
|
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="],
|
||||||
|
|
||||||
|
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA=="],
|
||||||
|
|
||||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="],
|
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="],
|
||||||
|
|
||||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="],
|
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="],
|
||||||
@@ -798,8 +803,6 @@
|
|||||||
|
|
||||||
"@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="],
|
"@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="],
|
||||||
|
|
||||||
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
|
|
||||||
|
|
||||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||||
|
|
||||||
"@plasmicapp/data-sources-context": ["@plasmicapp/data-sources-context@0.1.21", "", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-DF86rstDK2zs/aTbPOUTWwXiVmFG2GDxXYvztIfXUNtuCvNDJKAZK2thYaULv9bGTRitPKWlwcz0zAMjYRHavQ=="],
|
"@plasmicapp/data-sources-context": ["@plasmicapp/data-sources-context@0.1.21", "", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-DF86rstDK2zs/aTbPOUTWwXiVmFG2GDxXYvztIfXUNtuCvNDJKAZK2thYaULv9bGTRitPKWlwcz0zAMjYRHavQ=="],
|
||||||
@@ -964,6 +967,8 @@
|
|||||||
|
|
||||||
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
"@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="],
|
||||||
|
|
||||||
|
"@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.8.1", "", {}, "sha512-J1dev372wtJqmqn9U/qbpbZxbJSQrogNN2+Qv1lKlpATpe/WQ9aCZfl/xSb9d2Rgh1IyLSvNxZAXPZxruO6Xig=="],
|
||||||
|
|
||||||
"@rjsf/core": ["@rjsf/core@5.22.2", "", { "dependencies": { "lodash": "^4.17.21", "lodash-es": "^4.17.21", "markdown-to-jsx": "^7.4.1", "nanoid": "^3.3.7", "prop-types": "^15.8.1" }, "peerDependencies": { "@rjsf/utils": "^5.22.x", "react": "^16.14.0 || >=17" } }, "sha512-awosG7y1fcu770GtKT64L3dHj3IF7iRiDwYU6jv1TUf5/Jf2TiWhTilTMyBfAEVfSiNWvh8U8dKH69k2WqswKQ=="],
|
"@rjsf/core": ["@rjsf/core@5.22.2", "", { "dependencies": { "lodash": "^4.17.21", "lodash-es": "^4.17.21", "markdown-to-jsx": "^7.4.1", "nanoid": "^3.3.7", "prop-types": "^15.8.1" }, "peerDependencies": { "@rjsf/utils": "^5.22.x", "react": "^16.14.0 || >=17" } }, "sha512-awosG7y1fcu770GtKT64L3dHj3IF7iRiDwYU6jv1TUf5/Jf2TiWhTilTMyBfAEVfSiNWvh8U8dKH69k2WqswKQ=="],
|
||||||
|
|
||||||
"@rjsf/utils": ["@rjsf/utils@5.22.0", "", { "dependencies": { "json-schema-merge-allof": "^0.8.1", "jsonpointer": "^5.0.1", "lodash": "^4.17.21", "lodash-es": "^4.17.21", "react-is": "^18.2.0" }, "peerDependencies": { "react": "^16.14.0 || >=17" } }, "sha512-lM9BxMziTo4R/WG82yBJA7qNrbFY/BxYE44rwacZoHW0wFprstXAu4qJpWcQihgWYSe0SRT7SxXJtsQG+TEjSw=="],
|
"@rjsf/utils": ["@rjsf/utils@5.22.0", "", { "dependencies": { "json-schema-merge-allof": "^0.8.1", "jsonpointer": "^5.0.1", "lodash": "^4.17.21", "lodash-es": "^4.17.21", "react-is": "^18.2.0" }, "peerDependencies": { "react": "^16.14.0 || >=17" } }, "sha512-lM9BxMziTo4R/WG82yBJA7qNrbFY/BxYE44rwacZoHW0wFprstXAu4qJpWcQihgWYSe0SRT7SxXJtsQG+TEjSw=="],
|
||||||
@@ -1216,7 +1221,7 @@
|
|||||||
|
|
||||||
"@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="],
|
"@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="],
|
||||||
|
|
||||||
"@tsconfig/strictest": ["@tsconfig/strictest@2.0.8", "", {}, "sha512-XnQ7vNz5HRN0r88GYf1J9JJjqtZPiHt2woGJOo2dYqyHGGcd6OLGqSlBB6p1j9mpzja6Oe5BoPqWmeDx6X9rLw=="],
|
"@tsconfig/strictest": ["@tsconfig/strictest@2.0.7", "", {}, "sha512-lG1bXQloBVvQXasPBZSBaWbs7GNW4txZMYs7XMUVzM0s4seCbJACYBPFIRpGNVD3gC8gndK00AZON1uBrJt4Fw=="],
|
||||||
|
|
||||||
"@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="],
|
"@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="],
|
||||||
|
|
||||||
@@ -1228,7 +1233,7 @@
|
|||||||
|
|
||||||
"@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="],
|
"@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
"@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
|
||||||
|
|
||||||
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
||||||
|
|
||||||
@@ -1324,41 +1329,41 @@
|
|||||||
|
|
||||||
"@ungap/with-resolvers": ["@ungap/with-resolvers@0.1.0", "", {}, "sha512-g7f0IkJdPW2xhY7H4iE72DAsIyfuwEFc6JWc2tYFwKDMWWAF699vGjrM348cwQuOXgHpe1gWFe+Eiyjx/ewvvw=="],
|
"@ungap/with-resolvers": ["@ungap/with-resolvers@0.1.0", "", {}, "sha512-g7f0IkJdPW2xhY7H4iE72DAsIyfuwEFc6JWc2tYFwKDMWWAF699vGjrM348cwQuOXgHpe1gWFe+Eiyjx/ewvvw=="],
|
||||||
|
|
||||||
"@verdaccio/auth": ["@verdaccio/auth@8.0.0-next-8.33", "", { "dependencies": { "@verdaccio/config": "8.0.0-next-8.33", "@verdaccio/core": "8.0.0-next-8.33", "@verdaccio/loaders": "8.0.0-next-8.23", "@verdaccio/signature": "8.0.0-next-8.25", "debug": "4.4.3", "lodash": "4.17.23", "verdaccio-htpasswd": "13.0.0-next-8.33" } }, "sha512-HRIqEj9J7t95ZG3pCVpaCJoXCNVPB0R2DpkEXfG19TXsapf/mv5vMnBYG6O1C/ShNagHMA7z+Z6NwFZXHUzm9g=="],
|
"@verdaccio/auth": ["@verdaccio/auth@8.0.0-next-8.24", "", { "dependencies": { "@verdaccio/config": "8.0.0-next-8.24", "@verdaccio/core": "8.0.0-next-8.24", "@verdaccio/loaders": "8.0.0-next-8.14", "@verdaccio/signature": "8.0.0-next-8.16", "debug": "4.4.3", "lodash": "4.17.21", "verdaccio-htpasswd": "13.0.0-next-8.24" } }, "sha512-stRp0DdTTx3p6dnh2cKOPJZOhu6sZOf8evV2fpYtADYW0UyhhZwELBXukpa5WGQ3H3rWzsXSaccra+D7tB1LgA=="],
|
||||||
|
|
||||||
"@verdaccio/config": ["@verdaccio/config@8.0.0-next-8.33", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.33", "debug": "4.4.3", "js-yaml": "4.1.1", "lodash": "4.17.23" } }, "sha512-6/n/qcVMbNTK8oFY8l6vlJMeG6zor/aOFcR2Wd6yCoKcehITigmLn8MFziZPriYivzxYJJRjlaKO9+HuuQSKCA=="],
|
"@verdaccio/config": ["@verdaccio/config@8.0.0-next-8.24", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.24", "debug": "4.4.3", "js-yaml": "4.1.0", "lodash": "4.17.21", "minimatch": "7.4.6" } }, "sha512-TRTVY6g2bH5V/1MQOXmdwOIuiT8i/tAtRX4T7FHwCutWTMfdFLgwy4e1VvTH8d1MwGRNyVin4O8PHVu5Xh4ouA=="],
|
||||||
|
|
||||||
"@verdaccio/core": ["@verdaccio/core@8.0.0-next-8.33", "", { "dependencies": { "ajv": "8.18.0", "http-errors": "2.0.1", "http-status-codes": "2.3.0", "minimatch": "7.4.9", "process-warning": "1.0.0", "semver": "7.7.4" } }, "sha512-ndPAfZVyN677y/EZX+USkL0/aOcU5rvnzS99nRCIHarZB44WMno9jl6FdX5Ax3b3exGo9GsxhEdbYHgOYaRlLQ=="],
|
"@verdaccio/core": ["@verdaccio/core@8.0.0-next-8.24", "", { "dependencies": { "ajv": "8.17.1", "http-errors": "2.0.0", "http-status-codes": "2.3.0", "minimatch": "7.4.6", "process-warning": "1.0.0", "semver": "7.7.3" } }, "sha512-58Et0Mj562ergUd7galslWNsTseOHBCDkCIHokmoeWGX4+P46Aoa9+G0laFHyZxxfkuGkZXhO1WHysc9LzkfiA=="],
|
||||||
|
|
||||||
"@verdaccio/file-locking": ["@verdaccio/file-locking@10.3.1", "", { "dependencies": { "lockfile": "1.0.4" } }, "sha512-oqYLfv3Yg3mAgw9qhASBpjD50osj2AX4IwbkUtyuhhKGyoFU9eZdrbeW6tpnqUnj6yBMtAPm2eGD4BwQuX400g=="],
|
"@verdaccio/file-locking": ["@verdaccio/file-locking@10.3.1", "", { "dependencies": { "lockfile": "1.0.4" } }, "sha512-oqYLfv3Yg3mAgw9qhASBpjD50osj2AX4IwbkUtyuhhKGyoFU9eZdrbeW6tpnqUnj6yBMtAPm2eGD4BwQuX400g=="],
|
||||||
|
|
||||||
"@verdaccio/hooks": ["@verdaccio/hooks@8.0.0-next-8.33", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.33", "@verdaccio/logger": "8.0.0-next-8.33", "debug": "4.4.3", "got-cjs": "12.5.4", "handlebars": "4.7.8" } }, "sha512-8xP6kVOCufjaZWriFtaxP3HEmvYTIBhcxWldui4eCG7dzBdZzPlCkRbYKWP8LMnOEIxbJNbeFkokOaI1nho1jg=="],
|
"@verdaccio/hooks": ["@verdaccio/hooks@8.0.0-next-8.24", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.24", "@verdaccio/logger": "8.0.0-next-8.24", "debug": "4.4.3", "got-cjs": "12.5.4", "handlebars": "4.7.8" } }, "sha512-jrBHk51rsANI47YbHCFKprBPelLDklwKhkMINEYnFOQwuB3HEcupd6hGNDaj64sRnZNoK2VuJH0cAWn0iqVErg=="],
|
||||||
|
|
||||||
"@verdaccio/loaders": ["@verdaccio/loaders@8.0.0-next-8.23", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.33", "debug": "4.4.3", "lodash": "4.17.23" } }, "sha512-UruK7pFz7aRKkR41/Hg/cPFSqX5Oxbx9rKGWK5ql3mvg2+xaWleRwknmnNjbVod7QK6cWsYA6cKnttRBTQuPkQ=="],
|
"@verdaccio/loaders": ["@verdaccio/loaders@8.0.0-next-8.14", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.24", "debug": "4.4.3", "lodash": "4.17.21" } }, "sha512-cWrTTJ7HWjrzhXIVVXPHFUFTdzbRgvU5Xwte8O/JPMtrEAxtbjg18kCIdQwAcomB1S+BkffnnQJ8TPLymnuCrg=="],
|
||||||
|
|
||||||
"@verdaccio/local-storage-legacy": ["@verdaccio/local-storage-legacy@11.1.1", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.21", "@verdaccio/file-locking": "10.3.1", "@verdaccio/streams": "10.2.1", "async": "3.2.6", "debug": "4.4.1", "lodash": "4.17.21", "lowdb": "1.0.0", "mkdirp": "1.0.4" } }, "sha512-P6ahH2W6/KqfJFKP+Eid7P134FHDLNvHa+i8KVgRVBeo2/IXb6FEANpM1mCVNvPANu0LCAmNJBOXweoUKViaoA=="],
|
"@verdaccio/local-storage-legacy": ["@verdaccio/local-storage-legacy@11.1.1", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.21", "@verdaccio/file-locking": "10.3.1", "@verdaccio/streams": "10.2.1", "async": "3.2.6", "debug": "4.4.1", "lodash": "4.17.21", "lowdb": "1.0.0", "mkdirp": "1.0.4" } }, "sha512-P6ahH2W6/KqfJFKP+Eid7P134FHDLNvHa+i8KVgRVBeo2/IXb6FEANpM1mCVNvPANu0LCAmNJBOXweoUKViaoA=="],
|
||||||
|
|
||||||
"@verdaccio/logger": ["@verdaccio/logger@8.0.0-next-8.33", "", { "dependencies": { "@verdaccio/logger-commons": "8.0.0-next-8.33", "pino": "9.14.0" } }, "sha512-8nR3hreOcINIbMIOohhxZNUL3l0uvgGYQecl8WJvR4XizYTp1vPHv4qz1CPyuaI5bKj1QFRqHkKvvtEnD1A0rw=="],
|
"@verdaccio/logger": ["@verdaccio/logger@8.0.0-next-8.24", "", { "dependencies": { "@verdaccio/logger-commons": "8.0.0-next-8.24", "pino": "9.13.1" } }, "sha512-7/arkwQy2zI5W5z9zMf5wyWiZx18xbLultteNPWHkQv9CtoFtuK+TSY8rH7ITtCGoNCcB94jvvTYRylxAdaHEw=="],
|
||||||
|
|
||||||
"@verdaccio/logger-commons": ["@verdaccio/logger-commons@8.0.0-next-8.33", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.33", "@verdaccio/logger-prettify": "8.0.0-next-8.4", "colorette": "2.0.20", "debug": "4.4.3" } }, "sha512-WmMq/6HyRliKWus3sQR9Pgodopzbp84dl/h/E0tnxuOmzc/eDwYCEiQMfFhIBaOlpVJdsdLYqNAM9+YkoSDK0g=="],
|
"@verdaccio/logger-commons": ["@verdaccio/logger-commons@8.0.0-next-8.24", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.24", "@verdaccio/logger-prettify": "8.0.0-next-8.4", "colorette": "2.0.20", "debug": "4.4.3" } }, "sha512-gEBUajG1m93xG+FJ+9+Mxg3FC9tSnP0RHyrhb4gPZPqft7JpRkwjbqpjxASGzPyxdTJZwiAefr7aafXv0xMpJA=="],
|
||||||
|
|
||||||
"@verdaccio/logger-prettify": ["@verdaccio/logger-prettify@8.0.0-next-8.4", "", { "dependencies": { "colorette": "2.0.20", "dayjs": "1.11.13", "lodash": "4.17.21", "on-exit-leak-free": "2.1.2", "pino-abstract-transport": "1.2.0", "sonic-boom": "3.8.1" } }, "sha512-gjI/JW29fyalutn/X1PQ0iNuGvzeVWKXRmnLa7gXVKhdi4p37l/j7YZ7n44XVbbiLIKAK0pbavEg9Yr66QrYaA=="],
|
"@verdaccio/logger-prettify": ["@verdaccio/logger-prettify@8.0.0-next-8.4", "", { "dependencies": { "colorette": "2.0.20", "dayjs": "1.11.13", "lodash": "4.17.21", "on-exit-leak-free": "2.1.2", "pino-abstract-transport": "1.2.0", "sonic-boom": "3.8.1" } }, "sha512-gjI/JW29fyalutn/X1PQ0iNuGvzeVWKXRmnLa7gXVKhdi4p37l/j7YZ7n44XVbbiLIKAK0pbavEg9Yr66QrYaA=="],
|
||||||
|
|
||||||
"@verdaccio/middleware": ["@verdaccio/middleware@8.0.0-next-8.33", "", { "dependencies": { "@verdaccio/config": "8.0.0-next-8.33", "@verdaccio/core": "8.0.0-next-8.33", "@verdaccio/url": "13.0.0-next-8.33", "debug": "4.4.3", "express": "4.22.1", "express-rate-limit": "5.5.1", "lodash": "4.17.23", "lru-cache": "7.18.3" } }, "sha512-iMzE3stUp/qocHzFX2+xkzUe6L89vWZ/J4a4v7IxPu6KqBH39XCg4gI8Qu0xXRIFeYSTpZNzRUO+FTkeZRhJ1A=="],
|
"@verdaccio/middleware": ["@verdaccio/middleware@8.0.0-next-8.24", "", { "dependencies": { "@verdaccio/config": "8.0.0-next-8.24", "@verdaccio/core": "8.0.0-next-8.24", "@verdaccio/url": "13.0.0-next-8.24", "debug": "4.4.3", "express": "4.21.2", "express-rate-limit": "5.5.1", "lodash": "4.17.21", "lru-cache": "7.18.3", "mime": "2.6.0" } }, "sha512-LuFralbC8bxl2yQx9prKEMrNbxj8BkojcUDIa+jZZRnghaZrMm1yHqf5eLHCrBBIOM3m2thJ6Ux2UfBCQP4UKA=="],
|
||||||
|
|
||||||
"@verdaccio/search-indexer": ["@verdaccio/search-indexer@8.0.0-next-8.5", "", {}, "sha512-0GC2tJKstbPg/W2PZl2yE+hoAxffD2ZWilEnEYSEo2e9UQpNIy2zg7KE/uMUq2P72Vf5EVfVzb8jdaH4KV4QeA=="],
|
"@verdaccio/search-indexer": ["@verdaccio/search-indexer@8.0.0-next-8.5", "", {}, "sha512-0GC2tJKstbPg/W2PZl2yE+hoAxffD2ZWilEnEYSEo2e9UQpNIy2zg7KE/uMUq2P72Vf5EVfVzb8jdaH4KV4QeA=="],
|
||||||
|
|
||||||
"@verdaccio/signature": ["@verdaccio/signature@8.0.0-next-8.25", "", { "dependencies": { "@verdaccio/config": "8.0.0-next-8.33", "@verdaccio/core": "8.0.0-next-8.33", "debug": "4.4.3", "jsonwebtoken": "9.0.3" } }, "sha512-3qMHZ9uXgNmRQDw7Bz3coCbXJAfI3q+bWRXQ0/fKg03LgSV6pbMD0HinGKcIBn0Kni+e8IsXxBfrs5ga4FF+Rg=="],
|
"@verdaccio/signature": ["@verdaccio/signature@8.0.0-next-8.16", "", { "dependencies": { "@verdaccio/config": "8.0.0-next-8.24", "@verdaccio/core": "8.0.0-next-8.24", "debug": "4.4.3", "jsonwebtoken": "9.0.2" } }, "sha512-JBIpoYJQFjo3ITTRjum1IjXxNrSYcPoBLZTPlt9OLL5Brd7s1fJkTBgs9tbcCRZPrek/XBIJ/cLFZZn8zkc/bQ=="],
|
||||||
|
|
||||||
"@verdaccio/streams": ["@verdaccio/streams@10.2.1", "", {}, "sha512-OojIG/f7UYKxC4dYX8x5ax8QhRx1b8OYUAMz82rUottCuzrssX/4nn5QE7Ank0DUSX3C9l/HPthc4d9uKRJqJQ=="],
|
"@verdaccio/streams": ["@verdaccio/streams@10.2.1", "", {}, "sha512-OojIG/f7UYKxC4dYX8x5ax8QhRx1b8OYUAMz82rUottCuzrssX/4nn5QE7Ank0DUSX3C9l/HPthc4d9uKRJqJQ=="],
|
||||||
|
|
||||||
"@verdaccio/tarball": ["@verdaccio/tarball@13.0.0-next-8.33", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.33", "@verdaccio/url": "13.0.0-next-8.33", "debug": "4.4.3", "gunzip-maybe": "1.4.2", "tar-stream": "3.1.7" } }, "sha512-VQZ8I5Fs6INxO1cY6Lpk4Wx3sD0Uy7YIzTY9qWrZNRPGDxGcZu2fyFMUfoNc1+ygJP0r5TTqdVAY74z0iQnsWg=="],
|
"@verdaccio/tarball": ["@verdaccio/tarball@13.0.0-next-8.24", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.24", "@verdaccio/url": "13.0.0-next-8.24", "debug": "4.4.3", "gunzip-maybe": "1.4.2", "tar-stream": "3.1.7" } }, "sha512-rDz8gWukO7dcaWzMTr7wMvKPUsRHclVzZljyTERplpIX9NWGHRsMRO7NjoTIUWtDS0euMlRVDnR8QVnYDWl2uA=="],
|
||||||
|
|
||||||
"@verdaccio/ui-theme": ["@verdaccio/ui-theme@8.0.0-next-8.30", "", {}, "sha512-MSvFnYeocTWp6hqtIqtwp7Zm920UNhO3zQkKTPT6qGNjarkhxCCWRcSOAz7oJZhpbyLXc85zuxOLkTezCl4KUg=="],
|
"@verdaccio/ui-theme": ["@verdaccio/ui-theme@8.0.0-next-8.24", "", {}, "sha512-A8lMenzJmC0EioBjQ9+L2e8tv/iEB/jLJKH4WJjPYa8B1xlm/LLUuk2aBg7pYD1fPWuazvJiH/NAnkrA09541g=="],
|
||||||
|
|
||||||
"@verdaccio/url": ["@verdaccio/url@13.0.0-next-8.33", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.33", "debug": "4.4.3", "validator": "13.15.26" } }, "sha512-26LzQTCuUFFcNasAdzayBqsYWBheQy+D4tSWnVtmj4kKQnVhufBvHLjpEjK1gqphOMx6/Mju0IQe0LsjRc1zdg=="],
|
"@verdaccio/url": ["@verdaccio/url@13.0.0-next-8.24", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.24", "debug": "4.4.3", "lodash": "4.17.21", "validator": "13.15.15" } }, "sha512-zNHR9qgiDTXp+IuOtz925tzGteXGj18IZphvxo2apgz3cAeUpL/nnmp4ZScczpxWxE8sT/88xVYgJm+Ec8fNCA=="],
|
||||||
|
|
||||||
"@verdaccio/utils": ["@verdaccio/utils@8.1.0-next-8.33", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.33", "lodash": "4.17.23", "minimatch": "7.4.9" } }, "sha512-WLE8qTBgzuZPa+gq/nKPWtF4MTMayaeOD3Qy6yfChxNvFXY+6yPFkS0QocXL7CdB2A+w+ylgTOOzKr0tWvyTpQ=="],
|
"@verdaccio/utils": ["@verdaccio/utils@8.1.0-next-8.24", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.24", "lodash": "4.17.21", "minimatch": "7.4.6" } }, "sha512-2e54Z1J1+OPM0LCxjkJHgwFm8jESsCYaX6ARs3+29hjoI75uiSphxFI3Hrhr+67ho/7Mtul0oyakK6l18MN/Dg=="],
|
||||||
|
|
||||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.0", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.43", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew=="],
|
"@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.0", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.43", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew=="],
|
||||||
|
|
||||||
@@ -1418,7 +1423,7 @@
|
|||||||
|
|
||||||
"agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
"agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="],
|
||||||
|
|
||||||
"ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
|
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||||
|
|
||||||
"ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="],
|
"ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="],
|
||||||
|
|
||||||
@@ -1610,7 +1615,7 @@
|
|||||||
|
|
||||||
"builtin-modules": ["builtin-modules@3.3.0", "", {}, "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw=="],
|
"builtin-modules": ["builtin-modules@3.3.0", "", {}, "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
"bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
||||||
|
|
||||||
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
|
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
|
||||||
|
|
||||||
@@ -1748,7 +1753,7 @@
|
|||||||
|
|
||||||
"core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
|
"core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="],
|
||||||
|
|
||||||
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
|
"cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="],
|
||||||
|
|
||||||
"cosmiconfig": ["cosmiconfig@6.0.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.1.0", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.7.2" } }, "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg=="],
|
"cosmiconfig": ["cosmiconfig@6.0.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.1.0", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.7.2" } }, "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg=="],
|
||||||
|
|
||||||
@@ -1932,7 +1937,7 @@
|
|||||||
|
|
||||||
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
"entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||||
|
|
||||||
"envinfo": ["envinfo@7.21.0", "", { "bin": { "envinfo": "dist/cli.js" } }, "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow=="],
|
"envinfo": ["envinfo@7.15.0", "", { "bin": { "envinfo": "dist/cli.js" } }, "sha512-chR+t7exF6y59kelhXw5I3849nTy7KIRO+ePdLMhCD+JRP/JvmkenDWP7QSFGlsHX+kxGxdDutOPrmj5j1HR6g=="],
|
||||||
|
|
||||||
"error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
|
"error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
|
||||||
|
|
||||||
@@ -2028,7 +2033,7 @@
|
|||||||
|
|
||||||
"expect-type": ["expect-type@1.2.0", "", {}, "sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA=="],
|
"expect-type": ["expect-type@1.2.0", "", {}, "sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA=="],
|
||||||
|
|
||||||
"express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "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.12", "proxy-addr": "~2.0.7", "qs": "~6.14.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" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="],
|
"express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "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.12", "proxy-addr": "~2.0.7", "qs": "6.13.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" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="],
|
||||||
|
|
||||||
"express-rate-limit": ["express-rate-limit@5.5.1", "", {}, "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg=="],
|
"express-rate-limit": ["express-rate-limit@5.5.1", "", {}, "sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg=="],
|
||||||
|
|
||||||
@@ -2234,7 +2239,7 @@
|
|||||||
|
|
||||||
"http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="],
|
"http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="],
|
||||||
|
|
||||||
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
||||||
|
|
||||||
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
||||||
|
|
||||||
@@ -2476,7 +2481,7 @@
|
|||||||
|
|
||||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||||
|
|
||||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||||
|
|
||||||
"jsbn": ["jsbn@0.1.1", "", {}, "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="],
|
"jsbn": ["jsbn@0.1.1", "", {}, "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="],
|
||||||
|
|
||||||
@@ -2516,7 +2521,7 @@
|
|||||||
|
|
||||||
"jsonv-ts": ["jsonv-ts@0.10.1", "", { "optionalDependencies": { "hono": "*" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-IfuXZigNjLQzW4X7dLRTpwd1pD1lk86SoXBWmLdF+VE6SE4PcXevWs8c/bPl7qVrZXhh8lYwbTF7TFtgO2/jXg=="],
|
"jsonv-ts": ["jsonv-ts@0.10.1", "", { "optionalDependencies": { "hono": "*" }, "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-IfuXZigNjLQzW4X7dLRTpwd1pD1lk86SoXBWmLdF+VE6SE4PcXevWs8c/bPl7qVrZXhh8lYwbTF7TFtgO2/jXg=="],
|
||||||
|
|
||||||
"jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="],
|
"jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="],
|
||||||
|
|
||||||
"jsprim": ["jsprim@2.0.2", "", { "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.4.0", "verror": "1.10.0" } }, "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ=="],
|
"jsprim": ["jsprim@2.0.2", "", { "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.4.0", "verror": "1.10.0" } }, "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ=="],
|
||||||
|
|
||||||
@@ -2524,9 +2529,9 @@
|
|||||||
|
|
||||||
"jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="],
|
"jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="],
|
||||||
|
|
||||||
"jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
|
"jwa": ["jwa@1.4.1", "", { "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA=="],
|
||||||
|
|
||||||
"jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
|
"jws": ["jws@3.2.2", "", { "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA=="],
|
||||||
|
|
||||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||||
|
|
||||||
@@ -2594,7 +2599,7 @@
|
|||||||
|
|
||||||
"lockfile": ["lockfile@1.0.4", "", { "dependencies": { "signal-exit": "^3.0.2" } }, "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA=="],
|
"lockfile": ["lockfile@1.0.4", "", { "dependencies": { "signal-exit": "^3.0.2" } }, "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA=="],
|
||||||
|
|
||||||
"lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
|
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||||
|
|
||||||
"lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
|
"lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
|
||||||
|
|
||||||
@@ -2690,7 +2695,7 @@
|
|||||||
|
|
||||||
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
|
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
|
||||||
|
|
||||||
"miniflare": ["miniflare@3.20250718.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250718.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-JuPrDJhwLrNLEJiNLWO7ZzJrv/Vv9kZuwMYCfv0LskQDM6Eonw4OvywO3CH/wCGjgHzha/qyjUh8JQ068TjDgQ=="],
|
"miniflare": ["miniflare@3.20250718.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250718.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-cW/NQPBKc+fb0FwcEu+z/v93DZd+/6q/AF0iR0VFELtNPOsCvLalq6ndO743A7wfZtFxMxvuDQUXNx3aKQhOwA=="],
|
||||||
|
|
||||||
"minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
"minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
@@ -2908,7 +2913,7 @@
|
|||||||
|
|
||||||
"pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="],
|
"pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="],
|
||||||
|
|
||||||
"pino": ["pino@9.14.0", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w=="],
|
"pino": ["pino@9.13.1", "", { "dependencies": { "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "slow-redact": "^0.3.0", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-Szuj+ViDTjKPQYiKumGmEn3frdl+ZPSdosHyt9SnUevFosOkMY2b7ipxlEctNKPmMD/VibeBI+ZcZCJK+4DPuw=="],
|
||||||
|
|
||||||
"pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="],
|
"pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="],
|
||||||
|
|
||||||
@@ -3004,7 +3009,7 @@
|
|||||||
|
|
||||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
|
|
||||||
"qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="],
|
"qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
|
||||||
|
|
||||||
"query-selector-shadow-dom": ["query-selector-shadow-dom@1.0.1", "", {}, "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw=="],
|
"query-selector-shadow-dom": ["query-selector-shadow-dom@1.0.1", "", {}, "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw=="],
|
||||||
|
|
||||||
@@ -3196,7 +3201,7 @@
|
|||||||
|
|
||||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|
||||||
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||||
|
|
||||||
"send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="],
|
"send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="],
|
||||||
|
|
||||||
@@ -3254,6 +3259,8 @@
|
|||||||
|
|
||||||
"slice-ansi": ["slice-ansi@2.1.0", "", { "dependencies": { "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" } }, "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ=="],
|
"slice-ansi": ["slice-ansi@2.1.0", "", { "dependencies": { "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" } }, "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ=="],
|
||||||
|
|
||||||
|
"slow-redact": ["slow-redact@0.3.2", "", {}, "sha512-MseHyi2+E/hBRqdOi5COy6wZ7j7DxXRz9NkseavNYSvvWC06D8a5cidVZX3tcG5eCW3NIyVU4zT63hw0Q486jw=="],
|
||||||
|
|
||||||
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
|
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
|
||||||
|
|
||||||
"smtp-address-parser": ["smtp-address-parser@1.0.10", "", { "dependencies": { "nearley": "^2.20.1" } }, "sha512-Osg9LmvGeAG/hyao4mldbflLOkkr3a+h4m1lwKCK5U8M6ZAr7tdXEz/+/vr752TSGE4MNUlUl9cIK2cB8cgzXg=="],
|
"smtp-address-parser": ["smtp-address-parser@1.0.10", "", { "dependencies": { "nearley": "^2.20.1" } }, "sha512-Osg9LmvGeAG/hyao4mldbflLOkkr3a+h4m1lwKCK5U8M6ZAr7tdXEz/+/vr752TSGE4MNUlUl9cIK2cB8cgzXg=="],
|
||||||
@@ -3434,7 +3441,7 @@
|
|||||||
|
|
||||||
"tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
|
"tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
|
||||||
|
|
||||||
"tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||||
|
|
||||||
"tinypool": ["tinypool@1.0.2", "", {}, "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA=="],
|
"tinypool": ["tinypool@1.0.2", "", {}, "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA=="],
|
||||||
|
|
||||||
@@ -3512,7 +3519,7 @@
|
|||||||
|
|
||||||
"typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="],
|
"typedarray-to-buffer": ["typedarray-to-buffer@3.1.5", "", { "dependencies": { "is-typedarray": "^1.0.0" } }, "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q=="],
|
||||||
|
|
||||||
"typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="],
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
"ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="],
|
"ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="],
|
||||||
|
|
||||||
@@ -3596,15 +3603,15 @@
|
|||||||
|
|
||||||
"validate.io-number": ["validate.io-number@1.0.3", "", {}, "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg=="],
|
"validate.io-number": ["validate.io-number@1.0.3", "", {}, "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg=="],
|
||||||
|
|
||||||
"validator": ["validator@13.15.26", "", {}, "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA=="],
|
"validator": ["validator@13.15.15", "", {}, "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A=="],
|
||||||
|
|
||||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||||
|
|
||||||
"verdaccio": ["verdaccio@6.3.2", "", { "dependencies": { "@cypress/request": "3.0.10", "@verdaccio/auth": "8.0.0-next-8.33", "@verdaccio/config": "8.0.0-next-8.33", "@verdaccio/core": "8.0.0-next-8.33", "@verdaccio/hooks": "8.0.0-next-8.33", "@verdaccio/loaders": "8.0.0-next-8.23", "@verdaccio/local-storage-legacy": "11.1.1", "@verdaccio/logger": "8.0.0-next-8.33", "@verdaccio/middleware": "8.0.0-next-8.33", "@verdaccio/search-indexer": "8.0.0-next-8.5", "@verdaccio/signature": "8.0.0-next-8.25", "@verdaccio/streams": "10.2.1", "@verdaccio/tarball": "13.0.0-next-8.33", "@verdaccio/ui-theme": "8.0.0-next-8.30", "@verdaccio/url": "13.0.0-next-8.33", "@verdaccio/utils": "8.1.0-next-8.33", "JSONStream": "1.3.5", "async": "3.2.6", "clipanion": "4.0.0-rc.4", "compression": "1.8.1", "cors": "2.8.6", "debug": "4.4.3", "envinfo": "7.21.0", "express": "4.22.1", "lodash": "4.17.23", "lru-cache": "7.18.3", "mime": "3.0.0", "semver": "7.7.4", "verdaccio-audit": "13.0.0-next-8.33", "verdaccio-htpasswd": "13.0.0-next-8.33" }, "bin": { "verdaccio": "bin/verdaccio" } }, "sha512-9BmfrGlakdAW1QNBrD2GgO8hOhwIp6ogbAhaaDgtDsK3/94qXwS6n2PM1/gG2V/zFC5JH1rWbLia390i0xbodA=="],
|
"verdaccio": ["verdaccio@6.2.1", "", { "dependencies": { "@cypress/request": "3.0.9", "@verdaccio/auth": "8.0.0-next-8.24", "@verdaccio/config": "8.0.0-next-8.24", "@verdaccio/core": "8.0.0-next-8.24", "@verdaccio/hooks": "8.0.0-next-8.24", "@verdaccio/loaders": "8.0.0-next-8.14", "@verdaccio/local-storage-legacy": "11.1.1", "@verdaccio/logger": "8.0.0-next-8.24", "@verdaccio/middleware": "8.0.0-next-8.24", "@verdaccio/search-indexer": "8.0.0-next-8.5", "@verdaccio/signature": "8.0.0-next-8.16", "@verdaccio/streams": "10.2.1", "@verdaccio/tarball": "13.0.0-next-8.24", "@verdaccio/ui-theme": "8.0.0-next-8.24", "@verdaccio/url": "13.0.0-next-8.24", "@verdaccio/utils": "8.1.0-next-8.24", "JSONStream": "1.3.5", "async": "3.2.6", "clipanion": "4.0.0-rc.4", "compression": "1.8.1", "cors": "2.8.5", "debug": "4.4.3", "envinfo": "7.15.0", "express": "4.21.2", "lodash": "4.17.21", "lru-cache": "7.18.3", "mime": "3.0.0", "semver": "7.7.2", "verdaccio-audit": "13.0.0-next-8.24", "verdaccio-htpasswd": "13.0.0-next-8.24" }, "bin": { "verdaccio": "bin/verdaccio" } }, "sha512-b7EjPyVKvO/7J2BtLaybQqDd8dh4uUsuQL1zQMVLsw3aYqBsHCAOa6T1zb6gpCg68cNUHluw7IjLs2hha72TZA=="],
|
||||||
|
|
||||||
"verdaccio-audit": ["verdaccio-audit@13.0.0-next-8.33", "", { "dependencies": { "@verdaccio/config": "8.0.0-next-8.33", "@verdaccio/core": "8.0.0-next-8.33", "express": "4.22.1", "https-proxy-agent": "5.0.1", "node-fetch": "cjs" } }, "sha512-qBMN0b4cbSbo6RbOke0QtVy/HuSzmxgRXIjnXM3C86ZhjN6DlhdeAoQYcZUfbXM8BklRXtObAnMoTlgeJ7BJLA=="],
|
"verdaccio-audit": ["verdaccio-audit@13.0.0-next-8.24", "", { "dependencies": { "@verdaccio/config": "8.0.0-next-8.24", "@verdaccio/core": "8.0.0-next-8.24", "express": "4.21.2", "https-proxy-agent": "5.0.1", "node-fetch": "cjs" } }, "sha512-dXqsnhTGqOuIsZq/MrW05YKwhuKg94VtL0tcYI4kcT+J+fN3gKiZ1Q3wDPaVzCMc081stBlKhi+SL66gIidHdA=="],
|
||||||
|
|
||||||
"verdaccio-htpasswd": ["verdaccio-htpasswd@13.0.0-next-8.33", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.33", "@verdaccio/file-locking": "13.0.0-next-8.6", "apache-md5": "1.1.8", "bcryptjs": "2.4.3", "debug": "4.4.3", "http-errors": "2.0.1", "unix-crypt-td-js": "1.1.4" } }, "sha512-ZYqvF/EuA4W4idfv2O1ZN8YhSI2xreFbGdrcWgOd+QBedDin7NzMhgypbafutm9mPhkI6Xv/+3syee1u1cEL8g=="],
|
"verdaccio-htpasswd": ["verdaccio-htpasswd@13.0.0-next-8.24", "", { "dependencies": { "@verdaccio/core": "8.0.0-next-8.24", "@verdaccio/file-locking": "13.0.0-next-8.6", "apache-md5": "1.1.8", "bcryptjs": "2.4.3", "debug": "4.4.3", "http-errors": "2.0.0", "unix-crypt-td-js": "1.1.4" } }, "sha512-nZ+V/szt3lXQRIyqvOpJlW0MceLM3tUyTGwqy4y0uwq7w9KGD/VqytnCpiQbkjVmmfScimXwRW7PHjHyRXLCVw=="],
|
||||||
|
|
||||||
"verror": ["verror@1.10.0", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw=="],
|
"verror": ["verror@1.10.0", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw=="],
|
||||||
|
|
||||||
@@ -3668,7 +3675,7 @@
|
|||||||
|
|
||||||
"wouter": ["wouter@3.7.1", "", { "dependencies": { "mitt": "^3.0.1", "regexparam": "^3.0.0", "use-sync-external-store": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-od5LGmndSUzntZkE2R5CHhoiJ7YMuTIbiXsa0Anytc2RATekgv4sfWRAxLEULBrp7ADzinWQw8g470lkT8+fOw=="],
|
"wouter": ["wouter@3.7.1", "", { "dependencies": { "mitt": "^3.0.1", "regexparam": "^3.0.0", "use-sync-external-store": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-od5LGmndSUzntZkE2R5CHhoiJ7YMuTIbiXsa0Anytc2RATekgv4sfWRAxLEULBrp7ADzinWQw8g470lkT8+fOw=="],
|
||||||
|
|
||||||
"wrangler": ["wrangler@4.45.4", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.9", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251011.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251011.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251011.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-niXT7B463wQi7WXIHjYK8txgWhuKQLrGmhjoR58SnPhlkq4wGjd3rFrkVyRc/O58clGTfs672BSGOph4XMoQKw=="],
|
"wrangler": ["wrangler@4.52.1", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.1", "@cloudflare/unenv-preset": "2.7.12", "blake3-wasm": "2.1.5", "esbuild": "0.27.0", "miniflare": "4.20251202.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251202.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251202.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-rIzDxzPnLAaqBF+SdHGd9Az0ELEWtIBwPp5diCR58p2F4C+KgNGGpPMFswMntuViQ2RKRgGbk4jIzStJoUUfjQ=="],
|
||||||
|
|
||||||
"wrap-ansi": ["wrap-ansi@3.0.1", "", { "dependencies": { "string-width": "^2.1.1", "strip-ansi": "^4.0.0" } }, "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ=="],
|
"wrap-ansi": ["wrap-ansi@3.0.1", "", { "dependencies": { "string-width": "^2.1.1", "strip-ansi": "^4.0.0" } }, "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ=="],
|
||||||
|
|
||||||
@@ -3846,14 +3853,20 @@
|
|||||||
|
|
||||||
"@babel/traverse/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
"@babel/traverse/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||||
|
|
||||||
"@bknd/plasmic/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
"@bknd/plasmic/@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="],
|
||||||
|
|
||||||
"@bundled-es-modules/tough-cookie/tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="],
|
"@bundled-es-modules/tough-cookie/tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/miniflare": ["miniflare@4.20251125.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251125.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-xY6deLx0Drt8GfGG2Fv0fHUocHAIG/Iv62Kl36TPfDzgq7/+DQ5gYNisxnmyISQdA/sm7kOvn2XRBncxjWYrLg=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/wrangler": ["wrangler@4.51.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.1", "@cloudflare/unenv-preset": "2.7.11", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251125.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251125.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251125.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-JHv+58UxM2//e4kf9ASDwg016xd/OdDNDUKW6zLQyE7Uc9ayYKX1QJ9NsYtpo4dC1dfg6rT67pf1aNK1cTzUDg=="],
|
||||||
|
|
||||||
"@cloudflare/vitest-pool-workers/miniflare": ["miniflare@4.20251011.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251011.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-5oAaz6lqZus4QFwzEJiNtgpjZR2TBVwBeIhOW33V4gu+l23EukpKja831tFIX2o6sOD/hqZmKZHplOrWl3YGtQ=="],
|
"@cloudflare/vitest-pool-workers/miniflare": ["miniflare@4.20251011.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251011.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-5oAaz6lqZus4QFwzEJiNtgpjZR2TBVwBeIhOW33V4gu+l23EukpKja831tFIX2o6sOD/hqZmKZHplOrWl3YGtQ=="],
|
||||||
|
|
||||||
"@cloudflare/vitest-pool-workers/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
"@cloudflare/vitest-pool-workers/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||||
|
|
||||||
|
"@cloudflare/vitest-pool-workers/wrangler": ["wrangler@4.45.4", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.9", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251011.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251011.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251011.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-niXT7B463wQi7WXIHjYK8txgWhuKQLrGmhjoR58SnPhlkq4wGjd3rFrkVyRc/O58clGTfs672BSGOph4XMoQKw=="],
|
||||||
|
|
||||||
"@cypress/request/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
"@cypress/request/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
||||||
|
|
||||||
"@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
@@ -3924,10 +3937,6 @@
|
|||||||
|
|
||||||
"@puppeteer/browsers/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
"@puppeteer/browsers/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||||
|
|
||||||
"@rjsf/core/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"@rjsf/utils/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"@rollup/plugin-babel/@rollup/pluginutils": ["@rollup/pluginutils@3.1.0", "", { "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", "picomatch": "^2.2.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0" } }, "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg=="],
|
"@rollup/plugin-babel/@rollup/pluginutils": ["@rollup/pluginutils@3.1.0", "", { "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", "picomatch": "^2.2.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0" } }, "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg=="],
|
||||||
|
|
||||||
"@rollup/plugin-commonjs/@rollup/pluginutils": ["@rollup/pluginutils@3.1.0", "", { "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", "picomatch": "^2.2.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0" } }, "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg=="],
|
"@rollup/plugin-commonjs/@rollup/pluginutils": ["@rollup/pluginutils@3.1.0", "", { "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", "picomatch": "^2.2.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0" } }, "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg=="],
|
||||||
@@ -4094,27 +4103,27 @@
|
|||||||
|
|
||||||
"@typescript-eslint/typescript-estree/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
"@typescript-eslint/typescript-estree/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||||
|
|
||||||
"@verdaccio/core/minimatch": ["minimatch@7.4.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-Brg/fp/iAVDOQoHxkuN5bEYhyQlZhxddI78yWsCbeEwTHXQjlNLtiJDUsp1GIptVqMI7/gkJMz4vVAc01mpoBw=="],
|
"@verdaccio/config/minimatch": ["minimatch@7.4.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw=="],
|
||||||
|
|
||||||
|
"@verdaccio/core/minimatch": ["minimatch@7.4.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw=="],
|
||||||
|
|
||||||
|
"@verdaccio/core/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||||
|
|
||||||
"@verdaccio/local-storage-legacy/@verdaccio/core": ["@verdaccio/core@8.0.0-next-8.21", "", { "dependencies": { "ajv": "8.17.1", "http-errors": "2.0.0", "http-status-codes": "2.3.0", "minimatch": "7.4.6", "process-warning": "1.0.0", "semver": "7.7.2" } }, "sha512-n3Y8cqf84cwXxUUdTTfEJc8fV55PONPKijCt2YaC0jilb5qp1ieB3d4brqTOdCdXuwkmnG2uLCiGpUd/RuSW0Q=="],
|
"@verdaccio/local-storage-legacy/@verdaccio/core": ["@verdaccio/core@8.0.0-next-8.21", "", { "dependencies": { "ajv": "8.17.1", "http-errors": "2.0.0", "http-status-codes": "2.3.0", "minimatch": "7.4.6", "process-warning": "1.0.0", "semver": "7.7.2" } }, "sha512-n3Y8cqf84cwXxUUdTTfEJc8fV55PONPKijCt2YaC0jilb5qp1ieB3d4brqTOdCdXuwkmnG2uLCiGpUd/RuSW0Q=="],
|
||||||
|
|
||||||
"@verdaccio/local-storage-legacy/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
"@verdaccio/local-storage-legacy/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||||
|
|
||||||
"@verdaccio/local-storage-legacy/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"@verdaccio/logger-prettify/dayjs": ["dayjs@1.11.13", "", {}, "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="],
|
"@verdaccio/logger-prettify/dayjs": ["dayjs@1.11.13", "", {}, "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="],
|
||||||
|
|
||||||
"@verdaccio/logger-prettify/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"@verdaccio/logger-prettify/pino-abstract-transport": ["pino-abstract-transport@1.2.0", "", { "dependencies": { "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q=="],
|
"@verdaccio/logger-prettify/pino-abstract-transport": ["pino-abstract-transport@1.2.0", "", { "dependencies": { "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q=="],
|
||||||
|
|
||||||
"@verdaccio/logger-prettify/sonic-boom": ["sonic-boom@3.8.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg=="],
|
"@verdaccio/logger-prettify/sonic-boom": ["sonic-boom@3.8.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg=="],
|
||||||
|
|
||||||
"@verdaccio/utils/minimatch": ["minimatch@7.4.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-Brg/fp/iAVDOQoHxkuN5bEYhyQlZhxddI78yWsCbeEwTHXQjlNLtiJDUsp1GIptVqMI7/gkJMz4vVAc01mpoBw=="],
|
"@verdaccio/middleware/mime": ["mime@2.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="],
|
||||||
|
|
||||||
|
"@verdaccio/utils/minimatch": ["minimatch@7.4.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw=="],
|
||||||
|
|
||||||
"@vitejs/plugin-react/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@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" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
|
"@vitejs/plugin-react/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@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" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
|
||||||
|
|
||||||
@@ -4162,8 +4171,6 @@
|
|||||||
|
|
||||||
"archiver-utils/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
"archiver-utils/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
||||||
|
|
||||||
"archiver-utils/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"archiver-utils/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
|
"archiver-utils/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
|
||||||
|
|
||||||
"aria-hidden/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"aria-hidden/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
@@ -4186,20 +4193,20 @@
|
|||||||
|
|
||||||
"base/pascalcase": ["pascalcase@0.1.1", "", {}, "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw=="],
|
"base/pascalcase": ["pascalcase@0.1.1", "", {}, "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw=="],
|
||||||
|
|
||||||
"bknd/miniflare": ["miniflare@4.20251011.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251011.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-5oAaz6lqZus4QFwzEJiNtgpjZR2TBVwBeIhOW33V4gu+l23EukpKja831tFIX2o6sOD/hqZmKZHplOrWl3YGtQ=="],
|
"bknd/miniflare": ["miniflare@4.20251125.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251125.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-xY6deLx0Drt8GfGG2Fv0fHUocHAIG/Iv62Kl36TPfDzgq7/+DQ5gYNisxnmyISQdA/sm7kOvn2XRBncxjWYrLg=="],
|
||||||
|
|
||||||
"bknd-cli/@libsql/client": ["@libsql/client@0.14.0", "", { "dependencies": { "@libsql/core": "^0.14.0", "@libsql/hrana-client": "^0.7.0", "js-base64": "^3.7.5", "libsql": "^0.4.4", "promise-limit": "^2.7.0" } }, "sha512-/9HEKfn6fwXB5aTEEoMeFh4CtG0ZzbncBb1e++OCdVpgKZ/xyMsIVYXm0w7Pv4RUel803vE6LwniB3PqD72R0Q=="],
|
"bknd-cli/@libsql/client": ["@libsql/client@0.14.0", "", { "dependencies": { "@libsql/core": "^0.14.0", "@libsql/hrana-client": "^0.7.0", "js-base64": "^3.7.5", "libsql": "^0.4.4", "promise-limit": "^2.7.0" } }, "sha512-/9HEKfn6fwXB5aTEEoMeFh4CtG0ZzbncBb1e++OCdVpgKZ/xyMsIVYXm0w7Pv4RUel803vE6LwniB3PqD72R0Q=="],
|
||||||
|
|
||||||
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"body-parser/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
|
||||||
|
|
||||||
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||||
|
|
||||||
"body-parser/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
"body-parser/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
||||||
|
|
||||||
"browser-resolve/resolve": ["resolve@1.1.7", "", {}, "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg=="],
|
"browser-resolve/resolve": ["resolve@1.1.7", "", {}, "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg=="],
|
||||||
|
|
||||||
|
"bun-types/@types/node": ["@types/node@22.13.10", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw=="],
|
||||||
|
|
||||||
"cheerio/undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="],
|
"cheerio/undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="],
|
||||||
|
|
||||||
"class-utils/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "^0.1.0" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="],
|
"class-utils/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "^0.1.0" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="],
|
||||||
@@ -4250,8 +4257,6 @@
|
|||||||
|
|
||||||
"eslint/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
|
"eslint/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
|
||||||
|
|
||||||
"eslint/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"eslint/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
"eslint/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||||
|
|
||||||
"eslint/mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
"eslint/mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
||||||
@@ -4266,8 +4271,6 @@
|
|||||||
|
|
||||||
"eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
"eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||||
|
|
||||||
"eslint-plugin-flowtype/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
"eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
||||||
|
|
||||||
"eslint-plugin-import/doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
"eslint-plugin-import/doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
||||||
@@ -4298,13 +4301,13 @@
|
|||||||
|
|
||||||
"expand-brackets/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
|
"expand-brackets/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="],
|
||||||
|
|
||||||
"express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"express/cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="],
|
||||||
|
|
||||||
"express/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
"express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"express/path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="],
|
"express/path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="],
|
||||||
|
|
||||||
"express/qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
|
"express/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
||||||
|
|
||||||
"external-editor/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
"external-editor/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||||
|
|
||||||
@@ -4356,16 +4359,12 @@
|
|||||||
|
|
||||||
"has-values/kind-of": ["kind-of@4.0.0", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw=="],
|
"has-values/kind-of": ["kind-of@4.0.0", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw=="],
|
||||||
|
|
||||||
"http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
|
||||||
|
|
||||||
"http-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
"http-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||||
|
|
||||||
"https-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
"https-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||||
|
|
||||||
"import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
"import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||||
|
|
||||||
"inquirer/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"ip-address/jsbn": ["jsbn@1.1.0", "", {}, "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="],
|
"ip-address/jsbn": ["jsbn@1.1.0", "", {}, "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="],
|
||||||
|
|
||||||
"ip-address/sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
|
"ip-address/sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="],
|
||||||
@@ -4438,9 +4437,7 @@
|
|||||||
|
|
||||||
"jsdom/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
|
"jsdom/ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
|
||||||
|
|
||||||
"json-schema-compare/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
"jsonwebtoken/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||||
|
|
||||||
"json-schema-merge-allof/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"jszip/pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
"jszip/pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
||||||
|
|
||||||
@@ -4454,8 +4451,6 @@
|
|||||||
|
|
||||||
"log-update/cli-cursor": ["cli-cursor@2.1.0", "", { "dependencies": { "restore-cursor": "^2.0.0" } }, "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw=="],
|
"log-update/cli-cursor": ["cli-cursor@2.1.0", "", { "dependencies": { "restore-cursor": "^2.0.0" } }, "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw=="],
|
||||||
|
|
||||||
"lowdb/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"lower-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"lower-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
"make-dir/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
"make-dir/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||||
@@ -4502,6 +4497,8 @@
|
|||||||
|
|
||||||
"postcss-mixins/sugarss": ["sugarss@5.0.1", "", { "peerDependencies": { "postcss": "^8.3.3" } }, "sha512-ctS5RYCBVvPoZAnzIaX5QSShK8ZiZxD5HUqSxlusvEMC+QZQIPCPOIJg6aceFX+K2rf4+SH89eu++h1Zmsr2nw=="],
|
"postcss-mixins/sugarss": ["sugarss@5.0.1", "", { "peerDependencies": { "postcss": "^8.3.3" } }, "sha512-ctS5RYCBVvPoZAnzIaX5QSShK8ZiZxD5HUqSxlusvEMC+QZQIPCPOIJg6aceFX+K2rf4+SH89eu++h1Zmsr2nw=="],
|
||||||
|
|
||||||
|
"postcss-mixins/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
"pretty-format/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
"progress-estimator/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
|
"progress-estimator/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
|
||||||
@@ -4516,8 +4513,6 @@
|
|||||||
|
|
||||||
"pumpify/pump": ["pump@2.0.1", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA=="],
|
"pumpify/pump": ["pump@2.0.1", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA=="],
|
||||||
|
|
||||||
"raw-body/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
|
||||||
|
|
||||||
"raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
"raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||||
|
|
||||||
"react-redux/use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="],
|
"react-redux/use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="],
|
||||||
@@ -4552,8 +4547,6 @@
|
|||||||
|
|
||||||
"request/uuid": ["uuid@3.4.0", "", { "bin": { "uuid": "./bin/uuid" } }, "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="],
|
"request/uuid": ["uuid@3.4.0", "", { "bin": { "uuid": "./bin/uuid" } }, "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="],
|
||||||
|
|
||||||
"request-promise-core/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"request-promise-native/tough-cookie": ["tough-cookie@2.5.0", "", { "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" } }, "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g=="],
|
"request-promise-native/tough-cookie": ["tough-cookie@2.5.0", "", { "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" } }, "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g=="],
|
||||||
|
|
||||||
"resq/fast-deep-equal": ["fast-deep-equal@2.0.1", "", {}, "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w=="],
|
"resq/fast-deep-equal": ["fast-deep-equal@2.0.1", "", {}, "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w=="],
|
||||||
@@ -4584,8 +4577,6 @@
|
|||||||
|
|
||||||
"send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
|
"send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
|
||||||
|
|
||||||
"send/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
|
||||||
|
|
||||||
"send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
"send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
||||||
|
|
||||||
"serialize-error/type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="],
|
"serialize-error/type-fest": ["type-fest@2.19.0", "", {}, "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="],
|
||||||
@@ -4650,8 +4641,6 @@
|
|||||||
|
|
||||||
"table/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
"table/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||||
|
|
||||||
"table/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
|
||||||
|
|
||||||
"table/string-width": ["string-width@3.1.0", "", { "dependencies": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^5.1.0" } }, "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w=="],
|
"table/string-width": ["string-width@3.1.0", "", { "dependencies": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^5.1.0" } }, "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w=="],
|
||||||
|
|
||||||
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||||
@@ -4660,10 +4649,6 @@
|
|||||||
|
|
||||||
"test-exclude/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
"test-exclude/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="],
|
||||||
|
|
||||||
"tinyglobby/fdir": ["fdir@6.4.5", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw=="],
|
|
||||||
|
|
||||||
"tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
|
|
||||||
|
|
||||||
"to-object-path/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="],
|
"to-object-path/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="],
|
||||||
|
|
||||||
"ts-jest/mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
"ts-jest/mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
||||||
@@ -4682,6 +4667,8 @@
|
|||||||
|
|
||||||
"tsup/rollup": ["rollup@4.35.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.35.0", "@rollup/rollup-android-arm64": "4.35.0", "@rollup/rollup-darwin-arm64": "4.35.0", "@rollup/rollup-darwin-x64": "4.35.0", "@rollup/rollup-freebsd-arm64": "4.35.0", "@rollup/rollup-freebsd-x64": "4.35.0", "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", "@rollup/rollup-linux-arm-musleabihf": "4.35.0", "@rollup/rollup-linux-arm64-gnu": "4.35.0", "@rollup/rollup-linux-arm64-musl": "4.35.0", "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", "@rollup/rollup-linux-riscv64-gnu": "4.35.0", "@rollup/rollup-linux-s390x-gnu": "4.35.0", "@rollup/rollup-linux-x64-gnu": "4.35.0", "@rollup/rollup-linux-x64-musl": "4.35.0", "@rollup/rollup-win32-arm64-msvc": "4.35.0", "@rollup/rollup-win32-ia32-msvc": "4.35.0", "@rollup/rollup-win32-x64-msvc": "4.35.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg=="],
|
"tsup/rollup": ["rollup@4.35.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.35.0", "@rollup/rollup-android-arm64": "4.35.0", "@rollup/rollup-darwin-arm64": "4.35.0", "@rollup/rollup-darwin-x64": "4.35.0", "@rollup/rollup-freebsd-arm64": "4.35.0", "@rollup/rollup-freebsd-x64": "4.35.0", "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", "@rollup/rollup-linux-arm-musleabihf": "4.35.0", "@rollup/rollup-linux-arm64-gnu": "4.35.0", "@rollup/rollup-linux-arm64-musl": "4.35.0", "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", "@rollup/rollup-linux-riscv64-gnu": "4.35.0", "@rollup/rollup-linux-s390x-gnu": "4.35.0", "@rollup/rollup-linux-x64-gnu": "4.35.0", "@rollup/rollup-linux-x64-musl": "4.35.0", "@rollup/rollup-win32-arm64-msvc": "4.35.0", "@rollup/rollup-win32-ia32-msvc": "4.35.0", "@rollup/rollup-win32-x64-msvc": "4.35.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg=="],
|
||||||
|
|
||||||
|
"tsup/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"union-value/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="],
|
"union-value/is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="],
|
||||||
|
|
||||||
"unset-value/has-value": ["has-value@0.3.1", "", { "dependencies": { "get-value": "^2.0.3", "has-values": "^0.1.4", "isobject": "^2.0.0" } }, "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q=="],
|
"unset-value/has-value": ["has-value@0.3.1", "", { "dependencies": { "get-value": "^2.0.3", "has-values": "^0.1.4", "isobject": "^2.0.0" } }, "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q=="],
|
||||||
@@ -4702,8 +4689,6 @@
|
|||||||
|
|
||||||
"vite/rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
|
"vite/rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
|
||||||
|
|
||||||
"vite/tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
|
||||||
|
|
||||||
"vite-node/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
"vite-node/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||||
|
|
||||||
"vite-node/vite": ["vite@6.2.1", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q=="],
|
"vite-node/vite": ["vite@6.2.1", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q=="],
|
||||||
@@ -4732,9 +4717,13 @@
|
|||||||
|
|
||||||
"wouter/use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="],
|
"wouter/use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="],
|
||||||
|
|
||||||
"wrangler/miniflare": ["miniflare@4.20251011.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251011.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-5oAaz6lqZus4QFwzEJiNtgpjZR2TBVwBeIhOW33V4gu+l23EukpKja831tFIX2o6sOD/hqZmKZHplOrWl3YGtQ=="],
|
"wrangler/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.12", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20251125.0" }, "optionalPeers": ["workerd"] }, "sha512-SIBo+k58R9OyBsxF1jL6GdL7XHbzatT86c0be+UY5v5tg6TAuJ1/2QsRuC3pHgKVHile1HcJqEEORoS9hv8hNw=="],
|
||||||
|
|
||||||
"wrangler/workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="],
|
"wrangler/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="],
|
||||||
|
|
||||||
|
"wrangler/miniflare": ["miniflare@4.20251202.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251202.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-Pa5iBAVzzVT/yr7rcyr75ETm5IGCpdT61foGx+6jDj+vzISNfWZgEcSxWk1nlJboJumUJ10kC498hQudpdbDWg=="],
|
||||||
|
|
||||||
|
"wrangler/workerd": ["workerd@1.20251202.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251202.0", "@cloudflare/workerd-darwin-arm64": "1.20251202.0", "@cloudflare/workerd-linux-64": "1.20251202.0", "@cloudflare/workerd-linux-arm64": "1.20251202.0", "@cloudflare/workerd-windows-64": "1.20251202.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-p08YfrUMHkjCECNdT36r+6DpJIZX4kixbZ4n6GMUcLR5Gh18fakSCsiQrh72iOm4M9QHv/rM7P8YvCrUPWT5sg=="],
|
||||||
|
|
||||||
"wrap-ansi/string-width": ["string-width@2.1.1", "", { "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } }, "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw=="],
|
"wrap-ansi/string-width": ["string-width@2.1.1", "", { "dependencies": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" } }, "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw=="],
|
||||||
|
|
||||||
@@ -4776,14 +4765,30 @@
|
|||||||
|
|
||||||
"@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.3", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg=="],
|
"@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.3", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg=="],
|
||||||
|
|
||||||
|
"@bknd/plasmic/@types/bun/bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="],
|
||||||
|
|
||||||
"@bundled-es-modules/tough-cookie/tough-cookie/universalify": ["universalify@0.2.0", "", {}, "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="],
|
"@bundled-es-modules/tough-cookie/tough-cookie/universalify": ["universalify@0.2.0", "", {}, "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/miniflare/workerd": ["workerd@1.20251125.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251125.0", "@cloudflare/workerd-darwin-arm64": "1.20251125.0", "@cloudflare/workerd-linux-64": "1.20251125.0", "@cloudflare/workerd-linux-arm64": "1.20251125.0", "@cloudflare/workerd-windows-64": "1.20251125.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-oQYfgu3UZ15HlMcEyilKD1RdielRnKSG5MA0xoi1theVs99Rop9AEFYicYCyK1R4YjYblLRYEiL1tMgEFqpReA=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/miniflare/youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/wrangler/workerd": ["workerd@1.20251125.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251125.0", "@cloudflare/workerd-darwin-arm64": "1.20251125.0", "@cloudflare/workerd-linux-64": "1.20251125.0", "@cloudflare/workerd-linux-arm64": "1.20251125.0", "@cloudflare/workerd-windows-64": "1.20251125.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-oQYfgu3UZ15HlMcEyilKD1RdielRnKSG5MA0xoi1theVs99Rop9AEFYicYCyK1R4YjYblLRYEiL1tMgEFqpReA=="],
|
||||||
|
|
||||||
"@cloudflare/vitest-pool-workers/miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="],
|
"@cloudflare/vitest-pool-workers/miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="],
|
||||||
|
|
||||||
"@cloudflare/vitest-pool-workers/miniflare/workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="],
|
"@cloudflare/vitest-pool-workers/miniflare/workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="],
|
||||||
|
|
||||||
"@cloudflare/vitest-pool-workers/miniflare/youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
|
"@cloudflare/vitest-pool-workers/miniflare/youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
|
||||||
|
|
||||||
|
"@cloudflare/vitest-pool-workers/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],
|
||||||
|
|
||||||
|
"@cloudflare/vitest-pool-workers/wrangler/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.9", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20250927.0" }, "optionalPeers": ["workerd"] }, "sha512-Drm7qlTKnvncEv+DANiQNEonq0H0LyIsoFZYJ6tJ8OhAoy5udIE8yp6BsVDYcIjcYLIybp4M7c/P7ly/56SoHg=="],
|
||||||
|
|
||||||
|
"@cloudflare/vitest-pool-workers/wrangler/workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="],
|
||||||
|
|
||||||
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||||
|
|
||||||
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="],
|
||||||
@@ -4858,20 +4863,10 @@
|
|||||||
|
|
||||||
"@types/yauzl/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
"@types/yauzl/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||||
|
|
||||||
"@verdaccio/core/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
|
||||||
|
|
||||||
"@verdaccio/local-storage-legacy/@verdaccio/core/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
|
||||||
|
|
||||||
"@verdaccio/local-storage-legacy/@verdaccio/core/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
|
||||||
|
|
||||||
"@verdaccio/local-storage-legacy/@verdaccio/core/minimatch": ["minimatch@7.4.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw=="],
|
"@verdaccio/local-storage-legacy/@verdaccio/core/minimatch": ["minimatch@7.4.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw=="],
|
||||||
|
|
||||||
"@verdaccio/local-storage-legacy/@verdaccio/core/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
|
||||||
|
|
||||||
"@verdaccio/logger-prettify/pino-abstract-transport/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
|
"@verdaccio/logger-prettify/pino-abstract-transport/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
|
||||||
|
|
||||||
"@verdaccio/utils/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
|
||||||
|
|
||||||
"@vitejs/plugin-react/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
"@vitejs/plugin-react/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
||||||
|
|
||||||
"@vitejs/plugin-react/@babel/core/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
|
"@vitejs/plugin-react/@babel/core/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
|
||||||
@@ -4908,6 +4903,8 @@
|
|||||||
|
|
||||||
"@vitest/mocker/vite/rollup": ["rollup@4.35.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.35.0", "@rollup/rollup-android-arm64": "4.35.0", "@rollup/rollup-darwin-arm64": "4.35.0", "@rollup/rollup-darwin-x64": "4.35.0", "@rollup/rollup-freebsd-arm64": "4.35.0", "@rollup/rollup-freebsd-x64": "4.35.0", "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", "@rollup/rollup-linux-arm-musleabihf": "4.35.0", "@rollup/rollup-linux-arm64-gnu": "4.35.0", "@rollup/rollup-linux-arm64-musl": "4.35.0", "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", "@rollup/rollup-linux-riscv64-gnu": "4.35.0", "@rollup/rollup-linux-s390x-gnu": "4.35.0", "@rollup/rollup-linux-x64-gnu": "4.35.0", "@rollup/rollup-linux-x64-musl": "4.35.0", "@rollup/rollup-win32-arm64-msvc": "4.35.0", "@rollup/rollup-win32-ia32-msvc": "4.35.0", "@rollup/rollup-win32-x64-msvc": "4.35.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg=="],
|
"@vitest/mocker/vite/rollup": ["rollup@4.35.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.35.0", "@rollup/rollup-android-arm64": "4.35.0", "@rollup/rollup-darwin-arm64": "4.35.0", "@rollup/rollup-darwin-x64": "4.35.0", "@rollup/rollup-freebsd-arm64": "4.35.0", "@rollup/rollup-freebsd-x64": "4.35.0", "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", "@rollup/rollup-linux-arm-musleabihf": "4.35.0", "@rollup/rollup-linux-arm64-gnu": "4.35.0", "@rollup/rollup-linux-arm64-musl": "4.35.0", "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", "@rollup/rollup-linux-riscv64-gnu": "4.35.0", "@rollup/rollup-linux-s390x-gnu": "4.35.0", "@rollup/rollup-linux-x64-gnu": "4.35.0", "@rollup/rollup-linux-x64-musl": "4.35.0", "@rollup/rollup-win32-arm64-msvc": "4.35.0", "@rollup/rollup-win32-ia32-msvc": "4.35.0", "@rollup/rollup-win32-x64-msvc": "4.35.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg=="],
|
||||||
|
|
||||||
|
"@vitest/mocker/vite/tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||||
|
|
||||||
"@vitest/snapshot/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
"@vitest/snapshot/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
||||||
|
|
||||||
"@vitest/ui/tinyglobby/fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="],
|
"@vitest/ui/tinyglobby/fdir": ["fdir@6.4.3", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw=="],
|
||||||
@@ -4946,12 +4943,14 @@
|
|||||||
|
|
||||||
"bknd/miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="],
|
"bknd/miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="],
|
||||||
|
|
||||||
"bknd/miniflare/workerd": ["workerd@1.20251011.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251011.0", "@cloudflare/workerd-darwin-arm64": "1.20251011.0", "@cloudflare/workerd-linux-64": "1.20251011.0", "@cloudflare/workerd-linux-arm64": "1.20251011.0", "@cloudflare/workerd-windows-64": "1.20251011.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Dq35TLPEJAw7BuYQMkN3p9rge34zWMU2Gnd4DSJFeVqld4+DAO2aPG7+We2dNIAyM97S8Y9BmHulbQ00E0HC7Q=="],
|
"bknd/miniflare/workerd": ["workerd@1.20251125.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251125.0", "@cloudflare/workerd-darwin-arm64": "1.20251125.0", "@cloudflare/workerd-linux-64": "1.20251125.0", "@cloudflare/workerd-linux-arm64": "1.20251125.0", "@cloudflare/workerd-windows-64": "1.20251125.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-oQYfgu3UZ15HlMcEyilKD1RdielRnKSG5MA0xoi1theVs99Rop9AEFYicYCyK1R4YjYblLRYEiL1tMgEFqpReA=="],
|
||||||
|
|
||||||
"bknd/miniflare/youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
|
"bknd/miniflare/youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
|
||||||
|
|
||||||
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
|
"bun-types/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||||
|
|
||||||
"class-utils/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="],
|
"class-utils/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="],
|
||||||
|
|
||||||
"compress-commons/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
"compress-commons/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||||
@@ -5110,8 +5109,6 @@
|
|||||||
|
|
||||||
"next/sharp/detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
|
"next/sharp/detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
|
||||||
|
|
||||||
"next/sharp/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
|
||||||
|
|
||||||
"node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
"node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||||
|
|
||||||
"node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
"node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||||
@@ -5120,6 +5117,10 @@
|
|||||||
|
|
||||||
"object-copy/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="],
|
"object-copy/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="],
|
||||||
|
|
||||||
|
"postcss-mixins/tinyglobby/fdir": ["fdir@6.4.5", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw=="],
|
||||||
|
|
||||||
|
"postcss-mixins/tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
|
||||||
|
|
||||||
"progress-estimator/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
|
"progress-estimator/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
|
||||||
|
|
||||||
"progress-estimator/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
|
"progress-estimator/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
|
||||||
@@ -5180,6 +5181,10 @@
|
|||||||
|
|
||||||
"tsup/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
"tsup/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||||
|
|
||||||
|
"tsup/tinyglobby/fdir": ["fdir@6.4.5", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw=="],
|
||||||
|
|
||||||
|
"tsup/tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
|
||||||
|
|
||||||
"unset-value/has-value/has-values": ["has-values@0.1.4", "", {}, "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="],
|
"unset-value/has-value/has-values": ["has-values@0.1.4", "", {}, "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="],
|
||||||
|
|
||||||
"unset-value/has-value/isobject": ["isobject@2.1.0", "", { "dependencies": { "isarray": "1.0.0" } }, "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA=="],
|
"unset-value/has-value/isobject": ["isobject@2.1.0", "", { "dependencies": { "isarray": "1.0.0" } }, "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA=="],
|
||||||
@@ -5242,19 +5247,69 @@
|
|||||||
|
|
||||||
"webdriverio/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
"webdriverio/@types/node/undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.0", "", { "os": "android", "cpu": "arm" }, "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.0", "", { "os": "android", "cpu": "arm64" }, "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.0", "", { "os": "android", "cpu": "x64" }, "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.0", "", { "os": "none", "cpu": "x64" }, "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ=="],
|
||||||
|
|
||||||
|
"wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="],
|
||||||
|
|
||||||
"wrangler/miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="],
|
"wrangler/miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="],
|
||||||
|
|
||||||
"wrangler/miniflare/youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
|
"wrangler/miniflare/youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="],
|
||||||
|
|
||||||
"wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251011.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg=="],
|
"wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251202.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-/uvEAWEukTWb1geHhbjGUeZqcSSSyYzp0mvoPUBl+l0ont4NVGao3fgwM0q8wtKvgoKCHSG6zcG23wj9Opj3Nw=="],
|
||||||
|
|
||||||
"wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251011.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1WuFBGwZd15p4xssGN/48OE2oqokIuc51YvHvyNivyV8IYnAs3G9bJNGWth1X7iMDPe4g44pZrKhRnISS2+5dA=="],
|
"wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251202.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-f52xRvcI9cWRd6400EZStRtXiRC5XKEud7K5aFIbbUv0VeINltujFQQ9nHWtsF6g1quIXWkjhh5u01gPAYNNXA=="],
|
||||||
|
|
||||||
"wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251011.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BccMiBzFlWZyFghIw2szanmYJrJGBGHomw2y/GV6pYXChFzMGZkeCEMfmCyJj29xczZXxcZmUVJxNy4eJxO8QA=="],
|
"wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251202.0", "", { "os": "linux", "cpu": "x64" }, "sha512-HYXinF5RBH7oXbsFUMmwKCj+WltpYbf5mRKUBG5v3EuPhUjSIFB84U+58pDyfBJjcynHdy3EtvTWcvh/+lcgow=="],
|
||||||
|
|
||||||
"wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251011.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-79o/216lsbAbKEVDZYXR24ivEIE2ysDL9jvo0rDTkViLWju9dAp3CpyetglpJatbSi3uWBPKZBEOqN68zIjVsQ=="],
|
"wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251202.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-++L02Jdoxz7hEA9qDaQjbVU1RzQS+S+eqIi22DkPe2Tgiq2M3UfNpeu+75k5L9DGRIkZPYvwMBMbcmKvQqdIIg=="],
|
||||||
|
|
||||||
"wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251011.0", "", { "os": "win32", "cpu": "x64" }, "sha512-RIXUQRchFdqEvaUqn1cXZXSKjpqMaSaVAkI5jNZ8XzAw/bw2bcdOVUtakrflgxDprltjFb0PTNtuss1FKtH9Jg=="],
|
"wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251202.0", "", { "os": "win32", "cpu": "x64" }, "sha512-gzeU6eDydTi7ib+Q9DD/c0hpXtqPucnHk2tfGU03mljPObYxzMkkPGgB5qxpksFvub3y4K0ChjqYxGJB4F+j3g=="],
|
||||||
|
|
||||||
"wrap-ansi/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="],
|
"wrap-ansi/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@2.0.0", "", {}, "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="],
|
||||||
|
|
||||||
@@ -5270,6 +5325,30 @@
|
|||||||
|
|
||||||
"@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
"@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="],
|
||||||
|
|
||||||
|
"@bknd/plasmic/@types/bun/bun-types/@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251125.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-xDIVJi8fPxBseRoEIzLiUJb0N+DXnah/ynS+Unzn58HEoKLetUWiV/T1Fhned//lo5krnToG9KRgVRs0SOOTpw=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251125.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-k5FQET5PXnWjeDqZUpl4Ah/Rn0bH6mjfUtTyeAy6ky7QB3AZpwIhgWQD0vOFB3OvJaK4J/K4cUtNChYXB9mY/A=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251125.0", "", { "os": "linux", "cpu": "x64" }, "sha512-at6n/FomkftykWx0EqVLUZ0juUFz3ORtEPeBbW9ZZ3BQEyfVUtYfdcz/f1cN8Yyb7TE9ovF071P0mBRkx83ODw=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251125.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-EiRn+jrNaIs1QveabXGHFoyn3s/l02ui6Yp3nssyNhtmtgviddtt8KObBfM1jQKjXTpZlunhwdN4Bxf4jhlOMw=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251125.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6fdIsSeu65g++k8Y2DKzNKs0BkoU+KKI6GAAVBOLh2vvVWWnCP1OgMdVb5JAdjDrjDT5i0GSQu0bgQ8fPsW6zw=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/miniflare/youch/cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251125.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-xDIVJi8fPxBseRoEIzLiUJb0N+DXnah/ynS+Unzn58HEoKLetUWiV/T1Fhned//lo5krnToG9KRgVRs0SOOTpw=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251125.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-k5FQET5PXnWjeDqZUpl4Ah/Rn0bH6mjfUtTyeAy6ky7QB3AZpwIhgWQD0vOFB3OvJaK4J/K4cUtNChYXB9mY/A=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251125.0", "", { "os": "linux", "cpu": "x64" }, "sha512-at6n/FomkftykWx0EqVLUZ0juUFz3ORtEPeBbW9ZZ3BQEyfVUtYfdcz/f1cN8Yyb7TE9ovF071P0mBRkx83ODw=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251125.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-EiRn+jrNaIs1QveabXGHFoyn3s/l02ui6Yp3nssyNhtmtgviddtt8KObBfM1jQKjXTpZlunhwdN4Bxf4jhlOMw=="],
|
||||||
|
|
||||||
|
"@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251125.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6fdIsSeu65g++k8Y2DKzNKs0BkoU+KKI6GAAVBOLh2vvVWWnCP1OgMdVb5JAdjDrjDT5i0GSQu0bgQ8fPsW6zw=="],
|
||||||
|
|
||||||
"@cloudflare/vitest-pool-workers/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251011.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg=="],
|
"@cloudflare/vitest-pool-workers/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251011.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg=="],
|
||||||
|
|
||||||
"@cloudflare/vitest-pool-workers/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251011.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1WuFBGwZd15p4xssGN/48OE2oqokIuc51YvHvyNivyV8IYnAs3G9bJNGWth1X7iMDPe4g44pZrKhRnISS2+5dA=="],
|
"@cloudflare/vitest-pool-workers/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251011.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1WuFBGwZd15p4xssGN/48OE2oqokIuc51YvHvyNivyV8IYnAs3G9bJNGWth1X7iMDPe4g44pZrKhRnISS2+5dA=="],
|
||||||
@@ -5282,6 +5361,16 @@
|
|||||||
|
|
||||||
"@cloudflare/vitest-pool-workers/miniflare/youch/cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
|
"@cloudflare/vitest-pool-workers/miniflare/youch/cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
|
||||||
|
|
||||||
|
"@cloudflare/vitest-pool-workers/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251011.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg=="],
|
||||||
|
|
||||||
|
"@cloudflare/vitest-pool-workers/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251011.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1WuFBGwZd15p4xssGN/48OE2oqokIuc51YvHvyNivyV8IYnAs3G9bJNGWth1X7iMDPe4g44pZrKhRnISS2+5dA=="],
|
||||||
|
|
||||||
|
"@cloudflare/vitest-pool-workers/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251011.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BccMiBzFlWZyFghIw2szanmYJrJGBGHomw2y/GV6pYXChFzMGZkeCEMfmCyJj29xczZXxcZmUVJxNy4eJxO8QA=="],
|
||||||
|
|
||||||
|
"@cloudflare/vitest-pool-workers/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251011.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-79o/216lsbAbKEVDZYXR24ivEIE2ysDL9jvo0rDTkViLWju9dAp3CpyetglpJatbSi3uWBPKZBEOqN68zIjVsQ=="],
|
||||||
|
|
||||||
|
"@cloudflare/vitest-pool-workers/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251011.0", "", { "os": "win32", "cpu": "x64" }, "sha512-RIXUQRchFdqEvaUqn1cXZXSKjpqMaSaVAkI5jNZ8XzAw/bw2bcdOVUtakrflgxDprltjFb0PTNtuss1FKtH9Jg=="],
|
||||||
|
|
||||||
"@plasmicapp/nextjs-app-router/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
"@plasmicapp/nextjs-app-router/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||||
|
|
||||||
"@verdaccio/logger-prettify/pino-abstract-transport/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
"@verdaccio/logger-prettify/pino-abstract-transport/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||||
@@ -5334,15 +5423,15 @@
|
|||||||
|
|
||||||
"bknd-cli/@libsql/client/libsql/@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.4.7", "", { "os": "win32", "cpu": "x64" }, "sha512-7pJzOWzPm6oJUxml+PCDRzYQ4A1hTMHAciTAHfFK4fkbDZX33nWPVG7Y3vqdKtslcwAzwmrNDc6sXy2nwWnbiw=="],
|
"bknd-cli/@libsql/client/libsql/@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.4.7", "", { "os": "win32", "cpu": "x64" }, "sha512-7pJzOWzPm6oJUxml+PCDRzYQ4A1hTMHAciTAHfFK4fkbDZX33nWPVG7Y3vqdKtslcwAzwmrNDc6sXy2nwWnbiw=="],
|
||||||
|
|
||||||
"bknd/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251011.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-0DirVP+Z82RtZLlK2B+VhLOkk+ShBqDYO/jhcRw4oVlp0TOvk3cOVZChrt3+y3NV8Y/PYgTEywzLKFSziK4wCg=="],
|
"bknd/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20251125.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-xDIVJi8fPxBseRoEIzLiUJb0N+DXnah/ynS+Unzn58HEoKLetUWiV/T1Fhned//lo5krnToG9KRgVRs0SOOTpw=="],
|
||||||
|
|
||||||
"bknd/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251011.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1WuFBGwZd15p4xssGN/48OE2oqokIuc51YvHvyNivyV8IYnAs3G9bJNGWth1X7iMDPe4g44pZrKhRnISS2+5dA=="],
|
"bknd/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20251125.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-k5FQET5PXnWjeDqZUpl4Ah/Rn0bH6mjfUtTyeAy6ky7QB3AZpwIhgWQD0vOFB3OvJaK4J/K4cUtNChYXB9mY/A=="],
|
||||||
|
|
||||||
"bknd/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251011.0", "", { "os": "linux", "cpu": "x64" }, "sha512-BccMiBzFlWZyFghIw2szanmYJrJGBGHomw2y/GV6pYXChFzMGZkeCEMfmCyJj29xczZXxcZmUVJxNy4eJxO8QA=="],
|
"bknd/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20251125.0", "", { "os": "linux", "cpu": "x64" }, "sha512-at6n/FomkftykWx0EqVLUZ0juUFz3ORtEPeBbW9ZZ3BQEyfVUtYfdcz/f1cN8Yyb7TE9ovF071P0mBRkx83ODw=="],
|
||||||
|
|
||||||
"bknd/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251011.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-79o/216lsbAbKEVDZYXR24ivEIE2ysDL9jvo0rDTkViLWju9dAp3CpyetglpJatbSi3uWBPKZBEOqN68zIjVsQ=="],
|
"bknd/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20251125.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-EiRn+jrNaIs1QveabXGHFoyn3s/l02ui6Yp3nssyNhtmtgviddtt8KObBfM1jQKjXTpZlunhwdN4Bxf4jhlOMw=="],
|
||||||
|
|
||||||
"bknd/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251011.0", "", { "os": "win32", "cpu": "x64" }, "sha512-RIXUQRchFdqEvaUqn1cXZXSKjpqMaSaVAkI5jNZ8XzAw/bw2bcdOVUtakrflgxDprltjFb0PTNtuss1FKtH9Jg=="],
|
"bknd/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20251125.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6fdIsSeu65g++k8Y2DKzNKs0BkoU+KKI6GAAVBOLh2vvVWWnCP1OgMdVb5JAdjDrjDT5i0GSQu0bgQ8fPsW6zw=="],
|
||||||
|
|
||||||
"bknd/miniflare/youch/cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
|
"bknd/miniflare/youch/cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { TypeTable } from "fumadocs-ui/components/type-table";
|
|||||||
|
|
||||||
|
|
||||||
bknd features an integrated Admin UI that can be used to:
|
bknd features an integrated Admin UI that can be used to:
|
||||||
- fully manage your backend visually when run in [`db` mode](/usage/setup/#ui-only-mode)
|
- fully manage your backend visually when run in [`db` mode](/usage/introduction/#ui-only-mode)
|
||||||
- manage your database contents
|
- manage your database contents
|
||||||
- manage your media contents
|
- manage your media contents
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ If the connection object is omitted, the app will try to use an in-memory databa
|
|||||||
|
|
||||||
As configuration, you can either pass a partial configuration object or a complete one
|
As configuration, you can either pass a partial configuration object or a complete one
|
||||||
with a version number. The version number is used to automatically migrate the configuration up
|
with a version number. The version number is used to automatically migrate the configuration up
|
||||||
to the latest version upon boot ([`db` mode](/usage/setup#ui-only-mode) only). The default configuration looks like this:
|
to the latest version upon boot ([`db` mode](/usage/introduction#ui-only-mode) only). The default configuration looks like this:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -210,10 +210,6 @@ to the latest version upon boot ([`db` mode](/usage/setup#ui-only-mode) only). T
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<Callout type="warn" title="Guard lockout warning">
|
|
||||||
Setting `auth.guard.enabled` to `true` without first creating a user with an admin role will lock you out of the admin portal. See [Securing Your Admin Portal](/modules/auth#securing-your-admin-portal) for the required steps before enabling the Guard.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
You can use the [CLI](/usage/cli/#getting-the-configuration-config) to get the default configuration:
|
You can use the [CLI](/usage/cli/#getting-the-configuration-config) to get the default configuration:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ export const prerender = false;
|
|||||||
theme: "dark",
|
theme: "dark",
|
||||||
logo_return_path: "/../"
|
logo_return_path: "/../"
|
||||||
}}
|
}}
|
||||||
client:only="react"
|
client:only
|
||||||
/>
|
/>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"pages": ["nextjs", "react-router", "astro", "sveltekit", "tanstack-start", "universal", "vite", "nuxt"]
|
"pages": ["nextjs", "react-router", "astro", "vite"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,402 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Nuxt"
|
|
||||||
description: "Run bknd inside Nuxt"
|
|
||||||
tags: ["documentation"]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
To get started with Nuxt and bknd, create a new Nuxt project by following the [official guide](https://nuxt.com/docs/4.x/getting-started/installation), and then install bknd as a dependency:
|
|
||||||
|
|
||||||
<Tabs groupId='package-manager' persist items={[ 'npm', 'pnpm', 'yarn', 'bun']}>
|
|
||||||
|
|
||||||
```bash tab="npm"
|
|
||||||
npm install bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="pnpm"
|
|
||||||
pnpm install bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="yarn"
|
|
||||||
yarn add bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="bun"
|
|
||||||
bun add bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
<Callout type="warning">
|
|
||||||
When run with Node.js, a version of 22 (LTS) or higher is required. Please
|
|
||||||
verify your version by running `node -v`, and
|
|
||||||
[upgrade](https://nodejs.org/en/download/) if necessary.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
Now create a `bknd.config.ts` file in the root of your project:
|
|
||||||
|
|
||||||
```typescript title="bknd.config.ts"
|
|
||||||
import type NuxtBkndConfig from "bknd/adapter/nuxt";
|
|
||||||
import { em, entity, text, boolean } from "bknd";
|
|
||||||
import { secureRandomString } from "bknd/utils";
|
|
||||||
|
|
||||||
const schema = em({
|
|
||||||
todos: entity("todos", {
|
|
||||||
title: text(),
|
|
||||||
done: boolean(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default {
|
|
||||||
connection: {
|
|
||||||
url: "file:data.db",
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
data: schema.toJSON(),
|
|
||||||
auth: {
|
|
||||||
enabled: true,
|
|
||||||
jwt: {
|
|
||||||
secret: secureRandomString(32),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
// the seed option is only executed if the database was empty
|
|
||||||
seed: async (ctx) => {
|
|
||||||
// create some entries
|
|
||||||
await ctx.em.mutator("todos").insertMany([
|
|
||||||
{ title: "Learn bknd", done: true },
|
|
||||||
{ title: "Build something cool", done: false },
|
|
||||||
]);
|
|
||||||
|
|
||||||
// and create a user
|
|
||||||
await ctx.app.module.auth.createUser({
|
|
||||||
email: "test@bknd.io",
|
|
||||||
password: "12345678",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies NuxtBkndConfig;
|
|
||||||
```
|
|
||||||
|
|
||||||
For more information about the connection object, refer to the [Database](/usage/database) guide.
|
|
||||||
|
|
||||||
|
|
||||||
See [bknd.config.ts](/extending/config) for more information on how to configure bknd. The `NuxtBkndConfig` type extends the base config type with the following properties:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export type NuxtBkndConfig<Env = NuxtEnv> = FrameworkBkndConfig<Env>;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Serve the API and Admin UI
|
|
||||||
|
|
||||||
The Nuxt adapter uses Nuxt middleware to handle API requests and serve the Admin UI. Create a `/server/middleware/bknd.ts` file:
|
|
||||||
|
|
||||||
```typescript title="/server/middleware/bknd.ts"
|
|
||||||
import { serve } from "bknd/adapter/nuxt";
|
|
||||||
import config from "../../bknd.config";
|
|
||||||
|
|
||||||
const handler = serve(config, process.env);
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
const pathname = event.path;
|
|
||||||
const request = toWebRequest(event);
|
|
||||||
|
|
||||||
if (pathname.startsWith("/api") || pathname !== "/") {
|
|
||||||
const res = await handle(request);
|
|
||||||
|
|
||||||
if (res && res.status !== 404) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="success">
|
|
||||||
You can visit https://localhost:3000/admin to see the admin UI. Additionally you can create more todos as you explore the admin UI.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
Create a helper file to instantiate the bknd instance and retrieve the API, importing the configuration from the `bknd.config.ts` file:
|
|
||||||
|
|
||||||
```ts title="server/utils/bknd.ts"
|
|
||||||
import { type NuxtBkndConfig, getApp as getNuxtApp } from "bknd/adapter/nuxt";
|
|
||||||
import bkndConfig from "../../bknd.config";
|
|
||||||
|
|
||||||
export async function getApp<Env = NodeJS.ProcessEnv>(
|
|
||||||
config: NuxtBkndConfig<Env>,
|
|
||||||
args: Env = process.env as Env,
|
|
||||||
) {
|
|
||||||
return await getNuxtApp(config, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getApi({ headers, verify }: { verify?: boolean; headers?: Headers }) {
|
|
||||||
const app = await getApp(bkndConfig, process.env);
|
|
||||||
|
|
||||||
if (verify) {
|
|
||||||
const api = app.getApi({ headers });
|
|
||||||
await api.verifyAuth();
|
|
||||||
return api;
|
|
||||||
}
|
|
||||||
|
|
||||||
return app.getApi();
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
The adapter uses `process.env` to access environment variables, this works because Nuxt uses Nitro underneath and it will use polyfills for `process.env` making it platform/runtime agnostic.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
|
|
||||||
## Example usage of the API
|
|
||||||
|
|
||||||
You can use the `getApp` function to access the bknd API in your app to expose endpoints,
|
|
||||||
Here are some examples:
|
|
||||||
|
|
||||||
```typescript title="server/routes/todos.post.ts"
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
const body = await readBody(event);
|
|
||||||
const { data, action } = body;
|
|
||||||
|
|
||||||
const api = await getApi({});
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case 'get': {
|
|
||||||
const limit = 5;
|
|
||||||
const todos = await api.data.readMany("todos", { limit, sort: "-id" });
|
|
||||||
return { total: todos.body.meta.total, todos, limit };
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'create': {
|
|
||||||
return await api.data.createOne("todos", { title: data.title });
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'delete': {
|
|
||||||
return await api.data.deleteOne("todos", data.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'toggle': {
|
|
||||||
return await api.data.updateOne("todos", data.id, { done: !data.done });
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
return { path: action };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="warning">
|
|
||||||
This can't be done in the `server/api` directory as it will collide with the API endpoints created by the middleware. We will use [defineEventHandler](https://nuxt.com/docs/4.x/directory-structure/server) in the [server/routes](https://nuxt.com/docs/4.x/directory-structure/server) directory to create endpoints and use them in conjunction with composables to access the API safely.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
|
|
||||||
### Using the API through composables
|
|
||||||
|
|
||||||
To use the API in your frontend components/pages, you can create a composable that uses the enpoints created in previous steps:
|
|
||||||
|
|
||||||
```typescript title="app/composables/useTodoActions.ts"
|
|
||||||
import type { DB } from "bknd";
|
|
||||||
|
|
||||||
type Todo = DB["todos"];
|
|
||||||
|
|
||||||
export const useTodoActions = () => {
|
|
||||||
const fetchTodos = () =>
|
|
||||||
$fetch<{ limit: number; todos: Array<Todo>; total: number }>("/todos", {
|
|
||||||
method: "POST",
|
|
||||||
body: { action: "get" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const createTodo = (title: string) =>
|
|
||||||
$fetch("/todos", {
|
|
||||||
method: "POST",
|
|
||||||
body: { action: "create", data: { title } },
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteTodo = (todo: Todo) =>
|
|
||||||
$fetch("/todos", {
|
|
||||||
method: "POST",
|
|
||||||
body: { action: "delete", data: { id: todo.id } },
|
|
||||||
});
|
|
||||||
|
|
||||||
const toggleTodo = (todo: Todo) =>
|
|
||||||
$fetch("/todos", {
|
|
||||||
method: "POST",
|
|
||||||
body: { action: "toggle", data: todo },
|
|
||||||
});
|
|
||||||
|
|
||||||
return { fetchTodos, createTodo, deleteTodo, toggleTodo };
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Usage in a page/component
|
|
||||||
|
|
||||||
Make a composable to fetch the todos:
|
|
||||||
|
|
||||||
```ts title="app/composables/useTodoActions.ts"
|
|
||||||
import type { DB } from "bknd";
|
|
||||||
|
|
||||||
type Todo = DB["todos"];
|
|
||||||
|
|
||||||
export const useTodoActions = () => {
|
|
||||||
const fetchTodos = () =>
|
|
||||||
$fetch<{ limit: number; todos: Array<Todo>; total: number }>("/todos", {
|
|
||||||
method: "POST",
|
|
||||||
body: { action: "get" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const createTodo = (title: string) =>
|
|
||||||
$fetch("/todos", {
|
|
||||||
method: "POST",
|
|
||||||
body: { action: "create", data: { title } },
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteTodo = (todo: Todo) =>
|
|
||||||
$fetch("/todos", {
|
|
||||||
method: "POST",
|
|
||||||
body: { action: "delete", data: { id: todo.id } },
|
|
||||||
});
|
|
||||||
|
|
||||||
const toggleTodo = (todo: Todo) =>
|
|
||||||
$fetch("/todos", {
|
|
||||||
method: "POST",
|
|
||||||
body: { action: "toggle", data: todo },
|
|
||||||
});
|
|
||||||
|
|
||||||
return { fetchTodos, createTodo, deleteTodo, toggleTodo };
|
|
||||||
};
|
|
||||||
```
|
|
||||||
Then use the `useTodoActions` composable in a page:
|
|
||||||
|
|
||||||
```vue title="app/pages/todos.vue"
|
|
||||||
<script lang="ts" setup>
|
|
||||||
const { fetchTodos, createTodo, deleteTodo, toggleTodo } = useTodoActions();
|
|
||||||
const { data:todos, execute } = await useAsyncData("todos", () => fetchTodos());
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
execute();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
v-if="todos"
|
|
||||||
className="flex flex-col items-center justify-center min-h-screen p-8 pb-20 gap-16 sm:p-20"
|
|
||||||
>
|
|
||||||
<main className="flex flex-col gap-8 row-start-2 justify-center items-center sm:items-start">
|
|
||||||
<div class="flex flex-row items-center ">
|
|
||||||
<img class="dark:invert size-24" src="/nuxt.svg" alt="Nuxt logo" />
|
|
||||||
<div class="ml-3.5 mr-2 font-mono opacity-70">&</div>
|
|
||||||
<img class="dark:invert" src="/bknd.svg" alt="bknd logo" width="183" height="59" />
|
|
||||||
</div>
|
|
||||||
<div v-if="data?.todos">
|
|
||||||
<ul>
|
|
||||||
<li v-for="todo in data.todos" :key="todo.id">
|
|
||||||
{{ todo.title }}
|
|
||||||
<button @click="toggleTodo(todo)">Toggle</button>
|
|
||||||
<button @click="deleteTodo(todo)">Delete</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<form @submit.prevent="createTodo('New Todo')">
|
|
||||||
<input type="text" placeholder="New Todo" />
|
|
||||||
<button type="submit">Add</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div v-else className="flex flex-col gap-1">
|
|
||||||
<p>
|
|
||||||
No todos found.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="success">
|
|
||||||
You can visit https://localhost:3000/todos to see all the todos.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
### Using authentication
|
|
||||||
|
|
||||||
Make a composable to fetch the user:
|
|
||||||
|
|
||||||
```ts title="app/composables/useUser.ts"
|
|
||||||
import type { User } from "bknd";
|
|
||||||
|
|
||||||
export const useUser = () => {
|
|
||||||
const getUser = () => $fetch("/api/auth/me") as Promise<{ user: User }>;
|
|
||||||
return { getUser };
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use the `useUser` composable in a page:
|
|
||||||
|
|
||||||
```vue title="app/pages/user.vue"
|
|
||||||
<script lang="ts" setup>
|
|
||||||
const { getUser } = useUser();
|
|
||||||
const { data, status: userStatus, execute } = await useAsyncData("user", () => getUser());
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
execute();
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
v-if="userStatus !== 'pending'"
|
|
||||||
className="flex flex-col items-center justify-center min-h-screen p-8 pb-20 gap-16 sm:p-20"
|
|
||||||
>
|
|
||||||
<main className="flex flex-col gap-8 row-start-2 justify-center items-center sm:items-start">
|
|
||||||
<div class="flex flex-row items-center ">
|
|
||||||
<img class="dark:invert size-24" src="/nuxt.svg" alt="Nuxt logo" />
|
|
||||||
<div class="ml-3.5 mr-2 font-mono opacity-70">&</div>
|
|
||||||
<img class="dark:invert" src="/bknd.svg" alt="bknd logo" width="183" height="59" />
|
|
||||||
</div>
|
|
||||||
<div v-if="data?.user">
|
|
||||||
Logged in as {{ data.user.email }}.
|
|
||||||
<a className="font-medium underline" href='/api/auth/logout'>
|
|
||||||
Logout
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div v-else className="flex flex-col gap-1">
|
|
||||||
<p>
|
|
||||||
Not logged in.
|
|
||||||
<a className="font-medium underline" href="/admin/auth/login">
|
|
||||||
Login
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<p className="text-xs opacity-50">
|
|
||||||
Sign in with:
|
|
||||||
<b>
|
|
||||||
<code>test@bknd.io</code>
|
|
||||||
</b>
|
|
||||||
/
|
|
||||||
<b>
|
|
||||||
<code>12345678</code>
|
|
||||||
</b>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Important Note
|
|
||||||
|
|
||||||
Use `external` attribute on Nuxt links, anytime you are traversing to an external route (like `/api/*` which are handled by bknd's middleware) to prevent vue router from intercepting the link.
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<NuxtLink external href="/admin">
|
|
||||||
Admin
|
|
||||||
</NuxtLink>
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="error">
|
|
||||||
If you don't use the `external` attribute, vue router will intercept the link and try to navigate to it, which will fail and result in a 404 error.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
Check the [Nuxt repository example](https://github.com/bknd-io/bknd/tree/main/examples/nuxt) for more implementation details.
|
|
||||||
@@ -73,9 +73,7 @@ export type ReactRouterBkndConfig<Env = ReactRouterEnv> =
|
|||||||
|
|
||||||
## Serve the API
|
## Serve the API
|
||||||
|
|
||||||
### Helper Functions (Optional)
|
Create a helper file to instantiate the bknd instance and retrieve the API, importing the configurationfrom the `bknd.config.ts` file:
|
||||||
|
|
||||||
For convenience, you can create a helper file to instantiate the bknd instance and retrieve the API. This is optional but recommended as it simplifies usage throughout your app. The examples below assume you've created this helper, but you can adjust the approach according to your needs.
|
|
||||||
|
|
||||||
```ts title="app/bknd.ts"
|
```ts title="app/bknd.ts"
|
||||||
import {
|
import {
|
||||||
@@ -110,9 +108,7 @@ export async function getApi(
|
|||||||
|
|
||||||
For more information about the connection object, refer to the [Database](/usage/database) guide.
|
For more information about the connection object, refer to the [Database](/usage/database) guide.
|
||||||
|
|
||||||
### API Route
|
Create a new api splat route file at `app/routes/api.$.ts`:
|
||||||
|
|
||||||
Create a catch-all route file at `app/routes/api.$.ts` that forwards requests to bknd:
|
|
||||||
|
|
||||||
```ts title="app/routes/api.$.ts"
|
```ts title="app/routes/api.$.ts"
|
||||||
import { getApp } from "~/bknd";
|
import { getApp } from "~/bknd";
|
||||||
@@ -126,22 +122,9 @@ export const loader = handler;
|
|||||||
export const action = handler;
|
export const action = handler;
|
||||||
```
|
```
|
||||||
|
|
||||||
If you're using [`@react-router/fs-routes`](https://reactrouter.com/how-to/file-route-conventions), this file will automatically be picked up as a route.
|
|
||||||
|
|
||||||
If you're manually defining routes in [`app/routes.ts`](https://reactrouter.com/api/framework-conventions/routes.ts), reference this file in your configuration:
|
|
||||||
|
|
||||||
```ts title="app/routes.ts"
|
|
||||||
import { type RouteConfig, route } from "@react-router/dev/routes";
|
|
||||||
|
|
||||||
export default [
|
|
||||||
// your other routes...
|
|
||||||
route("api/*", "./routes/api.$.ts"),
|
|
||||||
] satisfies RouteConfig;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Enabling the Admin UI
|
## Enabling the Admin UI
|
||||||
|
|
||||||
Create a route file at `app/routes/admin.$.tsx` to enable the bknd Admin UI for managing your data, schema, and users:
|
Create a new splat route file at `app/routes/admin.$.tsx`:
|
||||||
|
|
||||||
```tsx title="app/routes/admin.$.tsx"
|
```tsx title="app/routes/admin.$.tsx"
|
||||||
import { lazy, Suspense, useSyncExternalStore } from "react";
|
import { lazy, Suspense, useSyncExternalStore } from "react";
|
||||||
@@ -182,19 +165,6 @@ export default function AdminPage() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you're using [`@react-router/fs-routes`](https://reactrouter.com/how-to/file-route-conventions), this file will automatically be picked up as a route.
|
|
||||||
|
|
||||||
If you're manually defining routes in `app/routes.ts`, reference this file in your configuration:
|
|
||||||
|
|
||||||
```ts title="app/routes.ts"
|
|
||||||
import { type RouteConfig, route } from "@react-router/dev/routes";
|
|
||||||
|
|
||||||
export default [
|
|
||||||
// your other routes...
|
|
||||||
route("admin/*", "./routes/admin.$.tsx"),
|
|
||||||
] satisfies RouteConfig;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example usage of the API
|
## Example usage of the API
|
||||||
|
|
||||||
You can use the `getApi` helper function we've already set up to fetch and mutate:
|
You can use the `getApi` helper function we've already set up to fetch and mutate:
|
||||||
@@ -223,24 +193,3 @@ export default function Index() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using React Hooks (Optional)
|
|
||||||
|
|
||||||
If you want to use bknd's client-side React hooks (like `useEntityQuery`, `useAuth`, etc.), wrap your app in the `ClientProvider` component. This is typically done in `app/root.tsx`:
|
|
||||||
|
|
||||||
```tsx title="app/root.tsx"
|
|
||||||
// other imports
|
|
||||||
import { ClientProvider } from "bknd/client";
|
|
||||||
|
|
||||||
// ...
|
|
||||||
export default function App() {
|
|
||||||
return (
|
|
||||||
<ClientProvider>
|
|
||||||
<Outlet />
|
|
||||||
</ClientProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
```
|
|
||||||
|
|
||||||
The `ClientProvider` automatically uses the same origin for API requests, which works perfectly when bknd is served from your React Router app. For more details on using React hooks, see the [React SDK documentation](/usage/react).
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
---
|
|
||||||
title: "SvelteKit"
|
|
||||||
description: "Run bknd inside SvelteKit"
|
|
||||||
tags: ["documentation"]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
To get started with SvelteKit and bknd, create a new SvelteKit project by following the [official guide](https://svelte.dev/docs/kit/creating-a-project), and then install bknd as a dependency:
|
|
||||||
|
|
||||||
<Tabs groupId='package-manager' persist items={[ 'npm', 'pnpm', 'yarn', 'bun']}>
|
|
||||||
|
|
||||||
```bash tab="npm"
|
|
||||||
npm install bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="pnpm"
|
|
||||||
pnpm install bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="yarn"
|
|
||||||
yarn add bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="bun"
|
|
||||||
bun add bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
<Callout type="warning">
|
|
||||||
When run with Node.js, a version of 22 (LTS) or higher is required. Please
|
|
||||||
verify your version by running `node -v`, and
|
|
||||||
[upgrade](https://nodejs.org/en/download/) if necessary.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
Now create a `bknd.config.ts` file in the root of your project:
|
|
||||||
|
|
||||||
```typescript title="bknd.config.ts"
|
|
||||||
import type { SvelteKitBkndConfig } from "bknd/adapter/sveltekit";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
connection: {
|
|
||||||
url: "file:data.db",
|
|
||||||
},
|
|
||||||
} satisfies SvelteKitBkndConfig;
|
|
||||||
```
|
|
||||||
|
|
||||||
See [bknd.config.ts](/extending/config) for more information on how to configure bknd. The `SvelteKitBkndConfig` type extends the base config type with the following properties:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export type SvelteKitBkndConfig<Env> = Pick<RuntimeBkndConfig<Env>, "adminOptions">;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Serve the API
|
|
||||||
|
|
||||||
The SvelteKit adapter uses SvelteKit's hooks mechanism to handle API requests. Create a `src/hooks.server.ts` file:
|
|
||||||
|
|
||||||
```typescript title="src/hooks.server.ts"
|
|
||||||
import type { Handle } from "@sveltejs/kit";
|
|
||||||
import { serve } from "bknd/adapter/sveltekit";
|
|
||||||
import { env } from "$env/dynamic/private";
|
|
||||||
import config from "../bknd.config";
|
|
||||||
|
|
||||||
const bkndHandler = serve(config, env);
|
|
||||||
|
|
||||||
export const handle: Handle = async ({ event, resolve }) => {
|
|
||||||
// handle bknd API requests
|
|
||||||
const pathname = event.url.pathname;
|
|
||||||
if (pathname.startsWith("/api/")) {
|
|
||||||
const res = await bkndHandler(event);
|
|
||||||
if (res.status !== 404) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolve(event);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
For more information about the connection object, refer to the [Database](/usage/database) guide.
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
The adapter uses `$env/dynamic/private` to access environment variables, making it runtime-agnostic
|
|
||||||
and compatible with any deployment target (Node.js, Bun, Cloudflare, etc.).
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
## Enabling the Admin UI
|
|
||||||
|
|
||||||
The SvelteKit adapter supports serving the Admin UI statically. First, copy the required assets to your `static` folder by adding a postinstall script to your `package.json`:
|
|
||||||
|
|
||||||
```json title="package.json"
|
|
||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"postinstall": "bknd copy-assets --out static" // [!code highlight]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then update your `bknd.config.ts` to configure the admin base path:
|
|
||||||
|
|
||||||
```typescript title="bknd.config.ts"
|
|
||||||
import type { SvelteKitBkndConfig } from "bknd/adapter/sveltekit";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
connection: {
|
|
||||||
url: "file:data.db",
|
|
||||||
},
|
|
||||||
adminOptions: { // [!code highlight]
|
|
||||||
adminBasepath: "/admin" // [!code highlight]
|
|
||||||
}, // [!code highlight]
|
|
||||||
} satisfies SvelteKitBkndConfig;
|
|
||||||
```
|
|
||||||
|
|
||||||
Finally, update your `hooks.server.ts` to also handle admin routes:
|
|
||||||
|
|
||||||
```typescript title="src/hooks.server.ts"
|
|
||||||
import type { Handle } from "@sveltejs/kit";
|
|
||||||
import { serve } from "bknd/adapter/sveltekit";
|
|
||||||
import { env } from "$env/dynamic/private";
|
|
||||||
import config from "../bknd.config";
|
|
||||||
|
|
||||||
const bkndHandler = serve(config, env);
|
|
||||||
|
|
||||||
export const handle: Handle = async ({ event, resolve }) => {
|
|
||||||
// handle bknd API and admin requests
|
|
||||||
const pathname = event.url.pathname;
|
|
||||||
if (pathname.startsWith("/api/") || pathname.startsWith("/admin")) { // [!code highlight]
|
|
||||||
const res = await bkndHandler(event);
|
|
||||||
if (res.status !== 404) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolve(event);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example usage of the API
|
|
||||||
|
|
||||||
You can use the `getApp` function to access the bknd API in your server-side load functions:
|
|
||||||
|
|
||||||
```typescript title="src/routes/+page.server.ts"
|
|
||||||
import type { PageServerLoad } from "./$types";
|
|
||||||
import { getApp } from "bknd/adapter/sveltekit";
|
|
||||||
import { env } from "$env/dynamic/private";
|
|
||||||
import config from "../../bknd.config";
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async () => {
|
|
||||||
const app = await getApp(config, env);
|
|
||||||
const api = app.getApi();
|
|
||||||
|
|
||||||
const todos = await api.data.readMany("todos");
|
|
||||||
|
|
||||||
return {
|
|
||||||
todos: todos.data ?? [],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Then display the data in your Svelte component:
|
|
||||||
|
|
||||||
```svelte title="src/routes/+page.svelte"
|
|
||||||
<script lang="ts">
|
|
||||||
import type { PageData } from "./$types";
|
|
||||||
|
|
||||||
let { data }: { data: PageData } = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h1>Todos</h1>
|
|
||||||
<ul>
|
|
||||||
{#each data.todos as todo (todo.id)}
|
|
||||||
<li>{todo.title}</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using authentication
|
|
||||||
|
|
||||||
To use authentication in your load functions, pass the request headers to the API:
|
|
||||||
|
|
||||||
```typescript title="src/routes/+page.server.ts"
|
|
||||||
import type { PageServerLoad } from "./$types";
|
|
||||||
import { getApp } from "bknd/adapter/sveltekit";
|
|
||||||
import { env } from "$env/dynamic/private";
|
|
||||||
import config from "../../bknd.config";
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ request }) => {
|
|
||||||
const app = await getApp(config, env);
|
|
||||||
const api = app.getApi({ headers: request.headers });
|
|
||||||
await api.verifyAuth();
|
|
||||||
|
|
||||||
const todos = await api.data.readMany("todos");
|
|
||||||
|
|
||||||
return {
|
|
||||||
todos: todos.data ?? [],
|
|
||||||
user: api.getUser(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Check the [SvelteKit repository example](https://github.com/bknd-io/bknd/tree/main/examples/sveltekit) for more implementation details.
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Tanstack Start"
|
|
||||||
description: "Run bknd inside Tanstack Start"
|
|
||||||
tags: ["documentation"]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
To get started with Tanstack Start and bknd, create a new Tanstack Start project by following the [official guide](https://tanstack.com/start/latest/docs/framework/react/getting-started#start-a-new-project-from-scratch), and then install bknd as a dependency:
|
|
||||||
|
|
||||||
<Tabs groupId='package-manager' persist items={[ 'npm', 'pnpm', 'yarn', 'bun']}>
|
|
||||||
|
|
||||||
```bash tab="npm"
|
|
||||||
npm install bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="pnpm"
|
|
||||||
pnpm install bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="yarn"
|
|
||||||
yarn add bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="bun"
|
|
||||||
bun add bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
<Callout type="warning">
|
|
||||||
When run with Node.js, a version of 22 (LTS) or higher is required. Please
|
|
||||||
verify your version by running `node -v`, and
|
|
||||||
[upgrade](https://nodejs.org/en/download/) if necessary.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
Now create a `bknd.config.ts` file in the root of your project:
|
|
||||||
|
|
||||||
```typescript title="bknd.config.ts"
|
|
||||||
import { type TanstackStartConfig } from "bknd/adapter/tanstack-start";
|
|
||||||
import { em, entity, text, boolean } from "bknd";
|
|
||||||
|
|
||||||
const schema = em({
|
|
||||||
todos: entity("todos", {
|
|
||||||
title: text(),
|
|
||||||
done: boolean(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default {
|
|
||||||
connection: {
|
|
||||||
url: "file:data.db",
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
data: schema.toJSON(),
|
|
||||||
auth: {
|
|
||||||
enabled: true,
|
|
||||||
jwt: {
|
|
||||||
secret: "random_gibberish_please_change_this",
|
|
||||||
// use something like `openssl rand -hex 32` for production
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
// the seed option is only executed if the database was empty
|
|
||||||
seed: async (ctx) => {
|
|
||||||
// create some entries
|
|
||||||
await ctx.em.mutator("todos").insertMany([
|
|
||||||
{ title: "Learn bknd", done: true },
|
|
||||||
{ title: "Build something cool", done: false },
|
|
||||||
]);
|
|
||||||
|
|
||||||
// and create a user
|
|
||||||
await ctx.app.module.auth.createUser({
|
|
||||||
email: "test@bknd.io",
|
|
||||||
password: "12345678",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies TanstackStartConfig;
|
|
||||||
```
|
|
||||||
|
|
||||||
For more information about the connection object, refer to the [Database](/usage/database) guide.
|
|
||||||
|
|
||||||
|
|
||||||
See [bknd.config.ts](/extending/config) for more information on how to configure bknd. The `TanstackStartConfig` type extends the base config type with the following properties:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export type TanstackStartConfig<Env = TanstackStartEnv> = FrameworkBkndConfig<Env>;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Serve the API
|
|
||||||
|
|
||||||
The Tanstack Start adapter uses Tanstack Start's hooks mechanism to handle API requests. Create a `/src/routes/api.$.ts` file:
|
|
||||||
|
|
||||||
```typescript title="/src/routes/api.$.ts"
|
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
import config from "../../bknd.config";
|
|
||||||
import { serve } from "bknd/adapter/tanstack-start";
|
|
||||||
|
|
||||||
const handler = serve(config);
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/api/$")({
|
|
||||||
server: {
|
|
||||||
handlers: {
|
|
||||||
ANY: async ({ request }) => await handler(request),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Create a helper file to instantiate the bknd instance and retrieve the API, importing the configuration from the `bknd.config.ts` file:
|
|
||||||
|
|
||||||
```ts title="src/bknd.ts"
|
|
||||||
import config from "../bknd.config";
|
|
||||||
import { getApp } from "bknd/adapter/tanstack-start";
|
|
||||||
|
|
||||||
export async function getApi({
|
|
||||||
headers,
|
|
||||||
verify,
|
|
||||||
}: {
|
|
||||||
verify?: boolean;
|
|
||||||
headers?: Headers;
|
|
||||||
}) {
|
|
||||||
const app = await getApp(config, process.env);
|
|
||||||
|
|
||||||
if (verify) {
|
|
||||||
const api = app.getApi({ headers });
|
|
||||||
await api.verifyAuth();
|
|
||||||
return api;
|
|
||||||
}
|
|
||||||
|
|
||||||
return app.getApi();
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
The adapter uses `process.env` to access environment variables, this works because Tanstack Start uses Nitro underneath and it will use polyfills for `process.env` making it platform/runtime agnostic.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
## Enabling the Admin UI
|
|
||||||
|
|
||||||
Create a page at /src/routes/admin.$.tsx:
|
|
||||||
|
|
||||||
```typescript title="/src/routes/admin.$.tsx"
|
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
import { useAuth } from "bknd/client";
|
|
||||||
import "bknd/dist/styles.css";
|
|
||||||
import { Admin } from "bknd/ui";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/admin/$")({
|
|
||||||
ssr: false, // [!code highlight] "data-only" works too
|
|
||||||
component: RouteComponent,
|
|
||||||
});
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
const { user } = useAuth();
|
|
||||||
return (
|
|
||||||
<Admin
|
|
||||||
withProvider={{ user: user }}
|
|
||||||
config={{
|
|
||||||
basepath: "/admin",
|
|
||||||
logo_return_path: "/../",
|
|
||||||
theme: "system",
|
|
||||||
}}
|
|
||||||
baseUrl={import.meta.env.APP_URL}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
Admin routes are expected to run on the client not using `ssr: false` will cause errors like `✘ [ERROR] No matching export in "node_modules/json-schema-library/dist/index.mjs" for import "Draft2019"` and production build might fail because of this
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
|
|
||||||
## Example usage of the API
|
|
||||||
|
|
||||||
You can use the `getApp` function to access the bknd API in your app:
|
|
||||||
These are a few examples how you can validate user and handle server-side requests using `createServerFn`.
|
|
||||||
|
|
||||||
```typescript title="src/routes/index.tsx"
|
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
import { getApi } from "@/bknd";
|
|
||||||
import { createServerFn } from "@tanstack/react-start";
|
|
||||||
|
|
||||||
export const getTodo = createServerFn()
|
|
||||||
.handler(async () => {
|
|
||||||
const api = await getApi({});
|
|
||||||
const limit = 5;
|
|
||||||
const todos = await api.data.readMany("todos", { limit, sort: "-id" });
|
|
||||||
return { todos };
|
|
||||||
});
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/")({
|
|
||||||
ssr: false,
|
|
||||||
component: App,
|
|
||||||
loader: async () => {
|
|
||||||
return await getTodo();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
const { todos } = Route.useLoaderData();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>Todos</h1>
|
|
||||||
<ul>
|
|
||||||
{todos.map((todo) => (
|
|
||||||
<li key={todo.id}>{todo.title}</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Using authentication
|
|
||||||
|
|
||||||
To use authentication in your app, pass the request headers to the API:
|
|
||||||
|
|
||||||
```typescript title="src/routes/user.tsx"
|
|
||||||
import { getApi } from "@/bknd";
|
|
||||||
import { createServerFn } from "@tanstack/react-start";
|
|
||||||
import { Link } from "@tanstack/react-router";
|
|
||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
import { getRequest } from "@tanstack/react-start/server";
|
|
||||||
|
|
||||||
export const getUser = createServerFn()
|
|
||||||
.handler(async () => {
|
|
||||||
const request = getRequest();
|
|
||||||
const api = await getApi({ verify: true, headers: request.headers });
|
|
||||||
const user = api.getUser();
|
|
||||||
return { user };
|
|
||||||
});
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/user")({
|
|
||||||
component: RouteComponent,
|
|
||||||
loader: async () => {
|
|
||||||
return { user: await getUser() };
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
const { user } = Route.useLoaderData();
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{user ? (
|
|
||||||
<>
|
|
||||||
Logged in as {user.email}.{" "}
|
|
||||||
<Link
|
|
||||||
className="font-medium underline"
|
|
||||||
to={"/api/auth/logout" as string}
|
|
||||||
>
|
|
||||||
Logout
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div className="flex flex-col gap-1">
|
|
||||||
<p>
|
|
||||||
Not logged in.
|
|
||||||
<Link
|
|
||||||
className="font-medium underline"
|
|
||||||
to={"/admin/auth/login" as string}
|
|
||||||
>
|
|
||||||
Login
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
<p className="text-xs opacity-50">
|
|
||||||
Sign in with:
|
|
||||||
<b>
|
|
||||||
<code>test@bknd.io</code>
|
|
||||||
</b>
|
|
||||||
/
|
|
||||||
<b>
|
|
||||||
<code>12345678</code>
|
|
||||||
</b>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Check the [Tanstack Start repository example](https://github.com/bknd-io/bknd/tree/main/examples/tanstack-start) for more implementation details.
|
|
||||||
@@ -1,277 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Universal Adapter"
|
|
||||||
description: "Bring bknd to any web framework or runtime"
|
|
||||||
tags: ["documentation"]
|
|
||||||
---
|
|
||||||
|
|
||||||
## What is the Universal Adapter?
|
|
||||||
|
|
||||||
The universal adapter (`bknd/adapter/universal`) is a framework/runtime agnostic adapter which can integrate `bknd` into **any** web framework or runtime — even ones without a dedicated adapter.
|
|
||||||
|
|
||||||
**Use the universal adapter when:**
|
|
||||||
- Your framework doesn't have a dedicated bknd adapter
|
|
||||||
- You want full control over your server setup
|
|
||||||
- You're building a custom server or edge function
|
|
||||||
|
|
||||||
**Use a platform-specific adapter when:**
|
|
||||||
- You're using a supported framework (Next.js, SvelteKit, Nuxt, Astro, etc.)
|
|
||||||
- You prefer opinionated, zero-config setup
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
<Tabs groupId='package-manager' persist items={[ 'npm', 'pnpm', 'yarn', 'bun']}>
|
|
||||||
|
|
||||||
```bash tab="npm"
|
|
||||||
npm install bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="pnpm"
|
|
||||||
pnpm install bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="yarn"
|
|
||||||
yarn add bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="bun"
|
|
||||||
bun add bknd
|
|
||||||
```
|
|
||||||
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
## Basic Setup
|
|
||||||
|
|
||||||
Start by creating a config file:
|
|
||||||
|
|
||||||
```typescript title="bknd.config.ts"
|
|
||||||
import type { Config } from "bknd/adapter/universal";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
connection: {
|
|
||||||
url: "file:data.db",
|
|
||||||
},
|
|
||||||
} satisfies Config<"api">;
|
|
||||||
```
|
|
||||||
<Callout type="info" title="The Config type helper">
|
|
||||||
The `Config` type is a helper type which maps the correct config type based on mode.
|
|
||||||
|
|
||||||
Use `Config<"api">` when you are using a React-based framework otherwise use `Config<"standalone">` to serve both the Admin UI and bknd api using the same handler
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
### Helper Singleton
|
|
||||||
```typescript title="lib/bknd.ts"
|
|
||||||
import { createBknd } from "bknd/adapter/universal";
|
|
||||||
import config from "../bknd.config";
|
|
||||||
|
|
||||||
export const bknd = createBknd({ mode: "standalone", options: config }, process.env);
|
|
||||||
// ^ use "api" if using a React-based framework
|
|
||||||
```
|
|
||||||
|
|
||||||
`createBknd` returns three methods:
|
|
||||||
|
|
||||||
| Method | Returns | Description |
|
|
||||||
|--------|---------|-------------|
|
|
||||||
| `getApp(env: Env)` | `Promise<App>` | The built bknd app instance |
|
|
||||||
| `getApi({ headers, verify })` | `Promise<Api>` | Convenience wrapper around `app.getApi()` |
|
|
||||||
| `serve()` | `(req: Request) => Promise<Response>` | A fetch handler for your server |
|
|
||||||
|
|
||||||
## Choosing an Admin UI Path
|
|
||||||
|
|
||||||
The web adapter supports two approaches to serving the admin UI:
|
|
||||||
|
|
||||||
| Path | Best For | How |
|
|
||||||
|------|----------|-----|
|
|
||||||
| **Client-side** (default) | React frameworks: Next.js, Astro, React Router, Tanstack Start, Waku | Import and render `<Admin />` in your own React route |
|
|
||||||
| **Server-side** | Non-React frameworks: SvelteKit, Nuxt, Bun, Node, Deno | The server serves the Admin UI assets and API |
|
|
||||||
|
|
||||||
## Path 1: Client-Side Admin (Default)
|
|
||||||
|
|
||||||
For React-capable frameworks, render the admin UI directly in a route:
|
|
||||||
|
|
||||||
```tsx title="app/admin/[[...admin]]/page.tsx"
|
|
||||||
import { Admin } from "bknd/ui";
|
|
||||||
import "bknd/dist/styles.css";
|
|
||||||
import { bknd } from "@/bknd";
|
|
||||||
|
|
||||||
export default async function AdminPage() {
|
|
||||||
const api = await bknd.getApi({ verify: true });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Admin
|
|
||||||
withProvider={{ user: api.getUser() }}
|
|
||||||
config={{ basepath: "/admin" }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
No server-side admin configuration needed. The admin UI runs entirely in the browser.
|
|
||||||
|
|
||||||
## Path 2: Server-Side Admin
|
|
||||||
|
|
||||||
For non-React frameworks or standalone servers, enable the admin controller:
|
|
||||||
|
|
||||||
```typescript title="src/bknd.ts"
|
|
||||||
import { createBknd } from "bknd/adapter/universal";
|
|
||||||
import config from "../bknd.config";
|
|
||||||
|
|
||||||
export const bknd = createBknd({ mode: "standalone", options: config }, env);
|
|
||||||
// ^ use "api" if using a React-based framework
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll also need to serve the admin's static assets (JS, CSS). Choose one of three strategies:
|
|
||||||
|
|
||||||
### Strategy 1: serveStatic Middleware
|
|
||||||
|
|
||||||
**Best for:** Bun, Node, standalone servers with filesystem access
|
|
||||||
|
|
||||||
Use Hono's platform-specific `serveStatic` in `bknd.config.ts` to serve assets from `node_modules/bknd/dist/static/`:
|
|
||||||
|
|
||||||
```typescript title="bknd.config.ts"
|
|
||||||
import type { Config } from "bknd/adapter/universal";
|
|
||||||
import { serveStatic } from "hono/bun"; // or "@hono/node-server/serve-static"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
connection: {
|
|
||||||
url: "file:data.db",
|
|
||||||
serveStatic: serveStatic({ root: "./node_modules/bknd/dist/static" }),
|
|
||||||
adminOptions: {
|
|
||||||
adminBasepath: "/admin",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies Config<"standalone">;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Strategy 2: copy-assets Postinstall
|
|
||||||
|
|
||||||
**Best for:** SvelteKit, Nuxt, any framework with a static directory
|
|
||||||
|
|
||||||
Copy assets at install time and let your framework serve them:
|
|
||||||
|
|
||||||
```json title="package.json"
|
|
||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"postinstall": "bknd copy-assets --out static"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Per-framework output paths:
|
|
||||||
|
|
||||||
| Framework | `--out` flag |
|
|
||||||
|-----------|-------------|
|
|
||||||
| SvelteKit | `--out static` |
|
|
||||||
| Nuxt | `--out public` |
|
|
||||||
| Next.js | `--out public` |
|
|
||||||
| Astro | `--out public` |
|
|
||||||
| Qwik | `--out public` |
|
|
||||||
|
|
||||||
### Strategy 3: serveStaticViaImport
|
|
||||||
|
|
||||||
**Best for:** Edge/serverless runtimes (Deno Deploy, Cloudflare Workers)
|
|
||||||
|
|
||||||
For environments without filesystem access:
|
|
||||||
|
|
||||||
```typescript title="bknd.config.ts"
|
|
||||||
import type { Config } from "bknd/adapter/universal";
|
|
||||||
import { serveStaticViaImport } from "bknd/adapter";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
connection: {
|
|
||||||
url: "file:data.db",
|
|
||||||
serveStatic: serveStaticViaImport(),
|
|
||||||
adminOptions: {
|
|
||||||
adminBasepath: "/admin",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies Config<"standalone">;
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Serving Requests
|
|
||||||
|
|
||||||
Example use `bknd.serve()` as a fetch handler in your server:
|
|
||||||
|
|
||||||
```typescript title="server.ts"
|
|
||||||
import { bknd } from "./bknd";
|
|
||||||
|
|
||||||
// Bun
|
|
||||||
Bun.serve({ fetch: bknd.serve(), port: 3000 });
|
|
||||||
|
|
||||||
// Node (with @hono/node-server)
|
|
||||||
import { serve } from "@hono/node-server";
|
|
||||||
serve({ fetch: bknd.serve(), port: 3000 });
|
|
||||||
|
|
||||||
// Next.js
|
|
||||||
const handler = bknd.serve(); // here you'll use "api" for mode when setting up `bknd` instance
|
|
||||||
|
|
||||||
export const GET = handler;
|
|
||||||
export const POST = handler;
|
|
||||||
export const PUT = handler;
|
|
||||||
export const PATCH = handler;
|
|
||||||
export const DELETE = handler;
|
|
||||||
|
|
||||||
// Qwik City (Middleware)
|
|
||||||
export const onRequest: RequestHandler = async ({
|
|
||||||
url,
|
|
||||||
next,
|
|
||||||
status,
|
|
||||||
headers,
|
|
||||||
request,
|
|
||||||
redirect,
|
|
||||||
getWritableStream,
|
|
||||||
}) => {
|
|
||||||
const pathname = url.pathname;
|
|
||||||
|
|
||||||
if (pathname.startsWith("/api") || pathname !== "/") {
|
|
||||||
const response = await handler(request);
|
|
||||||
|
|
||||||
// skips unknown paths
|
|
||||||
if (response.status === 404) {
|
|
||||||
await next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// adds the set-cookie header
|
|
||||||
response.headers.forEach((value, key) => {
|
|
||||||
headers.set(key, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
// for redirect
|
|
||||||
if (response.status >= 300 && response.status < 400) {
|
|
||||||
const location = response.headers.get("location");
|
|
||||||
if (location) {
|
|
||||||
throw redirect(response.status as any, location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stream back the body
|
|
||||||
status(response.status);
|
|
||||||
if (response.body) {
|
|
||||||
await response.body?.pipeTo(getWritableStream());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use `bknd.getApp()` to integrate with an existing Hono or other router setup.
|
|
||||||
|
|
||||||
## Reference: Config\<T\>
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export type AdapterModeWithOptions<Env = Record<string, string | undefined>> =
|
|
||||||
| {
|
|
||||||
mode: "standalone";
|
|
||||||
options: RuntimeBkndConfig<Env>;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
mode: "api";
|
|
||||||
options: FrameworkBkndConfig<Env>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Config<T extends AdapterModeWithOptions["mode"]> = Extract<
|
|
||||||
Parameters<typeof createBknd>[0],
|
|
||||||
{ mode: T }
|
|
||||||
>['options'];
|
|
||||||
```
|
|
||||||
@@ -214,7 +214,7 @@ Now you can use the CLI with your Cloudflare resources.
|
|||||||
Instead, it's recommended to split this configuration into separate files, e.g. `bknd.config.ts` and `config.ts`:
|
Instead, it's recommended to split this configuration into separate files, e.g. `bknd.config.ts` and `config.ts`:
|
||||||
|
|
||||||
```typescript title="config.ts"
|
```typescript title="config.ts"
|
||||||
import { d1, type CloudflareBkndConfig } from "bknd/adapter/cloudflare";
|
import type { CloudflareBkndConfig } from "bknd/adapter/cloudflare";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
app: (env) => ({
|
app: (env) => ({
|
||||||
|
|||||||
@@ -27,30 +27,6 @@ bknd seamlessly integrates with popular frameworks, allowing you to use what you
|
|||||||
href="/integration/astro"
|
href="/integration/astro"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Card
|
|
||||||
icon={<Icon icon="simple-icons:svelte" className="text-fd-primary !size-6" />}
|
|
||||||
title="SvelteKit"
|
|
||||||
href="/integration/sveltekit"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Card
|
|
||||||
icon={<Icon icon="simple-icons:tanstack" className="text-fd-primary !size-6" />}
|
|
||||||
title="Tanstack Start"
|
|
||||||
href="/integration/tanstack-start"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Card
|
|
||||||
icon={<Icon icon="simple-icons:nuxt" className="text-fd-primary !size-6" />}
|
|
||||||
title="Nuxt"
|
|
||||||
href="/integration/nuxt"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Card
|
|
||||||
icon={<Icon icon="tabler:world" className="text-fd-primary !size-6" />}
|
|
||||||
title="Universal Web Adapter"
|
|
||||||
href="/integration/universal"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Card title="Yours missing?" href="https://github.com/bknd-io/bknd/issues/new">
|
<Card title="Yours missing?" href="https://github.com/bknd-io/bknd/issues/new">
|
||||||
Create a new issue to request a guide for your framework.
|
Create a new issue to request a guide for your framework.
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"start",
|
"start",
|
||||||
"motivation",
|
"motivation",
|
||||||
"---Usage---",
|
"---Usage---",
|
||||||
"./usage/setup",
|
"./usage/introduction",
|
||||||
"./usage/database",
|
"./usage/database",
|
||||||
"./usage/cli",
|
"./usage/cli",
|
||||||
"./usage/sdk",
|
"./usage/sdk",
|
||||||
|
|||||||
@@ -27,69 +27,3 @@ Authentication is essential for securing applications, and **bknd** provides a s
|
|||||||
- Compatible with any specification-compliant provider.
|
- Compatible with any specification-compliant provider.
|
||||||
|
|
||||||
With a focus on flexibility and ease of integration, bknd's authentication system offers the essentials for managing secure user access in your applications.
|
With a focus on flexibility and ease of integration, bknd's authentication system offers the essentials for managing secure user access in your applications.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Server Module: `module.auth`
|
|
||||||
|
|
||||||
### `auth.changePassword([userId], [password])`
|
|
||||||
|
|
||||||
To change a user's password, use the `changePassword` method:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await app.module.auth.changePassword(user.id, password);
|
|
||||||
```
|
|
||||||
|
|
||||||
This method updates the password for the specified user.
|
|
||||||
- `userId`: The ID of the user whose password should be changed.
|
|
||||||
- `password`: The new password value.
|
|
||||||
|
|
||||||
This method throws an error if:
|
|
||||||
- The user with the given `userId` is not found.
|
|
||||||
- The user's authentication strategy is not `"password"` (e.g. the user registered via an OAuth provider).
|
|
||||||
|
|
||||||
|
|
||||||
## Securing Your Admin Portal
|
|
||||||
|
|
||||||
<Callout type="warn" title="Do not enable the Guard without an admin user">
|
|
||||||
Enabling the Guard without first creating a user with an admin role **will lock you out of the admin portal entirely**. There is no login screen that can save you — you'll need to manually edit the database to recover. Follow the checklist below before enabling the Guard.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
The **Guard** protects your admin portal and API endpoints by requiring authentication and proper permissions. Before enabling it, you must set up at least one user with full admin access.
|
|
||||||
|
|
||||||
### Checklist Before Enabling Guard
|
|
||||||
|
|
||||||
Complete these steps **in order** before turning on the Guard:
|
|
||||||
|
|
||||||
1. **Create an admin role** with `implicit_allow: true`
|
|
||||||
- This grants full access to all permissions
|
|
||||||
- Go to Auth → Roles → Create a new role
|
|
||||||
- Enable the "Implicit Allow" toggle
|
|
||||||
|
|
||||||
2. **Create a user**
|
|
||||||
- Go to Auth → Users → Create a new user
|
|
||||||
- Set up their email and password
|
|
||||||
|
|
||||||
3. **Attach the admin role to the user**
|
|
||||||
- Edit the user you just created
|
|
||||||
- Assign the admin role to them
|
|
||||||
|
|
||||||
4. **Verify you can sign in**
|
|
||||||
- Open an incognito/private browser window
|
|
||||||
- Navigate to your app and sign in with the admin user
|
|
||||||
- Confirm you have access
|
|
||||||
|
|
||||||
5. **Now enable the Guard**
|
|
||||||
- Go to Auth → Settings
|
|
||||||
- Enable the Guard
|
|
||||||
|
|
||||||
### Recovery: If You're Locked Out
|
|
||||||
|
|
||||||
If you enabled the Guard without setting up an admin user, you'll need to access your database directly:
|
|
||||||
|
|
||||||
1. Connect to your database using a database client or CLI tool
|
|
||||||
2. Find the `__bknd` table
|
|
||||||
3. Locate the row where `type = 'config'`
|
|
||||||
4. In the `json` column, set `auth.guard.enabled` to `false`
|
|
||||||
5. Restart your bknd instance
|
|
||||||
6. Complete the checklist above, then re-enable the Guard
|
|
||||||
|
|||||||
@@ -6,81 +6,22 @@ tags: ["documentation"]
|
|||||||
|
|
||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
import { examples } from "@/app/_components/StackBlitz";
|
import { examples } from "@/app/_components/StackBlitz";
|
||||||
import { SquareMousePointer, Code, Blend, Rocket } from 'lucide-react';
|
import { SquareMousePointer, Code, Blend } from 'lucide-react';
|
||||||
|
|
||||||
## bknd is a lightweight batteries-included backend that embeds into your frontend app
|
|
||||||
|
|
||||||
<Callout type="warning" title="We are in beta">
|
Glad you're here! **bknd** is a lightweight, infrastructure agnostic and feature-rich backend that runs in any JavaScript environment.
|
||||||
We're making great progress towards v1, but don't recommend production use yet.
|
|
||||||
Follow along for updates on [GitHub Releases](https://github.com/bknd-io/bknd/releases) and in our [Discord community](https://discord.gg/952SFk8Tb8)
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
bknd includes full REST APIs, an admin dashboard, auth, media uploads, a [type-safe SDK](/usage/sdk), [React hooks](/usage/react), and plugins to extend it. Host it with your SSR app or as a standalone service. Built on Web Standards, it runs anywhere JavaScript runs.
|
- Instant backend with full REST API
|
||||||
|
- Built on Web Standards for maximum compatibility
|
||||||
|
- Multiple ready-made [integrations](/integration/introduction) (standalone, runtime, framework)
|
||||||
|
- Official [API SDK](/usage/sdk) and [React SDK](/usage/react) with type-safety
|
||||||
|
- [React elements](/usage/elements) for auto-configured authentication and media components
|
||||||
|
- Built-in [MCP server](/usage/mcp/overview) for controlling your backend
|
||||||
|
- Multiple run [modes](/usage/introduction#modes) (ui-only, code-only, hybrid)
|
||||||
|
|
||||||
Bring your [favorite frontend](./#start-with-a-frameworkruntime) and [favorite SQL database](./#use-your-favorite-sql-database), and we'll bring the ~~backend~~ bknd.
|
## Preview
|
||||||
|
|
||||||
<Cards>
|
Here is a preview of **bknd** in StackBlitz:
|
||||||
<Card
|
|
||||||
href="/motivation"
|
|
||||||
title="Learn about why we built bknd"
|
|
||||||
icon={<Rocket />}
|
|
||||||
>
|
|
||||||
Why another backend system?
|
|
||||||
</Card>
|
|
||||||
</Cards>
|
|
||||||
|
|
||||||
## Quickstart
|
|
||||||
|
|
||||||
<Callout type="info" title="This demo bknd instance is for playing and learning">
|
|
||||||
Don't worry about messing anything up in this stage since you're learning the ropes of bknd. If you want to start over, please delete the generated `data.db` database file and follow this tutorial again.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
Spin up a bknd instance via the [bknd CLI](/usage/cli):
|
|
||||||
|
|
||||||
<Tabs groupId='package-manager' persist items={[ 'npm','bun' ]}>
|
|
||||||
|
|
||||||
```bash tab="npm"
|
|
||||||
npx bknd run
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash tab="bun"
|
|
||||||
bunx bknd run
|
|
||||||
```
|
|
||||||
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
This creates a local `data.db` SQLite database and starts the bknd web server at http://localhost:1337.
|
|
||||||
|
|
||||||
By default, the admin dashboard is open and not guarded. This is intentional — bknd uses an opt-in philosophy to allow quick prototyping. Let's enable authentication and guard the dashboard to secure it.
|
|
||||||
|
|
||||||
1. Visit http://localhost:1337/auth/settings. Toggle "Authentication Enabled" to enable auth. Select "Update" to save.
|
|
||||||
2. Visit http://localhost:1337/data/entity/users. Create a user by selecting "New User" and entering an email and password.
|
|
||||||
3. Visit http://localhost:1337/auth/roles. Create an admin role by selecting "Create new". Name it "Admin" and select "Create". Then, scroll to the bottom of the page and enable "Implicit allow missing permissions?". Select "Update" to save.
|
|
||||||
4. Visit http://localhost:1337/data/entity/users. Select on your newly created user, select the "Admin" role, and select "Update".
|
|
||||||
5. It's time to guard your admin dashboard. Visit http://localhost:1337/settings/auth. Select "Edit". Scroll to "Guard" and enable it. Then, select "Save". _(This should log you out!)_
|
|
||||||
6. Now, log in to your secured admin dashboard at http://localhost:1337/auth/login.
|
|
||||||
|
|
||||||
You did it! You've started the bknd server, created an admin user, and protected your app by enabling the guard.
|
|
||||||
|
|
||||||
## Modes
|
|
||||||
|
|
||||||
What you just experienced is **UI-only mode** — bknd's data and configuration is managed entirely via the admin dashboard. But that's not the only way to use bknd:
|
|
||||||
|
|
||||||
<Cards className="grid-cols-3">
|
|
||||||
<Card title="UI-only" href="/usage/setup#ui-only-mode" icon={<SquareMousePointer className="text-fd-primary !size-6" />}>
|
|
||||||
Configure your backend and manage your data visually with the built-in Admin UI.
|
|
||||||
</Card>
|
|
||||||
<Card title="Code-only" href="/usage/setup#code-only-mode" icon={<Code className="text-fd-primary !size-6" />}>
|
|
||||||
Configure your backend programmatically with a Drizzle-like API, manage your data with the Admin UI.
|
|
||||||
</Card>
|
|
||||||
<Card title="Hybrid" href="/usage/setup#hybrid-mode" icon={<Blend className="text-fd-primary !size-6" />}>
|
|
||||||
Configure your backend visually while in development, use a read-only configuration in production.
|
|
||||||
</Card>
|
|
||||||
</Cards>
|
|
||||||
|
|
||||||
Learn more about each mode and the underlying configuration in [Setup & Modes](/usage/setup).
|
|
||||||
|
|
||||||
## Try bknd in the browser
|
|
||||||
|
|
||||||
<Card className="p-0 pb-1">
|
<Card className="p-0 pb-1">
|
||||||
<StackBlitz path="github/bknd-io/bknd-demo" initialPath="/" />
|
<StackBlitz path="github/bknd-io/bknd-demo" initialPath="/" />
|
||||||
@@ -94,9 +35,27 @@ Learn more about each mode and the underlying configuration in [Setup & Modes](/
|
|||||||
</Accordions>
|
</Accordions>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
Enter the following command to spin up an instance:
|
||||||
|
|
||||||
|
<Tabs groupId='package-manager' persist items={[ 'npm','bun' ]}>
|
||||||
|
|
||||||
|
```bash tab="npm"
|
||||||
|
npx bknd run
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="bun"
|
||||||
|
bunx bknd run
|
||||||
|
```
|
||||||
|
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
To learn more about the CLI, check out the [Using the CLI](/usage/cli) guide.
|
||||||
|
|
||||||
## Start with a Framework/Runtime
|
## Start with a Framework/Runtime
|
||||||
|
|
||||||
Pick your framework or runtime to get started.
|
Start by using the integration guide for these popular frameworks/runtimes. There will be more in the future, so stay tuned!
|
||||||
|
|
||||||
<Cards>
|
<Cards>
|
||||||
<Card icon={<Icon icon="tabler:brand-nextjs" className="text-fd-primary !size-6" />} title="NextJS" href="/integration/nextjs" />
|
<Card icon={<Icon icon="tabler:brand-nextjs" className="text-fd-primary !size-6" />} title="NextJS" href="/integration/nextjs" />
|
||||||
@@ -144,24 +103,6 @@ Pick your framework or runtime to get started.
|
|||||||
href="/integration/deno"
|
href="/integration/deno"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Card
|
|
||||||
icon={<Icon icon="simple-icons:svelte" className="text-fd-primary !size-6" />}
|
|
||||||
title="SvelteKit"
|
|
||||||
href="/integration/sveltekit"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Card
|
|
||||||
icon={<Icon icon="simple-icons:tanstack" className="text-fd-primary !size-6" />}
|
|
||||||
title="Tanstack Start"
|
|
||||||
href="/integration/tanstack-start"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Card
|
|
||||||
icon={<Icon icon="simple-icons:nuxt" className="text-fd-primary !size-6" />}
|
|
||||||
title="Nuxt"
|
|
||||||
href="/integration/nuxt"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
icon={<Icon icon="tabler:lambda" className="text-fd-primary !size-6" />}
|
icon={<Icon icon="tabler:lambda" className="text-fd-primary !size-6" />}
|
||||||
title="AWS Lambda"
|
title="AWS Lambda"
|
||||||
@@ -169,13 +110,7 @@ Pick your framework or runtime to get started.
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
icon={<Icon icon="tabler:world" className="text-fd-primary !size-6" />}
|
icon={<Icon icon="simple-icons:vitest" className="text-fd-primary !size-6" />}
|
||||||
title="Universal Web Adapter"
|
|
||||||
href="/integration/universal"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Card
|
|
||||||
icon={<Icon icon="simple-icons:vite" className="text-fd-primary !size-6" />}
|
|
||||||
title="Vite"
|
title="Vite"
|
||||||
href="/integration/vite"
|
href="/integration/vite"
|
||||||
/>
|
/>
|
||||||
@@ -232,3 +167,19 @@ The following databases are currently supported. Request a new integration if yo
|
|||||||
Create a new issue to request a new database integration.
|
Create a new issue to request a new database integration.
|
||||||
</Card>
|
</Card>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|
||||||
|
## Choose a mode
|
||||||
|
|
||||||
|
**bknd** supports multiple modes to suit your needs.
|
||||||
|
|
||||||
|
<Cards className="grid-cols-3">
|
||||||
|
<Card title="UI-only" href="/usage/introduction#ui-only-mode" icon={<SquareMousePointer className="text-fd-primary !size-6" />}>
|
||||||
|
Configure your backend and manage your data visually with the built-in Admin UI.
|
||||||
|
</Card>
|
||||||
|
<Card title="Code-only" href="/usage/introduction#code-only-mode" icon={<Code className="text-fd-primary !size-6" />}>
|
||||||
|
Configure your backend programmatically with a Drizzle-like API, manage your data with the Admin UI.
|
||||||
|
</Card>
|
||||||
|
<Card title="Hybrid" href="/usage/introduction#hybrid-mode" icon={<Blend className="text-fd-primary !size-6" />}>
|
||||||
|
Configure your backend visually while in development, use a read-only configuration in production.
|
||||||
|
</Card>
|
||||||
|
</Cards>
|
||||||
@@ -263,7 +263,7 @@ To automatically sync your secrets to a file, you may also use the [`syncSecrets
|
|||||||
|
|
||||||
## Syncing the database (`sync`)
|
## Syncing the database (`sync`)
|
||||||
|
|
||||||
Sync your database can be useful when running in [`code`](/usage/setup/#code-only-mode) mode. When you're ready to deploy, you can point to the production configuration and sync the database. Schema mutations are only applied when running with the `--force` option.
|
Sync your database can be useful when running in [`code`](/usage/introduction/#code-only-mode) mode. When you're ready to deploy, you can point to the production configuration and sync the database. Schema mutations are only applied when running with the `--force` option.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ npx bknd sync --help
|
$ npx bknd sync --help
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ const app = createApp({ connection });
|
|||||||
|
|
||||||
## Data Structure
|
## Data Structure
|
||||||
|
|
||||||
To provide a database structure, you can pass `config` to the creation of an app. In [`db` mode](/usage/setup#ui-only-mode), the data structure is only respected if the database is empty. If you made updates, ensure to delete the database first, or perform updates through the Admin UI.
|
To provide a database structure, you can pass `config` to the creation of an app. In [`db` mode](/usage/introduction#ui-only-mode), the data structure is only respected if the database is empty. If you made updates, ensure to delete the database first, or perform updates through the Admin UI.
|
||||||
|
|
||||||
Here is a quick example:
|
Here is a quick example:
|
||||||
|
|
||||||
@@ -506,9 +506,9 @@ const app = createApp({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that in [`db` mode](/usage/setup#ui-only-mode), the seed function will only be executed on app's first boot. If a configuration already exists in the database, it will not be executed.
|
Note that in [`db` mode](/usage/introduction#ui-only-mode), the seed function will only be executed on app's first boot. If a configuration already exists in the database, it will not be executed.
|
||||||
|
|
||||||
In [`code` mode](/usage/setup#code-only-mode), the seed function will not be automatically executed. You can manually execute it by running the following command:
|
In [`code` mode](/usage/introduction#code-only-mode), the seed function will not be automatically executed. You can manually execute it by running the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx bknd sync --seed --force
|
npx bknd sync --seed --force
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user