JavaScript SDK (published as ryuu.js on npm) for building custom apps inside the Domo platform. Apps run in iframes; this library handles authenticated HTTP requests, messaging with the parent window, mobile WebView bridges, and provides high-level helpers for Data API, AppDB, Code Engine, Workflows, and AI services.
- Version: 6.0.0-alpha.0
- Zero runtime dependencies
- Build: Webpack 5 → UMD bundle at
dist/domo.js(~28KB), exposed as globalDomo - Tests: Jest + jsdom (
npm test) — 235 tests across 20 suites - Type check:
tsc --noEmit --skipLibCheck
npm test # jest --silent
npm run coverage # jest --coverage --silent
npm run build # webpack production build → dist/domo.js
npm run build:demo # node demo/build.js
src/
├── index.ts # Re-exports everything (barrel file)
├── domo.ts # Domo class: static API surface, MessageChannel setup
├── domo.test.ts # Tests for the Domo class
├── init.ts # MutationObserver setup, __mutationObserverCallback export
├── transport.ts # Shared mutable HTTP transport — namespace services read from here,
│ # Domo.extend() updates it so overrides propagate everywhere
│
├── types/
│ └── global.d.ts # Ambient types for mobile globals (domovariable, domofilter, webkit)
│
├── models/
│ ├── errors.ts # Structured error types: DomoHttpError, DomoAuthError,
│ │ # DomoTimeoutError, DomoValidationError, DomoConnectionError
│ │
│ ├── constants/
│ │ └── general.ts # DomoEvent const object, eventToListenerMap, getToken()
│ │
│ ├── enums/
│ │ ├── askReply.ts # EventType const: ASK, ACK, REPLY
│ │ ├── data-formats.ts # DataFormats enum (MIME-based Accept headers)
│ │ ├── domo-data-types.ts # DomoDataTypes enum (STRING, LONG, DECIMAL, etc.)
│ │ └── request-methods.ts # RequestMethods enum (GET, POST, PUT, DELETE)
│ │
│ ├── interfaces/
│ │ ├── ask-reply.ts # AskReplyMap, AskRequestStatus, AskResponseStatus
│ │ ├── filter.ts # Filter union type, operator/dataType enums
│ │ ├── json.ts # Loose JSON type (Json, JsonMap, JsonArray)
│ │ ├── request.ts # RequestOptions (incl. schema), QueryParams, ResponseBody types
│ │ └── variable.ts # Variable interface { functionId?, name?, value }
│ │
│ └── services/ # Business logic
│ ├── ai.ts # domo.ai.generateText, domo.ai.textToSQL
│ ├── appdata.ts # requestAppDataUpdate, onAppDataUpdated, handleAppData
│ ├── appdb.ts # domo.appdb.{list,get,create,update,remove,query,partialUpdate,bulk*,export,*Collection}
│ ├── codeengine.ts # domo.codeEngine(alias, input)
│ ├── data.ts # domo.data.query(alias, opts), domo.data.sql(alias, sql)
│ ├── dataset.ts # onDataUpdated, handleDataUpdated
│ ├── env.ts # buildEnv() — typed env from query params + GET /domo/environment/v1
│ ├── filters.ts # requestFiltersUpdate, onFiltersUpdated, handleFiltersUpdated
│ ├── http.ts # domoHttp, get, getAll, post, put, delete (+ schema validation, interceptors, debug)
│ ├── interceptors.ts # Request interceptor middleware (onion model)
│ ├── navigation.ts # navigate(url, isNewWindow) via window.parent.postMessage
│ ├── variables.ts # requestVariablesUpdate, onVariablesUpdated, handleVariablesUpdated
│ └── workflow.ts # domo.workflow.start, domo.workflow.getInstance
│
└── utils/
├── ask-reply.ts # handleAck, handleReply — request lifecycle tracking
├── data-helpers.ts # domoFormatToRequestFormat (user format → DataFormats enum)
├── debug.ts # domoDebug singleton — category-based logging, localStorage persistence
├── domoutils.ts # setContentHeaders, setAuthTokenHeader, handleNode, processBody
├── filter.ts # isFilter, isFilterArray, guardAgainstInvalidFilters
├── general.ts # isSuccess, isVerifiedOrigin, getQueryParams, setFormatHeaders, generateUniqueId, isIOS, isMobile
└── variable.ts # isVariable, isVariableArray, guardAgainstInvalidVariables
- Static class, no instantiation. All public API is
Domo.get(),Domo.onFiltersUpdated(), etc. - Service methods use
thisreferencing the Domo class. They're assigned directly as static properties (not.bind()); only handlers insideconnect()use.bind(this). - Namespace services (
data,appdb,ai,workflow) are plain objects with functions that call throughtransport.ts— a mutable registry ofget/post/put/delete.Domo.extend()updates both the Domo class properties and the transport, so overrides propagate to all services. - MessageChannel (receive-only) — SDK creates a
MessageChannel, transfersport2to the parent viawindow.parent.postMessageduring subscribe. Parent sends events (filtersUpdated, variablesUpdated, ack, etc.) throughport2; SDK listens onport1. Outbound SDK messages (requestFiltersUpdate, requestVariablesUpdate, navigate, etc.) always usewindow.parent.postMessage— this is intentional: the parent can verifyevent.originon postMessage, whereas MessagePort events carry no origin. Legacywindow.postMessagelistener for v4.7.0 compat. - MutationObserver — single observer on
document.documentElementwithsubtree: true, catches all DOM additions at any depth. Injects auth token (ryuu_sid) into relativehref/srcattributes. Token fetched once per batch, early bail if no token. Setup ininit.ts. - Mobile bridge: tries global
domofilter/domovariableobjects first, falls back towebkit.messageHandlers(iOS) orwindow.parent.postMessage. domo.env— synchronously populated from query params, then enriched in the background fromGET /domo/environment/v1.- Structured error hierarchy —
DomoHttpError>DomoAuthErrorfor HTTP;DomoConnectionErrorfor network;DomoValidationErrorfor input validation and schema failures. - Request interceptors — onion-model middleware chain via
Domo.intercept(). Wraps the fetch call indomoHttp. - Debug mode —
Domo.debug.enable()orlocalStorage.__domo_debug__. Logs HTTP requests, MessageChannel messages, filter/variable updates with category filtering.
All messaging is logged through domoDebug.log('messages', prefix, eventType, payload) with structured prefixes:
| Prefix | Direction | Transport | Where |
|---|---|---|---|
received:channel |
in | MessageChannel (port1) | domo.ts — port1.onmessage |
received:postMessage |
in | window.postMessage | domo.ts — legacy handler |
sent:postMessage |
out | window.parent.postMessage | subscribe, filter, variable, appData, navigate |
sent:mobile |
out | mobile bridge | domofilter, domovariable, webkit |
sent:ack |
out | window.postMessage | legacy ACKs in domo.ts |
sent:ack:channel |
out | MessagePort (responsePort) | ACKs in filters, variables, dataset, appdata handlers |
| API | Description |
|---|---|
Domo.intercept(fn) |
Register request interceptor, returns unsubscribe |
Domo.debug.enable(categories?) |
Enable debug logging ('http', 'messages', 'filters', 'variables', 'all') |
Domo.debug.disable() |
Disable debug logging |
RequestOptions.schema |
Optional { parse } for runtime response validation |
requestAppDataUpdate(payload, onAck, onReply, { echoRequestId }) |
Optional 4th opts bag — echoes a host correlation id (DOMO-483472) back on the wire |
requestFiltersUpdate(filters, pageStateUpdate, onAck, onReply, { echoRequestId }) |
Optional 5th opts bag — echoes a host correlation id (DOMO-483472) back on the wire |
| Class | Thrown When |
|---|---|
DomoHttpError |
Non-2xx HTTP response (has status, statusText, body, headers) |
DomoAuthError |
401 or 403 response (extends DomoHttpError) |
DomoConnectionError |
Network failure (fetch rejects) |
DomoValidationError |
Invalid filters/variables/schema parse failure (has errors[]) |
DomoTimeoutError |
Request or message timeout (has url) |
DomoEventis aconstobject (not a TS enum).DataFormatsenum values are MIME strings (e.g.,CSV = 'text/csv',JSON = 'application/json').- Auth header is
X-DOMO-Ryuu-Session. - Filter wire event name is
"filter"(not"filtersUpdated"), and the wire format usescolumnName+operator(desktop) orcolumn+operand(mobile). requestFiltersUpdate(null)clears all filters on the parent page.onFiltersUpdatedcallsconnect()without skipFilters; the SUBSCRIBE replay delivers initial filters. All other listeners callconnect(true). The SDK intentionally does NOT follow up withrequestFiltersUpdate(null, false)— DomoWeb treats a null-filter request as a page-level clear (DOMO-483920).handleNode(node, token)takes two args and recurses into children.setAuthTokenHeader(headers, token)takes two args.appdb.createandappdb.updateauto-wrap documents in{ content: ... }if not already wrapped.data.sqluses POST (not GET) withContent-Type: text/plain.- Validation errors (
guardAgainstInvalidFilters,guardAgainstInvalidVariables) log the expected data model as an actual object toconsole.errorbefore throwingDomoValidationError. Domo.listenersis typed viaDomoListenersinterface — callback signatures are(filters: Filter[]) => void,(variables: Variable[]) => void,(appData: string) => void,(alias: string) => void.- Interceptors wrap the fetch call (not the entire domoHttp flow), so headers/auth are already set when the interceptor runs.
- Host echo correlation (DOMO-483472): inbound
onAppDataUpdated/onFiltersUpdatedcallbacks receive an optional 2ndrequestId?: stringarg holding the host's correlation id. OutboundrequestAppDataUpdate/requestFiltersUpdateaccept{ echoRequestId }in their opts bag; the SDK emits it as a wire field distinct from its own ACK-trackingrequestId. Validated against^[A-Za-z0-9_\-:.]{1,128}$(mirrors DomoWeb'ssanitizeRequestId).