@marcus-dutton/atlas
v2.0.15
Published
Atlas is a small, pragmatic dependency-injection framework for Node.js + TypeScript with first-class support for AOT provider emission and a generator (`ad`) that scaffolds Express apps wired to the DI system.
Maintainers
Readme
Atlas — TypeScript Dependency Injection Framework
Atlas is a small, pragmatic dependency-injection framework for Node.js + TypeScript with first-class support for AOT provider emission and a generator (ad) that scaffolds Express apps wired to the DI system.
This README covers the high-level concepts, a quick start, AOT usage and publishing notes.
Features
- Lightweight DI Container (async resolution)
- Provider helpers (Express, Socket.IO, Mongoose placeholders)
- Lifetimes and lifecycle hooks (onInit / onDestroy)
- App generator (
ad) to scaffold apps (interactive or --yes) - AOT emitter + runtime hydrator: emit provider manifest at build-time and hydrate at runtime with no discovery
Quick start (developer)
Install dependencies
npm installBuild the project
npm run build # or build + AOT emit npm run emit:aotRun the CLI (local dev)
npm run dev # or invoke the compiled CLI after build node dist/cli/index.jsGenerate a new app
The CLI includes a generator
ad new <name>that scaffolds an Express application wired into Atlas providers. Use interactive prompts or pass--yesto accept defaults.
Using the Container (code snippets)
The core Container supports synchronous and asynchronous resolution. Example:
import { Container } from './src/di/container';
const container = new Container();
// register a simple value provider
container.register({ provide: 'CONFIG', useValue: { port: 3000 } });
// register a factory provider
container.register({ provide: 'LOGGER', useFactory: () => console, deps: [] });
// resolve asynchronously
const logger = await container.resolveAsync('LOGGER');
logger.log('Hello from Atlas');Provider helper examples are exported from src/providers (e.g. provideExpress, provideMongoose, provideSocketIo, provideAppInitializer). Generated apps use these helpers.
App bootstrap
Use bootstrapApp(AppConfig) to prepare a Container from an application config class (this will attempt AOT hydration first, then register any providers declared in the AppConfig):
import { bootstrapApp } from './src/run/bootstrap';
import { AppConfig } from './app.config';
const { container } = await bootstrapApp(AppConfig);
// container is ready; call your Application.start(container) or other explicit startup as desiredImportant: bootstrapApp executes APP_INITIALIZER providers so initialization code (DB registration, model registration) runs with DI available.
AOT emitter & runtime hydration
To avoid runtime discovery in production, Atlas supports an AOT emitter that scans your TypeScript sources for AppConfig providers and emits a manifest and small JS registration modules into dist/aot.
Workflow (recommended for production builds):
- Run
tscto compile sources todist. - Run the emitter:
node dist/aot/emitter.js(ornpm run emit:aot). This creates:dist/aot/providers-manifest.json— provider metadatadist/aot/providers.aot.js— inlined providers when possibledist/aot/providers.js— runtime provider invocation fallbackdist/aot/models.js— small models shim (if models discovered)
- At runtime
bootstrapAppcallshydrateContainerFromAOT(container)which prefersproviders.aot.js, falls back toproviders.js, and finally parses the manifest to require modules and call factories.
Model registration
- Use
@Model()decorator to mark classes you want the emitter to pick up. If your model exports a staticregisterModel(conn, container)method the consumer will call it when aMONGOOSE_CONNECTIONis available.
Generator (ad)
The CLI ships a generator that scaffolds an Express application pre-wired to Atlas providers. The generator supports interactive (Y/n) prompts for optional features (Socket.IO, Mongoose) and a --yes non-interactive mode.
Templates are located under src/templates (with runtime fallback templates/ used by the compiled dist generator).
Generator usage and flags
The generator command follows the pattern ad new <name> (the CLI binary is ad when installed globally from this package). Key flags:
--yes: non-interactive mode; accepts default options and auto-installs dependencies.--no-install: do not run package-manager installs after scaffolding (useful in CI or when you want to manage installs yourself).--no-git: skip git initialization and initial commit.--github: (future) attempt to create a GitHub repo and push the initial commit (not enabled by default).--branch <name>: set the initial branch name forgit init(default:main).
Examples:
# interactive (prompts for Socket.IO / Mongoose)
ad new my-app
# non-interactive and install dependencies
ad new my-app --yes
# scaffold without running installs
ad new my-app --yes --no-install
# scaffold without git initialization
ad new my-app --yes --no-gitNotes on installs
- The generator writes a minimal
package.jsoninto the generated project and then runs a package-manager install in that project folder (so dependencies land inside the new project, not the CLI's CWD). - The generator detects available package managers by probing
pnpm,yarn, andnpm(in that order) and runs installs with the detected tool. You can opt out with--no-install. - To ensure installs work correctly when the CLI is globally installed (Windows in particular), the generator runs install commands via the system shell so wrapper executables like
npm.cmd/pnpm.cmdare resolved.
Troubleshooting
- If installs fail with
spawn ENOENTit usually means the chosen package manager isn't on PATH in your environment. Install Node.js and the package manager you prefer (npm is included with Node.js). Example on Windows: install Node.js from https://nodejs.org/. - If git commit fails during initialization, you may need to set
user.nameanduser.emailin your git config:git config --global user.name "Your Name"andgit config --global user.email "[email protected]".
Tests
There are integration tests for the AOT emitter/consumer under tests/. Run the AOT integration test with:
npm run test:aotPublishing to npm
The package includes these helpers in package.json:
build— runs TypeScript compileremit:aot— run the emitter against the compiled distprepublishOnly— configured to runnpm run build && node dist/aot/emitter.jsbefore publish
Publishing steps (local interactive):
Login to npm (interactive):
npm adduser # or npm loginVerify login:
npm whoamiPublish (the
prepublishOnlyhook will build and emit AOT automatically):npm publish --access public
CI / token-based publishing
- For CI, store an npm token in secrets and use
npm ci+npm publish --access publicwith the token configured (e.g.,echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc). I can scaffold a GitHub Actions workflow if you'd like.
Contributing & development
- Run tests and lints as you edit.
- Emit AOT files again when you change providers or app.config templates:
npm run emit:aot.
Using the Config Loader Utility
Atlas provides a utility to load and cache JSON config files with full TypeScript type safety. You can use this utility anywhere in your app:
import { loadConfig } from '@marcus-dutton/atlas';
// Define your config interface
interface MyConfig {
port: number;
dbUri: string;
}
// Load and type your config
const config = await loadConfig<MyConfig>('config.json');
console.log(config.port, config.dbUri);If you do not specify a type, the return type will be any and you will not get IntelliSense or type checking for config properties.
The loader will search for the config file in the current directory, a config/ subdirectory, or the parent directory.
Conventions and recommended patterns
- App authors should export an
AppConfigdecorated object/class (the generator createsapp.config.tsexamples). The emitter looks for@AppConfig({ providers: [...] })usages. - Export models with
@Model()and include astatic registerModel(conn, container)method for deterministic AOT model registration.
License
Specify your license here (add a LICENSE file). If you plan to publish on npm, ensure your package has the proper license metadata.
If you'd like, I can:
- Add an example
Application+ModeldemonstratingregisterModelfor Mongoose. - Add a GitHub Actions workflow to run build + emit + publish using an npm token in secrets.
- Flesh out API docs for
Containerand provider helpers as separate markdown files.
Which of these should I do next?
Atlas DI - backend DI framework inspired by Angular
This repository contains an initial scaffold for a TypeScript-based dependency injection framework for backend servers (Express, Fastify etc.) inspired by Angular's AOT/JIT style and provider system.
What is included:
- Core DI primitives (Container, Injectable, Inject decorators)
- CLI scaffold (generate app, service, controller, gateway, model templates)
- Provider helpers (stubs for Mongoose, Socket.IO, Express)
Run in dev:
# install deps
npm install
# run CLI in dev using ts-node
npm run dev -- generate app my-appExample providers usage in app.config.ts:
import { AppConfig } from '../../src/di/decorators';
import { provideMongoose, provideSocketIo } from '../../src/providers';
@AppConfig({
providers: [
provideMongoose('mongodb://localhost:27017/mydb'),
provideSocketIo()
]
})
export class MyAppConfig {}CLI examples:
# generate an app scaffold
npm run dev -- generate app demo-app
# generate a service in current workspace
npm run dev -- generate service User