Skip to content

Working with LLM

The doc below would support llm to craft dependency graph using @pumped-fn/core-next

md
# @pumped-fn/core-next Expert (v2.0)

TypeScript DI/reactive programming library. Function-based architecture, lazy evaluation, type-safe resolution, graph-based testing.

## Quick Patterns

| Pattern            | Code                                         | Use When             |
| ------------------ | -------------------------------------------- | -------------------- |
| **Basic DI**       | `derive(deps, factory)`                      | Service dependencies |
| **No Deps**        | `provide(factory)`                           | Config/singletons    |
| **Reactive**       | `derive(dep.reactive, f)`                    | Auto-updates needed  |
| **Lazy**           | `executor.lazy`                              | Defer resolution     |
| **Static Control** | `executor.static`                            | Manual state control |
| **Test Mock**      | `createScope(preset(exec, mock))`            | Testing              |
| **Cleanup**        | `(ctl) => { ctl.cleanup(() => r.close()); }` | Resource disposal    |
| **Batch Resolve**  | `resolves(exec1, exec2, exec3)`              | Multiple executors   |
| **Flow Pattern**   | `flow.provide(spec, factory)`                | Structured workflows |

## Critical Rules ⚠️

| ✅ DO                                           | ❌ NEVER                                      |
| ----------------------------------------------- | --------------------------------------------- |
| Store main executor: `const e = provide(f)`     | Store variants: `const r = e.reactive`        |
| Use inline: `derive(e.reactive, f)`             | Use with scope: `scope.update(e.reactive, v)` |
| Resolve first: `await resolve(e); update(e, v)` | Update unresolved: `update(e, v)`             |
| Functions: `(db) => ({get: () => db.query()})`  | Classes: `new Service(db)`                    |
| Graph testing: Test entire flows                | Unit testing: Test isolated pieces            |

## Core API Reference

### Executor Creation

```typescript
provide(factory, ...metas); // No dependencies
derive(deps, factory, ...metas); // With dependencies
derive([dep1, dep2, dep3], factory, ...metas); // With dependencies array. Factory will receive [resolved1, resolved2, resolved3]
derive({ dep1, dep2, dep3 }, factory, ...metas); // With dependencies object. Factory will receive { dep1: resolved1, dep2: resolved2, dep3: resolved 3}
preset(executor, value); // Override value
placeholder<T>(); // Throws if resolved
resolves(...executors); // Batch resolution helper
```

### Scope Operations

```typescript
scope.resolve(executor); // Get value
scope.update(executor, value); // Update & trigger reactive
scope.onUpdate(executor, cb); // Subscribe to changes
scope.onChange(cb); // Global change events
scope.onRelease(executor, cb); // Cleanup events
scope.use(middleware); // Add middleware
scope.pod(...presets); // Create sub-scope
scope.dispose(); // Cleanup all
```

### Variants (inline only)

```typescript
executor.reactive  // Triggers updates
executor.lazy      // Returns Accessor<T>
executor.static    // Returns Accessor<T> (eager)

// Accessor<T> interface:
accessor.get(): T              // Current value
accessor.update(v | fn): void  // Update value
accessor.resolve(force?): T    // Re-resolve
accessor.release(): void       // Release
accessor.lookup(): State<T>    // Check state without resolving
```

## Meta System 🏷️

Type-safe metadata with StandardSchema:

```typescript
// Define meta with schema
const serviceName = meta("service", string());
const version = meta("version", object({ major: number() }));

// Apply to executors
const api = provide(factory, serviceName("user-api"), version({ major: 2 }));

// Retrieve meta
serviceName.find(api); // 'user-api'
version.get(api); // [{ major: 2 }]
serviceName.some(api); // true

// Use in middleware
const telemetry = middleware({
  init: (scope) =>
    scope.onChange((event, exec) => {
      const name = serviceName.find(exec);
      if (name) metrics.record(event, name);
    }),
});
```

## Controller Parameter 🎮

Every factory receives controller with enhanced capabilities:

```typescript
// provide((ctl) => ...) or derive(deps, (deps, ctl) => ...)
ctl.cleanup(() => dispose()); // Register cleanup
ctl.release(); // Release self
ctl.scope; // Access parent scope
ctl.meta; // Access executor metadata

// Scope access patterns
const manager = provide((ctl) => {
  // Create sub-scope
  const pod = ctl.scope.pod(preset(config, override));
  ctl.cleanup(() => pod.dispose());

  // Access other executors
  const other = await ctl.scope.resolve(otherExec);

  // Self-management
  if (shouldStop) ctl.release();
});
```

## Advanced Patterns

### Static Accessor Control

```typescript
// Build control systems with .static
const ctrl = derive(config.static, (cfgAccessor) => ({
  update: (v) => cfgAccessor.update(v),
  reset: () => cfgAccessor.update(defaultConfig),
  get: () => cfgAccessor.get(),
  check: () => cfgAccessor.lookup(), // Check without resolving
}));

// Mixed reactive + control
const svc = derive([state.reactive, config.static], ([state, cfgCtl], ctl) => {
  // React to state changes, control config
  if (state.error) cfgCtl.update((c) => ({ ...c, retry: true }));
  ctl.cleanup(() => console.log("cleaning"));
  return { state, control: cfgCtl };
});
```

### Middleware System

```typescript
// Enhanced middleware with events
const analytics = middleware({
  init: (scope) => {
    scope.onChange((event, executor, value) => {
      // 'resolving' | 'resolved' | 'updated' | 'released'
      track(event, meta("name").find(executor));
    });

    scope.onUpdate(userState, (accessor) => {
      // React to specific executor updates
      logUserChange(accessor.get());
    });
  },

  // Transform values
  resolve: (value, executor) => {
    if (shouldSanitize(executor)) {
      return preset(executor, sanitize(value));
    }
    return value;
  },

  dispose: async () => {
    await analytics.flush();
  },
});
```

## Service Architecture

### Function-Based (✅ Preferred)

```typescript
// Handles async dependencies elegantly
const createAPI = async (config: Config, http: Http) => {
  await http.connect(config.baseUrl); // Async init

  return {
    async get(path: string) {
      return http.get(config.baseUrl + path);
    },
    async post(path: string, data: any) {
      return http.post(config.baseUrl + path, data);
    },
  };
};

const api = derive([config.reactive, httpClient], createAPI);
```

### Why Not Classes (❌)

```typescript
// PROBLEMS with classes:
class APIService {
  constructor(private config: Config, private http: Http) {
    // ❌ Can't handle async init in constructor
    // ❌ Can't react to config changes
    // ❌ Encourages mutable state
  }
}

// ❌ Classes break reactive updates
const api = derive(
  [config.reactive, http],
  (cfg, http) => new APIService(cfg, http)
); // Service never updates when config changes
```

## Graph-Based Testing 🧪

Test entire dependency graphs, not units:

```typescript
// Configure entire system for testing
const testScope = createScope(
  preset(environment, "test"),
  preset(database, mockDb),
  preset(logger, silentLogger)
);

// Test complete flows
test("user registration flow", async () => {
  const scope = createScope(
    preset(emailService, mockEmail),
    preset(database, inMemoryDb)
  );

  try {
    // Test entire registration graph
    const registration = await scope.resolve(registrationFlow);
    const result = await registration.execute({
      email: "test@example.com",
      password: "secure123",
    });

    // Verify side effects
    expect(mockEmail.sent).toHaveLength(1);
    expect(await inMemoryDb.users.count()).toBe(1);
  } finally {
    await scope.dispose();
  }
});

// Concurrent testing with isolated scopes
await Promise.all([test1WithScope(), test2WithScope(), test3WithScope()]); // No interference between tests
```

### State Management Pattern

```typescript
// Global state with reactive updates
const appState = provide(() => ({
  user: null,
  theme: "light",
  notifications: [],
}));

// Derived states
const isAuthenticated = derive(
  appState.reactive,
  (state) => state.user !== null
);

const unreadCount = derive(
  appState.reactive,
  (state) => state.notifications.filter((n) => !n.read).length
);

// State updates
await scope.update(appState, (state) => ({
  ...state,
  user: { id: "123", name: "Alice" },
}));
```

## Performance Guidelines

### Container Selection

| Type        | Best For              | Avoid When        | Performance      |
| ----------- | --------------------- | ----------------- | ---------------- |
| Normal      | Standard deps         | Need lazy loading | O(n) resolution  |
| `.reactive` | Auto-updates          | Static values     | O(m) propagation |
| `.lazy`     | Expensive/conditional | Always need value | Deferred O(n)    |
| `.static`   | Manual control        | Frequent updates  | O(1) access      |

### Optimization Patterns

```typescript
// Conditional resolution with lazy
const service = derive(
  { cfg: config.reactive, db: database.lazy },
  async ({ cfg, db }) => {
    // Only resolve DB if needed
    if (cfg.useDb) {
      const database = await db.resolve();
      return createDbService(database);
    }
    return createMemoryService();
  }
);

// Batch updates for performance
await scope.update(state, (s) => ({
  ...s,
  multiple: "changes",
  at: "once",
})); // Single reactive propagation
```

## Debugging Tools

```typescript
// Comprehensive tracing
scope.onChange((event, exec, val) => {
  const name = meta("name").find(exec);
  console.log(`[${event}] ${name || "anonymous"}: ${val}`);
});

// State inspection
const state = accessor.lookup();
switch (state?.kind) {
  case "resolved":
    console.log("Value:", state.value);
    break;
  case "rejected":
    console.error("Error:", state.error);
    break;
  case "pending":
    console.log("Still resolving...");
    break;
}

// Debug metadata
const debug = meta(
  "debug",
  object({
    created: string(),
    purpose: string(),
  })
);

const svc = provide(
  factory,
  debug({
    created: new Date().toISOString(),
    purpose: "User authentication",
  })
);
```

## Common Issues & Solutions

| Error                   | Cause                   | Fix                                      |
| ----------------------- | ----------------------- | ---------------------------------------- |
| "Executor not resolved" | `.get()` before resolve | `await scope.resolve(e)` first           |
| "Maximum call stack"    | Circular reactive deps  | Break with `.static` or restructure      |
| Missing updates         | Not using `.reactive`   | Change to `derive(dep.reactive, f)`      |
| Memory leaks            | No cleanup/disposal     | Add `ctl.cleanup()` or `scope.dispose()` |
| Type inference fails    | Complex deps            | Use `as const` or explicit types         |
| Async in constructor    | Using classes           | Switch to functions                      |
| Flow validation fails   | Schema mismatch         | Check input/output schemas               |

## Decision Tree

```
Need DI? → provide/derive
├─ Changes? → .reactive
├─ Expensive? → .lazy
├─ Control? → .static
├─ Testing? → preset + graph testing
└─ Workflow? → flow.provide/derive

Service Layer?
├─ Sync deps → (deps) => service
├─ Async deps → async (deps) => service
├─ Cleanup → (deps, ctl) => { ctl.cleanup(...); }
├─ Control → derive(e.static, (accessor) => controlApi)
├─ Scope access → (ctl) => { /* use ctl.scope */ }
└─ Validation → flow.provide({ input, output }, factory)

State Management?
├─ Local → single reactive executor
├─ Derived → derive(state.reactive, transform)
├─ Global → shared scope with executors
└─ Updates → scope.update(executor, value)

Testing Strategy?
├─ Unit → Individual executor with mocks
├─ Integration → Graph with presets
├─ E2E → Full scope with real deps
└─ Concurrent → Isolated scopes
```

## Anti-Pattern Gallery

```typescript
// ❌ WRONG
const r = counter.reactive;           // Storing variant
scope.onUpdate(counter.reactive, cb); // Using variant with scope
class Svc { constructor(db) {} }      // Class with deps
await scope.update(exec, val);        // Update before resolve
new Database() in derive()            // Side effects in factory
derive([a, b, c, d, e, f, g])        // Too many deps (refactor!)

// ✅ CORRECT
const c = provide(() => 0);           // Store main
derive(c.reactive, v => v * 2);       // Inline variant
const svc = (db) => ({ query: db.q }); // Function factory
await scope.resolve(exec); scope.update(exec, val); // Resolve first
derive(deps, async () => new Database()) // Async for side effects
derive(serviceGroup, ([a, b, c]) => ...) // Group related deps
```

Released under the MIT License.