├── .assets ├── applets-chat-demo.gif ├── builders.png └── web-applets-inspector.png ├── .github └── workflows │ ├── publish.yml │ └── publish_preview.yml ├── .gitignore ├── .nvmrc ├── LICENSE.md ├── README.md ├── inspector ├── cli │ └── index.ts ├── package-lock.json ├── package.json ├── scripts │ └── build.sh ├── tsconfig.json └── web │ ├── components │ ├── app-header.css │ ├── app-header.ts │ ├── app-history.css │ ├── app-history.ts │ ├── app-prompt.css │ ├── app-prompt.ts │ ├── app-root.css │ ├── app-root.ts │ ├── app-sidebar.css │ ├── app-sidebar.ts │ ├── app-viewer.css │ ├── app-viewer.ts │ ├── settings-button.css │ ├── settings-button.ts │ ├── settings-dialog.css │ ├── settings-dialog.ts │ ├── toggle-css-variable-button.css │ ├── toggle-css-variable-button.ts │ ├── url-input.css │ └── url-input.ts │ ├── global.css │ ├── index.html │ ├── lib │ ├── history-context.ts │ ├── model.ts │ └── store.ts │ ├── package-lock.json │ ├── package.json │ ├── tsconfig.json │ └── utils.ts ├── package-lock.json ├── package.json ├── scripts ├── publish-preview.sh ├── publish-tag-only.sh └── publish.sh ├── sdk ├── README.md ├── package-lock.json ├── package.json ├── scripts │ └── build.js ├── src │ ├── applets │ │ ├── actions.ts │ │ ├── applet-factory.ts │ │ ├── applet-scope.ts │ │ ├── applet.ts │ │ ├── errors.ts │ │ └── events.ts │ ├── constants.ts │ ├── debug.ts │ ├── elements │ │ └── applet-frame.ts │ ├── index.ts │ ├── messages.ts │ ├── polyfill.ts │ └── utils.ts └── tsconfig.json └── test └── example ├── index.html ├── main.js └── package.json /.assets/applets-chat-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unternet-co/web-applets/1cdb16ab12029e4eb7fd2a2f42001bb5cadc0c01/.assets/applets-chat-demo.gif -------------------------------------------------------------------------------- /.assets/builders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unternet-co/web-applets/1cdb16ab12029e4eb7fd2a2f42001bb5cadc0c01/.assets/builders.png -------------------------------------------------------------------------------- /.assets/web-applets-inspector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unternet-co/web-applets/1cdb16ab12029e4eb7fd2a2f42001bb5cadc0c01/.assets/web-applets-inspector.png -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to npm 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | branches: 8 | - production 9 | 10 | jobs: 11 | publish: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: '18' 19 | registry-url: 'https://registry.npmjs.org' 20 | 21 | - run: npm ci 22 | 23 | - run: ./scripts/publish.sh 24 | env: 25 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/publish_preview.yml: -------------------------------------------------------------------------------- 1 | name: Publish to npm (preview) 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | branches: 8 | - preview 9 | 10 | jobs: 11 | publish: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version: '18' 19 | registry-url: 'https://registry.npmjs.org' 20 | 21 | - run: npm ci 22 | 23 | - run: ./scripts/publish-preview.sh 24 | env: 25 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/dist/ 3 | **/.env -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.14.0 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2024 Unternet, PBC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web Applets 2 | 3 | > An open spec & SDK for creating web apps that agents can use. 4 | 5 | 🌐 [Docs](https://unternet.co/docs) | 👾 [Community Discord](https://discord.gg/VsMuEKmqvt) | 💌 [Mailing List](https://buttondown.com/unternet) 6 | 7 | [![Mozilla builders logo](.assets/builders.png)](https://builders.mozilla.org/) 8 | 9 | Web Applets is a [Mozilla Builders](https://builders.mozilla.org/) project. 10 | 11 | ## What is it? 12 | 13 | **Web Applets is an open specification for building software that both humans and AI can understand and use together.** Instead of forcing AI to operate traditional point-and-click apps built for humans, Web Applets creates a new kind of web software designed for human-AI collaboration. You can read more about it on our website. 14 | 15 | Read [the docs](https://unternet.co/docs/web-applets/introduction)! 16 | 17 | ![Demo of a web applets chatbot](.assets/applets-chat-demo.gif) 18 | 19 | ## Feedback & Community 20 | 21 | This is a community project, and we're open to community members discussing the project direction, and submitting code! 22 | 23 | - Join the [mailing list](https://buttondown.com/unternet). 24 | - Follow [our blog](https://unternet.co/blog) for regular updates 25 | - Join [our discord](https://discord.gg/VsMuEKmqvt) 26 | 27 | ## Get the codebase running 28 | 29 | ```sh 30 | npm install 31 | npm run build 32 | ``` 33 | 34 | ## License 35 | 36 | [MIT](./LICENSE.md) 37 | 38 | --- 39 | 40 | Built by [Unternet](https://unternet.co). 41 | -------------------------------------------------------------------------------- /inspector/cli/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Command } from 'commander'; 4 | import express from 'express'; 5 | import path from 'path'; 6 | 7 | const program = new Command(); 8 | 9 | program 10 | .name('@web-applets/inspector') 11 | .description('Launch the web applets inspector.') 12 | .action(serve); 13 | 14 | program.parse(process.argv); 15 | 16 | export async function serve() { 17 | let port = 1234; 18 | const serverDir = path.join(__dirname, 'web'); 19 | const app = express(); 20 | app.use(express.static(serverDir)); 21 | 22 | try { 23 | const server = app 24 | .listen(port, () => { 25 | console.log(`Applets inspector running at http://localhost:${port}`); 26 | }) 27 | .on('error', (err: any) => { 28 | if (err.code === 'EADDRINUSE') { 29 | const oldPort = port; 30 | port += 1; 31 | console.log(`Port ${oldPort} is busy, trying port ${port}`); 32 | server.listen(port); 33 | } else { 34 | console.error(err); 35 | } 36 | }); 37 | } catch (error) { 38 | console.error('Error starting inspector web server:', error); 39 | process.exit(1); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /inspector/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web-applets/inspector", 3 | "author": "Rupert Manfredi ", 4 | "license": "MIT", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "version": "0.2.6", 8 | "description": "An inspector CLI for web applets.", 9 | "scripts": { 10 | "start": "npm run build && node dist/index.js", 11 | "build": "./scripts/build.sh", 12 | "build:cli": "tsc", 13 | "build:web": "cd web && npm run build", 14 | "prepublishOnly": "npm run build" 15 | }, 16 | "files": [ 17 | "dist" 18 | ], 19 | "bin": { 20 | "web-applets-inspector": "dist/index.js" 21 | }, 22 | "publishConfig": { 23 | "access": "public" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/unternet-co/web-applets.git" 28 | }, 29 | "dependencies": { 30 | "commander": "^12.1.0", 31 | "express": "^4.21.2" 32 | }, 33 | "devDependencies": { 34 | "@types/express": "^5.0.0", 35 | "@types/fs-extra": "^11.0.4", 36 | "@types/node": "^22.7.5", 37 | "typescript": "^5.6.2" 38 | }, 39 | "optionalDependencies": { 40 | "@rollup/rollup-linux-x64-gnu": "4.9.5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /inspector/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | rm -rf dist/ 4 | npm run build:cli 5 | npm run build:web 6 | cp -r web/dist dist/web 7 | chmod +x dist/index.js 8 | -------------------------------------------------------------------------------- /inspector/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "CommonJS", 5 | "outDir": "./dist", 6 | "rootDir": "cli", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "declaration": true, 10 | "declarationMap": true 11 | }, 12 | "include": ["cli/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /inspector/web/components/app-header.css: -------------------------------------------------------------------------------- 1 | app-header { 2 | display: flex; 3 | align-items: center; 4 | border-bottom: var(--panel-border); 5 | padding: var(--space-xs) var(--space-lg); 6 | gap: var(--space-xs); 7 | flex-shrink: 0; 8 | } 9 | 10 | app-header h1 { 11 | font-size: var(--text-lg); 12 | line-height: calc(var(--space-unit) * 6); 13 | display: flex; 14 | flex-shrink: 0; 15 | margin-inline-end: auto; 16 | } 17 | 18 | .center-group { 19 | flex-grow: 1; 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | margin-left: -70px; 24 | } 25 | -------------------------------------------------------------------------------- /inspector/web/components/app-header.ts: -------------------------------------------------------------------------------- 1 | import { html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | import './app-header.css'; 4 | import './url-input'; 5 | import './settings-button'; 6 | import './toggle-css-variable-button'; 7 | 8 | @customElement('app-header') 9 | export class UrlInput extends LitElement { 10 | createRenderRoot() { 11 | return this; 12 | } 13 | 14 | render() { 15 | return html` 16 | 17 | 18 |

Applet Inspector

19 |
20 | 21 | 22 |
23 | 29 | 30 | `; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /inspector/web/components/app-history.css: -------------------------------------------------------------------------------- 1 | app-history { 2 | border-left: var(--panel-border); 3 | padding: var(--space-lg) 0; 4 | grid-area: history; 5 | display: flex; 6 | overflow: hidden; 7 | flex-direction: column-reverse; 8 | gap: var(--space-md); 9 | transition: width 0.3s ease; 10 | } 11 | 12 | app-history app-prompt { 13 | padding: 0 var(--space-lg); 14 | } 15 | 16 | app-history .history { 17 | flex: 1; 18 | display: flex; 19 | flex-direction: column; 20 | gap: var(--space-md); 21 | overflow-y: scroll; 22 | padding: 0 var(--space-lg); 23 | } 24 | 25 | app-history .history__push-to-bottom { 26 | flex: 1 1 auto; 27 | } 28 | 29 | app-history .bubble-container { 30 | display: flex; 31 | font-size: var(--text-base); 32 | } 33 | 34 | /* Align assistant bubbles to the left */ 35 | app-history .assistant-bubble-container { 36 | justify-content: flex-start; 37 | } 38 | 39 | /* Align user bubbles to the right */ 40 | app-history .user-bubble-container { 41 | justify-content: flex-end; 42 | } 43 | 44 | app-history .action-output { 45 | justify-content: center; 46 | display: flex; 47 | gap: var(--space-sm); 48 | color: var(--color-neutral-600); 49 | flex-direction: column; 50 | } 51 | 52 | app-history .action-output span { 53 | word-break: break-all; 54 | font-family: monospace; 55 | font-size: 11px; 56 | } 57 | 58 | app-history .action-output:only-child { 59 | flex-grow: 1; 60 | align-items: center; 61 | } 62 | 63 | app-history .bubble { 64 | background-color: var(--color-neutral-0); 65 | border-radius: var(--rounded-md); 66 | padding: var(--space-sm) var(--space-md); 67 | max-width: 64ch; 68 | text-align: left; 69 | word-break: break-word; 70 | } 71 | 72 | /* User bubble styling */ 73 | app-history .user { 74 | background-color: var(--color-background); 75 | border: 1px solid var(--color-border); 76 | } 77 | 78 | /* Assistant bubble styling (keeps the shadow) */ 79 | app-history .assistant { 80 | /* background-color: var(--color-neutral-0); */ 81 | border: 1px solid var(--color-border); 82 | } 83 | 84 | app-history span { 85 | white-space: pre-line; 86 | } 87 | 88 | app-history.open { 89 | width: 350px; 90 | transition: opacity 0.3s ease-in, width 0.3s ease; 91 | } 92 | 93 | app-history.closed { 94 | width: 0px; 95 | padding: 0px; 96 | overflow: hidden; 97 | opacity: 0; 98 | } 99 | -------------------------------------------------------------------------------- /inspector/web/components/app-history.ts: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from 'lit'; 2 | import { customElement, state } from 'lit/decorators.js'; 3 | import './app-history.css'; 4 | import { 5 | historyContext, 6 | Interaction, 7 | TextOutput, 8 | } from '../lib/history-context'; 9 | 10 | @customElement('app-history') 11 | export class AppHistory extends LitElement { 12 | createRenderRoot() { 13 | return this; 14 | } 15 | 16 | @state() 17 | interactions: Interaction[] = []; 18 | 19 | private unsubscribe?: () => void; 20 | 21 | connectedCallback() { 22 | super.connectedCallback(); 23 | this.unsubscribe = historyContext.subscribe((interactions) => { 24 | // Create a new array reference to trigger reactivity. 25 | this.interactions = [...interactions]; 26 | }); 27 | } 28 | 29 | disconnectedCallback() { 30 | this.unsubscribe && this.unsubscribe(); 31 | super.disconnectedCallback(); 32 | } 33 | 34 | render() { 35 | // Helper to render all properties of an output (except the "type" property). 36 | const renderOutputProperties = (output: any): string => { 37 | return Object.entries(output) 38 | .filter(([key]) => key !== 'type') 39 | .map(([key, value]) => { 40 | if (typeof value === 'object' && value !== null) { 41 | // Use JSON.stringify for objects; you can also customize formatting if needed. 42 | return `${key}: ${JSON.stringify(value)}`; 43 | } 44 | return `${key}: ${value}`; 45 | }) 46 | .join(', '); 47 | }; 48 | 49 | return html` 50 |
51 | ${this.interactions.length <= 0 52 | ? html` 53 |
54 | Enter a message below to interact 55 |
56 | ` 57 | : html`
`} 58 | ${this.interactions.map((interaction) => { 59 | // Render the user's input bubble. 60 | const inputBubble = 61 | interaction.input.type === 'command' 62 | ? html` 63 |
64 |
65 | You: 66 | ${interaction.input.text} 67 |
68 |
69 | ` 70 | : html``; 71 | 72 | // Render outputs that are not of type "data". 73 | const outputBubbles = interaction.outputs 74 | .filter((output) => output.type !== 'data') 75 | .map((output) => { 76 | if (interaction.input.type === 'command') { 77 | // For command inputs: 78 | if (output.type === 'text') { 79 | // "text" outputs from the assistant. 80 | return html` 81 |
82 |
83 | Assistant: 84 | ${(output as TextOutput).content} 85 |
86 |
87 | `; 88 | } else { 89 | // Any other output type (generic AppletOutput) shown as grey bubble. 90 | return html` 91 |
92 | Action: ${output.type} 93 | ${renderOutputProperties(output)} 94 |
95 | `; 96 | } 97 | } else if (interaction.input.type === 'action') { 98 | // For action inputs, always use a grey bubble. 99 | return html` 100 |
101 | Action: ${interaction.input.text} 102 | {${renderOutputProperties(output)}} 103 |
104 | `; 105 | } 106 | return html``; 107 | }); 108 | 109 | return html`${inputBubble}${outputBubbles}`; 110 | })} 111 |
112 | `; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /inspector/web/components/app-prompt.css: -------------------------------------------------------------------------------- 1 | app-prompt form { 2 | position: relative; 3 | } 4 | 5 | app-prompt input { 6 | width: 100%; 7 | } 8 | 9 | app-prompt .loader { 10 | position: absolute; 11 | inset-block-start: var(--space-sm); 12 | inset-inline-end: var(--space-sm); 13 | stroke-dasharray: 40 56; 14 | stroke-dashoffset: 0; 15 | animation: spin 1.5s linear infinite, dashOffset 2s ease-in-out infinite; 16 | transform-origin: center; 17 | } 18 | 19 | @keyframes spin { 20 | from { 21 | transform: rotate(0deg); 22 | } 23 | to { 24 | transform: rotate(360deg); 25 | } 26 | } 27 | 28 | @keyframes dashOffset { 29 | from { 30 | stroke-dashoffset: 0; 31 | } 32 | to { 33 | stroke-dashoffset: -96; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /inspector/web/components/app-prompt.ts: -------------------------------------------------------------------------------- 1 | import { html, LitElement, nothing } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | import './app-prompt.css'; 4 | import { model } from '../lib/model'; 5 | import { store } from '../lib/store'; 6 | import { historyContext, InteractionOutput } from '../lib/history-context'; 7 | 8 | type State = 'idle' | 'thinking'; 9 | 10 | @customElement('app-prompt') 11 | export class AppPrompt extends LitElement { 12 | @property({ type: String }) 13 | openAIAPIToken: string = ''; 14 | 15 | @property({ type: String }) 16 | promptState: State = 'idle'; 17 | 18 | createRenderRoot() { 19 | return this; 20 | } 21 | 22 | connectedCallback() { 23 | store.subscribe((data) => { 24 | this.openAIAPIToken = data.settings?.openAIAPIToken; 25 | }); 26 | super.connectedCallback(); 27 | } 28 | 29 | async onSubmit(e: SubmitEvent) { 30 | e.preventDefault(); 31 | 32 | const form = e.target as HTMLFormElement; 33 | const input = form.elements.namedItem('prompt') as HTMLInputElement; 34 | const userMessage = input.value.trim(); 35 | if (!userMessage) return; 36 | 37 | // Retrieve the last 10 interactions (default value) to provide context to the model. 38 | const recentHistory = historyContext.getRecentInteractions(); 39 | 40 | this.promptState = 'thinking'; 41 | input.value = ''; 42 | 43 | const response = await model.getModelResponse( 44 | userMessage, 45 | recentHistory, 46 | window.applet 47 | ); 48 | 49 | // contruct array for storing outputs from model 50 | const outputs: InteractionOutput[] = []; 51 | 52 | // store text in the output array 53 | if (response.text) outputs.push({ type: 'text', content: response.text }); 54 | 55 | if (response.tools && response.tools.length > 0) { 56 | await Promise.all( 57 | response.tools.map(async (tool) => { 58 | await window.applet.sendAction(tool.id, tool.arguments); 59 | 60 | // store tool used in the output array 61 | outputs.push({ type: tool.id, arguments: tool.arguments }); 62 | 63 | // store tool data in the output array 64 | if (window.applet.data) 65 | outputs.push({ 66 | type: 'data', 67 | content: window.applet.data as object, 68 | }); 69 | }) 70 | ); 71 | } 72 | 73 | // Add the interaction to the context. 74 | historyContext.addInteraction({ 75 | input: { type: 'command', text: userMessage }, 76 | outputs, 77 | timestamp: Date.now(), 78 | }); 79 | 80 | this.promptState = 'idle'; 81 | setTimeout(() => 82 | (document.getElementById('prompt') as HTMLInputElement).focus() 83 | ); 84 | } 85 | 86 | render() { 87 | return html` 88 |
89 | 101 | ${this.promptState === 'thinking' 102 | ? html` 103 | 115 | 116 | 117 | ` 118 | : nothing} 119 |
120 | `; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /inspector/web/components/app-root.css: -------------------------------------------------------------------------------- 1 | app-root { 2 | width: 100%; 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | } 7 | 8 | app-root main { 9 | flex-grow: 1; 10 | overflow: auto; 11 | display: grid; 12 | grid-template-areas: 'sidebar viewer history'; 13 | grid-template-columns: auto 1fr auto; 14 | } 15 | -------------------------------------------------------------------------------- /inspector/web/components/app-root.ts: -------------------------------------------------------------------------------- 1 | import { html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | import './app-root.css'; 4 | import './app-header'; 5 | import './app-viewer'; 6 | import './app-sidebar'; 7 | import './app-prompt'; 8 | import './app-history'; 9 | import './settings-dialog'; 10 | 11 | @customElement('app-root') 12 | export class UrlInput extends LitElement { 13 | createRenderRoot() { 14 | return this; 15 | } 16 | 17 | render() { 18 | return html` 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | `; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /inspector/web/components/app-sidebar.css: -------------------------------------------------------------------------------- 1 | app-sidebar { 2 | border-right: var(--panel-border); 3 | padding: var(--space-lg); 4 | height: 100%; 5 | grid-area: sidebar; 6 | display: flex; 7 | transition: width 0.3s ease; 8 | } 9 | 10 | app-sidebar form { 11 | width: 100%; 12 | height: 100%; 13 | display: flex; 14 | gap: var(--space-lg); 15 | flex-direction: column; 16 | } 17 | 18 | app-sidebar fieldset { 19 | display: flex; 20 | flex-direction: column; 21 | gap: var(--space-md); 22 | border: none; 23 | padding: 0; 24 | margin: 0; 25 | min-width: 0; 26 | } 27 | 28 | app-sidebar .status-message { 29 | display: flex; 30 | align-items: center; 31 | justify-content: center; 32 | flex-grow: 1; 33 | padding: 0; 34 | margin: 0; 35 | } 36 | 37 | app-sidebar select { 38 | width: 100%; 39 | } 40 | 41 | .schema { 42 | margin: 0; 43 | background: var(--color-raised); 44 | padding: var(--space-md); 45 | font-size: var(--text-sm); 46 | border-radius: var(--rounded); 47 | border: 1px solid var(--color-border); 48 | overflow: auto; 49 | white-space: pre-wrap; 50 | } 51 | 52 | app-sidebar textarea { 53 | resize: none; 54 | } 55 | 56 | app-sidebar input[type='submit'] { 57 | /* padding: var(--space-sm); 58 | font-weight: 500; 59 | cursor: pointer; 60 | border: 1px solid var(--color-border); 61 | border-radius: var(--rounded); */ 62 | } 63 | 64 | app-sidebar input[type='submit']:hover { 65 | /* background: var(--color-border); */ 66 | } 67 | 68 | app-sidebar .error-message { 69 | padding: var(--space-xs); 70 | border-radius: var(--rounded); 71 | color: var(--color-negative); 72 | border: solid 1px var(--color-negative); 73 | } 74 | 75 | app-sidebar.open { 76 | width: 350px; 77 | transition: opacity 0.3s ease-in, width 0.3s ease; 78 | } 79 | 80 | app-sidebar.closed { 81 | width: 0px; 82 | padding: 0px; 83 | overflow: hidden; 84 | opacity: 0; 85 | } 86 | -------------------------------------------------------------------------------- /inspector/web/components/app-sidebar.ts: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | import './app-sidebar.css'; 4 | import { StorageData, store } from '../lib/store'; 5 | import { AppletActionDescriptor } from '@web-applets/sdk'; 6 | import { isEmpty } from '../utils'; 7 | import { historyContext } from '../lib/history-context'; 8 | 9 | @customElement('app-sidebar') 10 | export class AppSidebar extends LitElement { 11 | renderRoot = this; 12 | 13 | @property({ type: Number }) 14 | selected: number = 0; 15 | 16 | @property({ attribute: false }) 17 | actions: { [id: string]: AppletActionDescriptor } = {}; 18 | 19 | @property({ type: String }) 20 | schemaError: string = ''; 21 | 22 | connectedCallback() { 23 | store.subscribe((data: StorageData) => { 24 | if (!data.applet) return; 25 | this.actions = data.applet.actions; 26 | data.applet.onactions = (e) => (this.actions = e.actions); 27 | }); 28 | super.connectedCallback(); 29 | } 30 | 31 | handleSelect(e: InputEvent) { 32 | const select = e.target as HTMLSelectElement; 33 | this.selected = select.selectedIndex; 34 | } 35 | 36 | handleSchemaChange(e: InputEvent) { 37 | const textarea = e.target as HTMLTextAreaElement; 38 | 39 | const trimmed = textarea.value.trim(); 40 | 41 | if (!trimmed) { 42 | this.schemaError = ''; 43 | textarea.setCustomValidity(''); 44 | return; 45 | } 46 | try { 47 | const parsed = JSON.parse(trimmed); 48 | const formatted = JSON.stringify(parsed, null, 2); 49 | 50 | textarea.value = formatted; 51 | this.schemaError = ''; 52 | textarea.setCustomValidity(''); 53 | } catch (err) { 54 | this.schemaError = err.message; 55 | textarea.setCustomValidity('Invalid JSON'); 56 | } 57 | } 58 | 59 | handleSchemaFocus(e: InputEvent) { 60 | const textarea = e.target as HTMLTextAreaElement; 61 | 62 | textarea.setCustomValidity(''); 63 | } 64 | 65 | async handleSubmit(e: SubmitEvent) { 66 | e.preventDefault(); 67 | const form = e.target as HTMLFormElement; 68 | const formData = new FormData(form); 69 | const actionId = formData.get('action-id') as string; 70 | const params = formData.get('params') as string; 71 | 72 | await window.applet.sendAction(actionId, JSON.parse(params)); 73 | 74 | // Add the user's action to the context. 75 | historyContext.addInteraction({ 76 | input: { type: 'action', text: actionId }, 77 | outputs: [ 78 | { type: actionId, ...JSON.parse(params) }, 79 | { type: 'data', content: window.applet.data }, 80 | ], 81 | timestamp: Date.now(), 82 | }); 83 | } 84 | 85 | render() { 86 | if (!this.actions || isEmpty(this.actions)) { 87 | return html`

No actions available.

`; 88 | } 89 | 90 | const action = Object.values(this.actions)[this.selected]; 91 | 92 | const schema = JSON.stringify(action?.params_schema, null, 2) ?? 'None'; 93 | 94 | return html` 95 |
96 |
97 | 98 | 103 |
104 |
105 | 106 |

${action?.description}

107 |
108 |
109 | 110 |
${schema}
111 |
112 |
113 | 114 | 122 | ${this.schemaError 123 | ? html`
${this.schemaError}
` 124 | : ''} 125 |
126 |
127 | 128 |
129 |
130 | `; 131 | } 132 | } 133 | 134 | @customElement('action-select') 135 | export class ActionSelect extends LitElement { 136 | renderRoot = this; 137 | 138 | @property({ type: String }) 139 | name: string; 140 | 141 | @property({ attribute: false }) 142 | actions: { [id: string]: AppletActionDescriptor }; 143 | 144 | render() { 145 | return html` 146 | 151 | `; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /inspector/web/components/app-viewer.css: -------------------------------------------------------------------------------- 1 | app-viewer { 2 | background: var(--color-background); 3 | display: flex; 4 | overflow: auto; 5 | grid-area: viewer; 6 | } 7 | 8 | app-viewer .hidden { 9 | display: none; 10 | } 11 | 12 | app-viewer .container { 13 | display: flex; 14 | flex-direction: column; 15 | background: var(--color-raised); 16 | flex-grow: 1; 17 | overflow: hidden; 18 | } 19 | 20 | applet-frame { 21 | flex-grow: 1; 22 | } 23 | 24 | app-viewer .data-view { 25 | flex-grow: 1; 26 | background: var(--color-raised); 27 | padding: var(--space-md); 28 | margin: 0; 29 | white-space: pre-wrap; 30 | overflow: auto; 31 | } 32 | 33 | app-viewer .applet-header { 34 | border-bottom: 1px solid var(--color-border); 35 | padding: var(--space-unit) var(--space-md); 36 | display: flex; 37 | align-items: center; 38 | gap: var(--space-md); 39 | background: var(--color-background); 40 | } 41 | 42 | app-viewer .applet-header label { 43 | font-size: var(--text-sm); 44 | } 45 | 46 | app-viewer .applet-header .toggle { 47 | background: var(--color-neutral-50); 48 | border: var(--panel-border); 49 | display: flex; 50 | padding: 1px; 51 | border-radius: var(--rounded); 52 | gap: var(--space-xxs); 53 | } 54 | 55 | app-viewer .applet-header button { 56 | cursor: pointer; 57 | border: none; 58 | /* border-radius: var(--rounded); */ 59 | padding: var(--space-xxs) var(--space-xs); 60 | } 61 | 62 | app-viewer .applet-header button:hover { 63 | /* background: var(--color-background); */ 64 | } 65 | 66 | app-viewer .applet-header button[data-selected='true'] { 67 | border: var(--button-border); 68 | background: var(--color-raised); 69 | } 70 | 71 | app-viewer .applet-header button[data-selected='true']:hover { 72 | color: var(--text-color); 73 | } 74 | 75 | applet-frame { 76 | width: 100%; 77 | height: 100%; 78 | } 79 | 80 | app-viewer .applet-icon { 81 | width: 18px; 82 | } 83 | 84 | app-viewer .applet-title { 85 | font-weight: 600; 86 | flex-grow: 1; 87 | } 88 | -------------------------------------------------------------------------------- /inspector/web/components/app-viewer.ts: -------------------------------------------------------------------------------- 1 | import { html, LitElement } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | import { StorageData, store } from '../lib/store'; 4 | import { AppletFrameElement } from '@web-applets/sdk'; 5 | import { Applet } from '@web-applets/sdk'; 6 | import '@web-applets/sdk'; 7 | import './app-viewer.css'; 8 | import './url-input.css'; 9 | 10 | declare global { 11 | interface Window { 12 | applet: Applet; 13 | } 14 | } 15 | 16 | @customElement('app-viewer') 17 | export class AppViewer extends LitElement { 18 | renderRoot = this; 19 | 20 | @property() 21 | appletUrl: string = ''; 22 | 23 | @property() 24 | mode: string = 'gui'; 25 | 26 | @property({ attribute: false }) 27 | data: any = {}; 28 | 29 | connectedCallback() { 30 | store.subscribe((data: StorageData) => { 31 | this.appletUrl = data.appletUrl; 32 | if (!data.applet) return; 33 | this.data = data.applet.data; 34 | data.applet.ondata = () => (this.data = data.applet.data); 35 | }); 36 | super.connectedCallback(); 37 | } 38 | 39 | updated() { 40 | const frame = document.querySelector('applet-frame') as AppletFrameElement; 41 | if (!frame) return; 42 | frame.onload = () => { 43 | store.update({ applet: frame.applet }); 44 | window.applet = frame.applet; 45 | window.applet.ondata = (e) => (this.data = e.data); 46 | }; 47 | } 48 | 49 | render() { 50 | const footer = html`
51 | ${window.applet?.manifest?.icons 52 | ? html`` 56 | : ''} 57 |
${window.applet?.manifest.name}
58 |
59 | 65 | 71 |
72 |
`; 73 | 74 | return html` 75 |
76 | ${footer} 77 | 81 |
82 | ${JSON.stringify(this.data, null, 2)}
84 |
85 | `; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /inspector/web/components/settings-button.css: -------------------------------------------------------------------------------- 1 | .menu-button { 2 | display: flex; 3 | cursor: pointer; 4 | border: none; 5 | color: var(--color-text); 6 | border-radius: var(--rounded); 7 | border: 1px solid transparent; 8 | } 9 | 10 | .menu-button:hover { 11 | background: transparent; 12 | color: var(--color-action); 13 | } 14 | 15 | .menu-button.active { 16 | opacity: 0.5; 17 | } 18 | -------------------------------------------------------------------------------- /inspector/web/components/settings-button.ts: -------------------------------------------------------------------------------- 1 | import { html, LitElement } from 'lit'; 2 | import { customElement } from 'lit/decorators.js'; 3 | import { store } from '../lib/store'; 4 | import './settings-button.css'; 5 | 6 | @customElement('settings-button') 7 | export class SettingsButton extends LitElement { 8 | createRenderRoot() { 9 | return this; 10 | } 11 | 12 | openDialog() { 13 | store.update({ settingsDialogOpen: true }); 14 | } 15 | 16 | render() { 17 | return html` 18 | 27 | `; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /inspector/web/components/settings-dialog.css: -------------------------------------------------------------------------------- 1 | settings-dialog dialog { 2 | display: none; 3 | flex-direction: column; 4 | border: 1px solid var(--color-border); 5 | padding: 0; 6 | border-radius: var(--rounded); 7 | pointer-events: none; 8 | width: 400px; 9 | min-height: 200px; 10 | } 11 | 12 | settings-dialog dialog[open] { 13 | display: flex; 14 | pointer-events: auto; 15 | } 16 | 17 | settings-dialog .dialog-header, 18 | settings-dialog .dialog-content, 19 | settings-dialog .dialog-footer { 20 | padding-block: var(--space-xs); 21 | padding-inline: var(--space-md); 22 | } 23 | 24 | settings-dialog .dialog-header { 25 | border-block-end: var(--panel-border); 26 | position: relative; 27 | } 28 | 29 | settings-dialog .close-dialog-button { 30 | display: flex; 31 | position: absolute; 32 | inset-block-start: var(--space-xs); 33 | inset-inline-end: var(--space-sm); 34 | padding: 0; 35 | border: none; 36 | } 37 | 38 | settings-dialog .dialog-content { 39 | flex-grow: 1; 40 | } 41 | 42 | settings-dialog .dialog-footer { 43 | border-block-start: var(--panel-border); 44 | display: flex; 45 | flex-direction: row-reverse; 46 | gap: var(--space-sm); 47 | } 48 | 49 | settings-dialog .dialog-header h2 { 50 | font-size: var(--text-md); 51 | margin: var(--space-xs) 0; 52 | /* line-height: calc(var(--space-unit) * 6); */ 53 | } 54 | 55 | settings-dialog form { 56 | display: flex; 57 | flex-direction: row; 58 | gap: var(--space-xs); 59 | padding-block: var(--space-sm); 60 | } 61 | 62 | settings-dialog fieldset { 63 | border: none; 64 | margin: 0; 65 | padding: 0; 66 | display: flex; 67 | flex-direction: column; 68 | flex-grow: 1; 69 | gap: var(--space-sm); 70 | } 71 | 72 | settings-dialog input { 73 | width: 100%; 74 | } 75 | 76 | settings-dialog .clear-button { 77 | align-self: end; 78 | } -------------------------------------------------------------------------------- /inspector/web/components/settings-dialog.ts: -------------------------------------------------------------------------------- 1 | import { html, LitElement, nothing } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | import { ref, createRef, Ref } from 'lit/directives/ref.js'; 4 | import { store } from '../lib/store'; 5 | import './settings-dialog.css'; 6 | 7 | @customElement('settings-dialog') 8 | export class SettingsDialog extends LitElement { 9 | @property({ type: Boolean }) 10 | open: boolean = false; 11 | 12 | @property({ type: String }) 13 | openAIAPIToken: string = ''; 14 | 15 | private dialogRef: Ref = createRef(); 16 | private formRef: Ref = createRef(); 17 | 18 | createRenderRoot() { 19 | return this; 20 | } 21 | 22 | connectedCallback() { 23 | store.subscribe((data) => { 24 | this.openAIAPIToken = data.settings?.openAIAPIToken; 25 | this.open = data.settingsDialogOpen; 26 | }); 27 | super.connectedCallback(); 28 | } 29 | 30 | updated(changedProperties) { 31 | if (changedProperties.has('open')) { 32 | if (this.open) { 33 | this.dialogRef.value?.showModal(); 34 | } else { 35 | this.dialogRef.value?.close(); 36 | } 37 | } 38 | } 39 | 40 | onSave(e: SubmitEvent) { 41 | e.preventDefault(); 42 | const form = e.target as HTMLFormElement; 43 | const input = form.elements.namedItem('openAIAPIToken') as HTMLInputElement; 44 | const openAIAPIToken = input.value; 45 | const settings = store.get().settings; 46 | 47 | store.update({ settings: { openAIAPIToken, ...settings } }); 48 | 49 | this.onDismiss(); 50 | } 51 | 52 | onClear(e: SubmitEvent) { 53 | e.preventDefault(); 54 | 55 | const settings = store.get().settings; 56 | delete settings.openAIAPIToken; 57 | 58 | store.update({ settings: settings }); 59 | } 60 | 61 | onDismiss() { 62 | this.formRef.value?.reset(); 63 | store.update({ settingsDialogOpen: false }); 64 | } 65 | 66 | render() { 67 | return html` 68 | 69 |
70 |

Settings

71 | 92 |
93 |
94 |
100 |
101 | 102 | 103 | 110 |
111 | ${this.openAIAPIToken 112 | ? html`` 119 | : nothing} 120 |
121 |
122 | 126 |
127 | `; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /inspector/web/components/toggle-css-variable-button.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unternet-co/web-applets/1cdb16ab12029e4eb7fd2a2f42001bb5cadc0c01/inspector/web/components/toggle-css-variable-button.css -------------------------------------------------------------------------------- /inspector/web/components/toggle-css-variable-button.ts: -------------------------------------------------------------------------------- 1 | import { html, LitElement } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | import { unsafeHTML } from 'lit/directives/unsafe-html.js'; 4 | import { classMap } from 'lit/directives/class-map.js'; 5 | 6 | import './toggle-css-variable-button.css'; 7 | 8 | const defaultSvg = ` 9 | 10 | `; 11 | 12 | @customElement('toggle-css-variable-button') 13 | export class ToggleCSSVariableButton extends LitElement { 14 | // Name of the CSS variable to toggle (e.g. '--sidebar-width') 15 | @property({ type: String }) componentName: string = ''; 16 | // The value when the variable is "open" 17 | @property({ type: String }) defaultValue: string = '350px'; 18 | // The SVG icon markup to use (default uses the provided arrow). 19 | @property({ type: String }) icon: string = defaultSvg; 20 | // Internal state for the button’s active class 21 | @property({ type: Boolean }) active: boolean = true; 22 | 23 | createRenderRoot() { 24 | return this; 25 | } 26 | 27 | toggle() { 28 | // Target the main element, or adjust the selector as needed. 29 | const element = document.querySelector(this.componentName) as HTMLElement; 30 | if (!element) { 31 | console.error('Element to toggle not found.'); 32 | return; 33 | } 34 | 35 | if (element.classList.contains('open')) { 36 | element.classList.remove('open'); 37 | element.classList.add('closed'); 38 | this.active = false; 39 | } else { 40 | element.classList.remove('closed'); 41 | element.classList.add('open'); 42 | this.active = true; 43 | } 44 | } 45 | 46 | render() { 47 | return html` 48 | 54 | `; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /inspector/web/components/url-input.css: -------------------------------------------------------------------------------- 1 | url-input { 2 | align-self: stretch; 3 | max-width: 600px; 4 | width: 100%; 5 | } 6 | 7 | url-input input { 8 | height: 100%; 9 | width: 100%; 10 | } 11 | -------------------------------------------------------------------------------- /inspector/web/components/url-input.ts: -------------------------------------------------------------------------------- 1 | import { html, LitElement } from 'lit'; 2 | import { customElement, property } from 'lit/decorators.js'; 3 | import { store } from '../lib/store'; 4 | import './url-input.css'; 5 | 6 | @customElement('url-input') 7 | export class UrlInput extends LitElement { 8 | createRenderRoot() { 9 | return this; 10 | } 11 | 12 | connectedCallback() { 13 | super.connectedCallback(); 14 | } 15 | 16 | handleKeyDown(e: KeyboardEvent) { 17 | if (e.key === 'Enter') { 18 | const target = e.target as HTMLInputElement; 19 | if ( 20 | target.value && 21 | !target.value.startsWith('http://') && 22 | !target.value.startsWith('https://') 23 | ) { 24 | target.value = `http://${target.value}`; 25 | } 26 | store.update({ appletUrl: target.value }); 27 | } 28 | } 29 | 30 | render() { 31 | return html``; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /inspector/web/global.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-action: oklch(45.2% 0.313214 264.052); 3 | --color-highlight: oklch(93.59% 0.1528 101.65); 4 | 5 | --color-neutral-0: oklch(99.39% 0.002854 264.5422); 6 | --color-neutral-50: oklch(96.79% 0.0029 264.54); 7 | --color-neutral-100: oklch(92.17% 0.0029 264.54); 8 | --color-neutral-200: oklch(85.5% 0.0029 264.54); 9 | --color-neutral-300: oklch(77.32% 0.0044 271.36); 10 | --color-neutral-400: oklch(65.8% 0.0029 264.54); 11 | --color-neutral-500: oklch(53.92% 0.0046 286.24); 12 | --color-neutral-600: oklch(45.5% 0.0029 264.54); 13 | --color-neutral-700: oklch(38.2% 0.0029 264.54); 14 | --color-neutral-800: oklch(35.1% 0.0029 264.54); 15 | --color-neutral-900: oklch(32.11% 0.0029 264.54); 16 | --color-neutral-1000: oklch(24.11% 0.0029 264.54); 17 | 18 | --color-background: var(--color-neutral-50); 19 | --color-border: var(--color-neutral-200); 20 | --color-raised: var(--color-neutral-0); 21 | 22 | --color-text: var(--color-neutral-1000); 23 | --color-text-muted: var(--color-neutral-700); 24 | --color-text-disabled: var(--color-neutral-500); 25 | --color-button-text-hover: var(--color-neutral-0); 26 | 27 | /* Font size */ 28 | --text-scale: 1.125; 29 | --text-lg: calc(var(--text-md) * var(--text-scale)); 30 | --text-md: calc(var(--text-base) * var(--text-scale)); 31 | --text-base: 14px; 32 | --text-sm: calc(var(--text-base) / var(--text-scale)); 33 | --text-xs: calc(var(--text-sm) / var(--text-scale)); 34 | 35 | --space-unit: 4px; 36 | 37 | --space-xxs: calc(var(--space-unit) * 0.5); 38 | --space-xs: var(--space-unit); 39 | --space-sm: calc(var(--space-unit) * 2); 40 | --space-md: calc(var(--space-unit) * 3); 41 | --space-lg: calc(var(--space-unit) * 4); 42 | --space-xl: calc(var(--space-unit) * 6); 43 | --space-2xl: calc(var(--space-unit) * 8); 44 | --space-3xl: calc(var(--space-unit) * 12); 45 | --space-4xl: calc(var(--space-unit) * 16); 46 | 47 | --rounded-sm: calc(var(--space-unit) / 1.5); 48 | --rounded: var(--space-unit); 49 | --rounded-md: calc(var(--space-unit) * 1.5); 50 | --rounded-lg: calc(var(--space-unit) * 2); 51 | 52 | --shadow: 0 2px 4px 53 | color-mix(in srgb, var(--color-neutral-1000), transparent 90%); 54 | 55 | --panel-border: 1px solid var(--color-border); 56 | --input-border: 1px solid var(--color-border); 57 | --button-border: 1px solid var(--color-border); 58 | 59 | --history-width: 300px; 60 | } 61 | 62 | @media (prefers-color-scheme: dark) { 63 | :root { 64 | --color-neutral-0: oklch(24.11% 0.0029 264.54); 65 | --color-neutral-50: oklch(32.11% 0.0029 264.54); 66 | --color-neutral-100: oklch(35.1% 0.0029 264.54); 67 | --color-neutral-200: oklch(38.2% 0.0029 264.54); 68 | --color-neutral-300: oklch(45.5% 0.0029 264.54); 69 | --color-neutral-400: oklch(53.92% 0.0046 286.24); 70 | --color-neutral-500: oklch(65.8% 0.0029 264.54); 71 | --color-neutral-600: oklch(77.32% 0.0044 271.36); 72 | --color-neutral-700: oklch(85.5% 0.0029 264.54); 73 | --color-neutral-800: oklch(92.17% 0.0029 264.54); 74 | --color-neutral-900: oklch(95.79% 0.0029 264.54); 75 | --color-neutral-1000: oklch(99.39% 0.002854 264.5422); 76 | 77 | --color-button-text-hover: var(--color-neutral-1000); 78 | } 79 | } 80 | 81 | * { 82 | box-sizing: border-box; 83 | } 84 | 85 | html, 86 | body { 87 | width: 100%; 88 | height: 100%; 89 | padding: 0; 90 | margin: 0; 91 | } 92 | 93 | input, 94 | button { 95 | color: inherit; 96 | background-color: inherit; 97 | } 98 | 99 | label { 100 | font-size: var(--text-sm); 101 | font-weight: 500; 102 | } 103 | 104 | body { 105 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 106 | Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 107 | font-size: var(--text-sm); 108 | text-rendering: geometricPrecision; 109 | line-height: var(--space-lg); 110 | background: var(--color-background); 111 | color: var(--color-text); 112 | } 113 | 114 | header { 115 | display: flex; 116 | } 117 | 118 | h1, 119 | h2, 120 | h3, 121 | h4, 122 | h5 { 123 | padding: 0; 124 | margin: 0; 125 | } 126 | 127 | p { 128 | margin: 0; 129 | } 130 | 131 | select { 132 | appearance: unset; 133 | color: var(--color-text); 134 | background-image: url("data:image/svg+xml,%3Csvg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M5 7.5L10 12.5L15 7.5' stroke='currentColor' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); 135 | background-position: right var(--space-sm) center; 136 | background-repeat: no-repeat; 137 | cursor: pointer; 138 | } 139 | 140 | textarea, 141 | input, 142 | select { 143 | background-color: var(--color-neutral-0); 144 | border: var(--input-border); 145 | border-radius: var(--rounded); 146 | padding: var(--space-sm); 147 | } 148 | 149 | input::placeholder, 150 | textarea::placeholder, 151 | select::placeholder { 152 | color: var(--color-neutral-500); 153 | } 154 | 155 | button, 156 | input[type='submit'] { 157 | border: var(--button-border); 158 | border-radius: var(--rounded); 159 | cursor: pointer; 160 | } 161 | 162 | button:hover, 163 | input[type='submit']:hover { 164 | background: var(--color-action); 165 | color: var(--color-button-text-hover); 166 | } 167 | 168 | .status-message { 169 | color: var(--color-neutral-700); 170 | } 171 | -------------------------------------------------------------------------------- /inspector/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Web Applets Inspector 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /inspector/web/lib/history-context.ts: -------------------------------------------------------------------------------- 1 | // Extended the input type to allow actions from the user 2 | export interface CommandInput { 3 | type: 'command' | 'action'; 4 | text: string; 5 | } 6 | 7 | export type InteractionInput = CommandInput; 8 | 9 | export interface DataOutput { 10 | type: 'data'; 11 | resourceUrl?: string; 12 | content: object; 13 | } 14 | 15 | export interface TextOutput { 16 | type: 'text'; 17 | content: string; 18 | } 19 | 20 | export interface WebOutput { 21 | type: 'web'; 22 | processId: number; 23 | } 24 | 25 | export interface AppletOutput { 26 | type: string; 27 | arguments?: any; 28 | } 29 | 30 | export type InteractionOutput = 31 | | TextOutput 32 | | DataOutput 33 | | WebOutput 34 | | AppletOutput; 35 | 36 | // The Interaction type now contains an input (from the user), outputs (from the model), 37 | // and a timestamp for when the interaction occurred. 38 | export interface Interaction { 39 | input: InteractionInput; 40 | outputs: InteractionOutput[]; 41 | timestamp: number; 42 | } 43 | 44 | type Subscriber = (interactions: Interaction[]) => void; 45 | 46 | class HistoryContext { 47 | private interactions: Interaction[] = []; 48 | private subscribers = new Set(); 49 | 50 | // Add an interaction and notify subscribers 51 | addInteraction(interaction: Interaction): void { 52 | this.interactions.push(interaction); 53 | this.notify(); 54 | } 55 | 56 | // Return all interactions as a new array (to maintain immutability) 57 | getAllInteractions(): Interaction[] { 58 | return [...this.interactions]; 59 | } 60 | 61 | // Return the last `limit` interactions for context 62 | getRecentInteractions(limit: number = 10): Interaction[] { 63 | return this.interactions.slice(-limit); 64 | } 65 | 66 | // Clear the history and notify subscribers 67 | clearHistory(): void { 68 | this.interactions = []; 69 | this.notify(); 70 | } 71 | 72 | // Subscribe to changes in the history. 73 | // Returns an unsubscribe function. 74 | subscribe(callback: Subscriber): () => void { 75 | this.subscribers.add(callback); 76 | // Immediately call the callback with a copy of the current interactions. 77 | callback([...this.interactions]); 78 | return () => { 79 | this.subscribers.delete(callback); 80 | }; 81 | } 82 | 83 | // Notify all subscribers with a new array reference so that lit detects changes. 84 | private notify(): void { 85 | for (const cb of this.subscribers) { 86 | cb([...this.interactions]); 87 | } 88 | } 89 | } 90 | 91 | export const historyContext = new HistoryContext(); 92 | -------------------------------------------------------------------------------- /inspector/web/lib/model.ts: -------------------------------------------------------------------------------- 1 | import { createOpenAI } from '@ai-sdk/openai'; 2 | import { store } from './store'; 3 | import { generateObject, jsonSchema } from 'ai'; 4 | import { Applet } from '@web-applets/sdk'; 5 | import type { Interaction } from './history-context'; 6 | import { isEmpty } from '../utils'; 7 | 8 | type SchemaResponse = { 9 | text?: string; 10 | tools?: { id: string; arguments?: any }[]; 11 | }; 12 | 13 | function getSystemPrompt(applet: Applet) { 14 | if (!applet) return; 15 | 16 | const prompt = `\ 17 | In this environment you have access to a set of tools. Here are the functions available in JSONSchema format: 18 | ${JSON.stringify(applet.actions)} 19 | This is the start data that the tools have provided: ${JSON.stringify( 20 | applet.data 21 | )} 22 | Choose to respond as text and/or one or more functions to call to respond to the user's query. 23 | `; 24 | 25 | return prompt; 26 | } 27 | 28 | function getResponseSchema(applet: Applet) { 29 | if (!applet) 30 | return jsonSchema({ 31 | type: 'object', 32 | properties: { 33 | text: { type: 'string' }, 34 | }, 35 | additionalProperties: false, 36 | }); 37 | 38 | const responseSchema = { 39 | type: 'object', 40 | properties: { 41 | text: { type: 'string' }, 42 | tools: { 43 | type: 'array', 44 | items: { 45 | anyOf: Object.keys(applet.actions).map((actionId) => { 46 | const action = applet.actions[actionId]; 47 | 48 | const schema: any = { 49 | type: 'object', 50 | properties: { 51 | id: { 52 | type: 'string', 53 | enum: [actionId], 54 | }, 55 | }, 56 | additionalProperties: false, 57 | required: ['id'], 58 | }; 59 | 60 | if ( 61 | action.params_schema && 62 | !isEmpty(action.params_schema.properties) 63 | ) { 64 | schema.properties.arguments = action.params_schema; 65 | 66 | // Set some variables that OpenAI requires if they're not present 67 | if (!schema.properties.arguments.required) { 68 | schema.properties.arguments.required = Object.keys( 69 | schema.properties.arguments.properties 70 | ); 71 | } 72 | schema.properties.arguments.additionalProperties = false; 73 | 74 | schema.required.push('arguments'); 75 | } 76 | 77 | return schema; 78 | }), 79 | }, 80 | additionalProperties: false, 81 | }, 82 | }, 83 | additionalProperties: false, 84 | }; 85 | 86 | return jsonSchema(responseSchema); 87 | } 88 | 89 | async function getModelResponse( 90 | prompt: string, 91 | history: Interaction[], 92 | applet: Applet 93 | ) { 94 | const contextText = JSON.stringify(history); 95 | const fullPrompt = contextText ? `${contextText}\n${prompt}` : prompt; 96 | 97 | /** 98 | * @TODO 99 | * 100 | * We currently re-create this each time, can we store it? We need to 101 | * make sure it stays up to date with the token set in localStorage. 102 | */ 103 | const openAIAPIToken = store.get().settings?.openAIAPIToken; 104 | const openai = createOpenAI({ 105 | apiKey: openAIAPIToken, 106 | compatibility: 'strict', 107 | }); 108 | 109 | const model = openai('gpt-4o-mini'); 110 | 111 | const systemPrompt = getSystemPrompt(applet); 112 | const responseSchema = getResponseSchema(applet); 113 | 114 | const { object } = await generateObject({ 115 | model, 116 | prompt: fullPrompt, 117 | system: systemPrompt, 118 | schema: responseSchema, 119 | }); 120 | 121 | return object; 122 | } 123 | 124 | export const model = { 125 | getModelResponse, 126 | }; 127 | -------------------------------------------------------------------------------- /inspector/web/lib/store.ts: -------------------------------------------------------------------------------- 1 | import { Applet, AppletActionDescriptor } from '@web-applets/sdk'; 2 | 3 | type Subscriber = (data: any) => void; 4 | 5 | type Settings = { 6 | openAIAPIToken?: string; 7 | }; 8 | 9 | export interface StorageData { 10 | appletUrl: string; 11 | applet?: Applet; 12 | settings?: Settings; 13 | settingsDialogOpen: boolean; 14 | } 15 | 16 | let data: StorageData = { 17 | appletUrl: '', 18 | settings: {}, 19 | settingsDialogOpen: false, 20 | }; 21 | 22 | if (localStorage.getItem('data')) { 23 | data = JSON.parse(localStorage.getItem('data')); 24 | } 25 | 26 | const subscribers = new Set(); 27 | 28 | function update(newData: object) { 29 | data = { ...data, ...newData }; 30 | localStorage.setItem( 31 | 'data', 32 | JSON.stringify({ 33 | appletUrl: data.appletUrl, 34 | settings: data.settings, 35 | }) 36 | ); 37 | subscribers.forEach((callback) => callback(data)); 38 | } 39 | 40 | function get() { 41 | return data; 42 | } 43 | 44 | function subscribe(callback: Subscriber) { 45 | callback(data); 46 | subscribers.add(callback); 47 | return () => unsubscribe(callback); 48 | } 49 | 50 | function unsubscribe(callback: Subscriber) { 51 | subscribers.delete(callback); 52 | } 53 | 54 | export const store = { 55 | get, 56 | update, 57 | subscribe, 58 | unsubscribe, 59 | }; 60 | -------------------------------------------------------------------------------- /inspector/web/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web-applets/inspector", 3 | "version": "0.0.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@web-applets/inspector", 9 | "license": "ISC", 10 | "dependencies": { 11 | "@ai-sdk/openai": "^1.1.9", 12 | "@web-applets/sdk": "file:../../sdk", 13 | "ai": "^4.1.18", 14 | "lit": "^3.2.1" 15 | }, 16 | "devDependencies": { 17 | "vite": "^6.0.1" 18 | } 19 | }, 20 | "../../sdk": { 21 | "name": "@web-applets/sdk", 22 | "version": "0.1.5", 23 | "license": "MIT", 24 | "devDependencies": { 25 | "typescript": "^5.6.2" 26 | } 27 | }, 28 | "node_modules/@ai-sdk/openai": { 29 | "version": "1.1.9", 30 | "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-1.1.9.tgz", 31 | "integrity": "sha512-t/CpC4TLipdbgBJTMX/otzzqzCMBSPQwUOkYPGbT/jyuC86F+YO9o+LS0Ty2pGUE1kyT+B3WmJ318B16ZCg4hw==", 32 | "license": "Apache-2.0", 33 | "dependencies": { 34 | "@ai-sdk/provider": "1.0.7", 35 | "@ai-sdk/provider-utils": "2.1.6" 36 | }, 37 | "engines": { 38 | "node": ">=18" 39 | }, 40 | "peerDependencies": { 41 | "zod": "^3.0.0" 42 | } 43 | }, 44 | "node_modules/@ai-sdk/provider": { 45 | "version": "1.0.7", 46 | "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.0.7.tgz", 47 | "integrity": "sha512-q1PJEZ0qD9rVR+8JFEd01/QM++csMT5UVwYXSN2u54BrVw/D8TZLTeg2FEfKK00DgAx0UtWd8XOhhwITP9BT5g==", 48 | "license": "Apache-2.0", 49 | "dependencies": { 50 | "json-schema": "^0.4.0" 51 | }, 52 | "engines": { 53 | "node": ">=18" 54 | } 55 | }, 56 | "node_modules/@ai-sdk/provider-utils": { 57 | "version": "2.1.6", 58 | "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.1.6.tgz", 59 | "integrity": "sha512-Pfyaj0QZS22qyVn5Iz7IXcJ8nKIKlu2MeSAdKJzTwkAks7zdLaKVB+396Rqcp1bfQnxl7vaduQVMQiXUrgK8Gw==", 60 | "license": "Apache-2.0", 61 | "dependencies": { 62 | "@ai-sdk/provider": "1.0.7", 63 | "eventsource-parser": "^3.0.0", 64 | "nanoid": "^3.3.8", 65 | "secure-json-parse": "^2.7.0" 66 | }, 67 | "engines": { 68 | "node": ">=18" 69 | }, 70 | "peerDependencies": { 71 | "zod": "^3.0.0" 72 | }, 73 | "peerDependenciesMeta": { 74 | "zod": { 75 | "optional": true 76 | } 77 | } 78 | }, 79 | "node_modules/@ai-sdk/react": { 80 | "version": "1.1.8", 81 | "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.1.8.tgz", 82 | "integrity": "sha512-buHm7hP21xEOksnRQtJX9fKbi7cAUwanEBa5niddTDibCDKd+kIXP2vaJGy8+heB3rff+XSW3BWlA8pscK+n1g==", 83 | "license": "Apache-2.0", 84 | "dependencies": { 85 | "@ai-sdk/provider-utils": "2.1.6", 86 | "@ai-sdk/ui-utils": "1.1.8", 87 | "swr": "^2.2.5", 88 | "throttleit": "2.1.0" 89 | }, 90 | "engines": { 91 | "node": ">=18" 92 | }, 93 | "peerDependencies": { 94 | "react": "^18 || ^19 || ^19.0.0-rc", 95 | "zod": "^3.0.0" 96 | }, 97 | "peerDependenciesMeta": { 98 | "react": { 99 | "optional": true 100 | }, 101 | "zod": { 102 | "optional": true 103 | } 104 | } 105 | }, 106 | "node_modules/@ai-sdk/ui-utils": { 107 | "version": "1.1.8", 108 | "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.1.8.tgz", 109 | "integrity": "sha512-nbok53K1EalO2sZjBLFB33cqs+8SxiL6pe7ekZ7+5f2MJTwdvpShl6d9U4O8fO3DnZ9pYLzaVC0XNMxnJt030Q==", 110 | "license": "Apache-2.0", 111 | "dependencies": { 112 | "@ai-sdk/provider": "1.0.7", 113 | "@ai-sdk/provider-utils": "2.1.6", 114 | "zod-to-json-schema": "^3.24.1" 115 | }, 116 | "engines": { 117 | "node": ">=18" 118 | }, 119 | "peerDependencies": { 120 | "zod": "^3.0.0" 121 | }, 122 | "peerDependenciesMeta": { 123 | "zod": { 124 | "optional": true 125 | } 126 | } 127 | }, 128 | "node_modules/@esbuild/darwin-arm64": { 129 | "version": "0.24.0", 130 | "cpu": [ 131 | "arm64" 132 | ], 133 | "dev": true, 134 | "license": "MIT", 135 | "optional": true, 136 | "os": [ 137 | "darwin" 138 | ], 139 | "engines": { 140 | "node": ">=18" 141 | } 142 | }, 143 | "node_modules/@esbuild/openbsd-arm64": { 144 | "version": "0.24.0", 145 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", 146 | "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", 147 | "cpu": [ 148 | "arm64" 149 | ], 150 | "dev": true, 151 | "license": "MIT", 152 | "optional": true, 153 | "os": [ 154 | "openbsd" 155 | ], 156 | "engines": { 157 | "node": ">=18" 158 | } 159 | }, 160 | "node_modules/@lit-labs/ssr-dom-shim": { 161 | "version": "1.2.1", 162 | "license": "BSD-3-Clause" 163 | }, 164 | "node_modules/@lit/reactive-element": { 165 | "version": "2.0.4", 166 | "license": "BSD-3-Clause", 167 | "dependencies": { 168 | "@lit-labs/ssr-dom-shim": "^1.2.0" 169 | } 170 | }, 171 | "node_modules/@opentelemetry/api": { 172 | "version": "1.9.0", 173 | "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", 174 | "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", 175 | "license": "Apache-2.0", 176 | "engines": { 177 | "node": ">=8.0.0" 178 | } 179 | }, 180 | "node_modules/@rollup/rollup-darwin-arm64": { 181 | "version": "4.27.4", 182 | "cpu": [ 183 | "arm64" 184 | ], 185 | "dev": true, 186 | "license": "MIT", 187 | "optional": true, 188 | "os": [ 189 | "darwin" 190 | ] 191 | }, 192 | "node_modules/@types/diff-match-patch": { 193 | "version": "1.0.36", 194 | "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", 195 | "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", 196 | "license": "MIT" 197 | }, 198 | "node_modules/@types/estree": { 199 | "version": "1.0.6", 200 | "dev": true, 201 | "license": "MIT" 202 | }, 203 | "node_modules/@types/trusted-types": { 204 | "version": "2.0.7", 205 | "license": "MIT" 206 | }, 207 | "node_modules/@web-applets/sdk": { 208 | "resolved": "../../sdk", 209 | "link": true 210 | }, 211 | "node_modules/ai": { 212 | "version": "4.1.18", 213 | "resolved": "https://registry.npmjs.org/ai/-/ai-4.1.18.tgz", 214 | "integrity": "sha512-Ua0rfJ6PQqW3bC+pXcaznAjZyHto5qm/AFoAF7ifvPvkbOZEbENP/KF3/2gF7M+oklTo9yUSLAoShghMvKSh2A==", 215 | "license": "Apache-2.0", 216 | "dependencies": { 217 | "@ai-sdk/provider": "1.0.7", 218 | "@ai-sdk/provider-utils": "2.1.6", 219 | "@ai-sdk/react": "1.1.8", 220 | "@ai-sdk/ui-utils": "1.1.8", 221 | "@opentelemetry/api": "1.9.0", 222 | "jsondiffpatch": "0.6.0" 223 | }, 224 | "engines": { 225 | "node": ">=18" 226 | }, 227 | "peerDependencies": { 228 | "react": "^18 || ^19 || ^19.0.0-rc", 229 | "zod": "^3.0.0" 230 | }, 231 | "peerDependenciesMeta": { 232 | "react": { 233 | "optional": true 234 | }, 235 | "zod": { 236 | "optional": true 237 | } 238 | } 239 | }, 240 | "node_modules/chalk": { 241 | "version": "5.4.1", 242 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", 243 | "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", 244 | "license": "MIT", 245 | "engines": { 246 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 247 | }, 248 | "funding": { 249 | "url": "https://github.com/chalk/chalk?sponsor=1" 250 | } 251 | }, 252 | "node_modules/dequal": { 253 | "version": "2.0.3", 254 | "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", 255 | "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", 256 | "license": "MIT", 257 | "engines": { 258 | "node": ">=6" 259 | } 260 | }, 261 | "node_modules/diff-match-patch": { 262 | "version": "1.0.5", 263 | "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", 264 | "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", 265 | "license": "Apache-2.0" 266 | }, 267 | "node_modules/esbuild": { 268 | "version": "0.24.0", 269 | "dev": true, 270 | "hasInstallScript": true, 271 | "license": "MIT", 272 | "bin": { 273 | "esbuild": "bin/esbuild" 274 | }, 275 | "engines": { 276 | "node": ">=18" 277 | }, 278 | "optionalDependencies": { 279 | "@esbuild/aix-ppc64": "0.24.0", 280 | "@esbuild/android-arm": "0.24.0", 281 | "@esbuild/android-arm64": "0.24.0", 282 | "@esbuild/android-x64": "0.24.0", 283 | "@esbuild/darwin-arm64": "0.24.0", 284 | "@esbuild/darwin-x64": "0.24.0", 285 | "@esbuild/freebsd-arm64": "0.24.0", 286 | "@esbuild/freebsd-x64": "0.24.0", 287 | "@esbuild/linux-arm": "0.24.0", 288 | "@esbuild/linux-arm64": "0.24.0", 289 | "@esbuild/linux-ia32": "0.24.0", 290 | "@esbuild/linux-loong64": "0.24.0", 291 | "@esbuild/linux-mips64el": "0.24.0", 292 | "@esbuild/linux-ppc64": "0.24.0", 293 | "@esbuild/linux-riscv64": "0.24.0", 294 | "@esbuild/linux-s390x": "0.24.0", 295 | "@esbuild/linux-x64": "0.24.0", 296 | "@esbuild/netbsd-x64": "0.24.0", 297 | "@esbuild/openbsd-arm64": "0.24.0", 298 | "@esbuild/openbsd-x64": "0.24.0", 299 | "@esbuild/sunos-x64": "0.24.0", 300 | "@esbuild/win32-arm64": "0.24.0", 301 | "@esbuild/win32-ia32": "0.24.0", 302 | "@esbuild/win32-x64": "0.24.0" 303 | } 304 | }, 305 | "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": { 306 | "version": "0.24.0", 307 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", 308 | "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", 309 | "cpu": [ 310 | "ppc64" 311 | ], 312 | "dev": true, 313 | "license": "MIT", 314 | "optional": true, 315 | "os": [ 316 | "aix" 317 | ], 318 | "engines": { 319 | "node": ">=18" 320 | } 321 | }, 322 | "node_modules/esbuild/node_modules/@esbuild/android-arm": { 323 | "version": "0.24.0", 324 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", 325 | "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", 326 | "cpu": [ 327 | "arm" 328 | ], 329 | "dev": true, 330 | "license": "MIT", 331 | "optional": true, 332 | "os": [ 333 | "android" 334 | ], 335 | "engines": { 336 | "node": ">=18" 337 | } 338 | }, 339 | "node_modules/esbuild/node_modules/@esbuild/android-arm64": { 340 | "version": "0.24.0", 341 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", 342 | "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", 343 | "cpu": [ 344 | "arm64" 345 | ], 346 | "dev": true, 347 | "license": "MIT", 348 | "optional": true, 349 | "os": [ 350 | "android" 351 | ], 352 | "engines": { 353 | "node": ">=18" 354 | } 355 | }, 356 | "node_modules/esbuild/node_modules/@esbuild/android-x64": { 357 | "version": "0.24.0", 358 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", 359 | "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", 360 | "cpu": [ 361 | "x64" 362 | ], 363 | "dev": true, 364 | "license": "MIT", 365 | "optional": true, 366 | "os": [ 367 | "android" 368 | ], 369 | "engines": { 370 | "node": ">=18" 371 | } 372 | }, 373 | "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { 374 | "version": "0.24.0", 375 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", 376 | "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", 377 | "cpu": [ 378 | "x64" 379 | ], 380 | "dev": true, 381 | "license": "MIT", 382 | "optional": true, 383 | "os": [ 384 | "darwin" 385 | ], 386 | "engines": { 387 | "node": ">=18" 388 | } 389 | }, 390 | "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { 391 | "version": "0.24.0", 392 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", 393 | "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", 394 | "cpu": [ 395 | "arm64" 396 | ], 397 | "dev": true, 398 | "license": "MIT", 399 | "optional": true, 400 | "os": [ 401 | "freebsd" 402 | ], 403 | "engines": { 404 | "node": ">=18" 405 | } 406 | }, 407 | "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { 408 | "version": "0.24.0", 409 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", 410 | "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", 411 | "cpu": [ 412 | "x64" 413 | ], 414 | "dev": true, 415 | "license": "MIT", 416 | "optional": true, 417 | "os": [ 418 | "freebsd" 419 | ], 420 | "engines": { 421 | "node": ">=18" 422 | } 423 | }, 424 | "node_modules/esbuild/node_modules/@esbuild/linux-arm": { 425 | "version": "0.24.0", 426 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", 427 | "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", 428 | "cpu": [ 429 | "arm" 430 | ], 431 | "dev": true, 432 | "license": "MIT", 433 | "optional": true, 434 | "os": [ 435 | "linux" 436 | ], 437 | "engines": { 438 | "node": ">=18" 439 | } 440 | }, 441 | "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { 442 | "version": "0.24.0", 443 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", 444 | "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", 445 | "cpu": [ 446 | "arm64" 447 | ], 448 | "dev": true, 449 | "license": "MIT", 450 | "optional": true, 451 | "os": [ 452 | "linux" 453 | ], 454 | "engines": { 455 | "node": ">=18" 456 | } 457 | }, 458 | "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { 459 | "version": "0.24.0", 460 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", 461 | "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", 462 | "cpu": [ 463 | "ia32" 464 | ], 465 | "dev": true, 466 | "license": "MIT", 467 | "optional": true, 468 | "os": [ 469 | "linux" 470 | ], 471 | "engines": { 472 | "node": ">=18" 473 | } 474 | }, 475 | "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { 476 | "version": "0.24.0", 477 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", 478 | "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", 479 | "cpu": [ 480 | "loong64" 481 | ], 482 | "dev": true, 483 | "license": "MIT", 484 | "optional": true, 485 | "os": [ 486 | "linux" 487 | ], 488 | "engines": { 489 | "node": ">=18" 490 | } 491 | }, 492 | "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { 493 | "version": "0.24.0", 494 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", 495 | "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", 496 | "cpu": [ 497 | "mips64el" 498 | ], 499 | "dev": true, 500 | "license": "MIT", 501 | "optional": true, 502 | "os": [ 503 | "linux" 504 | ], 505 | "engines": { 506 | "node": ">=18" 507 | } 508 | }, 509 | "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { 510 | "version": "0.24.0", 511 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", 512 | "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", 513 | "cpu": [ 514 | "ppc64" 515 | ], 516 | "dev": true, 517 | "license": "MIT", 518 | "optional": true, 519 | "os": [ 520 | "linux" 521 | ], 522 | "engines": { 523 | "node": ">=18" 524 | } 525 | }, 526 | "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { 527 | "version": "0.24.0", 528 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", 529 | "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", 530 | "cpu": [ 531 | "riscv64" 532 | ], 533 | "dev": true, 534 | "license": "MIT", 535 | "optional": true, 536 | "os": [ 537 | "linux" 538 | ], 539 | "engines": { 540 | "node": ">=18" 541 | } 542 | }, 543 | "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { 544 | "version": "0.24.0", 545 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", 546 | "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", 547 | "cpu": [ 548 | "s390x" 549 | ], 550 | "dev": true, 551 | "license": "MIT", 552 | "optional": true, 553 | "os": [ 554 | "linux" 555 | ], 556 | "engines": { 557 | "node": ">=18" 558 | } 559 | }, 560 | "node_modules/esbuild/node_modules/@esbuild/linux-x64": { 561 | "version": "0.24.0", 562 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", 563 | "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", 564 | "cpu": [ 565 | "x64" 566 | ], 567 | "dev": true, 568 | "license": "MIT", 569 | "optional": true, 570 | "os": [ 571 | "linux" 572 | ], 573 | "engines": { 574 | "node": ">=18" 575 | } 576 | }, 577 | "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { 578 | "version": "0.24.0", 579 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", 580 | "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", 581 | "cpu": [ 582 | "x64" 583 | ], 584 | "dev": true, 585 | "license": "MIT", 586 | "optional": true, 587 | "os": [ 588 | "netbsd" 589 | ], 590 | "engines": { 591 | "node": ">=18" 592 | } 593 | }, 594 | "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { 595 | "version": "0.24.0", 596 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", 597 | "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", 598 | "cpu": [ 599 | "x64" 600 | ], 601 | "dev": true, 602 | "license": "MIT", 603 | "optional": true, 604 | "os": [ 605 | "openbsd" 606 | ], 607 | "engines": { 608 | "node": ">=18" 609 | } 610 | }, 611 | "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { 612 | "version": "0.24.0", 613 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", 614 | "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", 615 | "cpu": [ 616 | "x64" 617 | ], 618 | "dev": true, 619 | "license": "MIT", 620 | "optional": true, 621 | "os": [ 622 | "sunos" 623 | ], 624 | "engines": { 625 | "node": ">=18" 626 | } 627 | }, 628 | "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { 629 | "version": "0.24.0", 630 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", 631 | "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", 632 | "cpu": [ 633 | "arm64" 634 | ], 635 | "dev": true, 636 | "license": "MIT", 637 | "optional": true, 638 | "os": [ 639 | "win32" 640 | ], 641 | "engines": { 642 | "node": ">=18" 643 | } 644 | }, 645 | "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { 646 | "version": "0.24.0", 647 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", 648 | "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", 649 | "cpu": [ 650 | "ia32" 651 | ], 652 | "dev": true, 653 | "license": "MIT", 654 | "optional": true, 655 | "os": [ 656 | "win32" 657 | ], 658 | "engines": { 659 | "node": ">=18" 660 | } 661 | }, 662 | "node_modules/esbuild/node_modules/@esbuild/win32-x64": { 663 | "version": "0.24.0", 664 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", 665 | "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", 666 | "cpu": [ 667 | "x64" 668 | ], 669 | "dev": true, 670 | "license": "MIT", 671 | "optional": true, 672 | "os": [ 673 | "win32" 674 | ], 675 | "engines": { 676 | "node": ">=18" 677 | } 678 | }, 679 | "node_modules/eventsource-parser": { 680 | "version": "3.0.0", 681 | "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz", 682 | "integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==", 683 | "license": "MIT", 684 | "engines": { 685 | "node": ">=18.0.0" 686 | } 687 | }, 688 | "node_modules/fsevents": { 689 | "version": "2.3.3", 690 | "dev": true, 691 | "license": "MIT", 692 | "optional": true, 693 | "os": [ 694 | "darwin" 695 | ], 696 | "engines": { 697 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 698 | } 699 | }, 700 | "node_modules/json-schema": { 701 | "version": "0.4.0", 702 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", 703 | "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", 704 | "license": "(AFL-2.1 OR BSD-3-Clause)" 705 | }, 706 | "node_modules/jsondiffpatch": { 707 | "version": "0.6.0", 708 | "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", 709 | "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", 710 | "license": "MIT", 711 | "dependencies": { 712 | "@types/diff-match-patch": "^1.0.36", 713 | "chalk": "^5.3.0", 714 | "diff-match-patch": "^1.0.5" 715 | }, 716 | "bin": { 717 | "jsondiffpatch": "bin/jsondiffpatch.js" 718 | }, 719 | "engines": { 720 | "node": "^18.0.0 || >=20.0.0" 721 | } 722 | }, 723 | "node_modules/lit": { 724 | "version": "3.2.1", 725 | "license": "BSD-3-Clause", 726 | "dependencies": { 727 | "@lit/reactive-element": "^2.0.4", 728 | "lit-element": "^4.1.0", 729 | "lit-html": "^3.2.0" 730 | } 731 | }, 732 | "node_modules/lit-element": { 733 | "version": "4.1.1", 734 | "license": "BSD-3-Clause", 735 | "dependencies": { 736 | "@lit-labs/ssr-dom-shim": "^1.2.0", 737 | "@lit/reactive-element": "^2.0.4", 738 | "lit-html": "^3.2.0" 739 | } 740 | }, 741 | "node_modules/lit-html": { 742 | "version": "3.2.1", 743 | "license": "BSD-3-Clause", 744 | "dependencies": { 745 | "@types/trusted-types": "^2.0.2" 746 | } 747 | }, 748 | "node_modules/nanoid": { 749 | "version": "3.3.8", 750 | "funding": [ 751 | { 752 | "type": "github", 753 | "url": "https://github.com/sponsors/ai" 754 | } 755 | ], 756 | "license": "MIT", 757 | "bin": { 758 | "nanoid": "bin/nanoid.cjs" 759 | }, 760 | "engines": { 761 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 762 | } 763 | }, 764 | "node_modules/picocolors": { 765 | "version": "1.1.1", 766 | "dev": true, 767 | "license": "ISC" 768 | }, 769 | "node_modules/postcss": { 770 | "version": "8.4.49", 771 | "dev": true, 772 | "funding": [ 773 | { 774 | "type": "opencollective", 775 | "url": "https://opencollective.com/postcss/" 776 | }, 777 | { 778 | "type": "tidelift", 779 | "url": "https://tidelift.com/funding/github/npm/postcss" 780 | }, 781 | { 782 | "type": "github", 783 | "url": "https://github.com/sponsors/ai" 784 | } 785 | ], 786 | "license": "MIT", 787 | "dependencies": { 788 | "nanoid": "^3.3.7", 789 | "picocolors": "^1.1.1", 790 | "source-map-js": "^1.2.1" 791 | }, 792 | "engines": { 793 | "node": "^10 || ^12 || >=14" 794 | } 795 | }, 796 | "node_modules/react": { 797 | "version": "19.0.0", 798 | "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", 799 | "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", 800 | "license": "MIT", 801 | "peer": true, 802 | "engines": { 803 | "node": ">=0.10.0" 804 | } 805 | }, 806 | "node_modules/rollup": { 807 | "version": "4.27.4", 808 | "dev": true, 809 | "license": "MIT", 810 | "dependencies": { 811 | "@types/estree": "1.0.6" 812 | }, 813 | "bin": { 814 | "rollup": "dist/bin/rollup" 815 | }, 816 | "engines": { 817 | "node": ">=18.0.0", 818 | "npm": ">=8.0.0" 819 | }, 820 | "optionalDependencies": { 821 | "@rollup/rollup-android-arm-eabi": "4.27.4", 822 | "@rollup/rollup-android-arm64": "4.27.4", 823 | "@rollup/rollup-darwin-arm64": "4.27.4", 824 | "@rollup/rollup-darwin-x64": "4.27.4", 825 | "@rollup/rollup-freebsd-arm64": "4.27.4", 826 | "@rollup/rollup-freebsd-x64": "4.27.4", 827 | "@rollup/rollup-linux-arm-gnueabihf": "4.27.4", 828 | "@rollup/rollup-linux-arm-musleabihf": "4.27.4", 829 | "@rollup/rollup-linux-arm64-gnu": "4.27.4", 830 | "@rollup/rollup-linux-arm64-musl": "4.27.4", 831 | "@rollup/rollup-linux-powerpc64le-gnu": "4.27.4", 832 | "@rollup/rollup-linux-riscv64-gnu": "4.27.4", 833 | "@rollup/rollup-linux-s390x-gnu": "4.27.4", 834 | "@rollup/rollup-linux-x64-gnu": "4.27.4", 835 | "@rollup/rollup-linux-x64-musl": "4.27.4", 836 | "@rollup/rollup-win32-arm64-msvc": "4.27.4", 837 | "@rollup/rollup-win32-ia32-msvc": "4.27.4", 838 | "@rollup/rollup-win32-x64-msvc": "4.27.4", 839 | "fsevents": "~2.3.2" 840 | } 841 | }, 842 | "node_modules/secure-json-parse": { 843 | "version": "2.7.0", 844 | "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", 845 | "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", 846 | "license": "BSD-3-Clause" 847 | }, 848 | "node_modules/source-map-js": { 849 | "version": "1.2.1", 850 | "dev": true, 851 | "license": "BSD-3-Clause", 852 | "engines": { 853 | "node": ">=0.10.0" 854 | } 855 | }, 856 | "node_modules/swr": { 857 | "version": "2.3.0", 858 | "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.0.tgz", 859 | "integrity": "sha512-NyZ76wA4yElZWBHzSgEJc28a0u6QZvhb6w0azeL2k7+Q1gAzVK+IqQYXhVOC/mzi+HZIozrZvBVeSeOZNR2bqA==", 860 | "license": "MIT", 861 | "dependencies": { 862 | "dequal": "^2.0.3", 863 | "use-sync-external-store": "^1.4.0" 864 | }, 865 | "peerDependencies": { 866 | "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 867 | } 868 | }, 869 | "node_modules/throttleit": { 870 | "version": "2.1.0", 871 | "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", 872 | "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", 873 | "license": "MIT", 874 | "engines": { 875 | "node": ">=18" 876 | }, 877 | "funding": { 878 | "url": "https://github.com/sponsors/sindresorhus" 879 | } 880 | }, 881 | "node_modules/use-sync-external-store": { 882 | "version": "1.4.0", 883 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", 884 | "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", 885 | "license": "MIT", 886 | "peerDependencies": { 887 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 888 | } 889 | }, 890 | "node_modules/vite": { 891 | "version": "6.0.1", 892 | "dev": true, 893 | "license": "MIT", 894 | "dependencies": { 895 | "esbuild": "^0.24.0", 896 | "postcss": "^8.4.49", 897 | "rollup": "^4.23.0" 898 | }, 899 | "bin": { 900 | "vite": "bin/vite.js" 901 | }, 902 | "engines": { 903 | "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 904 | }, 905 | "funding": { 906 | "url": "https://github.com/vitejs/vite?sponsor=1" 907 | }, 908 | "optionalDependencies": { 909 | "fsevents": "~2.3.3" 910 | }, 911 | "peerDependencies": { 912 | "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 913 | "jiti": ">=1.21.0", 914 | "less": "*", 915 | "lightningcss": "^1.21.0", 916 | "sass": "*", 917 | "sass-embedded": "*", 918 | "stylus": "*", 919 | "sugarss": "*", 920 | "terser": "^5.16.0", 921 | "tsx": "^4.8.1", 922 | "yaml": "^2.4.2" 923 | }, 924 | "peerDependenciesMeta": { 925 | "@types/node": { 926 | "optional": true 927 | }, 928 | "jiti": { 929 | "optional": true 930 | }, 931 | "less": { 932 | "optional": true 933 | }, 934 | "lightningcss": { 935 | "optional": true 936 | }, 937 | "sass": { 938 | "optional": true 939 | }, 940 | "sass-embedded": { 941 | "optional": true 942 | }, 943 | "stylus": { 944 | "optional": true 945 | }, 946 | "sugarss": { 947 | "optional": true 948 | }, 949 | "terser": { 950 | "optional": true 951 | }, 952 | "tsx": { 953 | "optional": true 954 | }, 955 | "yaml": { 956 | "optional": true 957 | } 958 | } 959 | }, 960 | "node_modules/zod": { 961 | "version": "3.24.1", 962 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", 963 | "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", 964 | "license": "MIT", 965 | "peer": true, 966 | "funding": { 967 | "url": "https://github.com/sponsors/colinhacks" 968 | } 969 | }, 970 | "node_modules/zod-to-json-schema": { 971 | "version": "3.24.1", 972 | "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", 973 | "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", 974 | "license": "ISC", 975 | "peerDependencies": { 976 | "zod": "^3.24.1" 977 | } 978 | } 979 | } 980 | } 981 | -------------------------------------------------------------------------------- /inspector/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web-applets/inspector-web", 3 | "description": "Inspector for testing & developing web applets.", 4 | "main": "index.js", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vite build" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "vite": "^6.0.1" 13 | }, 14 | "dependencies": { 15 | "@ai-sdk/openai": "^1.1.9", 16 | "@web-applets/sdk": "file:../../sdk", 17 | "ai": "^4.1.18", 18 | "lit": "^3.2.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /inspector/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2020", "DOM"], 4 | "experimentalDecorators": true, 5 | "emitDecoratorMetadata": true, 6 | "downlevelIteration": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /inspector/web/utils.ts: -------------------------------------------------------------------------------- 1 | export function isEmpty(obj: object): boolean { 2 | return Object.keys(obj).length === 0; 3 | } 4 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-applets", 3 | "version": "0.2.6", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "0.2.6", 9 | "workspaces": [ 10 | "sdk", 11 | "inspector", 12 | "inspector/web", 13 | "test/example" 14 | ] 15 | }, 16 | "inspector": { 17 | "name": "@web-applets/inspector", 18 | "version": "0.2.6", 19 | "license": "MIT", 20 | "dependencies": { 21 | "commander": "^12.1.0", 22 | "express": "^4.21.2" 23 | }, 24 | "bin": { 25 | "web-applets-inspector": "dist/index.js" 26 | }, 27 | "devDependencies": { 28 | "@types/express": "^5.0.0", 29 | "@types/fs-extra": "^11.0.4", 30 | "@types/node": "^22.7.5", 31 | "typescript": "^5.6.2" 32 | }, 33 | "optionalDependencies": { 34 | "@rollup/rollup-linux-x64-gnu": "4.9.5" 35 | } 36 | }, 37 | "inspector/web": { 38 | "name": "@web-applets/inspector-web", 39 | "license": "ISC", 40 | "dependencies": { 41 | "@ai-sdk/openai": "^1.1.9", 42 | "@web-applets/sdk": "file:../../sdk", 43 | "ai": "^4.1.18", 44 | "lit": "^3.2.1" 45 | }, 46 | "devDependencies": { 47 | "vite": "^6.0.1" 48 | } 49 | }, 50 | "node_modules/@ai-sdk/openai": { 51 | "version": "1.2.1", 52 | "license": "Apache-2.0", 53 | "dependencies": { 54 | "@ai-sdk/provider": "1.0.10", 55 | "@ai-sdk/provider-utils": "2.1.11" 56 | }, 57 | "engines": { 58 | "node": ">=18" 59 | }, 60 | "peerDependencies": { 61 | "zod": "^3.0.0" 62 | } 63 | }, 64 | "node_modules/@ai-sdk/provider": { 65 | "version": "1.0.10", 66 | "license": "Apache-2.0", 67 | "dependencies": { 68 | "json-schema": "^0.4.0" 69 | }, 70 | "engines": { 71 | "node": ">=18" 72 | } 73 | }, 74 | "node_modules/@ai-sdk/provider-utils": { 75 | "version": "2.1.11", 76 | "license": "Apache-2.0", 77 | "dependencies": { 78 | "@ai-sdk/provider": "1.0.10", 79 | "eventsource-parser": "^3.0.0", 80 | "nanoid": "^3.3.8", 81 | "secure-json-parse": "^2.7.0" 82 | }, 83 | "engines": { 84 | "node": ">=18" 85 | }, 86 | "peerDependencies": { 87 | "zod": "^3.0.0" 88 | }, 89 | "peerDependenciesMeta": { 90 | "zod": { 91 | "optional": true 92 | } 93 | } 94 | }, 95 | "node_modules/@ai-sdk/react": { 96 | "version": "1.1.21", 97 | "license": "Apache-2.0", 98 | "dependencies": { 99 | "@ai-sdk/provider-utils": "2.1.11", 100 | "@ai-sdk/ui-utils": "1.1.17", 101 | "swr": "^2.2.5", 102 | "throttleit": "2.1.0" 103 | }, 104 | "engines": { 105 | "node": ">=18" 106 | }, 107 | "peerDependencies": { 108 | "react": "^18 || ^19 || ^19.0.0-rc", 109 | "zod": "^3.0.0" 110 | }, 111 | "peerDependenciesMeta": { 112 | "react": { 113 | "optional": true 114 | }, 115 | "zod": { 116 | "optional": true 117 | } 118 | } 119 | }, 120 | "node_modules/@ai-sdk/ui-utils": { 121 | "version": "1.1.17", 122 | "license": "Apache-2.0", 123 | "dependencies": { 124 | "@ai-sdk/provider": "1.0.10", 125 | "@ai-sdk/provider-utils": "2.1.11", 126 | "zod-to-json-schema": "^3.24.1" 127 | }, 128 | "engines": { 129 | "node": ">=18" 130 | }, 131 | "peerDependencies": { 132 | "zod": "^3.0.0" 133 | }, 134 | "peerDependenciesMeta": { 135 | "zod": { 136 | "optional": true 137 | } 138 | } 139 | }, 140 | "node_modules/@esbuild/darwin-arm64": { 141 | "version": "0.25.0", 142 | "cpu": [ 143 | "arm64" 144 | ], 145 | "dev": true, 146 | "license": "MIT", 147 | "optional": true, 148 | "os": [ 149 | "darwin" 150 | ], 151 | "engines": { 152 | "node": ">=18" 153 | } 154 | }, 155 | "node_modules/@lit-labs/ssr-dom-shim": { 156 | "version": "1.3.0", 157 | "license": "BSD-3-Clause" 158 | }, 159 | "node_modules/@lit/reactive-element": { 160 | "version": "2.0.4", 161 | "license": "BSD-3-Clause", 162 | "dependencies": { 163 | "@lit-labs/ssr-dom-shim": "^1.2.0" 164 | } 165 | }, 166 | "node_modules/@opentelemetry/api": { 167 | "version": "1.9.0", 168 | "license": "Apache-2.0", 169 | "engines": { 170 | "node": ">=8.0.0" 171 | } 172 | }, 173 | "node_modules/@rollup/rollup-android-arm-eabi": { 174 | "version": "4.34.9", 175 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz", 176 | "integrity": "sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==", 177 | "cpu": [ 178 | "arm" 179 | ], 180 | "dev": true, 181 | "license": "MIT", 182 | "optional": true, 183 | "os": [ 184 | "android" 185 | ] 186 | }, 187 | "node_modules/@rollup/rollup-android-arm64": { 188 | "version": "4.34.9", 189 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz", 190 | "integrity": "sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==", 191 | "cpu": [ 192 | "arm64" 193 | ], 194 | "dev": true, 195 | "license": "MIT", 196 | "optional": true, 197 | "os": [ 198 | "android" 199 | ] 200 | }, 201 | "node_modules/@rollup/rollup-darwin-arm64": { 202 | "version": "4.34.9", 203 | "cpu": [ 204 | "arm64" 205 | ], 206 | "dev": true, 207 | "license": "MIT", 208 | "optional": true, 209 | "os": [ 210 | "darwin" 211 | ] 212 | }, 213 | "node_modules/@rollup/rollup-darwin-x64": { 214 | "version": "4.34.9", 215 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz", 216 | "integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==", 217 | "cpu": [ 218 | "x64" 219 | ], 220 | "dev": true, 221 | "license": "MIT", 222 | "optional": true, 223 | "os": [ 224 | "darwin" 225 | ] 226 | }, 227 | "node_modules/@rollup/rollup-freebsd-arm64": { 228 | "version": "4.34.9", 229 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz", 230 | "integrity": "sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==", 231 | "cpu": [ 232 | "arm64" 233 | ], 234 | "dev": true, 235 | "license": "MIT", 236 | "optional": true, 237 | "os": [ 238 | "freebsd" 239 | ] 240 | }, 241 | "node_modules/@rollup/rollup-freebsd-x64": { 242 | "version": "4.34.9", 243 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz", 244 | "integrity": "sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==", 245 | "cpu": [ 246 | "x64" 247 | ], 248 | "dev": true, 249 | "license": "MIT", 250 | "optional": true, 251 | "os": [ 252 | "freebsd" 253 | ] 254 | }, 255 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 256 | "version": "4.34.9", 257 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz", 258 | "integrity": "sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==", 259 | "cpu": [ 260 | "arm" 261 | ], 262 | "dev": true, 263 | "license": "MIT", 264 | "optional": true, 265 | "os": [ 266 | "linux" 267 | ] 268 | }, 269 | "node_modules/@rollup/rollup-linux-arm-musleabihf": { 270 | "version": "4.34.9", 271 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz", 272 | "integrity": "sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==", 273 | "cpu": [ 274 | "arm" 275 | ], 276 | "dev": true, 277 | "license": "MIT", 278 | "optional": true, 279 | "os": [ 280 | "linux" 281 | ] 282 | }, 283 | "node_modules/@rollup/rollup-linux-arm64-gnu": { 284 | "version": "4.34.9", 285 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz", 286 | "integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==", 287 | "cpu": [ 288 | "arm64" 289 | ], 290 | "dev": true, 291 | "license": "MIT", 292 | "optional": true, 293 | "os": [ 294 | "linux" 295 | ] 296 | }, 297 | "node_modules/@rollup/rollup-linux-arm64-musl": { 298 | "version": "4.34.9", 299 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz", 300 | "integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==", 301 | "cpu": [ 302 | "arm64" 303 | ], 304 | "dev": true, 305 | "license": "MIT", 306 | "optional": true, 307 | "os": [ 308 | "linux" 309 | ] 310 | }, 311 | "node_modules/@rollup/rollup-linux-loongarch64-gnu": { 312 | "version": "4.34.9", 313 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz", 314 | "integrity": "sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==", 315 | "cpu": [ 316 | "loong64" 317 | ], 318 | "dev": true, 319 | "license": "MIT", 320 | "optional": true, 321 | "os": [ 322 | "linux" 323 | ] 324 | }, 325 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { 326 | "version": "4.34.9", 327 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz", 328 | "integrity": "sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==", 329 | "cpu": [ 330 | "ppc64" 331 | ], 332 | "dev": true, 333 | "license": "MIT", 334 | "optional": true, 335 | "os": [ 336 | "linux" 337 | ] 338 | }, 339 | "node_modules/@rollup/rollup-linux-riscv64-gnu": { 340 | "version": "4.34.9", 341 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz", 342 | "integrity": "sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==", 343 | "cpu": [ 344 | "riscv64" 345 | ], 346 | "dev": true, 347 | "license": "MIT", 348 | "optional": true, 349 | "os": [ 350 | "linux" 351 | ] 352 | }, 353 | "node_modules/@rollup/rollup-linux-s390x-gnu": { 354 | "version": "4.34.9", 355 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz", 356 | "integrity": "sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==", 357 | "cpu": [ 358 | "s390x" 359 | ], 360 | "dev": true, 361 | "license": "MIT", 362 | "optional": true, 363 | "os": [ 364 | "linux" 365 | ] 366 | }, 367 | "node_modules/@rollup/rollup-linux-x64-gnu": { 368 | "version": "4.9.5", 369 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.5.tgz", 370 | "integrity": "sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA==", 371 | "cpu": [ 372 | "x64" 373 | ], 374 | "license": "MIT", 375 | "optional": true, 376 | "os": [ 377 | "linux" 378 | ] 379 | }, 380 | "node_modules/@rollup/rollup-linux-x64-musl": { 381 | "version": "4.34.9", 382 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz", 383 | "integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==", 384 | "cpu": [ 385 | "x64" 386 | ], 387 | "dev": true, 388 | "license": "MIT", 389 | "optional": true, 390 | "os": [ 391 | "linux" 392 | ] 393 | }, 394 | "node_modules/@rollup/rollup-win32-arm64-msvc": { 395 | "version": "4.34.9", 396 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz", 397 | "integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==", 398 | "cpu": [ 399 | "arm64" 400 | ], 401 | "dev": true, 402 | "license": "MIT", 403 | "optional": true, 404 | "os": [ 405 | "win32" 406 | ] 407 | }, 408 | "node_modules/@rollup/rollup-win32-ia32-msvc": { 409 | "version": "4.34.9", 410 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz", 411 | "integrity": "sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==", 412 | "cpu": [ 413 | "ia32" 414 | ], 415 | "dev": true, 416 | "license": "MIT", 417 | "optional": true, 418 | "os": [ 419 | "win32" 420 | ] 421 | }, 422 | "node_modules/@rollup/rollup-win32-x64-msvc": { 423 | "version": "4.34.9", 424 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", 425 | "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", 426 | "cpu": [ 427 | "x64" 428 | ], 429 | "dev": true, 430 | "license": "MIT", 431 | "optional": true, 432 | "os": [ 433 | "win32" 434 | ] 435 | }, 436 | "node_modules/@types/body-parser": { 437 | "version": "1.19.5", 438 | "dev": true, 439 | "license": "MIT", 440 | "dependencies": { 441 | "@types/connect": "*", 442 | "@types/node": "*" 443 | } 444 | }, 445 | "node_modules/@types/connect": { 446 | "version": "3.4.38", 447 | "dev": true, 448 | "license": "MIT", 449 | "dependencies": { 450 | "@types/node": "*" 451 | } 452 | }, 453 | "node_modules/@types/diff-match-patch": { 454 | "version": "1.0.36", 455 | "license": "MIT" 456 | }, 457 | "node_modules/@types/estree": { 458 | "version": "1.0.6", 459 | "dev": true, 460 | "license": "MIT" 461 | }, 462 | "node_modules/@types/express": { 463 | "version": "5.0.0", 464 | "dev": true, 465 | "license": "MIT", 466 | "dependencies": { 467 | "@types/body-parser": "*", 468 | "@types/express-serve-static-core": "^5.0.0", 469 | "@types/qs": "*", 470 | "@types/serve-static": "*" 471 | } 472 | }, 473 | "node_modules/@types/express-serve-static-core": { 474 | "version": "5.0.6", 475 | "dev": true, 476 | "license": "MIT", 477 | "dependencies": { 478 | "@types/node": "*", 479 | "@types/qs": "*", 480 | "@types/range-parser": "*", 481 | "@types/send": "*" 482 | } 483 | }, 484 | "node_modules/@types/fs-extra": { 485 | "version": "11.0.4", 486 | "dev": true, 487 | "license": "MIT", 488 | "dependencies": { 489 | "@types/jsonfile": "*", 490 | "@types/node": "*" 491 | } 492 | }, 493 | "node_modules/@types/http-errors": { 494 | "version": "2.0.4", 495 | "dev": true, 496 | "license": "MIT" 497 | }, 498 | "node_modules/@types/jsonfile": { 499 | "version": "6.1.4", 500 | "dev": true, 501 | "license": "MIT", 502 | "dependencies": { 503 | "@types/node": "*" 504 | } 505 | }, 506 | "node_modules/@types/mime": { 507 | "version": "1.3.5", 508 | "dev": true, 509 | "license": "MIT" 510 | }, 511 | "node_modules/@types/node": { 512 | "version": "22.13.10", 513 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", 514 | "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", 515 | "dev": true, 516 | "license": "MIT", 517 | "dependencies": { 518 | "undici-types": "~6.20.0" 519 | } 520 | }, 521 | "node_modules/@types/qs": { 522 | "version": "6.9.18", 523 | "dev": true, 524 | "license": "MIT" 525 | }, 526 | "node_modules/@types/range-parser": { 527 | "version": "1.2.7", 528 | "dev": true, 529 | "license": "MIT" 530 | }, 531 | "node_modules/@types/send": { 532 | "version": "0.17.4", 533 | "dev": true, 534 | "license": "MIT", 535 | "dependencies": { 536 | "@types/mime": "^1", 537 | "@types/node": "*" 538 | } 539 | }, 540 | "node_modules/@types/serve-static": { 541 | "version": "1.15.7", 542 | "dev": true, 543 | "license": "MIT", 544 | "dependencies": { 545 | "@types/http-errors": "*", 546 | "@types/node": "*", 547 | "@types/send": "*" 548 | } 549 | }, 550 | "node_modules/@types/trusted-types": { 551 | "version": "2.0.7", 552 | "license": "MIT" 553 | }, 554 | "node_modules/@web-applets/inspector": { 555 | "resolved": "inspector", 556 | "link": true 557 | }, 558 | "node_modules/@web-applets/inspector-web": { 559 | "resolved": "inspector/web", 560 | "link": true 561 | }, 562 | "node_modules/@web-applets/sdk": { 563 | "resolved": "sdk", 564 | "link": true 565 | }, 566 | "node_modules/accepts": { 567 | "version": "1.3.8", 568 | "license": "MIT", 569 | "dependencies": { 570 | "mime-types": "~2.1.34", 571 | "negotiator": "0.6.3" 572 | }, 573 | "engines": { 574 | "node": ">= 0.6" 575 | } 576 | }, 577 | "node_modules/ai": { 578 | "version": "4.1.54", 579 | "license": "Apache-2.0", 580 | "dependencies": { 581 | "@ai-sdk/provider": "1.0.10", 582 | "@ai-sdk/provider-utils": "2.1.11", 583 | "@ai-sdk/react": "1.1.21", 584 | "@ai-sdk/ui-utils": "1.1.17", 585 | "@opentelemetry/api": "1.9.0", 586 | "jsondiffpatch": "0.6.0" 587 | }, 588 | "engines": { 589 | "node": ">=18" 590 | }, 591 | "peerDependencies": { 592 | "react": "^18 || ^19 || ^19.0.0-rc", 593 | "zod": "^3.0.0" 594 | }, 595 | "peerDependenciesMeta": { 596 | "react": { 597 | "optional": true 598 | }, 599 | "zod": { 600 | "optional": true 601 | } 602 | } 603 | }, 604 | "node_modules/ansi-regex": { 605 | "version": "5.0.1", 606 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 607 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 608 | "dev": true, 609 | "license": "MIT", 610 | "engines": { 611 | "node": ">=8" 612 | } 613 | }, 614 | "node_modules/ansi-styles": { 615 | "version": "4.3.0", 616 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 617 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 618 | "dev": true, 619 | "license": "MIT", 620 | "dependencies": { 621 | "color-convert": "^2.0.1" 622 | }, 623 | "engines": { 624 | "node": ">=8" 625 | }, 626 | "funding": { 627 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 628 | } 629 | }, 630 | "node_modules/array-flatten": { 631 | "version": "1.1.1", 632 | "license": "MIT" 633 | }, 634 | "node_modules/body-parser": { 635 | "version": "1.20.3", 636 | "license": "MIT", 637 | "dependencies": { 638 | "bytes": "3.1.2", 639 | "content-type": "~1.0.5", 640 | "debug": "2.6.9", 641 | "depd": "2.0.0", 642 | "destroy": "1.2.0", 643 | "http-errors": "2.0.0", 644 | "iconv-lite": "0.4.24", 645 | "on-finished": "2.4.1", 646 | "qs": "6.13.0", 647 | "raw-body": "2.5.2", 648 | "type-is": "~1.6.18", 649 | "unpipe": "1.0.0" 650 | }, 651 | "engines": { 652 | "node": ">= 0.8", 653 | "npm": "1.2.8000 || >= 1.4.16" 654 | } 655 | }, 656 | "node_modules/bytes": { 657 | "version": "3.1.2", 658 | "license": "MIT", 659 | "engines": { 660 | "node": ">= 0.8" 661 | } 662 | }, 663 | "node_modules/call-bind-apply-helpers": { 664 | "version": "1.0.2", 665 | "license": "MIT", 666 | "dependencies": { 667 | "es-errors": "^1.3.0", 668 | "function-bind": "^1.1.2" 669 | }, 670 | "engines": { 671 | "node": ">= 0.4" 672 | } 673 | }, 674 | "node_modules/call-bound": { 675 | "version": "1.0.4", 676 | "license": "MIT", 677 | "dependencies": { 678 | "call-bind-apply-helpers": "^1.0.2", 679 | "get-intrinsic": "^1.3.0" 680 | }, 681 | "engines": { 682 | "node": ">= 0.4" 683 | }, 684 | "funding": { 685 | "url": "https://github.com/sponsors/ljharb" 686 | } 687 | }, 688 | "node_modules/chalk": { 689 | "version": "5.4.1", 690 | "license": "MIT", 691 | "engines": { 692 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 693 | }, 694 | "funding": { 695 | "url": "https://github.com/chalk/chalk?sponsor=1" 696 | } 697 | }, 698 | "node_modules/cliui": { 699 | "version": "8.0.1", 700 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 701 | "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 702 | "dev": true, 703 | "license": "ISC", 704 | "dependencies": { 705 | "string-width": "^4.2.0", 706 | "strip-ansi": "^6.0.1", 707 | "wrap-ansi": "^7.0.0" 708 | }, 709 | "engines": { 710 | "node": ">=12" 711 | } 712 | }, 713 | "node_modules/color-convert": { 714 | "version": "2.0.1", 715 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 716 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 717 | "dev": true, 718 | "license": "MIT", 719 | "dependencies": { 720 | "color-name": "~1.1.4" 721 | }, 722 | "engines": { 723 | "node": ">=7.0.0" 724 | } 725 | }, 726 | "node_modules/color-name": { 727 | "version": "1.1.4", 728 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 729 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 730 | "dev": true, 731 | "license": "MIT" 732 | }, 733 | "node_modules/commander": { 734 | "version": "12.1.0", 735 | "license": "MIT", 736 | "engines": { 737 | "node": ">=18" 738 | } 739 | }, 740 | "node_modules/concurrently": { 741 | "version": "9.1.2", 742 | "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz", 743 | "integrity": "sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==", 744 | "dev": true, 745 | "license": "MIT", 746 | "dependencies": { 747 | "chalk": "^4.1.2", 748 | "lodash": "^4.17.21", 749 | "rxjs": "^7.8.1", 750 | "shell-quote": "^1.8.1", 751 | "supports-color": "^8.1.1", 752 | "tree-kill": "^1.2.2", 753 | "yargs": "^17.7.2" 754 | }, 755 | "bin": { 756 | "conc": "dist/bin/concurrently.js", 757 | "concurrently": "dist/bin/concurrently.js" 758 | }, 759 | "engines": { 760 | "node": ">=18" 761 | }, 762 | "funding": { 763 | "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" 764 | } 765 | }, 766 | "node_modules/concurrently/node_modules/chalk": { 767 | "version": "4.1.2", 768 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 769 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 770 | "dev": true, 771 | "license": "MIT", 772 | "dependencies": { 773 | "ansi-styles": "^4.1.0", 774 | "supports-color": "^7.1.0" 775 | }, 776 | "engines": { 777 | "node": ">=10" 778 | }, 779 | "funding": { 780 | "url": "https://github.com/chalk/chalk?sponsor=1" 781 | } 782 | }, 783 | "node_modules/concurrently/node_modules/chalk/node_modules/supports-color": { 784 | "version": "7.2.0", 785 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 786 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 787 | "dev": true, 788 | "license": "MIT", 789 | "dependencies": { 790 | "has-flag": "^4.0.0" 791 | }, 792 | "engines": { 793 | "node": ">=8" 794 | } 795 | }, 796 | "node_modules/content-disposition": { 797 | "version": "0.5.4", 798 | "license": "MIT", 799 | "dependencies": { 800 | "safe-buffer": "5.2.1" 801 | }, 802 | "engines": { 803 | "node": ">= 0.6" 804 | } 805 | }, 806 | "node_modules/content-type": { 807 | "version": "1.0.5", 808 | "license": "MIT", 809 | "engines": { 810 | "node": ">= 0.6" 811 | } 812 | }, 813 | "node_modules/cookie": { 814 | "version": "0.7.1", 815 | "license": "MIT", 816 | "engines": { 817 | "node": ">= 0.6" 818 | } 819 | }, 820 | "node_modules/cookie-signature": { 821 | "version": "1.0.6", 822 | "license": "MIT" 823 | }, 824 | "node_modules/cross-env": { 825 | "version": "7.0.3", 826 | "dev": true, 827 | "license": "MIT", 828 | "dependencies": { 829 | "cross-spawn": "^7.0.1" 830 | }, 831 | "bin": { 832 | "cross-env": "src/bin/cross-env.js", 833 | "cross-env-shell": "src/bin/cross-env-shell.js" 834 | }, 835 | "engines": { 836 | "node": ">=10.14", 837 | "npm": ">=6", 838 | "yarn": ">=1" 839 | } 840 | }, 841 | "node_modules/cross-spawn": { 842 | "version": "7.0.6", 843 | "dev": true, 844 | "license": "MIT", 845 | "dependencies": { 846 | "path-key": "^3.1.0", 847 | "shebang-command": "^2.0.0", 848 | "which": "^2.0.1" 849 | }, 850 | "engines": { 851 | "node": ">= 8" 852 | } 853 | }, 854 | "node_modules/debug": { 855 | "version": "2.6.9", 856 | "license": "MIT", 857 | "dependencies": { 858 | "ms": "2.0.0" 859 | } 860 | }, 861 | "node_modules/depd": { 862 | "version": "2.0.0", 863 | "license": "MIT", 864 | "engines": { 865 | "node": ">= 0.8" 866 | } 867 | }, 868 | "node_modules/dequal": { 869 | "version": "2.0.3", 870 | "license": "MIT", 871 | "engines": { 872 | "node": ">=6" 873 | } 874 | }, 875 | "node_modules/destroy": { 876 | "version": "1.2.0", 877 | "license": "MIT", 878 | "engines": { 879 | "node": ">= 0.8", 880 | "npm": "1.2.8000 || >= 1.4.16" 881 | } 882 | }, 883 | "node_modules/diff-match-patch": { 884 | "version": "1.0.5", 885 | "license": "Apache-2.0" 886 | }, 887 | "node_modules/dunder-proto": { 888 | "version": "1.0.1", 889 | "license": "MIT", 890 | "dependencies": { 891 | "call-bind-apply-helpers": "^1.0.1", 892 | "es-errors": "^1.3.0", 893 | "gopd": "^1.2.0" 894 | }, 895 | "engines": { 896 | "node": ">= 0.4" 897 | } 898 | }, 899 | "node_modules/ee-first": { 900 | "version": "1.1.1", 901 | "license": "MIT" 902 | }, 903 | "node_modules/emoji-regex": { 904 | "version": "8.0.0", 905 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 906 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 907 | "dev": true, 908 | "license": "MIT" 909 | }, 910 | "node_modules/encodeurl": { 911 | "version": "2.0.0", 912 | "license": "MIT", 913 | "engines": { 914 | "node": ">= 0.8" 915 | } 916 | }, 917 | "node_modules/es-define-property": { 918 | "version": "1.0.1", 919 | "license": "MIT", 920 | "engines": { 921 | "node": ">= 0.4" 922 | } 923 | }, 924 | "node_modules/es-errors": { 925 | "version": "1.3.0", 926 | "license": "MIT", 927 | "engines": { 928 | "node": ">= 0.4" 929 | } 930 | }, 931 | "node_modules/es-object-atoms": { 932 | "version": "1.1.1", 933 | "license": "MIT", 934 | "dependencies": { 935 | "es-errors": "^1.3.0" 936 | }, 937 | "engines": { 938 | "node": ">= 0.4" 939 | } 940 | }, 941 | "node_modules/esbuild": { 942 | "version": "0.25.0", 943 | "dev": true, 944 | "hasInstallScript": true, 945 | "license": "MIT", 946 | "bin": { 947 | "esbuild": "bin/esbuild" 948 | }, 949 | "engines": { 950 | "node": ">=18" 951 | }, 952 | "optionalDependencies": { 953 | "@esbuild/aix-ppc64": "0.25.0", 954 | "@esbuild/android-arm": "0.25.0", 955 | "@esbuild/android-arm64": "0.25.0", 956 | "@esbuild/android-x64": "0.25.0", 957 | "@esbuild/darwin-arm64": "0.25.0", 958 | "@esbuild/darwin-x64": "0.25.0", 959 | "@esbuild/freebsd-arm64": "0.25.0", 960 | "@esbuild/freebsd-x64": "0.25.0", 961 | "@esbuild/linux-arm": "0.25.0", 962 | "@esbuild/linux-arm64": "0.25.0", 963 | "@esbuild/linux-ia32": "0.25.0", 964 | "@esbuild/linux-loong64": "0.25.0", 965 | "@esbuild/linux-mips64el": "0.25.0", 966 | "@esbuild/linux-ppc64": "0.25.0", 967 | "@esbuild/linux-riscv64": "0.25.0", 968 | "@esbuild/linux-s390x": "0.25.0", 969 | "@esbuild/linux-x64": "0.25.0", 970 | "@esbuild/netbsd-arm64": "0.25.0", 971 | "@esbuild/netbsd-x64": "0.25.0", 972 | "@esbuild/openbsd-arm64": "0.25.0", 973 | "@esbuild/openbsd-x64": "0.25.0", 974 | "@esbuild/sunos-x64": "0.25.0", 975 | "@esbuild/win32-arm64": "0.25.0", 976 | "@esbuild/win32-ia32": "0.25.0", 977 | "@esbuild/win32-x64": "0.25.0" 978 | } 979 | }, 980 | "node_modules/escalade": { 981 | "version": "3.2.0", 982 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 983 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 984 | "dev": true, 985 | "license": "MIT", 986 | "engines": { 987 | "node": ">=6" 988 | } 989 | }, 990 | "node_modules/escape-html": { 991 | "version": "1.0.3", 992 | "license": "MIT" 993 | }, 994 | "node_modules/etag": { 995 | "version": "1.8.1", 996 | "license": "MIT", 997 | "engines": { 998 | "node": ">= 0.6" 999 | } 1000 | }, 1001 | "node_modules/eventsource-parser": { 1002 | "version": "3.0.0", 1003 | "license": "MIT", 1004 | "engines": { 1005 | "node": ">=18.0.0" 1006 | } 1007 | }, 1008 | "node_modules/example": { 1009 | "resolved": "test/example", 1010 | "link": true 1011 | }, 1012 | "node_modules/express": { 1013 | "version": "4.21.2", 1014 | "license": "MIT", 1015 | "dependencies": { 1016 | "accepts": "~1.3.8", 1017 | "array-flatten": "1.1.1", 1018 | "body-parser": "1.20.3", 1019 | "content-disposition": "0.5.4", 1020 | "content-type": "~1.0.4", 1021 | "cookie": "0.7.1", 1022 | "cookie-signature": "1.0.6", 1023 | "debug": "2.6.9", 1024 | "depd": "2.0.0", 1025 | "encodeurl": "~2.0.0", 1026 | "escape-html": "~1.0.3", 1027 | "etag": "~1.8.1", 1028 | "finalhandler": "1.3.1", 1029 | "fresh": "0.5.2", 1030 | "http-errors": "2.0.0", 1031 | "merge-descriptors": "1.0.3", 1032 | "methods": "~1.1.2", 1033 | "on-finished": "2.4.1", 1034 | "parseurl": "~1.3.3", 1035 | "path-to-regexp": "0.1.12", 1036 | "proxy-addr": "~2.0.7", 1037 | "qs": "6.13.0", 1038 | "range-parser": "~1.2.1", 1039 | "safe-buffer": "5.2.1", 1040 | "send": "0.19.0", 1041 | "serve-static": "1.16.2", 1042 | "setprototypeof": "1.2.0", 1043 | "statuses": "2.0.1", 1044 | "type-is": "~1.6.18", 1045 | "utils-merge": "1.0.1", 1046 | "vary": "~1.1.2" 1047 | }, 1048 | "engines": { 1049 | "node": ">= 0.10.0" 1050 | }, 1051 | "funding": { 1052 | "type": "opencollective", 1053 | "url": "https://opencollective.com/express" 1054 | } 1055 | }, 1056 | "node_modules/finalhandler": { 1057 | "version": "1.3.1", 1058 | "license": "MIT", 1059 | "dependencies": { 1060 | "debug": "2.6.9", 1061 | "encodeurl": "~2.0.0", 1062 | "escape-html": "~1.0.3", 1063 | "on-finished": "2.4.1", 1064 | "parseurl": "~1.3.3", 1065 | "statuses": "2.0.1", 1066 | "unpipe": "~1.0.0" 1067 | }, 1068 | "engines": { 1069 | "node": ">= 0.8" 1070 | } 1071 | }, 1072 | "node_modules/forwarded": { 1073 | "version": "0.2.0", 1074 | "license": "MIT", 1075 | "engines": { 1076 | "node": ">= 0.6" 1077 | } 1078 | }, 1079 | "node_modules/fresh": { 1080 | "version": "0.5.2", 1081 | "license": "MIT", 1082 | "engines": { 1083 | "node": ">= 0.6" 1084 | } 1085 | }, 1086 | "node_modules/fsevents": { 1087 | "version": "2.3.3", 1088 | "dev": true, 1089 | "license": "MIT", 1090 | "optional": true, 1091 | "os": [ 1092 | "darwin" 1093 | ], 1094 | "engines": { 1095 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1096 | } 1097 | }, 1098 | "node_modules/function-bind": { 1099 | "version": "1.1.2", 1100 | "license": "MIT", 1101 | "funding": { 1102 | "url": "https://github.com/sponsors/ljharb" 1103 | } 1104 | }, 1105 | "node_modules/get-caller-file": { 1106 | "version": "2.0.5", 1107 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1108 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1109 | "dev": true, 1110 | "license": "ISC", 1111 | "engines": { 1112 | "node": "6.* || 8.* || >= 10.*" 1113 | } 1114 | }, 1115 | "node_modules/get-intrinsic": { 1116 | "version": "1.3.0", 1117 | "license": "MIT", 1118 | "dependencies": { 1119 | "call-bind-apply-helpers": "^1.0.2", 1120 | "es-define-property": "^1.0.1", 1121 | "es-errors": "^1.3.0", 1122 | "es-object-atoms": "^1.1.1", 1123 | "function-bind": "^1.1.2", 1124 | "get-proto": "^1.0.1", 1125 | "gopd": "^1.2.0", 1126 | "has-symbols": "^1.1.0", 1127 | "hasown": "^2.0.2", 1128 | "math-intrinsics": "^1.1.0" 1129 | }, 1130 | "engines": { 1131 | "node": ">= 0.4" 1132 | }, 1133 | "funding": { 1134 | "url": "https://github.com/sponsors/ljharb" 1135 | } 1136 | }, 1137 | "node_modules/get-proto": { 1138 | "version": "1.0.1", 1139 | "license": "MIT", 1140 | "dependencies": { 1141 | "dunder-proto": "^1.0.1", 1142 | "es-object-atoms": "^1.0.0" 1143 | }, 1144 | "engines": { 1145 | "node": ">= 0.4" 1146 | } 1147 | }, 1148 | "node_modules/gopd": { 1149 | "version": "1.2.0", 1150 | "license": "MIT", 1151 | "engines": { 1152 | "node": ">= 0.4" 1153 | }, 1154 | "funding": { 1155 | "url": "https://github.com/sponsors/ljharb" 1156 | } 1157 | }, 1158 | "node_modules/has-flag": { 1159 | "version": "4.0.0", 1160 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1161 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1162 | "dev": true, 1163 | "license": "MIT", 1164 | "engines": { 1165 | "node": ">=8" 1166 | } 1167 | }, 1168 | "node_modules/has-symbols": { 1169 | "version": "1.1.0", 1170 | "license": "MIT", 1171 | "engines": { 1172 | "node": ">= 0.4" 1173 | }, 1174 | "funding": { 1175 | "url": "https://github.com/sponsors/ljharb" 1176 | } 1177 | }, 1178 | "node_modules/hasown": { 1179 | "version": "2.0.2", 1180 | "license": "MIT", 1181 | "dependencies": { 1182 | "function-bind": "^1.1.2" 1183 | }, 1184 | "engines": { 1185 | "node": ">= 0.4" 1186 | } 1187 | }, 1188 | "node_modules/http-errors": { 1189 | "version": "2.0.0", 1190 | "license": "MIT", 1191 | "dependencies": { 1192 | "depd": "2.0.0", 1193 | "inherits": "2.0.4", 1194 | "setprototypeof": "1.2.0", 1195 | "statuses": "2.0.1", 1196 | "toidentifier": "1.0.1" 1197 | }, 1198 | "engines": { 1199 | "node": ">= 0.8" 1200 | } 1201 | }, 1202 | "node_modules/iconv-lite": { 1203 | "version": "0.4.24", 1204 | "license": "MIT", 1205 | "dependencies": { 1206 | "safer-buffer": ">= 2.1.2 < 3" 1207 | }, 1208 | "engines": { 1209 | "node": ">=0.10.0" 1210 | } 1211 | }, 1212 | "node_modules/inherits": { 1213 | "version": "2.0.4", 1214 | "license": "ISC" 1215 | }, 1216 | "node_modules/ipaddr.js": { 1217 | "version": "1.9.1", 1218 | "license": "MIT", 1219 | "engines": { 1220 | "node": ">= 0.10" 1221 | } 1222 | }, 1223 | "node_modules/is-fullwidth-code-point": { 1224 | "version": "3.0.0", 1225 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1226 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1227 | "dev": true, 1228 | "license": "MIT", 1229 | "engines": { 1230 | "node": ">=8" 1231 | } 1232 | }, 1233 | "node_modules/isexe": { 1234 | "version": "2.0.0", 1235 | "dev": true, 1236 | "license": "ISC" 1237 | }, 1238 | "node_modules/json-schema": { 1239 | "version": "0.4.0", 1240 | "license": "(AFL-2.1 OR BSD-3-Clause)" 1241 | }, 1242 | "node_modules/jsondiffpatch": { 1243 | "version": "0.6.0", 1244 | "license": "MIT", 1245 | "dependencies": { 1246 | "@types/diff-match-patch": "^1.0.36", 1247 | "chalk": "^5.3.0", 1248 | "diff-match-patch": "^1.0.5" 1249 | }, 1250 | "bin": { 1251 | "jsondiffpatch": "bin/jsondiffpatch.js" 1252 | }, 1253 | "engines": { 1254 | "node": "^18.0.0 || >=20.0.0" 1255 | } 1256 | }, 1257 | "node_modules/lit": { 1258 | "version": "3.2.1", 1259 | "license": "BSD-3-Clause", 1260 | "dependencies": { 1261 | "@lit/reactive-element": "^2.0.4", 1262 | "lit-element": "^4.1.0", 1263 | "lit-html": "^3.2.0" 1264 | } 1265 | }, 1266 | "node_modules/lit-element": { 1267 | "version": "4.1.1", 1268 | "license": "BSD-3-Clause", 1269 | "dependencies": { 1270 | "@lit-labs/ssr-dom-shim": "^1.2.0", 1271 | "@lit/reactive-element": "^2.0.4", 1272 | "lit-html": "^3.2.0" 1273 | } 1274 | }, 1275 | "node_modules/lit-html": { 1276 | "version": "3.2.1", 1277 | "license": "BSD-3-Clause", 1278 | "dependencies": { 1279 | "@types/trusted-types": "^2.0.2" 1280 | } 1281 | }, 1282 | "node_modules/lodash": { 1283 | "version": "4.17.21", 1284 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 1285 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 1286 | "dev": true, 1287 | "license": "MIT" 1288 | }, 1289 | "node_modules/math-intrinsics": { 1290 | "version": "1.1.0", 1291 | "license": "MIT", 1292 | "engines": { 1293 | "node": ">= 0.4" 1294 | } 1295 | }, 1296 | "node_modules/media-typer": { 1297 | "version": "0.3.0", 1298 | "license": "MIT", 1299 | "engines": { 1300 | "node": ">= 0.6" 1301 | } 1302 | }, 1303 | "node_modules/merge-descriptors": { 1304 | "version": "1.0.3", 1305 | "license": "MIT", 1306 | "funding": { 1307 | "url": "https://github.com/sponsors/sindresorhus" 1308 | } 1309 | }, 1310 | "node_modules/methods": { 1311 | "version": "1.1.2", 1312 | "license": "MIT", 1313 | "engines": { 1314 | "node": ">= 0.6" 1315 | } 1316 | }, 1317 | "node_modules/mime": { 1318 | "version": "1.6.0", 1319 | "license": "MIT", 1320 | "bin": { 1321 | "mime": "cli.js" 1322 | }, 1323 | "engines": { 1324 | "node": ">=4" 1325 | } 1326 | }, 1327 | "node_modules/mime-db": { 1328 | "version": "1.52.0", 1329 | "license": "MIT", 1330 | "engines": { 1331 | "node": ">= 0.6" 1332 | } 1333 | }, 1334 | "node_modules/mime-types": { 1335 | "version": "2.1.35", 1336 | "license": "MIT", 1337 | "dependencies": { 1338 | "mime-db": "1.52.0" 1339 | }, 1340 | "engines": { 1341 | "node": ">= 0.6" 1342 | } 1343 | }, 1344 | "node_modules/ms": { 1345 | "version": "2.0.0", 1346 | "license": "MIT" 1347 | }, 1348 | "node_modules/nanoid": { 1349 | "version": "3.3.8", 1350 | "funding": [ 1351 | { 1352 | "type": "github", 1353 | "url": "https://github.com/sponsors/ai" 1354 | } 1355 | ], 1356 | "license": "MIT", 1357 | "bin": { 1358 | "nanoid": "bin/nanoid.cjs" 1359 | }, 1360 | "engines": { 1361 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1362 | } 1363 | }, 1364 | "node_modules/negotiator": { 1365 | "version": "0.6.3", 1366 | "license": "MIT", 1367 | "engines": { 1368 | "node": ">= 0.6" 1369 | } 1370 | }, 1371 | "node_modules/object-inspect": { 1372 | "version": "1.13.4", 1373 | "license": "MIT", 1374 | "engines": { 1375 | "node": ">= 0.4" 1376 | }, 1377 | "funding": { 1378 | "url": "https://github.com/sponsors/ljharb" 1379 | } 1380 | }, 1381 | "node_modules/on-finished": { 1382 | "version": "2.4.1", 1383 | "license": "MIT", 1384 | "dependencies": { 1385 | "ee-first": "1.1.1" 1386 | }, 1387 | "engines": { 1388 | "node": ">= 0.8" 1389 | } 1390 | }, 1391 | "node_modules/parseurl": { 1392 | "version": "1.3.3", 1393 | "license": "MIT", 1394 | "engines": { 1395 | "node": ">= 0.8" 1396 | } 1397 | }, 1398 | "node_modules/path-key": { 1399 | "version": "3.1.1", 1400 | "dev": true, 1401 | "license": "MIT", 1402 | "engines": { 1403 | "node": ">=8" 1404 | } 1405 | }, 1406 | "node_modules/path-to-regexp": { 1407 | "version": "0.1.12", 1408 | "license": "MIT" 1409 | }, 1410 | "node_modules/picocolors": { 1411 | "version": "1.1.1", 1412 | "dev": true, 1413 | "license": "ISC" 1414 | }, 1415 | "node_modules/postcss": { 1416 | "version": "8.5.3", 1417 | "dev": true, 1418 | "funding": [ 1419 | { 1420 | "type": "opencollective", 1421 | "url": "https://opencollective.com/postcss/" 1422 | }, 1423 | { 1424 | "type": "tidelift", 1425 | "url": "https://tidelift.com/funding/github/npm/postcss" 1426 | }, 1427 | { 1428 | "type": "github", 1429 | "url": "https://github.com/sponsors/ai" 1430 | } 1431 | ], 1432 | "license": "MIT", 1433 | "dependencies": { 1434 | "nanoid": "^3.3.8", 1435 | "picocolors": "^1.1.1", 1436 | "source-map-js": "^1.2.1" 1437 | }, 1438 | "engines": { 1439 | "node": "^10 || ^12 || >=14" 1440 | } 1441 | }, 1442 | "node_modules/proxy-addr": { 1443 | "version": "2.0.7", 1444 | "license": "MIT", 1445 | "dependencies": { 1446 | "forwarded": "0.2.0", 1447 | "ipaddr.js": "1.9.1" 1448 | }, 1449 | "engines": { 1450 | "node": ">= 0.10" 1451 | } 1452 | }, 1453 | "node_modules/qs": { 1454 | "version": "6.13.0", 1455 | "license": "BSD-3-Clause", 1456 | "dependencies": { 1457 | "side-channel": "^1.0.6" 1458 | }, 1459 | "engines": { 1460 | "node": ">=0.6" 1461 | }, 1462 | "funding": { 1463 | "url": "https://github.com/sponsors/ljharb" 1464 | } 1465 | }, 1466 | "node_modules/range-parser": { 1467 | "version": "1.2.1", 1468 | "license": "MIT", 1469 | "engines": { 1470 | "node": ">= 0.6" 1471 | } 1472 | }, 1473 | "node_modules/raw-body": { 1474 | "version": "2.5.2", 1475 | "license": "MIT", 1476 | "dependencies": { 1477 | "bytes": "3.1.2", 1478 | "http-errors": "2.0.0", 1479 | "iconv-lite": "0.4.24", 1480 | "unpipe": "1.0.0" 1481 | }, 1482 | "engines": { 1483 | "node": ">= 0.8" 1484 | } 1485 | }, 1486 | "node_modules/react": { 1487 | "version": "19.0.0", 1488 | "license": "MIT", 1489 | "peer": true, 1490 | "engines": { 1491 | "node": ">=0.10.0" 1492 | } 1493 | }, 1494 | "node_modules/require-directory": { 1495 | "version": "2.1.1", 1496 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1497 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1498 | "dev": true, 1499 | "license": "MIT", 1500 | "engines": { 1501 | "node": ">=0.10.0" 1502 | } 1503 | }, 1504 | "node_modules/rollup": { 1505 | "version": "4.34.9", 1506 | "dev": true, 1507 | "license": "MIT", 1508 | "dependencies": { 1509 | "@types/estree": "1.0.6" 1510 | }, 1511 | "bin": { 1512 | "rollup": "dist/bin/rollup" 1513 | }, 1514 | "engines": { 1515 | "node": ">=18.0.0", 1516 | "npm": ">=8.0.0" 1517 | }, 1518 | "optionalDependencies": { 1519 | "@rollup/rollup-android-arm-eabi": "4.34.9", 1520 | "@rollup/rollup-android-arm64": "4.34.9", 1521 | "@rollup/rollup-darwin-arm64": "4.34.9", 1522 | "@rollup/rollup-darwin-x64": "4.34.9", 1523 | "@rollup/rollup-freebsd-arm64": "4.34.9", 1524 | "@rollup/rollup-freebsd-x64": "4.34.9", 1525 | "@rollup/rollup-linux-arm-gnueabihf": "4.34.9", 1526 | "@rollup/rollup-linux-arm-musleabihf": "4.34.9", 1527 | "@rollup/rollup-linux-arm64-gnu": "4.34.9", 1528 | "@rollup/rollup-linux-arm64-musl": "4.34.9", 1529 | "@rollup/rollup-linux-loongarch64-gnu": "4.34.9", 1530 | "@rollup/rollup-linux-powerpc64le-gnu": "4.34.9", 1531 | "@rollup/rollup-linux-riscv64-gnu": "4.34.9", 1532 | "@rollup/rollup-linux-s390x-gnu": "4.34.9", 1533 | "@rollup/rollup-linux-x64-gnu": "4.34.9", 1534 | "@rollup/rollup-linux-x64-musl": "4.34.9", 1535 | "@rollup/rollup-win32-arm64-msvc": "4.34.9", 1536 | "@rollup/rollup-win32-ia32-msvc": "4.34.9", 1537 | "@rollup/rollup-win32-x64-msvc": "4.34.9", 1538 | "fsevents": "~2.3.2" 1539 | } 1540 | }, 1541 | "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { 1542 | "version": "4.34.9", 1543 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz", 1544 | "integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==", 1545 | "cpu": [ 1546 | "x64" 1547 | ], 1548 | "dev": true, 1549 | "license": "MIT", 1550 | "optional": true, 1551 | "os": [ 1552 | "linux" 1553 | ] 1554 | }, 1555 | "node_modules/rxjs": { 1556 | "version": "7.8.2", 1557 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", 1558 | "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", 1559 | "dev": true, 1560 | "license": "Apache-2.0", 1561 | "dependencies": { 1562 | "tslib": "^2.1.0" 1563 | } 1564 | }, 1565 | "node_modules/safe-buffer": { 1566 | "version": "5.2.1", 1567 | "funding": [ 1568 | { 1569 | "type": "github", 1570 | "url": "https://github.com/sponsors/feross" 1571 | }, 1572 | { 1573 | "type": "patreon", 1574 | "url": "https://www.patreon.com/feross" 1575 | }, 1576 | { 1577 | "type": "consulting", 1578 | "url": "https://feross.org/support" 1579 | } 1580 | ], 1581 | "license": "MIT" 1582 | }, 1583 | "node_modules/safer-buffer": { 1584 | "version": "2.1.2", 1585 | "license": "MIT" 1586 | }, 1587 | "node_modules/secure-json-parse": { 1588 | "version": "2.7.0", 1589 | "license": "BSD-3-Clause" 1590 | }, 1591 | "node_modules/send": { 1592 | "version": "0.19.0", 1593 | "license": "MIT", 1594 | "dependencies": { 1595 | "debug": "2.6.9", 1596 | "depd": "2.0.0", 1597 | "destroy": "1.2.0", 1598 | "encodeurl": "~1.0.2", 1599 | "escape-html": "~1.0.3", 1600 | "etag": "~1.8.1", 1601 | "fresh": "0.5.2", 1602 | "http-errors": "2.0.0", 1603 | "mime": "1.6.0", 1604 | "ms": "2.1.3", 1605 | "on-finished": "2.4.1", 1606 | "range-parser": "~1.2.1", 1607 | "statuses": "2.0.1" 1608 | }, 1609 | "engines": { 1610 | "node": ">= 0.8.0" 1611 | } 1612 | }, 1613 | "node_modules/send/node_modules/encodeurl": { 1614 | "version": "1.0.2", 1615 | "license": "MIT", 1616 | "engines": { 1617 | "node": ">= 0.8" 1618 | } 1619 | }, 1620 | "node_modules/send/node_modules/ms": { 1621 | "version": "2.1.3", 1622 | "license": "MIT" 1623 | }, 1624 | "node_modules/serve-static": { 1625 | "version": "1.16.2", 1626 | "license": "MIT", 1627 | "dependencies": { 1628 | "encodeurl": "~2.0.0", 1629 | "escape-html": "~1.0.3", 1630 | "parseurl": "~1.3.3", 1631 | "send": "0.19.0" 1632 | }, 1633 | "engines": { 1634 | "node": ">= 0.8.0" 1635 | } 1636 | }, 1637 | "node_modules/setprototypeof": { 1638 | "version": "1.2.0", 1639 | "license": "ISC" 1640 | }, 1641 | "node_modules/shebang-command": { 1642 | "version": "2.0.0", 1643 | "dev": true, 1644 | "license": "MIT", 1645 | "dependencies": { 1646 | "shebang-regex": "^3.0.0" 1647 | }, 1648 | "engines": { 1649 | "node": ">=8" 1650 | } 1651 | }, 1652 | "node_modules/shebang-regex": { 1653 | "version": "3.0.0", 1654 | "dev": true, 1655 | "license": "MIT", 1656 | "engines": { 1657 | "node": ">=8" 1658 | } 1659 | }, 1660 | "node_modules/shell-quote": { 1661 | "version": "1.8.2", 1662 | "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", 1663 | "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", 1664 | "dev": true, 1665 | "license": "MIT", 1666 | "engines": { 1667 | "node": ">= 0.4" 1668 | }, 1669 | "funding": { 1670 | "url": "https://github.com/sponsors/ljharb" 1671 | } 1672 | }, 1673 | "node_modules/side-channel": { 1674 | "version": "1.1.0", 1675 | "license": "MIT", 1676 | "dependencies": { 1677 | "es-errors": "^1.3.0", 1678 | "object-inspect": "^1.13.3", 1679 | "side-channel-list": "^1.0.0", 1680 | "side-channel-map": "^1.0.1", 1681 | "side-channel-weakmap": "^1.0.2" 1682 | }, 1683 | "engines": { 1684 | "node": ">= 0.4" 1685 | }, 1686 | "funding": { 1687 | "url": "https://github.com/sponsors/ljharb" 1688 | } 1689 | }, 1690 | "node_modules/side-channel-list": { 1691 | "version": "1.0.0", 1692 | "license": "MIT", 1693 | "dependencies": { 1694 | "es-errors": "^1.3.0", 1695 | "object-inspect": "^1.13.3" 1696 | }, 1697 | "engines": { 1698 | "node": ">= 0.4" 1699 | }, 1700 | "funding": { 1701 | "url": "https://github.com/sponsors/ljharb" 1702 | } 1703 | }, 1704 | "node_modules/side-channel-map": { 1705 | "version": "1.0.1", 1706 | "license": "MIT", 1707 | "dependencies": { 1708 | "call-bound": "^1.0.2", 1709 | "es-errors": "^1.3.0", 1710 | "get-intrinsic": "^1.2.5", 1711 | "object-inspect": "^1.13.3" 1712 | }, 1713 | "engines": { 1714 | "node": ">= 0.4" 1715 | }, 1716 | "funding": { 1717 | "url": "https://github.com/sponsors/ljharb" 1718 | } 1719 | }, 1720 | "node_modules/side-channel-weakmap": { 1721 | "version": "1.0.2", 1722 | "license": "MIT", 1723 | "dependencies": { 1724 | "call-bound": "^1.0.2", 1725 | "es-errors": "^1.3.0", 1726 | "get-intrinsic": "^1.2.5", 1727 | "object-inspect": "^1.13.3", 1728 | "side-channel-map": "^1.0.1" 1729 | }, 1730 | "engines": { 1731 | "node": ">= 0.4" 1732 | }, 1733 | "funding": { 1734 | "url": "https://github.com/sponsors/ljharb" 1735 | } 1736 | }, 1737 | "node_modules/source-map-js": { 1738 | "version": "1.2.1", 1739 | "dev": true, 1740 | "license": "BSD-3-Clause", 1741 | "engines": { 1742 | "node": ">=0.10.0" 1743 | } 1744 | }, 1745 | "node_modules/statuses": { 1746 | "version": "2.0.1", 1747 | "license": "MIT", 1748 | "engines": { 1749 | "node": ">= 0.8" 1750 | } 1751 | }, 1752 | "node_modules/string-width": { 1753 | "version": "4.2.3", 1754 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1755 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1756 | "dev": true, 1757 | "license": "MIT", 1758 | "dependencies": { 1759 | "emoji-regex": "^8.0.0", 1760 | "is-fullwidth-code-point": "^3.0.0", 1761 | "strip-ansi": "^6.0.1" 1762 | }, 1763 | "engines": { 1764 | "node": ">=8" 1765 | } 1766 | }, 1767 | "node_modules/strip-ansi": { 1768 | "version": "6.0.1", 1769 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1770 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1771 | "dev": true, 1772 | "license": "MIT", 1773 | "dependencies": { 1774 | "ansi-regex": "^5.0.1" 1775 | }, 1776 | "engines": { 1777 | "node": ">=8" 1778 | } 1779 | }, 1780 | "node_modules/supports-color": { 1781 | "version": "8.1.1", 1782 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1783 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1784 | "dev": true, 1785 | "license": "MIT", 1786 | "dependencies": { 1787 | "has-flag": "^4.0.0" 1788 | }, 1789 | "engines": { 1790 | "node": ">=10" 1791 | }, 1792 | "funding": { 1793 | "url": "https://github.com/chalk/supports-color?sponsor=1" 1794 | } 1795 | }, 1796 | "node_modules/swr": { 1797 | "version": "2.3.3", 1798 | "license": "MIT", 1799 | "dependencies": { 1800 | "dequal": "^2.0.3", 1801 | "use-sync-external-store": "^1.4.0" 1802 | }, 1803 | "peerDependencies": { 1804 | "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 1805 | } 1806 | }, 1807 | "node_modules/throttleit": { 1808 | "version": "2.1.0", 1809 | "license": "MIT", 1810 | "engines": { 1811 | "node": ">=18" 1812 | }, 1813 | "funding": { 1814 | "url": "https://github.com/sponsors/sindresorhus" 1815 | } 1816 | }, 1817 | "node_modules/toidentifier": { 1818 | "version": "1.0.1", 1819 | "license": "MIT", 1820 | "engines": { 1821 | "node": ">=0.6" 1822 | } 1823 | }, 1824 | "node_modules/tree-kill": { 1825 | "version": "1.2.2", 1826 | "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", 1827 | "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", 1828 | "dev": true, 1829 | "license": "MIT", 1830 | "bin": { 1831 | "tree-kill": "cli.js" 1832 | } 1833 | }, 1834 | "node_modules/tslib": { 1835 | "version": "2.8.1", 1836 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 1837 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 1838 | "dev": true, 1839 | "license": "0BSD" 1840 | }, 1841 | "node_modules/type-is": { 1842 | "version": "1.6.18", 1843 | "license": "MIT", 1844 | "dependencies": { 1845 | "media-typer": "0.3.0", 1846 | "mime-types": "~2.1.24" 1847 | }, 1848 | "engines": { 1849 | "node": ">= 0.6" 1850 | } 1851 | }, 1852 | "node_modules/typescript": { 1853 | "version": "5.8.2", 1854 | "dev": true, 1855 | "license": "Apache-2.0", 1856 | "bin": { 1857 | "tsc": "bin/tsc", 1858 | "tsserver": "bin/tsserver" 1859 | }, 1860 | "engines": { 1861 | "node": ">=14.17" 1862 | } 1863 | }, 1864 | "node_modules/undici-types": { 1865 | "version": "6.20.0", 1866 | "dev": true, 1867 | "license": "MIT" 1868 | }, 1869 | "node_modules/unpipe": { 1870 | "version": "1.0.0", 1871 | "license": "MIT", 1872 | "engines": { 1873 | "node": ">= 0.8" 1874 | } 1875 | }, 1876 | "node_modules/use-sync-external-store": { 1877 | "version": "1.4.0", 1878 | "license": "MIT", 1879 | "peerDependencies": { 1880 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 1881 | } 1882 | }, 1883 | "node_modules/utils-merge": { 1884 | "version": "1.0.1", 1885 | "license": "MIT", 1886 | "engines": { 1887 | "node": ">= 0.4.0" 1888 | } 1889 | }, 1890 | "node_modules/vary": { 1891 | "version": "1.1.2", 1892 | "license": "MIT", 1893 | "engines": { 1894 | "node": ">= 0.8" 1895 | } 1896 | }, 1897 | "node_modules/vite": { 1898 | "version": "6.2.3", 1899 | "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz", 1900 | "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==", 1901 | "dev": true, 1902 | "license": "MIT", 1903 | "dependencies": { 1904 | "esbuild": "^0.25.0", 1905 | "postcss": "^8.5.3", 1906 | "rollup": "^4.30.1" 1907 | }, 1908 | "bin": { 1909 | "vite": "bin/vite.js" 1910 | }, 1911 | "engines": { 1912 | "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 1913 | }, 1914 | "funding": { 1915 | "url": "https://github.com/vitejs/vite?sponsor=1" 1916 | }, 1917 | "optionalDependencies": { 1918 | "fsevents": "~2.3.3" 1919 | }, 1920 | "peerDependencies": { 1921 | "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 1922 | "jiti": ">=1.21.0", 1923 | "less": "*", 1924 | "lightningcss": "^1.21.0", 1925 | "sass": "*", 1926 | "sass-embedded": "*", 1927 | "stylus": "*", 1928 | "sugarss": "*", 1929 | "terser": "^5.16.0", 1930 | "tsx": "^4.8.1", 1931 | "yaml": "^2.4.2" 1932 | }, 1933 | "peerDependenciesMeta": { 1934 | "@types/node": { 1935 | "optional": true 1936 | }, 1937 | "jiti": { 1938 | "optional": true 1939 | }, 1940 | "less": { 1941 | "optional": true 1942 | }, 1943 | "lightningcss": { 1944 | "optional": true 1945 | }, 1946 | "sass": { 1947 | "optional": true 1948 | }, 1949 | "sass-embedded": { 1950 | "optional": true 1951 | }, 1952 | "stylus": { 1953 | "optional": true 1954 | }, 1955 | "sugarss": { 1956 | "optional": true 1957 | }, 1958 | "terser": { 1959 | "optional": true 1960 | }, 1961 | "tsx": { 1962 | "optional": true 1963 | }, 1964 | "yaml": { 1965 | "optional": true 1966 | } 1967 | } 1968 | }, 1969 | "node_modules/which": { 1970 | "version": "2.0.2", 1971 | "dev": true, 1972 | "license": "ISC", 1973 | "dependencies": { 1974 | "isexe": "^2.0.0" 1975 | }, 1976 | "bin": { 1977 | "node-which": "bin/node-which" 1978 | }, 1979 | "engines": { 1980 | "node": ">= 8" 1981 | } 1982 | }, 1983 | "node_modules/wrap-ansi": { 1984 | "version": "7.0.0", 1985 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1986 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1987 | "dev": true, 1988 | "license": "MIT", 1989 | "dependencies": { 1990 | "ansi-styles": "^4.0.0", 1991 | "string-width": "^4.1.0", 1992 | "strip-ansi": "^6.0.0" 1993 | }, 1994 | "engines": { 1995 | "node": ">=10" 1996 | }, 1997 | "funding": { 1998 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1999 | } 2000 | }, 2001 | "node_modules/y18n": { 2002 | "version": "5.0.8", 2003 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 2004 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 2005 | "dev": true, 2006 | "license": "ISC", 2007 | "engines": { 2008 | "node": ">=10" 2009 | } 2010 | }, 2011 | "node_modules/yargs": { 2012 | "version": "17.7.2", 2013 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 2014 | "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 2015 | "dev": true, 2016 | "license": "MIT", 2017 | "dependencies": { 2018 | "cliui": "^8.0.1", 2019 | "escalade": "^3.1.1", 2020 | "get-caller-file": "^2.0.5", 2021 | "require-directory": "^2.1.1", 2022 | "string-width": "^4.2.3", 2023 | "y18n": "^5.0.5", 2024 | "yargs-parser": "^21.1.1" 2025 | }, 2026 | "engines": { 2027 | "node": ">=12" 2028 | } 2029 | }, 2030 | "node_modules/yargs-parser": { 2031 | "version": "21.1.1", 2032 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 2033 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 2034 | "dev": true, 2035 | "license": "ISC", 2036 | "engines": { 2037 | "node": ">=12" 2038 | } 2039 | }, 2040 | "node_modules/zod": { 2041 | "version": "3.24.2", 2042 | "license": "MIT", 2043 | "peer": true, 2044 | "funding": { 2045 | "url": "https://github.com/sponsors/colinhacks" 2046 | } 2047 | }, 2048 | "node_modules/zod-to-json-schema": { 2049 | "version": "3.24.3", 2050 | "license": "ISC", 2051 | "peerDependencies": { 2052 | "zod": "^3.24.1" 2053 | } 2054 | }, 2055 | "sdk": { 2056 | "name": "@web-applets/sdk", 2057 | "version": "0.2.6", 2058 | "license": "MIT", 2059 | "devDependencies": { 2060 | "@types/node": "^22.13.10", 2061 | "cross-env": "^7.0.3", 2062 | "esbuild": "^0.25.0", 2063 | "typescript": "^5.6.2" 2064 | } 2065 | }, 2066 | "test/example": { 2067 | "version": "1.0.0", 2068 | "license": "ISC", 2069 | "dependencies": { 2070 | "@web-applets/sdk": "*" 2071 | }, 2072 | "devDependencies": { 2073 | "concurrently": "^9.1.2", 2074 | "vite": "^6.2.3" 2075 | } 2076 | } 2077 | } 2078 | } 2079 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.6", 3 | "workspaces": [ 4 | "sdk", 5 | "inspector", 6 | "inspector/web", 7 | "test/example" 8 | ], 9 | "scripts": { 10 | "sync-version": "cd sdk && npm version $npm_package_version && cd ../inspector && npm version $npm_package_version", 11 | "build": "npm run build:sdk && npm run build:inspector", 12 | "build:sdk": "cd sdk && npm run build", 13 | "build:inspector": "cd inspector && npm run build", 14 | "example": "cd test/example && npm run dev" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /scripts/publish-preview.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | npm run sync-version 4 | 5 | cd sdk 6 | npm publish --tag next 7 | cd .. 8 | 9 | cd inspector 10 | npm publish --tag next -------------------------------------------------------------------------------- /scripts/publish-tag-only.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | npm run sync-version 4 | 5 | tag_latest() { 6 | local package_name=$(node -p "require('./package.json').name") 7 | local version=$(node -p "require('./package.json').version") 8 | echo "Tagging $package_name@$version as latest" 9 | npm dist-tag add "$package_name@$version" latest 10 | } 11 | 12 | cd sdk 13 | tag_latest 14 | cd .. 15 | 16 | cd inspector 17 | tag_latest 18 | -------------------------------------------------------------------------------- /scripts/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | npm run sync-version 4 | 5 | cd sdk 6 | npm publish 7 | cd .. 8 | 9 | cd inspector 10 | npm publish -------------------------------------------------------------------------------- /sdk/README.md: -------------------------------------------------------------------------------- 1 | # Web Applets 2 | 3 | > An open spec & SDK for creating web apps that agents can use. 4 | 5 | 🌐 [Docs](https://unternet.co/docs) | 👾 [Community Discord](https://discord.gg/VsMuEKmqvt) | 💌 [Mailing List](https://buttondown.com/unternet) 6 | 7 | [![Mozilla builders logo](.assets/builders.png)](https://builders.mozilla.org/) 8 | 9 | Web Applets is a [Mozilla Builders](https://builders.mozilla.org/) project. 10 | 11 | ## What is it? 12 | 13 | **Web Applets is an open specification for building software that both humans and AI can understand and use together.** Instead of forcing AI to operate traditional point-and-click apps built for humans, Web Applets creates a new kind of web software designed for human-AI collaboration. You can read more about it on our website. 14 | 15 | Read [the docs](https://unternet.co/docs/web-applets/introduction)! 16 | 17 | ![Demo of a web applets chatbot](.assets/applets-chat-demo.gif) 18 | 19 | ## Feedback & Community 20 | 21 | This is a community project, and we're open to community members discussing the project direction, and submitting code! 22 | 23 | - Join the [mailing list](https://buttondown.com/unternet). 24 | - Follow [our blog](https://unternet.co/blog) for regular updates 25 | - Join [our discord](https://discord.gg/VsMuEKmqvt) 26 | 27 | ## Get the codebase running 28 | 29 | ```sh 30 | npm install 31 | npm run build 32 | ``` 33 | 34 | ## License 35 | 36 | [MIT](./LICENSE.md) 37 | 38 | --- 39 | 40 | Built by [Unternet](https://unternet.co). 41 | -------------------------------------------------------------------------------- /sdk/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web-applets/sdk", 3 | "version": "0.2.6", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@web-applets/sdk", 9 | "version": "0.2.6", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "@types/node": "^22.13.9", 13 | "esbuild": "^0.25.0", 14 | "typescript": "^5.6.2" 15 | } 16 | }, 17 | "node_modules/@esbuild/aix-ppc64": { 18 | "version": "0.25.0", 19 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", 20 | "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", 21 | "cpu": [ 22 | "ppc64" 23 | ], 24 | "dev": true, 25 | "license": "MIT", 26 | "optional": true, 27 | "os": [ 28 | "aix" 29 | ], 30 | "engines": { 31 | "node": ">=18" 32 | } 33 | }, 34 | "node_modules/@esbuild/android-arm": { 35 | "version": "0.25.0", 36 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", 37 | "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", 38 | "cpu": [ 39 | "arm" 40 | ], 41 | "dev": true, 42 | "license": "MIT", 43 | "optional": true, 44 | "os": [ 45 | "android" 46 | ], 47 | "engines": { 48 | "node": ">=18" 49 | } 50 | }, 51 | "node_modules/@esbuild/android-arm64": { 52 | "version": "0.25.0", 53 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", 54 | "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", 55 | "cpu": [ 56 | "arm64" 57 | ], 58 | "dev": true, 59 | "license": "MIT", 60 | "optional": true, 61 | "os": [ 62 | "android" 63 | ], 64 | "engines": { 65 | "node": ">=18" 66 | } 67 | }, 68 | "node_modules/@esbuild/android-x64": { 69 | "version": "0.25.0", 70 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", 71 | "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", 72 | "cpu": [ 73 | "x64" 74 | ], 75 | "dev": true, 76 | "license": "MIT", 77 | "optional": true, 78 | "os": [ 79 | "android" 80 | ], 81 | "engines": { 82 | "node": ">=18" 83 | } 84 | }, 85 | "node_modules/@esbuild/darwin-arm64": { 86 | "version": "0.25.0", 87 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", 88 | "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", 89 | "cpu": [ 90 | "arm64" 91 | ], 92 | "dev": true, 93 | "license": "MIT", 94 | "optional": true, 95 | "os": [ 96 | "darwin" 97 | ], 98 | "engines": { 99 | "node": ">=18" 100 | } 101 | }, 102 | "node_modules/@esbuild/darwin-x64": { 103 | "version": "0.25.0", 104 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", 105 | "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", 106 | "cpu": [ 107 | "x64" 108 | ], 109 | "dev": true, 110 | "license": "MIT", 111 | "optional": true, 112 | "os": [ 113 | "darwin" 114 | ], 115 | "engines": { 116 | "node": ">=18" 117 | } 118 | }, 119 | "node_modules/@esbuild/freebsd-arm64": { 120 | "version": "0.25.0", 121 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", 122 | "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", 123 | "cpu": [ 124 | "arm64" 125 | ], 126 | "dev": true, 127 | "license": "MIT", 128 | "optional": true, 129 | "os": [ 130 | "freebsd" 131 | ], 132 | "engines": { 133 | "node": ">=18" 134 | } 135 | }, 136 | "node_modules/@esbuild/freebsd-x64": { 137 | "version": "0.25.0", 138 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", 139 | "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", 140 | "cpu": [ 141 | "x64" 142 | ], 143 | "dev": true, 144 | "license": "MIT", 145 | "optional": true, 146 | "os": [ 147 | "freebsd" 148 | ], 149 | "engines": { 150 | "node": ">=18" 151 | } 152 | }, 153 | "node_modules/@esbuild/linux-arm": { 154 | "version": "0.25.0", 155 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", 156 | "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", 157 | "cpu": [ 158 | "arm" 159 | ], 160 | "dev": true, 161 | "license": "MIT", 162 | "optional": true, 163 | "os": [ 164 | "linux" 165 | ], 166 | "engines": { 167 | "node": ">=18" 168 | } 169 | }, 170 | "node_modules/@esbuild/linux-arm64": { 171 | "version": "0.25.0", 172 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", 173 | "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", 174 | "cpu": [ 175 | "arm64" 176 | ], 177 | "dev": true, 178 | "license": "MIT", 179 | "optional": true, 180 | "os": [ 181 | "linux" 182 | ], 183 | "engines": { 184 | "node": ">=18" 185 | } 186 | }, 187 | "node_modules/@esbuild/linux-ia32": { 188 | "version": "0.25.0", 189 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", 190 | "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", 191 | "cpu": [ 192 | "ia32" 193 | ], 194 | "dev": true, 195 | "license": "MIT", 196 | "optional": true, 197 | "os": [ 198 | "linux" 199 | ], 200 | "engines": { 201 | "node": ">=18" 202 | } 203 | }, 204 | "node_modules/@esbuild/linux-loong64": { 205 | "version": "0.25.0", 206 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", 207 | "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", 208 | "cpu": [ 209 | "loong64" 210 | ], 211 | "dev": true, 212 | "license": "MIT", 213 | "optional": true, 214 | "os": [ 215 | "linux" 216 | ], 217 | "engines": { 218 | "node": ">=18" 219 | } 220 | }, 221 | "node_modules/@esbuild/linux-mips64el": { 222 | "version": "0.25.0", 223 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", 224 | "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", 225 | "cpu": [ 226 | "mips64el" 227 | ], 228 | "dev": true, 229 | "license": "MIT", 230 | "optional": true, 231 | "os": [ 232 | "linux" 233 | ], 234 | "engines": { 235 | "node": ">=18" 236 | } 237 | }, 238 | "node_modules/@esbuild/linux-ppc64": { 239 | "version": "0.25.0", 240 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", 241 | "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", 242 | "cpu": [ 243 | "ppc64" 244 | ], 245 | "dev": true, 246 | "license": "MIT", 247 | "optional": true, 248 | "os": [ 249 | "linux" 250 | ], 251 | "engines": { 252 | "node": ">=18" 253 | } 254 | }, 255 | "node_modules/@esbuild/linux-riscv64": { 256 | "version": "0.25.0", 257 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", 258 | "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", 259 | "cpu": [ 260 | "riscv64" 261 | ], 262 | "dev": true, 263 | "license": "MIT", 264 | "optional": true, 265 | "os": [ 266 | "linux" 267 | ], 268 | "engines": { 269 | "node": ">=18" 270 | } 271 | }, 272 | "node_modules/@esbuild/linux-s390x": { 273 | "version": "0.25.0", 274 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", 275 | "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", 276 | "cpu": [ 277 | "s390x" 278 | ], 279 | "dev": true, 280 | "license": "MIT", 281 | "optional": true, 282 | "os": [ 283 | "linux" 284 | ], 285 | "engines": { 286 | "node": ">=18" 287 | } 288 | }, 289 | "node_modules/@esbuild/linux-x64": { 290 | "version": "0.25.0", 291 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", 292 | "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", 293 | "cpu": [ 294 | "x64" 295 | ], 296 | "dev": true, 297 | "license": "MIT", 298 | "optional": true, 299 | "os": [ 300 | "linux" 301 | ], 302 | "engines": { 303 | "node": ">=18" 304 | } 305 | }, 306 | "node_modules/@esbuild/netbsd-arm64": { 307 | "version": "0.25.0", 308 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", 309 | "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", 310 | "cpu": [ 311 | "arm64" 312 | ], 313 | "dev": true, 314 | "license": "MIT", 315 | "optional": true, 316 | "os": [ 317 | "netbsd" 318 | ], 319 | "engines": { 320 | "node": ">=18" 321 | } 322 | }, 323 | "node_modules/@esbuild/netbsd-x64": { 324 | "version": "0.25.0", 325 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", 326 | "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", 327 | "cpu": [ 328 | "x64" 329 | ], 330 | "dev": true, 331 | "license": "MIT", 332 | "optional": true, 333 | "os": [ 334 | "netbsd" 335 | ], 336 | "engines": { 337 | "node": ">=18" 338 | } 339 | }, 340 | "node_modules/@esbuild/openbsd-arm64": { 341 | "version": "0.25.0", 342 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", 343 | "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", 344 | "cpu": [ 345 | "arm64" 346 | ], 347 | "dev": true, 348 | "license": "MIT", 349 | "optional": true, 350 | "os": [ 351 | "openbsd" 352 | ], 353 | "engines": { 354 | "node": ">=18" 355 | } 356 | }, 357 | "node_modules/@esbuild/openbsd-x64": { 358 | "version": "0.25.0", 359 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", 360 | "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", 361 | "cpu": [ 362 | "x64" 363 | ], 364 | "dev": true, 365 | "license": "MIT", 366 | "optional": true, 367 | "os": [ 368 | "openbsd" 369 | ], 370 | "engines": { 371 | "node": ">=18" 372 | } 373 | }, 374 | "node_modules/@esbuild/sunos-x64": { 375 | "version": "0.25.0", 376 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", 377 | "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", 378 | "cpu": [ 379 | "x64" 380 | ], 381 | "dev": true, 382 | "license": "MIT", 383 | "optional": true, 384 | "os": [ 385 | "sunos" 386 | ], 387 | "engines": { 388 | "node": ">=18" 389 | } 390 | }, 391 | "node_modules/@esbuild/win32-arm64": { 392 | "version": "0.25.0", 393 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", 394 | "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", 395 | "cpu": [ 396 | "arm64" 397 | ], 398 | "dev": true, 399 | "license": "MIT", 400 | "optional": true, 401 | "os": [ 402 | "win32" 403 | ], 404 | "engines": { 405 | "node": ">=18" 406 | } 407 | }, 408 | "node_modules/@esbuild/win32-ia32": { 409 | "version": "0.25.0", 410 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", 411 | "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", 412 | "cpu": [ 413 | "ia32" 414 | ], 415 | "dev": true, 416 | "license": "MIT", 417 | "optional": true, 418 | "os": [ 419 | "win32" 420 | ], 421 | "engines": { 422 | "node": ">=18" 423 | } 424 | }, 425 | "node_modules/@esbuild/win32-x64": { 426 | "version": "0.25.0", 427 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", 428 | "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", 429 | "cpu": [ 430 | "x64" 431 | ], 432 | "dev": true, 433 | "license": "MIT", 434 | "optional": true, 435 | "os": [ 436 | "win32" 437 | ], 438 | "engines": { 439 | "node": ">=18" 440 | } 441 | }, 442 | "node_modules/@types/node": { 443 | "version": "22.13.9", 444 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz", 445 | "integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==", 446 | "dev": true, 447 | "license": "MIT", 448 | "dependencies": { 449 | "undici-types": "~6.20.0" 450 | } 451 | }, 452 | "node_modules/esbuild": { 453 | "version": "0.25.0", 454 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", 455 | "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", 456 | "dev": true, 457 | "hasInstallScript": true, 458 | "license": "MIT", 459 | "bin": { 460 | "esbuild": "bin/esbuild" 461 | }, 462 | "engines": { 463 | "node": ">=18" 464 | }, 465 | "optionalDependencies": { 466 | "@esbuild/aix-ppc64": "0.25.0", 467 | "@esbuild/android-arm": "0.25.0", 468 | "@esbuild/android-arm64": "0.25.0", 469 | "@esbuild/android-x64": "0.25.0", 470 | "@esbuild/darwin-arm64": "0.25.0", 471 | "@esbuild/darwin-x64": "0.25.0", 472 | "@esbuild/freebsd-arm64": "0.25.0", 473 | "@esbuild/freebsd-x64": "0.25.0", 474 | "@esbuild/linux-arm": "0.25.0", 475 | "@esbuild/linux-arm64": "0.25.0", 476 | "@esbuild/linux-ia32": "0.25.0", 477 | "@esbuild/linux-loong64": "0.25.0", 478 | "@esbuild/linux-mips64el": "0.25.0", 479 | "@esbuild/linux-ppc64": "0.25.0", 480 | "@esbuild/linux-riscv64": "0.25.0", 481 | "@esbuild/linux-s390x": "0.25.0", 482 | "@esbuild/linux-x64": "0.25.0", 483 | "@esbuild/netbsd-arm64": "0.25.0", 484 | "@esbuild/netbsd-x64": "0.25.0", 485 | "@esbuild/openbsd-arm64": "0.25.0", 486 | "@esbuild/openbsd-x64": "0.25.0", 487 | "@esbuild/sunos-x64": "0.25.0", 488 | "@esbuild/win32-arm64": "0.25.0", 489 | "@esbuild/win32-ia32": "0.25.0", 490 | "@esbuild/win32-x64": "0.25.0" 491 | } 492 | }, 493 | "node_modules/typescript": { 494 | "version": "5.6.2", 495 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", 496 | "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", 497 | "dev": true, 498 | "bin": { 499 | "tsc": "bin/tsc", 500 | "tsserver": "bin/tsserver" 501 | }, 502 | "engines": { 503 | "node": ">=14.17" 504 | } 505 | }, 506 | "node_modules/undici-types": { 507 | "version": "6.20.0", 508 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", 509 | "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", 510 | "dev": true, 511 | "license": "MIT" 512 | } 513 | } 514 | } 515 | -------------------------------------------------------------------------------- /sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web-applets/sdk", 3 | "description": "The Web Applets SDK, for creating & hosting Web Applets.", 4 | "author": "Rupert Manfredi ", 5 | "license": "MIT", 6 | "version": "0.2.6", 7 | "type": "module", 8 | "main": "dist/index.js", 9 | "types": "dist/index.d.ts", 10 | "files": [ 11 | "dist" 12 | ], 13 | "publishConfig": { 14 | "access": "public" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/unternet-co/web-applets.git" 19 | }, 20 | "scripts": { 21 | "build": "cross-env NODE_ENV=production npm run build:module && npm run build:shim:prod", 22 | "build:dev": "cross-env NODE_ENV=development npm run build:module && npm run build:shim:dev", 23 | "build:module": "tsc && cp ../README.md ./README.md", 24 | "build:shim:prod": "cross-env NODE_ENV=production node ./scripts/build.js", 25 | "build:shim:dev": "cross-env NODE_ENV=development node ./scripts/build.js", 26 | "prepublishOnly": "npm run build" 27 | }, 28 | "devDependencies": { 29 | "@types/node": "^22.13.10", 30 | "cross-env": "^7.0.3", 31 | "esbuild": "^0.25.0", 32 | "typescript": "^5.6.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sdk/scripts/build.js: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import process from 'process'; 3 | 4 | esbuild 5 | .build({ 6 | entryPoints: { 7 | 'web-applets.min': './src/polyfill.js', 8 | }, 9 | bundle: true, 10 | minify: true, 11 | outdir: './dist', 12 | }) 13 | .catch(() => { 14 | console.log('Failed to build web-applets.min.js'); 15 | process.exit(1); 16 | }); 17 | -------------------------------------------------------------------------------- /sdk/src/applets/actions.ts: -------------------------------------------------------------------------------- 1 | import { type JSONSchemaObject } from '../utils.js'; 2 | 3 | export interface AppletActionDescriptor { 4 | name?: string; 5 | description?: string; 6 | params_schema?: JSONSchemaObject; 7 | } 8 | -------------------------------------------------------------------------------- /sdk/src/applets/applet-factory.ts: -------------------------------------------------------------------------------- 1 | import { APPLET_CONNECT_TIMEOUT } from '../constants.js'; 2 | import { AppletConnectionError } from './errors.js'; 3 | import { Applet } from './applet.js'; 4 | import { AppletScope } from './applet-scope.js'; 5 | 6 | export class AppletFactory { 7 | async connect(window: Window): Promise> { 8 | return new Promise((resolve, reject) => { 9 | const applet = new Applet(window); 10 | const timeout = setTimeout(() => { 11 | reject( 12 | new AppletConnectionError( 13 | `Applet failed to connect before the timeout was reached (${APPLET_CONNECT_TIMEOUT}ms)` 14 | ) 15 | ); 16 | }, APPLET_CONNECT_TIMEOUT); 17 | const listener = () => { 18 | resolve(applet); 19 | applet.removeEventListener('connect', listener); 20 | clearTimeout(timeout); 21 | }; 22 | applet.addEventListener('connect', listener); 23 | }); 24 | } 25 | 26 | register(manifest?: Object): AppletScope { 27 | return new AppletScope(manifest); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sdk/src/applets/applet-scope.ts: -------------------------------------------------------------------------------- 1 | import { AppletActionDescriptor } from './actions.js'; 2 | import { debug } from '../debug.js'; 3 | import { AppletEvent } from './events.js'; 4 | import { 5 | AppletActionMessage, 6 | AppletActionsMessage, 7 | AppletDataMessage, 8 | AppletMessage, 9 | AppletRegisterMessage, 10 | AppletResizeMessage, 11 | AppletActionErrorMessage, 12 | AppletActionCompleteMessage, 13 | AppletConnectMessage, 14 | } from '../messages.js'; 15 | import { AppletManifest, dispatchEventAndHandler } from '../utils.js'; 16 | import { isEmpty } from '../utils.js'; 17 | 18 | export class AppletScope extends EventTarget { 19 | #actionHandlers: { [key: string]: Function } = {}; 20 | #manifest: AppletManifest; 21 | #actions: { [key: string]: AppletActionDescriptor }; 22 | #data: DataType; 23 | #dispatchEventAndHandler: typeof dispatchEventAndHandler; 24 | #postMessage: MessagePort['postMessage']; 25 | #width: number; 26 | #height: number; 27 | 28 | onconnect: (event: AppletEvent) => void; 29 | onactions: (event: AppletEvent) => void; 30 | ondata: (event: AppletEvent) => void; 31 | 32 | constructor(manifest?: Object | undefined) { 33 | super(); 34 | debug.log('AppletScope', 'Constructor called'); 35 | this.#dispatchEventAndHandler = dispatchEventAndHandler.bind(this); 36 | if (manifest) this.#manifest = manifest; 37 | 38 | // Listen for a connect event to set up message port 39 | const appletConnectListener = (event: MessageEvent) => { 40 | if ( 41 | event.source === window.parent && 42 | event.data.type === 'appletconnect' && 43 | event.ports && 44 | event.ports.length > 0 45 | ) { 46 | debug.log('AppletScope', 'Recieved message', event.data); 47 | const port = event.ports[0]; 48 | this.#postMessage = port.postMessage.bind(port); 49 | port.onmessage = this.#handleMessage.bind(this); 50 | this.removeEventListener('message', appletConnectListener); 51 | this.#initialize(); 52 | } 53 | }; 54 | window.addEventListener('message', appletConnectListener); 55 | 56 | const connectMessage: AppletConnectMessage = { 57 | type: 'appletconnect', 58 | }; 59 | window.parent.postMessage(connectMessage, '*'); 60 | debug.log('AppletScope', 'Send message', connectMessage); 61 | } 62 | 63 | async #initialize() { 64 | const manifest = this.manifest ?? (await this.#loadManifest()); 65 | this.#manifest = manifest || {}; 66 | this.#actions = this.#actions || manifest?.actions || {}; 67 | 68 | // Register the applet 69 | const registerMessage: AppletRegisterMessage = { 70 | type: 'register', 71 | manifest: this.#manifest, 72 | actions: this.#actions, 73 | data: this.#data, 74 | }; 75 | this.#postMessage(registerMessage); 76 | debug.log('AppletScope', 'Send message', registerMessage); 77 | 78 | const connectEvent = new AppletEvent('connect'); 79 | this.#dispatchEventAndHandler(connectEvent); 80 | 81 | this.#createResizeObserver(); 82 | } 83 | 84 | #handleMessage(messageEvent: MessageEvent) { 85 | const message = messageEvent.data as AppletMessage; 86 | debug.log('AppletScope', 'Recieved message', message); 87 | 88 | switch (message.type) { 89 | case 'data': 90 | if ('data' in message) this.data = message.data as DataType; 91 | break; 92 | case 'action': 93 | if ( 94 | 'type' in message && 95 | message.type === 'action' && 96 | 'id' in message && 97 | typeof message.id === 'string' && 98 | 'actionId' in message && 99 | typeof message.actionId == 'string' && 100 | 'arguments' in message 101 | ) { 102 | this.#handleActionMessage(message as AppletActionMessage); 103 | } 104 | break; 105 | } 106 | } 107 | 108 | async #handleActionMessage(message: AppletActionMessage) { 109 | const { actionId, arguments: args, id } = message; 110 | if (Object.keys(this.#actionHandlers).includes(actionId)) { 111 | try { 112 | await this.#actionHandlers[actionId](args); 113 | const actionCompleteMessage: AppletActionCompleteMessage = { 114 | type: 'actioncomplete', 115 | id, 116 | }; 117 | this.#postMessage(actionCompleteMessage); 118 | } catch (e) { 119 | const actionErrorMessage: AppletActionErrorMessage = { 120 | type: 'actionerror', 121 | id, 122 | message: e.message, 123 | }; 124 | this.#postMessage(actionErrorMessage); 125 | console.error(e); 126 | } 127 | } 128 | } 129 | 130 | #createResizeObserver() { 131 | const resizeObserver = new ResizeObserver((entries) => { 132 | for (let entry of entries) 133 | this.#handleResize({ 134 | width: entry.contentRect.width, 135 | height: entry.contentRect.height, 136 | }); 137 | }); 138 | resizeObserver.observe(document.querySelector('html')!); 139 | } 140 | 141 | #handleResize({ width, height }: { width: number; height: number }) { 142 | this.#width = width; 143 | this.#height = height; 144 | const resizeMessage: AppletResizeMessage = { 145 | type: 'resize', 146 | width, 147 | height, 148 | }; 149 | debug.log('AppletScope', 'Send message', resizeMessage); 150 | this.#postMessage(resizeMessage); 151 | } 152 | 153 | async #loadManifest(): Promise { 154 | const manifestLinkElem = document.querySelector('link[rel="manifest"]') as 155 | | HTMLLinkElement 156 | | undefined; 157 | if (!manifestLinkElem) { 158 | console.warn('No manifest link found'); 159 | return; 160 | } 161 | 162 | try { 163 | const manifestRequest = await fetch(manifestLinkElem.href); 164 | const manifest = (await manifestRequest.json()) as AppletManifest; 165 | for (const key in manifest.actions) { 166 | const action = manifest.actions[key]; 167 | if (action.params_schema && isEmpty(action.params_schema)) { 168 | action.params_schema = undefined; 169 | } 170 | } 171 | return manifest; 172 | } catch (e) { 173 | console.warn('Failed to fetch manifest', e.message); 174 | } 175 | } 176 | 177 | setActionHandler(actionId: string, handler: Function) { 178 | this.#actionHandlers[actionId] = handler; 179 | } 180 | 181 | defineAction( 182 | actionId: string, 183 | definition: AppletActionDescriptor & { handler?: Function } 184 | ) { 185 | const { handler, ...actionDefinition } = definition; 186 | if (handler) this.#actionHandlers[actionId] = handler; 187 | this.actions = { 188 | ...this.actions, 189 | [actionId]: actionDefinition, 190 | }; 191 | } 192 | 193 | set actions(actions: { [id: string]: AppletActionDescriptor }) { 194 | if (!actions) return; 195 | this.#actions = actions; 196 | 197 | const actionsMessage: AppletActionsMessage = { 198 | type: 'actions', 199 | actions: this.#actions, 200 | }; 201 | 202 | debug.log('AppletScope', 'Send message', actionsMessage); 203 | this.#postMessage && this.#postMessage(actionsMessage); 204 | 205 | // Set a timeout, so if data is set and a listener attached immediately after 206 | // the listener will still fire 207 | const dataEvent = new AppletEvent('actions', { actions }); 208 | setTimeout(() => this.#dispatchEventAndHandler(dataEvent), 1); 209 | } 210 | 211 | get actions(): { [id: string]: AppletActionDescriptor } { 212 | return this.#actions; 213 | } 214 | 215 | get manifest(): AppletManifest { 216 | return this.#manifest; 217 | } 218 | 219 | get actionHandlers(): { [id: string]: Function } { 220 | return this.#actionHandlers; 221 | } 222 | 223 | set actionHandlers(handlers: { [id: string]: Function }) { 224 | this.#actionHandlers = handlers; 225 | } 226 | 227 | set data(data: DataType) { 228 | this.#data = data; 229 | 230 | const dataMessage: AppletDataMessage = { 231 | type: 'data', 232 | data, 233 | }; 234 | debug.log('AppletScope', 'Send message', dataMessage); 235 | this.#postMessage && this.#postMessage(dataMessage); 236 | 237 | // Set a timeout, so if data is set and a listener attached immediately after 238 | // the listener will still fire 239 | const dataEvent = new AppletEvent('data', { data }); 240 | setTimeout(() => this.#dispatchEventAndHandler(dataEvent), 1); 241 | } 242 | 243 | get data(): DataType { 244 | return this.#data; 245 | } 246 | 247 | get width(): number { 248 | return this.#width; 249 | } 250 | 251 | get height(): number { 252 | return this.#height; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /sdk/src/applets/applet.ts: -------------------------------------------------------------------------------- 1 | import { RESPONSE_MESSAGE_TIMEOUT } from '../constants.js'; 2 | import { AppletManifest, dispatchEventAndHandler } from '../utils.js'; 3 | import { AppletEvent } from './events.js'; 4 | import { 5 | AppletActionErrorMessage, 6 | AppletActionMessage, 7 | AppletActionsMessage, 8 | AppletConnectMessage, 9 | AppletDataMessage, 10 | AppletMessage, 11 | AppletRegisterMessage, 12 | AppletResizeMessage, 13 | } from '../messages.js'; 14 | import { debug } from '../debug.js'; 15 | import { AppletExecutionError } from './errors.js'; 16 | import { AppletActionDescriptor } from './actions.js'; 17 | 18 | export class Applet extends EventTarget { 19 | #window: Window; 20 | #actions: { [id: string]: AppletActionDescriptor } = {}; 21 | #manifest: AppletManifest; 22 | #data: DataType; 23 | #dispatchEventAndHandler: typeof dispatchEventAndHandler; 24 | #messagePort: MessagePort; 25 | #postMessage: MessagePort['postMessage']; 26 | #width: number; 27 | #height: number; 28 | 29 | onconnect: (event: AppletEvent) => void; 30 | onresize: (event: AppletEvent) => void; 31 | onactions: (event: AppletEvent) => void; 32 | ondata: (event: AppletEvent) => void; 33 | 34 | constructor(targetWindow: Window) { 35 | super(); 36 | debug.log('Applet', 'Constructor called'); 37 | this.#window = targetWindow; 38 | this.#dispatchEventAndHandler = dispatchEventAndHandler.bind(this); 39 | 40 | // Set up message port 41 | this.#createMessageChannel(); 42 | 43 | // In case the window hasn't loaded, wait for load then 44 | const registerListener = (messageEvent: MessageEvent) => { 45 | if ( 46 | messageEvent.source === this.#window && 47 | 'type' in messageEvent.data && 48 | messageEvent.data.type === 'appletconnect' 49 | ) { 50 | debug.log('Applet', 'Recieved message', messageEvent.data); 51 | this.#createMessageChannel(); 52 | this.removeEventListener('message', registerListener); 53 | } 54 | }; 55 | window.addEventListener('message', registerListener); 56 | } 57 | 58 | #createMessageChannel() { 59 | if (this.#messagePort) this.#messagePort.close(); 60 | const messageChannel = new MessageChannel(); 61 | const connectMessage: AppletConnectMessage = { 62 | type: 'appletconnect', 63 | }; 64 | debug.log('Applet', 'Send message', connectMessage); 65 | this.#messagePort = messageChannel.port1; 66 | this.#messagePort.onmessage = this.#handleMessage.bind(this); 67 | this.#window.postMessage(connectMessage, '*', [messageChannel.port2]); 68 | this.#postMessage = this.#messagePort.postMessage.bind(this.#messagePort); 69 | } 70 | 71 | #handleMessage(messageEvent: MessageEvent) { 72 | const message = messageEvent.data; 73 | debug.log('Applet', 'Recieved message', message); 74 | 75 | switch (message.type) { 76 | case 'register': 77 | const registerMessage = message as AppletRegisterMessage; 78 | this.#manifest = registerMessage.manifest; 79 | const connectEvent = new AppletEvent('connect'); 80 | this.#dispatchEventAndHandler(connectEvent); 81 | this.#actions = registerMessage.actions; 82 | this.#dispatchActionsEvent(registerMessage.actions); 83 | this.#data = registerMessage.data; 84 | this.#dispatchDataEvent(registerMessage.data); 85 | break; 86 | case 'data': 87 | const dataMessage = message as AppletDataMessage; 88 | this.#data = dataMessage.data; 89 | this.#dispatchDataEvent(dataMessage.data); 90 | break; 91 | case 'resize': 92 | const resizeMessage = message as AppletResizeMessage; 93 | this.#width = resizeMessage.width; 94 | this.#height = resizeMessage.height; 95 | const resizeEvent = new AppletEvent('resize'); 96 | this.#dispatchEventAndHandler(resizeEvent); 97 | break; 98 | case 'actions': 99 | const actionsMessage = message as AppletActionsMessage; 100 | this.#actions = actionsMessage.actions; 101 | this.#dispatchActionsEvent(actionsMessage.actions); 102 | break; 103 | } 104 | } 105 | 106 | #dispatchDataEvent(data: any) { 107 | const actionsEvent = new AppletEvent('data', { 108 | data, 109 | }); 110 | this.#dispatchEventAndHandler(actionsEvent); 111 | } 112 | 113 | #dispatchActionsEvent(actions: { [id: string]: AppletActionDescriptor }) { 114 | const actionsEvent = new AppletEvent('actions', { 115 | actions, 116 | }); 117 | this.#dispatchEventAndHandler(actionsEvent); 118 | } 119 | 120 | async sendAction(actionId: string, args: any): Promise { 121 | const actionMessage: AppletActionMessage = { 122 | id: crypto.randomUUID(), 123 | type: 'action', 124 | actionId, 125 | arguments: args, 126 | }; 127 | 128 | return new Promise((resolve, reject) => { 129 | this.#postMessage(actionMessage); 130 | 131 | const timeout = setTimeout(() => { 132 | reject( 133 | new AppletExecutionError( 134 | `Applet action handler failed to complete before timeout (${RESPONSE_MESSAGE_TIMEOUT}ms)` 135 | ) 136 | ); 137 | }, RESPONSE_MESSAGE_TIMEOUT); 138 | 139 | const callback = (messageEvent: MessageEvent) => { 140 | const message = messageEvent.data as AppletMessage; 141 | if ( 142 | ['actioncomplete', 'actionerror'].includes(message.type) && 143 | 'id' in message && 144 | message.id === actionMessage.id 145 | ) { 146 | this.#messagePort.removeEventListener('message', callback); 147 | clearTimeout(timeout); 148 | 149 | if (message.type === 'actionerror') { 150 | const actionErrorMessage = message as AppletActionErrorMessage; 151 | reject(new AppletExecutionError(actionErrorMessage.message)); 152 | } else { 153 | resolve(); 154 | } 155 | } 156 | }; 157 | 158 | this.#messagePort.addEventListener('message', callback); 159 | }); 160 | } 161 | 162 | get data() { 163 | return this.#data; 164 | } 165 | 166 | set data(data: DataType) { 167 | this.#data = data; 168 | const dataMessage: AppletDataMessage = { 169 | type: 'data', 170 | data, 171 | }; 172 | this.#postMessage(dataMessage); 173 | } 174 | 175 | get window() { 176 | return this.#window; 177 | } 178 | 179 | // TODO: Allow set for this.window, which re-attaches the applet 180 | 181 | get manifest() { 182 | return this.#manifest; 183 | } 184 | 185 | get actions() { 186 | return this.#actions; 187 | } 188 | 189 | get width(): number { 190 | return this.#width; 191 | } 192 | 193 | get height(): number { 194 | return this.#height; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /sdk/src/applets/errors.ts: -------------------------------------------------------------------------------- 1 | export class AppletExecutionError extends Error { 2 | constructor(message: string) { 3 | super(message); 4 | this.name = 'AppletExecutionError'; 5 | } 6 | } 7 | 8 | export class AppletConnectionError extends Error { 9 | constructor(message?: string) { 10 | super(message); 11 | this.name = 'AppletConnectionError'; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sdk/src/applets/events.ts: -------------------------------------------------------------------------------- 1 | import { AppletActionDescriptor } from './actions.js'; 2 | 3 | export type AppletEventType = 'connect' | 'actions' | 'resize' | 'data'; 4 | 5 | export interface AppletEventInit extends EventInit { 6 | data?: any; 7 | actions?: { [id: string]: AppletActionDescriptor }; 8 | } 9 | 10 | export class AppletEvent extends Event { 11 | data?: any; 12 | actions?: { [id: string]: AppletActionDescriptor }; 13 | 14 | constructor(type: AppletEventType, init?: AppletEventInit | undefined) { 15 | super(type, { 16 | bubbles: init?.bubbles, 17 | composed: init?.composed, 18 | cancelable: init?.cancelable, 19 | }); 20 | 21 | this.data = init?.data; 22 | this.actions = init?.actions; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sdk/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const RESPONSE_MESSAGE_TIMEOUT = 10 * 1000; 2 | export const APPLET_CONNECT_TIMEOUT = 10 * 1000; 3 | 4 | export const errorCodes = { 5 | APPLET_ACTION_EXECUTION_ERROR: 'APPLET_ACTION_EXECUTION_ERROR', 6 | }; 7 | -------------------------------------------------------------------------------- /sdk/src/debug.ts: -------------------------------------------------------------------------------- 1 | const isProduction = process.env.NODE_ENV === "production"; 2 | 3 | function log(location: string, ...messages: any[]) { 4 | if (!isProduction) { 5 | console.log(`[${location}]`, ...messages); 6 | } 7 | } 8 | 9 | export const debug = { 10 | log, 11 | }; 12 | -------------------------------------------------------------------------------- /sdk/src/elements/applet-frame.ts: -------------------------------------------------------------------------------- 1 | import { Applet, AppletEvent, applets } from '../index.js'; 2 | import { dispatchEventAndHandler } from '../utils.js'; 3 | 4 | export class AppletFrameElement extends HTMLElement { 5 | #root: ShadowRoot; 6 | #src?: string; 7 | #applet?: Applet; 8 | #dispatchEventAndHandler = dispatchEventAndHandler.bind(this); 9 | #iframe?: HTMLIFrameElement = document.createElement('iframe'); 10 | 11 | onload: (event: Event) => Promise | void; 12 | onactions: (event: AppletEvent) => Promise | void; 13 | ondata: (event: AppletEvent) => Promise | void; 14 | 15 | static observedAttributes = ['src']; 16 | 17 | connectedCallback() { 18 | this.#root = this.attachShadow({ mode: 'closed' }); 19 | this.#root.appendChild(this.#iframe); 20 | const styles = document.createElement('style'); 21 | styles.textContent = this.styles; 22 | this.#root.appendChild(styles); 23 | this.src = this.getAttribute('src'); 24 | } 25 | 26 | get contentWindow() { 27 | return this.#iframe.contentWindow; 28 | } 29 | 30 | set src(value: string) { 31 | this.#src = value; 32 | this.#applet = undefined; 33 | this.#loadApplet(value); 34 | } 35 | 36 | get src() { 37 | return this.#src || ''; 38 | } 39 | 40 | attributeChangedCallback(name: string, oldValue: string, newValue: string) { 41 | if (name === 'src') { 42 | this.src = newValue; 43 | } 44 | } 45 | 46 | async #loadApplet(url: string) { 47 | this.#iframe.src = url; 48 | const window = this.#iframe.contentWindow; 49 | if (!window) return; 50 | 51 | this.#applet = await applets.connect(window); 52 | 53 | // When data received, bubble the event up 54 | this.#applet.ondata = (event: AppletEvent) => { 55 | this.#dispatchEventAndHandler(event); 56 | }; 57 | 58 | // Resize 59 | this.#applet.onresize = (event: AppletEvent) => { 60 | this.#resizeContainer({ 61 | width: this.#applet.width, 62 | height: this.#applet.height, 63 | }); 64 | }; 65 | 66 | this.#applet.onactions = (event: AppletEvent) => { 67 | this.#dispatchEventAndHandler(event); 68 | }; 69 | 70 | // Emit load event when setup & connection complete 71 | this.#dispatchEventAndHandler(new Event('load')); 72 | if (this['load'] && typeof this['load'] === 'function') { 73 | this.onload(new Event('load')); 74 | } 75 | } 76 | 77 | get applet() { 78 | return this.#applet; 79 | } 80 | 81 | set data(data: any) { 82 | if (this.applet) { 83 | this.applet.data = data; 84 | } else { 85 | const listener = () => { 86 | if (this.applet) this.applet.data = data; 87 | this.removeEventListener('load', listener); 88 | }; 89 | this.addEventListener('load', listener); 90 | } 91 | } 92 | 93 | #resizeContainer(dimensions: { height: number; width: number }) { 94 | this.style.height = `${dimensions.height}px`; 95 | } 96 | 97 | get styles() { 98 | return /*css*/ ` 99 | :host { 100 | background: white; 101 | display: flex; 102 | flex-direction: column; 103 | height: 350px; 104 | } 105 | 106 | iframe { 107 | border: none; 108 | height: 100%; 109 | width: 100%; 110 | } 111 | `; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | // AppletFactory 2 | 3 | import { AppletFactory } from './applets/applet-factory.js'; 4 | export const applets = new AppletFactory(); 5 | 6 | // Applet & AppletScope (as types, not intantiable classes) 7 | 8 | import { Applet as AppletClass } from './applets/applet.js'; 9 | import { AppletScope as AppletScopeClass } from './applets/applet-scope.js'; 10 | export type Applet = AppletClass; 11 | export type AppletScope = AppletScopeClass; 12 | 13 | // AppletEvent 14 | 15 | export { AppletEvent } from './applets/events.js'; 16 | 17 | // Actions 18 | 19 | export { type AppletActionDescriptor } from './applets/actions.js'; 20 | 21 | // AppletFrameElement 22 | 23 | import { AppletFrameElement } from './elements/applet-frame.js'; 24 | export { AppletFrameElement }; 25 | 26 | function registerElements() { 27 | if (!customElements.get('applet-frame')) { 28 | customElements.define('applet-frame', AppletFrameElement); 29 | } 30 | } 31 | 32 | registerElements(); 33 | 34 | export { registerElements }; 35 | -------------------------------------------------------------------------------- /sdk/src/messages.ts: -------------------------------------------------------------------------------- 1 | import { AppletActionDescriptor } from './applets/actions.js'; 2 | import { AppletManifest } from './utils.js'; 3 | 4 | export interface AppletMessage { 5 | type: string; 6 | } 7 | 8 | export interface AppletConnectMessage { 9 | type: 'appletconnect'; 10 | } 11 | 12 | export interface AppletRegisterMessage { 13 | type: 'register'; 14 | manifest?: AppletManifest; 15 | actions?: { [id: string]: AppletActionDescriptor }; 16 | data?: any; 17 | } 18 | 19 | export interface AppletActionMessage extends AppletMessage { 20 | type: 'action'; 21 | id: string; 22 | actionId: string; 23 | arguments: any; 24 | } 25 | 26 | export interface AppletActionCompleteMessage extends AppletMessage { 27 | type: 'actioncomplete'; 28 | id: string; 29 | } 30 | 31 | export interface AppletActionErrorMessage extends AppletMessage { 32 | type: 'actionerror'; 33 | id: string; 34 | message: string; 35 | } 36 | 37 | export interface AppletActionsMessage extends AppletMessage { 38 | type: 'actions'; 39 | actions: { [key: string]: AppletActionDescriptor }; 40 | } 41 | 42 | export interface AppletDataMessage extends AppletMessage { 43 | type: 'data'; 44 | data: T; 45 | } 46 | 47 | export interface AppletResizeMessage extends AppletMessage { 48 | type: 'resize'; 49 | height: number; 50 | width: number; 51 | } 52 | -------------------------------------------------------------------------------- /sdk/src/polyfill.ts: -------------------------------------------------------------------------------- 1 | import { AppletFrameElement, applets } from './index.js'; 2 | import { type AppletFactory } from './applets/applet-factory.js'; 3 | import { AppletEvent } from './applets/events.js'; 4 | 5 | declare global { 6 | interface Window { 7 | applets: AppletFactory; 8 | AppletEvent: typeof AppletEvent; 9 | AppletFrameElement: typeof AppletFrameElement; 10 | } 11 | } 12 | 13 | window.applets = applets; 14 | window.AppletEvent = AppletEvent; 15 | window.AppletFrameElement = AppletFrameElement; 16 | -------------------------------------------------------------------------------- /sdk/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { AppletActionDescriptor } from './applets/actions.js'; 2 | 3 | export function dispatchEventAndHandler(event: Event) { 4 | if (typeof this[`on${event.type}`] === 'function') { 5 | this[`on${event.type}`](event); 6 | } 7 | this.dispatchEvent(event); 8 | } 9 | 10 | export interface AppletManifest { 11 | name?: string; 12 | short_name?: string; 13 | icons?: { 14 | src: string; 15 | purpose?: string; 16 | sizes?: string; 17 | type?: string; 18 | }[]; 19 | description?: string; 20 | display?: string; 21 | start_url?: string; 22 | actions?: { [id: string]: AppletActionDescriptor }; 23 | [key: string]: any; 24 | } 25 | 26 | export interface JSONSchemaObject { 27 | type: 28 | | 'object' 29 | | 'string' 30 | | 'number' 31 | | 'integer' 32 | | 'array' 33 | | 'boolean' 34 | | 'null'; 35 | description?: string; 36 | properties?: { 37 | [key: string]: JSONSchemaObject; 38 | }; 39 | required?: string[]; 40 | additionalProperties?: boolean; 41 | } 42 | 43 | export function isEmpty(obj: object): boolean { 44 | return Object.keys(obj).length === 0; 45 | } 46 | -------------------------------------------------------------------------------- /sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "declaration": true, 5 | "outDir": "./dist", 6 | "esModuleInterop": true, 7 | "moduleResolution": "NodeNext", 8 | "module": "NodeNext" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 | Hello world! 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/example/main.js: -------------------------------------------------------------------------------- 1 | import { applets } from '@web-applets/sdk'; 2 | 3 | applets.register({ 4 | name: 'Hello world!', 5 | }); 6 | -------------------------------------------------------------------------------- /test/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "main": "main.js", 5 | "scripts": { 6 | "dev": "concurrently 'npm run dev:example' 'npm run dev:inspector'", 7 | "dev:example": "vite", 8 | "dev:inspector": "cd ../../inspector/web && npm run dev" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "description": "", 13 | "devDependencies": { 14 | "concurrently": "^9.1.2", 15 | "vite": "^6.2.3" 16 | }, 17 | "dependencies": { 18 | "@web-applets/sdk": "*" 19 | } 20 | } 21 | --------------------------------------------------------------------------------