unnbound-env
v0.0.9
Published
A type-safe environment variable management library with validation support.
Readme
unnbound-env
Maintenance line.
unnbound-env0.0.x serves workflows that read configuration from the boot-time snapshot (env.initialize(), synchronousenv.variables.get). New workflow code uses@ontemper/resources, which adds the live runtime resolver and Resources. Fix bugs here; add nothing new. The package is removed once every deployed workflow has moved (Resources rollout, T-3564).
Type-safe environment SDK for accessing partners, connections, and variables in Unnbound workflows.
Installation
npm install unnbound-envUsage
import { createServer } from 'unnbound-events';
import { env, selectPartnerFromEvent, createHttpClient } from 'unnbound-env';
const server = createServer();
// Determine partner from URL parameter
server.get(
'/webhook/:partnerId',
selectPartnerFromEvent((c) => c.params.partnerId),
(c) => ({ status: 200, body: {} }),
);
// Determine partner from authorization header
server.get(
'/webhook',
selectPartnerFromEvent((c) => {
const apiKey = c.request.headers.authorization;
if (!apiKey) return;
return env.partners.search((partner) => partner.variables.get('UNNBOUND_API_KEY') === apiKey);
}),
async (c) => {
// Get current partner (set by selectPartnerFromEvent middleware)
const partner = env.partners.current;
// Get a specific partner by key
const specificPartner = env.partners.get('EXTERNAL_PARTNER_ID');
// Get a global connection
const globalConnection = env.connections.get('TURVO_OAUTH');
// Get a partner connection
const partnerConnection = partner.connections.get('TURVO_OAUTH');
// Get a global variable
const globalApiKey = env.variables.get('GITHUB_API_KEY');
// Get a partner variable
const partnerApiKey = partner.variables.get('GITHUB_API_KEY');
// Create an HTTP client with a connection (accepts key or connection object)
const client = createHttpClient(partner.connections.get('GITHUB_API_KEY'));
// Standard axios request
const response = await client.get('/protected/resource');
return { status: 200, body: {} };
},
);API Reference
env
The main environment accessor singleton.
env.initialize()
Loads the initial environment configuration before a workflow handles requests, then refreshes it on a background interval. It retries transient startup failures before exiting when configuration cannot be loaded.
env.reload()
Refreshes the environment configuration. Before the first successful load,
reload() waits for configuration so the workflow has the state it needs. After
that load, it returns immediately and refreshes in the background. A failed
refresh keeps the last known good configuration and logs a warning.
Configuration refreshes every five minutes by default. Set
UNNBOUND_ENV_CACHE_MAX_AGE to a positive duration in milliseconds to change
the interval. Set UNNBOUND_ENV_API_TIMEOUT to change the configuration API
timeout in milliseconds. The timeout defaults to 5 seconds.
The SDK selects the Configuration for the current environment from TEMPER_ENVIRONMENT
(sandbox, staging, or production). Temper injects it in every workflow
environment; nothing to configure. If neither the Temper name nor the legacy
UNNBOUND_ENVIRONMENT name is set, initialization makes no request, retries
path resolution three times, logs the final error, and exits the process.
env.partners
env.partners.current- Get the current partner (requiresselectPartnerFromEventmiddleware)env.partners.get(key)- Get a partner by keyenv.partners.getAll()- Get all partnersenv.partners.has(key)- Check if a partner existsenv.partners.search(predicate)- Search for a partner by predicate
env.connections
env.connections.get(key, type?)- Get a global connection by keyenv.connections.has(key)- Check if a connection exists
env.variables
env.variables.get(key)- Get a global variable by keyenv.variables.has(key)- Check if a variable exists
Partner Accessors
Each partner has its own accessor for connections and variables:
const partner = env.partners.get('PARTNER_KEY');
// Partner connections
partner.connections.get('CONNECTION_KEY');
// Partner variables
partner.variables.get('VARIABLE_KEY');
// Partner attributes
partner.attributes;selectPartnerFromEvent
Middleware that sets the current partner from the request context.
selectPartnerFromEvent((c) => {
// Return partner key from request context
return c.params.partnerId;
});selectPartner
Utility to execute code in a partner context.
import { selectPartner } from 'unnbound-env';
selectPartner('PARTNER_KEY', (partner) => {
// Code runs with partner context
const apiKey = partner.variables.get('API_KEY');
});createHttpClient
Create an axios-based HTTP client with automatic connection authentication.
import { createHttpClient } from 'unnbound-env';
// With connection key
const client = createHttpClient('GITHUB_CONNECTION');
// With connection object
const client = createHttpClient(partner.connections.get('GITHUB_CONNECTION'));
// With custom axios config
const client = createHttpClient({ timeout: 5000 }, 'GITHUB_CONNECTION');Supported connection types:
basic-auth- Basic authenticationbearer-token- Bearer token / API key authenticationoauth20- OAuth 2.0 (client credentials or password grant)request- Custom headers only (no auth)
OAuth 2.0 tokens are reused until 30 seconds before expiry. A failed token request is not cached and is not retried: it rejects the current call, and the next call sends a new token request. Resource calls are never retried, except for one token refresh after an HTTP 401.
Connection Types
// Basic Auth
{
type: 'basic-auth',
config: {
url: string,
headers: { key: string, value: string }[],
username: string,
password: string,
}
}
// Bearer Token
{
type: 'bearer-token',
config: {
url: string,
headers: { key: string, value: string }[],
apiKey: string,
apiKeyPlacement: 'authorization_bearer' | 'custom_header',
apiKeyHeaderName?: string, // required when placement is 'custom_header'
}
}
// OAuth 2.0
{
type: 'oauth20',
config: {
url: string, // token endpoint
headers: { key: string, value: string }[],
grantType?: 'client_credentials' | 'password', // defaults to 'client_credentials'
bodyParams?: { key: string, value: string }[], // extra params merged into token request body
clientId: string,
clientSecret: string,
username?: string, // required for password flow
password?: string, // required for password flow
}
}
// Request (custom headers, no auth)
{
type: 'request',
config: {
url: string,
headers: { key: string, value: string }[],
}
}