Skip to content

Compiler Supported Syntax Reference

This page is compiler-specific (sloppyc) and lists enforced source-shape rules.

Input and Parse

  • Entry and module files must use .js, .mjs, or .ts.
  • Parse failures return SLOPPYC_E_PARSE.
  • Unsupported extension returns SLOPPYC_E_UNSUPPORTED_INPUT.

Sloppy Imports

Web compiler entry input must import:

  • Sloppy from "sloppy"

Files that contain route handlers calling Results.* must also import Results from "sloppy" in that same file. A thin entry file that only creates the app and registers function modules can import Sloppy alone; child route modules import Results when their handlers use it.

These imports must be named and unaliased in the current compiler subset.

Supported Import Modules

Compiler-recognized modules:

  • "sloppy" (Sloppy, Results, framework metadata types, data, sql, Migrations, ProviderHealth)
  • "sloppy/data" (sql, Migrations, ProviderHealth)
  • "sloppy/providers/sqlite" (sqlite, Sqlite)
  • "sloppy/providers/postgres" (Postgres)
  • "sloppy/providers/sqlserver" (SqlServer)
  • "sloppy/fs", "sloppy/time", "sloppy/crypto", "sloppy/codec", "sloppy/cache", "sloppy/net", "sloppy/os", "sloppy/workers", "sloppy/ffi"
  • relative imports constrained to source root
  • installed pure-JavaScript package imports in Program Mode
  • supported Node shim specifiers such as node:path in Program Mode

Unsupported specifiers/import names fail with a diagnostic that points at the import. The most common ones are:

  • SLOPPYC_E_UNSUPPORTED_IMPORT_SPECIFIER
  • SLOPPYC_E_UNSUPPORTED_IMPORT
  • SLOPPYC_E_PACKAGE_NOT_FOUND
  • SLOPPYC_E_PACKAGE_EXPORT_UNSUPPORTED
  • SLOPPYC_E_NATIVE_ADDON_UNSUPPORTED
  • SLOPPYC_E_UNSUPPORTED_NODE_BUILTIN

Web app extraction still rejects dynamic import with SLOPPYC_E_UNSUPPORTED_DYNAMIC_IMPORT. Program Mode supports string-literal dynamic imports and computed dynamic imports over explicit moduleInclude graphs.

RequestId and RequestLogging are supported for static middleware defaults. TestHost, experimental TestServices, Testing, FakeClock, and TestData are framework test helpers; compiler input rejects them with SLOPPYC_E_UNSUPPORTED_TESTING_IMPORT.

Program Mode Source

Program Mode supports route-free source files that use static ESM imports and exports. Oxc parses the source and strips supported TypeScript syntax before Sloppy rewrites supported module syntax into the generated artifact bundle.

Supported entrypoint shapes are:

ts
console.log("hello");

export async function main() {
  console.log("hello");
}

export default async function main() {
  console.log("hello");
}

If both named main and default function exports exist, named main wins. Non-function default exports are ignored as entrypoints after their module top-level code has executed.

main and default function entrypoints receive (args, ctx). args contains the strings passed after -- to sloppy run. ctx is the Program context:

ts
{
  kind: "program",
  args: string[],
  cwd: string,
  environment: string,
  plan: {
    kind: "program",
    metadataCompleteness: "opaque"
  }
}

Program Mode installs a Sloppy-owned console while the entry module runs. log, info, and debug write to stdout; warn and error write to stderr. Returning an integer from 0 through 255 sets the process exit code. Throwing, rejecting, or returning an out-of-range exit code exits non-zero with a diagnostic.

Program Mode accepts static relative imports, installed pure-JavaScript npm packages, CommonJS/JSON modules, the documented Sloppy stdlib subpaths, and the Node APIs that already have Sloppy compatibility shims. It also accepts sloppy/providers/sqlite as a bundled Program Mode module. That lets CLI tools create or inspect SQLite provider descriptors without pretending they are web apps.

Type-only imports are erased and do not turn on runtime features. Dynamic import("literal") calls are resolved at build time. Computed dynamic imports work only for modules you explicitly seal into the artifact graph with moduleInclude.

Some imports are still rejected because Sloppy cannot run them yet, not because the TypeScript syntax is invalid:

  • Remote URL imports, such as import "https://example.com/mod.ts", are not fetched during build. Sloppy currently builds from local files and installed packages so the artifact graph is repeatable.
  • Node native addons, such as packages that load .node binaries through node-gyp, are not supported by the Sloppy runtime. Pure JavaScript package entries can work; compiled Node ABI modules cannot.
  • Node builtins are supported only when the compatibility registry has a shim for that module. Unsupported builtins fail clearly instead of being replaced with a fake object.
  • Package exports shapes outside Sloppy's resolver subset are rejected until the resolver knows how to choose a compatible entry.
  • Provider modules need a Program Mode stdlib surface. Today that includes sloppy/providers/sqlite; other provider modules remain web/compiler metadata only.

Program Mode supports local exports, source re-exports such as export { value } from "./dep", export-all declarations, and namespace re-exports such as export * as dep from "./dep".

Package and compatibility diagnostics

Representative dependency diagnostics:

  • SLOPPYC_E_PACKAGE_NOT_FOUND
  • SLOPPYC_E_PACKAGE_EXPORT_UNSUPPORTED
  • SLOPPYC_E_NATIVE_ADDON_UNSUPPORTED
  • SLOPPYC_E_UNSUPPORTED_NODE_BUILTIN

Node compatibility is intentionally incremental. A supported shim means Sloppy has a local module for that Node API; it does not mean every Node global or every edge case from Node itself exists. Import node:process or node:buffer explicitly when that partial shim is supported.

Stdlib subpath imports

The compiler accepts named, unaliased imports from each stdlib subpath listed below. Each subpath emits the matching stdlib.* runtime feature into the Plan when imported. Default imports and import aliases are rejected with SLOPPYC_E_UNSUPPORTED_IMPORT; unsupported names fail with the same code.

SubpathSupported namesPlan feature
sloppy/fsFile, Directory, FileHandle, FileWatcher, Pathstdlib.fs
sloppy/netHttpClient, TcpClient, TcpListener, TcpConnection, LocalEndpoint, UnixSocket, NamedPipe, NetworkAddress, SloppyNetErrorstdlib.net (HttpClient emits stdlib.httpclient)
sloppy/osSystem, Environment, Process, ProcessHandle, Signals, OsErrorstdlib.os
sloppy/timeTime, Deadline, CancellationController, TimeoutError, CancelledError, InvalidDeadlineError, TimerDisposedErrorstdlib.time
sloppy/cryptoRandom, Hash, Hmac, Password, ConstantTime, Secret, NonCryptoHashstdlib.crypto
sloppy/codecBase64, Base64Url, Hex, Text, Binary, Compression, Checksumsstdlib.codec
sloppy/cacheCache, SloppyCacheErrorstdlib.cache
sloppy/workersBackgroundService, WorkQueue, WorkerPool, Worker, WorkerCancellationController, WorkerCancellationSignal, SloppyWorkerErrorstdlib.workers
sloppy/ffiunsafeFfi, tstdlib.ffi

Examples:

ts
import { File, Directory } from "sloppy/fs";
import { HttpClient } from "sloppy/net";
import { System, Environment, Process, Signals } from "sloppy/os";
import { Time, Deadline, CancellationController } from "sloppy/time";
import { Random, Hash, Hmac, Secret } from "sloppy/crypto";
import { Base64, Hex, Text, Binary } from "sloppy/codec";
import { Cache } from "sloppy/cache";
import { BackgroundService, WorkQueue } from "sloppy/workers";
import { unsafeFfi as ffi, t } from "sloppy/ffi";

FFI declarations

sloppy/ffi declarations must be static so the compiler can emit Plan-visible ABI metadata. Supported shapes are top-level ffi.library(...), ffi.fn(...), and ffi.struct(...) calls with literal library names, symbols, calling conventions, object specs, and t.* type descriptors.

Dynamic library names, generated descriptor objects, computed function names, non-t type descriptors, callbacks, unsupported return buffer types, and non-sequential struct layouts fail with FFI diagnostics such as SLOPPYC_E_FFI_DYNAMIC_DECLARATION, SLOPPYC_E_FFI_INVALID_TYPE, and SLOPPYC_E_FFI_UNSUPPORTED_CALLBACK.

Route Extraction Rules

Static route declarations are strongest when they are top-level statements on app/group receivers. Sloppy does not require every route to be statically understood. If the compiler can emit runnable JavaScript, the app can run. Static source gives stronger Plan metadata; dynamic source produces partial metadata and findings.

Route methods:

  • GET, POST, PUT, PATCH, DELETE (map* and plain verb forms)

Supported route metadata:

  • app.get("/path", handler).withName("Name")
  • app.get("/path", { name: "Name", tags: ["tag"] }, handler)
  • app.group("/prefix").withTags("tag")
  • app.group("/prefix").withTags("tag").requireAuth(...)
  • app.mapHealthChecks(...) with literal paths and literal check metadata
  • top-level app.use(fn) and group.use(fn) middleware with inline or top-level static functions;
  • top-level app.useCors({...}) with literal policy objects;
  • top-level app.use(RequestId.defaults({...})) and app.use(RequestLogging.defaults({...})) with static options;
  • top-level app.mapController(...) / app.controller(...) with a top-level plain controller class and literal mapper calls;

Route metadata options must be literal objects. name must be a non-empty string literal. tags must be an array of non-empty string literals. Health options must be omitted, a string literal aggregate path, or a literal object with path, livenessPath, readinessPath, and checks. Check objects must use literal names, boolean liveness/readiness flags, and inline check functions.

Common route failures:

  • SLOPPYC_E_UNSUPPORTED_ROUTE_SHAPE
  • SLOPPYC_E_UNSUPPORTED_HTTP_METHOD
  • SLOPPYC_E_UNSUPPORTED_ROUTE_PATTERN
  • SLOPPYC_E_UNSUPPORTED_ROUTE_NAME
  • SLOPPYC_E_UNSUPPORTED_ROUTE_OPTIONS
  • SLOPPYC_E_UNSUPPORTED_HEALTH_CHECKS

Dynamic route uncertainty is reported as Plan findings instead of fatal metadata extraction diagnostics:

  • SLOPPYC_W_DYNAMIC_ROUTE
  • SLOPPYC_W_PARTIAL_ROUTE_METADATA
  • SLOPPYC_W_DYNAMIC_RESPONSE_METADATA

Schema metadata supports local Schema.* declarations, concrete TypeScript aliases/interfaces, .accepts(...), .returns(...), ctx.body.validate(...), and named schema references such as Schema.array(User) or object fields that refer to another local schema. Plan emission resolves schema references into runtime-supported schema JSON. If a reference graph cannot be fully resolved, the compiler emits partial runtime-safe metadata and a schema.reference.partial doctor check instead of blocking otherwise runnable source.

Health checks must be inline functions that do not capture module-level locals. Captured values fail with SLOPPYC_E_UNSUPPORTED_HEALTH_CHECKS.

Framework Static Subset

These app-host features are emitted when they stay inside the static subset:

  • middleware functions must be inline or top-level functions with supported captures;
  • CORS policies must be literal objects;
  • RequestId options must be static and cannot use generator callbacks;
  • RequestLogging options must be static booleans;
  • controller mappings must target a top-level plain class and literal mapper route calls.

Unsupported dynamic framework features fail with SLOPPYC_E_UNSUPPORTED_MIDDLEWARE, SLOPPYC_E_UNSUPPORTED_CORS, SLOPPYC_E_UNSUPPORTED_REQUEST_ID, SLOPPYC_E_UNSUPPORTED_REQUEST_LOGGING, or SLOPPYC_E_UNSUPPORTED_CONTROLLER instead of accepting a partial Plan.

Services And Config

Literal service registrations are supported on both app.services and builder.services:

  • addSingleton("Token", () => value)
  • addScoped("Token", () => value)
  • addTransient("Token", () => value)

Factories must be inline functions. They may reference their scope parameter, local values, JavaScript globals, and compiler-emitted top-level helper functions. They may not capture the extracted app object or arbitrary top-level state that the generated artifact cannot preserve.

Config<"KEY"> typed parameters read the environment value first. When the source also contains a literal app.config.getString("KEY", "default") default, the generated wrapper uses that default if the environment value is absent.

Pattern Rules

Compiler-enforced route grammar:

  • /
  • static segments without braces
  • {name}, {name:str}, {name:int}, {name:uuid}, {name:alpha}, {name:float}

Normalization:

  • framework :name is normalized to {name} before validation

Handler Diagnostics

Representative handler diagnostics:

  • SLOPPYC_E_UNSUPPORTED_HANDLER
  • SLOPPYC_E_UNSUPPORTED_HANDLER_PARAMETERS
  • SLOPPYC_E_UNSUPPORTED_TYPESCRIPT_HANDLER
  • SLOPPYC_E_UNSUPPORTED_HANDLER_VALUE
  • SLOPPYC_E_UNSUPPORTED_ASYNC_HANDLER_BODY

Typed Handler Binding Diagnostics

Representative typed-binding diagnostics:

  • SLOPPYC_E_UNSUPPORTED_HEADER_BINDING
  • SLOPPYC_E_DYNAMIC_PROVIDER_NAME
  • SLOPPYC_E_UNKNOWN_INJECTION_MARKER
  • SLOPPYC_E_UNRESOLVED_TYPE
  • SLOPPYC_E_MULTIPLE_BODY_BINDINGS
  • SLOPPYC_E_ROUTE_BINDING_MISMATCH
  • SLOPPYC_E_UNBOUND_ROUTE_PARAMETER
  • SLOPPYC_E_AMBIGUOUS_BINDING

Provider Bridge Limitation

Compiler metadata recognizes sqlite/postgres/sqlserver providers. Typed provider parameters emit generated wrappers for all three provider kinds, with PostgreSQL and SQL Server execution gated on provider config, active bridge support, and live service dependencies.

Generated static provider handles are narrower: app.provider("sqlite:main") is the current executable static-handle path. Static non-SQLite handles such as app.provider("postgres:main") are rejected with SLOPPYC_E_UNSUPPORTED_PROVIDER_BRIDGE.

Public alpha. APIs and artifact formats may still change between alpha revisions.