Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ CHANGELOG.md
pnpm-lock.yaml

**/node_modules
**/.svelte-kit
**/.vercel
**/dist
**/build
**/test-results
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [
{
"files": ["*.svelte"],
"options": {
"bracketSameLine": false
}
},
{
"files": ["README.md", "**/README.md", "**/package.json"],
"options": {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@js-temporal/polyfill": "^0.5.1",
"@types/node": "^24.12.0",
"prettier": "^3.2.4",
"prettier-plugin-svelte": "^3.4.0",
"publint": "^0.3.12",
"typescript": "^7.0.2",
"vite": "8.3.0",
Expand Down
23 changes: 23 additions & 0 deletions playground/uneval-stream/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
node_modules

# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build

# OS
.DS_Store
Thumbs.db

# Env
.env
.env.*
!.env.example
!.env.test

# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
1 change: 1 addition & 0 deletions playground/uneval-stream/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
engine-strict=true
3 changes: 3 additions & 0 deletions playground/uneval-stream/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["svelte.svelte-vscode", "bradlc.vscode-tailwindcss"]
}
5 changes: 5 additions & 0 deletions playground/uneval-stream/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"files.associations": {
"*.css": "tailwindcss"
}
}
55 changes: 55 additions & 0 deletions playground/uneval-stream/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# unevalStream protocol lab

Interactive Svelte 5 playground for observing `devalue`'s streamed executable graph protocol.

Pretty formats blocks for display only, while Raw shows the exact source received over the wire.

## Setup and commands

Install once from the repository root:

```sh
pnpm install
pnpm --dir playground/uneval-stream dev
pnpm --dir playground/uneval-stream check
pnpm --dir playground/uneval-stream build
```

## Architecture

- `+page.svelte` owns immutable worker-message snapshots and the block timeline.
- `CodeEditor.svelte` embeds CodeMirror 6 with TypeScript mode and Mod-Enter execution.
- `stream.worker.ts` transpiles TypeScript to CommonJS, evaluates the user module, calls workspace `unevalStream`, and evaluates head/tail source in order in the worker realm.
- `snapshot.ts` turns the reconstructed graph into a structured-cloneable identity/reference tree without JSON serialization.
- `ObjectTree.svelte` renders recursive expandable nodes and aliases.

## User file contract

The editor is a single TypeScript CommonJS-transpiled module. It must provide a graph as its default export, and may provide an `unevalStream` replacer as a named `replacer` export. The replacer receives the value and a `js` template tag; interpolated values are serialized by devalue (preserving identity), and returning `undefined` falls back to the default serialization:

```ts
export class Point {
constructor(
readonly x: number,
readonly y: number
) {}
}

export const replacer = (
value: unknown,
js: (strings: TemplateStringsArray, ...values: unknown[]) => unknown
) => {
if (value instanceof Point) return js`new Point(${value.x},${value.y})`;
};

const graph = { answer: Promise.resolve(new Point(3, 4)) };
export default graph;
```

All other named exports are promoted onto the worker's global scope before head/tail blocks are evaluated, so custom constructor source emitted by the replacer (e.g. `new Point(…)`) can revive. The worker is discarded after every run, so promoted globals never leak across runs.

Static/dynamic imports and dependencies are not exposed to the user module.

## Security and limitations

Execution is isolated from the page in a dedicated module Web Worker and Stop terminates it. This is an isolation boundary for UI responsiveness, **not a hardened sandbox**: code can use worker-global browser APIs, consume CPU/memory, and initiate network requests allowed by the browser/CSP. Infinite synchronous work requires termination. TypeScript uses `transpileModule`, so it strips types but does not perform full project type-checking. Snapshots enforce depth/item/byte-preview limits and represent functions, symbols, and inaccessible properties as markers.
20 changes: 20 additions & 0 deletions playground/uneval-stream/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://shadcn-svelte.com/schema.json",
"tailwind": {
"css": "src/app.css",
"baseColor": "neutral"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks",
"lib": "$lib"
},
"typescript": true,
"registry": "https://shadcn-svelte.com/registry",
"style": "nova",
"iconLibrary": "remixicon",
"menuColor": "default",
"menuAccent": "subtle"
}
43 changes: 43 additions & 0 deletions playground/uneval-stream/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "uneval-stream",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"dependencies": {
"devalue": "workspace:*"
},
"devDependencies": {
"@codemirror/lang-javascript": "^6.2.4",
"@codemirror/language": "^6.12.4",
"@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.6",
"@fontsource-variable/roboto": "^5.3.0",
"@internationalized/date": "^3.12.3",
"@lezer/highlight": "^1.2.3",
"@sveltejs/adapter-vercel": "^6.3.4",
"@sveltejs/kit": "^2.63.0",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"@tailwindcss/vite": "^4.3.0",
"bits-ui": "^2.18.1",
"clsx": "^2.1.1",
"prettier": "^3.9.6",
"remixicon-svelte": "^0.0.5",
"shadcn-svelte": "^1.5.0",
"svelte": "^5.56.1",
"svelte-check": "^4.6.0",
"tailwind-merge": "^3.6.0",
"tailwind-variants": "^3.3.1",
"tailwindcss": "^4.3.0",
"tw-animate-css": "^1.4.0",
"typescript": "^6.0.3",
"vite": "^8.0.16"
}
}
Loading
Loading