{"_id":"@dataworks-technology/moments","_rev":"3-e39119de6ee9e95dd12fab6041e856b6","name":"@dataworks-technology/moments","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@dataworks-technology/moments","version":"0.1.0","keywords":["dataworks","sports","moments","triggers","rules","live","sdk"],"license":"MIT","_id":"@dataworks-technology/moments@0.1.0","maintainers":[{"name":"developersdw","email":"developers@dataworks.live"}],"homepage":"https://github.com/Dataworks-Technology/Dataworks-Moments#readme","bugs":{"url":"https://github.com/Dataworks-Technology/Dataworks-Moments/issues"},"dist":{"shasum":"4ec9fbd84430470333c554511a3141d227455e34","tarball":"https://registry.npmjs.org/@dataworks-technology/moments/-/moments-0.1.0.tgz","fileCount":8,"integrity":"sha512-AYL+OLXxKetzEteOJO4yNIP11j/1vUE4fH390t0LGQozL0abdPtjxFJARzD7RPeTZ1GvDQc0o1PaborhWojo+Q==","signatures":[{"sig":"MEUCIQDN0/lSIZDrHuIZIrDVM1rY7YwuvbIxwPmc6h53pGVnFAIgSrnFDfNGP5wtz51rKwujcE71hsId06/E2JmaZiBLptY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":113541},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"3a998303ea685d7ef31d2c2bdf5074da46da3cc2","scripts":{"test":"vitest run","build":"tsup","publish:major":"npm version major && npm publish --@dataworks-technology:registry=https://registry.npmjs.org/","publish:minor":"npm version minor && npm publish --@dataworks-technology:registry=https://registry.npmjs.org/","publish:patch":"npm version patch && npm publish --@dataworks-technology:registry=https://registry.npmjs.org/","prepublishOnly":"bun run build"},"_npmUser":{"name":"developersdw","email":"developers@dataworks.live"},"repository":{"url":"git+https://github.com/Dataworks-Technology/Dataworks-Moments.git","type":"git"},"_npmVersion":"10.8.2","description":"Dataworks Moments Engine SDK — authenticate, query triggers/moments, and subscribe to real-time moment events","directories":{},"_nodeVersion":"20.19.6","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","dotenv":"^16.4.7","vitest":"^3.2.3","typescript":"^5.8.3","@dataworks/sdk":"^1.6.0"},"_npmOperationalInternal":{"tmp":"tmp/moments_0.1.0_1778255349032_0.3511963368177402","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@dataworks-technology/moments","version":"0.1.1","keywords":["dataworks","sports","moments","triggers","rules","live","sdk"],"license":"MIT","_id":"@dataworks-technology/moments@0.1.1","maintainers":[{"name":"developersdw","email":"developers@dataworks.live"}],"homepage":"https://github.com/Dataworks-Technology/Dataworks-Moments#readme","bugs":{"url":"https://github.com/Dataworks-Technology/Dataworks-Moments/issues"},"dist":{"shasum":"0212bc7d3ec905405543a10c90a9ddc1eb000afe","tarball":"https://registry.npmjs.org/@dataworks-technology/moments/-/moments-0.1.1.tgz","fileCount":8,"integrity":"sha512-yJxoX5Gcab6nZKM5Y28HWZxcEX+dF/A6B16N8Cupey0I6Cd18uIfk0BA4Wk6OCUjP8vwd68WipdJRfZOTow6ZA==","signatures":[{"sig":"MEYCIQDyP+xFdJulrh1pasfxLfzGbA1ZtOM8UX+J+zszB9ymrAIhAJl4uDyrJSNdXRh9d+VPK5Q2KoWpUeaDdkzQfFXzK24n","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":113383},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"3a998303ea685d7ef31d2c2bdf5074da46da3cc2","scripts":{"test":"vitest run","build":"tsup","publish:major":"npm version major && npm publish --@dataworks-technology:registry=https://registry.npmjs.org/","publish:minor":"npm version minor && npm publish --@dataworks-technology:registry=https://registry.npmjs.org/","publish:patch":"npm version patch && npm publish --@dataworks-technology:registry=https://registry.npmjs.org/","prepublishOnly":"bun run build"},"_npmUser":{"name":"developersdw","email":"developers@dataworks.live"},"repository":{"url":"git+https://github.com/Dataworks-Technology/Dataworks-Moments.git","type":"git"},"_npmVersion":"10.8.2","description":"Dataworks Moments Engine SDK — authenticate, query triggers/moments, and subscribe to real-time moment events","directories":{},"_nodeVersion":"20.19.6","dependencies":{},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","dotenv":"^16.4.7","vitest":"^3.2.3","typescript":"^5.8.3","@dataworks/sdk":"^1.6.0"},"_npmOperationalInternal":{"tmp":"tmp/moments_0.1.1_1778256076032_0.13884709886297553","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2026-05-08T15:49:08.889Z","modified":"2026-05-15T16:18:54.227Z","0.1.0":"2026-05-08T15:49:09.265Z","0.1.1":"2026-05-08T16:01:16.165Z"},"bugs":{"url":"https://github.com/Dataworks-Technology/Dataworks-Moments/issues"},"license":"MIT","homepage":"https://github.com/Dataworks-Technology/Dataworks-Moments#readme","keywords":["dataworks","sports","moments","triggers","rules","live","sdk"],"repository":{"url":"git+https://github.com/Dataworks-Technology/Dataworks-Moments.git","type":"git"},"description":"Dataworks Moments Engine SDK — authenticate, query triggers/moments, and subscribe to real-time moment events","maintainers":[{"email":"developers@dataworks.live","name":"developersdw"},{"email":"james.haigh@wearesweet.co.uk","name":"jameshaigh"}],"readme":"# @dataworks-technology/moments\n\n[![npm version](https://img.shields.io/npm/v/@dataworks-technology/moments)](https://www.npmjs.com/package/@dataworks-technology/moments)\n[![license](https://img.shields.io/npm/l/@dataworks-technology/moments)](./LICENSE)\n[![bundle size](https://img.shields.io/bundlephobia/minzip/@dataworks-technology/moments)](https://bundlephobia.com/package/@dataworks-technology/moments)\n\nOfficial SDK for the Dataworks Moments Engine — authenticate, query triggers and moments, and subscribe to real-time moment events.\n\n## Features\n\n- **Zero dependencies** — fully self-contained, no transitive installs\n- **Dual format** — ESM and CommonJS bundles included\n- **TypeScript-first** — complete type declarations shipped with the package\n- **Real-time subscriptions** — GraphQL WebSocket-based live moment streaming\n- **Cognito authentication** — secure login with automatic token management\n\n## Prerequisites\n\nYou need a Dataworks developer account. Contact your Dataworks administrator to receive:\n\n| Credential | Description |\n|---|---|\n| `cognitoEndpoint` | Cognito User Pool endpoint URL |\n| `clientId` | Cognito app client ID |\n| `graphqlUrl` | Moments Engine AppSync GraphQL endpoint |\n| Username + password | Your developer login credentials |\n\n## Installation\n\n```bash\nnpm install @dataworks-technology/moments\n```\n\n```bash\nyarn add @dataworks-technology/moments\n```\n\n```bash\npnpm add @dataworks-technology/moments\n```\n\n```bash\nbun add @dataworks-technology/moments\n```\n\n## Quick Start\n\n```typescript\nimport { MomentsClient } from \"@dataworks-technology/moments\";\n\nconst client = new MomentsClient({\n  cognitoEndpoint: \"https://cognito-idp.eu-west-1.amazonaws.com/\",\n  clientId: \"your-client-id\",\n  graphqlUrl: \"https://your-appsync-endpoint.amazonaws.com/graphql\",\n});\n\n// Authenticate\nawait client.login(\"username\", \"password\");\n\n// List recent moments\nconst { items: moments } = await client.listMoments({ limit: 10 });\nconsole.log(`${moments.length} moments found`);\n\n// Subscribe to new moments in real-time\nconst subscription = client.onMoments((moment) => {\n  console.log(`New moment: ${moment.moment}`);\n  console.log(`  Athlete: ${moment.athleteId}, Event: ${moment.eventId}`);\n  console.log(`  Trigger: ${moment.triggerName}`);\n});\n\n// Later: close the subscription\nsubscription.close();\n```\n\n## API\n\n### `new MomentsClient(config)`\n\nCreate a new client instance.\n\n```typescript\nconst client = new MomentsClient({\n  cognitoEndpoint: \"https://cognito-idp.eu-west-1.amazonaws.com/\",\n  clientId: \"your-client-id\",\n  graphqlUrl: \"https://your-appsync-endpoint.amazonaws.com/graphql\",\n  realtimeUrl: \"wss://...\", // Optional — derived from graphqlUrl if omitted\n});\n```\n\n### `client.login(username, password)`\n\nAuthenticate with Cognito. Must be called before any other operation.\n\n```typescript\nconst result = await client.login(\"username\", \"password\");\n// result: { accessToken, idToken, refreshToken, tenant }\n```\n\n### `client.listTriggers(options?)`\n\nList configured trigger rules with optional filtering, pagination, and ordering.\n\n```typescript\nconst { items, totalCount, nextToken } = await client.listTriggers({\n  filter: { isActive: { eq: 1 } },\n  limit: 25,\n  orderBy: [{ createdAt: \"DESC\" }],\n});\n```\n\n### `client.getTrigger(publicId)`\n\nFetch a single trigger by its public ID.\n\n```typescript\nconst trigger = await client.getTrigger(\"abc-123-def\");\nconsole.log(trigger.name, trigger.criteria);\n```\n\n### `client.listMoments(options?)`\n\nList matched moments with optional filtering, pagination, and ordering.\n\n```typescript\nconst { items, totalCount, nextToken } = await client.listMoments({\n  filter: { eventId: { eq: 7 } },\n  limit: 50,\n  orderBy: [{ createdAt: \"DESC\" }],\n});\n```\n\n### `client.getMoment(publicId)`\n\nFetch a single moment by its public ID.\n\n```typescript\nconst moment = await client.getMoment(\"moment-456\");\nconsole.log(moment.moment); // Hydrated output template\nconsole.log(moment.matches); // Metric values that triggered\n```\n\n### `client.onMoments(callback)`\n\nSubscribe to real-time moment creation events. The callback fires each time the Flink engine matches a trigger.\n\n```typescript\nconst subscription = client.onMoments((moment) => {\n  console.log(`[${moment.createdAt}] ${moment.moment}`);\n  console.log(`  Athlete ${moment.athleteId} matched trigger ${moment.triggerName}`);\n});\n\n// Clean up\nsubscription.close();\n```\n\n### `client.onTriggerCreated(callback)`\n\nSubscribe to new trigger creation events.\n\n```typescript\nconst sub = client.onTriggerCreated((trigger) => {\n  console.log(`New trigger: ${trigger.name}`);\n});\n```\n\n### `client.onTriggerUpdated(callback)`\n\nSubscribe to trigger update events.\n\n```typescript\nconst sub = client.onTriggerUpdated((trigger) => {\n  console.log(`Trigger updated: ${trigger.name} (active: ${trigger.isActive})`);\n});\n```\n\n### `client.isAuthenticated`\n\nCheck if the client has valid credentials.\n\n```typescript\nif (client.isAuthenticated) {\n  const triggers = await client.listTriggers();\n}\n```\n\n### `client.tenant`\n\nGet the tenant from the authenticated session (or `null`).\n\n```typescript\nconsole.log(`Logged in as tenant: ${client.tenant}`);\n```\n\n## Types\n\nAll types are exported for use in your application:\n\n```typescript\nimport type {\n  MomentsClientConfig,\n  LoginResult,\n  Trigger,\n  Moment,\n  MomentValue,\n  MomentTriggerSnapshot,\n  TriggerMarker,\n  TriggerCriteria,\n  TriggerCriteriaType,\n  TriggerConnection,\n  MomentConnection,\n  TriggerFilter,\n  MomentFilter,\n  ListTriggersOptions,\n  ListMomentsOptions,\n  Subscription,\n} from \"@dataworks-technology/moments\";\n```\n\n### Key Interfaces\n\n```typescript\ninterface Trigger {\n  publicId: string;\n  clientId: number;\n  isActive: number; // 1 = active, 0 = disabled\n  name: string;\n  outputTemplate: string; // Template with {{variable}} placeholders\n  labels?: string[];\n  marker?: TriggerMarker; // { color, icon? }\n  criteria: TriggerCriteria[];\n  timeWindowSeconds?: number;\n  cooldownSeconds?: number;\n  criteriaLogic?: \"AND\" | \"OR\";\n  metricStalenessSeconds?: number;\n  createdAt: string; // ISO timestamp\n  updatedAt: string;\n}\n\ninterface Moment {\n  publicId: string;\n  triggerPublicId?: string;\n  clientId: number;\n  athleteId: number;\n  eventId: number;\n  matches: MomentValue[]; // Metric values that triggered\n  moment: string; // Hydrated output (template + values)\n  triggerObject: MomentTriggerSnapshot;\n  triggerName?: string;\n  triggerMarker?: TriggerMarker;\n  createdAt: string;\n}\n\ninterface TriggerCriteria {\n  datasetDatasourceId?: number;\n  metric: string; // e.g. \"heartrate\", \"speed\", \"power\"\n  subMetric: string; // e.g. \"current\", \"average\", \"max\"\n  type: TriggerCriteriaType; // \"gt\" | \"lt\" | \"eq\" | \"ge\" | \"le\" | ...\n  threshold: number;\n}\n```\n\n## Trigger Criteria Types\n\n| Type | Description | Example |\n|------|-------------|---------|\n| `eq` | Equal to | `heartrate.current == 170` |\n| `ne` | Not equal to | `status != 0` |\n| `gt` | Greater than | `heartrate.current > 170` |\n| `ge` | Greater than or equal | `speed.average >= 30` |\n| `lt` | Less than | `power.current < 100` |\n| `le` | Less than or equal | `heartrate.max <= 200` |\n| `between` | Between two values | `heartrate.current between [150, 180]` |\n| `exists` | Metric exists | `heartrate exists` |\n| `avg_window` | Time-windowed average | `avg(heartrate) over 5min > 160` |\n| `trend_up` | Trending upward | `heartrate trending up` |\n| `trend_down` | Trending downward | `speed trending down` |\n\n## Error Handling\n\nAll async methods throw on failure. Wrap calls in try/catch:\n\n```typescript\ntry {\n  await client.login(\"user\", \"pass\");\n} catch (err) {\n  // Authentication failed\n}\n\ntry {\n  const triggers = await client.listTriggers();\n} catch (err) {\n  // GraphQL error, network failure, or 401 expired token\n}\n```\n\nCalling any query/subscription method before `login()` throws immediately.\n\n## Requirements\n\n- **Node.js** ≥ 18 (uses native `fetch` and `WebSocket`)\n- **ESM or CommonJS** — both module formats included\n- **TypeScript** ≥ 5.0 (optional — works with plain JavaScript too)\n- **Browser** — compatible with any environment that has `fetch` and `WebSocket`\n\n## Real-Time Moments — End-to-End Example\n\nA core use case: subscribe to live moment events as they fire, process them externally (alerting, analytics, dashboards), and integrate with your own systems.\n\n### Concept\n\n```\nLive Metrics → Kinesis → Flink (trigger evaluation) → Moment Created → AppSync Subscription → Your App\n```\n\nThe 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.\n\n### TypeScript Example\n\n```typescript\nimport { MomentsClient } from \"@dataworks-technology/moments\";\n\nconst client = new MomentsClient({\n  cognitoEndpoint: \"https://cognito-idp.eu-west-1.amazonaws.com/\",\n  clientId: \"your-client-id\",\n  clientSecret: \"your-client-secret\",\n  graphqlUrl: \"https://your-appsync-endpoint.amazonaws.com/graphql\",\n});\n\nawait client.login(\"developer\", \"password\");\n\n// 1. List active triggers to understand what we're monitoring\nconst { items: triggers } = await client.listTriggers({\n  filter: { isActive: { eq: 1 } },\n});\nconsole.log(`Monitoring ${triggers.length} active triggers`);\n\n// 2. Subscribe to live moments — fires each time a trigger matches\nclient.onMoments((moment) => {\n  console.log(`🎯 [${moment.createdAt}] ${moment.triggerName}`);\n  console.log(`   Athlete: ${moment.athleteId}, Event: ${moment.eventId}`);\n  console.log(`   Output: ${moment.moment}`);\n  console.log(`   Matches: ${moment.matches.map(m => `${m.metric}.${m.subMetric}=${m.value}`).join(\", \")}`);\n\n  // 3. Forward to your alerting system, dashboard, or analytics\n  sendToSlack(`${moment.triggerName}: ${moment.moment}`);\n  writeToDatabase(moment);\n});\n```\n\n### Python Example (Cross-Platform)\n\nThe 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:\n\n| Phase | What it does | SDK equivalent |\n|-------|-------------|----------------|\n| 1. Authenticate | Cognito USER_PASSWORD_AUTH | `client.login()` |\n| 2. List triggers | GraphQL query with auth | `client.listTriggers()` |\n| 3. List moments | GraphQL query with filter | `client.listMoments()` |\n| 4. Subscribe | WebSocket → AppSync GraphQL subscriptions | `client.onMoments()` |\n\n#### Run the demo\n\n```bash\npip install websocket-client requests\npython test/demo/round-trip.py\n```\n\n#### Output\n\n```\nPHASE 2: LIST TRIGGERS\n  Found 3 triggers:\n    → HR > 170 (active, 2 criteria)\n    → Speed < 2.0 (active, 1 criteria)\n\nPHASE 3: LIST MOMENTS\n  Found 12 moments for the last hour\n\nPHASE 4: SUBSCRIBE TO LIVE MOMENTS\n  ✓ Connected to real-time endpoint\n  Waiting for moments (10s)...\n    🎯 HR > 170: Heart rate alert — 182 bpm (athlete 7)\n  ✓ Received 1 live moment\n```\n\nSee `test/demo/README.md` for full setup and Google Sheets integration.\n\n### Use Cases for Moments\n\n| Use case | How |\n|---|---|\n| Real-time alerts | Subscribe to moments, forward to Slack/PagerDuty |\n| Live dashboard | Subscribe + render moment cards in your UI |\n| Post-event analysis | List moments filtered by event, athlete, trigger |\n| Trigger management | List/get triggers to show active monitoring rules |\n| Cross-platform integration | Python/Go/Rust via raw GraphQL + WebSocket |\n\n## Development\n\n> Internal contributors only — this section covers building, testing, and publishing the package.\n\nThis 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.\n\n### Build\n\n```bash\nbunx tsup         # → dist/ (ESM + CJS + DTS)\nbunx vitest run   # unit + E2E tests\n```\n\n### Publish\n\nLogin to npm first (one-time):\n```bash\nnpm login --registry https://registry.npmjs.org/\n```\n\nThen from `packages/sdk/`:\n```bash\nbun run publish:patch   # bump patch + publish\nbun run publish:minor   # bump minor + publish\nbun run publish:major   # bump major + publish\n```\n\n### E2E Tests\n\nThe test suite (`test/moments-client.test.ts`) includes both unit tests (mocked fetch) and E2E tests that validate the full flow against deployed infrastructure.\n\n#### Prerequisites\n\nDeploy the Moments stack — `make deploy` generates `test.env` with the required env vars:\n\n| Env var | Source | Purpose |\n|---|---|---|\n| `CLIENT_ID` | Shared stack → Cognito | App client for USER_PASSWORD_AUTH |\n| `CLIENT_SECRET` | Shared stack → Cognito | App client secret |\n| `COGNITO_BASE_DOMAIN` | Shared stack → Cognito | IDP endpoint for auth |\n| `GRAPHQL_ENDPOINT` | Moments stack → AppSync | GraphQL API endpoint |\n| `WSS_ENDPOINT` | Moments stack → AppSync | WebSocket real-time endpoint |\n| `USERNAME` | Shared stack → Cognito | Test user credentials |\n| `USERPASSWORD` | Shared stack → Cognito | Test user password |\n\n#### Run\n\n```bash\ncd packages/sdk && bunx vitest run\n```\n\n#### What each test proves\n\n| # | Area | What it validates |\n|---|---|---|\n| 1 | Login | USER_PASSWORD_AUTH flow, tenant extracted from JWT |\n| 2 | Login | Invalid credentials rejected cleanly |\n| 3 | getTrigger | GraphQL query with Bearer auth + tenant context |\n| 4 | getMoment | Query pattern for moments |\n| 5 | listTriggers | Filter, limit, nextToken, orderBy serialisation |\n| 6 | listMoments | Event/athlete filtering |\n| 7 | Errors | HTTP/GraphQL error propagation |\n| 8 | onMoments | WebSocket protocol (init → ack → start → data) |\n| 9–10 | E2E login | Live Cognito auth + bad credential rejection |\n| 11–12 | E2E queries | Full round-trip against deployed AppSync |\n\n## Related Packages\n\n- [`@dataworks-technology/data`](https://www.npmjs.com/package/@dataworks-technology/data) — Data Engine SDK (ingest metrics, subscribe to streams)\n","readmeFilename":"README.md"}