Packages
@loop-engine/registry-client
Archetype: Registry — implements LoopRegistry, slotted at options.registry on LoopEngineOptions / createLoopSystem.
Terminology: In product docs we call this the loop catalog (versioned loop definitions for Loop Engine). The npm package remains @loop-engine/registry-client and TypeScript types still use the name LoopRegistry. That is not the Commerce Gateway Registry (gateway discovery and verification).
@loop-engine/registry-client provides local-first loop definition lookup with optional network adapters.
Install
1npm install @loop-engine/registry-clientLocal catalog
1"cmt">// @no-typecheck2import { localRegistry } from "@loop-engine/registry-client"3 4const registry = localRegistry({5 definitions: [customLoop],6 loopsDir: "./loops",7 watch: true8})definitionsworks in browser and Node.loopsDirloads.yaml,.yml, and.jsondefinitions in Node.- Browser usage with
loopsDirlogs a warning and ignores filesystem mode.
HTTP catalog
1import { httpRegistry } from "@loop-engine/registry-client"2 3const registry = httpRegistry({4 baseUrl: "https:">//registry.example.com",5 headers: { Authorization: `Bearer ${token}` },6 timeoutMs: 10_000,7 retries: 28})Expected server contract:
GET /loopsGET /loops?domain={domain}GET /loops/{loopId}GET /loops/{loopId}/{version}POST /loopsDELETE /loops/{loopId}
Better Data adapter
1import { betterDataRegistry } from "@loop-engine/registry-client/betterdata"2 3const registry = betterDataRegistry({4 apiKey: process.env.BD_API_KEY!,5 orgId: "your-org-id",6 env: "production"7})LoopRegistry interface
The runtime type is still named LoopRegistry:
1interface LoopRegistry {2 get(id: LoopId): Promise<LoopDefinition | null>3 getVersion(id: LoopId, version: string): Promise<LoopDefinition | null>4 list(options?: { domain?: string }): Promise<LoopDefinition[]>5 has(id: LoopId): Promise<boolean>6 register(definition: LoopDefinition, options?: { force?: boolean }): Promise<void>7 remove(id: LoopId): Promise<boolean>8}SDK integration
createLoopSystem({ loops, registry }) merges catalog results with local loops. Local loops[] definitions override matching catalog IDs, and catalog load failures fall back to local-only startup.
Error classes
The package exports three typed errors. Catch by class to distinguish missing definitions from conflicts and from network/IO failures.
1import {2 RegistryNotFoundError,3 RegistryConflictError,4 RegistryNetworkError5} from "@loop-engine/registry-client"RegistryNotFoundError
Thrown by get() and getVersion() when a loop (or specific version) is not found. Exposes loopId and optional version.
RegistryConflictError
Thrown by register() when a loop with the same loopId@version already exists. Pass { force: true } (development only) to overwrite. Exposes loopId and version.
RegistryNetworkError
Thrown by httpRegistry and the Better Data adapter on network or HTTP failures. Exposes url, optional statusCode, and the underlying cause when available.
1try {2 await registry.register(definition)3} catch (error) {4 if (error instanceof RegistryConflictError) {5 console.warn(`Already registered: ${error.loopId}@${error.version}`)6 return7 }8 throw error9}