@ovok/core 0.4.21 → 0.4.23

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.
@@ -0,0 +1,10 @@
1
+ import { ProfileResource } from "@medplum/core";
2
+ import { Project, Reference } from "@medplum/fhirtypes";
3
+ /** Safe account metadata for account pickers; authentication tokens are omitted. */
4
+ export interface OvokSavedAccount {
5
+ id: string;
6
+ profile: Reference<ProfileResource>;
7
+ project: Reference<Project>;
8
+ active: boolean;
9
+ expired: boolean;
10
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,6 @@
1
1
  import { IClientStorage } from "@medplum/core";
2
2
  import { Bundle, BundleEntry, Observation } from "@medplum/fhirtypes";
3
- declare const DEFAULT_STORAGE_KEY = "ovok.core.offline-measurements";
3
+ export declare const OFFLINE_MEASUREMENT_QUEUE_STORAGE_KEY = "ovok.core.offline-measurements";
4
4
  declare const IDENTIFIER_SYSTEM = "https://api.ovok.com/fhir/StructureDefinition/offline-measurement-id";
5
5
  export type OfflineMeasurementQueueOptions = {
6
6
  enabled?: boolean;
@@ -8,6 +8,8 @@ export type OfflineMeasurementQueueOptions = {
8
8
  maxRetries?: number;
9
9
  retryBackoffMs?: number;
10
10
  maxRetryBackoffMs?: number;
11
+ /** Returns the active account key. Queue items are delivered only to their owner. */
12
+ getOwnerKey?: () => string | undefined;
11
13
  };
12
14
  export type OfflineMeasurementFlushOptions = {
13
15
  includeExhausted?: boolean;
@@ -30,6 +32,7 @@ export declare class OfflineMeasurementQueue {
30
32
  private readonly maxRetries;
31
33
  private readonly retryBackoffMs;
32
34
  private readonly maxRetryBackoffMs;
35
+ private readonly getOwnerKey?;
33
36
  private operation;
34
37
  constructor(storage: IClientStorage, transport: MeasurementTransport, options?: OfflineMeasurementQueueOptions);
35
38
  enqueueAndFlush: (observations: Observation[]) => Promise<{
@@ -42,4 +45,4 @@ export declare class OfflineMeasurementQueue {
42
45
  private writeQueue;
43
46
  private flushQueue;
44
47
  }
45
- export { DEFAULT_STORAGE_KEY as DEFAULT_OFFLINE_MEASUREMENT_QUEUE_KEY, IDENTIFIER_SYSTEM as OFFLINE_MEASUREMENT_IDENTIFIER_SYSTEM, entryForObservation, stableMeasurementId, };
48
+ export { OFFLINE_MEASUREMENT_QUEUE_STORAGE_KEY as DEFAULT_OFFLINE_MEASUREMENT_QUEUE_KEY, IDENTIFIER_SYSTEM as OFFLINE_MEASUREMENT_IDENTIFIER_SYSTEM, entryForObservation, stableMeasurementId, };
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.stableMeasurementId = exports.entryForObservation = exports.OFFLINE_MEASUREMENT_IDENTIFIER_SYSTEM = exports.DEFAULT_OFFLINE_MEASUREMENT_QUEUE_KEY = exports.OfflineMeasurementQueue = void 0;
4
- const DEFAULT_STORAGE_KEY = "ovok.core.offline-measurements";
5
- exports.DEFAULT_OFFLINE_MEASUREMENT_QUEUE_KEY = DEFAULT_STORAGE_KEY;
3
+ exports.stableMeasurementId = exports.entryForObservation = exports.OFFLINE_MEASUREMENT_IDENTIFIER_SYSTEM = exports.DEFAULT_OFFLINE_MEASUREMENT_QUEUE_KEY = exports.OfflineMeasurementQueue = exports.OFFLINE_MEASUREMENT_QUEUE_STORAGE_KEY = void 0;
4
+ exports.OFFLINE_MEASUREMENT_QUEUE_STORAGE_KEY = "ovok.core.offline-measurements";
5
+ exports.DEFAULT_OFFLINE_MEASUREMENT_QUEUE_KEY = exports.OFFLINE_MEASUREMENT_QUEUE_STORAGE_KEY;
6
6
  const DEFAULT_MAX_RETRIES = 8;
7
7
  const DEFAULT_RETRY_BACKOFF_MS = 5000;
8
8
  const DEFAULT_MAX_RETRY_BACKOFF_MS = 60 * 60 * 1000;
@@ -82,14 +82,19 @@ class OfflineMeasurementQueue {
82
82
  this.transport = transport;
83
83
  this.operation = Promise.resolve();
84
84
  this.enqueueAndFlush = (observations) => this.runExclusive(async () => {
85
- var _a, _b;
85
+ var _a, _b, _c;
86
86
  await ((_b = (_a = this.storage).getInitPromise) === null || _b === void 0 ? void 0 : _b.call(_a));
87
87
  const queue = this.readQueue();
88
88
  const entries = observations.map(entryForObservation);
89
- const id = stableValueId(entries);
89
+ const ownerKey = (_c = this.getOwnerKey) === null || _c === void 0 ? void 0 : _c.call(this);
90
+ if (this.getOwnerKey && !ownerKey) {
91
+ throw new Error("Offline measurements require an active account to prevent cross-account delivery.");
92
+ }
93
+ const id = stableValueId({ ownerKey, entries });
90
94
  if (!queue.some((item) => item.id === id)) {
91
95
  queue.push({
92
96
  id,
97
+ ownerKey,
93
98
  queuedAt: new Date().toISOString(),
94
99
  attempt: 0,
95
100
  nextAttemptAt: 0,
@@ -130,6 +135,7 @@ class OfflineMeasurementQueue {
130
135
  }
131
136
  };
132
137
  this.flushQueue = async (queue, options = {}) => {
138
+ var _a;
133
139
  const result = {
134
140
  attempted: 0,
135
141
  saved: 0,
@@ -138,6 +144,7 @@ class OfflineMeasurementQueue {
138
144
  responses: {},
139
145
  };
140
146
  const now = Date.now();
147
+ const ownerKey = (_a = this.getOwnerKey) === null || _a === void 0 ? void 0 : _a.call(this);
141
148
  let changed = false;
142
149
  for (const item of [...queue]) {
143
150
  if (item.exhausted && options.resetExhausted) {
@@ -156,6 +163,10 @@ class OfflineMeasurementQueue {
156
163
  }
157
164
  continue;
158
165
  }
166
+ if (this.getOwnerKey && (!ownerKey || item.ownerKey !== ownerKey)) {
167
+ result.queued++;
168
+ continue;
169
+ }
159
170
  result.attempted++;
160
171
  const attempt = item.attempt + 1;
161
172
  try {
@@ -172,7 +183,7 @@ class OfflineMeasurementQueue {
172
183
  result.responses[item.id] = response;
173
184
  changed = true;
174
185
  }
175
- catch (_a) {
186
+ catch (_b) {
176
187
  item.attempt = attempt;
177
188
  item.exhausted = attempt >= this.maxRetries;
178
189
  item.nextAttemptAt = item.exhausted
@@ -193,10 +204,12 @@ class OfflineMeasurementQueue {
193
204
  }
194
205
  return result;
195
206
  };
196
- this.storageKey = (_a = options.storageKey) !== null && _a !== void 0 ? _a : DEFAULT_STORAGE_KEY;
207
+ this.storageKey =
208
+ (_a = options.storageKey) !== null && _a !== void 0 ? _a : exports.OFFLINE_MEASUREMENT_QUEUE_STORAGE_KEY;
197
209
  this.maxRetries = Math.max(1, (_b = options.maxRetries) !== null && _b !== void 0 ? _b : DEFAULT_MAX_RETRIES);
198
210
  this.retryBackoffMs = Math.max(1, (_c = options.retryBackoffMs) !== null && _c !== void 0 ? _c : DEFAULT_RETRY_BACKOFF_MS);
199
211
  this.maxRetryBackoffMs = Math.max(this.retryBackoffMs, (_d = options.maxRetryBackoffMs) !== null && _d !== void 0 ? _d : DEFAULT_MAX_RETRY_BACKOFF_MS);
212
+ this.getOwnerKey = options.getOwnerKey;
200
213
  }
201
214
  }
202
215
  exports.OfflineMeasurementQueue = OfflineMeasurementQueue;
@@ -1,8 +1,9 @@
1
- import { IClientStorage, MedplumClient, MedplumClientOptions, SubscriptionManager } from "@medplum/core";
1
+ import { IClientStorage, LoginState, MedplumClient, MedplumClientOptions, SubscriptionManager } from "@medplum/core";
2
2
  import { Bundle, CapabilityStatement, Observation } from "@medplum/fhirtypes";
3
3
  import { OvokPublicApiEndpoint } from "../conformance/capability";
4
4
  import * as aiFhirMethods from "./ai-fhir/methods";
5
5
  import * as authMethods from "./auth/methods";
6
+ import { OvokSavedAccount } from "./auth/types/Account";
6
7
  import * as botMethods from "./bot/methods";
7
8
  import * as changeHistoryMethods from "./change-history/methods";
8
9
  import * as aiMethods from "./chat/ai/methods";
@@ -34,6 +35,7 @@ export type OvokFhirVersion = "R4" | "R5";
34
35
  export declare class OvokClient extends MedplumClient {
35
36
  protected socialLoginClientId: string;
36
37
  clientStorage: IClientStorage | undefined;
38
+ private readonly hasExplicitStorageAdapter;
37
39
  private readonly offlineQueueOptions;
38
40
  private offlineMeasurementQueue;
39
41
  /**
@@ -44,6 +46,19 @@ export declare class OvokClient extends MedplumClient {
44
46
  socialLoginClientId?: string;
45
47
  offlineQueue?: OfflineMeasurementQueueOptions;
46
48
  });
49
+ /** Lists saved accounts without exposing their access or refresh tokens. */
50
+ getSavedAccounts(): OvokSavedAccount[];
51
+ /** Activates a previously saved account without asking for its password again. */
52
+ switchAccount(accountId: string): Promise<void>;
53
+ /**
54
+ * Ends the selected account's current server session and removes its saved login.
55
+ * The account is retained locally if the server cannot confirm the logout.
56
+ */
57
+ removeAccount(accountId: string): Promise<void>;
58
+ /** Removes every saved account and expiry marker from client storage. */
59
+ clearAllAccounts(): void;
60
+ /** Reauthentication clears the expired marker for the account that signed in. */
61
+ setActiveLogin(login: LoginState): Promise<void>;
47
62
  /** Saves a measurement immediately, or durably queues it when the opt-in queue is enabled. */
48
63
  saveMeasurement(params: GenerateObservationBodyParams): Promise<SaveObservationsResult>;
49
64
  /** Reads Ovok's composed FHIR and public-route capability statement. */
@@ -55,6 +70,16 @@ export declare class OvokClient extends MedplumClient {
55
70
  /** Flushes measurements queued while offline. Call this on connectivity/app-resume events. */
56
71
  flushOfflineMeasurementQueue(options?: OfflineMeasurementFlushOptions): Promise<OfflineMeasurementFlushResult>;
57
72
  private getOfflineMeasurementQueue;
73
+ /**
74
+ * Medplum clears its entire client storage after an unrecoverable 401. Keep
75
+ * saved accounts (marking only the active one expired) and queued measurements.
76
+ * Use clearAllAccounts() when the caller intends to remove all saved accounts.
77
+ */
78
+ clear(): void;
79
+ private getLogin;
80
+ private getExpiredAccountIds;
81
+ private setAccountExpired;
82
+ private restoreActiveLogin;
58
83
  private executeConditionalObservationCreates;
59
84
  private executeObservationBundle;
60
85
  private bindMethods;
@@ -63,16 +63,20 @@ const slimMethods = __importStar(require("./slim/methods"));
63
63
  exports.MAX_CONDITIONAL_OBSERVATION_ENTRIES = 8;
64
64
  var observation_bundle_utils_2 = require("./observation/observation-bundle-utils");
65
65
  Object.defineProperty(exports, "MAX_OBSERVATION_BUNDLE_BYTES", { enumerable: true, get: function () { return observation_bundle_utils_2.MAX_OBSERVATION_BUNDLE_BYTES; } });
66
+ const LOGINS_STORAGE_KEY = "logins";
67
+ const EXPIRED_ACCOUNTS_STORAGE_KEY = "ovok.core.expired-accounts";
66
68
  class OvokClient extends core_1.MedplumClient {
67
69
  /**
68
70
  * @param config - The configuration for the client.
69
71
  * @param config.socialLoginClientId - The social login client id to use for the client.
70
72
  */
71
73
  constructor(config) {
72
- var _a, _b;
73
- super(config);
74
+ var _a, _b, _c;
75
+ const storage = (_a = config.storage) !== null && _a !== void 0 ? _a : new core_1.ClientStorage();
76
+ super({ ...config, storage });
74
77
  this.socialLoginClientId = "";
75
- this.socialLoginClientId = (_a = config.socialLoginClientId) !== null && _a !== void 0 ? _a : "";
78
+ this.hasExplicitStorageAdapter = config.storage !== undefined;
79
+ this.socialLoginClientId = (_b = config.socialLoginClientId) !== null && _b !== void 0 ? _b : "";
76
80
  this.bindMethods(authMethods);
77
81
  this.bindMethods(observationMethods);
78
82
  this.bindMethods(questionnaireResponseMethods);
@@ -96,8 +100,88 @@ class OvokClient extends core_1.MedplumClient {
96
100
  });
97
101
  return (0, errors_1.isSignalsError)(mapped) ? mapped : (0, ovok_error_1.mapOvokApiError)(mapped);
98
102
  });
99
- this.clientStorage = config.storage;
100
- this.offlineQueueOptions = (_b = config.offlineQueue) !== null && _b !== void 0 ? _b : {};
103
+ this.clientStorage = storage;
104
+ this.offlineQueueOptions = (_c = config.offlineQueue) !== null && _c !== void 0 ? _c : {};
105
+ }
106
+ /** Lists saved accounts without exposing their access or refresh tokens. */
107
+ getSavedAccounts() {
108
+ var _a, _b;
109
+ const activeAccountId = (_b = (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.profile) === null || _b === void 0 ? void 0 : _b.reference;
110
+ const expiredAccountIds = new Set(this.getExpiredAccountIds());
111
+ return this.getLogins().flatMap((login) => {
112
+ var _a;
113
+ const id = (_a = login.profile) === null || _a === void 0 ? void 0 : _a.reference;
114
+ return id
115
+ ? [{
116
+ id,
117
+ profile: login.profile,
118
+ project: login.project,
119
+ active: id === activeAccountId,
120
+ expired: expiredAccountIds.has(id),
121
+ }]
122
+ : [];
123
+ });
124
+ }
125
+ /** Activates a previously saved account without asking for its password again. */
126
+ async switchAccount(accountId) {
127
+ const login = this.getLogin(accountId);
128
+ if (!login) {
129
+ throw new Error(`No saved account found for ${accountId}.`);
130
+ }
131
+ if (this.getExpiredAccountIds().includes(accountId)) {
132
+ throw new Error("This account's session has expired. Sign in again.");
133
+ }
134
+ await this.setActiveLogin(login);
135
+ }
136
+ /**
137
+ * Ends the selected account's current server session and removes its saved login.
138
+ * The account is retained locally if the server cannot confirm the logout.
139
+ */
140
+ async removeAccount(accountId) {
141
+ var _a, _b;
142
+ const login = this.getLogin(accountId);
143
+ if (!login) {
144
+ throw new Error(`No saved account found for ${accountId}.`);
145
+ }
146
+ const previousLogin = this.getActiveLogin();
147
+ try {
148
+ await this.setActiveLogin(login);
149
+ await this.delete("auth/session/current");
150
+ }
151
+ catch (error) {
152
+ if (previousLogin && previousLogin.profile.reference !== accountId) {
153
+ await this.restoreActiveLogin(previousLogin);
154
+ }
155
+ throw error;
156
+ }
157
+ this.clearActiveLogin();
158
+ const remainingLogins = this.getLogins().filter((savedLogin) => savedLogin.profile.reference !== accountId);
159
+ (_a = this.clientStorage) === null || _a === void 0 ? void 0 : _a.setObject(LOGINS_STORAGE_KEY, remainingLogins);
160
+ this.setAccountExpired(accountId, false);
161
+ const nextLogin = (_b = ((previousLogin === null || previousLogin === void 0 ? void 0 : previousLogin.profile.reference) !== accountId ? previousLogin : undefined)) !== null && _b !== void 0 ? _b : remainingLogins[0];
162
+ if (nextLogin) {
163
+ await this.restoreActiveLogin(nextLogin);
164
+ }
165
+ }
166
+ /** Removes every saved account and expiry marker from client storage. */
167
+ clearAllAccounts() {
168
+ var _a;
169
+ const storage = this.clientStorage;
170
+ const queueKey = (_a = this.offlineQueueOptions.storageKey) !== null && _a !== void 0 ? _a : offline_1.OFFLINE_MEASUREMENT_QUEUE_STORAGE_KEY;
171
+ const queuedMeasurements = storage === null || storage === void 0 ? void 0 : storage.getObject(queueKey);
172
+ super.clear();
173
+ if (Array.isArray(queuedMeasurements)) {
174
+ storage === null || storage === void 0 ? void 0 : storage.setObject(queueKey, queuedMeasurements);
175
+ }
176
+ }
177
+ /** Reauthentication clears the expired marker for the account that signed in. */
178
+ async setActiveLogin(login) {
179
+ var _a;
180
+ await super.setActiveLogin(login);
181
+ const accountId = (_a = login.profile) === null || _a === void 0 ? void 0 : _a.reference;
182
+ if (accountId) {
183
+ this.setAccountExpired(accountId, false);
184
+ }
101
185
  }
102
186
  /** Saves a measurement immediately, or durably queues it when the opt-in queue is enabled. */
103
187
  async saveMeasurement(params) {
@@ -145,12 +229,85 @@ class OvokClient extends core_1.MedplumClient {
145
229
  if (this.offlineQueueOptions.enabled !== true) {
146
230
  return undefined;
147
231
  }
148
- if (!this.clientStorage) {
232
+ if (!this.clientStorage || !this.hasExplicitStorageAdapter) {
149
233
  throw new Error("Offline measurement queue requires a durable client storage adapter.");
150
234
  }
151
- (_a = this.offlineMeasurementQueue) !== null && _a !== void 0 ? _a : (this.offlineMeasurementQueue = new offline_1.OfflineMeasurementQueue(this.clientStorage, (bundle) => this.executeObservationBundle(bundle), this.offlineQueueOptions));
235
+ (_a = this.offlineMeasurementQueue) !== null && _a !== void 0 ? _a : (this.offlineMeasurementQueue = new offline_1.OfflineMeasurementQueue(this.clientStorage, (bundle) => this.executeObservationBundle(bundle), {
236
+ ...this.offlineQueueOptions,
237
+ getOwnerKey: () => {
238
+ var _a, _b, _c;
239
+ const login = this.getActiveLogin();
240
+ const profileReference = (_a = login === null || login === void 0 ? void 0 : login.profile) === null || _a === void 0 ? void 0 : _a.reference;
241
+ if (!profileReference) {
242
+ return undefined;
243
+ }
244
+ return [
245
+ this.getBaseUrl(),
246
+ (_c = (_b = login === null || login === void 0 ? void 0 : login.project) === null || _b === void 0 ? void 0 : _b.reference) !== null && _c !== void 0 ? _c : "",
247
+ profileReference,
248
+ ].join("|");
249
+ },
250
+ }));
152
251
  return this.offlineMeasurementQueue;
153
252
  }
253
+ /**
254
+ * Medplum clears its entire client storage after an unrecoverable 401. Keep
255
+ * saved accounts (marking only the active one expired) and queued measurements.
256
+ * Use clearAllAccounts() when the caller intends to remove all saved accounts.
257
+ */
258
+ clear() {
259
+ var _a, _b, _c;
260
+ const storage = this.clientStorage;
261
+ const logins = this.getLogins();
262
+ const activeAccountId = (_b = (_a = this.getActiveLogin()) === null || _a === void 0 ? void 0 : _a.profile) === null || _b === void 0 ? void 0 : _b.reference;
263
+ const expiredAccountIds = new Set(this.getExpiredAccountIds());
264
+ if (activeAccountId) {
265
+ expiredAccountIds.add(activeAccountId);
266
+ }
267
+ const queueKey = (_c = this.offlineQueueOptions.storageKey) !== null && _c !== void 0 ? _c : offline_1.OFFLINE_MEASUREMENT_QUEUE_STORAGE_KEY;
268
+ const queuedMeasurements = storage === null || storage === void 0 ? void 0 : storage.getObject(queueKey);
269
+ super.clear();
270
+ if (logins.length > 0) {
271
+ storage === null || storage === void 0 ? void 0 : storage.setObject(LOGINS_STORAGE_KEY, logins);
272
+ }
273
+ if (expiredAccountIds.size > 0) {
274
+ storage === null || storage === void 0 ? void 0 : storage.setObject(EXPIRED_ACCOUNTS_STORAGE_KEY, Array.from(expiredAccountIds));
275
+ }
276
+ if (Array.isArray(queuedMeasurements)) {
277
+ storage === null || storage === void 0 ? void 0 : storage.setObject(queueKey, queuedMeasurements);
278
+ }
279
+ }
280
+ getLogin(accountId) {
281
+ return this.getLogins().find((login) => { var _a; return ((_a = login.profile) === null || _a === void 0 ? void 0 : _a.reference) === accountId; });
282
+ }
283
+ getExpiredAccountIds() {
284
+ var _a, _b;
285
+ return ((_b = (_a = this.clientStorage) === null || _a === void 0 ? void 0 : _a.getObject(EXPIRED_ACCOUNTS_STORAGE_KEY)) !== null && _b !== void 0 ? _b : []);
286
+ }
287
+ setAccountExpired(accountId, expired) {
288
+ var _a, _b;
289
+ const expiredAccountIds = new Set(this.getExpiredAccountIds());
290
+ if (expired) {
291
+ expiredAccountIds.add(accountId);
292
+ }
293
+ else {
294
+ expiredAccountIds.delete(accountId);
295
+ }
296
+ if (expiredAccountIds.size > 0) {
297
+ (_a = this.clientStorage) === null || _a === void 0 ? void 0 : _a.setObject(EXPIRED_ACCOUNTS_STORAGE_KEY, Array.from(expiredAccountIds));
298
+ }
299
+ else {
300
+ (_b = this.clientStorage) === null || _b === void 0 ? void 0 : _b.setString(EXPIRED_ACCOUNTS_STORAGE_KEY, undefined);
301
+ }
302
+ }
303
+ async restoreActiveLogin(login) {
304
+ try {
305
+ await this.setActiveLogin(login);
306
+ }
307
+ catch (_a) {
308
+ // Keep the saved login available for a later retry if restoration is offline.
309
+ }
310
+ }
154
311
  async executeConditionalObservationCreates(observations) {
155
312
  const entries = observations.map(offline_1.entryForObservation);
156
313
  return this.executeObservationBundle({
@@ -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.4.21",
51
+ version: "0.4.23",
52
52
  },
53
53
  fhirVersion: "4.0.1",
54
54
  format: ["json"],
package/dist/index.d.ts CHANGED
@@ -929,6 +929,7 @@ export * from "./client/errors/ovok-error";
929
929
  export * from "./client/auth/errors";
930
930
  export type { RegisterBody } from "./client/auth/types/RegisterBody";
931
931
  export type { RegisterPractitionerBody } from "./client/auth/types/RegisterPractitionerBody";
932
+ export type { OvokSavedAccount } from "./client/auth/types/Account";
932
933
  export { MAX_CONDITIONAL_OBSERVATION_ENTRIES, MAX_OBSERVATION_BUNDLE_BYTES, } from "./client/ovok-client";
933
934
  export type { SaveObservationsResult } from "./client/ovok-client";
934
935
  export * from "./client/offline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ovok/core",
3
- "version": "0.4.21",
3
+ "version": "0.4.23",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",