@ovok/core 0.3.23 → 0.4.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 CHANGED
@@ -282,6 +282,35 @@ if ("nextStep" in result && result.nextStep === "mfa") {
282
282
  }
283
283
  ```
284
284
 
285
+ ## Patient registration invitations
286
+
287
+ When a project invites people with a business email address to join as clinicians,
288
+ `register()` can return a clinician-invite step instead of an authenticated session. Check
289
+ `nextStep` before reading tokens or setting the active login:
290
+
291
+ ```typescript
292
+ const result = await client.register(registerBody);
293
+
294
+ if ("nextStep" in result && result.nextStep === "clinician-invite") {
295
+ showCheckYourEmailMessage();
296
+ return;
297
+ }
298
+
299
+ await client.setActiveLogin(result);
300
+ ```
301
+
302
+ The invitation email's “continue as patient” link includes a
303
+ `continueAsPatientToken` query parameter. The host app owns its deep-link and universal-link
304
+ configuration: read that parameter from the incoming URL and pass it to `register()` to finish
305
+ registration as a patient:
306
+
307
+ ```typescript
308
+ await client.register({ ...registerBody, continueAsPatientToken });
309
+ ```
310
+
311
+ The token is optional and is sent only when provided. An invalid or expired token is rejected
312
+ with HTTP 400 and the message “The continue-as-patient link is invalid or has expired.”
313
+
285
314
  ## Large measurements
286
315
 
287
316
  `saveMeasurement` and `saveObservations` use conditional creates for idempotent saves. Medplum
@@ -1,4 +1,4 @@
1
1
  import { OvokClient } from "../../ovok-client";
2
- import { AuthResponse } from "../types/AuthResponse";
3
2
  import { RegisterBody } from "../types/RegisterBody";
4
- export declare function register(this: OvokClient, body: RegisterBody): Promise<AuthResponse>;
3
+ import { RegisterResponse } from "../types/RegisterResponse";
4
+ export declare function register(this: OvokClient, body: RegisterBody): Promise<RegisterResponse>;
@@ -11,5 +11,8 @@ async function register(body) {
11
11
  name: body.name,
12
12
  surname: body.surname,
13
13
  tenantCode: body.tenantCode,
14
+ ...(body.continueAsPatientToken !== undefined
15
+ ? { continueAsPatientToken: body.continueAsPatientToken }
16
+ : {}),
14
17
  }, "application/json");
15
18
  }
@@ -5,4 +5,5 @@ export interface RegisterBody {
5
5
  name: string;
6
6
  surname: string;
7
7
  tenantCode: string;
8
+ continueAsPatientToken?: string;
8
9
  }
@@ -0,0 +1,5 @@
1
+ import { AuthResponse } from "./AuthResponse";
2
+ export type ClinicianInviteStep = {
3
+ nextStep: "clinician-invite";
4
+ };
5
+ export type RegisterResponse = AuthResponse | ClinicianInviteStep;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  import { Observation } from "@medplum/fhirtypes";
2
- import { BloodGlucoseMeasurement, BloodPressureMeasurement, BodyTemperatureMeasurement, BodyWeightMeasurement, EcgMeasurement, HeartRateMeasurement, HeartRateVariabilityMeasurement, LipidPanelMeasurement, Measurement, MeasurementTypeKey, PulseOximeterMeasurement, RespiratoryRateMeasurement, RespiratoryTherapyMeasurement, RestingHeartRateMeasurement, StepCountMeasurement, UrineAnalyzeMeasurement, Vo2MaxMeasurement, WalkingHeartRateAverageMeasurement } from "../../../types";
3
- export type SupportedMeasurement = BloodGlucoseMeasurement | BloodPressureMeasurement | BodyTemperatureMeasurement | BodyWeightMeasurement | EcgMeasurement | HeartRateMeasurement | HeartRateVariabilityMeasurement | LipidPanelMeasurement | PulseOximeterMeasurement | RespiratoryRateMeasurement | RespiratoryTherapyMeasurement | RestingHeartRateMeasurement | StepCountMeasurement | UrineAnalyzeMeasurement | Vo2MaxMeasurement | WalkingHeartRateAverageMeasurement | Measurement;
2
+ import { BabySleepMeasurement, BloodGlucoseMeasurement, BloodPressureMeasurement, BodyTemperatureMeasurement, BodyWeightMeasurement, EcgMeasurement, HeartRateMeasurement, HeartRateVariabilityMeasurement, LipidPanelMeasurement, Measurement, MeasurementTypeKey, PulseOximeterMeasurement, RespiratoryRateMeasurement, RespiratoryTherapyMeasurement, RestingHeartRateMeasurement, StepCountMeasurement, UrineAnalyzeMeasurement, Vo2MaxMeasurement, WalkingHeartRateAverageMeasurement } from "../../../types";
3
+ export type SupportedMeasurement = BloodGlucoseMeasurement | BabySleepMeasurement | BloodPressureMeasurement | BodyTemperatureMeasurement | BodyWeightMeasurement | EcgMeasurement | HeartRateMeasurement | HeartRateVariabilityMeasurement | LipidPanelMeasurement | PulseOximeterMeasurement | RespiratoryRateMeasurement | RespiratoryTherapyMeasurement | RestingHeartRateMeasurement | StepCountMeasurement | UrineAnalyzeMeasurement | Vo2MaxMeasurement | WalkingHeartRateAverageMeasurement | Measurement;
4
4
  type SupportedType = Exclude<MeasurementTypeKey, MeasurementTypeKey.symptomQuestionnaire>;
5
5
  export type ObservationGrouping = "hasMember" | "time" | ((observation: Observation, index: number, observations: readonly Observation[]) => string);
6
6
  export interface ObservationsToMeasurementsOptions {
@@ -384,6 +384,12 @@ const inverseGroup = (type, observations) => {
384
384
  measurementTypeKey: type,
385
385
  components: respiratoryComponents(observations),
386
386
  };
387
+ case types_1.MeasurementTypeKey.babySleep:
388
+ return {
389
+ ...measurement,
390
+ measurementTypeKey: type,
391
+ components: respiratoryComponents(observations),
392
+ };
387
393
  default:
388
394
  return measurement;
389
395
  }
@@ -415,6 +421,7 @@ const codesForType = {
415
421
  ...LIPID_FIELDS.flatMap(({ codes }) => codes),
416
422
  ],
417
423
  [types_1.MeasurementTypeKey.respiratoryTherapy]: [types_1.ObservationCode.RESPIRATORY_THERAPY],
424
+ [types_1.MeasurementTypeKey.babySleep]: [types_1.ObservationCode.BABY_SLEEP],
418
425
  };
419
426
  const observationCodesForTypes = (types) => [...new Set(types.flatMap((type) => { var _a; return (_a = codesForType[type]) !== null && _a !== void 0 ? _a : []; }))].join(",");
420
427
  exports.observationCodesForTypes = observationCodesForTypes;
@@ -437,6 +444,10 @@ const observationsToMeasurements = (observations, types, options = {}) => {
437
444
  respiratoryComponents(group.observations).length === 0) {
438
445
  return;
439
446
  }
447
+ if (type === types_1.MeasurementTypeKey.babySleep &&
448
+ respiratoryComponents(group.observations).length === 0) {
449
+ return;
450
+ }
440
451
  output.push(inverseGroup(type, group.observations));
441
452
  });
442
453
  });
@@ -0,0 +1,28 @@
1
+ import { Observation } from "@medplum/fhirtypes";
2
+ import { BabySleepMeasurement, Measurement, MeasurementTypeKey, ObservationCode } from "../../../types";
3
+ import { RecordResult, RecordType } from "../types/generate-measurement/HealthConnectTypes";
4
+ import { Sample } from "../types/generate-measurement/Sample";
5
+ import { ObservationFragment } from "../types/generate-observation-body/ObservationFragment";
6
+ import { ObservationValue } from "../types/generate-observation-body/ObservationValue";
7
+ import { ObservationMetadata } from "../types/service/ObservationMetadata";
8
+ import { BaseObservationService } from "./base-observation-service";
9
+ export declare class BabySleepService extends BaseObservationService<BabySleepMeasurement> {
10
+ protected measurementTypeKey: MeasurementTypeKey;
11
+ protected measurementTypeName: string;
12
+ protected observationMetadata: ObservationMetadata<BabySleepMeasurement>[];
13
+ generateObservationFragments(measurement: Measurement): ObservationFragment[];
14
+ getObservationCodes(): {
15
+ coding: {
16
+ code: ObservationCode;
17
+ display: string;
18
+ system: string;
19
+ }[];
20
+ }[];
21
+ getAdditionalProperties(measurement: Measurement): Partial<Observation>[];
22
+ generateValues(_measurement: Measurement): ObservationValue[];
23
+ getUnits(): string[];
24
+ getMeasurementDisplay(measurement: Measurement): string;
25
+ getObservationDisplay(observations: Observation[]): string | undefined;
26
+ convertSamplesToMeasurement(_samples: Sample[]): BabySleepMeasurement;
27
+ convertRecordToMeasurement(_record: RecordResult<RecordType>): BabySleepMeasurement;
28
+ }
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BabySleepService = void 0;
4
+ const types_1 = require("../../../types");
5
+ const base_observation_service_1 = require("./base-observation-service");
6
+ const BABY_SLEEP_SYSTEM = "https://sdk.ovok.com/fhir/CodeSystem/measurement-type";
7
+ const recordedAt = (measurement) => measurement.recordedAt instanceof Date &&
8
+ !Number.isNaN(measurement.recordedAt.getTime())
9
+ ? measurement.recordedAt.toISOString()
10
+ : new Date().toISOString();
11
+ class BabySleepService extends base_observation_service_1.BaseObservationService {
12
+ constructor() {
13
+ super(...arguments);
14
+ this.measurementTypeKey = types_1.MeasurementTypeKey.babySleep;
15
+ this.measurementTypeName = "baby sleep";
16
+ this.observationMetadata = [];
17
+ }
18
+ generateObservationFragments(measurement) {
19
+ const babySleep = this.getCastedMeasurement(measurement);
20
+ if (babySleep.components.length === 0) {
21
+ return [];
22
+ }
23
+ return [{
24
+ code: {
25
+ coding: [{
26
+ code: types_1.ObservationCode.BABY_SLEEP,
27
+ display: "Baby sleep observation",
28
+ system: BABY_SLEEP_SYSTEM,
29
+ }],
30
+ },
31
+ additionalProperties: {
32
+ effectiveDateTime: recordedAt(babySleep),
33
+ component: babySleep.components,
34
+ },
35
+ }];
36
+ }
37
+ getObservationCodes() {
38
+ return [{
39
+ coding: [{
40
+ code: types_1.ObservationCode.BABY_SLEEP,
41
+ display: "Baby sleep observation",
42
+ system: BABY_SLEEP_SYSTEM,
43
+ }],
44
+ }];
45
+ }
46
+ getAdditionalProperties(measurement) {
47
+ return [{ effectiveDateTime: recordedAt(this.getCastedMeasurement(measurement)) }];
48
+ }
49
+ generateValues(_measurement) {
50
+ return [];
51
+ }
52
+ getUnits() {
53
+ return [];
54
+ }
55
+ getMeasurementDisplay(measurement) {
56
+ return `${this.getCastedMeasurement(measurement).components.length} baby sleep metrics`;
57
+ }
58
+ getObservationDisplay(observations) {
59
+ 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);
60
+ return count ? `${count} baby sleep metrics` : undefined;
61
+ }
62
+ convertSamplesToMeasurement(_samples) {
63
+ throw new Error("Baby sleep samples are not supported.");
64
+ }
65
+ convertRecordToMeasurement(_record) {
66
+ throw new Error("Baby sleep Health Connect records are not supported.");
67
+ }
68
+ }
69
+ exports.BabySleepService = BabySleepService;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ObservationServiceCreator = void 0;
4
4
  const types_1 = require("../../../types");
5
+ const baby_sleep_1 = require("./baby-sleep");
5
6
  const blood_glucose_1 = require("./blood-glucose");
6
7
  const blood_oxygen_1 = require("./blood-oxygen");
7
8
  const blood_pressure_1 = require("./blood-pressure");
@@ -47,6 +48,7 @@ class ObservationServiceCreator {
47
48
  this.services.set(types_1.MeasurementTypeKey.uricAcid, new uric_acid_service_1.UricAcidService());
48
49
  this.services.set(types_1.MeasurementTypeKey.lipidPanel, new lipid_panel_1.LipidPanelService());
49
50
  this.services.set(types_1.MeasurementTypeKey.respiratoryTherapy, new respiratory_therapy_1.RespiratoryTherapyService());
51
+ this.services.set(types_1.MeasurementTypeKey.babySleep, new baby_sleep_1.BabySleepService());
50
52
  }
51
53
  getService(type) {
52
54
  const service = this.services.get(type);
@@ -20,5 +20,6 @@ export declare enum MeasurementTypeKey {
20
20
  restingHeartRate = "resting-heart-rate",
21
21
  walkingHeartRateAverage = "walking-heart-rate-average",
22
22
  lipidPanel = "lipid-panel",
23
- respiratoryTherapy = "respiratory-therapy"
23
+ respiratoryTherapy = "respiratory-therapy",
24
+ babySleep = "baby-sleep"
24
25
  }
@@ -25,4 +25,5 @@ var MeasurementTypeKey;
25
25
  MeasurementTypeKey["walkingHeartRateAverage"] = "walking-heart-rate-average";
26
26
  MeasurementTypeKey["lipidPanel"] = "lipid-panel";
27
27
  MeasurementTypeKey["respiratoryTherapy"] = "respiratory-therapy";
28
+ MeasurementTypeKey["babySleep"] = "baby-sleep";
28
29
  })(MeasurementTypeKey || (exports.MeasurementTypeKey = MeasurementTypeKey = {}));
@@ -45,5 +45,6 @@ export declare enum ObservationCode {
45
45
  TRIGLYCERIDES_MASS = "2571-8",
46
46
  TRIGLYCERIDES_MOLAR = "14927-8",
47
47
  TOTAL_CHOLESTEROL_HDL_RATIO = "9830-1",
48
- RESPIRATORY_THERAPY = "respiratory-therapy"
48
+ RESPIRATORY_THERAPY = "respiratory-therapy",
49
+ BABY_SLEEP = "baby-sleep"
49
50
  }
@@ -50,4 +50,5 @@ var ObservationCode;
50
50
  ObservationCode["TRIGLYCERIDES_MOLAR"] = "14927-8";
51
51
  ObservationCode["TOTAL_CHOLESTEROL_HDL_RATIO"] = "9830-1";
52
52
  ObservationCode["RESPIRATORY_THERAPY"] = "respiratory-therapy";
53
+ ObservationCode["BABY_SLEEP"] = "baby-sleep";
53
54
  })(ObservationCode || (exports.ObservationCode = ObservationCode = {}));
@@ -0,0 +1,12 @@
1
+ import { ObservationComponent } from "@medplum/fhirtypes";
2
+ import { MeasurementTypeKey } from "../MeasurementTypeKey";
3
+ import { Measurement } from "./Measurement";
4
+ /**
5
+ * One BabySleep observation, such as a clip sample or a session summary.
6
+ * Core preserves the coded components supplied by the device adapter rather than
7
+ * assigning vendor-specific meaning to sleep states, posture, or device status.
8
+ */
9
+ export type BabySleepMeasurement = Measurement & {
10
+ measurementTypeKey: MeasurementTypeKey.babySleep;
11
+ components: ObservationComponent[];
12
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -17,3 +17,4 @@ export * from "./Vo2MaxMeasurement";
17
17
  export * from "./WalkingHeartRateAverageMeasurement";
18
18
  export * from "./LipidPanelMeasurement";
19
19
  export * from "./RespiratoryTherapyMeasurement";
20
+ export * from "./BabySleepMeasurement";
@@ -33,3 +33,4 @@ __exportStar(require("./Vo2MaxMeasurement"), exports);
33
33
  __exportStar(require("./WalkingHeartRateAverageMeasurement"), exports);
34
34
  __exportStar(require("./LipidPanelMeasurement"), exports);
35
35
  __exportStar(require("./RespiratoryTherapyMeasurement"), exports);
36
+ __exportStar(require("./BabySleepMeasurement"), exports);
@@ -68,7 +68,7 @@ export type OvokProjectFeatures = {
68
68
  export type OvokProjectFeaturesUpdate = {
69
69
  features: OvokProjectFeature[];
70
70
  };
71
- export declare const OVOK_PROJECT_SETTING_KEYS: readonly ["CONTENT_ENABLED", "MAILING_ENABLED", "PATIENT_INVITATION_ENABLED", "PATIENT_REGISTRATION_ENABLED", "PATIENT_LOGIN_ENABLED", "PRACTITIONER_INVITATION_ENABLED", "PRACTITIONER_REGISTRATION_ENABLED", "PRACTITIONER_LOGIN_ENABLED", "IOT_ENABLED"];
71
+ export declare const OVOK_PROJECT_SETTING_KEYS: readonly ["CONTENT_ENABLED", "MAILING_ENABLED", "PATIENT_INVITATION_ENABLED", "PATIENT_REGISTRATION_ENABLED", "PATIENT_LOGIN_ENABLED", "PRACTITIONER_INVITATION_ENABLED", "PRACTITIONER_REGISTRATION_ENABLED", "PRACTITIONER_LOGIN_ENABLED", "IOT_ENABLED", "CLINICIAN_INVITE_ON_BUSINESS_EMAIL"];
72
72
  export type OvokProjectSettingKey = (typeof OVOK_PROJECT_SETTING_KEYS)[number];
73
73
  export type OvokProjectSettings = {
74
74
  projectId: string;
@@ -24,4 +24,5 @@ exports.OVOK_PROJECT_SETTING_KEYS = [
24
24
  "PRACTITIONER_REGISTRATION_ENABLED",
25
25
  "PRACTITIONER_LOGIN_ENABLED",
26
26
  "IOT_ENABLED",
27
+ "CLINICIAN_INVITE_ON_BUSINESS_EMAIL",
27
28
  ];
@@ -48,7 +48,7 @@ exports.OVOK_CORE_REQUIREMENTS = {
48
48
  description: "The FHIR surface a server must provide for the @ovok/core client library's own methods to function.",
49
49
  software: {
50
50
  name: "@ovok/core",
51
- version: "0.3.23",
51
+ version: "0.4.0",
52
52
  },
53
53
  fhirVersion: "4.0.1",
54
54
  format: ["json"],
@@ -2,6 +2,7 @@ export * from "../client/auth/types/AuthResponse";
2
2
  export * from "../client/auth/types/LoginResponse";
3
3
  export * from "../client/auth/types/LoginBody";
4
4
  export * from "../client/auth/types/RegisterBody";
5
+ export * from "../client/auth/types/RegisterResponse";
5
6
  export * from "../client/auth/types/ChangePasswordBody";
6
7
  export * from "../client/auth/types/ChangePasswordResponse";
7
8
  export * from "../client/auth/types/RequestDeleteUserBody";
@@ -19,6 +19,7 @@ __exportStar(require("../client/auth/types/AuthResponse"), exports);
19
19
  __exportStar(require("../client/auth/types/LoginResponse"), exports);
20
20
  __exportStar(require("../client/auth/types/LoginBody"), exports);
21
21
  __exportStar(require("../client/auth/types/RegisterBody"), exports);
22
+ __exportStar(require("../client/auth/types/RegisterResponse"), exports);
22
23
  __exportStar(require("../client/auth/types/ChangePasswordBody"), exports);
23
24
  __exportStar(require("../client/auth/types/ChangePasswordResponse"), exports);
24
25
  __exportStar(require("../client/auth/types/RequestDeleteUserBody"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ovok/core",
3
- "version": "0.3.23",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",