Advanced API#

Factories, messaging, and other advanced utilities.

defineFactory#

Create an injectable factory with lifecycle management.

TSX
function defineFactory<T>(
    setup: (ctx: SetupFactoryContext, ...args: any[]) => T,
    lifetime: InstanceLifetimes,
    typeIdentifier?: string
): (...args: any[]) => T & { dispose?: () => void };

Parameters#

NameTypeDescription
setup(ctx, ...args) => TFactory setup function
lifetimeInstanceLifetimesInstance lifetime mode
typeIdentifierstringOptional unique identifier

SetupFactoryContext#

PropertyTypeDescription
onDeactivated(fn: () => void) => voidRegister cleanup callback
subscriptionsSubscriptionHandlerSubscription manager
overrideDispose(fn) => voidCustom disposal logic

Examples#

TSX
import { defineFactory, InstanceLifetimes } from 'sigx';

// Singleton service
const useApi = defineFactory((ctx) => {
    const cache = new Map();
    
    ctx.onDeactivated(() => {
        cache.clear();
    });
    
    return {
        fetch: async (url: string) => {
            if (cache.has(url)) return cache.get(url);
            const data = await fetch(url).then(r => r.json());
            cache.set(url, data);
            return data;
        },
        invalidate: (url: string) => cache.delete(url)
    };
}, InstanceLifetimes.Singleton);

// Factory with parameters
const useRepository = defineFactory((ctx, entityName: string) => {
    return {
        getAll: () => fetch(`/api/${entityName}`),
        getById: (id: number) => fetch(`/api/${entityName}/${id}`)
    };
}, InstanceLifetimes.Transient);

// Usage
const api = useApi();
const usersRepo = useRepository('users');

InstanceLifetimes#

Enum for factory instance lifetimes.

TSX
enum InstanceLifetimes {
    Transient = 0,  // New instance every time
    Scoped = 1,     // One per scope/provider
    Singleton = 2   // Single global instance
}

Examples#

TSX
import { defineFactory, InstanceLifetimes } from 'sigx';

// New instance every call
const useTransient = defineFactory(setup, InstanceLifetimes.Transient);

// Shared within a scope
const useScoped = defineFactory(setup, InstanceLifetimes.Scoped);

// Single global instance
const useSingleton = defineFactory(setup, InstanceLifetimes.Singleton);

SubscriptionHandler#

Helper class for managing cleanup subscriptions.

TSX
class SubscriptionHandler {
    add(unsub: () => void): void;
    unsubscribe(): void;
}

Examples#

TSX
const useWebSocket = defineFactory((ctx) => {
    let ws: WebSocket;
    
    return {
        connect: (url: string) => {
            ws = new WebSocket(url);
            
            // Auto-cleanup on factory deactivation
            ctx.subscriptions.add(() => ws.close());
            
            return ws;
        }
    };
}, InstanceLifetimes.Singleton);

createTopic#

Create a pub/sub topic for messaging.

TSX
function createTopic<T>(options?: {
    namespace?: string;
    name?: string;
}): Topic<T>;

Parameters#

NameTypeDescription
options.namespacestringOptional namespace for debugging
options.namestringOptional name for debugging

Returns#

Topic<T> with:

  • publish(data: T): void - Send to all subscribers
  • subscribe(handler: (data: T) => void): Subscription - Add subscriber
  • destroy(): void - Remove all subscribers

Examples#

TSX
import { createTopic } from 'sigx';

// Create typed topic
const userEvents = createTopic<{
    type: 'login' | 'logout';
    userId: number;
}>();

// Subscribe
const sub = userEvents.subscribe((event) => {
    console.log(`User ${event.userId} ${event.type}`);
});

// Publish
userEvents.publish({ type: 'login', userId: 123 });

// Unsubscribe
sub.unsubscribe();

// Or destroy all
userEvents.destroy();

toSubscriber#

Create a read-only subscriber from a topic.

TSX
function toSubscriber<T>(topic: Topic<T>): {
    subscribe: (handler: (data: T) => void) => Subscription;
};

Parameters#

NameTypeDescription
topicTopic<T>Full topic to wrap

Returns#

Object with only subscribe method (no publish).

Examples#

TSX
import { createTopic, toSubscriber } from 'sigx';

// Internal: full control
const _notifications = createTopic<{ message: string }>();

// External: read-only
export const notifications = toSubscriber(_notifications);

// Can subscribe
notifications.subscribe(({ message }) => alert(message));

// Cannot publish (type error)
notifications.publish({ message: 'Hello' }); // Error!

Topic Interface#

TSX
interface Topic<T> {
    publish(data: T): void;
    subscribe(handler: (data: T) => void): Subscription;
    destroy(): void;
}

Subscription Interface#

TSX
interface Subscription {
    unsubscribe(): void;
}

guid#

Generate a unique identifier.

TSX
function guid(): string;
type guid = string;

Examples#

TSX
import { guid } from 'sigx';

const id = guid();
// "a1b2c3d4-e5f6-7890-abcd-ef1234567890"

valueOf#

Type assertion helper.

TSX
function valueOf<T>(obj: any): T;

Examples#

TSX
import { valueOf } from 'sigx';

const data = valueOf<MyType>(untypedData);

Patterns#

Event Bus#

TSX
import { createTopic } from 'sigx';

// Centralized event bus
export const eventBus = {
    userLogin: createTopic<{ userId: number }>(),
    userLogout: createTopic<void>(),
    notification: createTopic<{ message: string; type: 'info' | 'error' }>(),
    themeChange: createTopic<'light' | 'dark'>()
};

// Usage
eventBus.userLogin.publish({ userId: 123 });
eventBus.notification.subscribe(({ message }) => toast(message));

Service Layer#

TSX
import { defineFactory, createTopic, InstanceLifetimes, signal } from 'sigx';

const useCartService = defineFactory((ctx) => {
    const state = signal({ items: [] as CartItem[] });
    const cartUpdated = createTopic<{ itemCount: number }>();
    
    ctx.onDeactivated(() => {
        cartUpdated.destroy();
    });
    
    return {
        get items() { return state.items; },
        get count() { return state.items.length; },
        
        onUpdate: toSubscriber(cartUpdated),
        
        add: (item: CartItem) => {
            state.items = [...state.items, item];
            cartUpdated.publish({ itemCount: state.items.length });
        },
        
        remove: (id: string) => {
            state.items = state.items.filter(i => i.id !== id);
            cartUpdated.publish({ itemCount: state.items.length });
        },
        
        clear: () => {
            state.items = [];
            cartUpdated.publish({ itemCount: 0 });
        }
    };
}, InstanceLifetimes.Singleton);

Async Operations#

TSX
const useAsyncOperation = defineFactory((ctx) => {
    const state = signal({ 
        loading: false, 
        error: null as Error | null,
        data: null as any
    });
    
    const execute = async <T>(operation: () => Promise<T>): Promise<T | null> => {
        state.loading = true;
        state.error = null;
        
        try {
            const result = await operation();
            state.data = result;
            return result;
        } catch (err) {
            state.error = err instanceof Error ? err : new Error(String(err));
            return null;
        } finally {
            state.loading = false;
        }
    };
    
    return {
        get loading() { return state.loading; },
        get error() { return state.error; },
        get data() { return state.data; },
        execute
    };
}, InstanceLifetimes.Transient);