@openedc/sdk 3.8.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 ADDED
@@ -0,0 +1,189 @@
1
+ <br>
2
+ <div align="center">
3
+ <img src="../public/icons/custom/openedc.svg" alt="OpenEDC Logo" width="48">
4
+
5
+ ### SDK for the OpenEDC Health Platform
6
+ #### OpenEDC Health is a modular, standards-compliant platform for medical research
7
+ </div>
8
+ <br>
9
+
10
+ The official TypeScript SDK for the OpenEDC Health Platform — a modular, standards-compliant platform for medical research.
11
+
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
+
14
+ - **Query** metadata, clinical data, administrative data, and more — with filtering, sorting, and cross-project support
15
+ - **Mutate** study data with a single `.commit()` call, including cascading saves across related records
16
+ - **Stream live updates** via reactive queries that automatically reflect changes in real time
17
+ - **Track changes** with a built-in audit trail for all relevant study data
18
+ - **Manage files** with resumable uploads and signed downloads, supporting files up to 5 GB
19
+
20
+ ## Getting started
21
+
22
+ ### Installation
23
+
24
+ ```sh
25
+ # npm
26
+ npm install @openedc/sdk
27
+
28
+ # pnpm / yarn / bun
29
+ pnpm add @openedc/sdk
30
+
31
+ # Deno
32
+ deno add npm:@openedc/sdk
33
+ ```
34
+
35
+ ### Initialization
36
+
37
+ ```ts
38
+ import { login, logout } from "@openedc/sdk";
39
+
40
+ const user = await login({
41
+ serverUrl: "server-base-url",
42
+ apiKey: "your-api-key"
43
+ });
44
+
45
+ await logout();
46
+ ```
47
+
48
+ ## Usage
49
+
50
+ ### Data querying, creation, and modification
51
+
52
+ #### Explicit project scope
53
+
54
+ ```ts
55
+ import { Project, Location, SubjectData } from "@openedc/sdk";
56
+
57
+ // Get project by name
58
+ const project = await Project
59
+ .where({ name: "Exemplary study" })
60
+ .first();
61
+
62
+ // Get the first location
63
+ const location = await Location
64
+ .where()
65
+ .project(project)
66
+ .first();
67
+
68
+ // Get all subjects from a location sorted by creation
69
+ const subjects = await SubjectData
70
+ .where({ location })
71
+ .project(project)
72
+ .sort("createdDate")
73
+ .asc().all();
74
+
75
+ // Create a new subject within a location
76
+ const subject = await new SubjectData("Patient-001", location.reference)
77
+ .commit({ project });
78
+
79
+ // Update an existing subject (no need to specify the project again)
80
+ subject.subjectKey = "Patient-002";
81
+ await subject.commit();
82
+
83
+ // Alternatively, use .set() for a single expression
84
+ await subject.set({ subjectKey: "Patient-003" }).commit();
85
+ ```
86
+
87
+ #### Global project scope
88
+
89
+ ```ts
90
+ import { Location, SubjectData } from "@openedc/sdk";
91
+
92
+ // Instead of specifying the project for each query or commit, it can also be scoped globally for
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.
95
+ scope(project);
96
+
97
+ const sameLocation = await Location
98
+ .where()
99
+ .first();
100
+
101
+ const sameSubjects = await SubjectData
102
+ .where({ location })
103
+ .sort("createdDate")
104
+ .asc().all();
105
+
106
+ // Create a new subject (note that Patient-003 would result in a unique constraint violation)
107
+ const newSubject = await new SubjectData("Patient-004").commit();
108
+ ```
109
+
110
+ #### Clinical data retrieval
111
+
112
+ ```ts
113
+ import { ItemGroupDef, ItemDef, ItemData } from "@openedc/sdk";
114
+
115
+ // Get data for specific subject and form (project-scoped from above)
116
+ const form = await ItemGroupDef
117
+ .where({
118
+ type: "Form",
119
+ name: "Demographics"
120
+ })
121
+ .first();
122
+
123
+ const formData = await ItemData
124
+ .where({
125
+ subject,
126
+ formOID: form?.oid
127
+ })
128
+ .all();
129
+
130
+ // Get data for specific subject, form, and item (using a form-scoped local search)
131
+ const item = form?.find(ItemDef, "name", "Gender");
132
+
133
+ const itemData = await ItemData
134
+ .where({
135
+ subject,
136
+ formOID: form?.oid,
137
+ itemOID: item?.oid
138
+ })
139
+ .all();
140
+ ```
141
+
142
+ ### Live queries
143
+
144
+ ```ts
145
+ // Subscribe to subject changes with a live query
146
+ await SubjectData.where({ location }).all((_, subject, method) => {
147
+ console.log(`Subject ${subject.subjectKey} was ${method} in ${location.name}.`);
148
+ });
149
+ ```
150
+
151
+ ```ts
152
+ // Subscribe to item data changes via the form status as proxy
153
+ await FormStatus.where().all(async (_, status) => {
154
+ const subject = await status.subject.data;
155
+ const items = await ItemData.where(ODMPath.with(status).toObject()).all();
156
+ console.log(`Updated form with ${items.length} items for subject ${subject.subjectKey}.`);
157
+ });
158
+ ```
159
+
160
+ ### Audit trail
161
+
162
+ ```ts
163
+ // Get the audit trail of all subjects within a project
164
+ const auditTrailSubjects = await SubjectData.where().audit();
165
+
166
+ // Get the audit trail of all item data of a given subject
167
+ const auditTrailItemsBySubject = await ItemData.where({ subject }).audit();
168
+ ```
169
+
170
+ ### File management
171
+
172
+ ```ts
173
+ // Upload a file (only readable to users with access to the subject)
174
+ const file = new File(["exemplary-data"], "file.txt");
175
+ const fileMetadata = await new FileMetadata(file, subject.reference).commit();
176
+ const fileContent = await new FileContent(fileMetadata.reference, file).commit();
177
+
178
+ // Track upload progress in real time
179
+ await fileContent.upload(progress => {
180
+ console.log(`Upload progress: ${progress * 100}`);
181
+ });
182
+ ```
183
+
184
+ ```ts
185
+ // Download a file
186
+ const downloadMetadata = await FileMetadata.where({ fileName: "file.txt" }).first();
187
+ const downloadContent = await FileContent.where({ metadata: downloadMetadata }).first();
188
+ const downloadURL = await downloadContent?.getUrl();
189
+ ```