From c25bd306d13cbbee54286f2de78227b89fd9a19c Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Fri, 1 May 2026 22:07:41 -0700 Subject: [PATCH 01/18] feat: add yjs-rich-markdown package and learning demo Adds a new package for rich markdown artifacts using TipTap/Yjs: - `packages/yjs-rich-markdown`: Core library with artifact store, path-keyed registry, and TipTap collaboration bindings - `examples/yjs-rich-markdown-demo`: TanStack Router demo app for testing collaboration, presence, and content isolation Learning tests confirm: - Multiple TipTap fragments in one Y.Doc remain isolated - One Durable Streams connection syncs all artifacts - Cursor presence is natively scoped per fragment (no adapter needed) - Both `fragment` and `field` binding modes work Co-Authored-By: Claude Opus 4.5 --- bun.lock | 58 +++++- examples/yjs-rich-markdown-demo/index.html | 62 +++++++ examples/yjs-rich-markdown-demo/package.json | 37 ++++ .../yjs-rich-markdown-demo/server/index.ts | 37 ++++ .../src/components/ArtifactEditor.tsx | 69 ++++++++ .../src/components/ArtifactList.tsx | 52 ++++++ .../src/components/AwarenessDebugPanel.tsx | 82 +++++++++ .../src/components/ConnectionStatus.tsx | 46 +++++ .../src/components/MarkdownExportPanel.tsx | 75 ++++++++ .../src/lib/collaboration.ts | 167 ++++++++++++++++++ .../src/lib/provider-awareness.ts | 124 +++++++++++++ examples/yjs-rich-markdown-demo/src/main.tsx | 18 ++ .../src/providers/ArtifactProvider.tsx | 132 ++++++++++++++ .../src/routeTree.gen.ts | 131 ++++++++++++++ .../src/routes/__root.tsx | 57 ++++++ .../src/routes/awareness.tsx | 116 ++++++++++++ .../src/routes/compare.tsx | 77 ++++++++ .../src/routes/index.tsx | 103 +++++++++++ .../src/routes/same-doc.tsx | 66 +++++++ .../src/routes/single.tsx | 66 +++++++ examples/yjs-rich-markdown-demo/tsconfig.json | 26 +++ .../yjs-rich-markdown-demo/vite.config.ts | 20 +++ package.json | 3 +- packages/yjs-rich-markdown/build.ts | 3 + packages/yjs-rich-markdown/package.json | 50 ++++++ .../yjs-rich-markdown/src/artifact-path.ts | 38 ++++ .../yjs-rich-markdown/src/artifact-store.ts | 86 +++++++++ packages/yjs-rich-markdown/src/index.ts | 6 + .../yjs-rich-markdown/src/tiptap-bindings.ts | 40 +++++ .../test/fragment-isolation.test.ts | 31 ++++ .../yjs-rich-markdown/tsconfig.build.json | 23 +++ packages/yjs-rich-markdown/tsconfig.json | 24 +++ 32 files changed, 1921 insertions(+), 4 deletions(-) create mode 100644 examples/yjs-rich-markdown-demo/index.html create mode 100644 examples/yjs-rich-markdown-demo/package.json create mode 100644 examples/yjs-rich-markdown-demo/server/index.ts create mode 100644 examples/yjs-rich-markdown-demo/src/components/ArtifactEditor.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/components/ArtifactList.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/components/AwarenessDebugPanel.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/components/ConnectionStatus.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/components/MarkdownExportPanel.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/lib/collaboration.ts create mode 100644 examples/yjs-rich-markdown-demo/src/lib/provider-awareness.ts create mode 100644 examples/yjs-rich-markdown-demo/src/main.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/providers/ArtifactProvider.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/routeTree.gen.ts create mode 100644 examples/yjs-rich-markdown-demo/src/routes/__root.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/routes/awareness.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/routes/compare.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/routes/index.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/routes/same-doc.tsx create mode 100644 examples/yjs-rich-markdown-demo/src/routes/single.tsx create mode 100644 examples/yjs-rich-markdown-demo/tsconfig.json create mode 100644 examples/yjs-rich-markdown-demo/vite.config.ts create mode 100644 packages/yjs-rich-markdown/build.ts create mode 100644 packages/yjs-rich-markdown/package.json create mode 100644 packages/yjs-rich-markdown/src/artifact-path.ts create mode 100644 packages/yjs-rich-markdown/src/artifact-store.ts create mode 100644 packages/yjs-rich-markdown/src/index.ts create mode 100644 packages/yjs-rich-markdown/src/tiptap-bindings.ts create mode 100644 packages/yjs-rich-markdown/test/fragment-isolation.test.ts create mode 100644 packages/yjs-rich-markdown/tsconfig.build.json create mode 100644 packages/yjs-rich-markdown/tsconfig.json diff --git a/bun.lock b/bun.lock index 02edbbd..076e4f1 100644 --- a/bun.lock +++ b/bun.lock @@ -77,6 +77,33 @@ "typescript": "^5", }, }, + "examples/yjs-rich-markdown-demo": { + "name": "@humanlayer/yjs-rich-markdown-demo", + "version": "0.0.1", + "dependencies": { + "@durable-streams/server": "^0.3.1", + "@durable-streams/y-durable-streams": "^0.2.4", + "@humanlayer/yjs-rich-markdown": "workspace:*", + "@tanstack/react-router": "^1.93.0", + "@tiptap/extension-collaboration": "^2.11.0", + "@tiptap/extension-collaboration-cursor": "^2.11.0", + "@tiptap/pm": "^2.11.0", + "@tiptap/react": "^2.11.0", + "@tiptap/starter-kit": "^2.11.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "y-protocols": "1.0.7", + "yjs": "13.6.30", + }, + "devDependencies": { + "@tanstack/router-plugin": "^1.93.0", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.6.3", + "vite": "^6.0.0", + }, + }, "packages/agentlayer-core": { "name": "@humanlayer/agentlayer-core", "version": "0.0.0", @@ -275,6 +302,23 @@ "yjs": "13.6.30", }, }, + "packages/yjs-rich-markdown": { + "name": "@humanlayer/yjs-rich-markdown", + "version": "0.0.0", + "devDependencies": { + "@durable-streams/server": "^0.3.1", + "@durable-streams/y-durable-streams": "^0.2.4", + "@types/bun": "latest", + }, + "peerDependencies": { + "@tiptap/core": "^2.11.0", + "@tiptap/extension-collaboration": "^2.11.0", + "@tiptap/starter-kit": "^2.11.0", + "typescript": "^5", + "y-protocols": "1.0.7", + "yjs": "13.6.30", + }, + }, }, "catalog": { "@ai-sdk/anthropic": "3.0.69", @@ -512,6 +556,10 @@ "@humanlayer/yjs-fs-react": ["@humanlayer/yjs-fs-react@workspace:packages/yjs-fs-react"], + "@humanlayer/yjs-rich-markdown": ["@humanlayer/yjs-rich-markdown@workspace:packages/yjs-rich-markdown"], + + "@humanlayer/yjs-rich-markdown-demo": ["@humanlayer/yjs-rich-markdown-demo@workspace:examples/yjs-rich-markdown-demo"], + "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.78", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-I3lkNp0Qu7q2iZWkdcf/I2hqGhzK6qxdILh9T7XqowQrnpmG/BayDsiCf6PktDoWlW0U971xA5g+panm+NFrfQ=="], "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], @@ -1252,7 +1300,7 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], @@ -2056,6 +2104,8 @@ "@humanlayer/yjs-fs-editor/@durable-streams/server": ["@durable-streams/server@0.1.7", "", { "dependencies": { "@durable-streams/client": "0.2.0", "@durable-streams/state": "0.2.0", "@neophi/sieve-cache": "^1.0.0", "lmdb": "^3.3.0" } }, "sha512-JaCzfKS/GI3ZlbmInHvqX/fAh6XENTjc+acfhRlZH9iaRQiD1rOjfircj+jFDTIHgeT9oPqOVscmISGot8kI3w=="], + "@humanlayer/yjs-rich-markdown/@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], + "@mermaid-js/mermaid-mindmap/@braintree/sanitize-url": ["@braintree/sanitize-url@6.0.4", "", {}, "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="], "@npmcli/git/ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], @@ -2098,6 +2148,8 @@ "markdown-it/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "mdast-util-frontmatter/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "minipass-fetch/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -2108,8 +2160,6 @@ "normalize-package-data/hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="], - "prosemirror-trailing-node/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - "rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], @@ -2156,6 +2206,8 @@ "@humanlayer/yjs-fs/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "@humanlayer/yjs-rich-markdown/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "@npmcli/git/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], "@npmcli/promise-spawn/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], diff --git a/examples/yjs-rich-markdown-demo/index.html b/examples/yjs-rich-markdown-demo/index.html new file mode 100644 index 0000000..6702802 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/index.html @@ -0,0 +1,62 @@ + + + + + + Rich Markdown Learning Demo + + + +
+ + + diff --git a/examples/yjs-rich-markdown-demo/package.json b/examples/yjs-rich-markdown-demo/package.json new file mode 100644 index 0000000..940c13a --- /dev/null +++ b/examples/yjs-rich-markdown-demo/package.json @@ -0,0 +1,37 @@ +{ + "name": "@humanlayer/yjs-rich-markdown-demo", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "dev": "bun run --parallel dev:server dev:vite", + "dev:vite": "vite --port 5176", + "dev:server": "bun run server/index.ts", + "build": "tsc -b && vite build", + "preview": "vite preview", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@durable-streams/server": "^0.3.1", + "@durable-streams/y-durable-streams": "^0.2.4", + "@humanlayer/yjs-rich-markdown": "workspace:*", + "@tiptap/extension-collaboration": "^2.11.0", + "@tiptap/extension-collaboration-cursor": "^2.11.0", + "@tiptap/pm": "^2.11.0", + "@tiptap/react": "^2.11.0", + "@tiptap/starter-kit": "^2.11.0", + "@tanstack/react-router": "^1.93.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "yjs": "13.6.30", + "y-protocols": "1.0.7" + }, + "devDependencies": { + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@tanstack/router-plugin": "^1.93.0", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.6.3", + "vite": "^6.0.0" + } +} diff --git a/examples/yjs-rich-markdown-demo/server/index.ts b/examples/yjs-rich-markdown-demo/server/index.ts new file mode 100644 index 0000000..64cd460 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/server/index.ts @@ -0,0 +1,37 @@ +import { DurableStreamTestServer } from '@durable-streams/server' +import { YjsServer } from '@durable-streams/y-durable-streams/server' + +const DS_PORT = 4447 +const YJS_PORT = 4438 + +async function main() { + console.log('Starting Durable Streams server...') + const dsServer = new DurableStreamTestServer({ + port: DS_PORT, + host: '127.0.0.1', + }) + await dsServer.start() + console.log(`Durable Streams server running at http://127.0.0.1:${DS_PORT}`) + + console.log('Starting Y.js server...') + const yjsServer = new YjsServer({ + port: YJS_PORT, + host: '127.0.0.1', + dsServerUrl: `http://127.0.0.1:${DS_PORT}`, + compactionThreshold: 1024 * 1024, + }) + const yjsUrl = await yjsServer.start() + console.log(`Y.js server running at ${yjsUrl}`) + + console.log('\nServers ready. Press Ctrl+C to stop.') + + process.on('SIGINT', async () => { + console.log('\nShutting down...') + process.exit(0) + }) +} + +main().catch((err) => { + console.error('Failed to start servers:', err) + process.exit(1) +}) diff --git a/examples/yjs-rich-markdown-demo/src/components/ArtifactEditor.tsx b/examples/yjs-rich-markdown-demo/src/components/ArtifactEditor.tsx new file mode 100644 index 0000000..e5b43ad --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/components/ArtifactEditor.tsx @@ -0,0 +1,69 @@ +import { artifactCollaborationExtension } from '@humanlayer/yjs-rich-markdown' +import CollaborationCursor from '@tiptap/extension-collaboration-cursor' +import { EditorContent, useEditor } from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' +import { useEffect } from 'react' +import { useArtifactSession } from '../providers/ArtifactProvider' + +type ArtifactEditorProps = { + path: string + mode?: 'fragment' | 'field' +} + +export function ArtifactEditor({ path, mode = 'fragment' }: ArtifactEditorProps) { + const { doc, store, awareness, provider, localUser } = useArtifactSession() + + store.ensureArtifact(path) + + const editor = useEditor( + { + extensions: [ + StarterKit.configure({ + history: false, + }), + artifactCollaborationExtension({ doc, path, mode }), + CollaborationCursor.configure({ + provider: provider as any, + user: { + name: localUser.name, + color: localUser.color, + }, + }), + ], + editorProps: { + attributes: { + class: 'tiptap', + }, + }, + }, + [path, mode], + ) + + useEffect(() => { + awareness.setLocalStateField('presence', { artifactPath: path }) + + return () => { + awareness.setLocalStateField('presence', { artifactPath: undefined }) + } + }, [path, awareness]) + + useEffect(() => { + return () => { + editor?.destroy() + } + }, [editor]) + + return ( +
+ +
+ ) +} diff --git a/examples/yjs-rich-markdown-demo/src/components/ArtifactList.tsx b/examples/yjs-rich-markdown-demo/src/components/ArtifactList.tsx new file mode 100644 index 0000000..6005af8 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/components/ArtifactList.tsx @@ -0,0 +1,52 @@ +import type { ArtifactMetadata } from '@humanlayer/yjs-rich-markdown' +import { useEffect, useState } from 'react' +import { useArtifactSession } from '../providers/ArtifactProvider' + +type ArtifactListProps = { + onSelect?: (path: string) => void + selectedPath?: string +} + +export function ArtifactList({ onSelect, selectedPath }: ArtifactListProps) { + const { store } = useArtifactSession() + const [artifacts, setArtifacts] = useState([]) + + useEffect(() => { + const update = () => { + setArtifacts(store.listArtifacts()) + } + + update() + store.registry.observe(update) + return () => store.registry.unobserve(update) + }, [store]) + + return ( +
+

Artifacts ({artifacts.length})

+ {artifacts.length === 0 ? ( +
No artifacts yet
+ ) : ( +
    + {artifacts.map((artifact) => ( +
  • onSelect?.(artifact.path)} + style={{ + padding: '6px 8px', + cursor: 'pointer', + background: selectedPath === artifact.path ? '#e3f2fd' : 'transparent', + borderRadius: 4, + marginBottom: 2, + fontSize: 14, + }} + > + {artifact.path} + {artifact.title && ({artifact.title})} +
  • + ))} +
+ )} +
+ ) +} diff --git a/examples/yjs-rich-markdown-demo/src/components/AwarenessDebugPanel.tsx b/examples/yjs-rich-markdown-demo/src/components/AwarenessDebugPanel.tsx new file mode 100644 index 0000000..f2f429f --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/components/AwarenessDebugPanel.tsx @@ -0,0 +1,82 @@ +import { useEffect, useState } from 'react' +import { type Collaborator, getCollaborators } from '../lib/collaboration' +import { useArtifactSession } from '../providers/ArtifactProvider' + +type AwarenessDebugPanelProps = { + filterByArtifact?: string +} + +export function AwarenessDebugPanel({ filterByArtifact }: AwarenessDebugPanelProps) { + const { awareness } = useArtifactSession() + const [states, setStates] = useState>(new Map()) + const [collaborators, setCollaborators] = useState([]) + + useEffect(() => { + const update = () => { + const newStates = new Map(awareness.getStates()) + setStates(newStates) + setCollaborators( + getCollaborators(newStates, awareness.clientID, { + includeSelf: true, + filterByArtifact, + }), + ) + } + + update() + awareness.on('change', update) + return () => awareness.off('change', update) + }, [awareness, filterByArtifact]) + + return ( +
+

+ Awareness Debug {filterByArtifact ? `(filtered: ${filterByArtifact})` : '(all)'} +

+
+ Local Client ID: {awareness.clientID} +
+ +
+ Collaborators ({collaborators.length}): + {collaborators.map((c) => ( +
+
+ {c.user.name} {c.isSelf ? '(you)' : ''} +
+
+ clientId: {c.clientId}, path: {c.presence.artifactPath ?? 'none'} +
+
+ ))} +
+ +
+ + Raw States ({states.size}) + +
+					{JSON.stringify(Array.from(states.entries()), null, 2)}
+				
+
+
+ ) +} diff --git a/examples/yjs-rich-markdown-demo/src/components/ConnectionStatus.tsx b/examples/yjs-rich-markdown-demo/src/components/ConnectionStatus.tsx new file mode 100644 index 0000000..fa3af8b --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/components/ConnectionStatus.tsx @@ -0,0 +1,46 @@ +import { useArtifactSession } from '../providers/ArtifactProvider' + +export function ConnectionStatus() { + const { connectionStatus, isSynced, localUser } = useArtifactSession() + + const statusColor = + connectionStatus === 'connected' ? '#4caf50' : connectionStatus === 'connecting' ? '#ff9800' : '#f44336' + + return ( +
+
+
+ {connectionStatus} +
+
synced: {isSynced ? 'yes' : 'no'}
+
+
+ {localUser.name} +
+
+ ) +} diff --git a/examples/yjs-rich-markdown-demo/src/components/MarkdownExportPanel.tsx b/examples/yjs-rich-markdown-demo/src/components/MarkdownExportPanel.tsx new file mode 100644 index 0000000..519460e --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/components/MarkdownExportPanel.tsx @@ -0,0 +1,75 @@ +import { artifactFragment } from '@humanlayer/yjs-rich-markdown' +import { useEffect, useState } from 'react' +import { useArtifactSession } from '../providers/ArtifactProvider' + +type MarkdownExportPanelProps = { + path: string +} + +export function MarkdownExportPanel({ path }: MarkdownExportPanelProps) { + const { doc } = useArtifactSession() + const [content, setContent] = useState('') + + useEffect(() => { + const fragment = artifactFragment(doc, path) + + const update = () => { + const text = fragmentToPlainText(fragment) + setContent(text) + } + + update() + fragment.observeDeep(update) + return () => fragment.unobserveDeep(update) + }, [doc, path]) + + return ( +
+

Content Export: {path}

+
+ (Currently showing plain text from Y.XmlFragment. Full markdown export requires TipTap serialization.) +
+
+				{content || '(empty)'}
+			
+
+ ) +} + +function fragmentToPlainText(fragment: any): string { + const parts: string[] = [] + + const traverse = (node: any) => { + if (!node) return + + if (typeof node.toString === 'function' && node._start !== undefined) { + parts.push(node.toString()) + } else if (node.toArray) { + for (const child of node.toArray()) { + traverse(child) + } + } + } + + traverse(fragment) + return parts.join('') +} diff --git a/examples/yjs-rich-markdown-demo/src/lib/collaboration.ts b/examples/yjs-rich-markdown-demo/src/lib/collaboration.ts new file mode 100644 index 0000000..bc03451 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/lib/collaboration.ts @@ -0,0 +1,167 @@ +export type AwarenessUser = { + id: string + name: string + color: string +} + +export type CollaboratorPresence = { + artifactPath?: string +} + +export type Collaborator = { + clientId: number + isSelf: boolean + user: AwarenessUser + presence: CollaboratorPresence +} + +const LOCAL_USER_STORAGE_KEY = 'yjs-rich-markdown-demo.local-user' + +const COLORS = [ + '#958DF1', + '#F98181', + '#FBBC88', + '#FAF594', + '#70CFF8', + '#94FADB', + '#B9F18D', + '#C3E2C2', + '#EAECCC', + '#AFC8AD', + '#EEC759', + '#9BB8CD', + '#FF90BC', + '#FFC0D9', + '#DC8686', + '#7ED7C1', +] + +function colorFromId(id: string): string { + let hash = 0 + for (let i = 0; i < id.length; i++) { + hash = (hash << 5) - hash + id.charCodeAt(i) + hash = hash & hash + } + return COLORS[Math.abs(hash) % COLORS.length] ?? COLORS[0] ?? '#958DF1' +} + +export function getOrCreateLocalUser(): AwarenessUser { + const storedUser = readStoredLocalUser() + if (storedUser) { + return storedUser + } + + const id = crypto.randomUUID() + const user: AwarenessUser = { + id, + name: `User ${id.slice(0, 4)}`, + color: colorFromId(id), + } + + try { + window.localStorage.setItem(LOCAL_USER_STORAGE_KEY, JSON.stringify(user)) + } catch {} + + return user +} + +export function updateLocalUserName(name: string): AwarenessUser { + const user = getOrCreateLocalUser() + user.name = name + + try { + window.localStorage.setItem(LOCAL_USER_STORAGE_KEY, JSON.stringify(user)) + } catch {} + + return user +} + +export function getCollaborators( + states: Map, + localClientId: number, + options: { includeSelf?: boolean; filterByArtifact?: string } = {}, +): Collaborator[] { + const collaborators: Collaborator[] = [] + + states.forEach((state, clientId) => { + const collaborator = getCollaboratorFromAwarenessState(state, clientId, localClientId) + if (!collaborator) { + return + } + + if (!options.includeSelf && collaborator.isSelf) { + return + } + + if (options.filterByArtifact && collaborator.presence.artifactPath !== options.filterByArtifact) { + return + } + + collaborators.push(collaborator) + }) + + return collaborators.sort((left, right) => { + if (left.isSelf !== right.isSelf) { + return left.isSelf ? -1 : 1 + } + return left.user.name.localeCompare(right.user.name) + }) +} + +export function getCollaboratorFromAwarenessState( + state: unknown, + clientId: number, + localClientId: number, +): Collaborator | null { + if (!isRecord(state)) { + return null + } + + const user = readUser(state.user) + if (!user) { + return null + } + + const presence = isRecord(state.presence) ? state.presence : null + + return { + clientId, + isSelf: clientId === localClientId, + user, + presence: { + artifactPath: presence && typeof presence.artifactPath === 'string' ? presence.artifactPath : undefined, + }, + } +} + +function readUser(value: unknown): AwarenessUser | null { + if (!isRecord(value)) { + return null + } + + if (typeof value.id !== 'string' || typeof value.name !== 'string' || typeof value.color !== 'string') { + return null + } + + return { + id: value.id, + name: value.name, + color: value.color, + } +} + +function readStoredLocalUser(): AwarenessUser | null { + try { + const value = window.localStorage.getItem(LOCAL_USER_STORAGE_KEY) + if (!value) { + return null + } + return readUser(JSON.parse(value)) + } catch { + return null + } +} + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value) +} diff --git a/examples/yjs-rich-markdown-demo/src/lib/provider-awareness.ts b/examples/yjs-rich-markdown-demo/src/lib/provider-awareness.ts new file mode 100644 index 0000000..e474617 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/lib/provider-awareness.ts @@ -0,0 +1,124 @@ +import type { YjsProvider } from '@durable-streams/y-durable-streams' + +const AWARENESS_HEARTBEAT_INTERVAL_MS = 15_000 +const PROVIDER_AWARENESS_PATCHED = Symbol('yjs-rich-markdown-demo.provider-awareness-patched') + +type ConnectionContext = { + controller: AbortController + id: number +} + +type RuntimeProvider = { + [PROVIDER_AWARENESS_PATCHED]?: boolean + awareness?: { + clientID: number + } + awarenessHeartbeat: ReturnType | null + headers: HeadersInit | undefined + broadcastAwareness: () => void + applyAwarenessUpdates: (bytes: Uint8Array) => void + awarenessUrl: (name?: string) => string + startAwareness: (ctx: ConnectionContext) => void + subscribeAwareness: (ctx: ConnectionContext) => Promise + connected: boolean + connecting: boolean +} + +export function patchProviderAwareness(provider: YjsProvider): void { + const runtimeProvider = provider as unknown as RuntimeProvider + if (runtimeProvider[PROVIDER_AWARENESS_PATCHED]) { + return + } + + runtimeProvider[PROVIDER_AWARENESS_PATCHED] = true + + runtimeProvider.startAwareness = function startAwareness(ctx: ConnectionContext): void { + if (!this.awareness || ctx.controller.signal.aborted) { + return + } + + this.broadcastAwareness() + + this.awarenessHeartbeat = setInterval(() => { + this.broadcastAwareness() + }, AWARENESS_HEARTBEAT_INTERVAL_MS) + + void this.subscribeAwareness(ctx) + } + + runtimeProvider.subscribeAwareness = async function subscribeAwareness(ctx: ConnectionContext): Promise { + if (!this.awareness) { + return + } + + const signal = ctx.controller.signal + let offset = '-1' + + try { + while (!signal.aborted && (this.connected || this.connecting)) { + const url = new URL(this.awarenessUrl()) + url.searchParams.set('offset', offset) + + if (offset !== '-1') { + url.searchParams.set('live', 'long-poll') + } + + const response = await fetch(url, { + method: 'GET', + headers: this.headers, + signal, + }) + + if (!response.ok) { + throw new Error(`Awareness request failed: ${response.status} ${response.statusText}`) + } + + const nextOffset = response.headers.get('stream-next-offset') + const upToDate = response.headers.has('stream-up-to-date') + const bytes = new Uint8Array(await response.arrayBuffer()) + + if (bytes.length > 0) { + this.applyAwarenessUpdates(bytes) + } + + if (nextOffset) { + offset = nextOffset + } + + if (!upToDate && offset === '-1') { + } + } + } catch (error) { + if (signal.aborted || (!this.connected && !this.connecting)) { + return + } + + console.error('Provider awareness subscription failed:', error) + await delay(1000, signal) + + if (this.connected) { + await this.subscribeAwareness(ctx) + } + } + } +} + +async function delay(ms: number, signal: AbortSignal): Promise { + if (signal.aborted) { + return + } + + await new Promise((resolve) => { + const timeout = globalThis.setTimeout(() => { + signal.removeEventListener('abort', onAbort) + resolve() + }, ms) + + const onAbort = () => { + globalThis.clearTimeout(timeout) + resolve() + } + + signal.addEventListener('abort', onAbort, { once: true }) + }) +} diff --git a/examples/yjs-rich-markdown-demo/src/main.tsx b/examples/yjs-rich-markdown-demo/src/main.tsx new file mode 100644 index 0000000..248ab6d --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/main.tsx @@ -0,0 +1,18 @@ +import { createRouter, RouterProvider } from '@tanstack/react-router' +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { routeTree } from './routeTree.gen' + +const router = createRouter({ routeTree }) + +declare module '@tanstack/react-router' { + interface Register { + router: typeof router + } +} + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/examples/yjs-rich-markdown-demo/src/providers/ArtifactProvider.tsx b/examples/yjs-rich-markdown-demo/src/providers/ArtifactProvider.tsx new file mode 100644 index 0000000..144d095 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/providers/ArtifactProvider.tsx @@ -0,0 +1,132 @@ +import { YjsProvider } from '@durable-streams/y-durable-streams' +import { RichMarkdownArtifactStore } from '@humanlayer/yjs-rich-markdown' +import { createContext, type PropsWithChildren, useContext, useEffect, useState } from 'react' +import { Awareness } from 'y-protocols/awareness.js' +import * as Y from 'yjs' +import { type AwarenessUser, getOrCreateLocalUser } from '../lib/collaboration' +import { patchProviderAwareness } from '../lib/provider-awareness' + +export type ArtifactSession = { + doc: Y.Doc + store: RichMarkdownArtifactStore + awareness: Awareness + provider: YjsProvider + localUser: AwarenessUser + connectionStatus: 'connecting' | 'connected' | 'disconnected' + isSynced: boolean +} + +const ArtifactSessionContext = createContext(null) + +export function useArtifactSession(): ArtifactSession { + const session = useContext(ArtifactSessionContext) + if (!session) { + throw new Error('useArtifactSession must be used within ArtifactProvider') + } + return session +} + +export function ArtifactProvider({ children }: PropsWithChildren) { + const [session, setSession] = useState(null) + const [error, setError] = useState(null) + + useEffect(() => { + let destroyed = false + let provider: YjsProvider | null = null + + async function init() { + const doc = new Y.Doc() + const awareness = new Awareness(doc) + const store = new RichMarkdownArtifactStore(doc) + const localUser = getOrCreateLocalUser() + + awareness.setLocalStateField('user', localUser) + awareness.setLocalStateField('presence', { artifactPath: undefined }) + + const docId = new URLSearchParams(window.location.search).get('doc') ?? 'rich-markdown-learning' + const baseUrl = `${window.location.origin}/v1/yjs/rich-markdown-demo` + + provider = new YjsProvider({ + doc, + awareness, + baseUrl, + docId, + connect: false, + liveMode: 'long-poll', + }) + + patchProviderAwareness(provider) + + const updateStatus = () => { + if (destroyed) return + setSession((prev) => + prev + ? { + ...prev, + connectionStatus: provider!.connected + ? 'connected' + : provider!.connecting + ? 'connecting' + : 'disconnected', + isSynced: provider!.synced, + } + : prev, + ) + } + + provider.on('status', updateStatus) + provider.on('synced', updateStatus) + + await provider.connect() + + if (destroyed) { + provider.destroy() + return + } + + setSession({ + doc, + store, + awareness, + provider, + localUser, + connectionStatus: provider.connected ? 'connected' : 'connecting', + isSynced: provider.synced, + }) + } + + init().catch((err) => { + if (!destroyed) { + setError(err) + } + }) + + return () => { + destroyed = true + if (provider) { + provider.flush().finally(() => provider?.destroy()) + } + } + }, []) + + if (error) { + return ( +
+

Connection Error

+
{error.message}
+
+ ) + } + + if (!session) { + return ( +
+
Connecting to Durable Streams...
+
+ ) + } + + return {children} +} diff --git a/examples/yjs-rich-markdown-demo/src/routeTree.gen.ts b/examples/yjs-rich-markdown-demo/src/routeTree.gen.ts new file mode 100644 index 0000000..afa5470 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/routeTree.gen.ts @@ -0,0 +1,131 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as SingleRouteImport } from './routes/single' +import { Route as SameDocRouteImport } from './routes/same-doc' +import { Route as CompareRouteImport } from './routes/compare' +import { Route as AwarenessRouteImport } from './routes/awareness' +import { Route as IndexRouteImport } from './routes/index' + +const SingleRoute = SingleRouteImport.update({ + id: '/single', + path: '/single', + getParentRoute: () => rootRouteImport, +} as any) +const SameDocRoute = SameDocRouteImport.update({ + id: '/same-doc', + path: '/same-doc', + getParentRoute: () => rootRouteImport, +} as any) +const CompareRoute = CompareRouteImport.update({ + id: '/compare', + path: '/compare', + getParentRoute: () => rootRouteImport, +} as any) +const AwarenessRoute = AwarenessRouteImport.update({ + id: '/awareness', + path: '/awareness', + getParentRoute: () => rootRouteImport, +} as any) +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/awareness': typeof AwarenessRoute + '/compare': typeof CompareRoute + '/same-doc': typeof SameDocRoute + '/single': typeof SingleRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/awareness': typeof AwarenessRoute + '/compare': typeof CompareRoute + '/same-doc': typeof SameDocRoute + '/single': typeof SingleRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/awareness': typeof AwarenessRoute + '/compare': typeof CompareRoute + '/same-doc': typeof SameDocRoute + '/single': typeof SingleRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/awareness' | '/compare' | '/same-doc' | '/single' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/awareness' | '/compare' | '/same-doc' | '/single' + id: '__root__' | '/' | '/awareness' | '/compare' | '/same-doc' | '/single' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + AwarenessRoute: typeof AwarenessRoute + CompareRoute: typeof CompareRoute + SameDocRoute: typeof SameDocRoute + SingleRoute: typeof SingleRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/single': { + id: '/single' + path: '/single' + fullPath: '/single' + preLoaderRoute: typeof SingleRouteImport + parentRoute: typeof rootRouteImport + } + '/same-doc': { + id: '/same-doc' + path: '/same-doc' + fullPath: '/same-doc' + preLoaderRoute: typeof SameDocRouteImport + parentRoute: typeof rootRouteImport + } + '/compare': { + id: '/compare' + path: '/compare' + fullPath: '/compare' + preLoaderRoute: typeof CompareRouteImport + parentRoute: typeof rootRouteImport + } + '/awareness': { + id: '/awareness' + path: '/awareness' + fullPath: '/awareness' + preLoaderRoute: typeof AwarenessRouteImport + parentRoute: typeof rootRouteImport + } + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + AwarenessRoute: AwarenessRoute, + CompareRoute: CompareRoute, + SameDocRoute: SameDocRoute, + SingleRoute: SingleRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/examples/yjs-rich-markdown-demo/src/routes/__root.tsx b/examples/yjs-rich-markdown-demo/src/routes/__root.tsx new file mode 100644 index 0000000..45ed9a4 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/routes/__root.tsx @@ -0,0 +1,57 @@ +import { createRootRoute, Link, Outlet } from '@tanstack/react-router' +import { ConnectionStatus } from '../components/ConnectionStatus' +import { ArtifactProvider } from '../providers/ArtifactProvider' + +export const Route = createRootRoute({ + component: RootLayout, +}) + +function RootLayout() { + return ( + +
+ + +
+ +
+
+
+ ) +} diff --git a/examples/yjs-rich-markdown-demo/src/routes/awareness.tsx b/examples/yjs-rich-markdown-demo/src/routes/awareness.tsx new file mode 100644 index 0000000..c89cf4d --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/routes/awareness.tsx @@ -0,0 +1,116 @@ +import { createFileRoute } from '@tanstack/react-router' +import { useEffect, useState } from 'react' +import { AwarenessDebugPanel } from '../components/AwarenessDebugPanel' +import { useArtifactSession } from '../providers/ArtifactProvider' + +export const Route = createFileRoute('/awareness')({ + component: AwarenessRoute, +}) + +function AwarenessRoute() { + const { awareness, localUser } = useArtifactSession() + const [simulatedPath, setSimulatedPath] = useState('') + const [rawState, setRawState] = useState('') + + useEffect(() => { + const update = () => { + setRawState(JSON.stringify(awareness.getLocalState(), null, 2)) + } + update() + awareness.on('change', update) + return () => awareness.off('change', update) + }, [awareness]) + + const setPresencePath = (path: string) => { + setSimulatedPath(path) + awareness.setLocalStateField('presence', { artifactPath: path || undefined }) + } + + return ( +
+

Awareness Debug View

+ +
+

Local User

+
+
+ + {localUser.name} ({localUser.id.slice(0, 8)}) + +
+
+ +
+

Simulate Presence

+

+ Set your artifact path to simulate being in an editor. Open multiple tabs to test awareness + visibility. +

+
+ setPresencePath(e.target.value)} + placeholder="e.g. /artifacts/plan.md" + style={{ padding: '8px 12px', width: 300, border: '1px solid #ccc', borderRadius: 4 }} + /> + + + +
+
+ +
+

Local Awareness State

+
+					{rawState}
+				
+
+ +
+

All Clients

+ +
+ +
+

Filtered: /artifacts/plan.md

+ +
+ +
+

Filtered: /artifacts/research.md

+ +
+
+ ) +} diff --git a/examples/yjs-rich-markdown-demo/src/routes/compare.tsx b/examples/yjs-rich-markdown-demo/src/routes/compare.tsx new file mode 100644 index 0000000..92416a8 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/routes/compare.tsx @@ -0,0 +1,77 @@ +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { ArtifactEditor } from '../components/ArtifactEditor' +import { AwarenessDebugPanel } from '../components/AwarenessDebugPanel' +import { MarkdownExportPanel } from '../components/MarkdownExportPanel' +import { useArtifactSession } from '../providers/ArtifactProvider' + +type CompareSearch = { + left: string + right: string +} + +export const Route = createFileRoute('/compare')({ + component: CompareRoute, + validateSearch: (search: Record): CompareSearch => ({ + left: typeof search.left === 'string' ? search.left : '/artifacts/plan.md', + right: typeof search.right === 'string' ? search.right : '/artifacts/research.md', + }), +}) + +function CompareRoute() { + const { left, right } = Route.useSearch() + const navigate = useNavigate({ from: '/compare' }) + const { store } = useArtifactSession() + + store.ensureArtifact(left) + store.ensureArtifact(right) + + return ( +
+

Compare: Two Different Artifacts

+

+ Open this page in two browser tabs. Edit different artifacts and verify cursors do NOT leak between + them. +

+ +
+ + +
+ +
+
+

{left}

+
+ +
+ +
+
+

{right}

+
+ +
+ +
+
+ + +
+ ) +} diff --git a/examples/yjs-rich-markdown-demo/src/routes/index.tsx b/examples/yjs-rich-markdown-demo/src/routes/index.tsx new file mode 100644 index 0000000..733dae1 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/routes/index.tsx @@ -0,0 +1,103 @@ +import { createFileRoute, Link } from '@tanstack/react-router' +import { useState } from 'react' +import { ArtifactList } from '../components/ArtifactList' +import { useArtifactSession } from '../providers/ArtifactProvider' + +export const Route = createFileRoute('/')({ + component: IndexRoute, +}) + +function IndexRoute() { + const { store, connectionStatus, isSynced } = useArtifactSession() + const [newPath, setNewPath] = useState('/artifacts/new.md') + + const createArtifact = () => { + try { + store.ensureArtifact(newPath) + } catch (err) { + alert(String(err)) + } + } + + return ( +
+

Rich Markdown Learning Demo

+ +
+

Connection Status

+
    +
  • Status: {connectionStatus}
  • +
  • Synced: {isSynced ? 'Yes' : 'No'}
  • +
+
+ +
+

Create Artifact

+
+ setNewPath(e.target.value)} + style={{ padding: '8px 12px', border: '1px solid #ccc', borderRadius: 4, width: 300 }} + /> + +
+
+ +
+

Existing Artifacts

+ +
+ +
+

Learning Scenarios

+
    +
  • + + Single Editor + {' '} + - Edit one artifact +
  • +
  • + + Compare + {' '} + - Two editors, two different artifact paths +
  • +
  • + + Same Doc + {' '} + - Two editors, same artifact path +
  • +
  • + Awareness Debug - Raw awareness state inspection +
  • +
+
+ +
+

Learning Goals

+
    +
  1. Multiple TipTap fragments in one Y.Doc keep content isolated
  2. +
  3. Durable Streams syncs all fragments in one document
  4. +
  5. Users editing the same artifact see each other's cursors
  6. +
  7. Users editing different artifacts do NOT see each other's cursors
  8. +
  9. Markdown export works per artifact
  10. +
+
+
+ ) +} diff --git a/examples/yjs-rich-markdown-demo/src/routes/same-doc.tsx b/examples/yjs-rich-markdown-demo/src/routes/same-doc.tsx new file mode 100644 index 0000000..93dd7d9 --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/routes/same-doc.tsx @@ -0,0 +1,66 @@ +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { ArtifactEditor } from '../components/ArtifactEditor' +import { AwarenessDebugPanel } from '../components/AwarenessDebugPanel' +import { MarkdownExportPanel } from '../components/MarkdownExportPanel' +import { useArtifactSession } from '../providers/ArtifactProvider' + +type SameDocSearch = { + path: string +} + +export const Route = createFileRoute('/same-doc')({ + component: SameDocRoute, + validateSearch: (search: Record): SameDocSearch => ({ + path: typeof search.path === 'string' ? search.path : '/artifacts/plan.md', + }), +}) + +function SameDocRoute() { + const { path } = Route.useSearch() + const navigate = useNavigate({ from: '/same-doc' }) + const { store } = useArtifactSession() + + store.ensureArtifact(path) + + return ( +
+

Same Doc: Two Editors, One Artifact

+

+ Both editors show the same artifact. Open in two browser tabs to verify cursors are visible across + clients. +

+ +
+ +
+ +
+
+

Editor A: {path}

+
+ +
+
+
+

Editor B: {path}

+
+ +
+
+
+ +
+ + +
+
+ ) +} diff --git a/examples/yjs-rich-markdown-demo/src/routes/single.tsx b/examples/yjs-rich-markdown-demo/src/routes/single.tsx new file mode 100644 index 0000000..41e003f --- /dev/null +++ b/examples/yjs-rich-markdown-demo/src/routes/single.tsx @@ -0,0 +1,66 @@ +import { createFileRoute, useNavigate } from '@tanstack/react-router' +import { ArtifactEditor } from '../components/ArtifactEditor' +import { AwarenessDebugPanel } from '../components/AwarenessDebugPanel' +import { MarkdownExportPanel } from '../components/MarkdownExportPanel' +import { useArtifactSession } from '../providers/ArtifactProvider' + +type SingleSearch = { + path: string + mode: 'fragment' | 'field' +} + +export const Route = createFileRoute('/single')({ + component: SingleRoute, + validateSearch: (search: Record): SingleSearch => ({ + path: typeof search.path === 'string' ? search.path : '/artifacts/plan.md', + mode: search.mode === 'field' ? 'field' : 'fragment', + }), +}) + +function SingleRoute() { + const { path, mode } = Route.useSearch() + const navigate = useNavigate({ from: '/single' }) + const { store } = useArtifactSession() + + store.ensureArtifact(path) + + return ( +
+
+ + +
+ +
+
+

Editor: {path}

+ +
+
+ + +
+
+
+ ) +} diff --git a/examples/yjs-rich-markdown-demo/tsconfig.json b/examples/yjs-rich-markdown-demo/tsconfig.json new file mode 100644 index 0000000..3c6105c --- /dev/null +++ b/examples/yjs-rich-markdown-demo/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "customConditions": ["source"], + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + "paths": { + "@/*": ["./src/*"] + }, + "types": ["vite/client"] + }, + "include": ["src"] +} diff --git a/examples/yjs-rich-markdown-demo/vite.config.ts b/examples/yjs-rich-markdown-demo/vite.config.ts new file mode 100644 index 0000000..57c1a8e --- /dev/null +++ b/examples/yjs-rich-markdown-demo/vite.config.ts @@ -0,0 +1,20 @@ +import { TanStackRouterVite } from '@tanstack/router-plugin/vite' +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [TanStackRouterVite(), react()], + resolve: { + conditions: ['source'], + }, + server: { + port: 5176, + strictPort: true, + proxy: { + '/v1/yjs': { + target: 'http://127.0.0.1:4438', + changeOrigin: true, + }, + }, + }, +}) diff --git a/package.json b/package.json index 1ed744b..02b4bec 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ }, "workspaces": [ "packages/*", - "agents/*" + "agents/*", + "examples/*" ], "catalog": { "@ai-sdk/anthropic": "3.0.69", diff --git a/packages/yjs-rich-markdown/build.ts b/packages/yjs-rich-markdown/build.ts new file mode 100644 index 0000000..c7c718f --- /dev/null +++ b/packages/yjs-rich-markdown/build.ts @@ -0,0 +1,3 @@ +import { buildPackageFromManifest } from '../../scripts/build/package-build.ts' + +await buildPackageFromManifest(import.meta.dir) diff --git a/packages/yjs-rich-markdown/package.json b/packages/yjs-rich-markdown/package.json new file mode 100644 index 0000000..e4a7d8e --- /dev/null +++ b/packages/yjs-rich-markdown/package.json @@ -0,0 +1,50 @@ +{ + "name": "@humanlayer/yjs-rich-markdown", + "source": "./src/index.ts", + "type": "module", + "version": "0.0.0", + "private": false, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "publishConfig": { + "access": "public" + }, + "files": [ + "src", + "dist" + ], + "exports": { + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js", + "bun": "./src/index.ts" + } + }, + "scripts": { + "typecheck": "tsgo --noEmit", + "build": "bun run build:js && bunx tsc -p tsconfig.build.json", + "build:js": "bun run build.ts", + "build:types": "bunx tsc -p tsconfig.build.json" + }, + "devDependencies": { + "@durable-streams/server": "^0.3.1", + "@durable-streams/y-durable-streams": "^0.2.4", + "@types/bun": "latest" + }, + "peerDependencies": { + "@tiptap/core": "^2.11.0", + "@tiptap/extension-collaboration": "^2.11.0", + "@tiptap/starter-kit": "^2.11.0", + "typescript": "^5", + "yjs": "13.6.30", + "y-protocols": "1.0.7" + }, + "dependencies": {}, + "author": { + "name": "Kyle Mistele", + "url": "https://x.com/0xblacklight" + } +} diff --git a/packages/yjs-rich-markdown/src/artifact-path.ts b/packages/yjs-rich-markdown/src/artifact-path.ts new file mode 100644 index 0000000..39c3436 --- /dev/null +++ b/packages/yjs-rich-markdown/src/artifact-path.ts @@ -0,0 +1,38 @@ +export type ArtifactPath = string + +export function normalizeArtifactPath(path: string): ArtifactPath { + const trimmed = path.trim() + if (!trimmed) { + throw new InvalidArtifactPathError(path, 'Artifact path must not be empty') + } + + if (!trimmed.startsWith('/')) { + throw new InvalidArtifactPathError(path, 'Artifact path must be absolute') + } + + const normalized = trimmed.replace(/\/+/g, '/') + if ( + normalized.includes('/../') || + normalized.endsWith('/..') || + normalized.includes('/./') || + normalized.endsWith('/.') + ) { + throw new InvalidArtifactPathError(path, 'Artifact path must not contain relative segments') + } + + return normalized +} + +export function artifactFragmentName(path: ArtifactPath): string { + return `artifact:${normalizeArtifactPath(path)}` +} + +export class InvalidArtifactPathError extends Error { + constructor( + readonly path: string, + message: string, + ) { + super(message) + this.name = 'InvalidArtifactPathError' + } +} diff --git a/packages/yjs-rich-markdown/src/artifact-store.ts b/packages/yjs-rich-markdown/src/artifact-store.ts new file mode 100644 index 0000000..cd813ca --- /dev/null +++ b/packages/yjs-rich-markdown/src/artifact-store.ts @@ -0,0 +1,86 @@ +import * as Y from 'yjs' +import { type ArtifactPath, artifactFragmentName, normalizeArtifactPath } from './artifact-path' + +export type ArtifactMetadata = { + path: ArtifactPath + title?: string + createdAt: number + modifiedAt: number +} + +export type CreateArtifactOptions = { + title?: string + now?: number +} + +const REGISTRY_NAME = 'artifacts' + +export class RichMarkdownArtifactStore { + readonly doc: Y.Doc + readonly registry: Y.Map + + constructor(doc = new Y.Doc()) { + this.doc = doc + this.registry = doc.getMap(REGISTRY_NAME) + } + + createArtifact(path: string, options: CreateArtifactOptions = {}): ArtifactMetadata { + const normalizedPath = normalizeArtifactPath(path) + const existing = this.registry.get(normalizedPath) + if (existing) { + throw new ArtifactAlreadyExistsError(normalizedPath) + } + + const now = options.now ?? Date.now() + const metadata: ArtifactMetadata = { + path: normalizedPath, + title: options.title, + createdAt: now, + modifiedAt: now, + } + + this.doc.transact(() => { + this.registry.set(normalizedPath, metadata) + this.doc.getXmlFragment(artifactFragmentName(normalizedPath)) + }) + + return metadata + } + + ensureArtifact(path: string, options: CreateArtifactOptions = {}): ArtifactMetadata { + const normalizedPath = normalizeArtifactPath(path) + return this.registry.get(normalizedPath) ?? this.createArtifact(normalizedPath, options) + } + + getArtifact(path: string): ArtifactMetadata { + const normalizedPath = normalizeArtifactPath(path) + const metadata = this.registry.get(normalizedPath) + if (!metadata) { + throw new ArtifactNotFoundError(normalizedPath) + } + return metadata + } + + listArtifacts(): ArtifactMetadata[] { + return Array.from(this.registry.values()).sort((a, b) => a.path.localeCompare(b.path)) + } + + getFragment(path: string): Y.XmlFragment { + const normalizedPath = normalizeArtifactPath(path) + return this.doc.getXmlFragment(artifactFragmentName(normalizedPath)) + } +} + +export class ArtifactAlreadyExistsError extends Error { + constructor(readonly path: ArtifactPath) { + super(`Artifact already exists: ${path}`) + this.name = 'ArtifactAlreadyExistsError' + } +} + +export class ArtifactNotFoundError extends Error { + constructor(readonly path: ArtifactPath) { + super(`Artifact not found: ${path}`) + this.name = 'ArtifactNotFoundError' + } +} diff --git a/packages/yjs-rich-markdown/src/index.ts b/packages/yjs-rich-markdown/src/index.ts new file mode 100644 index 0000000..2d28f05 --- /dev/null +++ b/packages/yjs-rich-markdown/src/index.ts @@ -0,0 +1,6 @@ +export type { ArtifactPath } from './artifact-path' +export { artifactFragmentName, InvalidArtifactPathError, normalizeArtifactPath } from './artifact-path' +export type { ArtifactMetadata, CreateArtifactOptions } from './artifact-store' +export { ArtifactAlreadyExistsError, ArtifactNotFoundError, RichMarkdownArtifactStore } from './artifact-store' +export type { ArtifactCollaborationMode, ArtifactCollaborationOptions } from './tiptap-bindings' +export { artifactCollaborationExtension, artifactFragment, defaultRichMarkdownExtensions } from './tiptap-bindings' diff --git a/packages/yjs-rich-markdown/src/tiptap-bindings.ts b/packages/yjs-rich-markdown/src/tiptap-bindings.ts new file mode 100644 index 0000000..df2920f --- /dev/null +++ b/packages/yjs-rich-markdown/src/tiptap-bindings.ts @@ -0,0 +1,40 @@ +import type { Extension } from '@tiptap/core' +import Collaboration from '@tiptap/extension-collaboration' +import StarterKit from '@tiptap/starter-kit' +import type * as Y from 'yjs' +import { type ArtifactPath, artifactFragmentName, normalizeArtifactPath } from './artifact-path' + +export type ArtifactCollaborationMode = 'fragment' | 'field' + +export type ArtifactCollaborationOptions = { + doc: Y.Doc + path: ArtifactPath + mode?: ArtifactCollaborationMode +} + +export function artifactFragment(doc: Y.Doc, path: string): Y.XmlFragment { + return doc.getXmlFragment(artifactFragmentName(normalizeArtifactPath(path))) +} + +export function artifactCollaborationExtension(options: ArtifactCollaborationOptions): Extension { + const path = normalizeArtifactPath(options.path) + if (options.mode === 'field') { + return Collaboration.configure({ + document: options.doc, + field: artifactFragmentName(path), + }) + } + + return Collaboration.configure({ + fragment: artifactFragment(options.doc, path), + }) +} + +export function defaultRichMarkdownExtensions(options: ArtifactCollaborationOptions): Extension[] { + return [ + StarterKit.configure({ + history: false, + }), + artifactCollaborationExtension(options), + ] +} diff --git a/packages/yjs-rich-markdown/test/fragment-isolation.test.ts b/packages/yjs-rich-markdown/test/fragment-isolation.test.ts new file mode 100644 index 0000000..8f7b12e --- /dev/null +++ b/packages/yjs-rich-markdown/test/fragment-isolation.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, test } from 'bun:test' +import * as Y from 'yjs' +import { artifactFragment, artifactFragmentName, RichMarkdownArtifactStore } from '../src' + +function syncDoc(source: Y.Doc, target: Y.Doc): void { + Y.applyUpdate(target, Y.encodeStateAsUpdate(source)) +} + +describe('rich markdown artifact fragments', () => { + test('stores multiple artifact fragments in one Y.Doc without cross-contamination', () => { + const doc1 = new Y.Doc() + const doc2 = new Y.Doc() + const store1 = new RichMarkdownArtifactStore(doc1) + const store2 = new RichMarkdownArtifactStore(doc2) + + store1.createArtifact('/artifacts/a.md', { now: 1, title: 'A' }) + store1.createArtifact('/artifacts/b.md', { now: 2, title: 'B' }) + + artifactFragment(doc1, '/artifacts/a.md').insert(0, [new Y.XmlText('Artifact A')]) + artifactFragment(doc1, '/artifacts/b.md').insert(0, [new Y.XmlText('Artifact B')]) + + syncDoc(doc1, doc2) + + expect(store2.listArtifacts().map((artifact) => artifact.path)).toEqual(['/artifacts/a.md', '/artifacts/b.md']) + expect(artifactFragment(doc2, '/artifacts/a.md').toString()).toBe('Artifact A') + expect(artifactFragment(doc2, '/artifacts/b.md').toString()).toBe('Artifact B') + expect(doc2.getXmlFragment(artifactFragmentName('/artifacts/a.md'))).not.toBe( + doc2.getXmlFragment(artifactFragmentName('/artifacts/b.md')), + ) + }) +}) diff --git a/packages/yjs-rich-markdown/tsconfig.build.json b/packages/yjs-rich-markdown/tsconfig.build.json new file mode 100644 index 0000000..e043bc7 --- /dev/null +++ b/packages/yjs-rich-markdown/tsconfig.build.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src"], + "exclude": [ + "test", + "dist", + "node_modules", + "**/*.test.*", + "**/*.bench.*", + "**/*.test-d.ts", + "**/__tests__/**", + "**/__mocks__/**", + "**/__snapshots__/**" + ] +} diff --git a/packages/yjs-rich-markdown/tsconfig.json b/packages/yjs-rich-markdown/tsconfig.json new file mode 100644 index 0000000..7cb9983 --- /dev/null +++ b/packages/yjs-rich-markdown/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + "types": ["bun"], + "moduleResolution": "bundler", + "customConditions": ["source"], + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} From 8e9ca780aa922f0babed92dc0287ceaf10e7861b Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 2 May 2026 14:46:58 -0700 Subject: [PATCH 02/18] feat: add markdown bridge for rich artifacts --- bun.lock | 244 +++++++++++++++++- examples/yjs-rich-markdown-demo/package.json | 10 +- .../src/components/ArtifactEditor.tsx | 6 +- .../src/components/MarkdownExportPanel.tsx | 44 ++-- packages/yjs-rich-markdown/package.json | 19 +- .../yjs-rich-markdown/src/artifact-read.ts | 6 + packages/yjs-rich-markdown/src/index.ts | 2 + packages/yjs-rich-markdown/src/markdown.ts | 37 +++ .../yjs-rich-markdown/src/tiptap-bindings.ts | 2 +- .../test/artifact-read.test.ts | 57 ++++ .../yjs-rich-markdown/test/markdown.test.ts | 88 +++++++ 11 files changed, 463 insertions(+), 52 deletions(-) create mode 100644 packages/yjs-rich-markdown/src/artifact-read.ts create mode 100644 packages/yjs-rich-markdown/src/markdown.ts create mode 100644 packages/yjs-rich-markdown/test/artifact-read.test.ts create mode 100644 packages/yjs-rich-markdown/test/markdown.test.ts diff --git a/bun.lock b/bun.lock index 076e4f1..ccda3ca 100644 --- a/bun.lock +++ b/bun.lock @@ -85,11 +85,11 @@ "@durable-streams/y-durable-streams": "^0.2.4", "@humanlayer/yjs-rich-markdown": "workspace:*", "@tanstack/react-router": "^1.93.0", - "@tiptap/extension-collaboration": "^2.11.0", - "@tiptap/extension-collaboration-cursor": "^2.11.0", - "@tiptap/pm": "^2.11.0", - "@tiptap/react": "^2.11.0", - "@tiptap/starter-kit": "^2.11.0", + "@tiptap/extension-collaboration": "3.22.5", + "@tiptap/extension-collaboration-caret": "3.22.5", + "@tiptap/pm": "3.22.5", + "@tiptap/react": "3.22.5", + "@tiptap/starter-kit": "3.22.5", "react": "^18.3.1", "react-dom": "^18.3.1", "y-protocols": "1.0.7", @@ -305,15 +305,23 @@ "packages/yjs-rich-markdown": { "name": "@humanlayer/yjs-rich-markdown", "version": "0.0.0", + "dependencies": { + "@tiptap/core": "3.22.5", + "@tiptap/extension-collaboration": "3.22.5", + "@tiptap/markdown": "3.22.5", + "@tiptap/pm": "3.22.5", + "@tiptap/starter-kit": "3.22.5", + "y-prosemirror": "1.3.7", + }, "devDependencies": { "@durable-streams/server": "^0.3.1", "@durable-streams/y-durable-streams": "^0.2.4", "@types/bun": "latest", }, "peerDependencies": { - "@tiptap/core": "^2.11.0", - "@tiptap/extension-collaboration": "^2.11.0", - "@tiptap/starter-kit": "^2.11.0", + "@tiptap/core": "3.22.5", + "@tiptap/extension-collaboration": "3.22.5", + "@tiptap/starter-kit": "3.22.5", "typescript": "^5", "y-protocols": "1.0.7", "yjs": "13.6.30", @@ -522,6 +530,12 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + "@gar/promise-retry": ["@gar/promise-retry@1.0.3", "", {}, "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA=="], "@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="], @@ -840,7 +854,7 @@ "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.161.7", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ=="], - "@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + "@tiptap/core": ["@tiptap/core@3.22.5", "", { "peerDependencies": { "@tiptap/pm": "3.22.5" } }, "sha512-L1lhWz6ujGny8LduTJ7MBWYhzigwOvfUJUrJ7IzOJSuy3+OAzisdGDD1GV7LEO/hU0Hr2Mkm1wajRIHExvS9HQ=="], "@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@2.27.2", "", { "peerDependencies": { "@tiptap/core": "^2.7.0" } }, "sha512-oIGZgiAeA4tG3YxbTDfrmENL4/CIwGuP3THtHsNhwRqwsl9SfMk58Ucopi2GXTQSdYXpRJ0ahE6nPqB5D6j/Zw=="], @@ -856,6 +870,8 @@ "@tiptap/extension-collaboration": ["@tiptap/extension-collaboration@2.27.2", "", { "peerDependencies": { "@tiptap/core": "^2.7.0", "@tiptap/pm": "^2.7.0", "y-prosemirror": "^1.2.11" } }, "sha512-Y61ItHxQ1uc/Ir27mBQRI/wY9JkOui194V+awNv+1YHeaKArTjC2cdSvNzj9+h8JIh5MyfvslSf8hBa7t7PzAg=="], + "@tiptap/extension-collaboration-caret": ["@tiptap/extension-collaboration-caret@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5", "@tiptap/y-tiptap": "^3.0.2" } }, "sha512-+2kOIb661vW16nh5ecSNw4vcRFdove1thkNkKJGGcwaTyORJ0Hc4Pds+tSc3dc9GxZw0bvwwAvX7bPdflnls6A=="], + "@tiptap/extension-collaboration-cursor": ["@tiptap/extension-collaboration-cursor@2.26.2", "", { "peerDependencies": { "@tiptap/core": "^2.7.0", "y-prosemirror": "^1.2.11" } }, "sha512-FdRb27mZ5Kr18hN6cbfBj1e9F0DOoHB1Gv3IYeic+g4h1C9BjDVMN0+JRBQc+4lamNA8TsHO0oKWRwaPe4sSlA=="], "@tiptap/extension-document": ["@tiptap/extension-document@2.27.2", "", { "peerDependencies": { "@tiptap/core": "^2.7.0" } }, "sha512-CFhAYsPnyYnosDC4639sCJnBUnYH4Cat9qH5NZWHVvdgtDwu8GZgZn2eSzaKSYXWH1vJ9DSlCK+7UyC3SNXIBA=="], @@ -876,8 +892,14 @@ "@tiptap/extension-italic": ["@tiptap/extension-italic@2.27.2", "", { "peerDependencies": { "@tiptap/core": "^2.7.0" } }, "sha512-1OFsw2SZqfaqx5Fa5v90iNlPRcqyt+lVSjBwTDzuPxTPFY4Q0mL89mKgkq2gVHYNCiaRkXvFLDxaSvBWbmthgg=="], + "@tiptap/extension-link": ["@tiptap/extension-link@3.22.5", "", { "dependencies": { "linkifyjs": "^4.3.2" }, "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-d671MvF3GPKoS2OVxjIlQ7hIE7MS3hREdR+d4cvnnoiLLD+ZJ6KgDnxmWqF0a1s4qxLWK2KxKRSOIfYGE31QWQ=="], + + "@tiptap/extension-list": ["@tiptap/extension-list@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-cVO3ZHCgxAWZ4zrFSs81FO2nyCk1wb2EHkpLpW98FzbJLkN9rDkazhW99P3HRWy/CvUldOT+8ecI1YrQtBojMg=="], + "@tiptap/extension-list-item": ["@tiptap/extension-list-item@2.27.2", "", { "peerDependencies": { "@tiptap/core": "^2.7.0" } }, "sha512-eJNee7IEGXMnmygM5SdMGDC8m/lMWmwNGf9fPCK6xk0NxuQRgmZHL6uApKcdH6gyNcRPHCqvTTkhEP7pbny/fg=="], + "@tiptap/extension-list-keymap": ["@tiptap/extension-list-keymap@3.22.5", "", { "peerDependencies": { "@tiptap/extension-list": "3.22.5" } }, "sha512-cGUnxJ0y515e1bVHNjUmbx7oWHoEon59w6BA5N2KwV9iW2mZZchlTX4yxJSOX+ixeVRChsa7YwC3Z1jUZ6AMEg=="], + "@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@2.27.2", "", { "peerDependencies": { "@tiptap/core": "^2.7.0" } }, "sha512-M7A4tLGJcLPYdLC4CI2Gwl8LOrENQW59u3cMVa+KkwG1hzSJyPsbDpa1DI6oXPC2WtYiTf22zrbq3gVvH+KA2w=="], "@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@2.27.2", "", { "peerDependencies": { "@tiptap/core": "^2.7.0" } }, "sha512-elYVn2wHJJ+zB9LESENWOAfI4TNT0jqEN34sMA/hCtA4im1ZG2DdLHwkHIshj/c4H0dzQhmsS/YmNC5Vbqab/A=="], @@ -888,12 +910,20 @@ "@tiptap/extension-text-style": ["@tiptap/extension-text-style@2.27.2", "", { "peerDependencies": { "@tiptap/core": "^2.7.0" } }, "sha512-Omk+uxjJLyEY69KStpCw5fA9asvV+MGcAX2HOxyISDFoLaL49TMrNjhGAuz09P1L1b0KGXo4ml7Q3v/Lfy4WPA=="], - "@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + "@tiptap/extension-underline": ["@tiptap/extension-underline@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-9ut09rJD0iEbS6sk7yd2j6IwuFDLTNmDEGTDLodvqAfi+bq7ddsTDv0YviXoZaA9sdHAdTEVr2ITy2m6WK5jpA=="], + + "@tiptap/extensions": ["@tiptap/extensions@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-Ifg4MzKCj3uRqe3ieTwYnomu2y4p7EXr2avVSKZYfh12i2dyWe2Gkn1KuZDREANVE+gHqFlQjJRYzhJFwzSCrg=="], + + "@tiptap/markdown": ["@tiptap/markdown@3.22.5", "", { "dependencies": { "marked": "^17.0.1" }, "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-lLuAySaY5EYNYLe7e4507B9yQMAEDJdOKy0g85UNFV8giorYLQx56aV2O94Qb9gv3egs5inkwVRNfeJzOWAwig=="], + + "@tiptap/pm": ["@tiptap/pm@3.22.5", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-keymap": "^1.2.2", "prosemirror-model": "^1.24.1", "prosemirror-schema-list": "^1.5.0", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.38.1" } }, "sha512-Cr9Mv4igxvI2tKMiahw48sZxva3PfDzypErH8IB82N+9qa9n9ygVMt0BOaDg53hLKxEEVeYr2S/wCcJIVFgBTw=="], "@tiptap/react": ["@tiptap/react@2.27.2", "", { "dependencies": { "@tiptap/extension-bubble-menu": "^2.27.2", "@tiptap/extension-floating-menu": "^2.27.2", "@types/use-sync-external-store": "^0.0.6", "fast-deep-equal": "^3", "use-sync-external-store": "^1" }, "peerDependencies": { "@tiptap/core": "^2.7.0", "@tiptap/pm": "^2.7.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-0EAs8Cpkfbvben1PZ34JN2Nd79Dhioynm2jML27DBbf1VWPk+FFWFGTMLUT0bu+Np5iVxio8fqV9t0mc4D6thA=="], "@tiptap/starter-kit": ["@tiptap/starter-kit@2.27.2", "", { "dependencies": { "@tiptap/core": "^2.27.2", "@tiptap/extension-blockquote": "^2.27.2", "@tiptap/extension-bold": "^2.27.2", "@tiptap/extension-bullet-list": "^2.27.2", "@tiptap/extension-code": "^2.27.2", "@tiptap/extension-code-block": "^2.27.2", "@tiptap/extension-document": "^2.27.2", "@tiptap/extension-dropcursor": "^2.27.2", "@tiptap/extension-gapcursor": "^2.27.2", "@tiptap/extension-hard-break": "^2.27.2", "@tiptap/extension-heading": "^2.27.2", "@tiptap/extension-history": "^2.27.2", "@tiptap/extension-horizontal-rule": "^2.27.2", "@tiptap/extension-italic": "^2.27.2", "@tiptap/extension-list-item": "^2.27.2", "@tiptap/extension-ordered-list": "^2.27.2", "@tiptap/extension-paragraph": "^2.27.2", "@tiptap/extension-strike": "^2.27.2", "@tiptap/extension-text": "^2.27.2", "@tiptap/extension-text-style": "^2.27.2", "@tiptap/pm": "^2.27.2" } }, "sha512-bb0gJvPoDuyRUQ/iuN52j1//EtWWttw+RXAv1uJxfR0uKf8X7uAqzaOOgwjknoCIDC97+1YHwpGdnRjpDkOBxw=="], + "@tiptap/y-tiptap": ["@tiptap/y-tiptap@3.0.3", "", { "dependencies": { "lib0": "^0.2.100" }, "peerDependencies": { "prosemirror-model": "^1.7.1", "prosemirror-state": "^1.2.3", "prosemirror-view": "^1.9.10", "y-protocols": "^1.0.1", "yjs": "^13.5.38" } }, "sha512-8UvuV4lTisCE9cMTc/X8kRyTn9edUO7Kball0I6wb17VwZSjNDfh/YKtP4O5vcPawEzFHQIvZGq/k1h37kAf0w=="], + "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="], "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], @@ -1300,7 +1330,7 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], @@ -1322,6 +1352,8 @@ "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + "fast-equals": ["fast-equals@5.4.0", "", {}, "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw=="], + "fast-xml-builder": ["fast-xml-builder@1.1.4", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg=="], "fast-xml-parser": ["fast-xml-parser@5.5.12", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-nUR0q8PPfoA/svPM43Gup7vLOZWppaNrYgGmrVqrAVJa7cOH4hMG6FX9M4mQ8dZA1/ObGZHzES7Ed88hxEBSJg=="], @@ -1478,6 +1510,8 @@ "linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="], + "linkifyjs": ["linkifyjs@4.3.2", "", {}, "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA=="], + "lmdb": ["lmdb@3.5.3", "", { "dependencies": { "@harperfast/extended-iterable": "^1.0.3", "msgpackr": "^1.11.2", "node-addon-api": "^6.1.0", "node-gyp-build-optional-packages": "5.2.2", "ordered-binary": "^1.5.3", "weak-lru-cache": "^1.2.2" }, "optionalDependencies": { "@lmdb/lmdb-darwin-arm64": "3.5.3", "@lmdb/lmdb-darwin-x64": "3.5.3", "@lmdb/lmdb-linux-arm": "3.5.3", "@lmdb/lmdb-linux-arm64": "3.5.3", "@lmdb/lmdb-linux-x64": "3.5.3", "@lmdb/lmdb-win32-arm64": "3.5.3", "@lmdb/lmdb-win32-x64": "3.5.3" }, "bin": { "download-lmdb-prebuilds": "bin/download-prebuilds.js" } }, "sha512-6A0iRKOv/N62P1vU8qhBr32tHQIY6pQMe8b6zqI4VLELqV8fWHUMdnfNjMR+Kyh7ZeRCo8PDzAnW2g+SJSoP1A=="], "lodash-es": ["lodash-es@4.18.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="], @@ -2104,8 +2138,18 @@ "@humanlayer/yjs-fs-editor/@durable-streams/server": ["@durable-streams/server@0.1.7", "", { "dependencies": { "@durable-streams/client": "0.2.0", "@durable-streams/state": "0.2.0", "@neophi/sieve-cache": "^1.0.0", "lmdb": "^3.3.0" } }, "sha512-JaCzfKS/GI3ZlbmInHvqX/fAh6XENTjc+acfhRlZH9iaRQiD1rOjfircj+jFDTIHgeT9oPqOVscmISGot8kI3w=="], + "@humanlayer/yjs-rich-markdown/@tiptap/extension-collaboration": ["@tiptap/extension-collaboration@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5", "@tiptap/y-tiptap": "^3.0.2", "yjs": "^13" } }, "sha512-3rax34HKSo8L5ihv5iGxISNBUIdVP0Fq9O6T/mq4kQOLhVhNbqwr9I/lWOvaz24nPE7u5Vkz0Ii3zWGlzxyPPA=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit": ["@tiptap/starter-kit@3.22.5", "", { "dependencies": { "@tiptap/core": "^3.22.5", "@tiptap/extension-blockquote": "^3.22.5", "@tiptap/extension-bold": "^3.22.5", "@tiptap/extension-bullet-list": "^3.22.5", "@tiptap/extension-code": "^3.22.5", "@tiptap/extension-code-block": "^3.22.5", "@tiptap/extension-document": "^3.22.5", "@tiptap/extension-dropcursor": "^3.22.5", "@tiptap/extension-gapcursor": "^3.22.5", "@tiptap/extension-hard-break": "^3.22.5", "@tiptap/extension-heading": "^3.22.5", "@tiptap/extension-horizontal-rule": "^3.22.5", "@tiptap/extension-italic": "^3.22.5", "@tiptap/extension-link": "^3.22.5", "@tiptap/extension-list": "^3.22.5", "@tiptap/extension-list-item": "^3.22.5", "@tiptap/extension-list-keymap": "^3.22.5", "@tiptap/extension-ordered-list": "^3.22.5", "@tiptap/extension-paragraph": "^3.22.5", "@tiptap/extension-strike": "^3.22.5", "@tiptap/extension-text": "^3.22.5", "@tiptap/extension-underline": "^3.22.5", "@tiptap/extensions": "^3.22.5", "@tiptap/pm": "^3.22.5" } }, "sha512-LZ/LYbwH6rnDi5DnRyagkuNsYAVyhM+yJvvz+ZuYA0JkPiTXJV86J5PWSKew8M0gVfMHcNVtKjfQCvViFCeIgw=="], + "@humanlayer/yjs-rich-markdown/@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], + "@humanlayer/yjs-rich-markdown-demo/@tiptap/extension-collaboration": ["@tiptap/extension-collaboration@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5", "@tiptap/y-tiptap": "^3.0.2", "yjs": "^13" } }, "sha512-3rax34HKSo8L5ihv5iGxISNBUIdVP0Fq9O6T/mq4kQOLhVhNbqwr9I/lWOvaz24nPE7u5Vkz0Ii3zWGlzxyPPA=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/react": ["@tiptap/react@3.22.5", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "fast-equals": "^5.3.3", "use-sync-external-store": "^1.4.0" }, "optionalDependencies": { "@tiptap/extension-bubble-menu": "^3.22.5", "@tiptap/extension-floating-menu": "^3.22.5" }, "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-36WHEs+vPmB//V1ff7Ujcnpz7Ey5g8lhpI/0+hoanSbdiPMTQ7qZVWwMovIkMKDlqWVp2fxBgeYM1861jyFzTw=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit": ["@tiptap/starter-kit@3.22.5", "", { "dependencies": { "@tiptap/core": "^3.22.5", "@tiptap/extension-blockquote": "^3.22.5", "@tiptap/extension-bold": "^3.22.5", "@tiptap/extension-bullet-list": "^3.22.5", "@tiptap/extension-code": "^3.22.5", "@tiptap/extension-code-block": "^3.22.5", "@tiptap/extension-document": "^3.22.5", "@tiptap/extension-dropcursor": "^3.22.5", "@tiptap/extension-gapcursor": "^3.22.5", "@tiptap/extension-hard-break": "^3.22.5", "@tiptap/extension-heading": "^3.22.5", "@tiptap/extension-horizontal-rule": "^3.22.5", "@tiptap/extension-italic": "^3.22.5", "@tiptap/extension-link": "^3.22.5", "@tiptap/extension-list": "^3.22.5", "@tiptap/extension-list-item": "^3.22.5", "@tiptap/extension-list-keymap": "^3.22.5", "@tiptap/extension-ordered-list": "^3.22.5", "@tiptap/extension-paragraph": "^3.22.5", "@tiptap/extension-strike": "^3.22.5", "@tiptap/extension-text": "^3.22.5", "@tiptap/extension-underline": "^3.22.5", "@tiptap/extensions": "^3.22.5", "@tiptap/pm": "^3.22.5" } }, "sha512-LZ/LYbwH6rnDi5DnRyagkuNsYAVyhM+yJvvz+ZuYA0JkPiTXJV86J5PWSKew8M0gVfMHcNVtKjfQCvViFCeIgw=="], + "@mermaid-js/mermaid-mindmap/@braintree/sanitize-url": ["@braintree/sanitize-url@6.0.4", "", {}, "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="], "@npmcli/git/ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], @@ -2122,6 +2166,78 @@ "@tanstack/router-plugin/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@tiptap/extension-blockquote/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-bold/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-bubble-menu/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-bubble-menu/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-bullet-list/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-code/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-code-block/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-code-block/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-collaboration/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-collaboration/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-collaboration-cursor/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-document/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-dropcursor/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-dropcursor/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-floating-menu/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-floating-menu/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-gapcursor/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-gapcursor/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-hard-break/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-heading/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-history/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-history/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-horizontal-rule/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-horizontal-rule/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-italic/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-list-item/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-ordered-list/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-paragraph/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-strike/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-text/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/extension-text-style/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/markdown/marked": ["marked@17.0.6", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA=="], + + "@tiptap/react/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/react/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/starter-kit/@tiptap/core": ["@tiptap/core@2.27.2", "", { "peerDependencies": { "@tiptap/pm": "^2.7.0" } }, "sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ=="], + + "@tiptap/starter-kit/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + "@vue/compiler-core/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], @@ -2148,8 +2264,6 @@ "markdown-it/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "mdast-util-frontmatter/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], - "minipass-fetch/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -2160,6 +2274,8 @@ "normalize-package-data/hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="], + "prosemirror-trailing-node/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + "rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], @@ -2206,12 +2322,114 @@ "@humanlayer/yjs-fs/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "@humanlayer/yjs-rich-markdown-demo/@tiptap/react/@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.22.5", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-yrNlFQQJY5MmhBpmD8tnmaSmyUQrEvgyPKa3bzVeWEhDSG1CW4A0ZSMx3hrA9yFO0HWfw3IJmvSCycEZQBalpQ=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/react/@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.22.5", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-dhem4sTPhyQgQ+pFp2Oud4k4FSQz9PVMgeQAC9288SmGwxBkJNveDAw6sKTMrumqDvwkJrtslXIupq9TZYQnzg=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-ajyP5W8fG5Hrru47T/eF3xMKOpNvWofgNJqBTeNuGl02sYxsy9a4EunyFxudsaZP9WW3VOD4SaIWr5+MqpbnOQ=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-bold": ["@tiptap/extension-bold@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-l/uDtpJISiFFyfctvnODNWBN/XPZI1jVZRacTRDDnSn8+x6KQ7G2qgFYueU7KvVJGDFVT39Iio56mcFRG/Pozg=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-bullet-list": ["@tiptap/extension-bullet-list@3.22.5", "", { "peerDependencies": { "@tiptap/extension-list": "3.22.5" } }, "sha512-cf54fG9AybU8NgPMv1TOcoqAkELeRc/VpnSCt/rIJZphWQx9nsFmrtkrlCatrIcCaGtNZYwlHlMnC5LVVMu0uA=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-code": ["@tiptap/extension-code@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-mwDNOJC9rYbDu/JcqrN4dbUQRklJU8Fuk2raxD/IvFw9qUIcPCmxQ2XT9UTKmZz/Ju7Kdy72fss6XpgWv6gLAQ=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-code-block": ["@tiptap/extension-code-block@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-d123kCfLdJTi4fue1m0+TNFztDkmIRSZGZmGu6H9KqwG5Q7IzjT9o8lzRsz+pXxYqHvqgYmXoEpM6srbzXx/Ag=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-document": ["@tiptap/extension-document@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-8NJERd+pCtvSuEP4C4WMGYmRRCV12ePZL7bC+QUdFlbdXg+kNZS0zZ7hh879tYA0Kidbi8rWWD1Tx+H2ezkmMw=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-dropcursor": ["@tiptap/extension-dropcursor@3.22.5", "", { "peerDependencies": { "@tiptap/extensions": "3.22.5" } }, "sha512-Mp40DaFrY3sEUVtFqmxrR0BmU4G3k8GCYYNGqNa9OqWv7BrcFDC03V2n3okESDKt4MKkzhQQmypq+ouLy8dLfA=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-gapcursor": ["@tiptap/extension-gapcursor@3.22.5", "", { "peerDependencies": { "@tiptap/extensions": "3.22.5" } }, "sha512-4WkMu7qqjbsm8hCQS+8X+la1wjriN0SKoRdvpfKH33qM50MB34tYJuGLAO+y7TTh4MMMco3AZCKPBL5JVMqNIg=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-hard-break": ["@tiptap/extension-hard-break@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-n0R2mUVYZU2AVbJhg/WcY9+zx690wVwvsItHJf0DrYbf1tCYHx+PRHUt/AoXk6u8BSmnkb8/FDziS8m3mjfpSg=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-heading": ["@tiptap/extension-heading@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-hjyEG4947PAhMBfP1G6B0QAh6+y9mp2C5BQmNjprA05/lQzDAT7KFZzNh8ZVp3ol6aICKq/N1gFOW9Dc/9FUOw=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-horizontal-rule": ["@tiptap/extension-horizontal-rule@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-vUV0/ugIbXOc8SJib0h8UMhgcqZXWu/dkEhlswZN4VVven1o5enkfxEiDw+OyIJHi5rUkrdhsQ/KTxG/Xb7X8A=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-italic": ["@tiptap/extension-italic@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-4T8baSiLkeIymTgEwirxDFt5YgYofkP3m1+MGYdGy2HKcOK+1vpvlPhEO1X5qtZngtJW5S4+njKjinRg52A4PA=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-list-item": ["@tiptap/extension-list-item@3.22.5", "", { "peerDependencies": { "@tiptap/extension-list": "3.22.5" } }, "sha512-W7uTmyKLhlsvuTPLv+8WwnsY+mlikBFIoLSvVcBaFt4MwpsZ+DeB6KQg02Y7tbtaAnG7rXu9Fvw2QORh2P728A=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@3.22.5", "", { "peerDependencies": { "@tiptap/extension-list": "3.22.5" } }, "sha512-OXdh4k4CNrukwiSdWdEQ49uvgnqvR0Z9aNSP4HI5/kZQ/Te1NtRtYCpUrzWyO/7CtjcCisXHti0o9C/TV8YMbQ=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-52KCto4+XKpnBWpIufspWLyq4UWxAWC72ANPdGuIhbi72NRTabiTbTVN40uwGSPkyakeESG0/vKdWJCVvB4f0g=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-strike": ["@tiptap/extension-strike@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-42WrrFK5gOom/0znH85x12Mw5IQ/6O6DWdyUWoRIrNA/qJpuHtU8oVU+bIgU2tuomMGHruRjIzgBQv5sBjEtww=="], + + "@humanlayer/yjs-rich-markdown-demo/@tiptap/starter-kit/@tiptap/extension-text": ["@tiptap/extension-text@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-bzpDOdAEo1JeoVZDIyV0oY0jGXkEG+AzF70SzHoRSjOvFDtKWunyXf9eO1OnOr2/fmMcckT2qwUBNBMQplWBzw=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-ajyP5W8fG5Hrru47T/eF3xMKOpNvWofgNJqBTeNuGl02sYxsy9a4EunyFxudsaZP9WW3VOD4SaIWr5+MqpbnOQ=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-bold": ["@tiptap/extension-bold@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-l/uDtpJISiFFyfctvnODNWBN/XPZI1jVZRacTRDDnSn8+x6KQ7G2qgFYueU7KvVJGDFVT39Iio56mcFRG/Pozg=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-bullet-list": ["@tiptap/extension-bullet-list@3.22.5", "", { "peerDependencies": { "@tiptap/extension-list": "3.22.5" } }, "sha512-cf54fG9AybU8NgPMv1TOcoqAkELeRc/VpnSCt/rIJZphWQx9nsFmrtkrlCatrIcCaGtNZYwlHlMnC5LVVMu0uA=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-code": ["@tiptap/extension-code@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-mwDNOJC9rYbDu/JcqrN4dbUQRklJU8Fuk2raxD/IvFw9qUIcPCmxQ2XT9UTKmZz/Ju7Kdy72fss6XpgWv6gLAQ=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-code-block": ["@tiptap/extension-code-block@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-d123kCfLdJTi4fue1m0+TNFztDkmIRSZGZmGu6H9KqwG5Q7IzjT9o8lzRsz+pXxYqHvqgYmXoEpM6srbzXx/Ag=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-document": ["@tiptap/extension-document@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-8NJERd+pCtvSuEP4C4WMGYmRRCV12ePZL7bC+QUdFlbdXg+kNZS0zZ7hh879tYA0Kidbi8rWWD1Tx+H2ezkmMw=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-dropcursor": ["@tiptap/extension-dropcursor@3.22.5", "", { "peerDependencies": { "@tiptap/extensions": "3.22.5" } }, "sha512-Mp40DaFrY3sEUVtFqmxrR0BmU4G3k8GCYYNGqNa9OqWv7BrcFDC03V2n3okESDKt4MKkzhQQmypq+ouLy8dLfA=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-gapcursor": ["@tiptap/extension-gapcursor@3.22.5", "", { "peerDependencies": { "@tiptap/extensions": "3.22.5" } }, "sha512-4WkMu7qqjbsm8hCQS+8X+la1wjriN0SKoRdvpfKH33qM50MB34tYJuGLAO+y7TTh4MMMco3AZCKPBL5JVMqNIg=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-hard-break": ["@tiptap/extension-hard-break@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-n0R2mUVYZU2AVbJhg/WcY9+zx690wVwvsItHJf0DrYbf1tCYHx+PRHUt/AoXk6u8BSmnkb8/FDziS8m3mjfpSg=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-heading": ["@tiptap/extension-heading@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-hjyEG4947PAhMBfP1G6B0QAh6+y9mp2C5BQmNjprA05/lQzDAT7KFZzNh8ZVp3ol6aICKq/N1gFOW9Dc/9FUOw=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-horizontal-rule": ["@tiptap/extension-horizontal-rule@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-vUV0/ugIbXOc8SJib0h8UMhgcqZXWu/dkEhlswZN4VVven1o5enkfxEiDw+OyIJHi5rUkrdhsQ/KTxG/Xb7X8A=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-italic": ["@tiptap/extension-italic@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-4T8baSiLkeIymTgEwirxDFt5YgYofkP3m1+MGYdGy2HKcOK+1vpvlPhEO1X5qtZngtJW5S4+njKjinRg52A4PA=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-list-item": ["@tiptap/extension-list-item@3.22.5", "", { "peerDependencies": { "@tiptap/extension-list": "3.22.5" } }, "sha512-W7uTmyKLhlsvuTPLv+8WwnsY+mlikBFIoLSvVcBaFt4MwpsZ+DeB6KQg02Y7tbtaAnG7rXu9Fvw2QORh2P728A=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@3.22.5", "", { "peerDependencies": { "@tiptap/extension-list": "3.22.5" } }, "sha512-OXdh4k4CNrukwiSdWdEQ49uvgnqvR0Z9aNSP4HI5/kZQ/Te1NtRtYCpUrzWyO/7CtjcCisXHti0o9C/TV8YMbQ=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-52KCto4+XKpnBWpIufspWLyq4UWxAWC72ANPdGuIhbi72NRTabiTbTVN40uwGSPkyakeESG0/vKdWJCVvB4f0g=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-strike": ["@tiptap/extension-strike@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-42WrrFK5gOom/0znH85x12Mw5IQ/6O6DWdyUWoRIrNA/qJpuHtU8oVU+bIgU2tuomMGHruRjIzgBQv5sBjEtww=="], + + "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit/@tiptap/extension-text": ["@tiptap/extension-text@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5" } }, "sha512-bzpDOdAEo1JeoVZDIyV0oY0jGXkEG+AzF70SzHoRSjOvFDtKWunyXf9eO1OnOr2/fmMcckT2qwUBNBMQplWBzw=="], + "@humanlayer/yjs-rich-markdown/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], "@npmcli/git/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], "@npmcli/promise-spawn/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], + "@tiptap/extension-blockquote/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-bold/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-bullet-list/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-code/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-collaboration-cursor/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-document/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-hard-break/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-heading/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-italic/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-list-item/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-ordered-list/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-paragraph/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-strike/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-text-style/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + + "@tiptap/extension-text/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], diff --git a/examples/yjs-rich-markdown-demo/package.json b/examples/yjs-rich-markdown-demo/package.json index 940c13a..1d647c6 100644 --- a/examples/yjs-rich-markdown-demo/package.json +++ b/examples/yjs-rich-markdown-demo/package.json @@ -15,11 +15,11 @@ "@durable-streams/server": "^0.3.1", "@durable-streams/y-durable-streams": "^0.2.4", "@humanlayer/yjs-rich-markdown": "workspace:*", - "@tiptap/extension-collaboration": "^2.11.0", - "@tiptap/extension-collaboration-cursor": "^2.11.0", - "@tiptap/pm": "^2.11.0", - "@tiptap/react": "^2.11.0", - "@tiptap/starter-kit": "^2.11.0", + "@tiptap/extension-collaboration": "3.22.5", + "@tiptap/extension-collaboration-caret": "3.22.5", + "@tiptap/pm": "3.22.5", + "@tiptap/react": "3.22.5", + "@tiptap/starter-kit": "3.22.5", "@tanstack/react-router": "^1.93.0", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/examples/yjs-rich-markdown-demo/src/components/ArtifactEditor.tsx b/examples/yjs-rich-markdown-demo/src/components/ArtifactEditor.tsx index e5b43ad..e6bd674 100644 --- a/examples/yjs-rich-markdown-demo/src/components/ArtifactEditor.tsx +++ b/examples/yjs-rich-markdown-demo/src/components/ArtifactEditor.tsx @@ -1,5 +1,5 @@ import { artifactCollaborationExtension } from '@humanlayer/yjs-rich-markdown' -import CollaborationCursor from '@tiptap/extension-collaboration-cursor' +import CollaborationCaret from '@tiptap/extension-collaboration-caret' import { EditorContent, useEditor } from '@tiptap/react' import StarterKit from '@tiptap/starter-kit' import { useEffect } from 'react' @@ -19,10 +19,10 @@ export function ArtifactEditor({ path, mode = 'fragment' }: ArtifactEditorProps) { extensions: [ StarterKit.configure({ - history: false, + undoRedo: false, }), artifactCollaborationExtension({ doc, path, mode }), - CollaborationCursor.configure({ + CollaborationCaret.configure({ provider: provider as any, user: { name: localUser.name, diff --git a/examples/yjs-rich-markdown-demo/src/components/MarkdownExportPanel.tsx b/examples/yjs-rich-markdown-demo/src/components/MarkdownExportPanel.tsx index 519460e..5b3eee6 100644 --- a/examples/yjs-rich-markdown-demo/src/components/MarkdownExportPanel.tsx +++ b/examples/yjs-rich-markdown-demo/src/components/MarkdownExportPanel.tsx @@ -8,14 +8,15 @@ type MarkdownExportPanelProps = { export function MarkdownExportPanel({ path }: MarkdownExportPanelProps) { const { doc } = useArtifactSession() - const [content, setContent] = useState('') + const [fragmentString, setFragmentString] = useState('') + const [fragmentJson, setFragmentJson] = useState('') useEffect(() => { const fragment = artifactFragment(doc, path) const update = () => { - const text = fragmentToPlainText(fragment) - setContent(text) + setFragmentString(fragment.toString()) + setFragmentJson(formatJson(fragment.toJSON())) } update() @@ -33,10 +34,20 @@ export function MarkdownExportPanel({ path }: MarkdownExportPanelProps) { fontFamily: 'monospace', }} > -

Content Export: {path}

+

Fragment Debug: {path}

- (Currently showing plain text from Y.XmlFragment. Full markdown export requires TipTap serialization.) + Raw Y.XmlFragment serialization for agent-readable rich artifact inspection.
+ + +
+ ) +} + +function DebugSection({ label, content }: { label: string; content: string }) { + return ( +
+
{label}
-				{content || '(empty)'}
+				{content || '(empty fragment)'}
 			
) } -function fragmentToPlainText(fragment: any): string { - const parts: string[] = [] - - const traverse = (node: any) => { - if (!node) return - - if (typeof node.toString === 'function' && node._start !== undefined) { - parts.push(node.toString()) - } else if (node.toArray) { - for (const child of node.toArray()) { - traverse(child) - } - } - } - - traverse(fragment) - return parts.join('') +function formatJson(value: unknown): string { + return JSON.stringify(value, null, 2) ?? '' } diff --git a/packages/yjs-rich-markdown/package.json b/packages/yjs-rich-markdown/package.json index e4a7d8e..e068fa5 100644 --- a/packages/yjs-rich-markdown/package.json +++ b/packages/yjs-rich-markdown/package.json @@ -35,14 +35,21 @@ "@types/bun": "latest" }, "peerDependencies": { - "@tiptap/core": "^2.11.0", - "@tiptap/extension-collaboration": "^2.11.0", - "@tiptap/starter-kit": "^2.11.0", + "@tiptap/core": "3.22.5", + "@tiptap/extension-collaboration": "3.22.5", + "@tiptap/starter-kit": "3.22.5", "typescript": "^5", - "yjs": "13.6.30", - "y-protocols": "1.0.7" + "y-protocols": "1.0.7", + "yjs": "13.6.30" + }, + "dependencies": { + "@tiptap/core": "3.22.5", + "@tiptap/extension-collaboration": "3.22.5", + "@tiptap/markdown": "3.22.5", + "@tiptap/pm": "3.22.5", + "@tiptap/starter-kit": "3.22.5", + "y-prosemirror": "1.3.7" }, - "dependencies": {}, "author": { "name": "Kyle Mistele", "url": "https://x.com/0xblacklight" diff --git a/packages/yjs-rich-markdown/src/artifact-read.ts b/packages/yjs-rich-markdown/src/artifact-read.ts new file mode 100644 index 0000000..82ba2d0 --- /dev/null +++ b/packages/yjs-rich-markdown/src/artifact-read.ts @@ -0,0 +1,6 @@ +import type * as Y from 'yjs' +import { artifactFragment } from './tiptap-bindings' + +export function readArtifact(doc: Y.Doc, path: string): string { + return artifactFragment(doc, path).toString() +} diff --git a/packages/yjs-rich-markdown/src/index.ts b/packages/yjs-rich-markdown/src/index.ts index 2d28f05..375d88c 100644 --- a/packages/yjs-rich-markdown/src/index.ts +++ b/packages/yjs-rich-markdown/src/index.ts @@ -1,6 +1,8 @@ export type { ArtifactPath } from './artifact-path' +export { readArtifact } from './artifact-read' export { artifactFragmentName, InvalidArtifactPathError, normalizeArtifactPath } from './artifact-path' export type { ArtifactMetadata, CreateArtifactOptions } from './artifact-store' export { ArtifactAlreadyExistsError, ArtifactNotFoundError, RichMarkdownArtifactStore } from './artifact-store' +export { markdownToTiptapJson, readArtifactMarkdown, tiptapJsonToMarkdown, writeArtifactMarkdown } from './markdown' export type { ArtifactCollaborationMode, ArtifactCollaborationOptions } from './tiptap-bindings' export { artifactCollaborationExtension, artifactFragment, defaultRichMarkdownExtensions } from './tiptap-bindings' diff --git a/packages/yjs-rich-markdown/src/markdown.ts b/packages/yjs-rich-markdown/src/markdown.ts new file mode 100644 index 0000000..4b72e9b --- /dev/null +++ b/packages/yjs-rich-markdown/src/markdown.ts @@ -0,0 +1,37 @@ +import type { JSONContent } from '@tiptap/core' +import { getSchema } from '@tiptap/core' +import { MarkdownManager } from '@tiptap/markdown' +import StarterKit from '@tiptap/starter-kit' +import { prosemirrorJSONToYXmlFragment, yXmlFragmentToProsemirrorJSON } from 'y-prosemirror' +import type * as Y from 'yjs' +import { artifactFragment } from './tiptap-bindings' + +const markdownExtensions = [StarterKit] + +export function markdownToTiptapJson(markdown: string): JSONContent { + return createMarkdownManager().parse(markdown) +} + +export function tiptapJsonToMarkdown(json: JSONContent): string { + return createMarkdownManager().serialize(json) +} + +export function writeArtifactMarkdown(doc: Y.Doc, path: string, markdown: string): void { + const fragment = artifactFragment(doc, path) + const json = markdownToTiptapJson(markdown) + replaceFragmentWithTiptapJson(fragment, json) +} + +export function readArtifactMarkdown(doc: Y.Doc, path: string): string { + const fragment = artifactFragment(doc, path) + return tiptapJsonToMarkdown(yXmlFragmentToProsemirrorJSON(fragment) as JSONContent) +} + +function createMarkdownManager(): MarkdownManager { + return new MarkdownManager({ extensions: markdownExtensions }) +} + +function replaceFragmentWithTiptapJson(fragment: Y.XmlFragment, json: JSONContent): void { + fragment.delete(0, fragment.length) + prosemirrorJSONToYXmlFragment(getSchema(markdownExtensions), json, fragment) +} diff --git a/packages/yjs-rich-markdown/src/tiptap-bindings.ts b/packages/yjs-rich-markdown/src/tiptap-bindings.ts index df2920f..364e241 100644 --- a/packages/yjs-rich-markdown/src/tiptap-bindings.ts +++ b/packages/yjs-rich-markdown/src/tiptap-bindings.ts @@ -33,7 +33,7 @@ export function artifactCollaborationExtension(options: ArtifactCollaborationOpt export function defaultRichMarkdownExtensions(options: ArtifactCollaborationOptions): Extension[] { return [ StarterKit.configure({ - history: false, + undoRedo: false, }), artifactCollaborationExtension(options), ] diff --git a/packages/yjs-rich-markdown/test/artifact-read.test.ts b/packages/yjs-rich-markdown/test/artifact-read.test.ts new file mode 100644 index 0000000..8b0b7fe --- /dev/null +++ b/packages/yjs-rich-markdown/test/artifact-read.test.ts @@ -0,0 +1,57 @@ +import { describe, expect, test } from 'bun:test' +import * as Y from 'yjs' +import { artifactFragment, readArtifact, RichMarkdownArtifactStore } from '../src' + +function syncDoc(source: Y.Doc, target: Y.Doc): void { + Y.applyUpdate(target, Y.encodeStateAsUpdate(source)) +} + +describe('readArtifact', () => { + test('returns the XML fragment string for an artifact path', () => { + const doc = new Y.Doc() + const store = new RichMarkdownArtifactStore(doc) + + store.createArtifact('/artifacts/plan.md') + const heading = new Y.XmlElement('heading') + heading.setAttribute('level', '1') + heading.insert(0, [new Y.XmlText('Plan')]) + artifactFragment(doc, '/artifacts/plan.md').insert(0, [heading]) + + expect(readArtifact(doc, '/artifacts/plan.md')).toBe('Plan') + }) + + test('reads only the requested path-keyed fragment', () => { + const doc = new Y.Doc() + const store = new RichMarkdownArtifactStore(doc) + + store.createArtifact('/artifacts/a.md') + store.createArtifact('/artifacts/b.md') + artifactFragment(doc, '/artifacts/a.md').insert(0, [new Y.XmlText('Artifact A')]) + artifactFragment(doc, '/artifacts/b.md').insert(0, [new Y.XmlText('Artifact B')]) + + expect(readArtifact(doc, '/artifacts/a.md')).toBe('Artifact A') + expect(readArtifact(doc, '/artifacts/b.md')).toBe('Artifact B') + }) + + test('returns an empty string for an empty artifact fragment', () => { + const doc = new Y.Doc() + const store = new RichMarkdownArtifactStore(doc) + + store.createArtifact('/artifacts/empty.md') + + expect(readArtifact(doc, '/artifacts/empty.md')).toBe('') + }) + + test('returns synced fragment content after a Yjs update round trip', () => { + const doc1 = new Y.Doc() + const doc2 = new Y.Doc() + const store1 = new RichMarkdownArtifactStore(doc1) + + store1.createArtifact('/artifacts/synced.md') + artifactFragment(doc1, '/artifacts/synced.md').insert(0, [new Y.XmlText('Synced content')]) + + syncDoc(doc1, doc2) + + expect(readArtifact(doc2, '/artifacts/synced.md')).toBe('Synced content') + }) +}) diff --git a/packages/yjs-rich-markdown/test/markdown.test.ts b/packages/yjs-rich-markdown/test/markdown.test.ts new file mode 100644 index 0000000..d585c02 --- /dev/null +++ b/packages/yjs-rich-markdown/test/markdown.test.ts @@ -0,0 +1,88 @@ +import { describe, expect, test } from 'bun:test' +import * as Y from 'yjs' +import { readArtifactMarkdown, RichMarkdownArtifactStore, writeArtifactMarkdown } from '../src' + +function syncDoc(source: Y.Doc, target: Y.Doc): void { + Y.applyUpdate(target, Y.encodeStateAsUpdate(source)) +} + +describe('markdown artifact bridge', () => { + test('writes markdown into a Yjs TipTap fragment and reads it back as markdown', () => { + const doc = new Y.Doc() + const store = new RichMarkdownArtifactStore(doc) + + store.createArtifact('/artifacts/plan.md') + writeArtifactMarkdown(doc, '/artifacts/plan.md', '# Plan\n\nThis is **bold** and *useful*.') + + expect(store.getFragment('/artifacts/plan.md').toString()).toBe( + 'PlanThis is bold and useful.', + ) + expect(readArtifactMarkdown(doc, '/artifacts/plan.md')).toBe('# Plan\n\nThis is **bold** and *useful*.') + }) + + test('round trips representative markdown blocks and marks', () => { + const doc = new Y.Doc() + const store = new RichMarkdownArtifactStore(doc) + const markdown = [ + '# Title', + '', + 'Paragraph with **bold**, *italic*, `code`, and [a link](https://example.com).', + '', + '- First', + '- Second', + '', + '1. One', + '2. Two', + '', + '> A quote', + '', + '```ts', + 'const value = 1', + '```', + ].join('\n') + + store.createArtifact('/artifacts/rich.md') + writeArtifactMarkdown(doc, '/artifacts/rich.md', markdown) + + expect(readArtifactMarkdown(doc, '/artifacts/rich.md')).toBe(markdown) + }) + + test('keeps markdown writes isolated by artifact path', () => { + const doc = new Y.Doc() + const store = new RichMarkdownArtifactStore(doc) + + store.createArtifact('/artifacts/a.md') + store.createArtifact('/artifacts/b.md') + writeArtifactMarkdown(doc, '/artifacts/a.md', '# A') + writeArtifactMarkdown(doc, '/artifacts/b.md', '# B') + + expect(readArtifactMarkdown(doc, '/artifacts/a.md')).toBe('# A') + expect(readArtifactMarkdown(doc, '/artifacts/b.md')).toBe('# B') + }) + + test('reads markdown after a Yjs update sync round trip', () => { + const doc1 = new Y.Doc() + const doc2 = new Y.Doc() + const store1 = new RichMarkdownArtifactStore(doc1) + + store1.createArtifact('/artifacts/synced.md') + writeArtifactMarkdown(doc1, '/artifacts/synced.md', '## Synced\n\nShared content.') + syncDoc(doc1, doc2) + + expect(readArtifactMarkdown(doc2, '/artifacts/synced.md')).toBe('## Synced\n\nShared content.') + }) + + test('write replaces existing fragment content from markdown', () => { + const doc = new Y.Doc() + const store = new RichMarkdownArtifactStore(doc) + + store.createArtifact('/artifacts/rewrite.md') + writeArtifactMarkdown(doc, '/artifacts/rewrite.md', '# Old') + writeArtifactMarkdown(doc, '/artifacts/rewrite.md', '# New\n\nReplacement.') + + expect(readArtifactMarkdown(doc, '/artifacts/rewrite.md')).toBe('# New\n\nReplacement.') + expect(store.getFragment('/artifacts/rewrite.md').toString()).toBe( + 'NewReplacement.', + ) + }) +}) From 5898d544535956d588391670c9502cfa72e2dc9e Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 2 May 2026 14:47:14 -0700 Subject: [PATCH 03/18] fix: add prepush hook script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 02b4bec..1d4b05d 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "test": "bun test --concurrency 16 **/*", "test:ci": "bun test --conditions=source ./packages/*/test/*.test.ts ./agents/*/test/*.test.ts", "typecheck": "bun --filter '*' --parallel typecheck", + "hook:prepush": "bun check", "release:prepare": "bun run scripts/release/prepare.ts", "release:publish": "bun run scripts/release/publish.ts" }, From 3735cd8a9d0339feb919047d83eeb6819b2bd24b Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 2 May 2026 14:48:09 -0700 Subject: [PATCH 04/18] style: format rich markdown bridge --- packages/yjs-rich-markdown/src/index.ts | 2 +- packages/yjs-rich-markdown/test/artifact-read.test.ts | 2 +- packages/yjs-rich-markdown/test/markdown.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/yjs-rich-markdown/src/index.ts b/packages/yjs-rich-markdown/src/index.ts index 375d88c..512c9f6 100644 --- a/packages/yjs-rich-markdown/src/index.ts +++ b/packages/yjs-rich-markdown/src/index.ts @@ -1,6 +1,6 @@ export type { ArtifactPath } from './artifact-path' -export { readArtifact } from './artifact-read' export { artifactFragmentName, InvalidArtifactPathError, normalizeArtifactPath } from './artifact-path' +export { readArtifact } from './artifact-read' export type { ArtifactMetadata, CreateArtifactOptions } from './artifact-store' export { ArtifactAlreadyExistsError, ArtifactNotFoundError, RichMarkdownArtifactStore } from './artifact-store' export { markdownToTiptapJson, readArtifactMarkdown, tiptapJsonToMarkdown, writeArtifactMarkdown } from './markdown' diff --git a/packages/yjs-rich-markdown/test/artifact-read.test.ts b/packages/yjs-rich-markdown/test/artifact-read.test.ts index 8b0b7fe..8a2a0dd 100644 --- a/packages/yjs-rich-markdown/test/artifact-read.test.ts +++ b/packages/yjs-rich-markdown/test/artifact-read.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from 'bun:test' import * as Y from 'yjs' -import { artifactFragment, readArtifact, RichMarkdownArtifactStore } from '../src' +import { artifactFragment, RichMarkdownArtifactStore, readArtifact } from '../src' function syncDoc(source: Y.Doc, target: Y.Doc): void { Y.applyUpdate(target, Y.encodeStateAsUpdate(source)) diff --git a/packages/yjs-rich-markdown/test/markdown.test.ts b/packages/yjs-rich-markdown/test/markdown.test.ts index d585c02..5e323c4 100644 --- a/packages/yjs-rich-markdown/test/markdown.test.ts +++ b/packages/yjs-rich-markdown/test/markdown.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from 'bun:test' import * as Y from 'yjs' -import { readArtifactMarkdown, RichMarkdownArtifactStore, writeArtifactMarkdown } from '../src' +import { RichMarkdownArtifactStore, readArtifactMarkdown, writeArtifactMarkdown } from '../src' function syncDoc(source: Y.Doc, target: Y.Doc): void { Y.applyUpdate(target, Y.encodeStateAsUpdate(source)) From dbfa6c902c6d6687b34016d4db61d81d45c3ae6b Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 2 May 2026 14:48:26 -0700 Subject: [PATCH 05/18] fix: use source-aware prepush checks --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1d4b05d..9efb3d6 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "test": "bun test --concurrency 16 **/*", "test:ci": "bun test --conditions=source ./packages/*/test/*.test.ts ./agents/*/test/*.test.ts", "typecheck": "bun --filter '*' --parallel typecheck", - "hook:prepush": "bun check", + "hook:prepush": "bun run test:ci && bun run typecheck && bun run biome:check", "release:prepare": "bun run scripts/release/prepare.ts", "release:publish": "bun run scripts/release/publish.ts" }, From 6ddcc1ff362cc80b35916ad66e5f2885528200f7 Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 2 May 2026 19:41:36 -0700 Subject: [PATCH 06/18] test: add QuickJS code mode learning coverage --- agents/yjs-markdown-apply-agent/package.json | 33 ++ .../test/learning/quickjs-emscripten.test.ts | 195 ++++++++ .../learning/yjs-tanstack-code-mode.test.ts | 194 ++++++++ bun.lock | 435 +++++++++++++++++- 4 files changed, 843 insertions(+), 14 deletions(-) create mode 100644 agents/yjs-markdown-apply-agent/package.json create mode 100644 agents/yjs-markdown-apply-agent/test/learning/quickjs-emscripten.test.ts create mode 100644 agents/yjs-markdown-apply-agent/test/learning/yjs-tanstack-code-mode.test.ts diff --git a/agents/yjs-markdown-apply-agent/package.json b/agents/yjs-markdown-apply-agent/package.json new file mode 100644 index 0000000..f9cdee7 --- /dev/null +++ b/agents/yjs-markdown-apply-agent/package.json @@ -0,0 +1,33 @@ +{ + "name": "@humanlayer/yjs-markdown-apply-agent", + "module": "src/index.ts", + "type": "module", + "private": true, + "scripts": { + "typecheck": "tsgo --noEmit" + }, + "exports": { + ".": { + "bun": "./src/index.ts", + "source": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "devDependencies": { + "@ai-sdk/provider": "catalog:", + "@humanlayer/agentlayer-core": "workspace:*", + "@tanstack/ai": "0.14.0", + "@tanstack/ai-code-mode": "0.1.8", + "@tanstack/ai-isolate-quickjs": "0.1.8", + "@types/bun": "latest", + "ai": "catalog:", + "quickjs-emscripten": "0.31.0", + "zod": "catalog:" + }, + "peerDependencies": { + "secure-exec": "0.2.1", + "typescript": "^5", + "yjs": "^13.6.30" + }, + "dependencies": {} +} \ No newline at end of file diff --git a/agents/yjs-markdown-apply-agent/test/learning/quickjs-emscripten.test.ts b/agents/yjs-markdown-apply-agent/test/learning/quickjs-emscripten.test.ts new file mode 100644 index 0000000..34a009c --- /dev/null +++ b/agents/yjs-markdown-apply-agent/test/learning/quickjs-emscripten.test.ts @@ -0,0 +1,195 @@ +import { describe, expect, test } from 'bun:test' +import { getQuickJS, newAsyncContext } from 'quickjs-emscripten' +import * as Y from 'yjs' + +describe('quickjs-emscripten learning tests', () => { + test('sync context can evaluate code and read values', async () => { + const QuickJS = await getQuickJS() + const vm = QuickJS.newContext() + + try { + const result = vm.evalCode(`({ answer: 40 + 2 })`) + const value = vm.unwrapResult(result) + + expect(vm.dump(value)).toEqual({ answer: 42 }) + value.dispose() + } finally { + vm.dispose() + } + }) + + test('sync context can call host functions', async () => { + const QuickJS = await getQuickJS() + const vm = QuickJS.newContext() + let callCount = 0 + + try { + const addHandle = vm.newFunction('add', (aHandle, bHandle) => { + callCount += 1 + const a = vm.getNumber(aHandle) + const b = vm.getNumber(bHandle) + + return vm.newNumber(a + b) + }) + vm.setProp(vm.global, 'add', addHandle) + addHandle.dispose() + + const result = vm.evalCode(`add(add(1, 1), 40)`) + const value = vm.unwrapResult(result) + + expect(vm.getNumber(value)).toBe(42) + expect(callCount).toBe(2) + value.dispose() + } finally { + vm.dispose() + } + }) + + test('sync host functions can mutate a live Y.XmlFragment', async () => { + const QuickJS = await getQuickJS() + const vm = QuickJS.newContext() + const yDoc = new Y.Doc() + const fragment = yDoc.getXmlFragment('quickjs-sync-bindings') + + try { + const pushElementHandle = vm.newFunction('pushElement', (jsonHandle) => { + const input = JSON.parse(vm.getString(jsonHandle)) as { + nodeName: string + text: string + attributes?: Record + } + const element = new Y.XmlElement(input.nodeName) + for (const [name, value] of Object.entries(input.attributes ?? {})) { + element.setAttribute(name, value) + } + + const text = new Y.XmlText() + text.insert(0, input.text) + element.push([text]) + fragment.push([element]) + + return vm.newString(JSON.stringify({ xml: fragment.toJSON(), length: fragment.length })) + }) + vm.setProp(vm.global, 'pushElementJson', pushElementHandle) + pushElementHandle.dispose() + + const result = vm.evalCode(` + JSON.parse(pushElementJson(JSON.stringify({ + nodeName: 'section', + text: 'Hello from sync QuickJS', + attributes: { id: 'intro' }, + }))) + `) + const value = vm.unwrapResult(result) + + expect(vm.dump(value)).toEqual({ + length: 1, + xml: '
Hello from sync QuickJS
', + }) + expect(fragment.toJSON()).toBe('
Hello from sync QuickJS
') + value.dispose() + } finally { + vm.dispose() + } + }) + + test('sync host functions can return QuickJS promises for awaited bindings', async () => { + const QuickJS = await getQuickJS() + const vm = QuickJS.newContext() + + try { + const asyncAddHandle = vm.newFunction('asyncAdd', (jsonHandle) => { + const { a, b } = JSON.parse(vm.getString(jsonHandle)) as { a: number; b: number } + const promise = vm.newPromise() + + queueMicrotask(() => { + promise.resolve(vm.newString(JSON.stringify({ sum: a + b }))) + }) + promise.settled.then(vm.runtime.executePendingJobs) + + return promise.handle + }) + vm.setProp(vm.global, 'asyncAddJson', asyncAddHandle) + asyncAddHandle.dispose() + + const result = vm.evalCode(` + (async () => { + const first = JSON.parse(await asyncAddJson(JSON.stringify({ a: 1, b: 1 }))) + const second = JSON.parse(await asyncAddJson(JSON.stringify({ a: first.sum, b: 40 }))) + + return second.sum + })() + `) + const promiseHandle = vm.unwrapResult(result) + const resolvedResult = await vm.resolvePromise(promiseHandle) + promiseHandle.dispose() + const value = vm.unwrapResult(resolvedResult) + + expect(vm.getNumber(value)).toBe(42) + value.dispose() + } finally { + vm.dispose() + } + }) + + test('asyncified host functions are synchronous from QuickJS code', async () => { + const context = await newAsyncContext() + + try { + const addHandle = context.newAsyncifiedFunction('add', async (jsonHandle) => { + const { a, b } = JSON.parse(context.getString(jsonHandle)) as { a: number; b: number } + + return context.newString(JSON.stringify({ sum: a + b })) + }) + context.setProp(context.global, 'addJson', addHandle) + addHandle.dispose() + + const result = await context.evalCodeAsync(` + const first = JSON.parse(addJson(JSON.stringify({ a: 1, b: 1 }))) + const second = JSON.parse(addJson(JSON.stringify({ a: first.sum, b: 40 }))) + + second.sum + `) + const value = context.unwrapResult(result) + + expect(context.getNumber(value)).toBe(42) + value.dispose() + } finally { + context.dispose() + } + }) + + test('asyncified host functions can also be used inside settled QuickJS promises', async () => { + const context = await newAsyncContext() + + try { + const addHandle = context.newAsyncifiedFunction('add', async (jsonHandle) => { + const { a, b } = JSON.parse(context.getString(jsonHandle)) as { a: number; b: number } + + return context.newString(JSON.stringify({ sum: a + b })) + }) + context.setProp(context.global, 'addJson', addHandle) + addHandle.dispose() + + const result = await context.evalCodeAsync(` + (async () => { + const first = JSON.parse(addJson(JSON.stringify({ a: 1, b: 1 }))) + const second = JSON.parse(addJson(JSON.stringify({ a: first.sum, b: 40 }))) + + return second.sum + })() + `) + const promiseHandle = context.unwrapResult(result) + const stateResult = context.getPromiseState(promiseHandle) + expect(stateResult.type).toBe('fulfilled') + if (stateResult.type !== 'fulfilled') throw new Error(`Expected fulfilled promise, got ${stateResult.type}`) + const value = stateResult.value + + expect(context.getNumber(value)).toBe(42) + value.dispose() + promiseHandle.dispose() + } finally { + context.dispose() + } + }) +}) diff --git a/agents/yjs-markdown-apply-agent/test/learning/yjs-tanstack-code-mode.test.ts b/agents/yjs-markdown-apply-agent/test/learning/yjs-tanstack-code-mode.test.ts new file mode 100644 index 0000000..e6db29e --- /dev/null +++ b/agents/yjs-markdown-apply-agent/test/learning/yjs-tanstack-code-mode.test.ts @@ -0,0 +1,194 @@ +import { describe, expect, test } from 'bun:test' +import { toolsToBindings, type ToolBinding } from '@tanstack/ai-code-mode' +import { createQuickJSIsolateDriver } from '@tanstack/ai-isolate-quickjs' +import { toolDefinition } from '@tanstack/ai' +import { z } from 'zod' +import * as Y from 'yjs' + +type QuickJSContext = Awaited['createContext']>> + +async function withQuickJSContext(bindings: Record, cb: (context: QuickJSContext) => Promise) { + const driver = createQuickJSIsolateDriver() + let context: QuickJSContext | undefined + + try { + context = await driver.createContext({ bindings, timeout: 5_000 }) + return await cb(context) + } finally { + await context?.dispose() + } +} + +describe('TanStack QuickJS isolate driver learning tests', () => { + test('driver can execute code without host bindings', async () => { + await withQuickJSContext({}, async (context) => { + const result = await context.execute(` + console.log('pure quickjs') + return { answer: 42 } + `) + + expect(result).toEqual({ + success: true, + value: { answer: 42 }, + logs: ['pure quickjs'], + }) + }) + }) + + test.todo('driver can create a context with direct bindings and execute code', async () => { + const bindings: Record = { + add: { + name: 'add', + description: 'Add two numbers together', + inputSchema: { + type: 'object', + properties: { + a: { type: 'number' }, + b: { type: 'number' }, + }, + required: ['a', 'b'], + }, + outputSchema: { + type: 'object', + properties: { sum: { type: 'number' } }, + required: ['sum'], + }, + execute: async (args) => { + const { a, b } = args as { a: number; b: number } + return { sum: a + b } + }, + }, + } + + await withQuickJSContext(bindings, async (context) => { + const result = await context.execute(` + const first = await add({ a: 1, b: 1 }) + const second = await add({ a: first.sum, b: 40 }) + console.log('computed', second.sum) + + return { answer: second.sum } + `) + + expect(result).toEqual({ + success: true, + value: { answer: 42 }, + logs: ['computed 42'], + }) + }) + }) + + test.todo('direct bindings can mutate a live Y.XmlFragment', async () => { + const yDoc = new Y.Doc() + const fragment = yDoc.getXmlFragment('tanstack-direct-bindings') + + const bindings: Record = { + pushElement: { + name: 'pushElement', + description: 'Append an XML element with text to the shared Y.XmlFragment', + inputSchema: { + type: 'object', + properties: { + nodeName: { type: 'string' }, + text: { type: 'string' }, + attributes: { + type: 'object', + additionalProperties: { type: 'string' }, + }, + }, + required: ['nodeName', 'text'], + }, + outputSchema: { + type: 'object', + properties: { + xml: { type: 'string' }, + length: { type: 'number' }, + }, + required: ['xml', 'length'], + }, + execute: async (args) => { + const { nodeName, text, attributes } = args as { + nodeName: string + text: string + attributes?: Record + } + const element = new Y.XmlElement(nodeName) + for (const [name, value] of Object.entries(attributes ?? {})) { + element.setAttribute(name, value) + } + + const xmlText = new Y.XmlText() + xmlText.insert(0, text) + element.push([xmlText]) + fragment.push([element]) + + return { xml: fragment.toJSON(), length: fragment.length } + }, + }, + readFragment: { + name: 'readFragment', + description: 'Read the shared Y.XmlFragment', + inputSchema: { type: 'object', properties: {} }, + outputSchema: { + type: 'object', + properties: { + xml: { type: 'string' }, + length: { type: 'number' }, + }, + required: ['xml', 'length'], + }, + execute: async () => ({ xml: fragment.toJSON(), length: fragment.length }), + }, + } + + await withQuickJSContext(bindings, async (context) => { + const result = await context.execute(` + await pushElement({ + nodeName: 'section', + text: 'Hello from QuickJS', + attributes: { id: 'intro' }, + }) + + await pushElement({ + nodeName: 'paragraph', + text: 'Bound directly through the isolate driver', + }) + + return await readFragment({}) + `) + + expect(result).toEqual({ + success: true, + value: { + length: 2, + xml: '
Hello from QuickJS
Bound directly through the isolate driver', + }, + logs: [], + }) + expect(fragment.toJSON()).toBe( + '
Hello from QuickJS
Bound directly through the isolate driver', + ) + }) + }) + + test.todo('TanStack tool definitions can be converted to direct isolate bindings', async () => { + const add = toolDefinition({ + name: 'add', + description: 'Add two numbers together', + inputSchema: z.object({ a: z.number(), b: z.number() }), + outputSchema: z.object({ sum: z.number() }), + }).server(({ a, b }) => ({ sum: a + b })) + + await withQuickJSContext(toolsToBindings([add], 'external_'), async (context) => { + const result = await context.execute(` + const answer = await external_add({ a: 20, b: 22 }) + return answer + `) + + expect(result).toEqual({ + success: true, + value: { sum: 42 }, + logs: [], + }) + }) + }) +}) diff --git a/bun.lock b/bun.lock index ccda3ca..426e927 100644 --- a/bun.lock +++ b/bun.lock @@ -77,6 +77,25 @@ "typescript": "^5", }, }, + "agents/yjs-markdown-apply-agent": { + "name": "@humanlayer/yjs-markdown-apply-agent", + "devDependencies": { + "@ai-sdk/provider": "catalog:", + "@humanlayer/agentlayer-core": "workspace:*", + "@tanstack/ai": "0.14.0", + "@tanstack/ai-code-mode": "0.1.8", + "@tanstack/ai-isolate-quickjs": "0.1.8", + "@types/bun": "latest", + "ai": "catalog:", + "quickjs-emscripten": "0.31.0", + "zod": "catalog:", + }, + "peerDependencies": { + "secure-exec": "0.2.1", + "typescript": "^5", + "yjs": "^13.6.30", + }, + }, "examples/yjs-rich-markdown-demo": { "name": "@humanlayer/yjs-rich-markdown-demo", "version": "0.0.1", @@ -93,7 +112,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "y-protocols": "1.0.7", - "yjs": "13.6.30", + "yjs": "^13.6.30", }, "devDependencies": { "@tanstack/router-plugin": "^1.93.0", @@ -249,7 +268,7 @@ "lib0": "^0.2.0", "typescript": "^5", "y-protocols": "1.0.7", - "yjs": "13.6.30", + "yjs": "^13.6.30", }, }, "packages/yjs-fs-editor": { @@ -271,7 +290,7 @@ "react-dom": "^18.3.1", "y-monaco": "^0.1.6", "y-protocols": "1.0.7", - "yjs": "13.6.30", + "yjs": "^13.6.30", }, "devDependencies": { "@tanstack/router-plugin": "^1.93.0", @@ -294,12 +313,12 @@ "react": "^18.3.1", "typescript": "catalog:", "y-protocols": "1.0.7", - "yjs": "13.6.30", + "yjs": "^13.6.30", }, "peerDependencies": { "react": "^18.3.1", "y-protocols": "1.0.7", - "yjs": "13.6.30", + "yjs": "^13.6.30", }, }, "packages/yjs-rich-markdown": { @@ -324,7 +343,7 @@ "@tiptap/starter-kit": "3.22.5", "typescript": "^5", "y-protocols": "1.0.7", - "yjs": "13.6.30", + "yjs": "^13.6.30", }, }, }, @@ -342,6 +361,8 @@ "zod": "4.3.6", }, "packages": { + "@ag-ui/core": ["@ag-ui/core@0.0.49", "", { "dependencies": { "zod": "^3.22.4" } }, "sha512-9ywypwjUGtIvTxJ2eKQjhPZgLnSFAfNK7vZUcT7Bz4ur4yAIB+lAFtzvu7VDYe6jsUx/6N/71Dh4R0zX5woNVw=="], + "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.69", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LshR7X3pFugY0o41G2VKTmg1XoGpSl7uoYWfzk6zjVZLhCfeFiwgpOga+eTV4XY1VVpZwKVqRnkDbIL7K2eH5g=="], "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.96", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-BDiVEMUVHGpngReeigzLyJobG0TvzYbNGzdHI8JYBZHrjOX4aL6qwIls7z3p7V4TuXVWUCbG8TSWEe7ksX4Vhw=="], @@ -454,6 +475,18 @@ "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], + "@cbor-extract/cbor-extract-darwin-arm64": ["@cbor-extract/cbor-extract-darwin-arm64@2.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZKZ/F8US7JR92J4DMct6cLW/Y66o2K576+zjlEN/MevH70bFIsB10wkZEQPLzl2oNh2SMGy55xpJ9JoBRl5DOA=="], + + "@cbor-extract/cbor-extract-darwin-x64": ["@cbor-extract/cbor-extract-darwin-x64@2.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-32b1mgc+P61Js+KW9VZv/c+xRw5EfmOcPx990JbCBSkYJFY0l25VinvyyWfl+3KjibQmAcYwmyzKF9J4DyKP/Q=="], + + "@cbor-extract/cbor-extract-linux-arm": ["@cbor-extract/cbor-extract-linux-arm@2.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-tNg0za41TpQfkhWjptD+0gSD2fggMiDCSacuIeELyb2xZhr7PrhPe5h66Jc67B/5dmpIhI2QOUtv4SBsricyYQ=="], + + "@cbor-extract/cbor-extract-linux-arm64": ["@cbor-extract/cbor-extract-linux-arm64@2.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-wfqgzqCAy/Vn8i6WVIh7qZd0DdBFaWBjPdB6ma+Wihcjv0gHqD/mw3ouVv7kbbUNrab6dKEx/w3xQZEdeXIlzg=="], + + "@cbor-extract/cbor-extract-linux-x64": ["@cbor-extract/cbor-extract-linux-x64@2.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rpiLnVEsqtPJ+mXTdx1rfz4RtUGYIUg2rUAZgd1KjiC1SehYUSkJN7Yh+aVfSjvCGtVP0/bfkQkXpPXKbmSUaA=="], + + "@cbor-extract/cbor-extract-win32-x64": ["@cbor-extract/cbor-extract-win32-x64@2.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-dI+9P7cfWxkTQ+oE+7Aa6onEn92PHgfWXZivjNheCRmTBDBf2fx6RyTi0cmgpYLnD1KLZK9ZYrMxaPZ4oiXhGA=="], + "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@12.0.0", "", { "dependencies": { "@chevrotain/gast": "12.0.0", "@chevrotain/types": "12.0.0" } }, "sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg=="], "@chevrotain/gast": ["@chevrotain/gast@12.0.0", "", { "dependencies": { "@chevrotain/types": "12.0.0" } }, "sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ=="], @@ -570,6 +603,8 @@ "@humanlayer/yjs-fs-react": ["@humanlayer/yjs-fs-react@workspace:packages/yjs-fs-react"], + "@humanlayer/yjs-markdown-apply-agent": ["@humanlayer/yjs-markdown-apply-agent@workspace:agents/yjs-markdown-apply-agent"], + "@humanlayer/yjs-rich-markdown": ["@humanlayer/yjs-rich-markdown@workspace:packages/yjs-rich-markdown"], "@humanlayer/yjs-rich-markdown-demo": ["@humanlayer/yjs-rich-markdown-demo@workspace:examples/yjs-rich-markdown-demo"], @@ -586,15 +621,15 @@ "@isaacs/string-locale-compare": ["@isaacs/string-locale-compare@1.1.0", "", {}, "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ=="], - "@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + "@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.31.0", "", {}, "sha512-1yrgvXlmXH2oNj3eFTrkwacGJbmM0crwipA3ohCrjv52gBeDaD7PsTvFYinlAnqU8iPME3LGP437yk05a2oejw=="], - "@jitl/quickjs-wasmfile-debug-asyncify": ["@jitl/quickjs-wasmfile-debug-asyncify@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-EX8zbXwGqCgAE764M+qvkHtyXDi/FUoMBea0JnES7vCM3P7a2+EOZOjGv85wtZ2sJhI1oJ+nekmqpOODFDY+hw=="], + "@jitl/quickjs-wasmfile-debug-asyncify": ["@jitl/quickjs-wasmfile-debug-asyncify@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-YkdzQdr1uaftFhgEnTRjTTZHk2SFZdpWO7XhOmRVbi6CEVsH9g5oNF8Ta1q3OuSJHRwwT8YsuR1YzEiEIJEk6w=="], - "@jitl/quickjs-wasmfile-debug-sync": ["@jitl/quickjs-wasmfile-debug-sync@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-LeYWrPGC1uNCTBWvibo3ZLJj0CSVNYUXvJpXMCmuQ5Sap2cCACc3uvGvYV4homHHBAzfw5akoTqMMS4YFRtw+Q=="], + "@jitl/quickjs-wasmfile-debug-sync": ["@jitl/quickjs-wasmfile-debug-sync@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-8XvloaaWBONqcHXYs5tWOjdhQVxzULilIfB2hvZfS6S+fI4m2+lFiwQy7xeP8ExHmiZ7D8gZGChNkdLgjGfknw=="], - "@jitl/quickjs-wasmfile-release-asyncify": ["@jitl/quickjs-wasmfile-release-asyncify@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-3oSwPfja12ICz4aIblB58cuY8JlEq5Txt8Cut4VLo+LH47QN+mzCnSgnbB03hWzg1LBcc+VyyI9UOag7a1NF+Q=="], + "@jitl/quickjs-wasmfile-release-asyncify": ["@jitl/quickjs-wasmfile-release-asyncify@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-uz0BbQYTxNsFkvkurd7vk2dOg57ElTBLCuvNtRl4rgrtbC++NIndD5qv2+AXb6yXDD3Uy1O2PCwmoaH0eXgEOg=="], - "@jitl/quickjs-wasmfile-release-sync": ["@jitl/quickjs-wasmfile-release-sync@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-BKNDI/TPBfGlLNGYpLrhcDGXmIk4xHm4MRAisOBnOzpXVn9HZWsfmMAc9WMBrAHjvvds6HOikKeaOBKdPdpVrg=="], + "@jitl/quickjs-wasmfile-release-sync": ["@jitl/quickjs-wasmfile-release-sync@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-hYduecOByj9AsAfsJhZh5nA6exokmuFC8cls39+lYmTCGY51bgjJJJwReEu7Ff7vBWaQCL6TeDdVlnp2WYz0jw=="], "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], @@ -798,6 +833,20 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ=="], + "@secure-exec/core": ["@secure-exec/core@0.2.1", "", { "dependencies": { "better-sqlite3": "^12.8.0" } }, "sha512-HsnUv6gClpMA1BBRmX86j30TKTZtgJC/fO1tVavr7IpM2zNKbHU8LgSlBd7mv2SNy02ImTmU/GnQ3aYB4NSbEg=="], + + "@secure-exec/nodejs": ["@secure-exec/nodejs@0.2.1", "", { "dependencies": { "@secure-exec/core": "0.2.1", "@secure-exec/v8": "0.2.1", "cbor-x": "^1.6.4", "cjs-module-lexer": "^2.1.0", "es-module-lexer": "^1.7.0", "esbuild": "^0.27.1", "node-stdlib-browser": "^1.3.1", "web-streams-polyfill": "^4.2.0" } }, "sha512-UJMJqVFxexlHJV0Q9nWURvrz6GElj8673DDOOFln6FHR6JS+9SaSU3eISrN158DuNC3SFi4rgjb/scKnK4YOYQ=="], + + "@secure-exec/v8": ["@secure-exec/v8@0.2.1", "", { "dependencies": { "cbor-x": "^1.6.4" }, "optionalDependencies": { "@secure-exec/v8-darwin-arm64": "0.2.1", "@secure-exec/v8-darwin-x64": "0.2.1", "@secure-exec/v8-linux-arm64-gnu": "0.2.1", "@secure-exec/v8-linux-x64-gnu": "0.2.1" } }, "sha512-ye/seCqzvyMGnvyP+AO7RkVMR/lE3x9m0D2PfmiAXA457R78ZmOFmZ6v+JlJG2vv3LM30KsSXTUhwpG+Teh0hw=="], + + "@secure-exec/v8-darwin-arm64": ["@secure-exec/v8-darwin-arm64@0.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gEWhMHzUpLwzuBNAD0lVkZXE8wFlWMLp4IOZ+56FYwOW/C+m07cYxuW4TjHyPqZ+vPm3IkoaMqqH5yT9VhjX/Q=="], + + "@secure-exec/v8-darwin-x64": ["@secure-exec/v8-darwin-x64@0.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-H2Z5K+Cq+fn/kxjGvhJzepnNFWG6qNdyhZybVWGr5bAAZoSz/Qkad4WnXcurWU+880tKDtnf19LHBXrg7zewNQ=="], + + "@secure-exec/v8-linux-arm64-gnu": ["@secure-exec/v8-linux-arm64-gnu@0.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-14subGhVV/gW35mYYm7Gv1Keeex7PxIgQfoKji/JH7wYyDuarP6kgaES0nJw+JXVkxEVud52c+kbcIjIggqCEw=="], + + "@secure-exec/v8-linux-x64-gnu": ["@secure-exec/v8-linux-x64-gnu@0.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Az4s+vUf+78vWtsC7rTn/jQc6WKJafAdt2YpEjB4Gnu+sX+FFTIst1hRV4gJonbRyJdy6SW+OQ6DZatmwczorQ=="], + "@shikijs/core": ["@shikijs/core@2.5.0", "", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="], "@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="], @@ -830,10 +879,20 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@tanstack/ai": ["@tanstack/ai@0.14.0", "", { "dependencies": { "@ag-ui/core": "0.0.49", "@tanstack/ai-event-client": "0.2.8", "partial-json": "^0.1.7" } }, "sha512-oiyYHcWMmyVoKDf018xZMEX62BaJVhdgeeRQeRbqP3p5Nvar2UZUhKzFkUiE4L9KzklPqrtI5aYhVdAp9Z142g=="], + + "@tanstack/ai-code-mode": ["@tanstack/ai-code-mode@0.1.8", "", { "dependencies": { "esbuild": "^0.25.12" }, "peerDependencies": { "@tanstack/ai": "0.14.0", "zod": "^3.0.0 || ^4.0.0" } }, "sha512-wsI4GWtAnLabirw3B2tUeLJjrxs2oWq2TWkarvVxSF6SAQwFxW/KaPOHbzfkc6vCg2cQqDYsVVIz2ncfV3L1xg=="], + + "@tanstack/ai-event-client": ["@tanstack/ai-event-client@0.2.8", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.4.1" }, "peerDependencies": { "@tanstack/ai": "0.14.0" } }, "sha512-pdvT1hh7UorwBiDLrXnI5BFDfc7JSqnN06LaQHG4TGRl/621UnUrh16hQKOMn/msArKiCj+n+TG+cw4mDwMaLw=="], + + "@tanstack/ai-isolate-quickjs": ["@tanstack/ai-isolate-quickjs@0.1.8", "", { "dependencies": { "quickjs-emscripten": "^0.31.0" }, "peerDependencies": { "@tanstack/ai-code-mode": "0.1.8" } }, "sha512-mAT8lDdkDrK38W7e/rpC8ckUZwspW3t9gK/w1QJgnCjFKQDHYKaXFUXQvYw0kWPrqxZk+9psjBd0K0eOrwKVpA=="], + "@tanstack/db": ["@tanstack/db@0.6.5", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@tanstack/db-ivm": "0.1.18", "@tanstack/pacer-lite": "^0.2.1" }, "peerDependencies": { "typescript": ">=4.7" } }, "sha512-gtCuAo4UtC9SR/kTMu5fVEff6qZ2R1FZi9X7MybtHKA6wve7RePifGG6qBI4OmMB+7juT5/+glNbnqZOrG0/pg=="], "@tanstack/db-ivm": ["@tanstack/db-ivm@0.1.18", "", { "dependencies": { "fractional-indexing": "^3.2.0", "sorted-btree": "^1.8.1" }, "peerDependencies": { "typescript": ">=4.7" } }, "sha512-+pZJiRKdoKRM5Epq9T7otD9ZJl82pRFauo7LKuJGrarjVKQ7r+QQlPe3kGdN9LEKSnuNGIWjX9OOY4M8kH4eLw=="], + "@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.4.3", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw=="], + "@tanstack/history": ["@tanstack/history@1.161.6", "", {}, "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg=="], "@tanstack/pacer-lite": ["@tanstack/pacer-lite@0.2.1", "", {}, "sha512-3PouiFjR4B6x1c969/Pl4ZIJleof1M0n6fNX8NRiC9Sqv1g06CVDlEaXUR4212ycGFyfq4q+t8Gi37Xy+z34iQ=="], @@ -1130,6 +1189,12 @@ "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "asn1.js": ["asn1.js@4.10.1", "", { "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw=="], + + "assert": ["assert@2.1.0", "", { "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", "object-is": "^1.1.5", "object.assign": "^4.1.4", "util": "^0.12.5" } }, "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], @@ -1140,30 +1205,66 @@ "baseline-browser-mapping": ["baseline-browser-mapping@2.10.21", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA=="], + "better-sqlite3": ["better-sqlite3@12.9.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ=="], + "bin-links": ["bin-links@6.0.0", "", { "dependencies": { "cmd-shim": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "proc-log": "^6.0.0", "read-cmd-shim": "^6.0.0", "write-file-atomic": "^7.0.0" } }, "sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w=="], "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], + "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], + "birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="], "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + "bn.js": ["bn.js@5.2.3", "", {}, "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w=="], + "brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "brorand": ["brorand@1.1.0", "", {}, "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="], + + "browser-resolve": ["browser-resolve@2.0.0", "", { "dependencies": { "resolve": "^1.17.0" } }, "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ=="], + + "browserify-aes": ["browserify-aes@1.2.0", "", { "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", "create-hash": "^1.1.0", "evp_bytestokey": "^1.0.3", "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA=="], + + "browserify-cipher": ["browserify-cipher@1.0.1", "", { "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", "evp_bytestokey": "^1.0.0" } }, "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w=="], + + "browserify-des": ["browserify-des@1.0.2", "", { "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A=="], + + "browserify-rsa": ["browserify-rsa@4.1.1", "", { "dependencies": { "bn.js": "^5.2.1", "randombytes": "^2.1.0", "safe-buffer": "^5.2.1" } }, "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ=="], + + "browserify-sign": ["browserify-sign@4.2.5", "", { "dependencies": { "bn.js": "^5.2.2", "browserify-rsa": "^4.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "elliptic": "^6.6.1", "inherits": "^2.0.4", "parse-asn1": "^5.1.9", "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1" } }, "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw=="], + + "browserify-zlib": ["browserify-zlib@0.2.0", "", { "dependencies": { "pako": "~1.0.5" } }, "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA=="], + "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + "buffer-xor": ["buffer-xor@1.0.3", "", {}, "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="], + + "builtin-status-codes": ["builtin-status-codes@3.0.0", "", {}, "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ=="], + "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], "cacache": ["cacache@20.0.4", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0" } }, "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA=="], + "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "caniuse-lite": ["caniuse-lite@1.0.30001790", "", {}, "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw=="], + "cbor-extract": ["cbor-extract@2.2.2", "", { "dependencies": { "node-gyp-build-optional-packages": "5.1.1" }, "optionalDependencies": { "@cbor-extract/cbor-extract-darwin-arm64": "2.2.2", "@cbor-extract/cbor-extract-darwin-x64": "2.2.2", "@cbor-extract/cbor-extract-linux-arm": "2.2.2", "@cbor-extract/cbor-extract-linux-arm64": "2.2.2", "@cbor-extract/cbor-extract-linux-x64": "2.2.2", "@cbor-extract/cbor-extract-win32-x64": "2.2.2" }, "bin": { "download-cbor-prebuilds": "bin/download-prebuilds.js" } }, "sha512-hlSxxI9XO2yQfe9g6msd3g4xCfDqK5T5P0fRMLuaLHhxn4ViPrm+a+MUfhrvH2W962RGxcBwEGzLQyjbDG1gng=="], + + "cbor-x": ["cbor-x@1.6.4", "", { "optionalDependencies": { "cbor-extract": "^2.2.2" } }, "sha512-UGKHjp6RHC6QuZ2yy5LCKm7MojM4716DwoSaqwQpaH4DvZvbBTGcoDNTiG9Y2lByXZYFEs9WRkS5tLl96IrF1Q=="], + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], @@ -1182,7 +1283,9 @@ "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], - "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], + "cipher-base": ["cipher-base@1.0.7", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.2" } }, "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA=="], + + "cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="], "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], @@ -1200,18 +1303,34 @@ "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + "console-browserify": ["console-browserify@1.2.0", "", {}, "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA=="], + + "constants-browserify": ["constants-browserify@1.0.0", "", {}, "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], "cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], "copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "^5.2.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="], + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="], + "create-ecdh": ["create-ecdh@4.0.4", "", { "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" } }, "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A=="], + + "create-hash": ["create-hash@1.2.0", "", { "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", "md5.js": "^1.3.4", "ripemd160": "^2.0.1", "sha.js": "^2.4.0" } }, "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg=="], + + "create-hmac": ["create-hmac@1.1.7", "", { "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", "inherits": "^2.0.1", "ripemd160": "^2.0.0", "safe-buffer": "^5.0.1", "sha.js": "^2.4.8" } }, "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg=="], + + "create-require": ["create-require@1.1.1", "", {}, "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="], + "crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + "crypto-browserify": ["crypto-browserify@3.12.1", "", { "dependencies": { "browserify-cipher": "^1.0.1", "browserify-sign": "^4.2.3", "create-ecdh": "^4.0.4", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "diffie-hellman": "^5.0.3", "hash-base": "~3.0.4", "inherits": "^2.0.4", "pbkdf2": "^3.1.2", "public-encrypt": "^4.0.3", "randombytes": "^2.1.0", "randomfill": "^1.0.4" } }, "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ=="], + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], @@ -1300,20 +1419,34 @@ "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + "delaunator": ["delaunator@5.1.0", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ=="], "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + "des.js": ["des.js@1.1.0", "", { "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg=="], + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], "diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], + "diffie-hellman": ["diffie-hellman@5.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" } }, "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg=="], + + "domain-browser": ["domain-browser@4.22.0", "", {}, "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw=="], + "dompurify": ["dompurify@3.4.0", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg=="], + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "electron-to-chromium": ["electron-to-chromium@1.5.344", "", {}, "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg=="], + "elliptic": ["elliptic@6.6.1", "", { "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", "hash.js": "^1.0.0", "hmac-drbg": "^1.0.1", "inherits": "^2.0.4", "minimalistic-assert": "^1.0.1", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g=="], + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="], @@ -1324,8 +1457,14 @@ "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -1336,8 +1475,12 @@ "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + "evp_bytestokey": ["evp_bytestokey@1.0.3", "", { "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" } }, "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA=="], + "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], @@ -1366,12 +1509,18 @@ "file-type": ["file-type@21.3.4", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g=="], + "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + "flexsearch": ["flexsearch@0.7.31", "", {}, "sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA=="], "focus-trap": ["focus-trap@7.8.0", "", { "dependencies": { "tabbable": "^6.4.0" } }, "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA=="], + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], "format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="], @@ -1386,10 +1535,16 @@ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="], @@ -1404,18 +1559,32 @@ "google-protobuf": ["google-protobuf@3.21.4", "", {}, "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ=="], + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hash-base": ["hash-base@3.0.5", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1" } }, "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg=="], + + "hash.js": ["hash.js@1.1.7", "", { "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" } }, "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA=="], + "hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="], "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + "hmac-drbg": ["hmac-drbg@1.0.1", "", { "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg=="], + "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], "hosted-git-info": ["hosted-git-info@9.0.2", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg=="], @@ -1426,6 +1595,8 @@ "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], + "https-browserify": ["https-browserify@1.0.0", "", {}, "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg=="], + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], @@ -1446,8 +1617,12 @@ "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + "is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], @@ -1456,20 +1631,32 @@ "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + "is-nan": ["is-nan@1.3.2", "", { "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" } }, "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w=="], + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + "is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="], + "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + "isbot": ["isbot@5.1.39", "", {}, "sha512-obH0yYahGXdzNxo+djmHhBYThUKDkz565cxkIlt2L9hXfv1NlaLKoDBHo6KxXsYrIXx2RK3x5vY36CfZcobxEw=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "isomorphic-timers-promises": ["isomorphic-timers-promises@1.0.1", "", {}, "sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ=="], + "isomorphic.js": ["isomorphic.js@0.2.5", "", {}, "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw=="], "jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="], @@ -1514,6 +1701,8 @@ "lmdb": ["lmdb@3.5.3", "", { "dependencies": { "@harperfast/extended-iterable": "^1.0.3", "msgpackr": "^1.11.2", "node-addon-api": "^6.1.0", "node-gyp-build-optional-packages": "5.2.2", "ordered-binary": "^1.5.3", "weak-lru-cache": "^1.2.2" }, "optionalDependencies": { "@lmdb/lmdb-darwin-arm64": "3.5.3", "@lmdb/lmdb-darwin-x64": "3.5.3", "@lmdb/lmdb-linux-arm": "3.5.3", "@lmdb/lmdb-linux-arm64": "3.5.3", "@lmdb/lmdb-linux-x64": "3.5.3", "@lmdb/lmdb-win32-arm64": "3.5.3", "@lmdb/lmdb-win32-x64": "3.5.3" }, "bin": { "download-lmdb-prebuilds": "bin/download-prebuilds.js" } }, "sha512-6A0iRKOv/N62P1vU8qhBr32tHQIY6pQMe8b6zqI4VLELqV8fWHUMdnfNjMR+Kyh7ZeRCo8PDzAnW2g+SJSoP1A=="], + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + "lodash-es": ["lodash-es@4.18.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="], "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], @@ -1538,6 +1727,10 @@ "marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "md5.js": ["md5.js@1.3.5", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg=="], + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], "mdast-util-frontmatter": ["mdast-util-frontmatter@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "escape-string-regexp": "^5.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0" } }, "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA=="], @@ -1600,6 +1793,8 @@ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + "miller-rabin": ["miller-rabin@4.0.1", "", { "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" }, "bin": { "miller-rabin": "bin/miller-rabin" } }, "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA=="], + "millify": ["millify@6.1.0", "", { "dependencies": { "yargs": "^17.0.1" }, "bin": { "millify": "bin/millify" } }, "sha512-H/E3J6t+DQs/F2YgfDhxUVZz/dF8JXPPKTLHL/yHCcLZLtCXJDUaqvhJXQwqOVBvbyNn4T0WjLpIHd7PAw7fBA=="], "mime": ["mime@2.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="], @@ -1608,6 +1803,10 @@ "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], + "minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="], + + "minimalistic-crypto-utils": ["minimalistic-crypto-utils@1.0.1", "", {}, "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="], + "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], @@ -1666,6 +1865,8 @@ "node-releases": ["node-releases@2.0.38", "", {}, "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw=="], + "node-stdlib-browser": ["node-stdlib-browser@1.3.1", "", { "dependencies": { "assert": "^2.0.0", "browser-resolve": "^2.0.0", "browserify-zlib": "^0.2.0", "buffer": "^5.7.1", "console-browserify": "^1.1.0", "constants-browserify": "^1.0.0", "create-require": "^1.1.1", "crypto-browserify": "^3.12.1", "domain-browser": "4.22.0", "events": "^3.0.0", "https-browserify": "^1.0.0", "isomorphic-timers-promises": "^1.0.1", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", "pkg-dir": "^5.0.0", "process": "^0.11.10", "punycode": "^1.4.1", "querystring-es3": "^0.2.1", "readable-stream": "^3.6.0", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", "string_decoder": "^1.0.0", "timers-browserify": "^2.0.4", "tty-browserify": "0.0.1", "url": "^0.11.4", "util": "^0.12.4", "vm-browserify": "^1.0.1" } }, "sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw=="], + "non-layered-tidy-tree-layout": ["non-layered-tidy-tree-layout@2.0.2", "", {}, "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw=="], "nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="], @@ -1690,6 +1891,14 @@ "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "object-is": ["object-is@1.1.6", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" } }, "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], @@ -1700,6 +1909,12 @@ "orderedmap": ["orderedmap@2.1.1", "", {}, "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="], + "os-browserify": ["os-browserify@0.3.0", "", {}, "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], @@ -1708,12 +1923,22 @@ "pacote": ["pacote@21.5.0", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "@npmcli/run-script": "^10.0.0", "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^13.0.0", "npm-packlist": "^10.0.1", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" }, "bin": { "pacote": "bin/index.js" } }, "sha512-VtZ0SB8mb5Tzw3dXDfVAIjhyVKUHZkS/ZH9/5mpKenwC9sFOXNI0JI7kEF7IMkwOnsWMFrvAZHzx1T5fmrp9FQ=="], + "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], + "papaparse": ["papaparse@5.5.3", "", {}, "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A=="], + "parse-asn1": ["parse-asn1@5.1.9", "", { "dependencies": { "asn1.js": "^4.10.1", "browserify-aes": "^1.2.0", "evp_bytestokey": "^1.0.3", "pbkdf2": "^3.1.5", "safe-buffer": "^5.2.1" } }, "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg=="], + "parse-conflict-json": ["parse-conflict-json@5.0.1", "", { "dependencies": { "json-parse-even-better-errors": "^5.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" } }, "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ=="], + "partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="], + + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + "path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], @@ -1726,18 +1951,24 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "pbkdf2": ["pbkdf2@3.1.5", "", { "dependencies": { "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "ripemd160": "^2.0.3", "safe-buffer": "^5.2.1", "sha.js": "^2.4.12", "to-buffer": "^1.2.1" } }, "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ=="], + "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + "pkg-dir": ["pkg-dir@5.0.0", "", { "dependencies": { "find-up": "^5.0.0" } }, "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA=="], + "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="], + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + "postcss": ["postcss@8.5.9", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw=="], "postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], @@ -1754,6 +1985,10 @@ "proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + + "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + "proggy": ["proggy@4.0.0", "", {}, "sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ=="], "promise-all-reject-late": ["promise-all-reject-late@1.0.1", "", {}, "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw=="], @@ -1800,17 +2035,29 @@ "protobufjs": ["protobufjs@7.5.5", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg=="], + "public-encrypt": ["public-encrypt@4.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", "randombytes": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q=="], + "pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="], + "punycode": ["punycode@1.4.1", "", {}, "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="], + "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], "pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="], + "qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="], + "qsu": ["qsu@1.12.0", "", {}, "sha512-kcB2A7XS+ApkxAIc63yOqF2mmvqyA4AezObM1RxAhOB6O0bRA8xCzm6PLwU70CVW9R+0Rozqd0pgOFskNtX2eQ=="], - "quickjs-emscripten": ["quickjs-emscripten@0.32.0", "", { "dependencies": { "@jitl/quickjs-wasmfile-debug-asyncify": "0.32.0", "@jitl/quickjs-wasmfile-debug-sync": "0.32.0", "@jitl/quickjs-wasmfile-release-asyncify": "0.32.0", "@jitl/quickjs-wasmfile-release-sync": "0.32.0", "quickjs-emscripten-core": "0.32.0" } }, "sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA=="], + "querystring-es3": ["querystring-es3@0.2.1", "", {}, "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA=="], + + "quickjs-emscripten": ["quickjs-emscripten@0.31.0", "", { "dependencies": { "@jitl/quickjs-wasmfile-debug-asyncify": "0.31.0", "@jitl/quickjs-wasmfile-debug-sync": "0.31.0", "@jitl/quickjs-wasmfile-release-asyncify": "0.31.0", "@jitl/quickjs-wasmfile-release-sync": "0.31.0", "quickjs-emscripten-core": "0.31.0" } }, "sha512-K7Yt78aRPLjPcqv3fIuLW1jW3pvwO21B9pmFOolsjM/57ZhdVXBr51GqJpalgBlkPu9foAvhEAuuQPnvIGvLvQ=="], - "quickjs-emscripten-core": ["quickjs-emscripten-core@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-QFnPfjFey8EqknSrSxe1hZrf1/8z7/6s1QzGOmKo6++02r7QRRX7ZoyNaZh7JuVjWsVW87KnQrbZqnHkOAzUyg=="], + "quickjs-emscripten-core": ["quickjs-emscripten-core@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-oQz8p0SiKDBc1TC7ZBK2fr0GoSHZKA0jZIeXxsnCyCs4y32FStzCW4d1h6E1sE0uHDMbGITbk2zhNaytaoJwXQ=="], + + "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], + + "randomfill": ["randomfill@1.0.4", "", { "dependencies": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" } }, "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw=="], "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], @@ -1856,6 +2103,8 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + "ripemd160": ["ripemd160@2.0.3", "", { "dependencies": { "hash-base": "^3.1.2", "inherits": "^2.0.4" } }, "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA=="], + "ripgrep": ["ripgrep@0.3.1", "", { "bin": { "rg": "lib/rg.mjs", "ripgrep": "lib/rg.mjs" } }, "sha512-6bDtNIBh1qPviVIU685/4uv0Ap5t8eS4wiJhy/tR2LdIeIey9CVasENlGS+ul3HnTmGANIp7AjnfsztsRmALfQ=="], "robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="], @@ -1870,6 +2119,8 @@ "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], @@ -1878,6 +2129,8 @@ "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="], + "secure-exec": ["secure-exec@0.2.1", "", { "dependencies": { "@secure-exec/core": "0.2.1", "@secure-exec/nodejs": "0.2.1" } }, "sha512-oaQDzTPDSCOckYC8G0PimIqzEVxY6sYEvcx0fMGsRR/Wl4wkFVHaZgQ3kc2DHWysV6WHWt5g1AXc/6seafO2XQ=="], + "seek-bzip": ["seek-bzip@2.0.0", "", { "dependencies": { "commander": "^6.0.0" }, "bin": { "seek-bunzip": "bin/seek-bunzip", "seek-table": "bin/seek-bzip-table" } }, "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg=="], "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], @@ -1886,6 +2139,12 @@ "seroval-plugins": ["seroval-plugins@1.5.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-qpY0Cl+fKYFn4GOf3cMiq6l72CpuVaawb6ILjubOQ+diJ54LfOWaSSPsaswN8DRPIPW4Yq+tE1k5aKd7ILyaFg=="], + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], + + "sha.js": ["sha.js@2.4.12", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" } }, "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], @@ -1894,6 +2153,14 @@ "shimmer": ["shimmer@1.2.1", "", {}, "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="], + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "sigstore": ["sigstore@4.1.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.1.0", "@sigstore/tuf": "^4.0.1", "@sigstore/verify": "^3.1.0" } }, "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA=="], @@ -1936,6 +2203,10 @@ "ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], + "stream-browserify": ["stream-browserify@3.0.0", "", { "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" } }, "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA=="], + + "stream-http": ["stream-http@3.2.0", "", { "dependencies": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", "readable-stream": "^3.6.0", "xtend": "^4.0.2" } }, "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A=="], + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], @@ -1968,6 +2239,8 @@ "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + "timers-browserify": ["timers-browserify@2.0.12", "", { "dependencies": { "setimmediate": "^1.0.4" } }, "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ=="], + "tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="], "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], @@ -1976,6 +2249,8 @@ "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], + "to-buffer": ["to-buffer@1.2.2", "", { "dependencies": { "isarray": "^2.0.5", "safe-buffer": "^5.2.1", "typed-array-buffer": "^1.0.3" } }, "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="], @@ -1992,12 +2267,16 @@ "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], + "tty-browserify": ["tty-browserify@0.0.1", "", {}, "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw=="], + "tuf-js": ["tuf-js@4.1.0", "", { "dependencies": { "@tufjs/models": "4.1.0", "debug": "^4.4.3", "make-fetch-happen": "^15.0.1" } }, "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ=="], "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], "turndown": ["turndown@7.2.4", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ=="], + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="], @@ -2030,8 +2309,12 @@ "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + "url": ["url@0.11.4", "", { "dependencies": { "punycode": "^1.4.1", "qs": "^6.12.3" } }, "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg=="], + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + "util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], @@ -2058,6 +2341,8 @@ "vitepress-sidebar": ["vitepress-sidebar@1.33.1", "", { "dependencies": { "glob": "11.1.0", "gray-matter": "4.0.3", "qsu": "^1.10.4" } }, "sha512-wPUbXezGakVldawixeRW5tKQTLKoMj2t4nWoThKfCltBM/9a38IE+wCXmmRNW22ZKC32SD/X/sG6NyCTK8QBRg=="], + "vm-browserify": ["vm-browserify@1.1.2", "", {}, "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="], + "vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="], "vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="], @@ -2078,16 +2363,22 @@ "weak-lru-cache": ["weak-lru-cache@1.2.2", "", {}, "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw=="], + "web-streams-polyfill": ["web-streams-polyfill@4.2.0", "", {}, "sha512-0rYDzGOh9EZpig92umN5g5D/9A1Kff7k0/mzPSSCY8jEQeYkgRMoY7LhbXtUCWzLCMX0TUE9aoHkjFNB7D9pfA=="], + "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], "write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="], + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + "y-monaco": ["y-monaco@0.1.6", "", { "dependencies": { "lib0": "^0.2.43" }, "peerDependencies": { "monaco-editor": ">=0.20.0", "yjs": "^13.3.1" } }, "sha512-sYRywMmcylt+Nupl+11AvizD2am06ST8lkVbUXuaEmrtV6Tf+TD4rsEm6u9YGGowYue+Vfg1IJ97SUP2J+PVXg=="], "y-prosemirror": ["y-prosemirror@1.3.7", "", { "dependencies": { "lib0": "^0.2.109" }, "peerDependencies": { "prosemirror-model": "^1.7.1", "prosemirror-state": "^1.2.3", "prosemirror-view": "^1.9.10", "y-protocols": "^1.0.1", "yjs": "^13.5.38" } }, "sha512-NpM99WSdD4Fx4if5xOMDpPtU3oAmTSjlzh5U4353ABbRHl1HtAFUx6HlebLZfyFxXN9jzKMDkVbcRjqOZVkYQg=="], @@ -2106,10 +2397,14 @@ "yjs": ["yjs@13.6.30", "", { "dependencies": { "lib0": "^0.2.99" } }, "sha512-vv/9h42eCMC81ZHDFswuu/MKzkl/vyq1BhaNGfHyOonwlG4CJbQF4oiBBJPvfdeCt/PlVDWh7Nov9D34YY09uQ=="], + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + "@ag-ui/core/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], @@ -2138,6 +2433,8 @@ "@humanlayer/yjs-fs-editor/@durable-streams/server": ["@durable-streams/server@0.1.7", "", { "dependencies": { "@durable-streams/client": "0.2.0", "@durable-streams/state": "0.2.0", "@neophi/sieve-cache": "^1.0.0", "lmdb": "^3.3.0" } }, "sha512-JaCzfKS/GI3ZlbmInHvqX/fAh6XENTjc+acfhRlZH9iaRQiD1rOjfircj+jFDTIHgeT9oPqOVscmISGot8kI3w=="], + "@humanlayer/yjs-markdown-apply-agent/@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], + "@humanlayer/yjs-rich-markdown/@tiptap/extension-collaboration": ["@tiptap/extension-collaboration@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5", "@tiptap/y-tiptap": "^3.0.2", "yjs": "^13" } }, "sha512-3rax34HKSo8L5ihv5iGxISNBUIdVP0Fq9O6T/mq4kQOLhVhNbqwr9I/lWOvaz24nPE7u5Vkz0Ii3zWGlzxyPPA=="], "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit": ["@tiptap/starter-kit@3.22.5", "", { "dependencies": { "@tiptap/core": "^3.22.5", "@tiptap/extension-blockquote": "^3.22.5", "@tiptap/extension-bold": "^3.22.5", "@tiptap/extension-bullet-list": "^3.22.5", "@tiptap/extension-code": "^3.22.5", "@tiptap/extension-code-block": "^3.22.5", "@tiptap/extension-document": "^3.22.5", "@tiptap/extension-dropcursor": "^3.22.5", "@tiptap/extension-gapcursor": "^3.22.5", "@tiptap/extension-hard-break": "^3.22.5", "@tiptap/extension-heading": "^3.22.5", "@tiptap/extension-horizontal-rule": "^3.22.5", "@tiptap/extension-italic": "^3.22.5", "@tiptap/extension-link": "^3.22.5", "@tiptap/extension-list": "^3.22.5", "@tiptap/extension-list-item": "^3.22.5", "@tiptap/extension-list-keymap": "^3.22.5", "@tiptap/extension-ordered-list": "^3.22.5", "@tiptap/extension-paragraph": "^3.22.5", "@tiptap/extension-strike": "^3.22.5", "@tiptap/extension-text": "^3.22.5", "@tiptap/extension-underline": "^3.22.5", "@tiptap/extensions": "^3.22.5", "@tiptap/pm": "^3.22.5" } }, "sha512-LZ/LYbwH6rnDi5DnRyagkuNsYAVyhM+yJvvz+ZuYA0JkPiTXJV86J5PWSKew8M0gVfMHcNVtKjfQCvViFCeIgw=="], @@ -2162,6 +2459,8 @@ "@npmcli/promise-spawn/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], + "@secure-exec/nodejs/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], + "@tanstack/router-generator/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@tanstack/router-plugin/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -2244,8 +2543,16 @@ "argparse/sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + "asn1.js/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], + + "browserify-sign/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "cacache/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], + "cbor-extract/node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.1.1", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-test": "build-test.js", "node-gyp-build-optional-packages-optional": "optional.js" } }, "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw=="], + + "create-ecdh/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], + "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="], "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], @@ -2254,16 +2561,26 @@ "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + "diffie-hellman/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], + + "elliptic/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], + "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "import-in-the-middle/cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], + "just-bash/ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], + "just-bash/quickjs-emscripten": ["quickjs-emscripten@0.32.0", "", { "dependencies": { "@jitl/quickjs-wasmfile-debug-asyncify": "0.32.0", "@jitl/quickjs-wasmfile-debug-sync": "0.32.0", "@jitl/quickjs-wasmfile-release-asyncify": "0.32.0", "@jitl/quickjs-wasmfile-release-sync": "0.32.0", "quickjs-emscripten-core": "0.32.0" } }, "sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA=="], + "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], "lmdb/node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="], "markdown-it/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "miller-rabin/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], + "minipass-fetch/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -2276,16 +2593,22 @@ "prosemirror-trailing-node/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + "public-encrypt/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], + "rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "ripemd160/hash-base": ["hash-base@3.1.2", "", { "dependencies": { "inherits": "^2.0.4", "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.1" } }, "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg=="], + "seek-bzip/commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="], "spdx-correct/spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + "to-buffer/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + "tsx/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], "validate-npm-package-license/spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], @@ -2322,6 +2645,8 @@ "@humanlayer/yjs-fs/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "@humanlayer/yjs-markdown-apply-agent/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "@humanlayer/yjs-rich-markdown-demo/@tiptap/react/@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.22.5", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-yrNlFQQJY5MmhBpmD8tnmaSmyUQrEvgyPKa3bzVeWEhDSG1CW4A0ZSMx3hrA9yFO0HWfw3IJmvSCycEZQBalpQ=="], "@humanlayer/yjs-rich-markdown-demo/@tiptap/react/@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.22.5", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-dhem4sTPhyQgQ+pFp2Oud4k4FSQz9PVMgeQAC9288SmGwxBkJNveDAw6sKTMrumqDvwkJrtslXIupq9TZYQnzg=="], @@ -2400,6 +2725,58 @@ "@npmcli/promise-spawn/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], + "@secure-exec/nodejs/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], + + "@secure-exec/nodejs/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], + + "@secure-exec/nodejs/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="], + + "@secure-exec/nodejs/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="], + + "@secure-exec/nodejs/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="], + + "@secure-exec/nodejs/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="], + + "@secure-exec/nodejs/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="], + + "@secure-exec/nodejs/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="], + + "@secure-exec/nodejs/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="], + + "@secure-exec/nodejs/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="], + + "@secure-exec/nodejs/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="], + + "@secure-exec/nodejs/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="], + + "@secure-exec/nodejs/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="], + + "@secure-exec/nodejs/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="], + + "@secure-exec/nodejs/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="], + + "@secure-exec/nodejs/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="], + + "@secure-exec/nodejs/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="], + + "@secure-exec/nodejs/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="], + + "@secure-exec/nodejs/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="], + + "@secure-exec/nodejs/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="], + + "@secure-exec/nodejs/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="], + + "@secure-exec/nodejs/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="], + + "@secure-exec/nodejs/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="], + + "@secure-exec/nodejs/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="], + + "@secure-exec/nodejs/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="], + + "@secure-exec/nodejs/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "@tiptap/extension-blockquote/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], "@tiptap/extension-bold/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], @@ -2430,10 +2807,24 @@ "@tiptap/extension-text/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], + "browserify-sign/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "browserify-sign/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-debug-asyncify": ["@jitl/quickjs-wasmfile-debug-asyncify@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-EX8zbXwGqCgAE764M+qvkHtyXDi/FUoMBea0JnES7vCM3P7a2+EOZOjGv85wtZ2sJhI1oJ+nekmqpOODFDY+hw=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-debug-sync": ["@jitl/quickjs-wasmfile-debug-sync@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-LeYWrPGC1uNCTBWvibo3ZLJj0CSVNYUXvJpXMCmuQ5Sap2cCACc3uvGvYV4homHHBAzfw5akoTqMMS4YFRtw+Q=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-release-asyncify": ["@jitl/quickjs-wasmfile-release-asyncify@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-3oSwPfja12ICz4aIblB58cuY8JlEq5Txt8Cut4VLo+LH47QN+mzCnSgnbB03hWzg1LBcc+VyyI9UOag7a1NF+Q=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-release-sync": ["@jitl/quickjs-wasmfile-release-sync@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-BKNDI/TPBfGlLNGYpLrhcDGXmIk4xHm4MRAisOBnOzpXVn9HZWsfmMAc9WMBrAHjvvds6HOikKeaOBKdPdpVrg=="], + + "just-bash/quickjs-emscripten/quickjs-emscripten-core": ["quickjs-emscripten-core@0.32.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.32.0" } }, "sha512-QFnPfjFey8EqknSrSxe1hZrf1/8z7/6s1QzGOmKo6++02r7QRRX7ZoyNaZh7JuVjWsVW87KnQrbZqnHkOAzUyg=="], + "minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], "minipass-pipeline/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], @@ -2442,6 +2833,8 @@ "normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "ripemd160/hash-base/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], @@ -2506,6 +2899,20 @@ "vitepress/vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-debug-asyncify/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-debug-sync/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-release-asyncify/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + + "just-bash/quickjs-emscripten/@jitl/quickjs-wasmfile-release-sync/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + + "just-bash/quickjs-emscripten/quickjs-emscripten-core/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], + + "ripemd160/hash-base/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "ripemd160/hash-base/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "vitepress/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], "vitepress/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], From e744e11efbf4095d7aae9cd5bd2568ecfe926ba5 Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 2 May 2026 20:11:47 -0700 Subject: [PATCH 07/18] feat: add quickjs exec package --- agents/yjs-markdown-apply-agent/package.json | 3 +- agents/yjs-markdown-apply-agent/src/agent.ts | 23 ++ agents/yjs-markdown-apply-agent/src/index.ts | 11 + .../yjs-markdown-apply-agent/src/prompts.ts | 72 ++++++ .../src/retry-controller.ts | 35 +++ .../src/secure-apply-tool.ts | 40 ++++ agents/yjs-markdown-apply-agent/src/tools.ts | 7 + agents/yjs-markdown-apply-agent/src/types.ts | 44 ++++ .../test/learning/README.md | 63 ++++++ .../test/learning/proxy.ts | 211 ++++++++++++++++++ .../test/learning/quickjs-harness.test.ts | 54 +++++ .../test/learning/yjs-secure-exec.test.ts | 158 +++++++++++++ .../test/prompt-cache-shape.test.ts | 64 ++++++ .../test/retry-controller.test.ts | 36 +++ .../test/secure-apply-contract.test.ts | 49 ++++ agents/yjs-markdown-apply-agent/tsconfig.json | 24 ++ bun.lock | 20 ++ packages/agentlayer-core/package.json | 6 + packages/quickjs-exec/build.ts | 3 + packages/quickjs-exec/package.json | 45 ++++ packages/quickjs-exec/src/index.ts | 130 +++++++++++ .../quickjs-exec/test/quickjs-exec.test.ts | 53 +++++ packages/quickjs-exec/tsconfig.build.json | 23 ++ packages/quickjs-exec/tsconfig.json | 23 ++ scripts/release/manifest.ts | 4 + 25 files changed, 1200 insertions(+), 1 deletion(-) create mode 100644 agents/yjs-markdown-apply-agent/src/agent.ts create mode 100644 agents/yjs-markdown-apply-agent/src/index.ts create mode 100644 agents/yjs-markdown-apply-agent/src/prompts.ts create mode 100644 agents/yjs-markdown-apply-agent/src/retry-controller.ts create mode 100644 agents/yjs-markdown-apply-agent/src/secure-apply-tool.ts create mode 100644 agents/yjs-markdown-apply-agent/src/tools.ts create mode 100644 agents/yjs-markdown-apply-agent/src/types.ts create mode 100644 agents/yjs-markdown-apply-agent/test/learning/README.md create mode 100644 agents/yjs-markdown-apply-agent/test/learning/proxy.ts create mode 100644 agents/yjs-markdown-apply-agent/test/learning/quickjs-harness.test.ts create mode 100644 agents/yjs-markdown-apply-agent/test/learning/yjs-secure-exec.test.ts create mode 100644 agents/yjs-markdown-apply-agent/test/prompt-cache-shape.test.ts create mode 100644 agents/yjs-markdown-apply-agent/test/retry-controller.test.ts create mode 100644 agents/yjs-markdown-apply-agent/test/secure-apply-contract.test.ts create mode 100644 agents/yjs-markdown-apply-agent/tsconfig.json create mode 100644 packages/quickjs-exec/build.ts create mode 100644 packages/quickjs-exec/package.json create mode 100644 packages/quickjs-exec/src/index.ts create mode 100644 packages/quickjs-exec/test/quickjs-exec.test.ts create mode 100644 packages/quickjs-exec/tsconfig.build.json create mode 100644 packages/quickjs-exec/tsconfig.json diff --git a/agents/yjs-markdown-apply-agent/package.json b/agents/yjs-markdown-apply-agent/package.json index f9cdee7..e5e4d65 100644 --- a/agents/yjs-markdown-apply-agent/package.json +++ b/agents/yjs-markdown-apply-agent/package.json @@ -16,6 +16,7 @@ "devDependencies": { "@ai-sdk/provider": "catalog:", "@humanlayer/agentlayer-core": "workspace:*", + "@humanlayer/quickjs-exec": "workspace:*", "@tanstack/ai": "0.14.0", "@tanstack/ai-code-mode": "0.1.8", "@tanstack/ai-isolate-quickjs": "0.1.8", @@ -30,4 +31,4 @@ "yjs": "^13.6.30" }, "dependencies": {} -} \ No newline at end of file +} diff --git a/agents/yjs-markdown-apply-agent/src/agent.ts b/agents/yjs-markdown-apply-agent/src/agent.ts new file mode 100644 index 0000000..ffe00cd --- /dev/null +++ b/agents/yjs-markdown-apply-agent/src/agent.ts @@ -0,0 +1,23 @@ +import { Agent, type AgentConfig } from '@humanlayer/agentlayer-core' +import type { LanguageModel } from 'ai' +import { APPLY_AGENT_SYSTEM_PROMPT } from './prompts' +import type { SecureApplyExecutor } from './secure-apply-tool' +import { createYjsMarkdownApplyTools } from './tools' + +export type CreateYjsMarkdownApplyAgentOptions = { + model: LanguageModel + executor: SecureApplyExecutor + system?: string + maxSteps?: number + providerOptions?: AgentConfig['providerOptions'] +} + +export function createYjsMarkdownApplyAgent(options: CreateYjsMarkdownApplyAgentOptions) { + return new Agent({ + model: options.model, + system: options.system ?? APPLY_AGENT_SYSTEM_PROMPT, + tools: createYjsMarkdownApplyTools(options.executor), + maxSteps: options.maxSteps, + providerOptions: options.providerOptions, + }) +} diff --git a/agents/yjs-markdown-apply-agent/src/index.ts b/agents/yjs-markdown-apply-agent/src/index.ts new file mode 100644 index 0000000..d630d5f --- /dev/null +++ b/agents/yjs-markdown-apply-agent/src/index.ts @@ -0,0 +1,11 @@ +export { createYjsMarkdownApplyAgent, type CreateYjsMarkdownApplyAgentOptions } from './agent' +export { APPLY_AGENT_SYSTEM_PROMPT, buildAbortSystemInformation, buildApplyUserMessage } from './prompts' +export { DEFAULT_APPLY_RETRY_POLICY, nextRetryDecision, type RetryDecision } from './retry-controller' +export { + createFakeSecureApplyExecutor, + createSecureApplyTool, + secureApplyResultSchema, + type SecureApplyExecutor, +} from './secure-apply-tool' +export { createYjsMarkdownApplyTools } from './tools' +export type { ApplyAttemptAbort, ApplyRetryPolicy, SecureApplyInput, SecureApplyResult } from './types' diff --git a/agents/yjs-markdown-apply-agent/src/prompts.ts b/agents/yjs-markdown-apply-agent/src/prompts.ts new file mode 100644 index 0000000..44f8002 --- /dev/null +++ b/agents/yjs-markdown-apply-agent/src/prompts.ts @@ -0,0 +1,72 @@ +import type { ApplyAttemptAbort, SecureApplyInput } from './types' + +export const APPLY_AGENT_SYSTEM_PROMPT = `You are the Yjs Markdown apply agent. + +Your job is to generate JavaScript that applies one requested Markdown oldString/newString edit to a disconnected Yjs document snapshot. The host will run your generated code in a secure executor and will reject any unsafe or invalid result. + +Stable contract: +- Reconstruct a disconnected Y.Doc from input.snapshotBase64. +- Open only input.artifactFragmentName as the target Y.XmlFragment. +- Apply the requested edit structurally with Y.XmlFragment, Y.XmlElement, and Y.XmlText operations. +- Validate the result before returning. +- Return a SecureApplyResult JSON object with proposedUpdateBase64 computed from Y.encodeStateAsUpdate(doc, baseStateVector). +- Do not access network, filesystem, process, environment, shell, provider, or the live document. +- Do not import arbitrary packages. Use only host-allowlisted dependencies and helpers. +- If the target document changed and the host provides a block, regenerate against the latest snapshot in that message. + +Prompt caching requirement: +- Treat this system prompt and tool definitions as stable. +- Dynamic document state appears only in user messages.` + +export function buildApplyUserMessage(input: SecureApplyInput): string { + const systemInformation = input.systemInformation ? `${input.systemInformation}\n\n` : '' + + return `${systemInformation}Apply this Markdown edit to the target Yjs XML fragment. + +path: ${input.path} +artifactFragmentName: ${input.artifactFragmentName} +baseArtifactRevision: ${input.baseArtifactRevision} + +oldString: +${fence(input.oldString)} + +newString: +${fence(input.newString)} + +currentMarkdown: +${fence(input.currentMarkdown)} + +currentXml: +${fence(input.currentXml)} + +snapshotBase64: +${input.snapshotBase64} + +baseStateVectorBase64: +${input.baseStateVectorBase64}` +} + +export function buildAbortSystemInformation(abort: ApplyAttemptAbort): string { + const summary = abort.changeSummary.map((line) => `- ${line}`).join('\n') + + return ` +The previous secure apply attempt was aborted because the target artifact changed while generated code was running. + +path: ${abort.path} +revision: ${abort.previousRevision} -> ${abort.currentRevision} +changeSummary: +${summary || '- target fragment changed'} + +updatedMarkdown: +${fence(abort.updatedMarkdown)} + +updatedXml: +${fence(abort.updatedXml)} + +The edit intent is unchanged. Apply the same requested oldString/newString transformation to this latest document state. +` +} + +function fence(value: string): string { + return `\`\`\`txt\n${value}\n\`\`\`` +} diff --git a/agents/yjs-markdown-apply-agent/src/retry-controller.ts b/agents/yjs-markdown-apply-agent/src/retry-controller.ts new file mode 100644 index 0000000..782be2d --- /dev/null +++ b/agents/yjs-markdown-apply-agent/src/retry-controller.ts @@ -0,0 +1,35 @@ +import type { ApplyRetryPolicy } from './types' + +export const DEFAULT_APPLY_RETRY_POLICY: ApplyRetryPolicy = { + maxAttempts: 3, + initialBackoffMs: 250, + maxBackoffMs: 2000, + waitForQuietMs: 1000, + maxWaitForQuietMs: 10000, +} + +export type RetryDecision = + | { type: 'retry'; attempt: number; delayMs: number } + | { type: 'fail'; reason: 'max-attempts-exceeded' | 'quiet-timeout-exceeded' } + +export function nextRetryDecision(input: { + attempt: number + policy?: Partial + waitedForQuietMs?: number +}): RetryDecision { + const policy = { ...DEFAULT_APPLY_RETRY_POLICY, ...input.policy } + if (input.attempt >= policy.maxAttempts) { + return { type: 'fail', reason: 'max-attempts-exceeded' } + } + + if ((input.waitedForQuietMs ?? 0) > policy.maxWaitForQuietMs) { + return { type: 'fail', reason: 'quiet-timeout-exceeded' } + } + + const exponentialDelay = policy.initialBackoffMs * 2 ** Math.max(0, input.attempt - 1) + return { + type: 'retry', + attempt: input.attempt + 1, + delayMs: Math.min(policy.maxBackoffMs, Math.max(policy.waitForQuietMs, exponentialDelay)), + } +} diff --git a/agents/yjs-markdown-apply-agent/src/secure-apply-tool.ts b/agents/yjs-markdown-apply-agent/src/secure-apply-tool.ts new file mode 100644 index 0000000..4c917bd --- /dev/null +++ b/agents/yjs-markdown-apply-agent/src/secure-apply-tool.ts @@ -0,0 +1,40 @@ +import { defineTool } from '@humanlayer/agentlayer-core' +import { z } from 'zod' + +export const secureApplyResultSchema = z.object({ + ok: z.boolean(), + proposedUpdateBase64: z.string().optional(), + beforeMarkdown: z.string().optional(), + afterMarkdown: z.string().optional(), + beforeXml: z.string().optional(), + afterXml: z.string().optional(), + changedArtifacts: z.array(z.string()).optional(), + validationErrors: z.array(z.string()).optional(), + operationLog: z.array(z.string()).optional(), +}) + +export const secureApplyToolInputSchema = z.object({ + generatedCode: z.string().min(1), +}) + +export type SecureApplyToolInput = z.infer +export type SecureApplyToolResult = z.infer + +export type SecureApplyExecutor = (input: SecureApplyToolInput, signal: AbortSignal) => Promise + +export function createSecureApplyTool(executor: SecureApplyExecutor) { + return defineTool({ + name: 'secure_apply_yjs_update', + description: + 'Run generated JavaScript in a secure disconnected Yjs apply environment. The code must return a SecureApplyResult with a proposed Yjs update.', + input: secureApplyToolInputSchema, + output: secureApplyResultSchema, + async execute(input, ctx) { + return executor(input, ctx.signal) + }, + }) +} + +export function createFakeSecureApplyExecutor(result: SecureApplyToolResult): SecureApplyExecutor { + return async () => result + } diff --git a/agents/yjs-markdown-apply-agent/src/tools.ts b/agents/yjs-markdown-apply-agent/src/tools.ts new file mode 100644 index 0000000..3db82df --- /dev/null +++ b/agents/yjs-markdown-apply-agent/src/tools.ts @@ -0,0 +1,7 @@ +import { createSecureApplyTool, type SecureApplyExecutor } from './secure-apply-tool' + +export function createYjsMarkdownApplyTools(executor: SecureApplyExecutor) { + return { + secure_apply_yjs_update: createSecureApplyTool(executor), + } +} diff --git a/agents/yjs-markdown-apply-agent/src/types.ts b/agents/yjs-markdown-apply-agent/src/types.ts new file mode 100644 index 0000000..f40c9d7 --- /dev/null +++ b/agents/yjs-markdown-apply-agent/src/types.ts @@ -0,0 +1,44 @@ +export type SecureApplyInput = { + path: string + oldString: string + newString: string + currentMarkdown: string + currentXml: string + snapshotBase64: string + baseStateVectorBase64: string + artifactFragmentName: string + baseArtifactRevision: number + systemInformation?: string +} + +export type SecureApplyResult = { + ok: boolean + proposedUpdateBase64?: string + beforeMarkdown?: string + afterMarkdown?: string + beforeXml?: string + afterXml?: string + changedArtifacts?: string[] + validationErrors?: string[] + operationLog?: string[] +} + +export type ApplyRetryPolicy = { + maxAttempts: number + initialBackoffMs: number + maxBackoffMs: number + waitForQuietMs: number + maxWaitForQuietMs: number +} + +export type ApplyAttemptAbort = { + reason: 'target-fragment-changed' + path: string + previousRevision: number + currentRevision: number + changeSummary: string[] + updatedMarkdown: string + updatedXml: string + snapshotBase64: string + baseStateVectorBase64: string +} diff --git a/agents/yjs-markdown-apply-agent/test/learning/README.md b/agents/yjs-markdown-apply-agent/test/learning/README.md new file mode 100644 index 0000000..459ca8a --- /dev/null +++ b/agents/yjs-markdown-apply-agent/test/learning/README.md @@ -0,0 +1,63 @@ +# QuickJS Y.Xml Proxy Learning Harness + +This directory is for learning the thinnest possible bridge between generated code and a live `Y.XmlFragment`. + +The current direction is direct mutation through host bindings: + +1. The edit tool receives `path`, `oldString`, and `newString`. +2. The host deterministically resolves `path` to the target `Y.XmlFragment`. +3. Generated code runs in an isolated JS runtime. +4. Bindings proxy operations onto the already-selected fragment. +5. Binding calls mutate the live Yjs fragment; returned strings/objects are diagnostics only. + +## Harness + +Use `withQuickJsMode` or `withAsyncQuickJsMode` from `@humanlayer/quickjs-exec` to avoid repeating QuickJS setup/teardown. The helpers are intentionally unopinionated; pass Yjs-specific behavior as bindings from the learning test: + +```ts +const doc = new Y.Doc() +const fragment = doc.getXmlFragment('learning') +fragment.push([yParagraph('Hello')]) + +await withQuickJsMode( + { + add: (a, b) => Number(a) + Number(b), + fragmentToJSON: () => fragment.toJSON(), + }, + ({ run }) => { + const result = run(` + ({ + sum: bindings.add(20, 22), + xml: bindings.fragmentToJSON(), + }) + `) + + expect(fragment.toJSON()).toBe('Hello') + }, +) +``` + +`bindings` registers host functions. In QuickJS code, call them through the injected `bindings` facade: + +```js +bindings.bindingName(arg1, arg2) +``` + +The facade JSON-serializes host results under the hood so the test code can stay close to the eventual Secure Exec code. `withAsyncQuickJsMode` has the same shape but uses QuickJS asyncified host functions, so bindings may be `async`. + +## Incremental Binding Plan + +Build one capability per test, in this order: + +1. Read-only fragment diagnostics: `fragment.toJSON`, `fragment.length`. +2. Top-level insertion: `fragment.push` with simple element specs. +3. Recursive content specs: nested element/text creation. +4. Refs: `fragment.get` returns a structured-cloneable `XmlRef`. +5. Element read methods: `element.nodeName`, `element.length`, `element.get`. +6. Deep traversal: repeated `element.get` over multiple levels. +7. Text read/write: `text.toString`, `text.insert`, `text.delete`. +8. `insertAfter` with refs returned by `get`. +9. Element attributes: `getAttributes`, `setAttribute`, `removeAttribute`. +10. Error behavior: invalid ref/kind/index errors come back to the generated-code caller. + +Keep the bridge thin. Do not add validation, schema checks, snapshot/commit flow, or replacement-string semantics while these learning tests are being built. diff --git a/agents/yjs-markdown-apply-agent/test/learning/proxy.ts b/agents/yjs-markdown-apply-agent/test/learning/proxy.ts new file mode 100644 index 0000000..128da71 --- /dev/null +++ b/agents/yjs-markdown-apply-agent/test/learning/proxy.ts @@ -0,0 +1,211 @@ +import * as Y from 'yjs' +import { createNodeDriver, NodeExecutionDriver } from 'secure-exec' +import type { BindingTree } from 'secure-exec' + +type XmlRef = { + id: string + kind: 'element' | 'text' +} + +type XmlContentSpec = + | { + kind: 'element' + nodeName: string + attributes?: Record + children?: XmlContentSpec[] + } + | { + kind: 'text' + text?: string + delta?: Array<{ insert: string; attributes?: Record }> + } + +type XmlNode = Y.XmlElement | Y.XmlText + +class XmlRefRegistry { + private nextId = 1 + private readonly byId = new Map() + private readonly byObject = new WeakMap() + + register(node: XmlNode): XmlRef { + const existing = this.byObject.get(node) + if (existing) return existing + + const ref: XmlRef = { + id: `xml_${this.nextId++}`, + kind: node instanceof Y.XmlText ? 'text' : 'element', + } + + this.byId.set(ref.id, node) + this.byObject.set(node, ref) + return ref + } + + resolve(ref: XmlRef): XmlNode { + const node = this.byId.get(ref.id) + if (!node) throw new Error(`Unknown XML ref: ${ref.id}`) + return node + } + + resolveElement(ref: XmlRef): Y.XmlElement { + if (ref.kind !== 'element') throw new Error(`Expected element ref, got ${ref.kind}`) + const node = this.resolve(ref) + if (!(node instanceof Y.XmlElement)) throw new Error(`XML ref ${ref.id} does not resolve to Y.XmlElement`) + return node + } + + resolveText(ref: XmlRef): Y.XmlText { + if (ref.kind !== 'text') throw new Error(`Expected text ref, got ${ref.kind}`) + const node = this.resolve(ref) + if (!(node instanceof Y.XmlText)) throw new Error(`XML ref ${ref.id} does not resolve to Y.XmlText`) + return node + } +} + +function createXmlNode(spec: XmlContentSpec): XmlNode { + if (spec.kind === 'text') { + const text = new Y.XmlText() + if (spec.delta) { + text.applyDelta(spec.delta) + } else if (spec.text) { + text.insert(0, spec.text) + } + return text + } + + const element = new Y.XmlElement(spec.nodeName) + for (const [name, value] of Object.entries(spec.attributes ?? {})) { + element.setAttribute(name, value) + } + if (spec.children?.length) { + const children = spec.children.map(createXmlNode) + element.insertAfter(null, children) + } + return element +} + +function createXmlNodes(specs: XmlContentSpec[]): XmlNode[] { + return specs.map(createXmlNode) +} + +function maybeRef(node: XmlNode | null | undefined, registry: XmlRefRegistry): XmlRef | null | undefined { + if (node === null) return null + if (node === undefined) return undefined + return registry.register(node) +} + +export function createYXmlBindings(fragment: Y.XmlFragment): BindingTree { + const registry = new XmlRefRegistry() + + return { + fragment: { + firstChild: () => maybeRef(fragment.firstChild, registry), + length: () => fragment.length, + insert: (index, content) => { + const nodes = createXmlNodes(content as XmlContentSpec[]) + fragment.insert(index as number, nodes) + return nodes.map((node) => registry.register(node)) + }, + insertAfter: (ref, content) => { + const after = ref === null ? null : registry.resolve(ref as XmlRef) + const nodes = createXmlNodes(content as XmlContentSpec[]) + fragment.insertAfter(after, nodes) + return nodes.map((node) => registry.register(node)) + }, + delete: (index, length) => fragment.delete(index as number, length as number), + push: (content) => { + const nodes = createXmlNodes(content as XmlContentSpec[]) + fragment.push(nodes) + return nodes.map((node) => registry.register(node)) + }, + unshift: (content) => { + const nodes = createXmlNodes(content as XmlContentSpec[]) + fragment.unshift(nodes) + return nodes.map((node) => registry.register(node)) + }, + get: (index) => maybeRef(fragment.get(index as number), registry), + slice: (start, end) => + fragment.slice(start as number | undefined, end as number | undefined).map((node) => registry.register(node)), + toJSON: () => fragment.toJSON(), + }, + element: { + firstChild: (ref) => maybeRef(registry.resolveElement(ref as XmlRef).firstChild, registry), + length: (ref) => registry.resolveElement(ref as XmlRef).length, + insert: (ref, index, content) => { + const parent = registry.resolveElement(ref as XmlRef) + const nodes = createXmlNodes(content as XmlContentSpec[]) + parent.insert(index as number, nodes) + return nodes.map((node) => registry.register(node)) + }, + insertAfter: (ref, afterRef, content) => { + const parent = registry.resolveElement(ref as XmlRef) + const after = afterRef === null ? null : registry.resolve(afterRef as XmlRef) + const nodes = createXmlNodes(content as XmlContentSpec[]) + parent.insertAfter(after, nodes) + return nodes.map((node) => registry.register(node)) + }, + delete: (ref, index, length) => registry.resolveElement(ref as XmlRef).delete(index as number, length as number), + push: (ref, content) => { + const parent = registry.resolveElement(ref as XmlRef) + const nodes = createXmlNodes(content as XmlContentSpec[]) + parent.push(nodes) + return nodes.map((node) => registry.register(node)) + }, + unshift: (ref, content) => { + const parent = registry.resolveElement(ref as XmlRef) + const nodes = createXmlNodes(content as XmlContentSpec[]) + parent.unshift(nodes) + return nodes.map((node) => registry.register(node)) + }, + get: (ref, index) => maybeRef(registry.resolveElement(ref as XmlRef).get(index as number), registry), + slice: (ref, start, end) => + registry + .resolveElement(ref as XmlRef) + .slice(start as number | undefined, end as number | undefined) + .map((node) => registry.register(node)), + nodeName: (ref: unknown) => registry.resolveElement(ref as XmlRef).nodeName, + prevSibling: (ref: unknown) => maybeRef(registry.resolveElement(ref as XmlRef).prevSibling, registry), + nextSibling: (ref: unknown) => maybeRef(registry.resolveElement(ref as XmlRef).nextSibling, registry), + toString: (ref: unknown) => registry.resolveElement(ref as XmlRef).toString(), + getAttribute: (ref, name) => registry.resolveElement(ref as XmlRef).getAttribute(name as string), + getAttributes: (ref) => registry.resolveElement(ref as XmlRef).getAttributes(), + setAttribute: (ref, name, value) => registry.resolveElement(ref as XmlRef).setAttribute(name as string, value as string), + removeAttribute: (ref, name) => registry.resolveElement(ref as XmlRef).removeAttribute(name as string), + }, + text: { + prevSibling: (ref: unknown) => maybeRef(registry.resolveText(ref as XmlRef).prevSibling, registry), + nextSibling: (ref: unknown) => maybeRef(registry.resolveText(ref as XmlRef).nextSibling, registry), + toString: (ref: unknown) => registry.resolveText(ref as XmlRef).toString(), + toJSON: (ref: unknown) => registry.resolveText(ref as XmlRef).toJSON(), + toDelta: (ref: unknown) => registry.resolveText(ref as XmlRef).toDelta(), + insert: (ref, index, text, attrs) => + registry.resolveText(ref as XmlRef).insert(index as number, text as string, attrs as Record | undefined), + delete: (ref, index, length) => registry.resolveText(ref as XmlRef).delete(index as number, length as number), + format: (ref, index, length, attrs) => + registry.resolveText(ref as XmlRef).format(index as number, length as number, attrs as Record), + }, + } +} + +export async function withCodeMode( + fragment: Y.XmlFragment, + bindings: BindingTree, + cb: (driver: NodeExecutionDriver) => Promise, +) { + const system = createNodeDriver() + + const driver = new NodeExecutionDriver({ + system, + runtime: system.runtime, + bindings: { + ...createYXmlBindings(fragment), + ...bindings, + }, + }) + + try { + await cb(driver) + } finally { + driver.dispose() + } +} diff --git a/agents/yjs-markdown-apply-agent/test/learning/quickjs-harness.test.ts b/agents/yjs-markdown-apply-agent/test/learning/quickjs-harness.test.ts new file mode 100644 index 0000000..4e9f7a8 --- /dev/null +++ b/agents/yjs-markdown-apply-agent/test/learning/quickjs-harness.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, test } from 'bun:test' +import { withAsyncQuickJsMode, withQuickJsMode } from '@humanlayer/quickjs-exec' +import * as Y from 'yjs' + +describe('quickjs yxml harness', () => { + test('sync harness mirrors withCodeMode setup shape', async () => { + await withQuickJsMode( + { + add: (a, b) => (a as number) + (b as number), + }, + ({ run }) => { + const result = run<{ sum: number }>(` + ({ + sum: bindings.add(20, 22), + }) + `) + + expect(result).toEqual({ sum: 42 }) + }, + ) + }) + + test('script with no final expression does not throw and returns undefined', async () => { + await withQuickJsMode({}, ({ run }) => { + const result = run(` + const x = 2 + `) + + expect(result).toBeUndefined() + }) + }) + + test('async harness supports async host bindings', async () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('quickjs-async') + + await withAsyncQuickJsMode( + { + addAsync: async (a, b) => (a as number) + (b as number), + fragmentLength: () => fragment.length, + }, + async ({ run }) => { + const result = await run<{ sum: number; length: number }>(` + ({ + sum: bindings.addAsync(20, 22), + length: bindings.fragmentLength(), + }) + `) + + expect(result).toEqual({ sum: 42, length: 0 }) + }, + ) + }) +}) diff --git a/agents/yjs-markdown-apply-agent/test/learning/yjs-secure-exec.test.ts b/agents/yjs-markdown-apply-agent/test/learning/yjs-secure-exec.test.ts new file mode 100644 index 0000000..d64aac8 --- /dev/null +++ b/agents/yjs-markdown-apply-agent/test/learning/yjs-secure-exec.test.ts @@ -0,0 +1,158 @@ +import { describe, expect, test } from 'bun:test' +import * as Y from 'yjs' +import { withCodeMode } from './proxy' +import { NodeExecutionDriver, type BindingTree } from 'secure-exec' + +describe('Secure-exec <> Y.js Learning tests', async () => { + test('Y.XMLFragment can be built normally', async () => { + const yDoc = new Y.Doc() + const yXmlFragment = yDoc.getXmlFragment('test-fragment') + + const yXmlElement = new Y.XmlElement('example-node') + yXmlFragment.insert(0, [yXmlElement]) + + expect(yXmlFragment.toJSON()).toEqual('') + }) + + test('Structured Clone-ability', async () => { + const yDoc = new Y.Doc() + const yXmlFragment = yDoc.getXmlFragment('test-fragment') + + const yXmlElement = new Y.XmlElement('example-node') + yXmlFragment.insert(0, [yXmlElement]) + + expect(yXmlFragment.toJSON()).toEqual('') + expect(yXmlFragment.get(0)).toEqual(yXmlElement) + expect(yXmlFragment.get(1)).toBeUndefined() + + expect(() => structuredClone(yXmlFragment)).toThrowError() + expect(() => structuredClone(yXmlElement)).toThrowError() + }) + + test('Secure-Exec Driver exposes basic custom bindings', async () => { + const yDoc = new Y.Doc() + const yXmlFragment = yDoc.getXmlFragment() + const bindings: BindingTree = { + add: (a, b) => (a as number) + (b as number), + } + + await withCodeMode(yXmlFragment, bindings, async (driver: NodeExecutionDriver) => { + const result = await driver.run(`module.exports = {a: SecureExec.bindings.add(1,1)}`) + expect(result.exports).toHaveProperty('a') + expect((result.exports as { a: number }).a).toEqual(2) + }) + }) + + test('fragment bindings can push nested content into the live fragment', async () => { + const yDoc = new Y.Doc() + const yXmlFragment = yDoc.getXmlFragment() + + await withCodeMode(yXmlFragment, {}, async (driver) => { + const result = await driver.run(` + const { fragment } = SecureExec.bindings + const refs = fragment.push([ + { + kind: 'element', + nodeName: 'section', + attributes: { id: 'intro' }, + children: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Hello' }], + }, + ], + }, + ]) + + module.exports = { + refs, + length: fragment.length(), + xml: fragment.toJSON(), + } + `) + + expect(result.code).toBe(0) + expect(result.exports).toEqual({ + refs: [{ id: 'xml_1', kind: 'element' }], + length: 1, + xml: '
Hello
', + }) + }) + + expect(yXmlFragment.toJSON()).toBe('
Hello
') + }) + + test('element bindings can mutate multiple levels deep', async () => { + const yDoc = new Y.Doc() + const yXmlFragment = yDoc.getXmlFragment() + const section = new Y.XmlElement('section') + const paragraph = new Y.XmlElement('paragraph') + const text = new Y.XmlText() + text.insert(0, 'Hello') + paragraph.push([text]) + section.push([paragraph]) + yXmlFragment.push([section]) + + await withCodeMode(yXmlFragment, {}, async (driver) => { + const result = await driver.run(` + const { fragment, element, text } = SecureExec.bindings + + const section = fragment.get(0) + const paragraph = element.get(section, 0) + const paragraphText = element.get(paragraph, 0) + + text.insert(paragraphText, 5, ' world') + element.setAttribute(section, 'status', 'updated') + + module.exports = { + sectionName: element.nodeName(section), + text: text.toString(paragraphText), + xml: fragment.toJSON(), + } + `) + + expect(result.code).toBe(0) + expect(result.exports).toEqual({ + sectionName: 'section', + text: 'Hello world', + xml: '
Hello world
', + }) + }) + + expect(yXmlFragment.toJSON()).toBe('
Hello world
') + }) + + test('insertAfter works with refs returned from get at nested levels', async () => { + const yDoc = new Y.Doc() + const yXmlFragment = yDoc.getXmlFragment() + const section = new Y.XmlElement('section') + section.push([new Y.XmlElement('first')]) + yXmlFragment.push([section]) + + await withCodeMode(yXmlFragment, {}, async (driver) => { + const result = await driver.run(` + const { fragment, element } = SecureExec.bindings + + const section = fragment.get(0) + const first = element.get(section, 0) + const inserted = element.insertAfter(section, first, [ + { kind: 'element', nodeName: 'second', children: [{ kind: 'text', text: 'two' }] }, + ]) + + module.exports = { + inserted, + xml: fragment.toJSON(), + } + `) + + expect(result.code).toBe(0) + expect(result.exports).toEqual({ + inserted: [{ id: 'xml_3', kind: 'element' }], + xml: '
two
', + }) + }) + + expect(yXmlFragment.toJSON()).toBe('
two
') + }) +}) diff --git a/agents/yjs-markdown-apply-agent/test/prompt-cache-shape.test.ts b/agents/yjs-markdown-apply-agent/test/prompt-cache-shape.test.ts new file mode 100644 index 0000000..4fec4b7 --- /dev/null +++ b/agents/yjs-markdown-apply-agent/test/prompt-cache-shape.test.ts @@ -0,0 +1,64 @@ +import { describe, expect, test } from 'bun:test' +import { APPLY_AGENT_SYSTEM_PROMPT, buildAbortSystemInformation, buildApplyUserMessage } from '../src/prompts' +import type { ApplyAttemptAbort, SecureApplyInput } from '../src/types' + +const baseInput: SecureApplyInput = { + path: '/artifacts/plan.md', + oldString: 'old', + newString: 'new', + currentMarkdown: '# Old', + currentXml: 'Old', + snapshotBase64: 'snapshot-1', + baseStateVectorBase64: 'state-vector-1', + artifactFragmentName: 'artifact:/artifacts/plan.md', + baseArtifactRevision: 1, +} + +describe('apply prompt cache shape', () => { + test('keeps stable system prompt separate from dynamic document state', () => { + const first = buildApplyUserMessage(baseInput) + const second = buildApplyUserMessage({ + ...baseInput, + currentMarkdown: '# Newer', + currentXml: 'Newer', + snapshotBase64: 'snapshot-2', + baseStateVectorBase64: 'state-vector-2', + baseArtifactRevision: 2, + }) + + expect(APPLY_AGENT_SYSTEM_PROMPT).toContain('Stable contract') + expect(APPLY_AGENT_SYSTEM_PROMPT).not.toContain('snapshot-1') + expect(first).toContain('snapshot-1') + expect(second).toContain('snapshot-2') + expect(first).not.toEqual(second) + }) + + test('adds system-information to retry user message', () => { + const abort: ApplyAttemptAbort = { + reason: 'target-fragment-changed', + path: '/artifacts/plan.md', + previousRevision: 1, + currentRevision: 2, + changeSummary: ['external insert'], + updatedMarkdown: '# External', + updatedXml: 'External', + snapshotBase64: 'snapshot-2', + baseStateVectorBase64: 'state-vector-2', + } + + const systemInformation = buildAbortSystemInformation(abort) + const retry = buildApplyUserMessage({ + ...baseInput, + systemInformation, + currentMarkdown: abort.updatedMarkdown, + currentXml: abort.updatedXml, + snapshotBase64: abort.snapshotBase64, + baseStateVectorBase64: abort.baseStateVectorBase64, + baseArtifactRevision: abort.currentRevision, + }) + + expect(retry).toContain('') + expect(retry).toContain('previous secure apply attempt was aborted') + expect(retry).toContain('snapshot-2') + }) +}) diff --git a/agents/yjs-markdown-apply-agent/test/retry-controller.test.ts b/agents/yjs-markdown-apply-agent/test/retry-controller.test.ts new file mode 100644 index 0000000..ccc5beb --- /dev/null +++ b/agents/yjs-markdown-apply-agent/test/retry-controller.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, test } from 'bun:test' +import { nextRetryDecision } from '../src/retry-controller' + +describe('apply retry controller', () => { + test('backs off with a quiet-period floor', () => { + expect( + nextRetryDecision({ + attempt: 1, + policy: { initialBackoffMs: 100, waitForQuietMs: 750, maxBackoffMs: 5000 }, + }), + ).toEqual({ type: 'retry', attempt: 2, delayMs: 750 }) + }) + + test('caps retry delay', () => { + expect( + nextRetryDecision({ + attempt: 5, + policy: { maxAttempts: 10, initialBackoffMs: 1000, waitForQuietMs: 100, maxBackoffMs: 1500 }, + }), + ).toEqual({ type: 'retry', attempt: 6, delayMs: 1500 }) + }) + + test('fails after retry budget is exhausted', () => { + expect(nextRetryDecision({ attempt: 3, policy: { maxAttempts: 3 } })).toEqual({ + type: 'fail', + reason: 'max-attempts-exceeded', + }) + }) + + test('fails after quiet wait budget is exhausted', () => { + expect(nextRetryDecision({ attempt: 1, waitedForQuietMs: 11_000, policy: { maxWaitForQuietMs: 10_000 } })).toEqual({ + type: 'fail', + reason: 'quiet-timeout-exceeded', + }) + }) +}) diff --git a/agents/yjs-markdown-apply-agent/test/secure-apply-contract.test.ts b/agents/yjs-markdown-apply-agent/test/secure-apply-contract.test.ts new file mode 100644 index 0000000..bcc7cbc --- /dev/null +++ b/agents/yjs-markdown-apply-agent/test/secure-apply-contract.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, test } from 'bun:test' +import type { LanguageModel } from 'ai' +import { createYjsMarkdownApplyAgent } from '../src/agent' +import { createFakeSecureApplyExecutor, secureApplyResultSchema } from '../src/secure-apply-tool' + +function createMockModel(): LanguageModel { + return { + specificationVersion: 'v3', + provider: 'mock', + modelId: 'mock-apply-model', + supportedUrls: {}, + async doGenerate() { + return { + content: [{ type: 'text', text: 'ok' }], + finishReason: { unified: 'stop', raw: 'stop' }, + usage: { + inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 }, + outputTokens: { total: 0, text: 0, reasoning: 0 }, + }, + warnings: [], + } + }, + async doStream() { + throw new Error('streaming not supported in test') + }, + } as LanguageModel +} + +describe('secure apply contract', () => { + test('validates secure apply result shape', () => { + const result = secureApplyResultSchema.parse({ + ok: true, + proposedUpdateBase64: 'update', + changedArtifacts: ['/artifacts/plan.md'], + }) + + expect(result.ok).toBe(true) + }) + + test('creates an agentlayer-core agent with the secure apply tool', () => { + const agent = createYjsMarkdownApplyAgent({ + model: createMockModel(), + executor: createFakeSecureApplyExecutor({ ok: true, proposedUpdateBase64: 'update' }), + }) as unknown as { tools?: Record; system?: string } + + expect(agent.tools?.secure_apply_yjs_update).toBeDefined() + expect(agent.system).toContain('Yjs Markdown apply agent') + }) +}) diff --git a/agents/yjs-markdown-apply-agent/tsconfig.json b/agents/yjs-markdown-apply-agent/tsconfig.json new file mode 100644 index 0000000..7cb9983 --- /dev/null +++ b/agents/yjs-markdown-apply-agent/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + "types": ["bun"], + "moduleResolution": "bundler", + "customConditions": ["source"], + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/bun.lock b/bun.lock index 426e927..583c216 100644 --- a/bun.lock +++ b/bun.lock @@ -82,6 +82,7 @@ "devDependencies": { "@ai-sdk/provider": "catalog:", "@humanlayer/agentlayer-core": "workspace:*", + "@humanlayer/quickjs-exec": "workspace:*", "@tanstack/ai": "0.14.0", "@tanstack/ai-code-mode": "0.1.8", "@tanstack/ai-isolate-quickjs": "0.1.8", @@ -248,6 +249,19 @@ "typescript": "^5", }, }, + "packages/quickjs-exec": { + "name": "@humanlayer/quickjs-exec", + "version": "0.0.0", + "dependencies": { + "quickjs-emscripten": "^0.31.0", + }, + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, "packages/yjs-fs": { "name": "@humanlayer/yjs-fs", "version": "0.0.0", @@ -595,6 +609,8 @@ "@humanlayer/docs-agent": ["@humanlayer/docs-agent@workspace:agents/docs-agent"], + "@humanlayer/quickjs-exec": ["@humanlayer/quickjs-exec@workspace:packages/quickjs-exec"], + "@humanlayer/yjs-fs": ["@humanlayer/yjs-fs@workspace:packages/yjs-fs"], "@humanlayer/yjs-fs-agents": ["@humanlayer/yjs-fs-agents@workspace:agents/yjs-fs-agents"], @@ -2429,6 +2445,8 @@ "@humanlayer/codelayer/@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], + "@humanlayer/quickjs-exec/@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], + "@humanlayer/yjs-fs/@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], "@humanlayer/yjs-fs-editor/@durable-streams/server": ["@durable-streams/server@0.1.7", "", { "dependencies": { "@durable-streams/client": "0.2.0", "@durable-streams/state": "0.2.0", "@neophi/sieve-cache": "^1.0.0", "lmdb": "^3.3.0" } }, "sha512-JaCzfKS/GI3ZlbmInHvqX/fAh6XENTjc+acfhRlZH9iaRQiD1rOjfircj+jFDTIHgeT9oPqOVscmISGot8kI3w=="], @@ -2639,6 +2657,8 @@ "@humanlayer/codelayer/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "@humanlayer/quickjs-exec/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "@humanlayer/yjs-fs-editor/@durable-streams/server/@durable-streams/client": ["@durable-streams/client@0.2.0", "", { "dependencies": { "@microsoft/fetch-event-source": "^2.0.1", "fastq": "^1.19.1" } }, "sha512-nlrbXDjQDqOsFbVFplg0OHkXwy4EAtztARg4zvaTn53WHzMCUwuiIY+V+u3AGvUWA9GJnnjRrbPKIXGkvak1CQ=="], "@humanlayer/yjs-fs-editor/@durable-streams/server/@durable-streams/state": ["@durable-streams/state@0.2.0", "", { "dependencies": { "@durable-streams/client": "0.2.0", "@standard-schema/spec": "^1.0.0" }, "peerDependencies": { "@tanstack/db": ">=0.5.0" } }, "sha512-9XKSKkubcCbbRgUHJCENTw3351uJ4hfi8GLmYWD8l5OCEjvhVegUF06utFKehIRy3xTr57t5MC78weBfVlD2Dg=="], diff --git a/packages/agentlayer-core/package.json b/packages/agentlayer-core/package.json index f777921..88ef629 100644 --- a/packages/agentlayer-core/package.json +++ b/packages/agentlayer-core/package.json @@ -32,36 +32,42 @@ }, "exports": { ".": { + "bun": "./src/index.ts", "source": "./src/index.ts", "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" }, "./prompts": { + "bun": "./src/prompts/index.ts", "source": "./src/prompts/index.ts", "types": "./dist/prompts/index.d.ts", "import": "./dist/prompts/index.js", "default": "./dist/prompts/index.js" }, "./tools": { + "bun": "./src/tools/index.ts", "source": "./src/tools/index.ts", "types": "./dist/tools/index.d.ts", "import": "./dist/tools/index.js", "default": "./dist/tools/index.js" }, "./hooks": { + "bun": "./src/hooks/index.ts", "source": "./src/hooks/index.ts", "types": "./dist/hooks/index.d.ts", "import": "./dist/hooks/index.js", "default": "./dist/hooks/index.js" }, "./utils": { + "bun": "./src/utils/index.ts", "source": "./src/utils/index.ts", "types": "./dist/utils/index.d.ts", "import": "./dist/utils/index.js", "default": "./dist/utils/index.js" }, "./interfaces": { + "bun": "./src/interfaces/index.ts", "source": "./src/interfaces/index.ts", "types": "./dist/interfaces/index.d.ts", "import": "./dist/interfaces/index.js", diff --git a/packages/quickjs-exec/build.ts b/packages/quickjs-exec/build.ts new file mode 100644 index 0000000..c7c718f --- /dev/null +++ b/packages/quickjs-exec/build.ts @@ -0,0 +1,3 @@ +import { buildPackageFromManifest } from '../../scripts/build/package-build.ts' + +await buildPackageFromManifest(import.meta.dir) diff --git a/packages/quickjs-exec/package.json b/packages/quickjs-exec/package.json new file mode 100644 index 0000000..9bac024 --- /dev/null +++ b/packages/quickjs-exec/package.json @@ -0,0 +1,45 @@ +{ + "name": "@humanlayer/quickjs-exec", + "source": "./src/index.ts", + "type": "module", + "version": "0.0.0", + "private": false, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "publishConfig": { + "access": "public" + }, + "files": [ + "src", + "dist" + ], + "exports": { + ".": { + "bun": "./src/index.ts", + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "scripts": { + "typecheck": "tsgo --noEmit", + "build": "bun run build:js && bunx tsc -p tsconfig.build.json", + "build:js": "bun run build.ts", + "build:types": "bunx tsc -p tsconfig.build.json" + }, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + }, + "dependencies": { + "quickjs-emscripten": "^0.31.0" + }, + "author": { + "name": "Kyle Mistele", + "url": "https://x.com/0xblacklight" + } +} diff --git a/packages/quickjs-exec/src/index.ts b/packages/quickjs-exec/src/index.ts new file mode 100644 index 0000000..db116ca --- /dev/null +++ b/packages/quickjs-exec/src/index.ts @@ -0,0 +1,130 @@ +import { getQuickJS, newAsyncContext, type QuickJSContext, type QuickJSHandle } from 'quickjs-emscripten' + +export type QuickJsBinding = (...args: unknown[]) => unknown | Promise +export type QuickJsBindings = Record + +export type QuickJsExecutionMode = 'sync' | 'async' + +export type QuickJsExec = { + readonly mode: QuickJsExecutionMode + run(code: string): T | Promise + dispose(): void +} + +export type QuickJsSyncExec = QuickJsExec & { + readonly mode: 'sync' + run(code: string): T +} + +export type QuickJsAsyncExec = QuickJsExec & { + readonly mode: 'async' + run(code: string): Promise +} + +export type WithQuickJsModeCallback = (exec: QuickJsSyncExec) => T | Promise +export type WithAsyncQuickJsModeCallback = (exec: QuickJsAsyncExec) => T | Promise + +export async function withQuickJsMode(bindings: QuickJsBindings, cb: WithQuickJsModeCallback): Promise { + const QuickJS = await getQuickJS() + const vm = QuickJS.newContext() + const disposableHandles: QuickJSHandle[] = [] + + try { + installSyncBindings({ vm, bindings, disposableHandles }) + return await cb({ + mode: 'sync', + run: (code) => runSync(vm, code) as never, + dispose: () => vm.dispose(), + }) + } finally { + disposeHandles(disposableHandles) + vm.dispose() + } +} + +export async function withAsyncQuickJsMode(bindings: QuickJsBindings, cb: WithAsyncQuickJsModeCallback): Promise { + const context = await newAsyncContext() + const disposableHandles: QuickJSHandle[] = [] + + try { + installAsyncifiedBindings({ context, bindings, disposableHandles }) + return await cb({ + mode: 'async', + run: (code) => runAsync(context, code) as never, + dispose: () => context.dispose(), + }) + } finally { + disposeHandles(disposableHandles) + context.dispose() + } +} + +type TypedUnknown = unknown + +function runSync(vm: QuickJSContext, code: string): T { + const result = vm.evalCode(code) + const value = vm.unwrapResult(result) + try { + return vm.dump(value) as T + } finally { + value.dispose() + } +} + +async function runAsync(context: Awaited>, code: string): Promise { + const result = await context.evalCodeAsync(code) + const value = context.unwrapResult(result) + try { + return context.dump(value) as T + } finally { + value.dispose() + } +} + +function installSyncBindings(input: { vm: QuickJSContext; bindings: QuickJsBindings; disposableHandles: QuickJSHandle[] }): void { + for (const [name, fn] of Object.entries(input.bindings)) { + const handle = input.vm.newFunction(name, (...args) => { + const output = fn(...args.map((arg) => input.vm.dump(arg))) + if (output instanceof Promise) throw new Error(`Async binding ${name} used with withQuickJsMode`) + return input.vm.newString(JSON.stringify(output ?? null)) + }) + input.vm.setProp(input.vm.global, name, handle) + input.disposableHandles.push(handle) + } + installBindingFacade(input.vm, input.disposableHandles) +} + +function installAsyncifiedBindings(input: { + context: Awaited> + bindings: QuickJsBindings + disposableHandles: QuickJSHandle[] +}): void { + for (const [name, fn] of Object.entries(input.bindings)) { + const handle = input.context.newAsyncifiedFunction(name, async (...args) => { + const output = await fn(...args.map((arg) => input.context.dump(arg))) + return input.context.newString(JSON.stringify(output ?? null)) + }) + input.context.setProp(input.context.global, name, handle) + input.disposableHandles.push(handle) + } + installBindingFacade(input.context, input.disposableHandles) +} + +function installBindingFacade(vm: QuickJSContext, disposableHandles: QuickJSHandle[]): void { + const result = vm.evalCode(` + globalThis.bindings = new Proxy({}, { + get(_target, prop) { + return (...args) => { + const raw = globalThis[prop](...args) + return typeof raw === 'string' ? JSON.parse(raw) : raw + } + } + }) + `) + const value = vm.unwrapResult(result) + disposableHandles.push(value) +} + +function disposeHandles(handles: QuickJSHandle[]): void { + for (const handle of handles) handle.dispose() +} diff --git a/packages/quickjs-exec/test/quickjs-exec.test.ts b/packages/quickjs-exec/test/quickjs-exec.test.ts new file mode 100644 index 0000000..6ee5058 --- /dev/null +++ b/packages/quickjs-exec/test/quickjs-exec.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, test } from 'bun:test' +import { withAsyncQuickJsMode, withQuickJsMode } from '../src' + +describe('quickjs-exec', () => { + test('runs sync code with sync host bindings', async () => { + await withQuickJsMode( + { + add: (a, b) => (a as number) + (b as number), + }, + ({ run }) => { + const result = run<{ sum: number }>(` + ({ sum: bindings.add(20, 22) }) + `) + + expect(result).toEqual({ sum: 42 }) + }, + ) + }) + + test('script with no final expression does not throw and returns undefined', async () => { + await withQuickJsMode({}, ({ run }) => { + const result = run(`const x = 2`) + + expect(result).toBeUndefined() + }) + }) + + test('runs asyncified host bindings', async () => { + await withAsyncQuickJsMode( + { + addAsync: async (a, b) => (a as number) + (b as number), + }, + async ({ run }) => { + const result = await run<{ sum: number }>(` + ({ sum: bindings.addAsync(20, 22) }) + `) + + expect(result).toEqual({ sum: 42 }) + }, + ) + }) + + test('sync harness rejects async binding functions', async () => { + await expect( + withQuickJsMode( + { + asyncBinding: async () => 42, + }, + ({ run }) => run(`bindings.asyncBinding()`), + ), + ).rejects.toThrow('Async binding asyncBinding used with withQuickJsMode') + }) +}) diff --git a/packages/quickjs-exec/tsconfig.build.json b/packages/quickjs-exec/tsconfig.build.json new file mode 100644 index 0000000..e043bc7 --- /dev/null +++ b/packages/quickjs-exec/tsconfig.build.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src"], + "exclude": [ + "test", + "dist", + "node_modules", + "**/*.test.*", + "**/*.bench.*", + "**/*.test-d.ts", + "**/__tests__/**", + "**/__mocks__/**", + "**/__snapshots__/**" + ] +} diff --git a/packages/quickjs-exec/tsconfig.json b/packages/quickjs-exec/tsconfig.json new file mode 100644 index 0000000..fe64dab --- /dev/null +++ b/packages/quickjs-exec/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "allowJs": true, + "types": ["bun"], + "moduleResolution": "bundler", + "customConditions": ["source"], + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +} diff --git a/scripts/release/manifest.ts b/scripts/release/manifest.ts index 2c2148d..0640467 100644 --- a/scripts/release/manifest.ts +++ b/scripts/release/manifest.ts @@ -33,6 +33,10 @@ export const publishablePackages = [ name: "@humanlayer/agentlayer-provider-openai-codex", dir: "packages/agentlayer-provider-openai-codex", }, + { + name: "@humanlayer/quickjs-exec", + dir: "packages/quickjs-exec", + }, { name: "@humanlayer/yjs-fs", dir: "packages/yjs-fs", From 202004565d6d818e653b389df220f3e7f1824d2e Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 2 May 2026 20:44:37 -0700 Subject: [PATCH 08/18] feat: add yxml proxy bindings --- packages/yjs-rich-markdown/src/index.ts | 3 + .../src/yxml-proxy-prompt.ts | 222 ++++++++++++++++ packages/yjs-rich-markdown/src/yxml-proxy.ts | 202 +++++++++++++++ .../test/markdown-yxml-shapes.test.ts | 68 +++++ .../yjs-rich-markdown/test/yxml-proxy.test.ts | 236 ++++++++++++++++++ 5 files changed, 731 insertions(+) create mode 100644 packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts create mode 100644 packages/yjs-rich-markdown/src/yxml-proxy.ts create mode 100644 packages/yjs-rich-markdown/test/markdown-yxml-shapes.test.ts create mode 100644 packages/yjs-rich-markdown/test/yxml-proxy.test.ts diff --git a/packages/yjs-rich-markdown/src/index.ts b/packages/yjs-rich-markdown/src/index.ts index 512c9f6..ddbeb0f 100644 --- a/packages/yjs-rich-markdown/src/index.ts +++ b/packages/yjs-rich-markdown/src/index.ts @@ -6,3 +6,6 @@ export { ArtifactAlreadyExistsError, ArtifactNotFoundError, RichMarkdownArtifact export { markdownToTiptapJson, readArtifactMarkdown, tiptapJsonToMarkdown, writeArtifactMarkdown } from './markdown' export type { ArtifactCollaborationMode, ArtifactCollaborationOptions } from './tiptap-bindings' export { artifactCollaborationExtension, artifactFragment, defaultRichMarkdownExtensions } from './tiptap-bindings' +export type { YXmlNodeKind, YXmlNodeRef, YXmlNodeSpec, YXmlNodeSummary, YXmlProxy } from './yxml-proxy' +export { YXmlProxyBindings } from './yxml-proxy' +export { YXML_PROXY_AGENT_PROMPT } from './yxml-proxy-prompt' diff --git a/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts b/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts new file mode 100644 index 0000000..59e0a90 --- /dev/null +++ b/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts @@ -0,0 +1,222 @@ +import dedent from 'dedent' + +export const YXML_PROXY_AGENT_PROMPT = dedent` + + You are editing one rich markdown artifact through a scoped Y.XmlFragment DSL. + + The artifact is a rich-text TipTap/ProseMirror markdown document, modeled in Tiptap/ProseMirror's XML DSL for markdown. + + The DSL is available with a \`bindings\` object. Use only these bindings to inspect and mutate the document. Do not import packages, access files, access the network, or assume direct access to Yjs objects. Attempts to do so will fail. + + Node references are opaque JSON values that are only valid for the current execution. + + Available node reference shape: + + \`\`\`ts + type NodeRef = { + id: string + kind: 'fragment' | 'element' | 'text' + } + \`\`\` + + Available content shape: + + \`\`\`ts + type NodeSpec = + | { + kind: 'element' + nodeName: string + attributes?: Record + children?: NodeSpec[] + } + | { + kind: 'text' + text?: string + } + \`\`\` + + Available bindings: + + \`\`\`ts + bindings.root(): NodeRef + bindings.summary({ node: NodeRef }): { id: string; kind: string; nodeName: string | null; length: number; xml: string; attributes?: Record } + bindings.children({ node?: NodeRef }): NodeRef[] + bindings.get({ node?: NodeRef; index: number }): NodeRef + bindings.toString({ node?: NodeRef }): string + bindings.append({ parent?: NodeRef; content: NodeSpec[] }): NodeRef[] + bindings.prepend({ parent?: NodeRef; content: NodeSpec[] }): NodeRef[] + bindings.insertBefore({ ref: NodeRef; content: NodeSpec[] }): NodeRef[] + bindings.insertAfter({ ref: NodeRef; content: NodeSpec[] }): NodeRef[] + bindings.remove({ node: NodeRef }): void + bindings.setAttribute({ node: NodeRef; name: string; value: string }): void + bindings.removeAttribute({ node: NodeRef; name: string }): void + \`\`\` + + Important rules: + + - Call \`bindings.toString({})\` to inspect the full current XML before editing. + - Use \`append\` or \`prepend\` when adding content to the root or to a known parent element. + - Use \`insertBefore\` or \`insertAfter\` when adding content relative to an existing node. These do not need a parent; the host infers it from the referenced node. + - Use \`remove\` to erase a node and all of its nested children. + - Use \`setAttribute\` to change an element attribute, such as changing a heading from level 2 to level 3. + - Use \`removeAttribute\` to delete an attribute. + - Do not append children to a text node. Only fragment and element nodes can contain children. + - Build nested content by nesting \`children\` arrays in \`NodeSpec\` objects. + - Prefer structural edits over string replacement of the full XML. + - When creating common markdown structures, use the YXml element names listed below. These are the serialized YXml names produced by TipTap StarterKit and y-prosemirror. + - Return a small JSON object with \`ok\`, \`beforeXml\`, and \`afterXml\`. + + Common markdown YXml elements: + + - Paragraph: \`{ kind: 'element', nodeName: 'paragraph', children: [...] }\` + - Heading: \`{ kind: 'element', nodeName: 'heading', attributes: { level: '1' | '2' | '3' | '4' | '5' | '6' }, children: [...] }\` + - Bold mark: \`{ kind: 'element', nodeName: 'bold', children: [...] }\` + - Italic mark: \`{ kind: 'element', nodeName: 'italic', children: [...] }\` + - Strikethrough mark: \`{ kind: 'element', nodeName: 'strike', children: [...] }\` + - Inline code mark: \`{ kind: 'element', nodeName: 'code', children: [{ kind: 'text', text: 'code' }] }\` + - Link mark: \`{ kind: 'element', nodeName: 'link', attributes: { href: 'https://example.com', target: '_blank', rel: 'noopener noreferrer nofollow', class: 'null', title: 'null' }, children: [...] }\` + - Bullet list: \`{ kind: 'element', nodeName: 'bulletlist', children: [...] }\` + - Ordered list: \`{ kind: 'element', nodeName: 'orderedlist', attributes: { start: '1' }, children: [...] }\` + - List item: \`{ kind: 'element', nodeName: 'listitem', children: [{ kind: 'element', nodeName: 'paragraph', children: [...] }] }\` + - Blockquote: \`{ kind: 'element', nodeName: 'blockquote', children: [{ kind: 'element', nodeName: 'paragraph', children: [...] }] }\` + - Horizontal rule: \`{ kind: 'element', nodeName: 'horizontalrule' }\` + - Hard break inside a paragraph: \`{ kind: 'element', nodeName: 'hardbreak' }\` + - Fenced code block with language: \`{ kind: 'element', nodeName: 'codeblock', attributes: { language: 'ts' }, children: [{ kind: 'text', text: 'const value = 1' }] }\` + - Multi-line code block: use one text child containing newline characters, e.g. \`{ kind: 'text', text: 'const value = 1\\nconsole.log(value)' }\`. + + Other elements may appear in normal usage. If you see these, do your best to follow existing patterns. + + Important casing note: use lowercase multi-word YXml node names when creating nodes manually: \`bulletlist\`, \`listitem\`, \`orderedlist\`, \`codeblock\`, \`hardbreak\`, and \`horizontalrule\`. Do not use TipTap camelCase names like \`bulletList\` or \`codeBlock\` in generated \`NodeSpec\` content. + + Example: add a paragraph after the first top-level node. + + \`\`\`js + const beforeXml = bindings.toString({}) + const first = bindings.get({ index: 0 }) + + bindings.insertAfter({ + ref: first, + content: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Added paragraph.' }], + }, + ], + }) + + return { ok: true, beforeXml, afterXml: bindings.toString({}) } + \`\`\` + + Example: add a new section containing a heading, paragraph, list, blockquote, horizontal rule, and code block. + + \`\`\`js + const beforeXml = bindings.toString({}) + + bindings.append({ + content: [ + { + kind: 'element', + nodeName: 'heading', + attributes: { level: '2' }, + children: [{ kind: 'text', text: 'Implementation Notes' }], + }, + { + kind: 'element', + nodeName: 'paragraph', + children: [ + { kind: 'text', text: 'The agent edits through ' }, + { kind: 'element', nodeName: 'code', children: [{ kind: 'text', text: 'bindings' }] }, + { kind: 'text', text: ' instead of direct Yjs objects.' }, + ], + }, + { + kind: 'element', + nodeName: 'bulletlist', + children: [ + { + kind: 'element', + nodeName: 'listitem', + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Use scoped NodeRefs.' }] }], + }, + { + kind: 'element', + nodeName: 'listitem', + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Validate after mutation.' }] }], + }, + ], + }, + { + kind: 'element', + nodeName: 'blockquote', + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Bindings keep generated code sandbox-safe.' }] }], + }, + { kind: 'element', nodeName: 'horizontalrule' }, + { + kind: 'element', + nodeName: 'codeblock', + attributes: { language: 'ts' }, + children: [{ kind: 'text', text: 'const root = bindings.root()\\nconst xml = bindings.toString({})' }], + }, + ], + }) + + return { ok: true, beforeXml, afterXml: bindings.toString({}) } + \`\`\` + + Example: add a bullet to an existing list. + + \`\`\`js + const beforeXml = bindings.toString({}) + const rootChildren = bindings.children({}) + const list = rootChildren.find((node) => bindings.summary({ node }).nodeName === 'bulletlist') + if (!list) throw new Error('Could not find a bullet list') + + bindings.append({ + parent: list, + content: [ + { + kind: 'element', + nodeName: 'listitem', + children: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'New bullet' }], + }, + ], + }, + ], + }) + + return { ok: true, beforeXml, afterXml: bindings.toString({}) } + \`\`\` + + Example: change a heading level attribute. + + \`\`\`js + const beforeXml = bindings.toString({}) + const heading = bindings.children({}).find((node) => bindings.summary({ node }).nodeName === 'heading') + if (!heading) throw new Error('Could not find a heading') + + bindings.setAttribute({ node: heading, name: 'level', value: '3' }) + + return { ok: true, beforeXml, afterXml: bindings.toString({}) } + \`\`\` + + Example: erase a section. + + \`\`\`js + const beforeXml = bindings.toString({}) + const section = bindings.children({}).find((node) => { + const summary = bindings.summary({ node }) + return summary.nodeName === 'section' && summary.attributes?.id === 'obsolete' + }) + if (!section) throw new Error('Could not find obsolete section') + + bindings.remove({ node: section }) + + return { ok: true, beforeXml, afterXml: bindings.toString({}) } + \`\`\` + +` diff --git a/packages/yjs-rich-markdown/src/yxml-proxy.ts b/packages/yjs-rich-markdown/src/yxml-proxy.ts new file mode 100644 index 0000000..92de512 --- /dev/null +++ b/packages/yjs-rich-markdown/src/yxml-proxy.ts @@ -0,0 +1,202 @@ +import * as Y from 'yjs' + +export type YXmlNodeKind = 'fragment' | 'element' | 'text' + +export type YXmlNodeRef = { + id: string + kind: YXmlNodeKind +} + +export type YXmlNodeSummary = YXmlNodeRef & { + nodeName: string | null + length: number + xml: string + attributes?: Record +} + +export type YXmlNodeSpec = + | { + kind: 'element' + nodeName: string + attributes?: Record + children?: YXmlNodeSpec[] + } + | { + kind: 'text' + text?: string + } + +export type YXmlProxy = { + root(): YXmlNodeRef + summary(input: { node: YXmlNodeRef }): YXmlNodeSummary + children(input?: { node?: YXmlNodeRef }): YXmlNodeRef[] + get(input: { node?: YXmlNodeRef; index: number }): YXmlNodeRef + toString(input?: { node?: YXmlNodeRef }): string + append(input: { parent?: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] + prepend(input: { parent?: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] + insertBefore(input: { ref: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] + insertAfter(input: { ref: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] + remove(input: { node: YXmlNodeRef }): void + setAttribute(input: { node: YXmlNodeRef; name: string; value: string }): void + removeAttribute(input: { node: YXmlNodeRef; name: string }): void +} + +type YXmlContainer = Y.XmlFragment | Y.XmlElement +type YXmlNode = YXmlContainer | Y.XmlText +type InsertableYXmlNode = Y.XmlElement | Y.XmlText + + +export class YXmlProxyBindings implements YXmlProxy { + private readonly nodes = new Map() + private readonly refs = new WeakMap() + private nextId = 1 + + constructor(private readonly rootFragment: Y.XmlFragment) { + this.register(rootFragment) + } + + root(): YXmlNodeRef { + return this.register(this.rootFragment) + } + + summary({ node }: { node: YXmlNodeRef }): YXmlNodeSummary { + const resolved = this.resolve(node) + const ref = this.register(resolved) + return { + ...ref, + nodeName: resolved instanceof Y.XmlElement ? resolved.nodeName : null, + length: resolved.length, + xml: resolved.toString(), + ...(resolved instanceof Y.XmlElement ? { attributes: resolved.getAttributes() } : {}), + } + } + + children(input: { node?: YXmlNodeRef } = {}): YXmlNodeRef[] { + return this.resolveContainer(input.node).slice().map((node) => this.register(node)) + } + + get({ node, index }: { node?: YXmlNodeRef; index: number }): YXmlNodeRef { + const child = this.resolveContainer(node).get(index) + if (!child) throw new Error(`No YXml child at index ${index}`) + return this.register(child) + } + + toString(input: { node?: YXmlNodeRef } = {}): string { + return this.resolve(input.node).toString() + } + + append({ parent, content }: { parent?: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] { + const container = this.resolveContainer(parent) + const nodesToInsert = this.buildNodes(content) + container.push(nodesToInsert) + return this.refsFor(nodesToInsert) + } + + prepend({ parent, content }: { parent?: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] { + const container = this.resolveContainer(parent) + const nodesToInsert = this.buildNodes(content) + container.unshift(nodesToInsert) + return this.refsFor(nodesToInsert) + } + + insertBefore({ ref, content }: { ref: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] { + const target = this.resolve(ref) + const parent = this.parentContainerOf(target) + const index = parent.slice().indexOf(target as Y.XmlElement | Y.XmlText) + if (index < 0) throw new Error('Referenced node is not a direct child of its parent') + const nodesToInsert = this.buildNodes(content) + parent.insert(index, nodesToInsert) + return this.refsFor(nodesToInsert) + } + + insertAfter({ ref, content }: { ref: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] { + const target = this.resolve(ref) + const parent = this.parentContainerOf(target) + const nodesToInsert = this.buildNodes(content) + parent.insertAfter(target as Y.XmlElement | Y.XmlText, nodesToInsert) + return this.refsFor(nodesToInsert) + } + + remove({ node }: { node: YXmlNodeRef }): void { + const target = this.resolve(node) + const parent = this.parentContainerOf(target) + const index = parent.slice().indexOf(target as Y.XmlElement | Y.XmlText) + if (index < 0) throw new Error('Referenced node is not a direct child of its parent') + parent.delete(index, 1) + } + + setAttribute({ node, name, value }: { node: YXmlNodeRef; name: string; value: string }): void { + this.resolveElement(node).setAttribute(name, value) + } + + removeAttribute({ node, name }: { node: YXmlNodeRef; name: string }): void { + this.resolveElement(node).removeAttribute(name) + } + + private register(node: YXmlNode): YXmlNodeRef { + const existing = this.refs.get(node) + if (existing) return existing + + const ref = { id: `n${this.nextId++}`, kind: kindOf(node) } + this.nodes.set(ref.id, node) + this.refs.set(node, ref) + return ref + } + + private resolve(ref?: YXmlNodeRef): YXmlNode { + if (!ref) return this.rootFragment + + const node = this.nodes.get(ref.id) + if (!node) throw new Error(`Unknown YXml node ref: ${ref.id}`) + if (kindOf(node) !== ref.kind) throw new Error(`YXml node ref kind mismatch for ${ref.id}`) + return node + } + + private resolveContainer(ref?: YXmlNodeRef): YXmlContainer { + const node = this.resolve(ref) + if (node instanceof Y.XmlText) throw new Error('Expected a fragment or element node') + return node + } + + private resolveElement(ref: YXmlNodeRef): Y.XmlElement { + const node = this.resolve(ref) + if (!(node instanceof Y.XmlElement)) throw new Error('Expected an element node') + return node + } + + private buildNodes(specs: YXmlNodeSpec[]): InsertableYXmlNode[] { + return specs.map((spec) => this.buildNode(spec)) + } + + private buildNode(spec: YXmlNodeSpec): InsertableYXmlNode { + if (spec.kind === 'text') { + const text = new Y.XmlText() + if (spec.text) text.insert(0, spec.text) + return text + } + + const element = new Y.XmlElement(spec.nodeName) + for (const [name, value] of Object.entries(spec.attributes ?? {})) { + element.setAttribute(name, value) + } + const children = this.buildNodes(spec.children ?? []) + if (children.length > 0) element.insert(0, children) + return element + } + + private refsFor(content: InsertableYXmlNode[]): YXmlNodeRef[] { + return content.map((node) => this.register(node)) + } + + private parentContainerOf(node: YXmlNode): YXmlContainer { + const parent = node.parent + if (parent instanceof Y.XmlFragment || parent instanceof Y.XmlElement) return parent + throw new Error('Referenced node is not attached to a YXml parent') + } +} + +function kindOf(node: YXmlNode): YXmlNodeKind { + if (node instanceof Y.XmlElement) return 'element' + if (node instanceof Y.XmlText) return 'text' + return 'fragment' +} diff --git a/packages/yjs-rich-markdown/test/markdown-yxml-shapes.test.ts b/packages/yjs-rich-markdown/test/markdown-yxml-shapes.test.ts new file mode 100644 index 0000000..5b450f2 --- /dev/null +++ b/packages/yjs-rich-markdown/test/markdown-yxml-shapes.test.ts @@ -0,0 +1,68 @@ +import { describe, expect, test } from 'bun:test' +import * as Y from 'yjs' +import { RichMarkdownArtifactStore, writeArtifactMarkdown } from '../src' + +function xmlFor(markdown: string): string { + const doc = new Y.Doc() + const store = new RichMarkdownArtifactStore(doc) + const path = '/artifacts/shapes.md' + store.createArtifact(path) + writeArtifactMarkdown(doc, path, markdown) + return store.getFragment(path).toString() +} + +describe('Markdown to YXml shapes', () => { + test('headings', () => { + expect(xmlFor('# H1\n\n## H2\n\n### H3')).toBe( + 'H1H2H3', + ) + }) + + test('paragraph with inline marks', () => { + expect(xmlFor('Plain **bold** *italic* `code` [link](https://example.com).')).toBe( + 'Plain bold italic code link.', + ) + }) + + test('unordered and ordered lists', () => { + expect(xmlFor('- First\n- Second\n\n1. One\n2. Two')).toBe( + 'FirstSecondOneTwo', + ) + }) + + test('nested unordered list', () => { + expect(xmlFor('- Parent\n - Child')).toBe( + 'ParentChild', + ) + }) + + test('blockquote', () => { + expect(xmlFor('> Quote line')).toBe('
Quote line
') + }) + + test('horizontal rule', () => { + expect(xmlFor('Before\n\n---\n\nAfter')).toBe('BeforeAfter') + }) + + test('hard break inside paragraph', () => { + expect(xmlFor('First line\\\nSecond line')).toBe('First lineSecond line') + }) + + test('single-line fenced code block', () => { + expect(xmlFor('```ts\nconst value = 1\n```')).toBe('const value = 1') + }) + + test('multi-line fenced code block', () => { + expect(xmlFor('```ts\nconst value = 1\nconsole.log(value)\n```')).toBe( + 'const value = 1\nconsole.log(value)', + ) + }) + + test('indented code block', () => { + expect(xmlFor(' const value = 1\n console.log(value)')).toBe('const value = 1\nconsole.log(value)') + }) + + test('strikethrough', () => { + expect(xmlFor('~~deleted~~')).toBe('deleted') + }) +}) diff --git a/packages/yjs-rich-markdown/test/yxml-proxy.test.ts b/packages/yjs-rich-markdown/test/yxml-proxy.test.ts new file mode 100644 index 0000000..90c0cce --- /dev/null +++ b/packages/yjs-rich-markdown/test/yxml-proxy.test.ts @@ -0,0 +1,236 @@ +import { describe, expect, test } from 'bun:test' +import * as Y from 'yjs' +import { YXmlProxyBindings } from '../src' + +describe('YXml proxy', () => { + test('appends XML elements and nested children', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const inserted = proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'section', + attributes: { id: 'intro' }, + children: [ + { + kind: 'element', + nodeName: 'heading', + attributes: { level: '2' }, + children: [{ kind: 'text', text: 'Intro' }], + }, + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Nested content works.' }], + }, + ], + }, + ], + }) + + expect(inserted).toEqual([{ id: expect.any(String), kind: 'element' }]) + expect(proxy.toString()).toBe( + '
IntroNested content works.
', + ) + }) + + test('inserts before and after refs without passing parent', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [middle] = proxy.append({ + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Middle' }] }], + }) + if (!middle) throw new Error('Expected middle node') + + proxy.insertBefore({ + ref: middle, + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Before' }] }], + }) + proxy.insertAfter({ + ref: middle, + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'After' }] }], + }) + + expect(proxy.toString()).toBe('BeforeMiddleAfter') + }) + + test('appends nested content inside an element parent', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [list] = proxy.append({ content: [{ kind: 'element', nodeName: 'bulletList' }] }) + if (!list) throw new Error('Expected list node') + + proxy.append({ + parent: list, + content: [ + { + kind: 'element', + nodeName: 'listItem', + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'First' }] }], + }, + { + kind: 'element', + nodeName: 'listItem', + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Second' }] }], + }, + ], + }) + + expect(proxy.toString()).toBe( + 'FirstSecond', + ) + }) + + test('sets and removes attributes on element refs', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [heading] = proxy.append({ + content: [{ kind: 'element', nodeName: 'heading', attributes: { level: '2' }, children: [{ kind: 'text', text: 'Title' }] }], + }) + if (!heading) throw new Error('Expected heading node') + + proxy.setAttribute({ node: heading, name: 'level', value: '3' }) + expect(proxy.summary({ node: heading }).attributes).toEqual({ level: '3' }) + expect(proxy.toString()).toBe('Title') + + proxy.removeAttribute({ node: heading, name: 'level' }) + expect(proxy.summary({ node: heading }).attributes).toEqual({}) + expect(proxy.toString()).toBe('Title') + }) + + test('adds a bullet to an existing list', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [list] = proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'bulletList', + children: [ + { + kind: 'element', + nodeName: 'listItem', + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Existing bullet' }] }], + }, + ], + }, + ], + }) + if (!list) throw new Error('Expected list node') + + proxy.append({ + parent: list, + content: [ + { + kind: 'element', + nodeName: 'listItem', + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Added bullet' }] }], + }, + ], + }) + + expect(proxy.toString()).toBe( + 'Existing bulletAdded bullet', + ) + }) + + test('changes an existing element attribute', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [heading] = proxy.append({ + content: [{ kind: 'element', nodeName: 'heading', attributes: { level: '2' }, children: [{ kind: 'text', text: 'Details' }] }], + }) + if (!heading) throw new Error('Expected heading node') + + proxy.setAttribute({ node: heading, name: 'level', value: '3' }) + + expect(proxy.summary({ node: heading }).attributes).toEqual({ level: '3' }) + expect(proxy.toString()).toBe('Details') + }) + + test('erases a section by removing its element ref', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [intro, obsolete, ending] = proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'section', + attributes: { id: 'intro' }, + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Keep intro.' }] }], + }, + { + kind: 'element', + nodeName: 'section', + attributes: { id: 'obsolete' }, + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Delete this section.' }] }], + }, + { + kind: 'element', + nodeName: 'section', + attributes: { id: 'ending' }, + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Keep ending.' }] }], + }, + ], + }) + if (!intro || !obsolete || !ending) throw new Error('Expected section nodes') + + proxy.remove({ node: obsolete }) + + expect(proxy.children()).toEqual([intro, ending]) + expect(proxy.toString()).toBe( + '
Keep intro.
Keep ending.
', + ) + }) + + test('removes nodes by ref', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [first, second] = proxy.append({ + content: [ + { kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Keep' }] }, + { kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Remove' }] }, + ], + }) + if (!first || !second) throw new Error('Expected paragraph nodes') + + proxy.remove({ node: second }) + + expect(proxy.toString()).toBe('Keep') + expect(proxy.children()).toEqual([first]) + }) + + test('rejects child insertion into text refs', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [paragraph] = proxy.append({ + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Text' }] }], + }) + if (!paragraph) throw new Error('Expected paragraph node') + const [text] = proxy.children({ node: paragraph }) + if (!text) throw new Error('Expected text node') + + expect(() => + proxy.append({ parent: text, content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Nope' }] }] }), + ).toThrow('Expected a fragment or element node') + }) +}) From 8a4e9fc48ab9e8843d5e39660e7e8d86b287d55c Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 2 May 2026 20:53:04 -0700 Subject: [PATCH 09/18] fix: package --- agents/yjs-fs-agents/package.json | 25 - agents/yjs-fs-agents/src/index.ts | 1 - agents/yjs-fs-agents/tsconfig.build.json | 23 - agents/yjs-fs-agents/tsconfig.json | 28 - agents/yjs-markdown-apply-agent/package.json | 34 - agents/yjs-markdown-apply-agent/src/agent.ts | 23 - agents/yjs-markdown-apply-agent/src/index.ts | 11 - .../yjs-markdown-apply-agent/src/prompts.ts | 72 - .../src/retry-controller.ts | 35 - .../src/secure-apply-tool.ts | 40 - agents/yjs-markdown-apply-agent/src/tools.ts | 7 - agents/yjs-markdown-apply-agent/src/types.ts | 44 - .../test/learning/README.md | 63 - .../test/learning/proxy.ts | 211 -- .../test/learning/quickjs-emscripten.test.ts | 195 -- .../test/learning/quickjs-harness.test.ts | 54 - .../test/learning/yjs-secure-exec.test.ts | 158 - .../learning/yjs-tanstack-code-mode.test.ts | 194 -- .../test/prompt-cache-shape.test.ts | 64 - .../test/retry-controller.test.ts | 36 - .../test/secure-apply-contract.test.ts | 49 - agents/yjs-markdown-apply-agent/tsconfig.json | 24 - bun.lock | 1 + examples/yjs-rich-markdown-demo/package.json | 4 +- packages/agentlayer-core/models.json | 2941 ++++++++++++++--- packages/yjs-fs-editor/package.json | 4 +- packages/yjs-fs-react/package.json | 6 +- packages/yjs-fs/package.json | 4 +- packages/yjs-rich-markdown/package.json | 5 +- scripts/release/manifest.ts | 2 +- 30 files changed, 2416 insertions(+), 1942 deletions(-) delete mode 100644 agents/yjs-fs-agents/package.json delete mode 100644 agents/yjs-fs-agents/src/index.ts delete mode 100644 agents/yjs-fs-agents/tsconfig.build.json delete mode 100644 agents/yjs-fs-agents/tsconfig.json delete mode 100644 agents/yjs-markdown-apply-agent/package.json delete mode 100644 agents/yjs-markdown-apply-agent/src/agent.ts delete mode 100644 agents/yjs-markdown-apply-agent/src/index.ts delete mode 100644 agents/yjs-markdown-apply-agent/src/prompts.ts delete mode 100644 agents/yjs-markdown-apply-agent/src/retry-controller.ts delete mode 100644 agents/yjs-markdown-apply-agent/src/secure-apply-tool.ts delete mode 100644 agents/yjs-markdown-apply-agent/src/tools.ts delete mode 100644 agents/yjs-markdown-apply-agent/src/types.ts delete mode 100644 agents/yjs-markdown-apply-agent/test/learning/README.md delete mode 100644 agents/yjs-markdown-apply-agent/test/learning/proxy.ts delete mode 100644 agents/yjs-markdown-apply-agent/test/learning/quickjs-emscripten.test.ts delete mode 100644 agents/yjs-markdown-apply-agent/test/learning/quickjs-harness.test.ts delete mode 100644 agents/yjs-markdown-apply-agent/test/learning/yjs-secure-exec.test.ts delete mode 100644 agents/yjs-markdown-apply-agent/test/learning/yjs-tanstack-code-mode.test.ts delete mode 100644 agents/yjs-markdown-apply-agent/test/prompt-cache-shape.test.ts delete mode 100644 agents/yjs-markdown-apply-agent/test/retry-controller.test.ts delete mode 100644 agents/yjs-markdown-apply-agent/test/secure-apply-contract.test.ts delete mode 100644 agents/yjs-markdown-apply-agent/tsconfig.json diff --git a/agents/yjs-fs-agents/package.json b/agents/yjs-fs-agents/package.json deleted file mode 100644 index 8dc04ab..0000000 --- a/agents/yjs-fs-agents/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "@humanlayer/yjs-fs-agents", - "module": "src/index.ts", - "type": "module", - "private": true, - "scripts": { - "typecheck": "tsgo --noEmit" - }, - "devDependencies": { - "@ai-sdk/anthropic": "catalog:", - "@ai-sdk/provider": "catalog:", - "@humanlayer/agentlayer-core": "workspace:*", - "@humanlayer/yjs-fs": "workspace:*", - "@types/bun": "latest", - "ai": "catalog:", - "zod": "catalog:" - }, - "peerDependencies": { - "typescript": "^5" - }, - "exports": { - ".": "./src/index.ts" - }, - "dependencies": {} -} diff --git a/agents/yjs-fs-agents/src/index.ts b/agents/yjs-fs-agents/src/index.ts deleted file mode 100644 index 336ce12..0000000 --- a/agents/yjs-fs-agents/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {} diff --git a/agents/yjs-fs-agents/tsconfig.build.json b/agents/yjs-fs-agents/tsconfig.build.json deleted file mode 100644 index e043bc7..0000000 --- a/agents/yjs-fs-agents/tsconfig.build.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": false, - "declaration": true, - "declarationMap": true, - "emitDeclarationOnly": true, - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src"], - "exclude": [ - "test", - "dist", - "node_modules", - "**/*.test.*", - "**/*.bench.*", - "**/*.test-d.ts", - "**/__tests__/**", - "**/__mocks__/**", - "**/__snapshots__/**" - ] -} diff --git a/agents/yjs-fs-agents/tsconfig.json b/agents/yjs-fs-agents/tsconfig.json deleted file mode 100644 index b9457eb..0000000 --- a/agents/yjs-fs-agents/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - // Environment setup & latest features - "lib": ["ESNext"], - "target": "ESNext", - "module": "Preserve", - "moduleDetection": "force", - "jsx": "react-jsx", - "allowJs": true, - "types": ["bun"], - // Bundler mode - "moduleResolution": "bundler", - "customConditions": ["source"], - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "noEmit": true, - // Best practices - "strict": true, - "skipLibCheck": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "noImplicitOverride": true, - // Some stricter flags (disabled by default) - "noUnusedLocals": false, - "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false - } -} diff --git a/agents/yjs-markdown-apply-agent/package.json b/agents/yjs-markdown-apply-agent/package.json deleted file mode 100644 index e5e4d65..0000000 --- a/agents/yjs-markdown-apply-agent/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@humanlayer/yjs-markdown-apply-agent", - "module": "src/index.ts", - "type": "module", - "private": true, - "scripts": { - "typecheck": "tsgo --noEmit" - }, - "exports": { - ".": { - "bun": "./src/index.ts", - "source": "./src/index.ts", - "default": "./src/index.ts" - } - }, - "devDependencies": { - "@ai-sdk/provider": "catalog:", - "@humanlayer/agentlayer-core": "workspace:*", - "@humanlayer/quickjs-exec": "workspace:*", - "@tanstack/ai": "0.14.0", - "@tanstack/ai-code-mode": "0.1.8", - "@tanstack/ai-isolate-quickjs": "0.1.8", - "@types/bun": "latest", - "ai": "catalog:", - "quickjs-emscripten": "0.31.0", - "zod": "catalog:" - }, - "peerDependencies": { - "secure-exec": "0.2.1", - "typescript": "^5", - "yjs": "^13.6.30" - }, - "dependencies": {} -} diff --git a/agents/yjs-markdown-apply-agent/src/agent.ts b/agents/yjs-markdown-apply-agent/src/agent.ts deleted file mode 100644 index ffe00cd..0000000 --- a/agents/yjs-markdown-apply-agent/src/agent.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Agent, type AgentConfig } from '@humanlayer/agentlayer-core' -import type { LanguageModel } from 'ai' -import { APPLY_AGENT_SYSTEM_PROMPT } from './prompts' -import type { SecureApplyExecutor } from './secure-apply-tool' -import { createYjsMarkdownApplyTools } from './tools' - -export type CreateYjsMarkdownApplyAgentOptions = { - model: LanguageModel - executor: SecureApplyExecutor - system?: string - maxSteps?: number - providerOptions?: AgentConfig['providerOptions'] -} - -export function createYjsMarkdownApplyAgent(options: CreateYjsMarkdownApplyAgentOptions) { - return new Agent({ - model: options.model, - system: options.system ?? APPLY_AGENT_SYSTEM_PROMPT, - tools: createYjsMarkdownApplyTools(options.executor), - maxSteps: options.maxSteps, - providerOptions: options.providerOptions, - }) -} diff --git a/agents/yjs-markdown-apply-agent/src/index.ts b/agents/yjs-markdown-apply-agent/src/index.ts deleted file mode 100644 index d630d5f..0000000 --- a/agents/yjs-markdown-apply-agent/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export { createYjsMarkdownApplyAgent, type CreateYjsMarkdownApplyAgentOptions } from './agent' -export { APPLY_AGENT_SYSTEM_PROMPT, buildAbortSystemInformation, buildApplyUserMessage } from './prompts' -export { DEFAULT_APPLY_RETRY_POLICY, nextRetryDecision, type RetryDecision } from './retry-controller' -export { - createFakeSecureApplyExecutor, - createSecureApplyTool, - secureApplyResultSchema, - type SecureApplyExecutor, -} from './secure-apply-tool' -export { createYjsMarkdownApplyTools } from './tools' -export type { ApplyAttemptAbort, ApplyRetryPolicy, SecureApplyInput, SecureApplyResult } from './types' diff --git a/agents/yjs-markdown-apply-agent/src/prompts.ts b/agents/yjs-markdown-apply-agent/src/prompts.ts deleted file mode 100644 index 44f8002..0000000 --- a/agents/yjs-markdown-apply-agent/src/prompts.ts +++ /dev/null @@ -1,72 +0,0 @@ -import type { ApplyAttemptAbort, SecureApplyInput } from './types' - -export const APPLY_AGENT_SYSTEM_PROMPT = `You are the Yjs Markdown apply agent. - -Your job is to generate JavaScript that applies one requested Markdown oldString/newString edit to a disconnected Yjs document snapshot. The host will run your generated code in a secure executor and will reject any unsafe or invalid result. - -Stable contract: -- Reconstruct a disconnected Y.Doc from input.snapshotBase64. -- Open only input.artifactFragmentName as the target Y.XmlFragment. -- Apply the requested edit structurally with Y.XmlFragment, Y.XmlElement, and Y.XmlText operations. -- Validate the result before returning. -- Return a SecureApplyResult JSON object with proposedUpdateBase64 computed from Y.encodeStateAsUpdate(doc, baseStateVector). -- Do not access network, filesystem, process, environment, shell, provider, or the live document. -- Do not import arbitrary packages. Use only host-allowlisted dependencies and helpers. -- If the target document changed and the host provides a block, regenerate against the latest snapshot in that message. - -Prompt caching requirement: -- Treat this system prompt and tool definitions as stable. -- Dynamic document state appears only in user messages.` - -export function buildApplyUserMessage(input: SecureApplyInput): string { - const systemInformation = input.systemInformation ? `${input.systemInformation}\n\n` : '' - - return `${systemInformation}Apply this Markdown edit to the target Yjs XML fragment. - -path: ${input.path} -artifactFragmentName: ${input.artifactFragmentName} -baseArtifactRevision: ${input.baseArtifactRevision} - -oldString: -${fence(input.oldString)} - -newString: -${fence(input.newString)} - -currentMarkdown: -${fence(input.currentMarkdown)} - -currentXml: -${fence(input.currentXml)} - -snapshotBase64: -${input.snapshotBase64} - -baseStateVectorBase64: -${input.baseStateVectorBase64}` -} - -export function buildAbortSystemInformation(abort: ApplyAttemptAbort): string { - const summary = abort.changeSummary.map((line) => `- ${line}`).join('\n') - - return ` -The previous secure apply attempt was aborted because the target artifact changed while generated code was running. - -path: ${abort.path} -revision: ${abort.previousRevision} -> ${abort.currentRevision} -changeSummary: -${summary || '- target fragment changed'} - -updatedMarkdown: -${fence(abort.updatedMarkdown)} - -updatedXml: -${fence(abort.updatedXml)} - -The edit intent is unchanged. Apply the same requested oldString/newString transformation to this latest document state. -` -} - -function fence(value: string): string { - return `\`\`\`txt\n${value}\n\`\`\`` -} diff --git a/agents/yjs-markdown-apply-agent/src/retry-controller.ts b/agents/yjs-markdown-apply-agent/src/retry-controller.ts deleted file mode 100644 index 782be2d..0000000 --- a/agents/yjs-markdown-apply-agent/src/retry-controller.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { ApplyRetryPolicy } from './types' - -export const DEFAULT_APPLY_RETRY_POLICY: ApplyRetryPolicy = { - maxAttempts: 3, - initialBackoffMs: 250, - maxBackoffMs: 2000, - waitForQuietMs: 1000, - maxWaitForQuietMs: 10000, -} - -export type RetryDecision = - | { type: 'retry'; attempt: number; delayMs: number } - | { type: 'fail'; reason: 'max-attempts-exceeded' | 'quiet-timeout-exceeded' } - -export function nextRetryDecision(input: { - attempt: number - policy?: Partial - waitedForQuietMs?: number -}): RetryDecision { - const policy = { ...DEFAULT_APPLY_RETRY_POLICY, ...input.policy } - if (input.attempt >= policy.maxAttempts) { - return { type: 'fail', reason: 'max-attempts-exceeded' } - } - - if ((input.waitedForQuietMs ?? 0) > policy.maxWaitForQuietMs) { - return { type: 'fail', reason: 'quiet-timeout-exceeded' } - } - - const exponentialDelay = policy.initialBackoffMs * 2 ** Math.max(0, input.attempt - 1) - return { - type: 'retry', - attempt: input.attempt + 1, - delayMs: Math.min(policy.maxBackoffMs, Math.max(policy.waitForQuietMs, exponentialDelay)), - } -} diff --git a/agents/yjs-markdown-apply-agent/src/secure-apply-tool.ts b/agents/yjs-markdown-apply-agent/src/secure-apply-tool.ts deleted file mode 100644 index 4c917bd..0000000 --- a/agents/yjs-markdown-apply-agent/src/secure-apply-tool.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { defineTool } from '@humanlayer/agentlayer-core' -import { z } from 'zod' - -export const secureApplyResultSchema = z.object({ - ok: z.boolean(), - proposedUpdateBase64: z.string().optional(), - beforeMarkdown: z.string().optional(), - afterMarkdown: z.string().optional(), - beforeXml: z.string().optional(), - afterXml: z.string().optional(), - changedArtifacts: z.array(z.string()).optional(), - validationErrors: z.array(z.string()).optional(), - operationLog: z.array(z.string()).optional(), -}) - -export const secureApplyToolInputSchema = z.object({ - generatedCode: z.string().min(1), -}) - -export type SecureApplyToolInput = z.infer -export type SecureApplyToolResult = z.infer - -export type SecureApplyExecutor = (input: SecureApplyToolInput, signal: AbortSignal) => Promise - -export function createSecureApplyTool(executor: SecureApplyExecutor) { - return defineTool({ - name: 'secure_apply_yjs_update', - description: - 'Run generated JavaScript in a secure disconnected Yjs apply environment. The code must return a SecureApplyResult with a proposed Yjs update.', - input: secureApplyToolInputSchema, - output: secureApplyResultSchema, - async execute(input, ctx) { - return executor(input, ctx.signal) - }, - }) -} - -export function createFakeSecureApplyExecutor(result: SecureApplyToolResult): SecureApplyExecutor { - return async () => result - } diff --git a/agents/yjs-markdown-apply-agent/src/tools.ts b/agents/yjs-markdown-apply-agent/src/tools.ts deleted file mode 100644 index 3db82df..0000000 --- a/agents/yjs-markdown-apply-agent/src/tools.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createSecureApplyTool, type SecureApplyExecutor } from './secure-apply-tool' - -export function createYjsMarkdownApplyTools(executor: SecureApplyExecutor) { - return { - secure_apply_yjs_update: createSecureApplyTool(executor), - } -} diff --git a/agents/yjs-markdown-apply-agent/src/types.ts b/agents/yjs-markdown-apply-agent/src/types.ts deleted file mode 100644 index f40c9d7..0000000 --- a/agents/yjs-markdown-apply-agent/src/types.ts +++ /dev/null @@ -1,44 +0,0 @@ -export type SecureApplyInput = { - path: string - oldString: string - newString: string - currentMarkdown: string - currentXml: string - snapshotBase64: string - baseStateVectorBase64: string - artifactFragmentName: string - baseArtifactRevision: number - systemInformation?: string -} - -export type SecureApplyResult = { - ok: boolean - proposedUpdateBase64?: string - beforeMarkdown?: string - afterMarkdown?: string - beforeXml?: string - afterXml?: string - changedArtifacts?: string[] - validationErrors?: string[] - operationLog?: string[] -} - -export type ApplyRetryPolicy = { - maxAttempts: number - initialBackoffMs: number - maxBackoffMs: number - waitForQuietMs: number - maxWaitForQuietMs: number -} - -export type ApplyAttemptAbort = { - reason: 'target-fragment-changed' - path: string - previousRevision: number - currentRevision: number - changeSummary: string[] - updatedMarkdown: string - updatedXml: string - snapshotBase64: string - baseStateVectorBase64: string -} diff --git a/agents/yjs-markdown-apply-agent/test/learning/README.md b/agents/yjs-markdown-apply-agent/test/learning/README.md deleted file mode 100644 index 459ca8a..0000000 --- a/agents/yjs-markdown-apply-agent/test/learning/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# QuickJS Y.Xml Proxy Learning Harness - -This directory is for learning the thinnest possible bridge between generated code and a live `Y.XmlFragment`. - -The current direction is direct mutation through host bindings: - -1. The edit tool receives `path`, `oldString`, and `newString`. -2. The host deterministically resolves `path` to the target `Y.XmlFragment`. -3. Generated code runs in an isolated JS runtime. -4. Bindings proxy operations onto the already-selected fragment. -5. Binding calls mutate the live Yjs fragment; returned strings/objects are diagnostics only. - -## Harness - -Use `withQuickJsMode` or `withAsyncQuickJsMode` from `@humanlayer/quickjs-exec` to avoid repeating QuickJS setup/teardown. The helpers are intentionally unopinionated; pass Yjs-specific behavior as bindings from the learning test: - -```ts -const doc = new Y.Doc() -const fragment = doc.getXmlFragment('learning') -fragment.push([yParagraph('Hello')]) - -await withQuickJsMode( - { - add: (a, b) => Number(a) + Number(b), - fragmentToJSON: () => fragment.toJSON(), - }, - ({ run }) => { - const result = run(` - ({ - sum: bindings.add(20, 22), - xml: bindings.fragmentToJSON(), - }) - `) - - expect(fragment.toJSON()).toBe('Hello') - }, -) -``` - -`bindings` registers host functions. In QuickJS code, call them through the injected `bindings` facade: - -```js -bindings.bindingName(arg1, arg2) -``` - -The facade JSON-serializes host results under the hood so the test code can stay close to the eventual Secure Exec code. `withAsyncQuickJsMode` has the same shape but uses QuickJS asyncified host functions, so bindings may be `async`. - -## Incremental Binding Plan - -Build one capability per test, in this order: - -1. Read-only fragment diagnostics: `fragment.toJSON`, `fragment.length`. -2. Top-level insertion: `fragment.push` with simple element specs. -3. Recursive content specs: nested element/text creation. -4. Refs: `fragment.get` returns a structured-cloneable `XmlRef`. -5. Element read methods: `element.nodeName`, `element.length`, `element.get`. -6. Deep traversal: repeated `element.get` over multiple levels. -7. Text read/write: `text.toString`, `text.insert`, `text.delete`. -8. `insertAfter` with refs returned by `get`. -9. Element attributes: `getAttributes`, `setAttribute`, `removeAttribute`. -10. Error behavior: invalid ref/kind/index errors come back to the generated-code caller. - -Keep the bridge thin. Do not add validation, schema checks, snapshot/commit flow, or replacement-string semantics while these learning tests are being built. diff --git a/agents/yjs-markdown-apply-agent/test/learning/proxy.ts b/agents/yjs-markdown-apply-agent/test/learning/proxy.ts deleted file mode 100644 index 128da71..0000000 --- a/agents/yjs-markdown-apply-agent/test/learning/proxy.ts +++ /dev/null @@ -1,211 +0,0 @@ -import * as Y from 'yjs' -import { createNodeDriver, NodeExecutionDriver } from 'secure-exec' -import type { BindingTree } from 'secure-exec' - -type XmlRef = { - id: string - kind: 'element' | 'text' -} - -type XmlContentSpec = - | { - kind: 'element' - nodeName: string - attributes?: Record - children?: XmlContentSpec[] - } - | { - kind: 'text' - text?: string - delta?: Array<{ insert: string; attributes?: Record }> - } - -type XmlNode = Y.XmlElement | Y.XmlText - -class XmlRefRegistry { - private nextId = 1 - private readonly byId = new Map() - private readonly byObject = new WeakMap() - - register(node: XmlNode): XmlRef { - const existing = this.byObject.get(node) - if (existing) return existing - - const ref: XmlRef = { - id: `xml_${this.nextId++}`, - kind: node instanceof Y.XmlText ? 'text' : 'element', - } - - this.byId.set(ref.id, node) - this.byObject.set(node, ref) - return ref - } - - resolve(ref: XmlRef): XmlNode { - const node = this.byId.get(ref.id) - if (!node) throw new Error(`Unknown XML ref: ${ref.id}`) - return node - } - - resolveElement(ref: XmlRef): Y.XmlElement { - if (ref.kind !== 'element') throw new Error(`Expected element ref, got ${ref.kind}`) - const node = this.resolve(ref) - if (!(node instanceof Y.XmlElement)) throw new Error(`XML ref ${ref.id} does not resolve to Y.XmlElement`) - return node - } - - resolveText(ref: XmlRef): Y.XmlText { - if (ref.kind !== 'text') throw new Error(`Expected text ref, got ${ref.kind}`) - const node = this.resolve(ref) - if (!(node instanceof Y.XmlText)) throw new Error(`XML ref ${ref.id} does not resolve to Y.XmlText`) - return node - } -} - -function createXmlNode(spec: XmlContentSpec): XmlNode { - if (spec.kind === 'text') { - const text = new Y.XmlText() - if (spec.delta) { - text.applyDelta(spec.delta) - } else if (spec.text) { - text.insert(0, spec.text) - } - return text - } - - const element = new Y.XmlElement(spec.nodeName) - for (const [name, value] of Object.entries(spec.attributes ?? {})) { - element.setAttribute(name, value) - } - if (spec.children?.length) { - const children = spec.children.map(createXmlNode) - element.insertAfter(null, children) - } - return element -} - -function createXmlNodes(specs: XmlContentSpec[]): XmlNode[] { - return specs.map(createXmlNode) -} - -function maybeRef(node: XmlNode | null | undefined, registry: XmlRefRegistry): XmlRef | null | undefined { - if (node === null) return null - if (node === undefined) return undefined - return registry.register(node) -} - -export function createYXmlBindings(fragment: Y.XmlFragment): BindingTree { - const registry = new XmlRefRegistry() - - return { - fragment: { - firstChild: () => maybeRef(fragment.firstChild, registry), - length: () => fragment.length, - insert: (index, content) => { - const nodes = createXmlNodes(content as XmlContentSpec[]) - fragment.insert(index as number, nodes) - return nodes.map((node) => registry.register(node)) - }, - insertAfter: (ref, content) => { - const after = ref === null ? null : registry.resolve(ref as XmlRef) - const nodes = createXmlNodes(content as XmlContentSpec[]) - fragment.insertAfter(after, nodes) - return nodes.map((node) => registry.register(node)) - }, - delete: (index, length) => fragment.delete(index as number, length as number), - push: (content) => { - const nodes = createXmlNodes(content as XmlContentSpec[]) - fragment.push(nodes) - return nodes.map((node) => registry.register(node)) - }, - unshift: (content) => { - const nodes = createXmlNodes(content as XmlContentSpec[]) - fragment.unshift(nodes) - return nodes.map((node) => registry.register(node)) - }, - get: (index) => maybeRef(fragment.get(index as number), registry), - slice: (start, end) => - fragment.slice(start as number | undefined, end as number | undefined).map((node) => registry.register(node)), - toJSON: () => fragment.toJSON(), - }, - element: { - firstChild: (ref) => maybeRef(registry.resolveElement(ref as XmlRef).firstChild, registry), - length: (ref) => registry.resolveElement(ref as XmlRef).length, - insert: (ref, index, content) => { - const parent = registry.resolveElement(ref as XmlRef) - const nodes = createXmlNodes(content as XmlContentSpec[]) - parent.insert(index as number, nodes) - return nodes.map((node) => registry.register(node)) - }, - insertAfter: (ref, afterRef, content) => { - const parent = registry.resolveElement(ref as XmlRef) - const after = afterRef === null ? null : registry.resolve(afterRef as XmlRef) - const nodes = createXmlNodes(content as XmlContentSpec[]) - parent.insertAfter(after, nodes) - return nodes.map((node) => registry.register(node)) - }, - delete: (ref, index, length) => registry.resolveElement(ref as XmlRef).delete(index as number, length as number), - push: (ref, content) => { - const parent = registry.resolveElement(ref as XmlRef) - const nodes = createXmlNodes(content as XmlContentSpec[]) - parent.push(nodes) - return nodes.map((node) => registry.register(node)) - }, - unshift: (ref, content) => { - const parent = registry.resolveElement(ref as XmlRef) - const nodes = createXmlNodes(content as XmlContentSpec[]) - parent.unshift(nodes) - return nodes.map((node) => registry.register(node)) - }, - get: (ref, index) => maybeRef(registry.resolveElement(ref as XmlRef).get(index as number), registry), - slice: (ref, start, end) => - registry - .resolveElement(ref as XmlRef) - .slice(start as number | undefined, end as number | undefined) - .map((node) => registry.register(node)), - nodeName: (ref: unknown) => registry.resolveElement(ref as XmlRef).nodeName, - prevSibling: (ref: unknown) => maybeRef(registry.resolveElement(ref as XmlRef).prevSibling, registry), - nextSibling: (ref: unknown) => maybeRef(registry.resolveElement(ref as XmlRef).nextSibling, registry), - toString: (ref: unknown) => registry.resolveElement(ref as XmlRef).toString(), - getAttribute: (ref, name) => registry.resolveElement(ref as XmlRef).getAttribute(name as string), - getAttributes: (ref) => registry.resolveElement(ref as XmlRef).getAttributes(), - setAttribute: (ref, name, value) => registry.resolveElement(ref as XmlRef).setAttribute(name as string, value as string), - removeAttribute: (ref, name) => registry.resolveElement(ref as XmlRef).removeAttribute(name as string), - }, - text: { - prevSibling: (ref: unknown) => maybeRef(registry.resolveText(ref as XmlRef).prevSibling, registry), - nextSibling: (ref: unknown) => maybeRef(registry.resolveText(ref as XmlRef).nextSibling, registry), - toString: (ref: unknown) => registry.resolveText(ref as XmlRef).toString(), - toJSON: (ref: unknown) => registry.resolveText(ref as XmlRef).toJSON(), - toDelta: (ref: unknown) => registry.resolveText(ref as XmlRef).toDelta(), - insert: (ref, index, text, attrs) => - registry.resolveText(ref as XmlRef).insert(index as number, text as string, attrs as Record | undefined), - delete: (ref, index, length) => registry.resolveText(ref as XmlRef).delete(index as number, length as number), - format: (ref, index, length, attrs) => - registry.resolveText(ref as XmlRef).format(index as number, length as number, attrs as Record), - }, - } -} - -export async function withCodeMode( - fragment: Y.XmlFragment, - bindings: BindingTree, - cb: (driver: NodeExecutionDriver) => Promise, -) { - const system = createNodeDriver() - - const driver = new NodeExecutionDriver({ - system, - runtime: system.runtime, - bindings: { - ...createYXmlBindings(fragment), - ...bindings, - }, - }) - - try { - await cb(driver) - } finally { - driver.dispose() - } -} diff --git a/agents/yjs-markdown-apply-agent/test/learning/quickjs-emscripten.test.ts b/agents/yjs-markdown-apply-agent/test/learning/quickjs-emscripten.test.ts deleted file mode 100644 index 34a009c..0000000 --- a/agents/yjs-markdown-apply-agent/test/learning/quickjs-emscripten.test.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { describe, expect, test } from 'bun:test' -import { getQuickJS, newAsyncContext } from 'quickjs-emscripten' -import * as Y from 'yjs' - -describe('quickjs-emscripten learning tests', () => { - test('sync context can evaluate code and read values', async () => { - const QuickJS = await getQuickJS() - const vm = QuickJS.newContext() - - try { - const result = vm.evalCode(`({ answer: 40 + 2 })`) - const value = vm.unwrapResult(result) - - expect(vm.dump(value)).toEqual({ answer: 42 }) - value.dispose() - } finally { - vm.dispose() - } - }) - - test('sync context can call host functions', async () => { - const QuickJS = await getQuickJS() - const vm = QuickJS.newContext() - let callCount = 0 - - try { - const addHandle = vm.newFunction('add', (aHandle, bHandle) => { - callCount += 1 - const a = vm.getNumber(aHandle) - const b = vm.getNumber(bHandle) - - return vm.newNumber(a + b) - }) - vm.setProp(vm.global, 'add', addHandle) - addHandle.dispose() - - const result = vm.evalCode(`add(add(1, 1), 40)`) - const value = vm.unwrapResult(result) - - expect(vm.getNumber(value)).toBe(42) - expect(callCount).toBe(2) - value.dispose() - } finally { - vm.dispose() - } - }) - - test('sync host functions can mutate a live Y.XmlFragment', async () => { - const QuickJS = await getQuickJS() - const vm = QuickJS.newContext() - const yDoc = new Y.Doc() - const fragment = yDoc.getXmlFragment('quickjs-sync-bindings') - - try { - const pushElementHandle = vm.newFunction('pushElement', (jsonHandle) => { - const input = JSON.parse(vm.getString(jsonHandle)) as { - nodeName: string - text: string - attributes?: Record - } - const element = new Y.XmlElement(input.nodeName) - for (const [name, value] of Object.entries(input.attributes ?? {})) { - element.setAttribute(name, value) - } - - const text = new Y.XmlText() - text.insert(0, input.text) - element.push([text]) - fragment.push([element]) - - return vm.newString(JSON.stringify({ xml: fragment.toJSON(), length: fragment.length })) - }) - vm.setProp(vm.global, 'pushElementJson', pushElementHandle) - pushElementHandle.dispose() - - const result = vm.evalCode(` - JSON.parse(pushElementJson(JSON.stringify({ - nodeName: 'section', - text: 'Hello from sync QuickJS', - attributes: { id: 'intro' }, - }))) - `) - const value = vm.unwrapResult(result) - - expect(vm.dump(value)).toEqual({ - length: 1, - xml: '
Hello from sync QuickJS
', - }) - expect(fragment.toJSON()).toBe('
Hello from sync QuickJS
') - value.dispose() - } finally { - vm.dispose() - } - }) - - test('sync host functions can return QuickJS promises for awaited bindings', async () => { - const QuickJS = await getQuickJS() - const vm = QuickJS.newContext() - - try { - const asyncAddHandle = vm.newFunction('asyncAdd', (jsonHandle) => { - const { a, b } = JSON.parse(vm.getString(jsonHandle)) as { a: number; b: number } - const promise = vm.newPromise() - - queueMicrotask(() => { - promise.resolve(vm.newString(JSON.stringify({ sum: a + b }))) - }) - promise.settled.then(vm.runtime.executePendingJobs) - - return promise.handle - }) - vm.setProp(vm.global, 'asyncAddJson', asyncAddHandle) - asyncAddHandle.dispose() - - const result = vm.evalCode(` - (async () => { - const first = JSON.parse(await asyncAddJson(JSON.stringify({ a: 1, b: 1 }))) - const second = JSON.parse(await asyncAddJson(JSON.stringify({ a: first.sum, b: 40 }))) - - return second.sum - })() - `) - const promiseHandle = vm.unwrapResult(result) - const resolvedResult = await vm.resolvePromise(promiseHandle) - promiseHandle.dispose() - const value = vm.unwrapResult(resolvedResult) - - expect(vm.getNumber(value)).toBe(42) - value.dispose() - } finally { - vm.dispose() - } - }) - - test('asyncified host functions are synchronous from QuickJS code', async () => { - const context = await newAsyncContext() - - try { - const addHandle = context.newAsyncifiedFunction('add', async (jsonHandle) => { - const { a, b } = JSON.parse(context.getString(jsonHandle)) as { a: number; b: number } - - return context.newString(JSON.stringify({ sum: a + b })) - }) - context.setProp(context.global, 'addJson', addHandle) - addHandle.dispose() - - const result = await context.evalCodeAsync(` - const first = JSON.parse(addJson(JSON.stringify({ a: 1, b: 1 }))) - const second = JSON.parse(addJson(JSON.stringify({ a: first.sum, b: 40 }))) - - second.sum - `) - const value = context.unwrapResult(result) - - expect(context.getNumber(value)).toBe(42) - value.dispose() - } finally { - context.dispose() - } - }) - - test('asyncified host functions can also be used inside settled QuickJS promises', async () => { - const context = await newAsyncContext() - - try { - const addHandle = context.newAsyncifiedFunction('add', async (jsonHandle) => { - const { a, b } = JSON.parse(context.getString(jsonHandle)) as { a: number; b: number } - - return context.newString(JSON.stringify({ sum: a + b })) - }) - context.setProp(context.global, 'addJson', addHandle) - addHandle.dispose() - - const result = await context.evalCodeAsync(` - (async () => { - const first = JSON.parse(addJson(JSON.stringify({ a: 1, b: 1 }))) - const second = JSON.parse(addJson(JSON.stringify({ a: first.sum, b: 40 }))) - - return second.sum - })() - `) - const promiseHandle = context.unwrapResult(result) - const stateResult = context.getPromiseState(promiseHandle) - expect(stateResult.type).toBe('fulfilled') - if (stateResult.type !== 'fulfilled') throw new Error(`Expected fulfilled promise, got ${stateResult.type}`) - const value = stateResult.value - - expect(context.getNumber(value)).toBe(42) - value.dispose() - promiseHandle.dispose() - } finally { - context.dispose() - } - }) -}) diff --git a/agents/yjs-markdown-apply-agent/test/learning/quickjs-harness.test.ts b/agents/yjs-markdown-apply-agent/test/learning/quickjs-harness.test.ts deleted file mode 100644 index 4e9f7a8..0000000 --- a/agents/yjs-markdown-apply-agent/test/learning/quickjs-harness.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { describe, expect, test } from 'bun:test' -import { withAsyncQuickJsMode, withQuickJsMode } from '@humanlayer/quickjs-exec' -import * as Y from 'yjs' - -describe('quickjs yxml harness', () => { - test('sync harness mirrors withCodeMode setup shape', async () => { - await withQuickJsMode( - { - add: (a, b) => (a as number) + (b as number), - }, - ({ run }) => { - const result = run<{ sum: number }>(` - ({ - sum: bindings.add(20, 22), - }) - `) - - expect(result).toEqual({ sum: 42 }) - }, - ) - }) - - test('script with no final expression does not throw and returns undefined', async () => { - await withQuickJsMode({}, ({ run }) => { - const result = run(` - const x = 2 - `) - - expect(result).toBeUndefined() - }) - }) - - test('async harness supports async host bindings', async () => { - const doc = new Y.Doc() - const fragment = doc.getXmlFragment('quickjs-async') - - await withAsyncQuickJsMode( - { - addAsync: async (a, b) => (a as number) + (b as number), - fragmentLength: () => fragment.length, - }, - async ({ run }) => { - const result = await run<{ sum: number; length: number }>(` - ({ - sum: bindings.addAsync(20, 22), - length: bindings.fragmentLength(), - }) - `) - - expect(result).toEqual({ sum: 42, length: 0 }) - }, - ) - }) -}) diff --git a/agents/yjs-markdown-apply-agent/test/learning/yjs-secure-exec.test.ts b/agents/yjs-markdown-apply-agent/test/learning/yjs-secure-exec.test.ts deleted file mode 100644 index d64aac8..0000000 --- a/agents/yjs-markdown-apply-agent/test/learning/yjs-secure-exec.test.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { describe, expect, test } from 'bun:test' -import * as Y from 'yjs' -import { withCodeMode } from './proxy' -import { NodeExecutionDriver, type BindingTree } from 'secure-exec' - -describe('Secure-exec <> Y.js Learning tests', async () => { - test('Y.XMLFragment can be built normally', async () => { - const yDoc = new Y.Doc() - const yXmlFragment = yDoc.getXmlFragment('test-fragment') - - const yXmlElement = new Y.XmlElement('example-node') - yXmlFragment.insert(0, [yXmlElement]) - - expect(yXmlFragment.toJSON()).toEqual('') - }) - - test('Structured Clone-ability', async () => { - const yDoc = new Y.Doc() - const yXmlFragment = yDoc.getXmlFragment('test-fragment') - - const yXmlElement = new Y.XmlElement('example-node') - yXmlFragment.insert(0, [yXmlElement]) - - expect(yXmlFragment.toJSON()).toEqual('') - expect(yXmlFragment.get(0)).toEqual(yXmlElement) - expect(yXmlFragment.get(1)).toBeUndefined() - - expect(() => structuredClone(yXmlFragment)).toThrowError() - expect(() => structuredClone(yXmlElement)).toThrowError() - }) - - test('Secure-Exec Driver exposes basic custom bindings', async () => { - const yDoc = new Y.Doc() - const yXmlFragment = yDoc.getXmlFragment() - const bindings: BindingTree = { - add: (a, b) => (a as number) + (b as number), - } - - await withCodeMode(yXmlFragment, bindings, async (driver: NodeExecutionDriver) => { - const result = await driver.run(`module.exports = {a: SecureExec.bindings.add(1,1)}`) - expect(result.exports).toHaveProperty('a') - expect((result.exports as { a: number }).a).toEqual(2) - }) - }) - - test('fragment bindings can push nested content into the live fragment', async () => { - const yDoc = new Y.Doc() - const yXmlFragment = yDoc.getXmlFragment() - - await withCodeMode(yXmlFragment, {}, async (driver) => { - const result = await driver.run(` - const { fragment } = SecureExec.bindings - const refs = fragment.push([ - { - kind: 'element', - nodeName: 'section', - attributes: { id: 'intro' }, - children: [ - { - kind: 'element', - nodeName: 'paragraph', - children: [{ kind: 'text', text: 'Hello' }], - }, - ], - }, - ]) - - module.exports = { - refs, - length: fragment.length(), - xml: fragment.toJSON(), - } - `) - - expect(result.code).toBe(0) - expect(result.exports).toEqual({ - refs: [{ id: 'xml_1', kind: 'element' }], - length: 1, - xml: '
Hello
', - }) - }) - - expect(yXmlFragment.toJSON()).toBe('
Hello
') - }) - - test('element bindings can mutate multiple levels deep', async () => { - const yDoc = new Y.Doc() - const yXmlFragment = yDoc.getXmlFragment() - const section = new Y.XmlElement('section') - const paragraph = new Y.XmlElement('paragraph') - const text = new Y.XmlText() - text.insert(0, 'Hello') - paragraph.push([text]) - section.push([paragraph]) - yXmlFragment.push([section]) - - await withCodeMode(yXmlFragment, {}, async (driver) => { - const result = await driver.run(` - const { fragment, element, text } = SecureExec.bindings - - const section = fragment.get(0) - const paragraph = element.get(section, 0) - const paragraphText = element.get(paragraph, 0) - - text.insert(paragraphText, 5, ' world') - element.setAttribute(section, 'status', 'updated') - - module.exports = { - sectionName: element.nodeName(section), - text: text.toString(paragraphText), - xml: fragment.toJSON(), - } - `) - - expect(result.code).toBe(0) - expect(result.exports).toEqual({ - sectionName: 'section', - text: 'Hello world', - xml: '
Hello world
', - }) - }) - - expect(yXmlFragment.toJSON()).toBe('
Hello world
') - }) - - test('insertAfter works with refs returned from get at nested levels', async () => { - const yDoc = new Y.Doc() - const yXmlFragment = yDoc.getXmlFragment() - const section = new Y.XmlElement('section') - section.push([new Y.XmlElement('first')]) - yXmlFragment.push([section]) - - await withCodeMode(yXmlFragment, {}, async (driver) => { - const result = await driver.run(` - const { fragment, element } = SecureExec.bindings - - const section = fragment.get(0) - const first = element.get(section, 0) - const inserted = element.insertAfter(section, first, [ - { kind: 'element', nodeName: 'second', children: [{ kind: 'text', text: 'two' }] }, - ]) - - module.exports = { - inserted, - xml: fragment.toJSON(), - } - `) - - expect(result.code).toBe(0) - expect(result.exports).toEqual({ - inserted: [{ id: 'xml_3', kind: 'element' }], - xml: '
two
', - }) - }) - - expect(yXmlFragment.toJSON()).toBe('
two
') - }) -}) diff --git a/agents/yjs-markdown-apply-agent/test/learning/yjs-tanstack-code-mode.test.ts b/agents/yjs-markdown-apply-agent/test/learning/yjs-tanstack-code-mode.test.ts deleted file mode 100644 index e6db29e..0000000 --- a/agents/yjs-markdown-apply-agent/test/learning/yjs-tanstack-code-mode.test.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { describe, expect, test } from 'bun:test' -import { toolsToBindings, type ToolBinding } from '@tanstack/ai-code-mode' -import { createQuickJSIsolateDriver } from '@tanstack/ai-isolate-quickjs' -import { toolDefinition } from '@tanstack/ai' -import { z } from 'zod' -import * as Y from 'yjs' - -type QuickJSContext = Awaited['createContext']>> - -async function withQuickJSContext(bindings: Record, cb: (context: QuickJSContext) => Promise) { - const driver = createQuickJSIsolateDriver() - let context: QuickJSContext | undefined - - try { - context = await driver.createContext({ bindings, timeout: 5_000 }) - return await cb(context) - } finally { - await context?.dispose() - } -} - -describe('TanStack QuickJS isolate driver learning tests', () => { - test('driver can execute code without host bindings', async () => { - await withQuickJSContext({}, async (context) => { - const result = await context.execute(` - console.log('pure quickjs') - return { answer: 42 } - `) - - expect(result).toEqual({ - success: true, - value: { answer: 42 }, - logs: ['pure quickjs'], - }) - }) - }) - - test.todo('driver can create a context with direct bindings and execute code', async () => { - const bindings: Record = { - add: { - name: 'add', - description: 'Add two numbers together', - inputSchema: { - type: 'object', - properties: { - a: { type: 'number' }, - b: { type: 'number' }, - }, - required: ['a', 'b'], - }, - outputSchema: { - type: 'object', - properties: { sum: { type: 'number' } }, - required: ['sum'], - }, - execute: async (args) => { - const { a, b } = args as { a: number; b: number } - return { sum: a + b } - }, - }, - } - - await withQuickJSContext(bindings, async (context) => { - const result = await context.execute(` - const first = await add({ a: 1, b: 1 }) - const second = await add({ a: first.sum, b: 40 }) - console.log('computed', second.sum) - - return { answer: second.sum } - `) - - expect(result).toEqual({ - success: true, - value: { answer: 42 }, - logs: ['computed 42'], - }) - }) - }) - - test.todo('direct bindings can mutate a live Y.XmlFragment', async () => { - const yDoc = new Y.Doc() - const fragment = yDoc.getXmlFragment('tanstack-direct-bindings') - - const bindings: Record = { - pushElement: { - name: 'pushElement', - description: 'Append an XML element with text to the shared Y.XmlFragment', - inputSchema: { - type: 'object', - properties: { - nodeName: { type: 'string' }, - text: { type: 'string' }, - attributes: { - type: 'object', - additionalProperties: { type: 'string' }, - }, - }, - required: ['nodeName', 'text'], - }, - outputSchema: { - type: 'object', - properties: { - xml: { type: 'string' }, - length: { type: 'number' }, - }, - required: ['xml', 'length'], - }, - execute: async (args) => { - const { nodeName, text, attributes } = args as { - nodeName: string - text: string - attributes?: Record - } - const element = new Y.XmlElement(nodeName) - for (const [name, value] of Object.entries(attributes ?? {})) { - element.setAttribute(name, value) - } - - const xmlText = new Y.XmlText() - xmlText.insert(0, text) - element.push([xmlText]) - fragment.push([element]) - - return { xml: fragment.toJSON(), length: fragment.length } - }, - }, - readFragment: { - name: 'readFragment', - description: 'Read the shared Y.XmlFragment', - inputSchema: { type: 'object', properties: {} }, - outputSchema: { - type: 'object', - properties: { - xml: { type: 'string' }, - length: { type: 'number' }, - }, - required: ['xml', 'length'], - }, - execute: async () => ({ xml: fragment.toJSON(), length: fragment.length }), - }, - } - - await withQuickJSContext(bindings, async (context) => { - const result = await context.execute(` - await pushElement({ - nodeName: 'section', - text: 'Hello from QuickJS', - attributes: { id: 'intro' }, - }) - - await pushElement({ - nodeName: 'paragraph', - text: 'Bound directly through the isolate driver', - }) - - return await readFragment({}) - `) - - expect(result).toEqual({ - success: true, - value: { - length: 2, - xml: '
Hello from QuickJS
Bound directly through the isolate driver', - }, - logs: [], - }) - expect(fragment.toJSON()).toBe( - '
Hello from QuickJS
Bound directly through the isolate driver', - ) - }) - }) - - test.todo('TanStack tool definitions can be converted to direct isolate bindings', async () => { - const add = toolDefinition({ - name: 'add', - description: 'Add two numbers together', - inputSchema: z.object({ a: z.number(), b: z.number() }), - outputSchema: z.object({ sum: z.number() }), - }).server(({ a, b }) => ({ sum: a + b })) - - await withQuickJSContext(toolsToBindings([add], 'external_'), async (context) => { - const result = await context.execute(` - const answer = await external_add({ a: 20, b: 22 }) - return answer - `) - - expect(result).toEqual({ - success: true, - value: { sum: 42 }, - logs: [], - }) - }) - }) -}) diff --git a/agents/yjs-markdown-apply-agent/test/prompt-cache-shape.test.ts b/agents/yjs-markdown-apply-agent/test/prompt-cache-shape.test.ts deleted file mode 100644 index 4fec4b7..0000000 --- a/agents/yjs-markdown-apply-agent/test/prompt-cache-shape.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { describe, expect, test } from 'bun:test' -import { APPLY_AGENT_SYSTEM_PROMPT, buildAbortSystemInformation, buildApplyUserMessage } from '../src/prompts' -import type { ApplyAttemptAbort, SecureApplyInput } from '../src/types' - -const baseInput: SecureApplyInput = { - path: '/artifacts/plan.md', - oldString: 'old', - newString: 'new', - currentMarkdown: '# Old', - currentXml: 'Old', - snapshotBase64: 'snapshot-1', - baseStateVectorBase64: 'state-vector-1', - artifactFragmentName: 'artifact:/artifacts/plan.md', - baseArtifactRevision: 1, -} - -describe('apply prompt cache shape', () => { - test('keeps stable system prompt separate from dynamic document state', () => { - const first = buildApplyUserMessage(baseInput) - const second = buildApplyUserMessage({ - ...baseInput, - currentMarkdown: '# Newer', - currentXml: 'Newer', - snapshotBase64: 'snapshot-2', - baseStateVectorBase64: 'state-vector-2', - baseArtifactRevision: 2, - }) - - expect(APPLY_AGENT_SYSTEM_PROMPT).toContain('Stable contract') - expect(APPLY_AGENT_SYSTEM_PROMPT).not.toContain('snapshot-1') - expect(first).toContain('snapshot-1') - expect(second).toContain('snapshot-2') - expect(first).not.toEqual(second) - }) - - test('adds system-information to retry user message', () => { - const abort: ApplyAttemptAbort = { - reason: 'target-fragment-changed', - path: '/artifacts/plan.md', - previousRevision: 1, - currentRevision: 2, - changeSummary: ['external insert'], - updatedMarkdown: '# External', - updatedXml: 'External', - snapshotBase64: 'snapshot-2', - baseStateVectorBase64: 'state-vector-2', - } - - const systemInformation = buildAbortSystemInformation(abort) - const retry = buildApplyUserMessage({ - ...baseInput, - systemInformation, - currentMarkdown: abort.updatedMarkdown, - currentXml: abort.updatedXml, - snapshotBase64: abort.snapshotBase64, - baseStateVectorBase64: abort.baseStateVectorBase64, - baseArtifactRevision: abort.currentRevision, - }) - - expect(retry).toContain('') - expect(retry).toContain('previous secure apply attempt was aborted') - expect(retry).toContain('snapshot-2') - }) -}) diff --git a/agents/yjs-markdown-apply-agent/test/retry-controller.test.ts b/agents/yjs-markdown-apply-agent/test/retry-controller.test.ts deleted file mode 100644 index ccc5beb..0000000 --- a/agents/yjs-markdown-apply-agent/test/retry-controller.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { describe, expect, test } from 'bun:test' -import { nextRetryDecision } from '../src/retry-controller' - -describe('apply retry controller', () => { - test('backs off with a quiet-period floor', () => { - expect( - nextRetryDecision({ - attempt: 1, - policy: { initialBackoffMs: 100, waitForQuietMs: 750, maxBackoffMs: 5000 }, - }), - ).toEqual({ type: 'retry', attempt: 2, delayMs: 750 }) - }) - - test('caps retry delay', () => { - expect( - nextRetryDecision({ - attempt: 5, - policy: { maxAttempts: 10, initialBackoffMs: 1000, waitForQuietMs: 100, maxBackoffMs: 1500 }, - }), - ).toEqual({ type: 'retry', attempt: 6, delayMs: 1500 }) - }) - - test('fails after retry budget is exhausted', () => { - expect(nextRetryDecision({ attempt: 3, policy: { maxAttempts: 3 } })).toEqual({ - type: 'fail', - reason: 'max-attempts-exceeded', - }) - }) - - test('fails after quiet wait budget is exhausted', () => { - expect(nextRetryDecision({ attempt: 1, waitedForQuietMs: 11_000, policy: { maxWaitForQuietMs: 10_000 } })).toEqual({ - type: 'fail', - reason: 'quiet-timeout-exceeded', - }) - }) -}) diff --git a/agents/yjs-markdown-apply-agent/test/secure-apply-contract.test.ts b/agents/yjs-markdown-apply-agent/test/secure-apply-contract.test.ts deleted file mode 100644 index bcc7cbc..0000000 --- a/agents/yjs-markdown-apply-agent/test/secure-apply-contract.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, expect, test } from 'bun:test' -import type { LanguageModel } from 'ai' -import { createYjsMarkdownApplyAgent } from '../src/agent' -import { createFakeSecureApplyExecutor, secureApplyResultSchema } from '../src/secure-apply-tool' - -function createMockModel(): LanguageModel { - return { - specificationVersion: 'v3', - provider: 'mock', - modelId: 'mock-apply-model', - supportedUrls: {}, - async doGenerate() { - return { - content: [{ type: 'text', text: 'ok' }], - finishReason: { unified: 'stop', raw: 'stop' }, - usage: { - inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 }, - outputTokens: { total: 0, text: 0, reasoning: 0 }, - }, - warnings: [], - } - }, - async doStream() { - throw new Error('streaming not supported in test') - }, - } as LanguageModel -} - -describe('secure apply contract', () => { - test('validates secure apply result shape', () => { - const result = secureApplyResultSchema.parse({ - ok: true, - proposedUpdateBase64: 'update', - changedArtifacts: ['/artifacts/plan.md'], - }) - - expect(result.ok).toBe(true) - }) - - test('creates an agentlayer-core agent with the secure apply tool', () => { - const agent = createYjsMarkdownApplyAgent({ - model: createMockModel(), - executor: createFakeSecureApplyExecutor({ ok: true, proposedUpdateBase64: 'update' }), - }) as unknown as { tools?: Record; system?: string } - - expect(agent.tools?.secure_apply_yjs_update).toBeDefined() - expect(agent.system).toContain('Yjs Markdown apply agent') - }) -}) diff --git a/agents/yjs-markdown-apply-agent/tsconfig.json b/agents/yjs-markdown-apply-agent/tsconfig.json deleted file mode 100644 index 7cb9983..0000000 --- a/agents/yjs-markdown-apply-agent/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "lib": ["ESNext"], - "target": "ESNext", - "module": "Preserve", - "moduleDetection": "force", - "jsx": "react-jsx", - "allowJs": true, - "types": ["bun"], - "moduleResolution": "bundler", - "customConditions": ["source"], - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "noEmit": true, - "strict": true, - "skipLibCheck": true, - "noFallthroughCasesInSwitch": true, - "noUncheckedIndexedAccess": true, - "noImplicitOverride": true, - "noUnusedLocals": false, - "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false - } -} diff --git a/bun.lock b/bun.lock index 583c216..a6a37f8 100644 --- a/bun.lock +++ b/bun.lock @@ -344,6 +344,7 @@ "@tiptap/markdown": "3.22.5", "@tiptap/pm": "3.22.5", "@tiptap/starter-kit": "3.22.5", + "dedent": "1.7.2", "y-prosemirror": "1.3.7", }, "devDependencies": { diff --git a/examples/yjs-rich-markdown-demo/package.json b/examples/yjs-rich-markdown-demo/package.json index 1d647c6..66fa816 100644 --- a/examples/yjs-rich-markdown-demo/package.json +++ b/examples/yjs-rich-markdown-demo/package.json @@ -23,7 +23,7 @@ "@tanstack/react-router": "^1.93.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "yjs": "13.6.30", + "yjs": "^13.6.30", "y-protocols": "1.0.7" }, "devDependencies": { @@ -34,4 +34,4 @@ "typescript": "^5.6.3", "vite": "^6.0.0" } -} +} \ No newline at end of file diff --git a/packages/agentlayer-core/models.json b/packages/agentlayer-core/models.json index e4ec7e4..ac40f0c 100644 --- a/packages/agentlayer-core/models.json +++ b/packages/agentlayer-core/models.json @@ -4187,6 +4187,37 @@ "output": 8192 } }, + "qwen3.6-max-preview": { + "id": "qwen3.6-max-preview", + "name": "Qwen3.6 Max Preview", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-04-20", + "last_updated": "2026-04-20", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.3, + "output": 7.8, + "cache_read": 0.13, + "cache_write": 1.625 + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, "qwen2-5-omni-7b": { "id": "qwen2-5-omni-7b", "name": "Qwen2.5-Omni 7B", @@ -26205,6 +26236,35 @@ "output": 65536 } }, + "openrouter/owl-alpha": { + "id": "openrouter/owl-alpha", + "name": "Owl Alpha", + "attachment": false, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-04-30", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0, + "output": 0 + }, + "limit": { + "context": 1048756, + "output": 262144 + }, + "status": "alpha" + }, "openrouter/pareto-code": { "id": "openrouter/pareto-code", "name": "Pareto Code Router", @@ -27362,6 +27422,70 @@ "output": 8192 } }, + "poolside/laguna-m.1:free": { + "id": "poolside/laguna-m.1:free", + "name": "Laguna M.1", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-04-28", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "poolside/laguna-xs.2:free": { + "id": "poolside/laguna-xs.2:free", + "name": "Laguna XS.2", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-04-28", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, "prime-intellect/intellect-3": { "id": "prime-intellect/intellect-3", "name": "Intellect 3", @@ -48564,42 +48688,6 @@ "npm": "@ai-sdk/openai-compatible" } }, - "glm-5": { - "id": "glm-5", - "name": "GLM-5", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "interleaved": { - "field": "reasoning_content" - }, - "structured_output": true, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 1, - "output": 3.2, - "cache_read": 0.2 - }, - "limit": { - "context": 202752, - "output": 131000 - }, - "provider": { - "npm": "@ai-sdk/openai-compatible" - } - }, "gemini-3.1-pro-preview": { "id": "gemini-3.1-pro-preview", "name": "Gemini 3.1 Pro Preview", @@ -48914,6 +49002,85 @@ "context": 400000, "output": 128000 } + }, + "gpt-5.5": { + "id": "gpt-5.5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-12-01", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5, + "context_over_200k": { + "input": 10, + "output": 45, + "cache_read": 1 + } + }, + "limit": { + "context": 1050000, + "input": 922000, + "output": 128000 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + } + }, + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.145 + }, + "limit": { + "context": 1000000, + "output": 384000 + }, + "provider": { + "npm": "@ai-sdk/openai-compatible" + } } } }, @@ -52481,6 +52648,34 @@ "output": 8192 } }, + "ibm-granite/granite-4.1-8b": { + "id": "ibm-granite/granite-4.1-8b", + "name": "IBM: Granite 4.1 8B", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-30", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.05, + "output": 0.1, + "cache_read": 0.05 + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, "ibm-granite/granite-4.0-h-micro": { "id": "ibm-granite/granite-4.0-h-micro", "name": "IBM: Granite 4.0 Micro", @@ -52866,6 +53061,62 @@ "output": 65536 } }, + "deepseek/deepseek-v4-flash": { + "id": "deepseek/deepseek-v4-flash", + "name": "DeepSeek: DeepSeek V4 Flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-24", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.14, + "output": 0.28, + "cache_read": 0.0028 + }, + "limit": { + "context": 1048576, + "output": 384000 + } + }, + "deepseek/deepseek-v4-pro": { + "id": "deepseek/deepseek-v4-pro", + "name": "DeepSeek: DeepSeek V4 Pro", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-24", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.435, + "output": 0.87, + "cache_read": 0.003625 + }, + "limit": { + "context": 1048576, + "output": 384000 + } + }, "deepseek/deepseek-v3.2": { "id": "deepseek/deepseek-v3.2", "name": "DeepSeek: DeepSeek V3.2", @@ -53037,16 +53288,16 @@ }, "status": "beta" }, - "openrouter/elephant-alpha": { - "id": "openrouter/elephant-alpha", - "name": "Elephant (new)", + "openrouter/owl-alpha": { + "id": "openrouter/owl-alpha", + "name": "Owl Alpha", "attachment": false, "reasoning": true, "tool_call": true, "structured_output": true, "temperature": true, - "release_date": "2026-01-15", - "last_updated": "2026-04-13", + "release_date": "2026-04-28", + "last_updated": "2026-04-30", "modalities": { "input": [ "text" @@ -53061,11 +53312,38 @@ "output": 0 }, "limit": { - "context": 262144, - "output": 32768 + "context": 1048756, + "output": 262144 }, "status": "alpha" }, + "openrouter/pareto-code": { + "id": "openrouter/pareto-code", + "name": "Pareto Code Router", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": false, + "release_date": "2026-04-21", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0, + "output": 0 + }, + "limit": { + "context": 200000, + "output": 65536 + } + }, "openrouter/free": { "id": "openrouter/free", "name": "Free Models Router", @@ -53094,6 +53372,61 @@ "output": 32768 } }, + "inclusionai/ling-2.6-1t:free": { + "id": "inclusionai/ling-2.6-1t:free", + "name": "inclusionAI: Ling-2.6-1T (free)", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-23", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0, + "output": 0 + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + "inclusionai/ling-2.6-flash": { + "id": "inclusionai/ling-2.6-flash", + "name": "inclusionAI: Ling-2.6 Flash", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-21", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.08, + "output": 0.24, + "cache_read": 0.016 + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, "arcee-ai/trinity-mini": { "id": "arcee-ai/trinity-mini", "name": "Arcee AI: Trinity Mini", @@ -53203,33 +53536,6 @@ "output": 65537 } }, - "arcee-ai/trinity-large-thinking:free": { - "id": "arcee-ai/trinity-large-thinking:free", - "name": "Arcee AI: Trinity Large Thinking (free)", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-08-26", - "last_updated": "2026-04-11", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0, - "output": 0 - }, - "limit": { - "context": 262144, - "output": 262144 - } - }, "arcee-ai/maestro-reasoning": { "id": "arcee-ai/maestro-reasoning", "name": "Arcee AI: Maestro Reasoning", @@ -53284,6 +53590,33 @@ "output": 32768 } }, + "arcee-ai/trinity-large-preview": { + "id": "arcee-ai/trinity-large-preview", + "name": "Arcee AI: Trinity Large Preview", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-01-28", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.15, + "output": 0.45 + }, + "limit": { + "context": 131000, + "output": 32768 + } + }, "deepcogito/cogito-v2.1-671b": { "id": "deepcogito/cogito-v2.1-671b", "name": "Deep Cogito: Cogito v2.1 671B", @@ -53365,34 +53698,6 @@ "output": 163840 } }, - "bytedance-seed/dola-seed-2.0-pro:free": { - "id": "bytedance-seed/dola-seed-2.0-pro:free", - "name": "ByteDance Seed: Dola Seed 2.0 Pro (free)", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-08-26", - "last_updated": "2026-04-11", - "modalities": { - "input": [ - "image", - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0, - "output": 0 - }, - "limit": { - "context": 256000, - "output": 128000 - } - }, "bytedance-seed/seed-1.6": { "id": "bytedance-seed/seed-1.6", "name": "ByteDance Seed: Seed 1.6", @@ -53563,6 +53868,72 @@ "output": 2048 } }, + "~google/gemini-pro-latest": { + "id": "~google/gemini-pro-latest", + "name": "Google: Gemini Pro Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2, + "output": 12, + "cache_read": 0.2, + "cache_write": 0.375 + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + "~google/gemini-flash-latest": { + "id": "~google/gemini-flash-latest", + "name": "Google: Gemini Flash Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.5, + "output": 3, + "cache_read": 0.05, + "cache_write": 0.08333333333333334 + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, "kilo-auto/balanced": { "id": "kilo-auto/balanced", "name": "Kilo Auto Balanced", @@ -53700,32 +54071,6 @@ "output": 4096 } }, - "allenai/olmo-2-0325-32b-instruct": { - "id": "allenai/olmo-2-0325-32b-instruct", - "name": "AllenAI: Olmo 2 32B Instruct", - "attachment": false, - "reasoning": false, - "tool_call": false, - "release_date": "2025-03-15", - "last_updated": "2026-03-15", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.05, - "output": 0.2 - }, - "limit": { - "context": 128000, - "output": 32768 - } - }, "allenai/olmo-3.1-32b-instruct": { "id": "allenai/olmo-3.1-32b-instruct", "name": "AllenAI: Olmo 3.1 32B Instruct", @@ -53970,15 +54315,15 @@ "output": 131072 } }, - "eleutherai/llemma_7b": { - "id": "eleutherai/llemma_7b", - "name": "EleutherAI: Llemma 7b", + "stepfun/step-3.5-flash:free": { + "id": "stepfun/step-3.5-flash:free", + "name": "StepFun: Step 3.5 Flash (free)", "attachment": false, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-04-14", - "last_updated": "2026-03-15", + "release_date": "2025-08-26", + "last_updated": "2026-05-01", "modalities": { "input": [ "text" @@ -53987,14 +54332,14 @@ "text" ] }, - "open_weights": true, + "open_weights": false, "cost": { - "input": 0.8, - "output": 1.2 + "input": 0, + "output": 0 }, "limit": { - "context": 4096, - "output": 4096 + "context": 262144, + "output": 262144 } }, "stepfun/step-3.5-flash": { @@ -54606,32 +54951,6 @@ "output": 131072 } }, - "mistralai/mistral-small-creative": { - "id": "mistralai/mistral-small-creative", - "name": "Mistral: Mistral Small Creative", - "attachment": false, - "reasoning": false, - "tool_call": true, - "release_date": "2025-12-17", - "last_updated": "2026-03-15", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.1, - "output": 0.3 - }, - "limit": { - "context": 32768, - "output": 32768 - } - }, "mistralai/devstral-small": { "id": "mistralai/devstral-small", "name": "Mistral: Devstral Small 1.1", @@ -54741,6 +55060,99 @@ "output": 52429 } }, + "~anthropic/claude-haiku-latest": { + "id": "~anthropic/claude-haiku-latest", + "name": "Anthropic: Claude Haiku Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1, + "output": 5, + "cache_read": 0.1, + "cache_write": 1.25 + }, + "limit": { + "context": 200000, + "output": 64000 + } + }, + "~anthropic/claude-sonnet-latest": { + "id": "~anthropic/claude-sonnet-latest", + "name": "Anthropic: Claude Sonnet Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3, + "cache_write": 3.75 + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "~anthropic/claude-opus-latest": { + "id": "~anthropic/claude-opus-latest", + "name": "Anthropic: Claude Opus Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-16", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, "meta-llama/llama-3.3-70b-instruct": { "id": "meta-llama/llama-3.3-70b-instruct", "name": "Meta: Llama 3.3 70B Instruct", @@ -55126,6 +55538,36 @@ "output": 10000 } }, + "x-ai/grok-4.3": { + "id": "x-ai/grok-4.3", + "name": "xAI: Grok 4.3", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-01", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2 + }, + "limit": { + "context": 1000000, + "output": 4096 + } + }, "x-ai/grok-4-fast": { "id": "x-ai/grok-4-fast", "name": "xAI: Grok 4 Fast", @@ -55383,6 +55825,33 @@ "output": 26215 } }, + "tencent/hy3-preview:free": { + "id": "tencent/hy3-preview:free", + "name": "Tencent: Hy3 Preview (free)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-22", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0, + "output": 0 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, "tencent/hunyuan-a13b-instruct": { "id": "tencent/hunyuan-a13b-instruct", "name": "Tencent: Hunyuan A13B Instruct", @@ -55626,6 +56095,60 @@ "output": 16384 } }, + "poolside/laguna-m.1:free": { + "id": "poolside/laguna-m.1:free", + "name": "Poolside: Laguna M.1 (free)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0, + "output": 0 + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, + "poolside/laguna-xs.2:free": { + "id": "poolside/laguna-xs.2:free", + "name": "Poolside: Laguna XS.2 (free)", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0, + "output": 0 + }, + "limit": { + "context": 131072, + "output": 8192 + } + }, "cohere/command-r7b-12-2024": { "id": "cohere/command-r7b-12-2024", "name": "Cohere: Command R7B (12-2024)", @@ -55816,42 +56339,45 @@ "output": 262144 } }, - "nvidia/nemotron-3-nano-30b-a3b": { - "id": "nvidia/nemotron-3-nano-30b-a3b", - "name": "NVIDIA: Nemotron 3 Nano 30B A3B", - "attachment": false, + "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free": { + "id": "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free", + "name": "NVIDIA: Nemotron 3 Nano Omni (free)", + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-12", - "last_updated": "2026-02-04", + "release_date": "2026-04-28", + "last_updated": "2026-05-01", "modalities": { "input": [ - "text" + "text", + "audio", + "image", + "video" ], "output": [ "text" ] }, - "open_weights": true, + "open_weights": false, "cost": { - "input": 0.05, - "output": 0.2 + "input": 0, + "output": 0 }, "limit": { - "context": 262144, - "output": 52429 + "context": 256000, + "output": 65536 } }, - "nvidia/llama-3.1-nemotron-ultra-253b-v1": { - "id": "nvidia/llama-3.1-nemotron-ultra-253b-v1", - "name": "NVIDIA: Llama 3.1 Nemotron Ultra 253B v1", + "nvidia/nemotron-3-nano-30b-a3b": { + "id": "nvidia/nemotron-3-nano-30b-a3b", + "name": "NVIDIA: Nemotron 3 Nano 30B A3B", "attachment": false, "reasoning": true, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2025-04-08", - "last_updated": "2026-04-11", + "release_date": "2024-12", + "last_updated": "2026-02-04", "modalities": { "input": [ "text" @@ -55862,12 +56388,12 @@ }, "open_weights": true, "cost": { - "input": 0.6, - "output": 1.8 + "input": 0.05, + "output": 0.2 }, "limit": { - "context": 131072, - "output": 131072 + "context": 262144, + "output": 52429 } }, "nvidia/nemotron-nano-12b-v2-vl": { @@ -55980,34 +56506,6 @@ "output": 16384 } }, - "inception/mercury-edit-2": { - "id": "inception/mercury-edit-2", - "name": "Mercury Edit 2", - "attachment": false, - "reasoning": true, - "tool_call": false, - "temperature": true, - "release_date": "2026-03-30", - "last_updated": "2026-03-30", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.25, - "output": 0.75, - "cache_read": 0.025 - }, - "limit": { - "context": 128000, - "output": 8192 - } - }, "inception/mercury-2": { "id": "inception/mercury-2", "name": "Inception: Mercury 2", @@ -56294,6 +56792,36 @@ "output": 16384 } }, + "openai/gpt-5.5": { + "id": "openai/gpt-5.5", + "name": "OpenAI: GPT-5.5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-24", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + }, + "limit": { + "context": 1050000, + "output": 128000 + } + }, "openai/gpt-5-mini": { "id": "openai/gpt-5-mini", "name": "OpenAI: GPT-5 Mini", @@ -57081,6 +57609,37 @@ "output": 100000 } }, + "openai/gpt-5.4-image-2": { + "id": "openai/gpt-5.4-image-2", + "name": "OpenAI: GPT-5.4 Image 2", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": false, + "release_date": "2026-04-21", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "image", + "text", + "pdf" + ], + "output": [ + "image", + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 8, + "output": 15, + "cache_read": 2 + }, + "limit": { + "context": 272000, + "output": 128000 + } + }, "openai/gpt-4": { "id": "openai/gpt-4", "name": "OpenAI: GPT-4", @@ -57221,35 +57780,6 @@ "output": 16384 } }, - "openai/gpt-4o:extended": { - "id": "openai/gpt-4o:extended", - "name": "OpenAI: GPT-4o (extended)", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2024-05-13", - "last_updated": "2026-03-15", - "modalities": { - "input": [ - "image", - "pdf", - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 6, - "output": 18 - }, - "limit": { - "context": 128000, - "output": 64000 - } - }, "openai/gpt-4o-search-preview": { "id": "openai/gpt-4o-search-preview", "name": "OpenAI: GPT-4o Search Preview", @@ -57621,6 +58151,35 @@ "output": 26215 } }, + "openai/gpt-5.5-pro": { + "id": "openai/gpt-5.5-pro", + "name": "OpenAI: GPT-5.5 Pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-24", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 30, + "output": 180 + }, + "limit": { + "context": 1050000, + "output": 128000 + } + }, "openai/gpt-3.5-turbo-instruct": { "id": "openai/gpt-3.5-turbo-instruct", "name": "OpenAI: GPT-3.5 Turbo Instruct", @@ -58273,6 +58832,34 @@ "output": 16000 } }, + "baidu/qianfan-ocr-fast:free": { + "id": "baidu/qianfan-ocr-fast:free", + "name": "Baidu: Qianfan-OCR-Fast (free)", + "attachment": true, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2026-04-20", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "image", + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0, + "output": 0 + }, + "limit": { + "context": 65536, + "output": 28672 + } + }, "baidu/ernie-4.5-vl-28b-a3b": { "id": "baidu/ernie-4.5-vl-28b-a3b", "name": "Baidu: ERNIE 4.5 VL 28B A3B", @@ -58630,6 +59217,66 @@ "output": 196608 } }, + "~openai/gpt-latest": { + "id": "~openai/gpt-latest", + "name": "OpenAI: GPT Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 5, + "output": 30, + "cache_read": 0.5 + }, + "limit": { + "context": 1050000, + "output": 128000 + } + }, + "~openai/gpt-mini-latest": { + "id": "~openai/gpt-mini-latest", + "name": "OpenAI: GPT Mini Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, "qwen/qwen3-235b-a22b": { "id": "qwen/qwen3-235b-a22b", "name": "Qwen: Qwen3 235B A22B", @@ -58687,15 +59334,15 @@ "output": 65536 } }, - "qwen/qwen2.5-coder-7b-instruct": { - "id": "qwen/qwen2.5-coder-7b-instruct", - "name": "Qwen: Qwen2.5 Coder 7B Instruct", + "qwen/qwen3-coder-plus": { + "id": "qwen/qwen3-coder-plus", + "name": "Qwen: Qwen3 Coder Plus", "attachment": false, "reasoning": false, - "tool_call": false, + "tool_call": true, "temperature": true, - "release_date": "2024-09-17", - "last_updated": "2024-11", + "release_date": "2025-07-01", + "last_updated": "2026-03-15", "modalities": { "input": [ "text" @@ -58706,39 +59353,41 @@ }, "open_weights": true, "cost": { - "input": 0.03, - "output": 0.09 + "input": 0.65, + "output": 3.25, + "cache_read": 0.2 }, "limit": { - "context": 32768, - "output": 6554 + "context": 1000000, + "output": 65536 } }, - "qwen/qwen3-coder-plus": { - "id": "qwen/qwen3-coder-plus", - "name": "Qwen: Qwen3 Coder Plus", - "attachment": false, - "reasoning": false, + "qwen/qwen3.6-27b": { + "id": "qwen/qwen3.6-27b", + "name": "Qwen: Qwen3.6 27B", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-07-01", - "last_updated": "2026-03-15", + "release_date": "2026-04-27", + "last_updated": "2026-05-01", "modalities": { "input": [ - "text" + "text", + "image", + "video" ], "output": [ "text" ] }, - "open_weights": true, + "open_weights": false, "cost": { - "input": 0.65, - "output": 3.25, - "cache_read": 0.2 + "input": 0.325, + "output": 3.25 }, "limit": { - "context": 1000000, + "context": 256000, "output": 65536 } }, @@ -58997,42 +59646,44 @@ "output": 32768 } }, - "qwen/qwen3-vl-235b-a22b-thinking": { - "id": "qwen/qwen3-vl-235b-a22b-thinking", - "name": "Qwen: Qwen3 VL 235B A22B Thinking", + "qwen/qwen3.6-35b-a3b": { + "id": "qwen/qwen3.6-35b-a3b", + "name": "Qwen: Qwen3.6 35B A3B", "attachment": true, "reasoning": true, - "tool_call": true, + "tool_call": false, "temperature": true, - "release_date": "2025-09-24", - "last_updated": "2026-03-15", + "release_date": "2026-04-27", + "last_updated": "2026-05-01", "modalities": { "input": [ + "text", "image", - "text" + "video" ], "output": [ "text" ] }, - "open_weights": true, + "open_weights": false, "cost": { - "input": 0.26, - "output": 2.6 + "input": 0.1612, + "output": 0.96525, + "cache_read": 0.1612 }, "limit": { - "context": 131072, - "output": 32768 + "context": 262144, + "output": 65536 } }, - "qwen/qwen3-vl-30b-a3b-thinking": { - "id": "qwen/qwen3-vl-30b-a3b-thinking", - "name": "Qwen: Qwen3 VL 30B A3B Thinking", + "qwen/qwen3-vl-235b-a22b-thinking": { + "id": "qwen/qwen3-vl-235b-a22b-thinking", + "name": "Qwen: Qwen3 VL 235B A22B Thinking", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-10-11", + "release_date": "2025-09-24", "last_updated": "2026-03-15", "modalities": { "input": [ @@ -59045,22 +59696,22 @@ }, "open_weights": true, "cost": { - "input": 0.13, - "output": 1.56 + "input": 0.26, + "output": 2.6 }, "limit": { "context": 131072, "output": 32768 } }, - "qwen/qwen2.5-vl-32b-instruct": { - "id": "qwen/qwen2.5-vl-32b-instruct", - "name": "Qwen: Qwen2.5 VL 32B Instruct", + "qwen/qwen3-vl-30b-a3b-thinking": { + "id": "qwen/qwen3-vl-30b-a3b-thinking", + "name": "Qwen: Qwen3 VL 30B A3B Thinking", "attachment": true, - "reasoning": false, - "tool_call": false, + "reasoning": true, + "tool_call": true, "temperature": true, - "release_date": "2025-03-24", + "release_date": "2025-10-11", "last_updated": "2026-03-15", "modalities": { "input": [ @@ -59073,13 +59724,12 @@ }, "open_weights": true, "cost": { - "input": 0.2, - "output": 0.6, - "cache_read": 0.025 + "input": 0.13, + "output": 1.56 }, "limit": { - "context": 128000, - "output": 16384 + "context": 131072, + "output": 32768 } }, "qwen/qwen3-vl-8b-instruct": { @@ -59472,33 +60122,6 @@ "output": 40960 } }, - "qwen/qwq-32b": { - "id": "qwen/qwq-32b", - "name": "Qwen: QwQ 32B", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2024-11-28", - "last_updated": "2025-04-11", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.15, - "output": 0.4 - }, - "limit": { - "context": 32768, - "output": 32768 - } - }, "qwen/qwen3-next-80b-a3b-instruct": { "id": "qwen/qwen3-next-80b-a3b-instruct", "name": "Qwen: Qwen3 Next 80B A3B Instruct", @@ -59526,6 +60149,35 @@ "output": 52429 } }, + "qwen/qwen3.5-plus-20260420": { + "id": "qwen/qwen3.5-plus-20260420", + "name": "Qwen: Qwen3.5 Plus 2026-04-20", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.4, + "output": 2.4 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, "qwen/qwen3-coder-next": { "id": "qwen/qwen3-coder-next", "name": "Qwen: Qwen3 Coder Next", @@ -59806,6 +60458,34 @@ "output": 32768 } }, + "qwen/qwen3.6-max-preview": { + "id": "qwen/qwen3.6-max-preview", + "name": "Qwen: Qwen3.6 Max Preview", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.04, + "output": 6.24, + "cache_write": 1.3 + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, "qwen/qwen-plus-2025-07-28:thinking": { "id": "qwen/qwen-plus-2025-07-28:thinking", "name": "Qwen: Qwen Plus 0728 (thinking)", @@ -59946,6 +60626,36 @@ "output": 65536 } }, + "qwen/qwen3.6-flash": { + "id": "qwen/qwen3.6-flash", + "name": "Qwen: Qwen3.6 Flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.25, + "output": 1.5, + "cache_write": 0.3125 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, "alfredpros/codellama-7b-instruct-solidity": { "id": "alfredpros/codellama-7b-instruct-solidity", "name": "AlfredPros: CodeLLaMa 7B Instruct Solidity", @@ -60351,33 +61061,6 @@ "output": 65536 } }, - "google/gemma-2-9b-it": { - "id": "google/gemma-2-9b-it", - "name": "Google: Gemma 2 9B", - "attachment": false, - "reasoning": false, - "tool_call": false, - "temperature": true, - "release_date": "2024-06-28", - "last_updated": "2024-06-28", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.03, - "output": 0.09 - }, - "limit": { - "context": 8192, - "output": 1639 - } - }, "google/gemini-3-pro-image-preview": { "id": "google/gemini-3-pro-image-preview", "name": "Google: Nano Banana Pro (Gemini 3 Pro Image Preview)", @@ -61021,6 +61704,35 @@ "output": 32768 } }, + "~moonshotai/kimi-latest": { + "id": "~moonshotai/kimi-latest", + "name": "MoonshotAI: Kimi Latest", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.74, + "output": 3.49, + "cache_read": 0.14 + }, + "limit": { + "context": 262142, + "output": 262142 + } + }, "thedrummer/unslopnemo-12b": { "id": "thedrummer/unslopnemo-12b", "name": "TheDrummer: UnslopNemo 12B", @@ -61284,6 +61996,37 @@ "output": 128000 } }, + "anthropic/claude-opus-4.7": { + "id": "anthropic/claude-opus-4.7", + "name": "Anthropic: Claude Opus 4.7", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-04-16", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 5, + "output": 25, + "cache_read": 0.5, + "cache_write": 6.25 + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, "anthropic/claude-sonnet-4": { "id": "anthropic/claude-sonnet-4", "name": "Anthropic: Claude Sonnet 4", @@ -61770,34 +62513,6 @@ "context": 163840, "output": 163840 } - }, - "meituan/longcat-flash-chat": { - "id": "meituan/longcat-flash-chat", - "name": "Meituan: LongCat Flash Chat", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2025-08-30", - "last_updated": "2026-03-15", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.2, - "output": 0.8, - "cache_read": 0.2 - }, - "limit": { - "context": 131072, - "output": 131072 - } } } }, @@ -76894,8 +77609,8 @@ }, "open_weights": true, "limit": { - "context": 204800, - "output": 131072 + "context": 196608, + "output": 196608 } }, "gpt-oss:20b": { @@ -77040,7 +77755,7 @@ "open_weights": true, "limit": { "context": 262144, - "output": 81920 + "output": 262144 } }, "deepseek-v3.1:671b": { @@ -77328,7 +78043,7 @@ "open_weights": true, "limit": { "context": 202752, - "output": 131072 + "output": 202752 } }, "mistral-large-3:675b": { @@ -85514,67 +86229,6 @@ "output": 128000 } }, - "gpt-5-4": { - "id": "gpt-5-4", - "name": "GPT-5.4", - "family": "gpt", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2025-08-31", - "release_date": "2026-03-05", - "last_updated": "2026-03-05", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 2.5, - "output": 15, - "cache_read": 0.25 - }, - "limit": { - "context": 272000, - "output": 128000 - } - }, - "deepseek-v3-2": { - "id": "deepseek-v3-2", - "name": "DeepSeek v3.2", - "family": "deepseek", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2024-12-26", - "last_updated": "2025-09-29", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.58, - "output": 1.68, - "cache_read": 0.28 - }, - "limit": { - "context": 128000, - "output": 8192 - } - }, "claude-sonnet-4-6": { "id": "claude-sonnet-4-6", "name": "Claude Sonnet 4.6", @@ -85676,105 +86330,6 @@ "output": 128000 } }, - "gpt-5-mini": { - "id": "gpt-5-mini", - "name": "GPT-5 Mini", - "family": "gpt-mini", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.25, - "output": 2, - "cache_read": 0.03 - }, - "limit": { - "context": 400000, - "output": 128000 - } - }, - "gpt-5-nano": { - "id": "gpt-5-nano", - "name": "GPT-5 Nano", - "family": "gpt-nano", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": false, - "knowledge": "2024-05-30", - "release_date": "2025-08-07", - "last_updated": "2025-08-07", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.05, - "output": 0.4, - "cache_read": 0.01 - }, - "limit": { - "context": 400000, - "output": 128000 - } - }, - "gemini-3-pro-preview": { - "id": "gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "structured_output": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", - "modalities": { - "input": [ - "text", - "image", - "video", - "audio", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 2, - "output": 12, - "cache_read": 0.2 - }, - "limit": { - "context": 1000000, - "output": 64000 - } - }, "zai-glm-5-1": { "id": "zai-glm-5-1", "name": "Z.AI GLM-5.1", @@ -85870,6 +86425,38 @@ "output": 128000 } }, + "gpt-5-4-nano": { + "id": "gpt-5-4-nano", + "name": "GPT-5.4 Nano", + "family": "gpt-nano", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2024-05-30", + "release_date": "2025-08-07", + "last_updated": "2025-08-07", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.2, + "output": 1.25, + "cache_read": 0.02 + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, "gemini-2.5-flash": { "id": "gemini-2.5-flash", "name": "Gemini 2.5 Flash", @@ -85934,6 +86521,102 @@ "output": 128000 } }, + "gpt-5-5": { + "id": "gpt-5-5", + "name": "GPT-5.5", + "family": "gpt", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-05", + "last_updated": "2026-03-05", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2.5, + "output": 15, + "cache_read": 0.25 + }, + "limit": { + "context": 272000, + "output": 128000 + } + }, + "grok-4-3": { + "id": "grok-4-3", + "name": "Grok 4.3", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": true, + "knowledge": "2024-11", + "release_date": "2026-04-30", + "last_updated": "2026-04-30", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2 + }, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + "gpt-5-4-mini": { + "id": "gpt-5-4-mini", + "name": "GPT-5.4 Mini", + "family": "gpt-mini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "structured_output": true, + "temperature": false, + "knowledge": "2025-08-31", + "release_date": "2026-03-17", + "last_updated": "2026-03-17", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.75, + "output": 4.5, + "cache_read": 0.075 + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, "claude-opus-4-6": { "id": "claude-opus-4-6", "name": "Claude Opus 4.6", @@ -85967,6 +86650,36 @@ "output": 128000 } }, + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek v4 Pro", + "family": "deepseek", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2026-01", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.74, + "output": 3.48, + "cache_read": 0.14 + }, + "limit": { + "context": 128000, + "output": 8192 + } + }, "gpt-oss-20b": { "id": "gpt-oss-20b", "name": "GPT OSS 20B", @@ -86027,6 +86740,36 @@ "output": 64000 } }, + "minimax-m2-7": { + "id": "minimax-m2-7", + "name": "MiniMax-M2.7", + "family": "minimax", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-09", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.3, + "output": 1.2, + "cache_read": 0.06 + }, + "limit": { + "context": 192000, + "output": 8192 + } + }, "minimax-m2-5": { "id": "minimax-m2-5", "name": "MiniMax-M2.5", @@ -90512,6 +91255,41 @@ "output": 4096 } }, + "grok-4.3": { + "id": "grok-4.3", + "name": "Grok 4.3", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-01", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2, + "context_over_200k": { + "input": 2.5, + "output": 5, + "cache_read": 0.4 + } + }, + "limit": { + "context": 1000000, + "output": 30000 + } + }, "grok-3-mini-fast": { "id": "grok-3-mini-fast", "name": "Grok 3 Mini Fast", @@ -100622,6 +101400,39 @@ "output": 64000 } }, + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "structured_output": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 1.74, + "output": 3.48 + }, + "limit": { + "context": 1048576, + "output": 393216 + } + }, "minimax-m2.5": { "id": "minimax-m2.5", "name": "MiniMax M2.5", @@ -103132,6 +103943,35 @@ "output": 200000 } }, + "qwen3-235b-a22b-instruct-2507": { + "id": "qwen3-235b-a22b-instruct-2507", + "name": "Qwen3 235B A22B Instruct 2507", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.062, + "output": 0.408 + }, + "limit": { + "context": 131000, + "output": 131000 + } + }, "kimi-k2.5": { "id": "kimi-k2.5", "name": "Kimi K2.5", @@ -103378,6 +104218,36 @@ "output": 16384 } }, + "codestral-2508": { + "id": "codestral-2508", + "name": "Codestral 2508", + "family": "mistral", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-03", + "release_date": "2025-07-30", + "last_updated": "2025-07-30", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.3, + "output": 0.9, + "cache_read": 0.03 + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, "claude-4-5-sonnet": { "id": "claude-4-5-sonnet", "name": "Claude 4.5 Sonnet", @@ -103438,6 +104308,34 @@ "output": 131000 } }, + "nemotron-3-super-120b-a12b": { + "id": "nemotron-3-super-120b-a12b", + "name": "Nemotron 3 Super 120B A12B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-12", + "release_date": "2026-03-11", + "last_updated": "2026-03-11", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.266, + "output": 0.799 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, "minimax-m2": { "id": "minimax-m2", "name": "MiniMax-M2", @@ -103897,6 +104795,62 @@ "output": 262000 } }, + "mixtral-8x7B-instruct-v0.1": { + "id": "mixtral-8x7B-instruct-v0.1", + "name": "Mixtral 8x7B Instruct v0.1", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "knowledge": "2023-09", + "release_date": "2023-12-11", + "last_updated": "2023-12-11", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.438, + "output": 0.68 + }, + "limit": { + "context": 32000, + "output": 32000 + } + }, + "hermes-4-70b": { + "id": "hermes-4-70b", + "name": "Hermes 4 70B", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2025-08-26", + "last_updated": "2025-08-26", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.116, + "output": 0.358 + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, "minimax-m2.5": { "id": "minimax-m2.5", "name": "MiniMax-M2.5", @@ -103928,6 +104882,35 @@ "output": 196608 } }, + "deepseek-v3.2": { + "id": "deepseek-v3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.266, + "output": 0.444 + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, "intellect-3": { "id": "intellect-3", "name": "INTELLECT 3", @@ -104017,6 +105000,64 @@ "output": 128000 } }, + "qwen-2.5-72b-instruct": { + "id": "qwen-2.5-72b-instruct", + "name": "Qwen2.5 72B Instruct", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2024-06", + "release_date": "2024-09-19", + "last_updated": "2024-09-19", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.062, + "output": 0.231 + }, + "limit": { + "context": 33000, + "output": 33000 + } + }, + "deepseek-r1-0528": { + "id": "deepseek-r1-0528", + "name": "DeepSeek R1 0528", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-07", + "release_date": "2025-05-28", + "last_updated": "2025-05-28", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.585, + "output": 2.307 + }, + "limit": { + "context": 164000, + "output": 164000 + } + }, "gpt-4.1": { "id": "gpt-4.1", "name": "GPT 4.1", @@ -104106,6 +105147,219 @@ "context": 128000, "output": 128000 } + }, + "qwen3.5-122b-a10b": { + "id": "qwen3.5-122b-a10b", + "name": "Qwen3.5 122B A10B", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2026-01", + "release_date": "2026-02-24", + "last_updated": "2026-02-24", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.444, + "output": 3.106 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "llama-3.3-70b-instruct": { + "id": "llama-3.3-70b-instruct", + "name": "Llama 3.3 70B Instruct", + "family": "llama", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.089, + "output": 0.275 + }, + "limit": { + "context": 131000, + "output": 131000 + } + }, + "mistral-large-2512": { + "id": "mistral-large-2512", + "name": "Mistral Large 3 2512", + "family": "mistral-large", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-12", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.5, + "output": 1.5, + "cache_read": 0.05 + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, + "qwen3.5-397b-a17b": { + "id": "qwen3.5-397b-a17b", + "name": "Qwen3.5 397B A17B", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2026-01", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.6, + "output": 3.6 + }, + "limit": { + "context": 250000, + "output": 250000 + } + }, + "deepseek-v4-flash": { + "id": "deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.133, + "output": 0.266, + "cache_read": 0.028 + }, + "limit": { + "context": 1048576, + "output": 384000 + } + }, + "deepseek-v4-pro": { + "id": "deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 1.553, + "output": 3.106, + "cache_read": 0.145 + }, + "limit": { + "context": 1048576, + "output": 384000 + } + }, + "qwen3-coder-30b-a3b-instruct": { + "id": "qwen3-coder-30b-a3b-instruct", + "name": "Qwen3 Coder 30B A3B Instruct", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-31", + "last_updated": "2025-07-31", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.053, + "output": 0.222 + }, + "limit": { + "context": 262000, + "output": 262000 + } } } }, @@ -106346,6 +107600,36 @@ "output": 1000000 } }, + "alibaba/qwen3.6-27b": { + "id": "alibaba/qwen3.6-27b", + "name": "Qwen 3.6 27B", + "family": "qwen3.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-22", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.6, + "output": 3.5999999999999996 + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, "alibaba/qwen3-embedding-8b": { "id": "alibaba/qwen3-embedding-8b", "name": "Qwen3 Embedding 8B", @@ -106819,6 +108103,35 @@ "output": 65536 } }, + "alibaba/qwen3-vl-235b-a22b-instruct": { + "id": "alibaba/qwen3-vl-235b-a22b-instruct", + "name": "Qwen3 VL 235B A22B Instruct", + "family": "qwen", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-09-24", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.39999999999999997, + "output": 1.5999999999999999 + }, + "limit": { + "context": 131072, + "output": 129024 + } + }, "alibaba/qwen3-coder": { "id": "alibaba/qwen3-coder", "name": "Qwen3 Coder 480B A35B Instruct", @@ -108312,6 +109625,37 @@ "output": 131072 } }, + "xai/grok-4.3": { + "id": "xai/grok-4.3", + "name": "Grok 4.3", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-30", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.19999999999999998 + }, + "limit": { + "context": 1000000, + "output": 1000000 + } + }, "xai/grok-4.20-non-reasoning": { "id": "xai/grok-4.20-non-reasoning", "name": "Grok 4.20 Non-Reasoning", @@ -109052,6 +110396,34 @@ "output": 128000 } }, + "inception/mercury-coder-small": { + "id": "inception/mercury-coder-small", + "name": "Mercury Coder Small Beta", + "family": "mercury", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-02-26", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.25, + "output": 1 + }, + "limit": { + "context": 32000, + "output": 16384 + } + }, "openai/gpt-5.1-codex-max": { "id": "openai/gpt-5.1-codex-max", "name": "GPT 5.1 Codex Max", @@ -113243,6 +114615,68 @@ "output": 32000 } }, + "xiaomi/mimo-v2.5-pro": { + "id": "xiaomi/mimo-v2.5-pro", + "name": "MiMo V2.5 Pro", + "family": "mimo-v2.5-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-22", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.19999999999999998 + }, + "limit": { + "context": 1050000, + "output": 131000 + } + }, + "xiaomi/mimo-v2.5": { + "id": "xiaomi/mimo-v2.5", + "name": "MiMo M2.5", + "family": "mimo-v2.5", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-22", + "last_updated": "2026-05-01", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.39999999999999997, + "output": 2, + "cache_read": 0.08 + }, + "limit": { + "context": 1050000, + "output": 131100 + } + }, "xiaomi/mimo-v2-pro": { "id": "xiaomi/mimo-v2-pro", "name": "MiMo V2 Pro", @@ -129764,6 +131198,435 @@ } } }, + "neuralwatt": { + "id": "neuralwatt", + "env": [ + "NEURALWATT_API_KEY" + ], + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.neuralwatt.com/v1", + "name": "Neuralwatt", + "doc": "https://portal.neuralwatt.com/docs", + "models": { + "glm-5-fast": { + "id": "glm-5-fast", + "name": "GLM 5 Fast", + "family": "glm", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 1.1, + "output": 3.6 + }, + "limit": { + "context": 200000, + "output": 200000 + } + }, + "kimi-k2.6-fast": { + "id": "kimi-k2.6-fast", + "name": "Kimi K2.6 Fast", + "family": "kimi", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.69, + "output": 3.22 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "qwen3.5-397b-fast": { + "id": "qwen3.5-397b-fast", + "name": "Qwen3.5 397B Fast", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-01", + "last_updated": "2026-02-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.69, + "output": 4.14 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "glm-5.1-fast": { + "id": "glm-5.1-fast", + "name": "GLM 5.1 Fast", + "family": "glm", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 1.1, + "output": 3.6 + }, + "limit": { + "context": 200000, + "output": 200000 + } + }, + "qwen3.6-35b-fast": { + "id": "qwen3.6-35b-fast", + "name": "Qwen3.6 35B Fast", + "family": "qwen3.6", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.05, + "output": 0.1 + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "kimi-k2.5-fast": { + "id": "kimi-k2.5-fast", + "name": "Kimi K2.5 Fast", + "family": "kimi", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.52, + "output": 2.59 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "Qwen/Qwen3.5-397B-A17B-FP8": { + "id": "Qwen/Qwen3.5-397B-A17B-FP8", + "name": "Qwen3.5 397B A17B FP8", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-01", + "last_updated": "2026-02-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.69, + "output": 4.14 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "Qwen/Qwen3.6-35B-A3B": { + "id": "Qwen/Qwen3.6-35B-A3B", + "name": "Qwen3.6 35B A3B", + "family": "qwen3.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.05, + "output": 0.1 + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + "zai-org/GLM-5.1-FP8": { + "id": "zai-org/GLM-5.1-FP8", + "name": "GLM 5.1 FP8", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-04-07", + "last_updated": "2026-04-07", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 1.1, + "output": 3.6 + }, + "limit": { + "context": 200000, + "output": 200000 + } + }, + "mistralai/Devstral-Small-2-24B-Instruct-2512": { + "id": "mistralai/Devstral-Small-2-24B-Instruct-2512", + "name": "Devstral Small 2 24B Instruct 2512", + "family": "devstral", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-12-09", + "last_updated": "2025-12-09", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.12, + "output": 0.35 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "openai/gpt-oss-20b": { + "id": "openai/gpt-oss-20b", + "name": "GPT OSS 20B", + "family": "gpt-oss", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-08-05", + "last_updated": "2025-08-05", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.03, + "output": 0.16 + }, + "limit": { + "context": 16384, + "output": 16384 + } + }, + "moonshotai/Kimi-K2.6": { + "id": "moonshotai/Kimi-K2.6", + "name": "Kimi K2.6", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.69, + "output": 3.22 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "moonshotai/Kimi-K2.5": { + "id": "moonshotai/Kimi-K2.5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.52, + "output": 2.59 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + "MiniMaxAI/MiniMax-M2.5": { + "id": "MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax M2.5", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "interleaved": { + "field": "reasoning_content" + }, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.35, + "output": 1.38 + }, + "limit": { + "context": 196608, + "output": 196608 + } + } + } + }, "togetherai": { "id": "togetherai", "env": [ diff --git a/packages/yjs-fs-editor/package.json b/packages/yjs-fs-editor/package.json index 8c2b021..62a7c2f 100644 --- a/packages/yjs-fs-editor/package.json +++ b/packages/yjs-fs-editor/package.json @@ -26,7 +26,7 @@ "y-monaco": "^0.1.6", "react": "^18.3.1", "react-dom": "^18.3.1", - "yjs": "13.6.30", + "yjs": "^13.6.30", "y-protocols": "1.0.7", "@humanlayer/yjs-fs": "workspace:*" }, @@ -39,4 +39,4 @@ "vite": "^6.0.0", "vite-plugin-monaco-editor": "^1.1.0" } -} +} \ No newline at end of file diff --git a/packages/yjs-fs-react/package.json b/packages/yjs-fs-react/package.json index 14060df..7759a52 100644 --- a/packages/yjs-fs-react/package.json +++ b/packages/yjs-fs-react/package.json @@ -24,7 +24,7 @@ "react": "^18.3.1", "typescript": "catalog:", "y-protocols": "1.0.7", - "yjs": "13.6.30" + "yjs": "^13.6.30" }, "dependencies": { "@humanlayer/yjs-fs": "workspace:*" @@ -32,7 +32,7 @@ "peerDependencies": { "react": "^18.3.1", "y-protocols": "1.0.7", - "yjs": "13.6.30" + "yjs": "^13.6.30" }, "exports": { ".": { @@ -42,4 +42,4 @@ "default": "./dist/index.js" } } -} +} \ No newline at end of file diff --git a/packages/yjs-fs/package.json b/packages/yjs-fs/package.json index 7f20204..d44cfaf 100644 --- a/packages/yjs-fs/package.json +++ b/packages/yjs-fs/package.json @@ -36,7 +36,7 @@ "peerDependencies": { "lib0": "^0.2.0", "typescript": "^5", - "yjs": "13.6.30", + "yjs": "^13.6.30", "y-protocols": "1.0.7" }, "exports": { @@ -58,4 +58,4 @@ "name": "Kyle Mistele", "url": "https://x.com/0xblacklight" } -} +} \ No newline at end of file diff --git a/packages/yjs-rich-markdown/package.json b/packages/yjs-rich-markdown/package.json index e068fa5..535b9a4 100644 --- a/packages/yjs-rich-markdown/package.json +++ b/packages/yjs-rich-markdown/package.json @@ -40,7 +40,7 @@ "@tiptap/starter-kit": "3.22.5", "typescript": "^5", "y-protocols": "1.0.7", - "yjs": "13.6.30" + "yjs": "^13.6.30" }, "dependencies": { "@tiptap/core": "3.22.5", @@ -48,10 +48,11 @@ "@tiptap/markdown": "3.22.5", "@tiptap/pm": "3.22.5", "@tiptap/starter-kit": "3.22.5", + "dedent": "1.7.2", "y-prosemirror": "1.3.7" }, "author": { "name": "Kyle Mistele", "url": "https://x.com/0xblacklight" } -} +} \ No newline at end of file diff --git a/scripts/release/manifest.ts b/scripts/release/manifest.ts index 0640467..515b1e6 100644 --- a/scripts/release/manifest.ts +++ b/scripts/release/manifest.ts @@ -1,7 +1,7 @@ import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; -import type { PackageManifest } from '../build/package-build' +import type { PackageManifest } from '../build/package-build'; export type WorkspaceReleaseEntry = { name: string; From 0a5751dc4adb84e137d0667f5219eb39e103a327 Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 2 May 2026 20:54:15 -0700 Subject: [PATCH 10/18] fix: formatting --- packages/quickjs-exec/src/index.ts | 11 +++- packages/yjs-fs-editor/package.json | 2 +- packages/yjs-fs-react/package.json | 2 +- packages/yjs-fs/package.json | 2 +- packages/yjs-rich-markdown/package.json | 2 +- packages/yjs-rich-markdown/src/yxml-proxy.ts | 5 +- .../test/markdown-yxml-shapes.test.ts | 12 +++- .../yjs-rich-markdown/test/yxml-proxy.test.ts | 59 +++++++++++++++---- 8 files changed, 74 insertions(+), 21 deletions(-) diff --git a/packages/quickjs-exec/src/index.ts b/packages/quickjs-exec/src/index.ts index db116ca..93831a6 100644 --- a/packages/quickjs-exec/src/index.ts +++ b/packages/quickjs-exec/src/index.ts @@ -42,7 +42,10 @@ export async function withQuickJsMode(bindings: QuickJsBindings, cb: WithQuic } } -export async function withAsyncQuickJsMode(bindings: QuickJsBindings, cb: WithAsyncQuickJsModeCallback): Promise { +export async function withAsyncQuickJsMode( + bindings: QuickJsBindings, + cb: WithAsyncQuickJsModeCallback, +): Promise { const context = await newAsyncContext() const disposableHandles: QuickJSHandle[] = [] @@ -81,7 +84,11 @@ async function runAsync(context: Awaited>, } } -function installSyncBindings(input: { vm: QuickJSContext; bindings: QuickJsBindings; disposableHandles: QuickJSHandle[] }): void { +function installSyncBindings(input: { + vm: QuickJSContext + bindings: QuickJsBindings + disposableHandles: QuickJSHandle[] +}): void { for (const [name, fn] of Object.entries(input.bindings)) { const handle = input.vm.newFunction(name, (...args) => { const output = fn(...args.map((arg) => input.vm.dump(arg))) diff --git a/packages/yjs-fs-editor/package.json b/packages/yjs-fs-editor/package.json index 62a7c2f..f268775 100644 --- a/packages/yjs-fs-editor/package.json +++ b/packages/yjs-fs-editor/package.json @@ -39,4 +39,4 @@ "vite": "^6.0.0", "vite-plugin-monaco-editor": "^1.1.0" } -} \ No newline at end of file +} diff --git a/packages/yjs-fs-react/package.json b/packages/yjs-fs-react/package.json index 7759a52..a2a2dff 100644 --- a/packages/yjs-fs-react/package.json +++ b/packages/yjs-fs-react/package.json @@ -42,4 +42,4 @@ "default": "./dist/index.js" } } -} \ No newline at end of file +} diff --git a/packages/yjs-fs/package.json b/packages/yjs-fs/package.json index d44cfaf..c1bf2f7 100644 --- a/packages/yjs-fs/package.json +++ b/packages/yjs-fs/package.json @@ -58,4 +58,4 @@ "name": "Kyle Mistele", "url": "https://x.com/0xblacklight" } -} \ No newline at end of file +} diff --git a/packages/yjs-rich-markdown/package.json b/packages/yjs-rich-markdown/package.json index 535b9a4..502deb2 100644 --- a/packages/yjs-rich-markdown/package.json +++ b/packages/yjs-rich-markdown/package.json @@ -55,4 +55,4 @@ "name": "Kyle Mistele", "url": "https://x.com/0xblacklight" } -} \ No newline at end of file +} diff --git a/packages/yjs-rich-markdown/src/yxml-proxy.ts b/packages/yjs-rich-markdown/src/yxml-proxy.ts index 92de512..69cf92e 100644 --- a/packages/yjs-rich-markdown/src/yxml-proxy.ts +++ b/packages/yjs-rich-markdown/src/yxml-proxy.ts @@ -45,7 +45,6 @@ type YXmlContainer = Y.XmlFragment | Y.XmlElement type YXmlNode = YXmlContainer | Y.XmlText type InsertableYXmlNode = Y.XmlElement | Y.XmlText - export class YXmlProxyBindings implements YXmlProxy { private readonly nodes = new Map() private readonly refs = new WeakMap() @@ -72,7 +71,9 @@ export class YXmlProxyBindings implements YXmlProxy { } children(input: { node?: YXmlNodeRef } = {}): YXmlNodeRef[] { - return this.resolveContainer(input.node).slice().map((node) => this.register(node)) + return this.resolveContainer(input.node) + .slice() + .map((node) => this.register(node)) } get({ node, index }: { node?: YXmlNodeRef; index: number }): YXmlNodeRef { diff --git a/packages/yjs-rich-markdown/test/markdown-yxml-shapes.test.ts b/packages/yjs-rich-markdown/test/markdown-yxml-shapes.test.ts index 5b450f2..49ca6f2 100644 --- a/packages/yjs-rich-markdown/test/markdown-yxml-shapes.test.ts +++ b/packages/yjs-rich-markdown/test/markdown-yxml-shapes.test.ts @@ -41,11 +41,15 @@ describe('Markdown to YXml shapes', () => { }) test('horizontal rule', () => { - expect(xmlFor('Before\n\n---\n\nAfter')).toBe('BeforeAfter') + expect(xmlFor('Before\n\n---\n\nAfter')).toBe( + 'BeforeAfter', + ) }) test('hard break inside paragraph', () => { - expect(xmlFor('First line\\\nSecond line')).toBe('First lineSecond line') + expect(xmlFor('First line\\\nSecond line')).toBe( + 'First lineSecond line', + ) }) test('single-line fenced code block', () => { @@ -59,7 +63,9 @@ describe('Markdown to YXml shapes', () => { }) test('indented code block', () => { - expect(xmlFor(' const value = 1\n console.log(value)')).toBe('const value = 1\nconsole.log(value)') + expect(xmlFor(' const value = 1\n console.log(value)')).toBe( + 'const value = 1\nconsole.log(value)', + ) }) test('strikethrough', () => { diff --git a/packages/yjs-rich-markdown/test/yxml-proxy.test.ts b/packages/yjs-rich-markdown/test/yxml-proxy.test.ts index 90c0cce..98ad5c2 100644 --- a/packages/yjs-rich-markdown/test/yxml-proxy.test.ts +++ b/packages/yjs-rich-markdown/test/yxml-proxy.test.ts @@ -56,7 +56,9 @@ describe('YXml proxy', () => { content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'After' }] }], }) - expect(proxy.toString()).toBe('BeforeMiddleAfter') + expect(proxy.toString()).toBe( + 'BeforeMiddleAfter', + ) }) test('appends nested content inside an element parent', () => { @@ -78,7 +80,9 @@ describe('YXml proxy', () => { { kind: 'element', nodeName: 'listItem', - children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Second' }] }], + children: [ + { kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Second' }] }, + ], }, ], }) @@ -94,7 +98,14 @@ describe('YXml proxy', () => { const proxy = new YXmlProxyBindings(fragment) const [heading] = proxy.append({ - content: [{ kind: 'element', nodeName: 'heading', attributes: { level: '2' }, children: [{ kind: 'text', text: 'Title' }] }], + content: [ + { + kind: 'element', + nodeName: 'heading', + attributes: { level: '2' }, + children: [{ kind: 'text', text: 'Title' }], + }, + ], }) if (!heading) throw new Error('Expected heading node') @@ -121,7 +132,13 @@ describe('YXml proxy', () => { { kind: 'element', nodeName: 'listItem', - children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Existing bullet' }] }], + children: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Existing bullet' }], + }, + ], }, ], }, @@ -135,7 +152,9 @@ describe('YXml proxy', () => { { kind: 'element', nodeName: 'listItem', - children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Added bullet' }] }], + children: [ + { kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Added bullet' }] }, + ], }, ], }) @@ -151,7 +170,14 @@ describe('YXml proxy', () => { const proxy = new YXmlProxyBindings(fragment) const [heading] = proxy.append({ - content: [{ kind: 'element', nodeName: 'heading', attributes: { level: '2' }, children: [{ kind: 'text', text: 'Details' }] }], + content: [ + { + kind: 'element', + nodeName: 'heading', + attributes: { level: '2' }, + children: [{ kind: 'text', text: 'Details' }], + }, + ], }) if (!heading) throw new Error('Expected heading node') @@ -172,19 +198,29 @@ describe('YXml proxy', () => { kind: 'element', nodeName: 'section', attributes: { id: 'intro' }, - children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Keep intro.' }] }], + children: [ + { kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Keep intro.' }] }, + ], }, { kind: 'element', nodeName: 'section', attributes: { id: 'obsolete' }, - children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Delete this section.' }] }], + children: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Delete this section.' }], + }, + ], }, { kind: 'element', nodeName: 'section', attributes: { id: 'ending' }, - children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Keep ending.' }] }], + children: [ + { kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Keep ending.' }] }, + ], }, ], }) @@ -230,7 +266,10 @@ describe('YXml proxy', () => { if (!text) throw new Error('Expected text node') expect(() => - proxy.append({ parent: text, content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Nope' }] }] }), + proxy.append({ + parent: text, + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Nope' }] }], + }), ).toThrow('Expected a fragment or element node') }) }) From 36467f97cd5bb95e223ae4604b72707e39bcdecc Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Sat, 2 May 2026 21:21:14 -0700 Subject: [PATCH 11/18] test: proxy --- packages/yjs-rich-markdown/src/index.ts | 11 +- .../src/yxml-proxy-prompt.ts | 30 ++--- packages/yjs-rich-markdown/src/yxml-proxy.ts | 109 +++++++++++++++--- .../test/yxml-invalid-structures.test.ts | 102 ++++++++++++++++ .../yjs-rich-markdown/test/yxml-proxy.test.ts | 87 +++++++++++++- 5 files changed, 303 insertions(+), 36 deletions(-) create mode 100644 packages/yjs-rich-markdown/test/yxml-invalid-structures.test.ts diff --git a/packages/yjs-rich-markdown/src/index.ts b/packages/yjs-rich-markdown/src/index.ts index ddbeb0f..b414657 100644 --- a/packages/yjs-rich-markdown/src/index.ts +++ b/packages/yjs-rich-markdown/src/index.ts @@ -7,5 +7,14 @@ export { markdownToTiptapJson, readArtifactMarkdown, tiptapJsonToMarkdown, write export type { ArtifactCollaborationMode, ArtifactCollaborationOptions } from './tiptap-bindings' export { artifactCollaborationExtension, artifactFragment, defaultRichMarkdownExtensions } from './tiptap-bindings' export type { YXmlNodeKind, YXmlNodeRef, YXmlNodeSpec, YXmlNodeSummary, YXmlProxy } from './yxml-proxy' -export { YXmlProxyBindings } from './yxml-proxy' +export { + DetachedYXmlNodeRefError, + UnknownYXmlNodeRefError, + YXmlChildIndexOutOfBoundsError, + YXmlInvalidNodeKindForOperationError, + YXmlNodeRefKindMismatchError, + YXmlProxyBindings, + YXmlProxyError, + YXmlRootOperationError, +} from './yxml-proxy' export { YXML_PROXY_AGENT_PROMPT } from './yxml-proxy-prompt' diff --git a/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts b/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts index 59e0a90..c7d8edb 100644 --- a/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts +++ b/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts @@ -63,7 +63,7 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` - Do not append children to a text node. Only fragment and element nodes can contain children. - Build nested content by nesting \`children\` arrays in \`NodeSpec\` objects. - Prefer structural edits over string replacement of the full XML. - - When creating common markdown structures, use the YXml element names listed below. These are the serialized YXml names produced by TipTap StarterKit and y-prosemirror. + - When creating common markdown structures, use the canonical TipTap/ProseMirror node names listed below. Some of these serialize to lowercase in XML strings, but generated \`NodeSpec\` content should use canonical names. - Return a small JSON object with \`ok\`, \`beforeXml\`, and \`afterXml\`. Common markdown YXml elements: @@ -75,18 +75,18 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` - Strikethrough mark: \`{ kind: 'element', nodeName: 'strike', children: [...] }\` - Inline code mark: \`{ kind: 'element', nodeName: 'code', children: [{ kind: 'text', text: 'code' }] }\` - Link mark: \`{ kind: 'element', nodeName: 'link', attributes: { href: 'https://example.com', target: '_blank', rel: 'noopener noreferrer nofollow', class: 'null', title: 'null' }, children: [...] }\` - - Bullet list: \`{ kind: 'element', nodeName: 'bulletlist', children: [...] }\` - - Ordered list: \`{ kind: 'element', nodeName: 'orderedlist', attributes: { start: '1' }, children: [...] }\` - - List item: \`{ kind: 'element', nodeName: 'listitem', children: [{ kind: 'element', nodeName: 'paragraph', children: [...] }] }\` + - Bullet list: \`{ kind: 'element', nodeName: 'bulletList', children: [...] }\` + - Ordered list: \`{ kind: 'element', nodeName: 'orderedList', attributes: { start: '1' }, children: [...] }\` + - List item: \`{ kind: 'element', nodeName: 'listItem', children: [{ kind: 'element', nodeName: 'paragraph', children: [...] }] }\` - Blockquote: \`{ kind: 'element', nodeName: 'blockquote', children: [{ kind: 'element', nodeName: 'paragraph', children: [...] }] }\` - - Horizontal rule: \`{ kind: 'element', nodeName: 'horizontalrule' }\` - - Hard break inside a paragraph: \`{ kind: 'element', nodeName: 'hardbreak' }\` - - Fenced code block with language: \`{ kind: 'element', nodeName: 'codeblock', attributes: { language: 'ts' }, children: [{ kind: 'text', text: 'const value = 1' }] }\` + - Horizontal rule: \`{ kind: 'element', nodeName: 'horizontalRule' }\` + - Hard break inside a paragraph: \`{ kind: 'element', nodeName: 'hardBreak' }\` + - Fenced code block with language: \`{ kind: 'element', nodeName: 'codeBlock', attributes: { language: 'ts' }, children: [{ kind: 'text', text: 'const value = 1' }] }\` - Multi-line code block: use one text child containing newline characters, e.g. \`{ kind: 'text', text: 'const value = 1\\nconsole.log(value)' }\`. Other elements may appear in normal usage. If you see these, do your best to follow existing patterns. - Important casing note: use lowercase multi-word YXml node names when creating nodes manually: \`bulletlist\`, \`listitem\`, \`orderedlist\`, \`codeblock\`, \`hardbreak\`, and \`horizontalrule\`. Do not use TipTap camelCase names like \`bulletList\` or \`codeBlock\` in generated \`NodeSpec\` content. + Important casing note: use canonical camelCase names when creating multi-word TipTap nodes manually: \`bulletList\`, \`listItem\`, \`orderedList\`, \`codeBlock\`, \`hardBreak\`, and \`horizontalRule\`. The XML string returned by \`bindings.toString({})\` may show lowercase names like \`\` or \`\`; do not copy that casing into new \`NodeSpec\` content. Example: add a paragraph after the first top-level node. @@ -132,16 +132,16 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` }, { kind: 'element', - nodeName: 'bulletlist', + nodeName: 'bulletList', children: [ { kind: 'element', - nodeName: 'listitem', + nodeName: 'listItem', children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Use scoped NodeRefs.' }] }], }, { kind: 'element', - nodeName: 'listitem', + nodeName: 'listItem', children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Validate after mutation.' }] }], }, ], @@ -151,10 +151,10 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` nodeName: 'blockquote', children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Bindings keep generated code sandbox-safe.' }] }], }, - { kind: 'element', nodeName: 'horizontalrule' }, + { kind: 'element', nodeName: 'horizontalRule' }, { kind: 'element', - nodeName: 'codeblock', + nodeName: 'codeBlock', attributes: { language: 'ts' }, children: [{ kind: 'text', text: 'const root = bindings.root()\\nconst xml = bindings.toString({})' }], }, @@ -169,7 +169,7 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` \`\`\`js const beforeXml = bindings.toString({}) const rootChildren = bindings.children({}) - const list = rootChildren.find((node) => bindings.summary({ node }).nodeName === 'bulletlist') + const list = rootChildren.find((node) => bindings.summary({ node }).nodeName === 'bulletList') if (!list) throw new Error('Could not find a bullet list') bindings.append({ @@ -177,7 +177,7 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` content: [ { kind: 'element', - nodeName: 'listitem', + nodeName: 'listItem', children: [ { kind: 'element', diff --git a/packages/yjs-rich-markdown/src/yxml-proxy.ts b/packages/yjs-rich-markdown/src/yxml-proxy.ts index 69cf92e..f0a1a74 100644 --- a/packages/yjs-rich-markdown/src/yxml-proxy.ts +++ b/packages/yjs-rich-markdown/src/yxml-proxy.ts @@ -41,6 +41,68 @@ export type YXmlProxy = { removeAttribute(input: { node: YXmlNodeRef; name: string }): void } +export class YXmlProxyError extends Error { + constructor( + message: string, + readonly code: string, + ) { + super(message) + this.name = 'YXmlProxyError' + } +} + +export class UnknownYXmlNodeRefError extends YXmlProxyError { + constructor(readonly ref: YXmlNodeRef) { + super(`Unknown YXml node ref: ${ref.id}`, 'UNKNOWN_NODE_REF') + this.name = 'UnknownYXmlNodeRefError' + } +} + +export class YXmlNodeRefKindMismatchError extends YXmlProxyError { + constructor( + readonly ref: YXmlNodeRef, + readonly actualKind: YXmlNodeKind, + ) { + super(`YXml node ref kind mismatch for ${ref.id}: expected ${ref.kind}, got ${actualKind}`, 'NODE_REF_KIND_MISMATCH') + this.name = 'YXmlNodeRefKindMismatchError' + } +} + +export class YXmlInvalidNodeKindForOperationError extends YXmlProxyError { + constructor( + readonly operation: string, + readonly expected: string, + readonly actual: YXmlNodeKind, + ) { + super(`Cannot ${operation} on ${actual} node; expected ${expected}`, 'INVALID_NODE_KIND_FOR_OPERATION') + this.name = 'YXmlInvalidNodeKindForOperationError' + } +} + +export class YXmlChildIndexOutOfBoundsError extends YXmlProxyError { + constructor( + readonly index: number, + readonly length: number, + ) { + super(`No YXml child at index ${index}; child count is ${length}`, 'CHILD_INDEX_OUT_OF_BOUNDS') + this.name = 'YXmlChildIndexOutOfBoundsError' + } +} + +export class DetachedYXmlNodeRefError extends YXmlProxyError { + constructor(readonly operation: string) { + super(`Cannot ${operation} deleted YXml node`, 'DETACHED_NODE_REF') + this.name = 'DetachedYXmlNodeRefError' + } +} + +export class YXmlRootOperationError extends YXmlProxyError { + constructor(readonly operation: string) { + super(`Cannot ${operation} the root YXml fragment`, 'ROOT_OPERATION') + this.name = 'YXmlRootOperationError' + } +} + type YXmlContainer = Y.XmlFragment | Y.XmlElement type YXmlNode = YXmlContainer | Y.XmlText type InsertableYXmlNode = Y.XmlElement | Y.XmlText @@ -48,6 +110,7 @@ type InsertableYXmlNode = Y.XmlElement | Y.XmlText export class YXmlProxyBindings implements YXmlProxy { private readonly nodes = new Map() private readonly refs = new WeakMap() + private readonly deletedRefs = new Set() private nextId = 1 constructor(private readonly rootFragment: Y.XmlFragment) { @@ -71,14 +134,15 @@ export class YXmlProxyBindings implements YXmlProxy { } children(input: { node?: YXmlNodeRef } = {}): YXmlNodeRef[] { - return this.resolveContainer(input.node) + return this.resolveContainer(input.node, 'read children from') .slice() .map((node) => this.register(node)) } get({ node, index }: { node?: YXmlNodeRef; index: number }): YXmlNodeRef { - const child = this.resolveContainer(node).get(index) - if (!child) throw new Error(`No YXml child at index ${index}`) + const container = this.resolveContainer(node, 'get child from') + const child = container.get(index) + if (!child) throw new YXmlChildIndexOutOfBoundsError(index, container.length) return this.register(child) } @@ -87,43 +151,50 @@ export class YXmlProxyBindings implements YXmlProxy { } append({ parent, content }: { parent?: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] { - const container = this.resolveContainer(parent) + const container = this.resolveContainer(parent, 'append children to') const nodesToInsert = this.buildNodes(content) container.push(nodesToInsert) return this.refsFor(nodesToInsert) } prepend({ parent, content }: { parent?: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] { - const container = this.resolveContainer(parent) + const container = this.resolveContainer(parent, 'prepend children to') const nodesToInsert = this.buildNodes(content) container.unshift(nodesToInsert) return this.refsFor(nodesToInsert) } insertBefore({ ref, content }: { ref: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] { + if (this.deletedRefs.has(ref.id)) throw new DetachedYXmlNodeRefError('insert before') const target = this.resolve(ref) - const parent = this.parentContainerOf(target) + if (target === this.rootFragment) throw new YXmlRootOperationError('insert before') + const parent = this.parentContainerOf(target, 'insert before') const index = parent.slice().indexOf(target as Y.XmlElement | Y.XmlText) - if (index < 0) throw new Error('Referenced node is not a direct child of its parent') + if (index < 0) throw new DetachedYXmlNodeRefError('insert before') const nodesToInsert = this.buildNodes(content) parent.insert(index, nodesToInsert) return this.refsFor(nodesToInsert) } insertAfter({ ref, content }: { ref: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] { + if (this.deletedRefs.has(ref.id)) throw new DetachedYXmlNodeRefError('insert after') const target = this.resolve(ref) - const parent = this.parentContainerOf(target) + if (target === this.rootFragment) throw new YXmlRootOperationError('insert after') + const parent = this.parentContainerOf(target, 'insert after') const nodesToInsert = this.buildNodes(content) parent.insertAfter(target as Y.XmlElement | Y.XmlText, nodesToInsert) return this.refsFor(nodesToInsert) } remove({ node }: { node: YXmlNodeRef }): void { + if (this.deletedRefs.has(node.id)) throw new DetachedYXmlNodeRefError('remove') const target = this.resolve(node) - const parent = this.parentContainerOf(target) + if (target === this.rootFragment) throw new YXmlRootOperationError('remove') + const parent = this.parentContainerOf(target, 'remove') const index = parent.slice().indexOf(target as Y.XmlElement | Y.XmlText) - if (index < 0) throw new Error('Referenced node is not a direct child of its parent') + if (index < 0) throw new DetachedYXmlNodeRefError('remove') parent.delete(index, 1) + this.deletedRefs.add(node.id) } setAttribute({ node, name, value }: { node: YXmlNodeRef; name: string; value: string }): void { @@ -148,20 +219,21 @@ export class YXmlProxyBindings implements YXmlProxy { if (!ref) return this.rootFragment const node = this.nodes.get(ref.id) - if (!node) throw new Error(`Unknown YXml node ref: ${ref.id}`) - if (kindOf(node) !== ref.kind) throw new Error(`YXml node ref kind mismatch for ${ref.id}`) + if (!node) throw new UnknownYXmlNodeRefError(ref) + const actualKind = kindOf(node) + if (actualKind !== ref.kind) throw new YXmlNodeRefKindMismatchError(ref, actualKind) return node } - private resolveContainer(ref?: YXmlNodeRef): YXmlContainer { + private resolveContainer(ref: YXmlNodeRef | undefined, operation: string): YXmlContainer { const node = this.resolve(ref) - if (node instanceof Y.XmlText) throw new Error('Expected a fragment or element node') + if (node instanceof Y.XmlText) throw new YXmlInvalidNodeKindForOperationError(operation, 'fragment or element', 'text') return node } private resolveElement(ref: YXmlNodeRef): Y.XmlElement { const node = this.resolve(ref) - if (!(node instanceof Y.XmlElement)) throw new Error('Expected an element node') + if (!(node instanceof Y.XmlElement)) throw new YXmlInvalidNodeKindForOperationError('set or remove attribute', 'element', kindOf(node)) return node } @@ -189,15 +261,16 @@ export class YXmlProxyBindings implements YXmlProxy { return content.map((node) => this.register(node)) } - private parentContainerOf(node: YXmlNode): YXmlContainer { + private parentContainerOf(node: YXmlNode, operation: string): YXmlContainer { + if (node.doc === null) throw new DetachedYXmlNodeRefError(operation) const parent = node.parent if (parent instanceof Y.XmlFragment || parent instanceof Y.XmlElement) return parent - throw new Error('Referenced node is not attached to a YXml parent') + throw new DetachedYXmlNodeRefError(operation) } } function kindOf(node: YXmlNode): YXmlNodeKind { - if (node instanceof Y.XmlElement) return 'element' if (node instanceof Y.XmlText) return 'text' + if ('nodeName' in node) return 'element' return 'fragment' } diff --git a/packages/yjs-rich-markdown/test/yxml-invalid-structures.test.ts b/packages/yjs-rich-markdown/test/yxml-invalid-structures.test.ts new file mode 100644 index 0000000..7247019 --- /dev/null +++ b/packages/yjs-rich-markdown/test/yxml-invalid-structures.test.ts @@ -0,0 +1,102 @@ +import { describe, expect, test } from 'bun:test' +import type { JSONContent } from '@tiptap/core' +import * as Y from 'yjs' +import { yXmlFragmentToProsemirrorJSON } from 'y-prosemirror' +import { markdownToTiptapJson, tiptapJsonToMarkdown } from '../src' + +function text(value: string): Y.XmlText { + const node = new Y.XmlText() + node.insert(0, value) + return node +} + +function element(nodeName: string, children: Array = []): Y.XmlElement { + const node = new Y.XmlElement(nodeName) + if (children.length > 0) node.insert(0, children) + return node +} + +function jsonFor(fragment: Y.XmlFragment): JSONContent { + return yXmlFragmentToProsemirrorJSON(fragment) as JSONContent +} + +describe('invalid or lossy YXml structures', () => { + test('unsupported block node type survives YXml but is dropped during markdown serialization', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('invalid') + fragment.insert(0, [element('madeupblock', [text('Unknown block')])]) + + expect(fragment.toString()).toBe('Unknown block') + expect(jsonFor(fragment)).toEqual({ + type: 'doc', + content: [{ type: 'madeupblock', content: [{ type: 'text', text: 'Unknown block' }] }], + }) + expect(tiptapJsonToMarkdown(jsonFor(fragment))).toBe('') + }) + + test('unsupported nested node type survives YXml but is dropped during markdown serialization', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('invalid') + fragment.insert(0, [element('paragraph', [text('Before '), element('custommark', [text('marked')]), text(' after')])]) + + expect(fragment.toString()).toBe('Before marked after') + expect(jsonFor(fragment)).toEqual({ + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { type: 'text', text: 'Before ' }, + { type: 'custommark', content: [{ type: 'text', text: 'marked' }] }, + { type: 'text', text: ' after' }, + ], + }, + ], + }) + expect(tiptapJsonToMarkdown(jsonFor(fragment))).toBe('Before after') + }) + + test('schema-invalid paragraph nesting converts to JSON and flattens during markdown serialization', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('invalid') + fragment.insert(0, [element('paragraph', [element('paragraph', [text('Nested paragraph')])])]) + + expect(fragment.toString()).toBe('Nested paragraph') + expect(jsonFor(fragment)).toEqual({ + type: 'doc', + content: [ + { + type: 'paragraph', + content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Nested paragraph' }] }], + }, + ], + }) + expect(tiptapJsonToMarkdown(jsonFor(fragment))).toBe('Nested paragraph') + }) + + test('whitespace in paragraphs is preserved by markdown round trip', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('whitespace') + fragment.insert(0, [element('paragraph', [text('one two')])]) + + const markdown = tiptapJsonToMarkdown(jsonFor(fragment)) + const reparsedMarkdown = tiptapJsonToMarkdown(markdownToTiptapJson(markdown)) + + expect(markdown).toBe('one two') + expect(reparsedMarkdown).toBe('one two') + }) + + test('whitespace in fenced code blocks is preserved by markdown round trip', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('code') + const code = element('codeBlock', [text('const value = 1\n console.log(value)')]) + code.setAttribute('language', 'ts') + fragment.insert(0, [code]) + + const markdown = tiptapJsonToMarkdown(jsonFor(fragment)) + const reparsedMarkdown = tiptapJsonToMarkdown(markdownToTiptapJson(markdown)) + + expect(markdown).toBe('```ts\nconst value = 1\n console.log(value)\n```') + expect(reparsedMarkdown).toBe(markdown) + }) +}) diff --git a/packages/yjs-rich-markdown/test/yxml-proxy.test.ts b/packages/yjs-rich-markdown/test/yxml-proxy.test.ts index 98ad5c2..eb40119 100644 --- a/packages/yjs-rich-markdown/test/yxml-proxy.test.ts +++ b/packages/yjs-rich-markdown/test/yxml-proxy.test.ts @@ -1,6 +1,14 @@ import { describe, expect, test } from 'bun:test' import * as Y from 'yjs' -import { YXmlProxyBindings } from '../src' +import { + DetachedYXmlNodeRefError, + UnknownYXmlNodeRefError, + YXmlChildIndexOutOfBoundsError, + YXmlInvalidNodeKindForOperationError, + YXmlNodeRefKindMismatchError, + YXmlProxyBindings, + YXmlRootOperationError, +} from '../src' describe('YXml proxy', () => { test('appends XML elements and nested children', () => { @@ -90,6 +98,7 @@ describe('YXml proxy', () => { expect(proxy.toString()).toBe( 'FirstSecond', ) + expect(proxy.summary({ node: list }).nodeName).toBe('bulletList') }) test('sets and removes attributes on element refs', () => { @@ -162,6 +171,7 @@ describe('YXml proxy', () => { expect(proxy.toString()).toBe( 'Existing bulletAdded bullet', ) + expect(proxy.summary({ node: list }).nodeName).toBe('bulletList') }) test('changes an existing element attribute', () => { @@ -270,6 +280,79 @@ describe('YXml proxy', () => { parent: text, content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Nope' }] }], }), - ).toThrow('Expected a fragment or element node') + ).toThrow(YXmlInvalidNodeKindForOperationError) + }) + + test('throws for unknown node refs', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + expect(() => proxy.summary({ node: { id: 'missing', kind: 'element' } })).toThrow(UnknownYXmlNodeRefError) + }) + + test('throws for node ref kind mismatches', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + const [paragraph] = proxy.append({ + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Text' }] }], + }) + if (!paragraph) throw new Error('Expected paragraph node') + + expect(() => proxy.summary({ node: { ...paragraph, kind: 'text' } })).toThrow(YXmlNodeRefKindMismatchError) + }) + + test('throws for child index out of bounds', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + expect(() => proxy.get({ index: 0 })).toThrow(YXmlChildIndexOutOfBoundsError) + }) + + test('throws when setting attributes on non-element refs', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + const root = proxy.root() + + expect(() => proxy.setAttribute({ node: root, name: 'id', value: 'root' })).toThrow( + YXmlInvalidNodeKindForOperationError, + ) + }) + + test('throws when removing or inserting relative to the root fragment', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + const root = proxy.root() + + expect(() => proxy.remove({ node: root })).toThrow(YXmlRootOperationError) + expect(() => + proxy.insertAfter({ + ref: root, + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Nope' }] }], + }), + ).toThrow(YXmlRootOperationError) + }) + + test('throws for operations on detached node refs', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + const [paragraph] = proxy.append({ + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Delete me' }] }], + }) + if (!paragraph) throw new Error('Expected paragraph node') + + proxy.remove({ node: paragraph }) + + expect(() => + proxy.insertAfter({ + ref: paragraph, + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Nope' }] }], + }), + ).toThrow(DetachedYXmlNodeRefError) }) }) From 1587a02eee0755936136fe9b724d4304b4b5f06a Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Mon, 4 May 2026 19:05:08 -0700 Subject: [PATCH 12/18] feat: capture quickjs console output --- bun.lock | 406 +----------------- packages/quickjs-exec/src/index.ts | 202 ++++++++- .../quickjs-exec/test/quickjs-exec.test.ts | 128 +++++- packages/yjs-rich-markdown/package.json | 3 +- packages/yjs-rich-markdown/src/index.ts | 11 +- .../src/yxml-proxy-prompt.ts | 115 ++++- packages/yjs-rich-markdown/src/yxml-proxy.ts | 223 +++++++++- .../test/yxml-proxy-prompt-examples.test.ts | 282 ++++++++++++ .../test/yxml-proxy-quickjs.test.ts | 245 +++++++++++ .../yjs-rich-markdown/test/yxml-proxy.test.ts | 333 ++++++++++++++ 10 files changed, 1527 insertions(+), 421 deletions(-) create mode 100644 packages/yjs-rich-markdown/test/yxml-proxy-prompt-examples.test.ts create mode 100644 packages/yjs-rich-markdown/test/yxml-proxy-quickjs.test.ts diff --git a/bun.lock b/bun.lock index a6a37f8..ea23661 100644 --- a/bun.lock +++ b/bun.lock @@ -62,41 +62,6 @@ "typescript": "^5", }, }, - "agents/yjs-fs-agents": { - "name": "@humanlayer/yjs-fs-agents", - "devDependencies": { - "@ai-sdk/anthropic": "catalog:", - "@ai-sdk/provider": "catalog:", - "@humanlayer/agentlayer-core": "workspace:*", - "@humanlayer/yjs-fs": "workspace:*", - "@types/bun": "latest", - "ai": "catalog:", - "zod": "catalog:", - }, - "peerDependencies": { - "typescript": "^5", - }, - }, - "agents/yjs-markdown-apply-agent": { - "name": "@humanlayer/yjs-markdown-apply-agent", - "devDependencies": { - "@ai-sdk/provider": "catalog:", - "@humanlayer/agentlayer-core": "workspace:*", - "@humanlayer/quickjs-exec": "workspace:*", - "@tanstack/ai": "0.14.0", - "@tanstack/ai-code-mode": "0.1.8", - "@tanstack/ai-isolate-quickjs": "0.1.8", - "@types/bun": "latest", - "ai": "catalog:", - "quickjs-emscripten": "0.31.0", - "zod": "catalog:", - }, - "peerDependencies": { - "secure-exec": "0.2.1", - "typescript": "^5", - "yjs": "^13.6.30", - }, - }, "examples/yjs-rich-markdown-demo": { "name": "@humanlayer/yjs-rich-markdown-demo", "version": "0.0.1", @@ -339,6 +304,7 @@ "name": "@humanlayer/yjs-rich-markdown", "version": "0.0.0", "dependencies": { + "@humanlayer/quickjs-exec": "workspace:*", "@tiptap/core": "3.22.5", "@tiptap/extension-collaboration": "3.22.5", "@tiptap/markdown": "3.22.5", @@ -376,8 +342,6 @@ "zod": "4.3.6", }, "packages": { - "@ag-ui/core": ["@ag-ui/core@0.0.49", "", { "dependencies": { "zod": "^3.22.4" } }, "sha512-9ywypwjUGtIvTxJ2eKQjhPZgLnSFAfNK7vZUcT7Bz4ur4yAIB+lAFtzvu7VDYe6jsUx/6N/71Dh4R0zX5woNVw=="], - "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.69", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LshR7X3pFugY0o41G2VKTmg1XoGpSl7uoYWfzk6zjVZLhCfeFiwgpOga+eTV4XY1VVpZwKVqRnkDbIL7K2eH5g=="], "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.96", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-BDiVEMUVHGpngReeigzLyJobG0TvzYbNGzdHI8JYBZHrjOX4aL6qwIls7z3p7V4TuXVWUCbG8TSWEe7ksX4Vhw=="], @@ -490,18 +454,6 @@ "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], - "@cbor-extract/cbor-extract-darwin-arm64": ["@cbor-extract/cbor-extract-darwin-arm64@2.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZKZ/F8US7JR92J4DMct6cLW/Y66o2K576+zjlEN/MevH70bFIsB10wkZEQPLzl2oNh2SMGy55xpJ9JoBRl5DOA=="], - - "@cbor-extract/cbor-extract-darwin-x64": ["@cbor-extract/cbor-extract-darwin-x64@2.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-32b1mgc+P61Js+KW9VZv/c+xRw5EfmOcPx990JbCBSkYJFY0l25VinvyyWfl+3KjibQmAcYwmyzKF9J4DyKP/Q=="], - - "@cbor-extract/cbor-extract-linux-arm": ["@cbor-extract/cbor-extract-linux-arm@2.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-tNg0za41TpQfkhWjptD+0gSD2fggMiDCSacuIeELyb2xZhr7PrhPe5h66Jc67B/5dmpIhI2QOUtv4SBsricyYQ=="], - - "@cbor-extract/cbor-extract-linux-arm64": ["@cbor-extract/cbor-extract-linux-arm64@2.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-wfqgzqCAy/Vn8i6WVIh7qZd0DdBFaWBjPdB6ma+Wihcjv0gHqD/mw3ouVv7kbbUNrab6dKEx/w3xQZEdeXIlzg=="], - - "@cbor-extract/cbor-extract-linux-x64": ["@cbor-extract/cbor-extract-linux-x64@2.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rpiLnVEsqtPJ+mXTdx1rfz4RtUGYIUg2rUAZgd1KjiC1SehYUSkJN7Yh+aVfSjvCGtVP0/bfkQkXpPXKbmSUaA=="], - - "@cbor-extract/cbor-extract-win32-x64": ["@cbor-extract/cbor-extract-win32-x64@2.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-dI+9P7cfWxkTQ+oE+7Aa6onEn92PHgfWXZivjNheCRmTBDBf2fx6RyTi0cmgpYLnD1KLZK9ZYrMxaPZ4oiXhGA=="], - "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@12.0.0", "", { "dependencies": { "@chevrotain/gast": "12.0.0", "@chevrotain/types": "12.0.0" } }, "sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg=="], "@chevrotain/gast": ["@chevrotain/gast@12.0.0", "", { "dependencies": { "@chevrotain/types": "12.0.0" } }, "sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ=="], @@ -614,14 +566,10 @@ "@humanlayer/yjs-fs": ["@humanlayer/yjs-fs@workspace:packages/yjs-fs"], - "@humanlayer/yjs-fs-agents": ["@humanlayer/yjs-fs-agents@workspace:agents/yjs-fs-agents"], - "@humanlayer/yjs-fs-editor": ["@humanlayer/yjs-fs-editor@workspace:packages/yjs-fs-editor"], "@humanlayer/yjs-fs-react": ["@humanlayer/yjs-fs-react@workspace:packages/yjs-fs-react"], - "@humanlayer/yjs-markdown-apply-agent": ["@humanlayer/yjs-markdown-apply-agent@workspace:agents/yjs-markdown-apply-agent"], - "@humanlayer/yjs-rich-markdown": ["@humanlayer/yjs-rich-markdown@workspace:packages/yjs-rich-markdown"], "@humanlayer/yjs-rich-markdown-demo": ["@humanlayer/yjs-rich-markdown-demo@workspace:examples/yjs-rich-markdown-demo"], @@ -850,20 +798,6 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ=="], - "@secure-exec/core": ["@secure-exec/core@0.2.1", "", { "dependencies": { "better-sqlite3": "^12.8.0" } }, "sha512-HsnUv6gClpMA1BBRmX86j30TKTZtgJC/fO1tVavr7IpM2zNKbHU8LgSlBd7mv2SNy02ImTmU/GnQ3aYB4NSbEg=="], - - "@secure-exec/nodejs": ["@secure-exec/nodejs@0.2.1", "", { "dependencies": { "@secure-exec/core": "0.2.1", "@secure-exec/v8": "0.2.1", "cbor-x": "^1.6.4", "cjs-module-lexer": "^2.1.0", "es-module-lexer": "^1.7.0", "esbuild": "^0.27.1", "node-stdlib-browser": "^1.3.1", "web-streams-polyfill": "^4.2.0" } }, "sha512-UJMJqVFxexlHJV0Q9nWURvrz6GElj8673DDOOFln6FHR6JS+9SaSU3eISrN158DuNC3SFi4rgjb/scKnK4YOYQ=="], - - "@secure-exec/v8": ["@secure-exec/v8@0.2.1", "", { "dependencies": { "cbor-x": "^1.6.4" }, "optionalDependencies": { "@secure-exec/v8-darwin-arm64": "0.2.1", "@secure-exec/v8-darwin-x64": "0.2.1", "@secure-exec/v8-linux-arm64-gnu": "0.2.1", "@secure-exec/v8-linux-x64-gnu": "0.2.1" } }, "sha512-ye/seCqzvyMGnvyP+AO7RkVMR/lE3x9m0D2PfmiAXA457R78ZmOFmZ6v+JlJG2vv3LM30KsSXTUhwpG+Teh0hw=="], - - "@secure-exec/v8-darwin-arm64": ["@secure-exec/v8-darwin-arm64@0.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gEWhMHzUpLwzuBNAD0lVkZXE8wFlWMLp4IOZ+56FYwOW/C+m07cYxuW4TjHyPqZ+vPm3IkoaMqqH5yT9VhjX/Q=="], - - "@secure-exec/v8-darwin-x64": ["@secure-exec/v8-darwin-x64@0.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-H2Z5K+Cq+fn/kxjGvhJzepnNFWG6qNdyhZybVWGr5bAAZoSz/Qkad4WnXcurWU+880tKDtnf19LHBXrg7zewNQ=="], - - "@secure-exec/v8-linux-arm64-gnu": ["@secure-exec/v8-linux-arm64-gnu@0.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-14subGhVV/gW35mYYm7Gv1Keeex7PxIgQfoKji/JH7wYyDuarP6kgaES0nJw+JXVkxEVud52c+kbcIjIggqCEw=="], - - "@secure-exec/v8-linux-x64-gnu": ["@secure-exec/v8-linux-x64-gnu@0.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Az4s+vUf+78vWtsC7rTn/jQc6WKJafAdt2YpEjB4Gnu+sX+FFTIst1hRV4gJonbRyJdy6SW+OQ6DZatmwczorQ=="], - "@shikijs/core": ["@shikijs/core@2.5.0", "", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="], "@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="], @@ -896,20 +830,10 @@ "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@tanstack/ai": ["@tanstack/ai@0.14.0", "", { "dependencies": { "@ag-ui/core": "0.0.49", "@tanstack/ai-event-client": "0.2.8", "partial-json": "^0.1.7" } }, "sha512-oiyYHcWMmyVoKDf018xZMEX62BaJVhdgeeRQeRbqP3p5Nvar2UZUhKzFkUiE4L9KzklPqrtI5aYhVdAp9Z142g=="], - - "@tanstack/ai-code-mode": ["@tanstack/ai-code-mode@0.1.8", "", { "dependencies": { "esbuild": "^0.25.12" }, "peerDependencies": { "@tanstack/ai": "0.14.0", "zod": "^3.0.0 || ^4.0.0" } }, "sha512-wsI4GWtAnLabirw3B2tUeLJjrxs2oWq2TWkarvVxSF6SAQwFxW/KaPOHbzfkc6vCg2cQqDYsVVIz2ncfV3L1xg=="], - - "@tanstack/ai-event-client": ["@tanstack/ai-event-client@0.2.8", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.4.1" }, "peerDependencies": { "@tanstack/ai": "0.14.0" } }, "sha512-pdvT1hh7UorwBiDLrXnI5BFDfc7JSqnN06LaQHG4TGRl/621UnUrh16hQKOMn/msArKiCj+n+TG+cw4mDwMaLw=="], - - "@tanstack/ai-isolate-quickjs": ["@tanstack/ai-isolate-quickjs@0.1.8", "", { "dependencies": { "quickjs-emscripten": "^0.31.0" }, "peerDependencies": { "@tanstack/ai-code-mode": "0.1.8" } }, "sha512-mAT8lDdkDrK38W7e/rpC8ckUZwspW3t9gK/w1QJgnCjFKQDHYKaXFUXQvYw0kWPrqxZk+9psjBd0K0eOrwKVpA=="], - "@tanstack/db": ["@tanstack/db@0.6.5", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@tanstack/db-ivm": "0.1.18", "@tanstack/pacer-lite": "^0.2.1" }, "peerDependencies": { "typescript": ">=4.7" } }, "sha512-gtCuAo4UtC9SR/kTMu5fVEff6qZ2R1FZi9X7MybtHKA6wve7RePifGG6qBI4OmMB+7juT5/+glNbnqZOrG0/pg=="], "@tanstack/db-ivm": ["@tanstack/db-ivm@0.1.18", "", { "dependencies": { "fractional-indexing": "^3.2.0", "sorted-btree": "^1.8.1" }, "peerDependencies": { "typescript": ">=4.7" } }, "sha512-+pZJiRKdoKRM5Epq9T7otD9ZJl82pRFauo7LKuJGrarjVKQ7r+QQlPe3kGdN9LEKSnuNGIWjX9OOY4M8kH4eLw=="], - "@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.4.3", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw=="], - "@tanstack/history": ["@tanstack/history@1.161.6", "", {}, "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg=="], "@tanstack/pacer-lite": ["@tanstack/pacer-lite@0.2.1", "", {}, "sha512-3PouiFjR4B6x1c969/Pl4ZIJleof1M0n6fNX8NRiC9Sqv1g06CVDlEaXUR4212ycGFyfq4q+t8Gi37Xy+z34iQ=="], @@ -1206,12 +1130,6 @@ "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - "asn1.js": ["asn1.js@4.10.1", "", { "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw=="], - - "assert": ["assert@2.1.0", "", { "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", "object-is": "^1.1.5", "object.assign": "^4.1.4", "util": "^0.12.5" } }, "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw=="], - - "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], - "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], @@ -1222,66 +1140,30 @@ "baseline-browser-mapping": ["baseline-browser-mapping@2.10.21", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA=="], - "better-sqlite3": ["better-sqlite3@12.9.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ=="], - "bin-links": ["bin-links@6.0.0", "", { "dependencies": { "cmd-shim": "^8.0.0", "npm-normalize-package-bin": "^5.0.0", "proc-log": "^6.0.0", "read-cmd-shim": "^6.0.0", "write-file-atomic": "^7.0.0" } }, "sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w=="], "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], - "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], - "birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="], "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], - "bn.js": ["bn.js@5.2.3", "", {}, "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w=="], - "brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "brorand": ["brorand@1.1.0", "", {}, "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="], - - "browser-resolve": ["browser-resolve@2.0.0", "", { "dependencies": { "resolve": "^1.17.0" } }, "sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ=="], - - "browserify-aes": ["browserify-aes@1.2.0", "", { "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", "create-hash": "^1.1.0", "evp_bytestokey": "^1.0.3", "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA=="], - - "browserify-cipher": ["browserify-cipher@1.0.1", "", { "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", "evp_bytestokey": "^1.0.0" } }, "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w=="], - - "browserify-des": ["browserify-des@1.0.2", "", { "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A=="], - - "browserify-rsa": ["browserify-rsa@4.1.1", "", { "dependencies": { "bn.js": "^5.2.1", "randombytes": "^2.1.0", "safe-buffer": "^5.2.1" } }, "sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ=="], - - "browserify-sign": ["browserify-sign@4.2.5", "", { "dependencies": { "bn.js": "^5.2.2", "browserify-rsa": "^4.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "elliptic": "^6.6.1", "inherits": "^2.0.4", "parse-asn1": "^5.1.9", "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1" } }, "sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw=="], - - "browserify-zlib": ["browserify-zlib@0.2.0", "", { "dependencies": { "pako": "~1.0.5" } }, "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA=="], - "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - "buffer-xor": ["buffer-xor@1.0.3", "", {}, "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ=="], - - "builtin-status-codes": ["builtin-status-codes@3.0.0", "", {}, "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ=="], - "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="], "cacache": ["cacache@20.0.4", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0" } }, "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA=="], - "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], - - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], - - "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - "caniuse-lite": ["caniuse-lite@1.0.30001790", "", {}, "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw=="], - "cbor-extract": ["cbor-extract@2.2.2", "", { "dependencies": { "node-gyp-build-optional-packages": "5.1.1" }, "optionalDependencies": { "@cbor-extract/cbor-extract-darwin-arm64": "2.2.2", "@cbor-extract/cbor-extract-darwin-x64": "2.2.2", "@cbor-extract/cbor-extract-linux-arm": "2.2.2", "@cbor-extract/cbor-extract-linux-arm64": "2.2.2", "@cbor-extract/cbor-extract-linux-x64": "2.2.2", "@cbor-extract/cbor-extract-win32-x64": "2.2.2" }, "bin": { "download-cbor-prebuilds": "bin/download-prebuilds.js" } }, "sha512-hlSxxI9XO2yQfe9g6msd3g4xCfDqK5T5P0fRMLuaLHhxn4ViPrm+a+MUfhrvH2W962RGxcBwEGzLQyjbDG1gng=="], - - "cbor-x": ["cbor-x@1.6.4", "", { "optionalDependencies": { "cbor-extract": "^2.2.2" } }, "sha512-UGKHjp6RHC6QuZ2yy5LCKm7MojM4716DwoSaqwQpaH4DvZvbBTGcoDNTiG9Y2lByXZYFEs9WRkS5tLl96IrF1Q=="], - "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], @@ -1300,9 +1182,7 @@ "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], - "cipher-base": ["cipher-base@1.0.7", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.2" } }, "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA=="], - - "cjs-module-lexer": ["cjs-module-lexer@2.2.0", "", {}, "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ=="], + "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], @@ -1320,34 +1200,18 @@ "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], - "console-browserify": ["console-browserify@1.2.0", "", {}, "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA=="], - - "constants-browserify": ["constants-browserify@1.0.0", "", {}, "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ=="], - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], "cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], "copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "^5.2.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="], - "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], - "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="], - "create-ecdh": ["create-ecdh@4.0.4", "", { "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" } }, "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A=="], - - "create-hash": ["create-hash@1.2.0", "", { "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", "md5.js": "^1.3.4", "ripemd160": "^2.0.1", "sha.js": "^2.4.0" } }, "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg=="], - - "create-hmac": ["create-hmac@1.1.7", "", { "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", "inherits": "^2.0.1", "ripemd160": "^2.0.0", "safe-buffer": "^5.0.1", "sha.js": "^2.4.8" } }, "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg=="], - - "create-require": ["create-require@1.1.1", "", {}, "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="], - "crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "crypto-browserify": ["crypto-browserify@3.12.1", "", { "dependencies": { "browserify-cipher": "^1.0.1", "browserify-sign": "^4.2.3", "create-ecdh": "^4.0.4", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "diffie-hellman": "^5.0.3", "hash-base": "~3.0.4", "inherits": "^2.0.4", "pbkdf2": "^3.1.2", "public-encrypt": "^4.0.3", "randombytes": "^2.1.0", "randomfill": "^1.0.4" } }, "sha512-r4ESw/IlusD17lgQi1O20Fa3qNnsckR126TdUuBgAu7GBYSIPvdNyONd3Zrxh0xCwA4+6w/TDArBPsMvhur+KQ=="], - "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], @@ -1436,34 +1300,20 @@ "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], - "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], - - "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], - "delaunator": ["delaunator@5.1.0", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ=="], "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], - "des.js": ["des.js@1.1.0", "", { "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg=="], - "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], "diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], - "diffie-hellman": ["diffie-hellman@5.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" } }, "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg=="], - - "domain-browser": ["domain-browser@4.22.0", "", {}, "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw=="], - "dompurify": ["dompurify@3.4.0", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg=="], - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - "electron-to-chromium": ["electron-to-chromium@1.5.344", "", {}, "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg=="], - "elliptic": ["elliptic@6.6.1", "", { "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", "hash.js": "^1.0.0", "hmac-drbg": "^1.0.1", "inherits": "^2.0.4", "minimalistic-assert": "^1.0.1", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g=="], - "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="], @@ -1474,14 +1324,8 @@ "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], - "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], - - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], - "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -1492,12 +1336,8 @@ "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], - "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], - "evp_bytestokey": ["evp_bytestokey@1.0.3", "", { "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" } }, "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA=="], - "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], @@ -1526,18 +1366,12 @@ "file-type": ["file-type@21.3.4", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g=="], - "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], - "flexsearch": ["flexsearch@0.7.31", "", {}, "sha512-XGozTsMPYkm+6b5QL3Z9wQcJjNYxp0CYn3U1gO7dwD6PAqU1SVWZxI9CCg3z+ml3YfqdPnrBehaBrnH2AGKbNA=="], "focus-trap": ["focus-trap@7.8.0", "", { "dependencies": { "tabbable": "^6.4.0" } }, "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA=="], - "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], - "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], "format": ["format@0.2.2", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="], @@ -1552,16 +1386,10 @@ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], - "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], - - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="], @@ -1576,32 +1404,18 @@ "google-protobuf": ["google-protobuf@3.21.4", "", {}, "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ=="], - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], - "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], - "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], - - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - - "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], - - "hash-base": ["hash-base@3.0.5", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1" } }, "sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg=="], - - "hash.js": ["hash.js@1.1.7", "", { "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" } }, "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA=="], - "hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="], "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], - "hmac-drbg": ["hmac-drbg@1.0.1", "", { "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", "minimalistic-crypto-utils": "^1.0.1" } }, "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg=="], - "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], "hosted-git-info": ["hosted-git-info@9.0.2", "", { "dependencies": { "lru-cache": "^11.1.0" } }, "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg=="], @@ -1612,8 +1426,6 @@ "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], - "https-browserify": ["https-browserify@1.0.0", "", {}, "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg=="], - "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], @@ -1634,12 +1446,8 @@ "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], - "is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], - "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], - "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], @@ -1648,32 +1456,20 @@ "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], - "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - "is-nan": ["is-nan@1.3.2", "", { "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" } }, "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w=="], - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], - "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], - "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], - "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], - "is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="], - "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], - "isbot": ["isbot@5.1.39", "", {}, "sha512-obH0yYahGXdzNxo+djmHhBYThUKDkz565cxkIlt2L9hXfv1NlaLKoDBHo6KxXsYrIXx2RK3x5vY36CfZcobxEw=="], "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "isomorphic-timers-promises": ["isomorphic-timers-promises@1.0.1", "", {}, "sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ=="], - "isomorphic.js": ["isomorphic.js@0.2.5", "", {}, "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw=="], "jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="], @@ -1718,8 +1514,6 @@ "lmdb": ["lmdb@3.5.3", "", { "dependencies": { "@harperfast/extended-iterable": "^1.0.3", "msgpackr": "^1.11.2", "node-addon-api": "^6.1.0", "node-gyp-build-optional-packages": "5.2.2", "ordered-binary": "^1.5.3", "weak-lru-cache": "^1.2.2" }, "optionalDependencies": { "@lmdb/lmdb-darwin-arm64": "3.5.3", "@lmdb/lmdb-darwin-x64": "3.5.3", "@lmdb/lmdb-linux-arm": "3.5.3", "@lmdb/lmdb-linux-arm64": "3.5.3", "@lmdb/lmdb-linux-x64": "3.5.3", "@lmdb/lmdb-win32-arm64": "3.5.3", "@lmdb/lmdb-win32-x64": "3.5.3" }, "bin": { "download-lmdb-prebuilds": "bin/download-prebuilds.js" } }, "sha512-6A0iRKOv/N62P1vU8qhBr32tHQIY6pQMe8b6zqI4VLELqV8fWHUMdnfNjMR+Kyh7ZeRCo8PDzAnW2g+SJSoP1A=="], - "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], - "lodash-es": ["lodash-es@4.18.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="], "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], @@ -1744,10 +1538,6 @@ "marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], - - "md5.js": ["md5.js@1.3.5", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg=="], - "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], "mdast-util-frontmatter": ["mdast-util-frontmatter@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "escape-string-regexp": "^5.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-extension-frontmatter": "^2.0.0" } }, "sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA=="], @@ -1810,8 +1600,6 @@ "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], - "miller-rabin": ["miller-rabin@4.0.1", "", { "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" }, "bin": { "miller-rabin": "bin/miller-rabin" } }, "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA=="], - "millify": ["millify@6.1.0", "", { "dependencies": { "yargs": "^17.0.1" }, "bin": { "millify": "bin/millify" } }, "sha512-H/E3J6t+DQs/F2YgfDhxUVZz/dF8JXPPKTLHL/yHCcLZLtCXJDUaqvhJXQwqOVBvbyNn4T0WjLpIHd7PAw7fBA=="], "mime": ["mime@2.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="], @@ -1820,10 +1608,6 @@ "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], - "minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="], - - "minimalistic-crypto-utils": ["minimalistic-crypto-utils@1.0.1", "", {}, "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="], - "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], @@ -1882,8 +1666,6 @@ "node-releases": ["node-releases@2.0.38", "", {}, "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw=="], - "node-stdlib-browser": ["node-stdlib-browser@1.3.1", "", { "dependencies": { "assert": "^2.0.0", "browser-resolve": "^2.0.0", "browserify-zlib": "^0.2.0", "buffer": "^5.7.1", "console-browserify": "^1.1.0", "constants-browserify": "^1.0.0", "create-require": "^1.1.1", "crypto-browserify": "^3.12.1", "domain-browser": "4.22.0", "events": "^3.0.0", "https-browserify": "^1.0.0", "isomorphic-timers-promises": "^1.0.1", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", "pkg-dir": "^5.0.0", "process": "^0.11.10", "punycode": "^1.4.1", "querystring-es3": "^0.2.1", "readable-stream": "^3.6.0", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", "string_decoder": "^1.0.0", "timers-browserify": "^2.0.4", "tty-browserify": "0.0.1", "url": "^0.11.4", "util": "^0.12.4", "vm-browserify": "^1.0.1" } }, "sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw=="], - "non-layered-tidy-tree-layout": ["non-layered-tidy-tree-layout@2.0.2", "", {}, "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw=="], "nopt": ["nopt@9.0.0", "", { "dependencies": { "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw=="], @@ -1908,14 +1690,6 @@ "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], - "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - - "object-is": ["object-is@1.1.6", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" } }, "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q=="], - - "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], - - "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], @@ -1926,12 +1700,6 @@ "orderedmap": ["orderedmap@2.1.1", "", {}, "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="], - "os-browserify": ["os-browserify@0.3.0", "", {}, "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A=="], - - "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - - "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], - "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], @@ -1940,22 +1708,12 @@ "pacote": ["pacote@21.5.0", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/git": "^7.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/promise-spawn": "^9.0.0", "@npmcli/run-script": "^10.0.0", "cacache": "^20.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^13.0.0", "npm-packlist": "^10.0.1", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "proc-log": "^6.0.0", "sigstore": "^4.0.0", "ssri": "^13.0.0", "tar": "^7.4.3" }, "bin": { "pacote": "bin/index.js" } }, "sha512-VtZ0SB8mb5Tzw3dXDfVAIjhyVKUHZkS/ZH9/5mpKenwC9sFOXNI0JI7kEF7IMkwOnsWMFrvAZHzx1T5fmrp9FQ=="], - "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], - "papaparse": ["papaparse@5.5.3", "", {}, "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A=="], - "parse-asn1": ["parse-asn1@5.1.9", "", { "dependencies": { "asn1.js": "^4.10.1", "browserify-aes": "^1.2.0", "evp_bytestokey": "^1.0.3", "pbkdf2": "^3.1.5", "safe-buffer": "^5.2.1" } }, "sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg=="], - "parse-conflict-json": ["parse-conflict-json@5.0.1", "", { "dependencies": { "json-parse-even-better-errors": "^5.0.0", "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" } }, "sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ=="], - "partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="], - - "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], - "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], - "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], - "path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], @@ -1968,24 +1726,18 @@ "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - "pbkdf2": ["pbkdf2@3.1.5", "", { "dependencies": { "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "ripemd160": "^2.0.3", "safe-buffer": "^5.2.1", "sha.js": "^2.4.12", "to-buffer": "^1.2.1" } }, "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ=="], - "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], - "pkg-dir": ["pkg-dir@5.0.0", "", { "dependencies": { "find-up": "^5.0.0" } }, "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA=="], - "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="], - "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "postcss": ["postcss@8.5.9", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw=="], "postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], @@ -2002,10 +1754,6 @@ "proc-log": ["proc-log@6.1.0", "", {}, "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ=="], - "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], - - "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], - "proggy": ["proggy@4.0.0", "", {}, "sha512-MbA4R+WQT76ZBm/5JUpV9yqcJt92175+Y0Bodg3HgiXzrmKu7Ggq+bpn6y6wHH+gN9NcyKn3yg1+d47VaKwNAQ=="], "promise-all-reject-late": ["promise-all-reject-late@1.0.1", "", {}, "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw=="], @@ -2052,30 +1800,18 @@ "protobufjs": ["protobufjs@7.5.5", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg=="], - "public-encrypt": ["public-encrypt@4.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", "randombytes": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q=="], - "pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="], - "punycode": ["punycode@1.4.1", "", {}, "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="], - "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], "pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="], - "qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="], - "qsu": ["qsu@1.12.0", "", {}, "sha512-kcB2A7XS+ApkxAIc63yOqF2mmvqyA4AezObM1RxAhOB6O0bRA8xCzm6PLwU70CVW9R+0Rozqd0pgOFskNtX2eQ=="], - "querystring-es3": ["querystring-es3@0.2.1", "", {}, "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA=="], - "quickjs-emscripten": ["quickjs-emscripten@0.31.0", "", { "dependencies": { "@jitl/quickjs-wasmfile-debug-asyncify": "0.31.0", "@jitl/quickjs-wasmfile-debug-sync": "0.31.0", "@jitl/quickjs-wasmfile-release-asyncify": "0.31.0", "@jitl/quickjs-wasmfile-release-sync": "0.31.0", "quickjs-emscripten-core": "0.31.0" } }, "sha512-K7Yt78aRPLjPcqv3fIuLW1jW3pvwO21B9pmFOolsjM/57ZhdVXBr51GqJpalgBlkPu9foAvhEAuuQPnvIGvLvQ=="], "quickjs-emscripten-core": ["quickjs-emscripten-core@0.31.0", "", { "dependencies": { "@jitl/quickjs-ffi-types": "0.31.0" } }, "sha512-oQz8p0SiKDBc1TC7ZBK2fr0GoSHZKA0jZIeXxsnCyCs4y32FStzCW4d1h6E1sE0uHDMbGITbk2zhNaytaoJwXQ=="], - "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], - - "randomfill": ["randomfill@1.0.4", "", { "dependencies": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" } }, "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw=="], - "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], "re2js": ["re2js@1.4.0", "", {}, "sha512-KTOIcZTSOpOxbu3i0+T6mFQ6tkxXKlTxfcMFs1trQbsMnG84qNq+DjXr8Afu+FEFjvF1NNlldpC7roPyazFI8g=="], @@ -2120,8 +1856,6 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "ripemd160": ["ripemd160@2.0.3", "", { "dependencies": { "hash-base": "^3.1.2", "inherits": "^2.0.4" } }, "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA=="], - "ripgrep": ["ripgrep@0.3.1", "", { "bin": { "rg": "lib/rg.mjs", "ripgrep": "lib/rg.mjs" } }, "sha512-6bDtNIBh1qPviVIU685/4uv0Ap5t8eS4wiJhy/tR2LdIeIey9CVasENlGS+ul3HnTmGANIp7AjnfsztsRmALfQ=="], "robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="], @@ -2136,8 +1870,6 @@ "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], - "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], @@ -2146,8 +1878,6 @@ "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="], - "secure-exec": ["secure-exec@0.2.1", "", { "dependencies": { "@secure-exec/core": "0.2.1", "@secure-exec/nodejs": "0.2.1" } }, "sha512-oaQDzTPDSCOckYC8G0PimIqzEVxY6sYEvcx0fMGsRR/Wl4wkFVHaZgQ3kc2DHWysV6WHWt5g1AXc/6seafO2XQ=="], - "seek-bzip": ["seek-bzip@2.0.0", "", { "dependencies": { "commander": "^6.0.0" }, "bin": { "seek-bunzip": "bin/seek-bunzip", "seek-table": "bin/seek-bzip-table" } }, "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg=="], "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], @@ -2156,12 +1886,6 @@ "seroval-plugins": ["seroval-plugins@1.5.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-qpY0Cl+fKYFn4GOf3cMiq6l72CpuVaawb6ILjubOQ+diJ54LfOWaSSPsaswN8DRPIPW4Yq+tE1k5aKd7ILyaFg=="], - "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], - - "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], - - "sha.js": ["sha.js@2.4.12", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" } }, "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w=="], - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], @@ -2170,14 +1894,6 @@ "shimmer": ["shimmer@1.2.1", "", {}, "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="], - "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], - - "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], - - "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], - - "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], - "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "sigstore": ["sigstore@4.1.0", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.1.0", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.1.0", "@sigstore/tuf": "^4.0.1", "@sigstore/verify": "^3.1.0" } }, "sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA=="], @@ -2220,10 +1936,6 @@ "ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], - "stream-browserify": ["stream-browserify@3.0.0", "", { "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" } }, "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA=="], - - "stream-http": ["stream-http@3.2.0", "", { "dependencies": { "builtin-status-codes": "^3.0.0", "inherits": "^2.0.4", "readable-stream": "^3.6.0", "xtend": "^4.0.2" } }, "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A=="], - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], @@ -2256,8 +1968,6 @@ "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], - "timers-browserify": ["timers-browserify@2.0.12", "", { "dependencies": { "setimmediate": "^1.0.4" } }, "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ=="], - "tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="], "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], @@ -2266,8 +1976,6 @@ "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], - "to-buffer": ["to-buffer@1.2.2", "", { "dependencies": { "isarray": "^2.0.5", "safe-buffer": "^5.2.1", "typed-array-buffer": "^1.0.3" } }, "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw=="], - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="], @@ -2284,16 +1992,12 @@ "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], - "tty-browserify": ["tty-browserify@0.0.1", "", {}, "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw=="], - "tuf-js": ["tuf-js@4.1.0", "", { "dependencies": { "@tufjs/models": "4.1.0", "debug": "^4.4.3", "make-fetch-happen": "^15.0.1" } }, "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ=="], "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], "turndown": ["turndown@7.2.4", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-I8yFsfRzmzK0WV1pNNOA4A7y4RDfFxPRxb3t+e3ui14qSGOxGtiSP6GjeX+Y6CHb7HYaFj7ECUD7VE5kQMZWGQ=="], - "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="], @@ -2326,12 +2030,8 @@ "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], - "url": ["url@0.11.4", "", { "dependencies": { "punycode": "^1.4.1", "qs": "^6.12.3" } }, "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg=="], - "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], - "util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], @@ -2358,8 +2058,6 @@ "vitepress-sidebar": ["vitepress-sidebar@1.33.1", "", { "dependencies": { "glob": "11.1.0", "gray-matter": "4.0.3", "qsu": "^1.10.4" } }, "sha512-wPUbXezGakVldawixeRW5tKQTLKoMj2t4nWoThKfCltBM/9a38IE+wCXmmRNW22ZKC32SD/X/sG6NyCTK8QBRg=="], - "vm-browserify": ["vm-browserify@1.1.2", "", {}, "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="], - "vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="], "vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="], @@ -2380,22 +2078,16 @@ "weak-lru-cache": ["weak-lru-cache@1.2.2", "", {}, "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw=="], - "web-streams-polyfill": ["web-streams-polyfill@4.2.0", "", {}, "sha512-0rYDzGOh9EZpig92umN5g5D/9A1Kff7k0/mzPSSCY8jEQeYkgRMoY7LhbXtUCWzLCMX0TUE9aoHkjFNB7D9pfA=="], - "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], - "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], "write-file-atomic": ["write-file-atomic@7.0.1", "", { "dependencies": { "signal-exit": "^4.0.1" } }, "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg=="], - "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], - "y-monaco": ["y-monaco@0.1.6", "", { "dependencies": { "lib0": "^0.2.43" }, "peerDependencies": { "monaco-editor": ">=0.20.0", "yjs": "^13.3.1" } }, "sha512-sYRywMmcylt+Nupl+11AvizD2am06ST8lkVbUXuaEmrtV6Tf+TD4rsEm6u9YGGowYue+Vfg1IJ97SUP2J+PVXg=="], "y-prosemirror": ["y-prosemirror@1.3.7", "", { "dependencies": { "lib0": "^0.2.109" }, "peerDependencies": { "prosemirror-model": "^1.7.1", "prosemirror-state": "^1.2.3", "prosemirror-view": "^1.9.10", "y-protocols": "^1.0.1", "yjs": "^13.5.38" } }, "sha512-NpM99WSdD4Fx4if5xOMDpPtU3oAmTSjlzh5U4353ABbRHl1HtAFUx6HlebLZfyFxXN9jzKMDkVbcRjqOZVkYQg=="], @@ -2414,14 +2106,10 @@ "yjs": ["yjs@13.6.30", "", { "dependencies": { "lib0": "^0.2.99" } }, "sha512-vv/9h42eCMC81ZHDFswuu/MKzkl/vyq1BhaNGfHyOonwlG4CJbQF4oiBBJPvfdeCt/PlVDWh7Nov9D34YY09uQ=="], - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - "@ag-ui/core/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], @@ -2452,8 +2140,6 @@ "@humanlayer/yjs-fs-editor/@durable-streams/server": ["@durable-streams/server@0.1.7", "", { "dependencies": { "@durable-streams/client": "0.2.0", "@durable-streams/state": "0.2.0", "@neophi/sieve-cache": "^1.0.0", "lmdb": "^3.3.0" } }, "sha512-JaCzfKS/GI3ZlbmInHvqX/fAh6XENTjc+acfhRlZH9iaRQiD1rOjfircj+jFDTIHgeT9oPqOVscmISGot8kI3w=="], - "@humanlayer/yjs-markdown-apply-agent/@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], - "@humanlayer/yjs-rich-markdown/@tiptap/extension-collaboration": ["@tiptap/extension-collaboration@3.22.5", "", { "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5", "@tiptap/y-tiptap": "^3.0.2", "yjs": "^13" } }, "sha512-3rax34HKSo8L5ihv5iGxISNBUIdVP0Fq9O6T/mq4kQOLhVhNbqwr9I/lWOvaz24nPE7u5Vkz0Ii3zWGlzxyPPA=="], "@humanlayer/yjs-rich-markdown/@tiptap/starter-kit": ["@tiptap/starter-kit@3.22.5", "", { "dependencies": { "@tiptap/core": "^3.22.5", "@tiptap/extension-blockquote": "^3.22.5", "@tiptap/extension-bold": "^3.22.5", "@tiptap/extension-bullet-list": "^3.22.5", "@tiptap/extension-code": "^3.22.5", "@tiptap/extension-code-block": "^3.22.5", "@tiptap/extension-document": "^3.22.5", "@tiptap/extension-dropcursor": "^3.22.5", "@tiptap/extension-gapcursor": "^3.22.5", "@tiptap/extension-hard-break": "^3.22.5", "@tiptap/extension-heading": "^3.22.5", "@tiptap/extension-horizontal-rule": "^3.22.5", "@tiptap/extension-italic": "^3.22.5", "@tiptap/extension-link": "^3.22.5", "@tiptap/extension-list": "^3.22.5", "@tiptap/extension-list-item": "^3.22.5", "@tiptap/extension-list-keymap": "^3.22.5", "@tiptap/extension-ordered-list": "^3.22.5", "@tiptap/extension-paragraph": "^3.22.5", "@tiptap/extension-strike": "^3.22.5", "@tiptap/extension-text": "^3.22.5", "@tiptap/extension-underline": "^3.22.5", "@tiptap/extensions": "^3.22.5", "@tiptap/pm": "^3.22.5" } }, "sha512-LZ/LYbwH6rnDi5DnRyagkuNsYAVyhM+yJvvz+ZuYA0JkPiTXJV86J5PWSKew8M0gVfMHcNVtKjfQCvViFCeIgw=="], @@ -2478,8 +2164,6 @@ "@npmcli/promise-spawn/which": ["which@6.0.1", "", { "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" } }, "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg=="], - "@secure-exec/nodejs/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], - "@tanstack/router-generator/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "@tanstack/router-plugin/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -2562,16 +2246,8 @@ "argparse/sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - "asn1.js/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], - - "browserify-sign/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - "cacache/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], - "cbor-extract/node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.1.1", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-test": "build-test.js", "node-gyp-build-optional-packages-optional": "optional.js" } }, "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw=="], - - "create-ecdh/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], - "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="], "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], @@ -2580,14 +2256,8 @@ "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], - "diffie-hellman/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], - - "elliptic/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], - "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "import-in-the-middle/cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], - "just-bash/ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="], "just-bash/quickjs-emscripten": ["quickjs-emscripten@0.32.0", "", { "dependencies": { "@jitl/quickjs-wasmfile-debug-asyncify": "0.32.0", "@jitl/quickjs-wasmfile-debug-sync": "0.32.0", "@jitl/quickjs-wasmfile-release-asyncify": "0.32.0", "@jitl/quickjs-wasmfile-release-sync": "0.32.0", "quickjs-emscripten-core": "0.32.0" } }, "sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA=="], @@ -2598,8 +2268,6 @@ "markdown-it/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "miller-rabin/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], - "minipass-fetch/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -2612,22 +2280,16 @@ "prosemirror-trailing-node/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - "public-encrypt/bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], - "rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], - "ripemd160/hash-base": ["hash-base@3.1.2", "", { "dependencies": { "inherits": "^2.0.4", "readable-stream": "^2.3.8", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.1" } }, "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg=="], - "seek-bzip/commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="], "spdx-correct/spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], - "to-buffer/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], - "tsx/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], "validate-npm-package-license/spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], @@ -2666,8 +2328,6 @@ "@humanlayer/yjs-fs/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], - "@humanlayer/yjs-markdown-apply-agent/@types/bun/bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], - "@humanlayer/yjs-rich-markdown-demo/@tiptap/react/@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.22.5", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-yrNlFQQJY5MmhBpmD8tnmaSmyUQrEvgyPKa3bzVeWEhDSG1CW4A0ZSMx3hrA9yFO0HWfw3IJmvSCycEZQBalpQ=="], "@humanlayer/yjs-rich-markdown-demo/@tiptap/react/@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.22.5", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "3.22.5", "@tiptap/pm": "3.22.5" } }, "sha512-dhem4sTPhyQgQ+pFp2Oud4k4FSQz9PVMgeQAC9288SmGwxBkJNveDAw6sKTMrumqDvwkJrtslXIupq9TZYQnzg=="], @@ -2746,58 +2406,6 @@ "@npmcli/promise-spawn/which/isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], - "@secure-exec/nodejs/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], - - "@secure-exec/nodejs/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], - - "@secure-exec/nodejs/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="], - - "@secure-exec/nodejs/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="], - - "@secure-exec/nodejs/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="], - - "@secure-exec/nodejs/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="], - - "@secure-exec/nodejs/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="], - - "@secure-exec/nodejs/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="], - - "@secure-exec/nodejs/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="], - - "@secure-exec/nodejs/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="], - - "@secure-exec/nodejs/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="], - - "@secure-exec/nodejs/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="], - - "@secure-exec/nodejs/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="], - - "@secure-exec/nodejs/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="], - - "@secure-exec/nodejs/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="], - - "@secure-exec/nodejs/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="], - - "@secure-exec/nodejs/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="], - - "@secure-exec/nodejs/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="], - - "@secure-exec/nodejs/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="], - - "@secure-exec/nodejs/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="], - - "@secure-exec/nodejs/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="], - - "@secure-exec/nodejs/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="], - - "@secure-exec/nodejs/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="], - - "@secure-exec/nodejs/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="], - - "@secure-exec/nodejs/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="], - - "@secure-exec/nodejs/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], - "@tiptap/extension-blockquote/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], "@tiptap/extension-bold/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], @@ -2828,10 +2436,6 @@ "@tiptap/extension-text/@tiptap/core/@tiptap/pm": ["@tiptap/pm@2.27.2", "", { "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", "prosemirror-commands": "^1.6.2", "prosemirror-dropcursor": "^1.8.1", "prosemirror-gapcursor": "^1.3.2", "prosemirror-history": "^1.4.1", "prosemirror-inputrules": "^1.4.0", "prosemirror-keymap": "^1.2.2", "prosemirror-markdown": "^1.13.1", "prosemirror-menu": "^1.2.4", "prosemirror-model": "^1.23.0", "prosemirror-schema-basic": "^1.2.3", "prosemirror-schema-list": "^1.4.1", "prosemirror-state": "^1.4.3", "prosemirror-tables": "^1.6.4", "prosemirror-trailing-node": "^3.0.0", "prosemirror-transform": "^1.10.2", "prosemirror-view": "^1.37.0" } }, "sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA=="], - "browserify-sign/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], - - "browserify-sign/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], @@ -2854,8 +2458,6 @@ "normalize-package-data/hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - "ripemd160/hash-base/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], @@ -2930,10 +2532,6 @@ "just-bash/quickjs-emscripten/quickjs-emscripten-core/@jitl/quickjs-ffi-types": ["@jitl/quickjs-ffi-types@0.32.0", "", {}, "sha512-v9T+GQpmk43VDJ7d72sf0Nexhk+ArvtUihW27dy7lqAl0zBObFKtSBBIm5RBjwIhE8VwsPPm9PNuvPvNqLWUEg=="], - "ripemd160/hash-base/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], - - "ripemd160/hash-base/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - "vitepress/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], "vitepress/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], diff --git a/packages/quickjs-exec/src/index.ts b/packages/quickjs-exec/src/index.ts index 93831a6..13bf064 100644 --- a/packages/quickjs-exec/src/index.ts +++ b/packages/quickjs-exec/src/index.ts @@ -5,6 +5,66 @@ export type QuickJsBindings = Record export type QuickJsExecutionMode = 'sync' | 'async' +export type QuickJsConsoleLevel = 'log' | 'info' | 'warn' | 'error' + +export type QuickJsConsoleEntry = { + level: QuickJsConsoleLevel + args: unknown[] + text: string +} + +export type QuickJsExecutionErrorDetails = { + name?: string + message: string + fileName?: string + lineNumber?: number + columnNumber?: number + codeFrame?: string +} + +export class QuickJsExecutionError extends Error { + readonly details: QuickJsExecutionErrorDetails + + constructor( + details: QuickJsExecutionErrorDetails, + options?: { + cause?: unknown + }, + ) { + super(formatQuickJsExecutionMessage(details)) + this.name = 'QuickJsExecutionError' + this.details = details + if (options && 'cause' in options) { + Object.defineProperty(this, 'cause', { + value: options.cause, + configurable: true, + writable: true, + }) + } + } +} + +export type QuickJsSerializedError = { + name?: string + message: string + fileName?: string + lineNumber?: number + columnNumber?: number + codeFrame?: string +} + +export type QuickJsRunWithConsoleResult = + | { + ok: true + value: T + console: QuickJsConsoleEntry[] + } + | { + ok: false + error: QuickJsSerializedError + console: QuickJsConsoleEntry[] + } + export type QuickJsExec = { readonly mode: QuickJsExecutionMode run(code: string): T | Promise @@ -14,15 +74,17 @@ export type QuickJsExec = { export type QuickJsSyncExec = QuickJsExec & { readonly mode: 'sync' run(code: string): T + runWithConsole(code: string): QuickJsRunWithConsoleResult } export type QuickJsAsyncExec = QuickJsExec & { readonly mode: 'async' run(code: string): Promise + runWithConsole(code: string): Promise> } -export type WithQuickJsModeCallback = (exec: QuickJsSyncExec) => T | Promise -export type WithAsyncQuickJsModeCallback = (exec: QuickJsAsyncExec) => T | Promise +export type WithQuickJsModeCallback = (exec: QuickJsSyncExec) => T +export type WithAsyncQuickJsModeCallback = (exec: QuickJsAsyncExec) => Promise export async function withQuickJsMode(bindings: QuickJsBindings, cb: WithQuickJsModeCallback): Promise { const QuickJS = await getQuickJS() @@ -31,9 +93,10 @@ export async function withQuickJsMode(bindings: QuickJsBindings, cb: WithQuic try { installSyncBindings({ vm, bindings, disposableHandles }) - return await cb({ + return cb({ mode: 'sync', run: (code) => runSync(vm, code) as never, + runWithConsole: (code) => runSyncWithConsole(vm, code) as never, dispose: () => vm.dispose(), }) } finally { @@ -54,6 +117,7 @@ export async function withAsyncQuickJsMode( return await cb({ mode: 'async', run: (code) => runAsync(context, code) as never, + runWithConsole: (code) => runAsyncWithConsole(context, code) as never, dispose: () => context.dispose(), }) } finally { @@ -66,7 +130,7 @@ type TypedUnknown = unknown function runSync(vm: QuickJSContext, code: string): T { const result = vm.evalCode(code) - const value = vm.unwrapResult(result) + const value = unwrapQuickJsResult(vm, result, code) try { return vm.dump(value) as T } finally { @@ -76,7 +140,7 @@ function runSync(vm: QuickJSContext, code: string): T { async function runAsync(context: Awaited>, code: string): Promise { const result = await context.evalCodeAsync(code) - const value = context.unwrapResult(result) + const value = unwrapQuickJsResult(context, result, code) try { return context.dump(value) as T } finally { @@ -84,6 +148,97 @@ async function runAsync(context: Awaited>, } } +function runSyncWithConsole(vm: QuickJSContext, code: string): QuickJsRunWithConsoleResult { + clearConsole(vm) + try { + const value = runSync(vm, code) + return { ok: true, value, console: readConsole(vm) } + } catch (error) { + return { ok: false, error: serializeError(error), console: readConsole(vm) } + } +} + +async function runAsyncWithConsole( + context: Awaited>, + code: string, +): Promise> { + clearConsole(context) + try { + const value = await runAsync(context, code) + return { ok: true, value, console: readConsole(context) } + } catch (error) { + return { ok: false, error: serializeError(error), console: readConsole(context) } + } +} + +function unwrapQuickJsResult( + context: QuickJSContext | Awaited>, + result: unknown, + code: string, +): QuickJSHandle { + try { + return context.unwrapResult(result as never) as QuickJSHandle + } catch (error) { + throw createQuickJsExecutionError(error, code) + } +} + +function createQuickJsExecutionError(error: unknown, code: string): QuickJsExecutionError { + const cause = error && typeof error === 'object' && 'cause' in error ? (error as { cause?: unknown }).cause : error + const causeRecord = cause && typeof cause === 'object' ? (cause as Record) : undefined + const name = typeof causeRecord?.name === 'string' ? causeRecord.name : error instanceof Error ? error.name : undefined + const message = typeof causeRecord?.message === 'string' ? causeRecord.message : error instanceof Error ? error.message : String(error) + const fileName = typeof causeRecord?.fileName === 'string' ? causeRecord.fileName : undefined + const lineNumber = typeof causeRecord?.lineNumber === 'number' ? causeRecord.lineNumber : undefined + const columnNumber = typeof causeRecord?.columnNumber === 'number' ? causeRecord.columnNumber : undefined + + return new QuickJsExecutionError( + { + name, + message, + fileName, + lineNumber, + columnNumber, + codeFrame: lineNumber ? formatCodeFrame(code, lineNumber, columnNumber) : undefined, + }, + { cause }, + ) +} + +function serializeError(error: unknown): QuickJsSerializedError { + if (error instanceof QuickJsExecutionError) return error.details + if (error instanceof Error) return { name: error.name, message: error.message } + return { message: String(error) } +} + +function formatQuickJsExecutionMessage(details: QuickJsExecutionErrorDetails): string { + const location = details.lineNumber + ? `${details.fileName ?? 'eval.js'}:${details.lineNumber}${details.columnNumber ? `:${details.columnNumber}` : ''}` + : undefined + const headline = [details.name, details.message].filter(Boolean).join(': ') + return [location ? `${headline} at ${location}` : headline, details.codeFrame].filter(Boolean).join('\n') +} + +function formatCodeFrame(code: string, lineNumber: number, columnNumber?: number): string { + const lines = code.replace(/^\n/, '').split('\n') + const normalizedLineNumber = Math.max(1, Math.min(lineNumber, lines.length)) + const start = Math.max(1, normalizedLineNumber - 2) + const end = Math.min(lines.length, normalizedLineNumber + 2) + const width = String(end).length + const frame: string[] = [] + + for (let line = start; line <= end; line++) { + const marker = line === normalizedLineNumber ? '>' : ' ' + const source = lines[line - 1] ?? '' + frame.push(`${marker} ${String(line).padStart(width, ' ')} | ${source}`) + if (line === normalizedLineNumber && columnNumber && columnNumber > 0) { + frame.push(` ${' '.repeat(width)} | ${' '.repeat(columnNumber - 1)}^`) + } + } + + return frame.join('\n') +} + function installSyncBindings(input: { vm: QuickJSContext bindings: QuickJsBindings @@ -119,6 +274,27 @@ function installAsyncifiedBindings(input: { function installBindingFacade(vm: QuickJSContext, disposableHandles: QuickJSHandle[]): void { const result = vm.evalCode(` + globalThis.__quickjsConsoleLogs = [] + function __quickjsConsoleText(args) { + return args.map((arg) => { + if (typeof arg === 'string') return arg + try { return JSON.stringify(arg) } + catch { return String(arg) } + }).join(' ') + } + function __quickjsConsolePush(level, args) { + globalThis.__quickjsConsoleLogs.push({ + level, + args, + text: __quickjsConsoleText(args), + }) + } + globalThis.console = { + log: (...args) => __quickjsConsolePush('log', args), + info: (...args) => __quickjsConsolePush('info', args), + warn: (...args) => __quickjsConsolePush('warn', args), + error: (...args) => __quickjsConsolePush('error', args), + } globalThis.bindings = new Proxy({}, { get(_target, prop) { return (...args) => { @@ -132,6 +308,22 @@ function installBindingFacade(vm: QuickJSContext, disposableHandles: QuickJSHand disposableHandles.push(value) } +function clearConsole(vm: QuickJSContext | Awaited>): void { + const result = vm.evalCode('globalThis.__quickjsConsoleLogs.length = 0') + const value = vm.unwrapResult(result) + value.dispose() +} + +function readConsole(vm: QuickJSContext | Awaited>): QuickJsConsoleEntry[] { + const result = vm.evalCode('globalThis.__quickjsConsoleLogs') + const value = vm.unwrapResult(result) + try { + return vm.dump(value) as QuickJsConsoleEntry[] + } finally { + value.dispose() + } +} + function disposeHandles(handles: QuickJSHandle[]): void { for (const handle of handles) handle.dispose() } diff --git a/packages/quickjs-exec/test/quickjs-exec.test.ts b/packages/quickjs-exec/test/quickjs-exec.test.ts index 6ee5058..6a9ba3f 100644 --- a/packages/quickjs-exec/test/quickjs-exec.test.ts +++ b/packages/quickjs-exec/test/quickjs-exec.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from 'bun:test' -import { withAsyncQuickJsMode, withQuickJsMode } from '../src' +import { QuickJsExecutionError, withAsyncQuickJsMode, withQuickJsMode } from '../src' describe('quickjs-exec', () => { test('runs sync code with sync host bindings', async () => { @@ -25,6 +25,84 @@ describe('quickjs-exec', () => { }) }) + test('script can return final parenthesized object expression', async () => { + await withQuickJsMode({}, ({ run }) => { + const result = run<{ ok: boolean; value: number }>(` + const value = 20 + 22 + ;({ ok: true, value }) + `) + + expect(result).toEqual({ ok: true, value: 42 }) + }) + }) + + test('runWithConsole captures logs and return value', async () => { + await withQuickJsMode({}, ({ runWithConsole }) => { + const result = runWithConsole<{ ok: boolean }>(` + console.log('created', { id: 'n2', kind: 'element' }) + ;({ ok: true }) + `) + + expect(result).toEqual({ + ok: true, + value: { ok: true }, + console: [ + { + level: 'log', + args: ['created', { id: 'n2', kind: 'element' }], + text: 'created {"id":"n2","kind":"element"}', + }, + ], + }) + }) + }) + + test('runWithConsole captures logs before runtime errors', async () => { + await withQuickJsMode({}, ({ runWithConsole }) => { + const result = runWithConsole(` + console.log('before failure', { step: 1 }) + missingReference + `) + + expect(result.ok).toBe(false) + if (result.ok) throw new Error('Expected failure') + expect(result.error.name).toBe('ReferenceError') + expect(result.error.message).toContain('missingReference') + expect(result.console).toEqual([ + { + level: 'log', + args: ['before failure', { step: 1 }], + text: 'before failure {"step":1}', + }, + ]) + }) + }) + + test('runWithConsole captures empty logs for syntax errors', async () => { + await withQuickJsMode({}, ({ runWithConsole }) => { + const result = runWithConsole(` + console.log('not reached') + const broken = { index 0 } + `) + + expect(result.ok).toBe(false) + if (result.ok) throw new Error('Expected failure') + expect(result.error.name).toBe('SyntaxError') + expect(result.error.codeFrame).toContain('index 0') + expect(result.console).toEqual([]) + }) + }) + + test('runWithConsole clears logs between runs', async () => { + await withQuickJsMode({}, ({ runWithConsole }) => { + const first = runWithConsole(`console.log('first')`) + const second = runWithConsole(`console.log('second')`) + + expect(first.console.map((entry) => entry.text)).toEqual(['first']) + expect(second.console.map((entry) => entry.text)).toEqual(['second']) + }) + }) + test('runs asyncified host bindings', async () => { await withAsyncQuickJsMode( { @@ -40,6 +118,26 @@ describe('quickjs-exec', () => { ) }) + test('async runWithConsole captures logs and return value', async () => { + await withAsyncQuickJsMode( + { + addAsync: async (a, b) => (a as number) + (b as number), + }, + async ({ runWithConsole }) => { + const result = await runWithConsole<{ sum: number }>(` + console.info('before async') + ;({ sum: bindings.addAsync(20, 22) }) + `) + + expect(result).toEqual({ + ok: true, + value: { sum: 42 }, + console: [{ level: 'info', args: ['before async'], text: 'before async' }], + }) + }, + ) + }) + test('sync harness rejects async binding functions', async () => { await expect( withQuickJsMode( @@ -50,4 +148,32 @@ describe('quickjs-exec', () => { ), ).rejects.toThrow('Async binding asyncBinding used with withQuickJsMode') }) + + test('throws concise QuickJS syntax errors with code frame', async () => { + await expect( + withQuickJsMode({}, ({ run }) => + run(` + const ok = 1 + const broken = { node: bindings.root(), index 0 } + broken + `), + ), + ).rejects.toThrow(QuickJsExecutionError) + + try { + await withQuickJsMode({}, ({ run }) => + run(` + const ok = 1 + const broken = { node: bindings.root(), index 0 } + broken + `), + ) + } catch (error) { + expect(error).toBeInstanceOf(QuickJsExecutionError) + expect((error as Error).message).toContain('SyntaxError: expecting') + expect((error as Error).message).toContain('at eval.js:3') + expect((error as Error).message).toContain('> 3 |') + expect((error as Error).message).toContain('index 0') + } + }) }) diff --git a/packages/yjs-rich-markdown/package.json b/packages/yjs-rich-markdown/package.json index 502deb2..f76f314 100644 --- a/packages/yjs-rich-markdown/package.json +++ b/packages/yjs-rich-markdown/package.json @@ -48,6 +48,7 @@ "@tiptap/markdown": "3.22.5", "@tiptap/pm": "3.22.5", "@tiptap/starter-kit": "3.22.5", + "@humanlayer/quickjs-exec": "workspace:*", "dedent": "1.7.2", "y-prosemirror": "1.3.7" }, @@ -55,4 +56,4 @@ "name": "Kyle Mistele", "url": "https://x.com/0xblacklight" } -} +} \ No newline at end of file diff --git a/packages/yjs-rich-markdown/src/index.ts b/packages/yjs-rich-markdown/src/index.ts index b414657..5678908 100644 --- a/packages/yjs-rich-markdown/src/index.ts +++ b/packages/yjs-rich-markdown/src/index.ts @@ -6,7 +6,15 @@ export { ArtifactAlreadyExistsError, ArtifactNotFoundError, RichMarkdownArtifact export { markdownToTiptapJson, readArtifactMarkdown, tiptapJsonToMarkdown, writeArtifactMarkdown } from './markdown' export type { ArtifactCollaborationMode, ArtifactCollaborationOptions } from './tiptap-bindings' export { artifactCollaborationExtension, artifactFragment, defaultRichMarkdownExtensions } from './tiptap-bindings' -export type { YXmlNodeKind, YXmlNodeRef, YXmlNodeSpec, YXmlNodeSummary, YXmlProxy } from './yxml-proxy' +export type { + YXmlElementWrapperSpec, + YXmlNodeKind, + YXmlNodeRef, + YXmlNodeSpec, + YXmlNodeSummary, + YXmlProxy, + YXmlProxyHostBindings, +} from './yxml-proxy' export { DetachedYXmlNodeRefError, UnknownYXmlNodeRefError, @@ -16,5 +24,6 @@ export { YXmlProxyBindings, YXmlProxyError, YXmlRootOperationError, + YXmlTextRangeOutOfBoundsError, } from './yxml-proxy' export { YXML_PROXY_AGENT_PROMPT } from './yxml-proxy-prompt' diff --git a/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts b/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts index c7d8edb..be74b73 100644 --- a/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts +++ b/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts @@ -42,11 +42,17 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` bindings.summary({ node: NodeRef }): { id: string; kind: string; nodeName: string | null; length: number; xml: string; attributes?: Record } bindings.children({ node?: NodeRef }): NodeRef[] bindings.get({ node?: NodeRef; index: number }): NodeRef + bindings.text({ node: NodeRef }): string bindings.toString({ node?: NodeRef }): string bindings.append({ parent?: NodeRef; content: NodeSpec[] }): NodeRef[] bindings.prepend({ parent?: NodeRef; content: NodeSpec[] }): NodeRef[] bindings.insertBefore({ ref: NodeRef; content: NodeSpec[] }): NodeRef[] bindings.insertAfter({ ref: NodeRef; content: NodeSpec[] }): NodeRef[] + bindings.insertText({ node: NodeRef; index: number; text: string }): void + bindings.deleteText({ node: NodeRef; index: number; length: number }): void + bindings.splitText({ node: NodeRef; index: number }): { left: NodeRef; right: NodeRef } + bindings.wrapTextRange({ node: NodeRef; start: number; end: number; wrapper: { nodeName: string; attributes?: Record } }): { wrapper: NodeRef; child: NodeRef; before?: NodeRef; after?: NodeRef } + bindings.wrap({ node: NodeRef; wrapper: { nodeName: string; attributes?: Record } }): { wrapper: NodeRef; child: NodeRef } bindings.remove({ node: NodeRef }): void bindings.setAttribute({ node: NodeRef; name: string; value: string }): void bindings.removeAttribute({ node: NodeRef; name: string }): void @@ -57,6 +63,11 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` - Call \`bindings.toString({})\` to inspect the full current XML before editing. - Use \`append\` or \`prepend\` when adding content to the root or to a known parent element. - Use \`insertBefore\` or \`insertAfter\` when adding content relative to an existing node. These do not need a parent; the host infers it from the referenced node. + - Use \`text\` to read the literal contents of a text node. + - Use \`insertText\` and \`deleteText\` to edit characters inside an existing text node. + - Use \`splitText\` to break one text node into two sibling text nodes at a character offset. + - Use \`wrapTextRange\` to wrap only part of a text node, such as bolding a single word inside a longer text node. + - Use \`wrap\` to surround an existing element or text node with a new element, such as wrapping text in a \`bold\` mark. The old node ref becomes detached; use the returned \`child\` ref if you need to keep editing the wrapped child. - Use \`remove\` to erase a node and all of its nested children. - Use \`setAttribute\` to change an element attribute, such as changing a heading from level 2 to level 3. - Use \`removeAttribute\` to delete an attribute. @@ -64,7 +75,11 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` - Build nested content by nesting \`children\` arrays in \`NodeSpec\` objects. - Prefer structural edits over string replacement of the full XML. - When creating common markdown structures, use the canonical TipTap/ProseMirror node names listed below. Some of these serialize to lowercase in XML strings, but generated \`NodeSpec\` content should use canonical names. - - Return a small JSON object with \`ok\`, \`beforeXml\`, and \`afterXml\`. + - Generated code is evaluated as a script. Top-level \`return\` is invalid unless you wrap the code in a function yourself. + - If you want to return a final value, end the script with a parenthesized object expression like \`;({ ok: true, beforeXml, afterXml })\`. + - Use \`console.log\`, \`console.info\`, \`console.warn\`, or \`console.error\` for diagnostics such as before/after XML, selected refs, summaries, and branch decisions. + - Console output is captured by the host and returned to the apply agent even if generated code throws after logging. Runtime failures return \`{ ok: false, error, console }\`; successful runs return \`{ ok: true, value, console }\`. + - Log enough context to repair failures: selected \`NodeRef\` values, \`bindings.summary(...)\` output, target text snippets, and final XML. Common markdown YXml elements: @@ -93,6 +108,8 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` \`\`\`js const beforeXml = bindings.toString({}) const first = bindings.get({ index: 0 }) + console.log('beforeXml', beforeXml) + console.log('first child', first, bindings.summary({ node: first })) bindings.insertAfter({ ref: first, @@ -105,13 +122,16 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` ], }) - return { ok: true, beforeXml, afterXml: bindings.toString({}) } + const afterXml = bindings.toString({}) + console.log('afterXml', afterXml) + ;({ ok: true, beforeXml, afterXml }) \`\`\` Example: add a new section containing a heading, paragraph, list, blockquote, horizontal rule, and code block. \`\`\`js const beforeXml = bindings.toString({}) + console.log('beforeXml', beforeXml) bindings.append({ content: [ @@ -161,7 +181,9 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` ], }) - return { ok: true, beforeXml, afterXml: bindings.toString({}) } + const afterXml = bindings.toString({}) + console.log('afterXml', afterXml) + ;({ ok: true, beforeXml, afterXml }) \`\`\` Example: add a bullet to an existing list. @@ -171,6 +193,7 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` const rootChildren = bindings.children({}) const list = rootChildren.find((node) => bindings.summary({ node }).nodeName === 'bulletList') if (!list) throw new Error('Could not find a bullet list') + console.log('list', list, bindings.summary({ node: list })) bindings.append({ parent: list, @@ -189,7 +212,9 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` ], }) - return { ok: true, beforeXml, afterXml: bindings.toString({}) } + const afterXml = bindings.toString({}) + console.log('afterXml', afterXml) + ;({ ok: true, beforeXml, afterXml }) \`\`\` Example: change a heading level attribute. @@ -198,10 +223,14 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` const beforeXml = bindings.toString({}) const heading = bindings.children({}).find((node) => bindings.summary({ node }).nodeName === 'heading') if (!heading) throw new Error('Could not find a heading') + console.log('heading before', heading, bindings.summary({ node: heading })) bindings.setAttribute({ node: heading, name: 'level', value: '3' }) + console.log('heading after', bindings.summary({ node: heading })) - return { ok: true, beforeXml, afterXml: bindings.toString({}) } + const afterXml = bindings.toString({}) + console.log('afterXml', afterXml) + ;({ ok: true, beforeXml, afterXml }) \`\`\` Example: erase a section. @@ -213,10 +242,84 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` return summary.nodeName === 'section' && summary.attributes?.id === 'obsolete' }) if (!section) throw new Error('Could not find obsolete section') + console.log('removing section', section, bindings.summary({ node: section })) bindings.remove({ node: section }) - return { ok: true, beforeXml, afterXml: bindings.toString({}) } + const afterXml = bindings.toString({}) + console.log('afterXml', afterXml) + ;({ ok: true, beforeXml, afterXml }) + \`\`\` + + Example: wrap existing deeply nested text in a bold mark. + + \`\`\`js + const beforeXml = bindings.toString({}) + const list = bindings.children({}).find((node) => bindings.summary({ node }).nodeName === 'bulletList') + if (!list) throw new Error('Could not find a bullet list') + const secondItem = bindings.get({ node: list, index: 1 }) + const paragraph = bindings.get({ node: secondItem, index: 0 }) + const text = bindings.children({ node: paragraph }).find((node) => bindings.summary({ node }).kind === 'text') + if (!text) throw new Error('Could not find text to bold') + console.log('wrapping text', text, bindings.summary({ node: text })) + + bindings.wrap({ node: text, wrapper: { nodeName: 'bold' } }) + + const afterXml = bindings.toString({}) + console.log('afterXml', afterXml) + ;({ ok: true, beforeXml, afterXml }) + \`\`\` + + Example: wrap only one word inside an interleaved text node. + + \`\`\`js + const beforeXml = bindings.toString({}) + const example = bindings.children({}).find((node) => bindings.summary({ node }).nodeName === 'example') + if (!example) throw new Error('Could not find example element') + const firstChild = bindings.get({ node: example, index: 0 }) + const firstText = bindings.text({ node: firstChild }) + const start = firstText.indexOf('example') + if (start < 0) throw new Error('Could not find target word') + console.log('wrapping range', { start, end: start + 'example'.length, firstText }) + + bindings.wrapTextRange({ + node: firstChild, + start, + end: start + 'example'.length, + wrapper: { nodeName: 'bold' }, + }) + + const afterXml = bindings.toString({}) + console.log('afterXml', afterXml) + ;({ ok: true, beforeXml, afterXml }) + \`\`\` + + Example: wrap one word inside text that already lives under an inline mark. + + \`\`\`js + const beforeXml = bindings.toString({}) + const paragraph = bindings.children({}).find((node) => bindings.summary({ node }).nodeName === 'paragraph') + if (!paragraph) throw new Error('Could not find paragraph') + const italic = bindings.children({ node: paragraph }).find((node) => bindings.summary({ node }).nodeName === 'italic') + if (!italic) throw new Error('Could not find italic mark') + const italicTextNode = bindings.children({ node: italic }).find((node) => bindings.summary({ node }).kind === 'text') + if (!italicTextNode) throw new Error('Could not find italic text node') + + const italicText = bindings.text({ node: italicTextNode }) + const start = italicText.indexOf('example') + if (start < 0) throw new Error('Could not find target word') + console.log('wrapping italic range', { start, end: start + 'example'.length, italicText }) + + bindings.wrapTextRange({ + node: italicTextNode, + start, + end: start + 'example'.length, + wrapper: { nodeName: 'bold' }, + }) + + const afterXml = bindings.toString({}) + console.log('afterXml', afterXml) + ;({ ok: true, beforeXml, afterXml }) \`\`\` ` diff --git a/packages/yjs-rich-markdown/src/yxml-proxy.ts b/packages/yjs-rich-markdown/src/yxml-proxy.ts index f0a1a74..03c7f80 100644 --- a/packages/yjs-rich-markdown/src/yxml-proxy.ts +++ b/packages/yjs-rich-markdown/src/yxml-proxy.ts @@ -1,4 +1,5 @@ import * as Y from 'yjs' +import type { QuickJsBindings } from '@humanlayer/quickjs-exec' export type YXmlNodeKind = 'fragment' | 'element' | 'text' @@ -26,21 +27,39 @@ export type YXmlNodeSpec = text?: string } +export type YXmlElementWrapperSpec = { + nodeName: string + attributes?: Record +} + export type YXmlProxy = { root(): YXmlNodeRef summary(input: { node: YXmlNodeRef }): YXmlNodeSummary children(input?: { node?: YXmlNodeRef }): YXmlNodeRef[] get(input: { node?: YXmlNodeRef; index: number }): YXmlNodeRef + text(input: { node: YXmlNodeRef }): string toString(input?: { node?: YXmlNodeRef }): string append(input: { parent?: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] prepend(input: { parent?: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] insertBefore(input: { ref: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] insertAfter(input: { ref: YXmlNodeRef; content: YXmlNodeSpec[] }): YXmlNodeRef[] + insertText(input: { node: YXmlNodeRef; index: number; text: string }): void + deleteText(input: { node: YXmlNodeRef; index: number; length: number }): void + splitText(input: { node: YXmlNodeRef; index: number }): { left: YXmlNodeRef; right: YXmlNodeRef } + wrapTextRange(input: { + node: YXmlNodeRef + start: number + end: number + wrapper: YXmlElementWrapperSpec + }): { wrapper: YXmlNodeRef; child: YXmlNodeRef; before?: YXmlNodeRef; after?: YXmlNodeRef } + wrap(input: { node: YXmlNodeRef; wrapper: YXmlElementWrapperSpec }): { wrapper: YXmlNodeRef; child: YXmlNodeRef } remove(input: { node: YXmlNodeRef }): void setAttribute(input: { node: YXmlNodeRef; name: string; value: string }): void removeAttribute(input: { node: YXmlNodeRef; name: string }): void } +export type YXmlProxyHostBindings = YXmlProxy & QuickJsBindings + export class YXmlProxyError extends Error { constructor( message: string, @@ -89,6 +108,18 @@ export class YXmlChildIndexOutOfBoundsError extends YXmlProxyError { } } +export class YXmlTextRangeOutOfBoundsError extends YXmlProxyError { + constructor( + readonly operation: string, + readonly start: number, + readonly end: number, + readonly length: number, + ) { + super(`Cannot ${operation} text range [${start}, ${end}) on text length ${length}`, 'TEXT_RANGE_OUT_OF_BOUNDS') + this.name = 'YXmlTextRangeOutOfBoundsError' + } +} + export class DetachedYXmlNodeRefError extends YXmlProxyError { constructor(readonly operation: string) { super(`Cannot ${operation} deleted YXml node`, 'DETACHED_NODE_REF') @@ -112,9 +143,36 @@ export class YXmlProxyBindings implements YXmlProxy { private readonly refs = new WeakMap() private readonly deletedRefs = new Set() private nextId = 1 + readonly bindings: YXmlProxyHostBindings constructor(private readonly rootFragment: Y.XmlFragment) { this.register(rootFragment) + this.bindings = { + root: () => this.root(), + summary: (input) => this.summary(input as { node: YXmlNodeRef }), + children: (input) => this.children(input as { node?: YXmlNodeRef } | undefined), + get: (input) => this.get(input as { node?: YXmlNodeRef; index: number }), + text: (input) => this.text(input as { node: YXmlNodeRef }), + toString: (input) => this.toString(input as { node?: YXmlNodeRef } | undefined), + append: (input) => this.append(input as { parent?: YXmlNodeRef; content: YXmlNodeSpec[] }), + prepend: (input) => this.prepend(input as { parent?: YXmlNodeRef; content: YXmlNodeSpec[] }), + insertBefore: (input) => this.insertBefore(input as { ref: YXmlNodeRef; content: YXmlNodeSpec[] }), + insertAfter: (input) => this.insertAfter(input as { ref: YXmlNodeRef; content: YXmlNodeSpec[] }), + insertText: (input) => this.insertText(input as { node: YXmlNodeRef; index: number; text: string }), + deleteText: (input) => this.deleteText(input as { node: YXmlNodeRef; index: number; length: number }), + splitText: (input) => this.splitText(input as { node: YXmlNodeRef; index: number }), + wrapTextRange: (input) => + this.wrapTextRange(input as { + node: YXmlNodeRef + start: number + end: number + wrapper: YXmlElementWrapperSpec + }), + wrap: (input) => this.wrap(input as { node: YXmlNodeRef; wrapper: YXmlElementWrapperSpec }), + remove: (input) => this.remove(input as { node: YXmlNodeRef }), + setAttribute: (input) => this.setAttribute(input as { node: YXmlNodeRef; name: string; value: string }), + removeAttribute: (input) => this.removeAttribute(input as { node: YXmlNodeRef; name: string }), + } as YXmlProxyHostBindings } root(): YXmlNodeRef { @@ -146,6 +204,10 @@ export class YXmlProxyBindings implements YXmlProxy { return this.register(child) } + text({ node }: { node: YXmlNodeRef }): string { + return this.resolveText(node).toString() + } + toString(input: { node?: YXmlNodeRef } = {}): string { return this.resolve(input.node).toString() } @@ -186,6 +248,112 @@ export class YXmlProxyBindings implements YXmlProxy { return this.refsFor(nodesToInsert) } + insertText({ node, index, text }: { node: YXmlNodeRef; index: number; text: string }): void { + if (this.deletedRefs.has(node.id)) throw new DetachedYXmlNodeRefError('insert text') + const target = this.resolveText(node) + this.assertTextRange({ operation: 'insert text into', length: target.length, start: index, end: index }) + if (text.length === 0) return + target.insert(index, text) + } + + deleteText({ node, index, length }: { node: YXmlNodeRef; index: number; length: number }): void { + if (this.deletedRefs.has(node.id)) throw new DetachedYXmlNodeRefError('delete text') + const target = this.resolveText(node) + this.assertTextRange({ operation: 'delete text from', length: target.length, start: index, end: index + length }) + if (length === 0) return + target.delete(index, length) + } + + splitText({ node, index }: { node: YXmlNodeRef; index: number }): { left: YXmlNodeRef; right: YXmlNodeRef } { + if (this.deletedRefs.has(node.id)) throw new DetachedYXmlNodeRefError('split text') + const target = this.resolveText(node) + this.assertTextRange({ operation: 'split text', length: target.length, start: index, end: index }) + if (index === 0 || index === target.length) { + throw new YXmlTextRangeOutOfBoundsError('split text', index, index, target.length) + } + + const value = target.toString() + const left = this.buildTextNode(value.slice(0, index)) + const right = this.buildTextNode(value.slice(index)) + this.replaceNode(target, 'split text', [left, right], node.id) + + return { left: this.register(left), right: this.register(right) } + } + + wrapTextRange({ + node, + start, + end, + wrapper, + }: { + node: YXmlNodeRef + start: number + end: number + wrapper: YXmlElementWrapperSpec + }): { wrapper: YXmlNodeRef; child: YXmlNodeRef; before?: YXmlNodeRef; after?: YXmlNodeRef } { + if (this.deletedRefs.has(node.id)) throw new DetachedYXmlNodeRefError('wrap text range') + const target = this.resolveText(node) + this.assertTextRange({ operation: 'wrap text range in', length: target.length, start, end }) + if (start === end) throw new YXmlTextRangeOutOfBoundsError('wrap text range', start, end, target.length) + + const value = target.toString() + const beforeText = value.slice(0, start) + const wrappedText = value.slice(start, end) + const afterText = value.slice(end) + + const nodesToInsert: InsertableYXmlNode[] = [] + let beforeNode: Y.XmlText | undefined + if (beforeText.length > 0) { + beforeNode = this.buildTextNode(beforeText) + nodesToInsert.push(beforeNode) + } + + const child = this.buildTextNode(wrappedText) + const wrapperNode = new Y.XmlElement(wrapper.nodeName) + for (const [name, value] of Object.entries(wrapper.attributes ?? {})) { + wrapperNode.setAttribute(name, value) + } + wrapperNode.insert(0, [child]) + nodesToInsert.push(wrapperNode) + + let afterNode: Y.XmlText | undefined + if (afterText.length > 0) { + afterNode = this.buildTextNode(afterText) + nodesToInsert.push(afterNode) + } + + this.replaceNode(target, 'wrap text range', nodesToInsert, node.id) + + return { + ...(beforeNode ? { before: this.register(beforeNode) } : {}), + wrapper: this.register(wrapperNode), + child: this.register(child), + ...(afterNode ? { after: this.register(afterNode) } : {}), + } + } + + wrap({ node, wrapper }: { node: YXmlNodeRef; wrapper: YXmlElementWrapperSpec }): { wrapper: YXmlNodeRef; child: YXmlNodeRef } { + if (this.deletedRefs.has(node.id)) throw new DetachedYXmlNodeRefError('wrap') + const target = this.resolve(node) + if (target === this.rootFragment) throw new YXmlRootOperationError('wrap') + const parent = this.parentContainerOf(target, 'wrap') + const index = parent.slice().indexOf(target as Y.XmlElement | Y.XmlText) + if (index < 0) throw new DetachedYXmlNodeRefError('wrap') + + const child = this.cloneNode(target as InsertableYXmlNode) + const wrapperNode = new Y.XmlElement(wrapper.nodeName) + for (const [name, value] of Object.entries(wrapper.attributes ?? {})) { + wrapperNode.setAttribute(name, value) + } + wrapperNode.insert(0, [child]) + + parent.delete(index, 1) + parent.insert(index, [wrapperNode]) + this.deletedRefs.add(node.id) + + return { wrapper: this.register(wrapperNode), child: this.register(child) } + } + remove({ node }: { node: YXmlNodeRef }): void { if (this.deletedRefs.has(node.id)) throw new DetachedYXmlNodeRefError('remove') const target = this.resolve(node) @@ -237,15 +405,19 @@ export class YXmlProxyBindings implements YXmlProxy { return node } + private resolveText(ref: YXmlNodeRef): Y.XmlText { + const node = this.resolve(ref) + if (!(node instanceof Y.XmlText)) throw new YXmlInvalidNodeKindForOperationError('read or edit text', 'text', kindOf(node)) + return node + } + private buildNodes(specs: YXmlNodeSpec[]): InsertableYXmlNode[] { return specs.map((spec) => this.buildNode(spec)) } private buildNode(spec: YXmlNodeSpec): InsertableYXmlNode { if (spec.kind === 'text') { - const text = new Y.XmlText() - if (spec.text) text.insert(0, spec.text) - return text + return this.buildTextNode(spec.text ?? '') } const element = new Y.XmlElement(spec.nodeName) @@ -261,6 +433,51 @@ export class YXmlProxyBindings implements YXmlProxy { return content.map((node) => this.register(node)) } + private buildTextNode(value: string): Y.XmlText { + const text = new Y.XmlText() + if (value.length > 0) text.insert(0, value) + return text + } + + private cloneNode(node: InsertableYXmlNode): InsertableYXmlNode { + if (node instanceof Y.XmlText) { + return this.buildTextNode(node.toString()) + } + + const element = new Y.XmlElement(node.nodeName) + for (const [name, value] of Object.entries(node.getAttributes())) { + element.setAttribute(name, value ?? '') + } + const children = node.slice().map((child) => this.cloneNode(child as InsertableYXmlNode)) + if (children.length > 0) element.insert(0, children) + return element + } + + private replaceNode(node: InsertableYXmlNode, operation: string, replacements: InsertableYXmlNode[], deletedRefId: string): void { + const parent = this.parentContainerOf(node, operation) + const index = parent.slice().indexOf(node) + if (index < 0) throw new DetachedYXmlNodeRefError(operation) + parent.delete(index, 1) + if (replacements.length > 0) parent.insert(index, replacements) + this.deletedRefs.add(deletedRefId) + } + + private assertTextRange({ + operation, + length, + start, + end, + }: { + operation: string + length: number + start: number + end: number + }): void { + if (start < 0 || end < start || end > length) { + throw new YXmlTextRangeOutOfBoundsError(operation, start, end, length) + } + } + private parentContainerOf(node: YXmlNode, operation: string): YXmlContainer { if (node.doc === null) throw new DetachedYXmlNodeRefError(operation) const parent = node.parent diff --git a/packages/yjs-rich-markdown/test/yxml-proxy-prompt-examples.test.ts b/packages/yjs-rich-markdown/test/yxml-proxy-prompt-examples.test.ts new file mode 100644 index 0000000..4e579fd --- /dev/null +++ b/packages/yjs-rich-markdown/test/yxml-proxy-prompt-examples.test.ts @@ -0,0 +1,282 @@ +import { describe, expect, test } from 'bun:test' +import * as Y from 'yjs' +import { YXmlProxyBindings } from '../src' + +describe('YXML proxy prompt examples', () => { + test('add a paragraph after the first top-level node', () => { + const proxy = createPromptExampleProxy() + + const beforeXml = proxy.toString({}) + expect(beforeXml).toContain('Original Heading') + + const first = proxy.get({ index: 0 }) + proxy.insertAfter({ + ref: first, + content: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Added paragraph.' }], + }, + ], + }) + + expect(proxy.toString({})).toBe( + 'Original HeadingAdded paragraph.Original paragraph.Use scoped NodeRefs.Validate after mutation.
Remove me.
', + ) + }) + + test('add a new section containing a heading, paragraph, list, blockquote, horizontal rule, and code block', () => { + const proxy = createPromptExampleProxy() + + proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'heading', + attributes: { level: '2' }, + children: [{ kind: 'text', text: 'Implementation Notes' }], + }, + { + kind: 'element', + nodeName: 'paragraph', + children: [ + { kind: 'text', text: 'The agent edits through ' }, + { kind: 'element', nodeName: 'code', children: [{ kind: 'text', text: 'bindings' }] }, + { kind: 'text', text: ' instead of direct Yjs objects.' }, + ], + }, + { + kind: 'element', + nodeName: 'bulletList', + children: [ + { + kind: 'element', + nodeName: 'listItem', + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Use scoped NodeRefs.' }] }], + }, + { + kind: 'element', + nodeName: 'listItem', + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Validate after mutation.' }] }], + }, + ], + }, + { + kind: 'element', + nodeName: 'blockquote', + children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Bindings keep generated code sandbox-safe.' }] }], + }, + { kind: 'element', nodeName: 'horizontalRule' }, + { + kind: 'element', + nodeName: 'codeBlock', + attributes: { language: 'ts' }, + children: [{ kind: 'text', text: 'const root = bindings.root()\nconst xml = bindings.toString({})' }], + }, + ], + }) + + expect(proxy.toString({})).toContain('Implementation Notes') + expect(proxy.toString({})).toContain('The agent edits through bindings instead of direct Yjs objects.') + expect(proxy.toString({})).toContain('
Bindings keep generated code sandbox-safe.
') + expect(proxy.toString({})).toContain('') + expect(proxy.toString({})).toContain('const root = bindings.root()\nconst xml = bindings.toString({})') + }) + + test('add a bullet to an existing list', () => { + const proxy = createPromptExampleProxy() + + const rootChildren = proxy.children({}) + const list = rootChildren.find((node) => proxy.summary({ node }).nodeName === 'bulletList') + if (!list) throw new Error('Could not find a bullet list') + + proxy.append({ + parent: list, + content: [ + { + kind: 'element', + nodeName: 'listItem', + children: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'New bullet' }], + }, + ], + }, + ], + }) + + expect(proxy.toString({})).toContain( + 'Use scoped NodeRefs.Validate after mutation.New bullet', + ) + }) + + test('change a heading level attribute', () => { + const proxy = createPromptExampleProxy() + + const heading = proxy.children({}).find((node) => proxy.summary({ node }).nodeName === 'heading') + if (!heading) throw new Error('Could not find a heading') + + proxy.setAttribute({ node: heading, name: 'level', value: '3' }) + + expect(proxy.summary({ node: heading }).attributes).toEqual({ level: '3' }) + expect(proxy.toString({})).toContain('Original Heading') + }) + + test('erase a section', () => { + const proxy = createPromptExampleProxy() + + const section = proxy.children({}).find((node) => { + const summary = proxy.summary({ node }) + return summary.nodeName === 'section' && summary.attributes?.id === 'obsolete' + }) + if (!section) throw new Error('Could not find obsolete section') + + proxy.remove({ node: section }) + + expect(proxy.toString({})).not.toContain('
') + expect(proxy.toString({})).toBe( + 'Original HeadingOriginal paragraph.Use scoped NodeRefs.Validate after mutation.', + ) + }) + + test('wrap existing deeply nested text in a bold mark', () => { + const proxy = createPromptExampleProxy() + + const list = proxy.children({}).find((node) => proxy.summary({ node }).nodeName === 'bulletList') + if (!list) throw new Error('Could not find a bullet list') + const secondItem = proxy.get({ node: list, index: 1 }) + const paragraph = proxy.get({ node: secondItem, index: 0 }) + const text = proxy.children({ node: paragraph }).find((node) => proxy.summary({ node }).kind === 'text') + if (!text) throw new Error('Could not find text to bold') + + proxy.wrap({ node: text, wrapper: { nodeName: 'bold' } }) + + expect(proxy.toString({})).toContain( + 'Validate after mutation.', + ) + }) + + test('wrap only one word inside an interleaved text node', () => { + const proxy = createInterleavedExampleProxy() + + const example = proxy.children({}).find((node) => proxy.summary({ node }).nodeName === 'example') + if (!example) throw new Error('Could not find example element') + const firstChild = proxy.get({ node: example, index: 0 }) + const firstText = proxy.text({ node: firstChild }) + const start = firstText.indexOf('example') + if (start < 0) throw new Error('Could not find target word') + + proxy.wrapTextRange({ + node: firstChild, + start, + end: start + 'example'.length, + wrapper: { nodeName: 'bold' }, + }) + + expect(proxy.toString({})).toBe( + 'this is an example this is a nested example This is another example', + ) + }) + + test('wrap one word inside text that already lives under an inline mark', () => { + const proxy = createInlineMarkedExampleProxy() + + const paragraph = proxy.children({}).find((node) => proxy.summary({ node }).nodeName === 'paragraph') + if (!paragraph) throw new Error('Could not find paragraph') + const italic = proxy.children({ node: paragraph }).find((node) => proxy.summary({ node }).nodeName === 'italic') + if (!italic) throw new Error('Could not find italic mark') + const italicTextNode = proxy.children({ node: italic }).find((node) => proxy.summary({ node }).kind === 'text') + if (!italicTextNode) throw new Error('Could not find italic text node') + + const italicText = proxy.text({ node: italicTextNode }) + const start = italicText.indexOf('example') + if (start < 0) throw new Error('Could not find target word') + + proxy.wrapTextRange({ + node: italicTextNode, + start, + end: start + 'example'.length, + wrapper: { nodeName: 'bold' }, + }) + + expect(proxy.toString({})).toBe( + 'Before nested example text after', + ) + }) +}) + +function createPromptExampleProxy(): YXmlProxyBindings { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + seedPromptExampleDocument(fragment) + return new YXmlProxyBindings(fragment) +} + +function createInterleavedExampleProxy(): YXmlProxyBindings { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const example = new Y.XmlElement('example') + example.insert(0, [ + text('this is an example '), + createElement('nestedExample', [text('this is a nested example')]), + text(' This is another example'), + ]) + fragment.insert(0, [example]) + return new YXmlProxyBindings(fragment) +} + +function createInlineMarkedExampleProxy(): YXmlProxyBindings { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const paragraph = new Y.XmlElement('paragraph') + paragraph.insert(0, [ + text('Before '), + createElement('italic', [text('nested example text')]), + text(' after'), + ]) + fragment.insert(0, [paragraph]) + return new YXmlProxyBindings(fragment) +} + +function seedPromptExampleDocument(fragment: Y.XmlFragment): void { + const heading = new Y.XmlElement('heading') + heading.setAttribute('level', '2') + heading.insert(0, [text('Original Heading')]) + + const paragraph = new Y.XmlElement('paragraph') + paragraph.insert(0, [text('Original paragraph.')]) + + const list = new Y.XmlElement('bulletList') + list.insert(0, [listItem('Use scoped NodeRefs.'), listItem('Validate after mutation.')]) + + const section = new Y.XmlElement('section') + section.setAttribute('id', 'obsolete') + const sectionParagraph = new Y.XmlElement('paragraph') + sectionParagraph.insert(0, [text('Remove me.')]) + section.insert(0, [sectionParagraph]) + + fragment.insert(0, [heading, paragraph, list, section]) +} + +function listItem(value: string): Y.XmlElement { + const item = new Y.XmlElement('listItem') + const paragraph = new Y.XmlElement('paragraph') + paragraph.insert(0, [text(value)]) + item.insert(0, [paragraph]) + return item +} + +function createElement(name: string, children: Array): Y.XmlElement { + const element = new Y.XmlElement(name) + element.insert(0, children) + return element +} + +function text(value: string): Y.XmlText { + const xmlText = new Y.XmlText() + xmlText.insert(0, value) + return xmlText +} diff --git a/packages/yjs-rich-markdown/test/yxml-proxy-quickjs.test.ts b/packages/yjs-rich-markdown/test/yxml-proxy-quickjs.test.ts new file mode 100644 index 0000000..4117bd7 --- /dev/null +++ b/packages/yjs-rich-markdown/test/yxml-proxy-quickjs.test.ts @@ -0,0 +1,245 @@ +import { beforeEach, describe, expect, test } from 'bun:test' +import { withQuickJsMode } from '@humanlayer/quickjs-exec' +import * as Y from 'yjs' +import { YXmlProxyBindings } from '../src' + +describe('Y.XMLFragment Proxy tests', () => { + describe('Operations through proxy should propagate to document', () => { + let doc: Y.Doc + let fragment: Y.XmlFragment + let proxy: YXmlProxyBindings + let root: Y.XmlElement + + beforeEach(() => { + doc = new Y.Doc() + fragment = doc.getXmlFragment('artifact') + proxy = new YXmlProxyBindings(fragment) + root = new Y.XmlElement('root') + }) + + test('Adding fragment should succeed', () => { + fragment.insert(0, [root]) + expect(fragment.toJSON()).toEqual('') + }) + + test('Adding another fragment NOT through proxy should succeed', () => { + fragment.insert(0, [root]) + expect(fragment.toJSON()).toEqual('') + + const parent = new Y.XmlElement('parent') + const child = new Y.XmlElement('child') + + parent.insert(0, [child]) + root.insert(0, [parent]) + + expect(fragment.toJSON()).toEqual('') + }) + + test('Adding another fragment through proxy should succeed', () => { + fragment.insert(0, [root]) + expect(fragment.toJSON()).toEqual('') + + const fragmentRef = proxy.root() + const rootRef = proxy.get({ node: fragmentRef, index: 0 }) + expect(proxy.toString({ node: rootRef })).toEqual('') + + const insertedParent = proxy.append({ + parent: rootRef, + content: [{ kind: 'element', nodeName: 'parent' }], + }) + expect(insertedParent).toHaveLength(1) + const parentRef = insertedParent[0] + if (!parentRef) throw new Error('Expected parent ref') + + expect(proxy.toString({ node: parentRef })).toEqual('') + expect(fragment.toJSON()).toEqual('') + + const insertedChild = proxy.append({ + parent: parentRef, + content: [{ kind: 'element', nodeName: 'child' }], + }) + expect(insertedChild).toHaveLength(1) + const childRef = insertedChild[0] + if (!childRef) throw new Error('Expected child ref') + + expect(proxy.toString({ node: childRef })).toEqual('') + expect(proxy.toString({ node: parentRef })).toEqual('') + expect(fragment.toJSON()).toEqual('') + }) + + test('Adding another fragment through quickjs through proxy should succeed', async () => { + fragment.insert(0, [root]) + expect(fragment.toJSON()).toEqual('') + + await withQuickJsMode(proxy.bindings, (qjs) => { + qjs.run<{ root: string }>(` + ({ + root: bindings.toString({node: bindings.get({node: bindings.root(), index: 0})}) + }) + `) + }) + + await withQuickJsMode(proxy.bindings, (qjs) => { + qjs.run(` + const fragmentRef = bindings.root() + const rootRef = bindings.get({ node: fragmentRef, index: 0 }) + bindings.append({ + parent: rootRef, + content: [{ kind: 'element', nodeName: 'parent' }], + }) + `) + }) + expect(fragment.toJSON()).toEqual('') + + await withQuickJsMode(proxy.bindings, (qjs) => { + qjs.run(` + const fragmentRef = bindings.root() + const rootRef = bindings.get({ node: fragmentRef, index: 0 }) + const parentRef = bindings.get({ node: rootRef, index: 0 }) + bindings.append({ + parent: parentRef, + content: [{ kind: 'element', nodeName: 'child' }], + }) + `) + }) + + expect(fragment.toJSON()).toEqual('') + }) + }) + + describe('Complex operations can be expressed through proxy', async () => { + let doc: Y.Doc + let fragment: Y.XmlFragment + let proxy: YXmlProxyBindings + let root: Y.XmlElement + beforeEach(() => { + doc = new Y.Doc() + fragment = doc.getXmlFragment('artifact') + proxy = new YXmlProxyBindings(fragment) + root = new Y.XmlElement('root') + }) + + test('can edit a deeply nested fragment at multiple tree levels', () => { + fragment.insert(0, [createDeepTreeRoot()]) + + expect(fragment.toJSON()).toEqual( + '
AlphaIntroFirst bulletSecond bullet
Beta intro
Nested quote
', + ) + + const fragmentRef = proxy.root() + const rootRef = proxy.get({ node: fragmentRef, index: 0 }) + const alphaSection = proxy.get({ node: rootRef, index: 0 }) + const betaSection = proxy.get({ node: rootRef, index: 1 }) + const alphaList = proxy.get({ node: alphaSection, index: 2 }) + const secondListItem = proxy.get({ node: alphaList, index: 1 }) + const secondListParagraph = proxy.get({ node: secondListItem, index: 0 }) + const betaQuote = proxy.get({ node: betaSection, index: 1 }) + + proxy.insertBefore({ + ref: alphaSection, + content: [ + { + kind: 'element', + nodeName: 'heading', + attributes: { level: '1' }, + children: [{ kind: 'text', text: 'Document Title' }], + }, + ], + }) + expect(fragment.toJSON()).toEqual( + 'Document Title
AlphaIntroFirst bulletSecond bullet
Beta intro
Nested quote
', + ) + + proxy.insertAfter({ + ref: secondListParagraph, + content: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Inserted after second bullet paragraph' }], + }, + ], + }) + expect(proxy.toString({ node: secondListItem })).toEqual( + 'Second bulletInserted after second bullet paragraph', + ) + + proxy.append({ + parent: betaQuote, + content: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Quote follow-up' }], + }, + ], + }) + expect(proxy.toString({ node: betaQuote })).toEqual( + '
Nested quoteQuote follow-up
', + ) + + proxy.prepend({ + parent: betaSection, + content: [ + { + kind: 'element', + nodeName: 'heading', + attributes: { level: '3' }, + children: [{ kind: 'text', text: 'Beta heading' }], + }, + ], + }) + expect(proxy.toString({ node: betaSection })).toEqual( + '
Beta headingBeta intro
Nested quoteQuote follow-up
', + ) + + expect(fragment.toJSON()).toEqual( + 'Document Title
AlphaIntroFirst bulletSecond bulletInserted after second bullet paragraph
Beta headingBeta intro
Nested quoteQuote follow-up
', + ) + }) + }) +}) + +function createDeepTreeRoot(): Y.XmlElement { + const root = new Y.XmlElement('root') + + const alphaSection = new Y.XmlElement('section') + alphaSection.setAttribute('id', 'alpha') + alphaSection.insert(0, [ + createElement('heading', { level: '2' }, [createText('Alpha')]), + createElement('paragraph', {}, [createText('Intro')]), + createElement('bulletList', {}, [ + createElement('listItem', {}, [createElement('paragraph', {}, [createText('First bullet')])]), + createElement('listItem', {}, [createElement('paragraph', {}, [createText('Second bullet')])]), + ]), + ]) + + const betaSection = new Y.XmlElement('section') + betaSection.setAttribute('id', 'beta') + betaSection.insert(0, [ + createElement('paragraph', {}, [createText('Beta intro')]), + createElement('blockquote', {}, [createElement('paragraph', {}, [createText('Nested quote')])]), + ]) + + root.insert(0, [alphaSection, betaSection]) + return root +} + +function createElement( + name: string, + attributes: Record, + children: Array, +): Y.XmlElement { + const element = new Y.XmlElement(name) + for (const [key, value] of Object.entries(attributes)) { + element.setAttribute(key, value) + } + if (children.length > 0) element.insert(0, children) + return element +} + +function createText(value: string): Y.XmlText { + const text = new Y.XmlText() + text.insert(0, value) + return text +} diff --git a/packages/yjs-rich-markdown/test/yxml-proxy.test.ts b/packages/yjs-rich-markdown/test/yxml-proxy.test.ts index eb40119..dec0c54 100644 --- a/packages/yjs-rich-markdown/test/yxml-proxy.test.ts +++ b/packages/yjs-rich-markdown/test/yxml-proxy.test.ts @@ -8,6 +8,7 @@ import { YXmlNodeRefKindMismatchError, YXmlProxyBindings, YXmlRootOperationError, + YXmlTextRangeOutOfBoundsError, } from '../src' describe('YXml proxy', () => { @@ -45,6 +46,22 @@ describe('YXml proxy', () => { ) }) + test('exposes bound bindings for host environments like QuickJS', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + expect(proxy.bindings).not.toBe(proxy) + expect(proxy.bindings.root?.()).toEqual(proxy.root()) + + proxy.bindings.append?.({ + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Via bindings' }] }], + }) + + expect(proxy.bindings.toString?.()).toBe('Via bindings') + expect(proxy.toString()).toBe('Via bindings') + }) + test('inserts before and after refs without passing parent', () => { const doc = new Y.Doc() const fragment = doc.getXmlFragment('artifact') @@ -101,6 +118,303 @@ describe('YXml proxy', () => { expect(proxy.summary({ node: list }).nodeName).toBe('bulletList') }) + test('reads interleaved text and element children inside an element', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [example] = proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'example', + children: [ + { kind: 'text', text: 'this is an example ' }, + { kind: 'element', nodeName: 'nestedExample', children: [{ kind: 'text', text: 'this is a nested example' }] }, + { kind: 'text', text: ' This is another example' }, + ], + }, + ], + }) + if (!example) throw new Error('Expected example node') + + const children = proxy.children({ node: example }) + const [firstText, nestedExample, secondText] = children + if (!firstText || !nestedExample || !secondText) throw new Error('Expected interleaved children') + + expect(children.map((node) => proxy.summary({ node }).kind)).toEqual(['text', 'element', 'text']) + expect(proxy.text({ node: firstText })).toBe('this is an example ') + expect(proxy.summary({ node: nestedExample }).nodeName).toBe('nestedExample') + expect(proxy.text({ node: secondText })).toBe(' This is another example') + }) + + test('edits text inside an interleaved text node', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [example] = proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'example', + children: [ + { kind: 'text', text: 'this is an example ' }, + { kind: 'element', nodeName: 'nestedExample', children: [{ kind: 'text', text: 'this is a nested example' }] }, + { kind: 'text', text: ' This is another example' }, + ], + }, + ], + }) + if (!example) throw new Error('Expected example node') + const [firstText, nestedExample, secondText] = proxy.children({ node: example }) + if (!firstText || !nestedExample || !secondText) throw new Error('Expected interleaved children') + + proxy.insertText({ node: firstText, index: 0, text: 'Actually, ' }) + proxy.deleteText({ node: secondText, index: 0, length: 9 }) + + expect(proxy.text({ node: firstText })).toBe('Actually, this is an example ') + expect(proxy.text({ node: secondText })).toBe('another example') + expect(proxy.toString()).toBe( + 'Actually, this is an example this is a nested exampleanother example', + ) + }) + + test('splits a text node into interleaved siblings', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [example] = proxy.append({ + content: [ + { kind: 'element', nodeName: 'example', children: [{ kind: 'text', text: 'this is an example' }] }, + ], + }) + if (!example) throw new Error('Expected example node') + const [textNode] = proxy.children({ node: example }) + if (!textNode) throw new Error('Expected text node') + + const split = proxy.splitText({ node: textNode, index: 8 }) + + expect(proxy.text({ node: split.left })).toBe('this is ') + expect(proxy.text({ node: split.right })).toBe('an example') + expect(proxy.children({ node: example })).toEqual([split.left, split.right]) + }) + + test('wraps only part of a text node while preserving interleaved siblings', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [example] = proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'example', + children: [ + { kind: 'text', text: 'this is an example ' }, + { kind: 'element', nodeName: 'nestedExample', children: [{ kind: 'text', text: 'this is a nested example' }] }, + { kind: 'text', text: ' This is another example' }, + ], + }, + ], + }) + if (!example) throw new Error('Expected example node') + const [firstText, nestedExample, secondText] = proxy.children({ node: example }) + if (!firstText || !nestedExample || !secondText) throw new Error('Expected interleaved children') + + const wrapped = proxy.wrapTextRange({ node: firstText, start: 11, end: 18, wrapper: { nodeName: 'bold' } }) + + const children = proxy.children({ node: example }) + const [beforeText, boldNode, afterText, sameNestedExample, sameSecondText] = children + if (!beforeText || !boldNode || !afterText || !sameNestedExample || !sameSecondText) throw new Error('Expected wrapped children') + + expect(proxy.text({ node: beforeText })).toBe('this is an ') + expect(proxy.toString({ node: boldNode })).toBe('example') + expect(proxy.text({ node: afterText })).toBe(' ') + expect(sameNestedExample).toEqual(nestedExample) + expect(sameSecondText).toEqual(secondText) + expect(wrapped.wrapper).toEqual(boldNode) + expect(proxy.toString()).toBe( + 'this is an example this is a nested example This is another example', + ) + }) + + test('wraps part of text inside an already marked inline element', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [paragraph] = proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [ + { kind: 'text', text: 'prefix ' }, + { kind: 'element', nodeName: 'italic', children: [{ kind: 'text', text: 'nested example text' }] }, + { kind: 'text', text: ' suffix' }, + ], + }, + ], + }) + if (!paragraph) throw new Error('Expected paragraph node') + + const [, italic, suffix] = proxy.children({ node: paragraph }) + if (!italic || !suffix) throw new Error('Expected italic and suffix nodes') + const [italicText] = proxy.children({ node: italic }) + if (!italicText) throw new Error('Expected italic text node') + + const wrapped = proxy.wrapTextRange({ + node: italicText, + start: 7, + end: 14, + wrapper: { nodeName: 'bold' }, + }) + + const italicChildren = proxy.children({ node: italic }) + const [beforeText, boldNode, afterText] = italicChildren + if (!beforeText || !boldNode || !afterText) throw new Error('Expected split italic children') + + expect(proxy.text({ node: beforeText })).toBe('nested ') + expect(proxy.toString({ node: boldNode })).toBe('example') + expect(proxy.text({ node: afterText })).toBe(' text') + expect(wrapped.wrapper).toEqual(boldNode) + expect(proxy.toString({ node: italic })).toBe('nested example text') + expect(proxy.text({ node: suffix })).toBe(' suffix') + expect(proxy.toString()).toBe( + 'prefix nested example text suffix', + ) + }) + + test('can combine splitText and wrapTextRange inside an existing inline mark', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [paragraph] = proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [ + { kind: 'element', nodeName: 'italic', children: [{ kind: 'text', text: 'alpha beta gamma' }] }, + ], + }, + ], + }) + if (!paragraph) throw new Error('Expected paragraph node') + + const [italic] = proxy.children({ node: paragraph }) + if (!italic) throw new Error('Expected italic node') + const [italicText] = proxy.children({ node: italic }) + if (!italicText) throw new Error('Expected italic text node') + + const firstSplit = proxy.splitText({ node: italicText, index: 6 }) + expect(proxy.text({ node: firstSplit.left })).toBe('alpha ') + expect(proxy.text({ node: firstSplit.right })).toBe('beta gamma') + + const secondSplit = proxy.splitText({ node: firstSplit.right, index: 4 }) + expect(proxy.text({ node: secondSplit.left })).toBe('beta') + expect(proxy.text({ node: secondSplit.right })).toBe(' gamma') + + proxy.wrap({ node: secondSplit.left, wrapper: { nodeName: 'bold' } }) + + expect(proxy.toString({ node: italic })).toBe('alpha beta gamma') + expect(proxy.toString()).toBe('alpha beta gamma') + }) + + test('can edit adjacent to a new bold node inside an existing inline mark', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [paragraph] = proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [ + { kind: 'element', nodeName: 'italic', children: [{ kind: 'text', text: 'alpha beta gamma' }] }, + ], + }, + ], + }) + if (!paragraph) throw new Error('Expected paragraph node') + + const [italic] = proxy.children({ node: paragraph }) + if (!italic) throw new Error('Expected italic node') + const [italicText] = proxy.children({ node: italic }) + if (!italicText) throw new Error('Expected italic text node') + + const wrapped = proxy.wrapTextRange({ + node: italicText, + start: 6, + end: 10, + wrapper: { nodeName: 'bold' }, + }) + + proxy.insertBefore({ + ref: wrapped.wrapper, + content: [{ kind: 'text', text: '[' }], + }) + proxy.insertAfter({ + ref: wrapped.wrapper, + content: [{ kind: 'text', text: ']' }], + }) + + expect(proxy.toString({ node: italic })).toBe('alpha [beta] gamma') + expect(proxy.toString()).toBe('alpha [beta] gamma') + }) + + test('can nest bold inside italic and then add another mark beside it', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + + const [paragraph] = proxy.append({ + content: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [ + { kind: 'element', nodeName: 'italic', children: [{ kind: 'text', text: 'alpha beta gamma' }] }, + ], + }, + ], + }) + if (!paragraph) throw new Error('Expected paragraph node') + + const [italic] = proxy.children({ node: paragraph }) + if (!italic) throw new Error('Expected italic node') + const [italicText] = proxy.children({ node: italic }) + if (!italicText) throw new Error('Expected italic text node') + + proxy.wrapTextRange({ + node: italicText, + start: 6, + end: 10, + wrapper: { nodeName: 'bold' }, + }) + + const italicChildren = proxy.children({ node: italic }) + const [, boldNode, afterText] = italicChildren + if (!boldNode || !afterText) throw new Error('Expected bold node and trailing text') + + const italicInserted = proxy.insertAfter({ + ref: boldNode, + content: [{ kind: 'element', nodeName: 'italic', children: [{ kind: 'text', text: '!' }] }], + }) + const extraItalic = italicInserted[0] + if (!extraItalic) throw new Error('Expected extra italic node') + + proxy.insertText({ node: afterText, index: 0, text: ' ' }) + + expect(proxy.toString({ node: extraItalic })).toBe('!') + expect(proxy.toString({ node: italic })).toBe('alpha beta! gamma') + expect(proxy.toString()).toBe('alpha beta! gamma') + }) + test('sets and removes attributes on element refs', () => { const doc = new Y.Doc() const fragment = doc.getXmlFragment('artifact') @@ -322,6 +636,25 @@ describe('YXml proxy', () => { ) }) + test('throws for invalid text ranges', () => { + const doc = new Y.Doc() + const fragment = doc.getXmlFragment('artifact') + const proxy = new YXmlProxyBindings(fragment) + const [paragraph] = proxy.append({ + content: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Text' }] }], + }) + if (!paragraph) throw new Error('Expected paragraph node') + const [text] = proxy.children({ node: paragraph }) + if (!text) throw new Error('Expected text node') + + expect(() => proxy.insertText({ node: text, index: 10, text: '!' })).toThrow(YXmlTextRangeOutOfBoundsError) + expect(() => proxy.deleteText({ node: text, index: 1, length: 10 })).toThrow(YXmlTextRangeOutOfBoundsError) + expect(() => proxy.splitText({ node: text, index: 0 })).toThrow(YXmlTextRangeOutOfBoundsError) + expect(() => proxy.wrapTextRange({ node: text, start: 2, end: 2, wrapper: { nodeName: 'bold' } })).toThrow( + YXmlTextRangeOutOfBoundsError, + ) + }) + test('throws when removing or inserting relative to the root fragment', () => { const doc = new Y.Doc() const fragment = doc.getXmlFragment('artifact') From 18b126bce66db7d5cf926bbadca5cd52baef4631 Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Mon, 4 May 2026 20:57:37 -0700 Subject: [PATCH 13/18] feat: tools --- bun.lock | 1 + .../quickjs-exec/test/quickjs-exec.test.ts | 61 +++++++++++ packages/yjs-rich-markdown/package.json | 5 +- .../yjs-rich-markdown/src/agents/index.ts | 1 + .../src/agents/yxml-fragment-editor.ts | 103 ++++++++++++++++++ .../src/tools/codemode-yxml-editor.ts | 65 +++++++++++ .../src/tools/edit-rich-markdown.ts | 24 ++++ packages/yjs-rich-markdown/src/tools/index.ts | 2 + .../src/yxml-proxy-prompt.ts | 3 +- .../test/quickjs-code-mode-tool.test.ts | 56 ++++++++++ 10 files changed, 318 insertions(+), 3 deletions(-) create mode 100644 packages/yjs-rich-markdown/src/agents/index.ts create mode 100644 packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts create mode 100644 packages/yjs-rich-markdown/src/tools/codemode-yxml-editor.ts create mode 100644 packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts create mode 100644 packages/yjs-rich-markdown/src/tools/index.ts create mode 100644 packages/yjs-rich-markdown/test/quickjs-code-mode-tool.test.ts diff --git a/bun.lock b/bun.lock index ea23661..274b054 100644 --- a/bun.lock +++ b/bun.lock @@ -312,6 +312,7 @@ "@tiptap/starter-kit": "3.22.5", "dedent": "1.7.2", "y-prosemirror": "1.3.7", + "zod": "catalog:", }, "devDependencies": { "@durable-streams/server": "^0.3.1", diff --git a/packages/quickjs-exec/test/quickjs-exec.test.ts b/packages/quickjs-exec/test/quickjs-exec.test.ts index 6a9ba3f..c7688ad 100644 --- a/packages/quickjs-exec/test/quickjs-exec.test.ts +++ b/packages/quickjs-exec/test/quickjs-exec.test.ts @@ -176,4 +176,65 @@ describe('quickjs-exec', () => { expect((error as Error).message).toContain('index 0') } }) + + test('prints QuickJsExecutionError field shapes for learning', async () => { + const cases = [ + { + label: 'syntax error', + code: ` + const ok = 1 + const broken = { node: bindings.root(), index 0 } + broken + `, + }, + { + label: 'runtime reference error', + code: ` + const ok = 1 + missingReference.property + `, + }, + ] + + const examples: unknown[] = [] + + for (const example of cases) { + try { + await withQuickJsMode({}, ({ run }) => run(example.code)) + } catch (error) { + expect(error).toBeInstanceOf(QuickJsExecutionError) + + const quickJsError = error as QuickJsExecutionError & { cause?: unknown } + const cause = quickJsError.cause + const causeRecord = cause && typeof cause === 'object' ? (cause as Record) : undefined + const ownProperties = Object.fromEntries( + Object.getOwnPropertyNames(quickJsError).map((name) => [ + name, + (quickJsError as unknown as Record)[name], + ]), + ) + + examples.push({ + label: example.label, + name: quickJsError.name, + message: quickJsError.message, + details: quickJsError.details, + cause: causeRecord + ? { + name: causeRecord.name, + message: causeRecord.message, + fileName: causeRecord.fileName, + lineNumber: causeRecord.lineNumber, + columnNumber: causeRecord.columnNumber, + } + : cause, + ownProperties, + stack: quickJsError.stack, + }) + } + } + + expect(examples).toHaveLength(cases.length) + console.log('QuickJsExecutionError examples:', JSON.stringify(examples, null, 2)) + }) }) diff --git a/packages/yjs-rich-markdown/package.json b/packages/yjs-rich-markdown/package.json index f76f314..22f0177 100644 --- a/packages/yjs-rich-markdown/package.json +++ b/packages/yjs-rich-markdown/package.json @@ -43,14 +43,15 @@ "yjs": "^13.6.30" }, "dependencies": { + "@humanlayer/quickjs-exec": "workspace:*", "@tiptap/core": "3.22.5", "@tiptap/extension-collaboration": "3.22.5", "@tiptap/markdown": "3.22.5", "@tiptap/pm": "3.22.5", "@tiptap/starter-kit": "3.22.5", - "@humanlayer/quickjs-exec": "workspace:*", "dedent": "1.7.2", - "y-prosemirror": "1.3.7" + "y-prosemirror": "1.3.7", + "zod": "catalog:" }, "author": { "name": "Kyle Mistele", diff --git a/packages/yjs-rich-markdown/src/agents/index.ts b/packages/yjs-rich-markdown/src/agents/index.ts new file mode 100644 index 0000000..bedd385 --- /dev/null +++ b/packages/yjs-rich-markdown/src/agents/index.ts @@ -0,0 +1 @@ +export * from './yxml-fragment-editor' diff --git a/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts b/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts new file mode 100644 index 0000000..1f40f8a --- /dev/null +++ b/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts @@ -0,0 +1,103 @@ +import { Agent, defineTool, doomLoop, maxSteps, toolCalled } from '@humanlayer/agentlayer-core' +import type { JSONContent } from '@tiptap/core' +import dedent from 'dedent' +import { yXmlFragmentToProsemirrorJSON } from 'y-prosemirror' +import type * as Y from 'yjs' +import z from 'zod/v4' +import { tiptapJsonToMarkdown } from '../markdown' +import { createCodeModeYXmlEditorTool } from '../tools/codemode-yxml-editor' +import { YXML_PROXY_AGENT_PROMPT } from '../yxml-proxy-prompt' + +const doneTool = defineTool({ + name: 'done', + description: 'Call this tool when you complete your edits', + input: z.object({}), + execute: async () => { + console.log('Done tool called') + }, +}) + +const stuckTool = defineTool({ + name: 'i_am_stuck', + description: 'Call this tool if you are stuck and unable to complete your edits', + input: z.object({ + message: z.string().describe('Indicate why you are stuck and unable to complete the requested edit'), + }), + execute: async ({ message }) => { + console.error('Agent declared it is stuck:', message) + }, +}) + +export const createYXmlFragmentEditorAgent = (agentConfig: { + modelConfig: ConstructorParameters[0]['model'] + fragment: Y.XmlFragment + oldString: string + newString: string + replaceAll?: boolean +}) => { + const { modelConfig, fragment, oldString, newString, replaceAll } = agentConfig + + const intructions = editorPrompt(fragment, oldString, newString, !!replaceAll) + + console.log('Editor Propmt:\n\n', editorPrompt) + return new Agent({ + system: [YXML_PROXY_AGENT_PROMPT, intructions], + model: modelConfig, + tools: { + edit_y_xml_fragment: createCodeModeYXmlEditorTool({ fragment }), + done: doneTool, + i_am_stuck: stuckTool, + }, + stopWhen: [ + doomLoop(), + maxSteps(30), // probably too many but we will see. + toolCalled(doneTool.name), + toolCalled(stuckTool.name), + ], + }) +} + +const editorPrompt = (fragment: Y.XmlFragment, oldString: string, newString: string, replaceAll: boolean) => dedent` + + The document you are editing is a live Y.XMLFragment. Other users may be editing it in live time. + The information you see below is a point-in-time snapshot that may be out of date after 1+ edits. + **IMPORTANT**: If you are unable to complete an edit because the document has changed, use the bindings API to inspect the document. + + This is how the document is currently represented in XML: + + ${fragment.toJSON()} + + + The document is rendered to the following markdown format, which is what the user can see. The user cannot see the raw Y.XMLFragment or the derived TipTap JSON. + The markdown is NOT the raw form of the document. + The TipTap document, which is in TipTap's JSON protocol but translated to Y.XMLFragment for collaboration, is the canonical source of the document's state. + This is the document: + + + ${tiptapJsonToMarkdown(yXmlFragmentToProsemirrorJSON(fragment) as JSONContent)} + + + The user has requested the following edit to the document, based on the markdown that the user is able to see: + + Replace the Following old string with the new string. + + ${oldString} + ${newString} + + ${replaceAll ?? 'Make sure to replace ALL occurrences of old string with new string'} + + + + Your job is to interpret this edit instruction in the context of the markdown which the user can see and has requested the edit, and to use the DSL and code execution tool to apply the edit to the Y XML Fragment. + + + Important system reminder: + 1. Use your tools to inspect and edit the document, to indicate when you are finished, or to indicate that you are stuck + 2. Your job is NOT to insert markdown into the XML fragment - rather, it is to use the fragment and the DSL to add the XML Elements, attributes, and text to apply the markdown-requested edit to the document. + 3. You should NOT include literal / XML tags in the document. + 4. The document may change over time, the bindings API will allow you to inspect it as needed. + 5. The console API in the edit tool will help you to inspect as needed. + 6. If an edit attempt fails, think about how you can apply the edit in a different way. Do not give up easily. + + +` diff --git a/packages/yjs-rich-markdown/src/tools/codemode-yxml-editor.ts b/packages/yjs-rich-markdown/src/tools/codemode-yxml-editor.ts new file mode 100644 index 0000000..eb9c686 --- /dev/null +++ b/packages/yjs-rich-markdown/src/tools/codemode-yxml-editor.ts @@ -0,0 +1,65 @@ +import { defineTool } from '@humanlayer/agentlayer-core' +import { + type QuickJsRunWithConsoleResult, + type QuickJsSerializedError, + withQuickJsMode, +} from '@humanlayer/quickjs-exec' +import dedent from 'dedent' +import type * as Y from 'yjs' +import z from 'zod/v4' +import { YXmlProxyBindings } from '../yxml-proxy' + +/** + * Given a Y.XMLFragment create a tool that allows an agent to edit it in a sandboxed QuickJS runtime using code mode and a proxy bindings object to wrap the Y doc + * @param options + * @returns + */ +export const createCodeModeYXmlEditorTool = (options: { fragment: Y.XmlFragment }) => { + return defineTool({ + name: 'edit_yjs_xml_fragment', + description: 'Edit Y.js XML Fragments using a JavaScript sandbox with binding APIs', + input: z.object({ + code: z + .string() + .describe( + `'Javascript code to edit the Y.js XML Fragment using the 'bindings' API for mutation and 'console' API for debugging. Only allows pure JavaScript and bindings and console. No Node.js or browser APIs are allowed, and no packages are provided.`, + ), + }), + execute: async ({ code }, ctx) => { + try { + const proxy = new YXmlProxyBindings(options.fragment) + await withQuickJsMode(proxy.bindings, (qjs) => { + const sandboxedExecutionResult = qjs.runWithConsole(code) + return formatQuickJsResult(sandboxedExecutionResult) + }) + } catch (error: any) { + console.error('Error with quickJS Execution against proxy:', error) + throw error + } + }, + }) +} + +function formatQuickJsResult(result: QuickJsRunWithConsoleResult) { + return dedent` + Executing code... + + Log: + + ${result.console.map((consoleLog) => `[${consoleLog.level}] ${JSON.stringify(consoleLog.args)}`).join('\n')} + + + ${result.ok ? `Execution succeeded. Result:\n${JSON.stringify(result.value)}` : `\n\nExecution failed:\n${formatQuickJsError(result.error)}`} + ` +} + +function formatQuickJsError(error: QuickJsSerializedError) { + return dedent` + + ${error.name} + ${error.message} + ${error.lineNumber} + ${error.columnNumber} + + ` +} diff --git a/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts b/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts new file mode 100644 index 0000000..8e79928 --- /dev/null +++ b/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts @@ -0,0 +1,24 @@ +import { EDIT_DESCRIPTION } from '@humanlayer/agentlayer-core' +import { EditTool } from '@humanlayer/agentlayer-core/interfaces' +import type * as Y from 'yjs' +import type { RichMarkdownArtifactStore } from '../artifact-store' +/** + * Edit tool for editing markdown. Uses the EditTool interface but deletes to code-mode sub-agent for edits + * @param artifactStore + * @returns + */ +export function createEditRichMarkdownTool(artifactStore: RichMarkdownArtifactStore) { + return EditTool.define( + async (input, ctx) => { + let matchCount: 0 + const artifact = artifactStore.getArtifact(input.file_path) + + const fragment: Y.XmlFragment = artifactStore.getFragment(input.file_path) + + return { content: '', matchCount: 0 } + }, + { + description: EDIT_DESCRIPTION, + }, + ) +} diff --git a/packages/yjs-rich-markdown/src/tools/index.ts b/packages/yjs-rich-markdown/src/tools/index.ts new file mode 100644 index 0000000..f349ffe --- /dev/null +++ b/packages/yjs-rich-markdown/src/tools/index.ts @@ -0,0 +1,2 @@ +export * from './codemode-yxml-editor' +export * from './edit-rich-markdown' diff --git a/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts b/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts index be74b73..401778b 100644 --- a/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts +++ b/packages/yjs-rich-markdown/src/yxml-proxy-prompt.ts @@ -4,7 +4,8 @@ export const YXML_PROXY_AGENT_PROMPT = dedent` You are editing one rich markdown artifact through a scoped Y.XmlFragment DSL. - The artifact is a rich-text TipTap/ProseMirror markdown document, modeled in Tiptap/ProseMirror's XML DSL for markdown. + The artifact is a rich-text TipTap/ProseMirror document, modeled in Tiptap/ProseMirror's JSON DSL but modeled as a Y.JS XML Fragment for editing. + You have a custom DSL to inspect, manipuate, and edit the document. The DSL is available with a \`bindings\` object. Use only these bindings to inspect and mutate the document. Do not import packages, access files, access the network, or assume direct access to Yjs objects. Attempts to do so will fail. diff --git a/packages/yjs-rich-markdown/test/quickjs-code-mode-tool.test.ts b/packages/yjs-rich-markdown/test/quickjs-code-mode-tool.test.ts new file mode 100644 index 0000000..84844a8 --- /dev/null +++ b/packages/yjs-rich-markdown/test/quickjs-code-mode-tool.test.ts @@ -0,0 +1,56 @@ +import { beforeEach, describe, expect, test } from 'bun:test' +import type { ToolContext } from '@humanlayer/agentlayer-core' +import dedent from 'dedent' +import * as Y from 'yjs' +import { createCodeModeYXmlEditorTool } from '../src/tools' + +describe('QuickJS Code Mode Tool Tests', async () => { + let doc: Y.Doc + let fragment: Y.XmlFragment + let root: Y.XmlElement + + beforeEach(() => { + doc = new Y.Doc() + fragment = doc.getXmlFragment('fragment') + root = new Y.XmlElement('root') + fragment.insert(0, [root]) + expect(fragment.toJSON()).toEqual('') + }) + test('Edits made through the tool should pass', async () => { + const tool = createCodeModeYXmlEditorTool({ fragment }) + + const toolExecution = tool.execute( + { + code: dedent` + const fragmentRef = bindings.root() + const rootRef = bindings.get({ node: fragmentRef, index: 0 }) + bindings.append({ + parent: rootRef, + content: [{ kind: 'element', nodeName: 'parent' }], + })`, + }, + {} as ToolContext, + ) + + await expect(toolExecution).resolves.toBeUndefined() + expect(fragment.toJSON()).toEqual('') + + const secondToolExecution = tool.execute( + { + code: dedent` + const fragmentRef = bindings.root() + const rootRef = bindings.get({ node: fragmentRef, index: 0 }) + const parentRef = bindings.get({ node: rootRef, index: 0 }) + bindings.append({ + parent: parentRef, + content: [{ kind: 'element', nodeName: 'child' }], + }) + `, + }, + {} as ToolContext, + ) + + await expect(secondToolExecution).resolves.toBeUndefined() + expect(fragment.toJSON()).toEqual('') + }) +}) From 12da3dc43155457411b1b175cae145421878f9b4 Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Tue, 5 May 2026 17:37:28 -0700 Subject: [PATCH 14/18] fix yxml editor agent tool results --- bun.lock | 1 + packages/yjs-rich-markdown/package.json | 3 +- .../src/agents/yxml-fragment-editor.ts | 58 ++++++++++----- .../src/tools/codemode-yxml-editor.ts | 2 +- .../test/apply-agent.test.ts | 74 +++++++++++++++++++ 5 files changed, 116 insertions(+), 22 deletions(-) create mode 100644 packages/yjs-rich-markdown/test/apply-agent.test.ts diff --git a/bun.lock b/bun.lock index 274b054..e05b046 100644 --- a/bun.lock +++ b/bun.lock @@ -315,6 +315,7 @@ "zod": "catalog:", }, "devDependencies": { + "@ai-sdk/anthropic": "catalog:", "@durable-streams/server": "^0.3.1", "@durable-streams/y-durable-streams": "^0.2.4", "@types/bun": "latest", diff --git a/packages/yjs-rich-markdown/package.json b/packages/yjs-rich-markdown/package.json index 22f0177..1a663cb 100644 --- a/packages/yjs-rich-markdown/package.json +++ b/packages/yjs-rich-markdown/package.json @@ -32,7 +32,8 @@ "devDependencies": { "@durable-streams/server": "^0.3.1", "@durable-streams/y-durable-streams": "^0.2.4", - "@types/bun": "latest" + "@types/bun": "latest", + "@ai-sdk/anthropic": "catalog:" }, "peerDependencies": { "@tiptap/core": "3.22.5", diff --git a/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts b/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts index 1f40f8a..325616c 100644 --- a/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts +++ b/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts @@ -1,4 +1,12 @@ -import { Agent, defineTool, doomLoop, maxSteps, toolCalled } from '@humanlayer/agentlayer-core' +import { + Agent, + defineTool, + doomLoop, + maxSteps, + type PostToolUseHook, + type PreToolUseHook, + toolCalled, +} from '@humanlayer/agentlayer-core' import type { JSONContent } from '@tiptap/core' import dedent from 'dedent' import { yXmlFragmentToProsemirrorJSON } from 'y-prosemirror' @@ -31,20 +39,19 @@ const stuckTool = defineTool({ export const createYXmlFragmentEditorAgent = (agentConfig: { modelConfig: ConstructorParameters[0]['model'] fragment: Y.XmlFragment - oldString: string - newString: string - replaceAll?: boolean + preToolHooks?: Array + postToolHooks?: Array }) => { - const { modelConfig, fragment, oldString, newString, replaceAll } = agentConfig + const { modelConfig, fragment, preToolHooks, postToolHooks } = agentConfig - const intructions = editorPrompt(fragment, oldString, newString, !!replaceAll) + const intructions = editorPrompt(fragment) - console.log('Editor Propmt:\n\n', editorPrompt) + console.log('Editor Prompt:\n\n', intructions) return new Agent({ system: [YXML_PROXY_AGENT_PROMPT, intructions], model: modelConfig, tools: { - edit_y_xml_fragment: createCodeModeYXmlEditorTool({ fragment }), + edit_yjs_xml_fragment: createCodeModeYXmlEditorTool({ fragment }), done: doneTool, i_am_stuck: stuckTool, }, @@ -54,16 +61,20 @@ export const createYXmlFragmentEditorAgent = (agentConfig: { toolCalled(doneTool.name), toolCalled(stuckTool.name), ], + hooks: { + preToolUse: preToolHooks ?? [], + postToolUse: postToolHooks ?? [], + }, }) } -const editorPrompt = (fragment: Y.XmlFragment, oldString: string, newString: string, replaceAll: boolean) => dedent` +const editorPrompt = (fragment: Y.XmlFragment) => dedent` The document you are editing is a live Y.XMLFragment. Other users may be editing it in live time. The information you see below is a point-in-time snapshot that may be out of date after 1+ edits. **IMPORTANT**: If you are unable to complete an edit because the document has changed, use the bindings API to inspect the document. - This is how the document is currently represented in XML: + This is how the document is currently represented in XML (IMPORTANT: the DOCUMENT_XML tags are NOT part of the document's XML structure, they are delimiters in this prompt.): ${fragment.toJSON()} @@ -77,16 +88,7 @@ const editorPrompt = (fragment: Y.XmlFragment, oldString: string, newString: str ${tiptapJsonToMarkdown(yXmlFragmentToProsemirrorJSON(fragment) as JSONContent)} - The user has requested the following edit to the document, based on the markdown that the user is able to see: - - Replace the Following old string with the new string. - - ${oldString} - ${newString} - - ${replaceAll ?? 'Make sure to replace ALL occurrences of old string with new string'} - - + The user has requested the following edit to the document wrapped in XML tags - replace the contents with the contents., based on the markdown that the user is able to see. Your job is to interpret this edit instruction in the context of the markdown which the user can see and has requested the edit, and to use the DSL and code execution tool to apply the edit to the Y XML Fragment. @@ -101,3 +103,19 @@ const editorPrompt = (fragment: Y.XmlFragment, oldString: string, newString: str ` + +export const userInstructionForYXmlFragmentEditorAgent = (options: { + oldString: string + newString: string + replaceAll?: boolean +}) => dedent` + + Replace the Following old string with the new string. + + ${options.oldString} + ${options.newString} + + ${options.replaceAll ?? 'Make sure to replace ALL occurrences of old string with new string'} + + + ` diff --git a/packages/yjs-rich-markdown/src/tools/codemode-yxml-editor.ts b/packages/yjs-rich-markdown/src/tools/codemode-yxml-editor.ts index eb9c686..ce93dfe 100644 --- a/packages/yjs-rich-markdown/src/tools/codemode-yxml-editor.ts +++ b/packages/yjs-rich-markdown/src/tools/codemode-yxml-editor.ts @@ -28,7 +28,7 @@ export const createCodeModeYXmlEditorTool = (options: { fragment: Y.XmlFragment execute: async ({ code }, ctx) => { try { const proxy = new YXmlProxyBindings(options.fragment) - await withQuickJsMode(proxy.bindings, (qjs) => { + return await withQuickJsMode(proxy.bindings, (qjs) => { const sandboxedExecutionResult = qjs.runWithConsole(code) return formatQuickJsResult(sandboxedExecutionResult) }) diff --git a/packages/yjs-rich-markdown/test/apply-agent.test.ts b/packages/yjs-rich-markdown/test/apply-agent.test.ts new file mode 100644 index 0000000..aca799e --- /dev/null +++ b/packages/yjs-rich-markdown/test/apply-agent.test.ts @@ -0,0 +1,74 @@ +import { beforeEach, describe, expect, setDefaultTimeout, test } from 'bun:test' +import { anthropic } from '@ai-sdk/anthropic' +import { + type Agent, + type PostToolUseHook, + type PreToolUseHook, + startState, + userMessage, +} from '@humanlayer/agentlayer-core' +import { yXmlFragmentToProsemirrorJSON } from 'y-prosemirror' +import * as Y from 'yjs' +import { tiptapJsonToMarkdown } from '../src' +import { createYXmlFragmentEditorAgent, userInstructionForYXmlFragmentEditorAgent } from '../src/agents' + +setDefaultTimeout(20_000) + +describe.skipIf(!process.env.ANTHROPIC_API_KEY || !!process.env.CI)('Live Apply Agent Tests', async () => { + let doc: Y.Doc + let fragment: Y.XmlFragment + let createApplyAgent: (oldString: string, newString: string, replaceAll?: boolean) => Agent + + beforeEach(() => { + doc = new Y.Doc() + fragment = doc.getXmlFragment('fragment') + + const inputLoggerHook: PreToolUseHook = (ctx) => { + console.log(`Pre-tool-use hook for ${ctx.toolName} with input: ${JSON.stringify(ctx.input)}`) + return ctx.next() + } + + const outputLoggerHook: PostToolUseHook = (ctx) => { + console.log(`Post-tool-use hook for ${ctx.toolName} with output ${JSON.stringify(ctx.output)}`) + return ctx.done() + } + + createApplyAgent = (oldString, newString, replaceAll) => { + return createYXmlFragmentEditorAgent({ + modelConfig: anthropic('claude-sonnet-4-6'), + fragment, + preToolHooks: [inputLoggerHook], + postToolHooks: [outputLoggerHook], + }) + } + }) + + test('Simple apply should succeed', async () => { + const heading = new Y.XmlElement('heading') + fragment.insert(0, [heading]) + + heading.setAttribute('level', '1') + heading.push([new Y.XmlText('This is a second-level heading')]) + + const oldString = '#' + const newString = '##' + expect(tiptapJsonToMarkdown(yXmlFragmentToProsemirrorJSON(fragment))).toContain('#') + expect(tiptapJsonToMarkdown(yXmlFragmentToProsemirrorJSON(fragment))).not.toContain('##') + expect(fragment.toJSON()).toEqual('This is a second-level heading') + + const applyAgent = createApplyAgent(oldString, newString, true) + + const userInstruction = userInstructionForYXmlFragmentEditorAgent({ oldString, newString }) + console.log(`user instruction:`, userInstruction) + + const run = applyAgent.run({ + state: startState([userMessage(userInstruction)]), + }) + + await expect(run.result).resolves.not.toBeUndefined() + const runResult = await run.result + + expect(tiptapJsonToMarkdown(yXmlFragmentToProsemirrorJSON(fragment))).toContain('##') + expect(fragment.toJSON()).toEqual('This is a second-level heading') + }) +}) From a79a44f29a0289c57c05c5f8912fcdbeeb49de45 Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Tue, 5 May 2026 18:25:19 -0700 Subject: [PATCH 15/18] test complex yxml apply agent edits --- .../test/apply-agent.test.ts | 203 +++++++++++++++++- 1 file changed, 202 insertions(+), 1 deletion(-) diff --git a/packages/yjs-rich-markdown/test/apply-agent.test.ts b/packages/yjs-rich-markdown/test/apply-agent.test.ts index aca799e..b281b8d 100644 --- a/packages/yjs-rich-markdown/test/apply-agent.test.ts +++ b/packages/yjs-rich-markdown/test/apply-agent.test.ts @@ -12,18 +12,21 @@ import * as Y from 'yjs' import { tiptapJsonToMarkdown } from '../src' import { createYXmlFragmentEditorAgent, userInstructionForYXmlFragmentEditorAgent } from '../src/agents' -setDefaultTimeout(20_000) +setDefaultTimeout(200_000) describe.skipIf(!process.env.ANTHROPIC_API_KEY || !!process.env.CI)('Live Apply Agent Tests', async () => { let doc: Y.Doc let fragment: Y.XmlFragment let createApplyAgent: (oldString: string, newString: string, replaceAll?: boolean) => Agent + let toolCalls: string[] beforeEach(() => { doc = new Y.Doc() fragment = doc.getXmlFragment('fragment') + toolCalls = [] const inputLoggerHook: PreToolUseHook = (ctx) => { + toolCalls.push(ctx.toolName) console.log(`Pre-tool-use hook for ${ctx.toolName} with input: ${JSON.stringify(ctx.input)}`) return ctx.next() } @@ -67,8 +70,206 @@ describe.skipIf(!process.env.ANTHROPIC_API_KEY || !!process.env.CI)('Live Apply await expect(run.result).resolves.not.toBeUndefined() const runResult = await run.result + logToolCallCount('simple apply', toolCalls) expect(tiptapJsonToMarkdown(yXmlFragmentToProsemirrorJSON(fragment))).toContain('##') expect(fragment.toJSON()).toEqual('This is a second-level heading') + expect(toolCalls.length).toBeGreaterThan(0) }) }) + +describe.skipIf(!process.env.ANTHROPIC_API_KEY || !!process.env.CI)( + 'Live Apply Agent Tests - complex nested document', + async () => { + let doc: Y.Doc + let fragment: Y.XmlFragment + let createApplyAgent: (oldString: string, newString: string, replaceAll?: boolean) => Agent + let toolCalls: string[] + + beforeEach(() => { + doc = new Y.Doc() + fragment = doc.getXmlFragment('fragment') + fragment.insert(0, createComplexDocumentNodes()) + toolCalls = [] + + const inputLoggerHook: PreToolUseHook = (ctx) => { + toolCalls.push(ctx.toolName) + console.log(`Pre-tool-use hook for ${ctx.toolName} with input: ${JSON.stringify(ctx.input)}`) + return ctx.next() + } + + const outputLoggerHook: PostToolUseHook = (ctx) => { + console.log(`Post-tool-use hook for ${ctx.toolName} with output ${JSON.stringify(ctx.output)}`) + return ctx.done() + } + + createApplyAgent = () => { + return createYXmlFragmentEditorAgent({ + modelConfig: anthropic('claude-sonnet-4-6'), + fragment, + preToolHooks: [inputLoggerHook], + postToolHooks: [outputLoggerHook], + }) + } + }) + + test('removes text inside inline code nested in a bulleted list item', async () => { + expect(fragment.toJSON()).toContain('this is a code block in python in a bulleted list') + + await applyEdit({ + agent: createApplyAgent('in python', '', true), + oldString: 'in python', + newString: '', + replaceAll: true, + }) + logToolCallCount('removing nested inline code text', toolCalls) + + const codeContents = codeTagContents(fragment.toJSON()) + expect(codeContents).toContain('this is a code block') + expect(codeContents).not.toContain('this is a code block in python') + expect(codeContents.some((content) => content.includes('in python'))).toBe(false) + expect(toolCalls.length).toBeGreaterThan(0) + }) + + test('edits text inside a nested ordered list item', async () => { + expect(fragment.toJSON()).toContain('numbered ') + + await applyEdit({ + agent: createApplyAgent('numbered', 'ordered'), + oldString: 'numbered', + newString: 'ordered', + }) + logToolCallCount('editing ordered list item', toolCalls) + + expect(fragment.toJSON()).toContain('ordered ') + expect(fragment.toJSON()).not.toContain('numbered') + expect(toolCalls.length).toBeGreaterThan(0) + }) + + test('edits text inside an inline bold mark in a paragraph', async () => { + expect(fragment.toJSON()).toContain( + 'This is a bold and italic item in a paragraph', + ) + + await applyEdit({ + agent: createApplyAgent('is a bold', 'is bold'), + oldString: 'is a bold', + newString: 'is bold', + }) + logToolCallCount('editing inline bold text', toolCalls) + + expect(fragment.toJSON()).toContain( + 'This is bold and italic item in a paragraph', + ) + expect(fragment.toJSON()).not.toContain('is a bold') + expect(toolCalls.length).toBeGreaterThan(0) + }) + }, +) + +function logToolCallCount(label: string, toolCalls: string[]) { + const editToolCalls = toolCalls.filter((toolName) => toolName === 'edit_yjs_xml_fragment') + console.log( + `Tool calls for ${label}: ${toolCalls.length} total, ${editToolCalls.length} edit calls (${toolCalls.join(', ')})`, + ) +} + +function codeTagContents(xml: string) { + return [...xml.matchAll(/(.*?)<\/code>/g)].map((match) => match[1] ?? '') +} + +async function applyEdit(options: { agent: Agent; oldString: string; newString: string; replaceAll?: boolean }) { + const run = options.agent.run({ + state: startState([ + userMessage( + userInstructionForYXmlFragmentEditorAgent({ + oldString: options.oldString, + newString: options.newString, + replaceAll: options.replaceAll, + }), + ), + ]), + }) + + await expect(run.result).resolves.not.toBeUndefined() +} + +function createComplexDocumentNodes(): Array { + return [ + element('heading', { level: '1' }, [ + element( + 'link', + { + class: 'null', + href: 'http://README.md', + rel: 'noopener noreferrer nofollow', + target: '_blank', + title: 'null', + }, + [text('README.md')], + ), + text(' '), + ]), + element('paragraph', {}, [text('This is an example file')]), + element('paragraph'), + element('paragraph'), + list('bulletList', ['this ', 'is a ', 'bulleted list']), + element('paragraph'), + element('paragraph'), + list('orderedList', ['This ', 'is ', 'a', 'numbered ', 'list'], { start: '1' }), + element('paragraph'), + element('codeBlock', {}, [text('this is a code block')]), + element('paragraph'), + element('paragraph'), + element('paragraph'), + element('paragraph', {}, [element('bold', {}, [text("i don't like code blocks")])]), + element('paragraph'), + element('codeBlock', { language: 'typescript' }, [text('this is a typescript code block')]), + element('paragraph'), + element('paragraph'), + element('bulletList', {}, [ + element('listItem', {}, [ + element('paragraph', {}, [ + element('code', {}, [text('this is a code block in python')]), + text(' in a bulleted list'), + ]), + ]), + element('listItem', {}, [element('paragraph', {}, [text('this is a bulleted list item ')])]), + ]), + element('paragraph'), + element('paragraph'), + element('paragraph', {}, [ + text('This '), + element('bold', {}, [text('is a bold')]), + text(' and '), + element('italic', {}, [text('italic')]), + text(' item in a paragraph'), + ]), + element('paragraph'), + ] +} + +function list(nodeName: 'bulletList' | 'orderedList', items: string[], attributes: Record = {}) { + return element( + nodeName, + attributes, + items.map((item) => element('listItem', {}, [element('paragraph', {}, [text(item)])])), + ) +} + +function element( + name: string, + attributes: Record = {}, + children: Array = [], +) { + const node = new Y.XmlElement(name) + for (const [key, value] of Object.entries(attributes)) node.setAttribute(key, value) + if (children.length > 0) node.insert(0, children) + return node +} + +function text(value: string) { + const node = new Y.XmlText() + node.insert(0, value) + return node +} From 7583e72db13bd33ebc757f9fe53d6114403f46cd Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Thu, 7 May 2026 19:21:02 -0700 Subject: [PATCH 16/18] feat rich markdown cli agent --- bun.lock | 1 + packages/yjs-rich-markdown/package.json | 4 +- .../yjs-rich-markdown/src/agents/index.ts | 1 + .../src/agents/rich-markdown-cli-agent.ts | 109 +++++++++++ .../src/agents/yxml-fragment-editor.ts | 4 +- .../src/cli/rich-markdown-agent.ts | 178 ++++++++++++++++++ .../src/tools/edit-rich-markdown.ts | 51 ++++- 7 files changed, 340 insertions(+), 8 deletions(-) create mode 100644 packages/yjs-rich-markdown/src/agents/rich-markdown-cli-agent.ts create mode 100644 packages/yjs-rich-markdown/src/cli/rich-markdown-agent.ts diff --git a/bun.lock b/bun.lock index e05b046..3653022 100644 --- a/bun.lock +++ b/bun.lock @@ -310,6 +310,7 @@ "@tiptap/markdown": "3.22.5", "@tiptap/pm": "3.22.5", "@tiptap/starter-kit": "3.22.5", + "ai": "catalog:", "dedent": "1.7.2", "y-prosemirror": "1.3.7", "zod": "catalog:", diff --git a/packages/yjs-rich-markdown/package.json b/packages/yjs-rich-markdown/package.json index 1a663cb..bb6420c 100644 --- a/packages/yjs-rich-markdown/package.json +++ b/packages/yjs-rich-markdown/package.json @@ -24,6 +24,7 @@ } }, "scripts": { + "agent": "NODE_USE_SYSTEM_CA=1 YJS_RICH_MARKDOWN_BASE_URL=${YJS_RICH_MARKDOWN_BASE_URL:-http://localhost:5176/v1/yjs/rich-markdown-demo} YJS_RICH_MARKDOWN_DOC_ID=${YJS_RICH_MARKDOWN_DOC_ID:-rich-markdown-learning} bun run src/cli/rich-markdown-agent.ts", "typecheck": "tsgo --noEmit", "build": "bun run build:js && bunx tsc -p tsconfig.build.json", "build:js": "bun run build.ts", @@ -45,6 +46,7 @@ }, "dependencies": { "@humanlayer/quickjs-exec": "workspace:*", + "ai": "catalog:", "@tiptap/core": "3.22.5", "@tiptap/extension-collaboration": "3.22.5", "@tiptap/markdown": "3.22.5", @@ -58,4 +60,4 @@ "name": "Kyle Mistele", "url": "https://x.com/0xblacklight" } -} \ No newline at end of file +} diff --git a/packages/yjs-rich-markdown/src/agents/index.ts b/packages/yjs-rich-markdown/src/agents/index.ts index bedd385..0794e4e 100644 --- a/packages/yjs-rich-markdown/src/agents/index.ts +++ b/packages/yjs-rich-markdown/src/agents/index.ts @@ -1 +1,2 @@ +export * from './rich-markdown-cli-agent' export * from './yxml-fragment-editor' diff --git a/packages/yjs-rich-markdown/src/agents/rich-markdown-cli-agent.ts b/packages/yjs-rich-markdown/src/agents/rich-markdown-cli-agent.ts new file mode 100644 index 0000000..6454ee2 --- /dev/null +++ b/packages/yjs-rich-markdown/src/agents/rich-markdown-cli-agent.ts @@ -0,0 +1,109 @@ +import { Agent, defineTool, doomLoop, maxSteps, startState, userMessage } from '@humanlayer/agentlayer-core' +import { anthropic } from '@ai-sdk/anthropic' +import dedent from 'dedent' +import z from 'zod/v4' +import type { RichMarkdownArtifactStore } from '../artifact-store' +import { readArtifactMarkdown, writeArtifactMarkdown } from '../markdown' +import { createEditRichMarkdownTool } from '../tools' + +export type CreateRichMarkdownCliAgentOptions = { + model: ConstructorParameters[0]['model'] + artifactStore: RichMarkdownArtifactStore +} + +export const anthropicEphemeralProviderOptions = { + anthropic: { + cacheControl: { type: 'ephemeral' }, + }, +} as const + +export function createRichMarkdownCliAgent(options: CreateRichMarkdownCliAgentOptions) { + const { model, artifactStore } = options + + const listArtifactsTool = defineTool({ + name: 'list_artifacts', + description: 'List rich markdown artifact paths available in the connected Yjs document.', + input: z.object({}), + execute: async () => { + const artifacts = artifactStore.listArtifacts() + if (artifacts.length === 0) return 'No rich markdown artifacts found.' + return artifacts.map((artifact) => artifact.path).join('\n') + }, + }) + + const readArtifactTool = defineTool({ + name: 'read_artifact', + description: 'Read one rich markdown artifact as Markdown.', + input: z.object({ + path: z.string().describe('Artifact path to read, e.g. /artifacts/README.md'), + }), + execute: async ({ path }) => { + artifactStore.getArtifact(path) + return readArtifactMarkdown(artifactStore.doc, path) + }, + }) + + const writeArtifactTool = defineTool({ + name: 'write_artifact', + description: 'Replace one rich markdown artifact with Markdown content.', + input: z.object({ + path: z.string().describe('Artifact path to write, e.g. /artifacts/README.md'), + markdown: z.string().describe('Full Markdown content for the artifact'), + }), + execute: async ({ path, markdown }) => { + artifactStore.ensureArtifact(path) + writeArtifactMarkdown(artifactStore.doc, path, markdown) + return `Wrote ${path}` + }, + }) + + const inspectArtifactTool = defineTool({ + name: 'inspect_artifact', + description: 'Inspect one rich markdown artifact as its internal YXml serialization.', + input: z.object({ + path: z.string().describe('Artifact path to inspect, e.g. /artifacts/README.md'), + }), + execute: async ({ path }) => { + artifactStore.getArtifact(path) + return artifactStore.getFragment(path).toJSON() + }, + }) + + return new Agent({ + system: cliAgentPrompt, + model, + tools: { + list_artifacts: listArtifactsTool, + read_artifact: readArtifactTool, + write_artifact: writeArtifactTool, + inspect_artifact: inspectArtifactTool, + edit: createEditRichMarkdownTool(artifactStore, { + model, + providerOptions: anthropicEphemeralProviderOptions as ConstructorParameters[0]['providerOptions'], + }), + }, + providerOptions: anthropicEphemeralProviderOptions as ConstructorParameters[0]['providerOptions'], + stopWhen: [doomLoop(), maxSteps(50)], + }) +} + +export function defaultRichMarkdownCliModel() { + return anthropic(process.env.ANTHROPIC_MODEL ?? 'claude-sonnet-4-6') +} + +export function createRichMarkdownCliAgentState(prompt: string) { + return startState([userMessage(prompt)]) +} + +const cliAgentPrompt = dedent` + You are a CLI agent connected to a live Durable Streams-backed Yjs document containing rich markdown artifacts. + + Use Markdown as the user-facing format: + - Use list_artifacts to discover available artifact paths. + - Use read_artifact to inspect current Markdown. + - Use write_artifact only when replacing an entire artifact is intended. + - Use edit for targeted old_string/new_string edits. The edit tool delegates to a rich markdown apply sub-agent that mutates the live Y.XmlFragment structurally through QuickJS proxy bindings. + - Use inspect_artifact only for debugging internal YXml structure. + + After completing the requested work, respond with a concise summary of changed artifact paths. +` diff --git a/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts b/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts index 325616c..9db9ca0 100644 --- a/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts +++ b/packages/yjs-rich-markdown/src/agents/yxml-fragment-editor.ts @@ -41,8 +41,9 @@ export const createYXmlFragmentEditorAgent = (agentConfig: { fragment: Y.XmlFragment preToolHooks?: Array postToolHooks?: Array + providerOptions?: ConstructorParameters[0]['providerOptions'] }) => { - const { modelConfig, fragment, preToolHooks, postToolHooks } = agentConfig + const { modelConfig, fragment, preToolHooks, postToolHooks, providerOptions } = agentConfig const intructions = editorPrompt(fragment) @@ -55,6 +56,7 @@ export const createYXmlFragmentEditorAgent = (agentConfig: { done: doneTool, i_am_stuck: stuckTool, }, + providerOptions, stopWhen: [ doomLoop(), maxSteps(30), // probably too many but we will see. diff --git a/packages/yjs-rich-markdown/src/cli/rich-markdown-agent.ts b/packages/yjs-rich-markdown/src/cli/rich-markdown-agent.ts new file mode 100644 index 0000000..a18d94f --- /dev/null +++ b/packages/yjs-rich-markdown/src/cli/rich-markdown-agent.ts @@ -0,0 +1,178 @@ +#!/usr/bin/env bun +import readline from 'node:readline/promises' +import { YjsProvider } from '@durable-streams/y-durable-streams' +import { createOutputRenderer, extractLastAssistantText, renderFinish, startState } from '@humanlayer/agentlayer-core' +import type { Agent, AgentState } from '@humanlayer/agentlayer-core' +import { Awareness } from 'y-protocols/awareness' +import * as Y from 'yjs' +import { RichMarkdownArtifactStore } from '../artifact-store' +import { + createRichMarkdownCliAgent, + createRichMarkdownCliAgentState, + defaultRichMarkdownCliModel, +} from '../agents' + +const DEFAULT_BASE_URL = 'https://localhost:4437/v1/yjs/rich-markdown' +const DEFAULT_DOC_ID = 'rich-artifacts-learning' + +async function main() { + const options = parseArgs(Bun.argv.slice(2)) + const prompt = options.prompt ?? options.positionals.join(' ').trim() + if (!prompt && !options.interactive) { + throw new Error('Usage: bun run rich-agent -- "your instruction" [--base-url URL] [--doc-id DOC_ID]') + } + + const doc = new Y.Doc() + const awareness = new Awareness(doc) + const provider = new YjsProvider({ + doc, + awareness, + docId: options.docId ?? process.env.YJS_RICH_MARKDOWN_DOC_ID ?? DEFAULT_DOC_ID, + baseUrl: options.baseUrl ?? process.env.YJS_RICH_MARKDOWN_BASE_URL ?? DEFAULT_BASE_URL, + liveMode: 'long-poll', + connect: false, + }) + + await provider.connect() + await waitForSync(provider) + + const artifactStore = new RichMarkdownArtifactStore(doc) + const agent = createRichMarkdownCliAgent({ + model: defaultRichMarkdownCliModel(), + artifactStore, + }) + const renderer = createOutputRenderer({ + output: process.stdout, + includeTokenUsage: options.verbose, + includeToolResults: options.verbose, + streamToolArgs: options.streamToolArgs, + }) + + if (options.interactive) { + await runInteractive(agent, prompt ? createRichMarkdownCliAgentState(prompt) : startState([]), renderer, { + runInitialPrompt: !!prompt, + }) + return + } + + const run = agent.run({ state: createRichMarkdownCliAgentState(prompt), stream: true }) + for await (const event of run) { + renderer.onEvent(event) + } + const result = await run.result + renderer.flush() + if (result.finishReason === 'error') { + throw result.error ?? new Error('Rich markdown CLI agent failed') + } + const finalText = extractLastAssistantText(result).trim() + if (finalText) console.log(`__RICH_MARKDOWN_AGENT_FINAL_MESSAGE_START__${finalText}__RICH_MARKDOWN_AGENT_FINAL_MESSAGE_END__`) + renderFinish(result) +} + +type CliOptions = { + baseUrl?: string + docId?: string + prompt?: string + interactive?: boolean + streamToolArgs?: boolean + verbose?: boolean + positionals: string[] +} + +function parseArgs(args: string[]): CliOptions { + const options: CliOptions = { positionals: [] } + for (let index = 0; index < args.length; index++) { + const arg = args[index] + if (arg === '--base-url') options.baseUrl = requiredValue(args, ++index, arg) + else if (arg === '--doc-id') options.docId = requiredValue(args, ++index, arg) + else if (arg === '--prompt') options.prompt = requiredValue(args, ++index, arg) + else if (arg === '--interactive') options.interactive = true + else if (arg === '--stream') options.streamToolArgs = true + else if (arg === '--verbose') options.verbose = true + else if (arg) options.positionals.push(arg) + } + return options +} + +async function runInteractive( + agent: Agent, + initialState: AgentState, + renderer: ReturnType, + options: { runInitialPrompt?: boolean } = {}, +) { + let state = initialState + let currentAbortController: AbortController | null = null + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) + + rl.on('SIGINT', () => { + if (currentAbortController) { + currentAbortController.abort() + return + } + rl.close() + process.exit(0) + }) + + try { + if (options.runInitialPrompt) { + const result = await runAgentTurn(agent, state, renderer) + state = result.state + } + + while (true) { + const line = await rl.question('rich-markdown> ') + const text = line.trim() + if (!text) continue + if (text === 'exit' || text === '/exit') return + + state.messages.push({ role: 'user', content: text }) + currentAbortController = new AbortController() + const result = await runAgentTurn(agent, state, renderer, currentAbortController.signal) + currentAbortController = null + state = result.state + } + } finally { + rl.close() + } +} + +async function runAgentTurn( + agent: Agent, + state: AgentState, + renderer: ReturnType, + signal?: AbortSignal, +) { + const run = agent.run({ state, signal, stream: true }) + for await (const event of run) renderer.onEvent(event) + const result = await run.result + renderer.flush() + renderFinish(result) + return result +} + +function requiredValue(args: string[], index: number, flag: string) { + const value = args[index] + if (!value) throw new Error(`Missing value for ${flag}`) + return value +} + +async function waitForSync(provider: YjsProvider, timeoutMs = 10_000) { + if (provider.synced) return + await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + provider.off('synced', handleSynced) + reject(new Error(`Timed out waiting for Yjs provider sync after ${timeoutMs}ms`)) + }, timeoutMs) + + const handleSynced = (synced: boolean) => { + if (!synced) return + clearTimeout(timeout) + provider.off('synced', handleSynced) + resolve() + } + + provider.on('synced', handleSynced) + }) +} + +await main() diff --git a/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts b/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts index 8e79928..ca3d4e2 100644 --- a/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts +++ b/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts @@ -1,21 +1,60 @@ -import { EDIT_DESCRIPTION } from '@humanlayer/agentlayer-core' +import { EDIT_DESCRIPTION, startState, userMessage } from '@humanlayer/agentlayer-core' import { EditTool } from '@humanlayer/agentlayer-core/interfaces' import type * as Y from 'yjs' import type { RichMarkdownArtifactStore } from '../artifact-store' +import { readArtifactMarkdown } from '../markdown' +import { createYXmlFragmentEditorAgent, userInstructionForYXmlFragmentEditorAgent } from '../agents' + +export type CreateEditRichMarkdownToolOptions = { + model: Parameters[0]['modelConfig'] + providerOptions?: Parameters[0]['providerOptions'] +} + /** * Edit tool for editing markdown. Uses the EditTool interface but deletes to code-mode sub-agent for edits * @param artifactStore * @returns */ -export function createEditRichMarkdownTool(artifactStore: RichMarkdownArtifactStore) { +export function createEditRichMarkdownTool( + artifactStore: RichMarkdownArtifactStore, + options: CreateEditRichMarkdownToolOptions, +) { return EditTool.define( async (input, ctx) => { - let matchCount: 0 - const artifact = artifactStore.getArtifact(input.file_path) - + artifactStore.getArtifact(input.file_path) const fragment: Y.XmlFragment = artifactStore.getFragment(input.file_path) + const beforeMarkdown = readArtifactMarkdown(artifactStore.doc, input.file_path) + + const applyAgent = createYXmlFragmentEditorAgent({ + modelConfig: options.model, + fragment, + providerOptions: options.providerOptions, + }) + + const run = applyAgent.run({ + state: startState([ + userMessage( + userInstructionForYXmlFragmentEditorAgent({ + oldString: input.old_string, + newString: input.new_string, + replaceAll: input.replace_all, + }), + ), + ]), + stream: ctx.stream, + signal: ctx.signal, + }) + + if (ctx.awaitSubAgent && ctx.toolCallId) { + await ctx.awaitSubAgent(run, `rich-markdown-apply:${input.file_path}`, ctx.toolCallId) + } else { + await run.result + } + + const afterMarkdown = readArtifactMarkdown(artifactStore.doc, input.file_path) + const changed = beforeMarkdown !== afterMarkdown - return { content: '', matchCount: 0 } + return { content: afterMarkdown, matchCount: changed ? 1 : 0 } }, { description: EDIT_DESCRIPTION, From 5e3c7a242c3d422b40cfa02a6c94bdeb5649ec84 Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Thu, 7 May 2026 19:22:50 -0700 Subject: [PATCH 17/18] fix quickjs tool test expectation --- .../yjs-rich-markdown/test/quickjs-code-mode-tool.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/yjs-rich-markdown/test/quickjs-code-mode-tool.test.ts b/packages/yjs-rich-markdown/test/quickjs-code-mode-tool.test.ts index 84844a8..cdab46b 100644 --- a/packages/yjs-rich-markdown/test/quickjs-code-mode-tool.test.ts +++ b/packages/yjs-rich-markdown/test/quickjs-code-mode-tool.test.ts @@ -32,7 +32,7 @@ describe('QuickJS Code Mode Tool Tests', async () => { {} as ToolContext, ) - await expect(toolExecution).resolves.toBeUndefined() + await expect(toolExecution).resolves.toContain('Execution succeeded') expect(fragment.toJSON()).toEqual('') const secondToolExecution = tool.execute( @@ -50,7 +50,7 @@ describe('QuickJS Code Mode Tool Tests', async () => { {} as ToolContext, ) - await expect(secondToolExecution).resolves.toBeUndefined() + await expect(secondToolExecution).resolves.toContain('Execution succeeded') expect(fragment.toJSON()).toEqual('') }) }) From bbdb545f6d497d07b59a5f773d72f880d6b6ff47 Mon Sep 17 00:00:00 2001 From: Kyle Mistele Date: Thu, 7 May 2026 19:24:33 -0700 Subject: [PATCH 18/18] style apply biome formatting --- packages/quickjs-exec/src/index.ts | 10 ++- .../src/agents/rich-markdown-cli-agent.ts | 6 +- .../src/cli/rich-markdown-agent.ts | 11 ++-- .../src/tools/edit-rich-markdown.ts | 2 +- packages/yjs-rich-markdown/src/yxml-proxy.ts | 61 +++++++++++++------ .../test/apply-agent.test.ts | 2 +- .../test/yxml-invalid-structures.test.ts | 6 +- .../test/yxml-proxy-prompt-examples.test.ts | 46 ++++++++++---- .../test/yxml-proxy-quickjs.test.ts | 4 +- .../yjs-rich-markdown/test/yxml-proxy.test.ts | 35 ++++++++--- 10 files changed, 127 insertions(+), 56 deletions(-) diff --git a/packages/quickjs-exec/src/index.ts b/packages/quickjs-exec/src/index.ts index 13bf064..8b606df 100644 --- a/packages/quickjs-exec/src/index.ts +++ b/packages/quickjs-exec/src/index.ts @@ -186,8 +186,14 @@ function unwrapQuickJsResult( function createQuickJsExecutionError(error: unknown, code: string): QuickJsExecutionError { const cause = error && typeof error === 'object' && 'cause' in error ? (error as { cause?: unknown }).cause : error const causeRecord = cause && typeof cause === 'object' ? (cause as Record) : undefined - const name = typeof causeRecord?.name === 'string' ? causeRecord.name : error instanceof Error ? error.name : undefined - const message = typeof causeRecord?.message === 'string' ? causeRecord.message : error instanceof Error ? error.message : String(error) + const name = + typeof causeRecord?.name === 'string' ? causeRecord.name : error instanceof Error ? error.name : undefined + const message = + typeof causeRecord?.message === 'string' + ? causeRecord.message + : error instanceof Error + ? error.message + : String(error) const fileName = typeof causeRecord?.fileName === 'string' ? causeRecord.fileName : undefined const lineNumber = typeof causeRecord?.lineNumber === 'number' ? causeRecord.lineNumber : undefined const columnNumber = typeof causeRecord?.columnNumber === 'number' ? causeRecord.columnNumber : undefined diff --git a/packages/yjs-rich-markdown/src/agents/rich-markdown-cli-agent.ts b/packages/yjs-rich-markdown/src/agents/rich-markdown-cli-agent.ts index 6454ee2..d6ebb3f 100644 --- a/packages/yjs-rich-markdown/src/agents/rich-markdown-cli-agent.ts +++ b/packages/yjs-rich-markdown/src/agents/rich-markdown-cli-agent.ts @@ -1,5 +1,5 @@ -import { Agent, defineTool, doomLoop, maxSteps, startState, userMessage } from '@humanlayer/agentlayer-core' import { anthropic } from '@ai-sdk/anthropic' +import { Agent, defineTool, doomLoop, maxSteps, startState, userMessage } from '@humanlayer/agentlayer-core' import dedent from 'dedent' import z from 'zod/v4' import type { RichMarkdownArtifactStore } from '../artifact-store' @@ -79,7 +79,9 @@ export function createRichMarkdownCliAgent(options: CreateRichMarkdownCliAgentOp inspect_artifact: inspectArtifactTool, edit: createEditRichMarkdownTool(artifactStore, { model, - providerOptions: anthropicEphemeralProviderOptions as ConstructorParameters[0]['providerOptions'], + providerOptions: anthropicEphemeralProviderOptions as ConstructorParameters< + typeof Agent + >[0]['providerOptions'], }), }, providerOptions: anthropicEphemeralProviderOptions as ConstructorParameters[0]['providerOptions'], diff --git a/packages/yjs-rich-markdown/src/cli/rich-markdown-agent.ts b/packages/yjs-rich-markdown/src/cli/rich-markdown-agent.ts index a18d94f..ff082db 100644 --- a/packages/yjs-rich-markdown/src/cli/rich-markdown-agent.ts +++ b/packages/yjs-rich-markdown/src/cli/rich-markdown-agent.ts @@ -1,16 +1,12 @@ #!/usr/bin/env bun import readline from 'node:readline/promises' import { YjsProvider } from '@durable-streams/y-durable-streams' -import { createOutputRenderer, extractLastAssistantText, renderFinish, startState } from '@humanlayer/agentlayer-core' import type { Agent, AgentState } from '@humanlayer/agentlayer-core' +import { createOutputRenderer, extractLastAssistantText, renderFinish, startState } from '@humanlayer/agentlayer-core' import { Awareness } from 'y-protocols/awareness' import * as Y from 'yjs' +import { createRichMarkdownCliAgent, createRichMarkdownCliAgentState, defaultRichMarkdownCliModel } from '../agents' import { RichMarkdownArtifactStore } from '../artifact-store' -import { - createRichMarkdownCliAgent, - createRichMarkdownCliAgentState, - defaultRichMarkdownCliModel, -} from '../agents' const DEFAULT_BASE_URL = 'https://localhost:4437/v1/yjs/rich-markdown' const DEFAULT_DOC_ID = 'rich-artifacts-learning' @@ -65,7 +61,8 @@ async function main() { throw result.error ?? new Error('Rich markdown CLI agent failed') } const finalText = extractLastAssistantText(result).trim() - if (finalText) console.log(`__RICH_MARKDOWN_AGENT_FINAL_MESSAGE_START__${finalText}__RICH_MARKDOWN_AGENT_FINAL_MESSAGE_END__`) + if (finalText) + console.log(`__RICH_MARKDOWN_AGENT_FINAL_MESSAGE_START__${finalText}__RICH_MARKDOWN_AGENT_FINAL_MESSAGE_END__`) renderFinish(result) } diff --git a/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts b/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts index ca3d4e2..9dc4064 100644 --- a/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts +++ b/packages/yjs-rich-markdown/src/tools/edit-rich-markdown.ts @@ -1,9 +1,9 @@ import { EDIT_DESCRIPTION, startState, userMessage } from '@humanlayer/agentlayer-core' import { EditTool } from '@humanlayer/agentlayer-core/interfaces' import type * as Y from 'yjs' +import { createYXmlFragmentEditorAgent, userInstructionForYXmlFragmentEditorAgent } from '../agents' import type { RichMarkdownArtifactStore } from '../artifact-store' import { readArtifactMarkdown } from '../markdown' -import { createYXmlFragmentEditorAgent, userInstructionForYXmlFragmentEditorAgent } from '../agents' export type CreateEditRichMarkdownToolOptions = { model: Parameters[0]['modelConfig'] diff --git a/packages/yjs-rich-markdown/src/yxml-proxy.ts b/packages/yjs-rich-markdown/src/yxml-proxy.ts index 03c7f80..dab292b 100644 --- a/packages/yjs-rich-markdown/src/yxml-proxy.ts +++ b/packages/yjs-rich-markdown/src/yxml-proxy.ts @@ -1,5 +1,5 @@ -import * as Y from 'yjs' import type { QuickJsBindings } from '@humanlayer/quickjs-exec' +import * as Y from 'yjs' export type YXmlNodeKind = 'fragment' | 'element' | 'text' @@ -46,12 +46,12 @@ export type YXmlProxy = { insertText(input: { node: YXmlNodeRef; index: number; text: string }): void deleteText(input: { node: YXmlNodeRef; index: number; length: number }): void splitText(input: { node: YXmlNodeRef; index: number }): { left: YXmlNodeRef; right: YXmlNodeRef } - wrapTextRange(input: { - node: YXmlNodeRef - start: number - end: number - wrapper: YXmlElementWrapperSpec - }): { wrapper: YXmlNodeRef; child: YXmlNodeRef; before?: YXmlNodeRef; after?: YXmlNodeRef } + wrapTextRange(input: { node: YXmlNodeRef; start: number; end: number; wrapper: YXmlElementWrapperSpec }): { + wrapper: YXmlNodeRef + child: YXmlNodeRef + before?: YXmlNodeRef + after?: YXmlNodeRef + } wrap(input: { node: YXmlNodeRef; wrapper: YXmlElementWrapperSpec }): { wrapper: YXmlNodeRef; child: YXmlNodeRef } remove(input: { node: YXmlNodeRef }): void setAttribute(input: { node: YXmlNodeRef; name: string; value: string }): void @@ -82,7 +82,10 @@ export class YXmlNodeRefKindMismatchError extends YXmlProxyError { readonly ref: YXmlNodeRef, readonly actualKind: YXmlNodeKind, ) { - super(`YXml node ref kind mismatch for ${ref.id}: expected ${ref.kind}, got ${actualKind}`, 'NODE_REF_KIND_MISMATCH') + super( + `YXml node ref kind mismatch for ${ref.id}: expected ${ref.kind}, got ${actualKind}`, + 'NODE_REF_KIND_MISMATCH', + ) this.name = 'YXmlNodeRefKindMismatchError' } } @@ -162,12 +165,14 @@ export class YXmlProxyBindings implements YXmlProxy { deleteText: (input) => this.deleteText(input as { node: YXmlNodeRef; index: number; length: number }), splitText: (input) => this.splitText(input as { node: YXmlNodeRef; index: number }), wrapTextRange: (input) => - this.wrapTextRange(input as { - node: YXmlNodeRef - start: number - end: number - wrapper: YXmlElementWrapperSpec - }), + this.wrapTextRange( + input as { + node: YXmlNodeRef + start: number + end: number + wrapper: YXmlElementWrapperSpec + }, + ), wrap: (input) => this.wrap(input as { node: YXmlNodeRef; wrapper: YXmlElementWrapperSpec }), remove: (input) => this.remove(input as { node: YXmlNodeRef }), setAttribute: (input) => this.setAttribute(input as { node: YXmlNodeRef; name: string; value: string }), @@ -259,7 +264,12 @@ export class YXmlProxyBindings implements YXmlProxy { deleteText({ node, index, length }: { node: YXmlNodeRef; index: number; length: number }): void { if (this.deletedRefs.has(node.id)) throw new DetachedYXmlNodeRefError('delete text') const target = this.resolveText(node) - this.assertTextRange({ operation: 'delete text from', length: target.length, start: index, end: index + length }) + this.assertTextRange({ + operation: 'delete text from', + length: target.length, + start: index, + end: index + length, + }) if (length === 0) return target.delete(index, length) } @@ -332,7 +342,10 @@ export class YXmlProxyBindings implements YXmlProxy { } } - wrap({ node, wrapper }: { node: YXmlNodeRef; wrapper: YXmlElementWrapperSpec }): { wrapper: YXmlNodeRef; child: YXmlNodeRef } { + wrap({ node, wrapper }: { node: YXmlNodeRef; wrapper: YXmlElementWrapperSpec }): { + wrapper: YXmlNodeRef + child: YXmlNodeRef + } { if (this.deletedRefs.has(node.id)) throw new DetachedYXmlNodeRefError('wrap') const target = this.resolve(node) if (target === this.rootFragment) throw new YXmlRootOperationError('wrap') @@ -395,19 +408,22 @@ export class YXmlProxyBindings implements YXmlProxy { private resolveContainer(ref: YXmlNodeRef | undefined, operation: string): YXmlContainer { const node = this.resolve(ref) - if (node instanceof Y.XmlText) throw new YXmlInvalidNodeKindForOperationError(operation, 'fragment or element', 'text') + if (node instanceof Y.XmlText) + throw new YXmlInvalidNodeKindForOperationError(operation, 'fragment or element', 'text') return node } private resolveElement(ref: YXmlNodeRef): Y.XmlElement { const node = this.resolve(ref) - if (!(node instanceof Y.XmlElement)) throw new YXmlInvalidNodeKindForOperationError('set or remove attribute', 'element', kindOf(node)) + if (!(node instanceof Y.XmlElement)) + throw new YXmlInvalidNodeKindForOperationError('set or remove attribute', 'element', kindOf(node)) return node } private resolveText(ref: YXmlNodeRef): Y.XmlText { const node = this.resolve(ref) - if (!(node instanceof Y.XmlText)) throw new YXmlInvalidNodeKindForOperationError('read or edit text', 'text', kindOf(node)) + if (!(node instanceof Y.XmlText)) + throw new YXmlInvalidNodeKindForOperationError('read or edit text', 'text', kindOf(node)) return node } @@ -453,7 +469,12 @@ export class YXmlProxyBindings implements YXmlProxy { return element } - private replaceNode(node: InsertableYXmlNode, operation: string, replacements: InsertableYXmlNode[], deletedRefId: string): void { + private replaceNode( + node: InsertableYXmlNode, + operation: string, + replacements: InsertableYXmlNode[], + deletedRefId: string, + ): void { const parent = this.parentContainerOf(node, operation) const index = parent.slice().indexOf(node) if (index < 0) throw new DetachedYXmlNodeRefError(operation) diff --git a/packages/yjs-rich-markdown/test/apply-agent.test.ts b/packages/yjs-rich-markdown/test/apply-agent.test.ts index b281b8d..123ce44 100644 --- a/packages/yjs-rich-markdown/test/apply-agent.test.ts +++ b/packages/yjs-rich-markdown/test/apply-agent.test.ts @@ -69,7 +69,7 @@ describe.skipIf(!process.env.ANTHROPIC_API_KEY || !!process.env.CI)('Live Apply }) await expect(run.result).resolves.not.toBeUndefined() - const runResult = await run.result + const _runResult = await run.result logToolCallCount('simple apply', toolCalls) expect(tiptapJsonToMarkdown(yXmlFragmentToProsemirrorJSON(fragment))).toContain('##') diff --git a/packages/yjs-rich-markdown/test/yxml-invalid-structures.test.ts b/packages/yjs-rich-markdown/test/yxml-invalid-structures.test.ts index 7247019..32c1c9d 100644 --- a/packages/yjs-rich-markdown/test/yxml-invalid-structures.test.ts +++ b/packages/yjs-rich-markdown/test/yxml-invalid-structures.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from 'bun:test' import type { JSONContent } from '@tiptap/core' -import * as Y from 'yjs' import { yXmlFragmentToProsemirrorJSON } from 'y-prosemirror' +import * as Y from 'yjs' import { markdownToTiptapJson, tiptapJsonToMarkdown } from '../src' function text(value: string): Y.XmlText { @@ -37,7 +37,9 @@ describe('invalid or lossy YXml structures', () => { test('unsupported nested node type survives YXml but is dropped during markdown serialization', () => { const doc = new Y.Doc() const fragment = doc.getXmlFragment('invalid') - fragment.insert(0, [element('paragraph', [text('Before '), element('custommark', [text('marked')]), text(' after')])]) + fragment.insert(0, [ + element('paragraph', [text('Before '), element('custommark', [text('marked')]), text(' after')]), + ]) expect(fragment.toString()).toBe('Before marked after') expect(jsonFor(fragment)).toEqual({ diff --git a/packages/yjs-rich-markdown/test/yxml-proxy-prompt-examples.test.ts b/packages/yjs-rich-markdown/test/yxml-proxy-prompt-examples.test.ts index 4e579fd..1b9f791 100644 --- a/packages/yjs-rich-markdown/test/yxml-proxy-prompt-examples.test.ts +++ b/packages/yjs-rich-markdown/test/yxml-proxy-prompt-examples.test.ts @@ -53,35 +53,61 @@ describe('YXML proxy prompt examples', () => { { kind: 'element', nodeName: 'listItem', - children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Use scoped NodeRefs.' }] }], + children: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Use scoped NodeRefs.' }], + }, + ], }, { kind: 'element', nodeName: 'listItem', - children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Validate after mutation.' }] }], + children: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Validate after mutation.' }], + }, + ], }, ], }, { kind: 'element', nodeName: 'blockquote', - children: [{ kind: 'element', nodeName: 'paragraph', children: [{ kind: 'text', text: 'Bindings keep generated code sandbox-safe.' }] }], + children: [ + { + kind: 'element', + nodeName: 'paragraph', + children: [{ kind: 'text', text: 'Bindings keep generated code sandbox-safe.' }], + }, + ], }, { kind: 'element', nodeName: 'horizontalRule' }, { kind: 'element', nodeName: 'codeBlock', attributes: { language: 'ts' }, - children: [{ kind: 'text', text: 'const root = bindings.root()\nconst xml = bindings.toString({})' }], + children: [ + { kind: 'text', text: 'const root = bindings.root()\nconst xml = bindings.toString({})' }, + ], }, ], }) expect(proxy.toString({})).toContain('Implementation Notes') - expect(proxy.toString({})).toContain('The agent edits through bindings instead of direct Yjs objects.') - expect(proxy.toString({})).toContain('
Bindings keep generated code sandbox-safe.
') + expect(proxy.toString({})).toContain( + 'The agent edits through bindings instead of direct Yjs objects.', + ) + expect(proxy.toString({})).toContain( + '
Bindings keep generated code sandbox-safe.
', + ) expect(proxy.toString({})).toContain('') - expect(proxy.toString({})).toContain('const root = bindings.root()\nconst xml = bindings.toString({})') + expect(proxy.toString({})).toContain( + 'const root = bindings.root()\nconst xml = bindings.toString({})', + ) }) test('add a bullet to an existing list', () => { @@ -232,11 +258,7 @@ function createInlineMarkedExampleProxy(): YXmlProxyBindings { const doc = new Y.Doc() const fragment = doc.getXmlFragment('artifact') const paragraph = new Y.XmlElement('paragraph') - paragraph.insert(0, [ - text('Before '), - createElement('italic', [text('nested example text')]), - text(' after'), - ]) + paragraph.insert(0, [text('Before '), createElement('italic', [text('nested example text')]), text(' after')]) fragment.insert(0, [paragraph]) return new YXmlProxyBindings(fragment) } diff --git a/packages/yjs-rich-markdown/test/yxml-proxy-quickjs.test.ts b/packages/yjs-rich-markdown/test/yxml-proxy-quickjs.test.ts index 4117bd7..753f9ae 100644 --- a/packages/yjs-rich-markdown/test/yxml-proxy-quickjs.test.ts +++ b/packages/yjs-rich-markdown/test/yxml-proxy-quickjs.test.ts @@ -111,12 +111,12 @@ describe('Y.XMLFragment Proxy tests', () => { let doc: Y.Doc let fragment: Y.XmlFragment let proxy: YXmlProxyBindings - let root: Y.XmlElement + let _root: Y.XmlElement beforeEach(() => { doc = new Y.Doc() fragment = doc.getXmlFragment('artifact') proxy = new YXmlProxyBindings(fragment) - root = new Y.XmlElement('root') + _root = new Y.XmlElement('root') }) test('can edit a deeply nested fragment at multiple tree levels', () => { diff --git a/packages/yjs-rich-markdown/test/yxml-proxy.test.ts b/packages/yjs-rich-markdown/test/yxml-proxy.test.ts index dec0c54..83aa3bb 100644 --- a/packages/yjs-rich-markdown/test/yxml-proxy.test.ts +++ b/packages/yjs-rich-markdown/test/yxml-proxy.test.ts @@ -130,7 +130,11 @@ describe('YXml proxy', () => { nodeName: 'example', children: [ { kind: 'text', text: 'this is an example ' }, - { kind: 'element', nodeName: 'nestedExample', children: [{ kind: 'text', text: 'this is a nested example' }] }, + { + kind: 'element', + nodeName: 'nestedExample', + children: [{ kind: 'text', text: 'this is a nested example' }], + }, { kind: 'text', text: ' This is another example' }, ], }, @@ -160,7 +164,11 @@ describe('YXml proxy', () => { nodeName: 'example', children: [ { kind: 'text', text: 'this is an example ' }, - { kind: 'element', nodeName: 'nestedExample', children: [{ kind: 'text', text: 'this is a nested example' }] }, + { + kind: 'element', + nodeName: 'nestedExample', + children: [{ kind: 'text', text: 'this is a nested example' }], + }, { kind: 'text', text: ' This is another example' }, ], }, @@ -213,7 +221,11 @@ describe('YXml proxy', () => { nodeName: 'example', children: [ { kind: 'text', text: 'this is an example ' }, - { kind: 'element', nodeName: 'nestedExample', children: [{ kind: 'text', text: 'this is a nested example' }] }, + { + kind: 'element', + nodeName: 'nestedExample', + children: [{ kind: 'text', text: 'this is a nested example' }], + }, { kind: 'text', text: ' This is another example' }, ], }, @@ -227,7 +239,8 @@ describe('YXml proxy', () => { const children = proxy.children({ node: example }) const [beforeText, boldNode, afterText, sameNestedExample, sameSecondText] = children - if (!beforeText || !boldNode || !afterText || !sameNestedExample || !sameSecondText) throw new Error('Expected wrapped children') + if (!beforeText || !boldNode || !afterText || !sameNestedExample || !sameSecondText) + throw new Error('Expected wrapped children') expect(proxy.text({ node: beforeText })).toBe('this is an ') expect(proxy.toString({ node: boldNode })).toBe('example') @@ -252,7 +265,11 @@ describe('YXml proxy', () => { nodeName: 'paragraph', children: [ { kind: 'text', text: 'prefix ' }, - { kind: 'element', nodeName: 'italic', children: [{ kind: 'text', text: 'nested example text' }] }, + { + kind: 'element', + nodeName: 'italic', + children: [{ kind: 'text', text: 'nested example text' }], + }, { kind: 'text', text: ' suffix' }, ], }, @@ -411,8 +428,12 @@ describe('YXml proxy', () => { proxy.insertText({ node: afterText, index: 0, text: ' ' }) expect(proxy.toString({ node: extraItalic })).toBe('!') - expect(proxy.toString({ node: italic })).toBe('alpha beta! gamma') - expect(proxy.toString()).toBe('alpha beta! gamma') + expect(proxy.toString({ node: italic })).toBe( + 'alpha beta! gamma', + ) + expect(proxy.toString()).toBe( + 'alpha beta! gamma', + ) }) test('sets and removes attributes on element refs', () => {