@openedc/sdk 3.8.1 → 3.8.2-next.1
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 +25 -14
- package/openedc-sdk.js +7 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
<br>
|
|
2
2
|
<div align="center">
|
|
3
|
-
<img src="
|
|
3
|
+
<img src="https://openedc.health/logo.png" alt="OpenEDC Logo" width="48">
|
|
4
4
|
|
|
5
5
|
### SDK for the OpenEDC Health Platform
|
|
6
6
|
#### OpenEDC Health is a modular, standards-compliant platform for medical research
|
|
7
7
|
</div>
|
|
8
8
|
<br>
|
|
9
9
|
|
|
10
|
-
The official TypeScript SDK for the OpenEDC Health Platform
|
|
10
|
+
The official TypeScript SDK for the OpenEDC Health Platform.
|
|
11
11
|
|
|
12
12
|
Fully typed end-to-end, it runs on any modern JavaScript runtime (Node, Deno, Bun) and gives you a clean, expressive API to work with all aspects of a clinical study:
|
|
13
13
|
|
|
14
|
-
- **Query
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
14
|
+
- **Data access:** Query study metadata, clinical data, administrative data, and more, with flexible filtering, sorting, and cross-project support
|
|
15
|
+
- **Data editing:** Create or modify study data reliably with a single commit call, with all changes automatically recorded in the audit trail
|
|
16
|
+
- **Audit trail:** Query the complete history of all study data changes, supporting data integrity and GCP compliance
|
|
17
|
+
- **Live updates:** Subscribe to real-time changes via live queries that automatically reflect updates as they happen
|
|
18
|
+
- **File management:** Upload and download files securely with resumable transfers, supporting files up to 5 GB
|
|
19
|
+
|
|
20
|
+
The OpenEDC Health platform and the SDK are built around PostgreSQL Row Level Security (RLS) Policies and Triggers. This ensures that:
|
|
21
|
+
|
|
22
|
+
- **Scoped access:** Only the projects and data the authenticated user has been granted access to can be read or modified through the SDK
|
|
23
|
+
- **Automated records:** All data changes are automatically recorded in the audit trail, attributed to the user with a server-side timestamp
|
|
19
24
|
|
|
20
25
|
## Getting started
|
|
21
26
|
|
|
@@ -34,6 +39,12 @@ deno add npm:@openedc/sdk
|
|
|
34
39
|
|
|
35
40
|
### Initialization
|
|
36
41
|
|
|
42
|
+
Before you can authenticate within the SDK, please create an API key in the OpenEDC Health application. Click on your name in the bottom-left corner, select *Account*, and then create an API key at the bottom of the dialog.
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
The API key can then be used as shown below.
|
|
47
|
+
|
|
37
48
|
```ts
|
|
38
49
|
import { login, logout } from "@openedc/sdk";
|
|
39
50
|
|
|
@@ -69,8 +80,8 @@ const location = await Location
|
|
|
69
80
|
const subjects = await SubjectData
|
|
70
81
|
.where({ location })
|
|
71
82
|
.project(project)
|
|
72
|
-
.sort("createdDate")
|
|
73
|
-
.
|
|
83
|
+
.sort("createdDate").asc()
|
|
84
|
+
.all();
|
|
74
85
|
|
|
75
86
|
// Create a new subject within a location
|
|
76
87
|
const subject = await new SubjectData("Patient-001", location.reference)
|
|
@@ -86,12 +97,12 @@ await subject.set({ subjectKey: "Patient-003" }).commit();
|
|
|
86
97
|
|
|
87
98
|
#### Global project scope
|
|
88
99
|
|
|
100
|
+
Instead of specifying the project per call, a global scope can be set for all upcoming statements. This works well for linear, sequential flows but should be avoided in parallel or callback-based code that handles multiple projects concurrently. Assumed for the rest of the examples.
|
|
101
|
+
|
|
89
102
|
```ts
|
|
90
103
|
import { Location, SubjectData } from "@openedc/sdk";
|
|
91
104
|
|
|
92
|
-
//
|
|
93
|
-
// all upcoming statements. This can be changed at any time but shouldn't be used when handling
|
|
94
|
-
// multiple projects at the same time. Will be assumed to be set for the rest of the examples.
|
|
105
|
+
// Scope subsequent queries and commits
|
|
95
106
|
scope(project);
|
|
96
107
|
|
|
97
108
|
const sameLocation = await Location
|
|
@@ -100,8 +111,8 @@ const sameLocation = await Location
|
|
|
100
111
|
|
|
101
112
|
const sameSubjects = await SubjectData
|
|
102
113
|
.where({ location })
|
|
103
|
-
.sort("createdDate")
|
|
104
|
-
.
|
|
114
|
+
.sort("createdDate").asc()
|
|
115
|
+
.all();
|
|
105
116
|
|
|
106
117
|
// Create a new subject (note that Patient-003 would result in a unique constraint violation)
|
|
107
118
|
const newSubject = await new SubjectData("Patient-004").commit();
|
package/openedc-sdk.js
CHANGED
|
@@ -10636,11 +10636,13 @@ const kt = class kt {
|
|
|
10636
10636
|
const [o, ...a] = s.split("."), u = a.at(0) === "ref" ? this.getLocalization(o, { locale: n, interpolation: r }) : r == null ? void 0 : r[o];
|
|
10637
10637
|
return String(a.reduce(
|
|
10638
10638
|
(c, h) => this.transform(c, h, n),
|
|
10639
|
-
u
|
|
10639
|
+
u
|
|
10640
10640
|
));
|
|
10641
10641
|
});
|
|
10642
10642
|
}
|
|
10643
10643
|
static transform(e, r, n) {
|
|
10644
|
+
if (e == null || e === "")
|
|
10645
|
+
return "—";
|
|
10644
10646
|
switch (r) {
|
|
10645
10647
|
case "upper":
|
|
10646
10648
|
return String(e).toUpperCase();
|
|
@@ -10663,7 +10665,7 @@ const kt = class kt {
|
|
|
10663
10665
|
}
|
|
10664
10666
|
}
|
|
10665
10667
|
};
|
|
10666
|
-
kt.SELECTED_LANGUAGE = "SELECTED_LANGUAGE", kt.
|
|
10668
|
+
kt.SELECTED_LANGUAGE = "SELECTED_LANGUAGE", kt.defaults = {}, kt.overwrites = {}, kt.defaultLocale = new Intl.Locale("en"), kt.currentLocale = kt.defaultLocale, kt.translationGlob = {}, kt.localizedElements = /* @__PURE__ */ new Map();
|
|
10667
10669
|
let ye = kt;
|
|
10668
10670
|
var _p = Object.defineProperty, Ep = Object.getOwnPropertyDescriptor, Ur = (t, e, r, n) => {
|
|
10669
10671
|
for (var i = n > 1 ? void 0 : n ? Ep(e, r) : e, s = t.length - 1, o; s >= 0; s--)
|
|
@@ -20777,7 +20779,7 @@ const xD = async ({ serverUrl: t, apiKey: e }) => {
|
|
|
20777
20779
|
}
|
|
20778
20780
|
}
|
|
20779
20781
|
});
|
|
20780
|
-
const s = await fetch(t + "/api/auth/
|
|
20782
|
+
const s = await fetch(t + "/api/auth/token", {
|
|
20781
20783
|
method: "POST",
|
|
20782
20784
|
headers: {
|
|
20783
20785
|
Authorization: `Bearer ${e}`
|
|
@@ -20786,9 +20788,10 @@ const xD = async ({ serverUrl: t, apiKey: e }) => {
|
|
|
20786
20788
|
if (!s.ok) throw new Error("Invalid API key");
|
|
20787
20789
|
const { email: o, token: a, type: u } = await s.json(), { data: c, error: h } = await Yn.auth.verifyOtp({ email: o, token: a, type: u });
|
|
20788
20790
|
if (!c.user) throw h ?? new Error("Error during OTP verification");
|
|
20791
|
+
Ps.supabaseClient = Yn, V.adapter = Ps;
|
|
20789
20792
|
const f = await ((d = ke.fromString(c.user.app_metadata.user)) == null ? void 0 : d.data);
|
|
20790
20793
|
if (!f) throw new Error("User not found");
|
|
20791
|
-
return
|
|
20794
|
+
return f;
|
|
20792
20795
|
}, TD = async () => {
|
|
20793
20796
|
await Yn.auth.signOut();
|
|
20794
20797
|
}, FD = (t) => {
|