Skip to content

dotlabshq/baseworks-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@baseworks/sdk

The Baseworks runtime binding surface. createEnv() resolves logical names — capability services (foldbase, org, iam), driver bindings (db, storage, kv), and plain config — to typed clients or connection descriptors, without the app hard-coding a single URL.

Resolution is lazy: an unresolved capability only throws when it is actually used (or eagerly if you list it in require). The mechanism is pluggable — today it reads process.env; a flect-manifest or Consul/DNS source drops in later without touching call sites.

zod + fetch only. Driver packages (e.g. ioredis) are optional peers, never hard dependencies.

Install

pnpm add @baseworks/sdk

Capability clients (@baseworks/foldbase, @baseworks/org, @baseworks/iam) come as dependencies. KV drivers are peers — install the one you bind:

pnpm add ioredis   # only if you bind a redis:// KV

Quick start

import { createEnv } from '@baseworks/sdk'

const env = createEnv({
  // optional: fail-fast at boot if these aren't configured
  require: ['org', 'iam'],
})

// capability services → typed clients (from the registry)
const org = env.service('org')          // OrgClient
const iam = env.service('iam')          // IamClient
const fb  = env.service('foldbase')     // FoldBase

await org.listOrgs()

// driver bindings → descriptors; you construct the driver (layer discipline)
const main = env.db('main')             // { kind:'db', url, token?, extra }

// KV → the raw driver client, handed back untouched
const redis = await env.kv<import('ioredis').Redis>('cache')
await redis.set('k', 'v')

// plain config
const port = env.var('PORT')

Resolution ladder

For a name org, env.service('org') resolves in order:

  1. explicit overridecreateEnv({ services: { org: 'http://…' } })
  2. per-binding env varORG_SERVICE_URL, then ORG_URL
  3. gateway conventionBASEWORKS_GATEWAY + /org

Driver bindings (db/storage/kv) skip the gateway and try a kind-prefixed var first, then the bare form:

Kind Tries then
db('main') DB_MAIN_URL MAIN_URL
storage('files') STORAGE_FILES_URL FILES_URL
kv('cache') KV_CACHE_URL CACHE_URL

So one logical name can be reused across kinds without colliding, while the short <NAME>_URL form keeps working.

Tokens / tenant ride along the binding: <NAME>_TOKEN (or the default BASEWORKS_TOKEN, or createEnv({ token })) and <NAME>_TENANT. foldbase defaults tenant to platform when none is given.

API

Member Returns Notes
service(name) typed client cached per name; throws BindingError if unresolved
db(name) / storage(name) ResolvedBinding descriptor only — you build the driver
kv<T>(name) Promise<T> raw driver client; driver dynamically imported; cached per name
var(key) string | undefined plain config passthrough
has(name) boolean is a capability resolvable? (no build, no throw)
require(names) void throws BindingError listing any unresolved

createEnv(opts):

interface CreateEnvOptions {
  services?: Record<string, string>  // explicit per-service URL overrides (highest precedence)
  token?: string                     // default bearer for outbound calls
  require?: CapabilityName[]         // fail-fast at construction
  source?: EnvSource                 // inject a resolver (tests / flect / Consul)
  env?: Record<string, string | undefined>  // inject process.env (tests)
}

KV drivers

The SDK writes no get/set wrapper — it picks a driver by URL scheme and hands back the driver's own client instance. Supported today:

Scheme Driver (optional peer)
redis:// · rediss:// ioredis

Absent binding → you simply never call kv. Absent driver → a clear pnpm add ioredis error at first use, not a bare module-not-found. Unsupported scheme → an explicit error. The client is cached per name (one connection).

Adding a capability

Register the name → typed-client factory in src/registry.ts:

export interface CapabilityClients {
  foldbase: FoldBase
  org: OrgClient
  iam: IamClient
  billing: BillingClient   // ← new
}

export const REGISTRY = {
  // …
  billing: (b) => new BillingClient({ baseUrl: b.url, token: b.token }),
}

env.service('billing') is now typed and resolvable via BILLING_SERVICE_URL / BASEWORKS_GATEWAY/billing.

Test

nvm use 22        # vitest 4 needs node:util.styleText (Node ≥ 20.12)
pnpm test         # vitest run
pnpm typecheck

Design notes

  • Layer disciplinedb/storage return descriptors, not live drivers, so the SDK stays free of DB/driver dependencies. The app constructs the driver.
  • Lazy + cached — nothing connects at createEnv; the first service/kv call builds and caches. A failed kv build is not cached, so a later call retries.
  • Pluggable source — swap EnvVarSource for a flect-manifest or Consul source via createEnv({ source }); call sites are unchanged.

License

UNLICENSED — internal Baseworks package.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors