diff --git a/.claude/agents/page-manager.md b/.claude/agents/page-manager.md
new file mode 100644
index 000000000..16ebe2258
--- /dev/null
+++ b/.claude/agents/page-manager.md
@@ -0,0 +1,51 @@
+---
+name: page-manager
+description: Use this agent when you need to create, manage, or update structured documentation pages using the CommonTools page.tsx recipe for project tracking, todo lists, progress reports, or collaborative workspaces. Examples: Context: User is starting a complex multi-step refactoring task that will span multiple sessions. user: "I need to refactor the authentication system across multiple modules - this will take several days" assistant: "I'll use the page-manager agent to create a progress tracking page for this complex refactoring task" Since this is a large, multi-step task that needs tracking across sessions, use the page-manager agent to set up structured documentation and progress tracking.Context: User wants to maintain a shared workspace for ongoing development discussions. user: "Can we create a shared space where we can track our architecture decisions and ongoing discussions?" assistant: "I'll use the page-manager agent to set up a collaborative blackboard page for our architecture discussions" The user wants a persistent collaborative workspace, which is exactly what the page-manager agent provides through CommonTools pages.Context: Agent proactively suggests creating documentation during a complex task. user: "Let's implement the new API endpoints for user management" assistant: "This looks like a substantial implementation task. Let me use the page-manager agent to create a progress tracking page so we can maintain visibility into our work across sessions" For complex implementation tasks, proactively use the page-manager agent to set up tracking and documentation.
+tools: Bash, Glob, Grep, LS, ExitPlanMode, Read, NotebookRead, WebFetch, WebSearch
+color: red
+---
+
+You are the Page Manager Agent, a specialized assistant for creating and maintaining structured documentation pages using the CommonTools page.tsx recipe (typically located at `../recipes/recipes/coralreef/page.tsx`). You excel at organizing complex development work through persistent, collaborative documentation spaces.
+
+**Core Responsibilities:**
+
+1. **Configuration Management**: Load/create .page-agent-config.json with recipe path, current space, identity, API URL, and active page mappings. Auto-initialize missing configurations by prompting for required parameters.
+
+2. **Page Lifecycle Management**: Deploy page.tsx instances as charms in date-based spaces (YYYY-MM-DD-[username] format), manage content through CT binary commands, and maintain real-time synchronization between local state and deployed charms.
+
+3. **Content Structure**: Use outliner tree format for all page content. Support todo lists with hierarchical task breakdown, progress reports with timestamped entries, shared blackboards for collaborative exchange, and general notes with flexible outline structure.
+
+4. **CommonTools Integration**: Execute CT binary commands for charm deployment (new), content updates (set), content retrieval (get), handler calls (call), and space listing (ls). Verify CT binary exists and build if missing. Manage claude.key identity and validate Tailnet connectivity.
+
+5. **Use helper script**: use the `page-manager-helper.sh` script in `.claude` to help speed up working with pages
+
+**Operational Workflow:**
+
+- **Session Start**: Load existing configuration or initialize new setup, verify CT binary and recipe availability, validate space access
+- **Page Creation**: Determine appropriate page type based on context, deploy new charm with template content, update configuration mappings
+- **Content Updates**: Read current content, apply modifications to outline structure, update via CT commands, verify success
+- **Error Recovery**: Handle missing recipes, CT binary issues, network problems, and space access errors with appropriate recovery procedures
+
+**Page Types and Templates:**
+
+- **Todo Lists**: Hierarchical tasks with status tracking and priority levels
+- **Progress Reports**: Timestamped entries with status, findings, next steps, and milestone tracking
+- **Shared Blackboards**: Free-form collaborative spaces with user/Claude message exchange
+- **General Notes**: Flexible outline format for various content types
+
+**Integration Requirements:**
+
+- Maintain consistency with existing recipe development workflows
+- Coordinate with wiki integration for knowledge capture
+- Sync with internal task management systems
+- Provide cross-session persistence for ongoing work
+
+**Quality Assurance:**
+
+- Always verify CT binary functionality before operations
+- Validate recipe syntax and deployment success
+- Maintain audit trail of content changes
+- Implement retry logic for network failures
+- Provide clear error messages and recovery guidance
+
+When users request documentation, tracking, or collaborative workspace needs, proactively suggest appropriate page types and create them with relevant template content. Always save configuration changes and provide clear access information for created pages.
diff --git a/.claude/agents/recipe-dev-guide.md b/.claude/agents/recipe-dev-guide.md
new file mode 100644
index 000000000..be86c17ec
--- /dev/null
+++ b/.claude/agents/recipe-dev-guide.md
@@ -0,0 +1,85 @@
+---
+name: recipe-dev-guide
+description: Use this agent when you need to guide users through CommonTools recipe development with the ct utility. This includes modifying existing recipes, creating new ones, setting up recipe networking, debugging recipe issues, and working with multi-file recipe structures. The agent assumes the user has already set up a space and needs help with recipe-specific tasks.\n\nExamples:\n- \n Context: User has a CommonTools space set up and wants to modify an existing recipe\n user: "I need to update my todo list recipe to add a priority field"\n assistant: "I'll use the recipe-dev-guide agent to help you modify your existing recipe"\n \n Since the user wants to modify a recipe in their CommonTools space, use the recipe-dev-guide agent to walk through the modification process.\n \n\n- \n Context: User is working with CommonTools and needs to create a new recipe\n user: "Can you help me create a recipe that filters items based on status?"\n assistant: "Let me use the recipe-dev-guide agent to guide you through creating a new filter recipe"\n \n The user needs help creating a new CommonTools recipe, so use the recipe-dev-guide agent for the development workflow.\n \n\n- \n Context: User is debugging recipe connections in their CommonTools space\n user: "My recipes aren't passing data correctly between each other"\n assistant: "I'll use the recipe-dev-guide agent to help debug your recipe networking and data flow"\n \n Recipe networking and debugging issues should be handled by the recipe-dev-guide agent.\n \n
+color: orange
+---
+
+You are an expert CommonTools recipe development guide specializing in helping users create, modify, and network recipes using the ct utility. You have deep knowledge of the CommonTools framework, recipe patterns, and the ct command-line interface.
+
+**Critical Prerequisites**:
+- You MUST first read `docs/common/CT.md` for CT binary setup instructions
+- You MUST search for and read `COMPONENTS.md` and `RECIPES.md` files in the user's recipes folder before working on recipes
+- Read `HANDLERS.md` when encountering event handler errors
+- The user should have already run the space setup script or have an existing space
+
+**Your Core Responsibilities**:
+
+1. **Initial Setup Verification**:
+ - Ensure CT binary is properly set up following the common instructions
+ - Verify the user has an existing space by running `ct charm ls`
+ - Show existing charms and ask what they want to work on
+ - Ensure TypeScript setup is current (user should run `ct init` in recipes directory)
+
+2. **Recipe Modification Workflow**:
+ - Get recipe source using `ct charm getsrc`
+ - Guide user through code changes while respecting project formatting (80 char lines, 2 spaces, semicolons, double quotes)
+ - Test syntax with `ct dev [recipe] --no-run` before deploying
+ - Update charm source with `ct charm setsrc`
+ - Verify changes with `ct charm inspect`
+
+3. **New Recipe Creation**:
+ - Help design recipe requirements (inputs, outputs, processing logic)
+ - Create recipe files following CommonTools patterns
+ - Start with appropriate templates based on recipe type (filter, transformer, aggregator, generator, side-effect)
+ - Deploy with `ct charm new` and record the CHARM_ID
+
+4. **Recipe Networking**:
+ - Inspect current connections and data flow
+ - Create links between charms using `ct charm link [source]/[field] [target]/[input]`
+ - Ensure schema compatibility between linked charms
+ - Help visualize and debug data flow
+
+5. **Multi-File Recipe Development**:
+ - Explain import/export patterns and self-contained deployment
+ - Guide on file organization and relative imports
+ - Help avoid common pitfalls (schema mismatches, path issues)
+ - Test composed recipes before deployment
+
+6. **Debugging and Testing**:
+ - Use `ct charm inspect --json` for detailed data inspection
+ - Leverage cell operations (`ct charm get/set`) for precise debugging
+ - Help interpret error messages and fix issues
+ - Create test scenarios with known inputs
+
+**Handler Pattern Guidance**:
+When working with UI handlers, ensure users understand:
+- Event schema (usually empty `{}` for clicks)
+- State schema (declares required data)
+- Handler invocation (pass object matching state schema)
+- Handler function receives `(event, state)`
+
+**Best Practices to Enforce**:
+- Always verify recipe syntax before deploying
+- Keep track of charm IDs when creating new ones
+- Test incrementally and inspect frequently
+- Use meaningful names for charms and fields
+- Save modified recipes to files before using setsrc
+- Follow TypeScript and project conventions from CLAUDE.md
+
+**Command Quick Reference**:
+You should be fluent in all ct commands:
+- `ct charm getsrc/setsrc` - Get/update recipe source
+- `ct dev [recipe] --no-run` - Test syntax
+- `ct charm new` - Create new charm
+- `ct charm link` - Connect charms
+- `ct charm inspect` - View charm details
+- `ct charm get/set` - Manipulate cell data
+- `ct charm ls` - List all charms
+
+**Error Handling Approach**:
+- Parse and explain syntax errors clearly
+- Debug data type mismatches systematically
+- Diagnose connection and permission issues
+- Guide users to fix issues step by step
+
+Remember: Recipe development is iterative. Guide users through each step, test frequently, and ensure they understand the data flow and patterns they're implementing. Always respect the project's coding standards and established patterns from CLAUDE.md.
diff --git a/.claude/agents/research-specialist.md b/.claude/agents/research-specialist.md
new file mode 100644
index 000000000..d9fcf4844
--- /dev/null
+++ b/.claude/agents/research-specialist.md
@@ -0,0 +1,56 @@
+---
+name: research-specialist
+description: Use this agent when you need to thoroughly investigate a topic, understand how specific code works, explore new areas of the codebase, or gather comprehensive information before making changes. Examples: Context: User wants to understand how authentication works in the codebase. user: "How does user authentication work in this system?" assistant: "I'll use the research-specialist agent to thoroughly investigate the authentication system." The user is asking about understanding how specific code works, which is perfect for the research-specialist agent.Context: User is planning to add a new feature and needs to understand existing patterns. user: "I want to add a new API endpoint for user profiles. What patterns should I follow?" assistant: "Let me use the research-specialist agent to research the existing API patterns and architecture before we proceed." This requires exploring the codebase and understanding patterns, which the research-specialist handles systematically.
+tools: Task, Bash, Glob, Grep, LS, ExitPlanMode, Read, NotebookRead, WebFetch, TodoWrite, WebSearch, mcp__playwright__browser_close, mcp__playwright__browser_resize, mcp__playwright__browser_console_messages, mcp__playwright__browser_handle_dialog, mcp__playwright__browser_evaluate, mcp__playwright__browser_file_upload, mcp__playwright__browser_install, mcp__playwright__browser_press_key, mcp__playwright__browser_type, mcp__playwright__browser_navigate, mcp__playwright__browser_navigate_back, mcp__playwright__browser_navigate_forward, mcp__playwright__browser_network_requests, mcp__playwright__browser_take_screenshot, mcp__playwright__browser_snapshot, mcp__playwright__browser_click, mcp__playwright__browser_drag, mcp__playwright__browser_hover, mcp__playwright__browser_select_option, mcp__playwright__browser_tab_list, mcp__playwright__browser_tab_new, mcp__playwright__browser_tab_select, mcp__playwright__browser_tab_close, mcp__playwright__browser_wait_for
+color: pink
+---
+
+You are a research specialist with expertise in systematic codebase investigation and technical analysis. Your role is to conduct thorough, methodical research on any topic or question using all available tools and resources.
+
+**CRITICAL FIRST STEP**: Before doing anything else, you MUST read docs/common/CT.md to understand how to use the CommonTools system properly.
+
+**Your Research Methodology**:
+
+1. **Start with existing knowledge**: Read .claude/commands/search-wiki.md and search the wiki first to check for existing research on this topic to avoid duplication
+
+2. **Systematic codebase exploration**:
+ - Use Glob tool to find relevant files and directories
+ - Use Grep tool to search for specific patterns, functions, or concepts
+ - Use Read tool to examine key files in detail
+ - Focus on understanding architecture, patterns, and implementations
+
+3. **Documentation review**:
+ - Check README.md files at all levels
+ - Review CLAUDE.md and other project documentation
+ - Examine inline code comments and JSDoc
+ - Look for configuration files and their documentation
+
+4. **Historical analysis**:
+ - Examine git history for relevant changes
+ - Look at recent commits related to the topic
+ - Understand evolution of the codebase in relevant areas
+
+5. **Test examination**:
+ - Find and analyze test files to understand expected behavior
+ - Look for integration tests that show real usage patterns
+ - Use tests to validate your understanding
+
+**Output Requirements**:
+Provide a comprehensive research report structured as:
+
+- **Executive Summary**: Key findings in 2-3 sentences
+- **Detailed Analysis**: Thorough investigation with specific file paths, line numbers, and code references
+- **Architecture Insights**: Design decisions, patterns, and structural understanding
+- **Recent Changes**: Relevant git history and development trends
+- **Recommendations**: Next steps or actionable insights if applicable
+
+**MANDATORY FINAL STEP**: After delivering your research report, you MUST ask the user if they want to deploy the research findings using the .claude/commands/deploy-research.md command. This is required regardless of whether the user seems interested - always offer this option.
+
+**Quality Standards**:
+- Be thorough but focused on the specific question or topic
+- Provide concrete evidence with file paths and line numbers
+- Explain complex concepts clearly
+- Identify gaps in your research and acknowledge limitations
+- Cross-reference findings across multiple sources when possible
+
+You are autonomous in your research approach but should ask for clarification if the research scope is unclear or too broad to handle effectively.
diff --git a/.claude/commands/add-attachment.md b/.claude/commands/add-attachment.md
new file mode 100644
index 000000000..fbbe7d50c
--- /dev/null
+++ b/.claude/commands/add-attachment.md
@@ -0,0 +1,175 @@
+# Adding Attachments to Outliner
+
+This guide explains how to attach charms to nodes in the outliner tree.
+
+The user wants to: $ARGUMENTS
+
+## Overview
+
+Attachments allow you to link charms to specific nodes in a page's outline structure. Any charm can be attached to provide additional functionality, data, or visual representations at that location.
+
+## Prerequisites
+
+- A page charm with an outline structure
+- A charm to attach (either existing or newly created from a recipe)
+- CT binary configured with proper credentials
+
+## Basic Steps
+
+### Step 1: Identify Target Nodes
+
+First, find the page charm and locate nodes where you want to add attachments:
+
+```bash
+# Get the outline structure
+./dist/ct charm get --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] --charm [PAGE_CHARM_ID] outline
+
+# Filter for specific nodes (e.g., nodes without attachments)
+./dist/ct charm get --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] --charm [PAGE_CHARM_ID] outline | jq '.root.children[].children[] | select(.attachments == [])'
+```
+
+### Step 2: Create or Identify Attachment Charm
+
+Either use an existing charm or create a new one from a recipe:
+
+```bash
+# Create new charm from recipe
+./dist/ct charm new --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] [RECIPES_PATH]/[recipe-name].tsx
+# Returns: NEW_CHARM_ID
+
+# Configure the charm if needed
+echo '[INPUT_DATA]' | ./dist/ct charm set --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] --charm [NEW_CHARM_ID] [INPUT_NAME] --input
+```
+
+### Step 3: Link Charm to Node
+
+Attach the charm to the target node's attachments array:
+
+```bash
+./dist/ct charm link --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] [ATTACHMENT_CHARM_ID] [PAGE_CHARM_ID]/[PATH_TO_NODE]/attachments/[INDEX]
+```
+
+## Path Structure
+
+The outline follows this hierarchy:
+```
+root
+├── body: "text content"
+├── children: [
+│ ├── [0]
+│ │ ├── body: "child text"
+│ │ ├── children: [...]
+│ │ └── attachments: []
+│ └── [1]
+│ ├── body: "another child"
+│ ├── children: [...]
+│ └── attachments: []
+└── attachments: []
+```
+
+Example paths:
+- `page/outline/root/attachments/0` - First attachment at root
+- `page/outline/root/children/0/attachments/0` - First attachment of first child
+- `page/outline/root/children/1/children/2/attachments/0` - Nested child attachment
+
+## Common Attachment Patterns
+
+### 1. Data Fetchers
+Attach charms that fetch and display external data (GitHub repos, APIs, databases):
+```bash
+# Example: Attach a data fetcher to a node mentioning a resource
+./dist/ct charm link --identity [...] [FETCHER_CHARM] [PAGE]/outline/root/children/0/attachments/0
+```
+
+### 2. Visualizations
+Attach charts, graphs, or other visual representations:
+```bash
+# Example: Attach a chart to display metrics mentioned in text
+./dist/ct charm link --identity [...] [CHART_CHARM] [PAGE]/outline/root/children/1/attachments/0
+```
+
+### 3. Interactive Elements
+Attach forms, buttons, or interactive components:
+```bash
+# Example: Attach an input form to a task node
+./dist/ct charm link --identity [...] [FORM_CHARM] [PAGE]/outline/root/children/2/attachments/0
+```
+
+### 4. Multiple Attachments
+Nodes can have multiple attachments at different indices:
+```bash
+# Attach multiple charms to the same node
+./dist/ct charm link --identity [...] [CHARM_1] [PAGE]/outline/root/attachments/0
+./dist/ct charm link --identity [...] [CHARM_2] [PAGE]/outline/root/attachments/1
+./dist/ct charm link --identity [...] [CHARM_3] [PAGE]/outline/root/attachments/2
+```
+
+## Efficient Querying
+
+When working with large outlines or many attachments:
+
+### Filter Unlinked Nodes
+```bash
+# Find nodes without attachments
+jq '.root.children[].children[] | select(.attachments == []) | {body: .body, path: path(.)}'
+```
+
+### Target Specific Paths
+```bash
+# Get only a specific branch to avoid large responses
+jq '.root.children[0].children[3]'
+```
+
+### Check Existing Attachments
+```bash
+# List nodes with attachments
+jq '.. | select(.attachments? and .attachments != []) | {body: .body, attachments: .attachments | length}'
+```
+
+## Verification
+
+After linking, verify the attachment:
+
+```bash
+# Check the specific node's attachments
+./dist/ct charm get --identity [...] --charm [PAGE_CHARM_ID] outline | jq '[PATH_TO_NODE].attachments'
+
+# Example: Check root's first attachment
+./dist/ct charm get --identity [...] --charm [PAGE_CHARM_ID] outline | jq '.root.attachments[0]'
+```
+
+## Tips
+
+1. **Index Management**: Attachments are indexed arrays. Use index 0 for the first attachment, 1 for the second, etc.
+
+2. **Selective Updates**: Target specific nodes rather than re-processing entire outlines.
+
+3. **Attachment Types**: Any charm can be an attachment - consider what makes sense for the content.
+
+4. **Performance**: When outlines contain many attachments with large data, use jq filters to query only needed parts.
+
+## Example: Complete Workflow
+
+```bash
+# 1. Find a page charm
+./dist/ct charm ls --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace
+# Returns: page123
+
+# 2. Examine outline for attachment points
+./dist/ct charm get --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace --charm page123 outline | jq '.root.children[0]'
+# Found: Node at children[0] needs a visualization
+
+# 3. Create visualization charm
+./dist/ct charm new --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace ~/recipes/chart.tsx
+# Returns: chart456
+
+# 4. Configure the chart
+echo '{"data": [1,2,3,4,5]}' | ./dist/ct charm set --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace --charm chart456 chartData --input
+
+# 5. Attach to node
+./dist/ct charm link --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace chart456 page123/outline/root/children/0/attachments/0
+
+# 6. Verify
+./dist/ct charm get --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace --charm page123 outline | jq '.root.children[0].attachments | length'
+# Returns: 1 (success!)
+```
diff --git a/.claude/commands/deploy-research.md b/.claude/commands/deploy-research.md
new file mode 100644
index 000000000..461125fd6
--- /dev/null
+++ b/.claude/commands/deploy-research.md
@@ -0,0 +1,72 @@
+# Deploy Research Command
+
+Deploy research findings as a CommonTools research report.
+
+## Usage
+
+`/deploy-research [research content or file]`
+
+## Process
+
+Launch a deployment subagent using the Task tool:
+
+```
+Task: Deploy research findings to CommonTools
+
+You are a deployment specialist. Take the provided research content and deploy it as a CommonTools research report.
+
+**Your Task:**
+1. Check for claude-research.key identity (create if needed)
+2. Deploy charm using recipes/research-report.tsx
+3. Set title and content using CT commands
+4. Return the final URL to the user
+
+**Research Content:** [paste research findings here]
+
+**Return to me:** The deployed research report URL.
+```
+
+## Deployment Steps
+
+### 1. Check/Generate Claude Identity
+```bash
+# Check for existing identity
+ls -la claude-research.key
+
+# If not found, generate new one
+./dist/ct id new > claude-research.key
+```
+
+### 2. Create Research Report Charm
+```bash
+./dist/ct charm new --identity claude-research.key --api-url https://toolshed.saga-castor.ts.net --space YYYY-MM-DD-claude-dev recipes/research-report.tsx
+```
+
+### 3. Set Title and Content
+```bash
+# Set the title
+echo '"Research: "' | ./dist/ct charm set --identity claude-research.key --api-url https://toolshed.saga-castor.ts.net --space YYYY-MM-DD-claude-dev --charm title
+
+# Set the content (use temp file for complex content)
+cat research-content.tmp | jq -Rs . | ./dist/ct charm set --identity claude-research.key --api-url https://toolshed.saga-castor.ts.net --space YYYY-MM-DD-claude-dev --charm content
+
+# Clean up
+rm research-content.tmp
+```
+
+### 4. Provide URL
+Return the final URL:
+```
+https://toolshed.saga-castor.ts.net/YYYY-MM-DD-claude-dev/
+```
+
+## Standard Parameters
+- **Identity**: `claude-research.key`
+- **API URL**: `https://toolshed.saga-castor.ts.net`
+- **Space**: `YYYY-MM-DD-claude-dev` (use current date)
+- **Recipe**: `recipes/research-report.tsx`
+
+## Error Handling
+- If deployment fails, provide research findings directly to user
+- Don't block research results on deployment issues
+- Validate JSON formatting before setting content
\ No newline at end of file
diff --git a/.claude/commands/deps.md b/.claude/commands/deps.md
new file mode 100644
index 000000000..b29ad9349
--- /dev/null
+++ b/.claude/commands/deps.md
@@ -0,0 +1,12 @@
+MUST HAVE:
+- deno 2 https://docs.deno.com/runtime/getting_started/installation/
+
+REALLY SHOULD HAVE:
+- gh https://github.com/cli/cli
+- claude code
+ - `npm i -g @anthropic/claude-code`
+ - mcp
+ - `claude mcp add --transport sse linear-server https://mcp.linear.app/sse`
+ - then use `/linear` for workflow
+ - `claude mcp add playwright npx '@playwright/mcp@latest'`
+ - then use `/explore-recipe`, `/imagine-recipe` or `/recipe-dev` etc.
diff --git a/.claude/commands/diagnose.md b/.claude/commands/diagnose.md
new file mode 100644
index 000000000..54ad7228d
--- /dev/null
+++ b/.claude/commands/diagnose.md
@@ -0,0 +1,78 @@
+# Diagnose Issue Command
+
+A command to analyze a GitHub issue and provide detailed diagnosis with Claude
+assistance.
+
+## Usage
+
+```
+claude diagnose
+```
+
+## Process
+
+1. **Fetch issue details**:
+ ```
+ gh issue view --json number,title,body,url,labels,state,createdAt,comments
+ ```
+
+2. **Format issue information**:
+ - Extract issue title, description, URL, state, creation date
+ - Format labels and comments for readability
+
+3. **Present to Claude**:
+ - Show Claude the formatted issue details
+ - Ask Claude to explore the codebase for relevant files/code
+ - Request analysis including problem summary, affected code, root causes, and
+ solutions
+
+4. **Generate diagnosis**:
+ - Claude analyzes the issue and codebase
+ - Claude produces a comprehensive diagnosis formatted for GitHub
+
+5. **Post comment (optional)**:
+ ```
+ gh issue comment --body ""
+ ```
+
+## Example Conversation
+
+```
+User: claude diagnose 789
+
+Claude: I'll help diagnose GitHub issue #789. First, let me fetch the issue details.
+ [Fetches issue with GitHub CLI]
+
+Claude: Here are the details for issue #789: "Button click not working on mobile"
+ [Shows formatted issue details]
+
+ I'll now explore the codebase to find relevant files and code that might be related to this issue.
+ [Claude explores codebase using tools]
+
+ Based on my exploration, here's my diagnosis:
+
+ ## Problem Summary
+ The button click handler isn't triggering on mobile devices due to touch event handling issues.
+
+ ## Affected Code
+ - `src/components/Button.tsx`: Uses onClick but not onTouch events
+ - `src/utils/eventHandlers.ts`: Missing mobile touch handling
+
+ ## Root Causes
+ 1. The event listener only handles mouse clicks, not touch events
+ 2. Touch event propagation is being stopped elsewhere in the code
+
+ ## Proposed Solutions
+ 1. Add touch event handlers to the Button component
+ 2. Refactor event handling to use a unified approach for both click and touch
+ 3. Ensure event propagation isn't being blocked
+
+ [Further technical details and specific code changes]
+
+Claude: Based on my analysis above, would you like me to post this diagnosis as a comment on the GitHub issue?
+
+User: Yes, please post it.
+
+Claude: [Posts comment to GitHub issue]
+ Successfully added diagnosis as a comment to issue #789.
+```
diff --git a/.claude/commands/explore-recipe.md b/.claude/commands/explore-recipe.md
new file mode 100644
index 000000000..de4c13f85
--- /dev/null
+++ b/.claude/commands/explore-recipe.md
@@ -0,0 +1,12 @@
+- you can start local dev using `scripts/start-local-dev.sh`
+- read `ct.md` to understand the `ct` binary
+- IMPORTANT: when you load the shell in the browser, you may have to complete registration by generating a one-time passphrase (this will persist between sessions)
+- use playwright MCP to explore recipes and charms from the browser
+ - use ct to support and debug your exporation
+- if the user gives an instruction like 'explore packages/patterns/counter.tsx` you should work out how to deploy it using ct, load it in the browser using playwright and explore it based on the recipe's sourcecode
+ - then you should describe what you find to the user
+- if the user gives no spacename, use the current date + a descriptive suffix e.g. `2025-06-01-counter`
+- you can stop the server using `scripts/stop-local-dev.sh`
+- you can restart the server using `scripts/start-local-dev.sh --force`
+- you should visit the charm directly using http://localhost:8000//
+- DO NOT deploy the charms to the remote server, make sure you use `--api-url http://localhost:8000` with `ct`
diff --git a/.claude/commands/fix-issue.md b/.claude/commands/fix-issue.md
new file mode 100644
index 000000000..8a95106af
--- /dev/null
+++ b/.claude/commands/fix-issue.md
@@ -0,0 +1,77 @@
+# Fix Issue Command
+
+A command to create a branch and fix a GitHub issue with Claude assistance.
+
+## Usage
+
+```
+claude fix-issue
+```
+
+## Process
+
+1. **Fetch issue details**:
+ ```
+ gh issue view --json number,title,body,url,labels,assignees,state,createdAt,updatedAt,milestone,comments
+ ```
+
+2. **Create a branch**:
+ ```
+ git checkout -b fix/claude-
+ ```
+
+3. **Present the issue to Claude**:
+ - Format all issue details (title, description, comments, labels, assignees,
+ etc.)
+ - Ask Claude to analyze the issue and suggest fixes
+
+4. **Implement changes**:
+ - Make code changes based on Claude's suggestions
+ - Stage changes with `git add`
+
+5. **Generate PR description**:
+ - Fetch the staged changes with `git diff --staged`
+ - Ask Claude to analyze changes and create a PR description
+
+6. **Commit and create PR**:
+ ```
+ git commit -m "Fix #: "
+ git push -u origin fix/claude-
+ gh pr create --title "Fix #: " --body ""
+ ```
+
+## Example Conversation
+
+```
+User: claude fix-issue 123
+
+Claude: I'll help you fix GitHub issue #123. First, let me fetch the issue details.
+ [Fetches issue with GitHub CLI]
+
+Claude: Created branch: fix/claude-123
+
+ Here's the full issue information:
+ [Shows formatted issue details]
+
+ Please analyze all the information above and provide a complete fix for this issue.
+ Explain your approach and any considerations.
+
+ [Claude and user work together on implementing fixes]
+
+Claude: Once you have implemented and staged the changes, let me know, and I'll help create a PR description and submit the PR.
+
+User: I've staged the changes.
+
+Claude: Let me get the changes for the PR description...
+ [Shows diff of staged changes]
+
+ I'll create a summary for the PR description based on these changes.
+ [Creates PR description]
+
+ Changes have been committed. When you're ready to create the PR, please provide the PR description you'd like to use.
+
+User: Use the PR description you suggested.
+
+Claude: [Creates PR with GitHub CLI]
+ Successfully created branch and committed changes for issue #123.
+```
diff --git a/.claude/commands/fix-pr.md b/.claude/commands/fix-pr.md
new file mode 100644
index 000000000..bb045a7a4
--- /dev/null
+++ b/.claude/commands/fix-pr.md
@@ -0,0 +1,75 @@
+# Fix PR Command
+
+A command to check out and improve a GitHub pull request with Claude assistance.
+
+## Usage
+
+```
+claude fix-pr
+```
+
+## Process
+
+1. **Fetch PR branch information**:
+ ```
+ gh pr view --json headRefName
+ ```
+
+2. **Checkout the PR branch**:
+ ```
+ gh pr checkout
+ ```
+
+3. **Get PR details**:
+ ```
+ gh pr view --json body,comments,state,statusCheckRollup,changedFiles
+ ```
+
+4. **Get PR diff**:
+ ```
+ git diff main...HEAD
+ ```
+
+5. **Present PR to Claude**:
+ - Show the PR description, comments, and code diff
+ - Ask Claude to review the PR and suggest improvements
+
+6. **Implement changes**:
+ - Make code changes based on Claude's review
+ - Stage changes with `git add`
+
+7. **Commit and push**:
+ ```
+ git commit -m "Apply changes from PR # review"
+ git push origin
+ ```
+
+## Example Conversation
+
+```
+User: claude fix-pr 456
+
+Claude: I'll help you review and fix pull request #456. First, let me fetch the PR details.
+ [Fetches PR with GitHub CLI]
+
+Claude: Checking out PR branch: feature/new-widget...
+ [Checks out the PR branch]
+
+ Here's the full PR information and diff:
+ [Shows PR details and code diff]
+
+ Please review the PR details above and decide if any changes are needed.
+ If changes are needed, please provide the exact code changes to implement.
+ If no changes are needed, please explain why.
+
+ [Claude and user discuss potential improvements]
+
+Claude: If you want to apply any changes, let me know when you're done, and I'll help commit and push them.
+
+User: I've made the changes you suggested.
+
+Claude: Let me commit and push those changes for you.
+ [Commits and pushes changes to PR branch]
+
+ Successfully pushed changes to PR branch.
+```
diff --git a/.claude/commands/imagine-recipe.md b/.claude/commands/imagine-recipe.md
new file mode 100644
index 000000000..8c0406193
--- /dev/null
+++ b/.claude/commands/imagine-recipe.md
@@ -0,0 +1,149 @@
+# Interactive Recipe Imagination and Creation
+
+This script guides Claude through creating new CommonTools recipes based on user ideas. It follows a streamlined approach similar to recipe development but focuses on bringing new recipe concepts to life.
+
+## Prerequisites
+
+**Before starting recipe imagination:**
+- User should have an existing space or have run the space setup script
+- Claude MUST read the common CT setup instructions in `docs/common/CT.md`
+
+**Recipe Documentation Reference:**
+Before working on recipes, search for these documentation files in the `docs/common` folder:
+- `RECIPES.md` - Core recipe development patterns and examples
+- `COMPONENTS.md` - Available UI components and usage patterns
+- `HANDLERS.md` - Event handler patterns and troubleshooting
+
+The user provides an initial prompt describing what they want their recipe to do: $ARGUMENTS
+
+## Script Flow for Claude
+
+### STEP 1: Initial Setup and Context
+
+**Read common setup instructions:**
+- First, read `docs/common/CT.md` for shared CT binary setup
+- Follow those instructions for CT binary check, identity management, environment setup
+- Collect required parameters (API URL, space name, recipe path, identity file)
+
+**Verify existing space:**
+- Run: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]`
+- Show user the existing charms in their space for context
+- Ask clarifying questions about the recipe idea
+
+### STEP 2: Requirements Gathering and Research
+
+**Clarify the recipe requirements:**
+1. Ask targeted questions about:
+ - What inputs the recipe needs (other charm results, user inputs, external APIs)
+ - What outputs it should produce
+ - What UI interactions are needed
+ - How it should integrate with existing charms
+
+**Research existing patterns:**
+1. Search user's recipe repo: `find [recipe-path] -name "*.tsx" -type f | head -20`
+2. **Search patterns package:** Look in `packages/patterns` for related examples and reusable components
+3. Look for similar recipes or reusable patterns
+4. Check existing space charms for potential data sources and targets
+5. Reference the recipe documentation files for patterns and components
+
+### STEP 3: Design and Plan
+
+**Create implementation plan:**
+1. Design the recipe structure (single file vs multi-file)
+2. Plan the input/output schemas
+3. Identify UI components needed (reference COMPONENTS.md)
+4. Plan integration and linking strategy
+5. Present plan to user and get approval
+
+### STEP 4: Implementation
+
+**Create the recipe:**
+1. Ensure TypeScript setup: User should have run `ct init` in recipes directory
+2. Create the recipe file following CommonTools patterns
+3. Implement UI components using `ct-` prefixed components
+4. Define proper schemas and handlers (reference HANDLERS.md for patterns)
+5. Add error handling and validation
+
+**Test syntax (if requested or if deployment fails):**
+- Run: `./dist/ct dev [recipe-file] --no-run`
+- Fix any syntax errors
+
+### STEP 5: Deploy and Test
+
+**Deploy new charm:**
+1. Deploy: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [recipe-file]`
+2. Record the new CHARM_ID
+3. Verify deployment: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]`
+
+**Create integrations:**
+1. Link to data sources: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] [source]/[field] [target]/[input]`
+2. Verify links work: `./dist/ct charm inspect --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id]`
+
+**Test and refine:**
+1. Use inspection commands to verify behavior
+2. Use cell operations for testing: `./dist/ct charm get/set` commands
+3. **Use Playwright for UI testing (if MCP available):** Test the recipe's user interface by navigating to the space URL and interacting with the deployed charm
+4. Make refinements using `./dist/ct charm setsrc` if needed
+5. Iterate based on user feedback
+
+### STEP 6: Documentation and Handoff
+
+**Finalize the recipe:**
+- Verify it meets original requirements
+- Ensure proper integration with existing charms
+- Add helpful comments and documentation
+
+**Repository management:**
+- Help save recipe to correct location
+- Guide git workflow if user wants to commit
+- Provide usage instructions
+
+## Common Recipe Patterns
+
+**Recipe types to consider:**
+- **Filter recipes**: Process collections, output filtered subsets
+- **Transformer recipes**: Convert data between formats
+- **Aggregator recipes**: Combine multiple inputs
+- **Generator recipes**: Create new data based on inputs
+- **UI recipes**: Provide interactive interfaces
+- **Integration recipes**: Connect to external APIs
+
+## Key Commands Reference
+
+```bash
+# List charms
+./dist/ct charm ls --identity [key] --api-url [url] --space [space]
+
+# Create new charm
+./dist/ct charm new --identity [key] --api-url [url] --space [space] [recipe-file]
+
+# Link charms
+./dist/ct charm link --identity [key] --api-url [url] --space [space] [source]/[field] [target]/[input]
+
+# Inspect charm
+./dist/ct charm inspect --identity [key] --api-url [url] --space [space] --charm [id]
+
+# Update recipe source
+./dist/ct charm setsrc --identity [key] --api-url [url] --space [space] --charm [id] [recipe-file]
+
+# Test recipe syntax
+./dist/ct dev [recipe-file] --no-run
+
+# Cell operations for testing
+./dist/ct charm get --identity [key] --api-url [url] --space [space] --charm [id] [path]
+echo '[json-value]' | ./dist/ct charm set --identity [key] --api-url [url] --space [space] --charm [id] [path]
+```
+
+## Notes for Claude
+
+- **Always search `packages/patterns` and the recipes repository first** - Look for related examples before writing new code
+- Start simple and iterate - build basic functionality first
+- Reference the recipe documentation files frequently
+- Test incrementally after each major change
+- Don't test syntax before deploying unless explicitly requested or deployment fails
+- Keep track of charm IDs when creating new ones
+- Use cell operations for precise testing and debugging
+- **Use Playwright MCP for comprehensive UI testing** - Navigate to the space URL and test the recipe's interface directly in the browser
+- Focus on user needs and practical functionality
+
+Remember: Recipe imagination is about turning ideas into working code. Help users build step by step, testing along the way, and creating recipes that solve real problems in their CommonTools spaces.
diff --git a/.claude/commands/linear.md b/.claude/commands/linear.md
new file mode 100644
index 000000000..55d35efe1
--- /dev/null
+++ b/.claude/commands/linear.md
@@ -0,0 +1,440 @@
+# Linear + GitHub Workflow - Session Foundation
+
+This document establishes your working context for Linear-driven development with GitHub integration. It runs at the start of each session to set up proper workflows and mental models.
+
+## ⚠️ CRITICAL: Always Follow This Workflow
+
+### For ANY Implementation Task:
+1. Create worktree
+2. Check for conflicts
+3. Update Linear status
+4. Use specialized agent
+5. Let agent handle PR
+
+## 🎯 Direct Command Responses
+
+When user says: **"let's fix CT-XXX"**
+You MUST respond with: "I'll help you fix CT-XXX. Let me set up the proper workflow first."
+Then execute these steps:
+
+```javascript
+// When user says "let's fix CT-XXX", ALWAYS start with:
+const issue = await mcp__linear-server__get_issue({ id: "CT-XXX" });
+
+// 1. Create worktree FIRST
+await Bash({ command: "cd /Users/ben/code/labs && git worktree add labs-ct-xxx -b fix/2025-MM-DD-ct-xxx-brief-description" });
+
+// 2. Check for conflicts
+await Bash({ command: "gh pr list | grep -i ct-xxx" });
+
+// 3. Update status
+await mcp__linear-server__update_issue({ id: issue.id, stateId: inProgressId });
+
+// 4. THEN delegate to implementation agent
+await Task({
+ description: "Fix issue CT-XXX",
+ subagent_type: "plan-implementer",
+ prompt: `Fix the issue described in Linear issue ${issue.identifier}...`
+});
+```
+
+When user says: **"can you implement..."**
+You MUST respond with: "I'll set up the workflow and delegate this to the appropriate implementation agent."
+
+**NEVER jump directly to: Read, Edit, MultiEdit, or Write tools**
+
+## 🚀 Quick Session Startup
+
+When the `/linear` command is run WITHOUT a specific issue:
+
+```javascript
+// 1. Check your active issues
+const myIssues = await mcp__linear-server__list_my_issues({ limit: 50 });
+
+// 2. Group by status for overview
+const inProgress = myIssues.filter(i => i.state?.name === "In Progress");
+const inReview = myIssues.filter(i => i.state?.name === "In Review");
+
+// 3. Present concise summary
+"Linear Status:
+- In Progress (3): CT-701, CT-703, CT-705
+- In Review (2): CT-699, CT-700
+
+What would you like to focus on today?"
+```
+
+### STOP! Pre-Work Checklist (MANDATORY)
+When starting work on a specific issue:
+- [ ] Have you created a git worktree for this issue?
+- [ ] Have you checked for concurrent work (gh pr list, git worktree list)?
+- [ ] Have you updated the Linear issue status to "In Progress"?
+- [ ] Have you created a TodoWrite list for tracking?
+
+⚠️ **DO NOT PROCEED TO CODE UNTIL ALL BOXES ARE CHECKED**
+
+### Finding Your Team ID
+```javascript
+// List all teams to find your team ID
+const teams = await mcp__linear-server__list_teams({});
+// Common team ID: "b75d85d3-3e07-4ed3-b876-619ee103cad3" (CommonTools)
+```
+
+## 🎯 Core Working Principles
+
+### 1. Issue-First Development
+**ALWAYS** check for existing issues before implementing anything:
+```javascript
+// Before ANY work, search for related issues
+await mcp__linear-server__list_issues({
+ query: "websocket authentication",
+ teamId: "your-team-id"
+});
+
+// No issue found? Create one FIRST
+await mcp__linear-server__create_issue({
+ title: "Add WebSocket authentication",
+ description: "Clear description with acceptance criteria...",
+ teamId: "team-id",
+ priority: 3 // 1=Urgent🔴, 2=High🟠, 3=Normal🟡, 4=Low
+});
+```
+
+### 2. Linear as Persistent Memory
+**Use Linear over TodoWrite() for anything substantial:**
+- Linear persists between sessions
+- Provides audit trail and collaboration
+- Integrates with GitHub PRs
+- TodoWrite() is only for quick, ephemeral lists
+
+### 3. Branch-Issue-PR Trinity
+Every feature follows this pattern:
+```bash
+# 1. Linear issue exists (CT-703)
+# 2. Create branch with issue ID
+git checkout -b feat/2025-07-31-ct-703-websocket-auth
+
+# 3. Create PR with issue reference
+gh pr create --title "Add WebSocket auth [CT-703]" --body "Closes CT-703..."
+```
+
+### 4. Working Safely with Multiple Agents
+Since multiple agents/users may work on the same repo simultaneously:
+
+**Use Git Worktrees for Isolation:**
+```bash
+# Create isolated worktree for your issue
+git worktree add ../labs-ct-703 -b feat/2025-07-31-ct-703-websocket-auth
+
+# Work in the isolated directory
+cd ../labs-ct-703
+
+# This prevents conflicts with other agents working on main or other branches
+```
+
+**Always Check Before Starting:**
+```bash
+# See who else might be working
+gh pr list
+git worktree list
+git branch -r | grep -E "(in-progress|wip)"
+
+# Check if someone is already on your issue
+await mcp__linear-server__get_issue({ id: "issue-id" });
+// Look for "In Progress" status or recent comments
+```
+
+**Benefits of Worktrees:**
+- Prevents "files changed on disk" conflicts
+- No interference between concurrent work
+- Each agent has isolated file state
+- Shared git history for coordination
+
+## 📋 Status Workflow
+
+Issues flow through these states:
+```
+Triage → On Deck → In Progress → In Review → Done
+```
+
+Update status immediately when:
+- Starting work: → In Progress
+- Creating PR: → In Review
+- PR merged: → Done
+
+## 🔧 Essential Commands
+
+### Linear Core Operations
+```javascript
+// Find issues
+mcp__linear-server__list_my_issues({ limit: 20 })
+mcp__linear-server__list_issues({ query: "search", teamId: "id" })
+mcp__linear-server__get_issue({ id: "issue-id" })
+
+// Create & update
+mcp__linear-server__create_issue({ title, description, teamId, priority })
+mcp__linear-server__update_issue({ id, stateId })
+mcp__linear-server__create_comment({ issueId, body })
+
+// Get status IDs
+mcp__linear-server__list_issue_statuses({ teamId })
+```
+
+### GitHub Integration
+```bash
+# Branch from issue
+git checkout -b feat/YYYY-MM-DD-ct-XXX-description
+
+# Create PR with Linear link
+gh pr create --title "Title [CT-XXX]" --body "Closes CT-XXX..."
+
+# Check and merge
+gh pr checks
+gh pr merge --squash --delete-branch
+```
+
+## 🏃 Complete Workflow Example
+
+Here's the full cycle from issue to completion:
+
+```javascript
+// 1. Find or create issue
+const issues = await mcp__linear-server__list_issues({
+ query: "memory leak websocket"
+});
+
+// 2. Create if needed
+const issue = await mcp__linear-server__create_issue({
+ title: "Fix WebSocket memory leak",
+ description: "Event listeners not being cleaned up...",
+ teamId: "team-id",
+ priority: 2 // High priority
+});
+
+// 3. Start work - update status
+const statuses = await mcp__linear-server__list_issue_statuses({ teamId });
+const inProgress = statuses.find(s => s.name === "In Progress");
+await mcp__linear-server__update_issue({
+ id: issue.id,
+ stateId: inProgress.id
+});
+```
+
+```bash
+# 4. Create branch and implement
+git checkout -b fix/2025-07-31-ct-704-memory-leak
+# ... make changes ...
+git add .
+git commit -m "fix: clean up WebSocket event listeners
+
+- Remove listeners on disconnect
+- Clear connection references
+- Add cleanup tests
+
+Fixes CT-704"
+
+# 5. Push and create PR
+git push -u origin fix/2025-07-31-ct-704-memory-leak
+gh pr create --title "Fix WebSocket memory leak [CT-704]" --body "$(cat <<'EOF'
+## Summary
+- Fixed memory leak by cleaning up event listeners
+- Added proper disconnect handling
+
+Closes CT-704
+
+## Test Plan
+- [x] Unit tests pass
+- [x] Memory profiler shows stable usage
+EOF
+)"
+```
+
+```javascript
+// 6. Update Linear to "In Review"
+const inReview = statuses.find(s => s.name === "In Review");
+await mcp__linear-server__update_issue({
+ id: issue.id,
+ stateId: inReview.id
+});
+
+// 7. After merge, close issue
+await mcp__linear-server__update_issue({
+ id: issue.id,
+ stateId: doneState.id
+});
+```
+
+## 🤝 Working with Specialized Agents
+
+Delegate complex tasks to specialized agents based on the work type:
+
+### When to Use Agents
+
+**Implementation Tasks** - When you have a clear plan to execute:
+```javascript
+await Task({
+ description: "Implement WebSocket auth",
+ subagent_type: "implementation-agent", // Use your available implementation agent
+ prompt: `Implement JWT authentication for WebSocket server as described in Linear issue ${issue.identifier}.
+ Follow the acceptance criteria and ensure all tests pass.`
+});
+```
+
+**Planning & Architecture** - For breaking down complex problems:
+```javascript
+await Task({
+ description: "Plan refactoring strategy",
+ subagent_type: "planning-agent", // Use your available planning agent
+ prompt: `Create a detailed plan for refactoring the authentication system.
+ Break down into incremental steps without implementing.`
+});
+```
+
+**Debugging & Investigation** - For systematic problem-solving:
+```javascript
+await Task({
+ description: "Debug memory leak",
+ subagent_type: "debugging-agent", // Use your available debugging agent
+ prompt: `Investigate memory leak described in Linear issue ${issue.identifier}.
+ Find root cause and propose fix.`
+});
+```
+
+**Code Research** - For understanding existing implementations:
+```javascript
+await Task({
+ description: "Research auth patterns",
+ subagent_type: "research-agent", // Use your available research agent
+ prompt: `Research how authentication is currently implemented across the codebase.
+ Document patterns and conventions.`
+});
+```
+
+**Key principle: Always include Linear issue context in agent prompts for continuity.**
+
+## ❌ Common Mistakes to Avoid
+
+### The "Quick Fix" Trap
+**Mistake**: "This looks simple, I'll just fix it quickly"
+**Why it's bad**: Skips workflow, causes conflicts, inconsistent approach
+**Instead**: ALWAYS use worktrees and agents, even for "simple" fixes
+
+### The "Already in the Right Directory" Fallacy
+**Mistake**: "I'm already in packages/ui, so I'll just work here"
+**Why it's bad**: Conflicts with other agents/users, no isolation
+**Instead**: Create a fresh worktree for EVERY issue
+
+### The "I Can Do It Myself" Syndrome
+**Mistake**: Implementing directly instead of using specialized agents
+**Why it's bad**: Misses domain knowledge, patterns, and optimizations
+**Instead**: Delegate to agents - they have specialized knowledge
+
+## 🎨 Best Practices
+
+### Issue Management
+- Clear, searchable titles with component names
+- Include reproduction steps for bugs
+- Add acceptance criteria for features
+- Link related issues via comments
+- Update status in real-time
+
+### Branch Naming
+- `feat/YYYY-MM-DD-ct-XXX-brief-description` - New features
+- `fix/YYYY-MM-DD-ct-XXX-brief-description` - Bug fixes
+- `refactor/YYYY-MM-DD-ct-XXX-brief-description` - Refactoring
+- Always include the Linear issue ID
+
+### PR Practices
+- Title: "Clear description [CT-XXX]"
+- Body: Include "Closes CT-XXX" for auto-close
+- Reference specific commits if addressing multiple issues
+- Request reviews via `gh pr edit --add-reviewer @user`
+
+### Comment Strategy
+Document in Linear:
+- Decisions and rationale
+- Blockers or dependencies
+- PR links
+- Completion summary
+
+## 📌 Quick Reference Card
+
+### Priority Levels
+- 0 = No priority
+- 1 = Urgent 🔴
+- 2 = High 🟠
+- 3 = Normal 🟡
+- 4 = Low
+
+### Common Patterns
+```bash
+# See all your PRs
+gh pr status
+
+# Check CI status
+gh pr checks
+
+# View issue in browser
+gh issue view CT-XXX --web
+
+# Add team labels
+gh pr edit --add-label "bug,high-priority"
+```
+
+### Progressive Context Loading
+1. **Level 1** (startup): Issue status overview
+2. **Level 2** (task start): Full issue details + comments
+3. **Level 3** (deep work): Related docs + codebase research
+
+## 🚨 Common Issues & Solutions
+
+### Linear API Issues
+```javascript
+// If team ID is unknown
+const teams = await mcp__linear-server__list_teams({});
+const team = teams.find(t => t.name === "Your Team Name");
+
+// If status names don't match exactly
+const statuses = await mcp__linear-server__list_issue_statuses({ teamId });
+console.log(statuses.map(s => ({ id: s.id, name: s.name })));
+
+// If issue not found
+const issues = await mcp__linear-server__list_issues({
+ query: "partial title keywords",
+ includeArchived: true
+});
+```
+
+### GitHub CLI Issues
+```bash
+# PR creation fails - check branch is pushed
+git push -u origin branch-name
+
+# Merge conflicts
+gh pr view # Check PR status
+git pull origin main
+git merge main
+# Resolve conflicts, then:
+git add .
+git commit -m "resolve: merge conflicts with main"
+git push
+
+# CI failures
+gh pr checks # See which checks failed
+gh run view # Get detailed logs
+```
+
+### Integration Issues
+- **PR not linking to Linear**: Ensure `[CT-XXX]` is in PR title
+- **Issue not auto-closing**: Use "Closes CT-XXX" in PR body
+- **Status not syncing**: Manually update via Linear API
+
+## 🔑 Remember
+
+1. **Check Linear first, implement second**
+2. **One issue = One branch = One PR**
+3. **Update status as you progress**
+4. **Comment significant findings**
+5. **Link everything properly**
+6. **Use specialized agents for complex tasks**
+
+This workflow ensures persistent context across sessions, clear audit trails, and seamless Linear-GitHub integration. Your work is always trackable, recoverable, and collaborative.
\ No newline at end of file
diff --git a/.claude/commands/link-github.md b/.claude/commands/link-github.md
new file mode 100644
index 000000000..70bdc4f22
--- /dev/null
+++ b/.claude/commands/link-github.md
@@ -0,0 +1,169 @@
+# GitHub Auto-Linking Command
+
+This command finds GitHub URLs in page.tsx charms and creates linked GitHub repository fetcher charms for each URL.
+
+## Prerequisites
+
+- **Recipe Required**: The `github-repo-fetcher.tsx` recipe must exist in your recipes directory
+
+## Usage
+
+```bash
+/link-github [API_URL] [SPACE_NAME] [IDENTITY_FILE] [RECIPES_PATH] [PAGE_CHARM_ID]
+```
+
+**Parameters:**
+- `API_URL`: CommonTools API endpoint
+- `SPACE_NAME`: Target space name
+- `IDENTITY_FILE`: Path to CT identity key file
+- `RECIPES_PATH`: Path to recipes directory containing `github-repo-fetcher.tsx`
+- `PAGE_CHARM_ID`: Specific page charm ID to process (optional)
+
+## Direct Execution Steps
+
+### Step 1: Find Page Charms
+```bash
+# List all charms
+./dist/ct charm ls --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME]
+
+# For each charm, check if it's a page by trying to get its outline
+./dist/ct charm get --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] --charm [CHARM_ID] outline
+
+# If this returns data with a 'root' structure, it's a page charm
+```
+
+### Step 2: Extract GitHub URLs from Outline
+For each page charm:
+```bash
+# IMPORTANT: For efficiency, especially when re-scanning, use jq to avoid pulling massive attachment data
+# Get only nodes with empty attachments (unlinked URLs)
+./dist/ct charm get --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] --charm [CHARM_ID] outline | jq '.root.children[].children[] | select(.attachments == []) | {body: .body, path: path(.)}'
+
+# Or for a specific path to avoid large JSON responses:
+./dist/ct charm get --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] --charm [CHARM_ID] outline | jq '.root.children[0].children[3]'
+
+# Look for patterns like: https://github.com/[owner]/[repo]
+```
+
+### Step 3: Create GitHub Fetcher Charms
+For each unique GitHub URL found:
+```bash
+# Create new github-repo-fetcher charm
+./dist/ct charm new --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] [RECIPES_PATH]/github-repo-fetcher.tsx
+
+# Set the repoUrl input (note the double quotes in the echo)
+echo '"https://github.com/owner/repo"' | ./dist/ct charm set --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] --charm [NEW_CHARM_ID] repoUrl --input
+```
+
+### Step 4: Link to Page Attachments
+For each node containing a GitHub URL:
+```bash
+# Link the github fetcher charm to the node's attachments array at index 0
+./dist/ct charm link --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] [GITHUB_FETCHER_CHARM_ID] [PAGE_CHARM_ID]/[PATH_TO_NODE]/attachments/0
+
+# Example path: charm1/outline/root/children/1/attachments/0
+```
+
+**Important Note**: The path requires the `page/` prefix because the input and output data shapes differ for page charms. While `ct charm get` returns the outline directly, when linking you must specify the `page/` prefix to target the correct input structure. You can verify this difference using `ct charm inspect [PAGE_CHARM_ID]` which shows the full input/output schema.
+
+## How to Traverse the Outline
+
+The outline structure looks like:
+```json
+{
+ "root": {
+ "body": "text that might contain https://github.com/user/repo",
+ "children": [
+ {
+ "body": "more text",
+ "children": [],
+ "attachments": []
+ }
+ ],
+ "attachments": []
+ }
+}
+```
+
+When searching:
+1. Check the `body` field of each node
+2. Recursively check all `children`
+3. Record the path to any node containing a GitHub URL
+4. Only link to nodes that actually contain the URL (not parent/child nodes)
+
+## Error Handling
+
+- **Non-page charms**: Skip silently if `ct charm get outline` fails
+- **404 GitHub URLs**: Skip and continue with other URLs
+- **Existing attachments**: Skip nodes that already have attachments (check with `attachments != []`)
+
+## Complete Example
+
+```bash
+# Step 1: List charms
+./dist/ct charm ls --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net --space 2025-08-06-ben-dev
+# Returns: charm1, charm2, charm3
+
+# Step 2: Check each for outline
+./dist/ct charm get --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net --space 2025-08-06-ben-dev --charm charm1 outline
+# Returns outline data - this is a page charm!
+
+# Step 3: Found https://github.com/vercel/next.js in outline/root/children/0/body
+
+# Step 4: Create fetcher
+./dist/ct charm new --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net --space 2025-08-06-ben-dev ~/code/recipes/recipes/github-repo-fetcher.tsx
+# Returns: newcharm123
+
+# Step 5: Configure fetcher
+echo '"https://github.com/vercel/next.js"' | ./dist/ct charm set --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net --space 2025-08-06-ben-dev --charm newcharm123 repoUrl --input
+
+# Step 6: Link to page
+./dist/ct charm link --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net --space 2025-08-06-ben-dev newcharm123 charm1/outline/root/children/0/attachments/0
+```
+
+## Summary Output
+
+```
+Found 3 page charms
+Found 2 GitHub URLs:
+ - https://github.com/vercel/next.js at charm1/outline/root/children/0
+ - https://github.com/facebook/react at charm2/outline/root/children/1
+Created 2 github-repo-fetcher charms
+Successfully linked all GitHub repositories
+```
+
+---
+
+# CLAUDE IMPLEMENTATION
+
+When this command is invoked, Claude should:
+
+1. **Parse arguments** from .common.json or command line
+2. **Verify prerequisites** (recipes directory, CT binary)
+3. **Execute the Direct Execution Steps** as documented above
+4. **Track progress** using TodoWrite tool
+5. **Report results** concisely
+
+Do NOT use subagents unless dealing with 10+ page charms. Just follow the steps directly.
+
+## Re-scanning Best Practices
+
+When re-scanning for new URLs:
+
+1. **Use precise jq queries** to filter only unlinked nodes:
+ ```bash
+ jq '.root.children[].children[] | select(.attachments == [])'
+ ```
+
+2. **Target specific paths** when you know where new URLs might be:
+ ```bash
+ jq '.root.children[0].children[3]'
+ ```
+
+3. **Avoid pulling full outline** when attachments contain large JSON data
+ - Each linked GitHub repo adds ~200+ lines of JSON to the attachment
+ - Use filtering to get only what you need
+
+4. **Check attachment status** before creating new fetchers:
+ - Only process nodes where `attachments == []`
+ - This prevents duplicate fetchers for already-linked URLs
diff --git a/.claude/commands/logs.md b/.claude/commands/logs.md
new file mode 100644
index 000000000..c005aa5fa
--- /dev/null
+++ b/.claude/commands/logs.md
@@ -0,0 +1,49 @@
+the logs of past sessions in this repository are stored in `~/.claude/projects/` under the `pwd` of this project. determine the correct folder, then list the jsonl files within.
+
+## Log Analysis Strategy
+
+1. **Use ripgrep (rg) for fast searching** across all log files:
+ ```bash
+ rg "pattern" ~/.claude/projects/-Users-ben-code-labs/*.jsonl
+ ```
+
+2. **Log file structure**: Each line is a JSON object with:
+ - `message.content`: Main content (string or array of objects)
+ - `type`: "user" or "assistant"
+ - `timestamp`: ISO timestamp
+ - `uuid`: Unique message ID
+ - `parentUuid`: Links to previous message
+
+3. **Effective search patterns**:
+ - For keywords: `rg -i "keyword1|keyword2" logs/*.jsonl`
+ - For conversation flow: Use `jq` to follow parentUuid chains
+ - For time ranges: `rg "2025-07-21" logs/*.jsonl`
+ - For tool usage: `rg "tool_use.*ToolName" logs/*.jsonl`
+
+4. **Find distraction patterns**:
+ - Look for TodoWrite usage showing task switches
+ - Search for "interrupt", "Request interrupted", or scope changes
+ - Find where assistant mentions getting confused or changing direction
+ - Check for long Task tool usage that might indicate research tangents
+
+5. **Analyze conversation flow**:
+ - Sample log format first with `head -2 file.jsonl | jq .`
+ - Use `jq` to extract message content: `jq '.message.content' file.jsonl`
+ - Look for thinking content: `jq 'select(.message.content[0].type == "thinking")' file.jsonl`
+
+6. **Common analysis queries**:
+ ```bash
+ # Find all TodoWrite usage
+ rg "TodoWrite" logs/*.jsonl | head -10
+
+ # Find task interruptions
+ rg "interrupt|distract|confused|forgot" logs/*.jsonl
+
+ # Find specific feature work
+ rg "ct-list|context.menu" logs/*.jsonl
+
+ # Extract conversation summary
+ jq -r '.message.content | if type == "string" then . else .[0].text // "" end' file.jsonl | head -20
+ ```
+
+The user has asked you to search for: $ARGUMENTS
diff --git a/.claude/commands/maintain-docs.md b/.claude/commands/maintain-docs.md
new file mode 100644
index 000000000..5f9edf1fd
--- /dev/null
+++ b/.claude/commands/maintain-docs.md
@@ -0,0 +1,56 @@
+# Maintain Documentation
+
+This command helps maintain the accuracy of the CT documentation by comparing the ct.md file with the actual CLI help output and identifying discrepancies.
+
+## What this command does:
+
+1. **Reads ct.md** - Loads the current documentation file
+2. **Rebuilds the CT binary** - Ensures we're checking against the latest version
+3. **Runs help commands** - Executes various `--help` commands to get actual CLI documentation
+4. **Compares documentation** - Identifies discrepancies between ct.md and actual help output
+5. **Offers fixes** - Proposes updates to fix any inconsistencies found
+
+## How to use:
+
+Simply ask to "maintain docs" or "check ct documentation" and the command will:
+- Analyze all command signatures and examples
+- Check for missing or outdated commands
+- Verify parameter descriptions match
+- Ensure examples are up-to-date
+
+## Commands checked:
+
+The following ct commands will be verified:
+- `ct charm ls`
+- `ct charm new`
+- `ct charm link`
+- `ct charm inspect`
+- `ct charm getsrc`
+- `ct charm setsrc`
+- `ct charm apply`
+- `ct charm map`
+- `ct dev`
+
+## What gets verified:
+
+- Command syntax and usage patterns
+- Parameter names and descriptions
+- Example commands and their descriptions
+- Environment variable documentation
+- Any new commands not yet documented
+
+## Example usage:
+
+```
+User: maintain the ct docs
+Assistant: I'll check the ct.md documentation against the actual CLI help output...
+
+[Rebuilds binary, runs help commands, compares with documentation]
+
+Found the following discrepancies:
+1. The `getsrc` command now outputs to a folder, but docs show single file
+2. New `--main-export` parameter not documented for `setsrc`
+3. Missing documentation for new `ct charm map` command
+
+Would you like me to update the documentation to fix these issues?
+```
\ No newline at end of file
diff --git a/.claude/commands/onboarding.md b/.claude/commands/onboarding.md
new file mode 100644
index 000000000..902cfd32f
--- /dev/null
+++ b/.claude/commands/onboarding.md
@@ -0,0 +1,118 @@
+# Common Tools Repository Onboarding Tour
+
+This command provides an interactive tour of the Common Tools platform repository, helping new contributors understand where to find information and how to navigate the codebase effectively.
+
+## Getting Started (Essential Foundation)
+
+**Everyone starts here to get oriented:**
+
+**Step 1: What is Common Tools? (Start small)**
+- Read only the "What is Common Tools?" section from README.md
+- Quote the key description to the user
+- Immediately offer the four focused paths:
+
+"Now that you have the basic idea, what would you like to know more about:
+
+- **Programs that run in this platform** (recipes, charms, UI components)
+- **The runtime that enables information flow analysis and storage** (runner, builder, storage)
+- **The application layer that users access the platform through** (toolshed, shell, CLI)
+- **The LLM tooling layer** (Claude commands, subagents, and development workflows)
+
+Which of these interests you most?"
+
+**Step 2: Follow their choice with targeted exploration**
+Based on their choice, dive into only the relevant packages and concepts for that area
+
+## Deep Dive Paths
+
+Based on the user's choice in Step 2, follow these focused exploration paths:
+
+### Path A: Programs That Run in the Platform
+*"I chose: Programs that run in this platform (recipes, charms, UI components)"*
+
+**Explore in order:**
+1. **What are recipes?** - Quote relevant sections from README about reactive programs
+2. **Recipe examples** - Show a simple example from `packages/patterns/`
+3. **UI components** - Brief look at `packages/ui/` and `ct-` prefixed components
+4. **How recipes become charms** - Deployment and linking concepts
+5. **Development commands** - `/recipe-dev`, `/imagine-recipe`, `/explore-recipe`
+
+### Path B: Runtime That Enables Information Flow
+*"I chose: The runtime that enables information flow analysis and storage"*
+
+**Explore in order:**
+1. **Runtime architecture** - Quote architecture sections about distributed runtime
+2. **Core packages** - Look at runner, storage, and execution-related packages
+3. **Information flow concepts** - How data moves and is tracked
+4. **Security model** - Sandbox execution and privacy features
+5. **Development setup** - How to work on runtime components
+
+### Path C: Application Layer Users Access
+*"I chose: The application layer that users access the platform through"*
+
+**Explore in order:**
+1. **Toolshed backend** - Quote from `packages/toolshed/README.md` about hosted platform
+2. **Shell frontend** - Quote from `packages/shell/README.md` about user interface
+3. **CT CLI** - Overview from `docs/common/CT.md`
+4. **How they work together** - Integration points and data flow
+5. **Development workflow** - Running local development environment
+
+### Path D: LLM Tooling Layer
+*"I chose: The LLM tooling layer (Claude commands, subagents, and development workflows)"*
+
+**Explore in order:**
+1. **Commands overview** - Quote from `.claude/commands/README.md` about available workflows
+2. **Command categories** - Recipe development, workflow management, research, etc.
+3. **Integration setup** - Quote from `deps.md` about MCP integrations (Linear, Playwright)
+4. **Development assistance patterns** - How LLMs help with Common Tools development
+5. **Creating new commands** - How this onboarding command was built
+
+## Navigation Support
+
+**Throughout any path, users can access:**
+- **Available commands**: List `.claude/commands/` and read `.claude/commands/README.md`
+- **Integration setup**: Review `deps.md` for tools and MCP integrations
+- **Development guidelines**: Reference `CLAUDE.md` for coding standards
+- **Research commands**: Use `/research` to dive deeper into specific areas
+
+## Adventure Branches
+
+**Users can switch paths or dive deeper:**
+- From Recipe Development → explore Runtime internals
+- From Runtime → understand Application layer integration
+- From Application layer → try Recipe development
+- Or combine multiple paths based on curiosity
+
+## Completion Indicators
+
+**Each path concludes when the user can:**
+- Navigate to relevant information sources independently
+- Understand their chosen area's development workflow
+- Know what commands/tools to use for their interests
+- Have clear next steps for hands-on work
+
+## Notes for Claude
+
+**Critical: This is a guided discovery experience, not a fire-hose lecture:**
+
+- **Start tiny and build** - Don't read multiple files at once. Start with one small section
+- **Quote small chunks** - Show 2-3 sentences from files, not entire sections
+- **Wait for their reaction** - After each quote, ask what they think and WAIT for response
+- **Follow their energy** - Only show more based on what sparked their curiosity
+- **Never charge ahead** - Resist the urge to show everything; be patient and responsive
+- **Let silence be OK** - Give them time to process and respond
+
+**Example flow:**
+1. Read just the "What is Common Tools?" section from README.md
+2. Quote the first paragraph: "Common Tools is a new distributed computing platform..."
+3. Ask: "What's your first reaction to this?"
+4. Wait for user response
+5. Based on their response, show ONLY the next relevant small piece
+6. Repeat this cycle
+
+**What NOT to do:**
+- Don't read README.md + list packages + read commands README all at once
+- Don't summarize what you found after reading files
+- Don't offer too many paths; the four focused paths are sufficient based on their actual interest
+
+**Key principle:** The user should feel like they're discovering things themselves with Claude as a helpful guide, not receiving a presentation.
diff --git a/.claude/commands/recipe-dev.md b/.claude/commands/recipe-dev.md
new file mode 100644
index 000000000..dff78ce4a
--- /dev/null
+++ b/.claude/commands/recipe-dev.md
@@ -0,0 +1,337 @@
+# Interactive Recipe Development Script
+
+This script guides Claude through recipe development with the `ct` utility after initial space setup. Claude should follow these steps to help users modify recipes, create new ones, and network them together.
+
+## Prerequisites
+
+**Before starting recipe development:**
+- User should have already run the space setup script or have an existing space
+- Claude MUST read the common CT setup instructions in `docs/common/CT.md`
+
+## Script Flow for Claude
+
+### STEP 0: Determine Starting Point
+
+**Check if user provided configuration:**
+- If user provides a config file (like `.common.json`) with space, api-url, key, and recipes paths:
+ 1. Read the config
+ 2. Test the connection: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]`
+ 3. Show what charms exist in the space (if any)
+ 4. Ask: "What would you like to do with this space?" (e.g., modify existing recipe, create new one, adjust networking)
+ 5. Skip to STEP 2
+
+- If NO config provided:
+ 1. Ask: "Do you have an existing CommonTools space set up, or would you like help setting one up?"
+ 2. Continue to STEP 1 for setup
+
+### STEP 1: Initial Setup and Context (ONLY if no config provided)
+
+**Read common setup instructions:**
+- First, read `docs/common/CT.md` for shared CT binary setup
+- Follow those instructions for:
+ - CT binary check
+ - Identity management
+ - Environment setup
+ - Parameter collection (API URL, space name, recipe path)
+ - Recipe development TypeScript setup (user should run `ct init` in recipes directory)
+
+**Verify existing space:**
+- Run: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]`
+- Show user the existing charms in their space
+- Ask user what they want to work on (modify existing recipe, create new one, or adjust networking)
+
+### STEP 2: Recipe Development Workflows
+
+Before working on recipes it is recommended to search for `COMPONENTS.md` and `RECIPES.md` files in the `docs/common` folder.
+
+Read `HANDLERS.md` when confused about event handler errors.
+
+#### Workflow A: Modifying Existing Recipes
+
+**Get recipe source:**
+1. Ask user which charm they want to modify (show charm list if needed)
+2. Run: `./dist/ct charm getsrc --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id]`
+3. Save the output to a temporary file or show user the current source
+4. Ask what changes they want to make
+
+**Edit recipe:**
+1. Guide user through making changes to the recipe code
+2. If saving to file: Create a temporary file with the modified recipe
+3. (Optional) Only if user requests or if deployment fails: Test syntax locally: `./dist/ct dev [modified-recipe-path] --no-run`
+4. If syntax errors occur, help fix them
+
+**Update recipe source:**
+1. Update the charm:
+ `./dist/ct charm setsrc --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] [modified-recipe-path]`
+2. Verify update: `./dist/ct charm inspect --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id]`
+3. Explain what changed and how it affects the charm's behavior
+
+#### Workflow B: Creating New Recipes
+
+**Design new recipe:**
+1. Ask user what the recipe should do
+2. Help them understand:
+ - What inputs it needs (other charm results, well-known cells, user inputs)
+ - What outputs it should produce
+ - What processing logic is required
+
+**Create recipe file:**
+1. Ensure TypeScript setup is current: User should run `ct init` manually in their recipes directory
+2. Guide user through creating a new .tsx file
+3. Start with a template based on their requirements
+4. (Optional) Only if user requests or if deployment fails: Test syntax: `./dist/ct dev [new-recipe-path] --no-run`
+5. If syntax errors occur, iterate on the recipe until it's correct
+
+**Deploy new charm:**
+1. Deploy the charm directly: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [new-recipe-path]`
+2. Record the new CHARM_ID
+3. Help user connect it to other charms as needed
+
+#### Workflow C: Networking and Linking
+
+**Inspect current connections:**
+1. For each charm, run: `./dist/ct charm inspect --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id]`
+2. Show user the current inputs and outputs
+3. Identify unconnected inputs or useful outputs
+
+**Create new links:**
+1. Ask user what data flow they want to create
+2. Help them understand source → target relationships
+3. Execute links: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] [source-charm]/[field] [target-charm]/[input-field]`
+4. Verify the link worked by inspecting the target charm
+
+NOTE: some recipes take the well known `allCharms` cell as a linked input which has the ID `baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye`, this can be achieved like so:
+
+`./dist/ct charm link baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [recipe]/allCharms`
+
+**Remove links (if needed):**
+1. If user wants to disconnect charms, guide them through identifying which links to remove
+2. Use appropriate unlink commands (if available in ct)
+
+### STEP 3: Advanced Recipe Development
+
+**Working with complex data flows:**
+1. Help user visualize the data flow between charms
+2. Suggest intermediate processing recipes if needed
+3. Guide creation of aggregator or transformer recipes
+
+**Debugging recipes:**
+1. Use inspect commands to see actual data: `./dist/ct charm inspect --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] --json`
+2. Help user understand why recipes might not be producing expected results
+3. Suggest logging or debug outputs in recipes
+
+**Direct data manipulation with cell operations:**
+Use the new cell get/set commands for precise data debugging and manipulation:
+
+```bash
+# Inspect specific data fields
+./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] config/apiKey
+./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] items/0/status
+
+# Set test data for debugging
+echo '"test-value"' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] debugField
+echo '[{"test": true}]' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] testItems
+
+# Reset or clear problematic data
+echo 'null' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] problematicField
+```
+
+**Recipe patterns:**
+- **Filter recipes**: Take collection input, output filtered subset
+- **Transformer recipes**: Convert data from one format to another
+- **Aggregator recipes**: Combine multiple inputs into single output
+- **Generator recipes**: Create new data based on inputs
+- **Side-effect recipes**: Perform actions (send emails, create files, etc.)
+
+#### Workflow D: Multi-File Recipe Development
+
+**Understanding multi-file recipes:**
+Multi-file recipes allow you to compose functionality from multiple source files. When deployed, all imported files are bundled together into a self-contained charm.
+
+**Key concepts:**
+1. **Import/Export pattern**: Export schemas, types, and even entire recipes from one file, import them in another
+2. **Self-contained deployment**: When you deploy a recipe that imports others, CT bundles all dependencies
+3. **Two deployment strategies**:
+ - **Single charm**: Deploy the main recipe that imports others (creates one bundled charm)
+ - **Linked charms**: Deploy each recipe separately and link their outputs/inputs
+
+**Common pitfalls and solutions:**
+
+1. **Schema mismatches between linked charms**:
+ - Problem: Charm A outputs `{items: [...]}` but Charm B expects `{source: {items: [...]}}`
+ - Solution: Carefully design schemas. Consider having "adapter" recipes if needed
+ - Better: Export shared schemas from a common file
+
+2. **File organization confusion**:
+ - Problem: Multiple versions of the same recipe in different locations
+ - Solution: Use clear folder structure (e.g., `recipes/feature-name/main.tsx`)
+ - Always clean up old versions after reorganizing
+
+3. **Deployment vs development paths**:
+ - Problem: Import paths work locally but fail when deployed
+ - Solution: Use relative imports (`./list.tsx` not absolute paths)
+ - The recipe will be compiled during deployment, which will catch any syntax errors
+
+**Best practices for multi-file recipes:**
+
+1. **Export reusable schemas**:
+ ```typescript
+ // list.tsx
+ export const TodoItemSchema = { ... };
+ export const TodoListSchema = { ... };
+
+ // suggestions.tsx
+ import { TodoListSchema } from "./list.tsx";
+ ```
+
+2. **Clear separation of concerns**:
+ - Core functionality in one file
+ - UI enhancements in another
+ - Shared utilities in a common file
+
+3. **Test incrementally**:
+ - Test each file independently first
+ - Test the composed recipe locally
+ - Deploy and verify linking works
+
+4. **Debug multi-file deployments**:
+ - Use `ct charm getsrc` to verify what was actually deployed
+ - Check that all files were bundled correctly
+ - Inspect charm inputs/outputs to ensure schemas match
+
+### STEP 4: Testing and Validation
+
+**Test recipe changes:**
+1. After any modification, inspect affected charms
+2. Trace data flow through the network
+3. Verify outputs match expectations
+
+**Create test scenarios:**
+1. Help user create test charms with known inputs
+2. Connect to recipes being developed
+3. Verify outputs are correct
+
+### Common Recipe Development Tasks
+
+**Finding recipe examples:**
+- **Search patterns package first:** Look in `packages/patterns` for related examples and reusable components
+- Search user's recipe repo: `find [recipe-path] -name "*.tsx" -type f | xargs grep -l "[pattern]"`
+- Show similar recipes as examples
+- Help adapt existing recipes for new purposes
+
+**Understanding recipe structure:**
+- Explain CommonTools recipe format
+- Show how to define inputs, outputs, and processing
+- Guide on using UI components and controls
+
+**Handler pattern for UI interactions:**
+Handlers in CommonTools follow a specific pattern for managing UI events and state:
+
+```typescript
+// Handler definition: handler(eventSchema, stateSchema, handlerFunction)
+const myHandler = handler(
+ {}, // Event schema (data from UI events like clicks)
+ { // State schema (data the handler needs to operate on)
+ type: "object",
+ properties: {
+ items: { type: "array" },
+ someValue: { type: "string" },
+ },
+ required: ["items"],
+ },
+ (event, { items, someValue }) => {
+ // Handler function receives (event, state)
+ // Modify state directly
+ items.push(someValue);
+ }
+);
+
+// Handler invocation: pass state data matching the state schema
+
+```
+
+Key points:
+- Event schema: For UI event data (usually empty `{}` for simple clicks)
+- State schema: Declares what data the handler needs access to
+- Handler invocation: Pass an object matching the state schema
+- Handler function: Receives `(event, state)` - destructure state as needed
+
+**Performance considerations:**
+- Advise on efficient data processing
+- Suggest when to use pagination or batching
+- Help optimize expensive operations
+
+### Error Handling
+
+**Recipe syntax errors:**
+- Parse error messages from `ct dev`
+- Guide user to fix TypeScript/JSX issues
+- Suggest proper imports and types
+
+**Runtime errors:**
+- Help interpret charm execution errors
+- Debug data type mismatches
+- Fix missing or incorrect inputs
+
+**Network errors:**
+- Diagnose connection issues
+- Verify API URL and identity
+- Check space permissions
+
+### Notes for Claude
+
+- Do NOT test recipe syntax before deploying unless explicitly requested by user or if deployment fails
+- The deployment process (`ct charm new` or `ct charm setsrc`) will compile and validate the recipe automatically
+- Keep track of charm IDs when creating new ones
+- Help user understand data flow direction (source → target)
+- Encourage incremental development and testing
+- Save modified recipes to files before using setsrc
+- Use inspect commands liberally to show current state
+- Use cell get/set commands for precise data debugging and testing
+- Leverage cell operations to set up test data and verify recipe behavior
+
+### Quick Command Reference
+
+**Development Commands:**
+```bash
+# Get recipe source
+./dist/ct charm getsrc --identity [key] --api-url [url] --space [space] --charm [id]
+
+# Update recipe source
+./dist/ct charm setsrc --identity [key] --api-url [url] --space [space] --charm [id] [recipe-file]
+
+# Test recipe syntax
+./dist/ct dev [recipe-file] --no-run
+
+# Create new charm
+./dist/ct charm new --identity [key] --api-url [url] --space [space] [recipe-file]
+
+# Link charms
+./dist/ct charm link --identity [key] --api-url [url] --space [space] [source]/[field] [target]/[input]
+
+# Inspect charm
+./dist/ct charm inspect --identity [key] --api-url [url] --space [space] --charm [id]
+
+# Get cell data
+./dist/ct charm get --identity [key] --api-url [url] --space [space] --charm [id] [path]
+
+# Set cell data
+echo '[json-value]' | ./dist/ct charm set --identity [key] --api-url [url] --space [space] --charm [id] [path]
+
+# List all charms
+./dist/ct charm ls --identity [key] --api-url [url] --space [space]
+```
+
+### Recipe Development Best Practices
+
+1. **Start simple**: Create basic recipes first, then add complexity
+2. **Test incrementally**: Deploy and test each change
+3. **Use meaningful names**: Name charms and fields descriptively
+4. **Document recipes**: Add comments explaining logic
+5. **Handle errors**: Include error handling in recipes
+6. **Validate inputs**: Check data types and required fields
+7. **Output consistently**: Use predictable output structures
+
+Remember: Recipe development is iterative. Help users build step by step, testing along the way.
diff --git a/.claude/commands/research.md b/.claude/commands/research.md
new file mode 100644
index 000000000..cf30db840
--- /dev/null
+++ b/.claude/commands/research.md
@@ -0,0 +1,66 @@
+# Research Command
+
+Research topics thoroughly using codebase exploration, documentation review, and analysis.
+
+## Usage
+
+`/research [question or topic]`
+
+## Process
+
+Launch a research subagent using the Task tool, then always ask about deployment:
+
+```
+Task: Research [topic/question]
+
+You are a research specialist. Conduct thorough investigation of the topic using all available tools.
+
+**First, learn how to use ct:** Read docs/common/CT.md to understand how to use the CommonTools system.
+
+**Your Task:**
+1. **Consult the wiki first** - Read .claude/commands/search-wiki.md to learn how to check for existing knowledge on this topic
+2. **Explore the codebase** using Glob, Grep, and Read tools
+3. **Review documentation** (README.md, CLAUDE.md, etc.)
+4. **Analyze git history** for relevant changes
+5. **Examine tests** to understand behavior
+6. **Provide comprehensive findings** with specific code references
+
+**Return to me:** Detailed research report with executive summary, analysis, architecture insights, and actionable findings.
+
+**CRITICAL:** After delivering the report, you MUST ask the user if they want to deploy it using the .claude/commands/deploy-research.md command.
+```
+
+## Research Methodology
+
+### Core Steps
+- **Learn ct usage first** - Read docs/common/CT.md to understand CommonTools
+- **Start with wiki search** to avoid duplicating previous research
+- Use Task tool for systematic codebase exploration
+- Check recent git history and commits
+- Review existing documentation and tests
+- Find relevant files, patterns, and implementations
+- Provide specific file paths and line numbers
+
+### Required Final Step
+- **Always ask about deployment** - Even if the user doesn't seem interested, you must offer the .claude/commands/deploy-research.md option
+
+### Output Format
+- **Executive summary** of key findings
+- **Detailed analysis** with code references
+- **Architecture insights** and design decisions
+- **Recent changes** and development history
+- **Recommendations** or next steps if applicable
+
+## Required: Ask About Deployment
+
+After research is complete, you MUST ask: "Would you like me to deploy this as a CommonTools research report?"
+
+If yes, use the .claude/commands/deploy-research.md command. Make sure to read docs/common/CT.md first to understand how to use the CommonTools system properly.
+
+## When to Use
+
+- Understanding how specific code works
+- Exploring new areas of the codebase
+- Before making architectural changes
+- Investigating bugs or issues
+- Learning about patterns and conventions
diff --git a/.claude/commands/review-code.md b/.claude/commands/review-code.md
new file mode 100644
index 000000000..18e557404
--- /dev/null
+++ b/.claude/commands/review-code.md
@@ -0,0 +1,48 @@
+# Code Review Guidelines
+
+Review the code we have written with these priorities:
+
+## Core Principles
+
+**Channel the spirit of Rich Hickey**: Embrace simplicity, embrace immutability, embrace data.
+
+Also consider the lessons of Erlang (Joe Armstrong), Elixir (José Valim), Elm (Evan Czaplicki), and Rust.
+
+## Specific Focus Areas
+
+### Code Structure
+- **Extract pure functions** for common logic and reusable operations
+- **Pay attention to the story that parameters and names tell** - use the code as a self-documenting structure
+- **Examine similar code** to ensure consistency and avoid duplication
+- **Use consistent naming conventions** that clearly express intent
+- **Decoupled modules** - consider inversion of control, decomposition, and breaking apart large files by extracting clear domains
+
+### Type Safety & Data
+- **Declare types for repeated shapes** - avoid inline type definitions
+- **Do not work around type issues** with `any` or excessive null checks and if statements
+- **Make invalid states unrepresentable** - follow the CLAUDE.md guidelines on avoiding ambiguous types
+
+### Error Handling
+- **Handle errors gracefully, or design APIs that make errors impossible**
+- Prefer consistent result types (Result) to throwing for routinely-fallible operations
+- Prefer throwing over silent failures or unclear undefined returns
+- Follow the error handling patterns outlined in CLAUDE.md
+
+### Functional Style
+- **Prefer a pure, functional programming style** over imperative approaches
+- Favor immutable data transformations in library code
+- Minimize side effects and make them explicit when necessary
+
+
+### Testing
+- Test all pure functions
+- Use tests to cement expectations, not to create busywork and upkeep
+- Aim for code coverage but do not worry about complex integration tests
+- Ensure tests are always kept up to date during refactors
+
+### Functional-Reactive Programming
+- When working on recipes, favor functional-reactive programming patterns to handle asynchronous data streams and side effects. See @recipe-dev.md.
+
+# Caveats
+
+$ARGUMENTS
diff --git a/.claude/commands/review-docs.md b/.claude/commands/review-docs.md
new file mode 100644
index 000000000..fd8cbc5d2
--- /dev/null
+++ b/.claude/commands/review-docs.md
@@ -0,0 +1,76 @@
+# Documentation Review Command
+
+Review documentation for accuracy, completeness, and developer workflow issues, with a focus on areas with recent development activity.
+
+## Process
+
+### Phase 1: Context Discovery (5-10 minutes)
+1. **Check recent git activity** to identify areas of active development:
+ - `git log --oneline --since="2 weeks ago" -- "*.md"` - Recent doc changes
+ - `git log --oneline --since="2 weeks ago" --name-only` - Recent code changes
+ - Focus on frequently modified packages and new features
+
+2. **Quick documentation landscape scan**:
+ - Identify main documentation files and their relationships
+ - Check for symlinks, aliases, or generated files
+ - Spot obvious critical issues (broken links, missing core docs)
+
+### Phase 2: Targeted Analysis (15-20 minutes)
+3. **Focus on developer workflow blockers**:
+ - Can someone follow the setup/development instructions?
+ - Do import statements and code examples actually work?
+ - Are package paths and directory structures accurate?
+
+4. **Cross-reference docs with recent code changes**:
+ - Check if areas with recent development have up-to-date documentation
+ - Verify that new features or architectural changes are documented
+ - Look for implementation details that contradict existing docs
+
+5. **Verify structural claims**:
+ - Do referenced packages, files, and directories exist?
+ - Are import paths and command examples correct?
+ - Do links resolve properly?
+
+### Phase 3: Prioritized Reporting (5 minutes)
+6. **Categorize findings by impact**:
+ - **Critical**: Blocks developer workflow, incorrect instructions
+ - **High**: Significant confusion or outdated major features
+ - **Medium**: Minor inconsistencies in active development areas
+ - **Low**: Stylistic issues, minor formatting problems
+
+7. **Ask clarifying questions** when scope is unclear:
+ - "Should I focus on specific packages or workflows?"
+ - "Are there particular types of issues you're most concerned about?"
+ - "Should I prioritize accuracy fixes or structural improvements?"
+
+## Focus Areas
+
+### Primary Concerns
+- **Workflow blockers**: Instructions that don't work
+- **Import/reference accuracy**: Code examples that fail
+- **Recent change documentation**: Areas with active development
+- **Package relationship clarity**: How components fit together
+
+### Secondary Concerns
+- Architectural documentation gaps
+- Missing guides for complex patterns
+- Cross-reference accuracy
+- Link integrity
+
+### Defer Unless Specifically Requested
+- Minor formatting inconsistencies
+- Stylistic preferences
+- Comprehensive style guide compliance
+- Low-impact wording improvements
+
+## Key Questions to Answer
+1. **"If someone tried to follow these docs today, where would they get stuck?"**
+2. **"What recent changes might have made existing docs inaccurate?"**
+3. **"Are there new features or patterns that need documentation?"**
+4. **"Do the most actively developed areas have adequate documentation?"**
+
+## Verification Before Fixes
+- Confirm file relationships (symlinks, aliases) before flagging duplicates
+- Verify proposed fixes are correct (check actual package names, paths)
+- Test that suggested import statements and commands actually work
+- Ask for confirmation on significant structural changes
\ No newline at end of file
diff --git a/.claude/commands/search-wiki.md b/.claude/commands/search-wiki.md
new file mode 100644
index 000000000..a4ca13478
--- /dev/null
+++ b/.claude/commands/search-wiki.md
@@ -0,0 +1,47 @@
+# Search Wiki Command
+
+Search the project wiki for existing knowledge, solutions, and documentation. Use this proactively before solving problems or when you need information.
+
+## Command Pattern
+
+When you need to search the wiki, launch a search subagent using the Task tool:
+
+```
+Task: Search wiki for [topic/problem/keywords]
+
+You are a wiki search specialist. Your job is to search the project wiki for relevant information and present findings clearly.
+
+**Standard Parameters:**
+- Identity: claude.key
+- API URL: https://toolshed.saga-castor.ts.net/
+- Space: 2025-wiki
+- Wiki Charm ID: baedreigkqfmhscbwwfhkjxicogsw3m66nxbetlhlnjkscgbs56hsqjrmkq
+
+**Your Task:**
+0. **First, learn how to use ct:** Read docs/common/CT.md to understand how to use the CommonTools system.
+
+1. Get all wiki content: `./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space 2025-wiki --charm baedreigkqfmhscbwwfhkjxicogsw3m66nxbetlhlnjkscgbs56hsqjrmkq wiki`
+
+2. Search through the content for: [specific search criteria]
+
+3. Present your findings as:
+ - **Relevant pages found**: List page keys and brief descriptions
+ - **Key excerpts**: Most relevant content snippets
+ - **Exact solutions**: If you find direct solutions to the problem
+ - **Related information**: Similar or adjacent topics that might help
+
+4. If you find specific pages worth reading in full, get them with: `./dist/ct charm get [params] wiki/[page-key]`
+
+**Return to me**: A clear summary of what you found, with actionable information extracted and organized for immediate use.
+```
+
+## When to Search
+- Before starting new development work
+- When encountering errors or problems
+- Before asking user for help
+- When exploring unfamiliar code areas
+- When debugging complex issues
+
+The subagent will handle the command execution and content analysis, returning organized results.
+
+The final response should be a report based on what was found by the subagent giving as much detail as appropriate to the user's query.
diff --git a/.claude/commands/setup-space.md b/.claude/commands/setup-space.md
new file mode 100644
index 000000000..a0ea967f2
--- /dev/null
+++ b/.claude/commands/setup-space.md
@@ -0,0 +1,174 @@
+# Interactive Space Setup Script
+
+This script guides Claude through setting up a complete space with the `ct` utility. Claude should follow these steps to interactively help the user set up recipes and network them together in a space.
+
+## Script Flow for Claude
+
+### STEP 1: Initial Setup Check and Preparation
+
+**Read common setup instructions:**
+- First, read `docs/common/CT.md` for shared CT binary setup and configuration
+- Follow those instructions for:
+ - Checking if user is in the right directory (should be in `labs`)
+ - CT binary check and build if needed
+ - Identity keyfile management
+ - Environment variable setup (CT_API_URL and CT_IDENTITY)
+ - API URL collection and connectivity test
+ - **Note:** Claude Code cannot cd into directories. User should run `ct init` manually in their recipes directory for TypeScript setup
+
+### STEP 2: Space-Specific Setup
+
+**Get space name:**
+- Ask user what they want to name their space (no spaces, lowercase recommended)
+- Store as variable for commands
+
+**Find recipe path:**
+- Follow the recipe path discovery process from common/ct.md
+- **Important:** User must run `ct init` manually in their recipes directory (Claude Code cannot cd into directories)
+- Look specifically for these key recipes:
+ - `find [user-provided-path] -name "*gmail*" -o -name "*simple-list*" -o -name "*page*" -o -name "*factory*" | head -10`
+ - Verify key recipes exist: `ls -la [user-provided-path]/gmail.tsx [user-provided-path]/simple-list.tsx [user-provided-path]/page.tsx [user-provided-path]/factory.tsx`
+ - If recipes are in a subfolder, help them find the right path: `find [user-provided-path] -name "recipes" -type d`
+
+### STEP 3: Execute Space Setup Workflow
+
+**For each step below, Claude should:**
+1. Explain what we're doing
+2. Run the command
+3. Capture and show the charm ID from output
+4. Verify the operation worked
+5. Store charm IDs for later linking
+
+**Create simple-list charm:**
+- Verify recipe exists: `ls -la [recipe-path]/simple-list.tsx`
+- Run: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [recipe-path]/simple-list.tsx`
+- Extract and record the SIMPLE_LIST_CHARM_ID from output
+- Verify: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]`
+
+**Create gmail charm:**
+- Verify recipe exists: `ls -la [recipe-path]/gmail.tsx`
+- Run: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [recipe-path]/gmail.tsx`
+- Record GMAIL_CHARM_ID
+
+**Create page charm:**
+- Verify recipe exists: `ls -la [recipe-path]/page.tsx`
+- Run: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [recipe-path]/page.tsx`
+- Record PAGE_CHARM_ID
+- Link well-known charms list to page allCharms input: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [PAGE_CHARM_ID]/allCharms`
+- Link well-known charms list to page mentionable input: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [PAGE_CHARM_ID]/mentionable`
+
+**Create factory charm:**
+- Verify recipe exists: `ls -la [recipe-path]/factory.tsx`
+- Run: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [recipe-path]/factory.tsx`
+- Record FACTORY_CHARM_ID
+- Link well-known charms list to factory allCharms input: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [FACTORY_CHARM_ID]/allCharms`
+- Link well-known charms list to factory mentionable input: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [FACTORY_CHARM_ID]/mentionable`
+
+### STEP 4: Final Verification and Optional Setup
+
+**Show final space:**
+- Run: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]`
+- Explain what each charm does
+
+**Offer additional recipes:**
+- Run: `find [recipe-path] -name "*.tsx" -type f`
+- Ask user if they want to deploy any additional recipes
+- For each additional recipe: verify, create charm, ask about linking needs
+
+### Error Handling
+
+**General error handling:**
+- Refer to error handling section in `docs/common/CT.md` for common issues
+- Don't continue to dependent steps until current step works
+
+**Space setup specific errors:**
+
+**If recipe files are missing:**
+- Ask user to double-check the recipe repository path
+- Help user locate them: `find [user-provided-path] -name "*.tsx" -type f`
+- Look in common subdirectories: `find [user-provided-path] -name "recipes" -type d`
+- Ask user to provide the correct path to their recipe repository
+- Verify all required files exist before proceeding: `ls -la [corrected-path]/simple-list.tsx [corrected-path]/gmail.tsx [corrected-path]/page.tsx [corrected-path]/factory.tsx`
+
+**If charm creation fails:**
+- Test recipe syntax with `./dist/ct dev [recipe] --no-run`
+- Check network connectivity
+- Verify identity file permissions
+
+### Data Management During Setup
+
+**Viewing charm data:**
+Use the new cell get commands to inspect charm data during setup:
+```bash
+# View charm input parameters
+./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] title
+
+# View nested data
+./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] config/apiKey
+
+# View array elements
+./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] items/0/name
+```
+
+**Modifying charm data:**
+Use cell set commands to configure charms during setup:
+```bash
+# Set simple values
+echo '"Updated Title"' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] title
+
+# Set configuration objects
+echo '{"apiKey": "your-key", "enabled": true}' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] config
+
+# Set array data
+echo '[{"name": "Item 1"}, {"name": "Item 2"}]' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] items
+```
+
+### Notes for Claude
+
+- Always run commands and show real output to user
+- Extract and track charm IDs as you go (they start with "bafy")
+- Verify each step worked before proceeding
+- Be helpful if user needs to troubleshoot
+- Ask questions when paths or parameters are unclear
+- Keep track of what's been created to avoid duplicates
+- Use cell get/set commands to inspect and configure charm data as needed
+
+### Important: External Recipe Repository Handling
+
+- **Recipes are NOT in the labs repo** - they are in a separate repository
+- **Always ask user for the full path** to their recipe repository first
+- **Example paths might be:**
+ - `/Users/username/my-recipes`
+ - `../my-recipe-repo`
+ - `/home/user/projects/recipe-collection`
+- **Validate the path exists** before proceeding: `ls -la [user-path]`
+- **Find recipes in their repo** with: `find [user-path] -name "*.tsx" | head -10`
+- **Use full paths in all ct commands** - don't assume recipes are local to labs
+- **If user gives wrong path**, help them find the right location with find commands
+
+## Reference Information for Claude
+
+### Key Commands and Linking Concepts:
+- See `docs/common/CT.md` for:
+ - Complete list of CT commands
+ - Understanding linking syntax and concepts
+ - Examples of charm-to-charm and well-known ID linking
+
+### Expected Workflow:
+1. Simple-list charm → standalone list for basic items
+2. Gmail charm → standalone email fetcher
+3. Page charm → link well-known charms list to its allCharms and mentionable inputs
+4. Factory charm → link well-known charms list to its allCharms and mentionable inputs
+
+### Well-Known IDs:
+- Charms list in any space: `baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye`
+
+### Space Setup Specific Notes:
+- See `docs/common/CT.md` for general troubleshooting
+- Recipe files needed for initial setup:
+ - simple-list.tsx
+ - gmail.tsx
+ - page.tsx
+ - factory.tsx
+- Verify all these recipes exist before starting the setup workflow
+- Remember: Claude Code cannot cd into directories - user must manually run `ct init` in their recipes directory
diff --git a/.claude/commands/todos.md b/.claude/commands/todos.md
new file mode 100644
index 000000000..e24f8a8eb
--- /dev/null
+++ b/.claude/commands/todos.md
@@ -0,0 +1,45 @@
+# Todos Command
+
+Manage todo lists using CommonTools. Deploy new lists or work with existing ones.
+
+## Usage
+
+`/todos [item]` - Add item to todo list (deploys new if needed)
+`/todos [URL]` - Work with existing todo list
+`/todos` - Deploy new empty todo list
+
+## Command Pattern
+
+Direct todo management using CommonTools:
+
+**Standard Parameters:**
+- Identity: claude.key
+- API URL: https://toolshed.saga-castor.ts.net/
+- Recipe: recipes/todo-list.tsx
+- Space: Use date-based naming like `2025-07-15-claude-dev` (format: YYYY-MM-DD-claude-dev)
+
+**Quick CT Commands:**
+- READ: `./dist/ct charm get [params] --charm [id] [path]`
+- SET: `echo '[value]' | ./dist/ct charm set [params] --charm [id] [path]`
+- CALL: `echo '[json]' | ./dist/ct charm call [params] --charm [id] [handler]`
+
+**Todo Operations:**
+- Add item: `echo '{"title": "text"}' | ct charm call [params] --charm [id] addItem`
+- Mark done: `echo 'true' | ct charm set [params] --charm [id] items/0/done`
+- Read items: `ct charm get [params] --charm [id] items`
+
+**Scenarios:**
+- [SCENARIO A: URL provided] Extract space/charm from URL, work with existing list
+- [SCENARIO B: Add item] Find/deploy todo list in date-based space (YYYY-MM-DD-claude-dev), add item
+- [SCENARIO C: Deploy only] Deploy new list, provide URL and usage
+
+**Return**: Confirmation, URL, current items, usage instructions
+
+## Benefits
+
+- Persistent task tracking across Claude sessions
+- Direct data manipulation via SET commands
+- Full web UI via CommonTools interface
+- API accessible for automation
+
+Simple, fast, persistent todo management.
diff --git a/.claude/commands/update-wiki.md b/.claude/commands/update-wiki.md
new file mode 100644
index 000000000..b4d629f44
--- /dev/null
+++ b/.claude/commands/update-wiki.md
@@ -0,0 +1,55 @@
+# Update Wiki Command
+
+Add knowledge, solutions, progress reports, and documentation to the project wiki. Use this to capture learnings and insights for future sessions.
+
+## Command Pattern
+
+When you need to update the wiki, follow these steps:
+
+**Standard Parameters:**
+- Identity: claude.key
+- API URL: https://toolshed.saga-castor.ts.net/
+- Space: 2025-wiki
+- Wiki Charm ID: baedreigkqfmhscbwwfhkjxicogsw3m66nxbetlhlnjkscgbs56hsqjrmkq
+
+**Process:**
+
+0. **First, learn how to use ct:** Read docs/common/CT.md to understand how to use the CommonTools system.
+
+1. Choose appropriate page key following naming conventions:
+ - Solutions: `[problem-type]-solution` or `fix-[specific-issue]`
+ - How-to guides: `how-to-[action]`
+ - Progress reports: `progress-YYYY-MM-DD-[username]-[topic]`
+ - Tips: `tips-[technology/area]`
+ - Timestamped entries: `YYYY-MM-DD-[username]-[topic]`
+
+2. Format content using appropriate template:
+ - **Problem-Solution**: Problem description, solution steps, context
+ - **Progress Report**: Status, findings, next steps, blockers
+ - **How-To Guide**: Overview, steps, examples, troubleshooting
+
+3. Create JSON file and add to wiki:
+ ```bash
+ cat > /tmp/wiki-update.json << 'EOF'
+ {
+ "key": "your-page-key",
+ "value": "# Title\n\nFormatted content here..."
+ }
+ EOF
+
+ cat /tmp/wiki-update.json | ./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space 2025-wiki --charm baedreigkqfmhscbwwfhkjxicogsw3m66nxbetlhlnjkscgbs56hsqjrmkq update
+ ```
+
+4. Verify the update worked by reading it back:
+ ```bash
+ ./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space 2025-wiki --charm baedreigkqfmhscbwwfhkjxicogsw3m66nxbetlhlnjkscgbs56hsqjrmkq wiki/[your-page-key]
+ ```
+
+## When to Update
+- After solving non-trivial problems
+- When discovering useful patterns or techniques
+- For ongoing complex work (progress reports)
+- When finding workarounds for common issues
+- At end of debugging sessions with lessons learned
+
+Updates should be well-formatted and include all necessary context for future reference.
diff --git a/.claude/commands/walk-space.md b/.claude/commands/walk-space.md
new file mode 100644
index 000000000..aed62470c
--- /dev/null
+++ b/.claude/commands/walk-space.md
@@ -0,0 +1,269 @@
+# CommonTools Space Mapping Workflow
+
+**INSTRUCTIONS FOR AI AGENT**: When the `/walk-space` command is invoked, execute the steps in this workflow to map a CommonTools space. Do not explain the workflow - actually run the commands and store data in the memory knowledge graph.
+
+## Overview
+
+This workflow enables semantic mapping and change tracking of CommonTools spaces using the memory MCP knowledge graph system. It creates a searchable knowledge graph of charm states, relationships, and evolution over time.
+
+## Core Concepts
+
+### Space Mapping
+- **Goal**: Build a semantic understanding of a CommonTools space
+- **Method**: Extract charm data and relationships, store as searchable fragments
+- **Benefit**: Enable natural language queries about space contents and history
+
+### Change Tracking
+- **Goal**: Monitor how spaces evolve over time
+- **Method**: Create timestamped snapshots and document differences
+- **Benefit**: Understand user patterns and space development
+
+## Workflow Steps
+
+### 1. Initial Space Discovery
+
+```bash
+# First, list all charms in the space
+./dist/ct charm ls --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name]
+
+# Optionally generate visual map
+./dist/ct charm map --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name]
+./dist/ct charm map --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --format dot
+```
+
+### 2. Data Extraction
+
+For each charm discovered:
+
+```bash
+# Get key fields
+./dist/ct charm get --identity [key] --api-url [url] --space [space] --charm [charm-id] title
+./dist/ct charm get --identity [key] --api-url [url] --space [space] --charm [charm-id] tags
+./dist/ct charm get --identity [key] --api-url [url] --space [space] --charm [charm-id] outline
+# Additional fields as needed based on charm type
+```
+
+### 3. Create Knowledge Graph Entries
+
+Use `mcp__memory__add_memory` to build the knowledge graph:
+
+#### Individual Charm Documentation
+- **Name**: "Charm: [Name] ([Type])"
+- **Episode Body**: Include charm ID, type, purpose, content summary, technical details, connections
+- **Source**: "text" or "json" (for structured charm data)
+- **Source Description**: "charm [type] in [space-name]"
+- **Group ID**: "[space-name]"
+
+#### Space Relationships (JSON Format)
+- **Name**: "Space Relationships: [space-name] @ [timestamp]"
+- **Episode Body**: JSON string containing charm connections and data flows
+ ```json
+ {
+ "space": "space-name",
+ "timestamp": "ISO-timestamp",
+ "relationships": [
+ {"source": "charm-id-1", "target": "charm-id-2", "type": "data-flow"},
+ {"source": "charm-id-2", "target": "charm-id-3", "type": "connection"}
+ ]
+ }
+ ```
+- **Source**: "json"
+- **Source Description**: "relationship mapping for [space-name]"
+- **Group ID**: "[space-name]"
+
+#### Space Snapshot
+- **Name**: "Space Snapshot: [space-name] @ [ISO-timestamp]"
+- **Episode Body**: Complete state of all charms, connections, and metadata
+- **Source**: "json"
+- **Source Description**: "complete snapshot of [space-name]"
+- **Group ID**: "[space-name]"
+
+### 4. Iterative Monitoring
+
+On subsequent scans:
+
+1. **Rescan the space**
+ - List charms again
+ - Extract current data for each charm
+
+2. **Compare with previous snapshot**
+ - Check for new/removed charms
+ - Identify changed titles, tags, content
+ - Note new or removed connections
+
+3. **Document changes**
+ Use `mcp__memory__add_memory`:
+ - **Name**: "Space Changes: [space-name] @ [ISO-timestamp]"
+ - **Episode Body**: Detailed list of all changes detected, including added/removed charms, modified content
+ - **Source**: "text"
+ - **Source Description**: "changes detected in [space-name]"
+ - **Group ID**: "[space-name]"
+
+4. **Create new snapshot**
+ - Reference previous snapshot
+ - Link to change documentation
+ - Update charm states
+
+### 5. Search and Analysis
+
+#### Find specific charms
+Use the memory MCP tools:
+
+- **Semantic search for nodes**: `mcp__memory__search_memory_nodes`
+ - Query: "dog pet border collie"
+ - Query: "page recipe outliner component"
+ - Group IDs: ["[space-name]"]
+
+- **Search for facts/relationships**: `mcp__memory__search_memory_facts`
+ - Query: "data flow connections"
+ - Group IDs: ["[space-name]"]
+
+#### Track evolution
+- **Get recent episodes**: `mcp__memory__get_episodes`
+ - Group ID: "[space-name]"
+ - Last N: 10 (to see recent snapshots)
+
+- **Search for changes**: `mcp__memory__search_memory_nodes`
+ - Query: "changes modifications updates snapshot"
+ - Group IDs: ["[space-name]"]
+
+## Memory Episode Guidelines
+
+### Essential Fields
+- **Name**: Consistent naming pattern for easy identification (include type in name)
+- **Episode Body**: Structured content (text or JSON)
+- **Source**: "text" for narratives, "json" for structured data, "message" for conversations
+- **Source Description**: Descriptive context including content type and space
+- **Group ID**: Single identifier per space (like a tag, but only one allowed)
+
+### Group ID Strategy
+- Use `[space-name]` as the single group ID for all content related to that space
+- Differentiate content types through:
+ - **Name patterns**: Include type ("Charm:", "Snapshot:", "Changes:", "Reflection:")
+ - **Source descriptions**: Be specific about what kind of data it is
+ - **Episode body structure**: Use consistent formats for each type
+
+## Example Implementation Flow
+
+### Initial Space Mapping
+1. List charms using `ct charm ls`
+2. For each charm:
+ - Use `ct charm get` to extract data
+ - Add to knowledge graph with `mcp__memory__add_memory` (group: "[space-name]")
+3. Document relationships with `mcp__memory__add_memory` (source: "json", group: "[space-name]")
+4. Create baseline snapshot with `mcp__memory__add_memory` (source: "json", group: "[space-name]")
+
+### Subsequent Scans
+1. List charms again with `ct charm ls`
+2. Extract current data for comparison
+3. Search previous data: `mcp__memory__search_memory_nodes` or `mcp__memory__get_episodes` (group: "[space-name]")
+4. If changes detected:
+ - Record changes with `mcp__memory__add_memory` (group: "[space-name]")
+ - Create new snapshot with `mcp__memory__add_memory` (group: "[space-name]")
+ - Add updated charm states to graph (new episodes build on existing knowledge)
+
+## Benefits
+
+1. **Semantic Search**: Find charms by meaning, not just keywords
+2. **Change History**: Understand how spaces evolve
+3. **Relationship Mapping**: Visualize data flows and dependencies
+4. **Pattern Recognition**: Identify common usage patterns
+5. **Space Understanding**: Build AI comprehension of user intent
+6. **AI Collaboration**: Agents can contribute meaningful content based on user data
+7. **Reflective Analysis**: Generate insights and questions that enrich user documentation
+
+## Known Limitations
+
+- Embedding similarity scores may be too high (bug to be filed)
+- Circular references in charm inspect can cause errors
+- Large spaces may require pagination strategies
+
+## AI Reflection Process
+
+### Purpose
+Enable AI agents to contribute meaningful content to spaces by analyzing user data and creating reflective pages with observations, questions, and suggestions.
+
+### Workflow for Content Reflection
+
+1. **Analyze existing content**
+ - Use `mcp__memory__search_memory_nodes` to find all entities about specific topics
+ - Use `mcp__memory__search_memory_facts` to understand relationships
+ - Extract actual user data from charm fields (not just metadata)
+ - Focus on what the user has written, not technical implementation
+
+2. **Generate reflections**
+ - **Observations**: What patterns or interesting details do you notice?
+ - **Questions**: What would you like to know more about?
+ - **Suggestions**: How could the user enrich their documentation?
+ - **Pattern Analysis**: What does their documentation style reveal?
+
+3. **Deploy reflection page**
+ ```bash
+ # Deploy a new page.tsx instance
+ ./dist/ct charm new --identity [key] --api-url [url] --space [space] [page-recipe-path]
+
+ # Set meaningful title
+ echo '"Reflections on [Topic] 🤔"' | ./dist/ct charm set [params] --charm [new-charm-id] title
+
+ # Set appropriate tags
+ echo '["reflection", "analysis", "topic", "ai-observations"]' | ./dist/ct charm set [params] --charm [new-charm-id] tags
+
+ # Create outline content (write to file first to avoid escaping issues)
+ cat outline.json | ./dist/ct charm set [params] --charm [new-charm-id] outline
+
+ # Link to allCharms for mentionable functionality
+ ./dist/ct charm link [params] [allCharms-id] [new-charm-id]/mentionable
+ ```
+
+4. **Document the reflection**
+ Add to knowledge graph recording the AI contribution:
+ - Name: "AI Reflection: [Topic] @ [timestamp]"
+ - Episode Body: Include charm ID, key insights, observations, questions
+ - Source: "text"
+ - Source Description: "ai reflection on [topic] in [space-name]"
+ - Group ID: "[space-name]"
+
+### Reflection Content Guidelines
+
+- **Be specific**: Reference actual data the user entered
+- **Be curious**: Ask thoughtful questions based on the content
+- **Be helpful**: Suggest concrete ways to expand documentation
+- **Be respectful**: Frame observations positively
+- **Be relevant**: Focus on the user's interests, not technical details
+
+### Example Reflection Structure
+```json
+{
+ "root": {
+ "body": "",
+ "children": [
+ {
+ "body": "## Observations about [Topic]",
+ "children": [/* Specific observations about user's content */]
+ },
+ {
+ "body": "## Questions I am Curious About",
+ "children": [/* Thoughtful questions based on the data */]
+ },
+ {
+ "body": "## Suggestions for Enriching [Topic]",
+ "children": [/* Concrete suggestions for expansion */]
+ },
+ {
+ "body": "## Patterns I Noticed",
+ "children": [/* Analysis of documentation style and evolution */]
+ }
+ ]
+ }
+}
+```
+
+## Future Enhancements
+
+1. **Automated scanning**: Periodic space checks
+2. **Change notifications**: Alert on significant modifications
+3. **Pattern analysis**: Identify common charm combinations
+4. **Space templates**: Suggest charms based on usage patterns
+5. **Version control**: Git-like branching for space states
+6. **AI collaboration**: Multiple agents contributing different perspectives
+7. **Content suggestions**: AI-generated prompts based on existing content
\ No newline at end of file
diff --git a/.claude/page-manager-helper.sh b/.claude/page-manager-helper.sh
new file mode 100755
index 000000000..2730bef32
--- /dev/null
+++ b/.claude/page-manager-helper.sh
@@ -0,0 +1,48 @@
+#\!/bin/bash
+
+# Page Manager Helper Script
+# Usage: ./page-manager-helper.sh [command] [args...]
+
+CONFIG_FILE=".page-agent-config.json"
+RECIPE_PATH="/Users/ben/code/recipes/recipes/coralreef/page.tsx"
+SPACE="2025-07-28-berni"
+IDENTITY="claude.key"
+API_URL="https://toolshed.saga-castor.ts.net/"
+WORK_NOTES_CHARM="baedreiadodmnlokromhwaotm3tfmjkv4xpbqwtopr74zbp3g54mxp5wmby"
+
+case "$1" in
+ "get-title")
+ ./dist/ct charm get --identity $IDENTITY --api-url $API_URL --space $SPACE --charm $WORK_NOTES_CHARM title
+ ;;
+ "get-outline")
+ ./dist/ct charm get --identity $IDENTITY --api-url $API_URL --space $SPACE --charm $WORK_NOTES_CHARM outline
+ ;;
+ "get-tags")
+ ./dist/ct charm get --identity $IDENTITY --api-url $API_URL --space $SPACE --charm $WORK_NOTES_CHARM tags
+ ;;
+ "add-task")
+ if [ -z "$2" ]; then
+ echo "Usage: $0 add-task 'task description'"
+ exit 1
+ fi
+ echo "Add task functionality would be implemented here for: $2"
+ ;;
+ "list-charms")
+ ./dist/ct charm ls --identity $IDENTITY --api-url $API_URL --space $SPACE
+ ;;
+ "inspect")
+ ./dist/ct charm inspect --identity $IDENTITY --api-url $API_URL --space $SPACE --charm $WORK_NOTES_CHARM
+ ;;
+ *)
+ echo "Available commands:"
+ echo " get-title - Get the page title"
+ echo " get-outline - Get the page outline structure"
+ echo " get-tags - Get the page tags"
+ echo " list-charms - List all charms in the space"
+ echo " inspect - Inspect the work notes charm"
+ echo ""
+ echo "Configuration:"
+ echo " Space: $SPACE"
+ echo " Work Notes Charm: $WORK_NOTES_CHARM"
+ ;;
+esac
diff --git a/.claude/scripts/block-node.ts b/.claude/scripts/block-node.ts
new file mode 100644
index 000000000..1eff7b47d
--- /dev/null
+++ b/.claude/scripts/block-node.ts
@@ -0,0 +1,31 @@
+#!/usr/bin/env -S deno run --allow-read
+/**
+ * .claude/scripts/block-node.ts
+ *
+ * Claude Code Pre-Tool hook.
+ * - Blocks shell commands that invoke npm, npx, yarn, pnpm, or node.
+ * - Prints a Deno-friendly reminder to stderr.
+ * - Exits 2 so Claude blocks the tool call and shows the message.
+ */
+
+const rawInput = await new Response(Deno.stdin.readable).text();
+
+let cmd = "";
+try {
+ const payload = JSON.parse(rawInput);
+ cmd = payload?.tool_input?.command ?? "";
+} catch {
+ // If the JSON is malformed we allow the call rather than choke the hook.
+}
+
+// Remove quoted strings before checking for node commands
+const cmdWithoutQuotes = cmd.replace(/(['"`])[^'"`]*?\1/g, '');
+
+if (/\b(npm|npx|yarn|pnpm|node)\b/.test(cmdWithoutQuotes)) {
+ console.error(
+ "We use **Deno** in this repo – please rewrite the command accordingly.",
+ );
+ Deno.exit(2); // Claude interprets exit-code 2 as "block & surface stderr"
+}
+
+Deno.exit(0); // Let the tool call proceed
diff --git a/.claude/settings.json b/.claude/settings.json
new file mode 100644
index 000000000..7a1a63b02
--- /dev/null
+++ b/.claude/settings.json
@@ -0,0 +1,26 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(grep:*)",
+ "Bash(deno test:*)",
+ "Bash(deno task test:*)",
+ "Bash(find:*)",
+ "Bash(deno lint:*)",
+ "Bash(rg:*)"
+ ],
+ "deny": []
+ },
+ "hooks": {
+ "PreToolUse": [
+ {
+ "matcher": "Bash",
+ "hooks": [
+ {
+ "type": "command",
+ "command": "deno run \"$(git rev-parse --show-toplevel)/.claude/scripts/block-node.ts\""
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/.github/actions/deno-setup/action.yml b/.github/actions/deno-setup/action.yml
new file mode 100644
index 000000000..cb7d33883
--- /dev/null
+++ b/.github/actions/deno-setup/action.yml
@@ -0,0 +1,23 @@
+name: "Deno Setup"
+description: "Setup deno"
+inputs:
+ cache:
+ description: "Load deno dependencies from github cache"
+ default: true
+runs:
+ using: "composite"
+ steps:
+ # Keep in sync with `./tasks/check.sh` version
+ - name: 🦕 Setup Deno 2.4.5
+ uses: denoland/setup-deno@v2
+ with:
+ deno-version: "2.4.5"
+
+ - name: 📦 Cache Deno dependencies
+ if: inputs.cache
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.deno
+ ~/.cache/deno
+ key: ${{ runner.os }}-deno-${{ hashFiles('**/deno.json') }}
diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml
new file mode 100644
index 000000000..cdf18a274
--- /dev/null
+++ b/.github/workflows/cli.yml
@@ -0,0 +1,45 @@
+name: CLI (Mac)
+
+on:
+ workflow_dispatch:
+ inputs:
+ commit_sha:
+ description: "Git commit SHA to evaluate"
+ required: true
+ type: string
+
+env:
+ target_sha: ${{ github.event.inputs.commit_sha }}
+
+jobs:
+ cli-mac:
+ name: CLI (Mac)
+ runs-on: macos-latest
+ steps:
+ - name: 📥 Checkout repository
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ env.target_sha }}
+
+ - name: 🦕 Setup Deno
+ uses: ./.github/actions/deno-setup
+
+ - name: 📦 Cache Deno dependencies
+ uses: actions/cache@v3
+ with:
+ # TODO these are not the paths used in macos
+ path: |
+ ~/.deno
+ ~/.cache/deno
+ key: ${{ runner.os }}-deno-${{ hashFiles('**/deno.json') }}
+
+ - name: 🧪 Build CLI
+ run: |
+ deno task build-binaries --cli-only
+
+ - name: 📤 Upload CLI
+ uses: actions/upload-artifact@v4
+ with:
+ name: macos-cli
+ path: |
+ ./dist/ct
diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml
new file mode 100644
index 000000000..0d3bae80c
--- /dev/null
+++ b/.github/workflows/deno.yml
@@ -0,0 +1,487 @@
+name: Deno Workflow
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+# Define the list of binaries we're building
+# This makes it easy to add more binaries in the future
+env:
+ BINARIES: "toolshed bg-charm-service ct"
+
+jobs:
+ build:
+ name: "Test and Build"
+ runs-on: ubuntu-24.04-32-core
+ steps:
+ - name: 📥 Checkout repository
+ uses: actions/checkout@v4
+
+ - name: 🦕 Setup Deno
+ uses: ./.github/actions/deno-setup
+
+ # Errors if `deno.lock` file was not committed with the current change
+ - name: 🔍 Verify lock file & install dependencies
+ run: deno install --frozen=true
+
+ - name: 📥 Download Deno dependency binaries
+ run: deno task initialize-db
+
+ - name: 🔎 Type check codebase
+ run: deno task check
+
+ - name: 🔎 Check codebase formatting
+ run: deno fmt --check
+
+ - name: 🔎 Check derived artifacts
+ working-directory: packages/static
+ run: |
+ deno task compile-api-types
+ if ! git diff --quiet . ; then
+ echo "Run 'deno task compile-api-types' inside the 'static' package."
+ exit 1
+ fi
+
+ - name: 🧹 Lint codebase
+ run: deno lint
+
+ # For deno-web-test browser tests
+ # https://github.com/lino-levan/astral/blob/f5ef833b2c5bde3783564a6b925073d5d46bb4b8/README.md#no-usable-sandbox-with-user-namespace-cloning-enabled
+ - name: 🛡️ Disable AppArmor for browser tests
+ run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
+
+ - name: 🧪 Run parallel workspace tests
+ run: deno task test
+
+ - name: 🏗️ Build application binaries
+ run: deno task build-binaries
+ env:
+ COMMIT_SHA: ${{ github.sha }}
+
+ # Upload binaries as artifacts for the integration tests
+ - name: 📤 Upload binaries for integration tests
+ uses: actions/upload-artifact@v4
+ with:
+ name: common-binaries
+ path: |
+ ./dist/toolshed
+ ./dist/bg-charm-service
+ ./dist/ct
+
+ package-integration-test:
+ name: "Package Integration Tests"
+ runs-on: ubuntu-latest
+ needs: ["build"]
+ environment: ci
+ steps:
+ - name: 📥 Checkout repository
+ uses: actions/checkout@v4
+
+ - name: 🦕 Setup Deno
+ uses: ./.github/actions/deno-setup
+
+ - name: 📥 Download built binaries
+ uses: actions/download-artifact@v4
+
+ - name: 🚀 Start Toolshed server for testing
+ run: |
+ chmod +x ./common-binaries/toolshed
+ CTTS_AI_LLM_ANTHROPIC_API_KEY=fake \
+ ./common-binaries/toolshed &
+
+ # For Astral
+ # https://github.com/lino-levan/astral/blob/f5ef833b2c5bde3783564a6b925073d5d46bb4b8/README.md#no-usable-sandbox-with-user-namespace-cloning-enabled
+ - name: 🛡️ Disable AppArmor for browser tests
+ run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
+
+ - name: 🧪 Run end-to-end runner integration tests
+ working-directory: packages/runner
+ run: |
+ API_URL=http://localhost:8000/ \
+ deno task integration
+
+ - name: 🧪 Run end-to-end shell integration tests
+ working-directory: packages/shell
+ run: |
+ HEADLESS=1 \
+ API_URL=http://localhost:8000/ \
+ deno task integration
+
+ - name: 🧪 Run background worker integration tests
+ working-directory: packages/background-charm-service
+ run: |
+ HEADLESS=1 \
+ API_URL=http://localhost:8000/ \
+ deno task integration
+
+ cli-integration-test:
+ name: "CLI Integration Tests"
+ runs-on: ubuntu-latest
+ needs: ["build"]
+ environment: ci
+ steps:
+ - name: 📥 Checkout repository
+ uses: actions/checkout@v4
+
+ - name: 🦕 Setup Deno
+ uses: ./.github/actions/deno-setup
+
+ - name: 📥 Download built binaries
+ uses: actions/download-artifact@v4
+
+ - name: 🚀 Start Toolshed server for testing
+ run: |
+ chmod +x ./common-binaries/ct
+ chmod +x ./common-binaries/toolshed
+ # Set tools to path
+ # Integration script needs `ct`
+ echo "${{ github.workspace }}/common-binaries" >> $GITHUB_PATH
+ ./common-binaries/toolshed &
+
+ - name: 🧪 Run CLI integration tests
+ working-directory: packages/cli
+ run: |
+ API_URL=http://localhost:8000 ./integration/integration.sh
+
+ pattern-integration-test:
+ name: "Pattern Integration Tests"
+ runs-on: ubuntu-latest
+ needs: ["build"]
+ environment: ci
+ steps:
+ - name: 📥 Checkout repository
+ uses: actions/checkout@v4
+
+ - name: 🦕 Setup Deno
+ uses: ./.github/actions/deno-setup
+
+ - name: 📥 Download built binaries
+ uses: actions/download-artifact@v4
+
+ - name: 🚀 Start Toolshed server for testing
+ run: |
+ chmod +x ./common-binaries/toolshed
+ CTTS_AI_LLM_ANTHROPIC_API_KEY=fake \
+ ./common-binaries/toolshed &
+
+ # For Astral
+ # https://github.com/lino-levan/astral/blob/f5ef833b2c5bde3783564a6b925073d5d46bb4b8/README.md#no-usable-sandbox-with-user-namespace-cloning-enabled
+ - name: 🛡️ Disable AppArmor for browser tests
+ run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
+
+ - name: 🧩 Run end-to-end patterns integration tests
+ working-directory: packages/patterns
+ run: |
+ HEADLESS=1 \
+ API_URL=http://localhost:8000/ \
+ deno task integration
+
+ attest-binaries:
+ name: "Attest and Upload Binaries"
+ if: github.ref == 'refs/heads/main'
+ runs-on: ubuntu-24.04-32-core
+ needs: [
+ "pattern-integration-test",
+ "package-integration-test",
+ "cli-integration-test",
+ ]
+ environment: ci
+ permissions:
+ id-token: write
+ contents: read
+ actions: read
+ attestations: write
+ steps:
+ - name: 📥 Checkout repository
+ uses: actions/checkout@v4
+
+ - name: 📥 Download built binaries
+ uses: actions/download-artifact@v4
+
+ - name: 🔐 Process & sign binaries
+ run: |
+ mkdir -p release
+ mkdir -p signed
+
+ mv ./common-binaries ./dist
+
+ # Process toolshed binary
+ echo "Processing binary: toolshed"
+ sha256sum ./dist/toolshed > ./dist/toolshed.hash.txt
+ TOOLSHED_HASH=$(cat ./dist/toolshed.hash.txt | awk '{print $1}')
+ echo "toolshed hash: $TOOLSHED_HASH"
+ echo "toolshed_hash=$TOOLSHED_HASH" >> $GITHUB_OUTPUT
+
+ # Sign the toolshed binary
+ openssl dgst -sha256 -sign <(echo "${{ secrets.ARTIFACT_SIGNING_KEY }}") -out ./dist/toolshed.sig ./dist/toolshed
+
+ # Copy to signed directory
+ cp ./dist/toolshed ./signed/
+ cp ./dist/toolshed.sig ./signed/
+
+ # Process bg-charm-service binary
+ echo "Processing binary: bg-charm-service"
+ sha256sum ./dist/bg-charm-service > ./dist/bg-charm-service.hash.txt
+ BG_CHARM_SERVICE_HASH=$(cat ./dist/bg-charm-service.hash.txt | awk '{print $1}')
+ echo "bg-charm-service hash: $BG_CHARM_SERVICE_HASH"
+ echo "bg_charm_service_hash=$BG_CHARM_SERVICE_HASH" >> $GITHUB_OUTPUT
+
+ # Sign the bg-charm-service binary
+ openssl dgst -sha256 -sign <(echo "${{ secrets.ARTIFACT_SIGNING_KEY }}") -out ./dist/bg-charm-service.sig ./dist/bg-charm-service
+
+ # Copy to signed directory
+ cp ./dist/bg-charm-service ./signed/
+ cp ./dist/bg-charm-service.sig ./signed/
+
+ # Process ct binary
+ echo "Processing binary: ct"
+ sha256sum ./dist/ct > ./dist/ct.hash.txt
+ CT_HASH=$(cat ./dist/ct.hash.txt | awk '{print $1}')
+ echo "ct hash: $CT_HASH"
+ echo "ct_hash=$CT_HASH" >> $GITHUB_OUTPUT
+
+ # Sign the ct binary
+ openssl dgst -sha256 -sign <(echo "${{ secrets.ARTIFACT_SIGNING_KEY }}") -out ./dist/ct.sig ./dist/ct
+
+ # Copy to signed directory
+ cp ./dist/ct ./signed/
+ cp ./dist/ct.sig ./signed/
+
+ # Create a single tarball with all binaries and signatures
+ tar -czf release/labs-${{ github.sha }}.tar.gz -C signed .
+
+ # Generate hash for the tarball
+ sha256sum release/labs-${{ github.sha }}.tar.gz > release/labs-${{ github.sha }}.hash.txt
+ TARBALL_HASH=$(cat release/labs-${{ github.sha }}.hash.txt | awk '{print $1}')
+ echo "Tarball hash: $TARBALL_HASH"
+ echo "tarball_hash=$TARBALL_HASH" >> $GITHUB_OUTPUT
+ id: binary_processing
+
+ - name: 📝 Generate attestation for toolshed binary
+ id: attest_toolshed
+ uses: actions/attest-build-provenance@v1
+ with:
+ subject-name: ./dist/toolshed
+ subject-digest: sha256:${{ steps.binary_processing.outputs.toolshed_hash }}
+
+ - name: 📝 Generate attestation for bg-charm-service binary
+ id: attest_bg_charm_service
+ uses: actions/attest-build-provenance@v1
+ with:
+ subject-name: ./dist/bg-charm-service
+ subject-digest: sha256:${{ steps.binary_processing.outputs.bg_charm_service_hash }}
+
+ - name: 📝 Generate attestation for ct binary
+ id: attest_ct
+ uses: actions/attest-build-provenance@v1
+ with:
+ subject-name: ./dist/ct
+ subject-digest: sha256:${{ steps.binary_processing.outputs.ct_hash }}
+
+ - name: 📝 Generate attestation for tarball
+ id: attest_tarball
+ uses: actions/attest-build-provenance@v1
+ with:
+ subject-name: https://storage.cloud.google.com/commontools-build-artifacts/workspace-artifacts/labs-${{ github.sha }}.tar.gz
+ subject-digest: sha256:${{ steps.binary_processing.outputs.tarball_hash }}
+
+ - name: 📤 Upload attestations as artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: binary_attestations
+ if-no-files-found: error
+ path: |
+ ${{ steps.attest_toolshed.outputs.bundle-path }}
+ ${{ steps.attest_bg_charm_service.outputs.bundle-path }}
+ ${{ steps.attest_ct.outputs.bundle-path }}
+ ${{ steps.attest_tarball.outputs.bundle-path }}
+
+ - name: 🔍 Verify binary attestations
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ # Verify tarball attestation
+ echo "::group::Tarball attestation details"
+ gh attestation verify release/labs-${{ github.sha }}.tar.gz -R ${{ github.repository }} --format json | jq
+ echo "::endgroup::"
+ if [ $? -eq 0 ]; then
+ echo -e "\033[32m✓ Tarball attestation verified successfully\033[0m"
+ else
+ echo -e "\033[31m✗ Tarball attestation verification failed\033[0m"
+ exit 1
+ fi
+
+ # Verify toolshed binary attestation
+ echo "::group::toolshed attestation details"
+ gh attestation verify ./dist/toolshed -R ${{ github.repository }} --format json | jq
+ echo "::endgroup::"
+ if [ $? -eq 0 ]; then
+ echo -e "\033[32m✓ toolshed attestation verified successfully\033[0m"
+ else
+ echo -e "\033[31m✗ toolshed attestation verification failed\033[0m"
+ exit 1
+ fi
+
+ # Verify bg-charm-service binary attestation
+ echo "::group::bg-charm-service attestation details"
+ gh attestation verify ./dist/bg-charm-service -R ${{ github.repository }} --format json | jq
+ echo "::endgroup::"
+ if [ $? -eq 0 ]; then
+ echo -e "\033[32m✓ bg-charm-service attestation verified successfully\033[0m"
+ else
+ echo -e "\033[31m✗ bg-charm-service attestation verification failed\033[0m"
+ exit 1
+ fi
+
+ # Verify ct binary attestation
+ echo "::group::ct attestation details"
+ gh attestation verify ./dist/ct -R ${{ github.repository }} --format json | jq
+ echo "::endgroup::"
+ if [ $? -eq 0 ]; then
+ echo -e "\033[32m✓ ct attestation verified successfully\033[0m"
+ else
+ echo -e "\033[31m✗ ct attestation verification failed\033[0m"
+ exit 1
+ fi
+
+ - name: 🔑 Authenticate to Google Cloud
+ uses: google-github-actions/auth@v1
+ with:
+ credentials_json: ${{ secrets.GCP_SA_KEY }}
+
+ - name: ⚙️ Setup Google Cloud SDK
+ uses: google-github-actions/setup-gcloud@v1
+
+ - name: 🚀 Upload artifacts to Google Cloud Storage
+ run: |
+ gsutil cp release/labs-${{ github.sha }}.tar.gz gs://commontools-build-artifacts/workspace-artifacts/
+ gsutil cp release/labs-${{ github.sha }}.hash.txt gs://commontools-build-artifacts/workspace-artifacts/
+
+ # Print clickable links to the uploaded files
+ echo "::group::📦 Artifact Links"
+ echo "Tarball URL: https://storage.cloud.google.com/commontools-build-artifacts/workspace-artifacts/labs-${{ github.sha }}.tar.gz"
+ echo "Hash URL: https://storage.cloud.google.com/commontools-build-artifacts/workspace-artifacts/labs-${{ github.sha }}.hash.txt"
+ echo "::endgroup::"
+
+ # Automatic deployment to staging (toolshed)
+ deploy-toolshed:
+ name: "Deploy to Toolshed (Staging)"
+ if: github.ref == 'refs/heads/main'
+ needs: ["attest-binaries"]
+ runs-on: ubuntu-latest
+ environment: toolshed
+ steps:
+ - name: 📥 Checkout repository
+ uses: actions/checkout@v4
+
+ - name: 🦕 Setup Deno
+ uses: ./.github/actions/deno-setup
+ with:
+ cache: false
+
+ - name: 🔽 Pre-download Sentry CLI
+ run: |
+ echo "::group::Downloading Sentry CLI"
+ deno run --allow-all npm:@sentry/cli --version
+ echo "::endgroup::"
+
+ - name: 📊 Create Toolshed server Sentry release
+ run: |
+ # Create a release with version based on commit SHA
+ deno run --allow-all npm:@sentry/cli releases new ${{ github.sha }}
+
+ # Associate commits with the release
+ deno run --allow-all npm:@sentry/cli releases set-commits ${{ github.sha }} --auto
+
+ # Finalize the release
+ deno run --allow-all npm:@sentry/cli releases finalize ${{ github.sha }}
+ env:
+ SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
+ SENTRY_ORG: ${{ vars.SENTRY_ORG }}
+ SENTRY_PROJECT: ${{ vars.SENTRY_TOOLSHED_PROJECT }}
+
+ - name: 🚀 Deploy application to Toolshed (Staging)
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.BASTION_HOST }}
+ username: bastion
+ key: ${{ secrets.BASTION_SSH_PRIVATE_KEY }}
+ script: /opt/ct/deploy.sh ${{ vars.DEPLOYMENT_ENVIRONMENT }} ${{ github.sha }}
+
+ # Post-deployment patterns test (runs after deployment, so this will NOT block deployments.)
+ post-deploy-patterns-test:
+ name: "Toolshed Post-Deploy Patterns Test"
+ if: github.ref == 'refs/heads/main'
+ needs: ["deploy-toolshed"]
+ runs-on: ubuntu-latest
+ environment: toolshed
+ continue-on-error: true # Don't fail the workflow if tests fail
+ steps:
+ - name: 🧩 Run post-deploy Patterns integration tests against Toolshed (Staging)
+ id: run_tests
+ continue-on-error: true
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.BASTION_HOST }}
+ username: bastion
+ key: ${{ secrets.BASTION_SSH_PRIVATE_KEY }}
+ command_timeout: 10m
+ script: |
+ /opt/ct/run-pattern-tests-against-toolshed.sh ${{ github.sha }} \
+ https://toolshed.saga-castor.ts.net \
+ https://toolshed.saga-castor.ts.net
+
+ - name: 📥 Retrieve test logs on failure
+ if: steps.run_tests.outcome == 'failure'
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.BASTION_HOST }}
+ username: bastion
+ key: ${{ secrets.BASTION_SSH_PRIVATE_KEY }}
+ script: |
+ LOG_FILE="/tmp/patterns-test-${{ github.sha }}.log"
+ if [ -f "$LOG_FILE" ]; then
+ echo "📋 Test failed - showing last 500 lines..."
+ echo "========================================="
+ tail -n 500 "$LOG_FILE"
+ echo "========================================="
+ echo "Full logs will be available as artifact"
+ else
+ echo "⚠️ Log file not found at $LOG_FILE"
+ fi
+
+ - name: 📦 Download logs from bastion
+ if: always()
+ uses: nicklasfrahm/scp-action@main
+ with:
+ direction: download
+ host: ${{ secrets.BASTION_HOST }}
+ username: bastion
+ key: ${{ secrets.BASTION_SSH_PRIVATE_KEY }}
+ insecure_ignore_fingerprint: true
+ source: /tmp/patterns-test-${{ github.sha }}.log
+ target: patterns-test-${{ github.sha }}.log
+
+ - name: 📤 Upload test logs as artifact
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: patterns-test-logs-${{ github.sha }}
+ path: patterns-test-${{ github.sha }}.log
+ retention-days: 7
+ if-no-files-found: warn
+
+ - name: 📊 Report test status
+ if: always()
+ run: |
+ if [ "${{ steps.run_tests.outcome }}" == "success" ]; then
+ echo "✅ Pattern integration tests PASSED"
+ else
+ echo "❌ Pattern integration tests FAILED"
+ echo "📥 Check the artifacts tab for full logs"
+ fi
+ exit ${{ steps.run_tests.outcome == 'success' && '0' || '1' }}
diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml
new file mode 100644
index 000000000..fda3c5f3b
--- /dev/null
+++ b/.github/workflows/deploy-production.yml
@@ -0,0 +1,82 @@
+name: Manual Production Deployment
+
+on:
+ workflow_dispatch:
+ inputs:
+ commit_sha:
+ description: "Git commit SHA to deploy"
+ required: true
+ type: string
+
+jobs:
+ deploy-estuary:
+ name: "Deploy to Estuary (Production)"
+ runs-on: ubuntu-latest
+ environment: estuary # Protection rules configured in GitHub repo settings
+
+ steps:
+ - name: 📥 Checkout specific commit
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.commit_sha }}
+
+ - name: 🔍 Validate commit SHA
+ run: |
+ echo "Deploying commit: ${{ github.event.inputs.commit_sha }}"
+ git rev-parse --verify ${{ github.event.inputs.commit_sha }}
+
+ # Download the artifacts for this specific SHA from the artifact storage
+ - name: 🔑 Authenticate to Google Cloud
+ uses: google-github-actions/auth@v1
+ with:
+ credentials_json: ${{ secrets.GCP_SA_KEY }}
+
+ - name: ⚙️ Setup Google Cloud SDK
+ uses: google-github-actions/setup-gcloud@v1
+
+ - name: 📥 Download tarball from GCS
+ run: |
+ mkdir -p downloaded-artifacts
+ gsutil cp gs://commontools-build-artifacts/workspace-artifacts/labs-${{ github.event.inputs.commit_sha }}.tar.gz downloaded-artifacts/
+
+ # Verify the tarball exists
+ if [ ! -f downloaded-artifacts/labs-${{ github.event.inputs.commit_sha }}.tar.gz ]; then
+ echo "::error::Artifact tarball for commit ${{ github.event.inputs.commit_sha }} not found!"
+ echo "Make sure this commit was successfully built and artifacts were uploaded."
+ exit 1
+ fi
+
+ - name: 🦕 Setup Deno
+ uses: ./.github/actions/deno-setup
+ with:
+ cache: false
+
+ - name: 🔽 Pre-download Sentry CLI
+ run: |
+ echo "::group::Downloading Sentry CLI"
+ deno run --allow-all npm:@sentry/cli --version
+ echo "::endgroup::"
+
+ - name: 📊 Create Toolshed server Sentry release
+ run: |
+ # Create a release with version based on commit SHA
+ deno run --allow-all npm:@sentry/cli releases new ${{ github.event.inputs.commit_sha }}
+
+ # Associate commits with the release
+ deno run --allow-all npm:@sentry/cli releases set-commits ${{ github.event.inputs.commit_sha }} --auto
+
+ # Finalize the release
+ deno run --allow-all npm:@sentry/cli releases finalize ${{ github.event.inputs.commit_sha }}
+ env:
+ SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
+ SENTRY_ORG: ${{ vars.SENTRY_ORG }}
+ SENTRY_PROJECT: ${{ vars.SENTRY_TOOLSHED_PROJECT }}
+
+ - name: 🚀 Deploy application to Estuary (Production)
+ id: deployment
+ uses: appleboy/ssh-action@master
+ with:
+ host: ${{ secrets.BASTION_HOST }}
+ username: bastion
+ key: ${{ secrets.BASTION_SSH_PRIVATE_KEY }}
+ script: /opt/ct/deploy.sh ${{ vars.DEPLOYMENT_ENVIRONMENT }} ${{ github.event.inputs.commit_sha }}
diff --git a/.github/workflows/evals.yml b/.github/workflows/evals.yml
new file mode 100644
index 000000000..4df9ad78e
--- /dev/null
+++ b/.github/workflows/evals.yml
@@ -0,0 +1,172 @@
+name: Evals
+
+on:
+ workflow_dispatch:
+ inputs:
+ commit_sha:
+ description: "Git commit SHA to evaluate"
+ required: true
+ type: string
+ test_size:
+ type: choice
+ description: "Number of charms to run"
+ options:
+ - one
+ - ten
+ - all
+ # FOR DEVELOPMENT
+ #pull_request:
+ # branches:
+ # - main
+
+env:
+ # FOR DEVELOPMENT
+ #target_sha: "4a4e488db80aa074a825923c757572f99137c957"
+ #test_size: "one"
+ target_sha: ${{ github.event.inputs.commit_sha }}
+ test_size: ${{ github.event.inputs.test_size }}
+
+jobs:
+ evals:
+ name: Evals
+ runs-on: ubuntu-latest
+ environment: seeder
+ services:
+ redis:
+ image: redis
+ ports:
+ - 6379:6379
+ options: >-
+ --health-cmd "redis-cli ping"
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+ steps:
+ - name: ✏️ Log Eval Run Metadata
+ run: |
+ echo "Running Evals:"
+ echo "- Size: ${{ env.test_size }} charms"
+ echo "- SHA: ${{ env.target_sha }}"
+
+ # TODO Upload `seeder` as a deno executable to avoid
+ # running from source.
+ - name: 📥 Checkout repository
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ env.target_sha }}
+
+ - name: 🦕 Setup Deno
+ uses: ./.github/actions/deno-setup
+
+ - name: 📦 Cache Deno dependencies
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.deno
+ ~/.cache/deno
+ key: ${{ runner.os }}-deno-${{ hashFiles('**/deno.json') }}
+
+ - name: 🔑 Authenticate to Google Cloud
+ uses: google-github-actions/auth@v1
+ with:
+ credentials_json: ${{ secrets.GCP_SA_KEY }}
+
+ - name: ⚙️ Setup Google Cloud SDK
+ uses: google-github-actions/setup-gcloud@v1
+
+ - name: 📥 Download built binaries from Google Cloud Storage
+ run: |
+ mkdir -p downloaded-artifacts
+ gsutil cp gs://commontools-build-artifacts/workspace-artifacts/labs-${{ env.target_sha }}.tar.gz downloaded-artifacts/
+ gsutil cp gs://commontools-build-artifacts/workspace-artifacts/labs-${{ env.target_sha }}.hash.txt hash.txt
+
+ # Verify the tarball exists
+ if [ ! -f downloaded-artifacts/labs-${{ env.target_sha }}.tar.gz ]; then
+ echo "::error::Artifact tarball for commit ${{ env.target_sha }} not found!"
+ echo "Make sure this commit was successfully built and artifacts were uploaded."
+ exit 1
+ fi
+ mkdir -p common-binaries
+ tar -xzf downloaded-artifacts/labs-${{ env.target_sha }}.tar.gz -C common-binaries
+
+ # CTTS_AI_LLM_GOOGLE_APPLICATION_CREDENTIALS points to `/tmp/gcp-vertex-creds.json`
+ - name: ⚙️ Setup Google Vertex Key
+ run: |
+ echo '${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }}' > /tmp/gcp-vertex-creds.json
+
+ # We want to essentially use a persistent directory
+ # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#update-a-cache
+ - name: 📦 Load LLM Cache
+ id: load-llm-cache
+ uses: actions/cache@v4
+ with:
+ path: ./llm_cache_dir
+ key: llm-cache-${{ github.run_id }}
+ restore-keys: llm-cache
+
+ - name: ✏️ List LLM Cache Load
+ run: |
+ mkdir -p ./llm_cache_dir/llm-api-cache
+ ls -1 ./llm_cache_dir/llm-api-cache > ./cache-log-pre
+ echo "$(cat ./cache-log-pre | wc -l) items found in cache."
+ cat ./cache-log-pre
+
+ - name: 🚀 Start Toolshed server
+ run: |
+ mkdir -p ./llm_cache_dir
+ chmod +x ./common-binaries/toolshed
+ CACHE_DIR=./llm_cache_dir \
+ CTTS_AI_LLM_ANTHROPIC_API_KEY=${{ secrets.CTTS_AI_LLM_ANTHROPIC_API_KEY }} \
+ CTTS_AI_LLM_OPENAI_API_KEY=${{ secrets.CTTS_AI_LLM_OPENAI_API_KEY }} \
+ CTTS_AI_LLM_GOOGLE_APPLICATION_CREDENTIALS=${{ secrets.CTTS_AI_LLM_GOOGLE_APPLICATION_CREDENTIALS }} \
+ ./common-binaries/toolshed &
+
+ # For Astral
+ # https://github.com/lino-levan/astral/blob/f5ef833b2c5bde3783564a6b925073d5d46bb4b8/README.md#no-usable-sandbox-with-user-namespace-cloning-enabled
+ - name: 🛡️ Disable AppArmor for browser tests
+ run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
+
+ - name: 🧪 Run seeder
+ working-directory: packages/seeder
+ run: |
+ FLAGS=""
+ if [ ${{ env.test_size }} == "one" ]; then
+ FLAGS="--tag smol"
+ elif [ ${{ env.test_size }} == "ten" ]; then
+ FLAGS="--tag 10x10"
+ fi
+
+ # Wrap in an if/else, we don't want the job to fail yet
+ # until we can confidently expect passing results.
+ if
+ API_URL=http://localhost:8000/ \
+ OPENAI_API_KEY=${{ secrets.CTTS_AI_LLM_OPENAI_API_KEY }} \
+ deno task start --name ${{ env.target_sha }} $FLAGS ; then
+ echo "All charms have been successfully verified!"
+ else
+ echo "Some charms have failed verification."
+ fi
+
+ - name: ✏️ List LLM Cache Save
+ run: |
+ mkdir -p ./llm_cache_dir/llm-api-cache
+ ls -1 ./llm_cache_dir/llm-api-cache > ./cache-log-post
+ echo "$(cat ./cache-log-post | wc -l) items found in cache."
+ cat ./cache-log-post
+
+ - name: 📦 LLM Cache Status
+ run: |
+ if diff ./cache-log-pre ./cache-log-post > ./cache-analysis-diff ; then
+ echo "No new LLM cache entries added!"
+ else
+ # `diff` outputs two lines per diff, one for location, one for content
+ ADDITIONS=$(cat ./cache-analysis-diff | wc -l)
+ echo "LLM cache has added $((ADDITIONS / 2)) entries."
+ fi
+
+ - name: 📤 Upload reports
+ uses: actions/upload-artifact@v4
+ with:
+ name: seeder-results
+ path: |
+ ./packages/seeder/results
diff --git a/.gitignore b/.gitignore
index e593dfbfb..500655f21 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,10 +8,17 @@ lib
.vscode
__pycache__
cache
-typescript/packages/planning-server/.env
-typescript/packages/lookslike-prototype/.reflect
-typescript/packages/linkheap/views
-typescript/packages/linkheap/.env
-typescript/packages/collectathon/collections.db
-typescript/packages/linkheap/links.db
-typescript/packages/collectathon/.env
+artifact
+_deno.lock
+packages/seeder/results
+.claude-prompt.tmp
+.pr-desc.tmp
+dist/
+.claude/settings.local.json
+claude-research.key
+*.key
+.page-agent-config.json
+.common.json
+.knowledge
+packages/local-dev-toolshed.log
+local-dev-shell.log
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 000000000..3bfc872d3
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+ "recommendations": [
+ "denoland.vscode-deno"
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..92ab15775
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,19 @@
+{
+ "editor.formatOnSave": true,
+ "editor.defaultFormatter": "denoland.vscode-deno",
+ "deno.enable": true,
+ "[typescript]": {
+ "editor.defaultFormatter": "denoland.vscode-deno"
+ },
+ "[typescriptreact]": {
+ "editor.defaultFormatter": "denoland.vscode-deno"
+ },
+ "deno.disablePaths": [
+ "**/node_modules",
+ "**/.vite",
+ "**/dist",
+ "**/build"
+ ],
+ "deno.cacheOnSave": true,
+ "deno.maxTsServerMemory": 3072
+}
diff --git a/.zed/settings.json b/.zed/settings.json
new file mode 100644
index 000000000..18c48cc41
--- /dev/null
+++ b/.zed/settings.json
@@ -0,0 +1,35 @@
+// Folder-specific settings
+//
+// For a full list of overridable settings, and general information on folder-specific settings,
+// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
+{
+ "lsp": {
+ "deno": {
+ "settings": {
+ "deno": {
+ "enable": true
+ }
+ }
+ }
+ },
+ "languages": {
+ "TypeScript": {
+ "language_servers": [
+ "deno",
+ "!typescript-language-server",
+ "!vtsls",
+ "!eslint"
+ ],
+ "formatter": "language_server"
+ },
+ "TSX": {
+ "language_servers": [
+ "deno",
+ "!typescript-language-server",
+ "!vtsls",
+ "!eslint"
+ ],
+ "formatter": "language_server"
+ }
+ }
+}
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 000000000..181d9ccd0
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,353 @@
+# Repository Guidelines for AI Agents
+
+The instructions in this document apply to the entire repository.
+
+## Basics
+
+### Build & Test
+
+- Check typings with `deno task check`.
+- Run all tests using `deno task test`.
+- To run a single test file use `deno test path/to/test.ts`.
+- To test a specific package, `cd` into the package directory and run
+ `deno task test`.
+
+### `ct` & Common Tools Framework
+
+Before ever calling `ct` you MUST read `docs/common/CT.md`.
+
+### Recipe Development
+
+Whenever you work on recipes, look in the `recipes` folder provided by the user
+for the `RECIPES.md` and `COMPONENTS.md` files. These are your bible. You can
+re-read `.claude/commands/recipe-dev.md` any time to refresh your memory.
+
+### Formatting
+
+- Line width is **80 characters**.
+- Indent with **2 spaces**.
+- **Semicolons are required.**
+- Use **double quotes** for strings.
+- Always run `deno fmt` before committing.
+
+### TypeScript
+
+- Export types explicitly using `export type { ... }`.
+- Provide descriptive JSDoc comments on public interfaces.
+- Prefer strong typing with interfaces or types instead of `any`.
+- Update package-level README.md files.
+
+### Imports
+
+- Group imports by source: standard library, external, then internal.
+- Prefer named exports over default exports.
+- Use package names for internal imports.
+- Destructure when importing multiple names from the same module.
+- Import either from `@commontools/api` (internal API) or
+ `@commontools/api/interface` (external API), but not both.
+
+### Error Handling
+
+- Write descriptive error messages.
+- Propagate errors using async/await.
+- Document possible errors in JSDoc.
+
+### Testing
+
+- Structure tests with `@std/testing/bdd` (`describe`/`it`).
+- Use `@std/expect` for assertions.
+- Give tests descriptive names.
+
+## Good Patterns & Practices
+
+Not all the code fits these patterns. For bigger changes, follow these
+guidelines and consider refactoring existing code towards these practices.
+
+### Avoid Singletons
+
+The singleton pattern may be useful when there's a single global state. But
+running multiple instances, unit tests, and reflecting state from another state
+becomes impossible. Additionally, this pattern is infectious, often requiring
+consuming code to also only support a single instance.
+
+> **❌ Avoid**
+
+```ts
+const cache = new Map();
+export const set = (key: string, value: string) => cache.set(key, value);
+export const get = (key: string): string | undefined => cache.get(key);
+```
+
+```ts
+export const cache = new Map();
+export const instance = new Foo();
+```
+
+> **✅ Prefer**
+
+In both cases, we can maintain multiple caches, or instances of cache consumers.
+
+```ts
+export class Cache {
+ private map: Map = new Map();
+ get(key: string): string | undefined {
+ return this.map.get(key);
+ }
+ set(key: string, value: string) {
+ this.map.set(key, value);
+ }
+}
+```
+
+Or with a functional pattern:
+
+```ts
+export type Cache = Map;
+export const get = (cache: Cache, key: string): string | undefined =>
+ cache.get(key);
+export const set = (cache: Cache, key: string, value: string) =>
+ cache.set(key, value);
+```
+
+### Keep the Module Graph clean
+
+We execute our JavaScript modules in many different environments:
+
+- Browsers (Vite built)
+- Browsers (deno-web-test>esbuild Built)
+- Browsers (eval'd recipes)
+- Deno (scripts and servers)
+- Deno (eval'd recipes)
+- Deno Workers
+- Deno workers (eval'd recipes)
+
+Each frontend bundle or script has a single entry point[^1]. For frontend
+bundles, it's a single JS file with every workspace/dependency module included.
+For deno environments, the module graph is built dynamically. While JavaScript
+can run in many environments, there's work to be done to run the same code
+across all invocations. We should strive for a clear module graph for all
+potential entries (e.g. each script, each bundle) for both portability,
+maintanance, and performance.
+
+[^1]: Our vite frontend has multiple "pieces" for lazy loading JSDOM/TSC, but a
+ single "main".
+
+> **❌ Avoid**
+
+- Modules depending on each other
+- Large quantity of module exports
+- Adding module-specific dependencies to workspace deno.json
+- Non-standard JS (env vars, vite-isms): All of our different invocation
+ mechanisms/environments need to handle these
+
+> **✅ Prefer**
+
+- Use
+ [manifest exports](https://docs.deno.com/runtime/fundamentals/workspaces/#multiple-package-entries)
+ to export a different entry point for a module. Don't pull in everything if
+ only e.g. types are needed.
+ - If needed, environment specific exports can be provided e.g.
+ `@workspace/module/browser` | `@workspace/module/deno`.
+- Consider leaf nodes in the graph: A `utils` module should not be heavy with
+ dependencies, external or otherwise.
+- Clean separation of public and private facing interfaces: only export what's
+ needed.
+- Add module-specific dependencies to that module's dependencies, not the entire
+ workspace. We don't need `vite` in a deno server.
+
+### Avoid Ambiguous Types
+
+Softly-typed JS allows quite a bit. We often accept a range of inputs, and based
+on type checking, perform actions.
+
+> **❌ Avoid**
+
+Minimize unknown type usage. Not only does `processData` allow any type, but
+it's unclear what the intended types are:
+
+```ts
+function processData(data: any) {
+ if (typeof data === "object") {
+ if (!data) {
+ processNull(data as null);
+ } else if (Array.isArray(data)) {
+ processArray(data as object[]);
+ } else {
+ processObject(data as object);
+ }
+ } else {
+ processPrimitive(data, typeof data);
+ }
+}
+```
+
+> **✅ Prefer**
+
+Wrap an `any` type as another type for consumers. There are many TypeScript
+solutions here, but in general, only at serialization boundaries (postMessage,
+HTTP requests) _must_ we transform untyped values. Elsewhere, we should have
+validated types.
+
+```ts
+class Data {
+ private inner: any;
+ constructor(inner: any) {
+ this.inner = inner;
+ }
+ process() {
+ // if (typeof this.inner === "object")
+ }
+}
+
+function processData(data: Data) {
+ data.process();
+}
+```
+
+### Avoid representing invalid state
+
+Similarly, permissive interfaces (including nullable properties and
+non-represented exclusive states e.g. "i accept a string or array of strings")
+may represent an invalid state at intermediate stages that will need be checked
+at every interface:
+
+> **❌ Avoid**
+
+```ts
+interface LLMRequest {
+ prompt?: string;
+ messages?: string[];
+ model?: string;
+}
+
+function request(req: LLMRequest) {
+ // Not only do we have to modify `req` into a valid
+ // state here, `processRequest` and any other user of `LLMRequest`
+ // must also handle this.
+
+ if (!req.model) {
+ req.model = "default model";
+ }
+ // If both prompt and messages provided,
+ // use only `messages`
+ if (req.prompt && req.messages) {
+ req.prompt = undefined;
+ }
+ processRequest(req);
+}
+
+request({ prompt: "hello world" });
+```
+
+> **✅ Prefer**
+
+For interfaces/types, not allowing unrepresented exclusive states (the prompt
+input is always an array; `model` is always defined) requires more explicit
+inputs, but then `LLMRequest` is always complete and valid. **Making invalid
+states unrepresentable is good**.
+
+Constructing the request could be also be a class, if we always wanted to apply
+appropriate e.g. defaults.
+
+```ts
+enum Model {
+ Default = "default model";
+}
+
+interface LLMRequest {
+ messages: string[],
+ model: Model,
+}
+
+function request(req: LLMRequest) {
+ // This is already a valid LLMRequest
+ processRequest(req);
+}
+
+request({ messages: ["hello world"], model: inputModel ?? Model.Default });
+```
+
+### Appropriate Error Handling
+
+If a function may throw, it's reasonable to wrap it in a try/catch. However, in
+complex codebases, handling every error is both tedious and limiting, and may be
+preferable to handle errors in a single place with context. Most importantly,
+throwing errors is OK, and preventing execution of invalid states is desirable.
+
+Whether or not an error should be handled in a subprocess could be determined by
+whether its a "fatal error" or not: was an assumption invalidated? are we
+missing some required capability? Throw an error. Can we continue safely
+processing and need to take no further action? Maybe a low-level try/catch is
+appropriate. LLMs generally don't have this context and are liberal in their
+try/catch usage. Avoid this.
+
+> **❌ Avoid**
+
+In this scenario, errors are logged different ways; if `fetch` throws, we have a
+console error log. If `getData()` returns `undefined`, something unexpected
+occurred, and there's nothing to be done. `run` should be considered errored and
+failed.
+
+```ts
+async function getData(): Promise {
+ try {
+ const res = await fetch(URL);
+ if (res.ok) {
+ return res.text();
+ }
+ throw new Error("Unsuccessful HTTP response");
+ } catch(e) {
+ console.error(e);
+ }
+}
+
+async function run() {
+ try {
+ const data = await getData();
+ if (data) {
+ // ..
+ }
+ } catch (e) {
+ console.error("There was an error": e);
+ }
+}
+```
+
+> **✅ Prefer**
+
+In this case, we expect `getData()` to throw, or always return a `string`. Less
+handling here, and let the caller determine what to do on failure.
+
+```ts
+async function getData(): Promise {
+ const res = await fetch(URL);
+ if (res.ok) {
+ return res.text();
+ }
+ throw new Error("Unsuccessful HTTP response");
+}
+
+async function run() {
+ const data = await getData();
+ await processStr(data);
+}
+
+async function main() {
+ try {
+ await run();
+ } catch (e) {
+ console.error(e);
+ }
+}
+```
+
+Sometimes a low-level try/catch is appropriate, of course:
+
+- `getData()` could have its own try/catch to e.g. retry on failure, throwing
+ after 3 failed attempts.
+- Exposing a `isFeatureSupported(): boolean` function that based on if some
+ other function throws, determines if "feature" is supported. If we can handle
+ both scenarios and translate the error into a boolean (e.g. are all of the
+ ED25519 features we need supported natively for this platform? if not use a
+ polyfill), then this is not a fatal error, and we explicitly do not want to
+ throw and handle it elsewhere.
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 120000
index 000000000..47dc3e3d8
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1 @@
+AGENTS.md
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
deleted file mode 100644
index 7cd6b7a7a..000000000
--- a/Cargo.lock
+++ /dev/null
@@ -1,4946 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 3
-
-[[package]]
-name = "Inflector"
-version = "0.11.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
-dependencies = [
- "lazy_static",
- "regex",
-]
-
-[[package]]
-name = "addr2line"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-
-[[package]]
-name = "ahash"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
-dependencies = [
- "cfg-if",
- "getrandom",
- "once_cell",
- "version_check",
- "zerocopy",
-]
-
-[[package]]
-name = "aho-corasick"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "allocator-api2"
-version = "0.2.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
-
-[[package]]
-name = "ambient-authority"
-version = "0.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9d4ee0d472d1cd2e28c97dfa124b3d8d992e10eb0a035f33f5d12e3a177ba3b"
-
-[[package]]
-name = "android_system_properties"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "anstream"
-version = "0.6.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
-dependencies = [
- "anstyle",
- "anstyle-parse",
- "anstyle-query",
- "anstyle-wincon",
- "colorchoice",
- "is_terminal_polyfill",
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
-
-[[package]]
-name = "anstyle-parse"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
-dependencies = [
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle-query"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
-dependencies = [
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "anstyle-wincon"
-version = "3.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
-dependencies = [
- "anstyle",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "anyhow"
-version = "1.0.83"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
-
-[[package]]
-name = "arbitrary"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
-dependencies = [
- "derive_arbitrary",
-]
-
-[[package]]
-name = "arraydeque"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
-
-[[package]]
-name = "arrayref"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
-
-[[package]]
-name = "arrayvec"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
-
-[[package]]
-name = "ast_node"
-version = "0.9.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ab31376d309dd3bfc9cfb3c11c93ce0e0741bbe0354b20e7f8c60b044730b79"
-dependencies = [
- "proc-macro2",
- "quote",
- "swc_macros_common",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "async-trait"
-version = "0.1.80"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "atomic-waker"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
-
-[[package]]
-name = "auto_impl"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
-
-[[package]]
-name = "axum"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
-dependencies = [
- "async-trait",
- "axum-core",
- "axum-macros",
- "bytes",
- "futures-util",
- "http",
- "http-body",
- "http-body-util",
- "hyper",
- "hyper-util",
- "itoa",
- "matchit",
- "memchr",
- "mime",
- "multer",
- "percent-encoding",
- "pin-project-lite",
- "rustversion",
- "serde",
- "serde_json",
- "serde_path_to_error",
- "serde_urlencoded",
- "sync_wrapper 1.0.1",
- "tokio",
- "tower",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "axum-core"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
-dependencies = [
- "async-trait",
- "bytes",
- "futures-util",
- "http",
- "http-body",
- "http-body-util",
- "mime",
- "pin-project-lite",
- "rustversion",
- "sync_wrapper 0.1.2",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "axum-macros"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa"
-dependencies = [
- "heck 0.4.1",
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "backtrace"
-version = "0.3.71"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
-dependencies = [
- "addr2line",
- "cc",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object 0.32.2",
- "rustc-demangle",
-]
-
-[[package]]
-name = "base64"
-version = "0.21.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
-
-[[package]]
-name = "base64"
-version = "0.22.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
-
-[[package]]
-name = "base64-simd"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5"
-dependencies = [
- "simd-abstraction",
-]
-
-[[package]]
-name = "better_scoped_tls"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "794edcc9b3fb07bb4aecaa11f093fd45663b4feadb782d68303a2268bc2701de"
-dependencies = [
- "scoped-tls",
-]
-
-[[package]]
-name = "bincode"
-version = "1.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "bitflags"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
-
-[[package]]
-name = "bitflags"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
-
-[[package]]
-name = "bitvec"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
-dependencies = [
- "funty",
- "radium",
- "tap",
- "wyz",
-]
-
-[[package]]
-name = "blake3"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52"
-dependencies = [
- "arrayref",
- "arrayvec",
- "cc",
- "cfg-if",
- "constant_time_eq",
-]
-
-[[package]]
-name = "block-buffer"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "bumpalo"
-version = "3.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
-
-[[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
-[[package]]
-name = "bytes"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
-
-[[package]]
-name = "cap-fs-ext"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fc2d2954524be4866aaa720f008fba9995de54784957a1b0e0119992d6d5e52"
-dependencies = [
- "cap-primitives",
- "cap-std",
- "io-lifetimes",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "cap-net-ext"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "799c81d79ea9c71a1438efd417c788214bc9e7986046d3710b6bbe60da4d8275"
-dependencies = [
- "cap-primitives",
- "cap-std",
- "rustix",
- "smallvec",
-]
-
-[[package]]
-name = "cap-primitives"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00172660727e2d7f808e7cc2bfffd093fdb3ea2ff2ef819289418a3c3ffab5ac"
-dependencies = [
- "ambient-authority",
- "fs-set-times",
- "io-extras",
- "io-lifetimes",
- "ipnet",
- "maybe-owned",
- "rustix",
- "windows-sys 0.52.0",
- "winx",
-]
-
-[[package]]
-name = "cap-rand"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "270f1d341a2afc62604f8f688bee4e444d052b7a74c1458dd3aa7efb47d4077f"
-dependencies = [
- "ambient-authority",
- "rand",
-]
-
-[[package]]
-name = "cap-std"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cd9187bb3f7478a4c135ea10473a41a5f029d2ac800c1adf64f35ec7d4c8603"
-dependencies = [
- "cap-primitives",
- "io-extras",
- "io-lifetimes",
- "rustix",
-]
-
-[[package]]
-name = "cap-time-ext"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91666f31e30c85b1d2ee8432c90987f752c45f5821f5638027b41e73e16a395b"
-dependencies = [
- "ambient-authority",
- "cap-primitives",
- "iana-time-zone",
- "once_cell",
- "rustix",
- "winx",
-]
-
-[[package]]
-name = "cc"
-version = "1.0.98"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
-dependencies = [
- "jobserver",
- "libc",
- "once_cell",
-]
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "clap"
-version = "4.5.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
-dependencies = [
- "clap_builder",
- "clap_derive",
-]
-
-[[package]]
-name = "clap_builder"
-version = "4.5.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
-dependencies = [
- "anstream",
- "anstyle",
- "clap_lex",
- "strsim",
-]
-
-[[package]]
-name = "clap_derive"
-version = "4.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
-dependencies = [
- "heck 0.5.0",
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "clap_lex"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
-
-[[package]]
-name = "cobs"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
-
-[[package]]
-name = "colorchoice"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
-
-[[package]]
-name = "constant_time_eq"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
-
-[[package]]
-name = "core-foundation"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "core-foundation-sys"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
-
-[[package]]
-name = "cpp_demangle"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "cpufeatures"
-version = "0.2.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "cranelift-bforest"
-version = "0.108.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29daf137addc15da6bab6eae2c4a11e274b1d270bf2759508e62f6145e863ef6"
-dependencies = [
- "cranelift-entity 0.108.1",
-]
-
-[[package]]
-name = "cranelift-codegen"
-version = "0.108.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de619867d5de4c644b7fd9904d6e3295269c93d8a71013df796ab338681222d4"
-dependencies = [
- "bumpalo",
- "cranelift-bforest",
- "cranelift-codegen-meta",
- "cranelift-codegen-shared",
- "cranelift-control",
- "cranelift-entity 0.108.1",
- "cranelift-isle",
- "gimli",
- "hashbrown 0.14.5",
- "log",
- "regalloc2",
- "rustc-hash",
- "smallvec",
- "target-lexicon",
-]
-
-[[package]]
-name = "cranelift-codegen-meta"
-version = "0.108.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29f5cf277490037d8dae9513d35e0ee8134670ae4a964a5ed5b198d4249d7c10"
-dependencies = [
- "cranelift-codegen-shared",
-]
-
-[[package]]
-name = "cranelift-codegen-shared"
-version = "0.108.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c3e22ecad1123343a3c09ac6ecc532bb5c184b6fcb7888df0ea953727f79924"
-
-[[package]]
-name = "cranelift-control"
-version = "0.108.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53ca3ec6d30bce84ccf59c81fead4d16381a3ef0ef75e8403bc1e7385980da09"
-dependencies = [
- "arbitrary",
-]
-
-[[package]]
-name = "cranelift-entity"
-version = "0.107.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32acd0632ba65c2566e75f64af9ef094bb8d90e58a9fbd33d920977a9d85c054"
-dependencies = [
- "serde",
- "serde_derive",
-]
-
-[[package]]
-name = "cranelift-entity"
-version = "0.108.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7eabb8d36b0ca8906bec93c78ea516741cac2d7e6b266fa7b0ffddcc09004990"
-dependencies = [
- "serde",
- "serde_derive",
-]
-
-[[package]]
-name = "cranelift-frontend"
-version = "0.108.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44b42630229e49a8cfcae90bdc43c8c4c08f7a7aa4618b67f79265cd2f996dd2"
-dependencies = [
- "cranelift-codegen",
- "log",
- "smallvec",
- "target-lexicon",
-]
-
-[[package]]
-name = "cranelift-isle"
-version = "0.108.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "918d1e36361805dfe0b6cdfd5a5ffdb5d03fa796170c5717d2727cbe623b93a0"
-
-[[package]]
-name = "cranelift-native"
-version = "0.108.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75aea85a0d7e1800b14ce9d3f53adf8ad4d1ee8a9e23b0269bdc50285e93b9b3"
-dependencies = [
- "cranelift-codegen",
- "libc",
- "target-lexicon",
-]
-
-[[package]]
-name = "cranelift-wasm"
-version = "0.108.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac491fd3473944781f0cf9528c90cc899d18ad438da21961a839a3a44d57dfb"
-dependencies = [
- "cranelift-codegen",
- "cranelift-entity 0.108.1",
- "cranelift-frontend",
- "itertools",
- "log",
- "smallvec",
- "wasmparser 0.207.0",
- "wasmtime-types 21.0.1",
-]
-
-[[package]]
-name = "crc"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23"
-dependencies = [
- "crc-catalog",
-]
-
-[[package]]
-name = "crc-catalog"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403"
-
-[[package]]
-name = "crc32fast"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "crossbeam-deque"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
-dependencies = [
- "crossbeam-epoch",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
-
-[[package]]
-name = "crypto-common"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
-dependencies = [
- "generic-array",
- "typenum",
-]
-
-[[package]]
-name = "dashmap"
-version = "5.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
-dependencies = [
- "cfg-if",
- "hashbrown 0.14.5",
- "lock_api",
- "once_cell",
- "parking_lot_core 0.9.10",
-]
-
-[[package]]
-name = "data-encoding"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
-
-[[package]]
-name = "data-url"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
-
-[[package]]
-name = "debugid"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
-dependencies = [
- "serde",
- "uuid",
-]
-
-[[package]]
-name = "deno_ast"
-version = "0.39.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "042645e6a505a359b288723ded5c8b30fdc4f70514a3bcd7a49221cc89c1ba90"
-dependencies = [
- "anyhow",
- "base64 0.21.7",
- "deno_media_type",
- "deno_terminal",
- "dprint-swc-ext",
- "once_cell",
- "percent-encoding",
- "serde",
- "swc_atoms",
- "swc_bundler",
- "swc_common",
- "swc_config",
- "swc_config_macro",
- "swc_ecma_ast",
- "swc_ecma_codegen",
- "swc_ecma_codegen_macros",
- "swc_ecma_loader",
- "swc_ecma_parser",
- "swc_ecma_transforms_base",
- "swc_ecma_transforms_classes",
- "swc_ecma_transforms_macros",
- "swc_ecma_transforms_optimization",
- "swc_ecma_transforms_proposal",
- "swc_ecma_transforms_react",
- "swc_ecma_transforms_typescript",
- "swc_ecma_utils",
- "swc_ecma_visit",
- "swc_eq_ignore_macros",
- "swc_graph_analyzer",
- "swc_macros_common",
- "swc_visit",
- "swc_visit_macros",
- "text_lines",
- "thiserror",
- "unicode-width",
- "url",
-]
-
-[[package]]
-name = "deno_emit"
-version = "0.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25bc64f886c76647400ed8f807ba7dba82e0b52e57e5426a83094cfe22ee19c9"
-dependencies = [
- "anyhow",
- "base64 0.21.7",
- "deno_ast",
- "deno_graph",
- "escape8259",
- "futures",
- "import_map",
- "parking_lot 0.11.2",
- "url",
-]
-
-[[package]]
-name = "deno_graph"
-version = "0.78.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d13080829a06062a14e41e190f64a3407e4a0f63cf7db5dcecbc3cf500445df3"
-dependencies = [
- "anyhow",
- "async-trait",
- "data-url",
- "deno_ast",
- "deno_semver",
- "deno_unsync",
- "encoding_rs",
- "futures",
- "import_map",
- "indexmap",
- "log",
- "monch",
- "once_cell",
- "parking_lot 0.12.3",
- "regex",
- "serde",
- "serde_json",
- "sha2",
- "thiserror",
- "twox-hash",
- "url",
-]
-
-[[package]]
-name = "deno_media_type"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8978229b82552bf8457a0125aa20863f023619cfc21ebb007b1e571d68fd85b"
-dependencies = [
- "data-url",
- "serde",
- "url",
-]
-
-[[package]]
-name = "deno_semver"
-version = "0.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b49e14effd9df8ed261f7a1a34ac19bbaf0fa940c59bd19a6d8313cf41525e1c"
-dependencies = [
- "monch",
- "once_cell",
- "serde",
- "thiserror",
- "url",
-]
-
-[[package]]
-name = "deno_terminal"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e6337d4e7f375f8b986409a76fbeecfa4bd8a1343e63355729ae4befa058eaf"
-dependencies = [
- "once_cell",
- "termcolor",
-]
-
-[[package]]
-name = "deno_unsync"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7557a5e9278b9a5cc8056dc37062ea4344770bda4eeb5973c7cbb7ebf636b9a4"
-dependencies = [
- "tokio",
-]
-
-[[package]]
-name = "derive_arbitrary"
-version = "1.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "digest"
-version = "0.10.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
-dependencies = [
- "block-buffer",
- "crypto-common",
-]
-
-[[package]]
-name = "directories-next"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
-dependencies = [
- "cfg-if",
- "dirs-sys-next",
-]
-
-[[package]]
-name = "dirs"
-version = "4.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
-dependencies = [
- "dirs-sys",
-]
-
-[[package]]
-name = "dirs-sys"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
-dependencies = [
- "libc",
- "redox_users",
- "winapi",
-]
-
-[[package]]
-name = "dirs-sys-next"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
-dependencies = [
- "libc",
- "redox_users",
- "winapi",
-]
-
-[[package]]
-name = "displaydoc"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "dprint-swc-ext"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "019d17f2c2457c5a70a7cf4505b1a562ca8ab168c0ac0c005744efbd29fcb8fe"
-dependencies = [
- "num-bigint",
- "rustc-hash",
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "swc_ecma_parser",
- "text_lines",
-]
-
-[[package]]
-name = "either"
-version = "1.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
-
-[[package]]
-name = "embedded-io"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
-
-[[package]]
-name = "encoding_rs"
-version = "0.8.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "equivalent"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
-
-[[package]]
-name = "errno"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
-dependencies = [
- "libc",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "escape8259"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6"
-
-[[package]]
-name = "fallible-iterator"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
-
-[[package]]
-name = "fastrand"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
-
-[[package]]
-name = "fd-lock"
-version = "4.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947"
-dependencies = [
- "cfg-if",
- "rustix",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "fixedbitset"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
-
-[[package]]
-name = "flate2"
-version = "1.0.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
-dependencies = [
- "crc32fast",
- "miniz_oxide",
-]
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "form_urlencoded"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
-dependencies = [
- "percent-encoding",
-]
-
-[[package]]
-name = "from_variant"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdc9cc75639b041067353b9bce2450d6847e547276c6fbe4487d7407980e07db"
-dependencies = [
- "proc-macro2",
- "swc_macros_common",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "fs-set-times"
-version = "0.20.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "033b337d725b97690d86893f9de22b67b80dcc4e9ad815f348254c38119db8fb"
-dependencies = [
- "io-lifetimes",
- "rustix",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "funty"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
-
-[[package]]
-name = "futures"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-channel"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
-dependencies = [
- "futures-core",
- "futures-sink",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
-
-[[package]]
-name = "futures-executor"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
-dependencies = [
- "futures-core",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-io"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
-
-[[package]]
-name = "futures-macro"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "futures-sink"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
-
-[[package]]
-name = "futures-task"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
-
-[[package]]
-name = "futures-util"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-macro",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "pin-utils",
- "slab",
-]
-
-[[package]]
-name = "fxhash"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
-dependencies = [
- "byteorder",
-]
-
-[[package]]
-name = "fxprof-processed-profile"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "27d12c0aed7f1e24276a241aadc4cb8ea9f83000f34bc062b7cc2d51e3b0fabd"
-dependencies = [
- "bitflags 2.5.0",
- "debugid",
- "fxhash",
- "serde",
- "serde_json",
-]
-
-[[package]]
-name = "generic-array"
-version = "0.14.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
-dependencies = [
- "typenum",
- "version_check",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
-]
-
-[[package]]
-name = "gimli"
-version = "0.28.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
-dependencies = [
- "fallible-iterator",
- "indexmap",
- "stable_deref_trait",
-]
-
-[[package]]
-name = "h2"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
-dependencies = [
- "atomic-waker",
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "http",
- "indexmap",
- "slab",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
-dependencies = [
- "ahash",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-dependencies = [
- "ahash",
- "allocator-api2",
-]
-
-[[package]]
-name = "hashlink"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
-dependencies = [
- "hashbrown 0.14.5",
-]
-
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
-[[package]]
-name = "heck"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
-
-[[package]]
-name = "hstr"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96274be293b8877e61974a607105d09c84caebe9620b47774aa8a6b942042dd4"
-dependencies = [
- "hashbrown 0.14.5",
- "new_debug_unreachable",
- "once_cell",
- "phf",
- "rustc-hash",
- "triomphe",
-]
-
-[[package]]
-name = "http"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http-body"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
-dependencies = [
- "bytes",
- "http",
-]
-
-[[package]]
-name = "http-body-util"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
-dependencies = [
- "bytes",
- "futures-core",
- "http",
- "http-body",
- "pin-project-lite",
-]
-
-[[package]]
-name = "httparse"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
-
-[[package]]
-name = "httpdate"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
-
-[[package]]
-name = "hyper"
-version = "1.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-util",
- "h2",
- "http",
- "http-body",
- "httparse",
- "httpdate",
- "itoa",
- "pin-project-lite",
- "smallvec",
- "tokio",
- "want",
-]
-
-[[package]]
-name = "hyper-rustls"
-version = "0.26.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
-dependencies = [
- "futures-util",
- "http",
- "hyper",
- "hyper-util",
- "rustls",
- "rustls-pki-types",
- "tokio",
- "tokio-rustls",
- "tower-service",
-]
-
-[[package]]
-name = "hyper-util"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-util",
- "http",
- "http-body",
- "hyper",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "iana-time-zone"
-version = "0.1.60"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
-dependencies = [
- "android_system_properties",
- "core-foundation-sys",
- "iana-time-zone-haiku",
- "js-sys",
- "wasm-bindgen",
- "windows-core",
-]
-
-[[package]]
-name = "iana-time-zone-haiku"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
-dependencies = [
- "cc",
-]
-
-[[package]]
-name = "id-arena"
-version = "2.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
-
-[[package]]
-name = "idna"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
-dependencies = [
- "unicode-bidi",
- "unicode-normalization",
-]
-
-[[package]]
-name = "if_chain"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
-
-[[package]]
-name = "import_map"
-version = "0.19.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72395c7d41857a714b5ce1266685ef3c5ceb761ce601a99c44c072d72b41a1e3"
-dependencies = [
- "indexmap",
- "log",
- "percent-encoding",
- "serde",
- "serde_json",
- "url",
-]
-
-[[package]]
-name = "indexmap"
-version = "2.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
-dependencies = [
- "equivalent",
- "hashbrown 0.14.5",
- "serde",
-]
-
-[[package]]
-name = "instant"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "io-extras"
-version = "0.18.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9f046b9af244f13b3bd939f55d16830ac3a201e8a9ba9661bfcb03e2be72b9b"
-dependencies = [
- "io-lifetimes",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "io-lifetimes"
-version = "2.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a611371471e98973dbcab4e0ec66c31a10bc356eeb4d54a0e05eac8158fe38c"
-
-[[package]]
-name = "ipnet"
-version = "2.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
-
-[[package]]
-name = "is-macro"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59a85abdc13717906baccb5a1e435556ce0df215f242892f721dff62bf25288f"
-dependencies = [
- "Inflector",
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "is_terminal_polyfill"
-version = "1.70.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
-
-[[package]]
-name = "itertools"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
-dependencies = [
- "either",
-]
-
-[[package]]
-name = "itoa"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
-
-[[package]]
-name = "ittapi"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b996fe614c41395cdaedf3cf408a9534851090959d90d54a535f675550b64b1"
-dependencies = [
- "anyhow",
- "ittapi-sys",
- "log",
-]
-
-[[package]]
-name = "ittapi-sys"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52f5385394064fa2c886205dba02598013ce83d3e92d33dbdc0c52fe0e7bf4fc"
-dependencies = [
- "cc",
-]
-
-[[package]]
-name = "jobserver"
-version = "0.1.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "js-component-bindgen"
-version = "1.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c80f3283255f5bb9cfa1a7c24701ea0fbb0b831564f286763af156477474eebf"
-dependencies = [
- "anyhow",
- "base64 0.22.1",
- "heck 0.5.0",
- "indexmap",
- "wasm-encoder 0.202.0",
- "wasmtime-environ 20.0.2",
- "wit-bindgen-core 0.24.0",
- "wit-component 0.202.0",
- "wit-parser 0.202.0",
-]
-
-[[package]]
-name = "js-sys"
-version = "0.3.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
-dependencies = [
- "wasm-bindgen",
-]
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
-name = "leb128"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
-
-[[package]]
-name = "libc"
-version = "0.2.155"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
-
-[[package]]
-name = "libm"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
-
-[[package]]
-name = "libredox"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
-dependencies = [
- "bitflags 2.5.0",
- "libc",
-]
-
-[[package]]
-name = "linux-raw-sys"
-version = "0.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
-
-[[package]]
-name = "lock_api"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
-[[package]]
-name = "log"
-version = "0.4.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
-
-[[package]]
-name = "mach2"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "matchers"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
-dependencies = [
- "regex-automata 0.1.10",
-]
-
-[[package]]
-name = "matchit"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
-
-[[package]]
-name = "maybe-owned"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4"
-
-[[package]]
-name = "memchr"
-version = "2.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
-
-[[package]]
-name = "memfd"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64"
-dependencies = [
- "rustix",
-]
-
-[[package]]
-name = "memoffset"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "mime"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
-
-[[package]]
-name = "mime_guess"
-version = "2.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
-dependencies = [
- "mime",
- "unicase",
-]
-
-[[package]]
-name = "miniz_oxide"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
-dependencies = [
- "adler",
-]
-
-[[package]]
-name = "mio"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
-dependencies = [
- "libc",
- "wasi",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "monch"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea"
-
-[[package]]
-name = "multer"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
-dependencies = [
- "bytes",
- "encoding_rs",
- "futures-util",
- "http",
- "httparse",
- "memchr",
- "mime",
- "spin",
- "version_check",
-]
-
-[[package]]
-name = "new_debug_unreachable"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
-
-[[package]]
-name = "nu-ansi-term"
-version = "0.46.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
-dependencies = [
- "overload",
- "winapi",
-]
-
-[[package]]
-name = "num-bigint"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
-dependencies = [
- "num-integer",
- "num-traits",
- "serde",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.46"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi",
- "libc",
-]
-
-[[package]]
-name = "object"
-version = "0.32.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "object"
-version = "0.33.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d"
-dependencies = [
- "crc32fast",
- "hashbrown 0.14.5",
- "indexmap",
- "memchr",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
-
-[[package]]
-name = "outref"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4"
-
-[[package]]
-name = "overload"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
-
-[[package]]
-name = "parking_lot"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
-dependencies = [
- "instant",
- "lock_api",
- "parking_lot_core 0.8.6",
-]
-
-[[package]]
-name = "parking_lot"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
-dependencies = [
- "lock_api",
- "parking_lot_core 0.9.10",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
-dependencies = [
- "cfg-if",
- "instant",
- "libc",
- "redox_syscall 0.2.16",
- "smallvec",
- "winapi",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall 0.5.1",
- "smallvec",
- "windows-targets 0.52.5",
-]
-
-[[package]]
-name = "paste"
-version = "1.0.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
-
-[[package]]
-name = "pathdiff"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
-
-[[package]]
-name = "percent-encoding"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
-
-[[package]]
-name = "petgraph"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
-dependencies = [
- "fixedbitset",
- "indexmap",
-]
-
-[[package]]
-name = "phf"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
-dependencies = [
- "phf_macros",
- "phf_shared",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
-dependencies = [
- "phf_shared",
- "rand",
-]
-
-[[package]]
-name = "phf_macros"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
-dependencies = [
- "phf_generator",
- "phf_shared",
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
-dependencies = [
- "siphasher",
-]
-
-[[package]]
-name = "pin-project"
-version = "1.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
-dependencies = [
- "pin-project-internal",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "1.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "pin-project-lite"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
-
-[[package]]
-name = "pin-utils"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
-
-[[package]]
-name = "pkg-config"
-version = "0.3.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
-
-[[package]]
-name = "postcard"
-version = "1.0.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8"
-dependencies = [
- "cobs",
- "embedded-io",
- "serde",
-]
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
-
-[[package]]
-name = "proc-macro-error"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
-dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
-dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.82"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "psm"
-version = "0.1.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874"
-dependencies = [
- "cc",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "radium"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
-
-[[package]]
-name = "radix_fmt"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426"
-
-[[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom",
-]
-
-[[package]]
-name = "rayon"
-version = "1.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
-dependencies = [
- "either",
- "rayon-core",
-]
-
-[[package]]
-name = "rayon-core"
-version = "1.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
-dependencies = [
- "crossbeam-deque",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "redb"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed7508e692a49b6b2290b56540384ccae9b1fb4d77065640b165835b56ffe3bb"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
-dependencies = [
- "bitflags 1.3.2",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
-dependencies = [
- "bitflags 2.5.0",
-]
-
-[[package]]
-name = "redox_users"
-version = "0.4.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
-dependencies = [
- "getrandom",
- "libredox",
- "thiserror",
-]
-
-[[package]]
-name = "regalloc2"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6"
-dependencies = [
- "hashbrown 0.13.2",
- "log",
- "rustc-hash",
- "slice-group-by",
- "smallvec",
-]
-
-[[package]]
-name = "regex"
-version = "1.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata 0.4.6",
- "regex-syntax 0.8.3",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "regex-syntax 0.6.29",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax 0.8.3",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
-
-[[package]]
-name = "relative-path"
-version = "1.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
-
-[[package]]
-name = "reqwest"
-version = "0.12.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10"
-dependencies = [
- "base64 0.22.1",
- "bytes",
- "encoding_rs",
- "futures-channel",
- "futures-core",
- "futures-util",
- "h2",
- "http",
- "http-body",
- "http-body-util",
- "hyper",
- "hyper-rustls",
- "hyper-util",
- "ipnet",
- "js-sys",
- "log",
- "mime",
- "once_cell",
- "percent-encoding",
- "pin-project-lite",
- "rustls",
- "rustls-pemfile",
- "rustls-pki-types",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "sync_wrapper 0.1.2",
- "system-configuration",
- "tokio",
- "tokio-rustls",
- "tower-service",
- "url",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "web-sys",
- "webpki-roots",
- "winreg",
-]
-
-[[package]]
-name = "ring"
-version = "0.17.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
-dependencies = [
- "cc",
- "cfg-if",
- "getrandom",
- "libc",
- "spin",
- "untrusted",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "rust-embed"
-version = "8.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19549741604902eb99a7ed0ee177a0663ee1eda51a29f71401f166e47e77806a"
-dependencies = [
- "rust-embed-impl",
- "rust-embed-utils",
- "walkdir",
-]
-
-[[package]]
-name = "rust-embed-impl"
-version = "8.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb9f96e283ec64401f30d3df8ee2aaeb2561f34c824381efa24a35f79bf40ee4"
-dependencies = [
- "proc-macro2",
- "quote",
- "rust-embed-utils",
- "syn 2.0.61",
- "walkdir",
-]
-
-[[package]]
-name = "rust-embed-utils"
-version = "8.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38c74a686185620830701348de757fd36bef4aa9680fd23c49fc539ddcc1af32"
-dependencies = [
- "sha2",
- "walkdir",
-]
-
-[[package]]
-name = "rustc-demangle"
-version = "0.1.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
-
-[[package]]
-name = "rustc-hash"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
-
-[[package]]
-name = "rustc_version"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
-dependencies = [
- "semver 0.9.0",
-]
-
-[[package]]
-name = "rustix"
-version = "0.38.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
-dependencies = [
- "bitflags 2.5.0",
- "errno",
- "itoa",
- "libc",
- "linux-raw-sys",
- "once_cell",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "rustls"
-version = "0.22.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
-dependencies = [
- "log",
- "ring",
- "rustls-pki-types",
- "rustls-webpki",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "rustls-pemfile"
-version = "2.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
-dependencies = [
- "base64 0.22.1",
- "rustls-pki-types",
-]
-
-[[package]]
-name = "rustls-pki-types"
-version = "1.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
-
-[[package]]
-name = "rustls-webpki"
-version = "0.102.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e"
-dependencies = [
- "ring",
- "rustls-pki-types",
- "untrusted",
-]
-
-[[package]]
-name = "rustversion"
-version = "1.0.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
-
-[[package]]
-name = "ryu"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
-
-[[package]]
-name = "ryu-js"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5"
-
-[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "scoped-tls"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
-
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
-[[package]]
-name = "semver"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
-dependencies = [
- "semver-parser",
-]
-
-[[package]]
-name = "semver"
-version = "1.0.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
-
-[[package]]
-name = "semver-parser"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
-
-[[package]]
-name = "serde"
-version = "1.0.201"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.201"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.117"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
-dependencies = [
- "indexmap",
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "serde_path_to_error"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
-dependencies = [
- "itoa",
- "serde",
-]
-
-[[package]]
-name = "serde_spanned"
-version = "0.6.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "serde_urlencoded"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
-dependencies = [
- "form_urlencoded",
- "itoa",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "sha-1"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
-[[package]]
-name = "sha2"
-version = "0.10.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
-[[package]]
-name = "sharded-slab"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
-name = "shellexpand"
-version = "2.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4"
-dependencies = [
- "dirs",
-]
-
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "simd-abstraction"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987"
-dependencies = [
- "outref",
-]
-
-[[package]]
-name = "siphasher"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
-
-[[package]]
-name = "slab"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "slice-group-by"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
-
-[[package]]
-name = "smallvec"
-version = "1.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "smartstring"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
-dependencies = [
- "autocfg",
- "static_assertions",
- "version_check",
-]
-
-[[package]]
-name = "socket2"
-version = "0.5.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
-dependencies = [
- "libc",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "sourcemap"
-version = "8.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4"
-dependencies = [
- "base64-simd",
- "bitvec",
- "data-encoding",
- "debugid",
- "if_chain",
- "rustc-hash",
- "rustc_version",
- "serde",
- "serde_json",
- "unicode-id-start",
- "url",
-]
-
-[[package]]
-name = "spdx"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29ef1a0fa1e39ac22972c8db23ff89aea700ab96aa87114e1fb55937a631a0c9"
-dependencies = [
- "smallvec",
-]
-
-[[package]]
-name = "spin"
-version = "0.9.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
-
-[[package]]
-name = "sptr"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
-
-[[package]]
-name = "stable_deref_trait"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
-
-[[package]]
-name = "stacker"
-version = "0.1.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
-dependencies = [
- "cc",
- "cfg-if",
- "libc",
- "psm",
- "winapi",
-]
-
-[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
-[[package]]
-name = "string_enum"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90"
-dependencies = [
- "proc-macro2",
- "quote",
- "swc_macros_common",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "strsim"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
-
-[[package]]
-name = "subtle"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
-
-[[package]]
-name = "swc_atoms"
-version = "0.6.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125"
-dependencies = [
- "hstr",
- "once_cell",
- "rustc-hash",
- "serde",
-]
-
-[[package]]
-name = "swc_bundler"
-version = "0.227.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1a212bd08b1121c7204a04407ea055779fc00cf80024fc666dd97b00749cf87"
-dependencies = [
- "anyhow",
- "crc",
- "indexmap",
- "is-macro",
- "once_cell",
- "parking_lot 0.12.3",
- "petgraph",
- "radix_fmt",
- "relative-path",
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "swc_ecma_codegen",
- "swc_ecma_loader",
- "swc_ecma_parser",
- "swc_ecma_transforms_base",
- "swc_ecma_transforms_optimization",
- "swc_ecma_utils",
- "swc_ecma_visit",
- "swc_fast_graph",
- "swc_graph_analyzer",
- "tracing",
-]
-
-[[package]]
-name = "swc_cached"
-version = "0.3.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c"
-dependencies = [
- "ahash",
- "anyhow",
- "dashmap",
- "once_cell",
- "regex",
- "serde",
-]
-
-[[package]]
-name = "swc_common"
-version = "0.33.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2f9706038906e66f3919028f9f7a37f3ed552f1b85578e93f4468742e2da438"
-dependencies = [
- "ast_node",
- "better_scoped_tls",
- "cfg-if",
- "either",
- "from_variant",
- "new_debug_unreachable",
- "num-bigint",
- "once_cell",
- "rustc-hash",
- "serde",
- "siphasher",
- "sourcemap",
- "swc_atoms",
- "swc_eq_ignore_macros",
- "swc_visit",
- "tracing",
- "unicode-width",
- "url",
-]
-
-[[package]]
-name = "swc_config"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7be1a689e146be1eae53139482cb061dcf0fa01dff296bbe7b96fff92d8e2936"
-dependencies = [
- "anyhow",
- "indexmap",
- "serde",
- "serde_json",
- "swc_cached",
- "swc_config_macro",
-]
-
-[[package]]
-name = "swc_config_macro"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399"
-dependencies = [
- "proc-macro2",
- "quote",
- "swc_macros_common",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "swc_ecma_ast"
-version = "0.113.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1690cc0c9ab60b44ac0225ba1e231ac532f7ba1d754df761c6ee607561afae"
-dependencies = [
- "bitflags 2.5.0",
- "is-macro",
- "num-bigint",
- "phf",
- "scoped-tls",
- "serde",
- "string_enum",
- "swc_atoms",
- "swc_common",
- "unicode-id-start",
-]
-
-[[package]]
-name = "swc_ecma_codegen"
-version = "0.149.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fef147127a2926ca26171c7afcbf028ff86dc543ced87d316713f25620a15b9"
-dependencies = [
- "memchr",
- "num-bigint",
- "once_cell",
- "rustc-hash",
- "serde",
- "sourcemap",
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "swc_ecma_codegen_macros",
- "tracing",
-]
-
-[[package]]
-name = "swc_ecma_codegen_macros"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "090e409af49c8d1a3c13b3aab1ed09dd4eda982207eb3e63c2ad342f072b49c8"
-dependencies = [
- "proc-macro2",
- "quote",
- "swc_macros_common",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "swc_ecma_loader"
-version = "0.45.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92c68f934bd2c51f29c4ad0bcae09924e9dc30d7ce0680367d45b42d40338a67"
-dependencies = [
- "anyhow",
- "pathdiff",
- "serde",
- "swc_atoms",
- "swc_common",
- "tracing",
-]
-
-[[package]]
-name = "swc_ecma_parser"
-version = "0.144.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0499e69683ae5d67a20ff0279b94bc90f29df7922a46331b54d5dd367bf89570"
-dependencies = [
- "either",
- "new_debug_unreachable",
- "num-bigint",
- "num-traits",
- "phf",
- "serde",
- "smallvec",
- "smartstring",
- "stacker",
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "tracing",
- "typed-arena",
-]
-
-[[package]]
-name = "swc_ecma_transforms_base"
-version = "0.138.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eddb95c2bdad1c9c29edf35712e1e0f9b9ddc1cdb5ba2d582fd93468cb075a03"
-dependencies = [
- "better_scoped_tls",
- "bitflags 2.5.0",
- "indexmap",
- "once_cell",
- "phf",
- "rustc-hash",
- "serde",
- "smallvec",
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "swc_ecma_parser",
- "swc_ecma_utils",
- "swc_ecma_visit",
- "tracing",
-]
-
-[[package]]
-name = "swc_ecma_transforms_classes"
-version = "0.127.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53043d81678f3c693604eeb1d1f0fe6ba10f303104a31b954dbeebed9cadf530"
-dependencies = [
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "swc_ecma_transforms_base",
- "swc_ecma_utils",
- "swc_ecma_visit",
-]
-
-[[package]]
-name = "swc_ecma_transforms_macros"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab"
-dependencies = [
- "proc-macro2",
- "quote",
- "swc_macros_common",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "swc_ecma_transforms_optimization"
-version = "0.199.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32ea30b3df748236c619409f222f0ba68ebeebc08dfff109d2195664a15689f9"
-dependencies = [
- "dashmap",
- "indexmap",
- "once_cell",
- "petgraph",
- "rustc-hash",
- "serde_json",
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "swc_ecma_parser",
- "swc_ecma_transforms_base",
- "swc_ecma_transforms_macros",
- "swc_ecma_utils",
- "swc_ecma_visit",
- "swc_fast_graph",
- "tracing",
-]
-
-[[package]]
-name = "swc_ecma_transforms_proposal"
-version = "0.172.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fbc414d6a9c5479cfb4c6e92fcdac504582bd7bc89a0ed7f8808b72dc8bd1f0"
-dependencies = [
- "either",
- "rustc-hash",
- "serde",
- "smallvec",
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "swc_ecma_transforms_base",
- "swc_ecma_transforms_classes",
- "swc_ecma_transforms_macros",
- "swc_ecma_utils",
- "swc_ecma_visit",
-]
-
-[[package]]
-name = "swc_ecma_transforms_react"
-version = "0.184.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "565a76c4ca47ce31d78301c0beab878e4c2cb4f624691254d834ec8c0e236755"
-dependencies = [
- "base64 0.21.7",
- "dashmap",
- "indexmap",
- "once_cell",
- "serde",
- "sha-1",
- "string_enum",
- "swc_atoms",
- "swc_common",
- "swc_config",
- "swc_ecma_ast",
- "swc_ecma_parser",
- "swc_ecma_transforms_base",
- "swc_ecma_transforms_macros",
- "swc_ecma_utils",
- "swc_ecma_visit",
-]
-
-[[package]]
-name = "swc_ecma_transforms_typescript"
-version = "0.189.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e209026c1d3c577cafac257d87e7c0d23119282fbdc8ed03d7f56077e95beb90"
-dependencies = [
- "ryu-js",
- "serde",
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "swc_ecma_transforms_base",
- "swc_ecma_transforms_react",
- "swc_ecma_utils",
- "swc_ecma_visit",
-]
-
-[[package]]
-name = "swc_ecma_utils"
-version = "0.128.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe5242670bc74e0a0b64b9d4912b37be36944517ce0881314162aeb4381272c3"
-dependencies = [
- "indexmap",
- "num_cpus",
- "once_cell",
- "rustc-hash",
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "swc_ecma_visit",
- "tracing",
- "unicode-id",
-]
-
-[[package]]
-name = "swc_ecma_visit"
-version = "0.99.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28a6ce28ad8e591f8d627f1f9cb26b25e5d83052a9bc1b674d95fc28040cfa98"
-dependencies = [
- "num-bigint",
- "swc_atoms",
- "swc_common",
- "swc_ecma_ast",
- "swc_visit",
- "tracing",
-]
-
-[[package]]
-name = "swc_eq_ignore_macros"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "695a1d8b461033d32429b5befbf0ad4d7a2c4d6ba9cd5ba4e0645c615839e8e4"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "swc_fast_graph"
-version = "0.21.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3fdd64bc3d161d6c1ea9a8ae5779e4ba132afc67e7b8ece5420bfc9c6e1275d"
-dependencies = [
- "indexmap",
- "petgraph",
- "rustc-hash",
- "swc_common",
-]
-
-[[package]]
-name = "swc_graph_analyzer"
-version = "0.22.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c728a8f9b82b7160a1ae246e31232177b371f827eb0d01006c0f120a3494871c"
-dependencies = [
- "auto_impl",
- "petgraph",
- "swc_common",
- "swc_fast_graph",
- "tracing",
-]
-
-[[package]]
-name = "swc_macros_common"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91745f3561057493d2da768437c427c0e979dff7396507ae02f16c981c4a8466"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "swc_visit"
-version = "0.5.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "043d11fe683dcb934583ead49405c0896a5af5face522e4682c16971ef7871b9"
-dependencies = [
- "either",
- "swc_visit_macros",
-]
-
-[[package]]
-name = "swc_visit_macros"
-version = "0.5.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ae9ef18ff8daffa999f729db056d2821cd2f790f3a11e46422d19f46bb193e7"
-dependencies = [
- "Inflector",
- "proc-macro2",
- "quote",
- "swc_macros_common",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "unicode-ident",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.61"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "sync_wrapper"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
-
-[[package]]
-name = "sync_wrapper"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
-
-[[package]]
-name = "system-configuration"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
-dependencies = [
- "bitflags 1.3.2",
- "core-foundation",
- "system-configuration-sys",
-]
-
-[[package]]
-name = "system-configuration-sys"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "system-interface"
-version = "0.27.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b858526d22750088a9b3cf2e3c2aacebd5377f13adeec02860c30d09113010a6"
-dependencies = [
- "bitflags 2.5.0",
- "cap-fs-ext",
- "cap-std",
- "fd-lock",
- "io-lifetimes",
- "rustix",
- "windows-sys 0.52.0",
- "winx",
-]
-
-[[package]]
-name = "tap"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
-
-[[package]]
-name = "target-lexicon"
-version = "0.12.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
-
-[[package]]
-name = "tempfile"
-version = "3.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
-dependencies = [
- "cfg-if",
- "fastrand",
- "rustix",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "termcolor"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "text_lines"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fd5828de7deaa782e1dd713006ae96b3bee32d3279b79eb67ecf8072c059bcf"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "thiserror"
-version = "1.0.61"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
-dependencies = [
- "thiserror-impl",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.61"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "thread_local"
-version = "1.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
-dependencies = [
- "cfg-if",
- "once_cell",
-]
-
-[[package]]
-name = "tinyvec"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
-dependencies = [
- "tinyvec_macros",
-]
-
-[[package]]
-name = "tinyvec_macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-
-[[package]]
-name = "tokio"
-version = "1.37.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787"
-dependencies = [
- "backtrace",
- "bytes",
- "libc",
- "mio",
- "num_cpus",
- "pin-project-lite",
- "signal-hook-registry",
- "socket2",
- "tokio-macros",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "tokio-rustls"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
-dependencies = [
- "rustls",
- "rustls-pki-types",
- "tokio",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.7.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "toml"
-version = "0.8.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
-dependencies = [
- "serde",
- "serde_spanned",
- "toml_datetime",
- "toml_edit",
-]
-
-[[package]]
-name = "toml_datetime"
-version = "0.6.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "toml_edit"
-version = "0.22.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
-dependencies = [
- "indexmap",
- "serde",
- "serde_spanned",
- "toml_datetime",
- "winnow",
-]
-
-[[package]]
-name = "tower"
-version = "0.4.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
-dependencies = [
- "futures-core",
- "futures-util",
- "pin-project",
- "pin-project-lite",
- "tokio",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "tower-http"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
-dependencies = [
- "bitflags 2.5.0",
- "bytes",
- "http",
- "http-body",
- "http-body-util",
- "pin-project-lite",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "tower-layer"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
-
-[[package]]
-name = "tower-service"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
-
-[[package]]
-name = "tracing"
-version = "0.1.40"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
-dependencies = [
- "log",
- "pin-project-lite",
- "tracing-attributes",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-attributes"
-version = "0.1.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
-dependencies = [
- "once_cell",
- "valuable",
-]
-
-[[package]]
-name = "tracing-log"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
-dependencies = [
- "log",
- "once_cell",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-serde"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
-dependencies = [
- "serde",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-subscriber"
-version = "0.3.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
-dependencies = [
- "matchers",
- "nu-ansi-term",
- "once_cell",
- "regex",
- "serde",
- "serde_json",
- "sharded-slab",
- "smallvec",
- "thread_local",
- "tracing",
- "tracing-core",
- "tracing-log",
- "tracing-serde",
-]
-
-[[package]]
-name = "triomphe"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b2cb4fbb9995eeb36ac86fadf24031ccd58f99d6b4b2d7b911db70bddb80d90"
-dependencies = [
- "serde",
- "stable_deref_trait",
-]
-
-[[package]]
-name = "try-lock"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
-
-[[package]]
-name = "twox-hash"
-version = "1.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
-dependencies = [
- "cfg-if",
- "rand",
- "static_assertions",
-]
-
-[[package]]
-name = "typed-arena"
-version = "2.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
-
-[[package]]
-name = "typenum"
-version = "1.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
-
-[[package]]
-name = "unicase"
-version = "2.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
-dependencies = [
- "version_check",
-]
-
-[[package]]
-name = "unicode-bidi"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
-
-[[package]]
-name = "unicode-id"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f"
-
-[[package]]
-name = "unicode-id-start"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02aebfa694eccbbbffdd92922c7de136b9fe764396d2f10e21bce1681477cfc1"
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
-
-[[package]]
-name = "unicode-normalization"
-version = "0.1.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
-dependencies = [
- "tinyvec",
-]
-
-[[package]]
-name = "unicode-width"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
-
-[[package]]
-name = "unicode-xid"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
-
-[[package]]
-name = "untrusted"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
-
-[[package]]
-name = "url"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
-dependencies = [
- "form_urlencoded",
- "idna",
- "percent-encoding",
- "serde",
-]
-
-[[package]]
-name = "usuba"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "async-trait",
- "axum",
- "blake3",
- "bytes",
- "hyper-util",
- "mime_guess",
- "redb",
- "rust-embed",
- "serde",
- "serde_json",
- "tempfile",
- "thiserror",
- "tokio",
- "tower-http",
- "tracing",
- "tracing-subscriber",
- "usuba-bundle",
- "utoipa",
- "utoipa-swagger-ui",
- "wasmtime",
- "wasmtime-wasi",
- "wit-parser 0.208.1",
-]
-
-[[package]]
-name = "usuba-bundle"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "bytes",
- "deno_emit",
- "deno_graph",
- "reqwest",
- "tokio",
- "tracing",
- "tracing-subscriber",
- "url",
-]
-
-[[package]]
-name = "usuba-compat"
-version = "0.1.0"
-dependencies = [
- "blake3",
- "js-component-bindgen",
- "wasmtime-environ 20.0.2",
- "wit-bindgen",
-]
-
-[[package]]
-name = "utf8parse"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
-
-[[package]]
-name = "utoipa"
-version = "4.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23"
-dependencies = [
- "indexmap",
- "serde",
- "serde_json",
- "utoipa-gen",
-]
-
-[[package]]
-name = "utoipa-gen"
-version = "4.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7bf0e16c02bc4bf5322ab65f10ab1149bdbcaa782cba66dc7057370a3f8190be"
-dependencies = [
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "regex",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "utoipa-swagger-ui"
-version = "7.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49396bb19184d3ac1220bb56b3ec91e3d6275baa6cbd5a0f36f110a88d6afb"
-dependencies = [
- "axum",
- "mime_guess",
- "regex",
- "reqwest",
- "rust-embed",
- "serde",
- "serde_json",
- "utoipa",
- "zip",
-]
-
-[[package]]
-name = "uuid"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
-
-[[package]]
-name = "valuable"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
-
-[[package]]
-name = "verification-svc"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "async-trait",
- "axum",
- "clap",
- "serde",
- "serde_json",
- "tempfile",
- "thiserror",
- "tokio",
- "tower-http",
- "tracing",
- "tracing-subscriber",
- "url",
- "yaml-rust2",
-]
-
-[[package]]
-name = "version_check"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
-
-[[package]]
-name = "walkdir"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
-dependencies = [
- "same-file",
- "winapi-util",
-]
-
-[[package]]
-name = "want"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
-dependencies = [
- "try-lock",
-]
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
-dependencies = [
- "cfg-if",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
-dependencies = [
- "bumpalo",
- "log",
- "once_cell",
- "proc-macro2",
- "quote",
- "syn 2.0.61",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-futures"
-version = "0.4.42"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
-dependencies = [
- "cfg-if",
- "js-sys",
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
-
-[[package]]
-name = "wasm-encoder"
-version = "0.202.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfd106365a7f5f7aa3c1916a98cbb3ad477f5ff96ddb130285a91c6e7429e67a"
-dependencies = [
- "leb128",
-]
-
-[[package]]
-name = "wasm-encoder"
-version = "0.207.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d996306fb3aeaee0d9157adbe2f670df0236caf19f6728b221e92d0f27b3fe17"
-dependencies = [
- "leb128",
-]
-
-[[package]]
-name = "wasm-encoder"
-version = "0.208.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6425e84e42f7f558478e40ecc2287912cb319f2ca68e5c0bb93c61d4fc63fa17"
-dependencies = [
- "leb128",
-]
-
-[[package]]
-name = "wasm-metadata"
-version = "0.202.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "094aea3cb90e09f16ee25a4c0e324b3e8c934e7fd838bfa039aef5352f44a917"
-dependencies = [
- "anyhow",
- "indexmap",
- "serde",
- "serde_derive",
- "serde_json",
- "spdx",
- "wasm-encoder 0.202.0",
- "wasmparser 0.202.0",
-]
-
-[[package]]
-name = "wasm-metadata"
-version = "0.208.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42a2c4280ad374a6db3d76d4bb61e2ec4b3b9ce5469cc4f2bbc5708047a2bbff"
-dependencies = [
- "anyhow",
- "indexmap",
- "serde",
- "serde_derive",
- "serde_json",
- "spdx",
- "wasm-encoder 0.208.1",
- "wasmparser 0.208.1",
-]
-
-[[package]]
-name = "wasmparser"
-version = "0.202.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6998515d3cf3f8b980ef7c11b29a9b1017d4cf86b99ae93b546992df9931413"
-dependencies = [
- "bitflags 2.5.0",
- "indexmap",
- "semver 1.0.23",
-]
-
-[[package]]
-name = "wasmparser"
-version = "0.207.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e19bb9f8ab07616da582ef8adb24c54f1424c7ec876720b7da9db8ec0626c92c"
-dependencies = [
- "ahash",
- "bitflags 2.5.0",
- "hashbrown 0.14.5",
- "indexmap",
- "semver 1.0.23",
-]
-
-[[package]]
-name = "wasmparser"
-version = "0.208.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd921789c9dcc495f589cb37d200155dee65b4a4beeb853323b5e24e0a5f9c58"
-dependencies = [
- "ahash",
- "bitflags 2.5.0",
- "hashbrown 0.14.5",
- "indexmap",
- "semver 1.0.23",
-]
-
-[[package]]
-name = "wasmprinter"
-version = "0.202.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab1cc9508685eef9502e787f4d4123745f5651a1e29aec047645d3cac1e2da7a"
-dependencies = [
- "anyhow",
- "wasmparser 0.202.0",
-]
-
-[[package]]
-name = "wasmprinter"
-version = "0.207.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c2d8a7b4dabb460208e6b4334d9db5766e84505038b2529e69c3d07ac619115"
-dependencies = [
- "anyhow",
- "wasmparser 0.207.0",
-]
-
-[[package]]
-name = "wasmtime"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f92a1370c66a0022e6d92dcc277e2c84f5dece19569670b8ce7db8162560d8b6"
-dependencies = [
- "addr2line",
- "anyhow",
- "async-trait",
- "bumpalo",
- "cc",
- "cfg-if",
- "encoding_rs",
- "fxprof-processed-profile",
- "gimli",
- "hashbrown 0.14.5",
- "indexmap",
- "ittapi",
- "libc",
- "libm",
- "log",
- "mach2",
- "memfd",
- "memoffset",
- "object 0.33.0",
- "once_cell",
- "paste",
- "postcard",
- "psm",
- "rayon",
- "rustix",
- "semver 1.0.23",
- "serde",
- "serde_derive",
- "serde_json",
- "smallvec",
- "sptr",
- "target-lexicon",
- "wasm-encoder 0.207.0",
- "wasmparser 0.207.0",
- "wasmtime-asm-macros",
- "wasmtime-cache",
- "wasmtime-component-macro",
- "wasmtime-component-util 21.0.1",
- "wasmtime-cranelift",
- "wasmtime-environ 21.0.1",
- "wasmtime-fiber",
- "wasmtime-jit-debug",
- "wasmtime-jit-icache-coherence",
- "wasmtime-slab",
- "wasmtime-versioned-export-macros",
- "wasmtime-winch",
- "wat",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "wasmtime-asm-macros"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6dee8679c974a7f258c03d60d3c747c426ed219945b6d08cbc77fd2eab15b2d1"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "wasmtime-cache"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b00103ffaf7ee980f4e750fe272b6ada79d9901659892e457c7ca316b16df9ec"
-dependencies = [
- "anyhow",
- "base64 0.21.7",
- "directories-next",
- "log",
- "postcard",
- "rustix",
- "serde",
- "serde_derive",
- "sha2",
- "toml",
- "windows-sys 0.52.0",
- "zstd",
-]
-
-[[package]]
-name = "wasmtime-component-macro"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32cae30035f1cf97dcc6657c979cf39f99ce6be93583675eddf4aeaa5548509c"
-dependencies = [
- "anyhow",
- "proc-macro2",
- "quote",
- "syn 2.0.61",
- "wasmtime-component-util 21.0.1",
- "wasmtime-wit-bindgen",
- "wit-parser 0.207.0",
-]
-
-[[package]]
-name = "wasmtime-component-util"
-version = "20.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7839a1b9e15d17be1cb2a105f18be8e0bbf52bdec7a7cd6eb5d80d4c2cdf74f0"
-
-[[package]]
-name = "wasmtime-component-util"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7ae611f08cea620c67330925be28a96115bf01f8f393a6cbdf4856a86087134"
-
-[[package]]
-name = "wasmtime-cranelift"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2909406a6007e28be964067167890bca4574bd48a9ff18f1fa9f4856d89ea40"
-dependencies = [
- "anyhow",
- "cfg-if",
- "cranelift-codegen",
- "cranelift-control",
- "cranelift-entity 0.108.1",
- "cranelift-frontend",
- "cranelift-native",
- "cranelift-wasm",
- "gimli",
- "log",
- "object 0.33.0",
- "target-lexicon",
- "thiserror",
- "wasmparser 0.207.0",
- "wasmtime-environ 21.0.1",
- "wasmtime-versioned-export-macros",
-]
-
-[[package]]
-name = "wasmtime-environ"
-version = "20.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad72e2e3f7ea5b50fedf66dd36ba24634e4f445c370644683b433d45d88f6126"
-dependencies = [
- "anyhow",
- "bincode",
- "cranelift-entity 0.107.2",
- "gimli",
- "indexmap",
- "log",
- "object 0.33.0",
- "serde",
- "serde_derive",
- "target-lexicon",
- "thiserror",
- "wasm-encoder 0.202.0",
- "wasmparser 0.202.0",
- "wasmprinter 0.202.0",
- "wasmtime-component-util 20.0.2",
- "wasmtime-types 20.0.2",
-]
-
-[[package]]
-name = "wasmtime-environ"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40e227f9ed2f5421473723d6c0352b5986e6e6044fde5410a274a394d726108f"
-dependencies = [
- "anyhow",
- "cpp_demangle",
- "cranelift-entity 0.108.1",
- "gimli",
- "indexmap",
- "log",
- "object 0.33.0",
- "postcard",
- "rustc-demangle",
- "serde",
- "serde_derive",
- "target-lexicon",
- "wasm-encoder 0.207.0",
- "wasmparser 0.207.0",
- "wasmprinter 0.207.0",
- "wasmtime-component-util 21.0.1",
- "wasmtime-types 21.0.1",
-]
-
-[[package]]
-name = "wasmtime-fiber"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42edb392586d07038c1638e854382db916b6ca7845a2e6a7f8dc49e08907acdd"
-dependencies = [
- "anyhow",
- "cc",
- "cfg-if",
- "rustix",
- "wasmtime-asm-macros",
- "wasmtime-versioned-export-macros",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "wasmtime-jit-debug"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95b26ef7914af0c0e3ca811bdc32f5f66fbba0fd21e1f8563350e8a7951e3598"
-dependencies = [
- "object 0.33.0",
- "once_cell",
- "rustix",
- "wasmtime-versioned-export-macros",
-]
-
-[[package]]
-name = "wasmtime-jit-icache-coherence"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afe088f9b56bb353adaf837bf7e10f1c2e1676719dd5be4cac8e37f2ba1ee5bc"
-dependencies = [
- "anyhow",
- "cfg-if",
- "libc",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "wasmtime-slab"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ff75cafffe47b04b036385ce3710f209153525b0ed19d57b0cf44a22d446460"
-
-[[package]]
-name = "wasmtime-types"
-version = "20.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84d5381ff174faded38c7b2085fbe430dff59489c87a91403354d710075750fb"
-dependencies = [
- "cranelift-entity 0.107.2",
- "serde",
- "serde_derive",
- "thiserror",
- "wasmparser 0.202.0",
-]
-
-[[package]]
-name = "wasmtime-types"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f2fa462bfea3220711c84e2b549f147e4df89eeb49b8a2a3d89148f6cc4a8b1"
-dependencies = [
- "cranelift-entity 0.108.1",
- "serde",
- "serde_derive",
- "smallvec",
- "wasmparser 0.207.0",
-]
-
-[[package]]
-name = "wasmtime-versioned-export-macros"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4cedc5bfef3db2a85522ee38564b47ef3b7fc7c92e94cacbce99808e63cdd47"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "wasmtime-wasi"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdbbe94245904d4c96c7c5f7b55bad896cc27908644efd9442063c0748b631fc"
-dependencies = [
- "anyhow",
- "async-trait",
- "bitflags 2.5.0",
- "bytes",
- "cap-fs-ext",
- "cap-net-ext",
- "cap-rand",
- "cap-std",
- "cap-time-ext",
- "fs-set-times",
- "futures",
- "io-extras",
- "io-lifetimes",
- "once_cell",
- "rustix",
- "system-interface",
- "thiserror",
- "tokio",
- "tracing",
- "url",
- "wasmtime",
- "wiggle",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "wasmtime-winch"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b27054fed6be4f3800aba5766f7ef435d4220ce290788f021a08d4fa573108"
-dependencies = [
- "anyhow",
- "cranelift-codegen",
- "gimli",
- "object 0.33.0",
- "target-lexicon",
- "wasmparser 0.207.0",
- "wasmtime-cranelift",
- "wasmtime-environ 21.0.1",
- "winch-codegen",
-]
-
-[[package]]
-name = "wasmtime-wit-bindgen"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c936a52ce69c28de2aa3b5fb4f2dbbb2966df304f04cccb7aca4ba56d915fda0"
-dependencies = [
- "anyhow",
- "heck 0.4.1",
- "indexmap",
- "wit-parser 0.207.0",
-]
-
-[[package]]
-name = "wast"
-version = "35.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68"
-dependencies = [
- "leb128",
-]
-
-[[package]]
-name = "wast"
-version = "208.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc00b3f023b4e2ccd2e054e240294263db52ae962892e6523e550783c83a67f1"
-dependencies = [
- "bumpalo",
- "leb128",
- "memchr",
- "unicode-width",
- "wasm-encoder 0.208.1",
-]
-
-[[package]]
-name = "wat"
-version = "1.208.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58ed38e59176550214c025ea2bd0eeefd8e86b92d0af6698d5ba95020ec2e07b"
-dependencies = [
- "wast 208.0.1",
-]
-
-[[package]]
-name = "web-sys"
-version = "0.3.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "webpki-roots"
-version = "0.26.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
-dependencies = [
- "rustls-pki-types",
-]
-
-[[package]]
-name = "wiggle"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a89ea6f74ece6d1cfbd089783006b8eb69a0219ca83cad22068f0d9fa9df3f91"
-dependencies = [
- "anyhow",
- "async-trait",
- "bitflags 2.5.0",
- "thiserror",
- "tracing",
- "wasmtime",
- "wiggle-macro",
-]
-
-[[package]]
-name = "wiggle-generate"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36beda94813296ecaf0d91b7ada9da073fd41865ba339bdd3b7764e2e785b8e9"
-dependencies = [
- "anyhow",
- "heck 0.4.1",
- "proc-macro2",
- "quote",
- "shellexpand",
- "syn 2.0.61",
- "witx",
-]
-
-[[package]]
-name = "wiggle-macro"
-version = "21.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b47d2b4442ce93106dba5d1a9c59d5f85b5732878bb3d0598d3c93c0d01b16b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
- "wiggle-generate",
-]
-
-[[package]]
-name = "winapi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
-dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
-]
-
-[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-
-[[package]]
-name = "winapi-util"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
-dependencies = [
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
-
-[[package]]
-name = "winch-codegen"
-version = "0.19.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dc69899ccb2da7daa4df31426dcfd284b104d1a85e1dae35806df0c46187f87"
-dependencies = [
- "anyhow",
- "cranelift-codegen",
- "gimli",
- "regalloc2",
- "smallvec",
- "target-lexicon",
- "wasmparser 0.207.0",
- "wasmtime-cranelift",
- "wasmtime-environ 21.0.1",
-]
-
-[[package]]
-name = "windows-core"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
-dependencies = [
- "windows-targets 0.52.5",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
-dependencies = [
- "windows-targets 0.48.5",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
-dependencies = [
- "windows-targets 0.52.5",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
-dependencies = [
- "windows_aarch64_gnullvm 0.48.5",
- "windows_aarch64_msvc 0.48.5",
- "windows_i686_gnu 0.48.5",
- "windows_i686_msvc 0.48.5",
- "windows_x86_64_gnu 0.48.5",
- "windows_x86_64_gnullvm 0.48.5",
- "windows_x86_64_msvc 0.48.5",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
-dependencies = [
- "windows_aarch64_gnullvm 0.52.5",
- "windows_aarch64_msvc 0.52.5",
- "windows_i686_gnu 0.52.5",
- "windows_i686_gnullvm",
- "windows_i686_msvc 0.52.5",
- "windows_x86_64_gnu 0.52.5",
- "windows_x86_64_gnullvm 0.52.5",
- "windows_x86_64_msvc 0.52.5",
-]
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
-
-[[package]]
-name = "winnow"
-version = "0.6.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "winreg"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
-dependencies = [
- "cfg-if",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "winx"
-version = "0.36.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9643b83820c0cd246ecabe5fa454dd04ba4fa67996369466d0747472d337346"
-dependencies = [
- "bitflags 2.5.0",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "wit-bindgen"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f497a5ce965e6cb9929079bb4af633bd88dfb19d0dfc5341580e354947f00b2"
-dependencies = [
- "wit-bindgen-rt",
- "wit-bindgen-rust-macro",
-]
-
-[[package]]
-name = "wit-bindgen-core"
-version = "0.24.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b67e11c950041849a10828c7600ea62a4077c01e8af72e8593253575428f91b"
-dependencies = [
- "anyhow",
- "wit-parser 0.202.0",
-]
-
-[[package]]
-name = "wit-bindgen-core"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7076a12e69af6e1f6093bd16657d7ae61c30cfd3c5f62321046eb863b17ab1e2"
-dependencies = [
- "anyhow",
- "heck 0.5.0",
- "wit-parser 0.208.1",
-]
-
-[[package]]
-name = "wit-bindgen-rt"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef83e2f948056d4195b4c2a236a10378b70c8fd7501039c5a106c1a756fa7da6"
-dependencies = [
- "bitflags 2.5.0",
-]
-
-[[package]]
-name = "wit-bindgen-rust"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f8ca0dd2aa75452450da1906391aba9d3a43d95fa920e872361ea00acc452a5"
-dependencies = [
- "anyhow",
- "heck 0.5.0",
- "indexmap",
- "wasm-metadata 0.208.1",
- "wit-bindgen-core 0.25.0",
- "wit-component 0.208.1",
-]
-
-[[package]]
-name = "wit-bindgen-rust-macro"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d734e18bdf439ed86afe9d85fc5bfa38db4b676fc0e0a0dae95bd8f14de039"
-dependencies = [
- "anyhow",
- "proc-macro2",
- "quote",
- "syn 2.0.61",
- "wit-bindgen-core 0.25.0",
- "wit-bindgen-rust",
-]
-
-[[package]]
-name = "wit-component"
-version = "0.202.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c836b1fd9932de0431c1758d8be08212071b6bba0151f7bac826dbc4312a2a9"
-dependencies = [
- "anyhow",
- "bitflags 2.5.0",
- "indexmap",
- "log",
- "serde",
- "serde_derive",
- "serde_json",
- "wasm-encoder 0.202.0",
- "wasm-metadata 0.202.0",
- "wasmparser 0.202.0",
- "wat",
- "wit-parser 0.202.0",
-]
-
-[[package]]
-name = "wit-component"
-version = "0.208.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fef7dd0e47f5135dd8739ccc5b188ab8b7e27e1d64df668aa36680f0b8646db8"
-dependencies = [
- "anyhow",
- "bitflags 2.5.0",
- "indexmap",
- "log",
- "serde",
- "serde_derive",
- "serde_json",
- "wasm-encoder 0.208.1",
- "wasm-metadata 0.208.1",
- "wasmparser 0.208.1",
- "wit-parser 0.208.1",
-]
-
-[[package]]
-name = "wit-parser"
-version = "0.202.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "744237b488352f4f27bca05a10acb79474415951c450e52ebd0da784c1df2bcc"
-dependencies = [
- "anyhow",
- "id-arena",
- "indexmap",
- "log",
- "semver 1.0.23",
- "serde",
- "serde_derive",
- "serde_json",
- "unicode-xid",
- "wasmparser 0.202.0",
-]
-
-[[package]]
-name = "wit-parser"
-version = "0.207.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78c83dab33a9618d86cfe3563cc864deffd08c17efc5db31a3b7cd1edeffe6e1"
-dependencies = [
- "anyhow",
- "id-arena",
- "indexmap",
- "log",
- "semver 1.0.23",
- "serde",
- "serde_derive",
- "serde_json",
- "unicode-xid",
- "wasmparser 0.207.0",
-]
-
-[[package]]
-name = "wit-parser"
-version = "0.208.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "516417a730725fe3e6c9e2efc8d86697480f80496d32b24e62736950704c047c"
-dependencies = [
- "anyhow",
- "id-arena",
- "indexmap",
- "log",
- "semver 1.0.23",
- "serde",
- "serde_derive",
- "serde_json",
- "unicode-xid",
- "wasmparser 0.208.1",
-]
-
-[[package]]
-name = "witx"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e366f27a5cabcddb2706a78296a40b8fcc451e1a6aba2fc1d94b4a01bdaaef4b"
-dependencies = [
- "anyhow",
- "log",
- "thiserror",
- "wast 35.0.2",
-]
-
-[[package]]
-name = "wyz"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
-dependencies = [
- "tap",
-]
-
-[[package]]
-name = "yaml-rust2"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8"
-dependencies = [
- "arraydeque",
- "encoding_rs",
- "hashlink",
-]
-
-[[package]]
-name = "zerocopy"
-version = "0.7.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
-dependencies = [
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.7.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.61",
-]
-
-[[package]]
-name = "zeroize"
-version = "1.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
-
-[[package]]
-name = "zip"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7"
-dependencies = [
- "arbitrary",
- "crc32fast",
- "crossbeam-utils",
- "displaydoc",
- "flate2",
- "indexmap",
- "thiserror",
-]
-
-[[package]]
-name = "zstd"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a"
-dependencies = [
- "zstd-safe",
-]
-
-[[package]]
-name = "zstd-safe"
-version = "7.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a"
-dependencies = [
- "zstd-sys",
-]
-
-[[package]]
-name = "zstd-sys"
-version = "2.0.11+zstd.1.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4"
-dependencies = [
- "cc",
- "pkg-config",
-]
diff --git a/Cargo.toml b/Cargo.toml
deleted file mode 100644
index f7a0160b6..000000000
--- a/Cargo.toml
+++ /dev/null
@@ -1,52 +0,0 @@
-[workspace]
-members = [
- "rust/usuba",
- "rust/usuba-compat",
- "rust/usuba-bundle",
- "rust/verification-svc"
-]
-
-# See: https://github.com/rust-lang/rust/issues/90148#issuecomment-949194352
-resolver = "2"
-
-[workspace.dependencies]
-anyhow = { version = "1" }
-async-trait = { version = "0.1" }
-axum = { version = "0.7" }
-blake3 = { version = "1.5" }
-bytes = { version = "1" }
-clap = { version = "4.5" }
-deno_emit = { version = "0.42" }
-deno_graph = { version = "0.78" }
-http = { version = "1.1" }
-http-body-util = { version = "0.1" }
-hyper-util = { version = "0.1", features = ["client", "client-legacy"] }
-js-component-bindgen = { version = "1", features = ["transpile-bindgen"] }
-js-sys = { version = "0.3" }
-mime_guess = { version = "2" }
-redb = { version = "2" }
-reqwest = { version = "0.12", default-features = false }
-rust-embed = { version = "8.4" }
-serde = { version = "1" }
-serde_json = { version = "1" }
-tempfile = { version = "3" }
-thiserror = { version = "1" }
-tokio = { version = "1" }
-tower-http = { version = "0.5" }
-tracing = { version = "0.1" }
-tracing-subscriber = { version = "0.3", features = ["env-filter", "tracing-log", "json"] }
-tracing-web = { version = "0.1" }
-url = { version = "2" }
-usuba-bundle = { path = "./rust/usuba-bundle" }
-utoipa = { version = "4" }
-utoipa-swagger-ui = { version = "7" }
-wasmtime = { version = "21" }
-wasm-bindgen = { version = "0.2" }
-wasmtime-environ = { version = "21" }
-web-sys = { version = "0.3" }
-wit-bindgen = { version = "0.25" }
-wit-parser = { version = "0.208" }
-
-[profile.release]
-opt-level = 'z'
-lto = true
diff --git a/README.md b/README.md
index 908012541..82f79edc0 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,125 @@
-# Common Labs
+# Common Tools Platform
-Radioactive experiments. Turn back! You will find no API stability here.
+**Common Labs** - Radioactive experiments. Turn back! You will find no API
+stability here.

-## Overview
+## What is Common Tools?
-Web, Node.js an Deno packages live in [./typescript](./typescript)
+Common Tools is a nascent distributed computing platform that provides both a
+runtime and storage layer. The design allows instrumentation of all information
+flow in the system, enabling safe & private collaboration at scale.
-Rust crates live in [./rust](./rust)
+### Core Concepts
-Refer to [./docs/tools.md](./docs/tools.md) for tool installation.
+**Recipes** are reactive programs that can be linked together to create data and
+program networks. They're written in TypeScript/JSX and run in a secure sandbox
+environment. Recipes can:
-Run [./scripts/component-demo.sh](./scripts/component-demo.sh) to see some fun Rust / Deno / Web tooling interop in action.
\ No newline at end of file
+- Process and transform data
+- Render interactive UIs using `ct-` prefixed components
+- React to changes from linked recipes
+- Connect to external APIs
+
+**Charms** are deployed instances of recipes running in CommonTools spaces.
+Charms can be linked together to create complex workflows where data flows
+automatically between connected components.
+
+**Spaces** are collaborative environments where charms live and interact. Users
+can run their own spaces or use hosted versions.
+
+## Quick Start (Development)
+
+Check out the repo, install `deno` and `claude` and then run `/onboarding`
+within Claude Code.
+
+## Architecture
+
+This is a multi-package monorepo with several key components:
+
+**Backend ([Toolshed](./packages/toolshed))**: The hosted platform backend,
+written in Deno2, that provides the distributed runtime and storage.
+
+**Frontend ([Shell](./packages/shell))**: A web client interface written with
+Lit Web Components for interacting with CommonTools spaces.
+
+**CLI (CT Binary)**: Command-line interface for managing charms, linking
+recipes, and deploying to spaces. See [CT Usage Guide](./docs/common/CT.md).
+
+**UI Components ([packages/ui](./packages/ui))**: Custom VDOM layer and `ct-`
+prefixed components for recipe UIs.
+
+**Examples & Patterns ([packages/patterns](./packages/patterns))**: Example
+recipes and common patterns for building with CommonTools.
+
+**Recipe Development**: Recipes can be developed using LLM assistance with
+commands like `/imagine-recipe`, `/recipe-dev`, and `/explore-recipe`. See
+[Recipe Documentation](./docs/common/) for patterns, components, and handlers.
+
+## Development & Integrations
+
+### Claude Code Commands
+
+This repository includes many Claude Code commands in
+[`.claude/commands/`](./.claude/commands/) for common workflows:
+
+- `/recipe-dev` - Work on existing recipes with LLM assistance
+- `/imagine-recipe` - Create new recipes from ideas
+- `/explore-recipe` - Test recipes interactively with Playwright
+- `/linear` - Task management integration
+- `/deps` - Dependency and integration setup
+- And many more - see the commands directory
+
+### Dependencies & Integrations
+
+**Required**:
+
+- [Deno 2](https://docs.deno.com/runtime/getting_started/installation/) -
+ Runtime for backend and tooling
+
+**Recommended Integrations**:
+
+- [GitHub CLI](https://github.com/cli/cli) - For PR and issue workflows
+- [Claude Code MCP integrations](./deps.md):
+ - Linear Server MCP for task management
+ - Playwright MCP for browser-based recipe testing
+
+### Development Practices
+
+- **CI/CD**: All changes must pass automated checks before merging
+- **Testing**: Tests are critical - run with `deno task test`
+- **Linting**: Use `deno task check` for type checking
+- **Formatting**: Always run `deno fmt` before committing
+- See [CLAUDE.md](./CLAUDE.md) for detailed coding guidelines
+
+## Running the backend
+
+For a more detailed guide, see
+[./packages/toolshed/README.md](./packages/toolshed/README.md).
+
+```bash
+cd ./packages/toolshed
+deno task dev
+```
+
+By default the backend will run at
+
+## Running the frontend
+
+Run the dev server
+
+```bash
+cd ./packages/shell
+deno task dev
+```
+
+By default, the frontend will run at , and it will point
+to a local backend running at .
+
+If you are not actively making updates to the backend, you can also point to the
+backend running in the cloud, by running the following command:
+
+```shell
+API_URL=https://toolshed.saga-castor.ts.net/ deno task dev
+```
diff --git a/cloudbuild.yaml b/cloudbuild.yaml
deleted file mode 100644
index 6f96b5595..000000000
--- a/cloudbuild.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-steps:
-- name: 'gcr.io/cloud-builders/docker'
- args: ['build', '-t', 'gcr.io/$PROJECT_ID/usuba', '-f', './rust/usuba/Dockerfile', '.']
-- name: 'gcr.io/cloud-builders/docker'
- args: ['push', 'gcr.io/$PROJECT_ID/usuba']
-- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
- entrypoint: gcloud
- args: ['run', 'deploy', 'labs', '--image', 'gcr.io/$PROJECT_ID/usuba', '--region', 'us-central1']
-images:
-- gcr.io/$PROJECT_ID/usuba
-options:
- logging: CLOUD_LOGGING_ONLY
\ No newline at end of file
diff --git a/compose.yaml b/compose.yaml
deleted file mode 100644
index 00ceb5b9b..000000000
--- a/compose.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-services:
- usuba:
- build:
- context: .
- dockerfile: ./rust/usuba/Dockerfile
- tags:
- - 'usuba:local'
- network_mode: 'host'
- environment:
- UPSTREAM: 'localhost:5173'
diff --git a/deno.json b/deno.json
new file mode 100644
index 000000000..043dce5c2
--- /dev/null
+++ b/deno.json
@@ -0,0 +1,121 @@
+{
+ "workspace": [
+ "./packages/api",
+ "./packages/background-charm-service",
+ "./packages/charm",
+ "./packages/cli",
+ "./packages/deno-web-test",
+ "./packages/felt",
+ "./packages/html",
+ "./packages/identity",
+ "./packages/iframe-sandbox",
+ "./packages/integration",
+ "./packages/js-runtime",
+ "./packages/js-sandbox",
+ "./packages/schema-generator",
+ "./packages/llm",
+ "./packages/memory",
+ "./packages/patterns",
+ "./packages/runner",
+ "./packages/seeder",
+ "./packages/shell",
+ "./packages/static",
+ "./packages/toolshed",
+ "./packages/ui",
+ "./packages/utils",
+ "./packages/vendor-astral"
+ ],
+ "tasks": {
+ "check": "./tasks/check.sh",
+ "ct": "ROOT=$(pwd) && cd $INIT_CWD && deno run --allow-net --allow-ffi --allow-read --allow-write --allow-env \"$ROOT/packages/cli/mod.ts\"",
+ "test": "./tasks/test.ts",
+ "test-all": "echo \"Use 'deno task test' instead.\" && exit 1",
+ "build-binaries": "./tasks/build-binaries.ts",
+ "initialize-db": "./tasks/initialize-db.sh"
+ },
+ "compilerOptions": {
+ "types": [
+ "./packages/static/assets/types/jsx.d.ts"
+ ],
+ "jsx": "react-jsxdev",
+ "lib": [
+ "deno.ns",
+ "dom",
+ "dom.iterable",
+ "dom.asynciterable",
+ "esnext"
+ ],
+ "experimentalDecorators": true
+ },
+ "exclude": [
+ "**/node_modules/",
+ "**/.vite/",
+ "**/dist/",
+ "**/build/",
+ "**/.cache/",
+ "**/coverage/"
+ ],
+ "lint": {
+ "exclude": [
+ "./packages/static/assets",
+ "./packages/vendor-astral"
+ ],
+ "rules": {
+ "tags": [
+ "recommended"
+ ],
+ "include": [
+ "ban-untagged-todo",
+ "no-external-import"
+ ],
+ "exclude": [
+ "no-unused-vars",
+ "no-explicit-any",
+ "require-yield"
+ ]
+ }
+ },
+ "fmt": {
+ "indentWidth": 2,
+ "lineWidth": 80,
+ "semiColons": true,
+ "singleQuote": false,
+ "proseWrap": "always",
+ "exclude": [
+ ".claude/",
+ "docs/",
+ "packages/seeder/templates/",
+ "packages/static/assets/",
+ "packages/js-runtime/test/fixtures",
+ "packages/schema-generator/test/fixtures",
+ "packages/vendor-astral"
+ ]
+ },
+ "imports": {
+ "react": "npm:react@^18.3.1",
+ "react-dom": "npm:react-dom@^18.3.1",
+ "@types/react": "npm:@types/react@^18.3.1",
+ "@babel/standalone": "npm:@babel/standalone@^7.28.2",
+ "commontools": "./packages/api/index.ts",
+ "core-js/proposals/explicit-resource-management": "https://esm.sh/core-js/proposals/explicit-resource-management",
+ "@astral/astral": "./packages/vendor-astral/mod.ts",
+ "@cliffy/command": "jsr:@cliffy/command@^1.0.0-rc.8",
+ "@std/assert": "jsr:@std/assert@^1",
+ "@std/async": "jsr:@std/async@^1",
+ "@std/cli": "jsr:@std/cli@^1",
+ "@std/crypto": "jsr:@std/crypto@^1",
+ "@std/dotenv": "jsr:@std/dotenv@^0.225.3",
+ "@std/encoding": "jsr:@std/encoding@^1",
+ "@std/expect": "jsr:@std/expect@^1",
+ "@std/fs": "jsr:@std/fs@^1",
+ "@std/http": "jsr:@std/http@^1",
+ "@std/path": "jsr:@std/path@^1",
+ "@std/testing": "jsr:@std/testing@^1",
+ "jsdom": "npm:jsdom",
+ "lit": "npm:lit@^3.3.0",
+ "merkle-reference": "npm:merkle-reference@^2.2.0",
+ "multiformats": "npm:multiformats@^13.3.2",
+ "turndown": "npm:turndown@^7.1.2",
+ "zod": "npm:zod@^3.24.1"
+ }
+}
diff --git a/deno.lock b/deno.lock
new file mode 100644
index 000000000..7f9a9aa27
--- /dev/null
+++ b/deno.lock
@@ -0,0 +1,2157 @@
+{
+ "version": "5",
+ "specifiers": {
+ "jsr:@cliffy/command@^1.0.0-rc.8": "1.0.0-rc.8",
+ "jsr:@cliffy/flags@1.0.0-rc.8": "1.0.0-rc.8",
+ "jsr:@cliffy/internal@1.0.0-rc.8": "1.0.0-rc.8",
+ "jsr:@cliffy/table@1.0.0-rc.8": "1.0.0-rc.8",
+ "jsr:@cliffy/table@^1.0.0-rc.8": "1.0.0-rc.8",
+ "jsr:@cmd-johnson/oauth2-client@2": "2.0.0",
+ "jsr:@db/sqlite@0.12": "0.12.0",
+ "jsr:@deno-library/progress@^1.5.1": "1.5.1",
+ "jsr:@deno/cache-dir@0.22.2": "0.22.2",
+ "jsr:@deno/esbuild-plugin@*": "1.2.0",
+ "jsr:@deno/graph@0.86": "0.86.9",
+ "jsr:@deno/loader@~0.3.3": "0.3.5",
+ "jsr:@denosaurs/plug@1": "1.1.0",
+ "jsr:@std/assert@0.217": "0.217.0",
+ "jsr:@std/assert@1": "1.0.14",
+ "jsr:@std/assert@^1.0.13": "1.0.14",
+ "jsr:@std/assert@^1.0.14": "1.0.14",
+ "jsr:@std/async@1": "1.0.14",
+ "jsr:@std/async@^1.0.13": "1.0.14",
+ "jsr:@std/bytes@^1.0.2": "1.0.6",
+ "jsr:@std/cli@1": "1.0.21",
+ "jsr:@std/cli@^1.0.12": "1.0.21",
+ "jsr:@std/cli@^1.0.21": "1.0.21",
+ "jsr:@std/crypto@1": "1.0.5",
+ "jsr:@std/data-structures@^1.0.9": "1.0.9",
+ "jsr:@std/dotenv@~0.225.3": "0.225.5",
+ "jsr:@std/encoding@1": "1.0.10",
+ "jsr:@std/encoding@^1.0.10": "1.0.10",
+ "jsr:@std/encoding@^1.0.5": "1.0.10",
+ "jsr:@std/expect@1": "1.0.17",
+ "jsr:@std/fmt@1": "1.0.8",
+ "jsr:@std/fmt@1.0.3": "1.0.3",
+ "jsr:@std/fmt@^1.0.3": "1.0.8",
+ "jsr:@std/fmt@^1.0.8": "1.0.8",
+ "jsr:@std/fmt@~1.0.2": "1.0.8",
+ "jsr:@std/fs@1": "1.0.19",
+ "jsr:@std/fs@^1.0.19": "1.0.19",
+ "jsr:@std/fs@^1.0.6": "1.0.19",
+ "jsr:@std/html@^1.0.4": "1.0.4",
+ "jsr:@std/http@1": "1.0.20",
+ "jsr:@std/internal@^1.0.10": "1.0.10",
+ "jsr:@std/internal@^1.0.9": "1.0.10",
+ "jsr:@std/io@0.225": "0.225.0",
+ "jsr:@std/io@0.225.0": "0.225.0",
+ "jsr:@std/media-types@^1.1.0": "1.1.0",
+ "jsr:@std/net@^1.0.4": "1.0.5",
+ "jsr:@std/path@0.217": "0.217.0",
+ "jsr:@std/path@1": "1.1.2",
+ "jsr:@std/path@^1.0.8": "1.1.2",
+ "jsr:@std/path@^1.1.1": "1.1.2",
+ "jsr:@std/streams@^1.0.10": "1.0.11",
+ "jsr:@std/testing@1": "1.0.15",
+ "jsr:@std/text@~1.0.7": "1.0.16",
+ "jsr:@zip-js/zip-js@^2.7.52": "2.7.72",
+ "npm:@ai-sdk/anthropic@^1.1.6": "1.2.12_zod@3.25.76",
+ "npm:@ai-sdk/anthropic@^2.0.9": "2.0.9_zod@3.25.76",
+ "npm:@ai-sdk/google-vertex@^3.0.16": "3.0.16_zod@3.25.76",
+ "npm:@ai-sdk/groq@^2.0.16": "2.0.16_zod@3.25.76",
+ "npm:@ai-sdk/openai@^1.1.9": "1.3.24_zod@3.25.76",
+ "npm:@ai-sdk/openai@^2.0.22": "2.0.22_zod@3.25.76",
+ "npm:@ai-sdk/perplexity@*": "2.0.8_zod@3.25.76",
+ "npm:@ai-sdk/perplexity@^2.0.8": "2.0.8_zod@3.25.76",
+ "npm:@ai-sdk/xai@^2.0.13": "2.0.13_zod@3.25.76",
+ "npm:@arizeai/openinference-semantic-conventions@^1.1.0": "1.1.0",
+ "npm:@arizeai/openinference-vercel@^2.0.1": "2.3.1_@opentelemetry+api@1.9.0",
+ "npm:@babel/standalone@^7.28.2": "7.28.2",
+ "npm:@codemirror/autocomplete@^6.15.0": "6.18.7",
+ "npm:@codemirror/lang-css@^6.3.1": "6.3.1",
+ "npm:@codemirror/lang-html@^6.4.9": "6.4.9",
+ "npm:@codemirror/lang-javascript@^6.2.2": "6.2.4",
+ "npm:@codemirror/lang-json@^6.0.1": "6.0.2",
+ "npm:@codemirror/lang-markdown@^6.3.0": "6.3.4",
+ "npm:@codemirror/language@^6.10.8": "6.11.3",
+ "npm:@codemirror/state@^6.5.1": "6.5.2",
+ "npm:@codemirror/theme-one-dark@^6.1.2": "6.1.3",
+ "npm:@codemirror/view@^6.26.0": "6.38.2",
+ "npm:@fal-ai/client@^1.2.2": "1.6.1",
+ "npm:@hono/sentry@^1.2.0": "1.2.2_hono@4.8.10",
+ "npm:@hono/zod-openapi@~0.18.3": "0.18.4_hono@4.8.10_zod@3.25.76",
+ "npm:@hono/zod-validator@~0.4.2": "0.4.3_hono@4.8.10_zod@3.25.76",
+ "npm:@jitl/quickjs-singlefile-mjs-debug-sync@*": "0.31.0",
+ "npm:@lit/context@^1.1.2": "1.1.6",
+ "npm:@lit/context@^1.1.5": "1.1.6",
+ "npm:@lit/task@^1.0.2": "1.0.3",
+ "npm:@noble/ed25519@^2.2.3": "2.3.0",
+ "npm:@opentelemetry/api@^1.7.0": "1.9.0",
+ "npm:@opentelemetry/api@^1.9.0": "1.9.0",
+ "npm:@opentelemetry/context-async-hooks@^1.19.0": "1.30.1_@opentelemetry+api@1.9.0",
+ "npm:@opentelemetry/core@^1.19.0": "1.30.1_@opentelemetry+api@1.9.0",
+ "npm:@opentelemetry/exporter-trace-otlp-proto@0.46": "0.46.0_@opentelemetry+api@1.9.0",
+ "npm:@opentelemetry/resources@^1.19.0": "1.30.1_@opentelemetry+api@1.9.0",
+ "npm:@opentelemetry/sdk-trace-base@^1.19.0": "1.30.1_@opentelemetry+api@1.9.0",
+ "npm:@opentelemetry/semantic-conventions@^1.19.0": "1.36.0",
+ "npm:@scalar/hono-api-reference@~0.5.165": "0.5.184_hono@4.8.10",
+ "npm:@scure/bip39@^1.5.4": "1.6.0",
+ "npm:@sentry/deno@^9.3.0": "9.43.0",
+ "npm:@types/node@*": "22.15.15",
+ "npm:@types/react@^18.3.1": "18.3.24",
+ "npm:ai@^5.0.27": "5.0.27_zod@3.25.76",
+ "npm:ajv@^8.17.1": "8.17.1",
+ "npm:codemirror@^6.0.1": "6.0.2",
+ "npm:esbuild@~0.25.5": "0.25.9",
+ "npm:gcp-metadata@6.1.0": "6.1.0",
+ "npm:hono-pino@0.7": "0.7.2_hono@4.8.10_pino@9.7.0",
+ "npm:hono@^4.7.0": "4.8.10",
+ "npm:jsdom@*": "26.1.0",
+ "npm:json5@^2.2.3": "2.2.3",
+ "npm:jsonschema@^1.5.0": "1.5.0",
+ "npm:lit@^3.3.0": "3.3.1",
+ "npm:marked@4": "4.3.0",
+ "npm:merkle-reference@^2.2.0": "2.2.0",
+ "npm:mistreevous@4.2.0": "4.2.0",
+ "npm:multiformats@^13.3.2": "13.3.7",
+ "npm:pino-pretty@13": "13.0.0",
+ "npm:pino@^9.6.0": "9.7.0",
+ "npm:plaid@36": "36.0.0",
+ "npm:quickjs-emscripten-core@*": "0.31.0",
+ "npm:react-dom@^18.3.1": "18.3.1_react@18.3.1",
+ "npm:react@^18.3.1": "18.3.1",
+ "npm:source-map-js@^1.2.1": "1.2.1",
+ "npm:stoker@^1.4.2": "1.4.3_@hono+zod-openapi@0.18.4__hono@4.8.10__zod@3.25.76_hono@4.8.10_zod@3.25.76",
+ "npm:turndown@^7.1.2": "7.2.0",
+ "npm:typescript@*": "5.8.3",
+ "npm:zod@^3.24.1": "3.25.76"
+ },
+ "jsr": {
+ "@cliffy/command@1.0.0-rc.8": {
+ "integrity": "758147790797c74a707e5294cc7285df665422a13d2a483437092ffce40b5557",
+ "dependencies": [
+ "jsr:@cliffy/flags",
+ "jsr:@cliffy/internal",
+ "jsr:@cliffy/table@1.0.0-rc.8",
+ "jsr:@std/fmt@~1.0.2",
+ "jsr:@std/text"
+ ]
+ },
+ "@cliffy/flags@1.0.0-rc.8": {
+ "integrity": "0f1043ce6ef037ba1cb5fe6b1bcecb25dc2f29371a1c17f278ab0f45e4b6f46c",
+ "dependencies": [
+ "jsr:@std/text"
+ ]
+ },
+ "@cliffy/internal@1.0.0-rc.8": {
+ "integrity": "34cdf2fad9b084b5aed493b138d573f52d4e988767215f7460daf0b918ff43d8"
+ },
+ "@cliffy/table@1.0.0-rc.8": {
+ "integrity": "8bbcdc2ba5e0061b4b13810a24e6f5c6ab19c09f0cce9eb691ccd76c7c6c9db5",
+ "dependencies": [
+ "jsr:@std/fmt@~1.0.2"
+ ]
+ },
+ "@cmd-johnson/oauth2-client@2.0.0": {
+ "integrity": "e2f2fa6e657db2a39a55f7d19a9e331c317f3ddbb6068aa44947aa247a99125e",
+ "dependencies": [
+ "jsr:@std/encoding@^1.0.5"
+ ]
+ },
+ "@db/sqlite@0.12.0": {
+ "integrity": "dd1ef7f621ad50fc1e073a1c3609c4470bd51edc0994139c5bf9851de7a6d85f",
+ "dependencies": [
+ "jsr:@denosaurs/plug",
+ "jsr:@std/path@0.217"
+ ]
+ },
+ "@deno-library/progress@1.5.1": {
+ "integrity": "966611826b8bb27baae73ab1c4fa4317cd4edd2abb99750cd6f8488d22d5b121",
+ "dependencies": [
+ "jsr:@std/fmt@1.0.3",
+ "jsr:@std/io@0.225.0"
+ ]
+ },
+ "@deno/cache-dir@0.22.2": {
+ "integrity": "0c84b8db6175618cc2e25ed7d7648d83b38e298c14c1aae1e4b4e1b2219b840c",
+ "dependencies": [
+ "jsr:@deno/graph",
+ "jsr:@std/fmt@^1.0.3",
+ "jsr:@std/fs@^1.0.6",
+ "jsr:@std/io@0.225",
+ "jsr:@std/path@^1.0.8"
+ ]
+ },
+ "@deno/esbuild-plugin@1.2.0": {
+ "integrity": "04ddd0fca9416d8a2866263928a53b9d5ed08dfca064d64504a0aaf9800c709e",
+ "dependencies": [
+ "jsr:@deno/loader",
+ "jsr:@std/path@^1.1.1",
+ "npm:esbuild"
+ ]
+ },
+ "@deno/graph@0.86.9": {
+ "integrity": "c4f353a695bcc5246c099602977dabc6534eacea9999a35a8cb24e807192e6a1"
+ },
+ "@deno/loader@0.3.5": {
+ "integrity": "72f6ce9c6e7242c6e070705dbd8a838884dd236d5dd0bd907d08bece92db5722"
+ },
+ "@denosaurs/plug@1.1.0": {
+ "integrity": "eb2f0b7546c7bca2000d8b0282c54d50d91cf6d75cb26a80df25a6de8c4bc044",
+ "dependencies": [
+ "jsr:@std/encoding@1",
+ "jsr:@std/fmt@1",
+ "jsr:@std/fs@1",
+ "jsr:@std/path@1"
+ ]
+ },
+ "@std/assert@0.217.0": {
+ "integrity": "c98e279362ca6982d5285c3b89517b757c1e3477ee9f14eb2fdf80a45aaa9642"
+ },
+ "@std/assert@1.0.14": {
+ "integrity": "68d0d4a43b365abc927f45a9b85c639ea18a9fab96ad92281e493e4ed84abaa4",
+ "dependencies": [
+ "jsr:@std/internal@^1.0.10"
+ ]
+ },
+ "@std/async@1.0.14": {
+ "integrity": "62e954a418652c704d37563a3e54a37d4cf0268a9dcaeac1660cc652880b5326"
+ },
+ "@std/bytes@1.0.6": {
+ "integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a"
+ },
+ "@std/cli@1.0.21": {
+ "integrity": "cd25b050bdf6282e321854e3822bee624f07aca7636a3a76d95f77a3a919ca2a"
+ },
+ "@std/crypto@1.0.5": {
+ "integrity": "0dcfbb319fe0bba1bd3af904ceb4f948cde1b92979ec1614528380ed308a3b40"
+ },
+ "@std/data-structures@1.0.9": {
+ "integrity": "033d6e17e64bf1f84a614e647c1b015fa2576ae3312305821e1a4cb20674bb4d"
+ },
+ "@std/dotenv@0.225.5": {
+ "integrity": "9ce6f9d0ec3311f74a32535aa1b8c62ed88b1ab91b7f0815797d77a6f60c922f"
+ },
+ "@std/encoding@1.0.10": {
+ "integrity": "8783c6384a2d13abd5e9e87a7ae0520a30e9f56aeeaa3bdf910a3eaaf5c811a1"
+ },
+ "@std/expect@1.0.17": {
+ "integrity": "316b47dd65c33e3151344eb3267bf42efba17d1415425f07ed96185d67fc04d9",
+ "dependencies": [
+ "jsr:@std/assert@^1.0.14",
+ "jsr:@std/internal@^1.0.10"
+ ]
+ },
+ "@std/fmt@1.0.3": {
+ "integrity": "97765c16aa32245ff4e2204ecf7d8562496a3cb8592340a80e7e554e0bb9149f"
+ },
+ "@std/fmt@1.0.8": {
+ "integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7"
+ },
+ "@std/fs@1.0.19": {
+ "integrity": "051968c2b1eae4d2ea9f79a08a3845740ef6af10356aff43d3e2ef11ed09fb06",
+ "dependencies": [
+ "jsr:@std/internal@^1.0.9",
+ "jsr:@std/path@^1.1.1"
+ ]
+ },
+ "@std/html@1.0.4": {
+ "integrity": "eff3497c08164e6ada49b7f81a28b5108087033823153d065e3f89467dd3d50e"
+ },
+ "@std/http@1.0.20": {
+ "integrity": "b5cc33fc001bccce65ed4c51815668c9891c69ccd908295997e983d8f56070a1",
+ "dependencies": [
+ "jsr:@std/cli@^1.0.21",
+ "jsr:@std/encoding@^1.0.10",
+ "jsr:@std/fmt@^1.0.8",
+ "jsr:@std/fs@^1.0.19",
+ "jsr:@std/html",
+ "jsr:@std/media-types",
+ "jsr:@std/net",
+ "jsr:@std/path@^1.1.1",
+ "jsr:@std/streams"
+ ]
+ },
+ "@std/internal@1.0.10": {
+ "integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7"
+ },
+ "@std/io@0.225.0": {
+ "integrity": "c1db7c5e5a231629b32d64b9a53139445b2ca640d828c26bf23e1c55f8c079b3",
+ "dependencies": [
+ "jsr:@std/bytes"
+ ]
+ },
+ "@std/media-types@1.1.0": {
+ "integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4"
+ },
+ "@std/net@1.0.5": {
+ "integrity": "b759d8c5e17d997e164af6379d57764668c6714f30109685eec0fd5e194d501a"
+ },
+ "@std/path@0.217.0": {
+ "integrity": "1217cc25534bca9a2f672d7fe7c6f356e4027df400c0e85c0ef3e4343bc67d11",
+ "dependencies": [
+ "jsr:@std/assert@0.217"
+ ]
+ },
+ "@std/path@1.1.2": {
+ "integrity": "c0b13b97dfe06546d5e16bf3966b1cadf92e1cc83e56ba5476ad8b498d9e3038",
+ "dependencies": [
+ "jsr:@std/internal@^1.0.10"
+ ]
+ },
+ "@std/streams@1.0.11": {
+ "integrity": "db583d27e28d133f389f1eec318cffdf4998305e5134c1d4b1c56b361cee6018"
+ },
+ "@std/testing@1.0.15": {
+ "integrity": "a490169f5ccb0f3ae9c94fbc69d2cd43603f2cffb41713a85f99bbb0e3087cbc",
+ "dependencies": [
+ "jsr:@std/assert@^1.0.13",
+ "jsr:@std/async@^1.0.13",
+ "jsr:@std/data-structures",
+ "jsr:@std/fs@^1.0.19",
+ "jsr:@std/internal@^1.0.10",
+ "jsr:@std/path@^1.1.1"
+ ]
+ },
+ "@std/text@1.0.16": {
+ "integrity": "ddb9853b75119a2473857d691cf1ec02ad90793a2e8b4a4ac49d7354281a0cf8"
+ },
+ "@zip-js/zip-js@2.7.72": {
+ "integrity": "b72877f90aaefa1f1bd265d51f354bb58b6dd0d0e2799c865584acf49eae9115"
+ }
+ },
+ "npm": {
+ "@ai-sdk/anthropic@1.2.12_zod@3.25.76": {
+ "integrity": "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ==",
+ "dependencies": [
+ "@ai-sdk/provider@1.1.3",
+ "@ai-sdk/provider-utils@2.2.8_zod@3.25.76",
+ "zod"
+ ]
+ },
+ "@ai-sdk/anthropic@2.0.9_zod@3.25.76": {
+ "integrity": "sha512-1kQgL2A3PeqfEcHHmqy4NxRc8rbgLS71bHBuvDFfDz3VAAyndkilPMCLNDSP2mJVGAej2EMWJ1sShRAxzn70jA==",
+ "dependencies": [
+ "@ai-sdk/provider@2.0.0",
+ "@ai-sdk/provider-utils@3.0.7_zod@3.25.76",
+ "zod"
+ ]
+ },
+ "@ai-sdk/gateway@1.0.15_zod@3.25.76": {
+ "integrity": "sha512-xySXoQ29+KbGuGfmDnABx+O6vc7Gj7qugmj1kGpn0rW0rQNn6UKUuvscKMzWyv1Uv05GyC1vqHq8ZhEOLfXscQ==",
+ "dependencies": [
+ "@ai-sdk/provider@2.0.0",
+ "@ai-sdk/provider-utils@3.0.7_zod@3.25.76",
+ "zod"
+ ]
+ },
+ "@ai-sdk/google-vertex@3.0.16_zod@3.25.76": {
+ "integrity": "sha512-tStlnOCRGRqKKJSCOtXhijX4r9kYVK2v+Vs7miJnfvr3sZfO8nRS0xnNhfgu17xuNi5LMMufeCYURTz4lKxzUQ==",
+ "dependencies": [
+ "@ai-sdk/anthropic@2.0.9_zod@3.25.76",
+ "@ai-sdk/google",
+ "@ai-sdk/provider@2.0.0",
+ "@ai-sdk/provider-utils@3.0.7_zod@3.25.76",
+ "google-auth-library",
+ "zod"
+ ]
+ },
+ "@ai-sdk/google@2.0.11_zod@3.25.76": {
+ "integrity": "sha512-dnVIgSz1DZD/0gVau6ifYN3HZFN15HZwC9VjevTFfvrfSfbEvpXj5x/k/zk/0XuQrlQ5g8JiwJtxc9bx24x2xw==",
+ "dependencies": [
+ "@ai-sdk/provider@2.0.0",
+ "@ai-sdk/provider-utils@3.0.7_zod@3.25.76",
+ "zod"
+ ]
+ },
+ "@ai-sdk/groq@2.0.16_zod@3.25.76": {
+ "integrity": "sha512-oW/bty0qy56jq4bOhu8IXPDovZyAn73bQVblIwpOMyruAO9CjGMncZmcSju68ZXwT/im8+qUq/vVFLqjdHgHig==",
+ "dependencies": [
+ "@ai-sdk/provider@2.0.0",
+ "@ai-sdk/provider-utils@3.0.7_zod@3.25.76",
+ "zod"
+ ]
+ },
+ "@ai-sdk/openai-compatible@1.0.13_zod@3.25.76": {
+ "integrity": "sha512-g46fLVWKcVg1XOFzDLoJ0XuhtY5XxxBwMQ0FT/aHwCtg6WUvk3Elrd+MKmgfvhZAdIR7CpUTvgJAAipu4RW75w==",
+ "dependencies": [
+ "@ai-sdk/provider@2.0.0",
+ "@ai-sdk/provider-utils@3.0.7_zod@3.25.76",
+ "zod"
+ ]
+ },
+ "@ai-sdk/openai@1.3.24_zod@3.25.76": {
+ "integrity": "sha512-GYXnGJTHRTZc4gJMSmFRgEQudjqd4PUN0ZjQhPwOAYH1yOAvQoG/Ikqs+HyISRbLPCrhbZnPKCNHuRU4OfpW0Q==",
+ "dependencies": [
+ "@ai-sdk/provider@1.1.3",
+ "@ai-sdk/provider-utils@2.2.8_zod@3.25.76",
+ "zod"
+ ]
+ },
+ "@ai-sdk/openai@2.0.22_zod@3.25.76": {
+ "integrity": "sha512-qjSIPL5+LNM9flcBPeR64ZWeAZdYg4XWkAK34H3FaY61dSbuIaeqFPSzmQUrxotVcphAzgfL5tuYRqRYP2ZYyg==",
+ "dependencies": [
+ "@ai-sdk/provider@2.0.0",
+ "@ai-sdk/provider-utils@3.0.7_zod@3.25.76",
+ "zod"
+ ]
+ },
+ "@ai-sdk/perplexity@2.0.8_zod@3.25.76": {
+ "integrity": "sha512-e24PUYXMoXPFhglzO17FEafr2+NljKS8ivHP5Tk0R+aiElfOSqDEjSDsxyL6aBFKzX+Bnrlr33FwfVwz8Ozt2Q==",
+ "dependencies": [
+ "@ai-sdk/provider@2.0.0",
+ "@ai-sdk/provider-utils@3.0.8_zod@3.25.76",
+ "zod"
+ ]
+ },
+ "@ai-sdk/provider-utils@2.2.8_zod@3.25.76": {
+ "integrity": "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==",
+ "dependencies": [
+ "@ai-sdk/provider@1.1.3",
+ "nanoid",
+ "secure-json-parse",
+ "zod"
+ ]
+ },
+ "@ai-sdk/provider-utils@3.0.7_zod@3.25.76": {
+ "integrity": "sha512-o3BS5/t8KnBL3ubP8k3w77AByOypLm+pkIL/DCw0qKkhDbvhCy+L3hRTGPikpdb8WHcylAeKsjgwOxhj4cqTUA==",
+ "dependencies": [
+ "@ai-sdk/provider@2.0.0",
+ "@standard-schema/spec",
+ "eventsource-parser@3.0.5",
+ "zod"
+ ]
+ },
+ "@ai-sdk/provider-utils@3.0.8_zod@3.25.76": {
+ "integrity": "sha512-cDj1iigu7MW2tgAQeBzOiLhjHOUM9vENsgh4oAVitek0d//WdgfPCsKO3euP7m7LyO/j9a1vr/So+BGNdpFXYw==",
+ "dependencies": [
+ "@ai-sdk/provider@2.0.0",
+ "@standard-schema/spec",
+ "eventsource-parser@3.0.5",
+ "zod"
+ ]
+ },
+ "@ai-sdk/provider@1.1.3": {
+ "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==",
+ "dependencies": [
+ "json-schema"
+ ]
+ },
+ "@ai-sdk/provider@2.0.0": {
+ "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==",
+ "dependencies": [
+ "json-schema"
+ ]
+ },
+ "@ai-sdk/xai@2.0.13_zod@3.25.76": {
+ "integrity": "sha512-nWOmAInQg8uGIJ08XBxKQ9vmFpgS+bulCKtNMatW5Q62sza+f/1vuVo7fBPbxGm5SWUOno6hjAKi3ipVpLcwRQ==",
+ "dependencies": [
+ "@ai-sdk/openai-compatible",
+ "@ai-sdk/provider@2.0.0",
+ "@ai-sdk/provider-utils@3.0.7_zod@3.25.76",
+ "zod"
+ ]
+ },
+ "@arizeai/openinference-core@1.0.4_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-8YiEZdUQKUztb4L5QkyqlW2a/J2yo4KcogfgyCmtxo7NoUmfKHf+YcOqy33rADtV6bN8NNyOuMMci2PgoiEMuQ==",
+ "dependencies": [
+ "@arizeai/openinference-semantic-conventions@2.1.0",
+ "@opentelemetry/api",
+ "@opentelemetry/core@1.30.1_@opentelemetry+api@1.9.0"
+ ]
+ },
+ "@arizeai/openinference-semantic-conventions@1.1.0": {
+ "integrity": "sha512-rxRYnUWjt28DlVXnWukcQAyGhPYQ3ckmKrjEdUjmUNnvvv4k8Dabbp5h6AEjNy7YzN9jL2smNRJnbLIVtkrLEg=="
+ },
+ "@arizeai/openinference-semantic-conventions@2.1.0": {
+ "integrity": "sha512-SN8vNn5F45TbA8c+cxXV+nGHeUFzV1KaZKU0R2Ke2JKauIPyHi9VB3DvbeOy4WPScMrcSkGWq3XXQvPm9CDjgA=="
+ },
+ "@arizeai/openinference-vercel@2.3.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-LkBtqUvGw2uV89uelVuHePzhCTeNJqL2fLh1v3+XRCgq1D3FSmLtZs04uJYObh894ukcQxtoALpFMtQXCmq6Xg==",
+ "dependencies": [
+ "@arizeai/openinference-core",
+ "@arizeai/openinference-semantic-conventions@2.1.0",
+ "@opentelemetry/api",
+ "@opentelemetry/core@1.30.1_@opentelemetry+api@1.9.0"
+ ]
+ },
+ "@asamuzakjp/css-color@3.2.0_@csstools+css-parser-algorithms@3.0.5__@csstools+css-tokenizer@3.0.4_@csstools+css-tokenizer@3.0.4": {
+ "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
+ "dependencies": [
+ "@csstools/css-calc",
+ "@csstools/css-color-parser",
+ "@csstools/css-parser-algorithms",
+ "@csstools/css-tokenizer",
+ "lru-cache"
+ ]
+ },
+ "@asteasolutions/zod-to-openapi@7.3.4_zod@3.25.76": {
+ "integrity": "sha512-/2rThQ5zPi9OzVwes6U7lK1+Yvug0iXu25olp7S0XsYmOqnyMfxH7gdSQjn/+DSOHRg7wnotwGJSyL+fBKdnEA==",
+ "dependencies": [
+ "openapi3-ts",
+ "zod"
+ ]
+ },
+ "@babel/standalone@7.28.2": {
+ "integrity": "sha512-1kjA8XzBRN68HoDDYKP38bucHtxYWCIX8XdYwe1drRNUOjOVNt8EMy9jiE6UwaGFfU7NOHCG+C8KgBc9CR08nA=="
+ },
+ "@codemirror/autocomplete@6.18.7": {
+ "integrity": "sha512-8EzdeIoWPJDsMBwz3zdzwXnUpCzMiCyz5/A3FIPpriaclFCGDkAzK13sMcnsu5rowqiyeQN2Vs2TsOcoDPZirQ==",
+ "dependencies": [
+ "@codemirror/language",
+ "@codemirror/state",
+ "@codemirror/view",
+ "@lezer/common"
+ ]
+ },
+ "@codemirror/commands@6.8.1": {
+ "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==",
+ "dependencies": [
+ "@codemirror/language",
+ "@codemirror/state",
+ "@codemirror/view",
+ "@lezer/common"
+ ]
+ },
+ "@codemirror/lang-css@6.3.1": {
+ "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
+ "dependencies": [
+ "@codemirror/autocomplete",
+ "@codemirror/language",
+ "@codemirror/state",
+ "@lezer/common",
+ "@lezer/css"
+ ]
+ },
+ "@codemirror/lang-html@6.4.9": {
+ "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==",
+ "dependencies": [
+ "@codemirror/autocomplete",
+ "@codemirror/lang-css",
+ "@codemirror/lang-javascript",
+ "@codemirror/language",
+ "@codemirror/state",
+ "@codemirror/view",
+ "@lezer/common",
+ "@lezer/css",
+ "@lezer/html"
+ ]
+ },
+ "@codemirror/lang-javascript@6.2.4": {
+ "integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
+ "dependencies": [
+ "@codemirror/autocomplete",
+ "@codemirror/language",
+ "@codemirror/lint",
+ "@codemirror/state",
+ "@codemirror/view",
+ "@lezer/common",
+ "@lezer/javascript"
+ ]
+ },
+ "@codemirror/lang-json@6.0.2": {
+ "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==",
+ "dependencies": [
+ "@codemirror/language",
+ "@lezer/json"
+ ]
+ },
+ "@codemirror/lang-markdown@6.3.4": {
+ "integrity": "sha512-fBm0BO03azXnTAsxhONDYHi/qWSI+uSEIpzKM7h/bkIc9fHnFp9y7KTMXKON0teNT97pFhc1a9DQTtWBYEZ7ug==",
+ "dependencies": [
+ "@codemirror/autocomplete",
+ "@codemirror/lang-html",
+ "@codemirror/language",
+ "@codemirror/state",
+ "@codemirror/view",
+ "@lezer/common",
+ "@lezer/markdown"
+ ]
+ },
+ "@codemirror/language@6.11.3": {
+ "integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==",
+ "dependencies": [
+ "@codemirror/state",
+ "@codemirror/view",
+ "@lezer/common",
+ "@lezer/highlight",
+ "@lezer/lr",
+ "style-mod"
+ ]
+ },
+ "@codemirror/lint@6.8.5": {
+ "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==",
+ "dependencies": [
+ "@codemirror/state",
+ "@codemirror/view",
+ "crelt"
+ ]
+ },
+ "@codemirror/search@6.5.11": {
+ "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==",
+ "dependencies": [
+ "@codemirror/state",
+ "@codemirror/view",
+ "crelt"
+ ]
+ },
+ "@codemirror/state@6.5.2": {
+ "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
+ "dependencies": [
+ "@marijn/find-cluster-break"
+ ]
+ },
+ "@codemirror/theme-one-dark@6.1.3": {
+ "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
+ "dependencies": [
+ "@codemirror/language",
+ "@codemirror/state",
+ "@codemirror/view",
+ "@lezer/highlight"
+ ]
+ },
+ "@codemirror/view@6.38.2": {
+ "integrity": "sha512-bTWAJxL6EOFLPzTx+O5P5xAO3gTqpatQ2b/ARQ8itfU/v2LlpS3pH2fkL0A3E/Fx8Y2St2KES7ZEV0sHTsSW/A==",
+ "dependencies": [
+ "@codemirror/state",
+ "crelt",
+ "style-mod",
+ "w3c-keyname"
+ ]
+ },
+ "@csstools/color-helpers@5.0.2": {
+ "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA=="
+ },
+ "@csstools/css-calc@2.1.4_@csstools+css-parser-algorithms@3.0.5__@csstools+css-tokenizer@3.0.4_@csstools+css-tokenizer@3.0.4": {
+ "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+ "dependencies": [
+ "@csstools/css-parser-algorithms",
+ "@csstools/css-tokenizer"
+ ]
+ },
+ "@csstools/css-color-parser@3.0.10_@csstools+css-parser-algorithms@3.0.5__@csstools+css-tokenizer@3.0.4_@csstools+css-tokenizer@3.0.4": {
+ "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==",
+ "dependencies": [
+ "@csstools/color-helpers",
+ "@csstools/css-calc",
+ "@csstools/css-parser-algorithms",
+ "@csstools/css-tokenizer"
+ ]
+ },
+ "@csstools/css-parser-algorithms@3.0.5_@csstools+css-tokenizer@3.0.4": {
+ "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+ "dependencies": [
+ "@csstools/css-tokenizer"
+ ]
+ },
+ "@csstools/css-tokenizer@3.0.4": {
+ "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw=="
+ },
+ "@esbuild/aix-ppc64@0.25.9": {
+ "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "os": ["aix"],
+ "cpu": ["ppc64"]
+ },
+ "@esbuild/android-arm64@0.25.9": {
+ "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "os": ["android"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/android-arm@0.25.9": {
+ "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "os": ["android"],
+ "cpu": ["arm"]
+ },
+ "@esbuild/android-x64@0.25.9": {
+ "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "os": ["android"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/darwin-arm64@0.25.9": {
+ "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "os": ["darwin"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/darwin-x64@0.25.9": {
+ "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "os": ["darwin"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/freebsd-arm64@0.25.9": {
+ "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "os": ["freebsd"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/freebsd-x64@0.25.9": {
+ "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "os": ["freebsd"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/linux-arm64@0.25.9": {
+ "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "os": ["linux"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/linux-arm@0.25.9": {
+ "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "os": ["linux"],
+ "cpu": ["arm"]
+ },
+ "@esbuild/linux-ia32@0.25.9": {
+ "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "os": ["linux"],
+ "cpu": ["ia32"]
+ },
+ "@esbuild/linux-loong64@0.25.9": {
+ "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "os": ["linux"],
+ "cpu": ["loong64"]
+ },
+ "@esbuild/linux-mips64el@0.25.9": {
+ "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "os": ["linux"],
+ "cpu": ["mips64el"]
+ },
+ "@esbuild/linux-ppc64@0.25.9": {
+ "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "os": ["linux"],
+ "cpu": ["ppc64"]
+ },
+ "@esbuild/linux-riscv64@0.25.9": {
+ "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "os": ["linux"],
+ "cpu": ["riscv64"]
+ },
+ "@esbuild/linux-s390x@0.25.9": {
+ "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "os": ["linux"],
+ "cpu": ["s390x"]
+ },
+ "@esbuild/linux-x64@0.25.9": {
+ "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "os": ["linux"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/netbsd-arm64@0.25.9": {
+ "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "os": ["netbsd"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/netbsd-x64@0.25.9": {
+ "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "os": ["netbsd"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/openbsd-arm64@0.25.9": {
+ "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "os": ["openbsd"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/openbsd-x64@0.25.9": {
+ "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "os": ["openbsd"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/openharmony-arm64@0.25.9": {
+ "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "os": ["openharmony"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/sunos-x64@0.25.9": {
+ "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "os": ["sunos"],
+ "cpu": ["x64"]
+ },
+ "@esbuild/win32-arm64@0.25.9": {
+ "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "os": ["win32"],
+ "cpu": ["arm64"]
+ },
+ "@esbuild/win32-ia32@0.25.9": {
+ "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "os": ["win32"],
+ "cpu": ["ia32"]
+ },
+ "@esbuild/win32-x64@0.25.9": {
+ "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "os": ["win32"],
+ "cpu": ["x64"]
+ },
+ "@fal-ai/client@1.6.1": {
+ "integrity": "sha512-LVtBVbDju1LStCu6Q7V4nQ13BHfHJ7agRO1wgKKVEBg9+SPaf2P2mfnHGXy5lLwVVt7YpQMMaZhUp9AQ9vn+hg==",
+ "dependencies": [
+ "@msgpack/msgpack",
+ "eventsource-parser@1.1.2",
+ "robot3"
+ ]
+ },
+ "@hono/sentry@1.2.2_hono@4.8.10": {
+ "integrity": "sha512-027grZBrRGDPor8mRd+QOBcSpUlF07YrTp/WFDXZhbvWZ+1LrZdERUqcdg1gBGDUTanHhd9ucblpNNN6+V1bxg==",
+ "dependencies": [
+ "hono",
+ "toucan-js"
+ ]
+ },
+ "@hono/zod-openapi@0.18.4_hono@4.8.10_zod@3.25.76": {
+ "integrity": "sha512-6NHMHU96Hh32B1yDhb94Z4Z5/POsmEu2AXpWLWcBq9arskRnOMt2752yEoXoADV8WUAc7H1IkNaQHGj1ytXbYw==",
+ "dependencies": [
+ "@asteasolutions/zod-to-openapi",
+ "@hono/zod-validator",
+ "hono",
+ "zod"
+ ]
+ },
+ "@hono/zod-validator@0.4.3_hono@4.8.10_zod@3.25.76": {
+ "integrity": "sha512-xIgMYXDyJ4Hj6ekm9T9Y27s080Nl9NXHcJkOvkXPhubOLj8hZkOL8pDnnXfvCf5xEE8Q4oMFenQUZZREUY2gqQ==",
+ "dependencies": [
+ "hono",
+ "zod"
+ ]
+ },
+ "@jitl/quickjs-ffi-types@0.31.0": {
+ "integrity": "sha512-1yrgvXlmXH2oNj3eFTrkwacGJbmM0crwipA3ohCrjv52gBeDaD7PsTvFYinlAnqU8iPME3LGP437yk05a2oejw=="
+ },
+ "@jitl/quickjs-singlefile-mjs-debug-sync@0.31.0": {
+ "integrity": "sha512-z7umztXCfzbPPAN0loy1GJKgns2Jp4P4isGoEJaKR/+Hq6IsksXvsbZ8cUG8uFMzr0IxZ6MQBTGdxRVVJrLrsA==",
+ "dependencies": [
+ "@jitl/quickjs-ffi-types"
+ ]
+ },
+ "@lezer/common@1.2.3": {
+ "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="
+ },
+ "@lezer/css@1.3.0": {
+ "integrity": "sha512-pBL7hup88KbI7hXnZV3PQsn43DHy6TWyzuyk2AO9UyoXcDltvIdqWKE1dLL/45JVZ+YZkHe1WVHqO6wugZZWcw==",
+ "dependencies": [
+ "@lezer/common",
+ "@lezer/highlight",
+ "@lezer/lr"
+ ]
+ },
+ "@lezer/highlight@1.2.1": {
+ "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
+ "dependencies": [
+ "@lezer/common"
+ ]
+ },
+ "@lezer/html@1.3.10": {
+ "integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==",
+ "dependencies": [
+ "@lezer/common",
+ "@lezer/highlight",
+ "@lezer/lr"
+ ]
+ },
+ "@lezer/javascript@1.5.3": {
+ "integrity": "sha512-jexmlKq5NpGiB7t+0QkyhSXRgaiab5YisHIQW9C7EcU19KSUsDguZe9WY+rmRDg34nXoNH2LQ4SxpC+aJUchSQ==",
+ "dependencies": [
+ "@lezer/common",
+ "@lezer/highlight",
+ "@lezer/lr"
+ ]
+ },
+ "@lezer/json@1.0.3": {
+ "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==",
+ "dependencies": [
+ "@lezer/common",
+ "@lezer/highlight",
+ "@lezer/lr"
+ ]
+ },
+ "@lezer/lr@1.4.2": {
+ "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
+ "dependencies": [
+ "@lezer/common"
+ ]
+ },
+ "@lezer/markdown@1.4.3": {
+ "integrity": "sha512-kfw+2uMrQ/wy/+ONfrH83OkdFNM0ye5Xq96cLlaCy7h5UT9FO54DU4oRoIc0CSBh5NWmWuiIJA7NGLMJbQ+Oxg==",
+ "dependencies": [
+ "@lezer/common",
+ "@lezer/highlight"
+ ]
+ },
+ "@lit-labs/ssr-dom-shim@1.4.0": {
+ "integrity": "sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw=="
+ },
+ "@lit/context@1.1.6": {
+ "integrity": "sha512-M26qDE6UkQbZA2mQ3RjJ3Gzd8TxP+/0obMgE5HfkfLhEEyYE3Bui4A5XHiGPjy0MUGAyxB3QgVuw2ciS0kHn6A==",
+ "dependencies": [
+ "@lit/reactive-element"
+ ]
+ },
+ "@lit/reactive-element@2.1.1": {
+ "integrity": "sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==",
+ "dependencies": [
+ "@lit-labs/ssr-dom-shim"
+ ]
+ },
+ "@lit/task@1.0.3": {
+ "integrity": "sha512-1gJGJl8WON+2j0y9xfcD+XsS1rvcy3XDgsIhcdUW++yTR8ESjZW6o7dn8M8a4SZM8NnJe6ynS2cKWwsbfLOurg==",
+ "dependencies": [
+ "@lit/reactive-element"
+ ]
+ },
+ "@marijn/find-cluster-break@1.0.2": {
+ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="
+ },
+ "@mixmark-io/domino@2.2.0": {
+ "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="
+ },
+ "@msgpack/msgpack@3.1.2": {
+ "integrity": "sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ=="
+ },
+ "@noble/ed25519@2.3.0": {
+ "integrity": "sha512-M7dvXL2B92/M7dw9+gzuydL8qn/jiqNHaoR3Q+cb1q1GHV7uwE17WCyFMG+Y+TZb5izcaXk5TdJRrDUxHXL78A=="
+ },
+ "@noble/hashes@1.3.3": {
+ "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA=="
+ },
+ "@noble/hashes@1.8.0": {
+ "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="
+ },
+ "@opentelemetry/api-logs@0.46.0": {
+ "integrity": "sha512-+9BcqfiEDGPXEIo+o3tso/aqGM5dGbGwAkGVp3FPpZ8GlkK1YlaKRd9gMVyPaeRATwvO5wYGGnCsAc/sMMM9Qw==",
+ "dependencies": [
+ "@opentelemetry/api"
+ ]
+ },
+ "@opentelemetry/api@1.9.0": {
+ "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="
+ },
+ "@opentelemetry/context-async-hooks@1.30.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==",
+ "dependencies": [
+ "@opentelemetry/api"
+ ]
+ },
+ "@opentelemetry/core@1.19.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-w42AukJh3TP8R0IZZOVJVM/kMWu8g+lm4LzT70WtuKqhwq7KVhcDzZZuZinWZa6TtQCl7Smt2wolEYzpHabOgw==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/semantic-conventions@1.19.0"
+ ]
+ },
+ "@opentelemetry/core@1.30.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/semantic-conventions@1.28.0"
+ ]
+ },
+ "@opentelemetry/exporter-trace-otlp-proto@0.46.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-A7PftDM57w1TLiirrhi8ceAnCpYkpUBObELdn239IyYF67zwngImGfBLf5Yo3TTAOA2Oj1TL76L8zWVL8W+Suw==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/core@1.19.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/otlp-exporter-base",
+ "@opentelemetry/otlp-proto-exporter-base",
+ "@opentelemetry/otlp-transformer",
+ "@opentelemetry/resources@1.19.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/sdk-trace-base@1.19.0_@opentelemetry+api@1.9.0"
+ ]
+ },
+ "@opentelemetry/otlp-exporter-base@0.46.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-hfkh7cG17l77ZSLRAogz19SIJzr0KeC7xv5PDyTFbHFpwwoxV/bEViO49CqUFH6ckXB63NrltASP9R7po+ahTQ==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/core@1.19.0_@opentelemetry+api@1.9.0"
+ ]
+ },
+ "@opentelemetry/otlp-proto-exporter-base@0.46.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-rEJBA8U2AxfEzrdIUcyyjOweyVFkO6V1XAxwP161JkxpvNuVDdULHAfRVnGtoZhiVA1XsJKcpIIq2MEKAqq4cg==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/core@1.19.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/otlp-exporter-base",
+ "protobufjs"
+ ]
+ },
+ "@opentelemetry/otlp-transformer@0.46.0_@opentelemetry+api@1.9.0_@opentelemetry+api-logs@0.46.0": {
+ "integrity": "sha512-Fj9hZwr6xuqgsaERn667Uf6kuDG884puWhyrai2Jen2Fq+bGf4/5BzEJp/8xvty0VSU4EfXOto/ys3KpSz2UHg==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/api-logs",
+ "@opentelemetry/core@1.19.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/resources@1.19.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/sdk-logs",
+ "@opentelemetry/sdk-metrics",
+ "@opentelemetry/sdk-trace-base@1.19.0_@opentelemetry+api@1.9.0"
+ ]
+ },
+ "@opentelemetry/resources@1.19.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-RgxvKuuMOf7nctOeOvpDjt2BpZvZGr9Y0vf7eGtY5XYZPkh2p7e2qub1S2IArdBMf9kEbz0SfycqCviOu9isqg==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/core@1.19.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/semantic-conventions@1.19.0"
+ ]
+ },
+ "@opentelemetry/resources@1.30.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/core@1.30.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/semantic-conventions@1.28.0"
+ ]
+ },
+ "@opentelemetry/sdk-logs@0.46.0_@opentelemetry+api@1.9.0_@opentelemetry+api-logs@0.46.0": {
+ "integrity": "sha512-Knlyk4+G72uEzNh6GRN1Fhmrj+/rkATI5/lOrevN7zRDLgp4kfyZBGGoWk7w+qQjlYvwhIIdPVxlIcipivdZIg==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/api-logs",
+ "@opentelemetry/core@1.19.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/resources@1.19.0_@opentelemetry+api@1.9.0"
+ ]
+ },
+ "@opentelemetry/sdk-metrics@1.19.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-FiMii40zr0Fmys4F1i8gmuCvbinBnBsDeGBr4FQemOf0iPCLytYQm5AZJ/nn4xSc71IgKBQwTFQRAGJI7JvZ4Q==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/core@1.19.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/resources@1.19.0_@opentelemetry+api@1.9.0",
+ "lodash.merge"
+ ]
+ },
+ "@opentelemetry/sdk-trace-base@1.19.0_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-+IRvUm+huJn2KqfFW3yW/cjvRwJ8Q7FzYHoUNx5Fr0Lws0LxjMJG1uVB8HDpLwm7mg5XXH2M5MF+0jj5cM8BpQ==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/core@1.19.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/resources@1.19.0_@opentelemetry+api@1.9.0",
+ "@opentelemetry/semantic-conventions@1.19.0"
+ ]
+ },
+ "@opentelemetry/sdk-trace-base@1.30.1_@opentelemetry+api@1.9.0": {
+ "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==",
+ "dependencies": [
+ "@opentelemetry/api",
+ "@opentelemetry/core@1.30.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/resources@1.30.1_@opentelemetry+api@1.9.0",
+ "@opentelemetry/semantic-conventions@1.28.0"
+ ]
+ },
+ "@opentelemetry/semantic-conventions@1.19.0": {
+ "integrity": "sha512-14jRpC8f5c0gPSwoZ7SbEJni1PqI+AhAE8m1bMz6v+RPM4OlP1PT2UHBJj5Qh/ALLPjhVU/aZUK3YyjTUqqQVg=="
+ },
+ "@opentelemetry/semantic-conventions@1.28.0": {
+ "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="
+ },
+ "@opentelemetry/semantic-conventions@1.36.0": {
+ "integrity": "sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ=="
+ },
+ "@protobufjs/aspromise@1.1.2": {
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+ },
+ "@protobufjs/base64@1.1.2": {
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "@protobufjs/codegen@2.0.4": {
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "@protobufjs/eventemitter@1.1.0": {
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+ },
+ "@protobufjs/fetch@1.1.0": {
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "dependencies": [
+ "@protobufjs/aspromise",
+ "@protobufjs/inquire"
+ ]
+ },
+ "@protobufjs/float@1.0.2": {
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+ },
+ "@protobufjs/inquire@1.1.0": {
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+ },
+ "@protobufjs/path@1.1.2": {
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+ },
+ "@protobufjs/pool@1.1.0": {
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+ },
+ "@protobufjs/utf8@1.1.0": {
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+ },
+ "@scalar/core@0.1.1": {
+ "integrity": "sha512-7qnZp8ykrXoKScFIZcwt638CuFFyj7G3SsgVfD5liNgb533K8/lhWqdmp1vK2u4BKKJ9GBAPKMlWZE/+yA8WTw==",
+ "dependencies": [
+ "@scalar/types"
+ ]
+ },
+ "@scalar/hono-api-reference@0.5.184_hono@4.8.10": {
+ "integrity": "sha512-vRSRwJkN1Xo5dW9KYQJlGpKZ+Nh9qH+x1sn0qf6/Lx8QLPyyEpNm1EEddKaIN6qd5wrtVjDN6adQhfAfcYGHzw==",
+ "dependencies": [
+ "@scalar/core",
+ "hono"
+ ]
+ },
+ "@scalar/openapi-types@0.1.9": {
+ "integrity": "sha512-HQQudOSQBU7ewzfnBW9LhDmBE2XOJgSfwrh5PlUB7zJup/kaRkBGNgV2wMjNz9Af/uztiU/xNrO179FysmUT+g=="
+ },
+ "@scalar/types@0.0.40": {
+ "integrity": "sha512-0J6o+yZzgZEvl3KhvLTAGiXXyrCeEPKvs9gUWQDf1Rb5NfFxF0lA10ougCQCwVJIguWNEzZfOUiSoAFzGy2EqQ==",
+ "dependencies": [
+ "@scalar/openapi-types",
+ "@unhead/schema",
+ "zod"
+ ]
+ },
+ "@scure/base@1.2.6": {
+ "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="
+ },
+ "@scure/bip39@1.6.0": {
+ "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==",
+ "dependencies": [
+ "@noble/hashes@1.8.0",
+ "@scure/base"
+ ]
+ },
+ "@sentry/core@8.9.2": {
+ "integrity": "sha512-ixm8NISFlPlEo3FjSaqmq4nnd13BRHoafwJ5MG+okCz6BKGZ1SexEggP42/QpGvDprUUHnfncG6WUMgcarr1zA==",
+ "dependencies": [
+ "@sentry/types",
+ "@sentry/utils"
+ ]
+ },
+ "@sentry/core@9.43.0": {
+ "integrity": "sha512-xuvERSUkSNBAldIlgihX3fz+JkcaAPvg0HulPtv3BH9qrKqvataeQ8TiTnqiRC7kWzF7EcxhQJ6WJRl/r3aH3w=="
+ },
+ "@sentry/deno@9.43.0": {
+ "integrity": "sha512-aSrhVK4CQSo//fHfRKPQZ0eqq5hv3tIxbB1t2CXkk24c7y9hVxo/ZAdoUgp48qut86yQiXT16xC6CAmbo83xHg==",
+ "dependencies": [
+ "@sentry/core@9.43.0"
+ ]
+ },
+ "@sentry/types@8.9.2": {
+ "integrity": "sha512-+LFOyQGl+zk5SZRGZD2MEURf7i5RHgP/mt3s85Rza+vz8M211WJ0YsjkIGUJFSY842nged5QLx4JysLaBlLymg=="
+ },
+ "@sentry/utils@8.9.2": {
+ "integrity": "sha512-A4srR9mEBFdVXwSEKjQ94msUbVkMr8JeFiEj9ouOFORw/Y/ux/WV2bWVD/ZI9wq0TcTNK8L1wBgU8UMS5lIq3A==",
+ "dependencies": [
+ "@sentry/types"
+ ]
+ },
+ "@standard-schema/spec@1.0.0": {
+ "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="
+ },
+ "@types/node@22.15.15": {
+ "integrity": "sha512-R5muMcZob3/Jjchn5LcO8jdKwSCbzqmPB6ruBxMcf9kbxtniZHP327s6C37iOfuw8mbKK3cAQa7sEl7afLrQ8A==",
+ "dependencies": [
+ "undici-types@6.21.0"
+ ]
+ },
+ "@types/node@24.1.0": {
+ "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
+ "dependencies": [
+ "undici-types@7.8.0"
+ ]
+ },
+ "@types/prop-types@15.7.15": {
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="
+ },
+ "@types/react@18.3.24": {
+ "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==",
+ "dependencies": [
+ "@types/prop-types",
+ "csstype"
+ ]
+ },
+ "@types/trusted-types@2.0.7": {
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
+ },
+ "@unhead/schema@1.11.20": {
+ "integrity": "sha512-0zWykKAaJdm+/Y7yi/Yds20PrUK7XabLe9c3IRcjnwYmSWY6z0Cr19VIs3ozCj8P+GhR+/TI2mwtGlueCEYouA==",
+ "dependencies": [
+ "hookable",
+ "zhead"
+ ]
+ },
+ "agent-base@7.1.4": {
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="
+ },
+ "ai@5.0.27_zod@3.25.76": {
+ "integrity": "sha512-V7I9Rvrap5+3ozAjOrETA5Mv9Z1LmQobyY13U88IkFRahFp0xrEwjvYTwjQa4q5lPgLxwKgbIZRLnZSbUQwnUg==",
+ "dependencies": [
+ "@ai-sdk/gateway",
+ "@ai-sdk/provider@2.0.0",
+ "@ai-sdk/provider-utils@3.0.7_zod@3.25.76",
+ "@opentelemetry/api",
+ "zod"
+ ]
+ },
+ "ajv@8.17.1": {
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dependencies": [
+ "fast-deep-equal",
+ "fast-uri",
+ "json-schema-traverse",
+ "require-from-string"
+ ]
+ },
+ "asynckit@0.4.0": {
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+ },
+ "atomic-sleep@1.0.0": {
+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="
+ },
+ "axios@1.11.0": {
+ "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
+ "dependencies": [
+ "follow-redirects",
+ "form-data",
+ "proxy-from-env"
+ ]
+ },
+ "base64-js@1.5.1": {
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+ },
+ "bignumber.js@9.3.1": {
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="
+ },
+ "buffer-equal-constant-time@1.0.1": {
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
+ "call-bind-apply-helpers@1.0.2": {
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dependencies": [
+ "es-errors",
+ "function-bind"
+ ]
+ },
+ "codemirror@6.0.2": {
+ "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
+ "dependencies": [
+ "@codemirror/autocomplete",
+ "@codemirror/commands",
+ "@codemirror/language",
+ "@codemirror/lint",
+ "@codemirror/search",
+ "@codemirror/state",
+ "@codemirror/view"
+ ]
+ },
+ "colorette@2.0.20": {
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
+ },
+ "combined-stream@1.0.8": {
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dependencies": [
+ "delayed-stream"
+ ]
+ },
+ "crelt@1.0.6": {
+ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
+ },
+ "cssstyle@4.6.0": {
+ "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
+ "dependencies": [
+ "@asamuzakjp/css-color",
+ "rrweb-cssom"
+ ]
+ },
+ "csstype@3.1.3": {
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+ },
+ "data-urls@5.0.0": {
+ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+ "dependencies": [
+ "whatwg-mimetype",
+ "whatwg-url@14.2.0"
+ ]
+ },
+ "dateformat@4.6.3": {
+ "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA=="
+ },
+ "debug@4.4.1": {
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "dependencies": [
+ "ms"
+ ]
+ },
+ "decimal.js@10.6.0": {
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="
+ },
+ "defu@6.1.4": {
+ "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="
+ },
+ "delayed-stream@1.0.0": {
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+ },
+ "dunder-proto@1.0.1": {
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dependencies": [
+ "call-bind-apply-helpers",
+ "es-errors",
+ "gopd"
+ ]
+ },
+ "ecdsa-sig-formatter@1.0.11": {
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": [
+ "safe-buffer"
+ ]
+ },
+ "end-of-stream@1.4.5": {
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "dependencies": [
+ "once"
+ ]
+ },
+ "entities@6.0.1": {
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="
+ },
+ "es-define-property@1.0.1": {
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
+ },
+ "es-errors@1.3.0": {
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
+ },
+ "es-object-atoms@1.1.1": {
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dependencies": [
+ "es-errors"
+ ]
+ },
+ "es-set-tostringtag@2.1.0": {
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dependencies": [
+ "es-errors",
+ "get-intrinsic",
+ "has-tostringtag",
+ "hasown"
+ ]
+ },
+ "esbuild@0.25.9": {
+ "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
+ "optionalDependencies": [
+ "@esbuild/aix-ppc64",
+ "@esbuild/android-arm",
+ "@esbuild/android-arm64",
+ "@esbuild/android-x64",
+ "@esbuild/darwin-arm64",
+ "@esbuild/darwin-x64",
+ "@esbuild/freebsd-arm64",
+ "@esbuild/freebsd-x64",
+ "@esbuild/linux-arm",
+ "@esbuild/linux-arm64",
+ "@esbuild/linux-ia32",
+ "@esbuild/linux-loong64",
+ "@esbuild/linux-mips64el",
+ "@esbuild/linux-ppc64",
+ "@esbuild/linux-riscv64",
+ "@esbuild/linux-s390x",
+ "@esbuild/linux-x64",
+ "@esbuild/netbsd-arm64",
+ "@esbuild/netbsd-x64",
+ "@esbuild/openbsd-arm64",
+ "@esbuild/openbsd-x64",
+ "@esbuild/openharmony-arm64",
+ "@esbuild/sunos-x64",
+ "@esbuild/win32-arm64",
+ "@esbuild/win32-ia32",
+ "@esbuild/win32-x64"
+ ],
+ "scripts": true,
+ "bin": true
+ },
+ "eventsource-parser@1.1.2": {
+ "integrity": "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA=="
+ },
+ "eventsource-parser@3.0.5": {
+ "integrity": "sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ=="
+ },
+ "extend@3.0.2": {
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+ },
+ "fast-copy@3.0.2": {
+ "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ=="
+ },
+ "fast-deep-equal@3.1.3": {
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+ },
+ "fast-redact@3.5.0": {
+ "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="
+ },
+ "fast-safe-stringify@2.1.1": {
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
+ },
+ "fast-uri@3.0.6": {
+ "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="
+ },
+ "follow-redirects@1.15.9": {
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
+ },
+ "form-data@4.0.4": {
+ "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
+ "dependencies": [
+ "asynckit",
+ "combined-stream",
+ "es-set-tostringtag",
+ "hasown",
+ "mime-types"
+ ]
+ },
+ "function-bind@1.1.2": {
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+ },
+ "gaxios@6.7.1": {
+ "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
+ "dependencies": [
+ "extend",
+ "https-proxy-agent",
+ "is-stream",
+ "node-fetch",
+ "uuid"
+ ]
+ },
+ "gcp-metadata@6.1.0": {
+ "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==",
+ "dependencies": [
+ "gaxios",
+ "json-bigint"
+ ]
+ },
+ "get-intrinsic@1.3.0": {
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dependencies": [
+ "call-bind-apply-helpers",
+ "es-define-property",
+ "es-errors",
+ "es-object-atoms",
+ "function-bind",
+ "get-proto",
+ "gopd",
+ "has-symbols",
+ "hasown",
+ "math-intrinsics"
+ ]
+ },
+ "get-proto@1.0.1": {
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dependencies": [
+ "dunder-proto",
+ "es-object-atoms"
+ ]
+ },
+ "google-auth-library@9.15.1": {
+ "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==",
+ "dependencies": [
+ "base64-js",
+ "ecdsa-sig-formatter",
+ "gaxios",
+ "gcp-metadata",
+ "gtoken",
+ "jws"
+ ]
+ },
+ "gopd@1.2.0": {
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
+ },
+ "gtoken@7.1.0": {
+ "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
+ "dependencies": [
+ "gaxios",
+ "jws"
+ ]
+ },
+ "has-symbols@1.1.0": {
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
+ },
+ "has-tostringtag@1.0.2": {
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dependencies": [
+ "has-symbols"
+ ]
+ },
+ "hasown@2.0.2": {
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dependencies": [
+ "function-bind"
+ ]
+ },
+ "help-me@5.0.0": {
+ "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="
+ },
+ "hono-pino@0.7.2_hono@4.8.10_pino@9.7.0": {
+ "integrity": "sha512-uLJOngId4Ia2eHXnCPE8xpyMVkh+AGxAkHZKgvZk8YkmuTbcVDDUMe7aHMEz+YLqCDgd/Hk9ytVmmoQ8QTUXgQ==",
+ "dependencies": [
+ "defu",
+ "hono",
+ "pino"
+ ]
+ },
+ "hono@4.8.10": {
+ "integrity": "sha512-DRMYbR3aFk6YET1FCSAFbgF2cWYTz5j0YAFYPECx9fmrbKBDAYnWU+YCgRTpOaatxMYN6e68U/2IG39zRP4W/A=="
+ },
+ "hookable@5.5.3": {
+ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
+ },
+ "html-encoding-sniffer@4.0.0": {
+ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+ "dependencies": [
+ "whatwg-encoding"
+ ]
+ },
+ "http-proxy-agent@7.0.2": {
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dependencies": [
+ "agent-base",
+ "debug"
+ ]
+ },
+ "https-proxy-agent@7.0.6": {
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "dependencies": [
+ "agent-base",
+ "debug"
+ ]
+ },
+ "iconv-lite@0.6.3": {
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dependencies": [
+ "safer-buffer"
+ ]
+ },
+ "is-potential-custom-element-name@1.0.1": {
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
+ },
+ "is-stream@2.0.1": {
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
+ },
+ "joycon@3.1.1": {
+ "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="
+ },
+ "js-tokens@4.0.0": {
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "jsdom@26.1.0": {
+ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
+ "dependencies": [
+ "cssstyle",
+ "data-urls",
+ "decimal.js",
+ "html-encoding-sniffer",
+ "http-proxy-agent",
+ "https-proxy-agent",
+ "is-potential-custom-element-name",
+ "nwsapi",
+ "parse5",
+ "rrweb-cssom",
+ "saxes",
+ "symbol-tree",
+ "tough-cookie",
+ "w3c-xmlserializer",
+ "webidl-conversions@7.0.0",
+ "whatwg-encoding",
+ "whatwg-mimetype",
+ "whatwg-url@14.2.0",
+ "ws",
+ "xml-name-validator"
+ ]
+ },
+ "json-bigint@1.0.0": {
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "dependencies": [
+ "bignumber.js"
+ ]
+ },
+ "json-schema-traverse@1.0.0": {
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+ },
+ "json-schema@0.4.0": {
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
+ },
+ "json5@2.2.3": {
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "bin": true
+ },
+ "jsonschema@1.5.0": {
+ "integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw=="
+ },
+ "jwa@2.0.1": {
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "dependencies": [
+ "buffer-equal-constant-time",
+ "ecdsa-sig-formatter",
+ "safe-buffer"
+ ]
+ },
+ "jws@4.0.0": {
+ "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
+ "dependencies": [
+ "jwa",
+ "safe-buffer"
+ ]
+ },
+ "lit-element@4.2.1": {
+ "integrity": "sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==",
+ "dependencies": [
+ "@lit-labs/ssr-dom-shim",
+ "@lit/reactive-element",
+ "lit-html"
+ ]
+ },
+ "lit-html@3.3.1": {
+ "integrity": "sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==",
+ "dependencies": [
+ "@types/trusted-types"
+ ]
+ },
+ "lit@3.3.1": {
+ "integrity": "sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==",
+ "dependencies": [
+ "@lit/reactive-element",
+ "lit-element",
+ "lit-html"
+ ]
+ },
+ "lodash.merge@4.6.2": {
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+ },
+ "long@5.3.2": {
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="
+ },
+ "loose-envify@1.4.0": {
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": [
+ "js-tokens"
+ ],
+ "bin": true
+ },
+ "lotto-draw@1.0.2": {
+ "integrity": "sha512-1ih414A35BWpApfNlWAHBKOBLSxTj45crAJ+CMWF/kVY5nx6N22DA1OVF/FWW5WM5CGJbIMRh1O+xe8ukyoQ8Q=="
+ },
+ "lru-cache@10.4.3": {
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
+ },
+ "marked@4.3.0": {
+ "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
+ "bin": true
+ },
+ "math-intrinsics@1.1.0": {
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
+ },
+ "merkle-reference@2.2.0": {
+ "integrity": "sha512-FYrTkqkAMq6nS4Zcgc+8AFMpjIZTPZ07oKBaIcc5/s0RLuZQw2upXKEwRBR7CIs2SIOCDCfgXe4amsNkmTwxRQ==",
+ "dependencies": [
+ "@noble/hashes@1.3.3",
+ "multiformats"
+ ]
+ },
+ "mime-db@1.52.0": {
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ },
+ "mime-types@2.1.35": {
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": [
+ "mime-db"
+ ]
+ },
+ "minimist@1.2.8": {
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
+ },
+ "mistreevous@4.2.0": {
+ "integrity": "sha512-ZlqX7Fp2O/wYG9QFIT/I1bRDBi6o2ko4S006/G17VT0YWgN1emVMJNAcNSxfD0u4Tg/HOfHziJe+J4L7Un7spA==",
+ "dependencies": [
+ "lotto-draw"
+ ]
+ },
+ "ms@2.1.3": {
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "multiformats@13.3.7": {
+ "integrity": "sha512-meL9DERHj+fFVWoOX9fXqfcYcSpUfSYJPcFvDPKrxitICbwAoWR+Ut4j5NO9zAT917HUHLQmqzQbAsGNHlDcxQ=="
+ },
+ "nanoid@3.3.11": {
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "bin": true
+ },
+ "node-fetch@2.7.0": {
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "dependencies": [
+ "whatwg-url@5.0.0"
+ ]
+ },
+ "nwsapi@2.2.21": {
+ "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA=="
+ },
+ "on-exit-leak-free@2.1.2": {
+ "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="
+ },
+ "once@1.4.0": {
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dependencies": [
+ "wrappy"
+ ]
+ },
+ "openapi3-ts@4.5.0": {
+ "integrity": "sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==",
+ "dependencies": [
+ "yaml"
+ ]
+ },
+ "parse5@7.3.0": {
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "dependencies": [
+ "entities"
+ ]
+ },
+ "pino-abstract-transport@2.0.0": {
+ "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
+ "dependencies": [
+ "split2"
+ ]
+ },
+ "pino-pretty@13.0.0": {
+ "integrity": "sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==",
+ "dependencies": [
+ "colorette",
+ "dateformat",
+ "fast-copy",
+ "fast-safe-stringify",
+ "help-me",
+ "joycon",
+ "minimist",
+ "on-exit-leak-free",
+ "pino-abstract-transport",
+ "pump",
+ "secure-json-parse",
+ "sonic-boom",
+ "strip-json-comments"
+ ],
+ "bin": true
+ },
+ "pino-std-serializers@7.0.0": {
+ "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA=="
+ },
+ "pino@9.7.0": {
+ "integrity": "sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==",
+ "dependencies": [
+ "atomic-sleep",
+ "fast-redact",
+ "on-exit-leak-free",
+ "pino-abstract-transport",
+ "pino-std-serializers",
+ "process-warning",
+ "quick-format-unescaped",
+ "real-require",
+ "safe-stable-stringify",
+ "sonic-boom",
+ "thread-stream"
+ ],
+ "bin": true
+ },
+ "plaid@36.0.0": {
+ "integrity": "sha512-Qw1DJkq4xhXcb7gDFiJPLyzYEqlTbJpJr4r0RcekMed9tpZ16RG5gzUfDO9w+ZHr6AVwDaXVa2IZlECp41yQWw==",
+ "dependencies": [
+ "axios"
+ ]
+ },
+ "process-warning@5.0.0": {
+ "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="
+ },
+ "protobufjs@7.5.3": {
+ "integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==",
+ "dependencies": [
+ "@protobufjs/aspromise",
+ "@protobufjs/base64",
+ "@protobufjs/codegen",
+ "@protobufjs/eventemitter",
+ "@protobufjs/fetch",
+ "@protobufjs/float",
+ "@protobufjs/inquire",
+ "@protobufjs/path",
+ "@protobufjs/pool",
+ "@protobufjs/utf8",
+ "@types/node@24.1.0",
+ "long"
+ ],
+ "scripts": true
+ },
+ "proxy-from-env@1.1.0": {
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
+ "pump@3.0.3": {
+ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+ "dependencies": [
+ "end-of-stream",
+ "once"
+ ]
+ },
+ "punycode@2.3.1": {
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
+ },
+ "quick-format-unescaped@4.0.4": {
+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="
+ },
+ "quickjs-emscripten-core@0.31.0": {
+ "integrity": "sha512-oQz8p0SiKDBc1TC7ZBK2fr0GoSHZKA0jZIeXxsnCyCs4y32FStzCW4d1h6E1sE0uHDMbGITbk2zhNaytaoJwXQ==",
+ "dependencies": [
+ "@jitl/quickjs-ffi-types"
+ ]
+ },
+ "react-dom@18.3.1_react@18.3.1": {
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "dependencies": [
+ "loose-envify",
+ "react",
+ "scheduler"
+ ]
+ },
+ "react@18.3.1": {
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "dependencies": [
+ "loose-envify"
+ ]
+ },
+ "real-require@0.2.0": {
+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="
+ },
+ "require-from-string@2.0.2": {
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
+ },
+ "robot3@0.4.1": {
+ "integrity": "sha512-hzjy826lrxzx8eRgv80idkf8ua1JAepRc9Efdtj03N3KNJuznQCPlyCJ7gnUmDFwZCLQjxy567mQVKmdv2BsXQ=="
+ },
+ "rrweb-cssom@0.8.0": {
+ "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="
+ },
+ "safe-buffer@5.2.1": {
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+ },
+ "safe-stable-stringify@2.5.0": {
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="
+ },
+ "safer-buffer@2.1.2": {
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "saxes@6.0.0": {
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dependencies": [
+ "xmlchars"
+ ]
+ },
+ "scheduler@0.23.2": {
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "dependencies": [
+ "loose-envify"
+ ]
+ },
+ "secure-json-parse@2.7.0": {
+ "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="
+ },
+ "sonic-boom@4.2.0": {
+ "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==",
+ "dependencies": [
+ "atomic-sleep"
+ ]
+ },
+ "source-map-js@1.2.1": {
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
+ },
+ "split2@4.2.0": {
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="
+ },
+ "stoker@1.4.3_@hono+zod-openapi@0.18.4__hono@4.8.10__zod@3.25.76_hono@4.8.10_zod@3.25.76": {
+ "integrity": "sha512-kijg+1PKUY6laFbNcY7hw5OPgg3QhWD+2wAZsk35IqiZfVwU3S/E3DYbemecRT7vdWbWrZ2mzewQrqD4zoJSeQ==",
+ "dependencies": [
+ "@hono/zod-openapi",
+ "hono"
+ ],
+ "optionalPeers": [
+ "@hono/zod-openapi"
+ ]
+ },
+ "strip-json-comments@3.1.1": {
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
+ },
+ "style-mod@4.1.2": {
+ "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
+ },
+ "symbol-tree@3.2.4": {
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
+ },
+ "thread-stream@3.1.0": {
+ "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
+ "dependencies": [
+ "real-require"
+ ]
+ },
+ "tldts-core@6.1.86": {
+ "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="
+ },
+ "tldts@6.1.86": {
+ "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
+ "dependencies": [
+ "tldts-core"
+ ],
+ "bin": true
+ },
+ "toucan-js@4.1.1": {
+ "integrity": "sha512-GTPwEaCRN8IbYe5/VeGiwxYvMO0dKaC16fTeLbF+QGswjkLZ9JUqAfDhLMyH2SWukYhmetH+uxWa1Bhluv/evQ==",
+ "dependencies": [
+ "@sentry/core@8.9.2",
+ "@sentry/types",
+ "@sentry/utils"
+ ]
+ },
+ "tough-cookie@5.1.2": {
+ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+ "dependencies": [
+ "tldts"
+ ]
+ },
+ "tr46@0.0.3": {
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
+ },
+ "tr46@5.1.1": {
+ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+ "dependencies": [
+ "punycode"
+ ]
+ },
+ "turndown@7.2.0": {
+ "integrity": "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==",
+ "dependencies": [
+ "@mixmark-io/domino"
+ ]
+ },
+ "typescript@5.8.3": {
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "bin": true
+ },
+ "undici-types@6.21.0": {
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
+ },
+ "undici-types@7.8.0": {
+ "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="
+ },
+ "uuid@9.0.1": {
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "bin": true
+ },
+ "w3c-keyname@2.2.8": {
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
+ },
+ "w3c-xmlserializer@5.0.0": {
+ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+ "dependencies": [
+ "xml-name-validator"
+ ]
+ },
+ "webidl-conversions@3.0.1": {
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
+ },
+ "webidl-conversions@7.0.0": {
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
+ },
+ "whatwg-encoding@3.1.1": {
+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+ "dependencies": [
+ "iconv-lite"
+ ]
+ },
+ "whatwg-mimetype@4.0.0": {
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="
+ },
+ "whatwg-url@14.2.0": {
+ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+ "dependencies": [
+ "tr46@5.1.1",
+ "webidl-conversions@7.0.0"
+ ]
+ },
+ "whatwg-url@5.0.0": {
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dependencies": [
+ "tr46@0.0.3",
+ "webidl-conversions@3.0.1"
+ ]
+ },
+ "wrappy@1.0.2": {
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "ws@8.18.3": {
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="
+ },
+ "xml-name-validator@5.0.0": {
+ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="
+ },
+ "xmlchars@2.2.0": {
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
+ },
+ "yaml@2.8.0": {
+ "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
+ "bin": true
+ },
+ "zhead@2.2.4": {
+ "integrity": "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag=="
+ },
+ "zod@3.25.76": {
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="
+ }
+ },
+ "redirects": {
+ "https://esm.sh/core-js/proposals/explicit-resource-management": "https://esm.sh/core-js@3.44.0/proposals/explicit-resource-management"
+ },
+ "remote": {
+ "https://esm.sh/core-js@3.44.0/denonext/proposals/explicit-resource-management.mjs": "4850503a2650ba47368eb95b1aa2565b47bad32efe9738766811f6de75cf51e1",
+ "https://esm.sh/core-js@3.44.0/proposals/explicit-resource-management": "06969d679705ab37fd058cc66a50cd7648f79bf5fd86fb13b29cb93c2667ebc7"
+ },
+ "workspace": {
+ "dependencies": [
+ "jsr:@cliffy/command@^1.0.0-rc.8",
+ "jsr:@std/assert@1",
+ "jsr:@std/async@1",
+ "jsr:@std/cli@1",
+ "jsr:@std/crypto@1",
+ "jsr:@std/dotenv@~0.225.3",
+ "jsr:@std/encoding@1",
+ "jsr:@std/expect@1",
+ "jsr:@std/fs@1",
+ "jsr:@std/http@1",
+ "jsr:@std/path@1",
+ "jsr:@std/testing@1",
+ "npm:@babel/standalone@^7.28.2",
+ "npm:@types/react@^18.3.1",
+ "npm:jsdom@*",
+ "npm:lit@^3.3.0",
+ "npm:merkle-reference@^2.2.0",
+ "npm:multiformats@^13.3.2",
+ "npm:react-dom@^18.3.1",
+ "npm:react@^18.3.1",
+ "npm:turndown@^7.1.2",
+ "npm:zod@^3.24.1"
+ ],
+ "members": {
+ "packages/cli": {
+ "dependencies": [
+ "jsr:@cliffy/table@^1.0.0-rc.8"
+ ]
+ },
+ "packages/felt": {
+ "dependencies": [
+ "jsr:@deno/esbuild-plugin@*",
+ "jsr:@std/fmt@^1.0.3",
+ "npm:esbuild@~0.25.5"
+ ]
+ },
+ "packages/identity": {
+ "dependencies": [
+ "npm:@noble/ed25519@^2.2.3",
+ "npm:@scure/bip39@^1.5.4"
+ ]
+ },
+ "packages/js-runtime": {
+ "dependencies": [
+ "npm:source-map-js@^1.2.1",
+ "npm:typescript@*"
+ ]
+ },
+ "packages/js-sandbox": {
+ "dependencies": [
+ "npm:@jitl/quickjs-singlefile-mjs-debug-sync@*",
+ "npm:quickjs-emscripten-core@*"
+ ]
+ },
+ "packages/llm": {
+ "dependencies": [
+ "npm:json5@^2.2.3"
+ ]
+ },
+ "packages/memory": {
+ "dependencies": [
+ "jsr:@db/sqlite@0.12",
+ "npm:@opentelemetry/api@^1.9.0"
+ ]
+ },
+ "packages/schema-generator": {
+ "dependencies": [
+ "npm:typescript@*"
+ ]
+ },
+ "packages/seeder": {
+ "dependencies": [
+ "npm:@ai-sdk/anthropic@^1.1.6",
+ "npm:@ai-sdk/openai@^1.1.9"
+ ]
+ },
+ "packages/shell": {
+ "dependencies": [
+ "npm:@lit/context@^1.1.5",
+ "npm:@lit/task@^1.0.2"
+ ]
+ },
+ "packages/toolshed": {
+ "dependencies": [
+ "jsr:@cmd-johnson/oauth2-client@2",
+ "jsr:@std/cli@^1.0.12",
+ "npm:@ai-sdk/anthropic@^2.0.9",
+ "npm:@ai-sdk/google-vertex@^3.0.16",
+ "npm:@ai-sdk/groq@^2.0.16",
+ "npm:@ai-sdk/openai@^2.0.22",
+ "npm:@ai-sdk/perplexity@^2.0.8",
+ "npm:@ai-sdk/xai@^2.0.13",
+ "npm:@arizeai/openinference-semantic-conventions@^1.1.0",
+ "npm:@arizeai/openinference-vercel@^2.0.1",
+ "npm:@fal-ai/client@^1.2.2",
+ "npm:@hono/sentry@^1.2.0",
+ "npm:@hono/zod-openapi@~0.18.3",
+ "npm:@hono/zod-validator@~0.4.2",
+ "npm:@opentelemetry/api@^1.7.0",
+ "npm:@opentelemetry/context-async-hooks@^1.19.0",
+ "npm:@opentelemetry/core@^1.19.0",
+ "npm:@opentelemetry/exporter-trace-otlp-proto@0.46",
+ "npm:@opentelemetry/resources@^1.19.0",
+ "npm:@opentelemetry/sdk-trace-base@^1.19.0",
+ "npm:@opentelemetry/semantic-conventions@^1.19.0",
+ "npm:@scalar/hono-api-reference@~0.5.165",
+ "npm:@sentry/deno@^9.3.0",
+ "npm:ai@^5.0.27",
+ "npm:ajv@^8.17.1",
+ "npm:gcp-metadata@6.1.0",
+ "npm:hono-pino@0.7",
+ "npm:hono@^4.7.0",
+ "npm:jsonschema@^1.5.0",
+ "npm:mistreevous@4.2.0",
+ "npm:pino-pretty@13",
+ "npm:pino@^9.6.0",
+ "npm:plaid@36",
+ "npm:stoker@^1.4.2"
+ ]
+ },
+ "packages/ui": {
+ "dependencies": [
+ "npm:@codemirror/autocomplete@^6.15.0",
+ "npm:@codemirror/lang-css@^6.3.1",
+ "npm:@codemirror/lang-html@^6.4.9",
+ "npm:@codemirror/lang-javascript@^6.2.2",
+ "npm:@codemirror/lang-json@^6.0.1",
+ "npm:@codemirror/lang-markdown@^6.3.0",
+ "npm:@codemirror/language@^6.10.8",
+ "npm:@codemirror/state@^6.5.1",
+ "npm:@codemirror/theme-one-dark@^6.1.2",
+ "npm:@codemirror/view@^6.26.0",
+ "npm:@lit/context@^1.1.2",
+ "npm:codemirror@^6.0.1",
+ "npm:marked@4"
+ ]
+ },
+ "packages/vendor-astral": {
+ "dependencies": [
+ "jsr:@deno-library/progress@^1.5.1",
+ "jsr:@deno/cache-dir@0.22.2",
+ "jsr:@std/assert@1",
+ "jsr:@std/async@1",
+ "jsr:@std/encoding@1",
+ "jsr:@std/fs@1",
+ "jsr:@std/path@1",
+ "jsr:@std/testing@1",
+ "jsr:@zip-js/zip-js@^2.7.52"
+ ]
+ }
+ }
+ }
+}
diff --git a/deploy/deployment.yml b/deploy/deployment.yml
deleted file mode 100644
index 9941dec5d..000000000
--- a/deploy/deployment.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
- name: usuba-lb
-spec:
- selector:
- app: usuba
- ports:
- - protocol: "TCP"
- targetPort: 8080
- port: 80
- type: LoadBalancer
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: usuba
-spec:
- selector:
- matchLabels:
- app: usuba
- replicas: 1
- template:
- metadata:
- labels:
- app: usuba
- spec:
- containers:
- - name: usuba
- image: gcr.io/constellation-test-426817/usuba:latest
- imagePullPolicy: Always
- ports:
- - containerPort: 8080
-# ---
-# apiVersion: networking.k8s.io/v1
-# kind: Ingress
-# metadata:
-# name: minimal-ingress
-# annotations:
-# nginx.ingress.kubernetes.io/rewrite-target: /
-#spec:
-# defaultBackend:
-# service:
-# name: usuba-lb
-# port:
-# number: 8080
-# rules:
-# - http:
-# paths:
-# - path: /
-# pathType: Prefix
-# backend:
-# service:
-# name: verification-service
-# port:
-# number: 9090
diff --git a/docs/charm-cell-diagram.md b/docs/charm-cell-diagram.md
new file mode 100644
index 000000000..2bc668b96
--- /dev/null
+++ b/docs/charm-cell-diagram.md
@@ -0,0 +1,12 @@
+Here's a diagram of how cells are typically connected.
+
+We often call the main result cell the "charm" and that cell's id is the id in the url.
+
+```mermaid
+flowchart TD
+ A["Result Cell"]
+ A --source--> B["Process Cell"]
+ B --value.resultRef--> A
+ B --value.spell--> C["Recipe Cell"]
+ D@{ shape: procs, label: "Data Cells"} --source--> B
+```
\ No newline at end of file
diff --git a/docs/charm_exploration.ipynb b/docs/charm_exploration.ipynb
new file mode 100644
index 000000000..1c29595d6
--- /dev/null
+++ b/docs/charm_exploration.ipynb
@@ -0,0 +1,923 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "8aaee0bd-59fc-441c-8527-4edc3e0c37bb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "// simple log function\n",
+ "const log: (s: T, prefix?: string) => void = (s, prefix?) =>\n",
+ " console.log(\n",
+ " (prefix ? prefix : \"\") + \": \" +\n",
+ " JSON.stringify(s, null, 2),\n",
+ " );\n",
+ "\n",
+ "//function logDocImpl(doc: DocImpl) {\n",
+ "// console.log(\"docImpl: entityId=\" + doc.entityId + \", value=\" + doc.value + \", space=\" + doc.space + \", sourceCell=\" + doc.sourceCell + \", ephemeral=\" + doc.ephemeral);\n",
+ "//}\n",
+ "function logDocImpl(doc: DocImpl, prefix?: string) {\n",
+ " let logString = \"docImpl: \\n\" +\n",
+ " \" entityId=\" + JSON.stringify(doc.entityId) + \"\\n\" +\n",
+ " \" space=\" + doc.space + \"\\n\" +\n",
+ " \" sourceCell=\" + doc.sourceCell + \"\\n\" +\n",
+ " \" ephemeral=\" + doc.ephemeral;\n",
+ "\n",
+ " // Check if value is an array and add each object individually to the log string\n",
+ " if (Array.isArray(doc.value)) {\n",
+ " logString += \"\\n value contains \" + doc.value.length + \" objects:\";\n",
+ " doc.value.forEach((item, index) => {\n",
+ " // Convert to JSON string with 2-space indentation\n",
+ " const jsonString = JSON.stringify(item, null, 2);\n",
+ "\n",
+ " // Split the JSON string into lines\n",
+ " const jsonLines = jsonString.split(\"\\n\");\n",
+ "\n",
+ " // Add the first line with the value[index] prefix\n",
+ " logString += `\\n value[${index}]: ${jsonLines[0]}`;\n",
+ "\n",
+ " // For remaining lines, add 4 additional spaces to maintain consistent indentation\n",
+ " for (let i = 1; i < jsonLines.length; i++) {\n",
+ " logString += \"\\n \" + jsonLines[i];\n",
+ " }\n",
+ " });\n",
+ " } else {\n",
+ " // For non-array values, apply the same indentation logic\n",
+ " const jsonString = JSON.stringify(doc.value, null, 2);\n",
+ " const jsonLines = jsonString.split(\"\\n\");\n",
+ "\n",
+ " // Add the first line with the value: prefix\n",
+ " logString += \"\\n value: \" + jsonLines[0];\n",
+ "\n",
+ " // For remaining lines, add proper indentation\n",
+ " for (let i = 1; i < jsonLines.length; i++) {\n",
+ " logString += \"\\n \" + jsonLines[i];\n",
+ " }\n",
+ " }\n",
+ " console.log((prefix ? prefix : \"\") + \": \" + logString);\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8fe4a001-c69b-40fb-ae3f-917d41561f1b",
+ "metadata": {},
+ "source": [
+ "### Introduction\n",
+ "\n",
+ "This exploration tries to show what the various \"parts\" of the system does. It\n",
+ "tries to make explicit all the things that usually happen in the backgroun.\n",
+ "\n",
+ "This means we won't be using the highest level abstractions. I believe this will\n",
+ "show what's really happening under the hood and give you a better idea of what's\n",
+ "in the guts of the system.\n",
+ "\n",
+ "### List of Charms\n",
+ "\n",
+ "The first thing I'd like to do is be able to see a list of our charms, just like\n",
+ "when you go to the toolshed common knowledge page.\n",
+ "\n",
+ "The call I'd like to do is `getDoc([], \"charms\", SPACE);`\n",
+ "\n",
+ "BUT before we can access charms, we need to set up our connection to the\n",
+ "database. Let's do that. NOTE: notice that `signer` is undefined in the Storage\n",
+ "object. We'll get back to that later."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f7936edf-cc47-4f4e-a886-92faaedb3497",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "StorageImpl {\n",
+ " storageProviders: Map(0) {},\n",
+ " remoteStorageUrl: URL {\n",
+ " href: \u001b[32m\"https://toolshed.saga-castor.ts.net/\"\u001b[39m,\n",
+ " origin: \u001b[32m\"https://toolshed.saga-castor.ts.net\"\u001b[39m,\n",
+ " protocol: \u001b[32m\"https:\"\u001b[39m,\n",
+ " username: \u001b[32m\"\"\u001b[39m,\n",
+ " password: \u001b[32m\"\"\u001b[39m,\n",
+ " host: \u001b[32m\"toolshed.saga-castor.ts.net\"\u001b[39m,\n",
+ " hostname: \u001b[32m\"toolshed.saga-castor.ts.net\"\u001b[39m,\n",
+ " port: \u001b[32m\"\"\u001b[39m,\n",
+ " pathname: \u001b[32m\"/\"\u001b[39m,\n",
+ " hash: \u001b[32m\"\"\u001b[39m,\n",
+ " search: \u001b[32m\"\"\u001b[39m\n",
+ " },\n",
+ " signer: \u001b[90mundefined\u001b[39m,\n",
+ " docIsSyncing: Set(0) {},\n",
+ " docIsLoading: Map(0) {},\n",
+ " loadingPromises: Map(0) {},\n",
+ " loadingResolves: Map(0) {},\n",
+ " writeDependentDocs: Map(0) {},\n",
+ " writeValues: Map(0) {},\n",
+ " readDependentDocs: Map(0) {},\n",
+ " readValues: Map(0) {},\n",
+ " currentBatch: [],\n",
+ " currentBatchProcessing: \u001b[33mfalse\u001b[39m,\n",
+ " currentBatchResolve: \u001b[36m[Function (anonymous)]\u001b[39m,\n",
+ " currentBatchPromise: Promise { \u001b[36m\u001b[39m },\n",
+ " lastBatchTime: \u001b[33m0\u001b[39m,\n",
+ " lastBatchDebounceCount: \u001b[33m0\u001b[39m,\n",
+ " debounceTimeout: \u001b[1mnull\u001b[22m,\n",
+ " batchStartTime: \u001b[33m0\u001b[39m,\n",
+ " cancel: \u001b[36m[Function: cancelAll]\u001b[39m,\n",
+ " addCancel: \u001b[36m[Function: addCancel]\u001b[39m\n",
+ "}"
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import { storage } from \"../runner/src/storage.ts\";\n",
+ "\n",
+ "// where our server is\n",
+ "const API_URL = \"https://toolshed.saga-castor.ts.net/\";\n",
+ "\n",
+ "// `storage` is a singleton and you have to give it the server URL\n",
+ "storage.setRemoteStorage(new URL(API_URL));\n",
+ "storage; // notice signer is undefined"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5558236d-0fe0-4013-9d31-e5f537ad5154",
+ "metadata": {},
+ "source": [
+ "### Space\n",
+ "\n",
+ "Spaces are in flux and we'll have private and public spaces, but for now, we\n",
+ "still have the ability to pass in a simple string for the space. We'll use\n",
+ "\"common-knowledge\" which is our default space.\n",
+ "\n",
+ "### getDoc()\n",
+ "\n",
+ "Now we have storage set up \"enough\" to call getDoc(value, cause, space). The\n",
+ "value we pass in here is basically the empty value `[]` which is the same as\n",
+ "undefined. The cause is a special hardcoded string \"charms\" that we use.\n",
+ "CharmManager has this hardcoded string in it. Lastly we pass in the space which\n",
+ "we already talked about."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "c8bddbac-278a-42de-90c3-aabc001b0663",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{\n",
+ " get: \u001b[36m[Function: get]\u001b[39m,\n",
+ " getAsQueryResult: \u001b[36m[Function: getAsQueryResult]\u001b[39m,\n",
+ " asCell: \u001b[36m[Function: asCell]\u001b[39m,\n",
+ " send: \u001b[36m[Function: send]\u001b[39m,\n",
+ " updates: \u001b[36m[Function: updates]\u001b[39m,\n",
+ " getAtPath: \u001b[36m[Function: getAtPath]\u001b[39m,\n",
+ " setAtPath: \u001b[36m[Function: setAtPath]\u001b[39m,\n",
+ " freeze: \u001b[36m[Function: freeze]\u001b[39m,\n",
+ " isFrozen: \u001b[36m[Function: isFrozen]\u001b[39m,\n",
+ " toJSON: \u001b[36m[Function: toJSON]\u001b[39m,\n",
+ " value: \u001b[36m[Getter]\u001b[39m,\n",
+ " entityId: \u001b[36m[Getter/Setter]\u001b[39m,\n",
+ " space: \u001b[36m[Getter/Setter]\u001b[39m,\n",
+ " sourceCell: \u001b[36m[Getter/Setter]\u001b[39m,\n",
+ " ephemeral: \u001b[36m[Getter/Setter]\u001b[39m,\n",
+ " copyTrap: \u001b[36m[Getter]\u001b[39m,\n",
+ " registerSchemaUse: \u001b[36m[Function: registerSchemaUse]\u001b[39m,\n",
+ " [\u001b[32mSymbol(toOpaqueRef)\u001b[39m]: \u001b[36m[Function: [toOpaqueRef]]\u001b[39m,\n",
+ " [\u001b[32mSymbol(isDoc)\u001b[39m]: \u001b[33mtrue\u001b[39m\n",
+ "}"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import { DocImpl, getDoc } from \"../runner/src/doc.ts\";\n",
+ "import { createRef } from \"../runner/src/doc-map.ts\";\n",
+ "\n",
+ "// space is usually a did now, but it still accepts a string\n",
+ "const SPACE = \"common-knowledge\";\n",
+ "\n",
+ "const allCharmsDoc: DocImpl = getDoc(\n",
+ " [],\n",
+ " \"charms\",\n",
+ " SPACE,\n",
+ ");\n",
+ "allCharmsDoc;"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ae00cac7-6470-4785-aa73-f60dda006386",
+ "metadata": {},
+ "source": [
+ "### More on getDoc()\n",
+ "\n",
+ "How does getDoc() find the doc it's looking for? As we discussed, getDoc()\n",
+ "received a `value` and `cause` parameters.\n",
+ "\n",
+ "Within getDoc(), we take these two parameters and create an `EntityId` from\n",
+ "them. What this means is that if we call getDoc() with different value and cause\n",
+ "parameters, we should get different `EntityId`s. Let's test this out. We'll get\n",
+ "another \"well known\" document for the pinned charms. If we're right, the IDs\n",
+ "will be different."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "0fb9e20d-b098-43dd-8165-2bf42c38478f",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "allCharmsDoc entity id: {\n",
+ " \"/\": \"baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye\"\n",
+ "}\n",
+ "pinsDoc entity id: {\n",
+ " \"/\": \"baedreihxpwcmhvzpf5weuf4ceow4zbahqikvu5ploox36ipeuvqnminyba\"\n",
+ "}\n",
+ "is the same?: false\n"
+ ]
+ }
+ ],
+ "source": [
+ "import { deepEqual } from \"../builder/src/utils.ts\";\n",
+ "\n",
+ "// get the pinned charms doc\n",
+ "const pinsDoc: DocImpl = getDoc(\n",
+ " [],\n",
+ " \"pinned-charms\",\n",
+ " SPACE,\n",
+ ");\n",
+ "\n",
+ "// see that the entityId for both allCharmsDoc and pinsDoc are different\n",
+ "log(allCharmsDoc.entityId, \"allCharmsDoc entity id\");\n",
+ "log(pinsDoc.entityId, \"pinsDoc entity id\");\n",
+ "log(deepEqual(allCharmsDoc, pinsDoc), \"is the same?\");"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "218d5164-2d6f-454a-80e2-756e935d68c8",
+ "metadata": {},
+ "source": [
+ "### createRef\n",
+ "\n",
+ "Let's continue our detour, how do EntityIds get created. This uses the enigmatic\n",
+ "merkle-reference. We don't need to understand how merkle-references are made,\n",
+ "just that it creates a unique digital fingerprint of the Doc, like a hash.\n",
+ "\n",
+ "Let's recreate the allCharmsDocEntityId from the \"value\" and \"cause\" arugments\n",
+ "to getDoc. As a reminder, this was our getDoc call:\n",
+ "\n",
+ "```ts\n",
+ "const allCharmsDoc: DocImpl = getDoc(\n",
+ " [],\n",
+ " \"charms\",\n",
+ " SPACE,\n",
+ ");\n",
+ "```\n",
+ "\n",
+ "So we should be able to pass in [] and \"charms\" to createRef() and get back the\n",
+ "same entity ID. Let's try it."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "9bba8a6b-b97b-45f6-9aaf-e03f1b228055",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "recreated allCharmsDoc entityid: {\n",
+ " \"/\": \"baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye\"\n",
+ "}\n",
+ "original allCharmsDoc entityid: {\n",
+ " \"/\": \"baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye\"\n",
+ "}\n",
+ "are they the same?: true\n"
+ ]
+ }
+ ],
+ "source": [
+ "const recreatedEntityId: EntityId = createRef([], \"charms\");\n",
+ "log(recreatedEntityId, \"recreated allCharmsDoc entityid\");\n",
+ "log(allCharmsDoc.entityId, \"original allCharmsDoc entityid\");\n",
+ "log(deepEqual(recreatedEntityId, allCharmsDoc.entityId), \"are they the same?\");"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2d16fb25-fa33-4bc6-b877-4501eb730a38",
+ "metadata": {},
+ "source": [
+ "### Spaces dont matter\n",
+ "\n",
+ "Did you notice that we don't care about spaces when we make the id? If I use a\n",
+ "different space for the original allCharmsDoc, I should get back the same\n",
+ "EntityID from getDoc(). Let's verify!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "0239e733-15fe-4905-81a3-30cb4699bbe3",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "entityid for allCharmsDoc but with different space: {\n",
+ " \"/\": \"baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye\"\n",
+ "}\n",
+ "entityid for original allCharmsDoc in the 'charms' space: {\n",
+ " \"/\": \"baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye\"\n",
+ "}\n",
+ "are they the same?: true\n"
+ ]
+ }
+ ],
+ "source": [
+ "const differentSpaceDoc: DocImpl = getDoc(\n",
+ " [],\n",
+ " \"charms\",\n",
+ " \"some other space\",\n",
+ ");\n",
+ "log(\n",
+ " differentSpaceDoc.entityId,\n",
+ " \"entityid for allCharmsDoc but with different space\",\n",
+ ");\n",
+ "log(\n",
+ " allCharmsDoc.entityId,\n",
+ " \"entityid for original allCharmsDoc in the 'charms' space\",\n",
+ ");\n",
+ "log(\n",
+ " deepEqual(differentSpaceDoc.entityId, allCharmsDoc.entityId),\n",
+ " \"are they the same?\",\n",
+ ");"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cc7a24ea-15ee-4555-8f6c-cb5bb18a1f75",
+ "metadata": {},
+ "source": [
+ "To further demystify getDoc(), the parameters we pass in really can be anything."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "904ac02f-9280-4573-a959-7a95788ff157",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "crazydoc: {\n",
+ " \"/\": \"baedreiekup34zo6rx6l33orhkktrzxgnrxxt7p3rmlnqylb4dv2swcc4kq\"\n",
+ "}\n"
+ ]
+ }
+ ],
+ "source": [
+ "const some_val = [\"arrays\", \"are\", { value: \"good\" }];\n",
+ "const some_cause = { some: \"random\", value: 42 };\n",
+ "const some_space = \"random string in here\";\n",
+ "const crazyDoc = getDoc(some_val, some_cause, some_space);\n",
+ "log(crazyDoc, \"crazydoc\");\n",
+ "\n",
+ "// can try changing val, cause, space and see that nested objects change merke-reference"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "0f93ff4f-676c-4372-bb24-ed6c283d721e",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ ": docImpl: \n",
+ " entityId={\"/\":\"baedreiekup34zo6rx6l33orhkktrzxgnrxxt7p3rmlnqylb4dv2swcc4kq\"}\n",
+ " space=random string in here\n",
+ " sourceCell=undefined\n",
+ " ephemeral=false\n",
+ " value contains 3 objects:\n",
+ " value[0]: \"arrays\"\n",
+ " value[1]: \"are\"\n",
+ " value[2]: {\n",
+ " \"value\": \"good\"\n",
+ " }\n"
+ ]
+ }
+ ],
+ "source": [
+ "// let's see what this crazy doc looks like\n",
+ "logDocImpl(crazyDoc);"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "65af0146-ebee-4c2e-bb56-bf2522a60e99",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "// we'll set this charmsDoc to the doc we want to use from here on to get the list of charms\n",
+ "// either to pinsDoc or allCharmsDoc\n",
+ "const charmsDoc = pinsDoc;"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "843c5bec-2653-4284-ad53-2cc51ca247d0",
+ "metadata": {},
+ "source": [
+ "### Back on track\n",
+ "\n",
+ "Back from the short detour, we want to continue to get the charms list. Right\n",
+ "now, we have `charmsDoc` that has an EntityID but we haven't loaded the doc from\n",
+ "Storage yet (the database). So let's do that now. However, we are...\n",
+ "\n",
+ "### Stuck on Storage\n",
+ "\n",
+ "We're making a point to access data via the `Storage` interface rather than\n",
+ "through CharmManager or other possibly more convenient methods. Thus, our next\n",
+ "step is to call `Storage.syncCell(docImpl);` This would let us get the data for\n",
+ "the doc. However, this would crash with an exception. Jake ran into this the\n",
+ "hard way and the system somehow ate the exception.\n",
+ "\n",
+ "### Another short detour, questioning our Identity\n",
+ "\n",
+ "Remember when we saw the signer property was undefined in storage before? We\n",
+ "have to set that now in order to call syncCell. What we need to call `setSigner`\n",
+ "on `storage`. We create a signer via the Identity module."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0714bfaa-cc87-420e-b64d-bb90bb89db48",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "user identity (looks empty but its just private fields: {}\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "StorageImpl {\n",
+ " storageProviders: Map(0) {},\n",
+ " remoteStorageUrl: URL {\n",
+ " href: \u001b[32m\"https://toolshed.saga-castor.ts.net/\"\u001b[39m,\n",
+ " origin: \u001b[32m\"https://toolshed.saga-castor.ts.net\"\u001b[39m,\n",
+ " protocol: \u001b[32m\"https:\"\u001b[39m,\n",
+ " username: \u001b[32m\"\"\u001b[39m,\n",
+ " password: \u001b[32m\"\"\u001b[39m,\n",
+ " host: \u001b[32m\"toolshed.saga-castor.ts.net\"\u001b[39m,\n",
+ " hostname: \u001b[32m\"toolshed.saga-castor.ts.net\"\u001b[39m,\n",
+ " port: \u001b[32m\"\"\u001b[39m,\n",
+ " pathname: \u001b[32m\"/\"\u001b[39m,\n",
+ " hash: \u001b[32m\"\"\u001b[39m,\n",
+ " search: \u001b[32m\"\"\u001b[39m\n",
+ " },\n",
+ " signer: Identity {},\n",
+ " docIsSyncing: Set(0) {},\n",
+ " docIsLoading: Map(0) {},\n",
+ " loadingPromises: Map(0) {},\n",
+ " loadingResolves: Map(0) {},\n",
+ " writeDependentDocs: Map(0) {},\n",
+ " writeValues: Map(0) {},\n",
+ " readDependentDocs: Map(0) {},\n",
+ " readValues: Map(0) {},\n",
+ " currentBatch: [],\n",
+ " currentBatchProcessing: \u001b[33mfalse\u001b[39m,\n",
+ " currentBatchResolve: \u001b[36m[Function (anonymous)]\u001b[39m,\n",
+ " currentBatchPromise: Promise { \u001b[36m\u001b[39m },\n",
+ " lastBatchTime: \u001b[33m0\u001b[39m,\n",
+ " lastBatchDebounceCount: \u001b[33m0\u001b[39m,\n",
+ " debounceTimeout: \u001b[1mnull\u001b[22m,\n",
+ " batchStartTime: \u001b[33m0\u001b[39m,\n",
+ " cancel: \u001b[36m[Function: cancelAll]\u001b[39m,\n",
+ " addCancel: \u001b[36m[Function: addCancel]\u001b[39m\n",
+ "}"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import { DIDKey, Identity } from \"../identity/src/index.ts\";\n",
+ "\n",
+ "// key system similar to mneumonic registration\n",
+ "const user_identity: Identity = await Identity.fromPassphrase(\n",
+ " \"some passphrase\",\n",
+ ");\n",
+ "\n",
+ "// NOTE: we can use this key directly with storage, however, to better replicate\n",
+ "// the long term goals, we derive a new key from the root key\n",
+ "// this is a lot like how users will have their own keys for each of their spaces\n",
+ "// we would normally pass in the space name (which is like the \"common-knowledge\" part of the URL)\n",
+ "const space_key = await user_identity.derive(SPACE);\n",
+ "\n",
+ "// helper function? why not\n",
+ "function initializeStorage(\n",
+ " storage: Storage,\n",
+ " remoteStorageURL: string,\n",
+ " signer: Signer,\n",
+ "): Storage {\n",
+ " storage.setRemoteStorage(new URL(remoteStorageURL));\n",
+ " storage.setSigner(signer);\n",
+ " return storage;\n",
+ "}\n",
+ "\n",
+ "// use `space_key` the derived space-based identity as the signer for the storage\n",
+ "const myStorage = initializeStorage(storage, API_URL, space_key);\n",
+ "\n",
+ "log(user_identity, \"user identity (looks empty but its just private fields\");\n",
+ "myStorage; // notice signer is no longer undefined"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2fff337d-be9c-40cb-b8b3-f276f3375466",
+ "metadata": {},
+ "source": [
+ "### Lessgo Sync!\n",
+ "\n",
+ "Now we are all clear to call syncCell! syncCell returns a Promise(), so we await\n",
+ "on it before accessing the object.\n",
+ "\n",
+ "While it's called syncCell, it really syncs either DocImpls or Cells. We'll get\n",
+ "into that more later.\n",
+ "\n",
+ "```ts\n",
+ "interface Storage {\n",
+ " syncCell(\n",
+ " subject: DocImpl | Cell,\n",
+ " expectedInStorage: boolean = false,\n",
+ " ): Promise> | DocImpl \n",
+ "}\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "ced12094-6ca1-415b-9b67-4e22690d7198",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "before syncCell: docImpl: \n",
+ " entityId={\"/\":\"baedreihxpwcmhvzpf5weuf4ceow4zbahqikvu5ploox36ipeuvqnminyba\"}\n",
+ " space=common-knowledge\n",
+ " sourceCell=undefined\n",
+ " ephemeral=false\n",
+ " value contains 0 objects:\n",
+ "after syncCell: docImpl: \n",
+ " entityId={\"/\":\"baedreihxpwcmhvzpf5weuf4ceow4zbahqikvu5ploox36ipeuvqnminyba\"}\n",
+ " space=common-knowledge\n",
+ " sourceCell=undefined\n",
+ " ephemeral=false\n",
+ " value contains 4 objects:\n",
+ " value[0]: {\n",
+ " \"cell\": {\n",
+ " \"/\": \"baedreietbxsdgmw67da47dp6aqervnk2zyppevhx4p4mn3pet6onvaxcmy\"\n",
+ " },\n",
+ " \"path\": []\n",
+ " }\n",
+ " value[1]: {\n",
+ " \"cell\": {\n",
+ " \"/\": \"baedreih6pdtzk4xvjuxlsp3htrugtipgndn6xtd4cwbdr6wgscb6fh2nlu\"\n",
+ " },\n",
+ " \"path\": []\n",
+ " }\n",
+ " value[2]: {\n",
+ " \"cell\": {\n",
+ " \"/\": \"baedreiel5fi755jovxnou5bpdsuvcylibayiaaipaysi2tiip26ik3b6ya\"\n",
+ " },\n",
+ " \"path\": []\n",
+ " }\n",
+ " value[3]: {\n",
+ " \"cell\": {\n",
+ " \"/\": \"baedreid2qh2k6gcie4ezckqukkplpeqlz7jnxirt7aehxdcw4sfg5aszh4\"\n",
+ " },\n",
+ " \"path\": []\n",
+ " }\n"
+ ]
+ }
+ ],
+ "source": [
+ "// start syncing on this document\n",
+ "// notice that we call syncCell on a DocImpl\n",
+ "// also, we have to await on this before accessing otherwise we may get a race condition!\n",
+ "logDocImpl(charmsDoc, \"before syncCell\");\n",
+ "await myStorage.syncCell(charmsDoc);\n",
+ "logDocImpl(charmsDoc, \"after syncCell\");"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3965fe43-6723-4a97-ab94-7f5118ac34d7",
+ "metadata": {},
+ "source": [
+ "### Document Loaded but not the Charms\n",
+ "\n",
+ "We have retrieved the document now, however, it doesn't actually contain the\n",
+ "charms, just references to them as you can see via the `value` fields. Each\n",
+ "element in the value array\n",
+ "\n",
+ "We have to look at each of the elements in the `value` array and load each one.\n",
+ "Each one has the format:\n",
+ "\n",
+ "```ts\n",
+ "{\n",
+ " \"cell\": {\n",
+ " \"/\": \"baedreid2qh2k6gcie4ezckqukkplpeqlz7jnxirt7aehxdcw4sfg5aszh4\"\n",
+ " },\n",
+ " \"path\": []\n",
+ "}\n",
+ "```\n",
+ "\n",
+ "This is a CellLink, which you can find in runner/src/cell.ts:\n",
+ "\n",
+ "```ts\n",
+ "/**\n",
+ " * Cell link.\n",
+ " *\n",
+ " * A cell link is a doc and a path within that doc.\n",
+ " */\n",
+ "export type CellLink = {\n",
+ " space?: string;\n",
+ " cell: DocImpl;\n",
+ " path: PropertyKey[];\n",
+ "};\n",
+ "```\n",
+ "\n",
+ "We sometimes call these DocLinks, they are the same thing. In cell.ts, you'll\n",
+ "still see\n",
+ "\n",
+ "```\n",
+ "* @method getAsDocLink Returns a document link for the cell.\n",
+ "* @returns {CellLink}\n",
+ "```\n",
+ "\n",
+ "We'll take each of the CellLinks and call getDocByEntityId(), this will load the\n",
+ "docImpl for us. This time we do _NOT_ need to sync on each child because the\n",
+ "first syncCell also sync'd on all the child cells. NOTE: this logic of loading\n",
+ "dependencies happens in `runner/src/storage.ts:_processCurrentBatch()`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "3be96f91-33dc-47de-bee2-64886dc77793",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ ": docImpl: \n",
+ " entityId={\"/\":\"baedreietbxsdgmw67da47dp6aqervnk2zyppevhx4p4mn3pet6onvaxcmy\"}\n",
+ " space=common-knowledge\n",
+ " sourceCell=[object Object]\n",
+ " ephemeral=false\n",
+ " value: {\n",
+ " \"$NAME\": \"Common Time\",\n",
+ " \"$UI\": {\n",
+ " \"type\": \"vnode\",\n",
+ " \"name\": \"common-iframe\",\n",
+ " \"props\": {\n",
+ " \"src\": \"\\n\\n\\n\\n\\n\\n\\nCommon Time\\n\\n\\n\\n\\n\\n\",\n",
+ " \"$context\": {\n",
+ " \"$alias\": {\n",
+ " \"cell\": {\n",
+ " \"/\": \"baedreig6yfypxumwytzlmw4wbvwcdxcfroa4lj46qrcoxck3lptmfa5y2u\"\n",
+ " },\n",
+ " \"path\": [\n",
+ " \"argument\"\n",
+ " ]\n",
+ " }\n",
+ " }\n",
+ " },\n",
+ " \"children\": []\n",
+ " }\n",
+ " }\n",
+ ": docImpl: \n",
+ " entityId={\"/\":\"baedreih6pdtzk4xvjuxlsp3htrugtipgndn6xtd4cwbdr6wgscb6fh2nlu\"}\n",
+ " space=common-knowledge\n",
+ " sourceCell=[object Object]\n",
+ " ephemeral=false\n",
+ " value: {\n",
+ " \"$NAME\": \"Exercise Tracker\",\n",
+ " \"$UI\": {\n",
+ " \"type\": \"vnode\",\n",
+ " \"name\": \"common-iframe\",\n",
+ " \"props\": {\n",
+ " \"src\": \"\\n\\n\\n\\n\\n\\n\\nExercise Tracker\\n\\n\\n\\n\\n\\n\",\n",
+ " \"$context\": {\n",
+ " \"$alias\": {\n",
+ " \"cell\": {\n",
+ " \"/\": \"baedreiedh6j73hk3cx65jrw2d26h3spgidiqkcv436ehocl5dqh2mavyvy\"\n",
+ " },\n",
+ " \"path\": [\n",
+ " \"argument\"\n",
+ " ]\n",
+ " }\n",
+ " }\n",
+ " },\n",
+ " \"children\": []\n",
+ " }\n",
+ " }\n",
+ ": docImpl: \n",
+ " entityId={\"/\":\"baedreiel5fi755jovxnou5bpdsuvcylibayiaaipaysi2tiip26ik3b6ya\"}\n",
+ " space=common-knowledge\n",
+ " sourceCell=[object Object]\n",
+ " ephemeral=false\n",
+ " value: {\n",
+ " \"$NAME\": \"Multi-User Chat App\",\n",
+ " \"$UI\": {\n",
+ " \"type\": \"vnode\",\n",
+ " \"name\": \"common-iframe\",\n",
+ " \"props\": {\n",
+ " \"src\": \"\\n\\n\\n\\n\\n\\n\\nMulti-User Chat App\\n\\n\\n\\n\\n\\n\",\n",
+ " \"$context\": {\n",
+ " \"$alias\": {\n",
+ " \"cell\": {\n",
+ " \"/\": \"baedreiewvjjphy2id2dqsubt4aim66b2sleps4cs7n2mae6hftgthgcc2a\"\n",
+ " },\n",
+ " \"path\": [\n",
+ " \"argument\"\n",
+ " ]\n",
+ " }\n",
+ " }\n",
+ " },\n",
+ " \"children\": []\n",
+ " }\n",
+ " }\n",
+ ": docImpl: \n",
+ " entityId={\"/\":\"baedreid2qh2k6gcie4ezckqukkplpeqlz7jnxirt7aehxdcw4sfg5aszh4\"}\n",
+ " space=common-knowledge\n",
+ " sourceCell=[object Object]\n",
+ " ephemeral=false\n",
+ " value: {\n",
+ " \"$NAME\": \"Origami Visualizer\",\n",
+ " \"$UI\": {\n",
+ " \"type\": \"vnode\",\n",
+ " \"name\": \"common-iframe\",\n",
+ " \"props\": {\n",
+ " \"src\": \"\\n\\n\\n\\n\\n\\n\\nOrigami Visualizer\\n\\n\\n\\n\\n\\n\",\n",
+ " \"$context\": {\n",
+ " \"$alias\": {\n",
+ " \"cell\": {\n",
+ " \"/\": \"baedreib3n7bscm5fre6c5rskgpa7xzbd6fx6g3fbmiv7fwor5nsqk4s5au\"\n",
+ " },\n",
+ " \"path\": [\n",
+ " \"argument\"\n",
+ " ]\n",
+ " }\n",
+ " }\n",
+ " },\n",
+ " \"children\": []\n",
+ " }\n",
+ " }\n"
+ ]
+ }
+ ],
+ "source": [
+ "import { getDocByEntityId } from \"../runner/src/doc-map.ts\";\n",
+ "\n",
+ "// lets load and look at each charm in the charms arrays\n",
+ "const allCharmsArray: CellLink[] = charmsDoc.value;\n",
+ "allCharmsArray.forEach(async (charmRef) => {\n",
+ " // pull out the entity id from each element of the array\n",
+ " const charmCellId: EntityId = charmRef[\"cell\"];\n",
+ "\n",
+ " // create the empty docImpl that has the EntityId in it\n",
+ " const charmCellDoc = getDocByEntityId(SPACE, charmCellId);\n",
+ " logDocImpl(charmCellDoc);\n",
+ "});"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "c29819f8-0a4b-4b8c-bc8f-5c5dc677127a",
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "ename": "ReferenceError",
+ "evalue": "manager is not defined",
+ "output_type": "error",
+ "traceback": [
+ "Stack trace:",
+ "ReferenceError: manager is not defined",
+ " at :3:1"
+ ]
+ }
+ ],
+ "source": [
+ "// lets see what the $UI.props.$context.$alias is about\n",
+ "\n",
+ "//log(charm); //charm[\"$UI\"]//.props[\"$context\"][\"$alias\"]\n",
+ "manager.runPersistent(allCharmsArray[0].cell.value, undefined, \"foo\");"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "44a2494d-feab-4db1-bce1-7ec8a7fc98be",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "382aeb8b-d07f-468e-a37f-63b32f85455c",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f03a0bcb-6c5d-47f1-a0d5-3c3c211da20e",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6231d245-6e6e-4929-aae0-6d808a7153f4",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "dd377c84-2a3e-4b24-91b0-083e623caa61",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Deno",
+ "language": "typescript",
+ "name": "deno"
+ },
+ "language_info": {
+ "codemirror_mode": "typescript",
+ "file_extension": ".ts",
+ "mimetype": "text/x.typescript",
+ "name": "typescript",
+ "nbconvert_exporter": "script",
+ "pygments_lexer": "typescript",
+ "version": "5.7.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/docs/common/COMPONENTS.md b/docs/common/COMPONENTS.md
new file mode 100644
index 000000000..5e5059bd9
--- /dev/null
+++ b/docs/common/COMPONENTS.md
@@ -0,0 +1,186 @@
+# ct-button
+
+A styled button component matching the regular `button` API. Pass a handler to the `onClick` prop to bind it.
+
+```tsx
+type InputSchema = { count: Cell };
+type OutputSchema = { count: Cell };
+
+const MyRecipe = recipe("MyRecipe", ({ count }) => {
+ const handleClick = handler }>((_event, { count }) => {
+ count.set(count.get() + 1);
+ });
+
+ return {
+ [UI]: ,
+ count,
+ };
+});
+```
+
+Notice how handlers are bound to the cell from the input schema _in_ the VDOM declaration? That's partial application of the state, the rest of the state (the actual event) comes through as the (unused) `_event` in the handler. This way you can merge the discrete updates from events with the reactive cells that are always changing values.
+
+(For even more detail, see `HANDLERS.md`)
+
+# ct-input
+
+Some components can work with a cell directly, as well as using handlers.
+
+```tsx
+type InputSchema = { value: Cell };
+type OutputSchema = { value: Cell };
+
+const MyRecipe = recipe("MyRecipe", ({ value }) => {
+ // Here we know the type of the event and can use the `detail` to get the value
+ const handleChange = handler<{ detail: { value: string } }, { value: Cell }>((event, { value }) => {
+ value.set(event.detail.value);
+ });
+
+ return {
+ [UI]:
+
+
+
,
+ };
+});
+```
+
+These two inputs are functionally equivalent. They both update the cell with the value from the input event, providing a cell via the `$` prefix allows the component to call `.set()` on the cell internally - reducing boilerplate.
+
+### Validation
+
+One reason to prefer a handler is validation of the value. This is a good idea, but you can _also_ consider using two cells. A raw input cell and a validated cell derived from the former, e.g.
+
+```tsx
+type InputSchema = { rawValue: Cell };
+type OutputSchema = { validatedValue: string | null };
+
+const MyRecipe = recipe("MyRecipe", ({ rawValue }) => {
+ // Example 1: Using full event object
+ // Here we destructure the event for convenience/brevity
+ const handleChange = handler<{ detail: { value: string } }, { rawValue: Cell }>(({ detail: { value } }, { rawValue }) => {
+ rawValue.set(value);
+ });
+
+ // Example 2: Destructuring specific event properties (often cleaner)
+ const handleChangeDestructured = handler<{ detail: { value: string } }, { rawValue: Cell }>(({ detail: { value } }, { rawValue }) => {
+ rawValue.set(value);
+ });
+
+ // Example 3: When event data isn't needed
+ const handleReset = handler }>((_ , { rawValue }) => {
+ rawValue.set("");
+ });
+
+ const validatedValue = derive(rawValue, v => v.length > 0 ? v : null);
+
+ return {
+ [UI]:
+
+ Reset
+
,
+ validatedValue
+ };
+});
+```
+
+# ct-list
+
+When working with a list of objects, of any kind, if they have `title` properties you can display and manage them via a `ct-list` component.
+
+```tsx
+type Item = { title: string };
+type ListSchema = { items: Cell };
+
+const MyRecipe = recipe("MyRecipe", ({ items }) => {
+ return {
+ [UI]:
+
+
,
+ items,
+ };
+});
+```
+
+# ct-message-input
+
+This component bundles an input and a button to 'send a message' or 'add an item to a list' which is a common pattern. You don't need to worry about the value until submission time.
+
+```tsx
+const addItem = handler<
+ { detail: { message: string } },
+ { list: { title: string; items: Cell } }
+>(({ detail: { message } }, { list }) => {
+ const item = message?.trim();
+ if (item) list.items.push({ title: item });
+});
+
+// ...
+
+
+````
+
+# ct-outliner
+
+`ct-outliner` is conceptually similar to `ct-list` except it works on a tree data structure. Below is a demonstration of the minimal use, see `page.tsx` for a more complete example.
+
+This example also demonstrates verbose specification of more complex types.
+
+```tsx
+import {
+ h,
+ derive,
+ handler,
+ ifElse,
+ NAME,
+ recipe,
+ str,
+ UI,
+ OpaqueRef,
+ Cell,
+ Default,
+ Opaque,
+} from "commontools";
+
+type Charm = any;
+
+type OutlinerNode = {
+ body: Default;
+ children: Default;
+ attachments: Default[], []>;
+};
+
+type Outliner = {
+ root: OutlinerNode;
+};
+
+type PageResult = {
+ outline: Cell<
+ Default
+ >;
+};
+
+export type PageInput = {
+ outline: Cell;
+};
+
+export default recipe(
+ "Outliner Page",
+ ({ outline }) => {
+ return {
+ [NAME]: "Outliner",
+ [UI]: (
+
+
+
+
+ ),
+ outline,
+ };
+ },
+);
+```
diff --git a/docs/common/CT.md b/docs/common/CT.md
new file mode 100644
index 000000000..9788a90f8
--- /dev/null
+++ b/docs/common/CT.md
@@ -0,0 +1,251 @@
+# CT Binary Usage
+
+## Essential Commands (90% of usage)
+
+**Standard Parameters** (use these for all commands):
+- Identity: `claude.key` (created automatically if missing)
+- API URL: `https://toolshed.saga-castor.ts.net/`
+- Space: Your choice (e.g., `2025-wiki`, `2024-07-15-claude-dev`)
+
+When gathering this information from the user, consider saving `.common.json` and reading it next time to shortcut the process.
+
+```bash
+# READ data from charm
+./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [your-space] --charm [charm-id] [path]
+
+# SET data directly in charm (value via stdin)
+echo '[value]' | ./dist/ct charm set --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [your-space] --charm [charm-id] [path]
+
+# CALL charm handler (JSON via stdin)
+echo '[json-data]' | ./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [your-space] --charm [charm-id] [handler-name]
+
+# LIST charms in space
+./dist/ct charm ls --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [your-space]
+
+# DEPLOY new charm
+./dist/ct charm new --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [your-space] [recipe-path]
+```
+
+**When to use each:**
+- **GET**: Read any data from charm
+- **SET**: Directly modify specific values (simple, fast)
+- **CALL**: Use charm's handlers (for complex operations, validation, side effects)
+- **LS**: Discover what charms exist in a space
+- **NEW**: Deploy a recipe as a new charm
+
+**Path Examples:**
+```bash
+# Read specific values
+./dist/ct charm get [params] --charm [id] title
+./dist/ct charm get [params] --charm [id] items/0/name
+./dist/ct charm get [params] --charm [id] config/database/host
+
+# Set specific values
+echo '"New Title"' | ./dist/ct charm set [params] --charm [id] title
+echo 'true' | ./dist/ct charm set [params] --charm [id] items/0/done
+echo '{"host": "localhost"}' | ./dist/ct charm set [params] --charm [id] config/database
+
+# Call handlers
+echo '{"title": "New item"}' | ./dist/ct charm call [params] --charm [id] addItem
+echo '{"key": "page", "value": "content"}' | ./dist/ct charm call [params] --charm [id] update
+```
+
+## Setup & Deployment
+
+### Initial Setup
+
+**Check CT binary:**
+- Run `ls -la ./dist/ct`
+- If missing: Run `deno task build-binaries --cli-only` (takes a few minutes)
+- Verify with `./dist/ct --help`
+
+**Identity management:**
+- Run `ls -la claude.key`
+- If missing: Run `./dist/ct id new > claude.key`
+
+**Recipe development setup:**
+- Navigate to your recipes repository
+- Run `./dist/ct init` to set up TypeScript types for recipe development
+- This creates/updates tsconfig.json and provides proper type definitions
+
+### Environment Variables (Optional)
+
+Set these to shorten commands:
+- `CT_API_URL="https://toolshed.saga-castor.ts.net/"`
+- `CT_IDENTITY="./claude.key"`
+
+**Important:** For `*.ts.net` URLs, you must be connected to the CT Tailnet. Commands will hang or timeout if not connected.
+
+### Recipe Development Commands
+
+```bash
+# Test recipe syntax
+./dist/ct dev [recipe-path] --no-run
+
+# Test recipe execution
+./dist/ct dev [recipe-path]
+
+# Get recipe source from deployed charm
+./dist/ct charm getsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [output-path]
+
+# Update recipe source in deployed charm
+./dist/ct charm setsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [recipe-path]
+```
+
+## Advanced Features
+
+### Working with Input vs Result Cells
+
+**Result Cell** (default): Contains the computed output/result of a charm
+**Input Cell**: Contains the input parameters/arguments passed to a charm
+
+```bash
+# Access input cell instead of result cell
+./dist/ct charm get [params] --charm [id] [path] --input
+echo '[value]' | ./dist/ct charm set [params] --charm [id] [path] --input
+```
+
+### Charm Inspection and Debugging
+
+```bash
+# Inspect charm details
+./dist/ct charm inspect --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id]
+
+# Get raw JSON output
+./dist/ct charm inspect [params] --charm [id] --json
+```
+
+### Linking Charms (Core Concept)
+
+**Linking is how you connect charms together** - it's the primary way to build complex applications from simple recipes.
+
+**Basic Syntax:** `ct charm link [source] [target]/[field]`
+
+```bash
+# Link charm output to another charm's input
+./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] [source-charm]/[field] [target-charm]/[input-field]
+
+# Link well-known ID to charm input
+./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] [well-known-id] [target-charm]/[input-field]
+```
+
+**How Links Work:**
+- **Data Flow**: `charmA/fieldName → charmB/inputField` means `charmB.input.inputField = charmA.result.fieldName`
+- **Source Side**: Reads from a charm's computed result/output field
+- **Target Side**: Writes to another charm's input parameters
+- **Live Updates**: When source charm updates, target automatically receives new data
+- **Reactive**: Changes propagate through the entire linked chain automatically
+
+**Common Patterns:**
+
+```bash
+# Email list → Feed into document generator
+./dist/ct charm link [email-list-charm]/emails [doc-gen-charm]/emailData
+
+# Search results → Feed into summarizer
+./dist/ct charm link [search-charm]/results [summarizer-charm]/content
+
+# Well-known charm list → Feed into page manager
+./dist/ct charm link baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [page-manager]/allCharms
+```
+
+**Why Links Matter:**
+- **Composability**: Build complex workflows from simple parts
+- **Separation of Concerns**: Each charm does one thing well
+- **Data Synchronization**: No manual copying between charms
+- **Scalability**: Add new functionality by linking new charms
+
+### Primary Operations
+
+**Call handlers** (most common for user actions):
+```bash
+# Call a handler with no arguments
+./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] handlerName
+
+# Call a handler with JSON arguments
+./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] handlerName '{"key": "value"}'
+```
+
+**Get/Set data** (direct field access):
+```bash
+# Get a field from charm result
+./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] fieldName
+
+# Get nested field with path syntax
+./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] data/users/0/email
+
+# Set a field in charm result
+echo '"New Value"' | ./dist/ct charm set --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] fieldName
+
+# Set a field in charm input (use --input flag)
+echo '{"config": "value"}' | ./dist/ct charm set --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] config --input
+```
+
+### Space Visualization
+
+**Map command** (see charm connections):
+```bash
+# ASCII map of all charms and links
+./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space]
+
+# Graphviz DOT format for visualization tools
+./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --format dot
+```
+
+### Advanced Operations
+
+**Apply inputs** (bulk input replacement - rarely needed):
+```bash
+# Replace all inputs at once (prefer call/set for most cases)
+echo '{"input1": "value1", "input2": "value2"}' | ./dist/ct charm apply --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id]
+```
+
+### Visualizing Space Maps
+
+```bash
+# ASCII format (default)
+./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space]
+
+# Graphviz DOT format
+./dist/ct charm map [params] --space [space] --format dot
+
+# Render to image (requires Graphviz: brew install graphviz)
+./dist/ct charm map [params] --space [space] --format dot | dot -Tpng -o map.png
+```
+
+**Online visualization:**
+1. Run: `./dist/ct charm map [params] --space [space] --format dot`
+2. Copy the DOT output
+3. URL encode and append to: `https://dreampuf.github.io/GraphvizOnline/?engine=dot#`
+4. Or paste directly into: `https://dreampuf.github.io/GraphvizOnline/`
+
+## Well-Known IDs
+
+CommonTools provides well-known IDs for accessing system-level data:
+
+**allCharms (Charms List):**
+- ID: `baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye`
+- Contains a list of all charms in the current space
+- Common usage: Link to charm inputs that need to access the full charm list
+- Example: `./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [target-charm]/allCharms`
+
+## Important Notes
+
+- **Recipes**: Typically in a separate repository, not in the labs repo
+- **Paths**: Always use absolute paths or full relative paths to recipes
+- **Charm IDs**: Start with "bafy" and are long content hashes
+- **Environment variables**: CT_API_URL and CT_IDENTITY can simplify commands
+- **Tailnet**: For `*.ts.net` URLs, ensure you're connected to the CT Tailnet
+
+## Error Handling
+
+**If commands fail:**
+- Check network connectivity and Tailnet connection
+- Verify identity file permissions
+- Ensure space name and charm ID are correct
+- For recipe syntax errors, use `./dist/ct dev [recipe] --no-run`
+
+**Path Format:**
+- Use forward slashes: `config/database/host`
+- Array indices are numeric: `users/0/profile`
+- Support nested objects: `data/items/2/metadata/tags`
diff --git a/docs/common/HANDLERS.md b/docs/common/HANDLERS.md
new file mode 100644
index 000000000..651d6bd7a
--- /dev/null
+++ b/docs/common/HANDLERS.md
@@ -0,0 +1,332 @@
+# CommonTools Handler Patterns Guide
+
+## Handler Function Structure
+
+Handlers in CommonTools follow a specific three-parameter pattern:
+
+```typescript
+const myHandler = handler(
+ eventSchema, // What data comes from UI events
+ stateSchema, // What data the handler needs to operate on
+ handlerFunction // The actual function that executes
+);
+
+// or
+
+type EventSchema = ...
+type StateSchema = ...
+const myHandler = handler(handlerFunction);
+```
+
+## Common Handler Patterns
+
+### 1. Simple Click Handler (No Event Data)
+
+```typescript
+const increment = handler, { count: Cell }>(
+ (_event, { count }) => {
+ count.set(count.get() + 1);
+ }
+);
+
+// Usage in UI
+
+ +1
+
+```
+
+### 2. Event Data Handler (Form Input, etc.)
+
+```typescript
+const updateTitle = handler<
+ { detail: { value: string } },
+ { title: Cell }
+>(
+ ({ detail }, { title }) => {
+ title.set(detail.value);
+ }
+);
+
+// Usage in UI
+
+
+// OR, bind directly to cell (see component docs for when this is available)
+
+
+```
+
+## Common TypeScript Issues & Fixes
+
+### Handler Function Parameters
+- **First parameter**: Event data (from UI interactions)
+- **Second parameter**: State data (destructured from state schema)
+- **Parameter naming**: Use descriptive names or `_` prefix for unused parameters
+
+Notice how handlers are bound to the cell from the input schema _in_ the VDOM declaration? That's partial application of the state, the rest of the state (the actual event) comes through as `e` in the handler. This way you can merge the discrete updates from events with the reactive cells that are always changing values.
+
+#### Event Parameter Patterns
+
+You can handle the event parameter in different ways depending on your needs:
+
+```typescript
+// Option 1: Destructure specific event properties (most common)
+const updateTitle = handler<
+ { detail: { value: string } },
+ { title: Cell }
+>(
+ ({ detail }, { title }) => {
+ title.set(detail.value);
+ }
+);
+
+// Option 2: Use full event object when you need multiple properties
+const handleComplexEvent = handler<
+ { detail: { value: string }, target: HTMLElement, timestamp: number },
+ { data: Cell }
+>(
+ (e, { data }) => {
+ // Access multiple event properties
+ console.log('Event timestamp:', e.timestamp);
+ console.log('Target element:', e.target);
+ data.set({ value: e.detail.value, element: e.target.tagName });
+ }
+);
+
+// Option 3: Use underscore when event data isn't needed
+const simpleIncrement = handler, { count: Cell }>(
+ (_, { count }) => {
+ count.set(count.get() + 1);
+ }
+);
+```
+
+## Handler Invocation Patterns
+
+### State Passing
+When invoking handlers in UI, pass an object that matches your state schema:
+
+```typescript
+// Handler definition state schema
+{
+ items: any[],
+ currentPage: string
+}
+
+// Handler invocation - must match state schema
+onClick={myHandler({ items: itemsArray, currentPage: currentPageString })}
+```
+
+### Event vs Props Confusion
+- **Event type**: Describes data coming FROM the UI event
+- **State type**: Describes data the handler needs to ACCESS
+- **Invocation object**: Must match the state type, NOT the event type
+
+## Debugging Handler Issues
+
+### Type Mismatches
+1. Check that handler invocation object matches state type
+2. Verify event type matches actual UI event structure
+3. Ensure destructuring in handler function matches types
+
+### Runtime Issues
+1. Use `console.log` in handler functions to debug
+2. Check that state objects have expected properties
+3. Verify UI events are firing correctly
+
+## Best Practices
+
+1. **Use meaningful parameter names**: `(formData, { items, title })` not `(event, state)`
+2. **Keep event types minimal**: Often `Record` for simple clicks
+3. **Make state types explicit**: Always define the exact properties needed
+4. **Match invocation to state type**: The object passed to handler() should match state type exactly
+5. **Prefer descriptive handler names**: `updateItemTitle` not `handleUpdate`
+
+## Examples by Use Case
+
+### Counter/Simple State
+```typescript
+const increment = handler, { count: Cell }>(
+ (_, { count }) => count.set(count.get() + 1)
+);
+```
+
+### Form/Input Updates
+```typescript
+const updateField = handler<
+ { detail: { value: string } },
+ { fieldValue: Cell }
+>(
+ ({ detail }, { fieldValue }) => fieldValue.set(detail.value)
+);
+```
+
+### List/Array Operations
+```typescript
+const addItem = handler<
+ { message: string },
+ { items: Cell> }
+>(
+ ({ message }, { items }) =>
+ items.push({ title: message, done: false })
+);
+```
+
+### Complex State Updates
+```typescript
+const updatePageContent = handler<
+ { detail: { value: string } },
+ { pages: Record, currentPage: string }
+>(
+ ({ detail }, { pages, currentPage }) => {
+ pages[currentPage] = detail.value;
+ }
+);
+```
+
+## Advanced Patterns
+
+### Handler Composition
+```typescript
+// Base handlers for reuse
+const createTimestamp = () => Date.now();
+
+const addItemWithTimestamp = handler<
+ { title: string },
+ { items: Cell> }
+>(
+ ({ title }, { items }) => {
+ items.push({
+ title,
+ done: false,
+ createdAt: createTimestamp()
+ });
+ }
+);
+```
+
+### Conditional State Updates
+```typescript
+const toggleItem = handler<
+ Record,
+ { item: { id: string }, items: Cell> }
+>(
+ (_, { item, items }) => {
+ const index = items.get().findIndex(i => i.id === item.id);
+ if (index !== -1) {
+ items.get()[index].done = !items.get()[index].done;
+ }
+ }
+);
+```
+
+#### Improving Event Typing
+
+While `Record` works for simple handlers that don't use event data, better event typing improves development experience and catches errors:
+
+```typescript
+// Better: Define specific event interfaces when you need event data
+interface ClickEvent {
+ target: HTMLElement;
+ shiftKey?: boolean;
+ metaKey?: boolean;
+}
+
+interface CustomEvent {
+ detail: T;
+ target: HTMLElement;
+}
+
+// Use specific types for better IntelliSense and error catching
+const handleItemClick = handler<
+ ClickEvent,
+ { items: Cell> }
+>(
+ ({ target, shiftKey }, { items }) => {
+ const itemId = target.getAttribute('data-item-id');
+ if (itemId) {
+ // Type-safe access to event properties
+ const shouldSelectMultiple = shiftKey;
+ // ... handler logic
+ }
+ }
+);
+
+// For custom events with specific detail shapes
+const handleFormSubmit = handler<
+ CustomEvent<{ formData: Record }>,
+ { submissions: Cell }
+>(
+ ({ detail }, { submissions }) => {
+ // detail.formData is properly typed
+ submissions.push(detail.formData);
+ }
+);
+```
+
+## Common Pitfalls
+
+### 1. Type Mismatch
+```typescript
+// ❌ Wrong: State type doesn't match invocation
+const handler = handler, { count: number }>(
+ (_, { count }) => { ... }
+);
+
+// Invocation passes wrong shape
+onClick={handler({ value: 5 })} // Should be { count: 5 }
+```
+
+### 2. Event Type Over-specification
+```typescript
+// ❌ Wrong: Over-complicated event type for simple clicks
+const handler = handler<{
+ target: object,
+ currentTarget: object
+}, any>(
+ (ev, state) => { ... }
+);
+
+// ✅ Better: Simple clicks rarely need event data
+const handler = handler, any>(
+ (ev, state) => { ... }
+);
+```
+
+### 3. Mutation vs Immutability
+```typescript
+// ❌ Wrong: Direct assignment to non-cell state
+(_, { title }) => {
+ title = newValue; // This won't work
+}
+
+// ✅ Right: Use cell methods for reactive state
+(_, { title }) => {
+ title.set(newValue); // For cells
+}
+
+// ✅ Right: Mutate arrays/objects directly for non-cell state
+(_, { items }) => {
+ items.push(newItem); // For regular arrays
+}
+```
+
+## Debugging Handlers
+
+### Console-based Debugging
+```typescript
+// In recipes, use console logging for debugging
+const addItem = handler<
+ { detail: { value: string } },
+ { items: Cell }
+>(
+ ({ detail }, { items }) => {
+ console.log("Adding item:", detail.value);
+ console.log("Current items:", items.get());
+ items.push({ title: detail.value, done: false });
+ console.log("Updated items:", items.get());
+ }
+);
+```
diff --git a/docs/common/RECIPES.md b/docs/common/RECIPES.md
new file mode 100644
index 000000000..0d472b20f
--- /dev/null
+++ b/docs/common/RECIPES.md
@@ -0,0 +1,659 @@
+# Recipe Framework Documentation
+
+## Overview
+
+The Recipe Framework is a declarative, reactive system for building
+integrations and data transformations. It uses a component-based architecture
+where recipes are autonomous modules that can import, process, and export data.
+
+## Core Concepts
+
+### Recipe
+
+A recipe is the fundamental building block, defined using the `recipe('My Recipe', (input) => {})`
+function. It takes two types and two parameters:
+
+- Types: define Input and Output relationships for composition with other recipes
+ - Properties such as `[UI]` and `[NAME]` do not have to be explicitly included in the output type
+- Parameters: a descriptive name and a function that receives the inputs and returns
+ outputs
+
+### Types and Runtime Safety
+
+The framework uses a TypeScript-first approach for defining types in recipes, handlers, and lifted functions:
+
+```typescript
+const myHandler = handler(
+ (input, state) => {/* ... */},
+);
+```
+
+This TypeScript-first approach provides several benefits:
+
+- Full TypeScript type inference and checking
+- Clean, readable type definitions
+- Integration with IDE tooling
+- Express Cell vs readonly value requirements directly in types
+
+Importantly, the framework automatically handles type validation and serialization using the CTS (Common Tools TypeScript) system. This gives you runtime validation, self-documentation, and serialization support. You can express your desire for Cells vs readonly values directly in TypeScript types, and the system will fulfill the values by reflecting on the types.
+
+### Data Flow
+
+The framework uses a reactive programming model:
+
+- `cell`: Represents a reactive state container that can be updated and observed
+- `derive`: Creates a derived value that updates when its dependencies change
+- `lift`: Similar to derive, but lifts a regular function to work on reactive values
+ - `derive(param, function)` is an alias to `lift(function)(param)`
+- `handler`: Creates an event handler that always fires with up-to-date dependencies (possibly mutating them)
+ - takes two parameters, an event and state (bound variables)
+ - e.g. ``
+
+### Handlers vs Reactive Functions
+
+There are important differences between the types of functions in the framework:
+
+#### Handlers
+
+(For even more detail, see `HANDLERS.md`)
+
+Handlers are functions that declare node types in the reactive graph that
+respond to events:
+
+- Created with `handler()` function
+- Use `Cell<>` to indicate you want a reactive value (for mutation, usually):
+
+ ```typescript
+ const updateCounter = handler }>(
+ (input, { count }) => {
+ // Now count is a Cell instance
+ count.set(count.get() + 1);
+ },
+ );
+ ```
+
+- Instantiated in recipes by passing parameters:
+
+ ```typescript
+ const stream = definedHandler({ cell1, cell2 });
+ ```
+
+- Return a stream that can be:
+ - Passed to JSX components as event handlers (e.g., `onClick={stream}`)
+ - Returned by a recipe for external consumption
+ - Passed to another handler which can call `.send(...)` on it to generate
+ events
+- Can update cells and trigger side effects
+- Support async operations for data processing
+- React to outside events (user interactions, API responses)
+- Cannot directly call built-in functions like `llm`
+
+#### Reactive Functions (lift/derive)
+
+- `lift`: Declares a reactive node type that transforms data in the reactive
+ graph
+
+ ```typescript
+ const transformData = lift(
+ ({ value, multiplier }: { value: number, multiplier: number }) => value * multiplier,
+ );
+ ```
+
+ - When instantiated, it inserts a reactive node in the graph:
+
+ ```typescript
+ const newCell = liftedFunction({ cell1, cell2 });
+ ```
+
+ - The result is a proxy cell that can be further referenced:
+
+ ```typescript
+ const compound = { data: newCell.field };
+ ```
+
+- `derive`: A convenience wrapper around lift:
+
+ ```typescript
+ // These are equivalent:
+ const result1 = derive({ x, y }, ({ x, y }) => x + y);
+ const result2 = lift(({ x, y }) => x + y)({ x, y });
+ ```
+
+- React to data changes within the reactive graph
+- Cannot directly call built-in functions like `llm`
+
+### Data as Futures
+
+Within recipes, functions cannot directly read values - they can only pass
+references to other nodes. Think of the data passed to a recipe as "futures" -
+promises of values that will be available when the program runs.
+
+The system allows accessing fields using the dot notation (e.g., `cell.field`),
+but this doesn't actually read values - it's creating new references to future
+data.
+
+```tsx
+// This doesn't read values, it creates references:
+const data = {
+ firstName: user.firstName,
+ lastName: user.lastName,
+};
+```
+
+### UI Components
+
+Recipes can include UI components using JSX syntax:
+
+- Common components like `ct-input`, `ct-hstack`, `ct-vstack`
+- Integration-specific components like `common-google-oauth`
+- Custom components can be created as needed via `const MyComponent = recipe(...)` ``
+
+### Built-in Functions
+
+Several utility functions are available:
+
+- `llm`: Makes calls to language models with parameters for system prompt, user
+ prompt, etc.
+- `fetchData`: Fetches data from URLs
+- `streamData`: Streams data from URLs
+- `ifElse`: Conditional logic for reactive flows
+
+ ```typescript
+ // Creates a reactive value that changes based on the condition
+ const message = ifElse(
+ user.isLoggedIn,
+ str`Welcome back, ${user.name}!`,
+ "Please log in to continue",
+ );
+ ```
+
+- `str`: Template literal for string interpolation with reactive values,
+ creating reactive strings
+
+ ```typescript
+ // Creates a reactive string that updates when cells change
+ const greeting =
+ str`Hello, ${user.name}! You have ${notifications.count} new messages.`;
+ ```
+
+**Important**: These built-in functions can only be called from within a recipe
+function, not from handlers, lift, or derive functions. They create nodes in the
+reactive graph and cannot be awaited directly.
+
+## JSX and Reactive Arrays
+
+The Recipe Framework has an interesting approach to handling arrays with JSX:
+
+```typescript
+{
+ items.map((item) => );
+}
+```
+
+While this looks like regular JSX mapping, in the Recipe framework, this
+actually creates mini-recipes for each item in the array, constructing a
+reactive graph. Each mapped item becomes a reactive node that updates when the
+source data changes.
+
+In the todo-list example, this pattern is used to create draggable todo items,
+where each item has its own encapsulated recipe:
+
+```typescript
+{items.map((item: TodoItem) => (
+ ({
+ [UI]: (
+
+ ),
+ })),
+ )}
+ >
+
+
+))}
+```
+
+This approach allows for efficient updates and encapsulation of item-specific
+logic.
+
+## Best Practices
+
+1. **Use CTS TypeScript Types**: Define clear TypeScript interfaces for your
+ input and output schemas. This provides runtime validation, self-documentation,
+ and compatibility with framework tooling through the CTS (Common Tools TypeScript) system.
+
+2. **Use `Cell<>` for Handler State**: When defining handler state types, use `Cell<>` for properties that need to be updated. This gives you direct access to the Cell methods like `.set()` and `.get()`.
+
+3. **Avoid All Direct Conditionals in Recipes**: Never use direct if statements,
+ ternary operators, or any other conditionals inside a recipe function - they
+ won't work properly because they immediately evaluate data instead of
+ creating reactive nodes:
+
+ ```typescript
+ // DON'T DO THIS - if statements don't work in recipes
+ const result = emails.map((email) => {
+ if (email.hasContent) { // This won't work!
+ return processEmail(email);
+ } else {
+ return { email, empty: true };
+ }
+ });
+
+ // DON'T DO THIS EITHER - ternary operators also don't work
+ const tableHeader = (
+
+
Name
+ {settings.showDetails ?
Details
: null} // This won't work!
+
+ );
+
+ // DON'T DO THIS - ternaries in string templates don't work
+ const prompt = str`
+ Process this data
+ ${
+ settings.includeTimestamp ? "Include timestamps" : "No timestamps"
+ } // This won't work!
+ `;
+
+ // DO THIS INSTEAD - use ifElse function for conditionals in data flow
+ const result = emails.map((email) =>
+ ifElse(
+ email.hasContent,
+ () => processEmail(email),
+ () => ({ email, empty: true }),
+ )
+ );
+
+ // USE ifElse IN JSX TOO
+ const tableHeader = (
+
+
Name
+ {ifElse(settings.showDetails,
Details
, null)}
+
+ );
+
+ // USE ifElse IN STRING TEMPLATES
+ const includeTimestampText = ifElse(
+ settings.includeTimestamp,
+ "Include timestamps",
+ "No timestamps",
+ );
+ const prompt = str`
+ Process this data
+ ${includeTimestampText}
+ `;
+
+ // WHEN APPROPRIATE - skip conditionals entirely
+ // and let LLM handle edge cases:
+ const result = emails.map((email) => {
+ const processed = processWithLLM(email);
+ return { email, result: processed };
+ });
+ ```
+
+4. **Reference Data Instead of Copying**: When transforming data, reference the
+ original objects rather than copying all their properties. This maintains
+ reactivity and creates cleaner code:
+
+ ```typescript
+ // DO THIS: Reference the original data
+ const processedItems = items.map((item) => ({
+ originalItem: item, // Direct reference
+ processed: processItem(item),
+ }));
+
+ // NOT THIS: Spread/copy all properties
+ const processedItems = items.map((item) => ({
+ id: item.id, // Copying each field
+ name: item.name, // breaks the reactive
+ date: item.date, // connection to the
+ // ... more fields // original data
+ processed: processItem(item),
+ }));
+ ```
+
+5. **Use Reactive String Templates**: Use the `str` template literal to create
+ reactive strings that update when their inputs change:
+
+ ```typescript
+ const message =
+ str`Hello ${user.name}, you have ${notifications.count} notifications`;
+ ```
+
+6. **Keep Logic Inside Recipes**: Place as much logic as possible inside recipe
+ functions or the `map` function. This creates a cleaner reactive system where
+ data flow is transparent.
+
+7. **Leverage Framework Reactivity**: Let the framework track changes and
+ updates. Avoid manually tracking which items have been processed or creating
+ complex state management patterns.
+
+8. **Composition**: Build complex flows by composing smaller recipes.
+
+9. **Minimize Side Effects**: Side effects should be managed through handlers
+ rather than directly in recipes.
+
+10. **Type Reuse**: Define types once and reuse them across recipes, handlers, and lifted functions to maintain consistency.
+
+## Type Best Practices
+
+When defining types in the Recipe Framework, follow these guidelines for best
+results:
+
+1. **Define Types as Reusable Interfaces**: Declare types as interfaces or type aliases for reuse and
+ reference:
+
+ ```typescript
+ type User = {
+ id: string;
+ name: string;
+ email?: string;
+ };
+ ```
+
+2. **Use Descriptive Type Names**: Always use clear, descriptive names that explain what the type represents:
+
+ ```typescript
+ // DO THIS
+ type UserPreferences = {
+ theme: "light" | "dark" | "system";
+ fontSize: number;
+ notifications: boolean;
+ };
+
+ // NOT THIS
+ type Config = { theme: string; size: number; alerts: boolean };
+ ```
+
+3. **Use Default<> for Sensible Defaults**: Where possible, provide sensible default values using the CTS Default<> type:
+
+ ```typescript
+ type UserSettings = {
+ theme: Default<"light" | "dark" | "system", "system">;
+ fontSize: Default;
+ notifications: Default;
+ };
+ ```
+
+4. **Compose Types Instead of Duplicating**: For complex objects, compose existing types:
+
+ ```typescript
+ type User = {
+ id: string;
+ name: string;
+ email: string;
+ };
+
+ type Post = {
+ author: User; // Reference the existing type
+ content: string;
+ timestamp: Date;
+ };
+ ```
+
+5. **Document Types with Comments**: Add JSDoc comments to types for better self-documentation:
+
+ ```typescript
+ /** User's primary email address used for notifications */
+ type Email = string;
+
+ type User = {
+ id: string;
+ name: string;
+ /** User's primary email address used for notifications */
+ email?: Email;
+ };
+ ```
+
+6. **Use Cell<> for Reactive State**: In handler state types, use `Cell<>` for properties that need direct access to Cell methods:
+
+ ```typescript
+ type HandlerState = {
+ /** Counter value that can be directly manipulated */
+ counter: Cell;
+ readonlyValue: string; // Regular values are readonly
+ };
+ ```
+
+7. **Make Optional vs Required Explicit**: Use TypeScript's optional properties (?) to clearly indicate what's required:
+
+ ```typescript
+ type User = {
+ id: string; // Required
+ name: string; // Required
+ email?: string; // Optional
+ };
+ ```
+
+8. **Use Type Composition**: Break down complex types into smaller, reusable parts:
+
+ ```typescript
+ type Address = {
+ street: string;
+ city: string;
+ zipCode: string;
+ };
+
+ type Contact = {
+ phone?: string;
+ email: string;
+ };
+
+ type User = {
+ // Basic info
+ id: string;
+ name: string;
+ // Composed types
+ address: Address;
+ contact: Contact;
+ };
+ ```
+
+9. **Use Union Types for Enums**: Define enums with union types and const assertions:
+
+ ```typescript
+ const StatusValues = ["pending", "active", "suspended", "deleted"] as const;
+ type Status = typeof StatusValues[number]; // "pending" | "active" | "suspended" | "deleted"
+
+ type User = {
+ id: string;
+ name: string;
+ status: Default; // Provide a reasonable default
+ };
+ ```
+
+## Advanced Type Concepts
+
+### TypeScript to Runtime Schema
+
+The CTS framework automatically handles your TypeScript types at runtime:
+
+```typescript
+// Define TypeScript types
+type Person = {
+ name: string;
+ age?: number;
+};
+
+// The framework automatically processes this TypeScript type
+// No manual schema definition needed - it's all handled by CTS reflection
+```
+
+### Cell Type vs Value Type
+
+When working with handlers, it's important to understand the distinction:
+
+```typescript
+// Regular state property (value type)
+{ count: number } // Handler receives the number directly (readonly)
+
+// Cell-typed property
+{ count: Cell } // Handler receives Cell for mutation
+```
+
+With Cell-typed properties, the handler function receives actual Cell instances
+with methods:
+
+- `cell.get()`: Get the current value
+- `cell.set(newValue)`: Set a new value
+- `cell.update(fn)`: Update using a function
+
+This allows for more control over state updates, including:
+
+- Batching multiple updates
+- Conditional updates based on current value
+- Handling complex state transitions
+
+## Framework Goals
+
+The primary goal of this framework is to generate "low taint code" that enables
+effective data flow analysis. In this system:
+
+- Recipes are transparent to data flow analysis
+- Functions passed to `handler` or `lift` aren't transparent (they're "tainted")
+- TypeScript types provide clean abstractions that are automatically converted to runtime validation
+- The `Cell<>` pattern maintains reactivity while allowing direct manipulation
+
+By following these principles, applications built with this framework can
+achieve predictable data flow, easier testing, and better security through data
+flow isolation.
+
+## Integration Process
+
+1. Define schemas for inputs and outputs
+2. Create cells to hold state
+3. Implement handlers for user interactions
+4. Return an object with processed data and UI components
+
+## Example Pattern
+
+```typescript
+export default recipe(
+ InputSchema,
+ OutputSchema,
+ ({ input1, input2 }) => {
+ const state = cell([]);
+
+ // Define handlers and side effects
+
+ return {
+ [NAME]: "Recipe Name",
+ [UI]: (
+ // JSX component
+ ),
+ outputField1: state,
+ outputField2: derivedValue
+ };
+ }
+);
+```
+
+## Integration Between Recipes
+
+Recipes can be composed together, where the output of one recipe serves as the
+input to another. This is done by:
+
+1. Defining a common data schema between recipes
+2. Exporting data from the source recipe
+3. Importing that data as input in the consuming recipe
+
+For example, our Email Summarizer recipe takes emails from the Gmail recipe as
+input:
+
+- Gmail recipe exports an array of emails
+- Email Summarizer recipe consumes these emails and processes them with LLM
+
+## LLM Integration
+
+The framework provides integration with language models through the `llm`
+function:
+
+```typescript
+const result = llm({
+ system: "System prompt here", // Instructions for the LLM
+ prompt: "User prompt here", // Content to process
+ // Optional parameters
+ stop: "custom stop sequence", // Stop generation at this sequence
+ max_tokens: 1000, // Max tokens to generate
+});
+```
+
+**Important restrictions**:
+
+1. The `llm` function can only be called directly within a recipe function, not
+ in handlers, lift, or derive functions
+2. You cannot `await` the result directly, as it's a node in the reactive graph
+3. To use LLM results, you need to access them through the reactive graph (e.g.,
+ via derive)
+
+The result object includes:
+
+- `result`: The generated text
+- `partial`: Streaming partial results
+- `pending`: Boolean indicating if the request is still processing
+- `error`: Any error that occurred
+
+## Reactive Processing Pattern
+
+A common pattern in recipes is:
+
+1. Initialize state using `cell()`
+2. Create derived values with `derive()`
+3. Define handlers for UI events and data processing
+4. Create async functions for complex operations
+5. Return a recipe object with UI and exported values
+
+## Example Pattern for Data Transformation Recipes
+
+```typescript
+export default recipe(
+ "Recipe Name",
+ ({ inputData, settings }) => {
+ // Initialize state
+ const processedData = cell([]);
+
+ // Process data with LLM (directly in recipe)
+ // Notice we call map() directly on the cell - inputData is a cell
+ const processedItems = inputData.map(item => {
+ return {
+ originalItem: item,
+ llmResult: llm({
+ system: "System prompt",
+ prompt: `Process this: ${item.content}`,
+ })
+ };
+ });
+
+ // Create derived value from LLM results
+ const summaries = derive(processedItems, items =>
+ items.map(item => ({
+ id: item.originalItem.id,
+ summary: item.llmResult.result || "Processing...",
+ }))
+ );
+
+ // Handler for user interactions
+ const refreshData = handler }>(
+ (_, state) => {
+ // Update state based on user action
+ // Note: Cannot call llm() directly here
+ }
+ );
+
+ return {
+ [NAME]: "Recipe Name",
+ [UI]: (
+ // JSX UI component
+ ),
+ processedData,
+ };
+ }
+);
+```
diff --git a/docs/future-tasks/ast.md b/docs/future-tasks/ast.md
new file mode 100644
index 000000000..09f8823cf
--- /dev/null
+++ b/docs/future-tasks/ast.md
@@ -0,0 +1,675 @@
+# TypeScript AST Transformers Guide
+
+This guide covers two main transformers that enable reactive programming
+patterns:
+
+1. **OpaqueRef Transformer** - Transforms operations on `OpaqueRef` types to use
+ reactive primitives
+2. **Schema Transformer** - Converts TypeScript type definitions to JSON Schema
+ at compile time
+
+## Core Concept: Opt-in Transformation
+
+Both transformers require the `/// ` directive at the top of files
+to be transformed:
+
+```typescript
+///
+import { cell, derive, ifElse, toSchema } from "commontools";
+```
+
+Without this directive, the transformers will not modify your code.
+
+## OpaqueRef Transformer
+
+### What is OpaqueRef?
+
+OpaqueRef is a type representing reactive values in CommonTools. It wraps:
+
+- The actual value type (e.g., `string`, `number`, `{ name: string }`)
+- Methods for reactivity (`.get()`, `.set()`, etc.) used inside
+ lift/handler/derive functions
+
+```typescript
+const count = cell(0); // count is OpaqueRef
+```
+
+### Important Scope Limitation
+
+**OpaqueRef transformations only apply within JSX expressions.** Statement-level
+transformations (like if statements, loops, etc.) are not supported because they
+require complex control flow analysis and handling of side effects.
+
+```typescript
+// ✅ Transformed - JSX expression context
+
{count + 1}
; // →
{derive(count, _v => _v + 1)}
+
+// ❌ Not transformed - statement context
+if (count > 5) { // Statements with OpaqueRef are not transformed
+ console.log("High");
+}
+```
+
+### Core Transformation Patterns (JSX Expression Context Only)
+
+The following transformations apply **only within JSX expressions**. OpaqueRef
+operations in regular TypeScript statements are not transformed.
+
+#### 1. Binary Operations in JSX
+
+Operations on OpaqueRef values inside JSX are wrapped in `derive()`:
+
+```typescript
+// Input - JSX context
+
+```
+
+Supported operators: `+`, `-`, `*`, `/`, `%`, `>`, `<`, `>=`, `<=`, `==`, `===`,
+`!=`, `!==`
+
+#### 2. Ternary Conditionals in JSX
+
+When a ternary operator's condition is an OpaqueRef inside JSX, it transforms to
+`ifElse()`:
+
+```typescript
+// Input - JSX context
+
{isActive ? "on" : "off"}
+
+// Output
+
{ifElse(isActive, "on", "off")}
+```
+
+Note: This transformation only occurs when the condition (`isActive`) is an
+OpaqueRef type.
+
+#### 3. Property Access and Method Calls in JSX
+
+When accessing properties or calling methods on OpaqueRef values inside JSX:
+
+```typescript
+// Input - JSX context
+
+```
+
+Key principle: Direct property access on an OpaqueRef object returns another
+OpaqueRef, while operations on the value require `derive()`.
+
+#### 4. Direct OpaqueRef References in JSX
+
+Direct OpaqueRef references inside JSX are preserved as-is, allowing the UI
+framework to handle reactivity:
+
+```typescript
+// Input & Output (no transformation needed)
+
{count}
+{user.name}
+```
+
+#### 5. Array and Object Literals in JSX
+
+Each element/property is transformed independently when used in JSX:
+
+```typescript
+// Input - JSX context
+
+
+
+// Output
+
_v + 1), derive(price, (_v) => _v * 2)]} />
+
_v + 1),
+ total: derive({ price, tax }, ({ price: _v1, tax: _v2 }) => _v1 * _v2),
+}} />
+```
+
+### Current Limitations
+
+1. **Statement-Level Transformations** - Not supported:
+ ```typescript
+ // These patterns in regular statements are NOT transformed
+ if (count > 5) { ... } // ❌ If statements
+ while (count < 10) { ... } // ❌ Loops
+ const result = count + 1; // ❌ Variable declarations outside JSX
+ ```
+ **Why:** Statement transformations require complex control flow analysis and
+ handling of side effects. OpaqueRef transformations are limited to JSX
+ expression contexts where the transformation is straightforward.
+
+2. **Array Methods** - Not yet supported:
+ ```typescript
+ const items = cell([1, 2, 3]);
+ const doubled = items.map((x) => x * 2); // ❌ Not transformed
+ const filtered = items.filter((x) => x > 2); // ❌ Not transformed
+ ```
+ **Why:** Array methods require special handling to maintain reactivity
+ through the callback function.
+
+3. **Async Operations** - Not yet supported:
+ ```typescript
+ const url = cell("https://api.example.com");
+ const data = await fetch(url); // ❌ Not transformed
+ ```
+ **Why:** Async operations with OpaqueRef require special handling for promise
+ resolution and error states.
+
+4. **Destructuring** - Extracts values, losing reactivity:
+ ```typescript
+ const user = cell({ name: "John", age: 25 });
+ const { name, age } = user; // ❌ name and age are plain values, not OpaqueRef
+ ```
+ **Why:** Destructuring extracts the current value, breaking the reactive
+ chain.
+
+## Schema Transformer
+
+### Basic Usage
+
+Transform TypeScript interfaces to JSON Schema at compile time:
+
+```typescript
+///
+import { toSchema } from "commontools";
+
+interface User {
+ name: string;
+ age: number;
+ email?: string;
+}
+
+const userSchema = toSchema();
+
+// This is transformed at compile time to:
+const userSchema = {
+ type: "object",
+ properties: {
+ name: { type: "string" },
+ age: { type: "number" },
+ email: { type: "string" },
+ },
+ required: ["name", "age"],
+} as const satisfies JSONSchema;
+```
+
+### Handler and Recipe Transformations
+
+The schema transformer also converts `handler` and `recipe` calls with type
+arguments:
+
+```typescript
+///
+import { Cell, handler, recipe } from "commontools";
+
+// Handler with type arguments
+const myHandler = handler }>(
+ (event, state) => {
+ state.count.set(state.count.get() + 1);
+ },
+);
+
+// Recipe with type argument
+export default recipe("Counter", (state) => {
+ return { [UI]:
+ ),
+ items: state.items,
+ filter: state.filter,
+ };
+ },
+);
+```
+
+## Notes on Implementation
+
+- Start with the simplest transformations first
+- Ensure each phase is fully tested before moving to the next
+- Consider performance implications of transformation patterns
+- Maintain clear separation between transformer concerns
+- Document edge cases and limitations clearly
diff --git a/docs/future-tasks/code-quality-tasks/invalid-state-representations-report.md b/docs/future-tasks/code-quality-tasks/invalid-state-representations-report.md
new file mode 100644
index 000000000..ae0ffd75a
--- /dev/null
+++ b/docs/future-tasks/code-quality-tasks/invalid-state-representations-report.md
@@ -0,0 +1,241 @@
+# Invalid State Representations Report
+
+Based on the analysis of the codebase against AGENTS.md guidelines, the
+following interfaces allow invalid state representations that should be
+refactored:
+
+## 1. LLMRequest Interface
+
+**File**: `packages/llm/src/types.ts` **Lines**: 28-38
+
+```typescript
+export interface LLMRequest {
+ cache?: boolean;
+ messages: LLMMessage[];
+ model: ModelName;
+ system?: string;
+ maxTokens?: number;
+ stream?: boolean;
+ stop?: string;
+ mode?: "json";
+ metadata?: LLMRequestMetadata;
+}
+```
+
+**Issue**: The `cache` property being optional creates ambiguity. Code
+throughout the codebase defaults it to `true` if undefined, but this implicit
+behavior isn't clear from the interface.
+
+**Recommendation**: Rename `cache` to `noCache` with `false` as default:
+
+```typescript
+export interface LLMRequest {
+ noCache?: boolean;
+ // ... other properties
+}
+```
+
+## 2. OAuth2Tokens Interface
+
+**File**:
+`packages/toolshed/routes/integrations/google-oauth/google-oauth.utils.ts`
+**Lines**: 9-16
+
+```typescript
+export interface OAuth2Tokens {
+ accessToken: string;
+ refreshToken?: string;
+ expiresIn?: number;
+ tokenType: string;
+ scope?: string[];
+ expiresAt?: number;
+}
+```
+
+**Issue**: `refreshToken` is optional but critical for token renewal. Code has
+to handle cases where it might be missing, leading to potential authentication
+failures.
+
+**Recommendation**: Create separate types for initial and renewable tokens:
+
+```typescript
+export interface InitialOAuth2Tokens {
+ accessToken: string;
+ tokenType: string;
+ expiresIn: number;
+ scope: string[];
+}
+
+export interface RenewableOAuth2Tokens extends InitialOAuth2Tokens {
+ refreshToken: string;
+ expiresAt: number;
+}
+```
+
+## 3. UserInfo Interface
+
+**File**:
+`packages/toolshed/routes/integrations/google-oauth/google-oauth.utils.ts`
+**Lines**: 18-28
+
+```typescript
+export interface UserInfo {
+ id?: string;
+ email?: string;
+ verified_email?: boolean;
+ name?: string;
+ given_name?: string;
+ family_name?: string;
+ picture?: string;
+ locale?: string;
+ error?: string;
+}
+```
+
+**Issue**: Mixes success and error states in one interface. Either you have user
+data OR an error, never both.
+
+**Recommendation**: Use discriminated union:
+
+```typescript
+type UserInfoResult =
+ | {
+ success: true;
+ data: {
+ id: string;
+ email: string;
+ verified_email: boolean;
+ name: string;
+ // ... other fields
+ };
+ }
+ | { success: false; error: string };
+```
+
+## 4. CallbackResult Interface
+
+**File**:
+`packages/toolshed/routes/integrations/google-oauth/google-oauth.utils.ts`
+**Lines**: 30-35
+
+```typescript
+export interface CallbackResult extends Record {
+ success: boolean;
+ error?: string;
+ message?: string;
+ details?: Record;
+}
+```
+
+**Issue**: When `success` is false, `error` should be required. When `success`
+is true, `error` shouldn't exist.
+
+**Recommendation**: Use discriminated union:
+
+```typescript
+type CallbackResult =
+ | { success: true; message?: string; details?: Record }
+ | { success: false; error: string; details?: Record };
+```
+
+## 5. BackgroundCharmServiceOptions Interface
+
+**File**: `packages/background-charm-service/src/service.ts` **Lines**: 12-19
+
+```typescript
+export interface BackgroundCharmServiceOptions {
+ identity: Identity;
+ toolshedUrl: string;
+ runtime: Runtime;
+ bgSpace?: string;
+ bgCause?: string;
+ workerTimeoutMs?: number;
+}
+```
+
+**Issue**: Optional properties have defaults (`BG_SYSTEM_SPACE_ID` and
+`BG_CELL_CAUSE`) applied in constructor, but this isn't clear from the
+interface.
+
+**Recommendation**: Make defaults explicit in the type:
+
+```typescript
+export interface BackgroundCharmServiceOptions {
+ identity: Identity;
+ toolshedUrl: string;
+ runtime: Runtime;
+ bgSpace: string;
+ bgCause: string;
+ workerTimeoutMs: number;
+}
+
+export const DEFAULT_BG_OPTIONS = {
+ bgSpace: BG_SYSTEM_SPACE_ID,
+ bgCause: BG_CELL_CAUSE,
+ workerTimeoutMs: 30000,
+} as const;
+```
+
+## 6. Module Interface
+
+**File**: `packages/builder/src/types.ts` **Lines**: 182-188
+
+```typescript
+export interface Module {
+ type: "ref" | "javascript" | "recipe" | "raw" | "isolated" | "passthrough";
+ implementation?: ((...args: any[]) => any) | Recipe | string;
+ wrapper?: "handler";
+ argumentSchema?: JSONSchema;
+ resultSchema?: JSONSchema;
+}
+```
+
+**Issue**: `implementation` is optional but certain module types require it. The
+relationship between `type` and required properties isn't enforced.
+
+**Recommendation**: Use discriminated unions:
+
+```typescript
+type Module =
+ | {
+ type: "ref";
+ implementation: string;
+ wrapper?: "handler";
+ argumentSchema?: JSONSchema;
+ resultSchema?: JSONSchema;
+ }
+ | {
+ type: "javascript";
+ implementation: (...args: any[]) => any;
+ wrapper?: "handler";
+ argumentSchema?: JSONSchema;
+ resultSchema?: JSONSchema;
+ }
+ | {
+ type: "recipe";
+ implementation: Recipe;
+ wrapper?: "handler";
+ argumentSchema?: JSONSchema;
+ resultSchema?: JSONSchema;
+ }
+ | { type: "raw"; implementation: string }
+ | { type: "isolated"; implementation: string }
+ | { type: "passthrough" };
+```
+
+## Impact of These Issues
+
+1. **Runtime Errors**: Optional properties that are actually required lead to
+ runtime failures
+2. **Defensive Programming**: Developers must add null checks everywhere,
+ cluttering the code
+3. **Hidden Dependencies**: Implicit defaults make it hard to understand the
+ true requirements
+4. **Type Safety Loss**: TypeScript can't catch invalid combinations of
+ properties
+
+## General Principles from AGENTS.md
+
+"Making invalid states unrepresentable is good" - These interfaces violate this
+principle by allowing combinations of properties that shouldn't exist together
+or by making required properties optional.
diff --git a/docs/future-tasks/code-quality-tasks/module-graph-import-issues-report.md b/docs/future-tasks/code-quality-tasks/module-graph-import-issues-report.md
new file mode 100644
index 000000000..3c15d7045
--- /dev/null
+++ b/docs/future-tasks/code-quality-tasks/module-graph-import-issues-report.md
@@ -0,0 +1,112 @@
+# Module Graph and Import Issues Report
+
+Based on the analysis of the codebase against AGENTS.md guidelines, the
+following module graph and import issues were found:
+
+## 1. Default Exports Instead of Named Exports
+
+The following files violate the "prefer named exports over default exports"
+guideline:
+
+### Component Files
+
+- `packages/charm/src/iframe/recipe.ts`
+- `packages/charm/src/commands.ts`
+- `packages/html/src/render.ts`
+- `packages/html/src/path.ts`
+- `packages/memory/clock.ts` - `export default new Clock();`
+
+### Plugin Files
+
+**Example Fix**:
+
+```typescript
+// Bad
+export default class Clock { ... }
+
+// Good
+export class Clock { ... }
+
+// Bad
+export default new Clock();
+
+// Good
+export const clock = new Clock();
+// Or better: export a factory function
+export function createClock(): Clock { ... }
+```
+
+## 2. Missing or Incorrect Import Grouping
+
+The following files don't follow the import grouping convention (standard
+library → external → internal):
+
+### `packages/builder/src/utils.ts`
+
+```typescript
+// Current (internal packages mixed without grouping)
+import { JSONSchema7 } from "json-schema";
+import { isOpaqueRef, OpaqueRef } from "./spell.js";
+import { diffAndUpdate, maybeGetCellLink } from "@commontools/runner";
+
+// Should be:
+// Standard library
+// (none)
+
+// External
+import { JSONSchema7 } from "json-schema";
+
+// Internal
+import { diffAndUpdate, maybeGetCellLink } from "@commontools/runner";
+import { isOpaqueRef, OpaqueRef } from "./spell.js";
+```
+
+### `packages/identity/src/ed25519/index.ts`
+
+- External imports (`@scure/bip39`) mixed with internal imports without proper
+ grouping
+
+## 3. Module-Specific Dependencies (Good Practices Found)
+
+The codebase correctly follows the guideline for module-specific dependencies:
+
+### Good Examples
+
+- `packages/llm/deno.json` - Correctly declares `json5` dependency
+- `packages/ui/deno.json` - Correctly declares `@shoelace-style/shoelace`
+ dependency
+- Most packages don't have unnecessary dependencies in their `deno.json`
+
+**This is a positive finding** - the codebase correctly avoids adding
+package-specific dependencies to the workspace `deno.json`.
+
+## Recommendations
+
+### 1. Convert Default to Named Exports
+
+```typescript
+// For all identified files:
+// Change: export default Clock
+// To: export { Clock }
+
+// Change: export default new Clock()
+// To: export const clock = new Clock()
+// or export function createClock(): Clock
+```
+
+### 2. Enforce Import Grouping
+
+Add ESLint rule or formatter configuration to enforce:
+
+1. Standard library imports
+2. External package imports
+3. Internal package imports
+4. Relative imports
+
+## Impact Summary
+
+- **Circular dependencies**: Critical issue affecting maintainability and
+ testability
+- **Default exports**: Medium impact - affects consistency and tree-shaking
+- **Import grouping**: Low impact - affects readability
+- **Heavy utilities**: Medium impact - affects bundle size and modularity
diff --git a/docs/future-tasks/unified-storage-stack.md b/docs/future-tasks/unified-storage-stack.md
new file mode 100644
index 000000000..c55963970
--- /dev/null
+++ b/docs/future-tasks/unified-storage-stack.md
@@ -0,0 +1,448 @@
+# Unifying the storage stack
+
+## Current state
+
+Right now we have a few overlapping and uncoordinated layers of storage related
+functionality that needs reconciling. Most importantly we see data loss since
+the transaction boundaries don't line up, but it's also a lot of code that can
+be simplified away.
+
+Specifically we have:
+
+- I/O over iframe boundaries, typically with the iframes running React, which in
+ turn assumes synchronous state. So data can roundtrip through iframe/React and
+ overwrite newer data that came in in the meantime based on outdated
+ assumptions. E.g. a user event happens, state X is updated in React, while new
+ data is waiting in the iframe's message queue: Now an update based on older
+ data is sent to the container, but since there is no versioning at this layer,
+ it is treated as updating the current data. Meanwhile the iframe processes the
+ queued up update, and now is out of sync with the container. Note that this is
+ a pretty tight race condition: Some event processing coincides exactly with
+ receiving data updates. It's rare, but we've seen this happen when tabs get
+ woken up again and a lot of pent up work happens all at once.
+- Scheduler executing event handlers and reactive functions, which would form a
+ natural transaction boundary -- especially for event handlers to re-run on
+ newer data to rebase changes -- but those boundaries don't mean anything to
+ the rest of the stack. The only thing is that we make sure data isn't changed
+ while a handler/lifted function is running (await idle in storage.ts).
+- DocImpl that represent the data exposed to user code, typically via Cell.
+ Changes to it are directly applied to the data, and listeners are notified.
+ The only two listeners are the scheduler, which uses this to mark data dirty
+ and schedule the respective reactive functions and storage.ts, which adds
+ those documents to the next batch to be processed.
+- storage.ts which connects the lower storage layer with DocImpl. It wants to
+ make sure that the upper layers see a consistent view, so when a new document
+ contains a link to another document, it'll fetch that before updating the doc,
+ recursively. It also fetches all source docs (i.e. `doc.sourceCell`), also
+ recursively.
+ - This also means that changes from the upper layer can accumulate while all
+ this loading happens, and then altogether become one transaction: And if
+ there is one conflict anywhere, the entire transaction is rejected. And
+ while the actual conflict source gets eventually updated (since the server
+ will send these, and document that is read is also being subscribed to) the
+ other documents that were locally changed are not reverted. The clients get
+ out of sync.
+ - Also, if new data arrives from the server that overwrites local data that
+ was just changed, that is effectively a silently handled conflict, with the
+ same issues as above!
+ - Progress: We now also have schema queries, which will immediately fetch all
+ documents that are needed per a given schema, and will keep those up to
+ date, even if links change (meaning the subscription adds newly needed
+ documents and no longer subscribes to no longer used documents). That could
+ already replace a lot of the logic above, but we haven't turned that off. It
+ also currently doesn't use the cache.
+- storage/cache.ts, the memory layer, which operates at the unit of documents,
+ supports CAS semantics for transactions. It's used by storage.ts only and
+ while it has stronger guarantees, those either don't apply or sometimes
+ backfire because they are not aligned with the top: The upper layers don't
+ have a concept of "cause" and depending on the order of operations we
+ currently issue updated with the latest cause, but actually based on older
+ data. It has a cache but we underuse it. Key implementation details:
+ - Heap (partial replica of the remote state) and nursery (pending changes)
+ separation
+ - WebSocket sync with `merge()` for server updates
+ - Schema query support exists but incomplete (see pull() at line 1082)
+
+## Desired state
+
+- Iframe transport layer sends incrementing versions and ignores changes that
+ are based on too old changes. It's then up to the inside of the iframe to use
+ that correctly. In fact `useDoc()` where `setX()` takes a callback (e.g.
+ `setX(x => x + 1)`) instead of just the new value would already work much
+ better, since we can rerun it on the newest state if new state arrives that
+ invalidates previously sent updates. Probably sufficiently well for most cases
+ (the remaining problem would be that if other changes based on `X` changing
+ aren't purely reactive, i.e. only based on the last state, that those are not
+ being undone by rerunning the setter. This is rare, even in React). But we
+ could go even further (maybe some popular game toolkits are worth
+ investigating here at some point in the future, since that's a good usecase
+ for iframes)
+- Cells –- constructed typically by the runner when bringing up a recipe, within
+ handlers or reactive functions and in some cases by parts of the shell to
+ bootstrap things -- directly read from memory via schema query. They
+ accumulate writes and wait for the scheduler to close out a transaction.
+ Interim reads see the new version.
+- Scheduler runs handlers and reactive functions and then issues a transaction
+ with pending writes directly to the underlying memory layer (we already log
+ reads and writes via `ReactivityLog`, so we can extend that to log the exact
+ writes, not just which documents were affected). It registers with the
+ underlying memory layer (instead of with `DocImpl` as before) for changes on
+ individual documents, marking –– as is already the case –– the corresponding
+ reactive functions as needing to run (semantically we want to subscribe to the
+ corresponding schema queries, but at least with the current queries, listening
+ to the actually read docs is the same). For events it will keep track of the
+ transaction and if it fails, and after we're sure to have caught up enough
+ with the server to reflect the new current state, retry up to N times.
+- Memory -- more or less like now, except that its lower level API is directly
+ exposed to cells, including `the` and the document ids as DIDs (so the Cell
+ will have to translate the ids an prepend `of:`)
+
+## Steps to get there
+
+This plan should be entirely incremental and can be rolled out step by step.
+
+- [x] Ephemeral storage provider + Get rid of `VolatileStorageProvider` CT-420
+- [x] Schema queries for everything + Source support CT-174 CT-428
+ - See design note on cache, but that's not blocking progress on the rest
+- [x] Turn off "crawler" mode in storage.ts, make sure things still work
+ - The crawler is in `storage.ts:_processCurrentBatch()` (lines 478-577) which
+ recursively loads dependencies
+ - Key areas: loading promises map (line 84), dependency tracking, and batch
+ processing
+ - Watch for the FIXME at line 84 about keying by doc+schema combination
+- [x] When connection is dropped, re-establish all schema queries again CT-442
+- [x] Replace all direct use of `DocImpl` with `Cell` (only `DocImpl` use inside
+ `Cell`, scheduler (just `.updates()`) and storage.ts should remain for
+ now) CT-446
+ - [x] Add .setRaw and .getRaw to internal `Cell` interface and use the cell
+ creation methods on `Runtime` and then almost all used of `DocImpl` can
+ be replaced by cells and using `.[set|get]Raw` instead of
+ `.[set|send|get]`
+- [x] Change all places that expect `{ cell: DocImpl, … }` to just use the JSON
+ representation. At the same time, let's support the new syntax for links
+ (@irakli has these in a RFC (CT-448), should be extracted, effectively
+ `{ "@": { "link": { ... }}}`). This is because today `storage.ts`
+ translates any `{ "/": string }` it finds to `{ "/": DocImpl }`, but we
+ don't want to carry this logic over to this new state. See `isCellLink`,
+ which might not be universally used, but should be. Maybe add a
+ `readCellLink` function to parse these. CT-447
+ - [x] Also change schema queries on the serverside
+ - [x] Remove that translation in storage.ts and make sure everything still
+ works.
+- [x] Create a new transaction API: Get a `tx` object from memory, which exposes
+ `tx.read(entity, path)`, `tx.write(entity, path, value)` (and/or other
+ mutation functions), etc., and `tx.commit()`, `tx.abort(reason?: Error)`
+ and a few more (see below). CT-449
+- [x] Implement a shim of the new TX API using `DocImpl`, etc. CT-485
+ - [x] Wherever `log` get created, switch to new API. So at least
+ - [x] scheduler
+ - [x] Cell.sink
+ - Need to call .commit() when done (TBD: Even on read-only?)
+ - Change read/write
+ - [x] `diffAndNormalize` and `applyChangeSet`
+ - [x] remaining `Cell.*` (e.g. `getRaw`, `setRaw`)
+ - [x] `QueryResultProxy`
+ - [ ] Move ephemeral and frozen concept into Cell
+ - [x] `Cell.freeze()` makes cell read-only
+ - [ ] `Cell.ephemeral` whether cell is persisted
+- [x] Switch all reading & writing over to this new TX API CT-486
+- [ ] Add a `VirtualCell` that is used in runner to construct inputs, which is
+ read-only, has no id or space and isn't synced to storage. It should throw
+ if used in any way that indicates it is being linked to or written to.
+ CT-493
+- [x] Add `Cell.sync` call that does what `Storage.syncCell` does today. CT-494
+ - [x] In fact automatically start syncing already, as this will be the future
+ behavior. This is just for await.
+ - [ ] Add option to only await a locally cached version if available.
+- [x] Add path-dependent listeners to memory: A helper on `Storage`, that given
+ a `TX` calls a callback _once_ on future changes on what was read during
+ the transaction (observing only changes affecting the read path). Make it
+ cancelable (the scheduler will e.g. cancel this before executing the
+ action again). First design the API.
+- [x] Shim the API above and switch scheduler to use it
+ - [x] The current reads and writes from a TX can be read out, which scheduler
+ will use to update the dependency graph. In fact scheduler will inside
+ the callback do both this and adding the callback just before returning.
+ It does so to not miss any updates.
+- [x] Implement new TX over memory CT-487
+ - [x] The user of the TX shall observe a consistent state during the lifetime
+ of the transaction. All its writes are only committed to the nursery
+ after `tx.commit()` is called. If the transaction attempts to read a
+ value that has changed since the start of the transaction, the
+ transaction is aborted.
+- [x] Shift `Cell.sync()` to make schema queries on memory directly.
+- [x] Implement new listener API.
+ - [x] Path-dependent means that we diff updates and compute what paths have
+ changed. Callback gets called if any paths overlap, i.e. one is a subset
+ of the other. See `compactifyPath` and `pathAffected` for current
+ implementation.
+- [x] Scheduler retries events whose transaction failed. It does so up to N
+ times and calls a callback after the last retry (both configurable via
+ `Runtime` constructor). Events are retried after all read cells are fully
+ synced and reactive functions that are queued up are settled, so it's
+ guaranteed to be a stable state (Future optimization: Dynamically insert
+ into the queue after any reactive function that might update the handlers
+ inputs, but before any that read its outputs)
+ - [ ] For change sets that only write (e.g. only push or set), we could just
+ reapply those without re-running the handler. But this could also be a
+ future optimization.
+- [x] More selectively purge the nursery on conflicts by observing conflicted
+ reads. CT-451
+- [x] On conflicts add data that changed unless it was already sent to the
+ client by a query. CT-452
+- [x] Remove `storage.ts` and `DocImpl`, they are now skipped CT-453
+- [x] Memory layer with pending changes after a conflicted write: rollback to
+ heap and notify that as changes where it changed things
+- [ ] Sanitize React at least a bit by implement CT-320
+ - Current iframe transport has TODO at
+ iframe-sandbox/common-iframe-sandbox.ts:212
+ - No version tracking causes overwrites with stale React state
+
+## Open questions
+
+- [ ] Debug tools we should build to support this future state
+- [ ] Behavior for clients that are offline for a while and then come back
+ online while there were changes. By default we'd just drop all of those,
+ but we would notice that explicitly. Unlike rejections that happen quickly
+ and users can react to in real-time, this might need something more
+ sophisticated.
+ - [ ] At the very least show a UI that we're offline. CT-445 tracks that.
+- [ ] Recovery flows for e.g. corrupted caches (interrupted in the middle of an
+ update)
+- [ ] Extending transaction boundaries beyond single event handlers: As
+ described above, each handler's execution and retry is independent of each
+ other and it's possible that one of them is rejected while others pass,
+ even for the same event. We could change this to broaden the transaction:
+ - A fairly simple change would be to treat all handlers of the same event as
+ one transaction. Currently scheduler executes them one-by-one and settles
+ the state (i.e. run all the reactive functions) in between, and it wouldn't
+ be difficult to change that to running all handlers for one event, then
+ settle the state. That way, at least the event is either accepted or
+ rejected as a whole. That said, I don't think we have any examples yet of
+ running multiple handlers in parallel.
+ - The more complex case would be a cascade of events, i.e. event handlers that
+ issue more events, and then accepting/rejecting the entire cascade. That's
+ significantly more complicated, and even more so if we allow async steps
+ inbetween (like a fetch). We haven't seen concrete examples of this yet, and
+ we should generally avoid this pattern in favor of reactive functions.
+- [ ] Incremental loading: As currently stated all pending schema queries are
+ expected to be resolved together. At least that is the easiest to model if
+ the goal is to represent consistent state. But it also means that the
+ initial load can take longer than needed, because it needs to load all the
+ data. Clever ordering of queries, treating some as pending, etc. could
+ improve this a lot, but is non-trivial. Fine for now, but something to
+ observe.
+- [ ] Anything we can do to make it easier to run handlers or functions in
+ parallel if they have no shared dependencies?
+
+## Design notes
+
+### Functions must see a consistent state
+
+We need to lock versions while executing a handler/reactive function? I.e. if an
+update comes from the server after the function started, and `.get()` is called,
+we need to return the state from the point when it was called? Considerations:
+
+- It's almost certainly going to cause issues if the function sees data from
+ different points of time, even if they are internally self consistent.
+- We don't know which cells, especially which cells linked from cells the
+ function will read, so making a copy of all of those is overkill.
+- That said, the functions are meant to be synchronous without any awaits in it.
+ We have exceptions (the importers) right now, but it's ok to behave as if they
+ were (i.e. stop everything else). These might become async from the outside
+ later, e.g. we can pause execution in a wasm sandbox to fetch more data across
+ a Worker boundary or so.
+- Hence, for now we can do the equivalent of `await runtime.idle()` before even
+ processing data coming from the websocket, and thus circumvent this question.
+ It really just timeshifts processing to after processing, and that's anyway
+ the intended effect. In fact we should even apply the writes before processing
+ server-side data, then everything will be based on the correct cause.
+
+### Schema queries
+
+Schema queries is how we maintain the invariant that a `.get()` on a cell should
+return a consistent view _across_ several documents by fetching and updating
+documents atomically. The schema lets us understand how to group these
+documents. This replaces the current "crawler" mode in storage.ts, which what
+most of the batch logic actually does.
+
+Specifically we rely on the server observing a change in any of the documents
+that were returned last time, rerun the query and send updates to the client
+about all documents that are now returned by the query.
+
+#### Schema queries & cache
+
+We have to store queries in the cache as well, noting for which `since` we're
+sure it is uptodate (`since` is monotonically increasing at the space level, so
+representing time: A document has a value _since_ that time). In fact we want to
+point to a session id (representing the current socket connection, since that
+represents the time the client and server share state (IOW: For each new
+connection the active queries have to be re-established, and then present the
+next set of shared state again). It is just a local concept, so any random or
+even monotonically increasing number will do) from each query, and the session
+id notes the last `since`. That's because once a subscription is up, all we need
+are new versions of documents, we don't need the association of which query they
+belonged to. And so all currently active queries are always current to the last
+update. (Once we also unsubscribe from queries this gets a little more complex)
+
+So when a new query is issued, we
+
+- issue the query to server with a `since` from the cache or `-1` (to be
+ confirmed) indicating that it never ran.
+- if it is in the cache run the query against the cache, and see whether any
+ documents are newer than the `since` for the query. If not, we can serve the
+ current cached version immediately. If yes, the state might be inconsistent
+ and we have to wait for the server (in the future we might want to keep older
+ versions for this reason)
+
+The server builds state of what documents the client already has at what version
+by running the queries server side _at that sent `since`_ and assuming that the
+client already has all the documents for that `since`. It is hence advantageous
+to send queries that are in the cache before any non-cached queries, to the
+degree that is in our control. Maybe batch them for a microtick?
+
+#### What happens if new data is needed
+
+A handler or reactive function might change data in such a way that a subsequent
+reactive function has a query as input that is now incomplete. We need to define
+what should happen in this case.
+
+An example is changing the currently selected item based on a list that has just
+the names of the items and a detail view reacting to it that shows all details.
+It might have to fetch the details now.
+
+The two options are:
+
+- Return `undefined` at the next highest point in the structure that is
+ optional. Eventually the function will be called again with the full data.
+ This seems reasonable, except if an event will be based on this incomplete
+ data and thus write incomplete data back to the store -- CAS will catch most
+ of these cases, but I can imagine UI roundtrips, especially if the user is
+ acting on stale mental state and races the UI with a click, where this breaks.
+- Skip execution entirely, effectively returning `` and let higher
+ layers deal with it. (A pattern we might want to adopt for `llm` and other
+ calls as well, see CT-394)
+
+Current implementation just returns `undefined` where data is missing and might
+cause errors. We shouldn't block building the above on resolving this though.
+
+## Future work that is related
+
+### Changes to schema
+
+- flip default interpretation of no `additionalProperties` to be `false` instead
+ of `true` (which is what "ignore additional properties" means in a query
+ context vs validation context)
+- change `{}` schema to mean `false` (matching nothing) instead of `true` (any).
+- use schema in cell links when resolving queries, at first only if schema is
+ `true` otherwise.
+- add `allOf` support and use it for schemas in cell links, so that it's now the
+ intersection of schemas.
+
+### Transition all links/etc to @-notation
+
+- Blobs: See upcoming doc, noting here as it's building on moving to the
+ `{ "@": { : { ... }}}` notation of links/etc
+- Charms: Add `{ "@": { process: { ... }}}` for charms, and make result just
+ data within that (as it's always a few static aliases anyway). System code
+ that deals with charms should probably directly operate on cells of processes
+ like this. And code where charms are just pointers to results (most current
+ code and all userland code) inherit the behavior from the now clear
+ containment.
+- Streams: Currently `{ $stream: true }` should also transition. I don't think
+ any extra data is needed, though we might want to discuss moving the schema of
+ the events into this vs storing it in the usual meta data for schemas.
+
+### Save scheduling information in storage as meta data, remove extra `value`
+
+Currently data in storage is actually
+`{ value?: , source?: }`. It
+should just be the value.
+
+We also don't store any information that the scheduler generates about
+dependencies, and so when loading a charm we have to recompute all reactive
+functions just to regenerate that.
+
+Instead we could save scheduling information as meta data, i.e. as a separate
+document with a different `the`, but the same `of`.
+
+For reactive functions we should store
+
+- What data was read to compute this output and at what `since`
+- Link to process cell that explains how to recompute this
+
+For data last manipulated by an event we might just - if the event actually did
+read the prior state - write itself as dependent data. This makes it so that the
+rule to allow overwriting is "can't use any sources that are older than the
+sources used to compute this value". The underlying CAS based on cause is then
+just there to make sure this reasoning is based on the current last state.
+
+For streams we want to write out all the processing cells that define handlers
+for this stream, so that they can be reloaded.
+
+### Changing recipe creation to just use `Cell` and get rid of `OpaqueRef`
+
+`OpaqueRef` are just opaque `Cell`s. So we can vastly simplify recipe
+generaetion by combining those.
+
+Essentially `lift` & co return cells instead. And the opaque cell passed in to
+the recipe function is already bound to the actual inputs (without revealing
+that to the recipe function).
+
+The tricky bit is generating good causal ids for these cells, which isn't good
+right now either.
+
+This could work like this:
+
+- Build up a graph of cells, just like opaque refs now, via builder functions.
+ Don't assign ids yet, so id-less cells is a new thing.
+- Eventually they are either assigned to a pre-existing cell or they are
+ returned (which is assigned to the result cell). Use this to derive ids, that
+ are causal to invocation and where they write to (entity id + path).
+- Do that recursively as ids are being set.
+- For all remaining cells in the graph, i.e. those that are never read, we can
+ assign them causal to the invocation and a sequence number or something else.
+ As nothing can read them it matters less. FWIW, the only use-case so far is a
+ `lift` that calls console.log, so strictly for debugging.
+
+Note that with the change above of writing transactions this also implies
+delayed writes. This is then also how the transaction object gets known to the
+cell: When it connects to the rest of the graph and gets its id.
+
+### Single event sourcing model + server-side query prediction
+
+Instead of sending transactions to the server we can send the original events
+instead and run the event handler server-side on what is guaranteed to be the
+canonical state. Thus have a simpler event source system.
+
+The code above becomes speculative execution that reapplies pending events on
+newer data until it is fully in sync again.
+
+Note that events can still conflict (unless they are inherently conflict-free of
+course). The client-server API then changes to both sending events and getting
+confirmation/rejections back. The client could reissue a new event in some rarer
+cases, but sending the same event again won't be needed, as it was already run
+on the current state. (That said, we can still support "sending an event" that
+just a basic patch and then re-issue a new patch based on that. That's really
+only useful for when we can't run the handler on the server, e.g. in some iframe
+use-cases)
+
+Note that since all reactive functions can run on the server as well, all that
+work is latency reduction on the client and doesn't need to be synced. The
+optimization is the other way around: If we know that a client will run these
+reactive functions we don't need to send that state from the server (in fact
+often we don't need to save it all, especially if recomputation is cheap, but
+that's yet another future optimization). This falls under a larger opportunity
+to look at a computation graph and decide which parts the client should even run
+(e.g. prune parts that only feed into something that must run on the server,
+e.g. any external fetches or API calls)
+
+#### Possible bonus: Server could predict most queries
+
+At this point knowing just a few things about the client, e.g. what UI is shown,
+we can reconstruct enough of the remaining client state server-side to predict
+what schema queries the client would send and just proactively run those and
+sync the documents. The main problem here is that we don't know the state of the
+cache. Maybe there is an anologous roll-up for cache state (which as noted above
+is really a map from queries to `since`), e.g. just remembering the `since` for
+a given place in the UI and the rest follows from there?
diff --git a/docs/glossary.md b/docs/glossary.md
new file mode 100644
index 000000000..ec745b25d
--- /dev/null
+++ b/docs/glossary.md
@@ -0,0 +1,226 @@
+# Glossary
+
+## ACL (Access Control List)
+
+Defines who can read or write specific data in a space, forming part of the
+data's access policy.
+
+## Cell
+
+Cell is a unit of reactivity, conceptually it is similar to a cell in a
+spreadsheet. It holds a value that can be updated by writing into a cell. Cell
+can also have subscribers that will be called whenever cell content is updated
+allowing them to compute derived state which will end up propgating it to some
+output cell.
+
+## CFC (Contextual Flow Control)
+
+A security model combining information flow control with contextual integrity;
+enforces policies on how data is used, attached to schemas and validated both
+statically and dynamically.
+
+## Charm
+
+Charm is a [spell] invocation binding set of [cell]s as inputs and set of
+[cell]s as outputs, creating an execution graph. It may help to think of [spell]
+as an open electric circuit, in this case [charm] would be a closed electric
+circuit as current will flow through it. Different analogy could be to think of
+[charm] as a process, where's [spell] would be a program and [cell]s would be
+program inputs and outputs.
+
+There are a few more specific terms for [some cells](charm-cell-diagram.md) within the charm.
+- Result Cell -- This is the main charm cell. The UI will be built here.
+- Process Cell -- This holds much of the working state of the charm.
+- Recipe Cell -- Contains the recipe source code.
+
+## CRDT (Conflict-free Replicated Data Type)
+
+A data structure that can resolve conflicts automatically in distributed
+systems. Used selectively, e.g. for collaborative text editing.
+
+## Space
+
+Space is primarily a sharing boundary, designed to enforce access control.
+Spaces are identified by unique [did:key] identifiers.
+
+> ℹ️ Currently each space has a corresponding sqlite database to store all of
+> its state.
+
+Space can be queried and updated using [memory protocol], which describes state
+in terms of [fact](#fact)s.
+
+## Fact
+
+Is a record of state in time represented using `{ the, of, is, cause }` tuples.
+E.g consider following fact: _The_ **color** _of_ **sky** _is_ **blue** it would
+directly translated to `{ the: "color", of: "object:sky", is: "blue" }`.
+
+> ℹ️ The `cause` filed is used to establis causal references, it effectively
+> represents a logical time per fact as opposed to global time.
+
+In practice we use `the` field to describe kind of the information value (`is`
+field) is provided about subject entity (`of` field). Predominantly `the` is
+`"application/json"` as we store [cell] contents as JSON values and consequently
+`is` field is a JSON value [cell]s hold at discrete points in time.
+
+The `of` field is a unique identifier represented via URI. In practice it
+usually a [merkle-reference] derived from some seed data with `of:` scheme
+prefix.
+
+## Memory
+
+Memory is an abstraction over [space] and an information system adhering to
+[The Value of Values] design principles. Abstraction provides efficient way to
+access current state - current facts about various entities, while still
+providing a way to recall facts that had being succeeded by the new ones.
+
+Memory also provides interface for accreting new information through an
+interface with [compare and swap (CAS)][CAS] semantics.
+
+> ℹ️ Please note that layers above [memory] do not follow same principals or
+> operate at the level of [fact]s, instead they use more traditional
+> document-oriented semantics and reference state by the address inside the
+> mutable memory space.
+
+## [did:key]
+
+A decentralized identifier derived from a keypair. Used to uniquely identify and
+control a [Space].
+
+## Event Handler
+
+Code that reacts to events and may update other cells or trigger further
+actions.
+
+## LLM (Large Language Model)
+
+AI models such as Claude or ChatGPT that can be called from recipes for
+AI-generated outputs.
+
+## Reactive Framework
+
+The runtime engine behind Open Ocean that computes state updates in a
+deterministic way, using dependency graphs of reactive cells.
+
+## Recipe
+
+A function that defines a reactive graph. Can produce UI, derived data, or
+streams. Used like components in other reactive frameworks.
+
+## CTS (Common TypeScript)
+
+Typescript dialect that is pre-processed in recipes to preserve familiar
+Typescript patterns when using Cells and shared storage. This leverages the
+typescript compiler to parse the AST (Abstract Syntax Tree) of the code, and
+make appropriate transformations.
+
+## Safe Rendering
+
+The secure, isolated rendering of recipe-generated UI, considered part of the
+Trusted Computing Base (TCB).
+
+## Space
+
+A namespace for user data, identified by a [did:key]. Users control access and
+permissions via [UCAN]s and ACLs.
+
+## Spell
+
+Unit of computation that describes transformation from the set of inputs to the
+set of outputs. In practice it is manifested as a typescript function that takes
+an object with set of properties and returns an object with a set of outputs.
+
+It is worth pointing out that while typescript function is used it does not
+actually defines a computation, instead it is a way to build a computation
+pipeline that flows through input [cell]s into output [cell]s.
+
+## Storage - Cache (IndexDB)
+
+The persistent storage layer using IndexedDB (when available) that survives
+across browser sessions and stores historical revisions fetched from the remote
+server. The cache is only accessed during load() operations when explicitly
+loading data into the heap at session start or when accessing new entities.
+Writes to the cache occur as a write-through persistence layer: after successful
+pulls from remote, when receiving subscription updates, or during load
+operations. The cache never stores data directly from local changes. If
+IndexedDB is unavailable, it falls back to NoCache which provides no
+persistence. This tier aims to improve startup performance.
+
+> Note: While IndexedDB provides the storage layer, queries are currently
+> performed through schema queries rather than direct IndexedDB queries. Direct
+> IndexedDB query functionality would require additional development to be
+> useful.
+
+## Storage - Heap
+
+The in-memory cache for the current session that stores confirmed revisions from
+the remote server. All incoming subscription data and remote updates flow
+directly into the heap (does not touch nursery). The heap maintains subscribers
+to notify them when facts change. Facts enter the heap through three paths:
+promotion from the nursery after successful commits, direct insertion when
+pulling data from remote, or from subscription updates. During reads, the heap
+is checked after the nursery. Unlike the nursery which only holds local changes,
+the heap represents the authoritative state as known by the server. The heap
+persists for the entire session.
+
+## Storage - Nursery
+
+A temporary cache layer that stores only locally-initiated changes before
+they're confirmed by the remote server. This enables optimistic updates - when
+you make a local change, it immediately goes into the nursery so the UI can
+reflect changes instantly without waiting for server confirmation. The nursery
+never stores incoming subscription data from the remote server. If a commit
+succeeds, facts are promoted from nursery to heap. If a commit fails, facts are
+deleted from the nursery to prevent building on rejected state. The nursery
+"shadows" the heap, meaning reads check here first, and any local unconfirmed
+change will be returned even if the heap has a newer version from the server.
+
+Nursery eviction occurs in several scenarios:
+
+- When the remote server returns a matching state, indicating the server has
+ caught up with the local change
+- When conflicts occur, which will purge conflicting entries from the nursery
+- When an update arrives that matches what was expected from the server, but
+ local changes have been built on top of those changes (in this case, the
+ nursery copy is retained to preserve the local changes)
+
+## TCB (Trusted Computing Base)
+
+The minimal set of components that must be trusted to enforce security. This
+includes rendering infrastructure (e.g. web components), and excludes
+user-authored recipes, which are sandboxed.
+
+## UCAN (User Controlled Authorization Network)
+
+A capability-based auth system that allows delegating access rights using signed
+tokens.
+
+## VDOM (Virtual DOM)
+
+A data representation of UI elements returned by recipes, which the runtime
+turns into rendered HTML.
+
+[spell]: #spell
+[cell]: #cell
+[charm]: #charm
+[acl]: #acl-access-control-list
+[cfc]: #cfc-contextual-flow-control
+[cts]: #cts-common-typescript
+[crdt]: #crdt-conflict-free-replicated-data-type
+[deno]: #deno
+[did:key]: #didkey
+[event-handler]: #event-handler
+[llm]: #llm-large-language-model
+[memory]: #memory
+[reactive-framework]: #reactive-framework
+[recipe]: #recipe
+[safe-rendering]: #safe-rendering
+[space]: #space
+[tcb]: #tcb-trusted-computing-base
+[ucan]: #ucan-user-controlled-authorization-network
+[vdom]: #vdom-virtual-dom
+[memory protocol]: https://github.com/commontoolsinc/RFC/blob/main/rfc/memory.md
+[The Value of Values]: https://www.youtube.com/watch?v=-I-VpPMzG7c
+[merkle-reference]: https://github.com/Gozala/merkle-reference/blob/main/docs/spec.md
+[CAS]: https://en.wikipedia.org/wiki/Compare-and-swap
+[did:key]: https://w3c-ccg.github.io/did-key-spec
diff --git a/docs/specs/data-model/sigil.md b/docs/specs/data-model/sigil.md
new file mode 100644
index 000000000..1ef45c86f
--- /dev/null
+++ b/docs/specs/data-model/sigil.md
@@ -0,0 +1,648 @@
+# Sigil Protocol: Cross-Fact References for Memory Protocol
+
+
+
+## Editors
+
+- [Irakli Gozalishvili], [Common Tools]
+
+## Authors
+
+- [Bernhard Seefeld], [Common Tools]
+
+## Abstract
+
+This RFC extends the [Memory Protocol] with a standardized system for creating
+references between facts in user-controlled spaces. The protocol defines five
+fundamental types of references:
+
+1. **Inline references**: Embed data directly within facts using
+ [IPLD data model] types.
+2. **Immutable references**: Reference data by content using [merkle reference]s
+ which are valid [IPLD Links].
+3. **Mutable references**: Reference data by its memory address using `link`
+ sigils.
+4. **Blob references**: Reference binary data interpreted as `Blob` instances
+5. **File references**: Reference binary data interpreted as `File` instances.
+
+These reference types enable sophisticated data modeling including binary
+content references, computed data sources, and relational queries within facts,
+while preserving the causal integrity of the memory protocol. References are
+resolved by the [Schema Query Protocol], which provides the
+`/memory/graph/query` capability for advanced querying with automatic
+resolution.
+
+## Language
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
+interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119).
+
+## Motivation
+
+The [Memory Protocol] provides a robust foundation for storing and querying
+facts in user-controlled spaces. However, applications need standardized ways to
+reference data across fact boundaries with different consistency guarantees:
+
+- **Immutable references**: Reference data that should never change, with
+ cryptographic integrity guarantees
+- **Mutable references**: Reference data that can be updated over time, with
+ automatic propagation of changes
+- **Binary content**: Handle binary data and file metadata consistently within
+ the fact model
+- **Computed relationships**: Establish dynamic relationships and computed
+ values between facts
+- **Reactive systems**: Build systems that respond to changes in referenced data
+
+This RFC addresses these needs by defining five fundamental reference types that
+provide different consistency and mutability guarantees, enabling applications
+to choose the appropriate reference semantics for their use cases.
+
+## IPLD Data Model and DAG-JSON Convention
+
+This protocol uses the [IPLD data model] which provides a canonical way to
+represent structured data that is encoding-agnostic. The IPLD data model
+supports the same basic types as JSON (null, boolean, integer, float, string,
+list, map) plus additional types for binary data and cryptographic links.
+
+For JSON encoding, we follow the [DAG-JSON] specification which provides a
+standard way to represent IPLD data in JSON format. The DAG-JSON specification
+uses the `/` field as a special namespace for encoding data types outside the
+JSON data model. We adopt the standard encoding for [bytes] and [links] per the
+[DAG-JSON] specification and define our own custom data types under the `/`
+field as described in detail in this document.
+
+This approach:
+
+- Maintains compatibility with standard JSON parsers
+- Provides a clear namespace for special types separate from user data
+- Enables progressive enhancement where simple JSON becomes linkable
+- Supports efficient serialization and deserialization
+- Preserves the memory protocol's fact structure
+
+## Reference Types
+
+This protocol describes a set of reference types with different characteristics
+and use cases:
+
+### Reference Resolution
+
+References are resolved by the [Schema Query Protocol] to reduce roundtrips when
+following references across facts. The resolution process understands the
+semantics of each reference type and can efficiently traverse relationships to
+build comprehensive result sets.
+
+### Inline References
+
+Inline references embed data directly within facts' `is` field using values
+conforming to the [IPLD data model]. Binary data can be inlined as IPLD [bytes].
+
+#### Bytes in JSON
+
+Inline bytes in JSON encoding follow the [DAG-JSON] specification for
+representing [bytes]. Memory protocol implementations are RECOMMENDED to impose
+reasonable size limits for inlined binary data.
+
+##### TypeScript Definition
+
+```ts
+type Bytes = {
+ "/": {
+ /* Base64 encoded binary */
+ "bytes": string;
+ };
+};
+```
+
+##### Example
+
+```json
+// Binary data using DAG-JSON bytes
+{
+ "/": {
+ "bytes": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFeAJ5gZ5fCQAAAABJRU5ErkJggg=="
+ }
+}
+```
+
+### Immutable References
+
+Immutable references use [merkle reference]s that provide cryptographic
+integrity guarantees while remaining agnostic of content encoding. They follow
+[IPLD data model] and are represented as [IPLD Links] with following added
+constraints:
+
+1. Immutable references MUST be [merkle reference]s.
+2. Immutable reference MUST reference content of the fact's `is` field.
+
+> ℹ️ Above constraints attempt to strike a good balance between flexibility and
+> practicality and are optimized for expected usage.
+
+**Key Properties**:
+
+- Reference only the content of the fact's `is` field, not the entire fact
+ structure or subset of the `is` field.
+- Use the [merkle reference] codec for content addressing
+- Provide cryptographic integrity guarantees
+- Never change - the same content always has the same reference and same
+ reference resolve to the same content.
+
+##### TypeScript Definition
+
+```ts
+type Reference = {
+ /* Multibase Base32 encoded CIDv1 with 0x07 merkle-reference codec */
+ "/": string;
+};
+```
+
+##### Example
+
+```json
+{
+ "/": "ba4jca7rv4dlr5n5uuvcz7iht5omeukavhzbbpmc5w4hcp6dl4y5sfkp5"
+}
+```
+
+### Mutable References
+
+Mutable references use `link` sigils to reference facts that can be updated over
+time. These references automatically reflect changes to the addressed data.
+
+**Key Properties**:
+
+- Reference facts by address in memory space using coordinates (`accept`, `id`,
+ `space`, `path`)
+- Automatically reflect changes when the addressed data is updated
+- Support path navigation within the target fact's `is` field
+- Provide configurable write behavior through the `overwrite` field
+
+#### Link Sigil (`link@1`)
+
+The `link` sigil provides mutable references to JSON values held by other facts.
+If `path` is omitted, it references the whole JSON value - the `is` field of the
+addressed fact. If `accept` is omitted, it defaults to the type of the fact the
+link is embedded in. If `id` is omitted, it defaults to the entity the link is
+embedded in. If both are omitted, it creates a self-reference. The `overwrite`
+field controls write behavior.
+
+> ℹ️ Therefore `link` with omitted `accept`, `id` and `path` represents a
+> self-reference.
+
+##### Fields
+
+- `id` (optional): Resource URI of the target fact. Defaults to linker's id
+- `accept` (optional): Media type preferences for the target fact, following
+ HTTP Accept header semantics. Defaults to linker's type
+- `path` (optional): Array of strings/numbers for navigating into the target
+ fact's `is` field.
+- `space` (optional): DID of the space containing the target fact. Defaults to
+ current space
+- `schema` (optional): JSON Schema for validation
+- `overwrite` (optional): Controls write behavior - `"this"` (default) or
+ `"redirect"`
+
+#### TypeScript Definition
+
+```typescript
+type LinkSigil = {
+ "link@1": {
+ // defaults to the entity containing this link
+ id?: URI;
+ // HTTP Accept header format, defaults to the type containing this link
+ accept?: string;
+ // defaults to the space containing this link
+ space?: SpaceDID;
+ // defaults to the empty path [] representing `is` of the addressed fact
+ path?: Array;
+ // defaults to "this"
+ overwrite?: "this" | "redirect";
+ // defaults to any if omitted
+ schema?: JSONSchema;
+ };
+};
+```
+
+#### Resolution Behavior
+
+Link sigils resolve to the current value at the specified location within the
+target fact's `is` field. When the target fact changes, all references
+automatically reflect the new value.
+
+##### Example with Default Values
+
+When `accept`, `id`, or `space` are omitted, they implicitly inherit the `type`,
+entity, and space of the record this link is contained in. If all are omitted,
+it creates a self-reference:
+
+```json
+{
+ "the": "application/json",
+ "of": "user:alice",
+ "is": {
+ "name": "Alice Smith",
+ "displayName": "ali",
+ "nickname": {
+ "/": {
+ "link@1": {
+ "path": ["displayName"]
+ }
+ }
+ }
+ },
+ "cause": "da6lce9tv6fnr7o7wwxez9kjv7qogwmcxjacdrod7y6jer8fn6a7ugmt7"
+}
+```
+
+In this example, the link creates a self-reference to the same fact
+(`user:alice` with `application/json`) at the `displayName` path, effectively
+creating an alias within the same fact.
+
+#### Write Behavior
+
+Setting a property in the referenced data structure keeps the fact containing
+the link unchanged and forwards changes to the addressed memory space.
+
+##### Example of setting property inside reference
+
+```json
+// We start with following set of facts
+{
+ "the": "application/json",
+ "of": "user:alice",
+ "is": {
+ "contact": {
+ "github": "@alice"
+ }
+ }
+}
+{
+ "the": "application/json",
+ "of": "profile:alice",
+ "is": {
+ "contact": {
+ "/": {
+ "link@1": {
+ "id": "user:alice",
+ "path": ["contact"]
+ }
+ }
+ }
+ }
+}
+
+// After profile.contact.email = "alice@web.mail", we end up with the first
+// fact updated and the second fact remaining unchanged
+{
+ "the": "application/json",
+ "of": "user:alice",
+ "is": {
+ "contact": {
+ "email": "alice@web.mail",
+ "github": "@alice"
+ }
+ }
+}
+{
+ "the": "application/json",
+ "of": "profile:alice",
+ "is": {
+ "contact": {
+ "/": {
+ "link@1": {
+ "id": "user:alice",
+ "path": ["contact"]
+ }
+ }
+ }
+ }
+}
+```
+
+The behavior of setting a property that is a link sigil itself depends on the
+`overwrite` setting:
+
+- When `overwrite` is `"this"` (default): The property is replaced.
+- When `overwrite` is `"redirect"`: The property addressed by the link is
+ updated.
+
+##### Link Write Behavior Examples
+
+The `overwrite` field controls how property assignment behaves:
+
+###### Default Behavior (`overwrite: "this"`)
+
+When assigning a value to a property that is a link with an `overwrite: "this"`
+(default) setting, the property is **replaced with the value**:
+
+```json
+// Before state
+{
+ "the": "application/json",
+ "of": "comment:4737",
+ "is": {
+ "archived": false,
+ "content": "Please add code comment"
+ }
+}
+{
+ "the": "application/json",
+ "of": "note:bcd9124e",
+ "is": {
+ "done": {
+ "/": {
+ "link@1": {
+ "accept": "application/json",
+ "id": "comment:4737",
+ "path": ["archived"]
+ }
+ }
+ }
+ }
+}
+
+// After setting `note.done = true`
+// Addressed fact remains unchanged
+{
+ "the": "application/json",
+ "of": "comment:4737",
+ "is": {
+ "archived": false,
+ "content": "Please add code comment"
+ }
+}
+// Fact containing link sigil is updated
+{
+ "the": "application/json",
+ "of": "note:bcd9124e",
+ "is": {
+ "done": true // Link sigil was replaced with literal value
+ }
+}
+```
+
+The addressed `archived` property of the comment (`comment:4737`) remains
+unchanged.
+
+###### Redirect Behavior (`overwrite: "redirect"`)
+
+When assigning a value to a property that is a link with an
+`overwrite: "redirect"` setting, the value in the addressed memory space is
+updated.
+
+```json
+// Initial state
+{
+ "the": "application/json",
+ "of": "comment:4737",
+ "is": {
+ "archived": false,
+ "content": "Please add code comment"
+ }
+}
+{
+ "the": "application/json",
+ "of": "note:bcd9124e",
+ "is": {
+ "done": {
+ "/": {
+ "link@1": {
+ "accept": "application/json",
+ "id": "comment:4737",
+ "path": ["archived"],
+ ”overwrite”: “redirect”
+ }
+ }
+ }
+ }
+}
+
+// After note.done = true
+// Addressed fact gets updated
+{
+ "the": "application/json",
+ "of": "comment:4737",
+ "is": {
+ "archived": true,
+ "content": "Please add code comment"
+ }
+}
+// Fact containing link sigil remains unchanged
+{
+ "the": "application/json",
+ "of": "note:bcd9124e",
+ "is": {
+ "done": {
+ "/": {
+ "link@1": {
+ "accept": "application/json",
+ "id": "comment:4737",
+ "path": ["archived"]
+ }
+ }
+ }
+ }
+}
+```
+
+### Blob References
+
+Blob references are sigils that need to be interpreted as `Blob` instances. They
+can use inline bytes, immutable references for content, or mutable references
+(link sigils) for binary data that can be updated.
+
+#### Blob Sigil (`blob@1`)
+
+The blob sigil provides references to binary data that should be interpreted as
+a `Blob` instance. It can reference either immutable content via immutable
+references or mutable binary facts via link sigils.
+
+#### Fields
+
+- `type` (optional): Media type of the binary data. If omitted and content is
+ referenced by immutable reference, it will be inferred from content as either
+ `application/json` or `application/octet-stream` depending on the referenced
+ content. If content is referenced via mutable reference (link sigil), content
+ type will be the `type` of resolved data.
+- `content` (required): Either a link sigil, an immutable reference or inline
+ bytes.
+
+#### TypeScript Definition
+
+```typescript
+type BlobSigil {
+ "blob@1": {
+ type?: MediaType
+ content: LinkSigil | Reference | Bytes
+ }
+}
+
+type MediaType = `${string}/${string}`
+```
+
+#### Resolution Behavior
+
+Blob sigils resolve to `Blob` instances by referencing data through the
+`content` field. When `content` is a link sigil, the content type is determined
+by the type of the resolved data. When `content` is an immutable reference, the
+content type is inferred from the content as either `application/json` or
+`application/octet-stream` if no `type` field is specified. When `content` is
+inline bytes, the binary data is embedded directly and the content type defaults
+to `application/octet-stream` if no `type` field is specified. In all cases,
+implementations SHOULD resolve to native `Blob` instances.
+
+#### Relationship to Binary Facts
+
+Blob sigils work directly with the memory protocol's binary fact support by
+referencing facts that contain binary data. This approach enables efficient
+storage and reuse of binary content across multiple references while maintaining
+proper media type information.
+
+#### Example with Link Reference
+
+```json
+{
+ "the": "application/json",
+ "of": "user:alice",
+ "is": {
+ "name": "Alice Smith",
+ "avatar": {
+ "/": {
+ "blob@1": {
+ "type": "image/png",
+ "content": {
+ "/": {
+ "link@1": {
+ "accept": "image/png",
+ "id": "blob:avatar-alice-2024"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+#### Example with Immutable Reference
+
+```json
+{
+ "the": "application/json",
+ "of": "user:alice",
+ "is": {
+ "name": "Alice Smith",
+ "avatar": {
+ "/": {
+ "blob@1": {
+ "type": "image/png",
+ "content": {
+ "/": "ba4jca7rv4dlr5n5uuvcz7iht5omeukavhzbbpmc5w4hcp6dl4y5sfkp5"
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+#### Example with Inline Bytes
+
+```json
+{
+ "the": "application/json",
+ "of": "user:alice",
+ "is": {
+ "name": "Alice Smith",
+ "avatar": {
+ "/": {
+ "blob@1": {
+ "type": "image/png",
+ "content": {
+ "/": {
+ "bytes": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFeAJ5gZ5fCQAAAABJRU5ErkJggg=="
+ }
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+### File References
+
+File references are sigils that extend blob references with filesystem metadata
+and are interpreted as `File` instances.
+
+#### File Sigil (`file@1`)
+
+The file sigil is an extension of the blob sigil that provides references to
+binary data interpreted as `File` instances. It has the same type inference
+rules as blob sigils, plus an optional `name` field for filesystem metadata.
+
+#### TypeScript Definition
+
+```typescript
+type FileSigil = {
+ "file@1": {
+ type?: string; // Media type, same inference rules as blob sigil
+ content?: LinkSigil | Reference | Bytes; // optional reference to binary data
+ name?: string; // optional filename
+ };
+};
+```
+
+## Integration with Memory Protocol
+
+### Fact Structure Preservation
+
+Sigils operate entirely within the `is` field of facts, preserving the memory
+protocol's core structure:
+
+- `the` field remains the media type of the containing fact
+- `of` field remains the resource URI of the containing fact
+- `cause` field maintains causal consistency as defined by the memory protocol
+- Sigils reference other facts but from the memory protocol's perspective are
+ just data structures in the `is` field.
+
+### Query Integration
+
+Sigils work with both the basic `/memory/query` and advanced
+`/memory/graph/query` capabilities:
+
+#### Basic Query Support (`/memory/query`)
+
+Basic queries return sigils in their raw form without following references.
+
+#### Schema Query Support (`/memory/graph/query`)
+
+The [Schema Query Protocol] can follow sigil references and return bundled
+results to reduce roundtrips.
+
+## Migration and Versioning
+
+### Version Evolution
+
+Sigil types include version suffixes (e.g., `link@1`) to enable
+backward-compatible evolution:
+
+1. **New versions**: Can add fields or change behavior while maintaining old
+ version support
+2. **Deprecation**: Old versions should be supported during transition periods
+3. **Migration tools**: Implementations SHOULD provide utilities for upgrading
+ sigil versions
+
+[Common Tools]: https://common.tools/
+[Irakli Gozalishvili]: https://github.com/gozala
+[Memory Protocol]: ./memory.md
+[Binary Data Support]: ./memory-blobs.md
+[Schema Query Protocol]: ./schema-query.md
+[DAG-JSON]: https://ipld.io/specs/codecs/dag-json/spec/
+[bytes]: https://ipld.io/specs/codecs/dag-json/spec/#bytes
+[links]: https://ipld.io/specs/codecs/dag-json/spec/#links
+[merkle reference]: https://github.com/Gozala/merkle-reference
+[Blob]: https://developer.mozilla.org/en-US/docs/Web/API/Blob
+[Accept header]: developer.mozilla.org/en-us/docs/web/http/reference/headers/accept
+[IPLD data model]: https://ipld.io/glossary/#data-model
diff --git a/docs/specs/json_schema.md b/docs/specs/json_schema.md
new file mode 100644
index 000000000..4afc8086e
--- /dev/null
+++ b/docs/specs/json_schema.md
@@ -0,0 +1,318 @@
+# JSON Schema
+
+Our system uses the JSON Schema standard, specifically the 2020-12
+[core](https://json-schema.org/draft/2020-12/json-schema-core) and
+[validation](https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01)
+specifications.
+
+Many cells are linked together in complex graphs. Since clients typically don't
+need all reachable cells, schemas serve as filters to limit what data is
+returned.
+
+We also automatically convert TypeScript types to JSON Schema as part of our
+recipe compilation workflow.
+
+Due to its specialized role in our system, we handle these schemas differently
+from standard implementations in several ways.
+
+## Extensions
+
+We add several custom fields to the schema that are meaningful to our system:
+
+- **`asCell`**: Indicates that instead of storing data directly in the object,
+ the containing object will have a link to a cell, and that linked cell will
+ contain the actual data
+- **`asStream`**: Indicates that this object should be treated as a stream
+ interface for easily connecting events to their listeners, rather than as a
+ traditional cell
+- **`ifc`**: Allows us to specify additional properties, such as the list of
+ `classification` properties associated with the data
+
+## Special Handling of additionalProperties
+
+The `additionalProperties` field in JSON Schema objects defaults to `true`, but
+our system treats this field as having three distinct states:
+
+- **`true`**: Follow additional properties (those not explicitly defined in the
+ schema) to find other linked cells
+- **`false`**: Don't follow additional properties and don't include linked cells
+ from the current object
+- **undefined** (not specified): Follow only the properties explicitly defined
+ in the schema
+
+### Example
+
+Consider a cell with the following contents:
+
+```json
+{
+ "country": "United States",
+ "postalCode": { "/": "baed...0002" }, // link to cell2
+ "state": { "/": "baed...0003" } // link to cell3
+}
+```
+
+**When `additionalProperties` is `false`:**
+
+```json
+{
+ "type": "object",
+ "properties": {
+ "country": { "type": "string" },
+ "postalCode": { "type": "string" }
+ },
+ "additionalProperties": false
+}
+```
+
+Result: We include the queried node but exclude cell2 and cell3.
+
+**When `additionalProperties` is `true`:**
+
+```json
+{
+ "type": "object",
+ "properties": {
+ "country": { "type": "string" },
+ "postalCode": { "type": "string" }
+ },
+ "additionalProperties": true
+}
+```
+
+Result: We include the queried node plus both cell2 and cell3.
+
+**When `additionalProperties` is not specified:**
+
+```json
+{
+ "type": "object",
+ "properties": {
+ "country": { "type": "string" },
+ "postalCode": { "type": "string" }
+ }
+}
+```
+
+Result: We include the queried node plus cell2, but exclude cell3.
+
+## Unsupported Features
+
+We currently don't support a significant subset of JSON Schema validation
+features. While this list isn't exhaustive, here are some key limitations:
+
+The `anyOf` field has limited support. Some parts of our codebase handle `anyOf`
+well, while others are restricted to matching only primitives.
+
+The following operations have minimal or no support:
+
+- Core logic: `allOf`, `anyOf`, `oneOf`, `not`
+- Core conditionals: `if`, `then`, `else`
+- Validation: `enum`
+
+Generally, these limitations make our system more permissive than standard JSON
+Schema implementations.
+
+## TypeScript Type Mappings
+
+TypeScript includes `never`, `void`, and `undefined` types that don't map
+directly to JSON Schema.
+
+### Handling of `undefined`
+
+- In objects: Properties with `undefined` values are removed entirely (both key
+ and value)
+- In arrays: `undefined` values are replaced with `null`
+
+### Handling of `void`
+
+The `void` type is only used for function return types and shouldn't appear
+where we use JSON Schema.
+
+### Handling of `never`
+
+The `never` type is commonly used in scenarios like rejecting invalid
+properties:
+
+```typescript
+type User = {
+ id: number;
+ name: string;
+ email?: string;
+ // This property should never exist
+ password?: never;
+};
+```
+
+We express this pattern in JSON Schema as:
+
+```json
+{
+ "type": "object",
+ "properties": {
+ "id": { "type": "number" },
+ "name": { "type": "string" },
+ "email": { "type": "string" },
+ "password": false
+ },
+ "required": ["id", "name"]
+}
+```
+
+The `never` type is also used for ensuring objects have no properties:
+
+```typescript
+type EmptyObj = Record;
+```
+
+This translates to:
+
+```json
+{
+ "type": "object",
+ "additionalProperties": false
+}
+```
+
+## Schema Narrowing
+
+We sometimes derive schemas from larger schemas, such as when accessing a field
+of one cell as another cell.
+
+### Example
+
+Starting with a contact entry schema:
+
+```json
+{
+ "type": "object",
+ "properties": {
+ "address": {
+ "type": "object",
+ "properties": {
+ "postOfficeBox": { "type": "string" },
+ "extendedAddress": { "type": "string" },
+ "streetAddress": { "type": "string" },
+ "locality": { "type": "string" },
+ "region": { "type": "string" },
+ "postalCode": { "type": "string" },
+ "countryName": { "type": "string" }
+ },
+ "required": ["locality", "region", "countryName"]
+ },
+ "name": { "type": "string" },
+ "phoneNumber": { "type": "string" }
+ }
+}
+```
+
+When accessing the cell's `address` field, we narrow the schema to just that
+portion:
+
+```json
+{
+ "type": "object",
+ "properties": {
+ "postOfficeBox": { "type": "string" },
+ "extendedAddress": { "type": "string" },
+ "streetAddress": { "type": "string" },
+ "locality": { "type": "string" },
+ "region": { "type": "string" },
+ "postalCode": { "type": "string" },
+ "countryName": { "type": "string" }
+ },
+ "required": ["locality", "region", "countryName"]
+}
+```
+
+### Complications with Core Logic
+
+Schema narrowing becomes complex when using operations like `anyOf`. For
+example, if you want to ensure every contact has either an address with a
+`streetAddress` OR a `phoneNumber` on the main contact, you might use `anyOf`
+where the first clause requires `phoneNumber` but not `streetAddress`, and the
+second clause requires `streetAddress` but not `phoneNumber`.
+
+However, when narrowing to just the address portion, we lose the context of how
+fields outside the narrowed schema affect the validation logic. The resulting
+narrowed schema may be more permissive than intended.
+
+This isn't problematic for our design since the schema still fulfills its
+primary role of limiting linked cells and field access, even when more
+permissive than expected.
+
+### Complex Example
+
+**Top-level schema:**
+
+```json
+{
+ "anyOf": [
+ {
+ "type": "object",
+ "properties": {
+ "address": {
+ "type": "object",
+ "properties": {
+ "streetAddress": { "type": "string" },
+ "locality": { "type": "string" },
+ "region": { "type": "string" },
+ "countryName": { "type": "string" }
+ },
+ "required": ["locality", "region", "countryName"]
+ },
+ "name": { "type": "string" },
+ "phoneNumber": { "type": "string" }
+ },
+ "required": ["name", "phoneNumber", "address"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "address": {
+ "type": "object",
+ "properties": {
+ "streetAddress": { "type": "string" },
+ "locality": { "type": "string" },
+ "region": { "type": "string" },
+ "countryName": { "type": "string" }
+ },
+ "required": ["streetAddress", "locality", "region", "countryName"]
+ },
+ "name": { "type": "string" },
+ "phoneNumber": { "type": "string" }
+ },
+ "required": ["name", "address"]
+ }
+ ]
+}
+```
+
+**Derived narrowed schema for the `address` field:**
+
+```json
+{
+ "anyOf": [
+ {
+ "type": "object",
+ "properties": {
+ "streetAddress": { "type": "string" },
+ "locality": { "type": "string" },
+ "region": { "type": "string" },
+ "countryName": { "type": "string" }
+ },
+ "required": ["locality", "region", "countryName"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "streetAddress": { "type": "string" },
+ "locality": { "type": "string" },
+ "region": { "type": "string" },
+ "countryName": { "type": "string" }
+ },
+ "required": ["streetAddress", "locality", "region", "countryName"]
+ }
+ ]
+}
+```
diff --git a/docs/specs/recipe-construction/capability-wrappers.md b/docs/specs/recipe-construction/capability-wrappers.md
new file mode 100644
index 000000000..522e7dc36
--- /dev/null
+++ b/docs/specs/recipe-construction/capability-wrappers.md
@@ -0,0 +1,112 @@
+# Capability Wrappers Prototype
+
+## Objectives
+
+- Collapse `OpaqueRef` and runtime `Cell` APIs into a single object whose
+ surface area is gated by explicit capabilities (`Opaque`, `Readonly`,
+ `Mutable`, `Writeonly`).
+- Keep recipe ergonomics intact by continuing to expose property proxies and
+ helpers like `.map()` while steering operations through capability-aware
+ wrappers.
+- Provide a migration path that lets existing recipes run during the migration
+ window by adapting the current builder implementation incrementally.
+
+## Current Behaviors Informing Design
+
+- `opaqueRef` proxies in `packages/runner/src/builder/opaque-ref.ts` capture
+ nested schema information via `ContextualFlowControl` so that child property
+ accesses can inherit IFC annotations. They also record connected nodes with
+ `.connect` and export builder metadata used by `factoryFromRecipe`.
+- Builders rely on push/pop frames from `recipe.ts` to keep track of
+ `unsafe_binding` context, generated id counters, and the causal object used by
+ `createCell` during runtime invocation.
+- Runtime cells in `packages/runner/src/cell.ts` already expose read, write,
+ redirect, and schema-centric helpers. `createCell` injects causes derived from
+ the enclosing frame before delegating to `runtime.getCell`.
+- Helper factories such as `.map()` call back into `createNodeFactory` to build
+ nested recipes. That indirection means wrappers must cooperate with module
+ factories that expect builder-time proxies yet execute against runtime cells.
+
+## Wrapper Shape
+
+- Introduce a lightweight `CapabilityCell` interface
+ that wraps a concrete runtime cell and exposes operations allowed by
+ capability `C`. Capabilities map to subsets of the current `Cell` API:
+ - `Opaque`: property proxies, `.map`, `.filter`, `.derive`, structural
+ equality, read-only helpers.
+ - `Readonly`: everything in `Opaque` plus `.get`, `.getAsQueryResult`, and
+ schema navigation helpers that do not mutate state.
+ - `Mutable`: superset of `Readonly` plus `.set`, `.update`, `.push`, and
+ `.redirectTo`.
+ - `Writeonly`: `.send`, `.set`, `.redirectTo`, but intentionally hides `.get`
+ to encourage event-sink authoring disciplines.
+- Use `Proxy` objects to intercept property access and method calls. Each proxy
+ lazily constructs child proxies with the same capability so nested lookups
+ remain ergonomic (e.g. `mutable.todos[0].title.set("…")`).
+- When a helper like `.map` is invoked, delegate to a capability-aware shim that
+ wraps the callee recipe factory so the inner function receives wrappers
+ instead of raw cells or `OpaqueRef`s. Internally reuse the existing
+ `createNodeFactory` path to avoid reimplementing module registration.
+
+## Construction Flow
+
+1. Builder entry points (`recipe`, `lift`, `handler`) push a frame, wrap inputs
+ in `CapabilityCell` proxies instead of `opaqueRef`, and still record the
+ graph using existing traversal utilities.
+2. For compatibility, supply adapters that allow legacy `OpaqueRef` helper
+ affordances (`setDefault`, `unsafe_bindToRecipeAndPath`) to continue working
+ until all call sites migrate. These adapters simply forward to the wrapped
+ runtime cell or translate to snapshot metadata updates.
+3. During runtime execution, `pushFrameFromCause` already seeds the frame with
+ the `cause` and `unsafe_binding`. Wrappers created while executing a lifted
+ function can therefore call `runtime.getCell` immediately because the cause
+ data is ready.
+
+## Type System Notes
+
+- Export capability-specific TypeScript types from `@commontools/api` such as
+ `OpaqueCell`, `ReadonlyCell`, `MutableCell`, and `WriteonlyCell`.
+ These extend a shared base that keeps the proxy type information available to
+ recipe authors.
+- Extend our JSON Schema annotations so authors can declare capabilities at any
+ depth. When `asCell: true` is present, allow an `opaque`, `readonly`, or
+ `writeonly` flag (or the closest JSON Schema standard equivalent if one
+ exists). Builder proxies read these flags to choose the capability for the
+ proxy returned by `key()` or nested property access.
+- Provide conditional helper types to map schema metadata to helper surfaces
+ (e.g., array helpers only appear when `T` extends `readonly any[]`). Reuse the
+ IFC-aware schema lookup utilities to keep helper availability aligned with the
+ JSON schema.
+- Augment builders so `recipe` factories default to `OpaqueCell` inputs while
+ `lift` can declare stronger capabilities for each argument via a typed options
+ bag (e.g., `{ inputs: { item: Capability.Mutable } }`).
+
+## Cause Defaults
+
+- Capability helpers (e.g., `.map`, `.filter`) should emit deterministic
+ default causes that combine the parent cell's cause with a helper-specific
+ label. Authors can still pass explicit `cause` overrides to `lift` or
+ `handler`, but most call sites inherit stable defaults automatically.
+- Expose an optional `.setCause(cause: CauseDescriptor)` chain on newly created
+ capability cells. It overrides the derived id before the cell participates in
+ the graph. If the cell has already been connected to a node or materialized
+ into a runtime cell, `.setCause` must throw to avoid inconsistencies.
+
+## Migration Strategy
+
+- Step 1: Introduce wrappers with shims that forward to existing opaque ref
+ behavior. Dual-write metadata (`export()`) so `factoryFromRecipe` can continue
+ serializing the graph until the snapshot pipeline lands.
+- Step 2: Convert builtin helpers and modules to consume wrappers. Audit
+ `packages/runner/src/builder` to replace direct `OpaqueRef` imports with
+ wrapper types while keeping `opaqueRef` available as a thin adapter.
+- Step 3: Update recipes in `recipes/` iteratively. Provide codemods that swap
+ `cell()` usage for capability-specific constructors when explicit mutability is
+ required.
+- Step 4: Remove legacy-only APIs (`setDefault`, `setPreExisting`) once the
+ snapshot work replaces path-based aliasing and defaults come from schemas.
+
+## Open Questions
+
+- When migrating existing schemas to the new capability annotations, should we
+ store capability metadata in-line or factor it into shared schema manifests?
diff --git a/docs/specs/recipe-construction/cause-derivation.md b/docs/specs/recipe-construction/cause-derivation.md
new file mode 100644
index 000000000..0d1740f65
--- /dev/null
+++ b/docs/specs/recipe-construction/cause-derivation.md
@@ -0,0 +1,105 @@
+# Cause Derivation Plan
+
+## Scope
+
+Causes must be stable for the cells that builder helpers (`lift`, `.map`,
+`handler`, etc.) return while assembling a recipe graph. Those helpers execute
+inside a frame created by `pushFrameFromCause`, so the frame already carries the
+cause of the invocation (e.g., handler event, parent lift). The work here is to
+derive deterministic causes for the **new cells** produced inside that frame by
+combining the frame cause with deterministic fingerprints of the inputs and
+implementation.
+
+## Frame Cause Setup
+
+1. When a lift or handler is invoked at runtime, the runtime computes a
+ structured `frameCause` that captures the triggering context (event id,
+ result cell id, handler name, etc.) and pushes it with `pushFrameFromCause`.
+2. `createCell` and other builder helpers can read `frame.cause` when they need
+ baseline information. The frame cause remains untouched while the author’s
+ recipe factory runs.
+
+## Deriving Causes for Builder Outputs
+
+For every helper that returns a cell (including plain `lift`, `handler`,
+`.map`, `.filter`, `recipe`, etc.), derive a cause using the following inputs:
+
+- **Frame cause**: Treat the `frameCause` as the parent component. It ensures
+ all descendants remain tied to the invocation that spawned them.
+- **Input cell ids**: Gather the normalized ids of the input cells passed to the
+ helper. For `.map`, include the list/array cell id plus ids of captured cells
+ in the callback. Wrappers expose `cell.__metadata.cause` or similar to make the
+ ids available without forcing a read.
+- **Implementation fingerprint**: Hash the helper’s implementation. For inline
+ functions reuse the hash already stored in the function cache. For `ref`
+ modules or builtins use their canonical identifier.
+- **Helper discriminator**: Include a stable label describing the helper and its
+ position (e.g., `"lift"`, `"map"`, `"handler"`). If a helper can be invoked
+ multiple times within the same frame (like `.map`), add an index or property
+ path to disambiguate siblings.
+- **Literal/configuration inputs**: Serialize options or literal data that alter
+ behavior (e.g., `.map({ chunkSize: 10 })`).
+
+Combine these pieces into a structured object and feed it to `createRef`:
+
+```ts
+const cause = createRef({
+ parent: frameCause,
+ helper: "lift",
+ impl: implementationHash,
+ inputs: inputIds.sort(),
+ index: helperIndex,
+ config: stableConfigJson,
+});
+```
+
+The resulting entity id seeds the cause for the cell returned by the helper.
+
+### Explicit Overrides
+
+- Newly created capability cells expose `.setCause(cause: CauseDescriptor)` to
+ replace the derived cause with an explicit value (useful for hand-authored
+ stability keys). The override must occur **before** the cell participates in
+ the graph. If the cell has been connected to a node, read, or written, the
+ call must throw so we do not retroactively change ids that other nodes may
+ already reference.
+
+## Propagating Causes to Nested Frames
+
+- When a helper constructs nested recipes or lifts (e.g., `.map` wrapping a
+ callback recipe), push a new frame using the derived cause: `pushFrameFromCause
+ (cause, unsafeBinding)`. This ensures nested cells inherit the parent cause
+ while still deriving their own causes using the same recipe.
+- `createCell` inside the nested frame now starts its `generatedIdCounter` from
+ a hash of `{ parent: cause, path: currentPath }` so sibling inserts don’t
+ shift identifiers.
+
+## Integration Points
+
+- Update capability wrappers so every helper funnels through a single
+ `deriveCause` utility before calling `runtime.getCell`.
+- Extend `pushFrameFromCause` to accept structured causes (with optional
+ metadata) and make `createCell` resilient to both legacy and structured
+ inputs.
+- Ensure `.setCause` checks with the builder runtime whether the cell has been
+ connected or materialized; reject overrides when unsafe.
+- Persist the derived cause string in the graph snapshot so consecutive runs can
+ detect churn.
+
+## Tooling and Instrumentation
+
+- Log derived causes when `RuntimeOptions.debug` is enabled, including the inputs
+ that contributed to the hash. This helps diagnose instability during recipe
+ authoring.
+- Provide a CLI helper (`deno task inspect-causes`) that runs a recipe, prints
+ generated causes, and highlights differences between runs.
+
+## Risks
+
+- **Missing inputs**: Ensure wrappers consistently report input ids even when
+ proxies wrap literals. Fall back to a placeholder (e.g., `"literal"`) so the
+ hash remains deterministic.
+- **Hash collisions**: Use strong hashes (SHA-256) and compress for storage. Keep
+ the full hash available for debugging.
+- **Performance**: Gathering input ids should reuse dependency tracking already
+ performed by the scheduler to avoid extra instrumentation.
diff --git a/docs/specs/recipe-construction/graph-snapshot.md b/docs/specs/recipe-construction/graph-snapshot.md
new file mode 100644
index 000000000..b6eb729ad
--- /dev/null
+++ b/docs/specs/recipe-construction/graph-snapshot.md
@@ -0,0 +1,127 @@
+# Graph Snapshot Schema
+
+## Motivation
+
+- `Runner.setupInternal` currently stores recipe metadata inside the process
+ cell (`TYPE`, `argument`, `internal`, `resultRef`). We plan to deprecate
+ `TYPE` while still persisting the originating program reference (rename the
+ `spell` field to `pattern`). The runtime still lacks a persisted view of the
+ concrete nodes it instantiated.
+- `instantiateNode` performs alias gymnastics through
+ `unwrapOneLevelAndBindtoDoc` so nested recipes and closures work. A snapshot
+ that records the resolved nodes makes this machinery obsolete: future runs can
+ reconstruct bindings directly from the snapshot.
+- Persisting a snapshot on the result cell lets the runtime answer "what does
+ this graph look like" without re-running the factory, simplifying rehydration
+ and teardown.
+
+## Snapshot Envelope
+
+Store an additional `graph` payload on the result cell alongside the existing
+`value`, `source`, and renamed `pattern` metadata. The payload is versioned to
+allow future format changes.
+
+```ts
+interface GraphSnapshotV1 {
+ version: 1;
+ program?: RuntimeProgram;
+ generation?: number; // Incremented each time the runtime rebuilds the graph
+ nodes: Array;
+}
+
+interface ReactiveNodeSnapshot {
+ module: ReactiveModuleDescriptor;
+ inputs: Record;
+ output?: CellLink; // Optional; some modules only read
+ argumentSchema?: JSONSchema;
+ resultSchema?: JSONSchema;
+}
+
+interface EventHandlerSnapshot {
+ module: EventHandlerDescriptor;
+ inputs: Record;
+ stream: CellLink; // Event stream destination
+ argumentSchema?: JSONSchema;
+}
+
+type Binding = CellLink | JSONValue | Record | Array;
+
+type ReactiveModuleDescriptor =
+ | {
+ type: "ref";
+ ref: string;
+ argumentSchema: JSONSchema;
+ resultSchema?: JSONSchema;
+ }
+ | {
+ type: "javascript";
+ implementation: string | { file: string; export: string };
+ argumentSchema: JSONSchema;
+ resultSchema?: JSONSchema;
+ metadata?: Record;
+ };
+
+type EventHandlerDescriptor = ReactiveModuleDescriptor & { handler: true };
+```
+
+- Notes:
+
+ - Only nodes are serialized; cells and links are implied by the cell links
+ used in `inputs`, `output`, or `stream`.
+ - `CellLink` reuses the normalized link format the runtime already
+ understands.
+ - `Binding` supports nested structures so aliases into deeply nested inputs
+ are preserved without separate link tables.
+ - Optional `argumentSchema`/`resultSchema` on the node let nodes further
+ constrain or specialize module signatures without mutating the module
+ definition.
+ - `program` points to the main compiled artifact so tooling can correlate
+ modules with source files (see `packages/runner/src/harness/types.ts` for
+ `RuntimeProgram`).
+ - Descriptor-level `argumentSchema` values MUST describe array/prefix-array
+ schemas so reactive nodes can express tuple-style inputs used by lifts and
+ handlers.
+
+## Generation Flow
+
+1. When `Runner.startWithTx` iterates `recipe.nodes`, instrument each
+ `instantiateNode` call to record:
+ - The resolved module descriptor, including implementation reference.
+ - Normalized input links (aliases already resolved by the capability
+ wrappers so `unwrapOneLevelAndBindtoDoc` is no longer needed).
+ - Either an output link (for reactive nodes) or a stream link (for event
+ handlers), whichever applies.
+ - Optional argument/result schemas attached by the builder.
+2. Compute the final snapshot object, attach it to
+ `resultCell.withTx(tx).setMetadata("graph", snapshot)`, and persist it in the
+ same transaction that finishes setup.
+
+## Rehydration Strategy
+
+- On `Runner.setupInternal`, if a prior snapshot exists, load it before
+ unpacking defaults. The runtime can:
+ - Reattach scheduler subscriptions by walking the nodes, reusing modules whose
+ implementation reference matches.
+ - Rehydrate handler streams directly from their stored links.
+- When a handler or lift causes a graph to rebuild, diff the previous and new
+ node lists. Nodes that disappear are torn down by following their stored
+ output/stream links. Nodes with matching descriptors can reuse their existing
+ cells.
+
+## Teardown and Diffing
+
+- Nodes uniquely define the dependencies; links are inferred from the cell links
+ embedded inside each snapshot entry. Diffing node descriptors is sufficient to
+ drive teardown.
+- Maintain a monotonic `generation` counter on the snapshot. When a handler
+ triggers rehydration, increment the counter so logs can correlate actions with
+ rebuilds.
+
+## Outstanding Questions
+
+- Should snapshots include scheduler bookkeeping (e.g., dependency edges, last
+ run timestamps) or should that remain derived at runtime?
+- How do we store large schemas efficiently? Option: store a content hash in the
+ snapshot and persist the full schema in a shared manifest keyed by hash.
+- What is the cleanest way to encode the `pattern` reference alongside the
+ snapshot so older runtimes can ignore it while newer ones rely on it?
diff --git a/docs/specs/recipe-construction/implementation-plan-v2.md b/docs/specs/recipe-construction/implementation-plan-v2.md
new file mode 100644
index 000000000..f2f314a3f
--- /dev/null
+++ b/docs/specs/recipe-construction/implementation-plan-v2.md
@@ -0,0 +1,54 @@
+# Implementation Plan (V2 Opt-In)
+
+## Phase 0: Integration Test Harness
+
+- Build a recipe integration harness that can run `.tsx` recipes from
+ `packages/patterns/` (and other sample directories) inside the runtime.
+- Allow tests to declare:
+ - Initial arguments/documents (by path or schema-aware fixtures).
+ - Expected result snapshots (selective path assertions to keep fixtures small).
+ - Event sequences: each entry specifies the stream cell path to emit on and
+ the event payload, followed by updated expectations.
+- Run the harness against the current implementation to produce a baseline
+ digest. These tests must be shared between V1 and V2 so behavior stays aligned.
+
+## Phase 1: Introduce V2 Entry Points
+
+- Add `recipeV2`, `liftV2`, and `handlerV2` exports (plus builder re-exports via
+ `createBuilder`) that immediately expose capability wrappers and the deferred
+ execution lifecycle.
+- Keep existing `recipe`/`lift`/`handler` untouched; they continue invoking the
+ factory eagerly and serializing legacy metadata.
+- Wire the integration test harness to run each scenario twice: once under V1
+ entry points, once under V2.
+
+## Phase 2: Runtime Support Behind Opt-In
+
+- Teach the runtime to detect V2 recipes via metadata stored on result cells and
+ execute the new pipeline:
+ - Generate the node-only graph snapshot (`graph`, `pattern`, `generation`).
+ - Use capability wrappers for runtime instantiation instead of `OpaqueRef`.
+ - Apply deterministic causes and respect `.setCause`.
+- Ensure V2 code paths can coexist with legacy metadata so the runtime can run
+ both versions simultaneously.
+
+## Phase 3: Serializable Node Factories
+
+- Implement the `nodeFactory@1` sigil, `.curry`, and automatic deserialization
+ for V2 factories. Legacy V1 factories continue using current serialization.
+- Extend the integration harness to assert that serialized factories round-trip
+ correctly when returned from recipes or passed through cells/events.
+
+## Phase 4: Migration & Parity
+
+- Incrementally migrate internal recipes to `recipeV2`/`liftV2` once tests pass.
+- Keep running the dual-mode integration suite to confirm V1 and V2 stay in
+ sync. Fix divergences in V2 before moving additional recipes.
+- Provide documentation and codemods to ease recipe author migration.
+
+## Phase 5: Flip Default
+
+- After V2 covers all production-like scenarios, alias the default exports to
+ the V2 implementations (`recipe = recipeV2`, etc.).
+- Remove V1-only code paths, including `OpaqueRef`, `TYPE`, and legacy aliasing.
+- Retain the integration tests as regression coverage for the unified system.
diff --git a/docs/specs/recipe-construction/node-factory-shipping.md b/docs/specs/recipe-construction/node-factory-shipping.md
new file mode 100644
index 000000000..ebf1e1cfb
--- /dev/null
+++ b/docs/specs/recipe-construction/node-factory-shipping.md
@@ -0,0 +1,97 @@
+# Serializable Node Factories
+
+## Goals
+
+- Allow lifts, handlers, and recipes to return reusable node factories that can
+ be shipped across spaces or persisted and later instantiated.
+- Support partial application via a `.curry(value, argIndex = 0)` helper that
+ yields another shippable node factory reflecting the bound argument.
+- Use a versioned sigil format (`nodeFactory@1`) that closely mirrors the graph
+ snapshot schema while capturing the additional data required to rehydrate the
+ factory and any curry operations performed on it.
+
+## User Experience
+
+- Any node factory created by `lift`, `handler`, or builder helpers returns a
+ callable object that also exposes `.toJSON()` producing the `nodeFactory@1`
+ sigil.
+- Recipes may return node factories or pass them into other lifts/recipes. When
+ the runtime encounters a `nodeFactory@1` payload (e.g., via `Cell.get()` or as
+ a recipe input), it materializes a callable node factory automatically.
+- `.curry(value, argIndex = 0)` returns a new factory that records the bound
+ argument. Currying is composable; each successive call appends metadata so the
+ runtime can reconstruct the applied arguments before instantiation.
+
+## Sigil Shape
+
+Serialized form:
+
+```json
+{
+ "/": {
+ "nodeFactory@1": {
+ "module": ReactiveModuleDescriptor | EventHandlerDescriptor,
+ "program": RuntimeProgram,
+ "curried": [
+ {
+ "index": 0,
+ "value": Binding
+ }
+ ],
+ "argumentSchema": JSONSchema,
+ "resultSchema": JSONSchema | null,
+ "metadata": Record | null
+ }
+ }
+}
+```
+
+Notes:
+
+- `module` matches the descriptors used in `graph-snapshot.md`, including
+ implementation references and descriptor-level `argumentSchema`/`resultSchema`.
+- `program` stores the complete `RuntimeProgram` (as defined in
+ `packages/runner/src/harness/types.ts`) so the runtime knows how to load the
+ compiled artifact and entry symbol.
+- `curried` is ordered; each entry binds `value` to the given `index` (0-based by
+ default). `Binding` mirrors the nested binding structure from the snapshot so
+ bound values can include cell links.
+- `argumentSchema` on the sigil represents the effective schema after all
+ currying. It must continue to describe array/prefix-array inputs.
+- `resultSchema` is optional and may be omitted/`null` for handlers.
+- `metadata` carries optional helper information (e.g., helper names) and may be
+ omitted if unused.
+
+## Runtime Behavior
+
+- Deserialization: When the runtime reads a value containing the `nodeFactory@1`
+ sigil, it constructs a callable factory that:
+ 1. Applies stored currying bindings before invoking the underlying module.
+ 2. Exposes `.curry` to append additional entries to `curried` and returns a new
+ serialized-aware wrapper.
+ 3. Implements `.toJSON()` to emit the sigil.
+- Invocation: Calling the materialized factory schedules a node instantiation
+ identical to calling the original lift/handler. Inputs are merged with stored
+ curry bindings using positional logic (array inputs) or schema-derived names.
+- Integration with snapshots: When a recipe instantiates a node from a serialized
+ factory, the resulting node appears in the graph snapshot exactly like a
+ regular node (same descriptor, inputs, etc.). The factory sigil itself can also
+ be stored in recipe state for later reuse.
+
+## Builder Integration
+
+- Builders surface `.curry` on node factories returned from `lift`, `handler`,
+ `.map`, etc. Currying prior to returning from a recipe automatically serializes
+ the bound arguments.
+- Recipes accepting factories receive them as callable objects. Internally the
+ runtime keeps the sigil available so passing the factory back to storage or
+ another recipe preserves currying metadata.
+
+## Open Questions
+
+- Do we need an explicit way to specify argument names alongside indexes for
+ currying when dealing with object-shaped schemas?
+- Should we limit the size of serialized `RuntimeProgram` payloads by hashing or
+ referencing a shared cache entry instead of embedding the entire program?
+- How do we ensure backwards compatibility if the module descriptor expands? The
+ sigil version (`nodeFactory@1`) gives us room to add future revisions.
diff --git a/docs/specs/recipe-construction/overview.md b/docs/specs/recipe-construction/overview.md
new file mode 100644
index 000000000..60b2ae0b8
--- /dev/null
+++ b/docs/specs/recipe-construction/overview.md
@@ -0,0 +1,211 @@
+# Recipe Graph Unification Spec
+
+## Summary
+
+Unify builder-time `OpaqueRef` proxies and runtime `Cell` objects into a single
+capability-driven cell model. The new model preserves recipe ergonomics
+(implicit property access, helpers like `.map`) while producing a concrete
+runtime graph with stable cell identifiers that the runtime can persist,
+rehydrate, and tear down. Result cells become the canonical owners of graph
+metadata, and lifts and handlers gain cause-based identifier stability.
+
+## Goals
+
+- Provide a single cell abstraction with explicit capabilities (`Opaque`,
+ `Mutable`, `Readonly`, `Writeonly`) that behaves consistently in builder code
+ and at runtime.
+- Preserve existing recipe ergonomics—automatic property proxies and helper
+ methods—so authored code remains concise.
+- Capture a post-instantiation graph snapshot with concrete cell ids so the
+ runtime can rehydrate or tear down dynamic graphs.
+- Stabilize ids across small edits by deriving causes from input cells plus
+ implementation details.
+
+## Non-goals
+
+- Redesigning scheduler semantics beyond the current "ignore self-writes" and
+ 100-iteration cap.
+- Guaranteeing backward compatibility for the existing process cell layout; the
+ process cell will be replaced by the graph snapshot stored on the result
+ cell.
+- Delivering a full type-level capability system across the entire codebase in
+ this phase; scope is builder APIs and runtime interfaces.
+
+## Background
+
+### Current builder flow
+
+- `packages/runner/src/builder/recipe.ts` pushes a frame, runs the author’s
+ factory immediately, and records every `OpaqueRef` and node it touches.
+- Inputs are wrapped in `opaqueRef` proxies so the builder can serialize graphs
+ before runtime cells exist.
+- `factoryFromRecipe` walks the returned structure, synthesizes paths
+ ("argument" and `internal/__#n`), infers defaults, and emits JSON with
+ `argumentSchema`, `resultSchema`, `initial`, `result`, and serialized nodes.
+ No runtime cell ids exist yet.
+- `OpaqueRef` proxies track connected nodes, expose helpers like `.map()`, and
+ rely on shadow refs to reuse parent cells without leaking frames.
+- `lift` transforms a function into a module factory whose implementation runs
+ reactively once live. Handlers wrap functions to produce streams and run once
+ per event.
+
+### Runtime instantiation today
+
+- `Runner.setup` ensures each result cell has a paired process cell storing
+ `TYPE` (recipe id), `argument`, `internal` state, `resultRef`, and optional
+ spell links. Result cells expose generated data plus `source` metadata.
+- `setupInternal` merges defaults into the process cell and binds the serialized
+ recipe graph via `unwrapOneLevelAndBindtoDoc`; aliases remain path based.
+- `startWithTx` iterates serialized nodes, resolves modules, and calls
+ `instantiateNode`, turning aliases into real `Cell` instances through
+ `sendValueToBinding`. The scheduler maintains reactivity.
+- Handlers can emit new recipes; lifts that return recipes spawn fresh graphs
+ and register teardown hooks.
+
+## Repository Observations
+
+- `packages/runner/src/builder/opaque-ref.ts` shows how `opaqueRef` proxies
+ track connected nodes via `.connect`, compute nested schema metadata with
+ `ContextualFlowControl`, and expose helpers such as `.map`. Capability
+ wrappers must recreate these affordances against real runtime cells.
+- `packages/runner/src/builder/factory.ts` pushes frames before calling the
+ author factory. `createCell` expects the frame to provide a `cause` and an
+ `unsafe_binding`, so the new wrappers must keep the frame lifecycle intact.
+- `packages/runner/src/runner.ts` writes recipe metadata into a process cell
+ (`TYPE`, `argument`, `internal`, `resultRef`) and later instantiates nodes by
+ unwrapping aliases in `unwrapOneLevelAndBindtoDoc`. Snapshot generation should
+ hook into this instantiation path to capture concrete cell ids.
+- `packages/runner/src/create-ref.ts` hashes the supplied `cause` and recorded
+ structure to derive entity ids. Stable causes therefore hinge on the data we
+ pass into frames when new cells are materialized.
+
+## Problem Statement
+
+- Dual abstractions (`OpaqueRef` vs `Cell`) confuse authors and limit helper
+ availability.
+- Recipes lack persisted runtime graphs, making rehydration and teardown fragile
+ for handler-produced or reactive graphs.
+- Alias metadata hinges on synthesized paths that shift as recipes evolve.
+- Cause assignment for runtime-created cells is ad hoc, so ids change under
+ small edits.
+
+## Proposed Design
+
+### Capability-driven cells
+
+- Keep the core `Cell` implementation for runtime internals.
+- Introduce capability wrappers (`Mutable`, `Readonly`, `Writeonly`, `Opaque`)
+ that proxy `Cell` instances.
+- Proxies map property access (`cell.foo`) to `cell.key("foo")` and surface
+ helpers. `.map`, `.filter`, etc., appear on array-like cells; unknown schemas
+ fall back to property access semantics.
+- Invoking proxied helpers (`cell.map(...)`) routes through the proxy so we can
+ distinguish reading vs executing members.
+- Remove `.setDefault`; rely on schema defaults. Other helpers become available
+ wherever semantically valid.
+- Rename the combination of opaque refs and literals to `OpaqueValue` so lifts
+ can accept cells or plain data uniformly.
+
+### Recipe definition and authorship
+
+- Treat `recipe(...)` as sugar for a `lift` whose inputs default to `Opaque`
+ cells. The author’s factory still runs immediately, returning capability-
+ wrapped cells instead of opaque refs.
+- Property access in recipes continues to use proxy wrappers, so existing code
+ (e.g., `items.map(...)`) keeps working.
+- Lifts and handlers may accept an optional `cause`. When omitted, the runtime
+ derives the cause from input cell ids and a hash of the implementation body.
+
+### Runtime graph instantiation
+
+- Instantiating a recipe produces a concrete graph snapshot with real cell ids,
+ capability kinds, aliases, and module bindings.
+- The snapshot is stored alongside `value` and `source` metadata on the result
+ cell. Documents written by the graph keep `source` pointing back to that
+ result cell.
+- Snapshot metadata is sufficient to rehydrate handler-generated graphs and to
+ tear down dynamic graphs before rebuilding them on change.
+
+### Serialization format
+
+- Define a versioned structure containing `graphVersion`, `cells`, `nodes`, and
+ `links`.
+ - `cells`: id, capability, cause, schema hash, redirect targets.
+ - `nodes`: module reference plus input/output bindings rewritten to concrete
+ cell ids.
+- Maintain backward compatibility by leaving existing `resultRef`/`source`
+ fields untouched and appending a `graph` payload.
+
+### Cause generation
+
+- Default cause: hash of (input cell ids + implementation source).
+- Optional `cause` parameter on `lift`, `handler`, and recipe factories
+ overrides the default when authors provide stable names.
+- Causes feed into cell id derivation to keep ids stable under benign edits.
+
+## Impacted Areas
+
+- Builder APIs (`recipe`, `lift`, `handler`, helper exports).
+- Runtime cell creation, alias resolution, and recipe instantiation.
+- Serialization (`json-utils.ts`, recipe manager persistence, result metadata).
+- Tests and tooling that assume path-based aliases or opaque-ref-only helpers.
+
+## Implementation Plan
+
+1. **Capability wrappers**
+ - Implement proxy-based wrappers and surface them from the builder API.
+ - Update TypeScript definitions for capability-aware helpers.
+2. **Unify builder outputs**
+ - Adapt `recipe`/`lift` to emit capability-wrapped cells.
+ - Replace `OpaqueRef` internals with compatibility shims or adapters.
+3. **Graph snapshot generation**
+ - Build runtime graph snapshots during instantiation and store them in result
+ cell metadata.
+ - Update rehydration/teardown logic to consume the snapshot.
+4. **Cause derivation**
+ - Implement default hashing and expose explicit overrides.
+ - Ensure handler-spawned recipes reuse ids whenever inputs and code are
+ unchanged.
+5. **Cleanup**
+ - Deprecate shadow refs and frame gymnastics once proxies land.
+ - Remove `.setDefault`; rely on schema-level defaults.
+6. **Testing & documentation**
+ - Update builder/runner tests for new helper behavior and metadata.
+ - Document capability wrappers and graph metadata for recipe authors.
+
+## Testing Strategy
+
+- Extend recipe and runner tests (e.g., `recipes/todo-list.tsx`) to assert
+ capability wrapper behavior, array helpers, and redirect semantics.
+- Add instantiation tests that serialize the new graph snapshot and rehydrate
+ it, focusing on handler-generated graphs.
+- Verify scheduler behavior (ignored self-writes, iteration cap) with the new
+ cell model.
+- Regression tests for `cellA.set(cellB)` vs `cellA.redirectTo(cellB)`.
+
+## Risks & Mitigations
+
+- **TypeScript proxy typing gaps.** Provide dedicated wrapper types with helper
+ availability per schema; fall back to index signatures when needed.
+- **Snapshot size/performance.** Start with minimal metadata (ids, capabilities,
+ aliases) and profile before expanding.
+- **Backward compatibility.** Introduce shims so existing recipes run unchanged
+ during migration.
+
+## Open Questions
+
+- How can authors override default helper capability mapping without undermining
+ safety guarantees?
+- What exact schema/versioning do we use for the graph snapshot payload?
+
+## Next Steps
+
+- Prototype capability wrappers and convert a sample recipe to validate
+ ergonomics (see `capability-wrappers.md`).
+- Draft and iterate on the graph snapshot schema, then review with
+ runtime/storage stakeholders (`graph-snapshot.md`).
+- Implement rehydration against the stored graph snapshot and exercise it with
+ a sample recipe while observing cause stability (`graph-snapshot.md`,
+ `cause-derivation.md`).
+- Plan the migration and documentation rollout for existing recipes
+ (`rollout-plan.md`).
diff --git a/docs/specs/recipe-construction/recipe-integration-tests.md b/docs/specs/recipe-construction/recipe-integration-tests.md
new file mode 100644
index 000000000..7a31fde9d
--- /dev/null
+++ b/docs/specs/recipe-construction/recipe-integration-tests.md
@@ -0,0 +1,76 @@
+# Recipe Integration Test Harness
+
+## Objectives
+
+- Provide end-to-end coverage that exercises recipes (especially in
+ `packages/patterns/`) under both the legacy (V1) and new (V2) builder/runtime
+ pipelines.
+- Capture expected behavior as structured fixtures so we can compare outputs
+ before and after the V2 migration without rewriting tests.
+
+## Test Artifact Structure
+
+Each recipe test case consists of:
+
+- **Recipe module**: A `.tsx` or `.ts` file exporting a recipe, lift, or handler.
+- **Fixture file** (proposed `.recipe.test.json` or `.recipe.test.ts`):
+ - `arguments`: Optional initial argument payload (JSON) passed to the recipe.
+ - `initialState`: Optional set of documents/cells to seed before running.
+ - `assertions`: Array of checkpoints. Each checkpoint contains:
+ - `expect`: A list of `{ path: string, value: JSONValue }` assertions on the
+ result cell (paths use dot/bracket notation or JSON pointers).
+ - `events`: Optional array of events to dispatch before the next checkpoint.
+ Each event is `{ stream: string, payload: JSONValue }`, where `stream` is a
+ cell link/path within the result graph.
+- **Optional snapshot**: For debugging we can persist the generated graph
+ snapshot and compare it between runs. Assertions stay selective to avoid
+ brittle fixtures.
+
+Example fixture snippet:
+
+```json
+{
+ "arguments": { "initialCount": 1 },
+ "assertions": [
+ { "expect": [{ "path": "result.count", "value": 1 }] },
+ {
+ "events": [{ "stream": "result.increment", "payload": {} }],
+ "expect": [{ "path": "result.count", "value": 2 }]
+ }
+ ]
+}
+```
+
+## Harness Execution Flow
+
+1. Compile the recipe module (shared pipeline for V1 and V2).
+2. Run the recipe twice per fixture:
+ - **V1 mode**: Use current `recipe`/`lift`/`handler` exports.
+ - **V2 mode**: Swap in `recipeV2`/`liftV2`/`handlerV2` and new runtime paths.
+3. For each mode:
+ - Instantiate the runtime, seed initial state, run the recipe, and evaluate
+ assertions.
+ - For checkpoints with events, emit them sequentially via handler streams and
+ re-run assertions after the scheduler settles.
+4. Report differences clearly (e.g., mismatched values, missing paths).
+5. Optionally dump V1 vs V2 graph snapshots when failures occur to aid debugging.
+
+## Tooling Considerations
+
+- Implement harness using Deno test runner (`deno task test`) so it integrates
+ with existing CI scripts.
+- Provide utilities for referencing cells/streams via friendly syntax (e.g.,
+ resolve `result.increment` to the correct link automatically using the stored
+ snapshot or capability wrappers).
+- Allow fixtures to specify tolerance for unordered arrays or partial structures
+ when recipes return collections.
+
+## Rollout
+
+- Add the harness and fixtures under `packages/runner/integration/` so UI-facing
+ patterns remain focused on front-end rendering scenarios.
+- Seed the suite with high-impact recipes (mirroring those in
+ `packages/patterns/`) to validate end-to-end behavior.
+- Expand coverage to other workspaces once the harness stabilizes.
+- Keep fixtures mode-agnostic so future migrations (beyond V2) can reuse the
+ same tests.
diff --git a/docs/specs/recipe-construction/rollout-plan.md b/docs/specs/recipe-construction/rollout-plan.md
new file mode 100644
index 000000000..36c73f244
--- /dev/null
+++ b/docs/specs/recipe-construction/rollout-plan.md
@@ -0,0 +1,42 @@
+# Migration and Rollout Plan
+
+## Phases
+
+1. **Prototype in Runner Sandbox**
+ - Implement capability wrappers behind a feature flag. Gate entry points in
+ `createBuilder` so recipes opt in per-runtime.
+ - Add regression tests under `packages/runner/src/builder/__tests__` that
+ exercise both legacy `OpaqueRef` paths and the new wrapper behavior.
+2. **Snapshot and Cause Pilot**
+ - Enable graph snapshot generation for internal recipes only. Use the
+ `RecipeManager` to tag participating recipes so telemetry can compare
+ runtime rebuild times pre- and post-snapshot.
+ - Surface snapshot diffs in a development panel (reuse existing harness UI)
+ to validate stability guarantees.
+3. **Recipe Migration**
+ - Provide codemods or manual guides to migrate high-traffic recipes in the
+ `recipes/` workspace. Update `recipes/README.md` to describe capabilities
+ and snapshot expectations.
+ - Teach docs and tutorials to reference capability wrappers instead of
+ `OpaqueRef`. Update `docs/common/COMPONENTS.md` once APIs stabilize.
+4. **Cleanup and Deprecation**
+ - Remove `OpaqueRef` exports from `@commontools/api` after recipes migrate.
+ - Delete shadow-ref utilities and legacy alias handling once snapshots power
+ rehydration. Simplify `factoryFromRecipe` to persist capability metadata
+ instead of path-derived aliases.
+
+## Testing Strategy
+
+- Extend existing recipe integration tests (`recipes/todo-list.tsx`, scheduler
+ suites) to assert snapshot creation, cause stability, and wrapper helper
+ behavior.
+- Add rehydration tests that serialize a snapshot, mutate result metadata, and
+ ensure the runtime can rebuild without rerunning the recipe factory.
+
+## Communication
+
+- Publish an ADR summarizing the capability model and snapshot design for review
+ by runtime, storage, and tooling stakeholders.
+- Coordinate with DX and documentation teams to schedule updates to learning
+ materials. Provide example migrations that highlight mixed-capability inputs
+ and handler rehydration flows.
diff --git a/docs/tools.md b/docs/tools.md
deleted file mode 100644
index 2f41b6229..000000000
--- a/docs/tools.md
+++ /dev/null
@@ -1,20 +0,0 @@
-## Toolchains / runtimes
-
-- [Rust][rustup]
-- [Deno][deno]
-- [Node.js][nvm]
-
-### Rust toolchain variations
-
-`rustup target add wasm32-wasi`
-
-## Other tools
-
-- [wasm-tools][wasm-tools]: `cargo install wasm-tools`
-- [jco][jco]: `npm install @bytecodealliance/jco`
-
-[rustup]: https://rustup.rs/
-[deno]: https://docs.deno.com/runtime/manual/getting_started/installation
-[nvm]: https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating
-[wasm-tools]: https://github.com/bytecodealliance/wasm-tools
-[jco]: https://github.com/bytecodealliance/jco
\ No newline at end of file
diff --git a/docs/usuba.md b/docs/usuba.md
deleted file mode 100644
index dc9a0ca63..000000000
--- a/docs/usuba.md
+++ /dev/null
@@ -1,41 +0,0 @@
-## Local Usuba Setup
-
-Install some environment dependencies:
-
-```sh
-cargo install wit-deps-cli wasm-tools
-rustup target add wasm32-wasi wasm32-unknown-unknown
-
-npm install -g @bytecodealliance/jco
-npm install -g @bytecodealliance/componentize-js
-
-pip install componentize-py
-```
-
-On a Mac, also do this also:
-
-```sh
-brew install wget gsed
-```
-
-Set up an NPM project (using whatever stack) in `/typescript/packages` and add `@commontools/runtime` to your dependencies:
-
-```sh
-mkdir -p ./typescript/packages/my-project
-cd ./typescript/packages/my-project
-
-# Actually, better to copy a package.json from another package
-echo '{"type": "module"}' > ./package.json
-npm install --save @commontools/runtime
-```
-
-Get a web server going and note the `PORT` it is running on.
-
-In another terminal, get `usuba` running:
-
-```sh
-cd ./rust/usuba
-UPSTREAM=localhost:$PORT cargo run --bin usuba
-```
-
-Open your browser to http://localhost:8080. You should see whatever you would expect to see from the first web server you started.
diff --git a/packages/api/deno.json b/packages/api/deno.json
new file mode 100644
index 000000000..ec7a3cebb
--- /dev/null
+++ b/packages/api/deno.json
@@ -0,0 +1,9 @@
+{
+ "name": "@commontools/api",
+ "tasks": {
+ "test": "echo 'No tests to run.'"
+ },
+ "exports": {
+ ".": "./index.ts"
+ }
+}
diff --git a/packages/api/index.ts b/packages/api/index.ts
new file mode 100644
index 000000000..e57bf80c1
--- /dev/null
+++ b/packages/api/index.ts
@@ -0,0 +1,934 @@
+/**
+ * Public interface for the builder package. This module exports only the types
+ * and functions that are part of the public recipe API.
+ *
+ * Workspace code should import these types via `@commontools/builder`.
+ */
+
+export const ID: unique symbol = Symbol("ID, unique to the context");
+export const ID_FIELD: unique symbol = Symbol(
+ "ID_FIELD, name of sibling that contains id",
+);
+
+// Should be Symbol("UI") or so, but this makes repeat() use these when
+// iterating over recipes.
+export const TYPE = "$TYPE";
+export const NAME = "$NAME";
+export const UI = "$UI";
+
+// Cell type with only public methods
+export interface Cell {
+ // Public methods available in spell code and system
+ get(): Readonly;
+ set(value: T): void;
+ send(value: T): void; // alias for set
+ update(values: Partial): void;
+ push(...value: T extends (infer U)[] ? U[] : never): void;
+ equals(other: Cell): boolean;
+ key(valueKey: K): Cell;
+}
+
+// Cell type with only public methods
+export interface Stream {
+ send(event: T): void;
+}
+
+export type OpaqueRef =
+ & OpaqueRefMethods
+ & (T extends Array ? Array>
+ : T extends object ? { [K in keyof T]: OpaqueRef }
+ : T);
+
+// Any OpaqueRef is also an Opaque, but can also have static values.
+// Use Opaque in APIs that get inputs from the developer and use OpaqueRef
+// when data gets passed into what developers see (either recipe inputs or
+// module outputs).
+export type Opaque =
+ | OpaqueRef
+ | (T extends Array ? Array>
+ : T extends object ? { [K in keyof T]: Opaque }
+ : T);
+
+// OpaqueRefMethods type with only public methods
+export interface OpaqueRefMethods {
+ get(): T;
+ set(value: Opaque | T): void;
+ key(key: K): OpaqueRef;
+ setDefault(value: Opaque | T): void;
+ setName(name: string): void;
+ setSchema(schema: JSONSchema): void;
+ map(
+ fn: (
+ element: T extends Array ? Opaque : Opaque,
+ index: Opaque,
+ array: T,
+ ) => Opaque,
+ ): Opaque;
+}
+
+// Factory types
+
+// TODO(seefeld): Subset of internal type, just enough to make it
+// differentiated. But this isn't part of the public API, so we need to find a
+// different way to handle this.
+export interface Recipe {
+ argumentSchema: JSONSchema;
+ resultSchema: JSONSchema;
+}
+export interface Module {
+ type: "ref" | "javascript" | "recipe" | "raw" | "isolated" | "passthrough";
+}
+
+export type toJSON = {
+ toJSON(): unknown;
+};
+
+export type Handler = Module & {
+ with: (inputs: Opaque>) => OpaqueRef;
+};
+
+export type NodeFactory =
+ & ((inputs: Opaque) => OpaqueRef)
+ & (Module | Handler | Recipe)
+ & toJSON;
+
+export type RecipeFactory =
+ & ((inputs: Opaque) => OpaqueRef)
+ & Recipe
+ & toJSON;
+
+export type ModuleFactory =
+ & ((inputs: Opaque) => OpaqueRef)
+ & Module
+ & toJSON;
+
+export type HandlerFactory =
+ & ((inputs: Opaque>) => OpaqueRef)
+ & Handler
+ & toJSON;
+
+// JSON types
+
+export type JSONValue =
+ | null
+ | boolean
+ | number
+ | string
+ | JSONArray
+ | JSONObject & IDFields;
+
+export interface JSONArray extends ArrayLike {}
+
+export interface JSONObject extends Record {}
+
+// Annotations when writing data that help determine the entity id. They are
+// removed before sending to storage.
+export interface IDFields {
+ [ID]?: unknown;
+ [ID_FIELD]?: unknown;
+}
+
+// Valid values for the "type" property of a JSONSchema
+export type JSONSchemaTypes =
+ | "object"
+ | "array"
+ | "string"
+ | "integer"
+ | "number"
+ | "boolean"
+ | "null";
+
+// See https://json-schema.org/draft/2020-12/json-schema-core
+// See https://json-schema.org/draft/2020-12/json-schema-validation
+// There is a lot of potential validation that is not handled, but this object
+// is defined to support them, so that generated schemas will still be usable.
+// TODO(@ubik2) When specifying a JSONSchema, you can often use a boolean
+// This is particularly useful for specifying the schema of a property.
+// That will require reworking some things, so for now, I'm not doing it
+export type JSONSchema = JSONSchemaObj | boolean;
+
+export type JSONSchemaObj = {
+ readonly $ref?: string;
+ readonly $defs?: Readonly>;
+ /** @deprecated Use `$defs` for 2019-09/Draft 8 or later */
+ readonly definitions?: Readonly>;
+
+ // Subschema logic
+ readonly allOf?: readonly (JSONSchema)[]; // not validated
+ readonly anyOf?: readonly (JSONSchema)[]; // not always validated
+ readonly oneOf?: readonly (JSONSchema)[]; // not validated
+ readonly not?: JSONSchema;
+ // Subschema conditionally - none applied
+ readonly if?: JSONSchema;
+ readonly then?: JSONSchema;
+ readonly else?: JSONSchema;
+ readonly dependentSchemas?: Readonly>;
+ // Subschema for array
+ readonly prefixItems?: (JSONSchema)[]; // not validated
+ readonly items?: Readonly;
+ readonly contains?: JSONSchema; // not validated
+ // Subschema for object
+ readonly properties?: Readonly>;
+ readonly patternProperties?: Readonly>; // not validated
+ readonly additionalProperties?: JSONSchema;
+ readonly propertyNames?: JSONSchema; // not validated
+
+ // Validation for any
+ readonly type?: JSONSchemaTypes | readonly JSONSchemaTypes[];
+ readonly enum?: readonly Readonly[]; // not validated
+ readonly const?: Readonly; // not validated
+ // Validation for numeric - none applied
+ readonly multipleOf?: number;
+ readonly maximum?: number;
+ readonly exclusiveMaximum?: number;
+ readonly minimum?: number;
+ readonly exclusiveMinimum?: number;
+ // Validation for string - none applied
+ readonly maxLength?: number;
+ readonly minLength?: number;
+ readonly pattern?: string;
+ // Validation for array - none applied
+ readonly maxItems?: number;
+ readonly minItems?: number;
+ readonly uniqueItems?: boolean;
+ readonly maxContains?: number;
+ readonly minContains?: number;
+ // Validation for object
+ readonly maxProperties?: number; // not validated
+ readonly minProperties?: number; // not validated
+ readonly required?: readonly string[];
+ readonly dependentRequired?: Readonly>; // not validated
+
+ // Format annotations
+ readonly format?: string; // not validated
+
+ // Contents - none applied
+ readonly contentEncoding?: string;
+ readonly contentMediaType?: string;
+ readonly contentSchema?: JSONSchema;
+
+ // Meta-Data
+ readonly title?: string;
+ readonly description?: string;
+ readonly default?: Readonly;
+ readonly readOnly?: boolean;
+ readonly writeOnly?: boolean;
+ readonly examples?: readonly Readonly[];
+ readonly $schema?: string;
+ readonly $comment?: string;
+
+ // Common Tools extensions
+ readonly [ID]?: unknown;
+ readonly [ID_FIELD]?: unknown;
+ // makes it so that your handler gets a Cell object for that property. So you can call .set()/.update()/.push()/etc on it.
+ readonly asCell?: boolean;
+ // streams are what handler returns. if you pass that to another handler/lift and declare it as asSteam, you can call .send on it
+ readonly asStream?: boolean;
+ // temporarily used to assign labels like "confidential"
+ readonly ifc?: { classification?: string[]; integrity?: string[] };
+};
+
+// LLM types matching Vercel AI SDK structure
+export type BuiltInLLMTextPart = {
+ type: "text";
+ text: string;
+};
+
+export type BuiltInLLMImagePart = {
+ type: "image";
+ image: string | Uint8Array | ArrayBuffer | URL;
+};
+
+export type BuiltInLLMToolCallPart = {
+ type: "tool-call";
+ toolCallId: string;
+ toolName: string;
+ input: Record;
+};
+
+export type BuiltInLLMToolResultPart = {
+ type: "tool-result";
+ toolCallId: string;
+ toolName: string;
+ output: { type: "text"; value: string } | { type: "json"; value: any };
+};
+
+export type BuiltInLLMContentPart =
+ | BuiltInLLMTextPart
+ | BuiltInLLMImagePart
+ | BuiltInLLMToolCallPart
+ | BuiltInLLMToolResultPart;
+
+export type BuiltInLLMContent = string | BuiltInLLMContentPart[];
+
+export type BuiltInLLMMessage = {
+ role: "user" | "assistant" | "system" | "tool";
+ content: BuiltInLLMContent;
+};
+
+export type BuiltInLLMTool =
+ & { description?: string }
+ & (
+ | { pattern: Recipe; handler?: never }
+ | { handler: Stream | OpaqueRef; pattern?: never }
+ );
+
+// Built-in types
+export interface BuiltInLLMParams {
+ messages?: BuiltInLLMMessage[];
+ model?: string;
+ system?: string;
+ stop?: string;
+ maxTokens?: number;
+ /**
+ * Specifies the mode of operation for the LLM.
+ * - `"json"`: Indicates that the LLM should process and return data in JSON format.
+ * This parameter is optional and defaults to undefined, which may result in standard behavior.
+ */
+ mode?: "json";
+ /**
+ * Tools that can be called by the LLM during generation.
+ * Each tool has a description, input schema, and handler function that runs client-side.
+ */
+ tools?: Record;
+}
+
+export interface BuiltInLLMState {
+ pending: boolean;
+ result?: BuiltInLLMContent;
+ partial?: string;
+ error: unknown;
+ cancelGeneration: Stream;
+}
+
+export interface BuiltInLLMGenerateObjectState {
+ pending: boolean;
+ result?: T;
+ partial?: string;
+ error: unknown;
+ cancelGeneration: Stream;
+}
+
+export interface BuiltInLLMDialogState {
+ pending: boolean;
+ error: unknown;
+ cancelGeneration: Stream;
+ addMessage: Stream;
+}
+
+export interface BuiltInGenerateObjectParams {
+ model?: string;
+ prompt?: string;
+ schema?: JSONSchema;
+ system?: string;
+ cache?: boolean;
+ maxTokens?: number;
+ metadata?: Record;
+}
+
+export interface BuiltInCompileAndRunParams {
+ files: Array<{ name: string; contents: string }>;
+ main: string;
+ input?: T;
+}
+
+export interface BuiltInCompileAndRunState {
+ pending: boolean;
+ result?: T;
+ error?: any;
+ errors?: Array<{
+ line: number;
+ column: number;
+ message: string;
+ type: string;
+ file?: string;
+ }>;
+}
+
+// Function type definitions
+export type RecipeFunction = {
+ (
+ argumentSchema: S,
+ fn: (input: OpaqueRef>>) => any,
+ ): RecipeFactory, ReturnType>;
+
+ (
+ argumentSchema: S,
+ fn: (input: OpaqueRef>>) => Opaque,
+ ): RecipeFactory, R>;
+
+ (
+ argumentSchema: S,
+ resultSchema: RS,
+ fn: (
+ input: OpaqueRef>>,
+ ) => Opaque>,
+ ): RecipeFactory, SchemaWithoutCell>;
+
+ (
+ argumentSchema: string | JSONSchema,
+ fn: (input: OpaqueRef>) => any,
+ ): RecipeFactory>;
+
+ (
+ argumentSchema: string | JSONSchema,
+ fn: (input: OpaqueRef>) => Opaque,
+ ): RecipeFactory;
+
+ (
+ argumentSchema: string | JSONSchema,
+ resultSchema: JSONSchema,
+ fn: (input: OpaqueRef>) => Opaque,
+ ): RecipeFactory;
+};
+
+export type LiftFunction = {
+ (
+ argumentSchema: T,
+ resultSchema: R,
+ implementation: (input: Schema) => Schema,
+ ): ModuleFactory, SchemaWithoutCell>;
+
+ (
+ implementation: (input: T) => R,
+ ): ModuleFactory;
+
+