This file provides guidance when working with code in this repository.
Harper is a Node.js unified development platform that fuses a document database (RocksDB-backed), in-memory cache, application runtime, and messaging broker (WebSockets, MQTT, NATS) into a single in-process runtime. This directory is the open-source core (harper npm package, Apache-2.0), which is the base for the enterprise harper-pro wrapper above it.
# Build
npm run build # TypeScript → dist/ via tsconfig.build.json
npm run build:watch # Incremental watch build
# Lint / Format
npm run lint # oxlint (warnings = errors)
npm run lint:fix # Auto-fix
npm run format:write # Prettier
# Test — run specific suites
npm run test:unit # All unit tests (mocha)
npm run test:unit:main # Core unit tests (excludes apiTests, lmdb, resources)
npm run test:unit:resources # Resource layer tests
npm run test:unit:server # Server layer tests
npm run test:unit:dataLayer # Data layer tests
npm run test:unit:components # Component/plugin system tests
npm run test:unit:security # Security tests
npm run test:unit:apitests # API tests (stops running server first)
npm run test:unit:lmdb # LMDB storage engine tests
npm run test:integration # Full integration test suiteRun a single test file directly:
npx mocha unitTests/resources/mytest.jsTypeScript is stripped at runtime via --conditions=typestrip (Node.js native type stripping) — no compilation required for development. Use npm run test:unit:typestrip to run tests with this mode.
Components (components/)
The plugin/application loader. Applications export a handleApplication(scope) function. Scope is the primary object passed to apps; it exposes:
scope.options—OptionsWatcherfor live-reloaded YAML configscope.resources— access to database tables and registered resourcesscope.server— the HTTP server handle
Files within a component are discovered via micromatch glob patterns and automatically mapped to URL paths.
Server (server/)
Two HTTP stacks coexist:
- Native layer (
server/http.ts) — direct socket handling for HTTP/1.1, HTTPS, HTTP/2, and WebSockets in one path; highest performance - Fastify layer (
server/fastifyRoutes.ts) — used for legacy custom functions; wraps Fastify with autoload
All inbound protocols (REST, GraphQL, MQTT, NATS, WebSockets) eventually resolve to the same Resource interface.
Resources (resources/)
The universal abstraction. Everything that can be queried or mutated — database tables, caches, message topics, custom endpoints — extends Resource (resources/Resource.ts).
Static methods (Resource.get, Resource.put, Resource.post, Resource.delete, Resource.patch, Resource.subscribe) are the entry points and are automatically wrapped with transactional() for transaction management. Override instance methods (get, put, etc.) for custom behavior.
Table.ts is the database table implementation (~177KB) — the most complex file in the codebase.
Data Layer (dataLayer/)
Legacy translation modules plus SQL translation (sqlTranslator/) via AlaSQL; these should be avoided. The storage engine is selectable via HARPER_STORAGE_ENGINE=lmdb.
Configuration (config/)
YAML-based. configUtils.js parses config; RootConfigWatcher.ts enables hot reload. Environment variables override YAML values.
Utility (utility/)
Logging, error types, helpers, async utilities.
transactional() wrapper — All static Resource methods go through this. It ensures async operations run inside a database transaction. Use contextStorage (AsyncLocalStorage) to access the current transaction context without passing it explicitly.
Resource discovery — A component's config file maps glob patterns to URL paths. Files matching a pattern become routable resources automatically; no explicit route registration is needed.
Lazy loading — GraphQL, secure sandboxing, and tarball extraction are imported on demand. Do not add top-level imports for these modules.
TypeScript + type stripping — Source files are .ts but Node.js runs them directly via type stripping in development. The dist/ directory is the compiled production artifact. Both .ts and legacy .js files coexist; new code should be .ts.
Minimal dependencies — dependencies.md documents the rationale for every dependency. Adding a new dependency requires justification; implementing something ourselves is often preferred.
Resourcestatic methods must stay wrapped withtransactional()— removing this breaks transaction isolation.- Worker threads (
server/threads/) receiveworkerData.noServerStart = trueto prevent recursive server startup; never start the server inside a worker. contextStorage(AsyncLocalStorage) carries per-request context (user, transaction) across async boundaries — this is how authorization and transactions work without explicit parameter threading.- Tests under
unitTests/apiTests/require the server to be stopped first (node ./dist/bin/harper.js stop) —test:unit:apitestsdoes this automatically. @exportannotation on a schema class auto-generates a REST API for that table — this is the primary developer-facing API.