@ovok/core 0.4.36 → 0.4.38

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 (127) hide show
  1. package/dist/client/auth/account-storage.d.ts +1 -0
  2. package/dist/client/auth/account-storage.js +2 -1
  3. package/dist/{utils → client}/auth/exchange-code.d.ts +1 -1
  4. package/dist/{utils → client}/auth/generate-code-challenge-and-verifier.js +5 -17
  5. package/dist/{utils → client}/auth/handle-tenant-code.d.ts +1 -1
  6. package/dist/{utils → client}/auth/handle-tenant-code.js +1 -1
  7. package/dist/client/auth/methods/appleLogin.js +2 -6
  8. package/dist/client/auth/methods/changePassword.js +2 -1
  9. package/dist/client/auth/methods/getSessions.js +2 -1
  10. package/dist/client/auth/methods/googleLogin.js +2 -6
  11. package/dist/client/auth/methods/login.js +26 -44
  12. package/dist/client/auth/methods/logout.js +2 -1
  13. package/dist/client/auth/methods/requestDeleteUser.js +5 -4
  14. package/dist/client/auth/methods/revokeSessions.js +2 -1
  15. package/dist/client/auth/methods/socialLogin.d.ts +7 -0
  16. package/dist/client/auth/methods/socialLogin.js +11 -0
  17. package/dist/client/auth/session-fetch.d.ts +35 -0
  18. package/dist/client/auth/session-fetch.js +206 -0
  19. package/dist/client/auth/session-http.d.ts +11 -0
  20. package/dist/client/auth/session-http.js +169 -0
  21. package/dist/client/auth/sha256.d.ts +2 -0
  22. package/dist/client/auth/sha256.js +65 -0
  23. package/dist/{utils → client}/auth/start-login.d.ts +2 -2
  24. package/dist/client/change-history/methods.js +2 -1
  25. package/dist/client/cms/methods.js +16 -28
  26. package/dist/client/consent/methods.js +3 -2
  27. package/dist/client/email/methods.js +2 -1
  28. package/dist/client/errors/ovok-error-utils.d.ts +6 -0
  29. package/dist/client/errors/ovok-error-utils.js +10 -1
  30. package/dist/client/errors/ovok-error.js +6 -13
  31. package/dist/client/errors/rate-limit-error.js +2 -6
  32. package/dist/client/experience/methods.js +28 -37
  33. package/dist/client/invites/methods.js +8 -12
  34. package/dist/client/notifications/methods.js +5 -5
  35. package/dist/client/observation/fhir-systems.d.ts +4 -0
  36. package/dist/client/observation/fhir-systems.js +7 -0
  37. package/dist/client/observation/methods/generateObservationBodiesByMeasurement.js +23 -29
  38. package/dist/client/observation/methods/getLatestObservationsByCodes.js +6 -9
  39. package/dist/client/observation/methods/observationsToMeasurements.js +44 -91
  40. package/dist/client/observation/services/base-observation-service.js +4 -3
  41. package/dist/client/observation/services/blood-pressure.js +1 -4
  42. package/dist/client/observation/services/{baby-sleep.d.ts → component-observation-services.d.ts} +18 -13
  43. package/dist/client/observation/services/component-observation-services.js +69 -0
  44. package/dist/client/observation/services/creator.d.ts +1 -2
  45. package/dist/client/observation/services/creator.js +15 -33
  46. package/dist/client/observation/services/ecg-observation-fragments.js +5 -6
  47. package/dist/client/observation/services/ecg.js +3 -2
  48. package/dist/client/observation/services/lipid-panel.js +8 -12
  49. package/dist/client/observation/services/measurement-time.d.ts +4 -0
  50. package/dist/client/observation/services/measurement-time.js +6 -0
  51. package/dist/client/observation/services/single-value-services.d.ts +36 -0
  52. package/dist/client/observation/services/single-value-services.js +100 -0
  53. package/dist/client/observation/services/uric-acid-service.js +6 -7
  54. package/dist/client/observation/types/generate-measurement/HealthConnectTypes.d.ts +2 -458
  55. package/dist/client/observation/types/generate-measurement/HealthConnectTypes.js +0 -6
  56. package/dist/client/offline/offline-measurement-queue.js +31 -28
  57. package/dist/client/ovok-client.d.ts +7 -15
  58. package/dist/client/ovok-client.js +54 -398
  59. package/dist/client/patient/methods.js +27 -36
  60. package/dist/client/patient-sharing/methods.js +4 -4
  61. package/dist/client/platform/methods.js +21 -30
  62. package/dist/client/platform/types.d.ts +3 -6
  63. package/dist/client/query-string.d.ts +3 -0
  64. package/dist/client/query-string.js +15 -0
  65. package/dist/client/questionnaire-response/methods/createQuestionnaireResponseWithObservations.js +2 -1
  66. package/dist/client/questionnaire-response/methods/extractQuestionnaireObservations.js +1 -2
  67. package/dist/client/questionnaire-response/methods/populateQuestionnaire.js +1 -2
  68. package/dist/client/questionnaire-response/methods/submitQuestionnaireResponse.js +2 -1
  69. package/dist/client/request-options.d.ts +2 -0
  70. package/dist/client/request-options.js +6 -0
  71. package/dist/client/signals/errors.js +2 -6
  72. package/dist/client/signals/methods.js +22 -22
  73. package/dist/client/slim/live-read.d.ts +4 -0
  74. package/dist/client/slim/live-read.js +87 -0
  75. package/dist/client/slim/methods.d.ts +3 -0
  76. package/dist/client/slim/methods.js +73 -166
  77. package/dist/client/storage.js +3 -2
  78. package/dist/conformance/capability-requirements.js +1 -1
  79. package/dist/conformance/capability.js +8 -7
  80. package/dist/hooks/account-hooks.js +6 -68
  81. package/dist/hooks/api-hooks.d.ts +10 -6
  82. package/dist/hooks/api-hooks.js +29 -88
  83. package/dist/hooks/cms-hooks.js +8 -13
  84. package/dist/hooks/email-hooks.js +1 -40
  85. package/dist/hooks/experience-hooks.js +4 -60
  86. package/dist/hooks/index.d.ts +2 -2
  87. package/dist/hooks/index.js +4 -4
  88. package/dist/hooks/invites-hooks.js +0 -1
  89. package/dist/hooks/notification-hooks.d.ts +4 -1
  90. package/dist/hooks/notification-hooks.js +29 -86
  91. package/dist/hooks/notification-invalidation.d.ts +6 -0
  92. package/dist/hooks/notification-invalidation.js +52 -0
  93. package/dist/hooks/notification-note-hooks.js +13 -28
  94. package/dist/hooks/observation-hooks.js +2 -4
  95. package/dist/hooks/patient-hooks.js +13 -113
  96. package/dist/hooks/patient-sharing-hooks.js +2 -7
  97. package/dist/hooks/platform-hooks.js +3 -51
  98. package/dist/hooks/poll-hooks.js +10 -8
  99. package/dist/hooks/signals-alert-hooks.js +4 -6
  100. package/dist/hooks/signals-hooks.js +2 -45
  101. package/package.json +1 -1
  102. package/dist/client/observation/services/baby-sleep.js +0 -69
  103. package/dist/client/observation/services/blood-glucose.d.ts +0 -12
  104. package/dist/client/observation/services/blood-glucose.js +0 -36
  105. package/dist/client/observation/services/body-temperature.d.ts +0 -13
  106. package/dist/client/observation/services/body-temperature.js +0 -36
  107. package/dist/client/observation/services/body-weight.d.ts +0 -13
  108. package/dist/client/observation/services/body-weight.js +0 -36
  109. package/dist/client/observation/services/heart-rate-variability.d.ts +0 -13
  110. package/dist/client/observation/services/heart-rate-variability.js +0 -38
  111. package/dist/client/observation/services/heart-rate.d.ts +0 -13
  112. package/dist/client/observation/services/heart-rate.js +0 -39
  113. package/dist/client/observation/services/respiratory-rate.d.ts +0 -13
  114. package/dist/client/observation/services/respiratory-rate.js +0 -38
  115. package/dist/client/observation/services/respiratory-therapy.d.ts +0 -28
  116. package/dist/client/observation/services/respiratory-therapy.js +0 -69
  117. package/dist/client/observation/services/resting-heart-rate.d.ts +0 -13
  118. package/dist/client/observation/services/resting-heart-rate.js +0 -38
  119. package/dist/client/observation/services/vo2-max.d.ts +0 -13
  120. package/dist/client/observation/services/vo2-max.js +0 -38
  121. package/dist/client/observation/services/walking-heart-rate-average.d.ts +0 -12
  122. package/dist/client/observation/services/walking-heart-rate-average.js +0 -38
  123. package/dist/utils/auth/sha256.d.ts +0 -8
  124. package/dist/utils/auth/sha256.js +0 -300
  125. /package/dist/{utils → client}/auth/exchange-code.js +0 -0
  126. /package/dist/{utils → client}/auth/generate-code-challenge-and-verifier.d.ts +0 -0
  127. /package/dist/{utils → client}/auth/start-login.js +0 -0
@@ -5,6 +5,7 @@ exports.invitePatient = invitePatient;
5
5
  exports.acceptPractitionerInvite = acceptPractitionerInvite;
6
6
  exports.acceptPatientInvite = acceptPatientInvite;
7
7
  exports.acceptInvite = acceptInvite;
8
+ const request_options_1 = require("../request-options");
8
9
  function invitePractitioner(body, requestOptions) {
9
10
  return this.post("/v1/invites/practitioner", body, undefined, requestOptions);
10
11
  }
@@ -20,17 +21,12 @@ function acceptPatientInvite(body, requestOptions) {
20
21
  function acceptInvite(body, requestOptions) {
21
22
  var _a;
22
23
  const resourceType = (_a = this.getProfile()) === null || _a === void 0 ? void 0 : _a.resourceType;
23
- if (resourceType === "Practitioner") {
24
- return this.acceptPractitionerInvite(body, ...(requestOptions === undefined ? [] : [requestOptions])).then((result) => {
25
- this.invalidateUrl("/v1/patient-sharing/shares");
26
- return result;
27
- });
24
+ if (resourceType !== "Practitioner" && resourceType !== "Patient") {
25
+ throw new Error("Accepting an invite requires a signed-in Patient or Practitioner profile.");
28
26
  }
29
- if (resourceType === "Patient") {
30
- return this.acceptPatientInvite(body, ...(requestOptions === undefined ? [] : [requestOptions])).then((result) => {
31
- this.invalidateUrl("/v1/patient-sharing/shares");
32
- return result;
33
- });
34
- }
35
- throw new Error("Accepting an invite requires a signed-in Patient or Practitioner profile.");
27
+ const accept = resourceType === "Practitioner" ? this.acceptPractitionerInvite : this.acceptPatientInvite;
28
+ return accept.call(this, body, ...(0, request_options_1.optionalArg)(requestOptions)).then((result) => {
29
+ this.invalidateUrl("/v1/patient-sharing/shares");
30
+ return result;
31
+ });
36
32
  }
@@ -5,7 +5,7 @@ exports.listNotificationNotes = listNotificationNotes;
5
5
  exports.updateNotificationNote = updateNotificationNote;
6
6
  exports.patchNotificationNote = patchNotificationNote;
7
7
  exports.deleteNotificationNote = deleteNotificationNote;
8
- const segment = (value) => encodeURIComponent(value);
8
+ const request_options_1 = require("../request-options");
9
9
  const TARGET_PATH = {
10
10
  oob: "oob",
11
11
  alarm: "alarms",
@@ -16,14 +16,14 @@ function createNotificationNote(body, requestOptions) {
16
16
  return this.post("/v1/slim/notifications/notes", body, undefined, requestOptions);
17
17
  }
18
18
  function listNotificationNotes(kind, id, requestOptions) {
19
- return this.get(`/v1/slim/notifications/${TARGET_PATH[kind]}/${segment(id)}/notes`, { ...{ cache: "reload" }, ...requestOptions });
19
+ return this.get(`/v1/slim/notifications/${TARGET_PATH[kind]}/${encodeURIComponent(id)}/notes`, { cache: "reload", ...requestOptions });
20
20
  }
21
21
  function updateNotificationNote(noteId, body, requestOptions) {
22
- return this.put(`/v1/slim/notifications/notes/${segment(noteId)}`, body, undefined, requestOptions);
22
+ return this.put(`/v1/slim/notifications/notes/${encodeURIComponent(noteId)}`, body, undefined, requestOptions);
23
23
  }
24
24
  function patchNotificationNote(noteId, body, requestOptions) {
25
- return this.patch(`/v1/slim/notifications/notes/${segment(noteId)}`, body, ...(requestOptions === undefined ? [] : [requestOptions]));
25
+ return this.patch(`/v1/slim/notifications/notes/${encodeURIComponent(noteId)}`, body, ...(0, request_options_1.optionalArg)(requestOptions));
26
26
  }
27
27
  async function deleteNotificationNote(noteId, requestOptions) {
28
- await this.delete(`/v1/slim/notifications/notes/${segment(noteId)}`, ...(requestOptions === undefined ? [] : [requestOptions]));
28
+ await this.delete(`/v1/slim/notifications/notes/${encodeURIComponent(noteId)}`, ...(0, request_options_1.optionalArg)(requestOptions));
29
29
  }
@@ -0,0 +1,4 @@
1
+ export declare const LOINC_SYSTEM = "http://loinc.org";
2
+ export declare const UCUM_SYSTEM = "http://unitsofmeasure.org";
3
+ /** Identifier system of the device-side reading id that makes a save idempotent. */
4
+ export declare const DEVICE_READING_IDENTIFIER_SYSTEM = "https://api.ovok.com/fhir/StructureDefinition/device-reading-id";
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEVICE_READING_IDENTIFIER_SYSTEM = exports.UCUM_SYSTEM = exports.LOINC_SYSTEM = void 0;
4
+ exports.LOINC_SYSTEM = "http://loinc.org";
5
+ exports.UCUM_SYSTEM = "http://unitsofmeasure.org";
6
+ /** Identifier system of the device-side reading id that makes a save idempotent. */
7
+ exports.DEVICE_READING_IDENTIFIER_SYSTEM = "https://api.ovok.com/fhir/StructureDefinition/device-reading-id";
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateObservationBodiesByMeasurement = generateObservationBodiesByMeasurement;
4
4
  const core_1 = require("@medplum/core");
5
+ const fhir_systems_1 = require("../fhir-systems");
5
6
  const creator_1 = require("../services/creator");
6
7
  const getDeviceObject_1 = require("../utils/getDeviceObject");
7
- const DEVICE_READING_IDENTIFIER_SYSTEM = "https://api.ovok.com/fhir/StructureDefinition/device-reading-id";
8
8
  const getPatientReference = async (client, patientId) => {
9
9
  if (patientId) {
10
10
  return `Patient/${patientId}`;
@@ -20,7 +20,7 @@ const addSourceIdentifier = (observations, patientReference, sourceId) => patien
20
20
  identifier: [
21
21
  ...((_a = observation.identifier) !== null && _a !== void 0 ? _a : []),
22
22
  {
23
- system: DEVICE_READING_IDENTIFIER_SYSTEM,
23
+ system: fhir_systems_1.DEVICE_READING_IDENTIFIER_SYSTEM,
24
24
  value: `${patientReference}/${sourceId}/${index}`,
25
25
  },
26
26
  ],
@@ -28,52 +28,46 @@ const addSourceIdentifier = (observations, patientReference, sourceId) => patien
28
28
  })
29
29
  : observations;
30
30
  async function generateObservationBodiesByMeasurement(params) {
31
- var _a;
31
+ var _a, _b;
32
32
  const { device, measurement } = params;
33
33
  const measurementTypeKey = measurement.measurementTypeKey;
34
34
  const patientReference = await getPatientReference(this, params.patientId);
35
35
  const observationService = creator_1.ObservationServiceCreator.getInstance().getService(measurementTypeKey);
36
36
  const deviceObject = (0, getDeviceObject_1.getDeviceObject)(device);
37
- const observationFragments = (_a = observationService.generateObservationFragments) === null || _a === void 0 ? void 0 : _a.call(observationService, measurement);
38
37
  const applyEffectiveDateTime = (observation) => params.effectiveDateTime && !observation.effectivePeriod
39
38
  ? {
40
39
  ...observation,
41
40
  effectiveDateTime: params.effectiveDateTime.toISOString(),
42
41
  }
43
42
  : observation;
44
- if (observationFragments) {
45
- const observations = observationFragments
46
- .map((fragment) => ({
47
- resourceType: "Observation",
48
- status: "registered",
49
- subject: {
50
- reference: patientReference,
51
- },
52
- device: deviceObject,
53
- ...fragment.value,
54
- code: fragment.code,
55
- ...(fragment.additionalProperties || {}),
56
- }))
57
- .filter(hasObservationValue)
58
- .map(applyEffectiveDateTime);
59
- return addSourceIdentifier(observations, patientReference, params.sourceId);
60
- }
61
- const observationCodes = observationService.getObservationCodes();
62
- const values = observationService.generateValues(measurement);
63
- const additionalProperties = observationService.getAdditionalProperties(measurement);
64
- const observationBodies = observationCodes.map((code, index) => ({
43
+ const fragments = (_b = (_a = observationService.generateObservationFragments) === null || _a === void 0 ? void 0 : _a.call(observationService, measurement)) !== null && _b !== void 0 ? _b : codeValueFragments(observationService, measurement);
44
+ const observations = fragments
45
+ .map((fragment) => ({
65
46
  resourceType: "Observation",
66
47
  status: "registered",
67
48
  subject: {
68
49
  reference: patientReference,
69
50
  },
70
51
  device: deviceObject,
71
- ...values[index],
52
+ ...fragment.value,
53
+ code: fragment.code,
54
+ ...(fragment.additionalProperties || {}),
55
+ }))
56
+ .filter(hasObservationValue)
57
+ .map(applyEffectiveDateTime);
58
+ return addSourceIdentifier(observations, patientReference, params.sourceId);
59
+ }
60
+ /** Services without fragments describe their observations as parallel code, value and property lists. */
61
+ const codeValueFragments = (service, measurement) => {
62
+ const codes = service.getObservationCodes();
63
+ const values = service.generateValues(measurement);
64
+ const additionalProperties = service.getAdditionalProperties(measurement);
65
+ return codes.map((code, index) => ({
72
66
  code,
73
- ...(additionalProperties[index] || {}),
67
+ value: values[index],
68
+ additionalProperties: additionalProperties[index],
74
69
  }));
75
- return addSourceIdentifier(observationBodies.filter(hasObservationValue).map(applyEffectiveDateTime), patientReference, params.sourceId);
76
- }
70
+ };
77
71
  const hasObservationValue = (observation) => {
78
72
  var _a, _b, _c, _d, _e, _f;
79
73
  return ((_a = observation.valueQuantity) === null || _a === void 0 ? void 0 : _a.value) !== undefined ||
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getLatestObservationsByCodes = getLatestObservationsByCodes;
4
+ const query_string_1 = require("../../query-string");
4
5
  async function getLatestObservationsByCodes(params, requestOptions) {
5
6
  var _a;
6
7
  let patientId = "patientId" in params ? params.patientId : "";
@@ -8,14 +9,10 @@ async function getLatestObservationsByCodes(params, requestOptions) {
8
9
  const currentUser = await this.getProfileAsync();
9
10
  patientId = (_a = currentUser === null || currentUser === void 0 ? void 0 : currentUser.id) !== null && _a !== void 0 ? _a : "";
10
11
  }
11
- const codes = params.codes.join(",");
12
- const urlParams = new URLSearchParams({
13
- max: "1",
14
- code: codes,
12
+ return this.get(`/fhir/Observation/$lastn${(0, query_string_1.toQueryString)({
13
+ max: 1,
14
+ code: params.codes,
15
15
  patient: `Patient/${patientId}`,
16
- ...(params.date ? { date: `ge${params.date}` } : {}),
17
- });
18
- return this.get(`/fhir/Observation/$lastn?${urlParams.toString()}`, { ...{
19
- cache: "no-cache",
20
- }, ...requestOptions });
16
+ date: params.date ? `ge${params.date}` : undefined,
17
+ })}`, { cache: "no-cache", ...requestOptions });
21
18
  }
@@ -3,10 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.observationsToMeasurements = exports.observationCodesForTypes = void 0;
5
5
  const types_1 = require("../../../types");
6
+ const fhir_systems_1 = require("../fhir-systems");
7
+ const single_value_services_1 = require("../services/single-value-services");
6
8
  const DEFAULT_OBSERVATION_TIME_TOLERANCE_MS = 1000;
7
9
  const codeOf = (observation) => {
8
10
  var _a, _b, _c, _d, _e, _f, _g;
9
- return (_d = (_c = (_b = (_a = observation.code) === null || _a === void 0 ? void 0 : _a.coding) === null || _b === void 0 ? void 0 : _b.find((coding) => coding.system === "http://loinc.org")) === null || _c === void 0 ? void 0 : _c.code) !== null && _d !== void 0 ? _d : (_g = (_f = (_e = observation.code) === null || _e === void 0 ? void 0 : _e.coding) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.code;
11
+ return (_d = (_c = (_b = (_a = observation.code) === null || _a === void 0 ? void 0 : _a.coding) === null || _b === void 0 ? void 0 : _b.find((coding) => coding.system === fhir_systems_1.LOINC_SYSTEM)) === null || _c === void 0 ? void 0 : _c.code) !== null && _d !== void 0 ? _d : (_g = (_f = (_e = observation.code) === null || _e === void 0 ? void 0 : _e.coding) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.code;
10
12
  };
11
13
  const valueOf = (observation) => {
12
14
  var _a;
@@ -199,9 +201,10 @@ const groupObservations = (observations, options, legacyKey) => {
199
201
  }
200
202
  return [...groups.values()].sort((left, right) => left.firstIndex - right.firstIndex);
201
203
  };
202
- const firstValue = (observations, code) => observations.find((observation) => codeOf(observation) === code)
203
- ? valueOf(observations.find((observation) => codeOf(observation) === code))
204
- : undefined;
204
+ const firstValue = (observations, code) => {
205
+ const match = observations.find((observation) => codeOf(observation) === code);
206
+ return match ? valueOf(match) : undefined;
207
+ };
205
208
  const LIPID_FIELDS = [
206
209
  { key: "totalCholesterol", codes: [types_1.ObservationCode.TOTAL_CHOLESTEROL_MASS, types_1.ObservationCode.TOTAL_CHOLESTEROL_MOLAR] },
207
210
  { key: "hdlCholesterol", codes: [types_1.ObservationCode.HDL_CHOLESTEROL_MASS, types_1.ObservationCode.HDL_CHOLESTEROL_MOLAR] },
@@ -220,40 +223,45 @@ const lipidValue = (observations, field) => {
220
223
  }
221
224
  return undefined;
222
225
  };
226
+ const MOLAR_LIPID_CODES = [
227
+ types_1.ObservationCode.TOTAL_CHOLESTEROL_MOLAR,
228
+ types_1.ObservationCode.HDL_CHOLESTEROL_MOLAR,
229
+ types_1.ObservationCode.LDL_CHOLESTEROL_MOLAR,
230
+ types_1.ObservationCode.TRIGLYCERIDES_MOLAR,
231
+ ];
232
+ const MASS_LIPID_CODES = [
233
+ types_1.ObservationCode.TOTAL_CHOLESTEROL_MASS,
234
+ types_1.ObservationCode.HDL_CHOLESTEROL_MASS,
235
+ types_1.ObservationCode.LDL_CHOLESTEROL_MASS,
236
+ types_1.ObservationCode.TRIGLYCERIDES_MASS,
237
+ ];
238
+ /** The unit a lipid code implies, else the quantity's own unit when it is a lipid unit. */
239
+ const lipidUnitOf = (code, unit) => {
240
+ if (code && MOLAR_LIPID_CODES.includes(code)) {
241
+ return "mmol/L";
242
+ }
243
+ if (code && MASS_LIPID_CODES.includes(code)) {
244
+ return "mg/dL";
245
+ }
246
+ return unit === "mmol/L" || unit === "mg/dL" ? unit : undefined;
247
+ };
223
248
  const lipidUnit = (observations) => {
224
- var _a, _b, _c, _d, _e, _f;
249
+ var _a, _b, _c, _d, _e;
225
250
  for (const observation of observations) {
226
251
  for (const component of (_a = observation.component) !== null && _a !== void 0 ? _a : []) {
227
- const code = (_c = (_b = component.code.coding) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.code;
228
- if ([types_1.ObservationCode.TOTAL_CHOLESTEROL_MOLAR, types_1.ObservationCode.HDL_CHOLESTEROL_MOLAR,
229
- types_1.ObservationCode.LDL_CHOLESTEROL_MOLAR, types_1.ObservationCode.TRIGLYCERIDES_MOLAR].includes(code)) {
230
- return "mmol/L";
252
+ const unit = lipidUnitOf((_c = (_b = component.code.coding) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.code, (_d = component.valueQuantity) === null || _d === void 0 ? void 0 : _d.unit);
253
+ if (unit) {
254
+ return unit;
231
255
  }
232
- if ([types_1.ObservationCode.TOTAL_CHOLESTEROL_MASS, types_1.ObservationCode.HDL_CHOLESTEROL_MASS,
233
- types_1.ObservationCode.LDL_CHOLESTEROL_MASS, types_1.ObservationCode.TRIGLYCERIDES_MASS].includes(code)) {
234
- return "mg/dL";
235
- }
236
- if (((_d = component.valueQuantity) === null || _d === void 0 ? void 0 : _d.unit) === "mmol/L" || ((_e = component.valueQuantity) === null || _e === void 0 ? void 0 : _e.unit) === "mg/dL") {
237
- return component.valueQuantity.unit;
238
- }
239
- }
240
- const code = codeOf(observation);
241
- const unit = (_f = observation.valueQuantity) === null || _f === void 0 ? void 0 : _f.unit;
242
- if (code && [types_1.ObservationCode.TOTAL_CHOLESTEROL_MOLAR, types_1.ObservationCode.HDL_CHOLESTEROL_MOLAR,
243
- types_1.ObservationCode.LDL_CHOLESTEROL_MOLAR, types_1.ObservationCode.TRIGLYCERIDES_MOLAR].includes(code)) {
244
- return "mmol/L";
245
256
  }
246
- if (code && [types_1.ObservationCode.TOTAL_CHOLESTEROL_MASS, types_1.ObservationCode.HDL_CHOLESTEROL_MASS,
247
- types_1.ObservationCode.LDL_CHOLESTEROL_MASS, types_1.ObservationCode.TRIGLYCERIDES_MASS].includes(code)) {
248
- return "mg/dL";
249
- }
250
- if (unit === "mmol/L" || unit === "mg/dL") {
257
+ const unit = lipidUnitOf(codeOf(observation), (_e = observation.valueQuantity) === null || _e === void 0 ? void 0 : _e.unit);
258
+ if (unit) {
251
259
  return unit;
252
260
  }
253
261
  }
254
262
  return "mg/dL";
255
263
  };
256
- const respiratoryComponents = (observations) => observations.flatMap((observation) => { var _a; return (_a = observation.component) !== null && _a !== void 0 ? _a : []; });
264
+ const componentsOf = (observations) => observations.flatMap((observation) => { var _a; return (_a = observation.component) !== null && _a !== void 0 ? _a : []; });
257
265
  const baseMeasurement = (measurementTypeKey, observation) => ({
258
266
  measurementTypeKey,
259
267
  ...(observation.id ? { id: observation.id } : {}),
@@ -269,6 +277,10 @@ const inverseGroup = (type, observations) => {
269
277
  })[0];
270
278
  const measurement = baseMeasurement(type, first);
271
279
  const value = (code) => firstValue(observations, code);
280
+ const single = single_value_services_1.singleValueFields.get(type);
281
+ if (single) {
282
+ return { ...measurement, measurementTypeKey: type, [single.valueKey]: value(single.code) };
283
+ }
272
284
  switch (type) {
273
285
  case types_1.MeasurementTypeKey.bloodPressure:
274
286
  return {
@@ -280,12 +292,6 @@ const inverseGroup = (type, observations) => {
280
292
  heartRate: value(types_1.ObservationCode.BLOOD_PRESSURE_HEART_RATE),
281
293
  irregularHeartbeat: irregularHeartbeatOf(observations),
282
294
  };
283
- case types_1.MeasurementTypeKey.bloodGlucose:
284
- return { ...measurement, measurementTypeKey: type, bloodGlucose: value(types_1.ObservationCode.BLOOD_GLUCOSE) };
285
- case types_1.MeasurementTypeKey.bodyWeight:
286
- return { ...measurement, measurementTypeKey: type, bodyWeight: value(types_1.ObservationCode.BODY_WEIGHT) };
287
- case types_1.MeasurementTypeKey.temperature:
288
- return { ...measurement, measurementTypeKey: type, bodyTemp: value(types_1.ObservationCode.BODY_TEMPERATURE) };
289
295
  case types_1.MeasurementTypeKey.bloodOxygenPulseRate:
290
296
  return {
291
297
  ...measurement,
@@ -293,42 +299,6 @@ const inverseGroup = (type, observations) => {
293
299
  bloodOxygen: value(types_1.ObservationCode.OXYGEN_SATURATION),
294
300
  pulseRate: value(types_1.ObservationCode.OXYGEN_SATURATION_HEART_RATE),
295
301
  };
296
- case types_1.MeasurementTypeKey.heartRate:
297
- return {
298
- ...measurement,
299
- measurementTypeKey: type,
300
- heartRate: value(types_1.ObservationCode.HEART_RATE),
301
- };
302
- case types_1.MeasurementTypeKey.heartRateVariability:
303
- return {
304
- ...measurement,
305
- measurementTypeKey: type,
306
- heartRateVariability: value(types_1.ObservationCode.HEART_RATE_VARIABILITY),
307
- };
308
- case types_1.MeasurementTypeKey.respiratoryRate:
309
- return {
310
- ...measurement,
311
- measurementTypeKey: type,
312
- respiratoryRate: value(types_1.ObservationCode.RESPIRATORY_RATE),
313
- };
314
- case types_1.MeasurementTypeKey.restingHeartRate:
315
- return {
316
- ...measurement,
317
- measurementTypeKey: type,
318
- restingHeartRate: value(types_1.ObservationCode.RESTING_HEART_RATE),
319
- };
320
- case types_1.MeasurementTypeKey.walkingHeartRateAverage:
321
- return {
322
- ...measurement,
323
- measurementTypeKey: type,
324
- walkingHeartRateAverage: value(types_1.ObservationCode.WALKING_HEART_RATE_AVERAGE),
325
- };
326
- case types_1.MeasurementTypeKey.vo2Max:
327
- return {
328
- ...measurement,
329
- measurementTypeKey: type,
330
- vo2Max: value(types_1.ObservationCode.VO2_MAX),
331
- };
332
302
  case types_1.MeasurementTypeKey.stepCount:
333
303
  return {
334
304
  ...measurement,
@@ -395,34 +365,22 @@ const inverseGroup = (type, observations) => {
395
365
  })),
396
366
  };
397
367
  case types_1.MeasurementTypeKey.respiratoryTherapy:
398
- return {
399
- ...measurement,
400
- measurementTypeKey: type,
401
- components: respiratoryComponents(observations),
402
- };
403
368
  case types_1.MeasurementTypeKey.babySleep:
404
369
  return {
405
370
  ...measurement,
406
371
  measurementTypeKey: type,
407
- components: respiratoryComponents(observations),
372
+ components: componentsOf(observations),
408
373
  };
409
374
  default:
410
375
  return measurement;
411
376
  }
412
377
  };
378
+ const COMPONENT_TYPES = new Set([types_1.MeasurementTypeKey.respiratoryTherapy, types_1.MeasurementTypeKey.babySleep]);
413
379
  const codesForType = {
380
+ ...Object.fromEntries([...single_value_services_1.singleValueFields].map(([type, { code }]) => [type, [code]])),
414
381
  [types_1.MeasurementTypeKey.bloodPressure]: [types_1.ObservationCode.SYSTOLIC_BLOOD_PRESSURE, types_1.ObservationCode.DIASTOLIC_BLOOD_PRESSURE, types_1.ObservationCode.MEAN_BLOOD_PRESSURE, types_1.ObservationCode.BLOOD_PRESSURE_HEART_RATE, types_1.PulseRhythmCoding.observation],
415
- [types_1.MeasurementTypeKey.bloodGlucose]: [types_1.ObservationCode.BLOOD_GLUCOSE],
416
- [types_1.MeasurementTypeKey.bodyWeight]: [types_1.ObservationCode.BODY_WEIGHT],
417
- [types_1.MeasurementTypeKey.temperature]: [types_1.ObservationCode.BODY_TEMPERATURE],
418
382
  [types_1.MeasurementTypeKey.bloodOxygenPulseRate]: [types_1.ObservationCode.OXYGEN_SATURATION, types_1.ObservationCode.OXYGEN_SATURATION_HEART_RATE],
419
383
  [types_1.MeasurementTypeKey.stepCount]: [types_1.ObservationCode.STEPS],
420
- [types_1.MeasurementTypeKey.heartRate]: [types_1.ObservationCode.HEART_RATE],
421
- [types_1.MeasurementTypeKey.heartRateVariability]: [types_1.ObservationCode.HEART_RATE_VARIABILITY],
422
- [types_1.MeasurementTypeKey.respiratoryRate]: [types_1.ObservationCode.RESPIRATORY_RATE],
423
- [types_1.MeasurementTypeKey.restingHeartRate]: [types_1.ObservationCode.RESTING_HEART_RATE],
424
- [types_1.MeasurementTypeKey.walkingHeartRateAverage]: [types_1.ObservationCode.WALKING_HEART_RATE_AVERAGE],
425
- [types_1.MeasurementTypeKey.vo2Max]: [types_1.ObservationCode.VO2_MAX],
426
384
  [types_1.MeasurementTypeKey.ecg]: [types_1.ObservationCode.ECG, types_1.ObservationCode.ECG_HEART_RATE, types_1.ObservationCode.HEART_RATE],
427
385
  [types_1.MeasurementTypeKey.uricAcid]: [
428
386
  types_1.ObservationCode.URIC_ACID,
@@ -456,12 +414,7 @@ const observationsToMeasurements = (observations, types, options = {}) => {
456
414
  !LIPID_FIELDS.some((field) => lipidValue(group.observations, field) !== undefined)) {
457
415
  return;
458
416
  }
459
- if (type === types_1.MeasurementTypeKey.respiratoryTherapy &&
460
- respiratoryComponents(group.observations).length === 0) {
461
- return;
462
- }
463
- if (type === types_1.MeasurementTypeKey.babySleep &&
464
- respiratoryComponents(group.observations).length === 0) {
417
+ if (COMPONENT_TYPES.has(type) && componentsOf(group.observations).length === 0) {
465
418
  return;
466
419
  }
467
420
  output.push(inverseGroup(type, group.observations));
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseObservationService = void 0;
4
+ const fhir_systems_1 = require("../fhir-systems");
4
5
  class BaseObservationService {
5
6
  getCastedMeasurement(measurement) {
6
7
  if (measurement.measurementTypeKey !== this.measurementTypeKey) {
@@ -25,7 +26,7 @@ class BaseObservationService {
25
26
  value: Number(fixedValue),
26
27
  unit: metadata.unit,
27
28
  code: metadata.primaryCode,
28
- system: "http://loinc.org",
29
+ system: fhir_systems_1.LOINC_SYSTEM,
29
30
  },
30
31
  };
31
32
  });
@@ -36,12 +37,12 @@ class BaseObservationService {
36
37
  {
37
38
  code: metadata.primaryCode,
38
39
  display: metadata.display,
39
- system: "http://loinc.org",
40
+ system: fhir_systems_1.LOINC_SYSTEM,
40
41
  },
41
42
  ...metadata.additionalCodes.map((code) => ({
42
43
  code,
43
44
  display: metadata.display,
44
- system: "http://loinc.org",
45
+ system: fhir_systems_1.LOINC_SYSTEM,
45
46
  })),
46
47
  ],
47
48
  text: metadata.display,
@@ -35,10 +35,7 @@ class BloodPressureService extends base_observation_service_1.BaseObservationSer
35
35
  },
36
36
  {
37
37
  primaryCode: types_1.ObservationCode.BLOOD_PRESSURE_HEART_RATE,
38
- additionalCodes: [
39
- types_1.ObservationCode.HEART_RATE,
40
- types_1.ObservationCode.BLOOD_PRESSURE_HEART_RATE,
41
- ],
38
+ additionalCodes: [types_1.ObservationCode.HEART_RATE],
42
39
  display: "Blood Pressure Heart Rate",
43
40
  unit: "bpm",
44
41
  valueKey: "heartRate",
@@ -1,28 +1,33 @@
1
- import { Observation } from "@medplum/fhirtypes";
2
- import { BabySleepMeasurement, Measurement, MeasurementTypeKey, ObservationCode } from "../../../types";
1
+ import { CodeableConcept, Observation, ObservationComponent } from "@medplum/fhirtypes";
2
+ import { Measurement, MeasurementTypeKey, ObservationCode } from "../../../types";
3
3
  import { RecordResult, RecordType } from "../types/generate-measurement/HealthConnectTypes";
4
4
  import { Sample } from "../types/generate-measurement/Sample";
5
5
  import { ObservationFragment } from "../types/generate-observation-body/ObservationFragment";
6
6
  import { ObservationValue } from "../types/generate-observation-body/ObservationValue";
7
7
  import { ObservationMetadata } from "../types/service/ObservationMetadata";
8
8
  import { BaseObservationService } from "./base-observation-service";
9
- export declare class BabySleepService extends BaseObservationService<BabySleepMeasurement> {
9
+ type ComponentMeasurement = Measurement & {
10
+ components: ObservationComponent[];
11
+ };
12
+ /** One observation that carries the device adapter's coded components as they are. */
13
+ declare class ComponentObservationService extends BaseObservationService<ComponentMeasurement> {
10
14
  protected measurementTypeKey: MeasurementTypeKey;
11
15
  protected measurementTypeName: string;
12
- protected observationMetadata: ObservationMetadata<BabySleepMeasurement>[];
16
+ private readonly code;
17
+ protected observationMetadata: ObservationMetadata<ComponentMeasurement>[];
18
+ private readonly title;
19
+ constructor(measurementTypeKey: MeasurementTypeKey, measurementTypeName: string, code: ObservationCode);
13
20
  generateObservationFragments(measurement: Measurement): ObservationFragment[];
14
- getObservationCodes(): {
15
- coding: {
16
- code: ObservationCode;
17
- display: string;
18
- system: string;
19
- }[];
20
- }[];
21
+ getObservationCodes(): CodeableConcept[];
21
22
  getAdditionalProperties(measurement: Measurement): Partial<Observation>[];
22
23
  generateValues(_measurement: Measurement): ObservationValue[];
23
24
  getUnits(): string[];
24
25
  getMeasurementDisplay(measurement: Measurement): string;
25
26
  getObservationDisplay(observations: Observation[]): string | undefined;
26
- convertSamplesToMeasurement(_samples: Sample[]): BabySleepMeasurement;
27
- convertRecordToMeasurement(_record: RecordResult<RecordType>): BabySleepMeasurement;
27
+ convertSamplesToMeasurement(_samples: Sample[]): ComponentMeasurement;
28
+ convertRecordToMeasurement(_record: RecordResult<RecordType>): ComponentMeasurement;
29
+ private codeableConcept;
28
30
  }
31
+ export declare const babySleepService: () => ComponentObservationService;
32
+ export declare const respiratoryTherapyService: () => ComponentObservationService;
33
+ export {};
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.respiratoryTherapyService = exports.babySleepService = void 0;
4
+ const types_1 = require("../../../types");
5
+ const base_observation_service_1 = require("./base-observation-service");
6
+ const measurement_time_1 = require("./measurement-time");
7
+ const MEASUREMENT_TYPE_SYSTEM = "https://sdk.ovok.com/fhir/CodeSystem/measurement-type";
8
+ /** One observation that carries the device adapter's coded components as they are. */
9
+ class ComponentObservationService extends base_observation_service_1.BaseObservationService {
10
+ constructor(measurementTypeKey, measurementTypeName, code) {
11
+ super();
12
+ this.measurementTypeKey = measurementTypeKey;
13
+ this.measurementTypeName = measurementTypeName;
14
+ this.code = code;
15
+ this.observationMetadata = [];
16
+ this.title = measurementTypeName.charAt(0).toUpperCase() + measurementTypeName.slice(1);
17
+ }
18
+ generateObservationFragments(measurement) {
19
+ const { components, recordedAt } = this.getCastedMeasurement(measurement);
20
+ if (components.length === 0) {
21
+ return [];
22
+ }
23
+ return [{
24
+ code: this.codeableConcept(),
25
+ additionalProperties: {
26
+ effectiveDateTime: (0, measurement_time_1.recordedAtOrNow)({ recordedAt }).toISOString(),
27
+ component: components,
28
+ },
29
+ }];
30
+ }
31
+ getObservationCodes() {
32
+ return [this.codeableConcept()];
33
+ }
34
+ getAdditionalProperties(measurement) {
35
+ return [{ effectiveDateTime: (0, measurement_time_1.recordedAtOrNow)(this.getCastedMeasurement(measurement)).toISOString() }];
36
+ }
37
+ generateValues(_measurement) {
38
+ return [];
39
+ }
40
+ getUnits() {
41
+ return [];
42
+ }
43
+ getMeasurementDisplay(measurement) {
44
+ return `${this.getCastedMeasurement(measurement).components.length} ${this.measurementTypeName} metrics`;
45
+ }
46
+ getObservationDisplay(observations) {
47
+ const count = observations.reduce((total, observation) => { var _a, _b; return total + ((_b = (_a = observation.component) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0); }, 0);
48
+ return count ? `${count} ${this.measurementTypeName} metrics` : undefined;
49
+ }
50
+ convertSamplesToMeasurement(_samples) {
51
+ throw new Error(`${this.title} samples are not supported.`);
52
+ }
53
+ convertRecordToMeasurement(_record) {
54
+ throw new Error(`${this.title} Health Connect records are not supported.`);
55
+ }
56
+ codeableConcept() {
57
+ return {
58
+ coding: [{
59
+ code: this.code,
60
+ display: `${this.title} observation`,
61
+ system: MEASUREMENT_TYPE_SYSTEM,
62
+ }],
63
+ };
64
+ }
65
+ }
66
+ const babySleepService = () => new ComponentObservationService(types_1.MeasurementTypeKey.babySleep, "baby sleep", types_1.ObservationCode.BABY_SLEEP);
67
+ exports.babySleepService = babySleepService;
68
+ const respiratoryTherapyService = () => new ComponentObservationService(types_1.MeasurementTypeKey.respiratoryTherapy, "respiratory therapy", types_1.ObservationCode.RESPIRATORY_THERAPY);
69
+ exports.respiratoryTherapyService = respiratoryTherapyService;
@@ -2,9 +2,8 @@ import { MeasurementTypeKey } from "../../../types";
2
2
  import { IObservationService } from "../types/IObservationService";
3
3
  export declare class ObservationServiceCreator {
4
4
  private static instance;
5
- private services;
5
+ private readonly services;
6
6
  private constructor();
7
7
  static getInstance(): ObservationServiceCreator;
8
- private initializeServices;
9
8
  getService(type: MeasurementTypeKey): IObservationService;
10
9
  }