@dataworks-technology/moments
v0.1.1
Published
Dataworks Moments Engine SDK — authenticate, query triggers/moments, and subscribe to real-time moment events
Readme
@dataworks-technology/moments
Official SDK for the Dataworks Moments Engine — authenticate, query triggers and moments, and subscribe to real-time moment events.
Features
- Zero dependencies — fully self-contained, no transitive installs
- Dual format — ESM and CommonJS bundles included
- TypeScript-first — complete type declarations shipped with the package
- Real-time subscriptions — GraphQL WebSocket-based live moment streaming
- Cognito authentication — secure login with automatic token management
Prerequisites
You need a Dataworks developer account. Contact your Dataworks administrator to receive:
| Credential | Description |
|---|---|
| cognitoEndpoint | Cognito User Pool endpoint URL |
| clientId | Cognito app client ID |
| graphqlUrl | Moments Engine AppSync GraphQL endpoint |
| Username + password | Your developer login credentials |
Installation
npm install @dataworks-technology/momentsyarn add @dataworks-technology/momentspnpm add @dataworks-technology/momentsbun add @dataworks-technology/momentsQuick Start
import { MomentsClient } from "@dataworks-technology/moments";
const client = new MomentsClient({
cognitoEndpoint: "https://cognito-idp.eu-west-1.amazonaws.com/",
clientId: "your-client-id",
graphqlUrl: "https://your-appsync-endpoint.amazonaws.com/graphql",
});
// Authenticate
await client.login("username", "password");
// List recent moments
const { items: moments } = await client.listMoments({ limit: 10 });
console.log(`${moments.length} moments found`);
// Subscribe to new moments in real-time
const subscription = client.onMoments((moment) => {
console.log(`New moment: ${moment.moment}`);
console.log(` Athlete: ${moment.athleteId}, Event: ${moment.eventId}`);
console.log(` Trigger: ${moment.triggerName}`);
});
// Later: close the subscription
subscription.close();API
new MomentsClient(config)
Create a new client instance.
const client = new MomentsClient({
cognitoEndpoint: "https://cognito-idp.eu-west-1.amazonaws.com/",
clientId: "your-client-id",
graphqlUrl: "https://your-appsync-endpoint.amazonaws.com/graphql",
realtimeUrl: "wss://...", // Optional — derived from graphqlUrl if omitted
});client.login(username, password)
Authenticate with Cognito. Must be called before any other operation.
const result = await client.login("username", "password");
// result: { accessToken, idToken, refreshToken, tenant }client.listTriggers(options?)
List configured trigger rules with optional filtering, pagination, and ordering.
const { items, totalCount, nextToken } = await client.listTriggers({
filter: { isActive: { eq: 1 } },
limit: 25,
orderBy: [{ createdAt: "DESC" }],
});client.getTrigger(publicId)
Fetch a single trigger by its public ID.
const trigger = await client.getTrigger("abc-123-def");
console.log(trigger.name, trigger.criteria);client.listMoments(options?)
List matched moments with optional filtering, pagination, and ordering.
const { items, totalCount, nextToken } = await client.listMoments({
filter: { eventId: { eq: 7 } },
limit: 50,
orderBy: [{ createdAt: "DESC" }],
});client.getMoment(publicId)
Fetch a single moment by its public ID.
const moment = await client.getMoment("moment-456");
console.log(moment.moment); // Hydrated output template
console.log(moment.matches); // Metric values that triggeredclient.onMoments(callback)
Subscribe to real-time moment creation events. The callback fires each time the Flink engine matches a trigger.
const subscription = client.onMoments((moment) => {
console.log(`[${moment.createdAt}] ${moment.moment}`);
console.log(` Athlete ${moment.athleteId} matched trigger ${moment.triggerName}`);
});
// Clean up
subscription.close();client.onTriggerCreated(callback)
Subscribe to new trigger creation events.
const sub = client.onTriggerCreated((trigger) => {
console.log(`New trigger: ${trigger.name}`);
});client.onTriggerUpdated(callback)
Subscribe to trigger update events.
const sub = client.onTriggerUpdated((trigger) => {
console.log(`Trigger updated: ${trigger.name} (active: ${trigger.isActive})`);
});client.isAuthenticated
Check if the client has valid credentials.
if (client.isAuthenticated) {
const triggers = await client.listTriggers();
}client.tenant
Get the tenant from the authenticated session (or null).
console.log(`Logged in as tenant: ${client.tenant}`);Types
All types are exported for use in your application:
import type {
MomentsClientConfig,
LoginResult,
Trigger,
Moment,
MomentValue,
MomentTriggerSnapshot,
TriggerMarker,
TriggerCriteria,
TriggerCriteriaType,
TriggerConnection,
MomentConnection,
TriggerFilter,
MomentFilter,
ListTriggersOptions,
ListMomentsOptions,
Subscription,
} from "@dataworks-technology/moments";Key Interfaces
interface Trigger {
publicId: string;
clientId: number;
isActive: number; // 1 = active, 0 = disabled
name: string;
outputTemplate: string; // Template with {{variable}} placeholders
labels?: string[];
marker?: TriggerMarker; // { color, icon? }
criteria: TriggerCriteria[];
timeWindowSeconds?: number;
cooldownSeconds?: number;
criteriaLogic?: "AND" | "OR";
metricStalenessSeconds?: number;
createdAt: string; // ISO timestamp
updatedAt: string;
}
interface Moment {
publicId: string;
triggerPublicId?: string;
clientId: number;
athleteId: number;
eventId: number;
matches: MomentValue[]; // Metric values that triggered
moment: string; // Hydrated output (template + values)
triggerObject: MomentTriggerSnapshot;
triggerName?: string;
triggerMarker?: TriggerMarker;
createdAt: string;
}
interface TriggerCriteria {
datasetDatasourceId?: number;
metric: string; // e.g. "heartrate", "speed", "power"
subMetric: string; // e.g. "current", "average", "max"
type: TriggerCriteriaType; // "gt" | "lt" | "eq" | "ge" | "le" | ...
threshold: number;
}Trigger Criteria Types
| Type | Description | Example |
|------|-------------|---------|
| eq | Equal to | heartrate.current == 170 |
| ne | Not equal to | status != 0 |
| gt | Greater than | heartrate.current > 170 |
| ge | Greater than or equal | speed.average >= 30 |
| lt | Less than | power.current < 100 |
| le | Less than or equal | heartrate.max <= 200 |
| between | Between two values | heartrate.current between [150, 180] |
| exists | Metric exists | heartrate exists |
| avg_window | Time-windowed average | avg(heartrate) over 5min > 160 |
| trend_up | Trending upward | heartrate trending up |
| trend_down | Trending downward | speed trending down |
Error Handling
All async methods throw on failure. Wrap calls in try/catch:
try {
await client.login("user", "pass");
} catch (err) {
// Authentication failed
}
try {
const triggers = await client.listTriggers();
} catch (err) {
// GraphQL error, network failure, or 401 expired token
}Calling any query/subscription method before login() throws immediately.
Requirements
- Node.js ≥ 18 (uses native
fetchandWebSocket) - ESM or CommonJS — both module formats included
- TypeScript ≥ 5.0 (optional — works with plain JavaScript too)
- Browser — compatible with any environment that has
fetchandWebSocket
Real-Time Moments — End-to-End Example
A core use case: subscribe to live moment events as they fire, process them externally (alerting, analytics, dashboards), and integrate with your own systems.
Concept
Live Metrics → Kinesis → Flink (trigger evaluation) → Moment Created → AppSync Subscription → Your AppThe Moments Engine evaluates configurable trigger rules against live athlete metrics using Apache Flink. When metrics match trigger criteria, a moment is created with a hydrated output template and pushed to all subscribers in real-time.
TypeScript Example
import { MomentsClient } from "@dataworks-technology/moments";
const client = new MomentsClient({
cognitoEndpoint: "https://cognito-idp.eu-west-1.amazonaws.com/",
clientId: "your-client-id",
clientSecret: "your-client-secret",
graphqlUrl: "https://your-appsync-endpoint.amazonaws.com/graphql",
});
await client.login("developer", "password");
// 1. List active triggers to understand what we're monitoring
const { items: triggers } = await client.listTriggers({
filter: { isActive: { eq: 1 } },
});
console.log(`Monitoring ${triggers.length} active triggers`);
// 2. Subscribe to live moments — fires each time a trigger matches
client.onMoments((moment) => {
console.log(`🎯 [${moment.createdAt}] ${moment.triggerName}`);
console.log(` Athlete: ${moment.athleteId}, Event: ${moment.eventId}`);
console.log(` Output: ${moment.moment}`);
console.log(` Matches: ${moment.matches.map(m => `${m.metric}.${m.subMetric}=${m.value}`).join(", ")}`);
// 3. Forward to your alerting system, dashboard, or analytics
sendToSlack(`${moment.triggerName}: ${moment.moment}`);
writeToDatabase(moment);
});Python Example (Cross-Platform)
The same flow works from any language. A full working Python demo is included at test/demo/round-trip.py — it proves every SDK capability end-to-end:
| Phase | What it does | SDK equivalent |
|-------|-------------|----------------|
| 1. Authenticate | Cognito USER_PASSWORD_AUTH | client.login() |
| 2. List triggers | GraphQL query with auth | client.listTriggers() |
| 3. List moments | GraphQL query with filter | client.listMoments() |
| 4. Subscribe | WebSocket → AppSync GraphQL subscriptions | client.onMoments() |
Run the demo
pip install websocket-client requests
python test/demo/round-trip.pyOutput
PHASE 2: LIST TRIGGERS
Found 3 triggers:
→ HR > 170 (active, 2 criteria)
→ Speed < 2.0 (active, 1 criteria)
PHASE 3: LIST MOMENTS
Found 12 moments for the last hour
PHASE 4: SUBSCRIBE TO LIVE MOMENTS
✓ Connected to real-time endpoint
Waiting for moments (10s)...
🎯 HR > 170: Heart rate alert — 182 bpm (athlete 7)
✓ Received 1 live momentSee test/demo/README.md for full setup and Google Sheets integration.
Use Cases for Moments
| Use case | How | |---|---| | Real-time alerts | Subscribe to moments, forward to Slack/PagerDuty | | Live dashboard | Subscribe + render moment cards in your UI | | Post-event analysis | List moments filtered by event, athlete, trigger | | Trigger management | List/get triggers to show active monitoring rules | | Cross-platform integration | Python/Go/Rust via raw GraphQL + WebSocket |
Development
Internal contributors only — this section covers building, testing, and publishing the package.
This package publishes to public npm (registry.npmjs.org), not to the internal CodeArtifact registry used by @dataworks/sdk. The publishConfig in package.json enforces this.
Build
bunx tsup # → dist/ (ESM + CJS + DTS)
bunx vitest run # unit + E2E testsPublish
Login to npm first (one-time):
npm login --registry https://registry.npmjs.org/Then from packages/sdk/:
bun run publish:patch # bump patch + publish
bun run publish:minor # bump minor + publish
bun run publish:major # bump major + publishE2E Tests
The test suite (test/moments-client.test.ts) includes both unit tests (mocked fetch) and E2E tests that validate the full flow against deployed infrastructure.
Prerequisites
Deploy the Moments stack — make deploy generates test.env with the required env vars:
| Env var | Source | Purpose |
|---|---|---|
| CLIENT_ID | Shared stack → Cognito | App client for USER_PASSWORD_AUTH |
| CLIENT_SECRET | Shared stack → Cognito | App client secret |
| COGNITO_BASE_DOMAIN | Shared stack → Cognito | IDP endpoint for auth |
| GRAPHQL_ENDPOINT | Moments stack → AppSync | GraphQL API endpoint |
| WSS_ENDPOINT | Moments stack → AppSync | WebSocket real-time endpoint |
| USERNAME | Shared stack → Cognito | Test user credentials |
| USERPASSWORD | Shared stack → Cognito | Test user password |
Run
cd packages/sdk && bunx vitest runWhat each test proves
| # | Area | What it validates | |---|---|---| | 1 | Login | USER_PASSWORD_AUTH flow, tenant extracted from JWT | | 2 | Login | Invalid credentials rejected cleanly | | 3 | getTrigger | GraphQL query with Bearer auth + tenant context | | 4 | getMoment | Query pattern for moments | | 5 | listTriggers | Filter, limit, nextToken, orderBy serialisation | | 6 | listMoments | Event/athlete filtering | | 7 | Errors | HTTP/GraphQL error propagation | | 8 | onMoments | WebSocket protocol (init → ack → start → data) | | 9–10 | E2E login | Live Cognito auth + bad credential rejection | | 11–12 | E2E queries | Full round-trip against deployed AppSync |
Related Packages
@dataworks-technology/data— Data Engine SDK (ingest metrics, subscribe to streams)
