Skip to main content

typectx v0.7: Request Suppliers and Unified Types

· 4 min read
@someone635

Version 0.7 brings a cleaner mental model to typectx by renaming "Resources" to "Request Suppliers" and unifying the Resource and Product types into a single Supply type. These breaking changes make the library's intent clearer and reduce conceptual overhead.

Let's dive in.

The Big Rename: Resources → Request Suppliers

In previous versions, we had two kinds of suppliers:

  • Resource Suppliers — for simple values you pack at the entry point
  • Product Suppliers — for values created by factories with dependencies

The problem? "Resource" is vague. It could mean anything—config, constants, database connections. This made it unclear when to use a Resource vs a Product.

v0.7 renames Resource Suppliers to Request Suppliers, which better communicates their primary purpose: holding data from the user's request (session, params, cookies, headers, etc.) that cannot be derived from other suppliers.

// v0.6 - The old way
const $session = market.offer("session").asResource<Session>()

// v0.7 - The new way
const $session = market.add("session").request<Session>()

This isn't just a cosmetic change—it guides better architecture. When you see request(), you immediately know this supplier is for request-scoped data that enters your system from outside.

API Changes: offer()add()

The market API has been streamlined:

v0.6v0.7
market.offer("name")market.add("name")
.asResource<T>().request<T>()
.asProduct({ ... }).product({ ... })

The new names are shorter and more direct:

// v0.6
const $session = market.offer("session").asResource<{ userId: string }>()
const $db = market.offer("db").asProduct({
suppliers: [],
factory: () => createDbConnection()
})

// v0.7
const $session = market.add("session").request<{ userId: string }>()
const $db = market.add("db").product({
suppliers: [],
factory: () => createDbConnection()
})

Unified Types: Resource + ProductSupply

Previously, packing a Resource gave you a Resource<T>, while assembling a Product gave you a Product<T>. This distinction was mostly artificial—both are just value containers with an unpack() method.

v0.7 unifies these into a single Supply type:

// v0.6 types
type Resource<VALUE, SUPPLIER> = {
unpack(): VALUE
supplier: SUPPLIER
}

type Product<VALUE, SUPPLIER, DEPS, RESOLVED, CTX> = {
unpack(): VALUE
deps: DEPS
supplies: RESOLVED
supplier: SUPPLIER
_: { ctx: CTX; packed: boolean }
}

// v0.7 - unified
type Supply<VALUE, SUPPLIER, DEPS, RESOLVED> = {
unpack(): VALUE
deps: DEPS
supplies: RESOLVED
supplier: SUPPLIER
_: { ctx: CTX; packed: boolean }
}

Both $request.pack(value) and $product.assemble(supplies) now return a Supply. The only difference is that request supplies have empty deps and supplies objects, while product supplies have populated ones.

This simplifies generic code that works with both:

// v0.6 - needed to handle both types
function processSupply(supply: Resource | Product) {
return supply.unpack()
}

// v0.7 - just one type
function processSupply(supply: Supply) {
return supply.unpack()
}

Type Renames

If you're working with typectx's types directly, here's the mapping:

v0.6 Typev0.7 Type
ResourceSupplierRequestSupplier
ResourceSupply
ProductSupply

Name Validation

v0.7 enforces that supplier names follow JavaScript identifier rules: they can only contain letters, digits, underscores, or $ signs, and cannot start with a digit.

// ✅ Valid names
market.add("session")
market.add("$user")
market.add("_private")
market.add("user123")

// ❌ Invalid names (will throw)
market.add("123user") // starts with digit
market.add("my-supplier") // contains hyphen
market.add("my.supplier") // contains dot

This ensures supplier names work seamlessly as object keys in deps destructuring.

@typectx/react Updates

The React adapter updates its types to match:

// Type imports
import type {
RequestSupplier, // was ResourceSupplier
Supply, // was Product | Resource
Deps,
Resolved
} from "typectx"

The hooks (useDeps, useAssembleComponent, useAssembleHook) work exactly the same—just with the updated type names internally.

Migration Guide

Step 1: Update market calls

// Before
const $session = market.offer("session").asResource<Session>()
const $db = market.offer("db").asProduct({ ... })

// After
const $session = market.add("session").request<Session>()
const $db = market.add("db").product({ ... })

Step 2: Update type imports (if using them)

// Before
import type { ResourceSupplier, Resource, Product } from "typectx"

// After
import type { RequestSupplier, Supply } from "typectx"

Step 3: Fix any invalid supplier names

If you have supplier names with hyphens, dots, or starting with digits, rename them to valid JavaScript identifiers.

Why These Changes?

  1. Clearer intent: "Request Supplier" immediately communicates that this is for request-scoped data
  2. Simpler types: One Supply type instead of two reduces cognitive load
  3. Better API: market.add().request() and market.add().product() are more concise
  4. Safer names: Enforcing identifier rules prevents edge cases in deps destructuring

These are breaking changes, but the migration is straightforward—mostly find-and-replace.


npm install typectx@latest @typectx/react@latest