ryuu-client
v5.0.0
Published
Node client for ryuu services
Downloads
2,562
Keywords
Readme
ryuu-client
Node.js client for the Domo Apps platform. Used by ryuu (the Domo Apps CLI) and ryuu-proxy to authenticate with Domo, manage app designs, upload assets, and proxy local development sessions.
Requirements
- Node.js >= 22.0.0
- pnpm (package manager)
Install
pnpm add ryuu-clientUsage
import { createClient, getHomeDir, getMostRecentLogin } from 'ryuu-client';
const client = createClient({
instance: 'mycompany.domo.com',
refreshToken: '...',
clientId: '...',
devToken: false,
proxy: { host: 'proxy.corp.com', port: 8080 }, // optional
});Authentication
// OAuth device-code flow (opens browser for authorization)
const loginData = await client.login();
console.log(`Welcome, ${loginData.displayName}`);
// Tokens are cached automatically — subsequent API calls
// skip redundant token exchanges until the cache expires.Designs
// Create a new design
const design = await client.designs.create(manifest);
// Get a design by ID
const design = await client.designs.get(designId, { parts: 'versions' });
// List all designs
const designs = await client.designs.list();
// Delete / undelete
await client.designs.delete(designId, true); // force=true
await client.designs.undelete(designId);
// Versions and releases
const versions = await client.designs.getVersions(designId);
await client.designs.release(designId, '2.0.0');Assets
// Upload a single asset
await client.assets.upload(designId, version, 'dist/app.js');
// Upload all assets (reads files from cwd, respects manifest.ignore)
const uploaded = await client.assets.uploadAll(manifest);
// Download assets as a zip stream
const response = await client.assets.download(designId, version);Apps
// Create a temporary app instance (for local dev proxying)
const { instance } = await client.apps.createInstance(designId);
// Get the full dev environment (domoapps domain, user info, etc.)
const env = await client.apps.getEnvironment(manifest, proxyId);Escape hatch
For endpoints not covered by the namespaced API, use client.request() directly. Auth headers are injected automatically.
const result = await client.request('/api/some/endpoint', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});Utilities
import { getHomeDir, getMostRecentLogin, getContentType } from 'ryuu-client';
// ~/.config/configstore path
const configDir = getHomeDir();
// Most recently modified login JSON
const login = getMostRecentLogin();
// Content-type lookup with octet-stream fallback
getContentType('font.ttf'); // 'font/ttf'
getContentType('data.bin'); // 'application/octet-stream'Error handling
All errors are typed with cause chaining support:
import { RyuuHttpError, RyuuAuthError, RyuuValidationError } from 'ryuu-client';
try {
await client.designs.get('bad-id');
} catch (err) {
if (err instanceof RyuuHttpError) {
console.error(err.statusCode, err.message, err.url);
}
}Architecture
src/
├── index.ts # Public barrel export
├── client.ts # createClient() factory
├── auth/ # OAuth device-code flow, token caching
├── http/ # Native fetch wrapper, undici proxy, error classes
├── api/ # Namespaced API modules (designs, assets, apps, users)
├── types/ # TypeScript interfaces and enums
└── util/ # Content-type map, endpoints, home-dir helpersKey design decisions
- Native
fetch— no axios. Proxy support via undiciProxyAgent. - Token caching — access tokens and SIDs are cached with TTL, reducing most requests from 2 extra roundtrips to 0.
- Scoped TLS bypass —
domorig.ioinstances use an undiciAgentwithrejectUnauthorized: falseinstead of the globalNODE_TLS_REJECT_UNAUTHORIZED=0. - Typed errors —
RyuuHttpError,RyuuAuthError,RyuuValidationErrorwithcausechaining. No morePromise<unknown>. - ESM only —
"type": "module", targeting ES2023 withNodeNextmodule resolution.
Dependencies
| Runtime | Purpose |
|---------|---------|
| open | Launch browser for OAuth device-code flow |
| tinyglobby | Lightweight glob for asset uploads |
| undici | ProxyAgent for proxy support with native fetch |
Development
pnpm install
pnpm build # TypeScript compile to dist/
pnpm test # Run unit tests (vitest)
pnpm test:watch # Watch mode
pnpm test:coverage # Coverage report
pnpm format # Prettierv5 migration guide
v5 is a ground-up rewrite. The default export class is replaced by a createClient() factory returning a namespaced API object.
Import changes
- import Domo from 'ryuu-client';
+ import { createClient, getHomeDir, type RyuuClient } from 'ryuu-client';Constructor
- const client = new Domo(instance, refreshToken, clientId, { host, port }, devToken);
+ const client = createClient({ instance, refreshToken, clientId, devToken, proxy: { host, port } });Getters
- client.getInstance()
+ client.instance
- client.getRefreshToken()
+ client.refreshTokenStatic methods
- Domo.getHomeDir()
+ getHomeDir()
- Domo.getMostRecentLogin()
+ getMostRecentLogin()API methods
- client.createDesign(manifest)
+ client.designs.create(manifest)
- client.getDesign(id, params)
+ client.designs.get(id, params)
- client.getDesigns(params)
+ client.designs.list(params)
- client.deleteDesign(id, force)
+ client.designs.delete(id, force)
- client.unDeleteDesign(id)
+ client.designs.undelete(id)
- client.getVersions(id)
+ client.designs.getVersions(id)
- client.release(id, version)
+ client.designs.release(id, version)
- client.uploadAsset(designId, version, path, contents)
+ client.assets.upload(designId, version, path, contents)
- client.uploadAllAssets(manifest)
+ client.assets.uploadAll(manifest)
- client.download(designId, version)
+ client.assets.download(designId, version)
- client.createApp(designId, proxyId)
+ client.apps.createInstance(designId, proxyId)
- client.getDomoappsData(manifest, proxyId)
+ client.apps.getEnvironment(manifest, proxyId)
- client.processRequest(options)
+ client.request(url, init)Error handling
- .catch(err => console.log(err.statusCode, err.message))
+ import { RyuuHttpError } from 'ryuu-client';
+ .catch(err => {
+ if (err instanceof RyuuHttpError) {
+ console.log(err.statusCode, err.message);
+ }
+ })