@dataworks-technology/data 0.1.4 → 0.1.6
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 +93 -38
- package/dist/index.cjs +288 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +288 -72
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@dataworks-technology/data)
|
|
4
4
|
[](./LICENSE)
|
|
5
5
|
[](https://bundlephobia.com/package/@dataworks-technology/data)
|
|
6
|
+
[](https://data-sdk-docs.dataworks.live)
|
|
6
7
|
|
|
7
8
|
Official SDK for the Dataworks Data Engine — authenticate, ingest live athlete metrics, subscribe to real-time data streams, and report errors.
|
|
8
9
|
|
|
@@ -51,7 +52,7 @@ bun add @dataworks-technology/data
|
|
|
51
52
|
```typescript
|
|
52
53
|
import { DataClient } from "@dataworks-technology/data";
|
|
53
54
|
|
|
54
|
-
const
|
|
55
|
+
const dataworks = new DataClient({
|
|
55
56
|
cognitoEndpoint: "https://cognito-idp.eu-west-1.amazonaws.com/",
|
|
56
57
|
clientId: "your-client-id",
|
|
57
58
|
ingestUrl: "https://your-ingest-endpoint.dataworks.live",
|
|
@@ -60,14 +61,14 @@ const client = new DataClient({
|
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
// Authenticate
|
|
63
|
-
await
|
|
64
|
+
await dataworks.login("username", "password");
|
|
64
65
|
|
|
65
66
|
// Ingest metrics
|
|
66
|
-
await
|
|
67
|
+
await dataworks.ingest(
|
|
67
68
|
[
|
|
68
69
|
{
|
|
69
70
|
athleteId: "athlete-1",
|
|
70
|
-
metric: "
|
|
71
|
+
metric: "heartrate_calculated",
|
|
71
72
|
value: 172,
|
|
72
73
|
timestamp: Math.floor(Date.now() / 1000),
|
|
73
74
|
},
|
|
@@ -84,7 +85,7 @@ await client.ingest(
|
|
|
84
85
|
Create a new client instance.
|
|
85
86
|
|
|
86
87
|
```typescript
|
|
87
|
-
const
|
|
88
|
+
const dataworks = new DataClient({
|
|
88
89
|
cognitoEndpoint: "https://cognito-idp.eu-west-1.amazonaws.com/",
|
|
89
90
|
clientId: "abc123",
|
|
90
91
|
ingestUrl: "https://ingest.dataworks.live",
|
|
@@ -93,52 +94,62 @@ const client = new DataClient({
|
|
|
93
94
|
});
|
|
94
95
|
```
|
|
95
96
|
|
|
96
|
-
### `
|
|
97
|
+
### `dataworks.login(username, password)`
|
|
97
98
|
|
|
98
99
|
Authenticate with Cognito. Must be called before any other operation.
|
|
99
100
|
|
|
100
101
|
```typescript
|
|
101
|
-
const result = await
|
|
102
|
+
const result = await dataworks.login("username", "password");
|
|
102
103
|
// result: { accessToken, idToken, refreshToken, tenant }
|
|
103
104
|
```
|
|
104
105
|
|
|
105
|
-
### `
|
|
106
|
+
### `dataworks.ingest(metrics, eventId, datasetDatasourceId)`
|
|
106
107
|
|
|
107
108
|
Send metric data points to the Data Engine. Invalid metrics are automatically filtered out.
|
|
108
109
|
|
|
109
110
|
```typescript
|
|
110
|
-
await
|
|
111
|
+
await dataworks.ingest(
|
|
111
112
|
[
|
|
112
|
-
{ athleteId: "1", metric: "
|
|
113
|
-
{ athleteId: "1", metric: "
|
|
113
|
+
{ athleteId: "1", metric: "heartrate_calculated", value: 172, timestamp: 1700000000 },
|
|
114
|
+
{ athleteId: "1", metric: "speed_calculated", value: 4.2, timestamp: 1700000000 },
|
|
114
115
|
],
|
|
115
116
|
"event-123",
|
|
116
117
|
"ds-456",
|
|
117
118
|
);
|
|
118
119
|
```
|
|
119
120
|
|
|
120
|
-
### `
|
|
121
|
+
### `dataworks.subscribe(channel, onEvent, onError?)`
|
|
121
122
|
|
|
122
123
|
Subscribe to real-time data events via WebSocket.
|
|
123
124
|
|
|
125
|
+
Tip: start with `dataworks/1/1/*` to inspect all metrics for a dataset-datasource/event pair, then narrow to a specific metric channel such as `dataworks/1/1/heartrate`.
|
|
126
|
+
|
|
124
127
|
```typescript
|
|
125
|
-
const subscription =
|
|
126
|
-
"/
|
|
128
|
+
const subscription = dataworks.subscribe(
|
|
129
|
+
"dataworks/1/1/heartrate",
|
|
127
130
|
(event) => {
|
|
128
131
|
console.log("Received:", event);
|
|
129
132
|
},
|
|
133
|
+
(error) => {
|
|
134
|
+
console.error("Subscription error:", error.message);
|
|
135
|
+
},
|
|
130
136
|
);
|
|
131
137
|
|
|
132
138
|
// Later: close the subscription
|
|
133
139
|
subscription.close();
|
|
134
140
|
```
|
|
135
141
|
|
|
136
|
-
|
|
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
|
+
|
|
147
|
+
### `dataworks.reportError(error)`
|
|
137
148
|
|
|
138
149
|
Report an error to the Data Engine for monitoring and alerting.
|
|
139
150
|
|
|
140
151
|
```typescript
|
|
141
|
-
await
|
|
152
|
+
await dataworks.reportError({
|
|
142
153
|
datasetDatasourceId: "ds-456",
|
|
143
154
|
athleteId: "athlete-123",
|
|
144
155
|
clientId: 1,
|
|
@@ -147,22 +158,22 @@ await client.reportError({
|
|
|
147
158
|
});
|
|
148
159
|
```
|
|
149
160
|
|
|
150
|
-
### `
|
|
161
|
+
### `dataworks.isAuthenticated`
|
|
151
162
|
|
|
152
163
|
Check if the client has valid credentials.
|
|
153
164
|
|
|
154
165
|
```typescript
|
|
155
|
-
if (
|
|
156
|
-
await
|
|
166
|
+
if (dataworks.isAuthenticated) {
|
|
167
|
+
await dataworks.ingest(metrics, eventId, dsId);
|
|
157
168
|
}
|
|
158
169
|
```
|
|
159
170
|
|
|
160
|
-
### `
|
|
171
|
+
### `dataworks.tenant`
|
|
161
172
|
|
|
162
173
|
Get the tenant from the authenticated session (or `null`).
|
|
163
174
|
|
|
164
175
|
```typescript
|
|
165
|
-
console.log(`Logged in as tenant: ${
|
|
176
|
+
console.log(`Logged in as tenant: ${dataworks.tenant}`);
|
|
166
177
|
```
|
|
167
178
|
|
|
168
179
|
## Validation Utilities
|
|
@@ -221,6 +232,7 @@ import type {
|
|
|
221
232
|
MetricsPayload,
|
|
222
233
|
ErrorReport,
|
|
223
234
|
SubscriptionHandler,
|
|
235
|
+
ErrorHandler,
|
|
224
236
|
Subscription,
|
|
225
237
|
} from "@dataworks-technology/data";
|
|
226
238
|
```
|
|
@@ -257,13 +269,13 @@ All async methods throw on failure. Wrap calls in try/catch:
|
|
|
257
269
|
|
|
258
270
|
```typescript
|
|
259
271
|
try {
|
|
260
|
-
await
|
|
272
|
+
await dataworks.login("user", "pass");
|
|
261
273
|
} catch (err) {
|
|
262
274
|
// Authentication failed (invalid credentials, network error, etc.)
|
|
263
275
|
}
|
|
264
276
|
|
|
265
277
|
try {
|
|
266
|
-
await
|
|
278
|
+
await dataworks.ingest(metrics, eventId, dsId);
|
|
267
279
|
} catch (err) {
|
|
268
280
|
// Ingestion failed (401 expired token, 5xx server error, etc.)
|
|
269
281
|
}
|
|
@@ -285,7 +297,7 @@ A core use case: subscribe to live metrics, apply your own calculations external
|
|
|
285
297
|
### Concept
|
|
286
298
|
|
|
287
299
|
```
|
|
288
|
-
Data Engine → subscribe("heartrate") → Your App → calculate rolling avg → ingest("
|
|
300
|
+
Data Engine → subscribe("dataworks/{datasetDatasourceId}/{eventId}/heartrate") → Your App → calculate rolling avg → ingest("heartrate_rolling_avg_calculated") → Data Engine
|
|
289
301
|
```
|
|
290
302
|
|
|
291
303
|
Any metric you ingest is treated the same as raw sensor data — it flows through the enrichment pipeline (avg/min/max/zones), gets stored, and is available via subscriptions and the GraphQL API.
|
|
@@ -295,7 +307,7 @@ Any metric you ingest is treated the same as raw sensor data — it flows throug
|
|
|
295
307
|
```typescript
|
|
296
308
|
import { DataClient } from "@dataworks-technology/data";
|
|
297
309
|
|
|
298
|
-
const
|
|
310
|
+
const dataworks = new DataClient({
|
|
299
311
|
cognitoEndpoint: "https://cognito-idp.eu-west-1.amazonaws.com/",
|
|
300
312
|
clientId: "your-client-id",
|
|
301
313
|
ingestUrl: "https://your-ingest-endpoint.dataworks.live",
|
|
@@ -303,13 +315,16 @@ const client = new DataClient({
|
|
|
303
315
|
realtimeUrl: "https://your-realtime-endpoint.dataworks.live",
|
|
304
316
|
});
|
|
305
317
|
|
|
306
|
-
await
|
|
318
|
+
await dataworks.login("developer", "password");
|
|
307
319
|
|
|
308
320
|
// 1. Subscribe to raw heart rate data
|
|
309
321
|
const buffer: number[] = [];
|
|
322
|
+
const datasetDatasourceId = "1";
|
|
323
|
+
const eventId = "1";
|
|
324
|
+
const heartrateChannel = `dataworks/${datasetDatasourceId}/${eventId}/heartrate`;
|
|
310
325
|
|
|
311
|
-
|
|
312
|
-
|
|
326
|
+
dataworks.subscribe(heartrateChannel, (event) => {
|
|
327
|
+
// No metric guard needed here: the channel already filters to heartrate.
|
|
313
328
|
|
|
314
329
|
// 2. Calculate a rolling 3-point average
|
|
315
330
|
buffer.push(event.value);
|
|
@@ -317,11 +332,11 @@ client.subscribe("dataworks/metrics", (event) => {
|
|
|
317
332
|
const avg = buffer.reduce((a, b) => a + b, 0) / buffer.length;
|
|
318
333
|
|
|
319
334
|
// 3. Push the derived metric back into the engine
|
|
320
|
-
|
|
335
|
+
dataworks.ingest(
|
|
321
336
|
[
|
|
322
337
|
{
|
|
323
338
|
athleteId: event.athleteId,
|
|
324
|
-
metric: "
|
|
339
|
+
metric: "heartrate_rolling_avg_calculated",
|
|
325
340
|
value: Math.round(avg * 10) / 10,
|
|
326
341
|
timestamp: event.timestamp,
|
|
327
342
|
},
|
|
@@ -338,12 +353,12 @@ The same flow works from any language. A full working Python demo is included at
|
|
|
338
353
|
|
|
339
354
|
| Phase | What it does | SDK equivalent |
|
|
340
355
|
|-------|-------------|----------------|
|
|
341
|
-
| 1. Authenticate | Cognito USER_PASSWORD_AUTH | `
|
|
342
|
-
| 2. Subscribe | WebSocket → AppSync Events API | `
|
|
356
|
+
| 1. Authenticate | Cognito USER_PASSWORD_AUTH | `dataworks.login()` |
|
|
357
|
+
| 2. Subscribe | WebSocket → AppSync Events API | `dataworks.subscribe()` |
|
|
343
358
|
| 3. Receive | Catch events on the subscriber | `onEvent` callback |
|
|
344
359
|
| 4. Calculate | Write to CSV/Google Sheets, compute rolling avg + HR zones | Your business logic |
|
|
345
|
-
| 5. Re-ingest | POST enriched metrics back to the Data Engine | `
|
|
346
|
-
| 6. Report Error | Send error through the error pipeline | `
|
|
360
|
+
| 5. Re-ingest | POST enriched metrics back to the Data Engine | `dataworks.ingest()` |
|
|
361
|
+
| 6. Report Error | Send error through the error pipeline | `dataworks.reportError()` |
|
|
347
362
|
|
|
348
363
|
#### Run the demo
|
|
349
364
|
|
|
@@ -375,7 +390,7 @@ PHASE 5: RE-INGEST ENRICHED METRICS
|
|
|
375
390
|
✓ Ingested (200)
|
|
376
391
|
```
|
|
377
392
|
|
|
378
|
-
The raw `heartrate` values (130–170 bpm) are transformed into `
|
|
393
|
+
The raw `heartrate` values (130–170 bpm) are transformed into `heartrate_rolling_avg_calculated` derived metrics and pushed back into the platform. These derived metrics are then available to all consumers (Console, Visual, Moments triggers) just like any other metric.
|
|
379
394
|
|
|
380
395
|
#### Google Sheets (optional)
|
|
381
396
|
|
|
@@ -385,7 +400,7 @@ The demo can write to Google Sheets instead of CSV — formulas compute rolling
|
|
|
385
400
|
|
|
386
401
|
| Derived metric | Calculation | Use case |
|
|
387
402
|
|---|---|---|
|
|
388
|
-
| `
|
|
403
|
+
| `heartrate_rolling_avg_calculated` | 3-point moving average | Smooth noisy sensor data |
|
|
389
404
|
| `heartrate_zone` | Zone classification (1–5) | Training load analysis |
|
|
390
405
|
| `speed_efficiency` | Speed / heart rate ratio | Fatigue detection |
|
|
391
406
|
| `power_to_weight` | Power / athlete weight | Normalised performance |
|
|
@@ -396,7 +411,7 @@ The demo can write to Google Sheets instead of CSV — formulas compute rolling
|
|
|
396
411
|
|
|
397
412
|
Full documentation with guides, examples, and detailed API reference:
|
|
398
413
|
|
|
399
|
-
**[https://data-docs.dataworks.live](https://data-docs.dataworks.live)**
|
|
414
|
+
**[https://data-sdk-docs.dataworks.live](https://data-sdk-docs.dataworks.live)**
|
|
400
415
|
|
|
401
416
|
## Development
|
|
402
417
|
|
|
@@ -408,9 +423,49 @@ This package publishes to **public npm** (`registry.npmjs.org`), not to the inte
|
|
|
408
423
|
|
|
409
424
|
```bash
|
|
410
425
|
bun run build # tsup → dist/ (ESM + CJS + DTS)
|
|
426
|
+
bun run dev # tsup --watch (rebuild on save)
|
|
411
427
|
bun run test # vitest unit tests (mocked — no infra needed)
|
|
412
428
|
```
|
|
413
429
|
|
|
430
|
+
### Local Development
|
|
431
|
+
|
|
432
|
+
To test SDK changes locally before publishing:
|
|
433
|
+
|
|
434
|
+
**Option A — `npm pack` (recommended, simulates real install):**
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
# In packages/sdk/
|
|
438
|
+
bun run build
|
|
439
|
+
npm pack # creates dataworks-technology-data-0.1.4.tgz
|
|
440
|
+
|
|
441
|
+
# In your consumer project (e.g. test/demo/)
|
|
442
|
+
npm install ../../packages/sdk/dataworks-technology-data-0.1.4.tgz
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
**Option B — `bun link` (faster iteration):**
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
# In packages/sdk/
|
|
449
|
+
bun run build
|
|
450
|
+
bun link
|
|
451
|
+
|
|
452
|
+
# In your consumer project
|
|
453
|
+
bun link @dataworks-technology/data
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**Option C — watch mode + link (live reload):**
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
# Terminal 1: packages/sdk/
|
|
460
|
+
bun run dev # rebuilds dist/ on every save
|
|
461
|
+
|
|
462
|
+
# Terminal 2: consumer project
|
|
463
|
+
bun link @dataworks-technology/data
|
|
464
|
+
npx tsx demo.ts # picks up latest build automatically
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
> **Tip:** After local testing, remember to `bun unlink` or reinstall from npm before committing.
|
|
468
|
+
|
|
414
469
|
### Publish
|
|
415
470
|
|
|
416
471
|
Login to npm first (one-time):
|
|
@@ -456,7 +511,7 @@ bun x vitest run test/e2e-public-sdk.test.ts
|
|
|
456
511
|
| 1 | **Login** | `should authenticate testdeveloper` | USER_PASSWORD_AUTH works with frontend client | AWS Console → Cognito → User Pool → Users → `testdeveloper` exists in `developer` group |
|
|
457
512
|
| 2 | **Login** | `should extract tenant from JWT` | `custom:tenant` claim is present in ID token | Decode the JWT at [jwt.io](https://jwt.io) — look for `custom:tenant` |
|
|
458
513
|
| 3 | **Login** | `should include resource server scopes` | Pre-token Lambda injects `dataworks.live/read` and `/write` | AWS Console → Cognito → User Pool → Lambda triggers → Pre token generation V2 is set |
|
|
459
|
-
| 4 | **Login** | `should reject invalid credentials` | Bad password returns error, not a token | Try `
|
|
514
|
+
| 4 | **Login** | `should reject invalid credentials` | Bad password returns error, not a token | Try `dataworks.login("testdeveloper", "wrong")` — should throw |
|
|
460
515
|
| 5 | **Ingest** | `should ingest valid metrics` | JWT passes API Gateway authoriser, Lambda processes payload | AWS Console → CloudWatch → Log group for the ingest Lambda — look for the metric |
|
|
461
516
|
| 6 | **Ingest** | `should filter invalid metrics` | Client-side validation drops bad metrics before sending | The `stderr` output shows `Dropping invalid metric` — local validation, no server call for bad data |
|
|
462
517
|
| 7 | **Ingest** | `should throw when not authenticated` | `ingest()` before `login()` throws immediately | Client-side guard — no network call made |
|