SQL Runtime
Interactive SQL learning via client-side execution using SQL.js (SQLite compiled to WebAssembly). Students run SQL queries in the browser against teacher-uploaded database files.
Architecture
- SQL.js: Loaded from CDN via script tag (avoids Next.js 16 + Turbopack build issues)
- Database Caching: Map-based cache allows multiple databases on the same page
- No Server Execution: All SQL runs in the browser — secure, scalable
- File Storage: Databases stored as regular skript files with content-addressed deduplication
Key Files:
src/lib/sql-executor.client.ts— SQL.js integration and query executionsrc/components/public/code-editor/index.tsx— Interactive SQL editor with multi-file supportsrc/components/markdown/markdown-renderer.tsx— SQL editor rendering in markdownsrc/lib/remark-plugins/code-editor.ts— Transforms code blocks to interactive editorssrc/lib/file-storage.ts— Database file upload/retrieval, public access support
Usage in Markdown
Basic SQL editor:
```sql editor db="netflix.db"
SELECT * FROM tv_show LIMIT 10;
```
With explicit schema image:
```sql editor db="world_bank_indicators.db" schema-image="world_bank-schema"
SELECT country_code, indicator_value FROM indicators WHERE indicator_code = 'NY.GDP.MKTP.CD';
```
Database File Management
- In page editor, drag database file (.db, .sqlite) over CodeMirror editor
- Select "Insert SQL editor" from popup
- File uploaded to skript storage with content-addressed hashing
- Markdown references DB by filename (e.g.,
db="netflix.db")
File Resolution: system resolves filename to file URL via fileList lookup. Supports .db and .sqlite (tries both if renamed). Public access automatically granted for published skripts.
Schema Visualization
Naming convention: {database-name}-schema.excalidraw.{light|dark}.svg. System auto-detects and displays the theme-appropriate schema next to the SQL editor.
Example for netflix.db:
netflix-schema.excalidraw.light.svgnetflix-schema.excalidraw.dark.svg
Query Features
- Default LIMIT 100 on SELECT queries with no LIMIT specified
- Tables rendered in canvas panel, multiple result sets, execution time displayed
- "No rows returned" warning for empty results
- Multiple Databases: each editor independently loads its specified DB; map-based caching prevents conflicts
Multi-File Support
All code editors (Python, JavaScript, SQL) support multiple files with language-appropriate extensions.
- Default:
main.sql, new files:file2.sql, etc. - Add:
+button. Rename: double-click. Remove:X(can't remove last file).
Persistent User Data
IndexedDB stores per-editor state (code, active file, font size, width, transform). Auto-save debounced. Version history with manual snapshots.
Reset behavior: restores original markdown content; detects markdown changes vs. cached data; preserves settings when markdown unchanged.
Versions: manual labels, auto-version every 100 keystrokes, restore/delete previous, filter autosaves.
Security
- DB files in published skripts are publicly accessible (no auth required for students)
- Authors retain full file control
- Content-addressed storage (SHA256), auto-dedup, extension mismatch handling, immutable caching (1yr max-age, hash-based ETag)
Implementation
SQL.js loading:
script.src = 'https://sql.js.org/dist/sql-wasm.js'
locateFile: (file) => `https://sql.js.org/dist/${file}`
Caching:
const databaseCache = new Map<string, SqlJsDatabase>()
Query execution:
export async function executeSqlQuery(query: string, dbPath: string): Promise<SqlExecutionResult> {
const database = databaseCache.get(dbPath)
if (!database) throw new Error('No database loaded')
const queryWithLimit = applyDefaultLimit(query)
const results = database.exec(queryWithLimit)
return { success: true, results, executionTime }
}
Markdown pipeline:
// ```sql editor db="netflix.db"``` → <code-editor data-db="netflix.db" ></code-editor>
const dbFile = markdownContext.fileList.find(f =>
f.name === db || f.name.replace(/\.(sqlite|db)$/i, '') === db
)
const dbUrl = dbFile?.url