
Research
/Security News
60 Malicious Ruby Gems Used in Targeted Credential Theft Campaign
A RubyGems malware campaign used 60 malicious packages posing as automation tools to steal credentials from social media and marketing tool users.
A lightweight, TypeScript-based dependency injection system designed for Astro applications with SSR support
A lightweight, TypeScript-based dependency injection system designed primarily for Astro applications, with first-class SSR (Server-Side Rendering) support.
sv-inject
provides a framework-agnostic, minimalistic DI system built on modern TypeScript features such as decorators and metadata. It supports request-scoped service containers to ensure safe and isolated service instances in concurrent server environments.
@Service()
) and dependency injection (@inject()
)postConstruct
)important: since this framework should be minimal, no "module scope" will be implemented. If you fear "global state pollution", you have to add containers to the root injection context, yourself.
Multi token/instance etc is also not part of this projects scope.
npm install sv-inject
This library uses experimental decorators and metadata reflection. You must enable these options in your tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
import { Service, inject } from 'sv-inject';
// Define a service
@Service()
class UserService {
constructor() {
// Service initialization
}
getUser(id: string) {
// Implementation
}
// Optional lifecycle hook
postConstruct() {
// Initialization after construction
}
}
// Define a service with dependencies
@Service()
class AuthService {
private userService = svInject(UserService);
constructor() {
// Service initialization
}
authenticate(credentials: any) {
// Use injected userService
const user = this.userService.getUser(credentials.userId);
// Implementation
}
}
import { svInject } from 'sv-inject';
import { AuthService } from './services/auth.service';
// In a component
const authService = svInject(AuthService);
authService.authenticate(credentials);
The svInject()
function pulls the service instance from the current injection context (global or request-scoped).
In SSR (Server-Side Rendering), every HTTP request runs in a shared server environment. Without proper isolation, service instances can leak data between users. sv-inject solves this by providing request-scoped containers β each request gets its own dependency graph, preventing cross-request pollution.
These request containers are created with makeInjectionContext()
, wrapping any async render or middleware logic.
For SSR capabilities in Astro, the application must be built in SSR mode (servermode), at least in standalone mode. The makeInjectionContext
function is designed for Astro but works with any other Vite-based SSR app that has an app render cycle within a promise-based rendering system.
import { type ApplicationConfig } from 'sv-inject';
import { makeInjectionContext } from "sv-inject/server"
// In an Astro middleware
export const MyMiddleware = defineMiddleware(async (context, next) => {
return new Promise<Response>(async (resolve, reject) => {
// Define request-specific configuration
const ssrConfig: ApplicationConfig = [
{
token: REQUEST_TOKEN,
provide: context.request,
},
{
token: COOKIES_TOKEN,
provide: context.cookies,
}
];
// Create a unique container for this request
makeInjectionContext(async () => {
const response = await next();
resolve(response);
}, ssrConfig).catch(reject);
});
});
By default this library uses import.meta.env.SSR
to detect SSR contexts.
If this is not available in your framework you have to use:
export function setSSRDetection(isSSRfn : () => boolean)
import { type ApplicationConfig, setSSRDetection } from 'sv-inject';
import { makeInjectionContext } from "sv-inject/server";
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
setSSRDetection(() => typeof window === "undefined");
// In a NextJS middleware
export async function middleware(request: NextRequest) {
// Define request-specific configuration
const ssrConfig: ApplicationConfig = [
{
token: { id: 'REQUEST' },
provide: request,
}
];
// Create a unique container for this request and wrap the response
return makeInjectionContext(async () => {
// Process the request and get the response
// You can perform any operations that need the injection context here
const response = NextResponse.next();
// You can also modify the response if needed
response.headers.set('x-middleware-cache', 'no-cache');
return response;
}, ssrConfig);
}
@Service()
Marks a class as injectable, making it available for dependency injection.
@Service()
class UserService {
// Class implementation
}
@inject(options?)
Marks constructor parameters for injection.
constructor(@inject() userService: UserService) {
// Constructor implementation
}
Options:
key?: string
- Custom key for the injectiondef?: { prototype: { constructor: Function } }
- Class definitiontoken?: Tokenizable
- Token for the injectionsvInject<T>(token: new (...args: any[]) => T): T
Injects a service by class constructor, throws error if its not registered.
const userService = svInject(UserService);
svInjectOptional<T>(token: Tokenizable<T>): T | undefined
Injects a service by token, returning undefined if not found.
const request = svInjectOptional<Request>(REQUEST_TOKEN);
initContainer(): Container
Initializes or retrieves the DI container if it is already initialized. A container will be initialized per request or on CSR on first render.
const container = initContainer();
Container
The core container class for dependency injection.
Methods:
registerProvider(key: Tokenizable, providedInstance: any)
: Registers a provider with a tokenregister(key: string, instance: any)
: Registers an instance with a string keygetByToken<T>(token: Tokenizable<T>): T
: Gets an instance by tokengetByTokenOptional<T>(token: Tokenizable): T | undefined
: Gets an instance by token, returning undefined if not foundgetByClass<T>(token: new (...args: any[]) => T): T
: Gets an instance by class constructorget<T>(key: string): T
: Gets an instance by string keyhas(key: string): boolean
: Checks if an instance existsloadConfig(config: ApplicationConfig)
: Loads configuration into the containerpostConstruct()
: Calls postConstruct lifecycle hook on all registered instancessetGlobalAppConfig(config: ApplicationConfig)
Sets the global application configuration for the application. In SSR mode, this is used to set the global configuration for the server, but it will not be request-scoped.
Setting the global config must happen in a suitable place of the app, before any container is initialized. Suitable places should be: index.ts or similar.
import { createToken } from "./sv-inject";
const API_URL_TOKEN = createToken<string>("https://my.api.com")
setGlobalAppConfig([
{
token: { id: 'API_URL' },
provide: 'https://api.example.com'
}
]);
makeInjectionContext<T>(callback: () => Promise<T>, config?: ApplicationConfig): Promise<T>
Creates an injection context utilizing async local storage and an application container. This method is mandatory for SSR request context aware injection containers.
await makeInjectionContext(async () => {
// Your SSR code here
return response;
}, config);
sv-inject
supports token-based dependency injection, allowing you to inject services, values, or request-specific objects (like cookies or headers) without relying solely on class constructors.
A token can be create with a unique id
:
import { createToken } from "./sv-inject";
const REQUEST_TOKEN = createToken("REQUEST");
π‘ Important: All tokens must be unique and non-empty. Failing to do so may result in collisions or unexpected injection behavior.
It is strongly advised to define all tokens within your ApplicationConfig
:
import { createToken } from "./sv-inject";
const REQUEST_TOKEN = createToken("REQUEST");
const config: ApplicationConfig = [
{
token: REQUEST_TOKEN,
provide: request
},
];
For the global AppConfig (or initial tokens/services etc):
For Global and CSR with setGlobalAppConfig(config)
in index of your application.
β οΈ Important everything in this config will NOT be request scoped. All configs that are request scoped on SSR must be used separately.
For SSR with makeInjectionContext(asyncfun, config)
.
These tokens are bound to the request lifecycle, ensuring safe and isolated access per user.
Tokens that only exist during SSR should be marked as optional using:
import { createToken } from "./AppInjector";
const SSR_ONLY_TOKEN = createToken<MyType>("SSR_ONLY_TOKEN");
const value = svInjectOptional(SSR_ONLY_TOKEN);
This avoids runtime errors when rendering in non-SSR or static contexts.
Do not access SSR-only tokens or runtime-injected tokens:
Instead, inject them dynamically or in the postConstruct
lifecycle hook:
@Service()
class Example {
private request: Request;
postConstruct() {
this.request = svInject({ id: 'REQUEST' });
}
}
All services in sv-inject
are singletons by default within their container context.
If a service or state needs to be reused or injected dynamically, register it manually using:
initContainer().registerProvider(token, instance);
For example:
initContainer().registerProvider({ id: 'LOCALE' }, 'en-US');
FAQs
A lightweight, TypeScript-based dependency injection system designed for Astro applications with SSR support
The npm package sv-inject receives a total of 10 weekly downloads. As such, sv-inject popularity was classified as not popular.
We found that sv-inject demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
A RubyGems malware campaign used 60 malicious packages posing as automation tools to steal credentials from social media and marketing tool users.
Security News
The CNA Scorecard ranks CVE issuers by data completeness, revealing major gaps in patch info and software identifiers across thousands of vulnerabilities.
Research
/Security News
Two npm packages masquerading as WhatsApp developer libraries include a kill switch that deletes all files if the phone number isnβt whitelisted.