rxome-generator 1.0.3 → 1.0.4-beta.2
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/JSON_INPUT_FORMAT.md +380 -0
- package/README.html +834 -0
- package/README.md +50 -19
- package/demos/demo_overfull.json +144 -0
- package/index.js +3 -3
- package/lib/package-info.js +4 -0
- package/lib/phenopackets.js +1951 -0
- package/lib/rxome-api-demo.js +26 -0
- package/lib/rxome-api.js +257 -0
- package/lib/rxome-api.test.js +9 -9
- package/lib/{rxome-generator.cjs → rxome-generator.js} +94 -80
- package/lib/rxome-generator.test.js +26 -17
- package/package.json +11 -2
- package/rxcode.js +12 -5
- package/rxcode.test.js +38 -39
- package/lib/rxome-api-demo.cjs +0 -60
- package/lib/rxome-api.cjs +0 -192
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
// https://openpgpjs.org/
|
|
2
|
-
|
|
2
|
+
import * as PGP from 'openpgp';
|
|
3
3
|
|
|
4
4
|
// https://github.com/soldair/node-qrcode
|
|
5
|
-
|
|
5
|
+
import QRCode from 'qrcode';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import jsonKeyConverter from 'json-key-converter';
|
|
8
|
+
import Protobuf from 'protobufjs';
|
|
9
|
+
import PhenoPacketDescriptor from "./phenopackets.js";
|
|
10
|
+
import { version } from './package-info.js';
|
|
10
11
|
|
|
11
12
|
//const { constants } = require('buffer');
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
import * as RxAPI from './rxome-api.js';
|
|
14
15
|
|
|
15
16
|
const apiVer = '1.0';
|
|
16
17
|
const RXAPI = RxAPI.API;
|
|
@@ -24,35 +25,42 @@ const ProtoBufRoot = Protobuf.Root.fromJSON( PhenoPacketDescriptor );
|
|
|
24
25
|
const PhenoPacket = ProtoBufRoot.lookupType('org.phenopackets.schema.v2.Phenopacket')
|
|
25
26
|
|
|
26
27
|
const ENUM_SEX = ['UNKNOWN', 'FEMALE', 'MALE', 'OTHER']
|
|
27
|
-
|
|
28
|
+
export { ENUM_SEX };
|
|
28
29
|
|
|
29
30
|
const ENUM_PROG_STATE = ['UNKNOWN_PROGRESS', 'IN_PROGRESS', 'COMPLETED', 'SOLVED', 'UNSOLVED']
|
|
30
|
-
|
|
31
|
+
export { ENUM_PROG_STATE };
|
|
31
32
|
|
|
32
|
-
const ENUM_ACMG =
|
|
33
|
+
const ENUM_ACMG =
|
|
33
34
|
['NOT_PROVIDED', 'BENIGN', 'LIKELY_BENIGN', 'UNCERTAIN_SIGNIFICANCE', 'LIKELY_PATHOGENIC', 'PATHOGENIC'];
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
export { ENUM_ACMG };
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
/************************************************************************************
|
|
39
40
|
* Functions for node.js only
|
|
40
41
|
************************************************************************************/
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
exports.generateRxomeKeyFiles = async (name = 'rxome', dir = '.') => {
|
|
47
|
-
const { privateKey, publicKey } = await exports.generateRxomeKeys( name );
|
|
43
|
+
export const generateRxomeKeyFiles = async (name = 'rxome', dir = '.') => {
|
|
44
|
+
try {
|
|
45
|
+
const FS = await import('fs/promises');
|
|
46
|
+
const { privateKey, publicKey } = await generateRxomeKeys( name );
|
|
48
47
|
await Promise.all([
|
|
49
48
|
FS.writeFile(`${dir}/${name}.private.key`, privateKey, { mode: 0o600 }, err => { console.log(err); }),
|
|
50
49
|
FS.writeFile(`${dir}/${name}.public.key`, publicKey, { mode: 0o600 }, function (err) { console.log(err); })
|
|
51
50
|
]);
|
|
52
51
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
catch (e) {
|
|
53
|
+
if (e instanceof Error && e.code !== "MODULE_NOT_FOUND") {
|
|
54
|
+
throw e;
|
|
55
|
+
} else {
|
|
56
|
+
throw new Error('fs module not available - cannot write key files');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const writeQR = async ( filename, data, api = RXAPI, apiEntry = APIENTRY ) => {
|
|
62
|
+
try {
|
|
63
|
+
const {qrData, pseudonym, content} = await prepareQR( data, api, apiEntry );
|
|
56
64
|
//const base64Data = qr_code.replace(/^data:image\/png;base64,/, "");
|
|
57
65
|
//FS.writeFile(filename, base64Data, 'base64', (err) => {console.log(err)} );
|
|
58
66
|
QRCode.toFile( filename, JSON.stringify( qrData ), { type: 'png'} )
|
|
@@ -61,19 +69,20 @@ try {
|
|
|
61
69
|
qr_content: content
|
|
62
70
|
}
|
|
63
71
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
catch (e) {
|
|
73
|
+
if (e instanceof Error && e.code !== "MODULE_NOT_FOUND") {
|
|
74
|
+
throw e;
|
|
75
|
+
} else {
|
|
76
|
+
throw new Error('fs module not available - cannot write QR file');
|
|
77
|
+
}
|
|
69
78
|
}
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
/************************************************************************************/
|
|
73
82
|
|
|
74
83
|
|
|
75
|
-
|
|
76
|
-
|
|
84
|
+
export const generateRxomeKeys = async (name = 'rxome') => {
|
|
85
|
+
const { privateKey, publicKey, revocationCertificate } = await PGP.generateKey({
|
|
77
86
|
type: 'ecc',
|
|
78
87
|
curve: 'curve25519',
|
|
79
88
|
//type: 'rsa',
|
|
@@ -82,41 +91,42 @@ exports.generateRxomeKeys = async (name = 'rxome') => {
|
|
|
82
91
|
passphrase: PASSPHRASE,
|
|
83
92
|
format: 'armored'
|
|
84
93
|
});
|
|
94
|
+
return { privateKey, publicKey, revocationCertificate };
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
|
|
88
|
-
|
|
98
|
+
export const fetchKey = async ( credentials, pseudonym = '', api = RXAPI, debug = false, apiEntry = APIENTRY ) => {
|
|
89
99
|
const result = await RxAPI.fetchData( `${api}/${apiEntry}/getpseudonym`, credentials, pseudonym, debug )
|
|
90
100
|
if ( result.pseudonym === '' )
|
|
91
101
|
result.pseudonym = pseudonym;
|
|
92
102
|
return result;
|
|
93
103
|
}
|
|
94
|
-
/*
|
|
104
|
+
/*
|
|
95
105
|
yields: { key: PGP_key, version: ..., pseudonym: ... }
|
|
96
106
|
*/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
export const fetchDemoPrivateKey = async ( credentials, api=RXTESTAPI, debug = false, apiEntry = APIENTRY ) =>
|
|
100
110
|
await RxAPI.fetchData( `${api}/${apiEntry}/getprivatedemokey`, credentials, '', debug )
|
|
101
111
|
|
|
102
|
-
/*
|
|
103
|
-
yields: { private_key: PGP_key }
|
|
112
|
+
/*
|
|
113
|
+
yields: { private_key: PGP_key }
|
|
104
114
|
*/
|
|
105
115
|
|
|
106
|
-
|
|
116
|
+
export const fetchRxomeKey = async ( credentials, api = RXAPI, debug = false, apiEntry = APIENTRY ) =>
|
|
107
117
|
await RxAPI.fetchData( `${api}/${apiEntry}/getrxomekey`, credentials, '', debug )
|
|
108
118
|
|
|
109
|
-
/*
|
|
110
|
-
yields: { key: PGP_key, version: ... }
|
|
119
|
+
/*
|
|
120
|
+
yields: { key: PGP_key, version: ... }
|
|
111
121
|
*/
|
|
112
|
-
|
|
122
|
+
export const convert_to_snake_case = data => jsonKeyConverter.convert( data, { camel: false } );
|
|
113
123
|
|
|
114
|
-
|
|
124
|
+
export const convertToCamelCase = data => jsonKeyConverter.convert( data, { camel: true } );
|
|
115
125
|
|
|
116
|
-
|
|
126
|
+
export const compressPhenoPacket = ( data ) => {
|
|
117
127
|
const compressedData = {
|
|
118
128
|
...data,
|
|
119
|
-
};
|
|
129
|
+
};
|
|
120
130
|
if ( data.phenotypicFeatures ) {
|
|
121
131
|
compressedData.compressedFeatures = {
|
|
122
132
|
includes: data.phenotypicFeatures.filter( feat => feat.excluded === undefined || feat.excluded === false || feat.excluded === 'false' ).map( feat => feat.type.id ),
|
|
@@ -128,26 +138,30 @@ exports.compressPhenoPacket = ( data ) => {
|
|
|
128
138
|
}
|
|
129
139
|
|
|
130
140
|
|
|
131
|
-
|
|
141
|
+
export const sanitizeSex = ( str ) =>
|
|
132
142
|
isNaN(+(str.toString()))
|
|
133
143
|
? Math.max(0, ENUM_SEX.indexOf(str.toUpperCase().replace('_SEX', '')))
|
|
134
144
|
: +str;
|
|
135
145
|
|
|
136
146
|
|
|
137
|
-
|
|
147
|
+
export const sanitizeProgState = ( str ) =>
|
|
138
148
|
isNaN(+(str.toString()))
|
|
139
149
|
? Math.max(0, ENUM_PROG_STATE.indexOf(str.toUpperCase()))
|
|
140
150
|
: +str;
|
|
141
151
|
|
|
142
152
|
|
|
143
|
-
|
|
153
|
+
export const sanitizeACMG = ( str ) => {
|
|
154
|
+
// special case for 'RISK_ALLELE'
|
|
155
|
+
if ( str == 'RISK_ALLELE' ) {
|
|
156
|
+
return 999;
|
|
157
|
+
}
|
|
144
158
|
isNaN(+(str.toString()))
|
|
145
159
|
? Math.max(0, ENUM_ACMG.indexOf(str.toUpperCase()))
|
|
146
160
|
: +str
|
|
161
|
+
}
|
|
147
162
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
res = {};
|
|
163
|
+
export const whiteListPhenoPacket = ( data ) => {
|
|
164
|
+
let res = {};
|
|
151
165
|
data.id && (res.id = data.id);
|
|
152
166
|
data.comment && (res.comment = data.comment);
|
|
153
167
|
data.subject && (res.subject = data.subject);
|
|
@@ -161,48 +175,48 @@ exports.whiteListPhenoPacket = ( data ) => {
|
|
|
161
175
|
}
|
|
162
176
|
|
|
163
177
|
|
|
164
|
-
|
|
178
|
+
export const sanitizePhenoPacket = ( data, key = '' ) => {
|
|
165
179
|
if ( data instanceof Object ) {
|
|
166
180
|
if (Array.isArray( data )) {
|
|
167
|
-
return data.map( x =>
|
|
181
|
+
return data.map( x => sanitizePhenoPacket(x));
|
|
168
182
|
} else {
|
|
169
183
|
let obj={};
|
|
170
|
-
for ( let k in data ) {
|
|
171
|
-
obj[k] =
|
|
184
|
+
for ( let k in data ) {
|
|
185
|
+
obj[k] = sanitizePhenoPacket( data[k], k );
|
|
172
186
|
}
|
|
173
187
|
return obj;
|
|
174
188
|
}
|
|
175
189
|
} else {
|
|
176
190
|
switch( key ){
|
|
177
191
|
case 'sex':
|
|
178
|
-
return
|
|
192
|
+
return sanitizeSex( data );
|
|
179
193
|
case 'progressStatus':
|
|
180
|
-
return
|
|
194
|
+
return sanitizeProgState( data );
|
|
181
195
|
case 'acmgPathogenicityClassification':
|
|
182
|
-
return
|
|
183
|
-
default:
|
|
196
|
+
return sanitizeACMG( data );
|
|
197
|
+
default:
|
|
184
198
|
return data;
|
|
185
199
|
}
|
|
186
200
|
}
|
|
187
201
|
}
|
|
188
202
|
|
|
189
203
|
|
|
190
|
-
|
|
204
|
+
export const verifyPhenoPacket = (data) => (
|
|
191
205
|
PhenoPacket.verify( data )
|
|
192
206
|
)
|
|
193
207
|
|
|
194
208
|
|
|
195
|
-
|
|
209
|
+
export const encodePhenoPacket = (data) => (
|
|
196
210
|
PhenoPacket.encode( data ).finish()
|
|
197
211
|
)
|
|
198
212
|
|
|
199
213
|
|
|
200
|
-
|
|
214
|
+
export const decodePhenoPacket = (data) => (
|
|
201
215
|
PhenoPacket.decode( data )
|
|
202
216
|
)
|
|
203
217
|
|
|
204
218
|
|
|
205
|
-
|
|
219
|
+
export const encode_serial = async (publicKeyStr, message) => {
|
|
206
220
|
const publicKey = await PGP.readKey({ armoredKey: publicKeyStr });
|
|
207
221
|
const encrypted = await PGP.encrypt({
|
|
208
222
|
message: await PGP.createMessage({ text: message }),
|
|
@@ -214,10 +228,10 @@ exports.encode_serial = async (publicKeyStr, message) => {
|
|
|
214
228
|
}
|
|
215
229
|
|
|
216
230
|
|
|
217
|
-
|
|
231
|
+
export const encode = async (publicKeyStr, message, binary = false) => {
|
|
218
232
|
return await Promise.all([
|
|
219
233
|
PGP.readKey({ armoredKey: publicKeyStr }),
|
|
220
|
-
PGP.createMessage( (binary
|
|
234
|
+
PGP.createMessage( (binary
|
|
221
235
|
? { binary: message, format: 'binary' }
|
|
222
236
|
: { text: message }) )
|
|
223
237
|
]).then( async ([publicKey, message]) => {
|
|
@@ -233,7 +247,7 @@ exports.encode = async (publicKeyStr, message, binary = false) => {
|
|
|
233
247
|
}
|
|
234
248
|
|
|
235
249
|
|
|
236
|
-
|
|
250
|
+
export const decode_serial = async (privateKeyStr, encrypted) => {
|
|
237
251
|
const privateKey = await PGP.readPrivateKey( { armoredKey: privateKeyStr } );
|
|
238
252
|
const privateKeyDecr = await PGP.decryptKey({
|
|
239
253
|
privateKey: privateKey,
|
|
@@ -250,16 +264,16 @@ exports.decode_serial = async (privateKeyStr, encrypted) => {
|
|
|
250
264
|
}
|
|
251
265
|
|
|
252
266
|
|
|
253
|
-
|
|
267
|
+
export const decode = async (privateKeyStr, encrypted, binary = false) => {
|
|
254
268
|
return await Promise.all([
|
|
255
269
|
PGP.readPrivateKey({ armoredKey: privateKeyStr })
|
|
256
270
|
.then( (privateKey) => {
|
|
257
271
|
return PGP.decryptKey({
|
|
258
272
|
privateKey: privateKey,
|
|
259
|
-
passphrase: PASSPHRASE
|
|
273
|
+
passphrase: PASSPHRASE
|
|
260
274
|
})
|
|
261
275
|
}),
|
|
262
|
-
PGP.readMessage( (binary
|
|
276
|
+
PGP.readMessage( (binary
|
|
263
277
|
? {binaryMessage: encrypted}
|
|
264
278
|
: {armoredMessage: encrypted} )
|
|
265
279
|
)
|
|
@@ -282,27 +296,28 @@ exports.decode = async (privateKeyStr, encrypted, binary = false) => {
|
|
|
282
296
|
}
|
|
283
297
|
|
|
284
298
|
|
|
285
|
-
|
|
299
|
+
export const prepareQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
|
|
300
|
+
console.log(`RxOME QR Generator version: ${version}`);
|
|
286
301
|
const { metaData, credentials, ...medical } = data;
|
|
287
302
|
|
|
288
|
-
const whiteListMedical =
|
|
289
|
-
const sanitizedMedical =
|
|
290
|
-
const compressedMedical =
|
|
291
|
-
const protobufMedical =
|
|
303
|
+
const whiteListMedical = whiteListPhenoPacket( medical );
|
|
304
|
+
const sanitizedMedical = sanitizePhenoPacket( whiteListMedical );
|
|
305
|
+
const compressedMedical = compressPhenoPacket( sanitizedMedical );
|
|
306
|
+
const protobufMedical = encodePhenoPacket( compressedMedical );
|
|
292
307
|
const base64Medical = RxAPI.bufferToBase64(protobufMedical);
|
|
293
308
|
|
|
294
|
-
const key = await
|
|
309
|
+
const key = await fetchKey( credentials, (metaData && metaData.pseudonym) || '', api, false, apiEntry );
|
|
295
310
|
|
|
296
311
|
// check:
|
|
297
312
|
const buff = RxAPI.base64ToBuffer( base64Medical );
|
|
298
|
-
const pheno =
|
|
313
|
+
const pheno = decodePhenoPacket( buff );
|
|
299
314
|
const medicalDeciphered = JSON.parse( JSON.stringify( pheno ));
|
|
300
315
|
// console.log( JSON.stringify(medicalDeciphered,' ', 2 ))
|
|
301
316
|
|
|
302
|
-
const cipher = await
|
|
317
|
+
const cipher = await encode( key.key, base64Medical );
|
|
303
318
|
|
|
304
319
|
//delete metaData.pseudonym;
|
|
305
|
-
const newMetaData = Object.fromEntries(
|
|
320
|
+
const newMetaData = Object.fromEntries(
|
|
306
321
|
Object.entries( metaData ).filter( ([key, val]) => key !== 'pseudonym')
|
|
307
322
|
)
|
|
308
323
|
|
|
@@ -312,7 +327,7 @@ exports.prepareQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
|
|
|
312
327
|
labid: key.lab_id,
|
|
313
328
|
keyver: key.version,
|
|
314
329
|
apiver: apiVer,
|
|
315
|
-
pseudonym: key.pseudonym,
|
|
330
|
+
pseudonym: key.pseudonym,
|
|
316
331
|
payload: cipher.toString()
|
|
317
332
|
}
|
|
318
333
|
|
|
@@ -331,8 +346,8 @@ exports.prepareQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
|
|
|
331
346
|
}
|
|
332
347
|
|
|
333
348
|
|
|
334
|
-
|
|
335
|
-
const {qrData, pseudonym, content} = await
|
|
349
|
+
export const makeQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
|
|
350
|
+
const {qrData, pseudonym, content} = await prepareQR( data, api, apiEntry );
|
|
336
351
|
return {
|
|
337
352
|
qr_code: await QRCode.toDataURL( JSON.stringify( qrData )),
|
|
338
353
|
pseudonym: pseudonym,
|
|
@@ -341,8 +356,8 @@ exports.makeQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
|
|
|
341
356
|
}
|
|
342
357
|
}
|
|
343
358
|
|
|
344
|
-
|
|
345
|
-
return
|
|
359
|
+
export const versionStr = () => {
|
|
360
|
+
return `RxOME QR Generator ${version}, 2025 Tom Kamphans, GeneTalk GmbH/RxOme GmbH`
|
|
346
361
|
}
|
|
347
362
|
|
|
348
363
|
|
|
@@ -356,5 +371,4 @@ exports.versionStr = () => {
|
|
|
356
371
|
// const clearBin = Buffer.from (clearBufferBin, 'Binary');
|
|
357
372
|
// const phenoPrimeBin = Coder.decodePhenoPacket(clearBin);
|
|
358
373
|
// const phenoBin = JSON.parse(JSON.stringify(phenoPrimeBin));
|
|
359
|
-
// ////
|
|
360
|
-
|
|
374
|
+
// ////
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import * as Coder from './rxome-generator.js';
|
|
2
|
+
import * as RxAPI from './rxome-api.js';
|
|
3
|
+
import * as ApiDemo from './rxome-api-demo.js';
|
|
4
|
+
import { getFetch } from './rxome-api.js';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
|
+
// Using native fetch API instead of axios
|
|
7
8
|
|
|
8
9
|
const RXAPI = RxAPI.API;
|
|
9
10
|
const RXTESTAPI = RxAPI.TESTAPI;
|
|
@@ -16,9 +17,9 @@ const CREDENTIALS = {
|
|
|
16
17
|
key: ApiDemo.DEMO_API_PRIVATE_KEY
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
const DEMO_DATA = JSON.parse(
|
|
20
|
+
const DEMO_DATA = JSON.parse(readFileSync('./demos/demo_data_full.json'));
|
|
20
21
|
|
|
21
|
-
const DEMO_DATA_COMPRESSED = JSON.parse(
|
|
22
|
+
const DEMO_DATA_COMPRESSED = JSON.parse(readFileSync('./demos/demo_data_full_compressed.json'));
|
|
22
23
|
// compressed: without credentials due to whitelist/phenopacket filtering
|
|
23
24
|
|
|
24
25
|
const DEMO_PSEUDONYM = 'HANSMOTKAMP';
|
|
@@ -98,7 +99,7 @@ describe('API access', () => {
|
|
|
98
99
|
Coder.fetchKey(wrong_credentials, 'lab_ps', RXTESTAPI)
|
|
99
100
|
.then(data => { })
|
|
100
101
|
.catch(err => {
|
|
101
|
-
expect(err.response
|
|
102
|
+
expect(err.response && err.response.status.toString().toMatch('403'));
|
|
102
103
|
});
|
|
103
104
|
});
|
|
104
105
|
});
|
|
@@ -277,8 +278,8 @@ describe('Coder', () => {
|
|
|
277
278
|
test('should encode and decode with key files', async () => {
|
|
278
279
|
const message = 'Be Sure To Drink Your Ovaltine';
|
|
279
280
|
await Coder.generateRxomeKeyFiles( 'testsuite' );
|
|
280
|
-
const privateKey =
|
|
281
|
-
const publicKey =
|
|
281
|
+
const privateKey = readFileSync('./testsuite.private.key').toString();
|
|
282
|
+
const publicKey = readFileSync('./testsuite.public.key').toString();
|
|
282
283
|
const cipher = await Coder.encode(publicKey, message);
|
|
283
284
|
const clear = await Coder.decode(privateKey, cipher);
|
|
284
285
|
expect(clear).toBe(message);
|
|
@@ -291,14 +292,22 @@ describe('Rails', () => {
|
|
|
291
292
|
const message = 'A-well-a bird bird bird, bird is the word';
|
|
292
293
|
const cipher = await Coder.encode(PUBLIC_KEY, message);
|
|
293
294
|
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
295
|
+
const params = new URLSearchParams({
|
|
296
|
+
cipher: cipher
|
|
297
|
+
});
|
|
298
|
+
const fetch = await getFetch();
|
|
299
|
+
const res = await fetch(`${RXTESTAPI}/api/v1/decryptTest?${params}`, {
|
|
300
|
+
method: 'GET',
|
|
301
|
+
headers: {
|
|
302
|
+
'Content-Type': 'application/json'
|
|
299
303
|
}
|
|
300
|
-
})
|
|
301
|
-
|
|
304
|
+
}).then(response => {
|
|
305
|
+
if (!response.ok) {
|
|
306
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
307
|
+
}
|
|
308
|
+
return response.json();
|
|
309
|
+
}).then(data => ({ data }))
|
|
310
|
+
expect( res.data && res.data.clearText ).toEqual( message )
|
|
302
311
|
});
|
|
303
312
|
});
|
|
304
313
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rxome-generator",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4-beta.2",
|
|
4
4
|
"description": "Generates QR codes containing medical information for use with the FindMe2Care platform.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"homepage": "https://github.com/GeneTalkTK/rxome-qrcode-generator#readme",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@noble/ed25519": "^2.2.3",
|
|
35
|
-
"axios": "^1.7.9",
|
|
36
35
|
"commander": "^13.1.0",
|
|
37
36
|
"json-key-converter": "^1.0.0",
|
|
38
37
|
"noble-ed25519": "^1.2.6",
|
|
38
|
+
"node-fetch": "^3.3.2",
|
|
39
39
|
"openpgp": "^6.1.0",
|
|
40
40
|
"protobufjs": "^7.4.0",
|
|
41
41
|
"protobufjs-cli": "^1.1.3",
|
|
@@ -48,5 +48,14 @@
|
|
|
48
48
|
},
|
|
49
49
|
"bin": {
|
|
50
50
|
"rxcode": "rxcode.js"
|
|
51
|
+
},
|
|
52
|
+
"jest": {
|
|
53
|
+
"globals": {
|
|
54
|
+
"NODE_OPTIONS": "--experimental-vm-modules"
|
|
55
|
+
},
|
|
56
|
+
"moduleNameMapper": {
|
|
57
|
+
"^(\\.{1,2}/.*)\\.js$": "$1"
|
|
58
|
+
},
|
|
59
|
+
"transform": {}
|
|
51
60
|
}
|
|
52
61
|
}
|
package/rxcode.js
CHANGED
|
@@ -4,9 +4,9 @@ import * as FS from 'fs';
|
|
|
4
4
|
import sharp from 'sharp';
|
|
5
5
|
|
|
6
6
|
// const Coder = require( './lib/rxome-generator' );
|
|
7
|
-
import * as Coder from './lib/rxome-generator.
|
|
8
|
-
import * as ApiDemo from './lib/rxome-api-demo.
|
|
9
|
-
import * as RxAPI from './lib/rxome-api.
|
|
7
|
+
import * as Coder from './lib/rxome-generator.js';
|
|
8
|
+
import * as ApiDemo from './lib/rxome-api-demo.js' ;
|
|
9
|
+
import * as RxAPI from './lib/rxome-api.js';
|
|
10
10
|
|
|
11
11
|
import { program } from 'commander';
|
|
12
12
|
import * as Path from 'path';
|
|
@@ -186,6 +186,10 @@ program.command('preprocess')
|
|
|
186
186
|
.option('-c, --compress', 'Compact HPO term list')
|
|
187
187
|
.action( async (inputfile, options) => {
|
|
188
188
|
let data = JSON.parse(FS.readFileSync( inputfile || '/dev/stdin' ));
|
|
189
|
+
if (! (options.case || options.whitelist || options.sanitize || options.compress)) {
|
|
190
|
+
process.stderr.write( 'Error: at least one preprocessing option must be selected (-C, -w, -s, -c).');
|
|
191
|
+
return 1;
|
|
192
|
+
}
|
|
189
193
|
options.case && (data = Coder.convertToCamelCase( data ));
|
|
190
194
|
options.whitelist && (data = Coder.whiteListPhenoPacket( data ));
|
|
191
195
|
options.sanitize && (data = Coder.sanitizePhenoPacket( data ));
|
|
@@ -306,12 +310,15 @@ program.command('decrypt')
|
|
|
306
310
|
key = FS.readFileSync( options.keyfile ).toString();
|
|
307
311
|
}
|
|
308
312
|
|
|
313
|
+
//@TODO: Guard against missing key or cipher
|
|
309
314
|
const data = await Coder.decode( key, cipher.toString() );
|
|
310
315
|
|
|
311
316
|
let result;
|
|
312
317
|
if ( options.complete ){
|
|
313
|
-
|
|
314
|
-
|
|
318
|
+
if ( options.debug ) {
|
|
319
|
+
const clearBufferB64 = await Coder.decode( key, cipher.toString() );
|
|
320
|
+
console.error( "buffer:", clearBufferB64 )
|
|
321
|
+
}
|
|
315
322
|
const clearB64 = RxAPI.base64ToBuffer(data);
|
|
316
323
|
options.debug && console.error( "clear ", clearB64 )
|
|
317
324
|
const phenoPrimeB64 = Coder.decodePhenoPacket(clearB64);
|