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