hyper-zepto
v0.1.0
Published
ScoutOS ports (data, cache, blob) facade: local dev adapters + ScoutOS REST adapters behind one interface
Maintainers
Readme
hyper-zepto
One data access layer, two environments. hyper-zepto implements the ScoutOS
ports (data, cache, blob) as TypeScript interfaces with two
interchangeable adapter sets:
- Local mode — in-process adapters for development. Documents persist to JSON files, blobs to the filesystem, cache in memory with real TTL semantics. No services to run.
- Remote mode — thin clients for the ScoutOS
/_ports/*REST API (https://scoutos.live/docs). When your app is deployed to ScoutOS, the same code talks to the platform's managed adapters.
Your app only depends on the port interfaces, so switching environments is a config change, not a code change.
Install
npm install hyper-zeptoNode 18+ (uses built-in fetch).
Quick start
import { createPorts } from "hyper-zepto";
const ports = createPorts(); // local in dev, remote when SCOUTOS_PORTS_URL is set
// Data port — Mongo-style documents
const { id } = await ports.data.create("users", { name: "Ada", role: "admin" });
await ports.data.update("users", id, { $set: { active: true }, $inc: { logins: 1 } });
const { documents } = await ports.data.find("users", {
filter: { role: { $in: ["admin", "owner"] } },
sort: { name: 1 },
limit: 20,
});
// Cache port
await ports.cache.set("session:abc", { userId: id }, 3600);
const { value, found } = await ports.cache.get("session:abc");
await ports.cache.incr("hits");
// Blob port
await ports.blob.put("avatars/ada.png", pngBytes, "image/png");
const { url } = await ports.blob.sign("avatars/ada.png", "get", { expiresIn: 600 });Mode selection
// Auto (default): remote if SCOUTOS_PORTS_URL or SCOUTOS_APP_URL is set, else local
createPorts();
// Explicit local with a custom persistence directory (default ".zepto")
createPorts({ mode: "local", dir: ".devdata" });
createPorts({ mode: "local", dir: null }); // in-memory data store
// Explicit remote
createPorts({
mode: "remote",
baseUrl: "https://myapp.scoutos.live",
token: process.env.SCOUTOS_TOKEN,
});Environment variables read in auto mode:
| Variable | Purpose |
|---|---|
| SCOUTOS_PORTS_URL / SCOUTOS_APP_URL | Base URL of the ScoutOS app → enables remote mode |
| SCOUTOS_TOKEN / SCOUTOS_API_KEY | Bearer token for app identity |
Port surface (mirrors ScoutOS REST API)
- DataPort —
create,get,replace,update($set,$unset,$inc,$push,$pull,$rename),delete,find(filters$gt/$lt/$in/$or/$and/$regex/$exists/..., sort, projection, cursor pagination),count,bulk(max 1000 ops),createIndex/listIndexes/dropIndex(unique + sparse),listCollections,dropCollection,health. - CachePort —
get,set(TTL, 1MB value limit),delete,exists,ttl(-1 no TTL, -2 missing),incr,decr. - BlobPort —
put(100MB limit, slash-separated keys),get,delete,meta,list(prefix + cursor),sign(time-limited GET/PUT URLs),copy.
Errors throw PortError with code and status matching the ScoutOS error
schema (DATA_DUPLICATE_KEY, BLOB_FILE_NOT_FOUND, VALUE_TOO_LARGE, ...),
so error handling works the same in both modes.
Using individual adapters
import { LocalDataPort, RemoteCachePort } from "hyper-zepto";
const db = new LocalDataPort(".devdata/data");
const cache = new RemoteCachePort({ baseUrl: "https://myapp.scoutos.live", token: "..." });Development
npm test # vitest
npm run build # tsup -> dist/Known local-mode differences
blob.sign()returns afile://URL — fine for dev, not a real signed URL.find().totalis exact locally; the remote API may return approximate totals.- Local data writes are debounced (~25ms); call
LocalDataPort.flush()on shutdown if you need a guaranteed write.
