@contractspec/integration.providers-impls 2.9.0 → 3.0.0

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.
Files changed (61) hide show
  1. package/README.md +59 -0
  2. package/dist/health.d.ts +1 -0
  3. package/dist/health.js +3 -0
  4. package/dist/impls/async-event-queue.d.ts +8 -0
  5. package/dist/impls/async-event-queue.js +47 -0
  6. package/dist/impls/health/base-health-provider.d.ts +98 -0
  7. package/dist/impls/health/base-health-provider.js +616 -0
  8. package/dist/impls/health/hybrid-health-providers.d.ts +34 -0
  9. package/dist/impls/health/hybrid-health-providers.js +1088 -0
  10. package/dist/impls/health/official-health-providers.d.ts +78 -0
  11. package/dist/impls/health/official-health-providers.js +968 -0
  12. package/dist/impls/health/provider-normalizers.d.ts +28 -0
  13. package/dist/impls/health/provider-normalizers.js +287 -0
  14. package/dist/impls/health/providers.d.ts +2 -0
  15. package/dist/impls/health/providers.js +1094 -0
  16. package/dist/impls/health-provider-factory.d.ts +3 -0
  17. package/dist/impls/health-provider-factory.js +1308 -0
  18. package/dist/impls/index.d.ts +8 -0
  19. package/dist/impls/index.js +2356 -176
  20. package/dist/impls/messaging-github.d.ts +17 -0
  21. package/dist/impls/messaging-github.js +110 -0
  22. package/dist/impls/messaging-slack.d.ts +14 -0
  23. package/dist/impls/messaging-slack.js +80 -0
  24. package/dist/impls/messaging-whatsapp-meta.d.ts +13 -0
  25. package/dist/impls/messaging-whatsapp-meta.js +52 -0
  26. package/dist/impls/messaging-whatsapp-twilio.d.ts +13 -0
  27. package/dist/impls/messaging-whatsapp-twilio.js +82 -0
  28. package/dist/impls/mistral-conversational.d.ts +23 -0
  29. package/dist/impls/mistral-conversational.js +476 -0
  30. package/dist/impls/mistral-conversational.session.d.ts +32 -0
  31. package/dist/impls/mistral-conversational.session.js +206 -0
  32. package/dist/impls/mistral-stt.d.ts +17 -0
  33. package/dist/impls/mistral-stt.js +167 -0
  34. package/dist/impls/provider-factory.d.ts +7 -1
  35. package/dist/impls/provider-factory.js +2338 -176
  36. package/dist/impls/stripe-payments.js +1 -1
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +2360 -174
  39. package/dist/messaging.d.ts +1 -0
  40. package/dist/messaging.js +3 -0
  41. package/dist/node/health.js +2 -0
  42. package/dist/node/impls/async-event-queue.js +46 -0
  43. package/dist/node/impls/health/base-health-provider.js +615 -0
  44. package/dist/node/impls/health/hybrid-health-providers.js +1087 -0
  45. package/dist/node/impls/health/official-health-providers.js +967 -0
  46. package/dist/node/impls/health/provider-normalizers.js +286 -0
  47. package/dist/node/impls/health/providers.js +1093 -0
  48. package/dist/node/impls/health-provider-factory.js +1307 -0
  49. package/dist/node/impls/index.js +2356 -176
  50. package/dist/node/impls/messaging-github.js +109 -0
  51. package/dist/node/impls/messaging-slack.js +79 -0
  52. package/dist/node/impls/messaging-whatsapp-meta.js +51 -0
  53. package/dist/node/impls/messaging-whatsapp-twilio.js +81 -0
  54. package/dist/node/impls/mistral-conversational.js +475 -0
  55. package/dist/node/impls/mistral-conversational.session.js +205 -0
  56. package/dist/node/impls/mistral-stt.js +166 -0
  57. package/dist/node/impls/provider-factory.js +2338 -176
  58. package/dist/node/impls/stripe-payments.js +1 -1
  59. package/dist/node/index.js +2360 -174
  60. package/dist/node/messaging.js +2 -0
  61. package/package.json +204 -12
@@ -0,0 +1,286 @@
1
+ // src/impls/health/provider-normalizers.ts
2
+ var DEFAULT_LIST_KEYS = [
3
+ "items",
4
+ "data",
5
+ "records",
6
+ "activities",
7
+ "workouts",
8
+ "sleep",
9
+ "biometrics",
10
+ "nutrition"
11
+ ];
12
+ function asRecord(value) {
13
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
14
+ return;
15
+ }
16
+ return value;
17
+ }
18
+ function asArray(value) {
19
+ return Array.isArray(value) ? value : undefined;
20
+ }
21
+ function readString(record, keys) {
22
+ if (!record)
23
+ return;
24
+ for (const key of keys) {
25
+ const value = record[key];
26
+ if (typeof value === "string" && value.trim().length > 0) {
27
+ return value;
28
+ }
29
+ }
30
+ return;
31
+ }
32
+ function readNumber(record, keys) {
33
+ if (!record)
34
+ return;
35
+ for (const key of keys) {
36
+ const value = record[key];
37
+ if (typeof value === "number" && Number.isFinite(value)) {
38
+ return value;
39
+ }
40
+ if (typeof value === "string" && value.trim().length > 0) {
41
+ const parsed = Number(value);
42
+ if (Number.isFinite(parsed)) {
43
+ return parsed;
44
+ }
45
+ }
46
+ }
47
+ return;
48
+ }
49
+ function readBoolean(record, keys) {
50
+ if (!record)
51
+ return;
52
+ for (const key of keys) {
53
+ const value = record[key];
54
+ if (typeof value === "boolean") {
55
+ return value;
56
+ }
57
+ }
58
+ return;
59
+ }
60
+ function extractList(payload, listKeys = DEFAULT_LIST_KEYS) {
61
+ const root = asRecord(payload);
62
+ if (!root) {
63
+ return asArray(payload)?.map((item) => asRecord(item)).filter((item) => Boolean(item)) ?? [];
64
+ }
65
+ for (const key of listKeys) {
66
+ const arrayValue = asArray(root[key]);
67
+ if (!arrayValue)
68
+ continue;
69
+ return arrayValue.map((item) => asRecord(item)).filter((item) => Boolean(item));
70
+ }
71
+ return [];
72
+ }
73
+ function extractPagination(payload) {
74
+ const root = asRecord(payload);
75
+ const nestedPagination = asRecord(root?.pagination);
76
+ const nextCursor = readString(nestedPagination, ["nextCursor", "next_cursor"]) ?? readString(root, [
77
+ "nextCursor",
78
+ "next_cursor",
79
+ "cursor",
80
+ "next_page_token"
81
+ ]);
82
+ const hasMore = readBoolean(nestedPagination, ["hasMore", "has_more"]) ?? readBoolean(root, ["hasMore", "has_more"]);
83
+ return {
84
+ nextCursor,
85
+ hasMore: hasMore ?? Boolean(nextCursor)
86
+ };
87
+ }
88
+ function toHealthActivity(item, context, fallbackType = "activity") {
89
+ const externalId = readString(item, ["external_id", "externalId", "uuid", "id"]) ?? `${context.providerKey}:${fallbackType}`;
90
+ const id = readString(item, ["id", "uuid", "workout_id"]) ?? `${context.providerKey}:activity:${externalId}`;
91
+ return {
92
+ id,
93
+ externalId,
94
+ tenantId: context.tenantId,
95
+ connectionId: context.connectionId ?? "unknown",
96
+ userId: readString(item, ["user_id", "userId", "athlete_id"]),
97
+ providerKey: context.providerKey,
98
+ activityType: readString(item, ["activity_type", "type", "sport_type", "sport"]) ?? fallbackType,
99
+ startedAt: readIsoDate(item, [
100
+ "started_at",
101
+ "start_time",
102
+ "start_date",
103
+ "created_at"
104
+ ]),
105
+ endedAt: readIsoDate(item, ["ended_at", "end_time"]),
106
+ durationSeconds: readNumber(item, [
107
+ "duration_seconds",
108
+ "duration",
109
+ "elapsed_time"
110
+ ]),
111
+ distanceMeters: readNumber(item, ["distance_meters", "distance"]),
112
+ caloriesKcal: readNumber(item, [
113
+ "calories_kcal",
114
+ "calories",
115
+ "active_kilocalories"
116
+ ]),
117
+ steps: readNumber(item, ["steps"])?.valueOf(),
118
+ metadata: item
119
+ };
120
+ }
121
+ function toHealthWorkout(item, context, fallbackType = "workout") {
122
+ const activity = toHealthActivity(item, context, fallbackType);
123
+ return {
124
+ id: activity.id,
125
+ externalId: activity.externalId,
126
+ tenantId: activity.tenantId,
127
+ connectionId: activity.connectionId,
128
+ userId: activity.userId,
129
+ providerKey: activity.providerKey,
130
+ workoutType: readString(item, [
131
+ "workout_type",
132
+ "sport_type",
133
+ "type",
134
+ "activity_type"
135
+ ]) ?? fallbackType,
136
+ startedAt: activity.startedAt,
137
+ endedAt: activity.endedAt,
138
+ durationSeconds: activity.durationSeconds,
139
+ distanceMeters: activity.distanceMeters,
140
+ caloriesKcal: activity.caloriesKcal,
141
+ averageHeartRateBpm: readNumber(item, [
142
+ "average_heart_rate",
143
+ "avg_hr",
144
+ "average_heart_rate_bpm"
145
+ ]),
146
+ maxHeartRateBpm: readNumber(item, [
147
+ "max_heart_rate",
148
+ "max_hr",
149
+ "max_heart_rate_bpm"
150
+ ]),
151
+ metadata: item
152
+ };
153
+ }
154
+ function toHealthSleep(item, context) {
155
+ const externalId = readString(item, ["external_id", "externalId", "uuid", "id"]) ?? `${context.providerKey}:sleep`;
156
+ const id = readString(item, ["id", "uuid"]) ?? `${context.providerKey}:sleep:${externalId}`;
157
+ const startedAt = readIsoDate(item, ["started_at", "start_time", "bedtime_start", "start"]) ?? new Date(0).toISOString();
158
+ const endedAt = readIsoDate(item, ["ended_at", "end_time", "bedtime_end", "end"]) ?? startedAt;
159
+ return {
160
+ id,
161
+ externalId,
162
+ tenantId: context.tenantId,
163
+ connectionId: context.connectionId ?? "unknown",
164
+ userId: readString(item, ["user_id", "userId"]),
165
+ providerKey: context.providerKey,
166
+ startedAt,
167
+ endedAt,
168
+ durationSeconds: readNumber(item, [
169
+ "duration_seconds",
170
+ "duration",
171
+ "total_sleep_duration"
172
+ ]),
173
+ deepSleepSeconds: readNumber(item, [
174
+ "deep_sleep_seconds",
175
+ "deep_sleep_duration"
176
+ ]),
177
+ lightSleepSeconds: readNumber(item, [
178
+ "light_sleep_seconds",
179
+ "light_sleep_duration"
180
+ ]),
181
+ remSleepSeconds: readNumber(item, [
182
+ "rem_sleep_seconds",
183
+ "rem_sleep_duration"
184
+ ]),
185
+ awakeSeconds: readNumber(item, ["awake_seconds", "awake_time"]),
186
+ sleepScore: readNumber(item, ["sleep_score", "score"]),
187
+ metadata: item
188
+ };
189
+ }
190
+ function toHealthBiometric(item, context, metricTypeFallback = "metric") {
191
+ const externalId = readString(item, ["external_id", "externalId", "uuid", "id"]) ?? `${context.providerKey}:biometric`;
192
+ const id = readString(item, ["id", "uuid"]) ?? `${context.providerKey}:biometric:${externalId}`;
193
+ return {
194
+ id,
195
+ externalId,
196
+ tenantId: context.tenantId,
197
+ connectionId: context.connectionId ?? "unknown",
198
+ userId: readString(item, ["user_id", "userId"]),
199
+ providerKey: context.providerKey,
200
+ metricType: readString(item, ["metric_type", "metric", "type", "name"]) ?? metricTypeFallback,
201
+ value: readNumber(item, ["value", "score", "measurement"]) ?? 0,
202
+ unit: readString(item, ["unit"]),
203
+ measuredAt: readIsoDate(item, ["measured_at", "timestamp", "created_at"]) ?? new Date().toISOString(),
204
+ metadata: item
205
+ };
206
+ }
207
+ function toHealthNutrition(item, context) {
208
+ const externalId = readString(item, ["external_id", "externalId", "uuid", "id"]) ?? `${context.providerKey}:nutrition`;
209
+ const id = readString(item, ["id", "uuid"]) ?? `${context.providerKey}:nutrition:${externalId}`;
210
+ return {
211
+ id,
212
+ externalId,
213
+ tenantId: context.tenantId,
214
+ connectionId: context.connectionId ?? "unknown",
215
+ userId: readString(item, ["user_id", "userId"]),
216
+ providerKey: context.providerKey,
217
+ loggedAt: readIsoDate(item, ["logged_at", "created_at", "date", "timestamp"]) ?? new Date().toISOString(),
218
+ caloriesKcal: readNumber(item, ["calories_kcal", "calories"]),
219
+ proteinGrams: readNumber(item, ["protein_grams", "protein"]),
220
+ carbsGrams: readNumber(item, ["carbs_grams", "carbs"]),
221
+ fatGrams: readNumber(item, ["fat_grams", "fat"]),
222
+ fiberGrams: readNumber(item, ["fiber_grams", "fiber"]),
223
+ hydrationMl: readNumber(item, ["hydration_ml", "water_ml", "water"]),
224
+ metadata: item
225
+ };
226
+ }
227
+ function toHealthConnectionStatus(payload, params, source) {
228
+ const record = asRecord(payload);
229
+ const rawStatus = readString(record, ["status", "connection_status", "health"]) ?? "healthy";
230
+ return {
231
+ tenantId: params.tenantId,
232
+ connectionId: params.connectionId,
233
+ status: rawStatus === "healthy" || rawStatus === "degraded" || rawStatus === "error" || rawStatus === "disconnected" ? rawStatus : "healthy",
234
+ source,
235
+ lastCheckedAt: readIsoDate(record, ["last_checked_at", "lastCheckedAt"]) ?? new Date().toISOString(),
236
+ errorCode: readString(record, ["error_code", "errorCode"]),
237
+ errorMessage: readString(record, ["error_message", "errorMessage"]),
238
+ metadata: asRecord(record?.metadata)
239
+ };
240
+ }
241
+ function toHealthWebhookEvent(payload, providerKey, verified) {
242
+ const record = asRecord(payload);
243
+ const entityType = readString(record, ["entity_type", "entityType", "type"]);
244
+ const normalizedEntityType = entityType === "activity" || entityType === "workout" || entityType === "sleep" || entityType === "biometric" || entityType === "nutrition" ? entityType : undefined;
245
+ return {
246
+ providerKey,
247
+ eventType: readString(record, ["event_type", "eventType", "event"]),
248
+ externalEntityId: readString(record, [
249
+ "external_entity_id",
250
+ "externalEntityId",
251
+ "entity_id",
252
+ "entityId",
253
+ "id"
254
+ ]),
255
+ entityType: normalizedEntityType,
256
+ receivedAt: new Date().toISOString(),
257
+ verified,
258
+ payload,
259
+ metadata: asRecord(record?.metadata)
260
+ };
261
+ }
262
+ function readIsoDate(record, keys) {
263
+ const value = readString(record, keys);
264
+ if (!value)
265
+ return;
266
+ const parsed = new Date(value);
267
+ if (Number.isNaN(parsed.getTime()))
268
+ return;
269
+ return parsed.toISOString();
270
+ }
271
+ export {
272
+ toHealthWorkout,
273
+ toHealthWebhookEvent,
274
+ toHealthSleep,
275
+ toHealthNutrition,
276
+ toHealthConnectionStatus,
277
+ toHealthBiometric,
278
+ toHealthActivity,
279
+ readString,
280
+ readNumber,
281
+ readBoolean,
282
+ extractPagination,
283
+ extractList,
284
+ asRecord,
285
+ asArray
286
+ };