@makaio/storage-core
Factory for creating bus-integrated storage namespaces with storage: prefix convention.
Quick Index
Section titled “Quick Index”./src/create-storage-namespace.ts– factory function for storage namespaces./src/create-extension-storage-namespace.ts– extension namespace helper that createsstorage:extension:{name}subjects./src/types.ts– type definitions and extension interfaces./src/index.ts– public exports
Key Contracts
Section titled “Key Contracts”export interface StorageNamespace< N extends string = string, Subjects extends SubjectRecord = SubjectRecord, FilterPayload = unknown, Ext extends StorageNamespaceExtensions = StorageNamespaceExtensions, Schemas extends SchemaRecord = SchemaRecord,> extends BusNamespace<`storage:${N}`, Subjects, FilterPayload, Schemas> { readonly domain: N; readonly extensions: Ext;}
export interface StorageNamespaceConfig< Schemas extends SchemaRecord, Ext extends StorageNamespaceExtensions = StorageNamespaceExtensions,> { schemas: Schemas; extensions?: Ext;}
// Extension point for extensions (declaration merging)export interface StorageNamespaceExtensions {}Usage Workflow
Section titled “Usage Workflow”-
Define Zod schemas for your bus RPC contract:
const schemas = {get: {request: z.object({ id: z.string() }),response: z.object({ item: ItemSchema.nullable() }),},set: {request: z.object({ id: z.string(), item: ItemSchema }),response: z.object({ success: z.boolean() }),},}; -
Create storage namespace with optional extensions:
import { createStorageNamespace } from '@makaio/storage-core';export const ItemStorageNamespace = createStorageNamespace('item', {schemas,extensions: {drizzle: { items: itemsTable },},});// Bus namespace is registered as 'storage:item'// ItemStorageNamespace.domain remains 'item'// Subjects: storage:item.get, storage:item.set -
Export typed subjects for consumers:
export const ItemStorageSubjects = ItemStorageNamespace.subjects; -
Implement handler with mappers:
export function registerDrizzleItemStorage(bus: IMakaioBus, db: MakaioDatabase) {return bus.on(ItemStorageSubjects.get, async (ctx) => {const [row] = await db.select().from(items).where(eq(items.id, ctx.payload.id)).limit(1);ctx.setResult({ item: row ? mapToItem(row) : null });});} -
Consume via bus (storage backend is transparent):
const { item } = await bus.request(ItemStorageSubjects.get, { id: '123' });
Architectural Responsibilities
Section titled “Architectural Responsibilities”- Prefix convention –
createStorageNamespace()registers typed bus subjects understorage:{domain}while preserving the namespacedomainproperty as the unprefixed domain - Schema ownership – Zod schemas define the bus contract; handlers own the mapping
- Handler ownership – Storage namespace creation does not register request handlers; each storage package must register handlers separately
- Extension points –
StorageNamespaceExtensionsinterface enables type-safe plugin data
Seams & Extension Points
Section titled “Seams & Extension Points”Backend Swapping
Section titled “Backend Swapping”Same namespace, different handlers:
// TestsregisterInMemoryItemStorage(bus);
// ProductionregisterDrizzleItemStorage(bus, db);Extension Augmentation
Section titled “Extension Augmentation”Extend StorageNamespaceExtensions for type-safe metadata:
// In @makaio/storage-drizzledeclare module '@makaio/storage-core' { interface StorageNamespaceExtensions { drizzle?: DrizzleSchemaRecord; }}
// Now available with typesItemStorageNamespace.extensions.drizzle?.itemsCustom Storage Domains
Section titled “Custom Storage Domains”Create new storage domains without modifying core:
export const CacheStorageNamespace = createStorageNamespace('cache', { schemas: { ... }, extensions: { redis: redisConfig },});// → storage:cache.get, storage:cache.set, etc.Extension Storage Domains
Section titled “Extension Storage Domains”Use createExtensionStorageNamespace() for extension-owned storage so extension subject names stay
under storage:extension:{extensionName}:
import { createExtensionStorageNamespace } from '@makaio/storage-core';
export const TerminalStorageNamespace = createExtensionStorageNamespace('terminal', { schemas,});// → storage:extension:terminal.get, storage:extension:terminal.set, etc.Pass the unprefixed extension name. The helper rejects empty names and names that already include
extension: so callers cannot accidentally double-prefix the domain.
Related
Section titled “Related”- @makaio/storage-drizzle – Drizzle/libSQL client factory
- @makaio/bus-core – underlying bus infrastructure