A language where Markdown is syntax, not decoration — .ssc files are
executable documents combining YAML front-matter, Markdown prose, and
Scala 3 code blocks.
.ssc files support two code-block languages:
| Annotation | Language | Backends |
|---|---|---|
```scalascript |
ScalaScript dialect — effects, handlers, content helpers, TCO | interpreter · JS transpiler · JVM · Rust (native binary; up to R.5 web toolkit) · WebAssembly |
```ssc |
Alias for scalascript |
interpreter · JS transpiler · JVM · Rust |
```scala |
Standard Scala 3 — no ScalaScript extensions | interpreter · Scala.js (JS) · JVM · Rust (passthrough) |
```rust |
Standard Rust — passthrough verbatim into the emitted Cargo crate | Rust |
---
name: hello
version: 1.0.0
---
# Hello World
```scalascript
def greet(name: String): String = s"Hello, $name!"
println(greet("World"))
```
Hello, World!
After installing, every .ssc file is a first-class executable:
$ ssc hello.ssc
Hello, World!
# The shebang line (#!/usr/bin/env ssc) lets you run files directly:
$ chmod +x hello.ssc && ./hello.ssc
Hello, World!
# Watch mode — re-runs on every save:
$ ssc watch hello.sscRequirements: scala-cli · Node.js (for JS backend) · sbt (optional, for library use)
git clone https://github.com/sergey-scherbina/scalascript
cd scalascript
# Standalone install, once releases are published:
cs install ssc --channel https://releases.scalascript.io/coursier.json
# Contributor checkout only: builds and stages local bin/ launchers.
./install.sh --dev
# Pipe sops-decrypted secrets into a script (${sops:key} references in databases:)
sops -d secrets.enc.yaml | ssc myapp.ssc
# Interpreter (tree-walking, no compilation step)
bin/ssc examples/hello.ssc
# Watch mode — re-run on every file change
bin/ssc watch examples/hello.ssc
# Interactive REPL
bin/ssc repl
# Transpile to JavaScript and run via Node.js
bin/jssc examples/hello.ssc
# Compile to JVM bytecode and run via Scala 3 / scala-cli
bin/sscc examples/hello.ssc
# Compile to a native binary via Rust + Cargo (requires `cargo` on PATH;
# see docs/rust-backend.md for the capability surface).
bin/ssc build-rust examples/hello.ssc && ./hello
bin/ssc run-rust examples/hello.ssc
# Run on Apache Spark (Spark 4.0.0 / Scala 3.7.1, local[*] by default)
bin/ssc-spark examples/spark-encoder-demo.ssc
# Run all examples
./examples/run-all.sc
# Start HTTP server for the examples browser
bin/http.sscFull categorized index of all docs: docs/README.md.
Getting started
| Project Summary | One-page overview — what ScalaScript is, the five backends, and the headline capabilities |
| User Guide | Installation, CLI commands, language basics, HTTP, effects, actors, Apache Spark, WebAssembly, frontend frameworks, cluster, x402 — practical day-to-day reference |
| Tutorial 1 — Todo API | Build a todo API step by step — data model → REST → auth → WebSocket → TLS → MCP |
| Tutorial 2 — Spark ETL | End-to-end Spark pipeline — Dataset[T] → @SqlFn UDF → @TempView → Delta Lake |
| Tutorial 3 — Frontend Toolkit demo | Compile + emit + serve a toolkit SPA through React / Vue / Solid / Custom + SSR via ssc serve |
| Tutorial 4 — Full-stack .ssc | SQLite + REST API + reactive runtime/std/ui frontend in one .ssc file — databases:, sql blocks, serve(lower(tree, theme), port) |
Language reference
| Language Specification | Formal grammar, type system, semantics, all language constructs |
| Direct Syntax | Do-notation over any monad — direct[M] { x = expr }, .! postfix bind, effect-row unions |
| Coroutines & Generators | Coroutine primitive underlying one-shot effects and generators |
| Algebraic Effects spec | Typed effect rows — ! operator, multi effect, Rémy-style unification, typed stdlib, Reader[R], NonDet |
| Error Handling | Checked errors via throws[A, E], attemptCatch, HasStackTrace |
| Metaprogramming / v2 macros | inline, derives, compiletime.*; partially implemented restricted quoted macros for ssc link, interpreter ssc run, and the JVM + JS backends, incl. Expr.asValue match compile-time constant folding, with targeted unsupported-body diagnostics; interpreter Mirror.Of[T] and user derived(m: Mirror) typeclasses |
| Markdown Content Introspection | Markdown-to-frontend content layer: Markdown/YAML/GFM tables/fenced language blocks lower to std/ui through contentToolkitNode() or explicit contentToolkitBlock(id) / contentToolkitSection(id) selectors, including declarative yaml @ui=toolkit controls, Markdown-authored tables as TableNode, contentData(id) lookup, explicit contentComponent(...) registries for component=<name> / data=<id> metadata, explicit contentBind(value, bindings) resolution for Markdown ${name} placeholders, and cross-backend std/content lookup helpers contentCurrentSection(), contentSection(id), contentBlock(id), contentMetadata(path), contentPlainText(value), contentToMarkdown(value), plus direct imported module content namespaces via contentModule(namespace) for interpreter, JS, and JVM targets; .scir and .sscc preserve the current-module DocumentContent snapshot when present; low-level contentView(contentDocument()) remains available |
| Markdown Content Linked Namespaces | Focused contract for reusing Markdown-authored sections, blocks, data, and content: metadata from direct .ssc imports through contentModules() and namespace-scoped lookup helpers |
| Markdown Content Native Client Parity | Same Markdown-authored toolkit controls render through Swing, JavaFX, and SwiftUI native frontends by normalizing shared std/ui/lower View.Element output into native text fields, checkboxes/toggles, buttons, and signal state |
| DSL Authoring | Parser combinators, multi-pass pipelines, runtime/std/parsing/* |
Platform & runtime
| Architecture | Compiler pipeline, module structure, backend SPI |
| Target Backends | Interpreter · JS transpiler · JVM · Rust (Cargo crate → native binary) — capabilities and tradeoffs |
| Rust backend guide | ssc emit-rust / build-rust / run-rust — compile .ssc to a native binary via Cargo, with mixed ```rust fence blocks |
| Actors & Distributed | Spawn, supervise, cluster over WebSocket |
| Distributed Wire Protocol | Planned: opt-in JSON/MsgPack/CBOR internal wire layer for actors, Dataset/DStream, typed route clients/RPC, object sync, security, compression, and compatibility |
| Distributed Runtime | Planned: canonical local/remote/distributed runtime model for streams, actors, typed async calls, cluster lifecycle, cluster operations, code deployment, and worker bundles |
| Dataset / MapReduce | Dataset[T] — local, parallel, distributed |
| Apache Spark backend | Spark 4 + Scala 3.7.1: Dataset[T], sql blocks, @SqlFn UDFs, Structured Streaming, Delta Lake, Hive catalog, MLlib — see §13 of the User Guide |
| Frontend Framework SPI | React / Vue / Solid / custom frontend backends; design doc has historical planning context |
| Frontend Toolkit | High-level declarative widgets: Forms, Routing, Widgets v2, Table |
| Cluster Management | Bully election, phi-accrual failure detector, federation, Raft, ZooKeeper client |
| x402 micropayments | HTTP 402 protocol, Ethereum + Cardano flows, MCP × x402 paid tools |
| Bank Rails v1.54 | SEPA CT/DD, ACH, Pix, FedNow — BankRailsProvider SPI, mandate lifecycle, async settlement |
| International Bank Rails v1.55 | SWIFT MT103 + pacs.008, SEPA Instant, UK FPS/BACS/CHAPS, India UPI, Japan Zengin, Singapore PayNow |
| Browser SQL | Cross-backend sql fenced blocks (JS / Node / Wasm / JVM) |
| Electron SQL | Current sqlite: behavior in Electron desktop bundles, including the localStorage-backed renderer fallback |
| Electron Persistence Bridge | Main/preload bridge for durable Electron SQLite under app.getPath("userData") |
| Secret Resolvers | ${env:} · ${file:} · ${sops:} · SecretResolver SPI for Vault / AWS SM / GCP / Doppler / 1Password |
| MCP Support | MCP server tools + resources, MCP client |
| Rozum / Agent SDK | Generic app-owned agent loop over a stateless OpenAI-compatible rozum gateway, with strict tool schemas and in-process tool handlers |
| Rozum Agent Streaming | runAgentStream / collectAgentStream for OpenAI-compatible SSE text/tool-call deltas with ordered AgentEvents |
| Rozum Agent Endpoint Pool | AgentEndpointPool + runAgentPool / streaming pool variants for bounded ordered failover across multiple rozum gateways |
| Rozum Agent Schema Derivation | AgentSchema[A] derives + agentToolFor[A] for typed tool inputs with OpenAI-compatible JSON Schema parameters |
| Markdown as Syntax | How Markdown constructs map to AST nodes |
| SwiftUI / iOS / macOS | Native Swift Package emission; ssc run --target ios / --target macos; ssc package + ssc publish to App Store / TestFlight |
| GraalVM native binary | ssc native binary via GraalVM native-image; no-JVM distribution; ssc-plugin-host.jar bridge |
| Native plugin guide | Compile a plugin to a native binary — fully JVM-free ssc → plugin |
Planned / partial
| Blockchain SPI | Draft / planned, not fully implemented: pluggable chain abstraction below wallet and x402 support |
| Electron JVM REST Backend | Partially implemented: explicit ssc run --frontend electron --backend jvm-rest, ssc run --target desktop-jvm, plain ssc run for frontend: electron full-stack sources, server-only ssc run --mode server --backend jvm, Electron client-only, and web client preview via ssc run --mode client --frontend react --server-url ... with opt-in --open-browser plus --host/--port preview binding; packaging and runtime-level JVM bind-host support remain planned |
| Full-stack in-process transport | Planned, partially implemented: BackendTransport types, `ssc run --transport http |
| JVM Desktop Frontend | Partially implemented: frontend-swing backend, ServiceLoader registration, ssc run-jvm --frontend swing, static Swing toolkit subset emission, local signal action bridge, Swing fetchAction / dataTable / typed route client backend dispatch in the same JVM process, no-socket full-stack examples, and a swing-plugin skeleton for future interpreter intrinsics; JavaFX/Compose adapters remain planned |
| Typed route clients | Planned, partially implemented: apiClients: / api-clients: front-matter endpoint metadata parses into AST and codegen metadata; JVM/Swing generates callable in-process client methods, and JS/browser/Electron bundle codegen emits Promise-returning HTTP clients over fetch using --server-url / __sscBackendBaseUrl; awaitClient(...) gives client-side ScalaScript code a small await bridge; generated clients call a stable typed JSON codec facade sourced from backend/typed-data; JVM/Swing typed clients use JsonCodec[T], and JS/browser/Electron typed clients use generated runtime codec metadata for known case-class/enum shapes |
| Contract validation | Planned, not implemented yet: shared OpenAPI/GraphQL validation model for route/resolver signatures, request/response bodies, typed errors/status codes, profiles, overlays/imports, CLI checks, compatibility diffs, and contract tests |
| Typer real types roadmap | Planned, partially implemented foundation: reduce accidental Any in exported symbols/IR and carry structured type evidence through case classes, enums, generics, routes/remotes, OpenAPI/GraphQL schemas, Dataset/Spark mapping, and plugin metadata |
| Client/server object store | Complete: JS/browser/Electron IndexedDb.store[A] typed local client store, JVM/JDBC ObjectStore, generated JVM REST sync routes, Sync.pull/push/sync[A], durable queued Sync.put/remove[A], explicit Sync.conflicts/resolve[A], generated JVM conflict policies (manual / server-wins / client-wins), and Sync.status / Sync.isOnline / Sync.isSyncing UI helpers |
| Graph storage | Planned, partially implemented: graphs: front matter parses into AST/IR; JVM codegen exposes Graph.* over in-memory, embedded TinkerGraph property, and RDF4J memory RDF stores plus Sparql.select over RDF4J; the interpreter exposes Graph.* over in-memory property/RDF storage. Production adapters such as Neo4j, JanusGraph/TinkerPop providers, and RDF4J-compatible servers remain planned |
| Typed data mapping | Planned, partially implemented: backend/typed-data owns the shared emitted typed JSON facade for generated route clients plus JsonValue / DecodeError / JsonCodec[A], explicit object helpers, JsonFieldSpec rename/default/key/unknown-field helpers, derives JsonCodec for case classes/sealed ADTs, schema annotations for derived JVM/interpreter product codecs, schemas: front-matter metadata for interpreter typed SQL, initial RowValue / RowFieldSpec / RowCodec[A] support for simple case-class rows and explicit JVM column metadata, initial ObjectValue / ObjectFieldSpec[A] / ObjectCodec[A] support for portable IndexedDB/ObjectStore document shapes, VertexValue / EdgeValue / RdfValue plus VertexCodec[A] / EdgeCodec[A] / RdfCodec[A] for graph/RDF mapping, DatasetCodec[A] for local/MapReduce Dataset element serialization and distributed worker partition payloads, DatasetWire JSON/MsgPack/CBOR envelope + chunk helpers for DatasetWirePartition, std/mapreduce runDistributedWire and runDistributedShuffleWire for direct DatasetWirePartition actor/shuffle payloads, DistributedDataset.encode/decode[A] typed boundary helpers, DistributedDataset.run/runShuffle[A, B] actor-effect wrappers, and SparkSchemaCodec[A] for Spark-like schema metadata using the same field annotations; JS/browser/Electron IndexedDb.store[A] typed local object storage, and JVM/JDBC ObjectStore storage through ObjectCodec[A]; SqlRuntime.query/insert/update[A] use RowCodec[A]; interpreter and JVM codegen expose Db.query/insert/update[A] for typed SQL reads and writes; SparkGen typed readers use SparkSchemaCodec[A] metadata when present and alias external column names back to Scala field names before .as[T]; richer cross-store convergence remains planned |
| Distributed binary wire protocol | Planned, partially implemented: opt-in wire: config with JSON fallback plus MsgPack/CBOR profiles for internal ScalaScript distributed actors, Dataset/DStream, typed route clients/RPC, WebSocket subscriptions, object sync, compression, integrity, and future schema evolution. Implemented pieces include shared WireEnvelope, actor binary WS, typed RPC binary HTTP negotiation, and DatasetWire JSON/MsgPack/CBOR partition envelopes with chunking |
| Distributed runtime | Planned, partially implemented: local / remote / distributed placement vocabulary, named operation registry, code identity, stream bridges, typed actor remote spawn, ! Async remote calls, cluster runner UX, token rotation, rolling upgrades, and cluster-aware deployment. Implemented pieces include stream bridge basics, typed actor refs/remote spawn, ClusterCapability, static SeedResolver, interpreter code identity checks, typed cluster: / registry front-matter metadata, interpreter remoteHandlers: lowering with in-process Remote.function plus HTTP JSON fallback routes, explicit Remote.http[A, B](url) calls, path-based Remote.stub(baseUrl) HTTP stubs, and generated RemoteRpc typed HTTP client metadata for path: remote handlers |
Dataset/MapReduce binary wire now reaches actor messages directly: wireFormat = "msgpack" | "cbor" sends DatasetWire envelope bytes for partition, shuffle-bucket, and key-result messages, while JSON keeps the object-message fallback.
All three backends support scalascript blocks. scala blocks (standard
Scala 3) are supported by the interpreter and JVM backend; the JS backend
compiles them via Scala.js.
| Feature | Syntax |
|---|---|
| Values and variables | val x = 42, var n = 0 |
| Functions | def f(x: Int): Int = x * 2 |
| Lambdas and closures | val double = (x: Int) => x * 2 |
| Higher-order functions | xs.map(double), xs.filter(_ > 0) |
| Default parameters | def f(x: Int, step: Int = 1), also on class/enum constructors |
| Case classes | case class Point(x: Double, y: Double) |
| Enums / sealed traits | enum Color { case Red; case Green; case Blue } |
| Recursive ADTs | enum Tree { case Leaf(v: Int); case Branch(l: Tree, r: Tree) } |
| Pattern matching | x match { case Some(n) => n; case None => 0 } |
| For comprehensions | for x <- xs if x > 0 yield x * x |
| While loops | while n > 0 do { ... } |
| Collections | List, Map, Option, Set, Vector, Array, LazyList, Seq, IndexedSeq, Iterable — full method dispatch |
| Real collection semantics | The interpreter models true Scala semantics — Array is mutable with reference identity (a(i) = x), LazyList is lazy (#:: defers; infinite streams work), Vector is a distinct indexed type with O(log₃₂ n) access; constructors Seq/Vector/Array/IndexedSeq/Iterable/LazyList(...) + .toSeq/.toVector/.toArray/.toList/.toLazyList conversions |
| Tuples | val t = (1, "hello"); t._1 |
| Bitwise operators | a & b, a | b, a ^ b, a << n, a >> n, a >>> n, ~a on Int |
| String interpolation | s"Hello, $name", md"..." (strips indent) |
| Math | math.sqrt, math.abs, math.pow, math.Pi, … |
| Extension methods | extension (n: Int) def squared: Int = n * n |
| Typeclasses | trait Show[A], given, summon[Show[Int]], context bounds |
| Higher-kinded types (HKT) | trait Functor[F[_]], sealed dispatch, auto-resolution |
| Standard typeclasses | Functor, Applicative, Monad, Foldable, Traversable, Either, Eq, Show, Hash, Order, Semigroup, Monoid, Bifunctor, MonadError, Selective |
| Recursion | factorial, Fibonacci, tree traversal |
| Tail-call optimisation | self-TCO and mutual TCO — no @tailrec required |
Case-class .copy |
p.copy(field = newValue, ...) |
| List / map literals | [1, 2, 3] → List(…), ["k" -> v, ...] → Map(…), [] → List() — compact sugar in .ssc code blocks |
| Feature | Syntax |
|---|---|
| Lenses | Focus[T](_.a.b) → Lens with get / set / modify / andThen |
| Prisms | Prism[Sum, Variant] → getOption / set / modify / reverseGet |
| Optionals | Focus[T](_.maybe.some.field) → Optional for paths through Option fields |
| Traversals | Focus[T](_.items.each.field) → Traversal — multi-foci getAll / modify / set |
| Feature | Syntax |
|---|---|
| Algebraic effects | effect E:, handle(body) { case E.op(arg, resume) => ... }, multi-shot |
| Typed effect rows | def foo(): A ! Logger — effect appears in function type; closed row (no !) = total/pure |
multi effect |
Multi-shot effects — continuation can be resumed many times |
Reader[R] capability |
Context-injection effect: Reader.get, runReader(value)(body) |
NonDet multi-shot |
Nondeterministic branching via multi-shot continuations |
EffectAnalysis |
Compile-time error for unhandled effects (not just a warning) |
| Standard effects — Logger | Logger.info(msg), runConsoleLogger, runTestLogger |
| Standard effects — Random | Random.nextInt(n), runSeededRandom(seed) |
| Standard effects — Clock | Clock.now(), Clock.millis(), runSystemClock |
| Standard effects — State | State.get, State.set(v), State.modify(f), runState(init) |
| Standard effects — Env | Env.get(key), runEnv(map) |
| Standard effects — Http | Http.get(url), Http.post(url, body), runHttpClient |
| Standard effects — Retry | Retry.attempt(n)(body), runRetry(policy) |
| Standard effects — Cache | Cache.getOrSet(key)(body), runCache |
| Standard effects — Tx | Tx.begin, Tx.commit, Tx.rollback, runTx |
| Standard effects — Auth | Auth.check(claims), runAuth(verifier) |
| Direct syntax (do-notation) | direct[M] { x = expr; y = expr2 }, .! postfix bind |
| Effect-row unions | direct[Async | Random] { ... } |
Built-in Async effect |
runAsync { Async.delay(ms); Async.parallel(...) } |
Real-thread runAsyncParallel |
genuine JVM concurrency without touching call sites |
Built-in Storage effect |
runStorage { Storage.put(k, v); Storage.get(k) } — JSON file-backed or ephemeral |
| Coroutines | coroutineCreate, coroutineResume, suspend, Step[Y,T] ADT, coroutineCancel |
| Generators | generator[T] { yield(v) }, fromGenerator, streaming interop |
| Reactive signals | Signal(0), s.get / s.set(v), computed { … }, effect { … } with diamond-dedup flush |
| Free monad | Free[F,A], liftF, foldMap, runM — in runtime/std/free.ssc |
| Feature | Syntax |
|---|---|
| Local actors | spawn, self(), pid ! msg, receive { case ... }, link, exit, supervision |
| Distributed actors | actor cluster over WS, bully leader election, Phi-accrual FD, gossip, membership events |
| Typed actor refs + named remote spawn | ActorRef[M], ref.tell(msg), ref.address, ref.isLocal, registerBehavior, spawnRemote over the current JSON actor WS control channel |
| Cluster capability + code identity | clusterOf, SeedResolver.staticList, codeIdentity, assertCodeIdentity for typed cluster snapshots and deterministic SHA-256 code checks |
| Remote handler registry | remoteHandlers:, @remote(...) def, and simple remote def declarations lower to an interpreter RemoteHandlerRegistry; Remote.function[A, B](name).call(value) uses the in-process path, path: entries expose POST HTTP JSON fallback routes, Remote.http[A, B](url) calls those routes explicitly, remoteStub[Api](baseUrl) / Remote.stub[Api](baseUrl) provide a path-based HTTP JSON fallback stub, and RemoteClientDeriver exposes path: handlers as generated RemoteRpc typed HTTP client metadata for JS/JVM codegen. Generated trait methods, async effect-row lowering, WebSocket/internal-wire transport, and binary WireCodec[A] negotiation are planned |
| Feature | Syntax |
|---|---|
| HTTP server | route(method, path)(handler), serve(port), Request/Response |
| REPL web mode | :serve/:stop/:clear/:mount/:load/:reload/:unmount/:routes/:http/:call — mount handlers and test routes interactively; :set errorDetails true|false |
| HTTP streaming | streamResponse, SSE via sse(req) |
| HTTP middleware | CORS, gzip, cache headers, /_health / /_ready, /_openapi.json / /_swagger with typed response schemas for front-matter/generated JVM routes when apiClients: metadata is available; ssc emit-openapi exports the same document as JSON/YAML without starting a server; @openapi(...) adds per-route summary/description/tags/deprecated/security metadata and openApiSecurity(...) declares OpenAPI security schemes |
| WebSocket server | onWebSocket(path), ws.send/recv/close/ping, rate limiting, per-route maxConnections |
| TLS | tls("cert.pem", "key.pem"), serve(443, tls=...), wss:// |
| HTTP client | httpGet/httpPost, httpClient { }, httpGetStream for SSE/LLM streaming |
| WebSocket client | wsConnect(url) { ws => }, wss:// |
| REST ergonomics | jsonParse/jsonStringify/jsonRead, req.json, JsonValue, validate { }, middleware |
| Typed handlers | CaseClass => CaseClass auto-deser (path/query/body) + auto-ser (JSON 200); Either[Request, Input] for explicit error handling |
| SQL databases | databases: front-matter declares named JDBC connections; ```sql ``` fenced blocks execute DDL/DML; ```transaction ``` fenced blocks run multiple ;-separated statements atomically (JDBC transaction, commit/rollback); Db.query/execute for programmatic access; SQLite, H2, PostgreSQL out of the box |
| XML markup | ```xml ``` fenced blocks parse well-formed XML 1.0 into Value.MarkupV(doc: Markup.Doc); ${expr} interpolation with automatic XML escaping; bound as <section>.xml; zero-dependency PureMarkupCodec parser |
| XSLT transformation | MarkupCodec.default.transform(doc, xslt, params) applies an XSLT 1.0 stylesheet to a Markup.Doc; returns Either[TransformError, Markup.Doc]; parameter substitution via Map[String, String]; JVM / interpreter only (Feature.Xslt); see examples/xslt-transform.ssc |
| Secret resolution | ${env:VAR}, ${file:/run/secrets/pw}, ${sops:key.path} in database URLs/credentials; SecretResolver SPI for Vault, AWS SM, GCP SM, Doppler, 1Password and more |
| Progressive Web App | pwa(name, themeColor, icons, precache) — registers GET /manifest.json + GET /sw.js; cache-first precaching service worker; works in ssc run and ssc run-jvm |
Planned, not implemented yet:
| Feature | Planned shape |
|---|---|
| Full-stack in-process transport | ssc run and ssc run-jvm recognize `--transport http |
| JVM desktop frontend | ssc run-jvm --frontend swing app.ssc launches the JDK-only Swing runtime in the current JVM process; ssc run-jvm --frontend swing --transport in-process is accepted as the monolithic JVM mode foundation; Swing fetchAction can call generated JVM backend routes in-process; ssc run --frontend swing remains the interpreter path and reports that Swing intrinsics are planned; JavaFX/Compose adapters remain planned |
| Typed route clients | apiClients: / api-clients: front matter preserves endpoint method/path/request/response metadata in AST and JVM/JS codegen. JVM/Swing generates callable in-process clients that encode case-class/ADT inputs through JsonCodec[T], dispatch through generated BackendTransport, and decode typed JSON responses through JsonCodec[T]. JS/browser/Electron bundle codegen emits Promise-returning HTTP clients over fetch and passes request/response type names through the shared typed JSON facade; generated JS case-class/enum metadata decodes known response shapes back into generated JS values. Client-side ScalaScript can use awaitClient(Messages.list()), which lowers to JS await and enables the needed async wrapper. emit-spa --server-url, web client mode, and Electron JVM REST dev mode can route those relative calls to a JVM backend. examples/frontend/typed-client-distributed/ demonstrates one source running as backend on one machine and client on another |
| Client/server object storage | JS/browser/Electron code can use IndexedDb.store[A]("store", "dbName", "keyField") for Promise-based typed local object storage, awaited with awaitClient(...); native IndexedDB is used when available, with a lightweight fallback for Node/tests. JVM code can use ObjectStore.put/get/all/delete/changes[A](dbName, store, ...) over the declared JDBC database. JVM codegen emits typed GET /__ssc/sync/{store}/changes and POST /__ssc/sync/{store}/push routes for objectStores: entries with sync: client-server. JS clients call Sync.pull/push[A] or the combined Sync.sync[A]; Sync.put/remove[A] persist local queued mutations for offline-friendly pushes. Conflicts are listed with Sync.conflicts, resolved with Sync.resolve[A], or handled automatically by generated JVM conflict: policies. Sync.status(store, db?) returns { pending, conflicts, lastPulled, lastPushed, isSyncing } for sync-status badges; Sync.isOnline and Sync.isSyncing(store, db?) give per-store and network availability signals |
| Graph storage | backend/graph now provides GraphCapabilities, PropertyGraphBackend, RdfGraphBackend, GraphBackend, GraphRuntime.inMemory(), GraphRuntime.tinkerGraph(), GraphRuntime.rdf4jMemory(), and GraphRuntime.sparqlSelect() for portable JVM property/RDF storage through typed codecs plus RDF4J SPARQL SELECT. graphs: front matter survives AST/IR/.sscc; JVM codegen emits a typed Graph.* facade for declared in-memory, embedded-tinkergraph, and rdf4j-memory graph stores plus Sparql.select for RDF4J-backed stores, and runtime/std/graph-plugin mirrors the portable in-memory facade for ssc run. Production adapters such as Neo4j, JanusGraph/TinkerPop providers, and RDF4J-compatible repositories remain planned |
| Typed data mapping | backend/typed-data now provides the shared emitted typed JSON facade for generated route clients plus explicit JsonValue, DecodeError, JsonCodec[A], JsonFieldSpec, RowValue, RowValueCodec[A], RowFieldSpec[A], RowCodec[A], ObjectValue, ObjectFieldSpec[A], ObjectCodec[A], VertexValue, EdgeValue, RdfValue, VertexCodec[A], EdgeCodec[A], RdfCodec[A], DatasetCodec[A], DatasetWire, and SparkSchemaCodec[A] helpers. derives JsonCodec supports case classes and sealed ADTs; derives RowCodec, derives ObjectCodec, derives VertexCodec, derives EdgeCodec, derives RdfCodec, and derives SparkSchemaCodec support simple case-class rows/documents/graph records/schema records. DatasetCodec[A] derives from JsonCodec[A] and now exposes partition payload helpers for distributed MapReduce worker movement; DatasetWire wraps those payloads in shared JSON/MsgPack/CBOR WireEnvelopes and chunks large partitions. std/mapreduce can move DatasetWirePartition payloads directly through runDistributedWire and runDistributedShuffleWire, with DistributedDataset.encode/decode[A] wrapping the typed boundary and DistributedDataset.run/runShuffle[A, B] wrapping the actor-effect calls for map and shuffle. SparkSchemaCodec[A] uses the same @fieldName, @key, and Option nullability conventions to produce Spark-like schema metadata, and SparkGen typed readers consume that metadata when it is in scope. Graph/RDF codecs add @graphLabel, @graphEdge, @graphFrom, @graphTo, @rdfClass, @rdfId, and @rdf. SqlRuntime.query/insert/update[A] use RowCodec[A], and interpreter/JVM codegen expose Db.query/insert/update[A] for typed SQL reads and writes. Broader cross-store migration helpers remain planned |
Dataset/MapReduce typed wire calls can select wireFormat = "msgpack" | "cbor" to send DatasetWire envelope bytes through actor partition, shuffle-bucket, and key-result messages.
| Feature | Syntax |
|---|---|
| Sessions + CSRF | req.session, withSession(Map(...)), csrfToken() / csrfValid(req) |
| JWT bearer tokens | jwtSign(Map(...)), jwtVerify(token), RS256 and HS256 |
| Password hashing | hashPassword/verifyPassword (PBKDF2) |
| TOTP 2FA | totpGenerateSecret, totpVerify(secret, code) |
| WebAuthn / passkeys | webAuthnRegisterChallenge, webAuthnVerify |
| OAuth2 | oauthAuthorizeUrl(...), oauthExchangeCode(...), oauthUserinfo(...), Google + GitHub presets |
| Feature | Syntax |
|---|---|
| MCP server | mcpServer { srv => srv.tool(...) }, serveMcp(Transport.stdio/Http/Ws) |
| MCP client | mcpConnect(url) { client => client.callTool(...) } |
| Feature | Syntax |
|---|---|
| Dataset / MapReduce | Dataset[T] with map/filter/flatMap/groupBy/reduceByKey/top/countByValue |
| Execution modes | runLocal, runParallel, runDistributed |
| Apache Spark | Same Dataset[T] source → sql fenced blocks · @SqlFn UDFs · Scala 3 native encoder derivation · Structured Streaming · Delta Lake · Hive metastore · MLlib pipelines |
| Feature | Syntax |
|---|---|
| Parser combinators | runtime/std/parsing/* — Parser ADT, ~/~>/~<, rep, opt, sep, error recovery, indentation-aware |
| Multi-pass pipelines | runtime/std/dsl/* — Pass[A,B], andThen/parallel/recover, Visitor, cata, ana |
| Metaprogramming | inline def/val/if/match, compiletime.constValue/summonInline/error. Restricted quoted macros run on the interpreter, JVM, and JS backends (MacroCodegen.expand pre-codegen pass), including Expr.asValue match compile-time constant folding and cross-module macro expansion, with targeted unsupported-body diagnostics; ssc check warns on interpreter-only macros. Mirror.Of[T] conformance + custom derived(m: Mirror) typeclasses (JVM + JS) |
| Derives | derives Eq/Show/Hash/Order/Foldable/Traversable/Functor; structural stdlib + custom derives cross-backend (JVM + JS) |
| Checked errors | throws[A, E] = Either[E, A], throwsRaw[A, E] = A | E, attemptCatch, HasStackTrace |
| Feature | Syntax |
|---|---|
| Package system | package: org.example.ui in frontmatter, namespaced exports, collision-safe imports |
| Module imports | [name](./lib.ssc) Markdown links bring definitions into scope; one pure Markdown paragraph may contain multiple import links |
| URL imports | [X](https://...) URL fetch, cached at ~/.cache/ssc/ |
| Dependency imports | [X](dep:org/lib:1.2) legacy source resolver, [X](dep:org:name:version) Coursier resolver, [X](jitpack:com.github.owner:repo:tag), [X](github:owner/repo@tag[#asset]), sha256: pins, ssc.lock |
| Package registry | ssc search / ssc info / ssc add against https://sergey-scherbina.github.io/scalascript/packages.yaml by default, overrideable with --registry or registry.url |
| Project scaffolding | ssc new my-app, `--template lib |
| Plugin system | .sscpkg format, essential bundled plugins auto-loaded from bin/lib/compiler/plugins, advanced bundled plugins available locally under bin/lib/compiler/plugin-available and enabled with --plugin / ssc plugin install, ~/.scalascript/registry.yaml |
| sbt integration | ScalascriptInteropPlugin, sscGenerateFacade, sscCompile, sscLink, sscTest, sscRun, sscRepl, sscWatch, sscBspSetup, sscBackends cross-build (emit JVM/JS/Rust/Wasm artifacts from one source), Phase 5 dependency resolution, src/main/scalascript/ source convention |
| Config system | config: front-matter, ```yaml config "name" fenced blocks, config.files: [...], typed derives Config, JsConfigEmitter, ScalaConfigEmitter — see specs/config-system.md |
| Separate compilation | ssc emit-interface, ssc emit-ir, ssc compile-jvm/compile-js, ssc link, ssc build --incremental, .scim/.scir/.scjvm/.scjs; .scir and .sscc preserve Markdown content snapshots when present |
| Feature | Syntax |
|---|---|
| Browser SPA target | ssc emit-spa file.ssc — same route() source runs as a single-page app |
| Web Components | ssc emit-wc, customElements.define |
| SSR + hydration | wc(tag, component, args*) declarative shadow DOM |
| Component library | runtime/std/ui/* — Button, Input, Select, Modal, Card, Spinner, Alert, DatePicker, Combobox, and more |
| Frontend Framework SPI | One .ssc source compiled to React, Vue 3, Solid, or a custom runtime via frontend-{react,vue,solid,custom} backends |
| Reactive primitives | Signal[T], ShowSignal (conditional render), ToggleSignal, ForSignal[T] (list render) — uniform semantics across all 4 frontend backends |
runtime/std/ui script toolkit |
Declarative widget DSL from a .ssc file — vstack/hstack, textField, checkbox, signalButton, actionButton, badge, spinner, card, modal, table, router + hashRouter, dataTable, fetchAction, and Markdown-driven contentToolkitNode() / contentToolkitBlock(id) / contentToolkitSection(id) / contentComponent(...) / contentData(id) / contentBind(value, bindings) / contentToolkitOptionsWithBindings(data) / contentSection(id) / contentBlock(id) / contentMetadata(path) / contentPlainText(value) / contentToMarkdown(value) / contentView(contentDocument()) with yaml @ui=toolkit controls (including {type: button, action: <id>} and {type: table, source: <id>} — with an optional inline columns: list of typed column specs (`kind: text |
| Native Markdown toolkit controls | Markdown/YAML @ui=toolkit controls lower through the same std/ui View pipeline for Swing, JavaFX, and SwiftUI native clients; see examples/frontend/markdown-native-controls/ |
NFC NDEF (std.nfc) |
nfcCapabilities, nfcPermissionStatus, readNdef, writeNdef, textRecord, uriRecord, mimeRecord; interpreter reports deterministic unsupported status, native Android/iOS/Web adapters are capability-gated by Feature.NfcNdef |
Rozum / agent loop (std.agent) |
runAgent, runAgentPool, runAgentStream, runAgentStreamPool, collectAgentStream, collectAgentStreamPool, AgentEndpoint, AgentEndpointPool, AgentSchema, AgentTool, agentToolFor, RunOptions, ToolResult, AgentEvent — OpenAI-compatible tool-call loop for stateless rozum gateways; apps own prompts, typed or explicit tool schemas, validation, streaming callbacks, failover policy, and side effects |
| Fetch primitives | fetchUrlSignal — live GET binding (v2: real fetch + headers); seedSignal — editable draft seeded from another Signal[String] until user input makes it dirty, supported by the browser runtime and frontend emitters including JVM desktop; fetchAction/fetchActionClear — POST/PUT/DELETE on button click with optional headers: Signal[String] for bearer tokens; dataTableView over any TableDataSource — staticRowsSource (in-memory rows, no fetch), signalRowsSource (reactive rows signal), or a bare fetch signal (Remote) — on interpreter, JVM, and JS browser backends alike; rowPostAction bodies via fieldPayload / wholeRowPayload / fieldsPayload; incSignal — manual refresh; emptyHeaders sentinel |
| Themes | defaultTheme (light) · darkTheme · custom Theme(ColorPalette, SpacingScale, TypographyScale, RadiusScale) |
| Frontend Toolkit (v1.18 B+ / B++ / C) | High-level declarative UI via Tk facade (sbt API) — vstack/hstack, card, textField, form with validators, router, modal/drawer/tabs, table. Backend-agnostic: lowers to React / Vue / Solid / Custom or to static HTML via Ssr.renderToHtml. |
| WebAssembly target | ssc emit-wasm file.ssc — scalascript blocks lowered to Wasm; cross-backend sql fenced blocks supported. Algebraic effects compile and run on Wasm (arithmetic, collection HOFs, multi-shot resume, cross-module) via a pure-Scala effect runtime with no preamble bloat. @wasm extern FFI + cross-module inlining + macro expansion are wired |
| Native Rust SSR server | serve(view, port) on the Rust backend (ssc build-rust) emits a self-contained tokio + hyper server with server-side rendering, a reactive signal store, computed-signal live recompute, Server-Sent Events push (/__ssc/events), and a direct WebSocket signal endpoint on port + 1 — no JS framework, no Node runtime. See docs/rust-backend.md |
| i18n | translations: frontmatter, t(key), setLocale(code) |
| Env access | getenv(key) / getenv(key, default) |
| Content helpers | doc(...) / render(...) structured output |
| HTML / CSS interpolators | html"..." (auto-escaping) and css"..." with ${expr} |
| Feature | Syntax |
|---|---|
| x402 micropayments | HTTP 402 → typed payment challenge / settlement via Payment[T], x402Server { … }, x402Client(...) — Ethereum + Cardano payment families |
| Blockchain SPI (draft / planned, not fully implemented) | BlockchainBackend trait — EVM (mainnet, L2s), Bitcoin, Solana, Cardano via pluggable backends. See specs/blockchain-spi.md |
| Wallet Connect (WC v2) | Relay-transport cryptographic primitives — pairing, session, JSON-RPC, X25519 / HKDF / ChaCha20-Poly1305 |
| Browser MetaMask wallet | x402ClientJs helper — Wallets.metaMask(Network.Base) over window.ethereum + EIP-712 signing |
| Ledger hardware wallets | JVM HID and browser WebHID vaults — Ledger HID APDU framing, Ethereum app signing, Cardano CIP-8 helpers |
| MPC wallet vaults | wallet-vault-mpc remote signing core plus wallet-vault-mpc-fireblocks provider adapter — Fireblocks JWT auth, RAW transaction signing, polling |
| Solana Wallet Standard | solana-wallet-std translator — Wallet Standard ↔ unified Wallet SPI |
| ERC-4337 account abstraction | EntryPoint v0.7 PackedUserOperation — bundlerless and bundler-driven flows |
| Cardano CIP-8 wallet | Ed25519 key-derived enterprise bech32 address, CIP-8 message signing, Scalus-source escrow validator with on-chain canonical CIP-8 proof verification, exact receiver-output check, claim/refund validity-window checks, Scalus script-context validator tests, stable EscrowScript.address(network) script address helper, Scalus-mode structured claim-message signing, server-side Scalus proof verification, bloxbean claim Tx draft with script data hash + relayer witness, typed Blockfrost protocol params, protocol min-fee draft balancing, static/bloxbean/Blockfrost/Ogmios Plutus ex-units, and env-gated Preprod integration coverage |
| Cross-backend crypto | JVM: native Ed25519, secp256k1, BLS12-381; JS: @noble/curves Scala.js backend — uniform CryptoBackend SPI |
std.crypto hashing |
sha256 (hex) / sha256Base64 (base64 digest) / sha256OfBase64 (digest over decoded bytes), byteLengthUtf8, hmacSha256, base64Encode/base64Decode — digests + encoding (e.g. KSeF invoiceHash/encryptedInvoiceHash) |
std.crypto encryption |
AES-256-GCM (aesGenKey/aesGcmEncrypt/aesGcmDecrypt + byte variants), AES-256-CBC + PKCS#7 with external IV (aesGenIv/aesCbcEncrypt/aesCbcDecrypt), RSA-OAEP (rsaOaepEncrypt), X.509 SPKI extraction (x509PublicKey) — hybrid encryption for KSeF 2.0 etc. (JVM only) |
std.crypto signature verify |
verifyEd25519/verifyEd25519Url, verifyRsaSha256 (PKCS1/PSS) — total verifiers (malformed → false, never throw) for trustless federation (JVM only) |
std.crypto signing |
ed25519Sign/ed25519SignUrl, rsaSignSha256 (PKCS1/PSS) — private-key signers that round-trip with the verifiers; for chain-checkpoint evidence a third party can verify without a shared secret (JVM only) |
std.pdf generation |
htmlToPdfBase64(html) — render a confined HTML/CSS subset (table layout, A4, typography/borders/bg) to base64 PDF bytes via OpenHTMLtoPDF; drop-in for an HTML→PDF relay (JVM only) |
std.pdf extraction |
pdfToMarkdown(pdfBase64) / pdfPageCount(pdfBase64) — read a PDF's text layer (Apache PDFBox), pages split by ---; honest plain-text (no layout inference), non-PDF throws (JVM only) |
std.mime assembly |
buildMimeMessage(from,to,subject,htmlBody,attachments) — hand-rolled RFC 5322 multipart/mixed (base64 HTML body + base64 attachments, RFC 2047 subject), ready for SMTP DATA (JVM only) |
| MCP × x402 | mcpServer { srv => srv.tool(...).requirePayment(...) } — paid LLM tools |
| Feature | Syntax |
|---|---|
| Bully leader election | cluster.leader() returns the current bully-elected leader; reactive on membership change |
| Phi-accrual failure detector | Heartbeat-driven liveness; tunable phi threshold per cluster |
| Self-health | cluster.self.health — driver-side health metric stream |
| Federation | Multi-cluster gossip + cross-cluster routing — see specs/cluster-federation.md |
| Raft consensus | Strongly-consistent state machine — see specs/cluster-raft.md |
| ZooKeeper client | zkClient { … } for legacy coordinator integration — see specs/client-zookeeper.md |
| Operational HTTP routes | /_ssc-cluster/status, /_ssc-cluster/members, /_ssc-cluster/leader — built-in across all 4 frontend / Node backends |
| File | Description |
|---|---|
| hello.ssc | Minimal "Hello, World!" |
| fs-roundtrip.ssc | std.fs write / read / append / list / delete round-trip |
| os-env.ssc | std.os env vars, path helpers, platform detection |
| nfc-ndef.ssc | std.nfc capability check plus text/URI/MIME NDEF record constructors |
| rozum-agent.ssc | Self-contained fake rozum gateway plus std.agent tool-call loop over an accounting-style tool |
| rozum-agent-schema-derived.ssc | Self-contained fake rozum gateway with derived AgentSchema and explicit tool schemas side by side |
| rozum-agent-pool.ssc | Self-contained fake primary/secondary rozum gateways plus AgentEndpointPool failover |
| rozum-agent-streaming.ssc | Self-contained fake rozum SSE gateway plus runAgentStream callbacks for text/tool-call events |
| yaml-parse.ssc | std.yaml parse YAML + accessors + fenced block wiring |
| ui-typed-json.ssc | std.json navigable JsonValue (total accessors, exact asDecimal) + structured builders |
| ui-fetch-json.ssc | fetchJsonValue (GET → navigable JsonValue) + fetchJsonAction (POST structured body) |
| frontend/std-ui/styled-primitives.ssc | std.ui status primitives (badge/tag/pill/kpiCard/tabBar), token-aware styled(), box sizing; re-themes under dark |
| datatable-static-spa.ssc | Browser SPA backed by a static in-memory DataTable (staticDataTable / fieldsBody); renders client-side with no fetch, identical on interpreter / JVM / JS |
| index.ssc | Landing page for the serve examples browser |
| script.ssc | Functions, loops, Fibonacci |
| data-types.ssc | Case classes, sealed traits, enums, pattern matching |
| functional.ssc | Lambdas, closures, HOF, composition, pipelines |
| enums.ssc | Simple and parameterised enums, recursive ADTs |
| extensions.ssc | Extension methods, for comprehensions, while, recursion |
| imports.ssc | Math, geometry, statistics |
| multi-link-imports.ssc | Two std modules imported from one pure Markdown paragraph |
| typeclass.ssc | Show, Eq, Ord, Monoid, Functor via given/summon |
| quoted-macro-interpreter.ssc | Restricted quoted macros on the interpreter run path with Expr.asValue / Expr.asTerm |
| quoted-macro-constfold.ssc | Compile-time constant folding via Expr.asValue match (literal → Some, else None); interpreter run + ssc link fold |
| custom-derives-mirror.ssc | User-defined typeclass derives through runtime Mirror metadata |
| typed-data.ssc | Data pipelines, Option, enums |
| graph-storage.ssc | JVM graphs: front matter plus generated Graph.* facade over embedded TinkerGraph property graph storage |
| graph-rdf4j-storage.ssc | JVM graphs: front matter plus generated Graph.* and Sparql.select facades over RDF4J memory RDF storage |
| graph-storage-interpreter.ssc | Interpreter Graph.* facade over in-memory property graph storage |
| content.ssc | md interpolator, auto-output, doc/render |
| content-introspection.ssc | Markdown-to-frontend authoring: Markdown body content, YAML data, section ids, and multiple real std/ui controls declared directly in yaml @id=... @ui=toolkit blocks and selected by id; runs as a live serve(page, 8099) phone preview |
| markdown-toolkit-links.ssc | Live serve(page, 8099) example where textField, checkbox, button, signalText, and badge controls are declared as ordinary Markdown toolkit: links instead of YAML, including a visible Apply-status update |
| content-to-markdown.ssc | Reverse-render selected DocumentContent regions back to semantic Markdown with contentToMarkdown(value) |
| content-linked-namespaces.ssc | Read section content from a directly imported module namespace with contentModuleSection(namespace, id) |
| content-tables.ssc | Use a Markdown GFM pipe table as ContentBlock.Table, show raw ${name} placeholders, bind YAML data with contentBind(...), and lower the bound table through contentToolkitBlock(id) to a toolkit TableNode |
| content-toolkit-transitive/app.ssc | Entry module renders a @ui=toolkit control block through contentToolkitBlock(id) called from a transitively-imported child — the JS backend emits the content-toolkit runtime for imports anywhere in the graph |
| content-live-rows.ssc | Bind a live fetch Signal as the rows of a Markdown-authored table: a toolkit:table?rows=<id> link resolves a contentRows(id, signal, columns) registration to a live DataTable (content-toolkit 3b) |
| content-toolkit-yaml-controls.ssc | Declarative @ui=toolkit YAML control tree whose {type: button, action: <id>}, {type: table, source: <id>}, and {type: signalText, signal: <id>} reference a registered action / data source / computed signal by id, and the table declares typed columns: (text/date/money/status/link) inline (declarative-ui Scope B.1 + B.5 + B.2) |
| content-data-source.ssc | @ui=toolkit tables bound to named data sources — contentDataSource(id, source, columns) registers a fetchSource(...) (managed GET, re-fetch on tick, with an optional dotted rowsPath envelope path) or a staticSource(...) (in-memory rows) under an id that source: resolves (declarative-ui Scope B.3) |
| content-action-onsuccess.ssc | A @ui=toolkit button bound to an action whose structured onSuccess runs after a 2xx — fetchActionWith(method, url, body, [onBumpTick(t), onSetSignal(s, v), onNavigate(path)]) refreshes a table, flashes a status signal, and routes, in order; a failed POST runs none (declarative-ui Scope B.4) |
| content-form-submit.ssc | A @ui=toolkit form whose submit action assembles its POST body from the named field signals — fetchActionWith("POST", url, formBody([("customerName", "customer"), "amount"]), …) serialises {customerName, amount} from the live signals at click (a (jsonKey, signalId) entry remaps a signal to a different wire key), no hand-maintained body signal (declarative-ui Scope B.4+) |
| content-slot.ssc | A @ui=toolkit panel with a {type: slot, id: <id>} escape hatch filled by a code-built TkNode registered with contentSlot(id, node) — when the declarative vocabulary can't express a widget, the author drops a ScalaScript-authored one into the panel by id (declarative-ui Scope B.6) |
| markdown-native-controls.ssc | Same Markdown/YAML-declared controls rendered through native clients: `ssc run-jvm --frontend swing |
| recursion.ssc | Self-TCO, mutual TCO, Collatz — deep recursion without overflow |
| effects.ssc | Algebraic effects — Console routing, nondeterminism, early return |
| std-effects-demo.ssc | Logger, Random, Clock, State, Env standard effects |
| direct-demo.ssc | direct[M] do-notation, .! postfix bind, effect-row unions |
| async-demo.ssc | Built-in Async effect — runAsync, async, await, parallel, delay |
| coroutine-demo.ssc | coroutineCreate, coroutineResume, suspend, generators |
| signals-demo.ssc | Reactive signals — Signal, computed, effect, diamond dedup |
| storage-demo.ssc | Built-in Storage effect — JSON-backed and ephemeral handlers |
| actors-demo.ssc | Actors — spawn, send, receive, supervision, cluster |
| actors-typed-remote-spawn.ssc | Typed ActorRef[M] helpers plus named registerBehavior / spawnRemote |
| cluster-capability.ssc | ClusterCapability, static seed discovery, and code identity checks |
| remote-registry-rpc.ssc | remoteHandlers: / @remote / remote def registry declarations, Remote.function, typed remoteTryCall, HTTP JSON fallback path metadata, and generated RemoteRpc typed client metadata for path: handlers |
| openapi-annotation.ssc | @openapi(...) route metadata and openApiSecurity(...) security schemes surfaced in /_openapi.json and Swagger UI |
| ws-recv-demo.ssc | Sync-style ws.recv() loop alternative to onMessage callbacks |
| mcp-demo.ssc | MCP server with tools and resources; MCP client usage |
| dataset-stats.ssc | Dataset MapReduce — runLocal, runParallel, aggregations |
| dsl-demo.ssc | Parser combinators, error recovery, multi-pass compilation pipeline |
| lenses.ssc | .copy(field = v), Focus[T](_.a.b), get / set / modify / andThen |
| default-params.ssc | Default parameter values on defs, classes, and enum cases |
| lang-split.ssc | scala vs scalascript block annotations side by side |
| scala-js-demo.ssc | Pure scala 3 document — runs on all three backends with byte-identical output |
| rest-api.ssc | Tiny in-memory REST API — route(), html"...", serve() |
| mount-demo/ | mount() intrinsic — file-based handlers, typed (CaseClass => CaseClass auto-deser/ser), 1-arg, 2-arg with ctx, static response |
| sql-sqlite-file.ssc | SQLite file database — databases: front-matter, sql DDL/DML blocks, Db.query/execute |
| typed-sql-crud.ssc | Typed SQL CRUD — derives RowCodec, Db.insert/update/query[A] on interpreter and JVM codegen paths |
| typed-object-codec.ssc | Typed object/document codec — derives ObjectCodec, portable object fields, aliases/defaults, and key extraction |
| graph-codecs.ssc | Typed graph/RDF codecs — derives VertexCodec, EdgeCodec, and RdfCodec |
| dataset-typed-mapping.ssc | Typed Dataset element mapping — DatasetCodec[A] over JsonCodec[A] in a JVM Dataset pipeline |
| distributed-dataset-codec.ssc | Distributed Dataset partition payloads — DatasetCodec.encodePartitions/decodePartitions[A] |
| distributed-dataset-wire-protocol.ssc | Distributed Dataset wire protocol — DatasetWirePartition payloads through local MapReduce actor workers |
| distributed-dataset-wire-shuffle.ssc | Distributed Dataset wire shuffle — DatasetWirePartition reduceByKey through local MapReduce actor workers |
| distributed-dataset-typed-helpers.ssc | Distributed Dataset typed helpers — DistributedDataset.run/runShuffle[A, B] around actor map + shuffle wire execution |
| spark-schema-mapping.ssc | Spark-like schema metadata — derives SparkSchemaCodec using shared field annotations |
| spark-shared-schema-reader.ssc | Spark typed CSV reader — Dataset.fromCsvAs[A] using SparkSchemaCodec[A] external column names |
| indexeddb-drafts.ssc | Typed client IndexedDB store — IndexedDb.store[A], local put/get/all/keys, awaitClient(...) |
| indexeddb-sync-client.ssc | Typed client sync helper — Sync.push[A] / Sync.pull[A] over local IndexedDB and REST sync endpoints |
| sync-todo.ssc | Full sync UI helper API — Sync.put, Sync.sync, Sync.status, Sync.isOnline, Sync.isSyncing, Sync.pending |
| object-store-jdbc.ssc | Typed server ObjectStore — JDBC JSON table, ObjectStore.put/get/all/delete/changes[A], derives ObjectCodec |
| object-store-sync-routes.ssc | Typed ObjectStore sync routes — objectStores: front matter generates JVM REST pull/push endpoints |
| sql-transaction.ssc | Atomic multi-statement transaction block — debit + credit in one JDBC transaction |
| spa-demo.ssc | Same route() / serve() source, browser SPA via ssc emit-spa |
| pwa-demo.ssc | Progressive Web App — pwa(name, icons, precache), GET /manifest.json + GET /sw.js |
| swing-hello.ssc | Minimal JDK-only Swing desktop window |
| swing-fullstack/ | No-socket JVM desktop full-stack example: Swing fetchActionClear posts to generated JVM backend routes and dataTable reads/deletes rows in the same process |
| swing-typed-client/ | No-socket JVM desktop full-stack example using generated apiClients: methods over backend routes |
| typed-client-distributed/ | Same-source distributed typed route client: JVM server on one machine, browser/Electron client on another via --server-url |
| auth-demo.ssc | Login / logout with signed cookie sessions + CSRF tokens |
| fetch-auth.ssc | Bearer-token authed fetch: fetchActionClear with headers Signal (v1) + fetchUrlSignal GET with headers (v2) |
| seed-signal.ssc | Editable draft signal seeded from fetchUrlSignal; reloads update the draft only while it is pristine |
| oauth-demo.ssc | Full OAuth2 sign-in (GitHub or Google) — state, exchange, userinfo |
| tls-demo.ssc | HTTPS + WSS server with tls(cert, key) |
| wc-demo.ssc | Web Components via ssc emit-wc, SSR + hydration |
| wasm-fibonacci.ssc | scalascript → WebAssembly module via ssc emit-wasm |
| wasm-sorting.ssc · wasm-matrix.ssc · wasm-primes.ssc · wasm-collections.ssc | Wasm benchmark suite |
| wasm-scalascript.ssc | scalascript blocks → WebAssembly — Point geometry with //> using dep |
| wasm-http.ssc | HTTP Fetch via scalajs-dom in Wasm — //> using dep hoisted directive |
| algebraic-effects.ssc | Typed effects end-to-end — discharge signatures, Reader[R], NonDet, multi effect |
examples/frontend/counter/ · show-hide/ · todo/ · toolkit-demo |
One source compiled to React / Vue / Solid / Custom — first three via Frontend Framework SPI, toolkit-demo via high-level Toolkit (Tk facade) and covered by an Electron Add-flow smoke test |
| x402-server.ssc · x402-client.ssc | HTTP 402 micropayment server + client (Ethereum settlement) |
| x402-metamask.ssc | Browser x402 wallet helper — connect MetaMask and sign EIP-712 via window.ethereum |
| x402-cardano.ssc | x402 on Cardano — CIP-8 wallet, Scalus escrow validator, end-to-end client + server |
| wallet-ledger-js.ssc | Browser Ledger/WebHID vault sketch — connect lifecycle, Ethereum signer, Cardano CIP-8 helper |
| wallet-mpc-fireblocks.ssc | JVM Fireblocks MPC vault sketch — remote secp256k1 signing through Fireblocks RAW transactions |
| spark-sql-demo.ssc | Spark SQL via sql fenced blocks + ${expr} bind parameters + section aliases |
| spark-encoder-demo.ssc | Scala 3 native Encoder[T] derivation — Dataset[CaseClass] end-to-end on Spark 4 |
| spark-streaming-rate-console.ssc | Structured Streaming — rate source → console sink with auto-awaitTermination |
| spark-delta-demo.ssc | Delta Lake — auto-emit delta-spark dep + extension/catalog configs |
| spark-hive-demo.ssc | Hive metastore via spark-hive-metastore: + @TempView("...") + Dataset.fromTable[T] |
| spark-mllib-pipeline.ssc | MLlib — Tokenizer + HashingTF + LogisticRegression pipeline end-to-end |
| xslt-transform.ssc | XSLT 1.0 transformation — identity, element rename, parameter substitution, HTML generation (MarkupCodec.transform) |
Run them all at once:
./examples/run-all.scA reference SPA built entirely through the high-level Tk facade
(layout + form-like fields + display widgets + theming). Compiles
to all four frontend backends + static HTML via SSR.
# 1. Compile + test (frontend-toolkit 217 tests, frontend-examples 41)
sbt frontendToolkit/test frontendExamples/test
# 2. Generate 16 (4 demos x 4 backends) HTML+JS bundles
sbt "frontendExamples/runMain scalascript.frontend.examples.EmitAll"
# → target/frontend-examples/toolkit-demo/{custom,react,solid,vue}/
# 3. Serve via the bundled ssc static-file server (no Python/Node)
ssc serve 8000 target/frontend-examples/toolkit-demo/react
# open http://localhost:8000/Details: examples/frontend/README.md
and docs/user-guide.md#16-frontend-toolkit.
For SSR (Ssr.renderToHtml) and the full widget catalog see
specs/frontend-toolkit-spec.md.
The interpreter uses lazy plugin loading (v1.33): the ServiceLoader scan for HTTP/SQL/OAuth/etc. plugins is deferred until the first plugin name is actually accessed. Scripts that never call a plugin skip the scan entirely.
| Script | Steady-state cold start |
|---|---|
hello.ssc (no plugins) |
~0.31 s |
| Script with HTTP/SQL/auth | ~0.35 s (scan runs on first access) |
Application Class-Data Sharing (AppCDS) is enabled in bin/ssc and the
install.sh launcher (-XX:+AutoCreateSharedArchive, JDK 19+; the archive is
auto-created on first run and auto-recreated on classpath change — no build
step). It cuts a fresh ssc run hello.ssc from ~378 ms → ~182 ms (−51%) and
peak RSS from 167 → 114 MB (−32%). Old JDKs ignore the flags; opt out with
SSC_NO_CDS=1. Three real-workload-perf harnesses live under tests/perf/:
coldstart/ (fresh-run wall-clock + RSS), serverrss/ (steady-state server RSS
- leak detection — the interpreter server settles at ~195 MB with no climb), and GC-under-load.
Everything benchmark-related goes through one wrapper:
scripts/bench interp # all interpreter microbenchmarks
scripts/bench interp recursionFib # filter to one bench
scripts/bench cross # interp vs JS vs JVM (RuntimeBench)
scripts/bench off recursionFib # prove the off-mode fall-back works
scripts/bench profile recursionFib # JFR alloc + GC profile
scripts/bench wall # cross-language wall-clock (ssc/scala/node)
scripts/bench help # full command listThe canonical reference — what each bench measures, when to use it, how to
add a new one — is docs/benchmarks.md.
Quick non-blocking smoke checks for perf-sensitive changes:
ssc bench --smoke # corpus-driven CLI smoke
scripts/bench smoke # one-iter JMH smoke (writes bench/jmh-smoke.json)The checked-in workflow and baseline policy live in
bench/perf-manifest.yaml and
bench/README.md. Raw JMH/runtime outputs are ignored; only
curated summaries should be promoted into tracked baseline files.
ssc watch-bench measures the same parse-cache, incremental typer, and
interpreter reload path that ssc watch uses, but runs against a temporary
copy so the source file is not modified:
ssc watch-bench --cycles 10 --target-ms 100 examples/rest-api.ssc
ssc watch-bench --cycles 10 --target-ms 100 --require-target examples/rest-api.sscThe command reports warm-up, p50, and max cycle times. --require-target
turns the target into a non-zero exit condition for local performance gates.
e2e/rest-smoke.sc boots examples/rest-api.ssc through each of the three
backends in turn and diffs their HTTP responses — guards against drift between
the interpreter / JVM / JS serve runtimes.
scala-cli e2e/rest-smoke.scCross-backend tests that verify JVM interpreter and JS transpiler produce identical output. Run with:
scala-cli conformance/run.sc| Test | What it covers |
|---|---|
| arithmetic | Integer and floating-point ops, math.* |
| strings | String methods and interpolation |
| collections | List — map, filter, fold, take, drop, … |
| option | Option — map, getOrElse, filter, … |
| pattern-matching | Literals, guards, Option, tuple patterns |
| case-classes | Case class construction, field access, pattern matching |
| for-comprehensions | yield, guards, nested generators, do |
| higher-order-functions | Lambdas, compose, flatMap, eta-expansion |
| recursion | Factorial and Fibonacci |
| tail-recursion | Self-TCO at depth 100 000 — sum, countdown |
| mutual-recursion | Mutual TCO — isEven/isOdd at depth 100 000 |
| sealed-traits | ADT hierarchy with sealed trait + case class |
| variables | var mutation and while loops |
| tuples | Tuple construction, _1/_2/_3, destructuring |
| maps | Map — size, getOrElse, contains, keys, values |
| list-companion | List.fill, List.tabulate, List.range |
| modules | [name](./path.ssc) imports — bind definitions from another file |
| effects | Algebraic effects: Console routing, Choose nondeterminism, Fail early-return |
| std-effects | Logger, Random, Clock, State, Env standard effects with default + test handlers |
| async | Built-in Async effect — runAsync drives async / await / parallel / delay |
| async-parallel | runAsyncParallel — real-thread Async on JVM |
| storage | Built-in Storage effect — get / put / remove / has / keys via ephemeral or file-backed handler |
| direct | Direct-syntax do-notation: direct[M], .! bind, pure lift, control flow |
| coroutines | coroutineCreate, coroutineResume, suspend, Step[Y,T], coroutineCancel |
| generators | generator[T] { yield(v) }, pipeline composition, lazy streams |
| streams | Source[A] / Sink[A] / Flow[A, B], stream { emit(x) }, bounded buffers, overflow strategies, wall-clock throttle/debounce, live Source.signal, and sig.bind(source) |
| signals | Reactive Signal / computed / effect with diamond-dedup flush |
| lenses | .copy(field = v) and Focus[T](_.a.b) — get / set / modify / andThen |
| prisms | Prism[Sum, Variant] — getOption / set / modify on enum / sealed-trait cases |
| optional | Focus[T](_.maybe.some.field) — Optional optic with getOption / set / modify / andThen |
| traversal | Focus[T](_.items.each.field) — Traversal with getAll / modify / set / andThen |
| actors | spawn / send / receive / supervision / link / exit |
| actors-dist | Distributed actor cluster, WS transport, gossip, leader election |
| mcp | MCP server tools + resources; MCP client callTool |
| dataset | Dataset[T] local sequential, parallel, distributed MapReduce |
| dsl | Parser combinators, error recovery, indentation-aware, multi-pass pipeline |
| metaprogramming | inline, derives, compiletime.*, restricted quoted macro link/interpreter expansion (partial), interpreter Mirror.Of[T] custom derives |
| checked-errors | throws[A, E], attemptCatch, HasStackTrace |
| websocket | onWebSocket, ws.send/recv, rate limiting, wss:// |
| tls | tls(cert, key), HTTPS, WSS |
ScalaScript supports the following bundled backends, all loaded through the
Backend SPI plugin architecture
(specs/backend-spi.md):
| Command | Backend id | How it works |
|---|---|---|
bin/ssc file.ssc |
int |
Tree-walking interpreter — instant startup, no compilation |
ssc run --target jvm file.ssc |
jvm |
Compile via JvmGen → temp .sc → scala-cli run. True JVM semantics, no artifacts left on disk. Requires scala-cli. |
ssc run-jvm file.ssc |
jvm |
Alias for ssc run --target jvm (kept for backward compatibility) |
ssc run-js file.ssc |
js |
Compile via JsGen → temp .js → node. True Node.js semantics, no artifacts left on disk. Requires node. |
bin/jssc file.ssc |
js |
Alias for ssc run-js via bin/ wrapper |
bin/sscc file.ssc |
jvm |
Alias for ssc run-jvm via bin/ wrapper |
ssc emit-openapi file.ssc |
openapi |
Headless interpreter dry-run that exports registered routes as OpenAPI 3.1 JSON or YAML. Flags: --format json|yaml, -o, --title, --version, repeatable --server. |
ssc emit-spa file.ssc |
scalajs-spa |
Self-contained SPA HTML + JS bundle |
bin/ssc-spark file.ssc |
spark |
Apache Spark 4 — generates a Scala 3.7.1 .sc script with //> using dep directives, runs via scala-cli. Auto-detects sql blocks, @SqlFn UDFs, readStream/writeStream, .format("delta"), @TempView, MLlib imports. See §13 of the User Guide. |
ssc emit-wasm file.ssc / examples/run-wasm.sh |
wasm |
WebAssembly module — scalascript/ssc blocks lowered to Wasm IR. Cross-backend sql fenced blocks supported (v1.27 Phase 5). |
ssc-native (GraalVM) |
native |
No-JVM ssc binary; plugins via ssc-plugin-host.jar subprocess bridge |
| (sub-backend) | node |
Node.js runtime variant of js — emits server-side JS with fs/path shims and a cross-backend SQL runtime (v1.27 Phase 4). |
| (sub-backends) | frontend-{react,vue,solid,custom} |
Frontend Framework SPI (v1.18 Phase A): same .ssc UI source compiled to React (useState/useEffect), Vue 3 (ref/render), Solid (createSignal/createEffect), or a minimal custom runtime — ShowSignal/ToggleSignal/ForSignal reactive primitives shared across all four. See specs/frontend-framework-spi-plan.md. |
| JVM desktop sub-backend | frontend-swing |
Partially implemented JDK-only JVM desktop frontend. It provides SPI discovery, ssc run-jvm --frontend swing, Swing source emission for text/buttons/fields/toggles/stacks/spacers/dividers/scroll views, basic style hints, local signal actions, Swing fetchAction / dataTable / typed route client dispatch through generated JVM BackendTransport, no-socket full-stack examples, and a swing-plugin skeleton for future interpreter intrinsics; JavaFX/Compose adapters remain planned. See specs/jvm-desktop-frontend.md. |
The Backend trait + ServiceLoader discovery let third parties
add their own backend without touching core — drop a JAR and
attach it via ssc --plugin path/to/your-backend.jar. See
docs/writing-a-backend.md and the
worked samples under examples/plugins/.
Useful flags:
ssc --list-backends # list every visible backend
ssc --list-source-languages # list registered SourceLanguage plugins
ssc --describe-backend jvm # capabilities + intrinsics + sources
ssc --backend js run file.ssc # override the per-command defaultBuilt-in fenced languages are SourceLanguage plugins too: scala, html,
css, javascript/js, xml, bind-aware sql, and bind-aware
transaction are visible through --list-source-languages. Third-party DSLs
follow the same path.
The JavaScript backend handles two block types differently:
scalascriptblocks — transpiled by our custom JS transpiler (JsGen), which supports ScalaScript-specific features (effects, content helpers, TCO, imports).scalablocks — compiled by Scala.js viascala-cli --js, giving full Scala 3 fidelity (standard library, type system, no custom runtime limitations).
When a .ssc file contains both, the Scala.js-compiled section runs first, followed by
the ScalaScript transpiled section.
A compiler plugin lets you extend ScalaScript with new native capabilities —
cryptographic primitives, ML inference, GPU kernels, hardware I/O — by mapping
extern def declarations in a .ssc source to platform-specific implementations.
// crypto.ssc (shipped inside the .sscpkg)
extern def sha256(input: String): String
extern def hmacSha256(key: String, data: String): StringCall it from any .ssc file just like a normal function:
import [Crypto](crypto)
val digest = sha256("hello, world")
println(hmacSha256("secret", digest))Each extern def maps to an IntrinsicImpl:
| Variant | When to use |
|---|---|
NativeImpl(fn) |
Interpreter — call the JVM lambda directly |
RuntimeCall(sym) |
JVM / JS codegen — emit sym(args…) and ship sym as a runtime helper |
InlineCode(emit) |
Emit arbitrary target code at each call site |
HostCallback(name) |
Out-of-process backends — route through the host wire protocol |
Plugins are packaged as .sscpkg (a ZIP containing manifest.yaml,
sources/*.ssc, runtime/jvm.scala + runtime/js.js, and an intrinsics/*.jar
that registers a Backend via ServiceLoader):
ssc plugin pack _pkg/ -o org.example.crypto-1.0.0.sscpkg
ssc plugin install ./org.example.crypto-1.0.0.sscpkg # permanent
ssc --plugin ./org.example.crypto-1.0.0.sscpkg run use-crypto.ssc # ad-hocInstalled distributions also include first-party advanced plugins locally under
bin/lib/compiler/plugin-available; use --plugin with one of those .sscpkg
files when you want opt-in capabilities without a registry domain.
See examples/plugins/crypto-plugin/ for a
complete worked example and docs/user-guide.md §21 for the full API reference.
ssc run file.ssc # interpret (tree-walking, instant startup)
ssc run --target jvm file.ssc # compile via JvmGen + run with scala-cli (no artifacts)
ssc run-jvm file.ssc # same as above (backward-compat alias)
ssc run-js file.ssc # compile via JsGen + run with node (no artifacts)
ssc watch file.ssc # watch mode (re-run on change)
ssc watch-bench file.ssc # benchmark watch reload cycles on a temp copy
ssc bench --smoke # quick interpreter-only benchmark wiring smoke
ssc check file.ssc # type-check only (parse + typer, no codegen); exit 0=clean 1=type-err 2=parse-err 3=notfound
# also warns on @ui=toolkit controls that reference an unregistered action/data-source id (declarative-ui B.7)
ssc check --json file.ssc # structured JSON diagnostics
ssc check --quiet file.ssc # no output, exit code only (for pre-commit hooks)
ssc check --watch file.ssc # re-check on file change, Ctrl-C to stop
ssc check src/ # recursively check all *.ssc files in a directory
ssc repl # interactive REPL
ssc build myapp.ssc # build project file → dist/ (--target ssc|jvm|js|web)
ssc build # auto-discover <dirname>.ssc or single .ssc in cwd
ssc build src/ # dir-walk mode (backward compat)
ssc package myapp.ssc # build all targets: listed in frontmatter
ssc install [--prefix <dir>] # install ssc to ~/.local (or custom prefix)
ssc compile-jvm file.ssc # compile to .scjvm artifact
ssc compile-js file.ssc # compile to .scjs artifact
ssc emit-interface file.ssc # emit .scim interface
ssc emit-ir file.ssc # emit .scir normalized IR
ssc link --backend jvm dir/ # link artifacts
ssc build --incremental dir/ # incremental build
ssc emit-js file.ssc # transpile to JS
ssc emit-lib --host js|jvm|rust|java --feature optics -o dir/ # standalone host library (npm/jar/crate/maven)
ssc emit-spa file.ssc # SPA HTML bundle
ssc emit-wc file.ssc # Web Components bundle
ssc test file.ssc # run tests
ssc preview file.ssc # preview component variants
ssc deps file.ssc # show import closure
ssc search json [--refresh|--offline]
ssc info io.scalascript/json [--registry <url>]
ssc add io.scalascript/json [<version>] [--file <manifest>]
ssc info artifact.scjvm # inspect artifact
ssc plugin install/list/uninstall/check/pack/registry
ssc --list-backends / --describe-backend <id>
jssc file.ssc # JS transpiler runner
sscc file.ssc # JVM runner
ssc-spark file.ssc # Apache Spark runner (Spark 4 + Scala 3.7.1)
ssc submit file.ssc # spark-submit fat JAR (cluster deploy)
ssc --spark-master <url> # override Spark master (local[*] / spark:// / yarn / k8s://)
ssc --spark-version <v> # override Spark version (default 4.0.0)bin/
ssc # interpreter launcher (tree-walking, no compilation step)
jssc # JS runner: transpiles .ssc → JS and runs via Node.js
sscc # JVM runner: compiles .ssc → Scala 3 → JVM via scala-cli
ssc-js # JS transpiler: emit JS to stdout, or --run to execute
http.ssc # HTTP server for examples browser
runtime/backend/spi/ # SPI traits (Backend, SourceLanguage, PluginRegistry, Capabilities, …)
runtime/scalascript-plugin-api/ # Stable plugin author API (PluginValue, PluginNative, capability traits)
ir/ # IR types + JSON/MsgPack codecs
core/
src/main/scala/scalascript/
parser/ # Markdown + YAML + Scala parser
typer/ # Type checker
ast/ # AST types
imports/ # Cross-file import resolver
interpreter/Value.scala # Computation Free monad (used by interpreter+codegens)
transform/ # Normalize, DirectDesugar, EffectAnalysis
plugin/ # PluginRegistry facade, BackendRegistry, SubprocessBackend, WireProtocol
runtime/backend/jvm/ # JvmGen — emits Scala 3 source
runtime/backend/js/ # JsGen — transpiles to JavaScript
runtime/backend/scalajs/ # ScalaJsBackend — emits SPA via Scala.js
runtime/backend/interpreter/
src/main/scala/scalascript/
interpreter/ # Tree-walking interpreter
server/ # Built-in HTTP / WebSocket / Actor / MCP runtime
bench/ # WsStress benchmark
runtime/runtime-server/common/ # Shared HTTP/WS server primitives (all backends)
mcp/common/ # Shared MCP protocol types + codec
cli/ # Main entry point (ssc command)
conformance/ # Cross-backend conformance test suite
expected/ # Canonical expected outputs
examples/ # Runnable .ssc files
run-all.sc # Runs all examples in order
plugins/ # Worked backend plugin examples
build.sbt # sbt build — multi-module per spec §4.1
project/
build.properties
plugins.sbt # sbt-assembly for fat-jar packaging
docs/ # Architecture, spec, design docs
architecture.md # Compiler pipeline and module structure
backend-spi.md # Backend SPI design (source of truth)
direct-syntax.md # Direct do-notation (v1.8+)
coroutines.md # Coroutine primitive (v1.9+)
dsl.md # DSL authoring and parser combinators (v1.20)
mapreduce.md # Dataset / MapReduce API (v1.21–1.22)
mcp.md # MCP server + client (v1.17)
actors-dist.md # Distributed actors design
metaprogramming.md # inline/derives (v1.14)
error-handling.md # Checked errors / throws (v1.15)
modularity.md # Package system and separate compilation
markdown-as-syntax.md # Markdown as Syntax design
targets.md # Target Backends
user-guide.md # Practical user reference
tutorial.md # Step-by-step todo-list application tutorial
tools/scripts/ # launchers/ and validate-frontmatter.scala
# Install scala-cli first (if needed)
./setup.sh
# Build ssc into bin/
./install.sh --devAfter installation:
ssc examples/hello.ssc
jssc examples/hello.ssc
sscc examples/hello.sscScalaScript can be used as a Scala 3 library via sbt:
sbt compile # compile all modules
sbt test # run unit tests
sbt cli/assembly # produce a self-contained ssc.jar
sbt cli/installBin # stage bin/lib so bin/ssc works from the checkoutThe public API surface:
import scalascript.parser.Parser
import scalascript.interpreter.Interpreter
import scalascript.codegen.{JsGen, JvmGen}
val module = Parser.parse(source) // parse a .ssc file
Interpreter.run(module) // interpret
val js = JsGen.generate(module) // emit JavaScript
val scala = JvmGen.generate(module) // emit Scala 3 scriptScalaScript libraries can also be packaged as .ssclib archives:
ssc package --lib --precompile --manifest ssclib-manifest.yaml -o dist/my-lib.ssclib
ssc check-compat dist/my-lib-1.0.ssclib dist/my-lib-1.1.ssclib--precompile embeds .scim public interface artifacts under ir/.
check-compat reports removed or changed public symbols between library
versions and falls back to deriving interfaces from packaged sources when a
library has no precompiled interface artifacts.
- Reuse, don't invent. Markdown, YAML, Scala 3 — use what works.
- One source, many targets. Source semantics are target-independent.
- Human and machine readable. Pleasant for humans, trivially parseable for machines.
- No AI at runtime or compile time. The language stands on its own.
Sergiy (Victorovych) Shcherbyna sergey.scherbina@gmail.com