@dataworks-technology/data 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -118,7 +118,7 @@ await dataworks.ingest(
118
118
  );
119
119
  ```
120
120
 
121
- ### `dataworks.subscribe(channel, onEvent)`
121
+ ### `dataworks.subscribe(channel, onEvent, onError?)`
122
122
 
123
123
  Subscribe to real-time data events via WebSocket.
124
124
 
@@ -130,12 +130,20 @@ const subscription = dataworks.subscribe(
130
130
  (event) => {
131
131
  console.log("Received:", event);
132
132
  },
133
+ (error) => {
134
+ console.error("Subscription error:", error.message);
135
+ },
133
136
  );
134
137
 
135
138
  // Later: close the subscription
136
139
  subscription.close();
137
140
  ```
138
141
 
142
+ The optional `onError` callback receives an `Error` for:
143
+ - **WebSocket connection errors** (network failures, TLS errors)
144
+ - **AppSync subscription errors** (invalid channel, server-side errors)
145
+ - **Reconnect failures** (token refresh failed after an auth expiry)
146
+
139
147
  ### `dataworks.reportError(error)`
140
148
 
141
149
  Report an error to the Data Engine for monitoring and alerting.
@@ -224,6 +232,7 @@ import type {
224
232
  MetricsPayload,
225
233
  ErrorReport,
226
234
  SubscriptionHandler,
235
+ ErrorHandler,
227
236
  Subscription,
228
237
  } from "@dataworks-technology/data";
229
238
  ```
package/dist/index.cjs CHANGED
@@ -350,7 +350,7 @@ var DataClient = class {
350
350
  * @throws Error if not authenticated
351
351
  * @see https://data-sdk-docs.dataworks.live/real-time-subscriptions
352
352
  */
353
- subscribe(channel, onEvent) {
353
+ subscribe(channel, onEvent, onError) {
354
354
  this.requireAuth();
355
355
  if (typeof WebSocket === "undefined") {
356
356
  throw new Error(
@@ -367,6 +367,9 @@ var DataClient = class {
367
367
  refreshAuth: async () => {
368
368
  await this.refreshSession();
369
369
  },
370
+ onReconnectError: (error) => {
371
+ onError?.(error);
372
+ },
370
373
  connect: ({ onUnexpectedClose }) => {
371
374
  const url = new URL(this.config.realtimeUrl);
372
375
  url.pathname = "/event/realtime";
@@ -391,6 +394,13 @@ var DataClient = class {
391
394
  ws.addEventListener("open", () => {
392
395
  ws.send(JSON.stringify({ type: "connection_init" }));
393
396
  });
397
+ ws.addEventListener("error", () => {
398
+ onError?.(
399
+ new Error(
400
+ "[@dataworks-technology/data] WebSocket connection error"
401
+ )
402
+ );
403
+ });
394
404
  ws.addEventListener("message", (event) => {
395
405
  try {
396
406
  const msg = JSON.parse(String(event.data));
@@ -408,10 +418,23 @@ var DataClient = class {
408
418
  );
409
419
  } else if (msg.type === "data") {
410
420
  onEvent(JSON.parse(msg.event));
411
- } else if (msg.type === "error" && JSON.stringify(msg).toLowerCase().includes("unauthor")) {
412
- handleUnexpectedCloseOnce();
421
+ } else if (msg.type === "error" || msg.type === "connection_error" || msg.type === "subscribe_error" || msg.type === "broadcast_error" || msg.type === "unsubscribe_error") {
422
+ const msgStr = JSON.stringify(msg);
423
+ if (msgStr.toLowerCase().includes("unauthor")) {
424
+ handleUnexpectedCloseOnce();
425
+ } else {
426
+ const errorMessage = msg.message ?? msg.errors?.[0]?.message ?? msg.errors?.[0]?.errorType ?? "AppSync subscription error";
427
+ onError?.(
428
+ new Error(`[@dataworks-technology/data] ${errorMessage}`)
429
+ );
430
+ }
413
431
  }
414
432
  } catch {
433
+ onError?.(
434
+ new Error(
435
+ "[@dataworks-technology/data] Received malformed message from AppSync"
436
+ )
437
+ );
415
438
  }
416
439
  });
417
440
  ws.addEventListener("close", () => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../node_modules/@dataworks/sdk/dist/client/subscription-manager.js","../node_modules/@dataworks/sdk/dist/client/validation.js","../node_modules/@dataworks/sdk/dist/client/base64url.js","../node_modules/@dataworks/sdk/dist/client/auth.js","../src/validation.ts","../src/data-client.ts"],"sourcesContent":["/**\n * @dataworks-technology/data — Dataworks Data Engine SDK\n *\n * Public npm package for external developers to:\n * - Authenticate via Cognito\n * - Ingest live athlete metrics\n * - Subscribe to real-time data streams\n * - Report errors\n *\n * @example\n * ```ts\n * import { DataClient } from \"@dataworks-technology/data\";\n *\n * const dataworks = new DataClient({ ... });\n * await dataworks.login(\"username\", \"password\");\n * await dataworks.ingest(metrics, eventId, dsId);\n * ```\n *\n * @module @dataworks-technology/data\n */\n\nexport { DataClient } from \"./data-client.js\";\n\nexport type {\n DataClientConfig,\n LoginResult,\n Metric,\n MetricItem,\n RawMetricItem,\n MetricsPayload,\n ErrorReport,\n SubscriptionHandler,\n Subscription,\n} from \"./types.js\";\n\n// Re-export validation utilities for standalone use\nexport { validateMetric, filterValidMetrics } from \"./validation.js\";\n","/**\n * Generic subscription lifecycle manager with auth-refresh reconnect support.\n *\n * The protocol-specific client provides a `connect` function and calls\n * `onUnexpectedClose` when auth expiry or unexpected socket termination occurs.\n */\nexport function createAutoRefreshingSubscription(options) {\n const maxReconnectAttempts = options.maxReconnectAttempts ?? 1;\n let reconnectAttempts = 0;\n let reconnectInFlight = false;\n let closedByUser = false;\n let activeConnection = null;\n const hooks = {\n onUnexpectedClose: () => {\n if (closedByUser || reconnectInFlight) {\n return;\n }\n if (reconnectAttempts >= maxReconnectAttempts) {\n return;\n }\n reconnectInFlight = true;\n reconnectAttempts += 1;\n void (async () => {\n try {\n await options.refreshAuth();\n if (closedByUser) {\n return;\n }\n activeConnection = options.connect(hooks);\n }\n catch {\n options.onReconnectError?.(new Error(\"Subscription reconnect failed. Please retry or reauthenticate.\"));\n }\n finally {\n reconnectInFlight = false;\n }\n })();\n },\n };\n activeConnection = options.connect(hooks);\n return {\n close() {\n closedByUser = true;\n activeConnection?.close();\n activeConnection = null;\n },\n };\n}\n","/**\n * Metric validation for Dataworks payloads.\n *\n * Ported from Dataworks-Data packages/adaptors/shared-ts/src/validation.ts\n * to serve as the single source of truth for all Dataworks engine SDKs.\n *\n * @module @dataworks/sdk/client\n */\n/**\n * Returns null if the metric is valid, or a human-readable reason string if invalid.\n */\nexport function validateMetric(m) {\n if (typeof m.metric !== \"string\" || m.metric.trim() === \"\")\n return \"metric must be a non-empty string\";\n if (typeof m.athleteId !== \"string\" || m.athleteId.trim() === \"\")\n return \"athleteId must be a non-empty string\";\n if (!Number.isInteger(m.timestamp) || m.timestamp <= 0)\n return `timestamp must be a positive integer, got ${m.timestamp}`;\n if (m.value === null || m.value === undefined)\n return \"value is null/undefined\";\n if (typeof m.value === \"number\" && !isFinite(m.value))\n return `value is non-finite number (${m.value})`;\n if (typeof m.value !== \"number\" && typeof m.value !== \"string\")\n return `value type ${typeof m.value} not allowed (must be number or string)`;\n if (typeof m.value === \"string\" && m.value === \"\")\n return \"value is empty string\";\n return null;\n}\n/**\n * Filters an array of metrics, returning only valid ones.\n * Logs a warning for each dropped item via the provided warn function.\n */\nexport function filterValidMetrics(metrics, warn) {\n const { valid, dropped } = getMetricValidationResult(metrics);\n for (const item of dropped) {\n let valueStr;\n try {\n valueStr = JSON.stringify(item.metric.value);\n }\n catch {\n valueStr = String(item.metric.value);\n }\n warn(`Dropping invalid metric [${String(item.metric.metric)}=${valueStr}]: ${item.reason}`);\n }\n return valid;\n}\n/**\n * Validates metrics and returns both accepted and dropped items (with reasons).\n * This is useful for consumers that need structured diagnostics.\n */\nexport function getMetricValidationResult(metrics) {\n const valid = [];\n const dropped = [];\n for (const [index, m] of metrics.entries()) {\n const reason = validateMetric(m);\n if (reason) {\n dropped.push({\n index,\n reason,\n metric: m,\n });\n }\n else {\n valid.push(m);\n }\n }\n return { valid, dropped };\n}\n","/**\n * Runtime-agnostic base64url encode/decode.\n *\n * Prefers Node.js Buffer when available, falls back to btoa/atob for\n * browser and edge runtimes. Used for AppSync auth subprotocol headers\n * and JWT payload parsing.\n *\n * @module @dataworks/sdk/client\n */\n/** Encode a UTF-8 string to base64url. */\nexport const toBase64Url = (input) => {\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(input).toString(\"base64url\");\n }\n return btoa(input).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n};\n/** Decode a base64url string to UTF-8. */\nexport const fromBase64Url = (b64url) => {\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(b64url, \"base64url\").toString(\"utf-8\");\n }\n const padded = b64url.replace(/-/g, \"+\").replace(/_/g, \"/\") +\n \"=\".repeat(((-b64url.length % 4) + 4) % 4);\n return atob(padded);\n};\n","/**\n * Cognito authentication helpers.\n *\n * Provides M2M (client_credentials) and user (USER_PASSWORD_AUTH) flows.\n * Refactored from @dataworks/sdk e2e-utils.ts — uses native fetch instead\n * of axios so the client sub-path has zero heavy dependencies.\n *\n * @module @dataworks/sdk/client\n */\nimport { fromBase64Url } from \"./base64url.js\";\n/**\n * Fetch an M2M access token using the client_credentials grant.\n *\n * @param clientId - Cognito app client ID\n * @param clientSecret - Cognito app client secret\n * @param tokenUrl - Cognito token endpoint (e.g. https://<domain>.auth.<region>.amazoncognito.com/oauth2/token)\n * @returns Access token string\n */\nexport async function getClientToken(clientId, clientSecret, tokenUrl) {\n const resp = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"client_credentials\",\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n if (!resp.ok) {\n throw new Error(`getClientToken: Cognito token request failed: ${resp.status} ${resp.statusText}`);\n }\n const data = (await resp.json());\n if (!data.access_token) {\n throw new Error(\"getClientToken: response missing access_token\");\n }\n return data.access_token;\n}\n/**\n * Authenticate a user via Cognito USER_PASSWORD_AUTH flow.\n * Returns access token, ID token, refresh token, and tenant (extracted from JWT).\n *\n * Used by external developer clients — no client secret required when the\n * Cognito app client is configured without one.\n *\n * @param username - Cognito username\n * @param password - Cognito password\n * @param config - Cognito endpoint and client configuration\n * @returns Login result with tokens and tenant\n */\nexport async function loginWithCredentials(username, password, config) {\n const authParams = {\n USERNAME: username,\n PASSWORD: password,\n };\n // If a client secret is configured, compute SECRET_HASH\n if (config.clientSecret) {\n authParams.SECRET_HASH = await computeSecretHashAsync(username, config.clientId, config.clientSecret);\n }\n const resp = await fetch(config.cognitoEndpoint, {\n method: \"POST\",\n headers: {\n \"X-Amz-Target\": \"AWSCognitoIdentityProviderService.InitiateAuth\",\n \"Content-Type\": \"application/x-amz-json-1.1\",\n },\n body: JSON.stringify({\n AuthFlow: \"USER_PASSWORD_AUTH\",\n ClientId: config.clientId,\n AuthParameters: authParams,\n }),\n });\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(`loginWithCredentials: Cognito auth failed: ${resp.status} — ${body}`);\n }\n const data = (await resp.json());\n const result = data.AuthenticationResult;\n if (!result?.AccessToken || !result?.IdToken) {\n throw new Error(\"loginWithCredentials: response missing AccessToken or IdToken\");\n }\n // Extract tenant from JWT claims (ID token payload)\n const tenant = extractTenantFromJwt(result.IdToken);\n return {\n accessToken: result.AccessToken,\n idToken: result.IdToken,\n refreshToken: result.RefreshToken ?? \"\",\n tenant,\n };\n}\n/**\n * Refreshes Cognito user tokens using REFRESH_TOKEN_AUTH.\n *\n * Returns updated tokens and tenant, preserving previous tenant/idToken when\n * Cognito omits fields in the refresh response.\n *\n * @param refreshToken - The refresh token string\n * @param config - Cognito endpoint and client configuration, plus optional username/previousTenant/previousIdToken\n */\nexport async function refreshWithToken(refreshToken, config) {\n const authParams = {\n REFRESH_TOKEN: refreshToken,\n };\n if (config.clientSecret) {\n if (!config.username) {\n throw new Error(\"refreshWithToken: username is required when clientSecret is configured\");\n }\n authParams.SECRET_HASH = await computeSecretHashAsync(config.username, config.clientId, config.clientSecret);\n }\n const resp = await fetch(config.cognitoEndpoint, {\n method: \"POST\",\n headers: {\n \"X-Amz-Target\": \"AWSCognitoIdentityProviderService.InitiateAuth\",\n \"Content-Type\": \"application/x-amz-json-1.1\",\n },\n body: JSON.stringify({\n AuthFlow: \"REFRESH_TOKEN_AUTH\",\n ClientId: config.clientId,\n AuthParameters: authParams,\n }),\n });\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(`refreshWithToken: Cognito refresh failed: ${resp.status} — ${body}`);\n }\n const data = (await resp.json());\n const result = data.AuthenticationResult;\n if (!result?.AccessToken) {\n throw new Error(\"refreshWithToken: response missing AccessToken\");\n }\n const idToken = result.IdToken ?? config.previousIdToken ?? \"\";\n const extractedTenant = idToken ? extractTenantFromJwt(idToken) : \"\";\n const tenant = extractedTenant || config.previousTenant || \"\";\n return {\n accessToken: result.AccessToken,\n idToken,\n refreshToken: result.RefreshToken ?? refreshToken,\n tenant,\n };\n}\n/**\n * Extracts the tenant claim from a JWT ID token without verifying the signature.\n * The token is validated server-side by AppSync/API Gateway — this is a client-side\n * convenience for reading the tenant value.\n */\nfunction extractTenantFromJwt(idToken) {\n try {\n const payload = idToken.split(\".\")[1];\n const decoded = JSON.parse(fromBase64Url(payload));\n // Cognito custom attributes use \"custom:tenant\" claim\n return (decoded[\"custom:tenants\"] ??\n decoded[\"custom:tenant\"] ??\n decoded.tenant ??\n decoded[\"cognito:groups\"]?.[0] ??\n \"\");\n }\n catch {\n return \"\";\n }\n}\nasync function computeSecretHashAsync(username, clientId, clientSecret) {\n // Dynamic import to keep this module usable in environments without crypto.\n const crypto = await import(\"crypto\");\n return crypto\n .createHmac(\"sha256\", clientSecret)\n .update(username + clientId)\n .digest(\"base64\");\n}\n","/**\n * Validation re-exports — thin wrappers around @dataworks/sdk/client validation.\n * Declared locally so the published .d.ts is fully self-contained.\n *\n * @module @dataworks-technology/data\n */\n\nimport {\n validateMetric as _validateMetric,\n filterValidMetrics as _filterValidMetrics,\n} from \"@dataworks/sdk/client\";\nimport type { RawMetricItem, MetricItem } from \"./types.js\";\n\n/**\n * Check whether a metric is valid.\n * Returns null if valid, or a human-readable reason string if invalid.\n */\nexport const validateMetric: (m: RawMetricItem) => string | null =\n _validateMetric;\n\n/**\n * Filter an array of metrics, keeping only valid ones.\n * Logs a warning for each dropped item via the provided warn function.\n */\nexport const filterValidMetrics: (\n metrics: RawMetricItem[],\n warn: (msg: string) => void,\n) => MetricItem[] = _filterValidMetrics;\n","/**\n * DataClient — the public entry point for external developers using the Dataworks Data Engine.\n *\n * Handles authentication, metric ingestion, error reporting, and real-time subscriptions.\n * All SDK foundation code (@dataworks/sdk) is bundled at build time — consumers install\n * only @dataworks-technology/data with zero transitive dependencies.\n *\n * @example\n * ```ts\n * import { DataClient } from \"@dataworks-technology/data\";\n *\n * const dataworks = new DataClient({\n * cognitoEndpoint: \"https://cognito-idp.eu-west-1.amazonaws.com/\",\n * clientId: \"abc123\",\n * ingestUrl: \"https://dev-realtime.dataworks.live\",\n * errorUrl: \"https://dev-realtime-errors.dataworks.live\",\n * realtimeUrl: \"https://dev-event-api.dataworks.live\",\n * });\n *\n * await dataworks.login(\"graeme\", \"password123\");\n * await dataworks.ingest([{ athleteId: \"1\", metric: \"heartrate_calculated\", value: 172, timestamp: Date.now() / 1000 }], \"evt1\", \"ds1\");\n * ```\n *\n * @module @dataworks-technology/data\n */\n\nimport {\n createAutoRefreshingSubscription,\n loginWithCredentials,\n refreshWithToken,\n toBase64Url,\n} from \"@dataworks/sdk/client\";\nimport { validateMetric, filterValidMetrics } from \"./validation.js\";\nimport type {\n DataClientConfig,\n LoginResult,\n Metric,\n ErrorReport,\n SubscriptionHandler,\n Subscription,\n} from \"./types.js\";\n\nexport class DataClient {\n private readonly config: DataClientConfig;\n private credentials: LoginResult | null = null;\n private lastLoginUsername: string | null = null;\n private refreshInFlight: Promise<void> | null = null;\n\n constructor(config: DataClientConfig) {\n this.config = config;\n }\n\n /**\n * Authenticate with the Dataworks platform using Cognito USER_PASSWORD_AUTH.\n * Must be called before ingest(), reportError(), or subscribe().\n *\n * @param username - Cognito username\n * @param password - Cognito password\n * @returns Login result containing tokens and tenant\n * @see https://data-sdk-docs.dataworks.live/authentication\n */\n async login(username: string, password: string): Promise<LoginResult> {\n this.lastLoginUsername = username;\n this.credentials = await loginWithCredentials(username, password, {\n cognitoEndpoint: this.config.cognitoEndpoint,\n clientId: this.config.clientId,\n clientSecret: this.config.clientSecret,\n });\n return this.credentials;\n }\n\n /**\n * Ingest metric data points into the Dataworks Data Engine.\n * Validates each metric before sending — invalid metrics are silently dropped.\n *\n * @param metrics - Array of metric data points\n * @param eventId - Event identifier\n * @param datasetDatasourceId - Dataset-datasource identifier\n * @throws Error if not authenticated or if the request fails\n * @see https://data-sdk-docs.dataworks.live/ingesting-data\n */\n async ingest(\n metrics: Metric[],\n eventId: string,\n datasetDatasourceId: string,\n ): Promise<void> {\n this.requireAuth();\n\n const valid = filterValidMetrics(metrics, (msg) =>\n console.warn(`[@dataworks-technology/data] ${msg}`),\n );\n\n if (valid.length === 0) return;\n\n const payload = {\n eventId,\n datasetDatasourceId,\n metrics: valid,\n };\n\n const resp = await this.fetchWithAutoRefresh((accessToken) =>\n fetch(this.config.ingestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify(payload),\n }),\n );\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(\n `[@dataworks-technology/data] ingest failed: ${resp.status} — ${body}`,\n );\n }\n }\n\n /**\n * Report an error to the Dataworks Data Engine.\n *\n * @param error - Error report details\n * @throws Error if not authenticated or if the request fails\n * @see https://data-sdk-docs.dataworks.live/error-reporting\n */\n async reportError(error: ErrorReport): Promise<void> {\n this.requireAuth();\n\n const resp = await this.fetchWithAutoRefresh((accessToken) =>\n fetch(this.config.errorUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify({\n client_id: String(error.clientId),\n dataset_datasource_id: error.datasetDatasourceId,\n athlete_id: error.athleteId,\n error_title: error.errorTitle,\n error_description: error.errorDescription,\n }),\n }),\n );\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(\n `[@dataworks-technology/data] reportError failed: ${resp.status} — ${body}`,\n );\n }\n }\n\n /**\n * Subscribe to a real-time data channel on the AppSync Events API.\n * Uses the Cognito JWT for authentication.\n *\n * Channel format: dataworks/{datasetDatasourceId}/{eventId}/{metric}\n * Use \"*\" for metric to receive all metrics for a dataset-datasource/event pair.\n * Examples: \"dataworks/1/1/heartrate\", \"dataworks/1/1/*\".\n *\n * @param channel - Channel path to subscribe to\n * @param onEvent - Callback invoked for each received event\n * @returns Subscription handle with a close() method\n * @throws Error if not authenticated\n * @see https://data-sdk-docs.dataworks.live/real-time-subscriptions\n */\n subscribe(channel: string, onEvent: SubscriptionHandler): Subscription {\n this.requireAuth();\n\n if (typeof WebSocket === \"undefined\") {\n throw new Error(\n \"[@dataworks-technology/data] WebSocket is not available. \" +\n \"Use Node >= 22, Bun, or a browser environment. \" +\n \"For older Node versions, install a WebSocket polyfill (e.g. ws) and assign it to globalThis.WebSocket.\",\n );\n }\n if (typeof crypto?.randomUUID !== \"function\") {\n throw new Error(\n \"[@dataworks-technology/data] crypto.randomUUID() is not available. \" +\n \"Use Node >= 19, Bun, or a browser with Crypto API support.\",\n );\n }\n\n const channelPath = channel.startsWith(\"/\") ? channel : `/${channel}`;\n\n return createAutoRefreshingSubscription({\n refreshAuth: async () => {\n await this.refreshSession();\n },\n connect: ({ onUnexpectedClose }) => {\n const url = new URL(this.config.realtimeUrl);\n // AppSync Events API uses the /event/realtime path for WebSocket connections\n url.pathname = \"/event/realtime\";\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\n const host = new URL(this.config.realtimeUrl).host;\n // AWS AppSync Events API requires base64URL-encoded auth in the subprotocol.\n const authPayload = JSON.stringify({\n Authorization: this.credentials!.accessToken,\n host,\n });\n const authHeader = toBase64Url(authPayload);\n\n const ws = new WebSocket(url.toString(), [\n \"aws-appsync-event-ws\",\n `header-${authHeader}`,\n ]);\n\n let closedByUser = false;\n let unexpectedCloseHandled = false;\n\n const handleUnexpectedCloseOnce = () => {\n if (unexpectedCloseHandled) return;\n unexpectedCloseHandled = true;\n onUnexpectedClose();\n };\n\n ws.addEventListener(\"open\", () => {\n // Send connection_init — required for AppSync to send connection_ack\n ws.send(JSON.stringify({ type: \"connection_init\" }));\n });\n\n ws.addEventListener(\"message\", (event) => {\n try {\n const msg = JSON.parse(String(event.data));\n\n if (msg.type === \"connection_ack\") {\n ws.send(\n JSON.stringify({\n type: \"subscribe\",\n id: crypto.randomUUID(),\n channel: channelPath,\n authorization: {\n Authorization: this.credentials!.accessToken,\n host,\n },\n }),\n );\n } else if (msg.type === \"data\") {\n onEvent(JSON.parse(msg.event));\n } else if (\n msg.type === \"error\" &&\n JSON.stringify(msg).toLowerCase().includes(\"unauthor\")\n ) {\n handleUnexpectedCloseOnce();\n }\n } catch {\n // Ignore malformed or non-data messages (ka, subscribe_success, etc.)\n }\n });\n\n ws.addEventListener(\"close\", () => {\n if (!closedByUser) {\n handleUnexpectedCloseOnce();\n }\n });\n\n return {\n close() {\n closedByUser = true;\n ws.close();\n },\n };\n },\n });\n }\n\n /**\n * Check whether a metric is valid before ingesting.\n * Returns null if valid, or a human-readable reason string if invalid.\n */\n static validateMetric = validateMetric;\n\n /**\n * Filter an array of metrics, keeping only valid ones.\n * Logs a warning for each dropped item.\n */\n static filterValidMetrics = filterValidMetrics;\n\n /** Returns true if the client has been authenticated via login(). */\n get isAuthenticated(): boolean {\n return this.credentials !== null;\n }\n\n /** Returns the tenant from the last successful login, or null. */\n get tenant(): string | null {\n return this.credentials?.tenant ?? null;\n }\n\n /** @internal Throws if not authenticated. */\n private requireAuth(): void {\n if (!this.credentials) {\n throw new Error(\n \"[@dataworks-technology/data] Not authenticated — call login() first\",\n );\n }\n }\n\n private async fetchWithAutoRefresh(\n makeRequest: (accessToken: string) => Promise<Response>,\n ): Promise<Response> {\n this.requireAuth();\n\n let response = await makeRequest(this.credentials!.accessToken);\n if (response.status !== 401) {\n return response;\n }\n\n await this.refreshSession();\n response = await makeRequest(this.credentials!.accessToken);\n return response;\n }\n\n private async refreshSession(): Promise<void> {\n this.requireAuth();\n\n if (!this.credentials!.refreshToken) {\n throw new Error(\n \"[@dataworks-technology/data] Session expired and no refresh token is available — call login() again\",\n );\n }\n\n if (!this.refreshInFlight) {\n this.refreshInFlight = (async () => {\n this.credentials = await refreshWithToken(\n this.credentials!.refreshToken,\n {\n cognitoEndpoint: this.config.cognitoEndpoint,\n clientId: this.config.clientId,\n clientSecret: this.config.clientSecret,\n username: this.lastLoginUsername ?? undefined,\n previousTenant: this.credentials!.tenant,\n previousIdToken: this.credentials!.idToken,\n },\n );\n })().finally(() => {\n this.refreshInFlight = null;\n });\n }\n\n await this.refreshInFlight;\n }\n}\n"],"mappings":"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","names":["filterValidMetrics","validateMetric","crypto","validateMetric","filterValidMetrics","filterValidMetrics","validateMetric"]}
1
+ {"version":3,"sources":["../src/index.ts","../node_modules/@dataworks/sdk/dist/client/subscription-manager.js","../node_modules/@dataworks/sdk/dist/client/validation.js","../node_modules/@dataworks/sdk/dist/client/base64url.js","../node_modules/@dataworks/sdk/dist/client/auth.js","../src/validation.ts","../src/data-client.ts"],"sourcesContent":["/**\n * @dataworks-technology/data — Dataworks Data Engine SDK\n *\n * Public npm package for external developers to:\n * - Authenticate via Cognito\n * - Ingest live athlete metrics\n * - Subscribe to real-time data streams\n * - Report errors\n *\n * @example\n * ```ts\n * import { DataClient } from \"@dataworks-technology/data\";\n *\n * const dataworks = new DataClient({ ... });\n * await dataworks.login(\"username\", \"password\");\n * await dataworks.ingest(metrics, eventId, dsId);\n * ```\n *\n * @module @dataworks-technology/data\n */\n\nexport { DataClient } from \"./data-client.js\";\n\nexport type {\n DataClientConfig,\n LoginResult,\n Metric,\n MetricItem,\n RawMetricItem,\n MetricsPayload,\n ErrorReport,\n SubscriptionHandler,\n Subscription,\n} from \"./types.js\";\n\n// Re-export validation utilities for standalone use\nexport { validateMetric, filterValidMetrics } from \"./validation.js\";\n","/**\n * Generic subscription lifecycle manager with auth-refresh reconnect support.\n *\n * The protocol-specific client provides a `connect` function and calls\n * `onUnexpectedClose` when auth expiry or unexpected socket termination occurs.\n */\nexport function createAutoRefreshingSubscription(options) {\n const maxReconnectAttempts = options.maxReconnectAttempts ?? 1;\n let reconnectAttempts = 0;\n let reconnectInFlight = false;\n let closedByUser = false;\n let activeConnection = null;\n const hooks = {\n onUnexpectedClose: () => {\n if (closedByUser || reconnectInFlight) {\n return;\n }\n if (reconnectAttempts >= maxReconnectAttempts) {\n return;\n }\n reconnectInFlight = true;\n reconnectAttempts += 1;\n void (async () => {\n try {\n await options.refreshAuth();\n if (closedByUser) {\n return;\n }\n activeConnection = options.connect(hooks);\n }\n catch {\n options.onReconnectError?.(new Error(\"Subscription reconnect failed. Please retry or reauthenticate.\"));\n }\n finally {\n reconnectInFlight = false;\n }\n })();\n },\n };\n activeConnection = options.connect(hooks);\n return {\n close() {\n closedByUser = true;\n activeConnection?.close();\n activeConnection = null;\n },\n };\n}\n","/**\n * Metric validation for Dataworks payloads.\n *\n * Ported from Dataworks-Data packages/adaptors/shared-ts/src/validation.ts\n * to serve as the single source of truth for all Dataworks engine SDKs.\n *\n * @module @dataworks/sdk/client\n */\n/**\n * Returns null if the metric is valid, or a human-readable reason string if invalid.\n */\nexport function validateMetric(m) {\n if (typeof m.metric !== \"string\" || m.metric.trim() === \"\")\n return \"metric must be a non-empty string\";\n if (typeof m.athleteId !== \"string\" || m.athleteId.trim() === \"\")\n return \"athleteId must be a non-empty string\";\n if (!Number.isInteger(m.timestamp) || m.timestamp <= 0)\n return `timestamp must be a positive integer, got ${m.timestamp}`;\n if (m.value === null || m.value === undefined)\n return \"value is null/undefined\";\n if (typeof m.value === \"number\" && !isFinite(m.value))\n return `value is non-finite number (${m.value})`;\n if (typeof m.value !== \"number\" && typeof m.value !== \"string\")\n return `value type ${typeof m.value} not allowed (must be number or string)`;\n if (typeof m.value === \"string\" && m.value === \"\")\n return \"value is empty string\";\n return null;\n}\n/**\n * Filters an array of metrics, returning only valid ones.\n * Logs a warning for each dropped item via the provided warn function.\n */\nexport function filterValidMetrics(metrics, warn) {\n const { valid, dropped } = getMetricValidationResult(metrics);\n for (const item of dropped) {\n let valueStr;\n try {\n valueStr = JSON.stringify(item.metric.value);\n }\n catch {\n valueStr = String(item.metric.value);\n }\n warn(`Dropping invalid metric [${String(item.metric.metric)}=${valueStr}]: ${item.reason}`);\n }\n return valid;\n}\n/**\n * Validates metrics and returns both accepted and dropped items (with reasons).\n * This is useful for consumers that need structured diagnostics.\n */\nexport function getMetricValidationResult(metrics) {\n const valid = [];\n const dropped = [];\n for (const [index, m] of metrics.entries()) {\n const reason = validateMetric(m);\n if (reason) {\n dropped.push({\n index,\n reason,\n metric: m,\n });\n }\n else {\n valid.push(m);\n }\n }\n return { valid, dropped };\n}\n","/**\n * Runtime-agnostic base64url encode/decode.\n *\n * Prefers Node.js Buffer when available, falls back to btoa/atob for\n * browser and edge runtimes. Used for AppSync auth subprotocol headers\n * and JWT payload parsing.\n *\n * @module @dataworks/sdk/client\n */\n/** Encode a UTF-8 string to base64url. */\nexport const toBase64Url = (input) => {\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(input).toString(\"base64url\");\n }\n return btoa(input).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n};\n/** Decode a base64url string to UTF-8. */\nexport const fromBase64Url = (b64url) => {\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(b64url, \"base64url\").toString(\"utf-8\");\n }\n const padded = b64url.replace(/-/g, \"+\").replace(/_/g, \"/\") +\n \"=\".repeat(((-b64url.length % 4) + 4) % 4);\n return atob(padded);\n};\n","/**\n * Cognito authentication helpers.\n *\n * Provides M2M (client_credentials) and user (USER_PASSWORD_AUTH) flows.\n * Refactored from @dataworks/sdk e2e-utils.ts — uses native fetch instead\n * of axios so the client sub-path has zero heavy dependencies.\n *\n * @module @dataworks/sdk/client\n */\nimport { fromBase64Url } from \"./base64url.js\";\n/**\n * Fetch an M2M access token using the client_credentials grant.\n *\n * @param clientId - Cognito app client ID\n * @param clientSecret - Cognito app client secret\n * @param tokenUrl - Cognito token endpoint (e.g. https://<domain>.auth.<region>.amazoncognito.com/oauth2/token)\n * @returns Access token string\n */\nexport async function getClientToken(clientId, clientSecret, tokenUrl) {\n const resp = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"client_credentials\",\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n if (!resp.ok) {\n throw new Error(`getClientToken: Cognito token request failed: ${resp.status} ${resp.statusText}`);\n }\n const data = (await resp.json());\n if (!data.access_token) {\n throw new Error(\"getClientToken: response missing access_token\");\n }\n return data.access_token;\n}\n/**\n * Authenticate a user via Cognito USER_PASSWORD_AUTH flow.\n * Returns access token, ID token, refresh token, and tenant (extracted from JWT).\n *\n * Used by external developer clients — no client secret required when the\n * Cognito app client is configured without one.\n *\n * @param username - Cognito username\n * @param password - Cognito password\n * @param config - Cognito endpoint and client configuration\n * @returns Login result with tokens and tenant\n */\nexport async function loginWithCredentials(username, password, config) {\n const authParams = {\n USERNAME: username,\n PASSWORD: password,\n };\n // If a client secret is configured, compute SECRET_HASH\n if (config.clientSecret) {\n authParams.SECRET_HASH = await computeSecretHashAsync(username, config.clientId, config.clientSecret);\n }\n const resp = await fetch(config.cognitoEndpoint, {\n method: \"POST\",\n headers: {\n \"X-Amz-Target\": \"AWSCognitoIdentityProviderService.InitiateAuth\",\n \"Content-Type\": \"application/x-amz-json-1.1\",\n },\n body: JSON.stringify({\n AuthFlow: \"USER_PASSWORD_AUTH\",\n ClientId: config.clientId,\n AuthParameters: authParams,\n }),\n });\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(`loginWithCredentials: Cognito auth failed: ${resp.status} — ${body}`);\n }\n const data = (await resp.json());\n const result = data.AuthenticationResult;\n if (!result?.AccessToken || !result?.IdToken) {\n throw new Error(\"loginWithCredentials: response missing AccessToken or IdToken\");\n }\n // Extract tenant from JWT claims (ID token payload)\n const tenant = extractTenantFromJwt(result.IdToken);\n return {\n accessToken: result.AccessToken,\n idToken: result.IdToken,\n refreshToken: result.RefreshToken ?? \"\",\n tenant,\n };\n}\n/**\n * Refreshes Cognito user tokens using REFRESH_TOKEN_AUTH.\n *\n * Returns updated tokens and tenant, preserving previous tenant/idToken when\n * Cognito omits fields in the refresh response.\n *\n * @param refreshToken - The refresh token string\n * @param config - Cognito endpoint and client configuration, plus optional username/previousTenant/previousIdToken\n */\nexport async function refreshWithToken(refreshToken, config) {\n const authParams = {\n REFRESH_TOKEN: refreshToken,\n };\n if (config.clientSecret) {\n if (!config.username) {\n throw new Error(\"refreshWithToken: username is required when clientSecret is configured\");\n }\n authParams.SECRET_HASH = await computeSecretHashAsync(config.username, config.clientId, config.clientSecret);\n }\n const resp = await fetch(config.cognitoEndpoint, {\n method: \"POST\",\n headers: {\n \"X-Amz-Target\": \"AWSCognitoIdentityProviderService.InitiateAuth\",\n \"Content-Type\": \"application/x-amz-json-1.1\",\n },\n body: JSON.stringify({\n AuthFlow: \"REFRESH_TOKEN_AUTH\",\n ClientId: config.clientId,\n AuthParameters: authParams,\n }),\n });\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(`refreshWithToken: Cognito refresh failed: ${resp.status} — ${body}`);\n }\n const data = (await resp.json());\n const result = data.AuthenticationResult;\n if (!result?.AccessToken) {\n throw new Error(\"refreshWithToken: response missing AccessToken\");\n }\n const idToken = result.IdToken ?? config.previousIdToken ?? \"\";\n const extractedTenant = idToken ? extractTenantFromJwt(idToken) : \"\";\n const tenant = extractedTenant || config.previousTenant || \"\";\n return {\n accessToken: result.AccessToken,\n idToken,\n refreshToken: result.RefreshToken ?? refreshToken,\n tenant,\n };\n}\n/**\n * Extracts the tenant claim from a JWT ID token without verifying the signature.\n * The token is validated server-side by AppSync/API Gateway — this is a client-side\n * convenience for reading the tenant value.\n */\nfunction extractTenantFromJwt(idToken) {\n try {\n const payload = idToken.split(\".\")[1];\n const decoded = JSON.parse(fromBase64Url(payload));\n // Cognito custom attributes use \"custom:tenant\" claim\n return (decoded[\"custom:tenants\"] ??\n decoded[\"custom:tenant\"] ??\n decoded.tenant ??\n decoded[\"cognito:groups\"]?.[0] ??\n \"\");\n }\n catch {\n return \"\";\n }\n}\nasync function computeSecretHashAsync(username, clientId, clientSecret) {\n // Dynamic import to keep this module usable in environments without crypto.\n const crypto = await import(\"crypto\");\n return crypto\n .createHmac(\"sha256\", clientSecret)\n .update(username + clientId)\n .digest(\"base64\");\n}\n","/**\n * Validation re-exports — thin wrappers around @dataworks/sdk/client validation.\n * Declared locally so the published .d.ts is fully self-contained.\n *\n * @module @dataworks-technology/data\n */\n\nimport {\n validateMetric as _validateMetric,\n filterValidMetrics as _filterValidMetrics,\n} from \"@dataworks/sdk/client\";\nimport type { RawMetricItem, MetricItem } from \"./types.js\";\n\n/**\n * Check whether a metric is valid.\n * Returns null if valid, or a human-readable reason string if invalid.\n */\nexport const validateMetric: (m: RawMetricItem) => string | null =\n _validateMetric;\n\n/**\n * Filter an array of metrics, keeping only valid ones.\n * Logs a warning for each dropped item via the provided warn function.\n */\nexport const filterValidMetrics: (\n metrics: RawMetricItem[],\n warn: (msg: string) => void,\n) => MetricItem[] = _filterValidMetrics;\n","/**\n * DataClient — the public entry point for external developers using the Dataworks Data Engine.\n *\n * Handles authentication, metric ingestion, error reporting, and real-time subscriptions.\n * All SDK foundation code (@dataworks/sdk) is bundled at build time — consumers install\n * only @dataworks-technology/data with zero transitive dependencies.\n *\n * @example\n * ```ts\n * import { DataClient } from \"@dataworks-technology/data\";\n *\n * const dataworks = new DataClient({\n * cognitoEndpoint: \"https://cognito-idp.eu-west-1.amazonaws.com/\",\n * clientId: \"abc123\",\n * ingestUrl: \"https://dev-realtime.dataworks.live\",\n * errorUrl: \"https://dev-realtime-errors.dataworks.live\",\n * realtimeUrl: \"https://dev-event-api.dataworks.live\",\n * });\n *\n * await dataworks.login(\"graeme\", \"password123\");\n * await dataworks.ingest([{ athleteId: \"1\", metric: \"heartrate_calculated\", value: 172, timestamp: Date.now() / 1000 }], \"evt1\", \"ds1\");\n * ```\n *\n * @module @dataworks-technology/data\n */\n\nimport {\n createAutoRefreshingSubscription,\n loginWithCredentials,\n refreshWithToken,\n toBase64Url,\n} from \"@dataworks/sdk/client\";\nimport { validateMetric, filterValidMetrics } from \"./validation.js\";\nimport type {\n DataClientConfig,\n LoginResult,\n Metric,\n ErrorReport,\n SubscriptionHandler,\n ErrorHandler,\n Subscription,\n} from \"./types.js\";\n\nexport class DataClient {\n private readonly config: DataClientConfig;\n private credentials: LoginResult | null = null;\n private lastLoginUsername: string | null = null;\n private refreshInFlight: Promise<void> | null = null;\n\n constructor(config: DataClientConfig) {\n this.config = config;\n }\n\n /**\n * Authenticate with the Dataworks platform using Cognito USER_PASSWORD_AUTH.\n * Must be called before ingest(), reportError(), or subscribe().\n *\n * @param username - Cognito username\n * @param password - Cognito password\n * @returns Login result containing tokens and tenant\n * @see https://data-sdk-docs.dataworks.live/authentication\n */\n async login(username: string, password: string): Promise<LoginResult> {\n this.lastLoginUsername = username;\n this.credentials = await loginWithCredentials(username, password, {\n cognitoEndpoint: this.config.cognitoEndpoint,\n clientId: this.config.clientId,\n clientSecret: this.config.clientSecret,\n });\n return this.credentials;\n }\n\n /**\n * Ingest metric data points into the Dataworks Data Engine.\n * Validates each metric before sending — invalid metrics are silently dropped.\n *\n * @param metrics - Array of metric data points\n * @param eventId - Event identifier\n * @param datasetDatasourceId - Dataset-datasource identifier\n * @throws Error if not authenticated or if the request fails\n * @see https://data-sdk-docs.dataworks.live/ingesting-data\n */\n async ingest(\n metrics: Metric[],\n eventId: string,\n datasetDatasourceId: string,\n ): Promise<void> {\n this.requireAuth();\n\n const valid = filterValidMetrics(metrics, (msg) =>\n console.warn(`[@dataworks-technology/data] ${msg}`),\n );\n\n if (valid.length === 0) return;\n\n const payload = {\n eventId,\n datasetDatasourceId,\n metrics: valid,\n };\n\n const resp = await this.fetchWithAutoRefresh((accessToken) =>\n fetch(this.config.ingestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify(payload),\n }),\n );\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(\n `[@dataworks-technology/data] ingest failed: ${resp.status} — ${body}`,\n );\n }\n }\n\n /**\n * Report an error to the Dataworks Data Engine.\n *\n * @param error - Error report details\n * @throws Error if not authenticated or if the request fails\n * @see https://data-sdk-docs.dataworks.live/error-reporting\n */\n async reportError(error: ErrorReport): Promise<void> {\n this.requireAuth();\n\n const resp = await this.fetchWithAutoRefresh((accessToken) =>\n fetch(this.config.errorUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify({\n client_id: String(error.clientId),\n dataset_datasource_id: error.datasetDatasourceId,\n athlete_id: error.athleteId,\n error_title: error.errorTitle,\n error_description: error.errorDescription,\n }),\n }),\n );\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(\n `[@dataworks-technology/data] reportError failed: ${resp.status} — ${body}`,\n );\n }\n }\n\n /**\n * Subscribe to a real-time data channel on the AppSync Events API.\n * Uses the Cognito JWT for authentication.\n *\n * Channel format: dataworks/{datasetDatasourceId}/{eventId}/{metric}\n * Use \"*\" for metric to receive all metrics for a dataset-datasource/event pair.\n * Examples: \"dataworks/1/1/heartrate\", \"dataworks/1/1/*\".\n *\n * @param channel - Channel path to subscribe to\n * @param onEvent - Callback invoked for each received event\n * @returns Subscription handle with a close() method\n * @throws Error if not authenticated\n * @see https://data-sdk-docs.dataworks.live/real-time-subscriptions\n */\n subscribe(\n channel: string,\n onEvent: SubscriptionHandler,\n onError?: ErrorHandler,\n ): Subscription {\n this.requireAuth();\n\n if (typeof WebSocket === \"undefined\") {\n throw new Error(\n \"[@dataworks-technology/data] WebSocket is not available. \" +\n \"Use Node >= 22, Bun, or a browser environment. \" +\n \"For older Node versions, install a WebSocket polyfill (e.g. ws) and assign it to globalThis.WebSocket.\",\n );\n }\n if (typeof crypto?.randomUUID !== \"function\") {\n throw new Error(\n \"[@dataworks-technology/data] crypto.randomUUID() is not available. \" +\n \"Use Node >= 19, Bun, or a browser with Crypto API support.\",\n );\n }\n\n const channelPath = channel.startsWith(\"/\") ? channel : `/${channel}`;\n\n return createAutoRefreshingSubscription({\n refreshAuth: async () => {\n await this.refreshSession();\n },\n onReconnectError: (error) => {\n onError?.(error);\n },\n connect: ({ onUnexpectedClose }) => {\n const url = new URL(this.config.realtimeUrl);\n // AppSync Events API uses the /event/realtime path for WebSocket connections\n url.pathname = \"/event/realtime\";\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\n const host = new URL(this.config.realtimeUrl).host;\n // AWS AppSync Events API requires base64URL-encoded auth in the subprotocol.\n const authPayload = JSON.stringify({\n Authorization: this.credentials!.accessToken,\n host,\n });\n const authHeader = toBase64Url(authPayload);\n\n const ws = new WebSocket(url.toString(), [\n \"aws-appsync-event-ws\",\n `header-${authHeader}`,\n ]);\n\n let closedByUser = false;\n let unexpectedCloseHandled = false;\n\n const handleUnexpectedCloseOnce = () => {\n if (unexpectedCloseHandled) return;\n unexpectedCloseHandled = true;\n onUnexpectedClose();\n };\n\n ws.addEventListener(\"open\", () => {\n // Send connection_init — required for AppSync to send connection_ack\n ws.send(JSON.stringify({ type: \"connection_init\" }));\n });\n\n ws.addEventListener(\"error\", () => {\n onError?.(\n new Error(\n \"[@dataworks-technology/data] WebSocket connection error\",\n ),\n );\n });\n\n ws.addEventListener(\"message\", (event) => {\n try {\n const msg = JSON.parse(String(event.data));\n\n if (msg.type === \"connection_ack\") {\n ws.send(\n JSON.stringify({\n type: \"subscribe\",\n id: crypto.randomUUID(),\n channel: channelPath,\n authorization: {\n Authorization: this.credentials!.accessToken,\n host,\n },\n }),\n );\n } else if (msg.type === \"data\") {\n onEvent(JSON.parse(msg.event));\n } else if (\n msg.type === \"error\" ||\n msg.type === \"connection_error\" ||\n msg.type === \"subscribe_error\" ||\n msg.type === \"broadcast_error\" ||\n msg.type === \"unsubscribe_error\"\n ) {\n const msgStr = JSON.stringify(msg);\n if (msgStr.toLowerCase().includes(\"unauthor\")) {\n handleUnexpectedCloseOnce();\n } else {\n const errorMessage =\n msg.message ??\n msg.errors?.[0]?.message ??\n msg.errors?.[0]?.errorType ??\n \"AppSync subscription error\";\n onError?.(\n new Error(`[@dataworks-technology/data] ${errorMessage}`),\n );\n }\n }\n } catch {\n onError?.(\n new Error(\n \"[@dataworks-technology/data] Received malformed message from AppSync\",\n ),\n );\n }\n });\n\n ws.addEventListener(\"close\", () => {\n if (!closedByUser) {\n handleUnexpectedCloseOnce();\n }\n });\n\n return {\n close() {\n closedByUser = true;\n ws.close();\n },\n };\n },\n });\n }\n\n /**\n * Check whether a metric is valid before ingesting.\n * Returns null if valid, or a human-readable reason string if invalid.\n */\n static validateMetric = validateMetric;\n\n /**\n * Filter an array of metrics, keeping only valid ones.\n * Logs a warning for each dropped item.\n */\n static filterValidMetrics = filterValidMetrics;\n\n /** Returns true if the client has been authenticated via login(). */\n get isAuthenticated(): boolean {\n return this.credentials !== null;\n }\n\n /** Returns the tenant from the last successful login, or null. */\n get tenant(): string | null {\n return this.credentials?.tenant ?? null;\n }\n\n /** @internal Throws if not authenticated. */\n private requireAuth(): void {\n if (!this.credentials) {\n throw new Error(\n \"[@dataworks-technology/data] Not authenticated — call login() first\",\n );\n }\n }\n\n private async fetchWithAutoRefresh(\n makeRequest: (accessToken: string) => Promise<Response>,\n ): Promise<Response> {\n this.requireAuth();\n\n let response = await makeRequest(this.credentials!.accessToken);\n if (response.status !== 401) {\n return response;\n }\n\n await this.refreshSession();\n response = await makeRequest(this.credentials!.accessToken);\n return response;\n }\n\n private async refreshSession(): Promise<void> {\n this.requireAuth();\n\n if (!this.credentials!.refreshToken) {\n throw new Error(\n \"[@dataworks-technology/data] Session expired and no refresh token is available — call login() again\",\n );\n }\n\n if (!this.refreshInFlight) {\n this.refreshInFlight = (async () => {\n this.credentials = await refreshWithToken(\n this.credentials!.refreshToken,\n {\n cognitoEndpoint: this.config.cognitoEndpoint,\n clientId: this.config.clientId,\n clientSecret: this.config.clientSecret,\n username: this.lastLoginUsername ?? undefined,\n previousTenant: this.credentials!.tenant,\n previousIdToken: this.credentials!.idToken,\n },\n );\n })().finally(() => {\n this.refreshInFlight = null;\n });\n }\n\n await this.refreshInFlight;\n }\n}\n"],"mappings":"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","names":["filterValidMetrics","validateMetric","crypto","validateMetric","filterValidMetrics","filterValidMetrics","validateMetric"]}
package/dist/index.d.cts CHANGED
@@ -72,6 +72,8 @@ interface ErrorReport {
72
72
  }
73
73
  /** Callback for subscription events. */
74
74
  type SubscriptionHandler = (event: unknown) => void;
75
+ /** Callback for subscription errors (WebSocket errors, AppSync errors, reconnect failures). */
76
+ type ErrorHandler = (error: Error) => void;
75
77
  /** Active subscription that can be closed. */
76
78
  interface Subscription {
77
79
  /** Close the subscription and disconnect. */
@@ -127,7 +129,7 @@ declare class DataClient {
127
129
  * @throws Error if not authenticated
128
130
  * @see https://data-sdk-docs.dataworks.live/real-time-subscriptions
129
131
  */
130
- subscribe(channel: string, onEvent: SubscriptionHandler): Subscription;
132
+ subscribe(channel: string, onEvent: SubscriptionHandler, onError?: ErrorHandler): Subscription;
131
133
  /**
132
134
  * Check whether a metric is valid before ingesting.
133
135
  * Returns null if valid, or a human-readable reason string if invalid.
package/dist/index.d.ts CHANGED
@@ -72,6 +72,8 @@ interface ErrorReport {
72
72
  }
73
73
  /** Callback for subscription events. */
74
74
  type SubscriptionHandler = (event: unknown) => void;
75
+ /** Callback for subscription errors (WebSocket errors, AppSync errors, reconnect failures). */
76
+ type ErrorHandler = (error: Error) => void;
75
77
  /** Active subscription that can be closed. */
76
78
  interface Subscription {
77
79
  /** Close the subscription and disconnect. */
@@ -127,7 +129,7 @@ declare class DataClient {
127
129
  * @throws Error if not authenticated
128
130
  * @see https://data-sdk-docs.dataworks.live/real-time-subscriptions
129
131
  */
130
- subscribe(channel: string, onEvent: SubscriptionHandler): Subscription;
132
+ subscribe(channel: string, onEvent: SubscriptionHandler, onError?: ErrorHandler): Subscription;
131
133
  /**
132
134
  * Check whether a metric is valid before ingesting.
133
135
  * Returns null if valid, or a human-readable reason string if invalid.
package/dist/index.js CHANGED
@@ -312,7 +312,7 @@ var DataClient = class {
312
312
  * @throws Error if not authenticated
313
313
  * @see https://data-sdk-docs.dataworks.live/real-time-subscriptions
314
314
  */
315
- subscribe(channel, onEvent) {
315
+ subscribe(channel, onEvent, onError) {
316
316
  this.requireAuth();
317
317
  if (typeof WebSocket === "undefined") {
318
318
  throw new Error(
@@ -329,6 +329,9 @@ var DataClient = class {
329
329
  refreshAuth: async () => {
330
330
  await this.refreshSession();
331
331
  },
332
+ onReconnectError: (error) => {
333
+ onError?.(error);
334
+ },
332
335
  connect: ({ onUnexpectedClose }) => {
333
336
  const url = new URL(this.config.realtimeUrl);
334
337
  url.pathname = "/event/realtime";
@@ -353,6 +356,13 @@ var DataClient = class {
353
356
  ws.addEventListener("open", () => {
354
357
  ws.send(JSON.stringify({ type: "connection_init" }));
355
358
  });
359
+ ws.addEventListener("error", () => {
360
+ onError?.(
361
+ new Error(
362
+ "[@dataworks-technology/data] WebSocket connection error"
363
+ )
364
+ );
365
+ });
356
366
  ws.addEventListener("message", (event) => {
357
367
  try {
358
368
  const msg = JSON.parse(String(event.data));
@@ -370,10 +380,23 @@ var DataClient = class {
370
380
  );
371
381
  } else if (msg.type === "data") {
372
382
  onEvent(JSON.parse(msg.event));
373
- } else if (msg.type === "error" && JSON.stringify(msg).toLowerCase().includes("unauthor")) {
374
- handleUnexpectedCloseOnce();
383
+ } else if (msg.type === "error" || msg.type === "connection_error" || msg.type === "subscribe_error" || msg.type === "broadcast_error" || msg.type === "unsubscribe_error") {
384
+ const msgStr = JSON.stringify(msg);
385
+ if (msgStr.toLowerCase().includes("unauthor")) {
386
+ handleUnexpectedCloseOnce();
387
+ } else {
388
+ const errorMessage = msg.message ?? msg.errors?.[0]?.message ?? msg.errors?.[0]?.errorType ?? "AppSync subscription error";
389
+ onError?.(
390
+ new Error(`[@dataworks-technology/data] ${errorMessage}`)
391
+ );
392
+ }
375
393
  }
376
394
  } catch {
395
+ onError?.(
396
+ new Error(
397
+ "[@dataworks-technology/data] Received malformed message from AppSync"
398
+ )
399
+ );
377
400
  }
378
401
  });
379
402
  ws.addEventListener("close", () => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../node_modules/@dataworks/sdk/dist/client/subscription-manager.js","../node_modules/@dataworks/sdk/dist/client/validation.js","../node_modules/@dataworks/sdk/dist/client/base64url.js","../node_modules/@dataworks/sdk/dist/client/auth.js","../src/validation.ts","../src/data-client.ts"],"sourcesContent":["/**\n * Generic subscription lifecycle manager with auth-refresh reconnect support.\n *\n * The protocol-specific client provides a `connect` function and calls\n * `onUnexpectedClose` when auth expiry or unexpected socket termination occurs.\n */\nexport function createAutoRefreshingSubscription(options) {\n const maxReconnectAttempts = options.maxReconnectAttempts ?? 1;\n let reconnectAttempts = 0;\n let reconnectInFlight = false;\n let closedByUser = false;\n let activeConnection = null;\n const hooks = {\n onUnexpectedClose: () => {\n if (closedByUser || reconnectInFlight) {\n return;\n }\n if (reconnectAttempts >= maxReconnectAttempts) {\n return;\n }\n reconnectInFlight = true;\n reconnectAttempts += 1;\n void (async () => {\n try {\n await options.refreshAuth();\n if (closedByUser) {\n return;\n }\n activeConnection = options.connect(hooks);\n }\n catch {\n options.onReconnectError?.(new Error(\"Subscription reconnect failed. Please retry or reauthenticate.\"));\n }\n finally {\n reconnectInFlight = false;\n }\n })();\n },\n };\n activeConnection = options.connect(hooks);\n return {\n close() {\n closedByUser = true;\n activeConnection?.close();\n activeConnection = null;\n },\n };\n}\n","/**\n * Metric validation for Dataworks payloads.\n *\n * Ported from Dataworks-Data packages/adaptors/shared-ts/src/validation.ts\n * to serve as the single source of truth for all Dataworks engine SDKs.\n *\n * @module @dataworks/sdk/client\n */\n/**\n * Returns null if the metric is valid, or a human-readable reason string if invalid.\n */\nexport function validateMetric(m) {\n if (typeof m.metric !== \"string\" || m.metric.trim() === \"\")\n return \"metric must be a non-empty string\";\n if (typeof m.athleteId !== \"string\" || m.athleteId.trim() === \"\")\n return \"athleteId must be a non-empty string\";\n if (!Number.isInteger(m.timestamp) || m.timestamp <= 0)\n return `timestamp must be a positive integer, got ${m.timestamp}`;\n if (m.value === null || m.value === undefined)\n return \"value is null/undefined\";\n if (typeof m.value === \"number\" && !isFinite(m.value))\n return `value is non-finite number (${m.value})`;\n if (typeof m.value !== \"number\" && typeof m.value !== \"string\")\n return `value type ${typeof m.value} not allowed (must be number or string)`;\n if (typeof m.value === \"string\" && m.value === \"\")\n return \"value is empty string\";\n return null;\n}\n/**\n * Filters an array of metrics, returning only valid ones.\n * Logs a warning for each dropped item via the provided warn function.\n */\nexport function filterValidMetrics(metrics, warn) {\n const { valid, dropped } = getMetricValidationResult(metrics);\n for (const item of dropped) {\n let valueStr;\n try {\n valueStr = JSON.stringify(item.metric.value);\n }\n catch {\n valueStr = String(item.metric.value);\n }\n warn(`Dropping invalid metric [${String(item.metric.metric)}=${valueStr}]: ${item.reason}`);\n }\n return valid;\n}\n/**\n * Validates metrics and returns both accepted and dropped items (with reasons).\n * This is useful for consumers that need structured diagnostics.\n */\nexport function getMetricValidationResult(metrics) {\n const valid = [];\n const dropped = [];\n for (const [index, m] of metrics.entries()) {\n const reason = validateMetric(m);\n if (reason) {\n dropped.push({\n index,\n reason,\n metric: m,\n });\n }\n else {\n valid.push(m);\n }\n }\n return { valid, dropped };\n}\n","/**\n * Runtime-agnostic base64url encode/decode.\n *\n * Prefers Node.js Buffer when available, falls back to btoa/atob for\n * browser and edge runtimes. Used for AppSync auth subprotocol headers\n * and JWT payload parsing.\n *\n * @module @dataworks/sdk/client\n */\n/** Encode a UTF-8 string to base64url. */\nexport const toBase64Url = (input) => {\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(input).toString(\"base64url\");\n }\n return btoa(input).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n};\n/** Decode a base64url string to UTF-8. */\nexport const fromBase64Url = (b64url) => {\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(b64url, \"base64url\").toString(\"utf-8\");\n }\n const padded = b64url.replace(/-/g, \"+\").replace(/_/g, \"/\") +\n \"=\".repeat(((-b64url.length % 4) + 4) % 4);\n return atob(padded);\n};\n","/**\n * Cognito authentication helpers.\n *\n * Provides M2M (client_credentials) and user (USER_PASSWORD_AUTH) flows.\n * Refactored from @dataworks/sdk e2e-utils.ts — uses native fetch instead\n * of axios so the client sub-path has zero heavy dependencies.\n *\n * @module @dataworks/sdk/client\n */\nimport { fromBase64Url } from \"./base64url.js\";\n/**\n * Fetch an M2M access token using the client_credentials grant.\n *\n * @param clientId - Cognito app client ID\n * @param clientSecret - Cognito app client secret\n * @param tokenUrl - Cognito token endpoint (e.g. https://<domain>.auth.<region>.amazoncognito.com/oauth2/token)\n * @returns Access token string\n */\nexport async function getClientToken(clientId, clientSecret, tokenUrl) {\n const resp = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"client_credentials\",\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n if (!resp.ok) {\n throw new Error(`getClientToken: Cognito token request failed: ${resp.status} ${resp.statusText}`);\n }\n const data = (await resp.json());\n if (!data.access_token) {\n throw new Error(\"getClientToken: response missing access_token\");\n }\n return data.access_token;\n}\n/**\n * Authenticate a user via Cognito USER_PASSWORD_AUTH flow.\n * Returns access token, ID token, refresh token, and tenant (extracted from JWT).\n *\n * Used by external developer clients — no client secret required when the\n * Cognito app client is configured without one.\n *\n * @param username - Cognito username\n * @param password - Cognito password\n * @param config - Cognito endpoint and client configuration\n * @returns Login result with tokens and tenant\n */\nexport async function loginWithCredentials(username, password, config) {\n const authParams = {\n USERNAME: username,\n PASSWORD: password,\n };\n // If a client secret is configured, compute SECRET_HASH\n if (config.clientSecret) {\n authParams.SECRET_HASH = await computeSecretHashAsync(username, config.clientId, config.clientSecret);\n }\n const resp = await fetch(config.cognitoEndpoint, {\n method: \"POST\",\n headers: {\n \"X-Amz-Target\": \"AWSCognitoIdentityProviderService.InitiateAuth\",\n \"Content-Type\": \"application/x-amz-json-1.1\",\n },\n body: JSON.stringify({\n AuthFlow: \"USER_PASSWORD_AUTH\",\n ClientId: config.clientId,\n AuthParameters: authParams,\n }),\n });\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(`loginWithCredentials: Cognito auth failed: ${resp.status} — ${body}`);\n }\n const data = (await resp.json());\n const result = data.AuthenticationResult;\n if (!result?.AccessToken || !result?.IdToken) {\n throw new Error(\"loginWithCredentials: response missing AccessToken or IdToken\");\n }\n // Extract tenant from JWT claims (ID token payload)\n const tenant = extractTenantFromJwt(result.IdToken);\n return {\n accessToken: result.AccessToken,\n idToken: result.IdToken,\n refreshToken: result.RefreshToken ?? \"\",\n tenant,\n };\n}\n/**\n * Refreshes Cognito user tokens using REFRESH_TOKEN_AUTH.\n *\n * Returns updated tokens and tenant, preserving previous tenant/idToken when\n * Cognito omits fields in the refresh response.\n *\n * @param refreshToken - The refresh token string\n * @param config - Cognito endpoint and client configuration, plus optional username/previousTenant/previousIdToken\n */\nexport async function refreshWithToken(refreshToken, config) {\n const authParams = {\n REFRESH_TOKEN: refreshToken,\n };\n if (config.clientSecret) {\n if (!config.username) {\n throw new Error(\"refreshWithToken: username is required when clientSecret is configured\");\n }\n authParams.SECRET_HASH = await computeSecretHashAsync(config.username, config.clientId, config.clientSecret);\n }\n const resp = await fetch(config.cognitoEndpoint, {\n method: \"POST\",\n headers: {\n \"X-Amz-Target\": \"AWSCognitoIdentityProviderService.InitiateAuth\",\n \"Content-Type\": \"application/x-amz-json-1.1\",\n },\n body: JSON.stringify({\n AuthFlow: \"REFRESH_TOKEN_AUTH\",\n ClientId: config.clientId,\n AuthParameters: authParams,\n }),\n });\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(`refreshWithToken: Cognito refresh failed: ${resp.status} — ${body}`);\n }\n const data = (await resp.json());\n const result = data.AuthenticationResult;\n if (!result?.AccessToken) {\n throw new Error(\"refreshWithToken: response missing AccessToken\");\n }\n const idToken = result.IdToken ?? config.previousIdToken ?? \"\";\n const extractedTenant = idToken ? extractTenantFromJwt(idToken) : \"\";\n const tenant = extractedTenant || config.previousTenant || \"\";\n return {\n accessToken: result.AccessToken,\n idToken,\n refreshToken: result.RefreshToken ?? refreshToken,\n tenant,\n };\n}\n/**\n * Extracts the tenant claim from a JWT ID token without verifying the signature.\n * The token is validated server-side by AppSync/API Gateway — this is a client-side\n * convenience for reading the tenant value.\n */\nfunction extractTenantFromJwt(idToken) {\n try {\n const payload = idToken.split(\".\")[1];\n const decoded = JSON.parse(fromBase64Url(payload));\n // Cognito custom attributes use \"custom:tenant\" claim\n return (decoded[\"custom:tenants\"] ??\n decoded[\"custom:tenant\"] ??\n decoded.tenant ??\n decoded[\"cognito:groups\"]?.[0] ??\n \"\");\n }\n catch {\n return \"\";\n }\n}\nasync function computeSecretHashAsync(username, clientId, clientSecret) {\n // Dynamic import to keep this module usable in environments without crypto.\n const crypto = await import(\"crypto\");\n return crypto\n .createHmac(\"sha256\", clientSecret)\n .update(username + clientId)\n .digest(\"base64\");\n}\n","/**\n * Validation re-exports — thin wrappers around @dataworks/sdk/client validation.\n * Declared locally so the published .d.ts is fully self-contained.\n *\n * @module @dataworks-technology/data\n */\n\nimport {\n validateMetric as _validateMetric,\n filterValidMetrics as _filterValidMetrics,\n} from \"@dataworks/sdk/client\";\nimport type { RawMetricItem, MetricItem } from \"./types.js\";\n\n/**\n * Check whether a metric is valid.\n * Returns null if valid, or a human-readable reason string if invalid.\n */\nexport const validateMetric: (m: RawMetricItem) => string | null =\n _validateMetric;\n\n/**\n * Filter an array of metrics, keeping only valid ones.\n * Logs a warning for each dropped item via the provided warn function.\n */\nexport const filterValidMetrics: (\n metrics: RawMetricItem[],\n warn: (msg: string) => void,\n) => MetricItem[] = _filterValidMetrics;\n","/**\n * DataClient — the public entry point for external developers using the Dataworks Data Engine.\n *\n * Handles authentication, metric ingestion, error reporting, and real-time subscriptions.\n * All SDK foundation code (@dataworks/sdk) is bundled at build time — consumers install\n * only @dataworks-technology/data with zero transitive dependencies.\n *\n * @example\n * ```ts\n * import { DataClient } from \"@dataworks-technology/data\";\n *\n * const dataworks = new DataClient({\n * cognitoEndpoint: \"https://cognito-idp.eu-west-1.amazonaws.com/\",\n * clientId: \"abc123\",\n * ingestUrl: \"https://dev-realtime.dataworks.live\",\n * errorUrl: \"https://dev-realtime-errors.dataworks.live\",\n * realtimeUrl: \"https://dev-event-api.dataworks.live\",\n * });\n *\n * await dataworks.login(\"graeme\", \"password123\");\n * await dataworks.ingest([{ athleteId: \"1\", metric: \"heartrate_calculated\", value: 172, timestamp: Date.now() / 1000 }], \"evt1\", \"ds1\");\n * ```\n *\n * @module @dataworks-technology/data\n */\n\nimport {\n createAutoRefreshingSubscription,\n loginWithCredentials,\n refreshWithToken,\n toBase64Url,\n} from \"@dataworks/sdk/client\";\nimport { validateMetric, filterValidMetrics } from \"./validation.js\";\nimport type {\n DataClientConfig,\n LoginResult,\n Metric,\n ErrorReport,\n SubscriptionHandler,\n Subscription,\n} from \"./types.js\";\n\nexport class DataClient {\n private readonly config: DataClientConfig;\n private credentials: LoginResult | null = null;\n private lastLoginUsername: string | null = null;\n private refreshInFlight: Promise<void> | null = null;\n\n constructor(config: DataClientConfig) {\n this.config = config;\n }\n\n /**\n * Authenticate with the Dataworks platform using Cognito USER_PASSWORD_AUTH.\n * Must be called before ingest(), reportError(), or subscribe().\n *\n * @param username - Cognito username\n * @param password - Cognito password\n * @returns Login result containing tokens and tenant\n * @see https://data-sdk-docs.dataworks.live/authentication\n */\n async login(username: string, password: string): Promise<LoginResult> {\n this.lastLoginUsername = username;\n this.credentials = await loginWithCredentials(username, password, {\n cognitoEndpoint: this.config.cognitoEndpoint,\n clientId: this.config.clientId,\n clientSecret: this.config.clientSecret,\n });\n return this.credentials;\n }\n\n /**\n * Ingest metric data points into the Dataworks Data Engine.\n * Validates each metric before sending — invalid metrics are silently dropped.\n *\n * @param metrics - Array of metric data points\n * @param eventId - Event identifier\n * @param datasetDatasourceId - Dataset-datasource identifier\n * @throws Error if not authenticated or if the request fails\n * @see https://data-sdk-docs.dataworks.live/ingesting-data\n */\n async ingest(\n metrics: Metric[],\n eventId: string,\n datasetDatasourceId: string,\n ): Promise<void> {\n this.requireAuth();\n\n const valid = filterValidMetrics(metrics, (msg) =>\n console.warn(`[@dataworks-technology/data] ${msg}`),\n );\n\n if (valid.length === 0) return;\n\n const payload = {\n eventId,\n datasetDatasourceId,\n metrics: valid,\n };\n\n const resp = await this.fetchWithAutoRefresh((accessToken) =>\n fetch(this.config.ingestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify(payload),\n }),\n );\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(\n `[@dataworks-technology/data] ingest failed: ${resp.status} — ${body}`,\n );\n }\n }\n\n /**\n * Report an error to the Dataworks Data Engine.\n *\n * @param error - Error report details\n * @throws Error if not authenticated or if the request fails\n * @see https://data-sdk-docs.dataworks.live/error-reporting\n */\n async reportError(error: ErrorReport): Promise<void> {\n this.requireAuth();\n\n const resp = await this.fetchWithAutoRefresh((accessToken) =>\n fetch(this.config.errorUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify({\n client_id: String(error.clientId),\n dataset_datasource_id: error.datasetDatasourceId,\n athlete_id: error.athleteId,\n error_title: error.errorTitle,\n error_description: error.errorDescription,\n }),\n }),\n );\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(\n `[@dataworks-technology/data] reportError failed: ${resp.status} — ${body}`,\n );\n }\n }\n\n /**\n * Subscribe to a real-time data channel on the AppSync Events API.\n * Uses the Cognito JWT for authentication.\n *\n * Channel format: dataworks/{datasetDatasourceId}/{eventId}/{metric}\n * Use \"*\" for metric to receive all metrics for a dataset-datasource/event pair.\n * Examples: \"dataworks/1/1/heartrate\", \"dataworks/1/1/*\".\n *\n * @param channel - Channel path to subscribe to\n * @param onEvent - Callback invoked for each received event\n * @returns Subscription handle with a close() method\n * @throws Error if not authenticated\n * @see https://data-sdk-docs.dataworks.live/real-time-subscriptions\n */\n subscribe(channel: string, onEvent: SubscriptionHandler): Subscription {\n this.requireAuth();\n\n if (typeof WebSocket === \"undefined\") {\n throw new Error(\n \"[@dataworks-technology/data] WebSocket is not available. \" +\n \"Use Node >= 22, Bun, or a browser environment. \" +\n \"For older Node versions, install a WebSocket polyfill (e.g. ws) and assign it to globalThis.WebSocket.\",\n );\n }\n if (typeof crypto?.randomUUID !== \"function\") {\n throw new Error(\n \"[@dataworks-technology/data] crypto.randomUUID() is not available. \" +\n \"Use Node >= 19, Bun, or a browser with Crypto API support.\",\n );\n }\n\n const channelPath = channel.startsWith(\"/\") ? channel : `/${channel}`;\n\n return createAutoRefreshingSubscription({\n refreshAuth: async () => {\n await this.refreshSession();\n },\n connect: ({ onUnexpectedClose }) => {\n const url = new URL(this.config.realtimeUrl);\n // AppSync Events API uses the /event/realtime path for WebSocket connections\n url.pathname = \"/event/realtime\";\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\n const host = new URL(this.config.realtimeUrl).host;\n // AWS AppSync Events API requires base64URL-encoded auth in the subprotocol.\n const authPayload = JSON.stringify({\n Authorization: this.credentials!.accessToken,\n host,\n });\n const authHeader = toBase64Url(authPayload);\n\n const ws = new WebSocket(url.toString(), [\n \"aws-appsync-event-ws\",\n `header-${authHeader}`,\n ]);\n\n let closedByUser = false;\n let unexpectedCloseHandled = false;\n\n const handleUnexpectedCloseOnce = () => {\n if (unexpectedCloseHandled) return;\n unexpectedCloseHandled = true;\n onUnexpectedClose();\n };\n\n ws.addEventListener(\"open\", () => {\n // Send connection_init — required for AppSync to send connection_ack\n ws.send(JSON.stringify({ type: \"connection_init\" }));\n });\n\n ws.addEventListener(\"message\", (event) => {\n try {\n const msg = JSON.parse(String(event.data));\n\n if (msg.type === \"connection_ack\") {\n ws.send(\n JSON.stringify({\n type: \"subscribe\",\n id: crypto.randomUUID(),\n channel: channelPath,\n authorization: {\n Authorization: this.credentials!.accessToken,\n host,\n },\n }),\n );\n } else if (msg.type === \"data\") {\n onEvent(JSON.parse(msg.event));\n } else if (\n msg.type === \"error\" &&\n JSON.stringify(msg).toLowerCase().includes(\"unauthor\")\n ) {\n handleUnexpectedCloseOnce();\n }\n } catch {\n // Ignore malformed or non-data messages (ka, subscribe_success, etc.)\n }\n });\n\n ws.addEventListener(\"close\", () => {\n if (!closedByUser) {\n handleUnexpectedCloseOnce();\n }\n });\n\n return {\n close() {\n closedByUser = true;\n ws.close();\n },\n };\n },\n });\n }\n\n /**\n * Check whether a metric is valid before ingesting.\n * Returns null if valid, or a human-readable reason string if invalid.\n */\n static validateMetric = validateMetric;\n\n /**\n * Filter an array of metrics, keeping only valid ones.\n * Logs a warning for each dropped item.\n */\n static filterValidMetrics = filterValidMetrics;\n\n /** Returns true if the client has been authenticated via login(). */\n get isAuthenticated(): boolean {\n return this.credentials !== null;\n }\n\n /** Returns the tenant from the last successful login, or null. */\n get tenant(): string | null {\n return this.credentials?.tenant ?? null;\n }\n\n /** @internal Throws if not authenticated. */\n private requireAuth(): void {\n if (!this.credentials) {\n throw new Error(\n \"[@dataworks-technology/data] Not authenticated — call login() first\",\n );\n }\n }\n\n private async fetchWithAutoRefresh(\n makeRequest: (accessToken: string) => Promise<Response>,\n ): Promise<Response> {\n this.requireAuth();\n\n let response = await makeRequest(this.credentials!.accessToken);\n if (response.status !== 401) {\n return response;\n }\n\n await this.refreshSession();\n response = await makeRequest(this.credentials!.accessToken);\n return response;\n }\n\n private async refreshSession(): Promise<void> {\n this.requireAuth();\n\n if (!this.credentials!.refreshToken) {\n throw new Error(\n \"[@dataworks-technology/data] Session expired and no refresh token is available — call login() again\",\n );\n }\n\n if (!this.refreshInFlight) {\n this.refreshInFlight = (async () => {\n this.credentials = await refreshWithToken(\n this.credentials!.refreshToken,\n {\n cognitoEndpoint: this.config.cognitoEndpoint,\n clientId: this.config.clientId,\n clientSecret: this.config.clientSecret,\n username: this.lastLoginUsername ?? undefined,\n previousTenant: this.credentials!.tenant,\n previousIdToken: this.credentials!.idToken,\n },\n );\n })().finally(() => {\n this.refreshInFlight = null;\n });\n }\n\n await this.refreshInFlight;\n }\n}\n"],"mappings":"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","names":["crypto","validateMetric","filterValidMetrics","filterValidMetrics","validateMetric"]}
1
+ {"version":3,"sources":["../node_modules/@dataworks/sdk/dist/client/subscription-manager.js","../node_modules/@dataworks/sdk/dist/client/validation.js","../node_modules/@dataworks/sdk/dist/client/base64url.js","../node_modules/@dataworks/sdk/dist/client/auth.js","../src/validation.ts","../src/data-client.ts"],"sourcesContent":["/**\n * Generic subscription lifecycle manager with auth-refresh reconnect support.\n *\n * The protocol-specific client provides a `connect` function and calls\n * `onUnexpectedClose` when auth expiry or unexpected socket termination occurs.\n */\nexport function createAutoRefreshingSubscription(options) {\n const maxReconnectAttempts = options.maxReconnectAttempts ?? 1;\n let reconnectAttempts = 0;\n let reconnectInFlight = false;\n let closedByUser = false;\n let activeConnection = null;\n const hooks = {\n onUnexpectedClose: () => {\n if (closedByUser || reconnectInFlight) {\n return;\n }\n if (reconnectAttempts >= maxReconnectAttempts) {\n return;\n }\n reconnectInFlight = true;\n reconnectAttempts += 1;\n void (async () => {\n try {\n await options.refreshAuth();\n if (closedByUser) {\n return;\n }\n activeConnection = options.connect(hooks);\n }\n catch {\n options.onReconnectError?.(new Error(\"Subscription reconnect failed. Please retry or reauthenticate.\"));\n }\n finally {\n reconnectInFlight = false;\n }\n })();\n },\n };\n activeConnection = options.connect(hooks);\n return {\n close() {\n closedByUser = true;\n activeConnection?.close();\n activeConnection = null;\n },\n };\n}\n","/**\n * Metric validation for Dataworks payloads.\n *\n * Ported from Dataworks-Data packages/adaptors/shared-ts/src/validation.ts\n * to serve as the single source of truth for all Dataworks engine SDKs.\n *\n * @module @dataworks/sdk/client\n */\n/**\n * Returns null if the metric is valid, or a human-readable reason string if invalid.\n */\nexport function validateMetric(m) {\n if (typeof m.metric !== \"string\" || m.metric.trim() === \"\")\n return \"metric must be a non-empty string\";\n if (typeof m.athleteId !== \"string\" || m.athleteId.trim() === \"\")\n return \"athleteId must be a non-empty string\";\n if (!Number.isInteger(m.timestamp) || m.timestamp <= 0)\n return `timestamp must be a positive integer, got ${m.timestamp}`;\n if (m.value === null || m.value === undefined)\n return \"value is null/undefined\";\n if (typeof m.value === \"number\" && !isFinite(m.value))\n return `value is non-finite number (${m.value})`;\n if (typeof m.value !== \"number\" && typeof m.value !== \"string\")\n return `value type ${typeof m.value} not allowed (must be number or string)`;\n if (typeof m.value === \"string\" && m.value === \"\")\n return \"value is empty string\";\n return null;\n}\n/**\n * Filters an array of metrics, returning only valid ones.\n * Logs a warning for each dropped item via the provided warn function.\n */\nexport function filterValidMetrics(metrics, warn) {\n const { valid, dropped } = getMetricValidationResult(metrics);\n for (const item of dropped) {\n let valueStr;\n try {\n valueStr = JSON.stringify(item.metric.value);\n }\n catch {\n valueStr = String(item.metric.value);\n }\n warn(`Dropping invalid metric [${String(item.metric.metric)}=${valueStr}]: ${item.reason}`);\n }\n return valid;\n}\n/**\n * Validates metrics and returns both accepted and dropped items (with reasons).\n * This is useful for consumers that need structured diagnostics.\n */\nexport function getMetricValidationResult(metrics) {\n const valid = [];\n const dropped = [];\n for (const [index, m] of metrics.entries()) {\n const reason = validateMetric(m);\n if (reason) {\n dropped.push({\n index,\n reason,\n metric: m,\n });\n }\n else {\n valid.push(m);\n }\n }\n return { valid, dropped };\n}\n","/**\n * Runtime-agnostic base64url encode/decode.\n *\n * Prefers Node.js Buffer when available, falls back to btoa/atob for\n * browser and edge runtimes. Used for AppSync auth subprotocol headers\n * and JWT payload parsing.\n *\n * @module @dataworks/sdk/client\n */\n/** Encode a UTF-8 string to base64url. */\nexport const toBase64Url = (input) => {\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(input).toString(\"base64url\");\n }\n return btoa(input).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n};\n/** Decode a base64url string to UTF-8. */\nexport const fromBase64Url = (b64url) => {\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(b64url, \"base64url\").toString(\"utf-8\");\n }\n const padded = b64url.replace(/-/g, \"+\").replace(/_/g, \"/\") +\n \"=\".repeat(((-b64url.length % 4) + 4) % 4);\n return atob(padded);\n};\n","/**\n * Cognito authentication helpers.\n *\n * Provides M2M (client_credentials) and user (USER_PASSWORD_AUTH) flows.\n * Refactored from @dataworks/sdk e2e-utils.ts — uses native fetch instead\n * of axios so the client sub-path has zero heavy dependencies.\n *\n * @module @dataworks/sdk/client\n */\nimport { fromBase64Url } from \"./base64url.js\";\n/**\n * Fetch an M2M access token using the client_credentials grant.\n *\n * @param clientId - Cognito app client ID\n * @param clientSecret - Cognito app client secret\n * @param tokenUrl - Cognito token endpoint (e.g. https://<domain>.auth.<region>.amazoncognito.com/oauth2/token)\n * @returns Access token string\n */\nexport async function getClientToken(clientId, clientSecret, tokenUrl) {\n const resp = await fetch(tokenUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n grant_type: \"client_credentials\",\n client_id: clientId,\n client_secret: clientSecret,\n }),\n });\n if (!resp.ok) {\n throw new Error(`getClientToken: Cognito token request failed: ${resp.status} ${resp.statusText}`);\n }\n const data = (await resp.json());\n if (!data.access_token) {\n throw new Error(\"getClientToken: response missing access_token\");\n }\n return data.access_token;\n}\n/**\n * Authenticate a user via Cognito USER_PASSWORD_AUTH flow.\n * Returns access token, ID token, refresh token, and tenant (extracted from JWT).\n *\n * Used by external developer clients — no client secret required when the\n * Cognito app client is configured without one.\n *\n * @param username - Cognito username\n * @param password - Cognito password\n * @param config - Cognito endpoint and client configuration\n * @returns Login result with tokens and tenant\n */\nexport async function loginWithCredentials(username, password, config) {\n const authParams = {\n USERNAME: username,\n PASSWORD: password,\n };\n // If a client secret is configured, compute SECRET_HASH\n if (config.clientSecret) {\n authParams.SECRET_HASH = await computeSecretHashAsync(username, config.clientId, config.clientSecret);\n }\n const resp = await fetch(config.cognitoEndpoint, {\n method: \"POST\",\n headers: {\n \"X-Amz-Target\": \"AWSCognitoIdentityProviderService.InitiateAuth\",\n \"Content-Type\": \"application/x-amz-json-1.1\",\n },\n body: JSON.stringify({\n AuthFlow: \"USER_PASSWORD_AUTH\",\n ClientId: config.clientId,\n AuthParameters: authParams,\n }),\n });\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(`loginWithCredentials: Cognito auth failed: ${resp.status} — ${body}`);\n }\n const data = (await resp.json());\n const result = data.AuthenticationResult;\n if (!result?.AccessToken || !result?.IdToken) {\n throw new Error(\"loginWithCredentials: response missing AccessToken or IdToken\");\n }\n // Extract tenant from JWT claims (ID token payload)\n const tenant = extractTenantFromJwt(result.IdToken);\n return {\n accessToken: result.AccessToken,\n idToken: result.IdToken,\n refreshToken: result.RefreshToken ?? \"\",\n tenant,\n };\n}\n/**\n * Refreshes Cognito user tokens using REFRESH_TOKEN_AUTH.\n *\n * Returns updated tokens and tenant, preserving previous tenant/idToken when\n * Cognito omits fields in the refresh response.\n *\n * @param refreshToken - The refresh token string\n * @param config - Cognito endpoint and client configuration, plus optional username/previousTenant/previousIdToken\n */\nexport async function refreshWithToken(refreshToken, config) {\n const authParams = {\n REFRESH_TOKEN: refreshToken,\n };\n if (config.clientSecret) {\n if (!config.username) {\n throw new Error(\"refreshWithToken: username is required when clientSecret is configured\");\n }\n authParams.SECRET_HASH = await computeSecretHashAsync(config.username, config.clientId, config.clientSecret);\n }\n const resp = await fetch(config.cognitoEndpoint, {\n method: \"POST\",\n headers: {\n \"X-Amz-Target\": \"AWSCognitoIdentityProviderService.InitiateAuth\",\n \"Content-Type\": \"application/x-amz-json-1.1\",\n },\n body: JSON.stringify({\n AuthFlow: \"REFRESH_TOKEN_AUTH\",\n ClientId: config.clientId,\n AuthParameters: authParams,\n }),\n });\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(`refreshWithToken: Cognito refresh failed: ${resp.status} — ${body}`);\n }\n const data = (await resp.json());\n const result = data.AuthenticationResult;\n if (!result?.AccessToken) {\n throw new Error(\"refreshWithToken: response missing AccessToken\");\n }\n const idToken = result.IdToken ?? config.previousIdToken ?? \"\";\n const extractedTenant = idToken ? extractTenantFromJwt(idToken) : \"\";\n const tenant = extractedTenant || config.previousTenant || \"\";\n return {\n accessToken: result.AccessToken,\n idToken,\n refreshToken: result.RefreshToken ?? refreshToken,\n tenant,\n };\n}\n/**\n * Extracts the tenant claim from a JWT ID token without verifying the signature.\n * The token is validated server-side by AppSync/API Gateway — this is a client-side\n * convenience for reading the tenant value.\n */\nfunction extractTenantFromJwt(idToken) {\n try {\n const payload = idToken.split(\".\")[1];\n const decoded = JSON.parse(fromBase64Url(payload));\n // Cognito custom attributes use \"custom:tenant\" claim\n return (decoded[\"custom:tenants\"] ??\n decoded[\"custom:tenant\"] ??\n decoded.tenant ??\n decoded[\"cognito:groups\"]?.[0] ??\n \"\");\n }\n catch {\n return \"\";\n }\n}\nasync function computeSecretHashAsync(username, clientId, clientSecret) {\n // Dynamic import to keep this module usable in environments without crypto.\n const crypto = await import(\"crypto\");\n return crypto\n .createHmac(\"sha256\", clientSecret)\n .update(username + clientId)\n .digest(\"base64\");\n}\n","/**\n * Validation re-exports — thin wrappers around @dataworks/sdk/client validation.\n * Declared locally so the published .d.ts is fully self-contained.\n *\n * @module @dataworks-technology/data\n */\n\nimport {\n validateMetric as _validateMetric,\n filterValidMetrics as _filterValidMetrics,\n} from \"@dataworks/sdk/client\";\nimport type { RawMetricItem, MetricItem } from \"./types.js\";\n\n/**\n * Check whether a metric is valid.\n * Returns null if valid, or a human-readable reason string if invalid.\n */\nexport const validateMetric: (m: RawMetricItem) => string | null =\n _validateMetric;\n\n/**\n * Filter an array of metrics, keeping only valid ones.\n * Logs a warning for each dropped item via the provided warn function.\n */\nexport const filterValidMetrics: (\n metrics: RawMetricItem[],\n warn: (msg: string) => void,\n) => MetricItem[] = _filterValidMetrics;\n","/**\n * DataClient — the public entry point for external developers using the Dataworks Data Engine.\n *\n * Handles authentication, metric ingestion, error reporting, and real-time subscriptions.\n * All SDK foundation code (@dataworks/sdk) is bundled at build time — consumers install\n * only @dataworks-technology/data with zero transitive dependencies.\n *\n * @example\n * ```ts\n * import { DataClient } from \"@dataworks-technology/data\";\n *\n * const dataworks = new DataClient({\n * cognitoEndpoint: \"https://cognito-idp.eu-west-1.amazonaws.com/\",\n * clientId: \"abc123\",\n * ingestUrl: \"https://dev-realtime.dataworks.live\",\n * errorUrl: \"https://dev-realtime-errors.dataworks.live\",\n * realtimeUrl: \"https://dev-event-api.dataworks.live\",\n * });\n *\n * await dataworks.login(\"graeme\", \"password123\");\n * await dataworks.ingest([{ athleteId: \"1\", metric: \"heartrate_calculated\", value: 172, timestamp: Date.now() / 1000 }], \"evt1\", \"ds1\");\n * ```\n *\n * @module @dataworks-technology/data\n */\n\nimport {\n createAutoRefreshingSubscription,\n loginWithCredentials,\n refreshWithToken,\n toBase64Url,\n} from \"@dataworks/sdk/client\";\nimport { validateMetric, filterValidMetrics } from \"./validation.js\";\nimport type {\n DataClientConfig,\n LoginResult,\n Metric,\n ErrorReport,\n SubscriptionHandler,\n ErrorHandler,\n Subscription,\n} from \"./types.js\";\n\nexport class DataClient {\n private readonly config: DataClientConfig;\n private credentials: LoginResult | null = null;\n private lastLoginUsername: string | null = null;\n private refreshInFlight: Promise<void> | null = null;\n\n constructor(config: DataClientConfig) {\n this.config = config;\n }\n\n /**\n * Authenticate with the Dataworks platform using Cognito USER_PASSWORD_AUTH.\n * Must be called before ingest(), reportError(), or subscribe().\n *\n * @param username - Cognito username\n * @param password - Cognito password\n * @returns Login result containing tokens and tenant\n * @see https://data-sdk-docs.dataworks.live/authentication\n */\n async login(username: string, password: string): Promise<LoginResult> {\n this.lastLoginUsername = username;\n this.credentials = await loginWithCredentials(username, password, {\n cognitoEndpoint: this.config.cognitoEndpoint,\n clientId: this.config.clientId,\n clientSecret: this.config.clientSecret,\n });\n return this.credentials;\n }\n\n /**\n * Ingest metric data points into the Dataworks Data Engine.\n * Validates each metric before sending — invalid metrics are silently dropped.\n *\n * @param metrics - Array of metric data points\n * @param eventId - Event identifier\n * @param datasetDatasourceId - Dataset-datasource identifier\n * @throws Error if not authenticated or if the request fails\n * @see https://data-sdk-docs.dataworks.live/ingesting-data\n */\n async ingest(\n metrics: Metric[],\n eventId: string,\n datasetDatasourceId: string,\n ): Promise<void> {\n this.requireAuth();\n\n const valid = filterValidMetrics(metrics, (msg) =>\n console.warn(`[@dataworks-technology/data] ${msg}`),\n );\n\n if (valid.length === 0) return;\n\n const payload = {\n eventId,\n datasetDatasourceId,\n metrics: valid,\n };\n\n const resp = await this.fetchWithAutoRefresh((accessToken) =>\n fetch(this.config.ingestUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify(payload),\n }),\n );\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(\n `[@dataworks-technology/data] ingest failed: ${resp.status} — ${body}`,\n );\n }\n }\n\n /**\n * Report an error to the Dataworks Data Engine.\n *\n * @param error - Error report details\n * @throws Error if not authenticated or if the request fails\n * @see https://data-sdk-docs.dataworks.live/error-reporting\n */\n async reportError(error: ErrorReport): Promise<void> {\n this.requireAuth();\n\n const resp = await this.fetchWithAutoRefresh((accessToken) =>\n fetch(this.config.errorUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify({\n client_id: String(error.clientId),\n dataset_datasource_id: error.datasetDatasourceId,\n athlete_id: error.athleteId,\n error_title: error.errorTitle,\n error_description: error.errorDescription,\n }),\n }),\n );\n\n if (!resp.ok) {\n const body = await resp.text().catch(() => \"\");\n throw new Error(\n `[@dataworks-technology/data] reportError failed: ${resp.status} — ${body}`,\n );\n }\n }\n\n /**\n * Subscribe to a real-time data channel on the AppSync Events API.\n * Uses the Cognito JWT for authentication.\n *\n * Channel format: dataworks/{datasetDatasourceId}/{eventId}/{metric}\n * Use \"*\" for metric to receive all metrics for a dataset-datasource/event pair.\n * Examples: \"dataworks/1/1/heartrate\", \"dataworks/1/1/*\".\n *\n * @param channel - Channel path to subscribe to\n * @param onEvent - Callback invoked for each received event\n * @returns Subscription handle with a close() method\n * @throws Error if not authenticated\n * @see https://data-sdk-docs.dataworks.live/real-time-subscriptions\n */\n subscribe(\n channel: string,\n onEvent: SubscriptionHandler,\n onError?: ErrorHandler,\n ): Subscription {\n this.requireAuth();\n\n if (typeof WebSocket === \"undefined\") {\n throw new Error(\n \"[@dataworks-technology/data] WebSocket is not available. \" +\n \"Use Node >= 22, Bun, or a browser environment. \" +\n \"For older Node versions, install a WebSocket polyfill (e.g. ws) and assign it to globalThis.WebSocket.\",\n );\n }\n if (typeof crypto?.randomUUID !== \"function\") {\n throw new Error(\n \"[@dataworks-technology/data] crypto.randomUUID() is not available. \" +\n \"Use Node >= 19, Bun, or a browser with Crypto API support.\",\n );\n }\n\n const channelPath = channel.startsWith(\"/\") ? channel : `/${channel}`;\n\n return createAutoRefreshingSubscription({\n refreshAuth: async () => {\n await this.refreshSession();\n },\n onReconnectError: (error) => {\n onError?.(error);\n },\n connect: ({ onUnexpectedClose }) => {\n const url = new URL(this.config.realtimeUrl);\n // AppSync Events API uses the /event/realtime path for WebSocket connections\n url.pathname = \"/event/realtime\";\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\n const host = new URL(this.config.realtimeUrl).host;\n // AWS AppSync Events API requires base64URL-encoded auth in the subprotocol.\n const authPayload = JSON.stringify({\n Authorization: this.credentials!.accessToken,\n host,\n });\n const authHeader = toBase64Url(authPayload);\n\n const ws = new WebSocket(url.toString(), [\n \"aws-appsync-event-ws\",\n `header-${authHeader}`,\n ]);\n\n let closedByUser = false;\n let unexpectedCloseHandled = false;\n\n const handleUnexpectedCloseOnce = () => {\n if (unexpectedCloseHandled) return;\n unexpectedCloseHandled = true;\n onUnexpectedClose();\n };\n\n ws.addEventListener(\"open\", () => {\n // Send connection_init — required for AppSync to send connection_ack\n ws.send(JSON.stringify({ type: \"connection_init\" }));\n });\n\n ws.addEventListener(\"error\", () => {\n onError?.(\n new Error(\n \"[@dataworks-technology/data] WebSocket connection error\",\n ),\n );\n });\n\n ws.addEventListener(\"message\", (event) => {\n try {\n const msg = JSON.parse(String(event.data));\n\n if (msg.type === \"connection_ack\") {\n ws.send(\n JSON.stringify({\n type: \"subscribe\",\n id: crypto.randomUUID(),\n channel: channelPath,\n authorization: {\n Authorization: this.credentials!.accessToken,\n host,\n },\n }),\n );\n } else if (msg.type === \"data\") {\n onEvent(JSON.parse(msg.event));\n } else if (\n msg.type === \"error\" ||\n msg.type === \"connection_error\" ||\n msg.type === \"subscribe_error\" ||\n msg.type === \"broadcast_error\" ||\n msg.type === \"unsubscribe_error\"\n ) {\n const msgStr = JSON.stringify(msg);\n if (msgStr.toLowerCase().includes(\"unauthor\")) {\n handleUnexpectedCloseOnce();\n } else {\n const errorMessage =\n msg.message ??\n msg.errors?.[0]?.message ??\n msg.errors?.[0]?.errorType ??\n \"AppSync subscription error\";\n onError?.(\n new Error(`[@dataworks-technology/data] ${errorMessage}`),\n );\n }\n }\n } catch {\n onError?.(\n new Error(\n \"[@dataworks-technology/data] Received malformed message from AppSync\",\n ),\n );\n }\n });\n\n ws.addEventListener(\"close\", () => {\n if (!closedByUser) {\n handleUnexpectedCloseOnce();\n }\n });\n\n return {\n close() {\n closedByUser = true;\n ws.close();\n },\n };\n },\n });\n }\n\n /**\n * Check whether a metric is valid before ingesting.\n * Returns null if valid, or a human-readable reason string if invalid.\n */\n static validateMetric = validateMetric;\n\n /**\n * Filter an array of metrics, keeping only valid ones.\n * Logs a warning for each dropped item.\n */\n static filterValidMetrics = filterValidMetrics;\n\n /** Returns true if the client has been authenticated via login(). */\n get isAuthenticated(): boolean {\n return this.credentials !== null;\n }\n\n /** Returns the tenant from the last successful login, or null. */\n get tenant(): string | null {\n return this.credentials?.tenant ?? null;\n }\n\n /** @internal Throws if not authenticated. */\n private requireAuth(): void {\n if (!this.credentials) {\n throw new Error(\n \"[@dataworks-technology/data] Not authenticated — call login() first\",\n );\n }\n }\n\n private async fetchWithAutoRefresh(\n makeRequest: (accessToken: string) => Promise<Response>,\n ): Promise<Response> {\n this.requireAuth();\n\n let response = await makeRequest(this.credentials!.accessToken);\n if (response.status !== 401) {\n return response;\n }\n\n await this.refreshSession();\n response = await makeRequest(this.credentials!.accessToken);\n return response;\n }\n\n private async refreshSession(): Promise<void> {\n this.requireAuth();\n\n if (!this.credentials!.refreshToken) {\n throw new Error(\n \"[@dataworks-technology/data] Session expired and no refresh token is available — call login() again\",\n );\n }\n\n if (!this.refreshInFlight) {\n this.refreshInFlight = (async () => {\n this.credentials = await refreshWithToken(\n this.credentials!.refreshToken,\n {\n cognitoEndpoint: this.config.cognitoEndpoint,\n clientId: this.config.clientId,\n clientSecret: this.config.clientSecret,\n username: this.lastLoginUsername ?? undefined,\n previousTenant: this.credentials!.tenant,\n previousIdToken: this.credentials!.idToken,\n },\n );\n })().finally(() => {\n this.refreshInFlight = null;\n });\n }\n\n await this.refreshInFlight;\n }\n}\n"],"mappings":";AAMO,SAAS,iCAAiC,SAAS;AACtD,QAAM,uBAAuB,QAAQ,wBAAwB;AAC7D,MAAI,oBAAoB;AACxB,MAAI,oBAAoB;AACxB,MAAI,eAAe;AACnB,MAAI,mBAAmB;AACvB,QAAM,QAAQ;AAAA,IACV,mBAAmB,MAAM;AACrB,UAAI,gBAAgB,mBAAmB;AACnC;AAAA,MACJ;AACA,UAAI,qBAAqB,sBAAsB;AAC3C;AAAA,MACJ;AACA,0BAAoB;AACpB,2BAAqB;AACrB,YAAM,YAAY;AACd,YAAI;AACA,gBAAM,QAAQ,YAAY;AAC1B,cAAI,cAAc;AACd;AAAA,UACJ;AACA,6BAAmB,QAAQ,QAAQ,KAAK;AAAA,QAC5C,QACM;AACF,kBAAQ,mBAAmB,IAAI,MAAM,gEAAgE,CAAC;AAAA,QAC1G,UACA;AACI,8BAAoB;AAAA,QACxB;AAAA,MACJ,GAAG;AAAA,IACP;AAAA,EACJ;AACA,qBAAmB,QAAQ,QAAQ,KAAK;AACxC,SAAO;AAAA,IACH,QAAQ;AACJ,qBAAe;AACf,wBAAkB,MAAM;AACxB,yBAAmB;AAAA,IACvB;AAAA,EACJ;AACJ;;;ACpCO,SAAS,eAAe,GAAG;AAC9B,MAAI,OAAO,EAAE,WAAW,YAAY,EAAE,OAAO,KAAK,MAAM;AACpD,WAAO;AACX,MAAI,OAAO,EAAE,cAAc,YAAY,EAAE,UAAU,KAAK,MAAM;AAC1D,WAAO;AACX,MAAI,CAAC,OAAO,UAAU,EAAE,SAAS,KAAK,EAAE,aAAa;AACjD,WAAO,6CAA6C,EAAE,SAAS;AACnE,MAAI,EAAE,UAAU,QAAQ,EAAE,UAAU;AAChC,WAAO;AACX,MAAI,OAAO,EAAE,UAAU,YAAY,CAAC,SAAS,EAAE,KAAK;AAChD,WAAO,+BAA+B,EAAE,KAAK;AACjD,MAAI,OAAO,EAAE,UAAU,YAAY,OAAO,EAAE,UAAU;AAClD,WAAO,cAAc,OAAO,EAAE,KAAK;AACvC,MAAI,OAAO,EAAE,UAAU,YAAY,EAAE,UAAU;AAC3C,WAAO;AACX,SAAO;AACX;AAKO,SAAS,mBAAmB,SAAS,MAAM;AAC9C,QAAM,EAAE,OAAO,QAAQ,IAAI,0BAA0B,OAAO;AAC5D,aAAW,QAAQ,SAAS;AACxB,QAAI;AACJ,QAAI;AACA,iBAAW,KAAK,UAAU,KAAK,OAAO,KAAK;AAAA,IAC/C,QACM;AACF,iBAAW,OAAO,KAAK,OAAO,KAAK;AAAA,IACvC;AACA,SAAK,4BAA4B,OAAO,KAAK,OAAO,MAAM,CAAC,IAAI,QAAQ,MAAM,KAAK,MAAM,EAAE;AAAA,EAC9F;AACA,SAAO;AACX;AAKO,SAAS,0BAA0B,SAAS;AAC/C,QAAM,QAAQ,CAAC;AACf,QAAM,UAAU,CAAC;AACjB,aAAW,CAAC,OAAO,CAAC,KAAK,QAAQ,QAAQ,GAAG;AACxC,UAAM,SAAS,eAAe,CAAC;AAC/B,QAAI,QAAQ;AACR,cAAQ,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACZ,CAAC;AAAA,IACL,OACK;AACD,YAAM,KAAK,CAAC;AAAA,IAChB;AAAA,EACJ;AACA,SAAO,EAAE,OAAO,QAAQ;AAC5B;;;ACzDO,IAAM,cAAc,CAAC,UAAU;AAClC,MAAI,OAAO,WAAW,aAAa;AAC/B,WAAO,OAAO,KAAK,KAAK,EAAE,SAAS,WAAW;AAAA,EAClD;AACA,SAAO,KAAK,KAAK,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAChF;AAEO,IAAM,gBAAgB,CAAC,WAAW;AACrC,MAAI,OAAO,WAAW,aAAa;AAC/B,WAAO,OAAO,KAAK,QAAQ,WAAW,EAAE,SAAS,OAAO;AAAA,EAC5D;AACA,QAAM,SAAS,OAAO,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG,IACtD,IAAI,QAAS,CAAC,OAAO,SAAS,IAAK,KAAK,CAAC;AAC7C,SAAO,KAAK,MAAM;AACtB;;;ACyBA,eAAsB,qBAAqB,UAAU,UAAU,QAAQ;AACnE,QAAM,aAAa;AAAA,IACf,UAAU;AAAA,IACV,UAAU;AAAA,EACd;AAEA,MAAI,OAAO,cAAc;AACrB,eAAW,cAAc,MAAM,uBAAuB,UAAU,OAAO,UAAU,OAAO,YAAY;AAAA,EACxG;AACA,QAAM,OAAO,MAAM,MAAM,OAAO,iBAAiB;AAAA,IAC7C,QAAQ;AAAA,IACR,SAAS;AAAA,MACL,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,IACpB;AAAA,IACA,MAAM,KAAK,UAAU;AAAA,MACjB,UAAU;AAAA,MACV,UAAU,OAAO;AAAA,MACjB,gBAAgB;AAAA,IACpB,CAAC;AAAA,EACL,CAAC;AACD,MAAI,CAAC,KAAK,IAAI;AACV,UAAM,OAAO,MAAM,KAAK,KAAK,EAAE,MAAM,MAAM,EAAE;AAC7C,UAAM,IAAI,MAAM,8CAA8C,KAAK,MAAM,WAAM,IAAI,EAAE;AAAA,EACzF;AACA,QAAM,OAAQ,MAAM,KAAK,KAAK;AAC9B,QAAM,SAAS,KAAK;AACpB,MAAI,CAAC,QAAQ,eAAe,CAAC,QAAQ,SAAS;AAC1C,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACnF;AAEA,QAAM,SAAS,qBAAqB,OAAO,OAAO;AAClD,SAAO;AAAA,IACH,aAAa,OAAO;AAAA,IACpB,SAAS,OAAO;AAAA,IAChB,cAAc,OAAO,gBAAgB;AAAA,IACrC;AAAA,EACJ;AACJ;AAUA,eAAsB,iBAAiB,cAAc,QAAQ;AACzD,QAAM,aAAa;AAAA,IACf,eAAe;AAAA,EACnB;AACA,MAAI,OAAO,cAAc;AACrB,QAAI,CAAC,OAAO,UAAU;AAClB,YAAM,IAAI,MAAM,wEAAwE;AAAA,IAC5F;AACA,eAAW,cAAc,MAAM,uBAAuB,OAAO,UAAU,OAAO,UAAU,OAAO,YAAY;AAAA,EAC/G;AACA,QAAM,OAAO,MAAM,MAAM,OAAO,iBAAiB;AAAA,IAC7C,QAAQ;AAAA,IACR,SAAS;AAAA,MACL,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,IACpB;AAAA,IACA,MAAM,KAAK,UAAU;AAAA,MACjB,UAAU;AAAA,MACV,UAAU,OAAO;AAAA,MACjB,gBAAgB;AAAA,IACpB,CAAC;AAAA,EACL,CAAC;AACD,MAAI,CAAC,KAAK,IAAI;AACV,UAAM,OAAO,MAAM,KAAK,KAAK,EAAE,MAAM,MAAM,EAAE;AAC7C,UAAM,IAAI,MAAM,6CAA6C,KAAK,MAAM,WAAM,IAAI,EAAE;AAAA,EACxF;AACA,QAAM,OAAQ,MAAM,KAAK,KAAK;AAC9B,QAAM,SAAS,KAAK;AACpB,MAAI,CAAC,QAAQ,aAAa;AACtB,UAAM,IAAI,MAAM,gDAAgD;AAAA,EACpE;AACA,QAAM,UAAU,OAAO,WAAW,OAAO,mBAAmB;AAC5D,QAAM,kBAAkB,UAAU,qBAAqB,OAAO,IAAI;AAClE,QAAM,SAAS,mBAAmB,OAAO,kBAAkB;AAC3D,SAAO;AAAA,IACH,aAAa,OAAO;AAAA,IACpB;AAAA,IACA,cAAc,OAAO,gBAAgB;AAAA,IACrC;AAAA,EACJ;AACJ;AAMA,SAAS,qBAAqB,SAAS;AACnC,MAAI;AACA,UAAM,UAAU,QAAQ,MAAM,GAAG,EAAE,CAAC;AACpC,UAAM,UAAU,KAAK,MAAM,cAAc,OAAO,CAAC;AAEjD,WAAQ,QAAQ,gBAAgB,KAC5B,QAAQ,eAAe,KACvB,QAAQ,UACR,QAAQ,gBAAgB,IAAI,CAAC,KAC7B;AAAA,EACR,QACM;AACF,WAAO;AAAA,EACX;AACJ;AACA,eAAe,uBAAuB,UAAU,UAAU,cAAc;AAEpE,QAAMA,UAAS,MAAM,OAAO,QAAQ;AACpC,SAAOA,QACF,WAAW,UAAU,YAAY,EACjC,OAAO,WAAW,QAAQ,EAC1B,OAAO,QAAQ;AACxB;;;ACpJO,IAAMC,kBACX;AAMK,IAAMC,sBAGO;;;ACgBb,IAAM,aAAN,MAAiB;AAAA,EAMtB,YAAY,QAA0B;AAJtC,SAAQ,cAAkC;AAC1C,SAAQ,oBAAmC;AAC3C,SAAQ,kBAAwC;AAG9C,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,MAAM,UAAkB,UAAwC;AACpE,SAAK,oBAAoB;AACzB,SAAK,cAAc,MAAM,qBAAqB,UAAU,UAAU;AAAA,MAChE,iBAAiB,KAAK,OAAO;AAAA,MAC7B,UAAU,KAAK,OAAO;AAAA,MACtB,cAAc,KAAK,OAAO;AAAA,IAC5B,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,OACJ,SACA,SACA,qBACe;AACf,SAAK,YAAY;AAEjB,UAAM,QAAQC;AAAA,MAAmB;AAAA,MAAS,CAAC,QACzC,QAAQ,KAAK,gCAAgC,GAAG,EAAE;AAAA,IACpD;AAEA,QAAI,MAAM,WAAW,EAAG;AAExB,UAAM,UAAU;AAAA,MACd;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IACX;AAEA,UAAM,OAAO,MAAM,KAAK;AAAA,MAAqB,CAAC,gBAC5C,MAAM,KAAK,OAAO,WAAW;AAAA,QAC3B,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,UAAU,WAAW;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU,OAAO;AAAA,MAC9B,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,KAAK,IAAI;AACZ,YAAM,OAAO,MAAM,KAAK,KAAK,EAAE,MAAM,MAAM,EAAE;AAC7C,YAAM,IAAI;AAAA,QACR,+CAA+C,KAAK,MAAM,WAAM,IAAI;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,YAAY,OAAmC;AACnD,SAAK,YAAY;AAEjB,UAAM,OAAO,MAAM,KAAK;AAAA,MAAqB,CAAC,gBAC5C,MAAM,KAAK,OAAO,UAAU;AAAA,QAC1B,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,UAAU,WAAW;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,WAAW,OAAO,MAAM,QAAQ;AAAA,UAChC,uBAAuB,MAAM;AAAA,UAC7B,YAAY,MAAM;AAAA,UAClB,aAAa,MAAM;AAAA,UACnB,mBAAmB,MAAM;AAAA,QAC3B,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,KAAK,IAAI;AACZ,YAAM,OAAO,MAAM,KAAK,KAAK,EAAE,MAAM,MAAM,EAAE;AAC7C,YAAM,IAAI;AAAA,QACR,oDAAoD,KAAK,MAAM,WAAM,IAAI;AAAA,MAC3E;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,UACE,SACA,SACA,SACc;AACd,SAAK,YAAY;AAEjB,QAAI,OAAO,cAAc,aAAa;AACpC,YAAM,IAAI;AAAA,QACR;AAAA,MAGF;AAAA,IACF;AACA,QAAI,OAAO,QAAQ,eAAe,YAAY;AAC5C,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AAEA,UAAM,cAAc,QAAQ,WAAW,GAAG,IAAI,UAAU,IAAI,OAAO;AAEnE,WAAO,iCAAiC;AAAA,MACtC,aAAa,YAAY;AACvB,cAAM,KAAK,eAAe;AAAA,MAC5B;AAAA,MACA,kBAAkB,CAAC,UAAU;AAC3B,kBAAU,KAAK;AAAA,MACjB;AAAA,MACA,SAAS,CAAC,EAAE,kBAAkB,MAAM;AAClC,cAAM,MAAM,IAAI,IAAI,KAAK,OAAO,WAAW;AAE3C,YAAI,WAAW;AACf,YAAI,WAAW,IAAI,aAAa,WAAW,SAAS;AAEpD,cAAM,OAAO,IAAI,IAAI,KAAK,OAAO,WAAW,EAAE;AAE9C,cAAM,cAAc,KAAK,UAAU;AAAA,UACjC,eAAe,KAAK,YAAa;AAAA,UACjC;AAAA,QACF,CAAC;AACD,cAAM,aAAa,YAAY,WAAW;AAE1C,cAAM,KAAK,IAAI,UAAU,IAAI,SAAS,GAAG;AAAA,UACvC;AAAA,UACA,UAAU,UAAU;AAAA,QACtB,CAAC;AAED,YAAI,eAAe;AACnB,YAAI,yBAAyB;AAE7B,cAAM,4BAA4B,MAAM;AACtC,cAAI,uBAAwB;AAC5B,mCAAyB;AACzB,4BAAkB;AAAA,QACpB;AAEA,WAAG,iBAAiB,QAAQ,MAAM;AAEhC,aAAG,KAAK,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC,CAAC;AAAA,QACrD,CAAC;AAED,WAAG,iBAAiB,SAAS,MAAM;AACjC;AAAA,YACE,IAAI;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAED,WAAG,iBAAiB,WAAW,CAAC,UAAU;AACxC,cAAI;AACF,kBAAM,MAAM,KAAK,MAAM,OAAO,MAAM,IAAI,CAAC;AAEzC,gBAAI,IAAI,SAAS,kBAAkB;AACjC,iBAAG;AAAA,gBACD,KAAK,UAAU;AAAA,kBACb,MAAM;AAAA,kBACN,IAAI,OAAO,WAAW;AAAA,kBACtB,SAAS;AAAA,kBACT,eAAe;AAAA,oBACb,eAAe,KAAK,YAAa;AAAA,oBACjC;AAAA,kBACF;AAAA,gBACF,CAAC;AAAA,cACH;AAAA,YACF,WAAW,IAAI,SAAS,QAAQ;AAC9B,sBAAQ,KAAK,MAAM,IAAI,KAAK,CAAC;AAAA,YAC/B,WACE,IAAI,SAAS,WACb,IAAI,SAAS,sBACb,IAAI,SAAS,qBACb,IAAI,SAAS,qBACb,IAAI,SAAS,qBACb;AACA,oBAAM,SAAS,KAAK,UAAU,GAAG;AACjC,kBAAI,OAAO,YAAY,EAAE,SAAS,UAAU,GAAG;AAC7C,0CAA0B;AAAA,cAC5B,OAAO;AACL,sBAAM,eACJ,IAAI,WACJ,IAAI,SAAS,CAAC,GAAG,WACjB,IAAI,SAAS,CAAC,GAAG,aACjB;AACF;AAAA,kBACE,IAAI,MAAM,gCAAgC,YAAY,EAAE;AAAA,gBAC1D;AAAA,cACF;AAAA,YACF;AAAA,UACF,QAAQ;AACN;AAAA,cACE,IAAI;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAED,WAAG,iBAAiB,SAAS,MAAM;AACjC,cAAI,CAAC,cAAc;AACjB,sCAA0B;AAAA,UAC5B;AAAA,QACF,CAAC;AAED,eAAO;AAAA,UACL,QAAQ;AACN,2BAAe;AACf,eAAG,MAAM;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA,EAeA,IAAI,kBAA2B;AAC7B,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA;AAAA,EAGA,IAAI,SAAwB;AAC1B,WAAO,KAAK,aAAa,UAAU;AAAA,EACrC;AAAA;AAAA,EAGQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,aAAa;AACrB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,qBACZ,aACmB;AACnB,SAAK,YAAY;AAEjB,QAAI,WAAW,MAAM,YAAY,KAAK,YAAa,WAAW;AAC9D,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,KAAK,eAAe;AAC1B,eAAW,MAAM,YAAY,KAAK,YAAa,WAAW;AAC1D,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,iBAAgC;AAC5C,SAAK,YAAY;AAEjB,QAAI,CAAC,KAAK,YAAa,cAAc;AACnC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,iBAAiB;AACzB,WAAK,mBAAmB,YAAY;AAClC,aAAK,cAAc,MAAM;AAAA,UACvB,KAAK,YAAa;AAAA,UAClB;AAAA,YACE,iBAAiB,KAAK,OAAO;AAAA,YAC7B,UAAU,KAAK,OAAO;AAAA,YACtB,cAAc,KAAK,OAAO;AAAA,YAC1B,UAAU,KAAK,qBAAqB;AAAA,YACpC,gBAAgB,KAAK,YAAa;AAAA,YAClC,iBAAiB,KAAK,YAAa;AAAA,UACrC;AAAA,QACF;AAAA,MACF,GAAG,EAAE,QAAQ,MAAM;AACjB,aAAK,kBAAkB;AAAA,MACzB,CAAC;AAAA,IACH;AAEA,UAAM,KAAK;AAAA,EACb;AACF;AAAA;AAAA;AAAA;AAAA;AAhVa,WAyQJ,iBAAiBC;AAAA;AAAA;AAAA;AAAA;AAzQb,WA+QJ,qBAAqBD;","names":["crypto","validateMetric","filterValidMetrics","filterValidMetrics","validateMetric"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dataworks-technology/data",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Dataworks Data Engine SDK — authenticate, ingest metrics, and subscribe to live data",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",