@swtt/core
v0.1.1
Published
Framework-free runtime for swtt-js apps: Mongo connection, validation, an error contract that survives the network, and DTO helpers.
Maintainers
Readme
@swtt/core
The framework-free runtime behind swtt-js. Five modules, no domain content, no cloud SDK.
You do not usually install this directly — npx @swtt/cli init my-app scaffolds an app that depends on it.
npm install @swtt/core mongoose zodmongoose and zod are peer dependencies, so your app owns their versions. mongoose is optional: a client-only consumer importing @swtt/core/errors and @swtt/core/http never needs it.
Entry points
The barrel pulls in mongoose. On a client, import the subpath instead — these bundle onto a phone:
import { getApiErrorText } from "@swtt/core/errors"; // client-safe
import { ApiClient } from "@swtt/core/http"; // client-safe
import { connectDb, model } from "@swtt/core"; // server onlyThe error contract
details is a required property, not an optional one. An empty array is a deliberate statement; an omitted array is a compile error. This exists because the most common failure in generated apps is a UI that renders message and silently drops the per-field reasons the server already sent.
throw AppError.conflict("That reference is taken.", [
{ path: "reference", message: "already used by invoice INV-14", code: "not_unique" },
]);getApiErrorText(e) is the only renderer, and it lives here rather than in each app so the newest form cannot drop half the response:
That reference is taken.
reference: already used by invoice INV-14The contract survives the network. The server serialises AppError.toPayload(); ApiClient rebuilds it with AppError.fromPayload(). A Zod issue raised in a domain operation therefore arrives at a screen still attached to the field it came from, on any platform.
What each module does
| Module | |
|---|---|
| db | connectDb() — reads process.env.MONGO_URI and nothing else. Caches the connection; bufferCommands: false so a missing one fails loudly. |
| model | model(name, schema) — reuses a compiled model so a hot reload cannot throw OverwriteModelError. |
| errors | AppError, getApiErrorText, the payload types. Imports nothing. |
| http | ApiClient — typed fetch that rebuilds AppError from any non-2xx body, times out at 30s, and synthesises a payload when the body is not one. |
| validation | parseInput (every Zod issue becomes one addressable detail) plus objectId, slug, externalUrl, text, isoDate. |
| dto | baseDto, id/idOrNull, iso/isoOrNull, Page<T>. |
externalUrl rejects anything but http/https. That is a security control, not tidiness: the value ends up in an href, and javascript: there is script execution — which Zod's .url() accepts.
Documentation
Full reference: docs/systems/core-runtime.md. The reasoning lives in the ADRs under docs/decisions/.
License
Apache-2.0
