npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

targwire

v1.2.1

Published

TargWire – mini IoC para TS/JS com Singleton, Transient e Scoped (ideal para Expo/React Native).

Readme

TargWire

License: MIT

Mini IoC container tipado para TypeScript com Singleton, Transient (Factory) e Scoped (child containers). Ideal para apps Expo/React Native (Hermes), web e Node — zero dependências.

  • ✅ Tokens tipados (Token<T>)
  • ✅ Ciclos de vida: registerSingleton, registerFactory (transient), createScope (scoped)
  • ✅ Overrides e reset (testes/HMR)
  • ✅ Opcionalmente via decorators: @Injectable, @Inject, @Resolve
  • ✅ Plugin registry para registrar módulos do app e dar bootstrapIOC() uma vez só

Instalação

npm i targwire
# ou
yarn add targwire
# ou
pnpm add targwire

O pacote já é publicado transpilado (ESM + CJS) com typings. Não precisa configurar Metro/Babel pra transpilar TS em node_modules.


TL;DR (Exemplo rápido)

1) Defina tokens do domínio (no app):

// src/ioc/tokens.users.ts
import { defineToken } from 'targwire';
import type { GetUsersUseCase } from '@/domain/usecases/get_users_usecase';
import type { UserRepository } from '@/domain/repositories/user_repository';
import type { UserRemoteDataSource } from '@/data/datasources/remote/user_remote_data_source';

export const TOKENS_USERS = {
  RemoteDataSource: defineToken<UserRemoteDataSource>('Users.RemoteDataSource'),
  Repository:       defineToken<UserRepository>('Users.Repository'),
  GetUsersUseCase:  defineToken<GetUsersUseCase>('Users.GetUsersUseCase'),
};

2) Registre seu módulo via plugin (no app):

// src/ioc/plugins.ts  (side-effect)
import { registerIOCPlugin } from 'targwire';
import { TOKENS_USERS } from './tokens.users';
import { UserRemoteDataSourceImpl } from '@/data/datasources/remote/user_remote_data_source';
import { UserRepositoryImpl } from '@/data/repositories/user_repository_impl';
import { GetUsersUseCase } from '@/domain/usecases/get_users_usecase';

registerIOCPlugin((c) => {
  if (!c.isRegistered(TOKENS_USERS.RemoteDataSource))
    c.registerSingleton(TOKENS_USERS.RemoteDataSource, () => new UserRemoteDataSourceImpl());
  if (!c.isRegistered(TOKENS_USERS.Repository))
    c.registerSingleton(TOKENS_USERS.Repository, () =>
      new UserRepositoryImpl(c.resolve(TOKENS_USERS.RemoteDataSource))
    );
  if (!c.isRegistered(TOKENS_USERS.GetUsersUseCase))
    c.registerFactory(TOKENS_USERS.GetUsersUseCase, () =>
      new GetUsersUseCase(c.resolve(TOKENS_USERS.Repository))
    );
});

3) Bootstrap uma vez no entrypoint do app:

// App.tsx
import React from 'react';
import '@/ioc/plugins';                 // importa teus módulos (efeito colateral)
import { bootstrapIOC } from 'targwire';

bootstrapIOC();

export default function App() {
  return null; // sua navegação/telas aqui
}

4) Consumir onde precisar:

import { resolve } from 'targwire';
import { TOKENS_USERS } from '@/ioc/tokens.users';

const getUsers = resolve(TOKENS_USERS.GetUsersUseCase); // transient
const users = await getUsers.execute();

API da biblioteca

// Container raiz pronto:
import {
  rootContainer,
  defineToken,             // <T>(name: string) => Token<T>
  registerValue,           // (token, value)
  registerSingleton,       // (token, () => T)  // lazy
  registerFactory,         // (token, () => T)  // transient
  resolve,                 // (token) => T
  isRegistered,            // (token) => boolean
  reset,                   // (tokens?) => void
  createScope,             // () => Container   // scoped child
  overrideValue,
  overrideSingleton,
  overrideFactory,
  // Decorators (opcional):
  Injectable,              // registra classe como singleton (default) ou transient
  Inject,                  // força token de parâmetro no construtor
  Resolve,                 // injeta propriedade via getter
  // Plugins/Bootstrap
  registerIOCPlugin,       // (registrar: (c: Container) => void)
  bootstrapIOC,            // executa todos os registrars uma vez
} from 'targwire';

Ciclos de vida

  • Singleton: registerSingleton(T, factory). A instância é criada no primeiro resolve(T) e reaproveitada naquele container/escopo.
  • Transient: registerFactory(T, factory). Uma nova instância a cada resolve(T).
  • Scoped: const scope = rootContainer.createScope(). Registros no escopo ficam isolados; resolve procura no escopo e sobe pro pai se necessário.

Padrão de plugins (módulos do app)

A lib não conhece teus domínios. Em vez de importar registradores manualmente no App.tsx, basta centralizar tudo num arquivo src/ioc/plugins.ts:

// src/ioc/plugins.ts
import { registerIOCPlugin } from 'targwire';
import { TOKENS_USERS } from './tokens.users';
// ...imports do módulo

registerIOCPlugin((c) => {
  // registros do módulo Users
});

// registerIOCPlugin(...); // Auth
// registerIOCPlugin(...); // Products

Depois, no App.tsx:

import '@/ioc/plugins';
import { bootstrapIOC } from 'targwire';
bootstrapIOC();

Quer reduzir a 1 import só? Crie src/ioc/bootstrap-app.ts com:

import '@/ioc/plugins';
export { bootstrapIOC as default } from 'targwire';

E no App.tsx:

import bootstrapIOC from '@/ioc/bootstrap-app';
bootstrapIOC();

Uso com decorators (opcional)

Os decorators facilitam o registro/injeção via construtor. Habilite decoradores no seu projeto (experimentalDecorators + emitDecoratorMetadata) e, se quiser inferir tipos automaticamente, instale reflect-metadata e importe-a no entrypoint.

npm i reflect-metadata    # opcional

Registrar classes

import 'reflect-metadata'; // se quiser inferência automática de tipos
import { Injectable, Inject, defineToken, resolve } from 'targwire';

const RepoToken = defineToken<UserRepo>('Users.Repo');

@Injectable() // singleton padrão
class GetUsers {
  constructor(@Inject(RepoToken) private repo: UserRepo) {}
}

@Injectable({ lifetime: 'transient' })
class Controller {
  constructor(private usecase: GetUsers) {} // inferido via reflect-metadata
}

const controller = resolve(defineToken<Controller>('Controller')); // token padrão = nome da classe

Injeção por propriedade

import { Resolve, defineToken } from 'targwire';
import type { Logger } from './logger';

const LoggerToken = defineToken<Logger>('Logger');

class MyComponent {
  @Resolve(LoggerToken)
  logger!: Logger;
}

Sem reflect-metadata, anote parâmetros com @Inject(Token) para evitar erros de token ausente.


Exemplos

Singleton

registerSingleton(TOKENS_USERS.Repository, () =>
  new UserRepositoryImpl(resolve(TOKENS_USERS.RemoteDataSource))
);
const a = resolve(TOKENS_USERS.Repository);
const b = resolve(TOKENS_USERS.Repository);
console.log(a === b); // true

Transient

registerFactory(TOKENS_USERS.GetUsersUseCase, () =>
  new GetUsersUseCase(resolve(TOKENS_USERS.Repository))
);
const x = resolve(TOKENS_USERS.GetUsersUseCase);
const y = resolve(TOKENS_USERS.GetUsersUseCase);
console.log(x === y); // false

Scoped por tela (React Native)

import React, { useMemo } from 'react';
import { rootContainer } from 'targwire';
import { TOKENS_USERS } from '@/ioc/tokens.users';
import { UserRemoteDataSourceImpl } from '@/data/datasources/remote/user_remote_data_source';
import { UserRepositoryImpl } from '@/data/repositories/user_repository_impl';
import { GetUsersUseCase } from '@/domain/usecases/get_users_usecase';

export default function UsersScreenScoped() {
  const scope = useMemo(() => {
    const s = rootContainer.createScope();
    s.registerSingleton(TOKENS_USERS.RemoteDataSource, () => new UserRemoteDataSourceImpl());
    s.registerSingleton(TOKENS_USERS.Repository, () =>
      new UserRepositoryImpl(s.resolve(TOKENS_USERS.RemoteDataSource))
    );
    s.registerFactory(TOKENS_USERS.GetUsersUseCase, () =>
      new GetUsersUseCase(s.resolve(TOKENS_USERS.Repository))
    );
    return s;
  }, []);

  // use s.resolve(...) aqui
  return null;
}

Testes e overrides

import { reset, bootstrapIOC, overrideValue, resolve } from 'targwire';
import { TOKENS_USERS } from '@/ioc/tokens.users';
import '@/ioc/plugins';

beforeEach(() => {
  reset();        // limpa o root container
  bootstrapIOC(); // re-registra os plugins
});

test('override de use case', async () => {
  overrideValue(TOKENS_USERS.GetUsersUseCase, {
    execute: async () => [{ id: '1', name: 'Ana' } as any],
  } as any);

  const uc = resolve(TOKENS_USERS.GetUsersUseCase);
  const users = await uc.execute();
  expect(users[0].name).toBe('Ana');
});

Dicas & Troubleshooting

  • bootstrapIOC is not a function
    Verifique export/import (nomeado vs default) e a ordem dos imports:
    • Importar @/ioc/plugins antes de bootstrapIOC().
    • Use um só estilo:
      import { bootstrapIOC } from 'targwire'; // export nomeado
  • Tokens repetidos em HMR/Metro
    Use Symbol.for('ioc:...') (já fazemos) e, se necessário, reset() durante dev.
  • Alias @
    Configure o alias no Babel/Metro do app. A lib não precisa.

Requisitos de runtime

  • TypeScript 5+
  • Target ES2019+ (ok para Hermes / RN 0.7x+)
  • Sem dependências nativas

Licença

Este projeto está licenciado sob a licença MIT © Rafael Targino.


Exemplo completo (Expo + Decorators + React Query + Zustand)

  • Veja examples/expo-zustand-react-query para um app Expo que usa decorators com TargWire, React Query e Zustand consumindo a API do JSONPlaceholder.
  • Inclui babel.config.js (com @babel/plugin-proposal-decorators e babel-plugin-transform-typescript-metadata) e tsconfig.json configurados para decorators.

Importante (Expo): para que os decorators (@Injectable, @Inject, @Resolve) funcionem, copie para a raiz do seu app:

  • babel.config.js com os plugins acima (decorators deve vir antes do metadata).
  • tsconfig.json com experimentalDecorators e emitDecoratorMetadata habilitados.
  • Além disso, instale como dependências de desenvolvimento: @babel/plugin-proposal-decorators e babel-plugin-transform-typescript-metadata.