diff --git a/defradb_versioned_docs/version-0.19.0/getting-started.md b/defradb_versioned_docs/version-0.19.0/getting-started.md index 8312975..615fcbc 100644 --- a/defradb_versioned_docs/version-0.19.0/getting-started.md +++ b/defradb_versioned_docs/version-0.19.0/getting-started.md @@ -326,24 +326,25 @@ Pubsub peers can be specified on the command line using the `--peers` flag, whic Let's go through an example of two nodes (*nodeA* and *nodeB*) connecting with each other over pubsub, on the same machine. -Start *nodeA* with a default configuration: +In the first terminal, start *nodeA* with a default configuration: ```bash +DEFRA_KEYRING_SECRET= defradb start ``` -Obtain the node's peer info: +In a second terminal, obtain the node's peer info and save the ID: ```bash -defradb client p2p info +PEER_ID=$(defradb client p2p info 2>/dev/null | grep -oE '12D3[a-zA-Z0-9]+') +echo $PEER_ID ``` -In this example, we use `12D3KooWNXm3dmrwCYSxGoRUyZstaKYiHPdt8uZH5vgVaEJyzU8B`, but locally it will be different. - -For *nodeB*, we provide the following configuration: +In this second terminal, start *nodeB*, configured to connect with *nodeA*: ```bash -defradb start --rootdir ~/.defradb-nodeB --url localhost:9182 --p2paddr /ip4/127.0.0.1/tcp/9172 --peers /ip4/127.0.0.1/tcp/9171/p2p/12D3KooWNXm3dmrwCYSxGoRUyZstaKYiHPdt8uZH5vgVaEJyzU8B +DEFRA_KEYRING_SECRET= +defradb start --rootdir ~/.defradb-nodeB --url localhost:9182 --p2paddr /ip4/127.0.0.1/tcp/9172 --peers /ip4/127.0.0.1/tcp/9171/p2p/$PEER_ID ``` About the flags: @@ -359,18 +360,76 @@ This starts two nodes and connects them via pubsub networking.
Subscription example -It is possible to subscribe to updates on a given collection by using its ID as the pubsub topic. The ID of a collection is found as the field `collectionID` in one of its documents. Here we use the collection ID of the `User` type we created above. After setting up 2 nodes as shown in the [Pubsub example](#pubsub-example) section, we can subscribe to collections updates on *nodeA* from *nodeB* by using the following command: +It is possible to subscribe to updates on a given collection by using its Name as the pubsub topic. To see the available collections, run the following command: + +```bash +defradb client collection describe +``` + +Nodes need to know the collection schema before they can subscribe to updates. Add the User collection schema to *nodeB* with this command: + +```bash +defradb client schema add --url localhost:9182 ' + type User { + name: String + age: Int + verified: Boolean + points: Float + } +' +``` + + After setting up 2 nodes as shown in the [Pubsub example](#pubsub-example) section, we can subscribe to collections updates on *nodeA* from *nodeB* by using the following command: ```bash -defradb client p2p collection add --url localhost:9182 bafkreibpnvkvjqvg4skzlijka5xe63zeu74ivcjwd76q7yi65jdhwqhske +defradb client p2p collection add --url localhost:9182 User ``` -Multiple collection IDs can be added at once. +Multiple collections can be added at once. ```bash -defradb client p2p collection add --url localhost:9182 ,, +defradb client p2p collection add --url localhost:9182 ,, +``` + +Now let's add a new user to *nodeA*. + +```bash +defradb client query ' + mutation { + create_User(input: {age: 22, verified: true, points: 5, name: "Zara"}) { + _docID + } + } +' +``` + +You can now see this data published from *nodeA* to *nodeB*. + +```bash +defradb client query --url localhost:9182 ' + query { User { name age verified points } } +' +``` + +This should return the following: + +```json +------ Request Results ------ +{ + "data": { + "User": [ + { + "age": 22, + "name": "Zara", + "points": 5, + "verified": true + } + ] + } +} ``` +Note that *nodeB* is **passively** synchronizing with *nodeA*. *nodeB* will only receive new updates to the User collection. The previous users we setup on *nodeA* won't sync with *nodeB* unless we setup Replicator peering.
@@ -414,19 +473,57 @@ defradb client schema add --url localhost:9182 ' ' ``` -Then copy the peer info from *nodeB*: +Now add some data entries to *nodeA*. ```bash -defradb client p2p info --url localhost:9182 +defradb client query ' + mutation { + article1: create_Article(input: {content: "hello", published: true}) { + _docID + } + article2: create_Article(input: {content: "world", published: false}) { + _docID + } + } +' ``` -Set *nodeA* to actively replicate the Article collection to *nodeB*: +Then copy the peer info from *nodeB* and set *nodeA* to actively replicate the Article collection to *nodeB*: ```bash -defradb client p2p replicator set -c Article +NODE_B_PEER_INFO=$(defradb client p2p info --url localhost:9182 2>/dev/null | grep -oE '"/ip4/127\.0\.0\.1/tcp/[^"]+' | tr -d '"') +echo $NODE_B_PEER_INFO +defradb client p2p replicator set -c Article $NODE_B_PEER_INFO +``` + +Now all documents in the Article collection on *nodeA* will be actively pushed to *nodeB*. Verify this with the following command: + +```bash +defradb client query --url localhost:9182 ' + query { Article { content published } } +' +``` +You should see the following returned: + +```json +------ Request Results ------ +{ + "data": { + "Article": [ + { + "content": "world", + "published": false + }, + { + "content": "hello", + "published": true + } + ] + } +} ``` -As we add or update documents in the Article collection on *nodeA*, they will be actively pushed to *nodeB*. Note that changes to *nodeB* will still be passively published back to *nodeA*, via pubsub. +All future documents in the Article collection added to *nodeA* will be actively pushed to *nodeB*.
## Securing the HTTP API with TLS diff --git a/docs/defradb/getting-started.md b/docs/defradb/getting-started.mdx similarity index 70% rename from docs/defradb/getting-started.md rename to docs/defradb/getting-started.mdx index 8312975..11ba446 100644 --- a/docs/defradb/getting-started.md +++ b/docs/defradb/getting-started.mdx @@ -4,13 +4,17 @@ title: Getting Started slug: / --- +import { RunButton } from '@site/src/components/MiniPlayground'; + # DefraDB Overview ![DefraDB Overview](/img/defradb-cover.png) DefraDB is an application-centric database that prioritizes data ownership, personal privacy, and information security. Its data model, powered by the convergence of [MerkleCRDTs](https://arxiv.org/pdf/2004.00107.pdf) and the content-addressability of [IPLD](https://docs.ipld.io/), enables a multi-write-master architecture. It features [DQL](./references/query-specification/query-language-overview.md), a query language compatible with GraphQL but providing extra convenience. By leveraging peer-to-peer infrastructure, it can be deployed nimbly in novel topologies. Access control is determined by a relationship-based DSL, supporting document or field-level policies, secured by the SourceHub infrastructure. DefraDB is a core part of the [Source technologies](https://source.network/) that enable new paradigms of local-first software, edge compute, access-control management, application-centric features, data trustworthiness, and much more. -Disclaimer: At this early stage, DefraDB does not offer data encryption, and the default configuration exposes the database to the infrastructure. The software is provided "as is" and is not guaranteed to be stable, secure, or error-free. We encourage you to experiment with DefraDB and provide feedback, but please do not use it for production purposes until it has been thoroughly tested and developed. +:::warning Disclaimer +At this early stage, DefraDB does not offer data encryption, and the default configuration exposes the database to the infrastructure. The software is provided "as is" and is not guaranteed to be stable, secure, or error-free. We encourage you to experiment with DefraDB and provide feedback, but please do not use it for production purposes until it has been thoroughly tested and developed. +::: ## Install @@ -32,7 +36,7 @@ We recommend experimenting with queries using a native GraphQL client. [GraphiQL ## Key Management - Initial Setup -DefraDB has a built-in keyring for storing private keys securely. Keys are loaded at startup, and a secret must be provided via the `DEFRA_KEYRING_SECRET` environment variable. The following keys are loaded from the keyring on start: +DefraDB has a built-in keyring for storing private keys securely. Keys are loaded at startup, and **a secret must be provided via the `DEFRA_KEYRING_SECRET` environment variable**. The following keys are loaded from the keyring on start: - `peer-key` Ed25519 private key (required) - `encryption-key` AES-128, AES-192, or AES-256 key (optional) @@ -59,7 +63,9 @@ To learn more about the available options: defradb keyring --help ``` -NOTE: Node identity is an identity assigned to the node. It is used to exchange encryption keys with other nodes. +:::note +Node identity is an identity assigned to the node. It is used to exchange encryption keys with other nodes. +::: ## Start @@ -84,12 +90,23 @@ DefraDB uses a default configuration: The `client` command interacts with the locally running node. -The GraphQL endpoint can be used with a GraphQL client (e.g., Altair) to conveniently perform requests (`query`, `mutation`) and obtain schema introspection. Read more about [configuration options](./references/config.md). +The GraphQL endpoint can be used with a GraphQL client (e.g., Altair) to conveniently perform requests (`query`, `mutation`) and obtain schema introspection. Read more about [configuration options](./references/config.md). ## Add a schema type Define and add a schema type. + + ```bash defradb client schema add ' type User { @@ -107,6 +124,22 @@ For more examples of schema type definitions, see the [examples/schema/](example Submit a `mutation` request to create documents of the `User` type: + + ```bash defradb client query ' mutation { @@ -153,6 +186,19 @@ Expected response (_docID will be different): Once you have populated your node with data, you can query it: + + ```bash defradb client query ' query { @@ -170,6 +216,19 @@ This query obtains *all* users and returns their fields `_docID, age, name, poin You can further filter results with the `filter` argument. + + ```bash defradb client query ' query { @@ -275,7 +334,8 @@ FIRST_CID=$(defradb client query " echo "The first CID is: $FIRST_CID" ``` -to obtain the specific commit from this content identifier: + +To obtain the specific commit from this content identifier: ```bash defradb client query " @@ -326,24 +386,25 @@ Pubsub peers can be specified on the command line using the `--peers` flag, whic Let's go through an example of two nodes (*nodeA* and *nodeB*) connecting with each other over pubsub, on the same machine. -Start *nodeA* with a default configuration: +In the first terminal, start *nodeA* with a default configuration: ```bash +DEFRA_KEYRING_SECRET= defradb start ``` -Obtain the node's peer info: +In a second terminal, obtain the node's peer info and save the ID: ```bash -defradb client p2p info +PEER_ID=$(defradb client p2p info 2>/dev/null | grep -oE '12D3[a-zA-Z0-9]+') +echo $PEER_ID ``` -In this example, we use `12D3KooWNXm3dmrwCYSxGoRUyZstaKYiHPdt8uZH5vgVaEJyzU8B`, but locally it will be different. - -For *nodeB*, we provide the following configuration: +In this second terminal, start *nodeB*, configured to connect with *nodeA*: ```bash -defradb start --rootdir ~/.defradb-nodeB --url localhost:9182 --p2paddr /ip4/127.0.0.1/tcp/9172 --peers /ip4/127.0.0.1/tcp/9171/p2p/12D3KooWNXm3dmrwCYSxGoRUyZstaKYiHPdt8uZH5vgVaEJyzU8B +DEFRA_KEYRING_SECRET= +defradb start --rootdir ~/.defradb-nodeB --url localhost:9182 --p2paddr /ip4/127.0.0.1/tcp/9172 --peers /ip4/127.0.0.1/tcp/9171/p2p/$PEER_ID ``` About the flags: @@ -359,18 +420,76 @@ This starts two nodes and connects them via pubsub networking.
Subscription example -It is possible to subscribe to updates on a given collection by using its ID as the pubsub topic. The ID of a collection is found as the field `collectionID` in one of its documents. Here we use the collection ID of the `User` type we created above. After setting up 2 nodes as shown in the [Pubsub example](#pubsub-example) section, we can subscribe to collections updates on *nodeA* from *nodeB* by using the following command: +It is possible to subscribe to updates on a given collection by using its Name as the pubsub topic. To see the available collections, run the following command: ```bash -defradb client p2p collection add --url localhost:9182 bafkreibpnvkvjqvg4skzlijka5xe63zeu74ivcjwd76q7yi65jdhwqhske +defradb client collection describe ``` -Multiple collection IDs can be added at once. +Nodes need to know the collection schema before they can subscribe to updates. Add the User collection schema to *nodeB* with this command: ```bash -defradb client p2p collection add --url localhost:9182 ,, +defradb client schema add --url localhost:9182 ' + type User { + name: String + age: Int + verified: Boolean + points: Float + } +' +``` + +After setting up 2 nodes as shown in the [Pubsub example](#pubsub-example) section, we can subscribe to collections updates on *nodeA* from *nodeB* by using the following command: + +```bash +defradb client p2p collection add --url localhost:9182 User +``` + +Multiple collections can be added at once. + +```bash +defradb client p2p collection add --url localhost:9182 ,, +``` + +Now let's add a new user to *nodeA*. + +```bash +defradb client query ' + mutation { + create_User(input: {age: 22, verified: true, points: 5, name: "Zara"}) { + _docID + } + } +' +``` + +You can now see this data published from *nodeA* to *nodeB*. + +```bash +defradb client query --url localhost:9182 ' + query { User { name age verified points } } +' ``` +This should return the following: + +```json +------ Request Results ------ +{ + "data": { + "User": [ + { + "age": 22, + "name": "Zara", + "points": 5, + "verified": true + } + ] + } +} +``` + +Note that *nodeB* is **passively** synchronizing with *nodeA*. *nodeB* will only receive new updates to the User collection. The previous users we setup on *nodeA* won't sync with *nodeB* unless we setup Replicator peering.
@@ -386,6 +505,15 @@ defradb start In another terminal, add this example schema to it: + + ```bash defradb client schema add ' type Article { @@ -414,19 +542,82 @@ defradb client schema add --url localhost:9182 ' ' ``` -Then copy the peer info from *nodeB*: +Now add some data entries to *nodeA*. + + + +```bash +defradb client query ' + mutation { + article1: create_Article(input: {content: "hello", published: true}) { + _docID + } + article2: create_Article(input: {content: "world", published: false}) { + _docID + } + } +' +``` + +Then copy the peer info from *nodeB* and set *nodeA* to actively replicate the Article collection to *nodeB*: ```bash -defradb client p2p info --url localhost:9182 +NODE_B_PEER_INFO=$(defradb client p2p info --url localhost:9182 2>/dev/null | grep -oE '"/ip4/127\.0\.0\.1/tcp/[^"]+' | tr -d '"') +echo $NODE_B_PEER_INFO +defradb client p2p replicator set -c Article $NODE_B_PEER_INFO ``` -Set *nodeA* to actively replicate the Article collection to *nodeB*: +Now all documents in the Article collection on *nodeA* will be actively pushed to *nodeB*. Verify this with the following command: + + ```bash -defradb client p2p replicator set -c Article +defradb client query --url localhost:9182 ' + query { Article { content published } } +' ``` -As we add or update documents in the Article collection on *nodeA*, they will be actively pushed to *nodeB*. Note that changes to *nodeB* will still be passively published back to *nodeA*, via pubsub. +You should see the following returned: + +```json +------ Request Results ------ +{ + "data": { + "Article": [ + { + "content": "world", + "published": false + }, + { + "content": "hello", + "published": true + } + ] + } +} +``` + +All future documents in the Article collection added to *nodeA* will be actively pushed to *nodeB*.
## Securing the HTTP API with TLS @@ -450,7 +641,6 @@ Then to start the server with TLS, using your generated keys in custom path: ```bash defradb start --tls --pubkeypath ~/path-to-pubkey.key --privkeypath ~/path-to-privkey.crt - ``` ## Access Control System @@ -505,4 +695,4 @@ defradb client backup import path/to/backup.json ## Conclusion -This gets you started to use DefraDB. Read on the documentation website for guides and further information. +This gets you started to use DefraDB. Read on the documentation website for guides and further information. \ No newline at end of file diff --git a/docs/defradb/guides/dual-wasm-playground.mdx b/docs/defradb/guides/dual-wasm-playground.mdx new file mode 100644 index 0000000..8e6f1a2 --- /dev/null +++ b/docs/defradb/guides/dual-wasm-playground.mdx @@ -0,0 +1,424 @@ +--- +title: Dual DefraDB WASM Playground +description: Run two independent DefraDB nodes simultaneously in your browser using WebAssembly +sidebar_position: 3 +--- + +import RealDualDefraDBPlayground from '@site/src/components/RealDualDefraDBPlayground'; + +# Dual DefraDB WASM Playground + +Experience the power of running **two independent DefraDB database instances** simultaneously in your browser using WebAssembly technology. No backend servers, no installation required. + +## 🎯 What You'll Experience + +This interactive playground demonstrates: + +- **Browser-Native Databases**: Two fully functional DefraDB instances running entirely client-side +- **Complete Isolation**: Each node maintains separate storage and executes queries independently +- **Real WASM Implementation**: Actual `defradb.wasm` binary compilation and execution +- **Persistent vs Ephemeral Storage**: Node 1 uses IndexedDB (survives browser restart), Node 2 uses in-memory storage +- **GraphQL Interface**: Full query and mutation support on both nodes + +## 🚀 Try It Now + + + +## 💡 Understanding the Architecture + +### Dual-Node Setup + +``` +┌─────────────────────────────────────────────────────┐ +│ Your Browser │ +│ │ +│ ┌────────────────────┐ ┌────────────────────┐ │ +│ │ DefraDB Node 1 │ │ DefraDB Node 2 │ │ +│ │ │ │ │ │ +│ │ WASM Instance 1 │ │ WASM Instance 2 │ │ +│ │ Go Runtime 1 │ │ Go Runtime 2 │ │ +│ │ │ │ │ │ +│ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │ +│ │ │ IndexedDB │ │ │ │ In-Memory │ │ │ +│ │ │ (Persistent)│ │ │ │ (Temporary) │ │ │ +│ │ └──────────────┘ │ │ └──────────────┘ │ │ +│ └────────────────────┘ └────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────┘ +``` + +### Key Technical Features + +**Separate WebAssembly Instances** +- Each node runs in its own WASM module instance +- Complete memory isolation between nodes +- Independent Go runtime environments + +**Storage Isolation** +- **Node 1 (Blue)**: Uses IndexedDB with database name `defradb_node1` + - Data persists across browser sessions + - Survives page reloads + - Suitable for development workflows + +- **Node 2 (Green)**: Uses in-memory storage + - Fastest performance + - Data lost on page reload + - Perfect for testing and demos + +**Independent Operation** +- Schemas defined in Node 1 don't affect Node 2 +- Documents created in one node remain isolated +- Queries execute independently without interference + +## 📚 How to Use This Playground + +### Step 1: Wait for Initialization +Watch the status indicators turn from "INITIALIZING" to "READY". This typically takes 2-5 seconds as the WASM modules load and initialize. + +### Step 2: Add Schemas +Click the **"📋 Add Schema"** button on each node to define a `User` type with fields for `name`, `age`, and `email`. + +### Step 3: Create Sample Data +Click **"➕ Add Sample Doc"** to create a test user document in each database. + +### Step 4: Execute Queries +Use the GraphQL query editors to retrieve data: + +```graphql +query { + User { + _docID + name + age + email + } +} +``` + +### Step 5: Create Custom Data +Try creating your own documents with mutations: + +```graphql +mutation { + create_User(input: { + name: "Bob Smith" + age: 35 + email: "bob@example.com" + }) { + _docID + name + age + email + } +} +``` + +### Step 6: Observe Isolation +Notice that data created in Node 1 doesn't appear in Node 2, and vice versa. Each maintains completely separate databases. + +## 🎓 Use Cases + +### Interactive Documentation +Embed live, working examples directly in documentation pages. Users can experiment without leaving the page or setting up infrastructure. + +### Educational Demonstrations +- Teach database concepts with hands-on examples +- Show distributed database principles in action +- No installation barriers for students + +### Prototyping & Testing +- Rapidly prototype database schemas +- Test queries and mutations in a sandbox +- Experiment with multi-node scenarios + +### Development Workflows +- Test schema changes before deploying +- Debug GraphQL queries interactively +- Validate data models with real DefraDB + +## 🔧 Technical Implementation + +### Prerequisites + +To run this playground, the following files must be present in your `/static` directory: + +1. **defradb.wasm** - The compiled DefraDB WebAssembly binary +2. **wasm_exec.js** - Go's WebAssembly runtime helper + +### Building from Source + +If you want to build the WASM binary yourself: + +```bash +# Clone the DefraDB Playground repository +git clone https://github.com/sourcenetwork/defradb-playground.git +cd defradb-playground + +# Build the WASM binary +GOOS=js GOARCH=wasm go build -o defradb.wasm ./cmd/defradb + +# Copy to your docs static directory +cp defradb.wasm /path/to/docs/static/ + +# Copy Go's WASM runtime +cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" /path/to/docs/static/ +``` + +### Component Integration + +The playground is built as a React component that can be embedded in any Docusaurus MDX page: + +```mdx +--- +title: Your Page +--- + +import RealDualDefraDBPlayground from '@site/src/components/RealDualDefraDBPlayground'; + +# Your Content + + +``` + +## 🌐 Browser Compatibility + +### Supported Browsers + +| Browser | Minimum Version | Status | +|---------|----------------|--------| +| Chrome/Edge | 87+ | ✅ Fully Supported | +| Firefox | 89+ | ✅ Fully Supported | +| Safari | 14+ | ✅ Fully Supported | +| Mobile Chrome | Latest | ✅ Works (performance varies) | +| Mobile Safari | iOS 14+ | ✅ Works (performance varies) | + +### Requirements + +Your browser must support: +- ✅ **WebAssembly** - For running the DefraDB binary +- ✅ **IndexedDB** - For persistent storage (Node 1) +- ✅ **ES6+ JavaScript** - Modern JavaScript features +- ✅ **Fetch API** - For loading WASM files + +## ⚡ Performance Considerations + +### Desktop Performance +- **Initial Load**: 2-5 seconds for WASM compilation +- **Query Execution**: < 100ms for typical queries +- **Memory Usage**: 50-200MB per instance +- **Recommended RAM**: 4GB+ for smooth operation + +### Mobile Performance +- **Initial Load**: 5-10 seconds on mid-range devices +- **Query Execution**: 100-500ms depending on device +- **Memory Usage**: Can cause issues on older devices +- **Battery Impact**: Moderate during intensive operations + +### Optimization Tips +- Preload WASM binary with service workers +- Use in-memory storage for better performance in demos +- Limit concurrent operations on mobile +- Clear browser data if experiencing issues + +## 🔐 Security & Privacy + +### Sandbox Environment +- All data stays in your browser - nothing sent to servers +- WASM runs in browser security sandbox +- No network requests after initial load +- Complete data isolation between nodes + +### Data Storage +- **IndexedDB data is not encrypted by default** +- Data persists only in your browser's storage +- Clear browser data to completely remove all information +- Not suitable for sensitive production data + +### CORS & CSP +- No CORS issues - everything runs client-side +- May need CSP header: `script-src 'self' 'wasm-unsafe-eval'` + +## 🚧 Current Limitations + +### Known Constraints +- **No P2P Synchronization**: Nodes don't communicate with each other (by design for this demo) +- **Browser Storage Limits**: IndexedDB typically limited to ~50MB +- **Single Tab**: Data doesn't sync across browser tabs +- **Memory Intensive**: Each instance requires significant RAM +- **No Persistence in Node 2**: In-memory storage lost on reload + +### Not Yet Supported +- Cross-node replication +- Distributed queries +- Access control policies (ACP) - coming soon +- Multi-tab synchronization +- Export/import functionality + +## 🎯 Advanced Examples + +### Complex Queries + +Query with filters: +```graphql +query { + User(filter: { age: { _gt: 25 } }) { + _docID + name + age + } +} +``` + +### Nested Types + +Define relationships: +```graphql +type User { + name: String + posts: [Post] +} + +type Post { + title: String + author: User +} +``` + +### Batch Operations + +Create multiple documents: +```graphql +mutation { + user1: create_User(input: { name: "Alice", age: 30 }) { + _docID + } + user2: create_User(input: { name: "Bob", age: 25 }) { + _docID + } +} +``` + +## 📊 Monitoring & Debugging + +### Log Panel +Each node has a real-time log panel showing: +- WASM initialization progress +- Schema operations +- Query execution +- Error messages with stack traces +- Timing information + +### Browser DevTools +Access advanced debugging via browser console: + +```javascript +// Access Node 1 client +window.defradbClient1.execRequest(`query { User { name } }`) + +// Access Node 2 client +window.defradbClient2.execRequest(`query { User { name } }`) +``` + +### Performance Profiling +Use Chrome DevTools to monitor: +- Memory usage +- CPU consumption +- Network requests +- WebAssembly compilation time + +## 🔮 Future Enhancements + +Potential features in development: + +- [ ] Node-to-node replication demo +- [ ] Access Control Policy (ACP) support +- [ ] Visual query builder +- [ ] Schema migration tools +- [ ] Performance monitoring dashboard +- [ ] Multi-tab state synchronization +- [ ] Export/import database snapshots +- [ ] Integration with remote DefraDB nodes + +## 🆘 Troubleshooting + +### WASM Fails to Load + +**Symptoms**: Error message about missing WASM binary + +**Solutions**: +1. Verify `defradb.wasm` is in `/static` directory +2. Check browser console for 404 errors +3. Rebuild WASM binary with correct Go version +4. Clear browser cache and reload + +### Go Runtime Error + +**Symptoms**: "Go runtime not available" message + +**Solutions**: +1. Verify `wasm_exec.js` is in `/static` directory +2. Ensure Go version matches WASM build +3. Check browser console for JavaScript errors +4. Try a different browser + +### Client Initialization Timeout + +**Symptoms**: Stuck on "INITIALIZING" status + +**Solutions**: +1. Wait up to 10 seconds for initialization +2. Check browser console for errors +3. Verify adequate RAM available +4. Close other browser tabs +5. Refresh the page + +### Queries Not Executing + +**Symptoms**: Queries return errors or no results + +**Solutions**: +1. Ensure schema was added first +2. Check query syntax in GraphQL +3. Verify node status is "READY" +4. Review error messages in log panel +5. Try the sample queries first + +### Performance Issues + +**Symptoms**: Slow query execution or UI lag + +**Solutions**: +1. Use in-memory storage instead of IndexedDB +2. Reduce query complexity +3. Close unused browser tabs +4. Clear browser data and restart +5. Try on a more powerful device + +## 📖 Additional Resources + +### DefraDB Documentation +- [DefraDB GitHub Repository](https://github.com/sourcenetwork/defradb) +- [DefraDB Playground Source](https://github.com/sourcenetwork/defradb-playground) +- [GraphQL Documentation](https://graphql.org/learn/) + +### WebAssembly Resources +- [WebAssembly Official Site](https://webassembly.org/) +- [Go WebAssembly Guide](https://github.com/golang/go/wiki/WebAssembly) +- [MDN WebAssembly Docs](https://developer.mozilla.org/en-US/docs/WebAssembly) + +### Community & Support +- [Source Network Discord](https://discord.gg/sourcenetwork) +- [DefraDB Discussions](https://github.com/sourcenetwork/defradb/discussions) +- [Report Issues](https://github.com/sourcenetwork/defradb/issues) + +## 💬 Feedback + +This is an experimental feature showcasing DefraDB's capabilities in the browser. We'd love to hear your thoughts: + +- **Found a bug?** [Open an issue](https://github.com/sourcenetwork/defradb/issues) +- **Have an idea?** [Start a discussion](https://github.com/sourcenetwork/defradb/discussions) +- **Want to contribute?** [Check our contributing guide](https://github.com/sourcenetwork/defradb/blob/develop/CONTRIBUTING.md) + +--- + +**Note**: This playground demonstrates DefraDB's WebAssembly capabilities for educational and development purposes. For production deployments, use the standard DefraDB server installation. \ No newline at end of file diff --git a/docusaurus.config.js b/docusaurus.config.js index 608eb42..5c69cb5 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -169,6 +169,57 @@ const config = { }, }, ], + // Custom webpack configuration for browser-only packages + function customWebpackPlugin(context, options) { + return { + name: 'custom-webpack-config', + configureWebpack(config, isServer, utils) { + return { + resolve: { + fallback: isServer ? {} : { + // Browser fallbacks for Node.js modules + crypto: require.resolve('crypto-browserify'), + stream: require.resolve('stream-browserify'), + buffer: require.resolve('buffer/'), + process: require.resolve('process/browser'), + vm: false, + fs: false, + path: false, + }, + alias: { + // Ensure process is available + process: 'process/browser', + }, + }, + plugins: isServer ? [] : [ + new (require('webpack')).ProvidePlugin({ + process: 'process/browser', + Buffer: ['buffer', 'Buffer'], + }), + ], + module: { + rules: [ + { + test: /\.m?js$/, + resolve: { + fullySpecified: false, // Disable the behavior for .mjs files + }, + }, + ], + }, + // Mark problematic packages as external during SSR + externals: isServer ? [ + '@sourcenetwork/acp-js', + '@sourcenetwork/hublet', + 'multiformats', + 'uint8arrays', + '@noble/hashes', + '@noble/curves', + ] : {}, + }; + }, + }; + }, // DefraDB instance [ "@docusaurus/plugin-content-docs", @@ -311,4 +362,4 @@ function reverseSidebarChangelog(items) { return item; }); return result; -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7e73950..9e675d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,15 +11,21 @@ "@docusaurus/core": "3.9.0", "@docusaurus/preset-classic": "3.9.0", "@mdx-js/react": "^3.0.0", + "@sourcenetwork/acp-js": "^1.0.4", + "@sourcenetwork/hublet": "^1.0.2", "@svgr/webpack": "^7.0.0", + "buffer": "^6.0.3", "clsx": "^1.2.1", + "crypto-browserify": "^3.12.0", "docusaurus-plugin-sass": "^0.2.5", "docusaurus-preset-openapi": "^0.7.1", "prism-react-renderer": "^2.1.0", + "process": "^0.11.10", "react": "^18.0.0", "react-dom": "^18.0.0", "react-icons": "^4.8.0", "sass": "^1.60.0", + "stream-browserify": "^3.0.0", "url": "^0.11.3" }, "devDependencies": { @@ -29,6 +35,9 @@ "simple-git": "^3.17.0", "tmp": "^0.2.1", "typescript": "^5.0.2" + }, + "engines": { + "node": ">=20.0.0" } }, "node_modules/@ai-sdk/gateway": { @@ -2034,6 +2043,12 @@ "node": ">=6.9.0" } }, + "node_modules/@bufbuild/protobuf": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.10.1.tgz", + "integrity": "sha512-ckS3+vyJb5qGpEYv/s1OebUHDi/xSNtfgw1wqKZo7MR9F2z+qXr0q5XagafAG/9O0QPVIUfST0smluYSTpYFkg==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -2044,6 +2059,23 @@ "node": ">=0.1.90" } }, + "node_modules/@cosmjs/encoding": { + "version": "0.33.1", + "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.33.1.tgz", + "integrity": "sha512-nuNxf29fUcQE14+1p//VVQDwd1iau5lhaW/7uMz7V2AH3GJbFJoJVaKvVyZvdFk+Cnu+s3wCqgq4gJkhRCJfKw==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "bech32": "^1.1.4", + "readonly-date": "^1.0.0" + } + }, + "node_modules/@cosmjs/encoding/node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "license": "MIT" + }, "node_modules/@csstools/cascade-layer-name-parser": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz", @@ -4242,6 +4274,18 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@improbable-eng/grpc-web": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.15.0.tgz", + "integrity": "sha512-ERft9/0/8CmYalqOVnJnpdDry28q+j+nAlFFARdjyxXDJ+Mhgv9+F600QC8BR9ygOfrXRlAk6CvST2j+JCpQPg==", + "license": "Apache-2.0", + "dependencies": { + "browser-headers": "^0.4.1" + }, + "peerDependencies": { + "google-protobuf": "^3.14.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -4558,6 +4602,21 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@noble/curves": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.0.tgz", + "integrity": "sha512-7YDlXiNMdO1YZeH6t/kvopHHbIZzlxrCV9WLqCY6QhcXOoXiNCMDqJIglZ9Yjx5+w7Dz30TITFrlTjnRg7sKEg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", @@ -5051,6 +5110,33 @@ "micromark-util-symbol": "^1.0.1" } }, + "node_modules/@sourcenetwork/acp-js": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@sourcenetwork/acp-js/-/acp-js-1.0.4.tgz", + "integrity": "sha512-2CGEB+M0yif+OITfSJVjDykYNemTNPR8ApJ0Zccmx2UFKS5K0iFT1GakCj2k6VhHhqo1/3Q7mqjgHRJYJyb4DQ==", + "license": "Apache-2.0", + "dependencies": { + "@sourcenetwork/hublet": "1.0.2" + } + }, + "node_modules/@sourcenetwork/hublet": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@sourcenetwork/hublet/-/hublet-1.0.2.tgz", + "integrity": "sha512-kTEHqIBag8V7r1v9mK6CdnbDrlyCk5R5y4p0nyvoGCYWirrk6l6Mx1gFfCA+AwJPg69QeX4+PzbmX4Wy4pJInA==", + "license": "Apache-2.0", + "dependencies": { + "@cosmjs/encoding": "0.33.1", + "@improbable-eng/grpc-web": "^0.15.0", + "@noble/curves": "1.9.0", + "@noble/hashes": "^1.8.0", + "bech32": "^2.0.0", + "bitcoinjs-lib": "^6.1.7", + "cross-fetch": "^4.1.0", + "multiformats": "^13.1.0", + "ts-proto": "^2.7.0", + "varint": "^6.0.0" + } + }, "node_modules/@standard-schema/spec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", @@ -6602,6 +6688,23 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "license": "MIT" }, + "node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" + }, "node_modules/astring": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", @@ -6660,6 +6763,21 @@ "postcss": "^8.1.0" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/babel-loader": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", @@ -6750,6 +6868,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base-x": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.1.tgz", + "integrity": "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==", + "license": "MIT" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -6785,6 +6909,12 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "license": "MIT" }, + "node_modules/bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==", + "license": "MIT" + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -6806,6 +6936,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bip174": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-2.1.1.tgz", + "integrity": "sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bitcoinjs-lib": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.7.tgz", + "integrity": "sha512-tlf/r2DGMbF7ky1MgUqXHzypYHakkEnm0SZP23CJKIqNY/5uNAnMbFhMJdhjrL/7anfb/U8+AlpdjPWjPnAalg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bech32": "^2.0.0", + "bip174": "^2.1.1", + "bs58check": "^3.0.1", + "typeforce": "^1.11.3", + "varuint-bitcoin": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bn.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", + "license": "MIT" + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -6941,6 +7103,131 @@ "node": ">=8" } }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" + }, + "node_modules/browser-headers": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz", + "integrity": "sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==", + "license": "Apache-2.0" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "license": "MIT", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "license": "MIT", + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/browserify-rsa": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.1.tgz", + "integrity": "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==", + "license": "MIT", + "dependencies": { + "bn.js": "^5.2.1", + "randombytes": "^2.1.0", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.5.tgz", + "integrity": "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==", + "license": "ISC", + "dependencies": { + "bn.js": "^5.2.2", + "browserify-rsa": "^4.1.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.6.1", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.9", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/browserify-sign/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/browserify-sign/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/browserify-sign/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/browserify-sign/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/browserify-sign/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/browserslist": { "version": "4.26.3", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", @@ -6974,6 +7261,25 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "license": "MIT", + "dependencies": { + "base-x": "^4.0.0" + } + }, + "node_modules/bs58check": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz", + "integrity": "sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bs58": "^5.0.0" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -7004,6 +7310,12 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "license": "MIT" + }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -7171,6 +7483,18 @@ ], "license": "CC-BY-4.0" }, + "node_modules/case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -7341,6 +7665,20 @@ "node": ">=8" } }, + "node_modules/cipher-base": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz", + "integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -7942,6 +8280,58 @@ } } }, + "node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/cross-fetch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", + "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -7956,6 +8346,32 @@ "node": ">= 8" } }, + "node_modules/crypto-browserify": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.1.tgz", + "integrity": "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ==", + "license": "MIT", + "dependencies": { + "browserify-cipher": "^1.0.1", + "browserify-sign": "^4.2.3", + "create-ecdh": "^4.0.4", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "diffie-hellman": "^5.0.3", + "hash-base": "~3.0.4", + "inherits": "^2.0.4", + "pbkdf2": "^3.1.2", + "public-encrypt": "^4.0.3", + "randombytes": "^2.1.0", + "randomfill": "^1.0.4" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/crypto-js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", @@ -8595,6 +9011,16 @@ "node": ">=6" } }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -8610,7 +9036,6 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "license": "Apache-2.0", - "optional": true, "bin": { "detect-libc": "bin/detect-libc.js" }, @@ -8676,6 +9101,23 @@ "wrappy": "1" } }, + "node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -8917,6 +9359,15 @@ "node": ">=8" } }, + "node_modules/dprint-node": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.8.tgz", + "integrity": "sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==", + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -8955,6 +9406,27 @@ "integrity": "sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==", "license": "ISC" }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -9382,6 +9854,16 @@ "node": ">=18.0.0" } }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "license": "MIT", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -9814,6 +10296,21 @@ } } }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/foreach": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", @@ -10150,6 +10647,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/google-protobuf": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", + "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==", + "license": "(BSD-3-Clause AND Apache-2.0)", + "peer": true + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -10332,6 +10836,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hash-base": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.5.tgz", + "integrity": "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -10609,6 +11136,17 @@ "value-equal": "^1.0.1" } }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -11166,6 +11704,18 @@ "node": ">=8" } }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-ci": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", @@ -11412,6 +11962,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -11970,6 +12535,17 @@ "node": ">= 0.4" } }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "node_modules/mdast-util-directive": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mdast-util-directive/-/mdast-util-directive-3.1.0.tgz", @@ -14235,6 +14811,25 @@ "node": ">=8.6" } }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" + }, "node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -14324,6 +14919,12 @@ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "license": "ISC" }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -14393,6 +14994,12 @@ "multicast-dns": "cli.js" } }, + "node_modules/multiformats": { + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.4.1.tgz", + "integrity": "sha512-VqO6OSvLrFVAYYjgsr8tyv62/rCQhPgsZUXLTqoFLSgdkgiUYKYeArbt1uWLlEpkjxQe+P0+sHlbPEte1Bi06Q==", + "license": "Apache-2.0 OR MIT" + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -15076,6 +15683,22 @@ "node": ">=6" } }, + "node_modules/parse-asn1": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.9.tgz", + "integrity": "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==", + "license": "ISC", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "pbkdf2": "^3.1.5", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", @@ -15288,6 +15911,23 @@ "node": ">=8" } }, + "node_modules/pbkdf2": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz", + "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==", + "license": "MIT", + "dependencies": { + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "ripemd160": "^2.0.3", + "safe-buffer": "^5.2.1", + "sha.js": "^2.4.12", + "to-buffer": "^1.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -15321,6 +15961,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -16977,6 +17626,26 @@ "node": ">= 0.10" } }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -17057,6 +17726,16 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "license": "MIT", + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -17351,6 +18030,12 @@ "node": ">=8.10.0" } }, + "node_modules/readonly-date": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz", + "integrity": "sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==", + "license": "Apache-2.0" + }, "node_modules/rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -17987,6 +18672,76 @@ "node": ">=0.10.0" } }, + "node_modules/ripemd160": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz", + "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==", + "license": "MIT", + "dependencies": { + "hash-base": "^3.1.2", + "inherits": "^2.0.4" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ripemd160/node_modules/hash-base": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz", + "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^2.3.8", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ripemd160/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/ripemd160/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/ripemd160/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/ripemd160/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/ripemd160/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/rtlcss": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", @@ -18518,6 +19273,26 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -18989,6 +19764,16 @@ "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "license": "MIT" }, + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -19490,6 +20275,26 @@ "node": ">=14.14" } }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-buffer/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -19562,6 +20367,39 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-poet": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-6.12.0.tgz", + "integrity": "sha512-xo+iRNMWqyvXpFTaOAvLPA5QAWO6TZrSUs5s4Odaya3epqofBu/fMLHEWl8jPmjhA0s9sgj9sNvF1BmaQlmQkA==", + "license": "Apache-2.0", + "dependencies": { + "dprint-node": "^1.0.8" + } + }, + "node_modules/ts-proto": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/ts-proto/-/ts-proto-2.8.3.tgz", + "integrity": "sha512-TdXInqG+61pj/TvORqITWjvjTTsL1EZxwX49iEj89+xFAcqPT8tjChpAGQXzfcF4MJwvNiuoCEbBOKqVf3ds3g==", + "license": "ISC", + "dependencies": { + "@bufbuild/protobuf": "^2.0.0", + "case-anything": "^2.1.13", + "ts-poet": "^6.12.0", + "ts-proto-descriptors": "2.0.0" + }, + "bin": { + "protoc-gen-ts_proto": "protoc-gen-ts_proto" + } + }, + "node_modules/ts-proto-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-2.0.0.tgz", + "integrity": "sha512-wHcTH3xIv11jxgkX5OyCSFfw27agpInAd6yh89hKG6zqIXnjW9SYqSER2CVQxdPj4czeOhGagNvZBEbJPy7qkw==", + "license": "ISC", + "dependencies": { + "@bufbuild/protobuf": "^2.0.0" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -19593,6 +20431,20 @@ "node": ">= 0.6" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", @@ -19602,6 +20454,12 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==", + "license": "MIT" + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -20114,6 +20972,21 @@ "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==", "license": "MIT" }, + "node_modules/varint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", + "license": "MIT" + }, + "node_modules/varuint-bitcoin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz", + "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -20617,6 +21490,27 @@ "node": ">= 8" } }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", diff --git a/package.json b/package.json index 2c87ad0..14332e4 100644 --- a/package.json +++ b/package.json @@ -19,15 +19,21 @@ "@docusaurus/core": "3.9.0", "@docusaurus/preset-classic": "3.9.0", "@mdx-js/react": "^3.0.0", + "@sourcenetwork/acp-js": "^1.0.4", + "@sourcenetwork/hublet": "^1.0.2", "@svgr/webpack": "^7.0.0", + "buffer": "^6.0.3", "clsx": "^1.2.1", + "crypto-browserify": "^3.12.0", "docusaurus-plugin-sass": "^0.2.5", "docusaurus-preset-openapi": "^0.7.1", "prism-react-renderer": "^2.1.0", + "process": "^0.11.10", "react": "^18.0.0", "react-dom": "^18.0.0", "react-icons": "^4.8.0", "sass": "^1.60.0", + "stream-browserify": "^3.0.0", "url": "^0.11.3" }, "browserslist": { @@ -49,5 +55,8 @@ "simple-git": "^3.17.0", "tmp": "^0.2.1", "typescript": "^5.0.2" + }, + "engines": { + "node": ">=20.0.0" } -} +} \ No newline at end of file diff --git a/src/components/MiniPlayground.tsx b/src/components/MiniPlayground.tsx new file mode 100644 index 0000000..2446d34 --- /dev/null +++ b/src/components/MiniPlayground.tsx @@ -0,0 +1,437 @@ +import React, { useState, useEffect, useContext, createContext, useRef, useCallback } from 'react'; +import BrowserOnly from '@docusaurus/BrowserOnly'; + +/** + * Source Network Mini Playground + * A persistent, floating terminal for live documentation interaction. + * + * Based on the working RealDualDefraDBPlayground pattern. + */ + +// --- Theme Constants --- +const THEME = { + bg: '#050505', + surface: '#0A0A0A', + border: '#333333', + primary: '#00FF94', + error: '#FF3333', + warning: '#FFaa00', + fontMono: '"Fira Code", "Courier New", monospace', +}; + +// --- Types --- +declare global { + interface Window { + defradb?: { + open(acpType?: string): Promise; + }; + defradbClient?: any; + globalACPConfig?: { + apiUrl: string; + rpcUrl: string; + grpcUrl: string; + chainId: string; + denom: string; + useZeroFees: boolean; + }; + } +} + +// --- Context --- +interface PlaygroundContextType { + client: any; + status: 'initializing' | 'ready' | 'error'; + logs: LogEntry[]; + isOpen: boolean; + setIsOpen: (v: boolean) => void; + runCommand: (type: 'schema' | 'query', code: string, label?: string) => Promise; + clearLogs: () => void; + resetDatabase: () => void; +} + +interface LogEntry { + id: string; + type: 'input' | 'output' | 'error' | 'system'; + content: string; + timestamp: string; +} + +const PlaygroundContext = createContext(null); + +export const usePlayground = () => { + const ctx = useContext(PlaygroundContext); + if (!ctx) throw new Error('usePlayground must be used within DefraDBProvider'); + return ctx; +}; + +// --- Provider Component --- +export const DefraDBProvider = ({ children }: { children: React.ReactNode }) => { + const [client, setClient] = useState(null); + const [status, setStatus] = useState<'initializing' | 'ready' | 'error'>('initializing'); + const [logs, setLogs] = useState([]); + const [isOpen, setIsOpen] = useState(false); + const initializationStarted = useRef(false); + + const addLog = useCallback((type: LogEntry['type'], content: string) => { + setLogs(prev => [...prev, { + id: Math.random().toString(36).substr(2, 9), + type, + content, + timestamp: new Date().toLocaleTimeString([], { hour12: false }) + }]); + }, []); + + const clearLogs = useCallback(() => setLogs([]), []); + + const resetDatabase = useCallback(() => { + window.location.reload(); + }, []); + + useEffect(() => { + // Prevent double initialization (same pattern as working component) + if (initializationStarted.current) return; + initializationStarted.current = true; + + const initializeDefraDB = async () => { + try { + addLog('system', '>> INITIALIZING KERNEL...'); + + // Setup Global Config (exact same as working component) + window.globalACPConfig = { + apiUrl: `${window.location.origin}/api`, + rpcUrl: `${window.location.origin}/rpc`, + grpcUrl: `${window.location.origin}/api`, + chainId: 'sourcehub-dev', + denom: 'uopen', + useZeroFees: true, + }; + + // Import and instantiate (exact same pattern as working component) + addLog('system', '>> LOADING WASM MODULE...'); + const { instantiate } = await import('@sourcenetwork/acp-js'); + await instantiate('https://defradbwasm.source.network/defradb.wasm'); + + // Wait for window.defradb (exact same pattern as working component) + addLog('system', '>> WAITING FOR DEFRADB...'); + let attempts = 0; + while (!window.defradb && attempts < 100) { + await new Promise(resolve => setTimeout(resolve, 100)); + attempts++; + } + + if (!window.defradb) { + throw new Error('Timeout waiting for window.defradb'); + } + + // Open the database (exact same pattern as working component) + addLog('system', '>> OPENING DATABASE...'); + const dbClient = await window.defradb.open(); + if (!dbClient) { + throw new Error('Client open failed'); + } + + // Store globally for debugging + window.defradbClient = dbClient; + + setClient(dbClient); + setStatus('ready'); + addLog('system', '>> SYSTEM ONLINE. RUN COMMANDS FROM THE DOCS.'); + addLog('system', '>> TIP: Add schema first, then create documents, then query.'); + + } catch (error: any) { + console.error('DefraDB init error:', error); + setStatus('error'); + addLog('error', `INIT FAILURE: ${error.message}`); + addLog('system', '>> Click RESET to try again.'); + } + }; + + initializeDefraDB(); + }, [addLog]); + + const runCommand = useCallback(async (type: 'schema' | 'query', code: string, label?: string) => { + if (!client) { + addLog('error', 'Database not initialized. Please wait or click RESET.'); + setIsOpen(true); + return; + } + + setIsOpen(true); + addLog('input', `> ${label || (type === 'schema' ? 'ADD SCHEMA' : 'EXECUTE QUERY')}`); + + try { + let result; + if (type === 'schema') { + result = await client.addSchema(code); + addLog('output', result ? JSON.stringify(result, null, 2) : 'Schema added successfully.'); + } else { + // Use execRequest with empty objects for variables and context (same as working component) + result = await client.execRequest(code, {}, {}); + addLog('output', JSON.stringify(result, null, 2)); + } + } catch (err: any) { + const errorMsg = err.message || String(err); + addLog('error', errorMsg); + + // Detect if the Go runtime has crashed + if (errorMsg.includes('Go program has already exited') || + errorMsg.includes('cannot resume') || + errorMsg.includes('exports')) { + addLog('system', '>> WASM runtime error. Click RESET to restart.'); + setStatus('error'); + } + } + }, [client, addLog]); + + return ( + + {children} + + + ); +}; + +// --- The Floating Widget UI --- +const FloatingWidget = () => { + const { isOpen, setIsOpen, logs, status, clearLogs, resetDatabase } = usePlayground(); + const bottomRef = useRef(null); + + useEffect(() => { + if (isOpen && bottomRef.current) { + bottomRef.current.scrollIntoView({ behavior: 'smooth' }); + } + }, [logs, isOpen]); + + if (!isOpen) { + return ( + + ); + } + + return ( +
+ {/* Header */} +
+
+
+ + DEFRADB_LIVE + +
+
+ + + +
+
+ + {/* Logs Area */} +
+ {logs.length === 0 && ( +
+
+ {status === 'initializing' ? '⏳ Initializing...' : status === 'error' ? '❌ Error occurred' : '✓ Ready'} +
+
+ Click "Run" buttons in the docs to execute commands.
+ Follow the tutorial steps in order. +
+
+ )} + {logs.map((log) => ( +
+
+ [{log.timestamp}] {log.type.toUpperCase()} +
+
+              {log.content}
+            
+
+ ))} +
+
+ + {/* Footer hint */} +
+ In-browser DefraDB • Data resets on page refresh +
+
+ ); +}; + +// --- The "Run" Button for MDX --- +export const RunButton = ({ + code, + type = 'query', + label +}: { + code: string, + type?: 'schema' | 'query', + label?: string +}) => { + return ( + + {() => { + const { runCommand, status } = usePlayground(); + const isDisabled = status !== 'ready'; + + return ( + + ); + }} + + ); +}; \ No newline at end of file diff --git a/src/components/RealDualDefraDBPlayground.tsx b/src/components/RealDualDefraDBPlayground.tsx new file mode 100644 index 0000000..280d682 --- /dev/null +++ b/src/components/RealDualDefraDBPlayground.tsx @@ -0,0 +1,669 @@ +import React, { useState, useEffect, useRef } from 'react'; +import BrowserOnly from '@docusaurus/BrowserOnly'; + +/** + * Source Network Themed DefraDB WASM Playground + * * visual-identity: Source Network (Black, Neon Green, Monospace, Grid) + * * fix: Corrected regex logic for namespaced mutations (create_User -> create_Node1_User) + */ + +// --- Theme Constants --- +const THEME = { + bg: '#000000', + surface: '#0A0A0A', + surfaceHighlight: '#111111', + border: '#333333', + primary: '#00FF94', // The specific Source Network green + primaryDim: 'rgba(0, 255, 148, 0.1)', + text: '#FFFFFF', + textDim: '#888888', + error: '#FF3333', + fontMono: '"Fira Code", "Courier New", monospace', + fontSans: '"Inter", -apple-system, BlinkMacSystemFont, sans-serif', +}; + +// --- Types --- +declare global { + interface Window { + defradb?: { + open(acpType?: string): Promise; + }; + defradbClient?: any; + globalACPConfig?: { + apiUrl: string; + rpcUrl: string; + grpcUrl: string; + chainId: string; + denom: string; + useZeroFees: boolean; + }; + } +} + +interface DefraDBInstance { + id: string; + name: string; + client: any; + status: 'initializing' | 'ready' | 'error'; + logs: string[]; + namespace: string; +} + +// --- Icons & Visual Elements --- +const DotGridBackground = () => ( +
+); + +const CubeIcon = ({ color }: { color: string }) => ( + + + + + +); + +const TerminalIcon = () => ( + + + + +); + +// --- Main Component --- +function RealDualDefraDBPlaygroundInner() { + const [instance1, setInstance1] = useState({ + id: 'node1', + name: 'EDGE_NODE_01', + client: null, + status: 'initializing', + logs: [], + namespace: 'Node1' + }); + + const [instance2, setInstance2] = useState({ + id: 'node2', + name: 'EDGE_NODE_02', + client: null, + status: 'initializing', + logs: [], + namespace: 'Node2' + }); + + const [query1, setQuery1] = useState(`query { + User { + _docID + name + age + points + verified + } +}`); + + const [query2, setQuery2] = useState(`query { + User { + _docID + name + age + points + verified + } +}`); + + const [result1, setResult1] = useState(''); + const [result2, setResult2] = useState(''); + const [globalError, setGlobalError] = useState(null); + const initializationStarted = useRef(false); + + // --- Logic --- + const addLog = (instanceId: string, message: string) => { + const timestamp = new Date().toLocaleTimeString([], { hour12: false }); + const formattedLog = `[${timestamp}] ${message}`; + const setter = instanceId === 'node1' ? setInstance1 : setInstance2; + setter(prev => ({ ...prev, logs: [...prev.logs, formattedLog] })); + }; + + /** + * Creates a wrapper that automatically prefixes types and mutations + * to simulate namespacing on a shared WASM client. + */ + const createNamespacedClient = (namespace: string, dbClient: any) => { + return { + execRequest: async (query: string, variables?: any, context?: any) => { + let namespacedQuery = query; + + // FIX: Handle standard CRUD operations specifically + // create_User -> create_Node1_User + // update_User -> update_Node1_User + // delete_User -> delete_Node1_User + namespacedQuery = namespacedQuery.replace( + /\b(create|update|delete|get)_User\b/g, + `$1_${namespace}_User` + ); + + // FIX: Handle the Type name itself (for queries) + // query { User { ... } } -> query { Node1_User { ... } } + namespacedQuery = namespacedQuery.replace( + /\bUser\b/g, + `${namespace}_User` + ); + + return await dbClient.execRequest(namespacedQuery, variables || {}, context || {}); + }, + addSchema: async (schema: string) => { + // Prefix type definition: type User -> type Node1_User + const namespacedSchema = schema.replace(/type\s+(\w+)/g, `type ${namespace}_$1`); + return await dbClient.addSchema(namespacedSchema); + }, + getNodeIdentity: async () => { + return await dbClient.getNodeIdentity(); + } + }; + }; + + const initializeDefraDB = async () => { + try { + setGlobalError(null); + addLog('node1', '>> INITIALIZING KERNEL...'); + addLog('node2', '>> INITIALIZING KERNEL...'); + + window.globalACPConfig = { + apiUrl: `${window.location.origin}/api`, + rpcUrl: `${window.location.origin}/rpc`, + grpcUrl: `${window.location.origin}/api`, + chainId: 'sourcehub-dev', + denom: 'uopen', + useZeroFees: true, + }; + + const { instantiate } = await import('@sourcenetwork/acp-js'); + await instantiate('https://defradbwasm.source.network/defradb.wasm'); + + let attempts = 0; + while (!window.defradb && attempts < 100) { + await new Promise(resolve => setTimeout(resolve, 100)); + attempts++; + } + + if (!window.defradb) throw new Error(`Timeout waiting for window.defradb`); + + const dbClient = await window.defradb.open(); + if (!dbClient) throw new Error('Client open failed'); + window.defradbClient = dbClient; + + const client1 = createNamespacedClient('Node1', dbClient); + const client2 = createNamespacedClient('Node2', dbClient); + + setInstance1(prev => ({ ...prev, client: client1, status: 'ready' })); + addLog('node1', '>> SYSTEM ONLINE'); + + setInstance2(prev => ({ ...prev, client: client2, status: 'ready' })); + addLog('node2', '>> SYSTEM ONLINE'); + + } catch (error) { + const errorMsg = `CRITICAL FAILURE: ${error.message}`; + setGlobalError(errorMsg); + setInstance1(prev => ({ ...prev, status: 'error' })); + setInstance2(prev => ({ ...prev, status: 'error' })); + } + }; + + const executeQuery = async (instanceId: string) => { + const instance = instanceId === 'node1' ? instance1 : instance2; + const query = instanceId === 'node1' ? query1 : query2; + const setResult = instanceId === 'node1' ? setResult1 : setResult2; + + if (!instance.client) return; + + try { + addLog(instanceId, `> EXEC: ${query.replace(/\n/g, ' ').substring(0, 40)}...`); + const result = await instance.client.execRequest(query); + const resultStr = JSON.stringify(result, null, 2); + setResult(resultStr); + addLog(instanceId, `> SUCCESS (${resultStr.length} bytes)`); + } catch (error) { + const msg = error.message || String(error); + addLog(instanceId, `> ERROR: ${msg}`); + setResult(`Error: ${msg}`); + } + }; + + const addSampleSchema = async (instanceId: string) => { + const instance = instanceId === 'node1' ? instance1 : instance2; + if (!instance.client) return; + + const schema = `type User { + name: String + age: Int + points: Int + verified: Boolean +}`; + try { + addLog(instanceId, `> INJECTING SCHEMA...`); + await instance.client.addSchema(schema); + addLog(instanceId, `> SCHEMA REGISTERED`); + } catch (error) { + addLog(instanceId, `> ERROR: ${error.message}`); + } + }; + + const addSampleDocument = async (instanceId: string) => { + const instance = instanceId === 'node1' ? instance1 : instance2; + const setResult = instanceId === 'node1' ? setResult1 : setResult2; + if (!instance.client) return; + + // FIX: Updated to use the batch mutation format requested + const mutation = `mutation { + user1: create_User(input: { + age: 31, + verified: true, + points: 90, + name: "Bob_${instance.namespace}" + }) { + _docID + name + } + user2: create_User(input: { + age: 28, + verified: false, + points: 15, + name: "Alice_${instance.namespace}" + }) { + _docID + name + } + user3: create_User(input: { + age: 35, + verified: true, + points: 100, + name: "Charlie_${instance.namespace}" + }) { + _docID + name + } +}`; + try { + addLog(instanceId, `> BATCH WRITING DATA...`); + const result = await instance.client.execRequest(mutation); + setResult(JSON.stringify(result, null, 2)); + addLog(instanceId, `> WRITE CONFIRMED`); + } catch (error) { + addLog(instanceId, `> ERROR: ${error.message}`); + setResult(JSON.stringify(error, null, 2)); + } + }; + + useEffect(() => { + if (initializationStarted.current) return; + initializationStarted.current = true; + initializeDefraDB(); + }, []); + + // --- Styled Components (Render Functions) --- + + const ActionButton = ({ onClick, disabled, label, primary = false }: any) => ( + + ); + + const renderInstance = ( + instance: DefraDBInstance, + query: string, + setQuery: (q: string) => void, + result: string, + onExecute: () => void, + onSchema: () => void, + onDoc: () => void, + onClear: () => void + ) => { + const isReady = instance.status === 'ready'; + + return ( +
+ {/* Header Bar */} +
+
+ + + {instance.name} + +
+
+
+ + {instance.status.toUpperCase()} + +
+
+ +
+ + {/* Controls */} +
+ + +
+ + {/* Editor Area */} +
+
+ // QUERY_INPUT +
+
+